in src/libguac/encode-png.c [269:404]
int guac_png_write(guac_socket* socket, guac_stream* stream,
cairo_surface_t* surface) {
png_structp png;
png_infop png_info;
png_byte** png_rows;
int bpp;
int x, y;
guac_png_write_state write_state;
/* Get image surface properties and data */
cairo_format_t format = cairo_image_surface_get_format(surface);
int width = cairo_image_surface_get_width(surface);
int height = cairo_image_surface_get_height(surface);
int stride = cairo_image_surface_get_stride(surface);
unsigned char* data = cairo_image_surface_get_data(surface);
/* If not RGB24, use Cairo PNG writer */
if (format != CAIRO_FORMAT_RGB24 || data == NULL)
return guac_png_cairo_write(socket, stream, surface);
/* Flush pending operations to surface */
cairo_surface_flush(surface);
/* Attempt to build palette */
guac_palette* palette = guac_palette_alloc(surface);
/* If not possible, resort to Cairo PNG writer */
if (palette == NULL)
return guac_png_cairo_write(socket, stream, surface);
/* Calculate BPP from palette size */
if (palette->size <= 2) bpp = 1;
else if (palette->size <= 4) bpp = 2;
else if (palette->size <= 16) bpp = 4;
else bpp = 8;
/* Set up PNG writer */
png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png) {
guac_palette_free(palette);
guac_error = GUAC_STATUS_INTERNAL_ERROR;
guac_error_message = "libpng failed to create write structure";
return -1;
}
png_info = png_create_info_struct(png);
if (!png_info) {
png_destroy_write_struct(&png, NULL);
guac_palette_free(palette);
guac_error = GUAC_STATUS_INTERNAL_ERROR;
guac_error_message = "libpng failed to create info structure";
return -1;
}
/* Set error handler */
if (setjmp(png_jmpbuf(png))) {
png_destroy_write_struct(&png, &png_info);
guac_palette_free(palette);
guac_error = GUAC_STATUS_IO_ERROR;
guac_error_message = "libpng output error";
return -1;
}
/* Init write state */
write_state.socket = socket;
write_state.stream = stream;
write_state.buffer_size = 0;
/* Set up writer */
png_set_write_fn(png, &write_state,
guac_png_write_handler,
guac_png_flush_handler);
/* Copy data from surface into PNG data */
png_rows = (png_byte**) guac_mem_alloc(sizeof(png_byte*), height);
for (y=0; y<height; y++) {
/* Allocate new PNG row */
png_byte* row = (png_byte*) guac_mem_alloc(sizeof(png_byte), width);
png_rows[y] = row;
/* Copy data from surface into current row */
for (x=0; x<width; x++) {
/* Get pixel color */
int color = ((uint32_t*) data)[x] & 0xFFFFFF;
/* Set index in row */
row[x] = guac_palette_find(palette, color);
}
/* Advance to next data row */
data += stride;
}
/* Write image info */
png_set_IHDR(
png,
png_info,
width,
height,
bpp,
PNG_COLOR_TYPE_PALETTE,
PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT
);
/* Write palette */
png_set_PLTE(png, png_info, palette->colors, palette->size);
/* Write image */
png_set_rows(png, png_info, png_rows);
png_write_png(png, png_info, PNG_TRANSFORM_PACKING, NULL);
/* Finish write */
png_destroy_write_struct(&png, &png_info);
/* Free palette */
guac_palette_free(palette);
/* Free PNG data */
for (y=0; y<height; y++)
guac_mem_free(png_rows[y]);
guac_mem_free(png_rows);
/* Ensure all data is written */
guac_png_flush_data(&write_state);
return 0;
}