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 * writefile.c 00018 * 00019 * High-level procedures for writing images to file: 00020 * l_int32 pixaWriteFiles() 00021 * l_int32 pixWrite() [behavior depends on WRITE_AS_NAMED] 00022 * l_int32 pixWriteStream() 00023 * l_int32 pixWriteImpliedFormat() 00024 * l_int32 pixWriteTempfile() 00025 * 00026 * Selection of output format if default is requested 00027 * l_int32 pixChooseOutputFormat() 00028 * l_int32 getImpliedFileFormat() 00029 * const char *getFormatExtension() 00030 * 00031 * Write to memory 00032 * l_int32 pixWriteMem() 00033 * 00034 * Image display for debugging 00035 * l_int32 pixDisplay() 00036 * l_int32 pixDisplayWithTitle() 00037 * l_int32 pixDisplayMultiple() 00038 * l_int32 pixDisplayWrite() 00039 * l_int32 pixDisplayWriteFormat() 00040 * l_int32 pixSaveTiled() 00041 * l_int32 pixSaveTiledOutline() 00042 * l_int32 pixSaveTiledWithText() 00043 * void l_chooseDisplayProg() 00044 */ 00045 00046 #include <string.h> 00047 #include "allheaders.h" 00048 00049 /* Special flag for pixWrite(). The default for both unix and */ 00050 /* windows is to use whatever filename is given, as opposed to */ 00051 /* insuring the filename extension matches the image compression. */ 00052 #define WRITE_AS_NAMED 1 00053 00054 /* MS VC++ can't handle array initialization with static consts ! */ 00055 #define L_BUF_SIZE 512 00056 00057 /* Display program (xv, xli or xzgv) to be invoked by pixDisplay() */ 00058 #ifdef _WIN32 00059 static l_int32 var_DISPLAY_PROG = L_DISPLAY_WITH_IV; /* default */ 00060 #else 00061 static l_int32 var_DISPLAY_PROG = L_DISPLAY_WITH_XZGV; /* default */ 00062 #endif /* _WIN32 */ 00063 static const l_int32 MAX_DISPLAY_WIDTH = 1000; 00064 static const l_int32 MAX_DISPLAY_HEIGHT = 800; 00065 static const l_int32 MAX_SIZE_FOR_PNG = 200; 00066 00067 /* PostScript output for printing */ 00068 static const l_float32 DEFAULT_SCALING = 1.0; 00069 00070 /* Global array of image file format extension names. */ 00071 /* This is in 1-1 corrspondence with format enum in imageio.h. */ 00072 /* The empty string at the end represents the serialized format, */ 00073 /* which has no recognizable extension name, but the array must */ 00074 /* be padded to agree with the format enum. */ 00075 /* (Note on 'const': The size of the array can't be defined 'const' */ 00076 /* because that makes it static. The 'const' in the definition of */ 00077 /* the array refers to the strings in the array; the ptr to the */ 00078 /* array is not const and can be used 'extern' in other files.) */ 00079 LEPT_DLL l_int32 NumImageFileFormatExtensions = 19; /* array size */ 00080 LEPT_DLL const char *ImageFileFormatExtensions[] = 00081 {"unknown", 00082 "bmp", 00083 "jpg", 00084 "png", 00085 "tif", 00086 "tif", 00087 "tif", 00088 "tif", 00089 "tif", 00090 "tif", 00091 "tif", 00092 "pnm", 00093 "ps", 00094 "gif", 00095 "jp2", 00096 "webp", 00097 "pdf", 00098 "default", 00099 ""}; 00100 00101 /* Local map of image file name extension to output format */ 00102 struct ExtensionMap 00103 { 00104 char extension[8]; 00105 l_int32 format; 00106 }; 00107 static const struct ExtensionMap extension_map[] = 00108 { { ".bmp", IFF_BMP }, 00109 { ".jpg", IFF_JFIF_JPEG }, 00110 { ".jpeg", IFF_JFIF_JPEG }, 00111 { ".png", IFF_PNG }, 00112 { ".tif", IFF_TIFF }, 00113 { ".tiff", IFF_TIFF }, 00114 { ".pnm", IFF_PNM }, 00115 { ".gif", IFF_GIF }, 00116 { ".jp2", IFF_JP2 }, 00117 { ".ps", IFF_PS }, 00118 { ".pdf", IFF_LPDF }, 00119 { ".webp", IFF_WEBP } }; 00120 00121 00122 /*---------------------------------------------------------------------* 00123 * Top-level procedures for writing images to file * 00124 *---------------------------------------------------------------------*/ 00125 /*! 00126 * pixaWriteFiles() 00127 * 00128 * Input: rootname 00129 * pixa 00130 * format (defined in imageio.h) 00131 * Return: 0 if OK; 1 on error 00132 */ 00133 l_int32 00134 pixaWriteFiles(const char *rootname, 00135 PIXA *pixa, 00136 l_int32 format) 00137 { 00138 char bigbuf[L_BUF_SIZE]; 00139 l_int32 i, n; 00140 PIX *pix; 00141 00142 PROCNAME("pixaWriteFiles"); 00143 00144 if (!rootname) 00145 return ERROR_INT("rootname not defined", procName, 1); 00146 if (!pixa) 00147 return ERROR_INT("pixa not defined", procName, 1); 00148 if (format < 0 || format >= NumImageFileFormatExtensions) 00149 return ERROR_INT("invalid format", procName, 1); 00150 00151 n = pixaGetCount(pixa); 00152 for (i = 0; i < n; i++) { 00153 snprintf(bigbuf, L_BUF_SIZE, "%s%03d.%s", rootname, i, 00154 ImageFileFormatExtensions[format]); 00155 pix = pixaGetPix(pixa, i, L_CLONE); 00156 pixWrite(bigbuf, pix, format); 00157 pixDestroy(&pix); 00158 } 00159 00160 return 0; 00161 } 00162 00163 00164 /*! 00165 * pixWrite() 00166 * 00167 * Input: filename 00168 * pix 00169 * format (defined in imageio.h) 00170 * Return: 0 if OK; 1 on error 00171 * 00172 * Notes: 00173 * (1) Open for write using binary mode (with the "b" flag) 00174 * to avoid having Windows automatically translate the NL 00175 * into CRLF, which corrupts image files. On non-windows 00176 * systems this flag should be ignored, per ISO C90. 00177 * Thanks to Dave Bryan for pointing this out. 00178 * (2) If the default image format is requested, we use the input format; 00179 * if the input format is unknown, a lossless format is assigned. 00180 * (3) There are two modes with respect to file naming. 00181 * (a) The default code writes to @filename. 00182 * (b) If WRITE_AS_NAMED is defined to 0, it's a bit fancier. 00183 * Then, if @filename does not have a file extension, one is 00184 * automatically appended, depending on the requested format. 00185 * The original intent for providing option (b) was to insure 00186 * that filenames on Windows have an extension that matches 00187 * the image compression. However, this is not the default. 00188 */ 00189 l_int32 00190 pixWrite(const char *filename, 00191 PIX *pix, 00192 l_int32 format) 00193 { 00194 char *fname; 00195 FILE *fp; 00196 00197 PROCNAME("pixWrite"); 00198 00199 if (!pix) 00200 return ERROR_INT("pix not defined", procName, 1); 00201 if (!filename) 00202 return ERROR_INT("filename not defined", procName, 1); 00203 if (format == IFF_JP2) 00204 return ERROR_INT("jp2 not supported", procName, 1); 00205 00206 fname = genPathname(filename, NULL); 00207 00208 #if WRITE_AS_NAMED /* Default */ 00209 00210 if ((fp = fopen(fname, "wb+")) == NULL) { 00211 FREE(fname); 00212 return ERROR_INT("stream not opened", procName, 1); 00213 } 00214 00215 #else /* Add an extension to the output name if none exists */ 00216 00217 {l_int32 extlen; 00218 char *extension, *filebuf; 00219 splitPathAtExtension(fname, NULL, &extension); 00220 extlen = strlen(extension); 00221 FREE(extension); 00222 if (extlen == 0) { 00223 if (format == IFF_DEFAULT || format == IFF_UNKNOWN) 00224 format = pixChooseOutputFormat(pix); 00225 00226 filebuf = (char *)CALLOC(strlen(fname) + 10, sizeof(char)); 00227 if (!filebuf) { 00228 return ERROR_INT("filebuf not made", procName, 1); 00229 FREE(fname); 00230 } 00231 strncpy(filebuf, fname, strlen(fname)); 00232 strcat(filebuf, "."); 00233 strcat(filebuf, ImageFileFormatExtensions[format]); 00234 } 00235 else 00236 filebuf = (char *)fname; 00237 00238 fp = fopen(filebuf, "wb+"); 00239 if (filebuf != fname) 00240 FREE(filebuf); 00241 if (fp == NULL) { 00242 FREE(fname); 00243 return ERROR_INT("stream not opened", procName, 1); 00244 } 00245 } 00246 00247 #endif /* WRITE_AS_NAMED */ 00248 00249 FREE(fname); 00250 if (pixWriteStream(fp, pix, format)) { 00251 fclose(fp); 00252 return ERROR_INT("pix not written to stream", procName, 1); 00253 } 00254 00255 /* Close the stream except if GIF under windows, because 00256 * EGifCloseFile() closes the windows file stream! */ 00257 if (format != IFF_GIF) 00258 fclose(fp); 00259 #ifndef _WIN32 00260 else /* gif file */ 00261 fclose(fp); 00262 #endif /* ! _WIN32 */ 00263 00264 return 0; 00265 } 00266 00267 00268 /*! 00269 * pixWriteStream() 00270 * 00271 * Input: stream 00272 * pix 00273 * format 00274 * Return: 0 if OK; 1 on error. 00275 */ 00276 l_int32 00277 pixWriteStream(FILE *fp, 00278 PIX *pix, 00279 l_int32 format) 00280 { 00281 PROCNAME("pixWriteStream"); 00282 00283 if (!fp) 00284 return ERROR_INT("stream not defined", procName, 1); 00285 if (!pix) 00286 return ERROR_INT("pix not defined", procName, 1); 00287 00288 if (format == IFF_DEFAULT) 00289 format = pixChooseOutputFormat(pix); 00290 00291 switch(format) 00292 { 00293 case IFF_BMP: 00294 pixWriteStreamBmp(fp, pix); 00295 break; 00296 00297 case IFF_JFIF_JPEG: /* default quality; baseline sequential */ 00298 return pixWriteStreamJpeg(fp, pix, 75, 0); 00299 break; 00300 00301 case IFF_PNG: /* no gamma value stored */ 00302 return pixWriteStreamPng(fp, pix, 0.0); 00303 break; 00304 00305 case IFF_TIFF: /* uncompressed */ 00306 case IFF_TIFF_PACKBITS: /* compressed, binary only */ 00307 case IFF_TIFF_RLE: /* compressed, binary only */ 00308 case IFF_TIFF_G3: /* compressed, binary only */ 00309 case IFF_TIFF_G4: /* compressed, binary only */ 00310 case IFF_TIFF_LZW: /* compressed, all depths */ 00311 case IFF_TIFF_ZIP: /* compressed, all depths */ 00312 return pixWriteStreamTiff(fp, pix, format); 00313 break; 00314 00315 case IFF_PNM: 00316 return pixWriteStreamPnm(fp, pix); 00317 break; 00318 00319 case IFF_GIF: 00320 return pixWriteStreamGif(fp, pix); 00321 break; 00322 00323 case IFF_PS: 00324 return pixWriteStreamPS(fp, pix, NULL, 0, DEFAULT_SCALING); 00325 break; 00326 00327 case IFF_JP2: 00328 return ERROR_INT("jp2 format not supported", procName, 1); 00329 break; 00330 00331 case IFF_WEBP: 00332 return pixWriteStreamWebP(fp, pix, 80); 00333 break; 00334 00335 case IFF_LPDF: 00336 return pixWriteStreamPdf(fp, pix, 0, NULL); 00337 break; 00338 00339 case IFF_SPIX: 00340 return pixWriteStreamSpix(fp, pix); 00341 break; 00342 00343 default: 00344 return ERROR_INT("unknown format", procName, 1); 00345 break; 00346 } 00347 00348 return 0; 00349 } 00350 00351 00352 /*! 00353 * pixWriteImpliedFormat() 00354 * 00355 * Input: filename 00356 * pix 00357 * quality (iff JPEG; 1 - 100, 0 for default) 00358 * progressive (iff JPEG; 0 for baseline seq., 1 for progressive) 00359 * Return: 0 if OK; 1 on error 00360 * 00361 * Notes: 00362 * (1) This determines the output format from the filename extension. 00363 * (2) The last two args are ignored except for requests for jpeg files. 00364 * (3) The jpeg default quality is 75. 00365 */ 00366 l_int32 00367 pixWriteImpliedFormat(const char *filename, 00368 PIX *pix, 00369 l_int32 quality, 00370 l_int32 progressive) 00371 { 00372 l_int32 format; 00373 00374 PROCNAME("pixWriteImpliedFormat"); 00375 00376 if (!filename) 00377 return ERROR_INT("filename not defined", procName, 1); 00378 if (!pix) 00379 return ERROR_INT("pix not defined", procName, 1); 00380 00381 /* Determine output format */ 00382 format = getImpliedFileFormat(filename); 00383 if (format == IFF_UNKNOWN) 00384 format = IFF_PNG; 00385 else if (format == IFF_TIFF) { 00386 if (pixGetDepth(pix) == 1) 00387 format = IFF_TIFF_G4; 00388 else 00389 #ifdef _WIN32 00390 format = IFF_TIFF_LZW; /* poor compression */ 00391 #else 00392 format = IFF_TIFF_ZIP; /* native windows tools can't handle this */ 00393 #endif /* _WIN32 */ 00394 } 00395 00396 if (format == IFF_JFIF_JPEG) { 00397 quality = L_MIN(quality, 100); 00398 quality = L_MAX(quality, 0); 00399 if (progressive != 0 && progressive != 1) { 00400 progressive = 0; 00401 L_WARNING("invalid progressive; setting to baseline", procName); 00402 } 00403 if (quality == 0) 00404 quality = 75; 00405 pixWriteJpeg (filename, pix, quality, progressive); 00406 } 00407 else 00408 pixWrite(filename, pix, format); 00409 00410 return 0; 00411 } 00412 00413 00414 /*! 00415 * pixWriteTempfile() 00416 * 00417 * Input: dir (directory name; use '.' for local dir; no trailing '/') 00418 * tail (<optional> tailname, including extension if any) 00419 * pix 00420 * format 00421 * &filename (<optional> return actual filename used; use 00422 * null to skip) 00423 * Return: 0 if OK; 1 on error 00424 * 00425 * Notes: 00426 * (1) This generates a temp filename, writes the pix to it, 00427 * and optionally returns the temp filename. 00428 * (2) If the filename is returned to a windows program from a DLL, 00429 * use lept_free() to free it. 00430 * (3) See genTempFilename() for details. We omit the time and pid 00431 * here. 00432 */ 00433 l_int32 00434 pixWriteTempfile(const char *dir, 00435 const char *tail, 00436 PIX *pix, 00437 l_int32 format, 00438 char **pfilename) 00439 { 00440 char *filename; 00441 l_int32 ret; 00442 00443 PROCNAME("pixWriteTempfile"); 00444 00445 if (!dir) 00446 return ERROR_INT("filename not defined", procName, 1); 00447 if (!pix) 00448 return ERROR_INT("pix not defined", procName, 1); 00449 00450 if ((filename = genTempFilename(dir, tail, 0, 0)) == NULL) 00451 return ERROR_INT("temp filename not made", procName, 1); 00452 00453 ret = pixWrite(filename, pix, format); 00454 if (pfilename) 00455 *pfilename = filename; 00456 else 00457 FREE(filename); 00458 00459 return ret; 00460 } 00461 00462 00463 /*---------------------------------------------------------------------* 00464 * Selection of output format if default is requested * 00465 *---------------------------------------------------------------------*/ 00466 /*! 00467 * pixChooseOutputFormat() 00468 * 00469 * Input: pix 00470 * Return: output format, or 0 on error 00471 * 00472 * Notes: 00473 * (1) This should only be called if the requested format is IFF_DEFAULT. 00474 * (2) If the pix wasn't read from a file, its input format value 00475 * will be IFF_UNKNOWN, and in that case it is written out 00476 * in a compressed but lossless format. 00477 */ 00478 l_int32 00479 pixChooseOutputFormat(PIX *pix) 00480 { 00481 l_int32 d, format; 00482 00483 PROCNAME("pixChooseOutputFormat"); 00484 00485 if (!pix) 00486 return ERROR_INT("pix not defined", procName, 0); 00487 00488 d = pixGetDepth(pix); 00489 format = pixGetInputFormat(pix); 00490 if (format == IFF_UNKNOWN) { /* output lossless */ 00491 if (d == 1) 00492 format = IFF_TIFF_G4; 00493 else 00494 format = IFF_PNG; 00495 } 00496 00497 return format; 00498 } 00499 00500 00501 /*! 00502 * getImpliedFileFormat() 00503 * 00504 * Input: filename 00505 * Return: output format, or IFF_UNKNOWN on error or invalid extension. 00506 * 00507 * Notes: 00508 * (1) This determines the output file format from the extension 00509 * of the input filename. 00510 */ 00511 l_int32 00512 getImpliedFileFormat(const char *filename) 00513 { 00514 char *extension; 00515 int i, numext; 00516 l_int32 format = IFF_UNKNOWN; 00517 00518 if (splitPathAtExtension (filename, NULL, &extension)) 00519 return IFF_UNKNOWN; 00520 00521 numext = sizeof(extension_map) / sizeof(extension_map[0]); 00522 for (i = 0; i < numext; i++) { 00523 if (!strcmp(extension, extension_map[i].extension)) { 00524 format = extension_map[i].format; 00525 break; 00526 } 00527 } 00528 00529 FREE(extension); 00530 return format; 00531 } 00532 00533 00534 /*! 00535 * getFormatExtension() 00536 * 00537 * Input: format (integer) 00538 * Return: extension (string), or null if format is out of range 00539 * 00540 * Notes: 00541 * (1) This string is NOT owned by the caller; it is just a pointer 00542 * to a global string. Do not free it. 00543 */ 00544 const char * 00545 getFormatExtension(l_int32 format) 00546 { 00547 PROCNAME("getFormatExtension"); 00548 00549 if (format < 0 || format >= NumImageFileFormatExtensions) 00550 return (const char *)ERROR_PTR("invalid format", procName, NULL); 00551 00552 return ImageFileFormatExtensions[format]; 00553 } 00554 00555 00556 /*---------------------------------------------------------------------* 00557 * Write to memory * 00558 *---------------------------------------------------------------------*/ 00559 /*! 00560 * pixWriteMem() 00561 * 00562 * Input: &data (<return> data of tiff compressed image) 00563 * &size (<return> size of returned data) 00564 * pix 00565 * format (defined in imageio.h) 00566 * Return: 0 if OK, 1 on error 00567 * 00568 * Notes: 00569 * (1) On windows, this will only write tiff and PostScript to memory. 00570 * For other formats, it requires open_memstream(3). 00571 * (2) PostScript output is uncompressed, in hex ascii. 00572 * Most printers support level 2 compression (tiff_g4 for 1 bpp, 00573 * jpeg for 8 and 32 bpp). 00574 */ 00575 l_int32 00576 pixWriteMem(l_uint8 **pdata, 00577 size_t *psize, 00578 PIX *pix, 00579 l_int32 format) 00580 { 00581 l_int32 ret; 00582 00583 PROCNAME("pixWriteMem"); 00584 00585 if (!pdata) 00586 return ERROR_INT("&data not defined", procName, 1 ); 00587 if (!psize) 00588 return ERROR_INT("&size not defined", procName, 1 ); 00589 if (!pix) 00590 return ERROR_INT("&pix not defined", procName, 1 ); 00591 00592 if (format == IFF_DEFAULT) 00593 format = pixChooseOutputFormat(pix); 00594 00595 switch(format) 00596 { 00597 case IFF_BMP: 00598 ret = pixWriteMemBmp(pdata, psize, pix); 00599 break; 00600 00601 case IFF_JFIF_JPEG: /* default quality; baseline sequential */ 00602 ret = pixWriteMemJpeg(pdata, psize, pix, 75, 0); 00603 break; 00604 00605 case IFF_PNG: /* no gamma value stored */ 00606 ret = pixWriteMemPng(pdata, psize, pix, 0.0); 00607 break; 00608 00609 case IFF_TIFF: /* uncompressed */ 00610 case IFF_TIFF_PACKBITS: /* compressed, binary only */ 00611 case IFF_TIFF_RLE: /* compressed, binary only */ 00612 case IFF_TIFF_G3: /* compressed, binary only */ 00613 case IFF_TIFF_G4: /* compressed, binary only */ 00614 case IFF_TIFF_LZW: /* compressed, all depths */ 00615 case IFF_TIFF_ZIP: /* compressed, all depths */ 00616 ret = pixWriteMemTiff(pdata, psize, pix, format); 00617 break; 00618 00619 case IFF_PNM: 00620 ret = pixWriteMemPnm(pdata, psize, pix); 00621 break; 00622 00623 case IFF_PS: 00624 ret = pixWriteMemPS(pdata, psize, pix, NULL, 0, DEFAULT_SCALING); 00625 break; 00626 00627 case IFF_GIF: 00628 ret = pixWriteMemGif(pdata, psize, pix); 00629 break; 00630 00631 case IFF_JP2: 00632 return ERROR_INT("jp2 not supported", procName, 1); 00633 break; 00634 00635 case IFF_SPIX: 00636 ret = pixWriteMemSpix(pdata, psize, pix); 00637 break; 00638 00639 default: 00640 return ERROR_INT("unknown format", procName, 1); 00641 break; 00642 } 00643 00644 return ret; 00645 } 00646 00647 00648 /*---------------------------------------------------------------------* 00649 * Image display for debugging * 00650 *---------------------------------------------------------------------*/ 00651 /*! 00652 * pixDisplay() 00653 * 00654 * Input: pix (1, 2, 4, 8, 16, 32 bpp) 00655 * x, y (location of display frame on the screen) 00656 * Return: 0 if OK; 1 on error 00657 * 00658 * Notes: 00659 * (1) This displays the image using xv, xli or xzgv on Unix, 00660 * or i_view on Windows. The display program must be on 00661 * your $PATH variable. It is chosen by setting the global 00662 * var_DISPLAY_PROG, using l_chooseDisplayProg(). 00663 * Default on Unix is xv. 00664 * (2) Images with dimensions larger than MAX_DISPLAY_WIDTH or 00665 * MAX_DISPLAY_HEIGHT are downscaled to fit those constraints. 00666 * This is particulary important for displaying 1 bpp images 00667 * with xv, because xv automatically downscales large images 00668 * by subsampling, which looks lousy. For 1 bpp, we use 00669 * scale-to-gray to get decent-looking anti-aliased images. 00670 * In all cases, we write a temporary file to /tmp, that is 00671 * read by the display program. 00672 * (3) Note: this function uses a static internal variable to number 00673 * output files written by a single process. Behavior with a 00674 * shared library may be unpredictable. 00675 */ 00676 l_int32 00677 pixDisplay(PIX *pixs, 00678 l_int32 x, 00679 l_int32 y) 00680 { 00681 return pixDisplayWithTitle(pixs, x, y, NULL, 1); 00682 } 00683 00684 00685 /*! 00686 * pixDisplayWithTitle() 00687 * 00688 * Input: pix (1, 2, 4, 8, 16, 32 bpp) 00689 * x, y (location of display frame) 00690 * title (<optional> on frame; can be NULL); 00691 * dispflag (1 to write, else disabled) 00692 * Return: 0 if OK; 1 on error 00693 * 00694 * Notes: 00695 * (1) See notes for pixDisplay(). 00696 * (2) This displays the image if dispflag == 1. 00697 */ 00698 l_int32 00699 pixDisplayWithTitle(PIX *pixs, 00700 l_int32 x, 00701 l_int32 y, 00702 const char *title, 00703 l_int32 dispflag) 00704 { 00705 char *tempname; 00706 char buffer[L_BUF_SIZE]; 00707 static l_int32 index = 0; /* caution: not .so or thread safe */ 00708 l_int32 w, h, d, ignore; 00709 l_float32 ratw, rath, ratmin; 00710 PIX *pixt; 00711 #ifndef _WIN32 00712 l_int32 wt, ht; 00713 #else 00714 char *pathname; 00715 char fullpath[_MAX_PATH]; 00716 #endif /* _WIN32 */ 00717 00718 PROCNAME("pixDisplayWithTitle"); 00719 00720 if (dispflag != 1) return 0; 00721 if (!pixs) 00722 return ERROR_INT("pixs not defined", procName, 1); 00723 if (var_DISPLAY_PROG != L_DISPLAY_WITH_XV && 00724 var_DISPLAY_PROG != L_DISPLAY_WITH_XLI && 00725 var_DISPLAY_PROG != L_DISPLAY_WITH_XZGV && 00726 var_DISPLAY_PROG != L_DISPLAY_WITH_IV) 00727 return ERROR_INT("no program chosen for display", procName, 1); 00728 00729 pixGetDimensions(pixs, &w, &h, &d); 00730 if (w <= MAX_DISPLAY_WIDTH && h <= MAX_DISPLAY_HEIGHT) { 00731 if (d == 16) /* take MSB */ 00732 pixt = pixConvert16To8(pixs, 1); 00733 else 00734 pixt = pixClone(pixs); 00735 } 00736 else { 00737 ratw = (l_float32)MAX_DISPLAY_WIDTH / (l_float32)w; 00738 rath = (l_float32)MAX_DISPLAY_HEIGHT / (l_float32)h; 00739 ratmin = L_MIN(ratw, rath); 00740 if (ratmin < 0.125 && d == 1) 00741 pixt = pixScaleToGray8(pixs); 00742 else if (ratmin < 0.25 && d == 1) 00743 pixt = pixScaleToGray4(pixs); 00744 else if (ratmin < 0.33 && d == 1) 00745 pixt = pixScaleToGray3(pixs); 00746 else if (ratmin < 0.5 && d == 1) 00747 pixt = pixScaleToGray2(pixs); 00748 else 00749 pixt = pixScale(pixs, ratmin, ratmin); 00750 if (!pixt) 00751 return ERROR_INT("pixt not made", procName, 1); 00752 } 00753 00754 if (index == 0) { 00755 lept_rmdir("display"); 00756 lept_mkdir("display"); 00757 } 00758 00759 index++; 00760 if (pixGetDepth(pixt) < 8 || 00761 (w < MAX_SIZE_FOR_PNG && h < MAX_SIZE_FOR_PNG)) { 00762 snprintf(buffer, L_BUF_SIZE, "/tmp/display/write.%03d.png", index); 00763 pixWrite(buffer, pixt, IFF_PNG); 00764 } 00765 else { 00766 snprintf(buffer, L_BUF_SIZE, "/tmp/display/write.%03d.jpg", index); 00767 pixWrite(buffer, pixt, IFF_JFIF_JPEG); 00768 } 00769 tempname = stringNew(buffer); 00770 00771 #ifndef _WIN32 00772 00773 /* Unix */ 00774 if (var_DISPLAY_PROG == L_DISPLAY_WITH_XV) { 00775 if (title) 00776 snprintf(buffer, L_BUF_SIZE, 00777 "xv -quit -geometry +%d+%d -name \"%s\" %s &", 00778 x, y, title, tempname); 00779 else 00780 snprintf(buffer, L_BUF_SIZE, 00781 "xv -quit -geometry +%d+%d %s &", x, y, tempname); 00782 } 00783 else if (var_DISPLAY_PROG == L_DISPLAY_WITH_XLI) { 00784 if (title) 00785 snprintf(buffer, L_BUF_SIZE, 00786 "xli -dispgamma 1.0 -quiet -geometry +%d+%d -title \"%s\" %s &", 00787 x, y, title, tempname); 00788 else 00789 snprintf(buffer, L_BUF_SIZE, 00790 "xli -dispgamma 1.0 -quiet -geometry +%d+%d %s &", 00791 x, y, tempname); 00792 } 00793 else if (var_DISPLAY_PROG == L_DISPLAY_WITH_XZGV) { 00794 /* no way to display title */ 00795 pixGetDimensions(pixt, &wt, &ht, NULL); 00796 snprintf(buffer, L_BUF_SIZE, 00797 "xzgv --geometry %dx%d+%d+%d %s &", wt + 10, ht + 10, 00798 x, y, tempname); 00799 } 00800 ignore = system(buffer); 00801 00802 #else /* _WIN32 */ 00803 00804 /* Windows: L_DISPLAY_WITH_IV */ 00805 pathname = genPathname(tempname, NULL); 00806 _fullpath(fullpath, pathname, sizeof(fullpath)); 00807 if (title) 00808 snprintf(buffer, L_BUF_SIZE, 00809 "i_view32.exe \"%s\" /pos=(%d,%d) /title=\"%s\"", 00810 fullpath, x, y, title); 00811 else 00812 snprintf(buffer, L_BUF_SIZE, "i_view32.exe \"%s\" /pos=(%d,%d)", 00813 fullpath, x, y); 00814 ignore = system(buffer); 00815 FREE(pathname); 00816 00817 #endif /* _WIN32 */ 00818 00819 pixDestroy(&pixt); 00820 FREE(tempname); 00821 return 0; 00822 } 00823 00824 00825 /*! 00826 * pixDisplayMultiple() 00827 * 00828 * Input: filepattern 00829 * Return: 0 if OK; 1 on error 00830 * 00831 * Notes: 00832 * (1) This allows display of multiple images using gthumb on unix 00833 * and i_view32 on windows. The @filepattern is a regular 00834 * expression that is expanded by the shell. 00835 * (2) _fullpath automatically changes '/' to '\' if necessary. 00836 */ 00837 l_int32 00838 pixDisplayMultiple(const char *filepattern) 00839 { 00840 char buffer[L_BUF_SIZE]; 00841 l_int32 ignore; 00842 #ifdef _WIN32 00843 char *pathname; 00844 char *dir, *tail; 00845 char fullpath[_MAX_PATH]; 00846 #endif /* _WIN32 */ 00847 00848 PROCNAME("pixDisplayMultiple"); 00849 00850 if (!filepattern || strlen(filepattern) == 0) 00851 return ERROR_INT("filepattern not defined", procName, 1); 00852 00853 #ifndef _WIN32 00854 snprintf(buffer, L_BUF_SIZE, "gthumb %s &", filepattern); 00855 #else 00856 /* irFanView wants absolute path for directory */ 00857 pathname = genPathname(filepattern, NULL); 00858 splitPathAtDirectory(pathname, &dir, &tail); 00859 _fullpath(fullpath, dir, sizeof(fullpath)); 00860 00861 snprintf(buffer, L_BUF_SIZE, 00862 "i_view32.exe \"%s\" /filepattern=\"%s\" /thumbs", fullpath, tail); 00863 FREE(pathname); 00864 FREE(dir); 00865 FREE(tail); 00866 #endif /* _WIN32 */ 00867 00868 ignore = system(buffer); 00869 return 0; 00870 } 00871 00872 00873 /*! 00874 * pixDisplayWrite() 00875 * 00876 * Input: pix (1, 2, 4, 8, 16, 32 bpp) 00877 * reduction (-1 to reset/erase; 0 to disable; 00878 * otherwise this is a reduction factor) 00879 * Return: 0 if OK; 1 on error 00880 * 00881 * Notes: 00882 * (1) This defaults to jpeg output for pix that are 32 bpp or 00883 * 8 bpp without a colormap. If you want to write all images 00884 * losslessly, use format == IFF_PNG in pixDisplayWriteFormat(). 00885 * (2) See pixDisplayWriteFormat() for usage details. 00886 */ 00887 l_int32 00888 pixDisplayWrite(PIX *pixs, 00889 l_int32 reduction) 00890 { 00891 return pixDisplayWriteFormat(pixs, reduction, IFF_JFIF_JPEG); 00892 } 00893 00894 00895 /*! 00896 * pixDisplayWriteFormat() 00897 * 00898 * Input: pix (1, 2, 4, 8, 16, 32 bpp) 00899 * reduction (-1 to reset/erase; 0 to disable; 00900 * otherwise this is a reduction factor) 00901 * format (IFF_PNG or IFF_JFIF_JPEG) 00902 * Return: 0 if OK; 1 on error 00903 * 00904 * Notes: 00905 * (1) This writes files if reduction > 0. These can be displayed using 00906 * pixDisplayMultiple("/tmp/junk_write_display*"); 00907 * (2) All previously written files can be erased by calling with 00908 * reduction < 0; the value of pixs is ignored. 00909 * (3) If reduction > 1 and depth == 1, this does a scale-to-gray 00910 * reduction. 00911 * (4) This function uses a static internal variable to number 00912 * output files written by a single process. Behavior 00913 * with a shared library may be unpredictable. 00914 * (5) Output file format is as follows: 00915 * format == IFF_JFIF_JPEG: 00916 * png if d < 8 or d == 16 or if the output pix 00917 * has a colormap. Otherwise, output is jpg. 00918 * format == IFF_PNG: 00919 * png (lossless) on all images. 00920 * (6) For 16 bpp, the choice of full dynamic range with log scale 00921 * is the best for displaying these images. Alternative outputs are 00922 * pix8 = pixMaxDynamicRange(pixt, L_LINEAR_SCALE); 00923 * pix8 = pixConvert16To8(pixt, 0); // low order byte 00924 * pix8 = pixConvert16To8(pixt, 1); // high order byte 00925 */ 00926 l_int32 00927 pixDisplayWriteFormat(PIX *pixs, 00928 l_int32 reduction, 00929 l_int32 format) 00930 { 00931 char buffer[L_BUF_SIZE]; 00932 l_int32 ignore; 00933 l_float32 scale; 00934 PIX *pixt, *pix8; 00935 static l_int32 index = 0; /* caution: not .so or thread safe */ 00936 00937 PROCNAME("pixDisplayWriteFormat"); 00938 00939 if (reduction == 0) return 0; 00940 00941 if (reduction < 0) { 00942 index = 0; /* reset; this will cause erasure at next call to write */ 00943 return 0; 00944 } 00945 00946 if (format != IFF_JFIF_JPEG && format != IFF_PNG) 00947 return ERROR_INT("invalid format", procName, 1); 00948 if (!pixs) 00949 return ERROR_INT("pixs not defined", procName, 1); 00950 00951 if (index == 0) { 00952 snprintf(buffer, L_BUF_SIZE, 00953 "rm -f /tmp/junk_write_display.*.png /tmp/junk_write_display.*.jpg"); 00954 ignore = system(buffer); 00955 } 00956 index++; 00957 00958 if (reduction == 1) 00959 pixt = pixClone(pixs); 00960 else { 00961 scale = 1. / (l_float32)reduction; 00962 if (pixGetDepth(pixs) == 1) 00963 pixt = pixScaleToGray(pixs, scale); 00964 else 00965 pixt = pixScale(pixs, scale, scale); 00966 } 00967 00968 if (pixGetDepth(pixt) == 16) { 00969 pix8 = pixMaxDynamicRange(pixt, L_LOG_SCALE); 00970 snprintf(buffer, L_BUF_SIZE, "/tmp/junk_write_display.%03d.png", index); 00971 pixWrite(buffer, pix8, IFF_PNG); 00972 pixDestroy(&pix8); 00973 } 00974 else if (pixGetDepth(pixt) < 8 || pixGetColormap(pixt) || 00975 format == IFF_PNG) { 00976 snprintf(buffer, L_BUF_SIZE, "/tmp/junk_write_display.%03d.png", index); 00977 pixWrite(buffer, pixt, IFF_PNG); 00978 } 00979 else { 00980 snprintf(buffer, L_BUF_SIZE, "/tmp/junk_write_display.%03d.jpg", index); 00981 pixWrite(buffer, pixt, format); 00982 } 00983 pixDestroy(&pixt); 00984 00985 return 0; 00986 } 00987 00988 00989 /*! 00990 * pixSaveTiled() 00991 * 00992 * Input: pixs (1, 2, 4, 8, 32 bpp) 00993 * pixa (the pix are accumulated here) 00994 * reduction (0 to disable; otherwise this is a reduction factor) 00995 * newrow (0 if placed on the same row as previous; 1 otherwise) 00996 * space (horizontal and vertical spacing, in pixels) 00997 * dp (depth of pixa; 8 or 32 bpp; only used on first call) 00998 * Return: 0 if OK, 1 on error. 00999 */ 01000 l_int32 01001 pixSaveTiled(PIX *pixs, 01002 PIXA *pixa, 01003 l_int32 reduction, 01004 l_int32 newrow, 01005 l_int32 space, 01006 l_int32 dp) 01007 { 01008 /* Save without an outline */ 01009 return pixSaveTiledOutline(pixs, pixa, reduction, newrow, space, 0, dp); 01010 } 01011 01012 01013 /*! 01014 * pixSaveTiledOutline() 01015 * 01016 * Input: pixs (1, 2, 4, 8, 32 bpp) 01017 * pixa (the pix are accumulated here) 01018 * reduction (0 to disable; otherwise this is a reduction factor) 01019 * newrow (0 if placed on the same row as previous; 1 otherwise) 01020 * space (horizontal and vertical spacing, in pixels) 01021 * linewidth (width of added outline for image; 0 for no outline) 01022 * dp (depth of pixa; 8 or 32 bpp; only used on first call) 01023 * Return: 0 if OK, 1 on error. 01024 * 01025 * Notes: 01026 * (1) Before calling this function for the first time, use 01027 * pixaCreate() to make the @pixa that will accumulate the pix. 01028 * This is passed in each time pixSaveTiled() is called. 01029 * (2) @reduction is the integer reduction factor for the input 01030 * image. After reduction and possible depth conversion, 01031 * the image is saved in the input pixa, along with a box 01032 * that specifies the location to place it when tiled later. 01033 * Disable saving the pix by setting reduction == 0. 01034 * (3) @newrow and @space specify the location of the new pix 01035 * with respect to the last one(s) that were entered. 01036 * (4) @dp specifies the depth at which all pix are saved. It can 01037 * be only 8 or 32 bpp. Any colormap is removed. This is only 01038 * used at the first invocation. 01039 * (5) This function uses two variables from call to call. 01040 * If they were static, the function would not be .so or thread 01041 * safe, and furthermore, there would be interference with two or 01042 * more pixa accumulating images at a time. Consequently, 01043 * we use the first pix in the pixa to store and obtain both 01044 * the depth and the current position of the bottom (one pixel 01045 * below the lowest image raster line when laid out using 01046 * the boxa). The bottom variable is stored in the input format 01047 * field, which is the only field available for storing an int. 01048 */ 01049 l_int32 01050 pixSaveTiledOutline(PIX *pixs, 01051 PIXA *pixa, 01052 l_int32 reduction, 01053 l_int32 newrow, 01054 l_int32 space, 01055 l_int32 linewidth, 01056 l_int32 dp) 01057 { 01058 l_int32 n, top, left, bx, by, bw, w, h, depth, bottom; 01059 l_float32 scale; 01060 BOX *box; 01061 PIX *pix, *pixt1, *pixt2, *pixt3; 01062 01063 PROCNAME("pixSaveTiledOutline"); 01064 01065 if (reduction == 0) return 0; 01066 01067 if (!pixs) 01068 return ERROR_INT("pixs not defined", procName, 1); 01069 if (!pixa) 01070 return ERROR_INT("pixa not defined", procName, 1); 01071 01072 n = pixaGetCount(pixa); 01073 if (n == 0) { 01074 bottom = 0; 01075 if (dp != 8 && dp != 32) { 01076 L_WARNING("dp not 8 or 32 bpp; using 32", procName); 01077 depth = 32; 01078 } else 01079 depth = dp; 01080 } 01081 else { /* extract the depth and bottom params from the first pix */ 01082 pix = pixaGetPix(pixa, 0, L_CLONE); 01083 depth = pixGetDepth(pix); 01084 bottom = pixGetInputFormat(pix); /* not typical usage! */ 01085 pixDestroy(&pix); 01086 } 01087 01088 /* Scale and convert to output depth */ 01089 if (reduction == 1) 01090 pixt1 = pixClone(pixs); 01091 else { 01092 scale = 1. / (l_float32)reduction; 01093 if (pixGetDepth(pixs) == 1) 01094 pixt1 = pixScaleToGray(pixs, scale); 01095 else 01096 pixt1 = pixScale(pixs, scale, scale); 01097 } 01098 if (depth == 8) 01099 pixt2 = pixConvertTo8(pixt1, 0); 01100 else 01101 pixt2 = pixConvertTo32(pixt1); 01102 pixDestroy(&pixt1); 01103 01104 /* Add black outline */ 01105 if (linewidth > 0) 01106 pixt3 = pixAddBorder(pixt2, linewidth, 0); 01107 else 01108 pixt3 = pixClone(pixt2); 01109 pixDestroy(&pixt2); 01110 01111 /* Find position of current pix (UL corner plus size) */ 01112 if (n == 0) { 01113 top = 0; 01114 left = 0; 01115 } 01116 else if (newrow == 1) { 01117 top = bottom + space; 01118 left = 0; 01119 } 01120 else if (n > 0) { 01121 pixaGetBoxGeometry(pixa, n - 1, &bx, &by, &bw, NULL); 01122 top = by; 01123 left = bx + bw + space; 01124 } 01125 01126 pixGetDimensions(pixt3, &w, &h, NULL); 01127 bottom = L_MAX(bottom, top + h); 01128 box = boxCreate(left, top, w, h); 01129 pixaAddPix(pixa, pixt3, L_INSERT); 01130 pixaAddBox(pixa, box, L_INSERT); 01131 01132 /* Save the new bottom value */ 01133 pix = pixaGetPix(pixa, 0, L_CLONE); 01134 pixSetInputFormat(pix, bottom); /* not typical usage! */ 01135 pixDestroy(&pix); 01136 01137 return 0; 01138 } 01139 01140 01141 /*! 01142 * pixSaveTiledWithText() 01143 * 01144 * Input: pixs (1, 2, 4, 8, 32 bpp) 01145 * pixa (the pix are accumulated here; as 32 bpp) 01146 * outwidth (in pixels; use 0 to disable entirely) 01147 * newrow (1 to start a new row; 0 to go on same row as previous) 01148 * space (horizontal and vertical spacing, in pixels) 01149 * linewidth (width of added outline for image; 0 for no outline) 01150 * bmf (<optional> font struct) 01151 * textstr (<optional> text string to be added) 01152 * val (color to set the text) 01153 * location (L_ADD_ABOVE, L_ADD_AT_TOP, L_ADD_AT_BOTTOM, 01154 * L_ADD_BELOW) 01155 * Return: 0 if OK, 1 on error. 01156 * 01157 * Notes: 01158 * (1) Before calling this function for the first time, use 01159 * pixaCreate() to make the @pixa that will accumulate the pix. 01160 * This is passed in each time pixSaveTiled() is called. 01161 * (2) @outwidth is the scaled width. After scaling, the image is 01162 * saved in the input pixa, along with a box that specifies 01163 * the location to place it when tiled later. Disable saving 01164 * the pix by setting @outwidth == 0. 01165 * (3) @newrow and @space specify the location of the new pix 01166 * with respect to the last one(s) that were entered. 01167 * (4) All pix are saved as 32 bpp RGB. 01168 * (5) If both @bmf and @textstr are defined, this generates a pix 01169 * with the additional text; otherwise, no text is written. 01170 * (6) The text is written before scaling, so it is properly 01171 * antialiased in the scaled pix. However, if the pix on 01172 * different calls have different widths, the size of the 01173 * text will vary. 01174 * (7) See pixSaveTiledOutline() for other implementation details. 01175 */ 01176 l_int32 01177 pixSaveTiledWithText(PIX *pixs, 01178 PIXA *pixa, 01179 l_int32 outwidth, 01180 l_int32 newrow, 01181 l_int32 space, 01182 l_int32 linewidth, 01183 L_BMF *bmf, 01184 const char *textstr, 01185 l_uint32 val, 01186 l_int32 location) 01187 { 01188 PIX *pixt1, *pixt2, *pixt3, *pixt4; 01189 01190 PROCNAME("pixSaveTiledWithText"); 01191 01192 if (outwidth == 0) return 0; 01193 01194 if (!pixs) 01195 return ERROR_INT("pixs not defined", procName, 1); 01196 if (!pixa) 01197 return ERROR_INT("pixa not defined", procName, 1); 01198 01199 pixt1 = pixConvertTo32(pixs); 01200 if (linewidth > 0) 01201 pixt2 = pixAddBorder(pixt1, linewidth, 0); 01202 else 01203 pixt2 = pixClone(pixt1); 01204 if (bmf && textstr) 01205 pixt3 = pixAddSingleTextblock(pixt2, bmf, textstr, val, location, NULL); 01206 else 01207 pixt3 = pixClone(pixt2); 01208 pixt4 = pixScaleToSize(pixt3, outwidth, 0); 01209 pixSaveTiled(pixt4, pixa, 1, newrow, space, 32); 01210 pixDestroy(&pixt1); 01211 pixDestroy(&pixt2); 01212 pixDestroy(&pixt3); 01213 pixDestroy(&pixt4); 01214 return 0; 01215 } 01216 01217 01218 void 01219 l_chooseDisplayProg(l_int32 selection) 01220 { 01221 if (selection == L_DISPLAY_WITH_XLI || 01222 selection == L_DISPLAY_WITH_XZGV || 01223 selection == L_DISPLAY_WITH_XV) 01224 var_DISPLAY_PROG = selection; 01225 else 01226 L_ERROR("invalid unix display program", "l_chooseDisplayProg"); 01227 return; 01228 }