androidc++c++17sdl-2sdl-image

PNG-file with wrong colors because of SDL_RenderReadPixels! Or: Which SDL_PixelFormat does this function return?


In my TexturWrapper class I need a method to save the main-texture or a part of it to a PNG-file.

IMG_SavePNG is just able to save a SDL_Surface to a file, not a SDL_Texture.

The main-texture of my class can be SDL_TEXTUREACCESS_STREAMING or SDL_TEXTUREACCESS_TARGET. Because of the Target possibility I can not access the Texture pixels directly with the SDL_LockTexture. But we need the Pixel-Data to create a Surface out of a Texture.

...in theory it works my way, BUT I can not get the correct colors. For example one of my testfiles have in the original the backgroundcolor cyan, which means in SDL_Color: {0, 255, 255, 255} but the png-file background is yellow. The SDL_Color yellow would be {255, 255, 0 , 255}. So I swapped the mask for the red and blue channels first. Interesting is that it still was a yellow background after that change. I tried other combinations with no effect. Some returned for sure the SDL_Error "unknown pixelformat".

I returned the mask to normal and wrote a function to change the Pixels of the surface manually:

void rearrangePixels(Uint8* pixelData, int width, int height, Uint32 rmask, Uint32 gmask, Uint32 bmask, Uint32 amask)

These are all the possible combinations of R, G, B and A:

RGBA
RGAB
RBGA
RBAG
RAGB
RABG
GRBA
GRAB
GBRA
GBAR
GARB
GABR
BRGA
BRAG
BGRA
BGAR
BARG
BAGR
ARGB
ARBG
AGRB
AGBR
ABRG
ABGR

...I got just 2 different png-file products. 1. Yellow background or 2. 100% transparent and so invisible picture.

I tried to solve this problem with Google and heavy use of phind.com for a month now.

