Leptonica 1.84.1
Image processing and image analysis suite
Loading...
Searching...
No Matches
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
69#ifdef HAVE_CONFIG_H
70#include <config_auto.h>
71#endif /* HAVE_CONFIG_H */
72
73#include <string.h>
74#include "allheaders.h"
75
76/* --------------------------------------------------------------------*/
77#if HAVE_LIBGIF || HAVE_LIBUNGIF /* defined in environ.h */
78/* --------------------------------------------------------------------*/
79
80#include "gif_lib.h"
81
82 /* Interface that enables low-level GIF support for reading from memory */
83static PIX * gifToPix(GifFileType *gif);
84 /* Interface that enables low-level GIF support for writing to memory */
85static l_int32 pixToGif(PIX *pix, GifFileType *gif);
86
88typedef struct GifReadBuffer
89{
90 size_t size;
91 size_t pos;
92 const l_uint8 *cdata;
93} GifReadBuffer;
94
96static l_int32 gifReadFunc(GifFileType *gif, GifByteType *dest,
97 l_int32 bytesToRead);
99static l_int32 gifWriteFunc(GifFileType *gif, const GifByteType *src,
100 l_int32 bytesToWrite);
101
102
103/*---------------------------------------------------------------------*
104 * Reading gif *
105 *---------------------------------------------------------------------*/
112PIX *
113pixReadStreamGif(FILE *fp)
114{
115l_uint8 *filedata;
116size_t filesize;
117PIX *pix;
118
119 if (!fp)
120 return (PIX *)ERROR_PTR("fp not defined", __func__, NULL);
121
122 /* Read data into memory from file */
123 rewind(fp);
124 if ((filedata = l_binaryReadStream(fp, &filesize)) == NULL)
125 return (PIX *)ERROR_PTR("filedata not read", __func__, NULL);
126
127 /* Uncompress from memory */
128 pix = pixReadMemGif(filedata, filesize);
129 LEPT_FREE(filedata);
130 if (!pix)
131 L_ERROR("failed to read gif from file data\n", __func__);
132 return pix;
133}
134
135
153PIX *
154pixReadMemGif(const l_uint8 *cdata,
155 size_t size)
156{
157GifFileType *gif;
158GifReadBuffer buffer;
159
160 /* 5.1+ and not 5.1.2 */
161#if (GIFLIB_MAJOR < 5 || (GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 0))
162 L_ERROR("Require giflib-5.1 or later\n", __func__);
163 return NULL;
164#endif /* < 5.1 */
165#if GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 1 && GIFLIB_RELEASE == 2 /* 5.1.2 */
166 L_ERROR("Can't use giflib-5.1.2; suggest 5.1.3 or later\n", __func__);
167 return NULL;
168#endif /* 5.1.2 */
169
170 if (!cdata)
171 return (PIX *)ERROR_PTR("cdata not defined", __func__, NULL);
172
173 buffer.cdata = cdata;
174 buffer.size = size;
175 buffer.pos = 0;
176 if ((gif = DGifOpen((void*)&buffer, gifReadFunc, NULL)) == NULL)
177 return (PIX *)ERROR_PTR("could not open gif stream from memory",
178 __func__, NULL);
179
180 return gifToPix(gif);
181}
182
183
184static l_int32
185gifReadFunc(GifFileType *gif,
186 GifByteType *dest,
187 l_int32 bytesToRead)
188{
189GifReadBuffer *buffer;
190l_int32 bytesRead;
191
192 if ((buffer = (GifReadBuffer*)gif->UserData) == NULL)
193 return ERROR_INT("UserData not set", __func__, -1);
194
195 if(buffer->pos >= buffer->size || bytesToRead > buffer->size)
196 return -1;
197
198 bytesRead = (buffer->pos < buffer->size - bytesToRead)
199 ? bytesToRead : buffer->size - buffer->pos;
200 memcpy(dest, buffer->cdata + buffer->pos, bytesRead);
201 buffer->pos += bytesRead;
202 return bytesRead;
203}
204
205
219static PIX *
220gifToPix(GifFileType *gif)
221{
222l_int32 wpl, i, j, w, h, d, cindex, ncolors, valid, nimages;
223l_int32 rval, gval, bval;
224l_uint32 *data, *line;
225PIX *pixd;
226PIXCMAP *cmap;
227ColorMapObject *gif_cmap;
228SavedImage si;
229int giferr;
230
231 /* Read all the data, but use only the first image found */
232 if (DGifSlurp(gif) != GIF_OK) {
233 DGifCloseFile(gif, &giferr);
234 return (PIX *)ERROR_PTR("failed to read GIF data", __func__, NULL);
235 }
236
237 if (gif->SavedImages == NULL) {
238 DGifCloseFile(gif, &giferr);
239 return (PIX *)ERROR_PTR("no images found in GIF", __func__, NULL);
240 }
241
242 nimages = gif->ImageCount;
243 if (nimages > 1)
244 L_WARNING("There are %d images in the file; we only read the first\n",
245 __func__, nimages);
246
247 si = gif->SavedImages[0];
248 w = si.ImageDesc.Width;
249 h = si.ImageDesc.Height;
250 if (w <= 0 || h <= 0) {
251 DGifCloseFile(gif, &giferr);
252 return (PIX *)ERROR_PTR("invalid image dimensions", __func__, NULL);
253 }
254
255 if (si.RasterBits == NULL) {
256 DGifCloseFile(gif, &giferr);
257 return (PIX *)ERROR_PTR("no raster data in GIF", __func__, NULL);
258 }
259
260 if (si.ImageDesc.ColorMap) {
261 /* private cmap for this image */
262 gif_cmap = si.ImageDesc.ColorMap;
263 } else if (gif->SColorMap) {
264 /* global cmap for whole picture */
265 gif_cmap = gif->SColorMap;
266 } else {
267 /* don't know where to take cmap from */
268 DGifCloseFile(gif, &giferr);
269 return (PIX *)ERROR_PTR("color map is missing", __func__, NULL);
270 }
271
272 ncolors = gif_cmap->ColorCount;
273 if (ncolors <= 0 || ncolors > 256) {
274 DGifCloseFile(gif, &giferr);
275 return (PIX *)ERROR_PTR("ncolors is invalid", __func__, NULL);
276 }
277 if (ncolors <= 2)
278 d = 1;
279 else if (ncolors <= 4)
280 d = 2;
281 else if (ncolors <= 16)
282 d = 4;
283 else /* [17 ... 256] */
284 d = 8;
285 cmap = pixcmapCreate(d);
286 for (cindex = 0; cindex < ncolors; cindex++) {
287 rval = gif_cmap->Colors[cindex].Red;
288 gval = gif_cmap->Colors[cindex].Green;
289 bval = gif_cmap->Colors[cindex].Blue;
290 pixcmapAddColor(cmap, rval, gval, bval);
291 }
292
293 if ((pixd = pixCreate(w, h, d)) == NULL) {
294 DGifCloseFile(gif, &giferr);
295 pixcmapDestroy(&cmap);
296 return (PIX *)ERROR_PTR("failed to allocate pixd", __func__, NULL);
297 }
298 pixSetInputFormat(pixd, IFF_GIF);
299 pixSetColormap(pixd, cmap);
300 pixcmapIsValid(cmap, pixd, &valid);
301 if (!valid) {
302 DGifCloseFile(gif, &giferr);
303 pixDestroy(&pixd);
304 pixcmapDestroy(&cmap);
305 return (PIX *)ERROR_PTR("colormap is invalid", __func__, NULL);
306 }
307
308 wpl = pixGetWpl(pixd);
309 data = pixGetData(pixd);
310 for (i = 0; i < h; i++) {
311 line = data + i * wpl;
312 if (d == 1) {
313 for (j = 0; j < w; j++) {
314 if (si.RasterBits[i * w + j])
315 SET_DATA_BIT(line, j);
316 }
317 } else if (d == 2) {
318 for (j = 0; j < w; j++)
319 SET_DATA_DIBIT(line, j, si.RasterBits[i * w + j]);
320 } else if (d == 4) {
321 for (j = 0; j < w; j++)
322 SET_DATA_QBIT(line, j, si.RasterBits[i * w + j]);
323 } else { /* d == 8 */
324 for (j = 0; j < w; j++)
325 SET_DATA_BYTE(line, j, si.RasterBits[i * w + j]);
326 }
327 }
328
329 /* Versions before 5.0 required un-interlacing to restore
330 * the raster lines to normal order if the image
331 * had been interlaced (for viewing in a browser):
332 if (gif->Image.Interlace) {
333 PIX *pixdi = pixUninterlaceGIF(pixd);
334 pixTransferAllData(pixd, &pixdi, 0, 0);
335 }
336 * This is no longer required. */
337
338 DGifCloseFile(gif, &giferr);
339 return pixd;
340}
341
342
343/*---------------------------------------------------------------------*
344 * Writing gif *
345 *---------------------------------------------------------------------*/
360l_ok
361pixWriteStreamGif(FILE *fp,
362 PIX *pix)
363{
364l_uint8 *filedata;
365size_t filebytes, nbytes;
366
367 if (!fp)
368 return ERROR_INT("stream not open", __func__, 1);
369 if (!pix)
370 return ERROR_INT("pix not defined", __func__, 1);
371
372 pixSetPadBits(pix, 0);
373 if (pixWriteMemGif(&filedata, &filebytes, pix) != 0) {
374 LEPT_FREE(filedata);
375 return ERROR_INT("failure to gif encode pix", __func__, 1);
376 }
377
378 rewind(fp);
379 nbytes = fwrite(filedata, 1, filebytes, fp);
380 LEPT_FREE(filedata);
381 if (nbytes != filebytes)
382 return ERROR_INT("write error", __func__, 1);
383 return 0;
384}
385
386
400l_ok
401pixWriteMemGif(l_uint8 **pdata,
402 size_t *psize,
403 PIX *pix)
404{
405int giferr;
406l_int32 result;
407GifFileType *gif;
408L_BBUFFER *buffer;
409
410 /* 5.1+ and not 5.1.2 */
411#if (GIFLIB_MAJOR < 5 || (GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 0))
412 L_ERROR("Require giflib-5.1 or later\n", __func__);
413 return 1;
414#endif /* < 5.1 */
415#if GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 1 && GIFLIB_RELEASE == 2 /* 5.1.2 */
416 L_ERROR("Can't use giflib-5.1.2; suggest 5.1.3 or later\n", __func__);
417 return 1;
418#endif /* 5.1.2 */
419
420 if (!pdata)
421 return ERROR_INT("&data not defined", __func__, 1 );
422 *pdata = NULL;
423 if (!psize)
424 return ERROR_INT("&size not defined", __func__, 1 );
425 *psize = 0;
426 if (!pix)
427 return ERROR_INT("&pix not defined", __func__, 1 );
428
429 if ((buffer = bbufferCreate(NULL, 0)) == NULL)
430 return ERROR_INT("failed to create buffer", __func__, 1);
431
432 if ((gif = EGifOpen((void*)buffer, gifWriteFunc, NULL)) == NULL) {
433 bbufferDestroy(&buffer);
434 return ERROR_INT("failed to create GIF image handle", __func__, 1);
435 }
436
437 result = pixToGif(pix, gif);
438 EGifCloseFile(gif, &giferr);
439
440 if (result == 0) {
441 *pdata = bbufferDestroyAndSaveData(&buffer, psize);
442 } else {
443 bbufferDestroy(&buffer);
444 }
445 return result;
446}
447
448
449static l_int32
450gifWriteFunc(GifFileType *gif,
451 const GifByteType *src,
452 l_int32 bytesToWrite)
453{
454L_BBUFFER *buffer;
455
456 if ((buffer = (L_BBUFFER*)gif->UserData) == NULL)
457 return ERROR_INT("UserData not set", __func__, -1);
458
459 if(bbufferRead(buffer, (l_uint8*)src, bytesToWrite) == 0)
460 return bytesToWrite;
461 return 0;
462}
463
464
479static l_int32
480pixToGif(PIX *pix,
481 GifFileType *gif)
482{
483char *text;
484l_int32 wpl, i, j, w, h, d, ncolor, rval, gval, bval, valid;
485l_int32 gif_ncolor = 0;
486l_uint32 *data, *line;
487PIX *pixd;
488PIXCMAP *cmap;
489ColorMapObject *gif_cmap;
490GifByteType *gif_line;
491
492 if (!pix)
493 return ERROR_INT("pix not defined", __func__, 1);
494 if (!gif)
495 return ERROR_INT("gif not defined", __func__, 1);
496
497 d = pixGetDepth(pix);
498 if (d == 32) {
499 pixd = pixConvertRGBToColormap(pix, 1);
500 } else if (d > 1) {
501 pixd = pixConvertTo8(pix, TRUE);
502 } else { /* d == 1; make sure there's a colormap */
503 pixd = pixClone(pix);
504 if (!pixGetColormap(pixd)) {
505 cmap = pixcmapCreate(1);
506 pixcmapAddColor(cmap, 255, 255, 255);
507 pixcmapAddColor(cmap, 0, 0, 0);
508 pixSetColormap(pixd, cmap);
509 }
510 }
511
512 if (!pixd)
513 return ERROR_INT("failed to convert to colormapped pix", __func__, 1);
514 d = pixGetDepth(pixd);
515 cmap = pixGetColormap(pixd);
516 if (!cmap) {
517 pixDestroy(&pixd);
518 return ERROR_INT("cmap is missing", __func__, 1);
519 }
520 pixcmapIsValid(cmap, pixd, &valid);
521 if (!valid) {
522 pixDestroy(&pixd);
523 return ERROR_INT("colormap is not valid", __func__, 1);
524 }
525
526 /* 'Round' the number of gif colors up to a power of 2 */
527 ncolor = pixcmapGetCount(cmap);
528 for (i = 0; i <= 8; i++) {
529 if ((1 << i) >= ncolor) {
530 gif_ncolor = (1 << i);
531 break;
532 }
533 }
534 if (gif_ncolor < 1) {
535 pixDestroy(&pixd);
536 return ERROR_INT("number of colors is invalid", __func__, 1);
537 }
538
539 /* Save the cmap colors in a gif_cmap */
540 if ((gif_cmap = GifMakeMapObject(gif_ncolor, NULL)) == NULL) {
541 pixDestroy(&pixd);
542 return ERROR_INT("failed to create GIF color map", __func__, 1);
543 }
544 for (i = 0; i < gif_ncolor; i++) {
545 rval = gval = bval = 0;
546 if (ncolor > 0) {
547 if (pixcmapGetColor(cmap, i, &rval, &gval, &bval) != 0) {
548 pixDestroy(&pixd);
549 GifFreeMapObject(gif_cmap);
550 return ERROR_INT("failed to get color from color map",
551 __func__, 1);
552 }
553 ncolor--;
554 }
555 gif_cmap->Colors[i].Red = rval;
556 gif_cmap->Colors[i].Green = gval;
557 gif_cmap->Colors[i].Blue = bval;
558 }
559
560 pixGetDimensions(pixd, &w, &h, NULL);
561 if (EGifPutScreenDesc(gif, w, h, gif_cmap->BitsPerPixel, 0, gif_cmap)
562 != GIF_OK) {
563 pixDestroy(&pixd);
564 GifFreeMapObject(gif_cmap);
565 return ERROR_INT("failed to write screen description", __func__, 1);
566 }
567 GifFreeMapObject(gif_cmap); /* not needed after this point */
568
569 if (EGifPutImageDesc(gif, 0, 0, w, h, FALSE, NULL) != GIF_OK) {
570 pixDestroy(&pixd);
571 return ERROR_INT("failed to image screen description", __func__, 1);
572 }
573
574 data = pixGetData(pixd);
575 wpl = pixGetWpl(pixd);
576 if (d != 1 && d != 2 && d != 4 && d != 8) {
577 pixDestroy(&pixd);
578 return ERROR_INT("image depth is not in {1, 2, 4, 8}", __func__, 1);
579 }
580
581 if ((gif_line = (GifByteType *)LEPT_CALLOC(sizeof(GifByteType), w))
582 == NULL) {
583 pixDestroy(&pixd);
584 return ERROR_INT("mem alloc fail for data line", __func__, 1);
585 }
586
587 for (i = 0; i < h; i++) {
588 line = data + i * wpl;
589 /* Gif's way of setting the raster line up for compression */
590 for (j = 0; j < w; j++) {
591 switch(d)
592 {
593 case 8:
594 gif_line[j] = GET_DATA_BYTE(line, j);
595 break;
596 case 4:
597 gif_line[j] = GET_DATA_QBIT(line, j);
598 break;
599 case 2:
600 gif_line[j] = GET_DATA_DIBIT(line, j);
601 break;
602 case 1:
603 gif_line[j] = GET_DATA_BIT(line, j);
604 break;
605 }
606 }
607
608 /* Compress and save the line */
609 if (EGifPutLine(gif, gif_line, w) != GIF_OK) {
610 LEPT_FREE(gif_line);
611 pixDestroy(&pixd);
612 return ERROR_INT("failed to write data line into GIF", __func__, 1);
613 }
614 }
615
616 /* Write a text comment. This must be placed after writing the
617 * data (!!) Note that because libgif does not provide a function
618 * for reading comments from file, you will need another way
619 * to read comments. */
620 if ((text = pixGetText(pix)) != NULL) {
621 if (EGifPutComment(gif, text) != GIF_OK)
622 L_WARNING("gif comment not written\n", __func__);
623 }
624
625 LEPT_FREE(gif_line);
626 pixDestroy(&pixd);
627 return 0;
628}
629
630
631#if 0
632/*---------------------------------------------------------------------*
633 * Removing interlacing (reference only; not used) *
634 *---------------------------------------------------------------------*/
635 /* GIF supports 4-way interlacing by raster lines.
636 * Before 5.0, it was necessary for leptonica to restore interlaced
637 * data to normal raster order when reading to a pix. With 5.0,
638 * the de-interlacing is done by the library read function.
639 * It is here only as a reference. */
640static const l_int32 InterlacedOffset[] = {0, 4, 2, 1};
641static const l_int32 InterlacedJumps[] = {8, 8, 4, 2};
642
643static PIX *
644pixUninterlaceGIF(PIX *pixs)
645{
646l_int32 w, h, d, wpl, j, k, srow, drow;
647l_uint32 *datas, *datad, *lines, *lined;
648PIX *pixd;
649
650 if (!pixs)
651 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
652
653 pixGetDimensions(pixs, &w, &h, &d);
654 wpl = pixGetWpl(pixs);
655 pixd = pixCreateTemplate(pixs);
656 datas = pixGetData(pixs);
657 datad = pixGetData(pixd);
658 for (k = 0, srow = 0; k < 4; k++) {
659 for (drow = InterlacedOffset[k]; drow < h;
660 drow += InterlacedJumps[k], srow++) {
661 lines = datas + srow * wpl;
662 lined = datad + drow * wpl;
663 for (j = 0; j < w; j++)
664 memcpy(lined, lines, 4 * wpl);
665 }
666 }
667
668 return pixd;
669}
670#endif
671
672
673/* -----------------------------------------------------------------*/
674#endif /* HAVE_LIBGIF || HAVE_LIBUNGIF */
#define GET_DATA_QBIT(pdata, n)
#define SET_DATA_BIT(pdata, n)
#define SET_DATA_DIBIT(pdata, n, val)
#define GET_DATA_BYTE(pdata, n)
#define GET_DATA_DIBIT(pdata, n)
#define SET_DATA_BYTE(pdata, n, val)
#define GET_DATA_BIT(pdata, n)
#define SET_DATA_QBIT(pdata, n, val)