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
27 /*!
28 * \file gifio.c
29 * <pre>
30 *
31 * Reading gif
32 * PIX *pixReadStreamGif()
33 * PIX *pixReadMemGif()
34 * static l_int32 gifReadFunc()
35 * static PIX *gifToPix()
36 *
37 * Writing gif
38 * l_int32 pixWriteStreamGif()
39 * l_int32 pixWriteMemGif()
40 * static l_int32 gifWriteFunc()
41 * static l_int32 pixToGif()
42 *
43 * Removing interlacing
44 * static PIX *pixUninterlaceGIF()
45 *
46 * The initial version of this module was generously contribued by
47 * Antony Dovgal.
48 *
49 * The functions that read and write from pix to gif-compressed memory,
50 * using gif internal functions DGifOpen() and EGifOpen() that are
51 * available in 5.1 and later, were contributed by Tobias Peirick.
52 *
53 * Version information:
54 *
55 * (1) This supports the gif library, version 5.1 or later, for which
56 * gif read-from-mem and write-to-mem allow these operations
57 * without writing temporary files.
58 * (2) There has never been a gif stream interface. For versions
59 * before 5.1, it was necessary to use a file descriptor, and to
60 * generate a file stream from the low-level descriptor. With the
61 * memory interface in 5.1 that can be used on all platforms, it
62 * is no longer necessary to use any API code with file descriptors.
63 * (3) The public interface changed with 5.0 and with 5.1, and we
64 * no longer support 4.6.1 and 5.0.
65 * (4) Version 5.1.2 came out on Jan 7, 2016. Leptonica cannot
66 * successfully read gif files that it writes with this version;
67 * DGifSlurp() gets an internal error from an uninitialized array
68 * and returns failure. The problem was fixed in 5.1.3.
69 * </pre>
70 */
71
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
90 /*! For in-memory decoding of GIF; 5.1+ */
91 typedef struct GifReadBuffer
92 {
93 size_t size; /*!< size of buffer */
94 size_t pos; /*!< position relative to beginning of buffer */
95 const l_uint8 *cdata; /*!< data in the buffer */
96 } GifReadBuffer;
97
98 /*! Low-level callback for in-memory decoding */
99 static l_int32 gifReadFunc(GifFileType *gif, GifByteType *dest,
100 l_int32 bytesToRead);
101 /*! Low-level callback for in-memory encoding */
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 *---------------------------------------------------------------------*/
117 /*!
118 * \brief pixReadStreamGif()
119 *
120 * \param[in] fp file stream opened for reading
121 * \return pix, or NULL on error
122 */
123 PIX *
pixReadStreamGif(FILE * fp)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
149 /*!
150 * \brief pixReadMemGif()
151 *
152 * \param[in] cdata const; gif-encoded
153 * \param[in] size bytes data
154 * \return pix, or NULL on error
155 *
156 * <pre>
157 * Notes:
158 * (1) For libgif version >= 5.1, this uses the DGifOpen() buffer
159 * interface. No temp files are required.
160 * (2) For libgif version < 5.1, it was necessary to write the compressed
161 * data to file and read it back, and we couldn't use the GNU
162 * runtime extension fmemopen() because libgif doesn't have a file
163 * stream interface.
164 * </pre>
165 */
166 PIX *
pixReadMemGif(const l_uint8 * cdata,size_t size)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
gifReadFunc(GifFileType * gif,GifByteType * dest,l_int32 bytesToRead)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
223 /*!
224 * \brief gifToPix()
225 *
226 * \param[in] gif opened gif stream
227 * \return pix, or NULL on error
228 *
229 * <pre>
230 * Notes:
231 * (1) This decodes the pix from the compressed gif stream and
232 * closes the stream.
233 * (2) It is static so that the stream is not exposed to clients.
234 * </pre>
235 */
236 static PIX *
gifToPix(GifFileType * gif)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 *---------------------------------------------------------------------*/
353 /*!
354 * \brief pixWriteStreamGif()
355 *
356 * \param[in] fp file stream opened for writing
357 * \param[in] pix 1, 2, 4, 8, 16 or 32 bpp
358 * \return 0 if OK, 1 on error
359 *
360 * <pre>
361 * Notes:
362 * (1) All output gif have colormaps. If the pix is 32 bpp rgb,
363 * this quantizes the colors and writes out 8 bpp.
364 * If the pix is 16 bpp grayscale, it converts to 8 bpp first.
365 * </pre>
366 */
367 l_int32
pixWriteStreamGif(FILE * fp,PIX * pix)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
396 /*!
397 * \brief pixWriteMemGif()
398 *
399 * \param[out] pdata data of gif compressed image
400 * \param[out] psize size of returned data
401 * \param[in] pix
402 * \return 0 if OK, 1 on error
403 *
404 * <pre>
405 * Notes:
406 * (1) See comments in pixReadMemGif()
407 * </pre>
408 */
409 l_int32
pixWriteMemGif(l_uint8 ** pdata,size_t * psize,PIX * pix)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
gifWriteFunc(GifFileType * gif,const GifByteType * src,l_int32 bytesToWrite)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
478 /*!
479 * \brief pixToGif()
480 *
481 * \param[in] pix 1, 2, 4, 8, 16 or 32 bpp
482 * \param[in] gif opened gif stream
483 * \return 0 if OK, 1 on error
484 *
485 * <pre>
486 * Notes:
487 * (1) This encodes the pix to the gif stream. The stream is not
488 * closed by this function.
489 * (2) It is static to make this function private.
490 * </pre>
491 */
492 static l_int32
pixToGif(PIX * pix,GifFileType * gif)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 *
pixUninterlaceGIF(PIX * pixs)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 */
677