I have tried to find a solution in older posts and answers in stackoverflow too and I have merged some code from other users, like user1902824 in my code (even I don't have a "SDL_CreateRGBSurfaceWithFormatFrom" in my version of SDL2)...

But my problem still persists.

I am working with C4droid, directly on Android in C++17 and with SDL2 + SDL_Image. My only Hardware is a 3-4 years old Tablet-PC from Huawaii with 3GB Ram and Android-8. That's why C4droid can not be in the Newest version and so my SDL2.

EDIT:

I really tried everything i found or could think about.

And maybe some other I don't remember just now.

I'm not sure, but I believe that SDL_RenderReadPixels returns different data, depending on the system and/or hardware!?

By the way, here is an original image I used: Original colors When I load the file to a surface and save it directly with IMG_SavePNG it is identical!

This is the one I get, when I used SDL_RenderReadPixels first as in my code: Colors after the SDL_RenderReadPixels function

Finally (and most surprisingly for me) is this the picture I get, when I use my rearrangePixels method with no matter what combination of channels: Colors after my rearrangePixels method

Here is a reduced and corrected version of my Code (you can copy and paste the whole code step by step in just one cpp file to compile it with c4droid (you will need to change a path to an image)):

First, the initialization of the variables, SDL2, SDL_Image and a few convenience functions:

#include <SDL2/SDL.h>
#include <SDL_image.h>
#include <string>


// Global variables
static SDL_Window *myWindow;
static SDL_Renderer *myRenderer;


// call a message box
void MessageBox( std::string header , std::string message )
{
   SDL_ShowSimpleMessageBox( SDL_MESSAGEBOX_INFORMATION , header.c_str() , message.c_str() , NULL );
}


// clean up
void sdlExit( void )
{
    SDL_DestroyWindow( myWindow );
    IMG_Quit();
    SDL_DestroyRenderer( myRenderer );
    SDL_Quit();
    return;
}


// returns the aktual display mode in a sdl_rect
SDL_Rect GetDisplayMode()
{
    SDL_DisplayMode displayM;
    SDL_GetCurrentDisplayMode(0, &displayM);

    // get a percent of the display hight
    int percentY = displayM.h / 100;

    // trick the android bars out for fullscreen immersive-mode
    int adjustedY = - (percentY * 3); // Set y-coordinate 3% below the upperbound 
    int adjustedHeight = displayM.h + (percentY * 6); // hight + 6%

    SDL_Rect displayMode = {0, adjustedY, displayM.w, adjustedHeight};

    return displayMode;
}


// Initialize SDL2, SDL_Image and open a window
bool sdlInit( const char* title)
{
  // Initialize SDL2
  if ( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER )  != 0 ) {
        char txt[ 64 ];
          sprintf( txt , "Could not initialisieren SDL2: %s.\n" , SDL_GetError() );
            MessageBox( "In Function sdlInit:" , txt );
            return false;
  }
  atexit( SDL_Quit );

        // Device display mode
        SDL_Rect displayMode = GetDisplayMode();

  // the window:
  myWindow = SDL_CreateWindow(
      title,                                                                   // title "..."
      SDL_WINDOWPOS_UNDEFINED,                  // initialize x position
      SDL_WINDOWPOS_UNDEFINED,                  // initialize y position
      displayMode.w,                                               // width, in pixel
      displayMode.h,                                                // hight, in pixel
      SDL_WINDOW_FULLSCREEN_DESKTOP ); // windowmode or 0

// Start Immersive Full-Screen-Mode
SDL_SetHint(SDL_HINT_ANDROID_SEPARATE_MOUSE_AND_TOUCH, "1");
SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_MODE_WARP, "1");
SDL_SetWindowFullscreen(myWindow, SDL_WINDOW_FULLSCREEN_DESKTOP);

  // Check window
  if ( myWindow == NULL ) {
        char txt[ 64 ];
          sprintf( txt , "Could not create window: %s\n" , SDL_GetError() );
            MessageBox( "In Function sdlInit:" , txt );
            return false;
  }

  // Initialize Renderer,
  myRenderer = SDL_CreateRenderer( myWindow , -1 , SDL_RENDERER_ACCELERATED ); 
  SDL_SetHint( SDL_HINT_RENDER_SCALE_QUALITY , "linear" );
  SDL_RenderSetLogicalSize( myRenderer , displayMode.w , displayMode.h );

  // Check Renderer
  if ( myRenderer == NULL ) {
        char txt[ 64 ];
          sprintf( txt , "Could not initialize renderer: %s\n" , SDL_GetError() );
            MessageBox( "In Function sdlInit:" , txt );
            return false;
  }

  // Set blending
  SDL_SetRenderDrawBlendMode( myRenderer , SDL_BLENDMODE_BLEND  );

  // Initialize SDL_Img--------------------------------F1.2
  int imgFlags = IMG_INIT_JPG | IMG_INIT_PNG | IMG_INIT_TIF;
  int initted = IMG_Init( imgFlags );
  if (( initted & imgFlags ) != imgFlags ) {
        char txt[ 64 ];
          sprintf( txt , "IMG_Init: Failed to init required support: %s\n", IMG_GetError() );
            MessageBox( "In Function sdlInit:" , txt );
            return false;
  }
  return true;
}

Now the TexturWrapper class body:

class TexturWrapper
{
      
    public:
    
        // Init Variables
        TexturWrapper();

        // Destructor
        ~TexturWrapper();

        // Load ...with colorKey?
        bool Laden( std::string pfad , bool CKAktiv = 0 , unsigned char CKr = 0 , unsigned char CKg = 0 , unsigned char CKb = 0  );

        // Blank-Texture
        bool Blank( int BREITE , int HOEHE , char ta ); // (w, h, 's' = streaming and 't' = target)

        // Size of Image
        int getBreite(); //width
        int getHoehe(); //highth

        // Set as Rendertarget
        void Renderziel();

            // Reset Rendertarget
            void RenderzielReset();

        // Render Texture (texture-clip) to specific place in specific angle in specific size
        void Rendern( int x , int y , SDL_Rect* clip = NULL , double angle = 0.0 , SDL_Point* center = NULL ,  SDL_RendererFlip flip = SDL_FLIP_NONE , SDL_Rect* rQuad = NULL , bool centerScale = true);

         // Pixel-Manipulation
         SDL_Surface* flip_surface_vertical(SDL_Surface* sfc);

         // Put ImageTextur (ImageTextur - clip) to a file
         void SaveImage(const char *filename, SDL_Rect* clip = NULL, SDL_Rect* rQuad = NULL);

        //For use if the Rendersize is adjustet (adjustRectToPreserveAspectRatio)
        int getPositionX();
      int getPositionY();
        int getRenderW();
        int getRenderH();

        // destroy just texture
        void free();
        // Object clear
        void leeren();

    private:

      // tried to get a correction of the result from SDL_RenderReadPixels()
      void rearrangePixels(Uint8* pixelData, int width, int height, Uint32 rmask, Uint32 gmask, Uint32 bmask, Uint32 amask) {
          for (int y = 0; y < height; ++y) {
              for (int x = 0; x < width; ++x) {
                  // pointer to one pixel
                  Uint8* pixel = pixelData + y * width * 4 + x * 4;
      
                  // extract pixeldata
                  Uint32 pixelValue = *(Uint32*)pixel;
//                  pixelValue = pixelValue << 8; // Ignore first byte if this would be the problem
      
                  Uint8 r = (pixelValue & rmask) >> 24;
                  Uint8 g = (pixelValue & gmask) >> 16;
                  Uint8 b = (pixelValue & bmask) >> 8;
                  Uint8 a = pixelValue & amask;
      
                  // invert colors ? tried the channels even separatet or in combination of 2 out
      //            r = 255 - r;
      //            g = 255 - g;
      //            b = 255 - b;
      
                  // new order of the color-channels
                  *pixel = (a << 24) | (r << 16) | (g << 8) | b;
              }
          }
      }

      
      void adjustRectToPreserveAspectRatio(SDL_Rect* srcRect, SDL_Rect* dstRect) {
          float srcAspectRatio = (float)srcRect->w / srcRect->h;
          float dstAspectRatio = (float)dstRect->w / dstRect->h;
          if (srcAspectRatio > dstAspectRatio) {
              // if texture.w > destination.w, change hight
              dstRect->h = dstRect->w / srcAspectRatio;
          } else if (srcAspectRatio < dstAspectRatio) {
              // if texture.h > destination.h, change width
              dstRect->w = dstRect->h * srcAspectRatio;
          }
      }
    
    // Main-Texture
    SDL_Texture* ImageTextur;

      // Renderdestination
      SDL_Rect renderQuad = { 0 , 0 , 0 , 0 };

            // The pixeldata if Lock is possible
        void* Pixels;
        int Pitch;

        // Image dimensions
        int ImageBreite;
        int ImageHoehe;
        
        // ColorKey
        bool CKAktiv;
        unsigned char CKr;
        unsigned char CKg;
        unsigned char CKb;
        
};

Here the implementation:

TexturWrapper::TexturWrapper() 
{
    // Initialise Variables
    ImageTextur = NULL;
    ImageBreite = 0;
    ImageHoehe = 0;
    Pixels = NULL;
    Pitch = 0;
}

TexturWrapper::~TexturWrapper()
{
    leeren();
}

bool TexturWrapper::Laden( std::string pfad , bool CKAktiv , unsigned char CKr , unsigned char CKg , unsigned char CKb )
{
    free();

    // New Texture
    SDL_Texture* NeueTextur = NULL;

    // Load Image from path
    SDL_Surface* FileSurface = IMG_Load( pfad.c_str() );
    if( FileSurface == NULL )
    {
            char txt[ 64 ];
              sprintf( txt , "Could not load image: %s! SDL_image Error: %s\n" , pfad.c_str() , IMG_GetError() );
                MessageBox( "In class TexturWrapper:" , txt );
                exit( EXIT_FAILURE );
    }
    else
    {

        SDL_Surface* formattedSurface = SDL_ConvertSurfaceFormat( FileSurface , SDL_PIXELFORMAT_RGBA8888 , 0 );

        NeueTextur = SDL_CreateTexture( myRenderer , SDL_PIXELFORMAT_RGBA8888 , SDL_TEXTUREACCESS_STREAMING , formattedSurface->w , formattedSurface->h );

        ImageBreite = formattedSurface->w;
        ImageHoehe = formattedSurface->h;

        SDL_SetTextureBlendMode( NeueTextur , SDL_BLENDMODE_BLEND );

        SDL_LockTexture( NeueTextur , &formattedSurface->clip_rect , &Pixels , &Pitch );

            memcpy( Pixels , formattedSurface->pixels , formattedSurface->pitch * formattedSurface->h );
      
            // Pixels-Data into editable Format
            Uint32* pixels = (Uint32*)Pixels;
            int pixelCount = ( Pitch / 4 ) * ImageHoehe;
      
            // ColorKey Image
            if ( CKAktiv == 1 ) {
                    // Map Colors               
                    Uint32 colorKey = SDL_MapRGB( formattedSurface->format , CKr , CKg , CKb );
                    Uint32 transparent = SDL_MapRGBA( formattedSurface->format , 0x00 , 0xFF , 0xFF , 0x00 );
            
                    // ColorKey pixels
                    for( int i = 0 ; i < pixelCount ; ++i ) {
                        if( pixels[ i ] == colorKey ) {
                            pixels[ i ] = transparent;
                        }
                    }
                  }

        SDL_UnlockTexture( NeueTextur );
        Pixels = NULL;


        // clean up
        SDL_FreeSurface( formattedSurface );
        SDL_FreeSurface( FileSurface );
    }

    ImageTextur = NeueTextur;
      NeueTextur = NULL;
      SDL_DestroyTexture(NeueTextur);
    
    CKAktiv = 0;
    CKr = 0;
    CKg = 0;
    CKb = 0;
    
    return ImageTextur != NULL; 
}

void TexturWrapper::free()
{
    if( ImageTextur != NULL )
    {
        SDL_DestroyTexture( ImageTextur );
        ImageTextur = NULL;
        ImageBreite = 0;
        ImageHoehe = 0;
        Pixels = NULL;
        Pitch = 0;
    }
}

void TexturWrapper::leeren()
{
        SDL_DestroyTexture( ImageTextur );
        ImageTextur = NULL;
        ImageBreite = 0;
        ImageHoehe = 0;
        Pixels = NULL;
        Pitch = 0;
        CKAktiv = 0;
        CKr = 0;
        CKg = 0;
        CKb = 0;
}

void TexturWrapper::Rendern( int x , int y , SDL_Rect* clip , double angle , SDL_Point* center , SDL_RendererFlip flip , SDL_Rect* rQuad , bool centerScale )
{

      if (rQuad != NULL)
      {
            //the picture
            SDL_Rect* TextureQuad = new SDL_Rect;
            TextureQuad->x = 0;
            TextureQuad->y = 0;
            TextureQuad->w = ImageBreite;
            TextureQuad->h = ImageHoehe;
            // Render area
            renderQuad.x = rQuad->x;
            renderQuad.y = rQuad->y;
            renderQuad.w = rQuad->w;
            renderQuad.h = rQuad->h;
            // scale, by keeping proportions
            adjustRectToPreserveAspectRatio(TextureQuad, &renderQuad);
            if (centerScale) {
                  // center picture in the middle on to renderpoint
                  renderQuad.x -= renderQuad.w / 2;
            }
            delete TextureQuad;
      }
      else {
            // Set render position and size on screen
            renderQuad = { x , y , ImageBreite , ImageHoehe };
      }

      // Set Clip_Rendering Dimensions and fit target-rect to it if clip exists
      if (clip != NULL)
      {
          renderQuad.w = clip->w;
          renderQuad.h = clip->h;
          adjustRectToPreserveAspectRatio(clip, &renderQuad);
      }

      SDL_RenderCopyEx( myRenderer , ImageTextur , clip , &renderQuad , angle , center , flip );
}

int TexturWrapper::getBreite()
{
    return ImageBreite;
}

int TexturWrapper::getHoehe()
{
    return ImageHoehe;
}

int TexturWrapper::getPositionX()
{
    return renderQuad.x;
}

int TexturWrapper::getPositionY()
{
    return renderQuad.y;
}

int TexturWrapper::getRenderW()
{
    return renderQuad.w;
}

int TexturWrapper::getRenderH()
{
    return renderQuad.h;
}

void TexturWrapper::Renderziel()
{
    SDL_SetRenderTarget( myRenderer , ImageTextur );
}

void TexturWrapper::RenderzielReset()
{
    SDL_SetRenderTarget( myRenderer , NULL );
}

bool TexturWrapper::Blank( int b , int h , char ta )
{
    // Blank Texture for Streaming....
      if ( ta == 's' ) {
    ImageTextur = SDL_CreateTexture( myRenderer , SDL_PIXELFORMAT_RGBA8888 , SDL_TEXTUREACCESS_STREAMING , b , h );
      }
      else if ( ta == 't' ) { // ...or Target access
    ImageTextur = SDL_CreateTexture( myRenderer , SDL_PIXELFORMAT_RGBA8888 , SDL_TEXTUREACCESS_TARGET , b , h );
      }
    if( ImageTextur == NULL )
    {
            char txt[ 64 ];
                sprintf( txt , "Could not create blank texture! SDL Error: %s\n" , SDL_GetError() );
                  MessageBox( "In class TexturWrapper:" , txt );
                  exit( EXIT_FAILURE );
    }
    else
    {
        ImageBreite = b;
        ImageHoehe = h;
    }
      Renderziel();
      SDL_SetRenderDrawColor(myRenderer, 0x00, 0x00, 0x00, 0x00);
      SDL_RenderClear(myRenderer);
      RenderzielReset();
      SDL_SetTextureBlendMode( ImageTextur , SDL_BLENDMODE_BLEND );
    return ImageTextur != NULL;
}


