Comment encoder PNG en tampon en utilisant libpng?
J'utilise actuellement ce qui suit pour écrire un PNG dans un fichier:
#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;
}
Comment puis-je écrire une fonction similaire (en C)pour encoder un PNG dans un tampon en mémoire?
Le prototype ressemblerait à ceci:
uint8_t *encode_png_to_buffer(RGBBitmap *source);
Et il semble que je devrais probablement utiliser png_set_write_fn()
.
Mais en dehors de cela, je ne suis pas sûr de savoir comment aborder cela. Y a-t-il des exemples de ce qui est fait? Certes, je ne suis pas le premier à avoir besoin de cette fonctionnalité.
3 réponses
Oui, en utilisant png_set_write_fn
quelque chose comme ceci-non testé:
Mise à jour avec les modifications du Commentaire
/* 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 <png.h>
#include <vector>
#include <iostream>
#include <stdlib.h>
//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<ui8> *p = (std::vector<ui8>*)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<ui8> *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<ui8*> 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);
}
J'ai trouvé une version antérieure de ce code et l'ai piraté pour générer et enregistrer un fichier PNG en niveaux de gris 16 bits. Il exécute et génère ce PNG:
Voici la source complète et la commande GCC pour construire l'exécutable. Il fonctionne sur mon système, OpenSuse 42/64 avec GCC - > gcc version 4.8.5 (SUSE Linux) et libpng 1.6.23, mais peut faire fondre votre condensateur de flux si quelqu'un est si imprudent ou impudent pour essayer de l'exécuter. ;)
#include <png.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#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, j'ai trouvé ce qui précède, dépouillé vers le bas du code utile pour obtenir 16 Qbit PNG code de travail. Ce hack non testé et potentiellement dangereux a été ajouté à des fins de référence uniquement avec un avertissement sur les risques inhérents à son exécution. La ligne d'erreur du compilateur a été ajoutée afin que quelqu'un doive l'éditer pour compiler, ce qui en fait sa propre version personnalisée. Le post peut avoir ajouté un iota d'informations utiles à un fil déjà bon. Les interrogations purement philosophiques ajoutent du bruit mais pas de lumière... Peut-être que vous pourriez ajouter une fonctionnalité à la code?
NOTE: la première exécution a obtenu un résultat étrange sur le processeur Intel Skylake:
Vous avez été Endiané!
A dû modifier cette ligne:
png_write_png(png_ptr,info_ptr, PNG_TRANSFORM_SWAP_ENDIAN,NULL);