Search code examples
csdlsdl-2

Showing an image with SDL (Simple Directmedia Layer)


I have an image stored in memory as an array where the values are between 0 and 255. I would like to display this image to the screen using SDL. I am writing code in C.

I have written the following code based on tutorials that I found on the internet. My impression is that this should display an image. But instead, no matter how I change the data array, I always get a white window.

Can anyone tell me how to fix this? Any help is very appreciated.

void main( void ){
  SDL_Window* window;
  SDL_Surface* imgSurf;
  SDL_Surface* winSurf;
  SDL_Event sdlEvent;
  unsigned char* data;
  size_t i, j;
  bool running;
  size_t h, w;

  window = NULL;
  imgSurf = NULL;
  winSurf = NULL;
  SDL_Init( SDL_INIT_VIDEO | SDL_INIT_AUDIO );

  h = 480;
  w = 640;

  window = SDL_CreateWindow( "CIMPL Image", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
    (int) img->w, (int) img->h, SDL_WINDOW_ALLOW_HIGHDPI );

  winSurf = SDL_GetWindowSurface( window );

  // Make an image that's black on left and increasing to white on right
  data = malloc( sizeof( uint8_t ) * (h*w) );
  for( j=0; j < img->h; ++j ){
    for( i=0; i < img->w; ++i ){
      data[ i + j * img->w ] = (uint8_t) i;
    }
  }

  imgSurf = SDL_CreateRGBSurfaceFrom( (void*) data, (int) img->w, (int) img->h,
    8, w, 0, 0, 0, 0 );

  running = true;
  while( running ){

    if( SDL_WaitEvent( &sdlEvent ) ){
      // WaitEvent is appropriate if window responds to user events
      // If things happen without user events, use SDL_PollEvent

      switch( sdlEvent.type ){
        case SDL_QUIT:
          running = false;
          break;
      }
    }

    SDL_BlitSurface( imgSurf, NULL, winSurf, NULL );
    SDL_UpdateWindowSurface( window );
  }
  
  SDL_FreeSurface( imgSurf );  imgSurf = NULL;
  SDL_FreeSurface( winSurf );  winSurf = NULL;

  free( data );

  SDL_DestroyWindow( window );
  SDL_Quit();

  return 0;
}

Solution

  • From SDL_CreateRGBSurface

    Remarks

    If depth is 4 or 8 bits, an empty palette is allocated for the surface.

    From SDL_Palette

    Remarks

    Each pixel in an 8-bit surface is an index into the colors field of the SDL_Palette structure stored in SDL_PixelFormat. An SDL_Palette should never need to be created manually. It is automatically created when SDL allocates an SDL_PixelFormat for a surface. The colors values of an SDL_Surface's palette can be set with SDL_SetPaletteColors().

    That means, you have to set set colors in the palette and your allocated data contains the indices to the colors in the palette.

    e.g.

    /* pixels/indices */
    
    data = malloc( sizeof( uint8_t ) * (h*w) );
    for (j=0; j < img->h; ++j) {
        for (i=0; i < img->w; ++i) {
            data[ i + j * img->w ] = (uint8_t) (((float) i / (float) img->w) * 255);
        }
    }
    
    /* surface */
    
    imgSurf = SDL_CreateRGBSurfaceFrom( 
        (void*) data, 
        (int) img->w, (int) img->h, 
        8, w, 
        0, 0, 0, 0
    );
    
    /* palette */
    
    SDL_Color colors[256];
    
    for (int i=0; i < 256; ++i)
       colors[i].r = colors[i].g = colors[i].b = (Uint8)i;
    
    SDL_SetPaletteColors(imgSurf->format->palette, colors, 0, 256);