Leptonica  1.73
Image processing and image analysis suite
gifio.c
Go to the documentation of this file.
1 /*====================================================================*
2  - Copyright (C) 2001 Leptonica. All rights reserved.
3  -
4  - Redistribution and use in source and binary forms, with or without
5  - modification, are permitted provided that the following conditions
6  - are met:
7  - 1. Redistributions of source code must retain the above copyright
8  - notice, this list of conditions and the following disclaimer.
9  - 2. Redistributions in binary form must reproduce the above
10  - copyright notice, this list of conditions and the following
11  - disclaimer in the documentation and/or other materials
12  - provided with the distribution.
13  -
14  - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15  - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16  - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17  - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY
18  - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23  - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24  - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *====================================================================*/
26 
72 #ifdef HAVE_CONFIG_H
73 #include "config_auto.h"
74 #endif /* HAVE_CONFIG_H */
75 
76 #include <string.h>
77 #include "allheaders.h"
78 
79 /* --------------------------------------------------------------------*/
80 #if HAVE_LIBGIF || HAVE_LIBUNGIF /* defined in environ.h */
81 /* --------------------------------------------------------------------*/
82 
83 #include "gif_lib.h"
84 
85  /* Interface that enables low-level GIF support for reading from memory */
86 static PIX * gifToPix(GifFileType *gif);
87  /* Interface that enables low-level GIF support for writing to memory */
88 static l_int32 pixToGif(PIX *pix, GifFileType *gif);
89 
91 typedef struct GifReadBuffer
92 {
93  size_t size;
94  size_t pos;
95  const l_uint8 *cdata;
96 } GifReadBuffer;
97 
99 static l_int32 gifReadFunc(GifFileType *gif, GifByteType *dest,
100  l_int32 bytesToRead);
102 static l_int32 gifWriteFunc(GifFileType *gif, const GifByteType *src,
103  l_int32 bytesToWrite);
104 
105  /* GIF supports 4-way interlacing by raster lines.
106  * Before 5.0, it was necessary for leptonica to restore interlaced
107  * data to normal raster order when reading to a pix. With 5.0,
108  * the de-interlacing is done by the library read function. */
109 static PIX * pixUninterlaceGIF(PIX *pixs);
110 static const l_int32 InterlacedOffset[] = {0, 4, 2, 1};
111 static const l_int32 InterlacedJumps[] = {8, 8, 4, 2};
112 
113 
114 /*---------------------------------------------------------------------*
115  * Reading gif *
116  *---------------------------------------------------------------------*/
123 PIX *
124 pixReadStreamGif(FILE *fp)
125 {
126 l_uint8 *filedata;
127 size_t filesize;
128 PIX *pix;
129 
130  PROCNAME("pixReadStreamGif");
131 
132  if (!fp)
133  return (PIX *)ERROR_PTR("fp not defined", procName, NULL);
134 
135  /* Read data into memory from file */
136  rewind(fp);
137  if ((filedata = l_binaryReadStream(fp, &filesize)) == NULL)
138  return (PIX *)ERROR_PTR("filedata not read", procName, NULL);
139 
140  /* Uncompress from memory */
141  pix = pixReadMemGif(filedata, filesize);
142  LEPT_FREE(filedata);
143  if (!pix)
144  L_ERROR("failed to read gif from file data\n", procName);
145  return pix;
146 }
147 
148 
166 PIX *
167 pixReadMemGif(const l_uint8 *cdata,
168  size_t size)
169 {
170 GifFileType *gif;
171 GifReadBuffer buffer;
172 
173  PROCNAME("pixReadMemGif");
174 
175  /* 5.1+ and not 5.1.2 */
176 #if (GIFLIB_MAJOR < 5 || (GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 0))
177  L_ERROR("Require giflib-5.1 or later\n", procName);
178  return NULL;
179 #endif /* < 5.1 */
180 #if GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 1 && GIFLIB_RELEASE == 2 /* 5.1.2 */
181  L_ERROR("Can't use giflib-5.1.2; suggest 5.1.3 or later\n", procName);
182  return NULL;
183 #endif /* 5.1.2 */
184 
185  if (!cdata)
186  return (PIX *)ERROR_PTR("cdata not defined", procName, NULL);
187 
188  buffer.cdata = cdata;
189  buffer.size = size;
190  buffer.pos = 0;
191  if ((gif = DGifOpen((void*)&buffer, gifReadFunc, NULL)) == NULL)
192  return (PIX *)ERROR_PTR("could not open gif stream from memory",
193  procName, NULL);
194 
195  return gifToPix(gif);
196 }
197 
198 
199 static l_int32
200 gifReadFunc(GifFileType *gif,
201  GifByteType *dest,
202  l_int32 bytesToRead)
203 {
204 GifReadBuffer *buffer;
205 l_int32 bytesRead;
206 
207  PROCNAME("gifReadFunc");
208 
209  if ((buffer = (GifReadBuffer*)gif->UserData) == NULL)
210  return ERROR_INT("UserData not set", procName, -1);
211 
212  if(buffer->pos >= buffer->size)
213  return -1;
214 
215  bytesRead = (buffer->pos < buffer->size - bytesToRead)
216  ? bytesToRead : buffer->size - buffer->pos;
217  memcpy(dest, buffer->cdata + buffer->pos, bytesRead);
218  buffer->pos += bytesRead;
219  return bytesRead;
220 }
221 
222 
236 static PIX *
237 gifToPix(GifFileType *gif)
238 {
239 l_int32 wpl, i, j, w, h, d, cindex, ncolors;
240 l_int32 rval, gval, bval;
241 l_uint32 *data, *line;
242 PIX *pixd;
243 PIXCMAP *cmap;
244 ColorMapObject *gif_cmap;
245 SavedImage si;
246 int giferr;
247 
248  PROCNAME("gifToPix");
249 
250  /* Read all the data, but use only the first image found */
251  if (DGifSlurp(gif) != GIF_OK) {
252  DGifCloseFile(gif, &giferr);
253  return (PIX *)ERROR_PTR("failed to read GIF data", procName, NULL);
254  }
255 
256  if (gif->SavedImages == NULL) {
257  DGifCloseFile(gif, &giferr);
258  return (PIX *)ERROR_PTR("no images found in GIF", procName, NULL);
259  }
260 
261  si = gif->SavedImages[0];
262  w = si.ImageDesc.Width;
263  h = si.ImageDesc.Height;
264  if (w <= 0 || h <= 0) {
265  DGifCloseFile(gif, &giferr);
266  return (PIX *)ERROR_PTR("invalid image dimensions", procName, NULL);
267  }
268 
269  if (si.RasterBits == NULL) {
270  DGifCloseFile(gif, &giferr);
271  return (PIX *)ERROR_PTR("no raster data in GIF", procName, NULL);
272  }
273 
274  if (si.ImageDesc.ColorMap) {
275  /* private cmap for this image */
276  gif_cmap = si.ImageDesc.ColorMap;
277  } else if (gif->SColorMap) {
278  /* global cmap for whole picture */
279  gif_cmap = gif->SColorMap;
280  } else {
281  /* don't know where to take cmap from */
282  DGifCloseFile(gif, &giferr);
283  return (PIX *)ERROR_PTR("color map is missing", procName, NULL);
284  }
285 
286  ncolors = gif_cmap->ColorCount;
287  if (ncolors <= 2)
288  d = 1;
289  else if (ncolors <= 4)
290  d = 2;
291  else if (ncolors <= 16)
292  d = 4;
293  else
294  d = 8;
295  if ((cmap = pixcmapCreate(d)) == NULL) {
296  DGifCloseFile(gif, &giferr);
297  return (PIX *)ERROR_PTR("cmap creation failed", procName, NULL);
298  }
299 
300  for (cindex = 0; cindex < ncolors; cindex++) {
301  rval = gif_cmap->Colors[cindex].Red;
302  gval = gif_cmap->Colors[cindex].Green;
303  bval = gif_cmap->Colors[cindex].Blue;
304  pixcmapAddColor(cmap, rval, gval, bval);
305  }
306 
307  if ((pixd = pixCreate(w, h, d)) == NULL) {
308  DGifCloseFile(gif, &giferr);
309  pixcmapDestroy(&cmap);
310  return (PIX *)ERROR_PTR("failed to allocate pixd", procName, NULL);
311  }
312  pixSetInputFormat(pixd, IFF_GIF);
313  pixSetColormap(pixd, cmap);
314 
315  wpl = pixGetWpl(pixd);
316  data = pixGetData(pixd);
317  for (i = 0; i < h; i++) {
318  line = data + i * wpl;
319  if (d == 1) {
320  for (j = 0; j < w; j++) {
321  if (si.RasterBits[i * w + j])
322  SET_DATA_BIT(line, j);
323  }
324  } else if (d == 2) {
325  for (j = 0; j < w; j++)
326  SET_DATA_DIBIT(line, j, si.RasterBits[i * w + j]);
327  } else if (d == 4) {
328  for (j = 0; j < w; j++)
329  SET_DATA_QBIT(line, j, si.RasterBits[i * w + j]);
330  } else { /* d == 8 */
331  for (j = 0; j < w; j++)
332  SET_DATA_BYTE(line, j, si.RasterBits[i * w + j]);
333  }
334  }
335 
336  /* Versions before 5.0 required un-interlacing to restore
337  * the raster lines to normal order if the image
338  * had been interlaced (for viewing in a browser):
339  if (gif->Image.Interlace) {
340  PIX *pixdi = pixUninterlaceGIF(pixd);
341  pixTransferAllData(pixd, &pixdi, 0, 0);
342  }
343  * This is no longer required. */
344 
345  DGifCloseFile(gif, &giferr);
346  return pixd;
347 }
348 
349 
350 /*---------------------------------------------------------------------*
351  * Writing gif *
352  *---------------------------------------------------------------------*/
367 l_int32
368 pixWriteStreamGif(FILE *fp,
369  PIX *pix)
370 {
371 l_uint8 *filedata;
372 size_t filebytes, nbytes;
373 
374  PROCNAME("pixWriteStreamGif");
375 
376  if (!fp)
377  return ERROR_INT("stream not open", procName, 1);
378  if (!pix)
379  return ERROR_INT("pix not defined", procName, 1);
380 
381  pixSetPadBits(pix, 0);
382  if (pixWriteMemGif(&filedata, &filebytes, pix) != 0) {
383  LEPT_FREE(filedata);
384  return ERROR_INT("failure to gif encode pix", procName, 1);
385  }
386 
387  rewind(fp);
388  nbytes = fwrite(filedata, 1, filebytes, fp);
389  LEPT_FREE(filedata);
390  if (nbytes != filebytes)
391  return ERROR_INT("write error", procName, 1);
392  return 0;
393 }
394 
395 
409 l_int32
410 pixWriteMemGif(l_uint8 **pdata,
411  size_t *psize,
412  PIX *pix)
413 {
414 int giferr;
415 l_int32 result;
416 GifFileType *gif;
417 L_BBUFFER *buffer;
418 
419  PROCNAME("pixWriteMemGif");
420 
421  /* 5.1+ and not 5.1.2 */
422 #if (GIFLIB_MAJOR < 5 || (GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 0))
423  L_ERROR("Require giflib-5.1 or later\n", procName);
424  return 1;
425 #endif /* < 5.1 */
426 #if GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 1 && GIFLIB_RELEASE == 2 /* 5.1.2 */
427  L_ERROR("Can't use giflib-5.1.2; suggest 5.1.3 or later\n", procName);
428  return 1;
429 #endif /* 5.1.2 */
430 
431  if (!pdata)
432  return ERROR_INT("&data not defined", procName, 1 );
433  *pdata = NULL;
434  if (!psize)
435  return ERROR_INT("&size not defined", procName, 1 );
436  *psize = 0;
437  if (!pix)
438  return ERROR_INT("&pix not defined", procName, 1 );
439 
440  if ((buffer = bbufferCreate(NULL, 0)) == NULL)
441  return ERROR_INT("failed to create buffer", procName, 1);
442 
443  if ((gif = EGifOpen((void*)buffer, gifWriteFunc, NULL)) == NULL) {
444  bbufferDestroy(&buffer);
445  return ERROR_INT("failed to create GIF image handle", procName, 1);
446  }
447 
448  result = pixToGif(pix, gif);
449  EGifCloseFile(gif, &giferr);
450 
451  if (result == 0) {
452  *pdata = bbufferDestroyAndSaveData(&buffer, psize);
453  } else {
454  bbufferDestroy(&buffer);
455  }
456  return result;
457 }
458 
459 
460 static l_int32
461 gifWriteFunc(GifFileType *gif,
462  const GifByteType *src,
463  l_int32 bytesToWrite)
464 {
465 L_BBUFFER *buffer;
466 
467  PROCNAME("gifWriteFunc");
468 
469  if ((buffer = (L_BBUFFER*)gif->UserData) == NULL)
470  return ERROR_INT("UserData not set", procName, -1);
471 
472  if(bbufferRead(buffer, (l_uint8*)src, bytesToWrite) == 0)
473  return bytesToWrite;
474  return 0;
475 }
476 
477 
492 static l_int32
493 pixToGif(PIX *pix,
494  GifFileType *gif)
495 {
496 char *text;
497 l_int32 wpl, i, j, w, h, d, ncolor, rval, gval, bval;
498 l_int32 gif_ncolor = 0;
499 l_uint32 *data, *line;
500 PIX *pixd;
501 PIXCMAP *cmap;
502 ColorMapObject *gif_cmap;
503 GifByteType *gif_line;
504 
505  PROCNAME("pixToGif");
506 
507  if (!pix)
508  return ERROR_INT("pix not defined", procName, 1);
509  if (!gif)
510  return ERROR_INT("gif not defined", procName, 1);
511 
512  d = pixGetDepth(pix);
513  if (d == 32) {
514  pixd = pixConvertRGBToColormap(pix, 1);
515  } else if (d > 1) {
516  pixd = pixConvertTo8(pix, TRUE);
517  } else { /* d == 1; make sure there's a colormap */
518  pixd = pixClone(pix);
519  if (!pixGetColormap(pixd)) {
520  cmap = pixcmapCreate(1);
521  pixcmapAddColor(cmap, 255, 255, 255);
522  pixcmapAddColor(cmap, 0, 0, 0);
523  pixSetColormap(pixd, cmap);
524  }
525  }
526 
527  if (!pixd)
528  return ERROR_INT("failed to convert image to indexed", procName, 1);
529  d = pixGetDepth(pixd);
530 
531  if ((cmap = pixGetColormap(pixd)) == NULL) {
532  pixDestroy(&pixd);
533  return ERROR_INT("cmap is missing", procName, 1);
534  }
535 
536  /* 'Round' the number of gif colors up to a power of 2 */
537  ncolor = pixcmapGetCount(cmap);
538  for (i = 0; i <= 8; i++) {
539  if ((1 << i) >= ncolor) {
540  gif_ncolor = (1 << i);
541  break;
542  }
543  }
544  if (gif_ncolor < 1) {
545  pixDestroy(&pixd);
546  return ERROR_INT("number of colors is invalid", procName, 1);
547  }
548 
549  /* Save the cmap colors in a gif_cmap */
550  if ((gif_cmap = GifMakeMapObject(gif_ncolor, NULL)) == NULL) {
551  pixDestroy(&pixd);
552  return ERROR_INT("failed to create GIF color map", procName, 1);
553  }
554  for (i = 0; i < gif_ncolor; i++) {
555  rval = gval = bval = 0;
556  if (ncolor > 0) {
557  if (pixcmapGetColor(cmap, i, &rval, &gval, &bval) != 0) {
558  pixDestroy(&pixd);
559  GifFreeMapObject(gif_cmap);
560  return ERROR_INT("failed to get color from color map",
561  procName, 1);
562  }
563  ncolor--;
564  }
565  gif_cmap->Colors[i].Red = rval;
566  gif_cmap->Colors[i].Green = gval;
567  gif_cmap->Colors[i].Blue = bval;
568  }
569 
570  pixGetDimensions(pixd, &w, &h, NULL);
571  if (EGifPutScreenDesc(gif, w, h, gif_cmap->BitsPerPixel, 0, gif_cmap)
572  != GIF_OK) {
573  pixDestroy(&pixd);
574  GifFreeMapObject(gif_cmap);
575  return ERROR_INT("failed to write screen description", procName, 1);
576  }
577  GifFreeMapObject(gif_cmap); /* not needed after this point */
578 
579  if (EGifPutImageDesc(gif, 0, 0, w, h, FALSE, NULL) != GIF_OK) {
580  pixDestroy(&pixd);
581  return ERROR_INT("failed to image screen description", procName, 1);
582  }
583 
584  data = pixGetData(pixd);
585  wpl = pixGetWpl(pixd);
586  if (d != 1 && d != 2 && d != 4 && d != 8) {
587  pixDestroy(&pixd);
588  return ERROR_INT("image depth is not in {1, 2, 4, 8}", procName, 1);
589  }
590 
591  if ((gif_line = (GifByteType *)LEPT_CALLOC(sizeof(GifByteType), w))
592  == NULL) {
593  pixDestroy(&pixd);
594  return ERROR_INT("mem alloc fail for data line", procName, 1);
595  }
596 
597  for (i = 0; i < h; i++) {
598  line = data + i * wpl;
599  /* Gif's way of setting the raster line up for compression */
600  for (j = 0; j < w; j++) {
601  switch(d)
602  {
603  case 8:
604  gif_line[j] = GET_DATA_BYTE(line, j);
605  break;
606  case 4:
607  gif_line[j] = GET_DATA_QBIT(line, j);
608  break;
609  case 2:
610  gif_line[j] = GET_DATA_DIBIT(line, j);
611  break;
612  case 1:
613  gif_line[j] = GET_DATA_BIT(line, j);
614  break;
615  }
616  }
617 
618  /* Compress and save the line */
619  if (EGifPutLine(gif, gif_line, w) != GIF_OK) {
620  LEPT_FREE(gif_line);
621  pixDestroy(&pixd);
622  return ERROR_INT("failed to write data line into GIF", procName, 1);
623  }
624  }
625 
626  /* Write a text comment. This must be placed after writing the
627  * data (!!) Note that because libgif does not provide a function
628  * for reading comments from file, you will need another way
629  * to read comments. */
630  if ((text = pixGetText(pix)) != NULL) {
631  if (EGifPutComment(gif, text) != GIF_OK)
632  L_WARNING("gif comment not written\n", procName);
633  }
634 
635  LEPT_FREE(gif_line);
636  pixDestroy(&pixd);
637  return 0;
638 }
639 
640 
641 /*---------------------------------------------------------------------*
642  * Removing interlacing *
643  *---------------------------------------------------------------------*/
644 static PIX *
645 pixUninterlaceGIF(PIX *pixs)
646 {
647 l_int32 w, h, d, wpl, j, k, srow, drow;
648 l_uint32 *datas, *datad, *lines, *lined;
649 PIX *pixd;
650 
651  PROCNAME("pixUninterlaceGIF");
652 
653  if (!pixs)
654  return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
655 
656  pixGetDimensions(pixs, &w, &h, &d);
657  wpl = pixGetWpl(pixs);
658  pixd = pixCreateTemplate(pixs);
659  datas = pixGetData(pixs);
660  datad = pixGetData(pixd);
661  for (k = 0, srow = 0; k < 4; k++) {
662  for (drow = InterlacedOffset[k]; drow < h;
663  drow += InterlacedJumps[k], srow++) {
664  lines = datas + srow * wpl;
665  lined = datad + drow * wpl;
666  for (j = 0; j < w; j++)
667  memcpy(lined, lines, 4 * wpl);
668  }
669  }
670 
671  return pixd;
672 }
673 
674 
675 /* -----------------------------------------------------------------*/
676 #endif /* HAVE_LIBGIF || HAVE_LIBUNGIF */
PIX * pixConvertTo8(PIX *pixs, l_int32 cmapflag)
pixConvertTo8()
Definition: pixconv.c:3041
PIX * pixCreate(l_int32 width, l_int32 height, l_int32 depth)
pixCreate()
Definition: pix1.c:302
#define SET_DATA_QBIT(pdata, n, val)
Definition: arrayaccess.h:162
void pixcmapDestroy(PIXCMAP **pcmap)
pixcmapDestroy()
Definition: colormap.c:263
l_uint32 * pixGetData(PIX *pix)
pixGetData()
Definition: pix1.c:1602
l_int32 pixcmapGetColor(PIXCMAP *cmap, l_int32 index, l_int32 *prval, l_int32 *pgval, l_int32 *pbval)
pixcmapGetColor()
Definition: colormap.c:709
#define GET_DATA_BIT(pdata, n)
Definition: arrayaccess.h:117
PIX * pixCreateTemplate(PIX *pixs)
pixCreateTemplate()
Definition: pix1.c:367
l_uint8 * bbufferDestroyAndSaveData(L_BBUFFER **pbb, size_t *pnbytes)
bbufferDestroyAndSaveData()
Definition: bbuffer.c:203
#define SET_DATA_DIBIT(pdata, n, val)
Definition: arrayaccess.h:143
PIXCMAP * pixcmapCreate(l_int32 depth)
pixcmapCreate()
Definition: colormap.c:110
l_int32 pixcmapAddColor(PIXCMAP *cmap, l_int32 rval, l_int32 gval, l_int32 bval)
pixcmapAddColor()
Definition: colormap.c:299
l_int32 pixSetPadBits(PIX *pix, l_int32 val)
pixSetPadBits()
Definition: pix2.c:1295
#define SET_DATA_BYTE(pdata, n, val)
Definition: arrayaccess.h:192
l_int32 pixSetColormap(PIX *pix, PIXCMAP *colormap)
pixSetColormap()
Definition: pix1.c:1556
#define GET_DATA_QBIT(pdata, n)
Definition: arrayaccess.h:158
#define GET_DATA_BYTE(pdata, n)
Definition: arrayaccess.h:182
PIX * pixClone(PIX *pixs)
pixClone()
Definition: pix1.c:517
void pixDestroy(PIX **ppix)
pixDestroy()
Definition: pix1.c:545
void bbufferDestroy(L_BBUFFER **pbb)
bbufferDestroy()
Definition: bbuffer.c:167
l_uint8 * l_binaryReadStream(FILE *fp, size_t *pnbytes)
l_binaryReadStream()
Definition: utils2.c:1200
char * pixGetText(PIX *pix)
pixGetText()
Definition: pix1.c:1442
#define GET_DATA_DIBIT(pdata, n)
Definition: arrayaccess.h:139
l_int32 bbufferRead(L_BBUFFER *bb, l_uint8 *src, l_int32 nbytes)
bbufferRead()
Definition: bbuffer.c:262
PIX * pixConvertRGBToColormap(PIX *pixs, l_int32 ditherflag)
pixConvertRGBToColormap()
Definition: pixconv.c:1412
l_int32 pixcmapGetCount(PIXCMAP *cmap)
pixcmapGetCount()
Definition: colormap.c:593
Definition: pix.h:134
l_int32 pixGetDimensions(PIX *pix, l_int32 *pw, l_int32 *ph, l_int32 *pd)
pixGetDimensions()
Definition: pix1.c:1052
L_BBUFFER * bbufferCreate(l_uint8 *indata, l_int32 nalloc)
bbufferCreate()
Definition: bbuffer.c:124
#define SET_DATA_BIT(pdata, n)
Definition: arrayaccess.h:121