Leptonica 1.68
C Image Processing Library
|
00001 /*====================================================================* 00002 - Copyright (C) 2001 Leptonica. All rights reserved. 00003 - This software is distributed in the hope that it will be 00004 - useful, but with NO WARRANTY OF ANY KIND. 00005 - No author or distributor accepts responsibility to anyone for the 00006 - consequences of using this software, or for whether it serves any 00007 - particular purpose or works at all, unless he or she says so in 00008 - writing. Everyone is granted permission to copy, modify and 00009 - redistribute this source code, for commercial or non-commercial 00010 - purposes, with the following restrictions: (1) the origin of this 00011 - source code must not be misrepresented; (2) modified versions must 00012 - be plainly marked as such; and (3) this notice may not be removed 00013 - or altered from any source or modified source distribution. 00014 *====================================================================*/ 00015 00016 /* 00017 * pngio.c 00018 * 00019 * Read png from file 00020 * PIX *pixReadStreamPng() 00021 * l_int32 readHeaderPng() 00022 * l_int32 freadHeaderPng() 00023 * l_int32 sreadHeaderPng() 00024 * l_int32 fgetPngResolution() 00025 * 00026 * Write png to file 00027 * l_int32 pixWritePng() [ special top level ] 00028 * l_int32 pixWriteStreamPng() 00029 * 00030 * Read and write of png to/from RGBA pix 00031 * PIX *pixReadRGBAPng(); 00032 * l_int32 pixWriteRGBAPng(); 00033 * 00034 * Setting flags for special modes 00035 * void l_pngSetStrip16To8() 00036 * void l_pngSetStripAlpha() 00037 * void l_pngSetWriteAlpha() 00038 * void l_pngSetZlibCompression() 00039 * 00040 * Read/write to memory [not on windows] 00041 * PIX *pixReadMemPng() 00042 * l_int32 pixWriteMemPng() 00043 * 00044 * Documentation: libpng.txt and example.c 00045 * 00046 * On input (decompression from file), palette color images 00047 * are read into an 8 bpp Pix with a colormap, and 24 bpp 00048 * 3 component color images are read into a 32 bpp Pix with 00049 * rgb samples. On output (compression to file), palette color 00050 * images are written as 8 bpp with the colormap, and 32 bpp 00051 * full color images are written compressed as a 24 bpp, 00052 * 3 component color image. 00053 * 00054 * In the following, we use these abbreviations: 00055 * bps == bit/sample 00056 * spp == samples/pixel 00057 * bpp == bits/pixel of image in Pix (memory) 00058 * where each component is referred to as a "sample". 00059 * 00060 * There are three special flags for determining the number or 00061 * size of samples retained or written: 00062 * (1) var_PNG_STRIP_16_to_8: default is TRUE. This strips each 00063 * 16 bit sample down to 8 bps: 00064 * - For 16 bps rgb (16 bps, 3 spp) --> 32 bpp rgb Pix 00065 * - For 16 bps gray (16 bps, 1 spp) --> 8 bpp grayscale Pix 00066 * (2) var_PNG_STRIP_ALPHA: default is TRUE. This does not copy 00067 * the alpha channel to the pix: 00068 * - For 8 bps rgba (8 bps, 4 spp) --> 32 bpp rgb Pix 00069 * (3) var_PNG_WRITE_ALPHA: default is FALSE. The default generates 00070 * an RGB png file with 3 spp. If set to TRUE, this generates 00071 * an RGBA png file with 4 spp, and writes the alpha channel. 00072 * These are set with accessors. 00073 * 00074 * Two convenience functions are included for reading the alpha 00075 * channel (if it exists) into the pix, and for writing out the 00076 * alpha sample of a pix to a png file: 00077 * pixReadRGBAPng() 00078 * pixWriteRGBAPng() 00079 * These use two of the special flags, setting to the non-default 00080 * value before use and resetting to default afterwards. 00081 * In leptonica, we make almost no explicit use of the alpha channel. 00082 * 00083 * Another special flag, var_ZLIB_COMPRESSION, is used to determine 00084 * the compression level. Default is for standard png compression. 00085 * The zlib compression value can be set [0 ... 9], with 00086 * 0 no compression (huge files) 00087 * 1 fastest compression 00088 * -1 default compression (equivalent to 6 in latest version) 00089 * 9 best compression 00090 * If not set, we use the default compression in zlib. 00091 * Note that if you are using the defined constants in zlib instead 00092 * of the compression integers given above, you must include zlib.h. 00093 * 00094 * Note: All special flags use global constants, so if used with 00095 * multi-threaded applications, results can be non-deterministic. 00096 */ 00097 00098 #include <string.h> 00099 #include "allheaders.h" 00100 00101 #ifdef HAVE_CONFIG_H 00102 #include "config_auto.h" 00103 #endif /* HAVE_CONFIG_H */ 00104 00105 /* --------------------------------------------*/ 00106 #if HAVE_LIBPNG /* defined in environ.h */ 00107 /* --------------------------------------------*/ 00108 00109 #include "png.h" 00110 00111 /* ----------------Set defaults for read/write options ----------------- */ 00112 /* strip 16 bpp --> 8 bpp on reading png; default is for stripping */ 00113 static l_int32 var_PNG_STRIP_16_TO_8 = 1; 00114 /* strip alpha on reading png; default is for stripping */ 00115 static l_int32 var_PNG_STRIP_ALPHA = 1; 00116 /* write alpha for 32 bpp images; default is to write only RGB */ 00117 static l_int32 var_PNG_WRITE_ALPHA = 0; 00118 /* zlib compression in png; default is for standard compression */ 00119 static l_int32 var_ZLIB_COMPRESSION = Z_DEFAULT_COMPRESSION; 00120 00121 00122 #ifndef NO_CONSOLE_IO 00123 #define DEBUG 0 00124 #endif /* ~NO_CONSOLE_IO */ 00125 00126 00127 /*---------------------------------------------------------------------* 00128 * Reading png * 00129 *---------------------------------------------------------------------*/ 00130 /*! 00131 * pixReadStreamPng() 00132 * 00133 * Input: stream 00134 * Return: pix, or null on error 00135 * 00136 * Notes: 00137 * (1) If called from pixReadStream(), the stream is positioned 00138 * at the beginning of the file. 00139 * (2) To do sequential reads of png format images from a stream, 00140 * use pixReadStreamPng() 00141 */ 00142 PIX * 00143 pixReadStreamPng(FILE *fp) 00144 { 00145 l_uint8 rval, gval, bval; 00146 l_int32 i, j, k; 00147 l_int32 wpl, d, spp, cindex; 00148 l_uint32 png_transforms; 00149 l_uint32 *data, *line, *ppixel; 00150 int num_palette, num_text; 00151 png_byte bit_depth, color_type, channels; 00152 png_uint_32 w, h, rowbytes; 00153 png_uint_32 xres, yres; 00154 png_bytep rowptr; 00155 png_bytep *row_pointers; 00156 png_structp png_ptr; 00157 png_infop info_ptr, end_info; 00158 png_colorp palette; 00159 png_textp text_ptr; /* ptr to text_chunk */ 00160 PIX *pix; 00161 PIXCMAP *cmap; 00162 00163 PROCNAME("pixReadStreamPng"); 00164 00165 if (!fp) 00166 return (PIX *)ERROR_PTR("fp not defined", procName, NULL); 00167 pix = NULL; 00168 00169 /* Allocate the 3 data structures */ 00170 if ((png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 00171 (png_voidp)NULL, NULL, NULL)) == NULL) 00172 return (PIX *)ERROR_PTR("png_ptr not made", procName, NULL); 00173 00174 if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) { 00175 png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); 00176 return (PIX *)ERROR_PTR("info_ptr not made", procName, NULL); 00177 } 00178 00179 if ((end_info = png_create_info_struct(png_ptr)) == NULL) { 00180 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); 00181 return (PIX *)ERROR_PTR("end_info not made", procName, NULL); 00182 } 00183 00184 /* Set up png setjmp error handling */ 00185 if (setjmp(png_jmpbuf(png_ptr))) { 00186 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); 00187 return (PIX *)ERROR_PTR("internal png error", procName, NULL); 00188 } 00189 00190 png_init_io(png_ptr, fp); 00191 00192 /* ---------------------------------------------------------- * 00193 * Set the transforms flags. Whatever happens here, 00194 * NEVER invert 1 bpp using PNG_TRANSFORM_INVERT_MONO. 00195 * ---------------------------------------------------------- */ 00196 /* To strip 16 --> 8 bit depth, use PNG_TRANSFORM_STRIP_16 */ 00197 if (var_PNG_STRIP_16_TO_8 == 1) /* our default */ 00198 png_transforms = PNG_TRANSFORM_STRIP_16; 00199 else 00200 png_transforms = PNG_TRANSFORM_IDENTITY; 00201 /* To remove alpha channel, use PNG_TRANSFORM_STRIP_ALPHA */ 00202 if (var_PNG_STRIP_ALPHA == 1) /* our default */ 00203 png_transforms |= PNG_TRANSFORM_STRIP_ALPHA; 00204 00205 /* Read it */ 00206 png_read_png(png_ptr, info_ptr, png_transforms, NULL); 00207 00208 row_pointers = png_get_rows(png_ptr, info_ptr); 00209 w = png_get_image_width(png_ptr, info_ptr); 00210 h = png_get_image_height(png_ptr, info_ptr); 00211 bit_depth = png_get_bit_depth(png_ptr, info_ptr); 00212 rowbytes = png_get_rowbytes(png_ptr, info_ptr); 00213 color_type = png_get_color_type(png_ptr, info_ptr); 00214 channels = png_get_channels(png_ptr, info_ptr); 00215 spp = channels; 00216 00217 if (spp == 1) 00218 d = bit_depth; 00219 else if (spp == 2) { 00220 d = 2 * bit_depth; 00221 L_WARNING("there shouldn't be 2 spp!", procName); 00222 } 00223 else /* spp == 3 (rgb), spp == 4 (rgba) */ 00224 d = 4 * bit_depth; 00225 00226 /* Remove if/when this is implemented for all bit_depths */ 00227 if (spp == 3 && bit_depth != 8) { 00228 fprintf(stderr, "Help: spp = 3 and depth = %d != 8\n!!", bit_depth); 00229 return (PIX *)ERROR_PTR("not implemented for this depth", 00230 procName, NULL); 00231 } 00232 00233 if (color_type == PNG_COLOR_TYPE_PALETTE || 00234 color_type == PNG_COLOR_MASK_PALETTE) { /* generate a colormap */ 00235 png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette); 00236 cmap = pixcmapCreate(d); /* spp == 1 */ 00237 for (cindex = 0; cindex < num_palette; cindex++) { 00238 rval = palette[cindex].red; 00239 gval = palette[cindex].green; 00240 bval = palette[cindex].blue; 00241 pixcmapAddColor(cmap, rval, gval, bval); 00242 } 00243 } 00244 else 00245 cmap = NULL; 00246 00247 if ((pix = pixCreate(w, h, d)) == NULL) { 00248 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); 00249 return (PIX *)ERROR_PTR("pix not made", procName, NULL); 00250 } 00251 wpl = pixGetWpl(pix); 00252 data = pixGetData(pix); 00253 pixSetColormap(pix, cmap); 00254 00255 if (spp == 1) { /* copy straight from buffer to pix */ 00256 for (i = 0; i < h; i++) { 00257 line = data + i * wpl; 00258 rowptr = row_pointers[i]; 00259 for (j = 0; j < rowbytes; j++) { 00260 SET_DATA_BYTE(line, j, rowptr[j]); 00261 } 00262 } 00263 } 00264 else { /* spp == 3 or spp == 4 */ 00265 for (i = 0; i < h; i++) { 00266 ppixel = data + i * wpl; 00267 rowptr = row_pointers[i]; 00268 for (j = k = 0; j < w; j++) { 00269 SET_DATA_BYTE(ppixel, COLOR_RED, rowptr[k++]); 00270 SET_DATA_BYTE(ppixel, COLOR_GREEN, rowptr[k++]); 00271 SET_DATA_BYTE(ppixel, COLOR_BLUE, rowptr[k++]); 00272 if (spp == 4) 00273 SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, rowptr[k++]); 00274 ppixel++; 00275 } 00276 } 00277 } 00278 00279 #if DEBUG 00280 if (cmap) { 00281 for (i = 0; i < 16; i++) { 00282 fprintf(stderr, "[%d] = %d\n", i, 00283 ((l_uint8 *)(cmap->array))[i]); 00284 } 00285 } 00286 #endif /* DEBUG */ 00287 00288 /* If there is no colormap, PNG defines black = 0 and 00289 * white = 1 by default for binary monochrome. Therefore, 00290 * since we use the opposite definition, we must invert 00291 * the image in either of these cases: 00292 * (i) there is no colormap (default) 00293 * (ii) there is a colormap which defines black to 00294 * be 0 and white to be 1. 00295 * We cannot use the PNG_TRANSFORM_INVERT_MONO flag 00296 * because that flag (since version 1.0.9) inverts 8 bpp 00297 * grayscale as well, which we don't want to do. 00298 * (It also doesn't work if there is a colormap.) 00299 * If there is a colormap that defines black = 1 and 00300 * white = 0, we don't need to do anything. 00301 * 00302 * How do we check the polarity of the colormap? 00303 * The colormap determines the values of black and 00304 * white pixels in the following way: 00305 * if black = 1 (255), white = 0 00306 * 255, 255, 255, 0, 0, 0, 0, 0, 0 00307 * if black = 0, white = 1 (255) 00308 * 0, 0, 0, 0, 255, 255, 255, 0 00309 * So we test the first byte to see if it is 0; 00310 * if so, invert the data. */ 00311 if (d == 1 && (!cmap || (cmap && ((l_uint8 *)(cmap->array))[0] == 0x0))) { 00312 /* fprintf(stderr, "Inverting binary data on png read\n"); */ 00313 pixInvert(pix, pix); 00314 } 00315 00316 xres = png_get_x_pixels_per_meter(png_ptr, info_ptr); 00317 yres = png_get_y_pixels_per_meter(png_ptr, info_ptr); 00318 pixSetXRes(pix, (l_int32)((l_float32)xres / 39.37 + 0.5)); /* to ppi */ 00319 pixSetYRes(pix, (l_int32)((l_float32)yres / 39.37 + 0.5)); /* to ppi */ 00320 00321 /* Get the text if there is any */ 00322 png_get_text(png_ptr, info_ptr, &text_ptr, &num_text); 00323 if (num_text && text_ptr) 00324 pixSetText(pix, text_ptr->text); 00325 00326 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); 00327 return pix; 00328 } 00329 00330 00331 /*! 00332 * readHeaderPng() 00333 * 00334 * Input: filename 00335 * &width (<return>) 00336 * &height (<return>) 00337 * &bps (<return>, bits/sample) 00338 * &spp (<return>, samples/pixel) 00339 * &iscmap (<optional return>; input NULL to ignore) 00340 * Return: 0 if OK, 1 on error 00341 * 00342 * Notes: 00343 * (1) If there is a colormap, iscmap is returned as 1; else 0. 00344 */ 00345 l_int32 00346 readHeaderPng(const char *filename, 00347 l_int32 *pwidth, 00348 l_int32 *pheight, 00349 l_int32 *pbps, 00350 l_int32 *pspp, 00351 l_int32 *piscmap) 00352 { 00353 l_int32 ret; 00354 FILE *fp; 00355 00356 PROCNAME("readHeaderPng"); 00357 00358 if (!filename) 00359 return ERROR_INT("filename not defined", procName, 1); 00360 if (!pwidth || !pheight || !pbps || !pspp) 00361 return ERROR_INT("input ptr(s) not defined", procName, 1); 00362 if ((fp = fopenReadStream(filename)) == NULL) 00363 return ERROR_INT("image file not found", procName, 1); 00364 ret = freadHeaderPng(fp, pwidth, pheight, pbps, pspp, piscmap); 00365 fclose(fp); 00366 return ret; 00367 } 00368 00369 00370 /*! 00371 * freadHeaderPng() 00372 * 00373 * Input: stream 00374 * &width (<return>) 00375 * &height (<return>) 00376 * &bps (<return>, bits/sample) 00377 * &spp (<return>, samples/pixel) 00378 * &iscmap (<optional return>; input NULL to ignore) 00379 * Return: 0 if OK, 1 on error 00380 * 00381 * Notes: 00382 * (1) If there is a colormap, iscmap is returned as 1; else 0. 00383 */ 00384 l_int32 00385 freadHeaderPng(FILE *fp, 00386 l_int32 *pwidth, 00387 l_int32 *pheight, 00388 l_int32 *pbps, 00389 l_int32 *pspp, 00390 l_int32 *piscmap) 00391 { 00392 l_int32 nbytes, ret; 00393 l_uint8 *data; 00394 00395 PROCNAME("freadHeaderPng"); 00396 00397 if (!fp) 00398 return ERROR_INT("stream not defined", procName, 1); 00399 if (!pwidth || !pheight || !pbps || !pspp) 00400 return ERROR_INT("input ptr(s) not defined", procName, 1); 00401 00402 nbytes = fnbytesInFile(fp); 00403 if (nbytes < 40) 00404 return ERROR_INT("file too small to be png", procName, 1); 00405 if ((data = (l_uint8 *)CALLOC(40, sizeof(l_uint8))) == NULL) 00406 return ERROR_INT("CALLOC fail for data", procName, 1); 00407 if (fread(data, 1, 40, fp) != 40) 00408 return ERROR_INT("error reading data", procName, 1); 00409 ret = sreadHeaderPng(data, pwidth, pheight, pbps, pspp, piscmap); 00410 FREE(data); 00411 return ret; 00412 } 00413 00414 00415 /*! 00416 * sreadHeaderPng() 00417 * 00418 * Input: data 00419 * &width (<return>) 00420 * &height (<return>) 00421 * &bps (<return>, bits/sample) 00422 * &spp (<return>, samples/pixel) 00423 * &iscmap (<optional return>; input NULL to ignore) 00424 * Return: 0 if OK, 1 on error 00425 * 00426 * Notes: 00427 * (1) If there is a colormap, iscmap is returned as 1; else 0. 00428 */ 00429 l_int32 00430 sreadHeaderPng(const l_uint8 *data, 00431 l_int32 *pwidth, 00432 l_int32 *pheight, 00433 l_int32 *pbps, 00434 l_int32 *pspp, 00435 l_int32 *piscmap) 00436 { 00437 l_uint8 colortype, bps; 00438 l_uint16 twobytes; 00439 l_uint16 *pshort; 00440 l_uint32 *pword; 00441 00442 PROCNAME("sreadHeaderPng"); 00443 00444 if (!data) 00445 return ERROR_INT("data not defined", procName, 1); 00446 if (!pwidth || !pheight || !pbps || !pspp) 00447 return ERROR_INT("input ptr(s) not defined", procName, 1); 00448 *pwidth = *pheight = *pbps = *pspp = 0; 00449 if (piscmap) 00450 *piscmap = 0; 00451 00452 /* Check password */ 00453 if (data[0] != 137 || data[1] != 80 || data[2] != 78 || 00454 data[3] != 71 || data[4] != 13 || data[5] != 10 || 00455 data[6] != 26 || data[7] != 10) 00456 return ERROR_INT("not a valid png file", procName, 1); 00457 00458 pword = (l_uint32 *)data; 00459 pshort = (l_uint16 *)data; 00460 *pwidth = convertOnLittleEnd32(pword[4]); 00461 *pheight = convertOnLittleEnd32(pword[5]); 00462 twobytes = convertOnLittleEnd16(pshort[12]); /* contains depth/sample */ 00463 /* and the color type */ 00464 colortype = twobytes & 0xff; /* color type */ 00465 bps = twobytes >> 8; /* bits/sample */ 00466 *pbps = bps; 00467 if (colortype == 2) /* RGB */ 00468 *pspp = 3; 00469 else if (colortype == 6) /* RGBA */ 00470 *pspp = 4; 00471 else /* palette or gray */ 00472 *pspp = 1; 00473 if (piscmap) { 00474 if (colortype & 1) /* palette: see png.h, PNG_COLOR_TYPE_... */ 00475 *piscmap = 1; 00476 else 00477 *piscmap = 0; 00478 } 00479 00480 return 0; 00481 } 00482 00483 00484 /* 00485 * fgetPngResolution() 00486 * 00487 * Input: stream (opened for read) 00488 * &xres, &yres (<return> resolution in ppi) 00489 * Return: 0 if OK; 0 on error 00490 * 00491 * Notes: 00492 * (1) If neither resolution field is set, this is not an error; 00493 * the returned resolution values are 0 (designating 'unknown'). 00494 * (2) Side-effect: this rewinds the stream. 00495 */ 00496 l_int32 00497 fgetPngResolution(FILE *fp, 00498 l_int32 *pxres, 00499 l_int32 *pyres) 00500 { 00501 png_uint_32 xres, yres; 00502 png_structp png_ptr; 00503 png_infop info_ptr; 00504 00505 PROCNAME("fgetPngResolution"); 00506 00507 if (!pxres || !pyres) 00508 return ERROR_INT("&xres and &yres not both defined", procName, 1); 00509 *pxres = *pyres = 0; 00510 if (!fp) 00511 return ERROR_INT("stream not opened", procName, 1); 00512 00513 /* Make the two required structs */ 00514 if ((png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 00515 (png_voidp)NULL, NULL, NULL)) == NULL) 00516 return ERROR_INT("png_ptr not made", procName, 1); 00517 if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) { 00518 png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); 00519 return ERROR_INT("info_ptr not made", procName, 1); 00520 } 00521 00522 /* Set up png setjmp error handling. 00523 * Without this, an error calls exit. */ 00524 if (setjmp(png_jmpbuf(png_ptr))) { 00525 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); 00526 return ERROR_INT("internal png error", procName, 1); 00527 } 00528 00529 /* Read the metadata */ 00530 rewind(fp); 00531 png_init_io(png_ptr, fp); 00532 png_read_png(png_ptr, info_ptr, 0, NULL); 00533 00534 xres = png_get_x_pixels_per_meter(png_ptr, info_ptr); 00535 yres = png_get_y_pixels_per_meter(png_ptr, info_ptr); 00536 *pxres = (l_int32)((l_float32)xres / 39.37 + 0.5); /* to ppi */ 00537 *pyres = (l_int32)((l_float32)yres / 39.37 + 0.5); 00538 00539 png_destroy_read_struct(&png_ptr, &info_ptr, NULL); 00540 rewind(fp); 00541 return 0; 00542 } 00543 00544 00545 /*---------------------------------------------------------------------* 00546 * Writing png * 00547 *---------------------------------------------------------------------*/ 00548 /*! 00549 * pixWritePng() 00550 * 00551 * Input: filename 00552 * pix 00553 * gamma 00554 * Return: 0 if OK; 1 on error 00555 * 00556 * Notes: 00557 * (1) Special version for writing png with a specified gamma. 00558 * When using pixWrite(), no field is given for gamma. 00559 */ 00560 l_int32 00561 pixWritePng(const char *filename, 00562 PIX *pix, 00563 l_float32 gamma) 00564 { 00565 FILE *fp; 00566 00567 PROCNAME("pixWritePng"); 00568 00569 if (!pix) 00570 return ERROR_INT("pix not defined", procName, 1); 00571 if (!filename) 00572 return ERROR_INT("filename not defined", procName, 1); 00573 00574 if ((fp = fopenWriteStream(filename, "wb+")) == NULL) 00575 return ERROR_INT("stream not opened", procName, 1); 00576 00577 if (pixWriteStreamPng(fp, pix, gamma)) { 00578 fclose(fp); 00579 return ERROR_INT("pix not written to stream", procName, 1); 00580 } 00581 00582 fclose(fp); 00583 return 0; 00584 } 00585 00586 00587 /*! 00588 * pixWriteStreamPng() 00589 * 00590 * Input: stream 00591 * pix 00592 * gamma (use 0.0 if gamma is not defined) 00593 * Return: 0 if OK; 1 on error 00594 * 00595 * Notes: 00596 * (1) If called from pixWriteStream(), the stream is positioned 00597 * at the beginning of the file. 00598 * (2) To do sequential writes of png format images to a stream, 00599 * use pixWriteStreamPng() directly. 00600 * (3) gamma is an optional png chunk. If no gamma value is to be 00601 * placed into the file, use gamma = 0.0. Otherwise, if 00602 * gamma > 0.0, its value is written into the header. 00603 * (4) The use of gamma in png is highly problematic. For an illuminating 00604 * discussion, see: http://hsivonen.iki.fi/png-gamma/ 00605 * (5) What is the effect/meaning of gamma in the png file? This 00606 * gamma, which we can call the 'source' gamma, is the 00607 * inverse of the gamma that was used in enhance.c to brighten 00608 * or darken images. The 'source' gamma is supposed to indicate 00609 * the intensity mapping that was done at the time the 00610 * image was captured. Display programs typically apply a 00611 * 'display' gamma of 2.2 to the output, which is intended 00612 * to linearize the intensity based on the response of 00613 * thermionic tubes (CRTs). Flat panel LCDs have typically 00614 * been designed to give a similar response as CRTs (call it 00615 * "backward compatibility"). The 'display' gamma is 00616 * in some sense the inverse of the 'source' gamma. 00617 * jpeg encoders attached to scanners and cameras will lighten 00618 * the pixels, applying a gamma corresponding to approximately 00619 * a square-root relation of output vs input: 00620 * output = input^(gamma) 00621 * where gamma is often set near 0.4545 (1/gamma is 2.2). 00622 * This is stored in the image file. Then if the display 00623 * program reads the gamma, it will apply a display gamma, 00624 * typically about 2.2; the product is 1.0, and the 00625 * display program produces a linear output. This works because 00626 * the dark colors were appropriately boosted by the scanner, 00627 * as described by the 'source' gamma, so they should not 00628 * be further boosted by the display program. 00629 * (6) As an example, with xv and display, if no gamma is stored, 00630 * the program acts as if gamma were 0.4545, multiplies this by 2.2, 00631 * and does a linear rendering. Taking this as a baseline 00632 * brightness, if the stored gamma is: 00633 * > 0.4545, the image is rendered lighter than baseline 00634 * < 0.4545, the image is rendered darker than baseline 00635 * In contrast, gqview seems to ignore the gamma chunk in png. 00636 * (7) The only valid pixel depths in leptonica are 1, 2, 4, 8, 16 00637 * and 32. However, it is possible, and in some cases desirable, 00638 * to write out a png file using an rgb pix that has 24 bpp. 00639 * For example, the open source xpdf SplashBitmap class generates 00640 * 24 bpp rgb images. Consequently, we anble writing 24 bpp pix. 00641 * To generate such a pix, you can make a 24 bpp pix without data 00642 * and assign the data array to the pix; e.g., 00643 * pix = pixCreateHeader(w, h, 24); 00644 * pixSetData(pix, rgbdata); 00645 * See pixConvert32To24() for an example, where we get rgbdata 00646 * from the 32 bpp pix. Caution: do not call pixSetPadBits(), 00647 * because the alignment is wrong and you may erase part of the 00648 * last pixel on each line. 00649 */ 00650 l_int32 00651 pixWriteStreamPng(FILE *fp, 00652 PIX *pix, 00653 l_float32 gamma) 00654 { 00655 char commentstring[] = "Comment"; 00656 l_int32 i, j, k; 00657 l_int32 wpl, d, cmflag; 00658 l_int32 ncolors; 00659 l_int32 *rmap, *gmap, *bmap; 00660 l_uint32 *data, *ppixel; 00661 png_byte bit_depth, color_type; 00662 png_uint_32 w, h; 00663 png_uint_32 xres, yres; 00664 png_bytep *row_pointers; 00665 png_bytep rowbuffer; 00666 png_structp png_ptr; 00667 png_infop info_ptr; 00668 png_colorp palette; 00669 PIX *pixt; 00670 PIXCMAP *cmap; 00671 char *text; 00672 00673 PROCNAME("pixWriteStreamPng"); 00674 00675 if (!fp) 00676 return ERROR_INT("stream not open", procName, 1); 00677 if (!pix) 00678 return ERROR_INT("pix not defined", procName, 1); 00679 00680 /* Allocate the 2 data structures */ 00681 if ((png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 00682 (png_voidp)NULL, NULL, NULL)) == NULL) 00683 return ERROR_INT("png_ptr not made", procName, 1); 00684 00685 if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) { 00686 png_destroy_write_struct(&png_ptr, (png_infopp)NULL); 00687 return ERROR_INT("info_ptr not made", procName, 1); 00688 } 00689 00690 /* Set up png setjmp error handling */ 00691 if (setjmp(png_jmpbuf(png_ptr))) { 00692 png_destroy_write_struct(&png_ptr, &info_ptr); 00693 return ERROR_INT("internal png error", procName, 1); 00694 } 00695 00696 png_init_io(png_ptr, fp); 00697 00698 /* With best zlib compression (9), get between 1 and 10% improvement 00699 * over default (5), but the compression is 3 to 10 times slower. 00700 * Our default compression is the zlib default (5). */ 00701 png_set_compression_level(png_ptr, var_ZLIB_COMPRESSION); 00702 00703 w = pixGetWidth(pix); 00704 h = pixGetHeight(pix); 00705 d = pixGetDepth(pix); 00706 if ((cmap = pixGetColormap(pix))) 00707 cmflag = 1; 00708 else 00709 cmflag = 0; 00710 00711 /* Set the color type and bit depth. */ 00712 if (d == 32 && var_PNG_WRITE_ALPHA == 1) { 00713 bit_depth = 8; 00714 color_type = PNG_COLOR_TYPE_RGBA; /* 6 */ 00715 cmflag = 0; /* ignore if it exists */ 00716 } 00717 else if (d == 24 || d == 32) { 00718 bit_depth = 8; 00719 color_type = PNG_COLOR_TYPE_RGB; /* 2 */ 00720 cmflag = 0; /* ignore if it exists */ 00721 } 00722 else { 00723 bit_depth = d; 00724 color_type = PNG_COLOR_TYPE_GRAY; /* 0 */ 00725 } 00726 if (cmflag) 00727 color_type = PNG_COLOR_TYPE_PALETTE; /* 3 */ 00728 00729 #if DEBUG 00730 fprintf(stderr, "cmflag = %d, bit_depth = %d, color_type = %d\n", 00731 cmflag, bit_depth, color_type); 00732 #endif /* DEBUG */ 00733 00734 png_set_IHDR(png_ptr, info_ptr, w, h, bit_depth, color_type, 00735 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, 00736 PNG_FILTER_TYPE_BASE); 00737 00738 /* Store resolution in ppm, if known */ 00739 xres = (png_uint_32)(39.37 * (l_float32)pixGetXRes(pix) + 0.5); 00740 yres = (png_uint_32)(39.37 * (l_float32)pixGetYRes(pix) + 0.5); 00741 if ((xres == 0) || (yres == 0)) 00742 png_set_pHYs(png_ptr, info_ptr, 0, 0, PNG_RESOLUTION_UNKNOWN); 00743 else 00744 png_set_pHYs(png_ptr, info_ptr, xres, yres, PNG_RESOLUTION_METER); 00745 00746 if (cmflag) { 00747 pixcmapToArrays(cmap, &rmap, &gmap, &bmap); 00748 ncolors = pixcmapGetCount(cmap); 00749 00750 /* Make and save the palette */ 00751 if ((palette = (png_colorp)(CALLOC(ncolors, sizeof(png_color)))) 00752 == NULL) 00753 return ERROR_INT("palette not made", procName, 1); 00754 00755 for (i = 0; i < ncolors; i++) { 00756 palette[i].red = (png_byte)rmap[i]; 00757 palette[i].green = (png_byte)gmap[i]; 00758 palette[i].blue = (png_byte)bmap[i]; 00759 } 00760 00761 png_set_PLTE(png_ptr, info_ptr, palette, (int)ncolors); 00762 FREE(rmap); 00763 FREE(gmap); 00764 FREE(bmap); 00765 } 00766 00767 /* 0.4545 is treated as the default by some image 00768 * display programs (not gqview). A value > 0.4545 will 00769 * lighten an image as displayed by xv, display, etc. */ 00770 if (gamma > 0.0) 00771 png_set_gAMA(png_ptr, info_ptr, (l_float64)gamma); 00772 00773 if ((text = pixGetText(pix))) { 00774 png_text text_chunk; 00775 text_chunk.compression = PNG_TEXT_COMPRESSION_NONE; 00776 text_chunk.key = commentstring; 00777 text_chunk.text = text; 00778 text_chunk.text_length = strlen(text); 00779 #ifdef PNG_ITXT_SUPPORTED 00780 text_chunk.itxt_length = 0; 00781 text_chunk.lang = NULL; 00782 text_chunk.lang_key = NULL; 00783 #endif 00784 png_set_text(png_ptr, info_ptr, &text_chunk, 1); 00785 } 00786 00787 /* Write header and palette info */ 00788 png_write_info(png_ptr, info_ptr); 00789 00790 if ((d != 32) && (d != 24)) { /* not rgb color */ 00791 /* Generate a temporary pix with bytes swapped. 00792 * For a binary image, there are two conditions in 00793 * which you must first invert the data for writing png: 00794 * (a) no colormap 00795 * (b) colormap with BLACK set to 0 00796 * png writes binary with BLACK = 0, unless contradicted 00797 * by a colormap. If the colormap has BLACK = "1" 00798 * (typ. about 255), do not invert the data. If there 00799 * is no colormap, you must invert the data to store 00800 * in default BLACK = 0 state. */ 00801 if (d == 1 && 00802 (!cmap || (cmap && ((l_uint8 *)(cmap->array))[0] == 0x0))) { 00803 pixt = pixInvert(NULL, pix); 00804 pixEndianByteSwap(pixt); 00805 } 00806 else 00807 pixt = pixEndianByteSwapNew(pix); 00808 if (!pixt) { 00809 png_destroy_write_struct(&png_ptr, &info_ptr); 00810 return ERROR_INT("pixt not made", procName, 1); 00811 } 00812 00813 /* Make and assign array of image row pointers */ 00814 if ((row_pointers = (png_bytep *)CALLOC(h, sizeof(png_bytep))) == NULL) 00815 return ERROR_INT("row-pointers not made", procName, 1); 00816 wpl = pixGetWpl(pixt); 00817 data = pixGetData(pixt); 00818 for (i = 0; i < h; i++) 00819 row_pointers[i] = (png_bytep)(data + i * wpl); 00820 png_set_rows(png_ptr, info_ptr, row_pointers); 00821 00822 /* Transfer the data */ 00823 png_write_image(png_ptr, row_pointers); 00824 png_write_end(png_ptr, info_ptr); 00825 00826 if (cmflag) 00827 FREE(palette); 00828 FREE(row_pointers); 00829 pixDestroy(&pixt); 00830 png_destroy_write_struct(&png_ptr, &info_ptr); 00831 return 0; 00832 } 00833 00834 /* For rgb, compose and write a row at a time */ 00835 data = pixGetData(pix); 00836 wpl = pixGetWpl(pix); 00837 if (d == 24) { /* See note 7 above: special case of 24 bpp rgb */ 00838 for (i = 0; i < h; i++) { 00839 ppixel = data + i * wpl; 00840 png_write_rows(png_ptr, (png_bytepp)&ppixel, 1); 00841 } 00842 } 00843 else { /* 32 bpp rgb and rgba */ 00844 if ((rowbuffer = (png_bytep)CALLOC(w, 4)) == NULL) 00845 return ERROR_INT("rowbuffer not made", procName, 1); 00846 for (i = 0; i < h; i++) { 00847 ppixel = data + i * wpl; 00848 for (j = k = 0; j < w; j++) { 00849 rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_RED); 00850 rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_GREEN); 00851 rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_BLUE); 00852 if (var_PNG_WRITE_ALPHA == 1) 00853 rowbuffer[k++] = GET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL); 00854 ppixel++; 00855 } 00856 00857 png_write_rows(png_ptr, &rowbuffer, 1); 00858 } 00859 FREE(rowbuffer); 00860 } 00861 00862 png_write_end(png_ptr, info_ptr); 00863 00864 if (cmflag) 00865 FREE(palette); 00866 png_destroy_write_struct(&png_ptr, &info_ptr); 00867 return 0; 00868 00869 } 00870 00871 00872 /*---------------------------------------------------------------------* 00873 * Read and write of png to RGBA * 00874 *---------------------------------------------------------------------*/ 00875 extern const char *ImageFileFormatExtensions[]; 00876 00877 /*! 00878 * pixReadRGBAPng() 00879 * 00880 * Input: filename (of png file) 00881 * Return: pix, or null on error 00882 * 00883 * Notes: 00884 * (1) Wrapper to keep the alpha channel of a png, if it exists. 00885 * (2) The default behavior of pix read functions is to ignore 00886 * the alpha channel. 00887 * (3) This always leaves alpha stripping in the same mode as 00888 * when this function begins. So if alpha stripping is in 00889 * default mode, this disables it, reads the file (including 00890 * the alpha channel), and resets back to stripping. Otherwise, 00891 * it leaves stripping disabled. 00892 */ 00893 PIX * 00894 pixReadRGBAPng(const char *filename) 00895 { 00896 l_int32 format; 00897 PIX *pix; 00898 00899 PROCNAME("pixReadRGBAPng"); 00900 00901 if (!filename) 00902 return (PIX *)ERROR_PTR("filename not defined", procName, NULL); 00903 00904 /* Make sure it's a png file */ 00905 findFileFormat(filename, &format); 00906 if (format != IFF_PNG) { 00907 L_ERROR_STRING("file format is %s, not png", procName, 00908 ImageFileFormatExtensions[format]); 00909 return NULL; 00910 } 00911 00912 /* If alpha channel reading is enabled, just read it */ 00913 if (var_PNG_STRIP_ALPHA == FALSE) 00914 return pixRead(filename); 00915 00916 l_pngSetStripAlpha(0); 00917 pix = pixRead(filename); 00918 l_pngSetStripAlpha(1); /* reset to default */ 00919 if (!pix) L_ERROR("pix not read", procName); 00920 return pix; 00921 } 00922 00923 00924 /*! 00925 * pixWriteRGBAPng() 00926 * 00927 * Input: filename 00928 * pix (rgba) 00929 * Return: 0 if OK, 1 on error 00930 * 00931 * Notes: 00932 * (1) Wrapper to write the alpha sample of a 32 bpp pix to 00933 * a png file in rgba format. 00934 * (2) The default behavior of pix write to png is to ignore 00935 * the alpha sample. 00936 * (3) This always leaves alpha writing in the same mode as 00937 * when this function begins. So if alpha writing is in 00938 * default mode, this enables it, writes out a rgba png file 00939 * that includes the alpha channel, and resets to default. 00940 * Otherwise, it leaves alpha writing enabled. 00941 */ 00942 l_int32 00943 pixWriteRGBAPng(const char *filename, 00944 PIX *pix) 00945 { 00946 l_int32 ret; 00947 00948 PROCNAME("pixWriteRGBAPng"); 00949 00950 if (!pix) 00951 return ERROR_INT("pix not defined", procName, 1); 00952 if (!filename) 00953 return ERROR_INT("filename not defined", procName, 1); 00954 00955 /* If alpha channel writing is enabled, just write it */ 00956 if (var_PNG_WRITE_ALPHA == TRUE) 00957 return pixWrite(filename, pix, IFF_PNG); 00958 00959 l_pngSetWriteAlpha(1); 00960 ret = pixWritePng(filename, pix, 0.0); 00961 l_pngSetWriteAlpha(0); /* reset to default */ 00962 return ret; 00963 } 00964 00965 00966 /*---------------------------------------------------------------------* 00967 * Setting flags for special modes * 00968 *---------------------------------------------------------------------*/ 00969 /*! 00970 * l_pngSetStrip16To8() 00971 * 00972 * Input: flag (1 for stripping 16 bpp to 8 bpp; 0 for leaving 16 bpp) 00973 * Return: void 00974 */ 00975 void 00976 l_pngSetStrip16To8(l_int32 flag) 00977 { 00978 var_PNG_STRIP_16_TO_8 = flag; 00979 } 00980 00981 00982 /*! 00983 * l_pngSetStripAlpha() 00984 * 00985 * Input: flag (1 for stripping alpha channel on read; 0 for leaving it) 00986 * Return: void 00987 */ 00988 void 00989 l_pngSetStripAlpha(l_int32 flag) 00990 { 00991 var_PNG_STRIP_ALPHA = flag; 00992 } 00993 00994 00995 /*! 00996 * l_pngSetWriteAlpha() 00997 * 00998 * Input: flag (1 for writing alpha channel; 0 for just writing rgb) 00999 * Return: void 01000 */ 01001 void 01002 l_pngSetWriteAlpha(l_int32 flag) 01003 { 01004 var_PNG_WRITE_ALPHA = flag; 01005 } 01006 01007 01008 /*! 01009 * l_pngSetZlibCompression() 01010 * 01011 * Input: val (zlib compression value) 01012 * Return: void 01013 * 01014 * Notes: 01015 * (1) Valid zlib compression values are in the interval [0 ... 9], 01016 * where, as defined in zlib.h: 01017 * 0 Z_NO_COMPRESSION 01018 * 1 Z_BEST_SPEED (poorest compression) 01019 * 9 Z_BEST_COMPRESSION 01020 * For the default value, use either of these: 01021 * 6 Z_DEFAULT_COMPRESSION 01022 * -1 (resolves to Z_DEFAULT_COMPRESSION) 01023 * (2) If you use the defined constants in zlib.h instead of the 01024 * compression integers given above, you must include zlib.h. 01025 */ 01026 void 01027 l_pngSetZlibCompression(l_int32 val) 01028 { 01029 PROCNAME("l_pngSetZlibCompression"); 01030 if (val < -1 || val > 9) { 01031 L_ERROR("Invalid zlib comp val; using default", procName); 01032 val = Z_DEFAULT_COMPRESSION; 01033 } 01034 var_ZLIB_COMPRESSION = val; 01035 } 01036 01037 01038 01039 /*---------------------------------------------------------------------* 01040 * Read/write to memory * 01041 *---------------------------------------------------------------------*/ 01042 #if HAVE_FMEMOPEN 01043 01044 extern FILE *open_memstream(char **data, size_t *size); 01045 extern FILE *fmemopen(void *data, size_t size, const char *mode); 01046 01047 /*! 01048 * pixReadMemPng() 01049 * 01050 * Input: cdata (const; png-encoded) 01051 * size (of data) 01052 * Return: pix, or null on error 01053 * 01054 * Notes: 01055 * (1) The @size byte of @data must be a null character. 01056 */ 01057 PIX * 01058 pixReadMemPng(const l_uint8 *cdata, 01059 size_t size) 01060 { 01061 l_uint8 *data; 01062 FILE *fp; 01063 PIX *pix; 01064 01065 PROCNAME("pixReadMemPng"); 01066 01067 if (!cdata) 01068 return (PIX *)ERROR_PTR("cdata not defined", procName, NULL); 01069 01070 data = (l_uint8 *)cdata; /* we're really not going to change this */ 01071 if ((fp = fmemopen(data, size, "r")) == NULL) 01072 return (PIX *)ERROR_PTR("stream not opened", procName, NULL); 01073 pix = pixReadStreamPng(fp); 01074 fclose(fp); 01075 return pix; 01076 } 01077 01078 01079 /*! 01080 * pixWriteMemPng() 01081 * 01082 * Input: &data (<return> data of tiff compressed image) 01083 * &size (<return> size of returned data) 01084 * pix 01085 * gamma (use 0.0 if gamma is not defined) 01086 * Return: 0 if OK, 1 on error 01087 * 01088 * Notes: 01089 * (1) See pixWriteStreamPng() for usage. This version writes to 01090 * memory instead of to a file stream. 01091 */ 01092 l_int32 01093 pixWriteMemPng(l_uint8 **pdata, 01094 size_t *psize, 01095 PIX *pix, 01096 l_float32 gamma) 01097 { 01098 l_int32 ret; 01099 FILE *fp; 01100 01101 PROCNAME("pixWriteMemPng"); 01102 01103 if (!pdata) 01104 return ERROR_INT("&data not defined", procName, 1 ); 01105 if (!psize) 01106 return ERROR_INT("&size not defined", procName, 1 ); 01107 if (!pix) 01108 return ERROR_INT("&pix not defined", procName, 1 ); 01109 01110 if ((fp = open_memstream((char **)pdata, psize)) == NULL) 01111 return ERROR_INT("stream not opened", procName, 1); 01112 ret = pixWriteStreamPng(fp, pix, gamma); 01113 fclose(fp); 01114 return ret; 01115 } 01116 01117 #else 01118 01119 PIX * 01120 pixReadMemPng(const l_uint8 *cdata, 01121 size_t size) 01122 { 01123 return (PIX *)ERROR_PTR( 01124 "png read from memory not implemented on this platform", 01125 "pixReadMemPng", NULL); 01126 } 01127 01128 01129 l_int32 01130 pixWriteMemPng(l_uint8 **pdata, 01131 size_t *psize, 01132 PIX *pix, 01133 l_float32 gamma) 01134 { 01135 return ERROR_INT( 01136 "png write to memory not implemented on this platform", 01137 "pixWriteMemPng", 1); 01138 } 01139 01140 #endif /* HAVE_FMEMOPEN */ 01141 01142 /* --------------------------------------------*/ 01143 #endif /* HAVE_LIBPNG */ 01144 /* --------------------------------------------*/ 01145