+struct png_tmp_buffer
+{
+ char *buffer;
+ size_t size;
+};
+
+static void
+pngWriteCallback(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+ struct png_tmp_buffer *buf = (struct png_tmp_buffer*) png_ptr->io_ptr;
+ size_t nsize = buf->size + length;
+
+ /* allocate or grow buffer */
+ if (buf->buffer)
+ buf->buffer = (char*)realloc(buf->buffer, nsize);
+ else
+ buf->buffer = (char*)malloc(nsize);
+
+ if (!buf->buffer)
+ png_error(png_ptr, "Buffer allocation error");
+
+ memcpy(buf->buffer + buf->size, data, length);
+ buf->size += length;
+}
+
+bool writePixelsToBuffer(unsigned char *pixels,
+ unsigned width, unsigned height, unsigned numChannels,
+ bool flipped,
+ char **buffer,
+ int *size)
+{
+ struct png_tmp_buffer png_mem;
+ png_structp png_ptr;
+ png_infop info_ptr;
+ int type;
+
+ png_mem.buffer = NULL;
+ png_mem.size = 0;
+
+ switch (numChannels) {
+ case 4:
+ type = PNG_COLOR_TYPE_RGB_ALPHA;
+ break;
+ case 3:
+ type = PNG_COLOR_TYPE_RGB;
+ break;
+ case 2:
+ type = PNG_COLOR_TYPE_GRAY_ALPHA;
+ break;
+ case 1:
+ type = PNG_COLOR_TYPE_GRAY;
+ break;
+ default:
+ goto no_png;
+ }
+
+ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!png_ptr)
+ goto no_png;
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr) {
+ png_destroy_write_struct(&png_ptr, NULL);
+ goto no_png;
+ }
+
+ if (setjmp(png_jmpbuf(png_ptr))) {
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+ goto no_png;
+ }
+
+ png_set_write_fn(png_ptr, &png_mem, pngWriteCallback, NULL);
+
+ png_set_IHDR(png_ptr, info_ptr, width, height, 8,
+ type, PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+
+ png_set_compression_level(png_ptr, Z_DEFAULT_COMPRESSION);
+
+ png_write_info(png_ptr, info_ptr);
+
+ if (!flipped) {
+ for (unsigned y = 0; y < height; ++y) {
+ png_bytep row = (png_bytep)(pixels + y*width*numChannels);
+ png_write_rows(png_ptr, &row, 1);
+ }
+ } else {
+ unsigned y = height;
+ while (y--) {
+ png_bytep row = (png_bytep)(pixels + y*width*numChannels);
+ png_write_rows(png_ptr, &row, 1);
+ }
+ }
+
+ png_write_end(png_ptr, info_ptr);
+ png_destroy_write_struct(&png_ptr, &info_ptr);