0

I have used SDL to save the window image via SDL_SaveBMP. The problem is that the saved image is upside down.

The saved image is

aaaa

while it has to be

bbbb

How should I fix the code?

screen_shot function:

void screen_shot(std::string filename)
{
    int width = glutGet(GLUT_WINDOW_WIDTH);
    int height = glutGet(GLUT_WINDOW_HEIGHT);

    SDL_Surface * image = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 24, 0x000000FF, 0x0000FF00, 0x00FF0000, 0);
    glReadBuffer(GL_FRONT);
    glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, image->pixels);

    SDL_SaveBMP(image, filename.c_str());
    SDL_FreeSurface(image);
}


Update

I try to flip the pixels. The result is a pure black output image. What is the problem?

#include "SDL/SDL.h"
#include "SDL/SDL_image.h"

#include <stdio.h>
#include <assert.h>
#include <iostream>
#include <GL/freeglut.h>
#include <boost/thread/thread.hpp> // for sleep
#include <SDL2/SDL.h>


Uint32 GetPixel24(SDL_Surface * surface,int x, int y)
{
    Uint32 rgb;
    Uint8 * pixel = (Uint8*)surface->pixels;
    pixel += (y * surface->pitch) + (x * sizeof(Uint8) * 3);
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
    rgb=(pixel[2] << 8)+(pixel[1] << 16)+(pixel[0] << 24);
#else
    rgb=(pixel[2] << 16)+(pixel[1] << 8)+(pixel[0]);
std::cout<<int(pixel[0])<<","<<int(pixel[1])<<","<<int(pixel[2])<<std::endl;

#endif

    return rgb;
}

void PutPixel24_nolock(SDL_Surface * surface, int x, int y, Uint32 color)
{
    Uint8 * pixel = (Uint8*)surface->pixels;
    pixel += (y * surface->pitch) + (x * sizeof(Uint8) * 3);
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
    pixel[0] = (color >> 24) & 0xFF;
    pixel[1] = (color >> 16) & 0xFF;
    pixel[2] = (color >> 8) & 0xFF;
#else
    pixel[0] = color & 0xFF;
    pixel[1] = (color >> 8) & 0xFF;
    pixel[2] = (color >> 16) & 0xFF;
#endif
}

void PutPixel24(SDL_Surface * surface, int x, int y, Uint32 rgb)
{
    if(SDL_MUSTLOCK(surface))
        SDL_LockSurface(surface);
    PutPixel24_nolock(surface,x,y,rgb);
    if(SDL_MUSTLOCK(surface))
        SDL_LockSurface(surface);
}

SDL_Surface *flip_vertical_surface(SDL_Surface *surface)
{
    SDL_Surface *flipped = nullptr;

    flipped = SDL_CreateRGBSurface( SDL_SWSURFACE, surface->w, surface->h, 24, 0x000000FF, 0x0000FF00, 0x00FF0000, 0);

    for(int x=0;x<surface->w;x++)
        for(int y=0;y<surface->h;y++)
        {
            int ry=(surface->h)-1-y;
            Uint32 pixel=GetPixel24(surface,x,y);
            PutPixel24(flipped,x,ry,pixel);
        }
    return flipped;
}

void screen_shot(std::string filename)
{
    int width = glutGet(GLUT_WINDOW_WIDTH);
    int height = glutGet(GLUT_WINDOW_HEIGHT);

    SDL_Surface * image = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 24, 0x000000FF, 0x0000FF00, 0x00FF0000, 0);
    SDL_Surface *image_fliped=flip_vertical_surface(image);
    if(image_fliped==nullptr)
        std::cout<<"image_fliped must be different than nullptr"<<std::endl;

    glReadBuffer(GL_FRONT);
    glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, image->pixels);

    SDL_SaveBMP(image_fliped, filename.c_str());
    SDL_FreeSurface(image);
    SDL_FreeSurface(image_fliped);
}

void cback_render()
{
    if(!glutGetWindow())
        return ;
    static float rotations = 0;

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glLoadIdentity();
    glRotatef(rotations, 0, 0, 1);

    glBegin(GL_TRIANGLES);
        glVertex3f(0,0,0);
        glVertex3f(1,0,0);
        glVertex3f(0,1,0);
    glEnd();

    glutSwapBuffers();
}

