Leptonica 1.68
C Image Processing Library

gifio.c

Go to the documentation of this file.
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 
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines