OpenGL snippets: Difference between revisions

From vegard.wiki
Jump to navigation Jump to search
Content added Content deleted
(C++/libpng)
Line 3: Line 3:
== Saving the framebuffer to file ==
== Saving the framebuffer to file ==


=== Python ===
=== Python/PIL ===


<source lang="Python">
<source lang="Python">
Line 15: Line 15:


Note: This should work for any file type/extension that PIL supports.
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 ===

<source lang="C++">
#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);

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);
}
</source>

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.


[[Category:Graphics programming]]
[[Category:Graphics programming]]

Revision as of 21:51, 30 December 2019

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);

        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.