OpenGL snippets

From vegard.wiki
Jump to navigation Jump to search

Saving the framebuffer to file

Python/PIL

from PIL import Image

pixels = glReadPixels(0, 0, window_width, window_height, GL_RGB, GL_UNSIGNED_BYTE)
image = Image.frombytes(mode="RGB", size=(window_width, window_height), data=pixels)
image = image.transpose(Image.FLIP_TOP_BOTTOM)
image.save('output.png')

Note: This should work for any file type/extension that PIL supports.

May or may not work for widths which are not multiples of 4 or 8. YMMV.

C++/libpng

#include <libpng/png.h>

// http://www.labbookpages.co.uk/software/imgProc/libPNG.html
static void capture()
{
        int width, height;
        glfwGetFramebufferSize(window, &width, &height);

        // round up width to a multiple of 8 avoid alignment problems
        unsigned int buffer_width = (width + 7) & ~7;
        unsigned int buffer_size = buffer_width * height * 3;
        auto buffer = std::make_unique<char []>(buffer_size);

        glReadnPixels(0, 0, buffer_width, height,
                GL_RGB, GL_UNSIGNED_BYTE, buffer_size, (void *) buffer.get());

        FILE *fp = fopen("output.png", "wb");
        if (!fp)
                error(EXIT_FAILURE, errno, "fopen()");

        auto png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
        if (!png_ptr)
                error(EXIT_FAILURE, 0, "png_create_write_struct()");

        auto info_ptr = png_create_info_struct(png_ptr);
        if (!info_ptr)
                error(EXIT_FAILURE, 0, "png_create_info_struct()");

        png_init_io(png_ptr, fp);
        png_set_IHDR(png_ptr, info_ptr, width, height,
                8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
                PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
        png_write_info(png_ptr, info_ptr);

        for (unsigned int y = 0; y < height; ++y)
                png_write_row(png_ptr, (png_bytep) &buffer[3 * buffer_width * (height - y - 1)]);

        png_write_end(png_ptr, info_ptr);
        fclose(fp);

        png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);
        png_destroy_write_struct(&png_ptr, (png_infopp) NULL);
}

This uses GLFW to obtain the frambuffer size, but should be easy to adapt to other windowing APIs.

The error handling leaves something to be desired for now.

See also