Как закодировать PNG для буферизации с помощью libpng?

В настоящее время я использую следующее для записи PNG в файл:

#include <png.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

/* Pixels in this bitmap structure are stored as BGR. */
typedef struct _RGBPixel {
    uint8_t blue;
    uint8_t green;
    uint8_t red;
} RGBPixel;

/* Structure for containing decompressed bitmaps. */
typedef struct _RGBBitmap {
    RGBPixel *pixels;
    size_t width;
    size_t height;
    size_t bytewidth;
    uint8_t bytes_per_pixel;
} RGBBitmap;

/* Returns pixel of bitmap at given point. */
#define RGBPixelAtPoint(image, x, y) \
    *(((image)->pixels) + (((image)->bytewidth * (y)) \
                        + ((x) * (image)->bytes_per_pixel)))

/* Attempts to save PNG to file; returns 0 on success, non-zero on error. */
int save_png_to_file(RGBBitmap *bitmap, const char *path)
{
    FILE *fp = fopen(path, "wb");
    png_structp png_ptr = NULL;
    png_infop info_ptr = NULL;
    size_t x, y;
    png_uint_32 bytes_per_row;
    png_byte **row_pointers = NULL;

    if (fp == NULL) return -1;

    /* Initialize the write struct. */
    png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    if (png_ptr == NULL) {
        fclose(fp);
        return -1;
    }

    /* Initialize the info struct. */
    info_ptr = png_create_info_struct(png_ptr);
    if (info_ptr == NULL) {
        png_destroy_write_struct(&png_ptr, NULL);
        fclose(fp);
        return -1;
    }

    /* Set up error handling. */
    if (setjmp(png_jmpbuf(png_ptr))) {
        png_destroy_write_struct(&png_ptr, &info_ptr);
        fclose(fp);
        return -1;
    }

    /* Set image attributes. */
    png_set_IHDR(png_ptr,
                 info_ptr,
                 bitmap->width,
                 bitmap->height,
                 8,
                 PNG_COLOR_TYPE_RGB,
                 PNG_INTERLACE_NONE,
                 PNG_COMPRESSION_TYPE_DEFAULT,
                 PNG_FILTER_TYPE_DEFAULT);

    /* Initialize rows of PNG. */
    bytes_per_row = bitmap->width * bitmap->bytes_per_pixel;
    row_pointers = png_malloc(png_ptr, bitmap->height * sizeof(png_byte *));
    for (y = 0; y < bitmap->height; ++y) {
        uint8_t *row = png_malloc(png_ptr, sizeof(uint8_t) * bitmap->bytes_per_pixel);
        row_pointers[y] = (png_byte *)row;
        for (x = 0; x < bitmap->width; ++x) {
            RGBPixel color = RGBPixelAtPoint(bitmap, x, y);
            *row++ = color.red;
            *row++ = color.green;
            *row++ = color.blue;
        }
    }

    /* Actually write the image data. */
    png_init_io(png_ptr, fp);
    png_set_rows(png_ptr, info_ptr, row_pointers);
    png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);

    /* Cleanup. */
    for (y = 0; y < bitmap->height; y++) {
        png_free(png_ptr, row_pointers[y]);
    }
    png_free(png_ptr, row_pointers);

    /* Finish writing. */
    png_destroy_write_struct(&png_ptr, &info_ptr);
    fclose(fp);
    return 0;
}

Как я могу написать аналогичную функцию (на C) для кодирования PNG в буфер в памяти?

Прототип будет выглядеть примерно так:

uint8_t *encode_png_to_buffer(RGBBitmap *source);

И кажется, что мне, вероятно, нужно будет как-то использовать png_set_write_fn().

Но кроме этого я не уверен, как подойти к этому. Есть ли примеры того, как это делается? Наверняка я не первый кому нужен этот функционал.


