1
/*********************************************************
2
* Copyright (C) 2002 VMware, Inc. All rights reserved.
4
* This program is free software; you can redistribute it and/or modify it
5
* under the terms of the GNU Lesser General Public License as published
6
* by the Free Software Foundation version 2.1 and no later version.
8
* This program is distributed in the hope that it will be useful, but
9
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10
* or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser GNU General Public
11
* License for more details.
13
* You should have received a copy of the GNU Lesser General Public License
14
* along with this program; if not, write to the Free Software Foundation, Inc.,
15
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
17
*********************************************************/
22
* This file contains platform independent code to read and write PNG files
27
#include "imageUtil.h"
28
#include "rasterConv.h"
34
#define PNG_HEADER_CHECK_BUF_SIZE 8
36
typedef struct ImageUtilReadPng {
42
*-----------------------------------------------------------------------------
44
* ImageUtilReadPngCallback --
46
* Callback function for reading from a PNG file.
52
* Fills buffer `data' by reading from the file. Note that png_error()
53
* calls longjmp() so that an error will unwind the stack.
55
*-----------------------------------------------------------------------------
59
ImageUtilReadPngCallback(png_structp png_ptr, // IN: PNG file info structure
60
png_bytep data, // OUT: input buffer
61
png_size_t length) // IN: byte count
63
ImageUtilReadPng *pngData = png_get_io_ptr(png_ptr);
64
memcpy(data, pngData->data + pngData->offset, length);
65
pngData->offset += (unsigned int)length;
70
*----------------------------------------------------------------------------
72
* ImageUtil_ReadPNGBuffer --
74
* Loads and reads the specified PNG file and returns its attributes and
75
* data in the provided out parameters.
81
* Reads PNG from disk.
83
*----------------------------------------------------------------------------
87
ImageUtil_ReadPNGBuffer(ImageInfo *image, // OUT
88
const unsigned char *data, // IN
93
int i, channel_depth, color_type, interlace_type, compression_type, filter_type;
97
png_bytep *row_pointers = NULL;
99
ImageUtilReadPng *pngData = NULL;
103
if (!image || !data || !dataLen) {
107
pngData = Util_SafeCalloc(1, sizeof *pngData);
108
pngData->data = (char *) data;
112
* Do an initial check to make sure this is a PNG file. This check also
113
* eliminate the case of 0-byte file due to the previous write error
115
if (dataLen < PNG_HEADER_CHECK_BUF_SIZE) {
119
if (png_sig_cmp(pngData->data, 0, PNG_HEADER_CHECK_BUF_SIZE)) {
123
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
125
if (png_ptr == NULL) {
129
/* Allocate/initialize the memory for image information. */
130
info_ptr = png_create_info_struct(png_ptr);
131
if (info_ptr == NULL) {
132
png_destroy_read_struct(&png_ptr, NULL, NULL);
136
if (setjmp(png_ptr->jmpbuf)) {
137
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
141
png_set_read_fn(png_ptr, pngData, ImageUtilReadPngCallback);
143
png_read_info(png_ptr, info_ptr);
145
png_get_IHDR(png_ptr, info_ptr,
147
&channel_depth, &color_type,
148
&interlace_type, &compression_type, &filter_type);
150
bytes_per_line = png_get_rowbytes(png_ptr, info_ptr);
152
if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
156
png_set_strip_alpha(png_ptr);
158
/* Update the bytes_per_line now that we've eliminated the alpha channel */
159
png_read_update_info(png_ptr, info_ptr);
160
bytes_per_line = png_get_rowbytes(png_ptr, info_ptr);
162
png_get_IHDR(png_ptr, info_ptr,
164
&channel_depth, &color_type, &interlace_type,
165
&compression_type, &filter_type);
167
} else if (color_type == PNG_COLOR_TYPE_RGB) {
169
} else if (color_type == PNG_COLOR_TYPE_PALETTE) {
173
if (channel_depth < 8) {
174
png_set_packing(png_ptr);
175
png_read_update_info(png_ptr, info_ptr);
176
bytes_per_line = png_get_rowbytes(png_ptr, info_ptr);
181
png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
182
ASSERT(num_palette <= 256);
184
for (i = 0; i < num_palette; i++) {
185
image->palette[i].red = palette[i].red;
186
image->palette[i].green = palette[i].green;
187
image->palette[i].blue = palette[i].blue;
188
image->palette[i].reserved = 0;
191
image->numColors = num_palette;
193
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
197
image->width = width;
198
image->height = height;
199
image->bytesPerLine = DWORD_ALIGN(bytes_per_line);
200
image->depth = image->bpp;
203
/* BGR instead of RGB - Intel byte-order is backwards */
204
png_set_bgr(png_ptr);
206
/* Allocate the memory to hold the image using the fields of info_ptr. */
207
image->data = Util_SafeMalloc(image->bytesPerLine * image->height);
208
row_pointers = Util_SafeMalloc(sizeof *row_pointers * image->height);
210
/* The easiest way to read the image: */
211
for (i = 0; i < image->height; i++) {
212
row_pointers[i] = image->data + i * (DWORD_ALIGN(bytes_per_line));
215
png_read_image(png_ptr, row_pointers);
216
png_read_end(png_ptr, info_ptr);
219
/* Read text fields (XXX eventually read rectangle data here) */
221
png_text *text_ptr = NULL;
224
if (png_get_text(png_ptr, info_ptr, &text_ptr, &num_text) > 0) {
225
for(i = 0; i < num_text; i++) {
226
fprintf(stderr, "Png text: (%s) %s\n", text_ptr[i].key,
233
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
244
*----------------------------------------------------------------------------
246
* ImageUtilDataWriteCallback
248
* Callback for the png library to write data into the DynBuf.
254
* Writes data to DynBuf.
256
*----------------------------------------------------------------------------
260
ImageUtilDataWriteCallback(png_structp png_ptr, // IN
261
png_bytep data, // IN
262
png_size_t length) // IN
264
DynBuf *imageData = png_get_io_ptr(png_ptr);
265
if (!DynBuf_Append(imageData, data, length)) {
266
png_error(png_ptr, "Unable to append data");
272
*----------------------------------------------------------------------------
274
* ImageUtil_ConstructPNGBuffer --
276
* Writes a PNG of the image to the DynBuf passed in.
279
* TRUE if successful, FALSE otherwise.
282
* If successful, imageData should be destroyed.
284
*----------------------------------------------------------------------------
288
ImageUtil_ConstructPNGBuffer(const ImageInfo *image, // IN
289
DynBuf *imageData) // OUT
292
ImagePngOptions options;
294
options.zlibCompressLevel = -1;
295
options.stripAlphaChannel = TRUE;
297
return ImageUtil_ConstructPNGBufferEx(image, &options, imageData);
302
*----------------------------------------------------------------------------
304
* ImageUtil_ConstructPNGBufferEx --
306
* Writes a PNG of the image to the DynBuf passed in. Accepts a zlib
307
* compression level (0-9, 0 means no compression, -1 means "use the
311
* TRUE if successful, FALSE otherwise.
314
* Allocates memory; If successful, imageData needs to be cleaned up later.
316
*----------------------------------------------------------------------------
320
ImageUtil_ConstructPNGBufferEx(const ImageInfo *image, // IN
321
const ImagePngOptions *options, // IN
322
DynBuf *imageData) // OUT
329
//png_text text_ptr[1];
337
png_bytep *row_pointers;
343
if (!image || !options || !imageData) {
347
row_pointers = malloc(sizeof *row_pointers * image->height);
352
DynBuf_Init(imageData);
354
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
356
if (png_ptr == NULL) {
360
/* Allocate/initialize the image information data. REQUIRED */
361
info_ptr = png_create_info_struct(png_ptr);
362
if (info_ptr == NULL) {
363
png_destroy_write_struct(&png_ptr, NULL);
367
if (setjmp(png_ptr->jmpbuf)) {
368
png_destroy_write_struct(&png_ptr, &info_ptr);
372
png_set_write_fn(png_ptr, imageData, ImageUtilDataWriteCallback, NULL);
374
if (image->bpp <= 8) {
378
color_type = PNG_COLOR_TYPE_PALETTE;
379
} else if (image->bpp == 32) {
381
* Save with alpha channel
383
color_type = PNG_COLOR_TYPE_RGB_ALPHA;
386
* Everything else is RGB
388
color_type = PNG_COLOR_TYPE_RGB;
391
png_set_IHDR(png_ptr, info_ptr, image->width, image->height, 8, color_type,
392
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
394
if (options->zlibCompressLevel >= 0 && options->zlibCompressLevel <= 9) {
395
png_set_compression_level(png_ptr, options->zlibCompressLevel);
399
/* Setup the color depths */
400
sig_bit.red = COUNT_BITS(image->redMask);
401
sig_bit.green = COUNT_BITS(image->greenMask);
402
sig_bit.blue = COUNT_BITS(image->blueMask);
403
png_set_sBIT(png_ptr, info_ptr, &sig_bit);
405
#if 1 //We probably DO need this
406
/* Shift the pixels up to a legal bit depth and fill in
407
* as appropriate to correctly scale the image.
409
png_set_shift(png_ptr, &sig_bit);
413
// BGR instead of RGB - Intel byte-order is backwards
414
png_set_bgr(png_ptr);
417
* Optionally write comments into the image, e.g.
418
* text_ptr[0].key = "Title";
419
* text_ptr[0].text = "Mona Lisa";
420
* text_ptr[0].compression = PNG_TEXT_COMPRESSION_NONE;
421
* png_set_text(png_ptr, info_ptr, text_ptr, 1);
426
* Now set up the transformations you want. Note that these are
427
* all optional. Only call them if you want them.
429
bytes_per_line = image->bytesPerLine;
432
/* pack pixels into bytes */
433
png_set_packing(png_ptr);
438
if (image->bpp == 24) {
440
* The image is already RGB, no need for any conversion/processing
442
} else if (image->bpp <= 8) {
446
png_color palette[256];
447
ASSERT(image->numColors <= 256);
449
for (i = 0; i < image->numColors; i++) {
450
palette[i].red = image->palette[i].red;
451
palette[i].green = image->palette[i].green;
452
palette[i].blue = image->palette[i].blue;
455
png_set_PLTE(png_ptr, info_ptr, palette, image->numColors);
456
} else if (image->bpp == 32) {
457
if (options->stripAlphaChannel) {
460
* Strip the alpha channel
463
png_set_strip_alpha(png_ptr);
466
* XXX: Following call appears to leak two allocations of 1 byte
467
* each deep inside the png library via png_malloc.
469
png_read_update_info(png_ptr, info_ptr);
472
/* XXX convert to 24 - we could try and support other modes? */
473
bytes_per_line = DWORD_ALIGN(png_get_rowbytes(png_ptr, info_ptr));
475
data = Util_SafeMalloc(bytes_per_line * image->height);
477
Raster_ConvertPixels(data, bytes_per_line, 24,
478
image->data, image->bytesPerLine, image->depth,
479
FALSE, NULL, 0, 0, 0, 0,
480
image->width, image->height);
483
/* Write the file header information. REQUIRED */
484
png_write_info(png_ptr, info_ptr);
486
if ((image->bpp == 32) && options->stripAlphaChannel) {
487
/* treat the alpha channel byte as filler */
488
png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
491
for (k = 0; k < image->height; k++) {
494
if (image->flags & IMAGE_FLAG_BOTTOM_UP) {
495
rowIndex = image->height - 1 - k;
500
row_pointers[rowIndex] = data + k * bytes_per_line;
503
png_write_image(png_ptr, row_pointers);
505
if (data != image->data) {
509
/* It is REQUIRED to call this to finish writing the rest of the file */
510
png_write_end(png_ptr, info_ptr);
512
/* if you allocated any text comments, free them here */
514
png_destroy_write_struct(&png_ptr, &info_ptr);
521
DynBuf_Destroy(imageData);