void timer(int )
{
    if(!glutGetWindow())
        return ;
    static bool saved=false;
    if(!saved)
    {
        screen_shot("image.bmp");
        saved=true;
    }
    glutPostRedisplay();
    glutMainLoopEvent();
    glutTimerFunc(30, timer, 1);
}

void init()
{
    int argc=1;
    glutInit(&argc, nullptr);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH);
    glutInitWindowPosition(100, 100);
    glutInitWindowSize(512, 512);
    glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_GLUTMAINLOOP_RETURNS);

    glutCreateWindow("freegluttest");
    glutDisplayFunc (cback_render);
    glutTimerFunc(30, timer, 1);
}

int main()
{

    init();
    glutMainLoop();

    return 0;
}
ar2015
  • 5,558
  • 8
  • 53
  • 110
  • 2
    afaik OGL coordinate system usually uses the bottom left corner as origin, whereas BMP uses the top right corner - you will have to either conform to the latter coordinate system or flip the image before saving to get the picture right. – Steeve Jan 03 '17 at 12:53
  • @Steeve, I am new in opengl. how should I do that via the code? – ar2015 Jan 03 '17 at 12:54
  • Here is a function that can flip an SDL image: http://lazyfoo.net/SDL_tutorials/lesson31/index.php – Steeve Jan 03 '17 at 12:57
  • 1
    When you say *"the saved image is [...]"*, how did you verify this? What tool have you used to display it? Does this tool honor, whether the bitmap is [top-down or bottom-up](https://msdn.microsoft.com/en-us/library/windows/desktop/dd407212.aspx)? By default, Windows GDI bitmaps are bottom-up. – IInspectable Jan 03 '17 at 13:09
  • @IInspectable, I used GNOME image viewer 3.18.2 (Ubuntu 16.04) – ar2015 Jan 03 '17 at 13:12
  • @Steeve, the code you referred is buggy unfortunately. It does not transform an image properly. – ar2015 Jan 03 '17 at 13:31
  • 1
    @Steeve, this url does not work with 24-bit colors. – ar2015 Jan 03 '17 at 14:24
  • "I try to flip the pixels": you copy from top to bottom and so halfway you start to overwrite. Loop y only over `flipped->h >> 1` (yes: rounding down--the center line, if there is one, does not need to be flipped), save the pixel at the top, store bottom in top, store saved top in bottom. – Jongware Jan 03 '17 at 14:57
  • @RadLexus, oops. it is my mistake. I had to read from `surface` than `flipped`. I have realized the problem is from `GetPixel24` all read pixels are zero. – ar2015 Jan 03 '17 at 15:23
  • Ah, and I thought you wanted an in-place flip :P Does your code finally work, then? – Jongware Jan 03 '17 at 15:28
  • @RadLexus, No still gives a black output. I am sure the problem is with `GetPixel24` which is written by me (as a beginner in openGL). It only gives zero as a pixel color. – ar2015 Jan 03 '17 at 15:29
  • Hm. Perhaps the bytes-per-pixel is not 3 for your screen? Try `screen->format->BytesPerPixel` instead. See an oldie of mine: http://stackoverflow.com/a/24687147/2564301 – Jongware Jan 03 '17 at 17:10
  • @RadLexus, It didnt fix the problem. – ar2015 Jan 03 '17 at 20:25
  • @Steeve: Actually BMP supports both top-down or bottom-up line ordering. The information which direction to use is stored in the image header's `height` field. A *negative* value indicates bottom-up order. – datenwolf Jan 04 '17 at 17:52
  • @datenwolf Yes, I was aware of that, but how the image is stored is irrevelant - when loaded, the upside-down image should be flipped by the loader, so still, it's origin is top left. – Steeve Jan 04 '17 at 18:03
  • @datenwolf, there is no field called `height`. are you talking about `h`? – ar2015 Jan 05 '17 at 19:15
  • @ar2015: There is a *biHeight* member in the [BITMAPINFOHEADER](https://msdn.microsoft.com/en-us/library/windows/desktop/dd318229.aspx) structure. – IInspectable Jan 05 '17 at 19:17
  • @IInspectable, thanks for quick response. You are linking to MS. What field is it in `SDL`? compiler does not recognize `surface->height`. – ar2015 Jan 05 '17 at 19:20
  • @ar2015: I linked to Microsoft's documentation, because the BMP file format is owned by Microsoft. [SDL_Surface](https://wiki.libsdl.org/SDL_Surface) stores the height in a member called *h*. I don't think it has the same semantics as `BITMAPINFOHEADER`'s *biHeight* member, though. You'd have to look into SDL's bitmap loader to find out. – IInspectable Jan 05 '17 at 20:05

1 Answers1

5

After a lot of struggles, I have found the solutions and I share it for programmers who will have the same problem in future.

The solution is here.

#include "SDL/SDL.h"
#include <iostream>
#include <GL/freeglut.h>
#include <SDL2/SDL.h>

#define SDL_LOCKIFMUST(s) (SDL_MUSTLOCK(s) ? SDL_LockSurface(s) : 0)
#define SDL_UNLOCKIFMUST(s) { if(SDL_MUSTLOCK(s)) SDL_UnlockSurface(s); }

int invert_surface_vertical(SDL_Surface *surface)
{
    Uint8 *t;
    register Uint8 *a, *b;
    Uint8 *last;
    register Uint16 pitch;

    if( SDL_LOCKIFMUST(surface) < 0 )
        return -2;

    /* do nothing unless at least two lines */
    if(surface->h < 2) {
        SDL_UNLOCKIFMUST(surface);
        return 0;
    }

    /* get a place to store a line */
    pitch = surface->pitch;
    t = (Uint8*)malloc(pitch);

    if(t == NULL) {
        SDL_UNLOCKIFMUST(surface);
        return -2;
    }

    /* get first line; it's about to be trampled */
    memcpy(t,surface->pixels,pitch);

    /* now, shuffle the rest so it's almost correct */
    a = (Uint8*)surface->pixels;
    last = a + pitch * (surface->h - 1);
    b = last;

    while(a < b) {
        memcpy(a,b,pitch);
        a += pitch;
        memcpy(b,a,pitch);
        b -= pitch;
    }

    /* in this shuffled state, the bottom slice is too far down */
    memmove( b, b+pitch, last-b );

    /* now we can put back that first row--in the last place */
    memcpy(last,t,pitch);

    /* everything is in the right place; close up. */
    free(t);
    SDL_UNLOCKIFMUST(surface);

    return 0;
}

void screen_shot(std::string filename)
{
    int width = glutGet(GLUT_WINDOW_WIDTH);
    int height = glutGet(GLUT_WINDOW_HEIGHT);

    SDL_Surface * image = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 24, 0x000000FF, 0x0000FF00, 0x00FF0000, 0);

    glReadBuffer(GL_FRONT);
    glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, image->pixels);

    invert_surface_vertical(image);
    SDL_SaveBMP(image, filename.c_str());
    SDL_FreeSurface(image);
}

