如何使用libpng将PNG编码为缓冲区?

我目前正在使用以下内容将PNG写入文件:

#include  #include  #include  #include  /* 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 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 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 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()

但除此之外,我不知道如何处理这个问题。 有没有这样做的例子? 当然,我不是第一个需要这个function的人。

是的,使用像这样的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); 
 #include  #include  #include  #include  //encode and write PNG to memory (std::vector) with libpng on C++ typedef unsigned char ui8; #define ASSERT_EX(cond, error_message) do { if (!(cond)) { std::cerr << error_message; exit(1);} } while(0) static void PngWriteCallback(png_structp png_ptr, png_bytep data, png_size_t length) { std::vector *p = (std::vector*)png_get_io_ptr(png_ptr); p->insert(p->end(), data, data + length); } struct TPngDestructor { png_struct *p; TPngDestructor(png_struct *p) : p(p) {} ~TPngDestructor() { if (p) { png_destroy_write_struct(&p, NULL); } } }; void WritePngToMemory(size_t w, size_t h, const ui8 *dataRGBA, std::vector *out) { out->clear(); png_structp p = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); ASSERT_EX(p, "png_create_write_struct() failed"); TPngDestructor destroyPng(p); png_infop info_ptr = png_create_info_struct(p); ASSERT_EX(info_ptr, "png_create_info_struct() failed"); ASSERT_EX(0 == setjmp(png_jmpbuf(p)), "setjmp(png_jmpbuf(p) failed"); png_set_IHDR(p, info_ptr, w, h, 8, PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); //png_set_compression_level(p, 1); std::vector rows(h); for (size_t y = 0; y < h; ++y) rows[y] = (ui8*)dataRGBA + y * w * 4; png_set_rows(p, info_ptr, &rows[0]); png_set_write_fn(p, out, PngWriteCallback, NULL); png_write_png(p, info_ptr, PNG_TRANSFORM_IDENTITY, NULL); } 

我找到了此代码的早期版本,并将其入侵以生成并保存16位灰度PNG文件。 它运行并生成此PNG: 在400x400 png范围内从0到64k附近采样灰度

以下是构建可执行文件的完整源代码和GCC命令。 它运行在我的系统上,OpenSuse 42/64与GCC – > gcc版本4.8.5(SUSE Linux)和libpng 1.6.23,但如果任何人如此不谨慎或无礼地实际尝试运行它,可能会融化你的磁通电容。 ;)

 #include  #include  #include  #include  #include  #include "string.h" /* 8 QBit RGB/24 to 16 QBit Grayscale hack * based on code found at * http://www.lemoda.net/c/write-png/ and png.h libpng version 1.6.23 */ /* gcc -L/usr/local/static -I/usr/local/static/include -lpng16 /home/photog/bin/png.test.gray16.c -lm -o /home/photog/bin/png.tg16 */ // ============================================================================= typedef struct { uint8_t red; uint8_t green; uint8_t blue; // A colored pixel } pixel_t; typedef struct { uint16_t gray; // A GRAY pixel } pixel_gray_16_t; typedef struct { // A picture pixel_gray_16_t *pixels; size_t width; size_t height; } bitmap_t; // ============================================================================= // Write "bitmap" to a PNG file specified by "path"; returns 0 on // success, non-zero on error static int save_png_to_file (bitmap_t *bitmap, const char *path) { FILE * fp; png_structp png_ptr = NULL; png_infop info_ptr = NULL; size_t x, y; int pidx=0; // Pixel_Index png_byte **row_pointers = NULL; // KLUDGE!! /* "status" contains the return value of this function. At first it is set to a value which means 'failure'. When the routine has finished its work, it is set to a value which means 'success'. */ int status = -1; /* The following number is set by trial and error only. I cannot see where it it is documented in the libpng manual */ int pixel_size = 2; // 3 for RGB/24; int depth = 16; // 8 for RGB/24; fp = fopen (path, "wb"); if (! fp) { goto fopen_failed; } png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (png_ptr == NULL) { goto png_create_write_struct_failed; } info_ptr = png_create_info_struct (png_ptr); if (info_ptr == NULL) { goto png_create_info_struct_failed; } /* Set up error handling. */ if (setjmp (png_jmpbuf (png_ptr))) { goto png_failure; } // Set image attributes; # de fine PNG_COLOR_TYPE_GRAY 0 png_set_IHDR (png_ptr, info_ptr, bitmap->width, bitmap->height, depth, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); /* Initialize rows of PNG. */ row_pointers=png_malloc(png_ptr, bitmap->height * sizeof(png_uint_16 *)); // Copy system Callocated user data to PNG owned space for (y=0, pidx=0; y < bitmap->height; ++y) { png_byte *row = png_malloc(png_ptr, sizeof(uint8_t) * bitmap->width * pixel_size); row_pointers[y] = row; memcpy((void *)row, bitmap->pixels+pidx, bitmap->width * 2); pidx += bitmap->width; // Move to next row } /* Write the image data to "fp". */ png_init_io (png_ptr, fp); png_set_rows (png_ptr, info_ptr, row_pointers); png_write_png(png_ptr,info_ptr, PNG_TRANSFORM_SWAP_ENDIAN,NULL); //png_write_png (png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL); // The routine has successfully written the file, so we set "status" to a // value which indicates success status = 0; for(y=0; y < bitmap->height; y++) png_free (png_ptr, row_pointers[y]); png_free (png_ptr, row_pointers); png_failure: png_create_info_struct_failed: png_destroy_write_struct (&png_ptr, &info_ptr); png_create_write_struct_failed: fclose (fp); fopen_failed: return status; } // ============================================================================= // ============================================================================= int main () { bitmap_t fruit; const char ofn[]={ "fruit.g16.png" }; // Output FileName int x, y, pidx=0; uint16_t gray_u16; float graysf; // Gray Scale factor. 0->0, last_pix -> QMax(16) fruit.width = 400; // Size the image fruit.height = 400; graysf=(65535.0f/fruit.width)/fruit.height; // Last pix => 65535 fruit.pixels=calloc(sizeof(pixel_gray_16_t), fruit.width * fruit.height); // Create linear black -> white gradient for(y=0; y < fruit.height; y++) { for(x=0; x < fruit.width; x++) { gray_u16=(uint16_t)lrintf((y*fruit.width+x)*graysf); fruit.pixels[pidx++].gray = gray_u16; } } // Write the image to a file save_png_to_file (&fruit, ofn); printf("Wrote gray/16 PNG file %s\n", ofn); return 0; } this line may not compile // ============================================================================= 

groch,我发现上面的,剥离代码有助于获得16个QBit PNG代码。 这个未经测试且具有潜在危险性的黑客被添加用于参考目的,仅有关于运行它的固有风险的免责声明。 编译器错误行已添加,因此有人必须编辑它以进行编译,使其成为自己的自定义版本。 该post可能已经为已经很好的post添加了大量有用的信息。 纯粹的哲学思考增加了噪音,但没有光......也许你可以在代码中添加一个function?

注意:第一次运行在Intel Skylake CPU上得到了一个奇怪的结果:

你已经被Endianed了!

错了Endian! PNG以健全的网络字节顺序保存。这是字节扰乱的dis-order真正的样子!

不得不改变这一行:

 png_write_png(png_ptr,info_ptr, PNG_TRANSFORM_SWAP_ENDIAN,NULL);