SDL_Surface* TexturWrapper::flip_surface_vertical(SDL_Surface* sfc)
{
    SDL_Surface* result = SDL_CreateRGBSurface(sfc->flags, sfc->w, sfc->h,
        sfc->format->BytesPerPixel * 8, sfc->format->Rmask, sfc->format->Gmask,
        sfc->format->Bmask, sfc->format->Amask);
    const auto pitch = sfc->pitch;
    const auto pxlength = pitch*(sfc->h - 1);
    auto pixels = static_cast<unsigned char*>(sfc->pixels) + pxlength;
    auto rpixels = static_cast<unsigned char*>(result->pixels) ;
    for(auto line = 0; line < sfc->h; ++line) { // copy sfc to result line by line, upside-down
        memcpy(rpixels,pixels,pitch);
        pixels -= pitch;
        rpixels += pitch;
    }
    return result;
}

This is the method with my problem in it:

// This is the method I try to get to work correctly!
void TexturWrapper::SaveImage(const char* file_name, SDL_Rect* clipRect, SDL_Rect* rQuadRect)
{
    // test clipRect and rQuadRect for NULL
    SDL_Rect clip = {0, 0, ImageBreite, ImageHoehe};
    SDL_Rect rQuad = {0, 0, ImageBreite, ImageHoehe};
    if (clipRect) {
        clip = *clipRect;
    }
    if (rQuadRect) {
        rQuad = *rQuadRect;
    }
    SDL_Texture* rQuadTexture = SDL_CreateTexture(myRenderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, rQuad.w, rQuad.h); // final-size texture
    SDL_SetRenderTarget(myRenderer, rQuadTexture); // make it target
    SDL_RenderCopy(myRenderer, ImageTextur, &clip, NULL); // render clip to it

    // surface in rQuad size
    SDL_Surface* surface = SDL_CreateRGBSurface(0, rQuad.w, rQuad.h, 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000);

    // get the pixels of rQuadTexture
    SDL_RenderReadPixels(myRenderer, NULL, surface->format->format, surface->pixels, surface->pitch);

    SDL_PixelFormat* format = surface->format;

/* // To gether information about the SDL_RenderReadPixels output-format
    char txt[ 256 ];
    sprintf( txt , "\nBits per pixel: %d\n Bytes per pixel: %d\n Rmask: %08X\n Gmask: %08X\n Bmask: %08X\n Amask: %08X\n" , format->BitsPerPixel, format->BytesPerPixel, format->Rmask, format->Gmask, format->Bmask, format->Amask);
    MessageBox( "Das Format:" , txt );
*/

      // lock if needed
      if (SDL_MUSTLOCK(surface)) {
          SDL_LockSurface(surface);
      }
      
      // manipulate pixeldata
      rearrangePixels((Uint8*)surface->pixels, surface->w, surface->h, format->Rmask, format->Gmask, format->Bmask, format->Amask);
      
      // Unlock if needet
      if (SDL_MUSTLOCK(surface)) {
          SDL_UnlockSurface(surface);
      }

    SDL_Surface* flipSurf = flip_surface_vertical( surface ); // because RenderReadPixels returns upsidedown data

    IMG_SavePNG(flipSurf, file_name);
    SDL_FreeSurface(surface);
    SDL_FreeSurface(flipSurf);
    SDL_SetRenderTarget(myRenderer, NULL);
}

And finally the main() to test everything:

int main( void )
{
  std::string picturefile = "resources/M.png"; // "path to an imagefile.png"

  if ( !sdlInit( "Testing TexturWrapper class" ) ) { // Initialize SDL2 and SDL_Image
      exit( EXIT_FAILURE );
  }

  // TexturWrapper class objects
  TexturWrapper tw1;
  TexturWrapper tw2;
  TexturWrapper tw3;

  tw1.Laden( picturefile ); // Load the imagefile without color-keying
  SDL_Rect screen = GetDisplayMode();
  tw1.Rendern( screen.x , screen.y , NULL , 0.0 , NULL , SDL_FLIP_NONE , &screen, false ); // Render it (scaled) to the middle of the screen...  // false = renderpoint is not centered (0 = 0 and not -(half of the texture.w))

  SDL_RenderPresent( myRenderer ); // Show rendered stuff on the screen
  SDL_Delay( 3000 );

  tw2.Blank( tw1.getRenderW() , tw1.getRenderH() , 't' ); // 't' = SDL_TEXTUREACCESS_TARGET
  tw2.Renderziel(); // This object is now the rendertarget

  tw1.Rendern( screen.x , screen.y , NULL , 0.0 , NULL , SDL_FLIP_NONE , &screen , false ); // 'copy' tw1 to tw2
  tw2.RenderzielReset(); // Rendertarget = myWindow

  tw2.Rendern( screen.x , screen.y , NULL , 0.0 , NULL , SDL_FLIP_NONE , &screen , false ); // show the copy

  SDL_RenderPresent( myRenderer ); // Show rendered stuff on the screen
  SDL_Delay( 3000 );

  SDL_Rect clip = {0, 0, 500, 500}; // if you want to save just a clip of the copy
  SDL_Rect rquad = {0, 0, tw2.getBreite()/4, tw2.getHoehe()/4}; // size of the picture.png
  tw2.SaveImage( "Testfile.png" );//, &clip, &rquad ); // Save the copy as image

  tw3.Laden( "Testfile.png" ); // Load the saved image
  tw3.Rendern( screen.x , screen.y , NULL , 0.0 , NULL , SDL_FLIP_NONE , &screen, false ); // Show then loaded

  SDL_RenderPresent( myRenderer ); // Show rendered stuff on the screen
  SDL_Delay( 3000 );

  // clean up
  tw1.leeren();
  tw2.leeren();
  tw3.leeren();
  sdlExit();
  exit( EXIT_SUCCESS );
}

Solution

  • Well, i dont know why my rearrangePixels method did not worked. nvm!

    For my excuse, it was even my own first thought that the r channel and the b channel are swapped and needed just to be swapped again.

    But this is exactly what i tried to do with the rearrangePixels method.

    The solution is to just swap the channels on a new surface.

    Here is the full code of my solution:

    void TexturWrapper::SaveImage(const char* file_name, SDL_Rect* clipRect, SDL_Rect* rQuadRect) {
    // test clipRect and rQuadRect for NULL
    SDL_Rect clip = {0, 0, ImageBreite, ImageHoehe};
    SDL_Rect rQuad = {0, 0, ImageBreite, ImageHoehe};
    if (clipRect) {
        clip = *clipRect;
    }
    if (rQuadRect) {
        rQuad = *rQuadRect;
    }
    SDL_Texture* rQuadTexture = SDL_CreateTexture(myRenderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, rQuad.w, rQuad.h); // final-size texture
    SDL_SetRenderTarget(myRenderer, rQuadTexture); // make it target
    SDL_RenderCopy(myRenderer, ImageTextur, &clip, NULL); // render clip to it
    
    // surface in rQuad size
    SDL_Surface* surface = SDL_CreateRGBSurface(0, rQuad.w, rQuad.h, 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000);
    
    // get the pixels of rQuadTexture
    SDL_RenderReadPixels(myRenderer, NULL, surface->format->format, surface->pixels, surface->pitch);
    
    SDL_PixelFormat* format = surface->format;
    
      // lock if needet
      if (SDL_MUSTLOCK(surface)) {
          SDL_LockSurface(surface);
      }
    
      // THIS IS THE SOLUTION!
      SDL_Surface* swapped_surface = SDL_CreateRGBSurfaceFrom(
         (void*)surface->pixels,
         surface->w,
         surface->h,
         surface->format->BytesPerPixel * 8,
         surface->pitch,
         format->Bmask, // Rmask = Bmask from the original surface
         format->Gmask, // Gmask = Gmask
         format->Rmask, // Bmask = Rmask from the original surface
         format->Amask // Amask = Amask
      );
    
      
      // Unlock if needet
      if (SDL_MUSTLOCK(surface)) {
          SDL_UnlockSurface(surface);
      }
    
    SDL_Surface* flipSurf = flip_surface_vertical( swapped_surface ); // because RenderReadPixels returns upsidedown data
    
    IMG_SavePNG(flipSurf, file_name);
    SDL_FreeSurface(surface);
    SDL_FreeSurface(swapped_surface);
    SDL_FreeSurface(flipSurf);
    SDL_SetRenderTarget(myRenderer, NULL);
    }
    

    I am sorry for wasteing your time.