Сохранение изображения с помощью libpng

от автора


Развлекаясь на досуге с OpenGL, решил научиться делать скриншоты средствами программы, а не системы. Довольно быстро нагуглил функцию glReadPixels, но вот с сохранением картинки вышла проблема. Вспомнил былые времена, когда полностью своим кодом сохранял в bmp, нашел функцию сохранения в tga, понял, что все эти варианты попахивают велосипедизмом и решил использовать широко распространенную библиотеку. Выбор пал на libpng.
Дальше пошли грабли.

Оказалось, что никакого описания библиотеки на русском языке нет (собственно поэтому и пишу сейчас этот пост), а английская документация написана не самым удобным образом и не содержит даже простейшего полноценного примера использования.
Ниже я постараюсь объяснить, как средствами libpng сохранить изображение в простейшем случае.

Прежде всего надо подключить заголовочный файл

#include <png.h> 

В функции/методе, в которой будем сохранять изображение, откроем файл, в который будем сохранять и создадим структуру png.

void Renderer::screenshoot(const std::string& name) {      FILE *fp = fopen(name.c_str(), "wb");     if (!fp) {        return;     }      png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);     if (!png_ptr) {         goto close_file;     } 

Теперь нужно создать структуру информации о png, вызвать setjmp, на случай ошибок и инициализировать вывод в файл.

    png_infop png_info;     if (!(png_info = png_create_info_struct(png_ptr))) {         goto destroy_write;     }      if (setjmp(png_jmpbuf(png_ptr))) {         goto destroy_write;     }      png_init_io(png_ptr, fp); 

Дальше нужно установить параметры изображения (размеры, количество цветов, количество битов на канал цвета, черезстрочность и сжатие. А также сформировать изображение и массив указателей на отдельные строки изображения.

Тут начинаются грабли.

Грабли №1, связанные с libpng: библиотека считает, что строчки идут снизу вверх, хотя обычно работая с графикой мы подразумеваем обратный порядок.
Грабли №2, связанные с OpenGL: если указать функции glReadPixels, что мы хотим получить цвета RGBA, то на самом деле мы получим их в порядке ARGB. Досадно, но на то, чтобы выяснить это, у меня ушел час, в течении которого я не понимал, почему у меня не правильно отображаются цвета на скриншоте.

Объявим массив data, в котором будут данные для libpng, и массив argb_data, в котором будут данные от OpenGL, ну и не забудем про массив указателей rows.

    png_set_IHDR(png_ptr, png_info, width, height, 0, PNG_COLOR_TYPE_RGB,         PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,         PNG_FILTER_TYPE_DEFAULT);      unsigned char data[width*height*3], argb_data[width*height*4];     unsigned char *rows[height];      render();     glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, argb_data);      for (int i = 0; i < height; ++i) {         rows[height - i - 1] = data + (i*width*3);         for (int j = 0; j < width; ++j) {             int i1 = (i*width+j)*3;             int i2 = (i*width+j)*4;             data[i1++] = argb_data[++i2];             data[i1++] = argb_data[++i2];             data[i1++] = argb_data[++i2];         }     } 

Теперь дело за малым — сохранить изображение, завершить ввод и обработать ошибки.

    png_set_rows(png_ptr, png_info, rows);     png_write_png(png_ptr, png_info, PNG_TRANSFORM_IDENTITY, nullptr);     png_write_end(png_ptr, png_info);  destroy_write:     png_destroy_write_struct(&png_str, nullptr); close_file:     fclose(fp); } 

Таким, относительно несложным образом можно сохранить изображение в png. Однако, не думайте, что это все, на что способна эта библиотека. Можно следить за процессом сохранения, применять фильтры, использовать черезстрочность, сжатие… Но чтобы разобраться с этим стоит читать оригинальную документацию.

ссылка на оригинал статьи http://habrahabr.ru/post/176163/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *