How to properly use SDL_BlitSurface() with SDL_CreateRGBSurface()?

11,207

You inverted source and destination. To blit on screen, it should be

SDL_BlitSurface(layer, NULL, screen, NULL);

doc for SDL_BlitSurface

Share:
11,207
Vilinkameni
Author by

Vilinkameni

Updated on July 14, 2022

Comments

  • Vilinkameni
    Vilinkameni almost 2 years

    (See "Edit 2" below for the solution.)

    I need to create SDL surfaces from scratch, instead of loading them from a file. Unfortunately, SDL_BlitSurface() seems to render all colors as black when used with the surface generated through SDL_CreateRGBSurface(). This is my code:

    int main(int argc, char** argv)
    {
        SDL_Surface* screen = SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE);
        SDL_Surface* layer = SDL_CreateRGBSurface(SDL_HWSURFACE, 100, 100,
            screen->format->BitsPerPixel,
            screen->format->Rmask,
            screen->format->Gmask,
            screen->format->Bmask,
            screen->format->Amask
        );
        SDL_Rect rect;
    
        rect.x = 0;
        rect.y = 0;
        rect.w = 100;
        rect.h = 100;
    
        Uint32 blue = SDL_MapRGB(screen->format, 0, 0, 255);
        SDL_FillRect(layer, &rect, blue);
        SDL_BlitSurface(screen, NULL, layer, NULL);
        SDL_Flip(screen);
        SDL_Delay(3000);
        return 0;
    }
    

    What I get is a black screen, instead of a 100x100 blue rectangle. What I could find by Googling doesn't seem to help me, as those questions either apply to 8bit surfaces (and setting palettes — my bpp is 32 here) or are left unanswered.

    So, I would like to know how should I properly blit a generated surface onto a SDL screen.

    Edit: I see it was an error in the parameter ordering. The line in question should read

    SDL_BlitSurface(layer, NULL, screen, NULL);
    

    Still, I am having trouble to achieve the same effect in my more complex C++ program. I will post the relevant parts of the code here:

    main.cpp:

    int main(int argc, char** argv)
    {
        SDLScreen screen(1024, 700, "Hello, SDL!");
    SDL_Event event;
        SDLMenu menu;
        bool shouldQuit = false;
    
        menu.setBounds(200, 100, 200, 600);
        menu.setFontName("NK211.otf");
        menu.setFontSize(36);
        menu.setEffect(sdlteShadowText);
        menu.addItem("New game");
        menu.addItem("Load game");
        menu.addItem("Save game");
        menu.addItem("Exit");
        menu.render();
    
        while (!shouldQuit)
        {
            menu.draw(screen.getSurface());
            SDL_Flip(screen.getSurface());
            SDL_Delay(10);
            while (SDL_PollEvent(&event))
            {
                if (event.type == SDL_QUIT)
                {
                    shouldQuit = true;
                }
                else if (event.type == SDL_KEYUP)
                {
                    if (event.key.keysym.sym == SDLK_q)
                    {
                        shouldQuit = true;
                    }
                }
            }
        }
    }
    

    SDLMenu.cpp:

    void
    SDLMenu::setSelectionColorRGB(int r, int g, int b)
    {
        SDL_VideoInfo* info = (SDL_VideoInfo*)SDL_GetVideoInfo();
        selectionColor = SDL_MapRGB(info->vfmt, r, g, b);
    }
    
    void
    SDLMenu::render()
    {
        SDLText* current = NULL;
        SDL_VideoInfo* info = (SDL_VideoInfo*)SDL_GetVideoInfo();
    
        if (!items->empty())
        {
            current = getItemAt(currentItem);
            selectionRect = getItemRect(current);
            setSelectionColorRGB(0,0,255);
            selectionCanvas = SDL_CreateRGBSurface(SDL_HWSURFACE,
                selectionRect->w, selectionRect->h,
                info->vfmt->BitsPerPixel,
                info->vfmt->Rmask,
                info->vfmt->Gmask,
                info->vfmt->Bmask,
                info->vfmt->Amask);
            SDL_FillRect(selectionCanvas, selectionRect, selectionColor);
            SDL_SaveBMP(selectionCanvas, "selection.bmp"); // debug
        }
    
        for (list<SDLText*>::iterator i = items->begin();
            i != items->end(); i++)
        {
            (*i)->render();
        }
    }
    
    void
    SDLMenu::draw(SDL_Surface* canvas)
    {
        int currentY = bounds.y;
    
        if (selectionCanvas != NULL)
        {
            SDL_BlitSurface(selectionCanvas, NULL, canvas, selectionRect);
        }
    
        for (list<SDLText*>::iterator i = items->begin();
            i != items->end(); i++)
        {
            (*i)->draw(bounds.x, currentY, canvas);
            currentY += fontSize + itemGap;
        }
    }
    

    SDLScreen.cpp:

    SDLScreen::SDLScreen(int w, int h, string t, int d)
        : width(w), height(h), depth(d), title(t)
    {
        SDL_Init(SDL_INIT_EVERYTHING);
        SDL_WM_SetCaption(title.c_str(), NULL);
        refresh();
    }
    
    void
    SDLScreen::refresh()
    {
        screen = SDL_SetVideoMode(width, height, 32, SDL_HWSURFACE);
    }
    

    The selection rectangle for the active menu item should be blue, but it shows up in black. The file selection.bmp is also all black.

    Edit 2: I found out what created the problem. The selectionRect was set relative to the screen, while the selectionCanvas had the width and height of a particular menu item. So, the filling was done out of bounds of the selectionCanvas. Adding separate SDL_Rect for filling solved the problem.

    SDL_Rect fillRect;
    fillRect.x = 0;
    fillRect.y = 0;
    fillRect.w = selectionRect->w;
    fillRect.h = selectionRect->h;
    SDL_FillRect(selectionCanvas, &fillRect, selectionColor);
    
    // and later...
    
    SDL_BlitSurface(selectionCanvas, NULL, canvas, selectionRect);