person Sam    schedule 30.11.2009    source источник
comment
Во-первых, попробуйте его скомпилировать. Проверьте определение save_png_to_file на отсутствие *, т.е. RGBBitmap * bitmap (поскольку позже вы обрабатываете растровое изображение как указатель. И где определяется bytes_per_pixel? (нигде!)   -  person James Morris    schedule 01.12.2009
comment
Что именно вы подразумеваете под кодированием PNG в буфер в памяти? Вы имеете в виду кодировать изображение в PNG в буфер? Вы имеете в виду декодировать PNG в буфер?   -  person James Morris    schedule 01.12.2009
comment
Ах, извините за опечатки, я их исправил. Извините, если я не был ясен; я имею в виду следующее: у меня есть распакованное растровое изображение в структуре RGBBitmap, которое я хочу преобразовать в PNG. Я не хочу записывать этот PNG в файл, мне просто нужен необработанный буфер данных, которые будут записаны.   -  person Sam    schedule 01.12.2009
comment
Думаю, row_pointers = png_malloc(png_ptr, bitmap-›height * sizeof(png_byte )); должно быть png_byte *row = (png_byte)png_malloc(png_ptr, sizeof(uint8_t) * bitmap-›bytes_per_pixel * bitmap-›width);   -  person ckg    schedule 03.04.2012
comment
пожалуйста, как вы установили libpng, потому что я не мог использовать #include <png.h> в Windows.   -  person Lucie kulza    schedule 28.03.2014


Ответы (4)


Да, используя png_set_write_fn что-то вроде этого - непроверенный:

Обновлено с изменениями из комментария

/* structure to store PNG image bytes */
struct mem_encode
{
  char *buffer;
  size_t size;
}


void
my_png_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
{
  /* with libpng15 next line causes pointer deference error; use libpng12 */
  struct mem_encode* p=(struct mem_encode*)png_get_io_ptr(png_ptr); /* was png_ptr->io_ptr */
  size_t nsize = p->size + length;

  /* allocate or grow buffer */
  if(p->buffer)
    p->buffer = realloc(p->buffer, nsize);
  else
    p->buffer = malloc(nsize);

  if(!p->buffer)
    png_error(png_ptr, "Write Error");

  /* copy new bytes to end of buffer */
  memcpy(p->buffer + p->size, data, length);
  p->size += length;
}

/* This is optional but included to show how png_set_write_fn() is called */
void
my_png_flush(png_structp png_ptr)
{
}



int save_png_to_file(RGBBitmap *bitmap, const char *path)
{
...
/* static */
struct mem_encode state;

/* initialise - put this before png_write_png() call */
state.buffer = NULL;
state.size = 0;

/* if my_png_flush() is not needed, change the arg to NULL */
png_set_write_fn(png_ptr, &state, my_png_write_data, my_png_flush);

... call png_write_png() ...

/* now state.buffer contains the PNG image of size s.size bytes */

/* cleanup */
if(state.buffer)
  free(state.buffer);
person dajobe    schedule 01.12.2009
comment
Спасибо, это то, что мне было нужно. Однако есть опечатка: &(p-›buffer + p-›size) должно быть просто p-›buffer + p-›size. Кроме того, я не думаю, что есть необходимость в пустой функции my_png_flush(); вы можете просто передать NULL в последний аргумент png_set_write_fn(). - person Sam; 01.12.2009
comment
Что касается комментария о том, что разыменование io_ptr не работает, правильный метод с более новыми версиями libpng — это вызвать png_get_io_ptr(png_ptr) — см. refspecs.linuxbase.org/LSB_3.1.0/LSB-Desktop-generic/ - person HulkHolden; 24.03.2013
comment
Определение пустой функции my_png_flush не является обязательным — если вы вместо этого используете NULL, libpng попытается использовать flush() в вашей структуре состояния. - person Noam; 24.11.2014
comment
Подскажите, пожалуйста, как правильно заменить png_get_io_ptr() в libpng16? - person Eddy_Em; 04.06.2015
comment
Этот код может привести к утечке памяти, также realloc() с указателем NULL по существу совпадает с malloc(). Так что if бессмысленно. - person Iharob Al Asimi; 20.02.2017
comment
Привет, я новичок в теме png, и я наткнулся на одну вещь, используя это решение. Мы направили данные в буфер вместо файла изображения, но там я увидел разницу в содержании, например, данные буфера начинаются с .IDAT, а данные в файле png начинаются с .png. Есть что-то, что мне не хватает? а если нет, то есть ли способ получить заголовок png? @dajobe - person Anon; 28.01.2021

Другие ответы, на мой взгляд, не кажутся полными. Итак, я использовал эти ответы и другие исследования, чтобы записать черный фон в буфер. Затем, чтобы проверить, я записал буфер в файл. Это было скомпилировано с помощью gcc. Добавлен флаг библиотеки -lpng.

#define PNG_SETJMP_NOT_SUPPORTED
#include <stdio.h>
#include <stdlib.h>
#include <png.h>

struct libpng_inmem_write_struct { /* This is from png.c */
  unsigned char * pngBfr;  /* destination memory */
  unsigned long pngSiz;  /* destination memory size (bytes) */
};

void freeExit_w_msg(char * msg);
void wrtBgPng(png_structp pngWrtPtr, png_bytep data, png_size_t length);

png_structp pngWrtPtr; /* The pointer that points the PNG write structure */
png_infop pngWrtInfoPtr; /* The pointer that points the PNG write information */ 
struct libpng_inmem_write_struct p_io; /* Holds the encoded PNG data */
FILE * fw; /* The file pointer of the test file that will be wrote. */

void freeExit_w_msg(char * msg) {
    if (pngWrtPtr) png_destroy_write_struct(&pngWrtPtr, &pngWrtInfoPtr);
    if (p_io.pngBfr) free(p_io.pngBfr);
    fclose(fw); 
    printf("%s\n", msg);
    exit(0);
}

int main(int argc, char *argv[])
{
    pngWrtInfoPtr = NULL;   /* write_info_ptr */
    p_io.pngBfr = NULL;
    p_io.pngSiz = 0;
    int imgWdth = 2558;
    int imgHght = 1438;
    fw = fopen (argv[1], "wb"); /* argv[1] is the name of the test file */

    if (!fw) {
        char msg[300];
        sprintf(msg, "The file, %s, did not correctly open.\n", argv[1]);
        freeExit_w_msg(msg);
    }

    pngWrtPtr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); /* write_ptr */
    if (!pngWrtPtr) freeExit_w_msg((char *) "The PNG write memory did not correctly allocate.");
    pngWrtInfoPtr = png_create_info_struct(pngWrtPtr);
    if (!pngWrtInfoPtr) freeExit_w_msg((char *) "The PNG write information memory did not correctly allocate.");
    png_set_IHDR(pngWrtPtr, pngWrtInfoPtr, imgWdth, imgHght, 8, PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);    
    png_byte ** row_pointers = (png_byte **) png_malloc(pngWrtPtr, imgHght * sizeof(png_byte *));
    size_t bytesPerRow = imgWdth << 2; /* 4 Bytes per pixel */
    unsigned char * imgBfr = (unsigned char *) calloc(1, imgHght * bytesPerRow * sizeof(unsigned char)); 

    for (int rw = 0; rw < imgHght; rw++) {
        png_byte * rwPtr = row_pointers[rw] = (png_byte *) (imgBfr + rw * bytesPerRow); 

        for (int pxl = 0, byt = 0; pxl < imgWdth; pxl++) { /* Write a black background */
            for (int clr = 0; clr < 3; clr++) rwPtr[byt++] = 0;
            rwPtr[byt++] = 0xff;
        }
    }   

    p_io.pngBfr = (unsigned char *) malloc(4); /* Defines final PNG data location */
    p_io.pngSiz = 4;
    png_init_io(pngWrtPtr, (png_FILE_p) &p_io);
    png_set_rows(pngWrtPtr, pngWrtInfoPtr, &row_pointers[0]);
    png_set_write_fn(pngWrtPtr, &p_io, wrtBgPng, NULL);
    png_write_png(pngWrtPtr, pngWrtInfoPtr, PNG_TRANSFORM_IDENTITY, NULL);
    fwrite(p_io.pngBfr + 4, 1, p_io.pngSiz, fw); /* Test file */
    freeExit_w_msg((char *) "The exit was normal.");
}

void wrtBgPng(png_structp pngWrtPtr, png_bytep data, png_size_t length) {
    struct libpng_inmem_write_struct * p = (struct libpng_inmem_write_struct *) png_get_io_ptr(pngWrtPtr);
    p->pngBfr = (unsigned char *) realloc(p->pngBfr, p->pngSiz + length); /* From png.c */
    if (!p->pngBfr) freeExit_w_msg((char *) "The PNG write memory did not correctly allocate.");
    memmove(p->pngBfr + p->pngSiz, data, length);
    p->pngSiz += length;
}
person VectorVortec    schedule 02.03.2019

person    schedule
comment
Похоже, это взято из здесь. - person imallett; 11.01.2016
comment
Для libpng это size_t, а не png_size_t. См. определение png_rw_ptr. - person Константин Ван; 21.12.2020

person    schedule
comment
Я утверждаю, что важнее задуматься о том, чем код отличается от OP, а не о том, запускать код или нет. - person grochmal; 13.07.2016