void cback_render()
{
    if(!glutGetWindow())
        return ;
    static float rotations = 0;

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glClearColor(1.0f, 0.0f, 0.0f, 1.0f);

    glLoadIdentity();
    glRotatef(rotations, 0, 0, 1);

    glBegin(GL_TRIANGLES);
        glVertex3f(0,0,0);
        glVertex3f(1,0,0);
        glVertex3f(0,1,0);
    glEnd();

    glutSwapBuffers();
}

void timer(int )
{
    if(!glutGetWindow())
        return ;
    static bool saved=false;
    if(!saved)
    {
        screen_shot("image.bmp");
        saved=true;
    }
    glutPostRedisplay();
    glutMainLoopEvent();
    glutTimerFunc(30, timer, 1);
}

void init()
{
    int argc=1;
    glutInit(&argc, NULL);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH);
    glutInitWindowPosition(100, 100);
    glutInitWindowSize(512, 512);
    glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_GLUTMAINLOOP_RETURNS);

    glutCreateWindow("freegluttest");
    glutDisplayFunc (cback_render);
    glutTimerFunc(30, timer, 1);
}

int main()
{
    init();
    glutMainLoop();

    return 0;
}

build:

g++ main.cpp -o main -lGL -lglut -lSDL -lSDL2
ar2015
  • 5,558
  • 8
  • 53
  • 110