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 * gifio.c 00018 * 00019 * Read gif from file 00020 * PIX *pixReadStreamGif() 00021 * static PIX *pixInterlaceGIF() 00022 * 00023 * Write gif to file 00024 * l_int32 pixWriteStreamGif() 00025 * 00026 * Read/write from/to memory (see warning) 00027 * PIX *pixReadMemGif() 00028 * l_int32 pixWriteMemGif() 00029 * 00030 * This uses the gif library, version 4.1.6. Do not use 4.1.4. 00031 * 00032 * This module was generously contribued by Antony Dovgal. 00033 * He can be contacted at: tony *AT* daylessday.org 00034 */ 00035 00036 #include <string.h> 00037 #include <sys/types.h> 00038 #ifndef _MSC_VER 00039 #include <unistd.h> 00040 #else 00041 #include <io.h> 00042 #endif /* _MSC_VER */ 00043 #include "allheaders.h" 00044 00045 #ifdef HAVE_CONFIG_H 00046 #include "config_auto.h" 00047 #endif /* HAVE_CONFIG_H */ 00048 00049 /* --------------------------------------------------------------------*/ 00050 #if HAVE_LIBGIF || HAVE_LIBUNGIF /* defined in environ.h */ 00051 /* --------------------------------------------------------------------*/ 00052 00053 #include "gif_lib.h" 00054 00055 /* GIF supports 4-way horizontal interlacing */ 00056 static PIX * pixInterlaceGIF(PIX *pixs); 00057 static const l_int32 InterlacedOffset[] = {0, 4, 2, 1}; 00058 static const l_int32 InterlacedJumps[] = {8, 8, 4, 2}; 00059 00060 00061 /*---------------------------------------------------------------------* 00062 * Reading gif from file * 00063 *---------------------------------------------------------------------*/ 00064 /*! 00065 * pixReadStreamGif() 00066 * 00067 * Input: stream 00068 * Return: pix, or null on error 00069 */ 00070 PIX * 00071 pixReadStreamGif(FILE *fp) 00072 { 00073 l_int32 fd, wpl, i, j, w, h, d, cindex, ncolors; 00074 l_int32 rval, gval, bval; 00075 l_uint32 *data, *line; 00076 GifFileType *gif; 00077 PIX *pixd, *pixdi; 00078 PIXCMAP *cmap; 00079 ColorMapObject *gif_cmap; 00080 SavedImage si; 00081 00082 PROCNAME("pixReadStreamGif"); 00083 00084 if ((fd = fileno(fp)) < 0) 00085 return (PIX *)ERROR_PTR("invalid file descriptor", procName, NULL); 00086 #ifndef _MSC_VER 00087 lseek(fd, 0, SEEK_SET); 00088 #else 00089 _lseek(fd, 0, SEEK_SET); 00090 #endif /* _MSC_VER */ 00091 00092 if ((gif = DGifOpenFileHandle(fd)) == NULL) 00093 return (PIX *)ERROR_PTR("invalid file or file not found", 00094 procName, NULL); 00095 00096 /* Read all the data, but use only the first image found */ 00097 if (DGifSlurp(gif) != GIF_OK) { 00098 DGifCloseFile(gif); 00099 return (PIX *)ERROR_PTR("failed to read GIF data", procName, NULL); 00100 } 00101 00102 if (gif->SavedImages == NULL) { 00103 DGifCloseFile(gif); 00104 return (PIX *)ERROR_PTR("no images found in GIF", procName, NULL); 00105 } 00106 00107 si = gif->SavedImages[0]; 00108 w = si.ImageDesc.Width; 00109 h = si.ImageDesc.Height; 00110 if (w <= 0 || h <= 0) { 00111 DGifCloseFile(gif); 00112 return (PIX *)ERROR_PTR("invalid image dimensions", procName, NULL); 00113 } 00114 00115 if (si.RasterBits == NULL) { 00116 DGifCloseFile(gif); 00117 return (PIX *)ERROR_PTR("no raster data in GIF", procName, NULL); 00118 } 00119 00120 if (si.ImageDesc.ColorMap) { 00121 /* private cmap for this image */ 00122 gif_cmap = si.ImageDesc.ColorMap; 00123 } 00124 else if (gif->SColorMap) { 00125 /* global cmap for whole picture */ 00126 gif_cmap = gif->SColorMap; 00127 } 00128 else { 00129 /* don't know where to take cmap from */ 00130 DGifCloseFile(gif); 00131 return (PIX *)ERROR_PTR("color map is missing", procName, NULL); 00132 } 00133 00134 ncolors = gif_cmap->ColorCount; 00135 if (ncolors <= 2) 00136 d = 1; 00137 else if (ncolors <= 4) 00138 d = 2; 00139 else if (ncolors <= 16) 00140 d = 4; 00141 else 00142 d = 8; 00143 if ((cmap = pixcmapCreate(d)) == NULL) 00144 return (PIX *)ERROR_PTR("cmap creation failed", procName, NULL); 00145 00146 for (cindex = 0; cindex < ncolors; cindex++) { 00147 rval = gif_cmap->Colors[cindex].Red; 00148 gval = gif_cmap->Colors[cindex].Green; 00149 bval = gif_cmap->Colors[cindex].Blue; 00150 pixcmapAddColor(cmap, rval, gval, bval); 00151 } 00152 00153 if ((pixd = pixCreate(w, h, d)) == NULL) { 00154 DGifCloseFile(gif); 00155 pixcmapDestroy(&cmap); 00156 return (PIX *)ERROR_PTR("failed to allocate pixd", procName, NULL); 00157 } 00158 pixSetColormap(pixd, cmap); 00159 00160 wpl = pixGetWpl(pixd); 00161 data = pixGetData(pixd); 00162 for (i = 0; i < h; i++) { 00163 line = data + i * wpl; 00164 if (d == 1) { 00165 for (j = 0; j < w; j++) { 00166 if (si.RasterBits[i * w + j]) 00167 SET_DATA_BIT(line, j); 00168 } 00169 } 00170 else if (d == 2) { 00171 for (j = 0; j < w; j++) 00172 SET_DATA_DIBIT(line, j, si.RasterBits[i * w + j]); 00173 } 00174 else if (d == 4) { 00175 for (j = 0; j < w; j++) 00176 SET_DATA_QBIT(line, j, si.RasterBits[i * w + j]); 00177 } 00178 else { /* d == 8 */ 00179 for (j = 0; j < w; j++) 00180 SET_DATA_BYTE(line, j, si.RasterBits[i * w + j]); 00181 } 00182 } 00183 00184 if (gif->Image.Interlace) { 00185 pixdi = pixInterlaceGIF(pixd); 00186 pixTransferAllData(pixd, &pixdi, 0, 0); 00187 } 00188 00189 DGifCloseFile(gif); 00190 return pixd; 00191 } 00192 00193 00194 static PIX * 00195 pixInterlaceGIF(PIX *pixs) 00196 { 00197 l_int32 w, h, d, wpl, j, k, srow, drow; 00198 l_uint32 *datas, *datad, *lines, *lined; 00199 PIX *pixd; 00200 00201 PROCNAME("pixInterlaceGIF"); 00202 00203 if (!pixs) 00204 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00205 00206 pixGetDimensions(pixs, &w, &h, &d); 00207 wpl = pixGetWpl(pixs); 00208 pixd = pixCreateTemplate(pixs); 00209 datas = pixGetData(pixs); 00210 datad = pixGetData(pixd); 00211 for (k = 0, srow = 0; k < 4; k++) { 00212 for (drow = InterlacedOffset[k]; drow < h; 00213 drow += InterlacedJumps[k], srow++) { 00214 lines = datas + srow * wpl; 00215 lined = datad + drow * wpl; 00216 for (j = 0; j < w; j++) 00217 memcpy(lined, lines, 4 * wpl); 00218 } 00219 } 00220 00221 return pixd; 00222 } 00223 00224 00225 /*---------------------------------------------------------------------* 00226 * Writing gif to file * 00227 *---------------------------------------------------------------------*/ 00228 /*! 00229 * pixWriteStreamGif() 00230 * 00231 * Input: stream 00232 * pix (1, 2, 4, 8, 16 or 32 bpp) 00233 * Return: 0 if OK, 1 on error 00234 * 00235 * Notes: 00236 * (1) All output gif have colormaps. If the pix is 32 bpp rgb, 00237 * this quantizes the colors and writes out 8 bpp. 00238 * If the pix is 16 bpp grayscale, it converts to 8 bpp first. 00239 * (2) We can't write to memory using open_memstream() because 00240 * the gif functions write through a file descriptor, not a 00241 * file stream. 00242 */ 00243 l_int32 00244 pixWriteStreamGif(FILE *fp, 00245 PIX *pix) 00246 { 00247 char *text; 00248 l_int32 fd, wpl, i, j, w, h, d, ncolor, rval, gval, bval; 00249 l_int32 gif_ncolor = 0; 00250 l_uint32 *data, *line; 00251 PIX *pixd; 00252 PIXCMAP *cmap; 00253 GifFileType *gif; 00254 ColorMapObject *gif_cmap; 00255 GifByteType *gif_line; 00256 00257 PROCNAME("pixWriteStreamGif"); 00258 00259 if (!fp) 00260 return ERROR_INT("stream not open", procName, 1); 00261 if (!pix) 00262 return ERROR_INT("pix not defined", procName, 1); 00263 rewind(fp); 00264 00265 if ((fd = fileno(fp)) < 0) 00266 return ERROR_INT("invalid file descriptor", procName, 1); 00267 00268 d = pixGetDepth(pix); 00269 if (d == 32) { 00270 pixd = pixConvertRGBToColormap(pix, 1); 00271 } 00272 else if (d > 1) { 00273 pixd = pixConvertTo8(pix, TRUE); 00274 } 00275 else { /* d == 1; make sure there's a colormap */ 00276 pixd = pixClone(pix); 00277 if (!pixGetColormap(pixd)) { 00278 cmap = pixcmapCreate(1); 00279 pixcmapAddColor(cmap, 255, 255, 255); 00280 pixcmapAddColor(cmap, 0, 0, 0); 00281 pixSetColormap(pixd, cmap); 00282 } 00283 } 00284 00285 if (!pixd) 00286 return ERROR_INT("failed to convert image to indexed", procName, 1); 00287 d = pixGetDepth(pixd); 00288 00289 if ((cmap = pixGetColormap(pixd)) == NULL) { 00290 pixDestroy(&pixd); 00291 return ERROR_INT("cmap is missing", procName, 1); 00292 } 00293 00294 /* 'Round' the number of gif colors up to a power of 2 */ 00295 ncolor = pixcmapGetCount(cmap); 00296 for (i = 0; i <= 8; i++) { 00297 if ((1 << i) >= ncolor) { 00298 gif_ncolor = (1 << i); 00299 break; 00300 } 00301 } 00302 if (gif_ncolor < 1) { 00303 pixDestroy(&pixd); 00304 return ERROR_INT("number of colors is invalid", procName, 1); 00305 } 00306 00307 /* Save the cmap colors in a gif_cmap */ 00308 if ((gif_cmap = MakeMapObject(gif_ncolor, NULL)) == NULL) { 00309 pixDestroy(&pixd); 00310 return ERROR_INT("failed to create GIF color map", procName, 1); 00311 } 00312 for (i = 0; i < gif_ncolor; i++) { 00313 rval = gval = bval = 0; 00314 if (ncolor > 0) { 00315 if (pixcmapGetColor(cmap, i, &rval, &gval, &bval) != 0) { 00316 pixDestroy(&pixd); 00317 FreeMapObject(gif_cmap); 00318 return ERROR_INT("failed to get color from color map", 00319 procName, 1); 00320 } 00321 ncolor--; 00322 } 00323 gif_cmap->Colors[i].Red = rval; 00324 gif_cmap->Colors[i].Green = gval; 00325 gif_cmap->Colors[i].Blue = bval; 00326 } 00327 00328 /* Get the gif file handle */ 00329 if ((gif = EGifOpenFileHandle(fd)) == NULL) { 00330 pixDestroy(&pixd); 00331 FreeMapObject(gif_cmap); 00332 return ERROR_INT("failed to create GIF image handle", procName, 1); 00333 } 00334 00335 pixGetDimensions(pixd, &w, &h, NULL); 00336 if (EGifPutScreenDesc(gif, w, h, gif_cmap->BitsPerPixel, 0, gif_cmap) 00337 != GIF_OK) { 00338 pixDestroy(&pixd); 00339 FreeMapObject(gif_cmap); 00340 EGifCloseFile(gif); 00341 return ERROR_INT("failed to write screen description", procName, 1); 00342 } 00343 FreeMapObject(gif_cmap); /* not needed after this point */ 00344 00345 if (EGifPutImageDesc(gif, 0, 0, w, h, FALSE, NULL) != GIF_OK) { 00346 pixDestroy(&pixd); 00347 EGifCloseFile(gif); 00348 return ERROR_INT("failed to image screen description", procName, 1); 00349 } 00350 00351 data = pixGetData(pixd); 00352 wpl = pixGetWpl(pixd); 00353 if (d != 1 && d != 2 && d != 4 && d != 8) { 00354 pixDestroy(&pixd); 00355 EGifCloseFile(gif); 00356 return ERROR_INT("image depth is not in {1, 2, 4, 8}", procName, 1); 00357 } 00358 00359 if ((gif_line = (GifByteType *)CALLOC(sizeof(GifByteType), w)) == NULL) { 00360 pixDestroy(&pixd); 00361 EGifCloseFile(gif); 00362 return ERROR_INT("mem alloc fail for data line", procName, 1); 00363 } 00364 00365 for (i = 0; i < h; i++) { 00366 line = data + i * wpl; 00367 /* Gif's way of setting the raster line up for compression */ 00368 for (j = 0; j < w; j++) { 00369 switch(d) 00370 { 00371 case 8: 00372 gif_line[j] = GET_DATA_BYTE(line, j); 00373 break; 00374 case 4: 00375 gif_line[j] = GET_DATA_QBIT(line, j); 00376 break; 00377 case 2: 00378 gif_line[j] = GET_DATA_DIBIT(line, j); 00379 break; 00380 case 1: 00381 gif_line[j] = GET_DATA_BIT(line, j); 00382 break; 00383 } 00384 } 00385 00386 /* Compress and save the line */ 00387 if (EGifPutLine(gif, gif_line, w) != GIF_OK) { 00388 FREE(gif_line); 00389 pixDestroy(&pixd); 00390 EGifCloseFile(gif); 00391 return ERROR_INT("failed to write data line into GIF", procName, 1); 00392 } 00393 } 00394 00395 /* Write a text comment. This must be placed after writing the 00396 * data (!!) Note that because libgif does not provide a function 00397 * for reading comments from file, you will need another way 00398 * to read comments. */ 00399 if ((text = pixGetText(pix)) != NULL) { 00400 if (EGifPutComment(gif, text) != GIF_OK) 00401 L_WARNING("gif comment not written", procName); 00402 } 00403 00404 FREE(gif_line); 00405 pixDestroy(&pixd); 00406 EGifCloseFile(gif); 00407 return 0; 00408 } 00409 00410 00411 /*---------------------------------------------------------------------* 00412 * Read/write from/to memory * 00413 *---------------------------------------------------------------------*/ 00414 /*! 00415 * pixReadMemGif() 00416 * 00417 * Input: data (const; gif-encoded) 00418 * size (of data) 00419 * Return: pix, or null on error 00420 * 00421 * Notes: 00422 * (1) Of course, we are cheating here -- writing the data out 00423 * to file and then reading it back in as a gif format. 00424 * (2) This should not be assumed to be safe from a sophisticated 00425 * attack, even though we have attempted to make the filename 00426 * difficult to guess by embedding the process number and the 00427 * current time in microseconds. The best way to handle 00428 * temporary files is to use file descriptors (capabilities) 00429 * or file handles. However, I know of no way to do this 00430 * for gif files because of the way that libgif handles the 00431 * file descriptors. The canonical approach would be to do this: 00432 * char templ[] = "hiddenfilenameXXXXXX"; 00433 * l_int32 fd = mkstemp(templ); 00434 * FILE *fp = fdopen(fd, "w+b"); 00435 * fwrite(data, 1, size, fp); 00436 * rewind(fp); 00437 * Pix *pix = pixReadStreamGif(fp); 00438 * but this fails because fp is in a bad state after writing. 00439 */ 00440 PIX * 00441 pixReadMemGif(const l_uint8 *cdata, 00442 size_t size) 00443 { 00444 char *tname; 00445 l_uint8 *data; 00446 PIX *pix; 00447 00448 PROCNAME("pixReadMemGif"); 00449 00450 if (!cdata) 00451 return (PIX *)ERROR_PTR("cdata not defined", procName, NULL); 00452 00453 data = (l_uint8 *)cdata; /* we're really not going to change this */ 00454 tname = genTempFilename("/tmp/", "mem.gif", 1, 1); 00455 l_binaryWrite(tname, "w", data, size); 00456 pix = pixRead(tname); 00457 remove(tname); 00458 FREE(tname); 00459 if (!pix) 00460 return (PIX *)ERROR_PTR("pix not read", procName, NULL); 00461 return pix; 00462 } 00463 00464 00465 /*! 00466 * pixWriteMemGif() 00467 * 00468 * Input: &data (<return> data of gif compressed image) 00469 * &size (<return> size of returned data) 00470 * pix 00471 * Return: 0 if OK, 1 on error 00472 * 00473 * Notes: 00474 * (1) See comments in pixReadMemGif() 00475 */ 00476 l_int32 00477 pixWriteMemGif(l_uint8 **pdata, 00478 size_t *psize, 00479 PIX *pix) 00480 { 00481 char *tname; 00482 l_uint8 *data; 00483 size_t nbytes; 00484 00485 PROCNAME("pixWriteMemGif"); 00486 00487 if (!pdata) 00488 return ERROR_INT("&data not defined", procName, 1 ); 00489 if (!psize) 00490 return ERROR_INT("&size not defined", procName, 1 ); 00491 if (!pix) 00492 return ERROR_INT("&pix not defined", procName, 1 ); 00493 00494 tname = genTempFilename("/tmp/", "mem.gif", 1, 1); 00495 pixWrite(tname, pix, IFF_GIF); 00496 data = l_binaryRead(tname, &nbytes); 00497 remove(tname); 00498 FREE(tname); 00499 if (!data) 00500 return ERROR_INT("data not returned", procName, 1 ); 00501 *pdata = data; 00502 *psize = nbytes; 00503 return 0; 00504 } 00505 00506 00507 /* -----------------------------------------------------------------*/ 00508 #endif /* HAVE_LIBGIF || HAVE_LIBUNGIF */ 00509 /* -----------------------------------------------------------------*/ 00510