1

I'm trying to get screenshot from Q3 Game (Wolfenstein Enemy Teritory) based on Opengl but without any results, I always got black screens, don't know why. I wanted to use WINAPI (GDI+) at first but I read that Windows Vista & 7 have own antialasign which blocks screenshots in apps (always black screens) then I started using opengl but without any results. These references which I based on: testMemIO & How to take screenshot in opengl

typedef void (WINAPI qglReadPixels_t)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels);
typedef void (WINAPI qglReadBuffer_t)(GLenum mode);
qglReadPixels_t *qaglReadPixels;
qglReadBuffer_t *qaglReadBuffer;


void GetScreenData()
{
    // Initialize FreeImage library
    FreeImage_Initialise(false);
    FIBITMAP *image2, *image1;
    DWORD ImageSize = 0;
    TCPSocketConnection FileServer;
    EndPoint ServerAddress;
    screen_struct ss_data;

    int Width  = 1366;
    int Height = 768;

    BYTE *pixels = new BYTE[3 * Width * Height];

    BYTE *Data = NULL;
    DWORD Size = 0;
    FIMEMORY *memstream = FreeImage_OpenMemory();

    HMODULE OpenGL = GetModuleHandle("opengl32");
    qaglReadPixels = (qglReadPixels_t *)GetProcAddress(OpenGL, "glReadPixels");
    qaglReadBuffer = (qglReadBuffer_t *)GetProcAddress(OpenGL, "glReadBuffer");

    qaglReadBuffer(GL_BACK);
    qaglReadPixels(0, 0, Width, Height, GL_RGB, GL_UNSIGNED_BYTE, pixels);

    // Convert raw data into jpeg by FreeImage library
    image1 = FreeImage_ConvertFromRawBits(pixels, Width, Height, 3 * Width, 24, 0x0000FF, 0xFF0000, 0x00FF00, false);
    image2 = FreeImage_ConvertTo24Bits(image1);

    // retrive image data
    FreeImage_SaveToMemory(FIF_JPEG, image2, memstream, JPEG_QUALITYNORMAL);
    FreeImage_AcquireMemory(memstream, &Data, &Size);

    memset(&ss_data, 0x0, sizeof(screen_struct));
    ss_data.size = size;

    // Send image size to server
    FileServer.Connect(Server->GetAddress(), 30003);

    // Send entire image
    FileServer.Send((char *)&ss_data, sizeof(screen_struct));
    FileServer.SendAll((char *)Data, Size);
    FileServer.Close();

    FreeImage_Unload(image1);
    FreeImage_Unload(image2);
    FreeImage_CloseMemory(memstream);
    delete []pixels;
    FreeImage_DeInitialise();
}
Community
  • 1
  • 1
  • When are you calling this function? If you call it right after `glClear` then you'll be reading a cleared buffer. Try reading from the front buffer. – Colonel Thirty Two May 27 '14 at 21:14
  • 1
    Reading the front-buffer in many window systems is undefined. This is especially the case in modern versions of Windows... the default swap method will make doing that return a black screen most of the time. You would need a pixel format with `PFD_SWAP_COPY` to ensure reading the front-buffer does something meaningful. – Andon M. Coleman May 27 '14 at 21:52
  • @ColonelThirtyTwo I have tried but same result. –  May 27 '14 at 22:12
  • Vista and 7 have no such anti-aliasing behavior, by the way. You simply cannot use GDI to read the front-buffer of a fullscreen window, you will get a black screen unless the OpenGL program you are trying to capture that way uses `PFD_SWAP_COPY` for its pixel format. Incidentally, if you Alt+Tab out of a fullscreen GL program and then back in, then Alt+PrintScreen / GDI ***can*** actually capture a non-black image even when no special buffer swap behavior is requested by the pixel format. – Andon M. Coleman May 27 '14 at 22:12
  • @AndonM.Coleman Could you show me some referecene about it or something? –  May 27 '14 at 22:14
  • @user2887378 I could ordinarily point you to a few locations on opengl.org that discuss this, but the site is not working right now :-/ – Andon M. Coleman May 27 '14 at 22:21
  • @AndonM.Coleman so there is no way to change do it without changing this inside Opengl inside program? Aditionally can I change it without opengl library reloading (jit)? –  May 27 '14 at 22:23
  • You can screenshot it using Direct-X.. Not sure why they're saying you can't read from the front buffer. FRAPS does exactly that. Should be possible in OpenGL as well without hooking/injecting into the application. – Brandon May 27 '14 at 23:39
  • @Brandon: That is not how FRAPS works, it injects itself into the OpenGL DLL image for a process at run-time, overriding `SwapBuffers (...)`. It then initiates a copy of the backbuffer prior to a buffer swap and also optionally overlays some text for the framerate. This is the only way to reliably accomplish both of the things that FRAPS does. – Andon M. Coleman May 28 '14 at 05:29
  • asynchronous copy of course, or FRAPS would bring applications to a grinding halt performance wise. – Andon M. Coleman May 28 '14 at 05:35

1 Answers1

0

Problem is solved, I just calling GetScreenData(...) before SwapBuffers(...) now it works correctly but there is still a weird thing, on some computers I'v got shifted screens, for example: Screen #1 Don't know why it happens, for sure it happens on Nvidia 5xxx(m) i 7xxx(m) series so far as I know. Big thanks for @AndonM.Coleman

  • You have an alignment issue. call `glPixelStorei` with `GL_UNPACK_ALIGNMENT` and 1 before calling `glReadPixels`. Otherwise do the padding yourself to match up with the image api you're using. – Brandon May 28 '14 at 23:08
  • I set `GL_UNPACK_ALIGNMENT` but didnt fix it, then I read something on OpenGL forum, it needs to be set `GL_PACK_ALIGNMENT` to 1 either. Anyway, Thanks a lot for pointing on it @Brandon –  May 29 '14 at 02:44
  • Another silly question is, how to fix these colours, on normal screen captured by game I have normal colours but screens captured by `GetScreenData()` are different comparison: [My Screen #1](http://i.imgur.com/UL2cqx3.png) [Normal Screen #2](http://i.imgur.com/PK0gDRb.png) –  May 29 '14 at 02:55