comparison mupdf-source/thirdparty/leptonica/src/gifio.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
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 * The initial version of this module was generously contribued by
44 * Antony Dovgal.
45 *
46 * The functions that read and write from pix to gif-compressed memory,
47 * using gif internal functions DGifOpen() and EGifOpen() that are
48 * available in 5.1 and later, were contributed by Tobias Peirick.
49 *
50 * Version information:
51 *
52 * (1) This supports the gif library, version 5.1 or later, for which
53 * gif read-from-mem and write-to-mem allow these operations
54 * without writing temporary files.
55 * (2) There has never been a gif stream interface. For versions
56 * before 5.1, it was necessary to use a file descriptor, and to
57 * generate a file stream from the low-level descriptor. With the
58 * memory interface in 5.1 that can be used on all platforms, it
59 * is no longer necessary to use any API code with file descriptors.
60 * (3) The public interface changed with 5.0 and with 5.1, and we
61 * no longer support 4.6.1 and 5.0.
62 * (4) Version 5.1.2 came out on Jan 7, 2016. Leptonica cannot
63 * successfully read gif files that it writes with this version;
64 * DGifSlurp() gets an internal error from an uninitialized array
65 * and returns failure. The problem was fixed in 5.1.3.
66 *
67 * Limitations:
68 *
69 * (1) We do not support animated gif. If the gif has more than one image,
70 * an error message is returned.
71 *
72 * </pre>
73 */
74
75 #ifdef HAVE_CONFIG_H
76 #include <config_auto.h>
77 #endif /* HAVE_CONFIG_H */
78
79 #include <string.h>
80 #include "allheaders.h"
81
82 /* --------------------------------------------------------------------*/
83 #if HAVE_LIBGIF || HAVE_LIBUNGIF /* defined in environ.h */
84 /* --------------------------------------------------------------------*/
85
86 #include "gif_lib.h"
87
88 /* Interface that enables low-level GIF support for reading from memory */
89 static PIX * gifToPix(GifFileType *gif);
90 /* Interface that enables low-level GIF support for writing to memory */
91 static l_int32 pixToGif(PIX *pix, GifFileType *gif);
92
93 /*! For in-memory decoding of GIF; 5.1+ */
94 typedef struct GifReadBuffer
95 {
96 size_t size; /*!< size of buffer */
97 size_t pos; /*!< position relative to beginning of buffer */
98 const l_uint8 *cdata; /*!< data in the buffer */
99 } GifReadBuffer;
100
101 /*! Low-level callback for in-memory decoding */
102 static l_int32 gifReadFunc(GifFileType *gif, GifByteType *dest,
103 l_int32 bytesToRead);
104 /*! Low-level callback for in-memory encoding */
105 static l_int32 gifWriteFunc(GifFileType *gif, const GifByteType *src,
106 l_int32 bytesToWrite);
107
108
109 /*---------------------------------------------------------------------*
110 * Reading gif *
111 *---------------------------------------------------------------------*/
112 /*!
113 * \brief pixReadStreamGif()
114 *
115 * \param[in] fp file stream opened for reading
116 * \return pix, or NULL on error
117 */
118 PIX *
119 pixReadStreamGif(FILE *fp)
120 {
121 l_uint8 *filedata;
122 size_t filesize;
123 PIX *pix;
124
125 if (!fp)
126 return (PIX *)ERROR_PTR("fp not defined", __func__, NULL);
127
128 /* Read data into memory from file */
129 rewind(fp);
130 if ((filedata = l_binaryReadStream(fp, &filesize)) == NULL)
131 return (PIX *)ERROR_PTR("filedata not read", __func__, NULL);
132
133 /* Uncompress from memory */
134 pix = pixReadMemGif(filedata, filesize);
135 LEPT_FREE(filedata);
136 if (!pix)
137 L_ERROR("failed to read gif from file data\n", __func__);
138 return pix;
139 }
140
141
142 /*!
143 * \brief pixReadMemGif()
144 *
145 * \param[in] cdata const; gif-encoded
146 * \param[in] size bytes data
147 * \return pix, or NULL on error
148 *
149 * <pre>
150 * Notes:
151 * (1) For libgif version >= 5.1, this uses the DGifOpen() buffer
152 * interface. No temp files are required.
153 * (2) For libgif version < 5.1, it was necessary to write the compressed
154 * data to file and read it back, and we couldn't use the GNU
155 * runtime extension fmemopen() because libgif doesn't have a file
156 * stream interface.
157 * </pre>
158 */
159 PIX *
160 pixReadMemGif(const l_uint8 *cdata,
161 size_t size)
162 {
163 GifFileType *gif;
164 GifReadBuffer buffer;
165
166 /* 5.1+ and not 5.1.2 */
167 #if (GIFLIB_MAJOR < 5 || (GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 0))
168 L_ERROR("Require giflib-5.1 or later\n", __func__);
169 return NULL;
170 #endif /* < 5.1 */
171 #if GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 1 && GIFLIB_RELEASE == 2 /* 5.1.2 */
172 L_ERROR("Can't use giflib-5.1.2; suggest 5.1.3 or later\n", __func__);
173 return NULL;
174 #endif /* 5.1.2 */
175
176 if (!cdata)
177 return (PIX *)ERROR_PTR("cdata not defined", __func__, NULL);
178
179 buffer.cdata = cdata;
180 buffer.size = size;
181 buffer.pos = 0;
182 if ((gif = DGifOpen((void*)&buffer, gifReadFunc, NULL)) == NULL)
183 return (PIX *)ERROR_PTR("could not open gif stream from memory",
184 __func__, NULL);
185
186 return gifToPix(gif);
187 }
188
189
190 static l_int32
191 gifReadFunc(GifFileType *gif,
192 GifByteType *dest,
193 l_int32 bytesToRead)
194 {
195 GifReadBuffer *buffer;
196 l_int32 bytesRead;
197
198 if ((buffer = (GifReadBuffer*)gif->UserData) == NULL)
199 return ERROR_INT("UserData not set", __func__, -1);
200
201 if(buffer->pos >= buffer->size || bytesToRead > buffer->size)
202 return -1;
203
204 bytesRead = (buffer->pos < buffer->size - bytesToRead)
205 ? bytesToRead : buffer->size - buffer->pos;
206 memcpy(dest, buffer->cdata + buffer->pos, bytesRead);
207 buffer->pos += bytesRead;
208 return bytesRead;
209 }
210
211
212 /*!
213 * \brief gifToPix()
214 *
215 * \param[in] gif opened gif stream
216 * \return pix, or NULL on error
217 *
218 * <pre>
219 * Notes:
220 * (1) This decodes the pix from the compressed gif stream and
221 * closes the stream.
222 * (2) It is static so that the stream is not exposed to clients.
223 * (3) Leptonica does not support gifanim (more than 1 image in the file).
224 * </pre>
225 */
226 static PIX *
227 gifToPix(GifFileType *gif)
228 {
229 l_int32 wpl, i, j, w, h, d, cindex, ncolors, valid, nimages;
230 l_int32 rval, gval, bval;
231 l_uint32 *data, *line;
232 PIX *pixd;
233 PIXCMAP *cmap;
234 ColorMapObject *gif_cmap;
235 SavedImage si;
236 int giferr;
237
238 /* Read all the data, but use only the first image found */
239 if (DGifSlurp(gif) != GIF_OK) {
240 DGifCloseFile(gif, &giferr);
241 return (PIX *)ERROR_PTR("failed to read GIF data", __func__, NULL);
242 }
243
244 if (gif->SavedImages == NULL) {
245 DGifCloseFile(gif, &giferr);
246 return (PIX *)ERROR_PTR("no images found in GIF", __func__, NULL);
247 }
248
249 nimages = gif->ImageCount;
250 if (nimages > 1) {
251 DGifCloseFile(gif, &giferr);
252 L_ERROR("There are %d images in the file; gifanim is not supported\n",
253 __func__, nimages);
254 return NULL;
255 }
256
257 si = gif->SavedImages[0];
258 w = si.ImageDesc.Width;
259 h = si.ImageDesc.Height;
260 if (w <= 0 || h <= 0) {
261 DGifCloseFile(gif, &giferr);
262 return (PIX *)ERROR_PTR("invalid image dimensions", __func__, NULL);
263 }
264
265 if (si.RasterBits == NULL) {
266 DGifCloseFile(gif, &giferr);
267 return (PIX *)ERROR_PTR("no raster data in GIF", __func__, NULL);
268 }
269
270 if (si.ImageDesc.ColorMap) {
271 /* private cmap for this image */
272 gif_cmap = si.ImageDesc.ColorMap;
273 } else if (gif->SColorMap) {
274 /* global cmap for whole picture */
275 gif_cmap = gif->SColorMap;
276 } else {
277 /* don't know where to take cmap from */
278 DGifCloseFile(gif, &giferr);
279 return (PIX *)ERROR_PTR("color map is missing", __func__, NULL);
280 }
281
282 ncolors = gif_cmap->ColorCount;
283 if (ncolors <= 0 || ncolors > 256) {
284 DGifCloseFile(gif, &giferr);
285 return (PIX *)ERROR_PTR("ncolors is invalid", __func__, NULL);
286 }
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 /* [17 ... 256] */
294 d = 8;
295 cmap = pixcmapCreate(d);
296 for (cindex = 0; cindex < ncolors; cindex++) {
297 rval = gif_cmap->Colors[cindex].Red;
298 gval = gif_cmap->Colors[cindex].Green;
299 bval = gif_cmap->Colors[cindex].Blue;
300 pixcmapAddColor(cmap, rval, gval, bval);
301 }
302
303 if ((pixd = pixCreate(w, h, d)) == NULL) {
304 DGifCloseFile(gif, &giferr);
305 pixcmapDestroy(&cmap);
306 return (PIX *)ERROR_PTR("failed to allocate pixd", __func__, NULL);
307 }
308 pixSetInputFormat(pixd, IFF_GIF);
309 pixSetColormap(pixd, cmap);
310 pixcmapIsValid(cmap, pixd, &valid);
311 if (!valid) {
312 DGifCloseFile(gif, &giferr);
313 pixDestroy(&pixd);
314 pixcmapDestroy(&cmap);
315 return (PIX *)ERROR_PTR("colormap is invalid", __func__, NULL);
316 }
317
318 wpl = pixGetWpl(pixd);
319 data = pixGetData(pixd);
320 for (i = 0; i < h; i++) {
321 line = data + i * wpl;
322 if (d == 1) {
323 for (j = 0; j < w; j++) {
324 if (si.RasterBits[i * w + j])
325 SET_DATA_BIT(line, j);
326 }
327 } else if (d == 2) {
328 for (j = 0; j < w; j++)
329 SET_DATA_DIBIT(line, j, si.RasterBits[i * w + j]);
330 } else if (d == 4) {
331 for (j = 0; j < w; j++)
332 SET_DATA_QBIT(line, j, si.RasterBits[i * w + j]);
333 } else { /* d == 8 */
334 for (j = 0; j < w; j++)
335 SET_DATA_BYTE(line, j, si.RasterBits[i * w + j]);
336 }
337 }
338
339 /* Versions before 5.0 required un-interlacing to restore
340 * the raster lines to normal order if the image
341 * had been interlaced (for viewing in a browser):
342 if (gif->Image.Interlace) {
343 PIX *pixdi = pixUninterlaceGIF(pixd);
344 pixTransferAllData(pixd, &pixdi, 0, 0);
345 }
346 * This is no longer required. */
347
348 DGifCloseFile(gif, &giferr);
349 return pixd;
350 }
351
352
353 /*---------------------------------------------------------------------*
354 * Writing gif *
355 *---------------------------------------------------------------------*/
356 /*!
357 * \brief pixWriteStreamGif()
358 *
359 * \param[in] fp file stream opened for writing
360 * \param[in] pix 1, 2, 4, 8, 16 or 32 bpp
361 * \return 0 if OK, 1 on error
362 *
363 * <pre>
364 * Notes:
365 * (1) All output gif have colormaps. If the pix is 32 bpp rgb,
366 * this quantizes the colors and writes out 8 bpp.
367 * If the pix is 16 bpp grayscale, it converts to 8 bpp first.
368 * </pre>
369 */
370 l_ok
371 pixWriteStreamGif(FILE *fp,
372 PIX *pix)
373 {
374 l_uint8 *filedata;
375 size_t filebytes, nbytes;
376
377 if (!fp)
378 return ERROR_INT("stream not open", __func__, 1);
379 if (!pix)
380 return ERROR_INT("pix not defined", __func__, 1);
381
382 pixSetPadBits(pix, 0);
383 if (pixWriteMemGif(&filedata, &filebytes, pix) != 0) {
384 LEPT_FREE(filedata);
385 return ERROR_INT("failure to gif encode pix", __func__, 1);
386 }
387
388 rewind(fp);
389 nbytes = fwrite(filedata, 1, filebytes, fp);
390 LEPT_FREE(filedata);
391 if (nbytes != filebytes)
392 return ERROR_INT("write error", __func__, 1);
393 return 0;
394 }
395
396
397 /*!
398 * \brief pixWriteMemGif()
399 *
400 * \param[out] pdata data of gif compressed image
401 * \param[out] psize size of returned data
402 * \param[in] pix
403 * \return 0 if OK, 1 on error
404 *
405 * <pre>
406 * Notes:
407 * (1) See comments in pixReadMemGif()
408 * </pre>
409 */
410 l_ok
411 pixWriteMemGif(l_uint8 **pdata,
412 size_t *psize,
413 PIX *pix)
414 {
415 int giferr;
416 l_int32 result;
417 GifFileType *gif;
418 L_BBUFFER *buffer;
419
420 /* 5.1+ and not 5.1.2 */
421 #if (GIFLIB_MAJOR < 5 || (GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 0))
422 L_ERROR("Require giflib-5.1 or later\n", __func__);
423 return 1;
424 #endif /* < 5.1 */
425 #if GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 1 && GIFLIB_RELEASE == 2 /* 5.1.2 */
426 L_ERROR("Can't use giflib-5.1.2; suggest 5.1.3 or later\n", __func__);
427 return 1;
428 #endif /* 5.1.2 */
429
430 if (!pdata)
431 return ERROR_INT("&data not defined", __func__, 1 );
432 *pdata = NULL;
433 if (!psize)
434 return ERROR_INT("&size not defined", __func__, 1 );
435 *psize = 0;
436 if (!pix)
437 return ERROR_INT("&pix not defined", __func__, 1 );
438
439 if ((buffer = bbufferCreate(NULL, 0)) == NULL)
440 return ERROR_INT("failed to create buffer", __func__, 1);
441
442 if ((gif = EGifOpen((void*)buffer, gifWriteFunc, NULL)) == NULL) {
443 bbufferDestroy(&buffer);
444 return ERROR_INT("failed to create GIF image handle", __func__, 1);
445 }
446
447 result = pixToGif(pix, gif);
448 EGifCloseFile(gif, &giferr);
449
450 if (result == 0) {
451 *pdata = bbufferDestroyAndSaveData(&buffer, psize);
452 } else {
453 bbufferDestroy(&buffer);
454 }
455 return result;
456 }
457
458
459 static l_int32
460 gifWriteFunc(GifFileType *gif,
461 const GifByteType *src,
462 l_int32 bytesToWrite)
463 {
464 L_BBUFFER *buffer;
465
466 if ((buffer = (L_BBUFFER*)gif->UserData) == NULL)
467 return ERROR_INT("UserData not set", __func__, -1);
468
469 if(bbufferRead(buffer, (l_uint8*)src, bytesToWrite) == 0)
470 return bytesToWrite;
471 return 0;
472 }
473
474
475 /*!
476 * \brief pixToGif()
477 *
478 * \param[in] pix 1, 2, 4, 8, 16 or 32 bpp
479 * \param[in] gif opened gif stream
480 * \return 0 if OK, 1 on error
481 *
482 * <pre>
483 * Notes:
484 * (1) This encodes the pix to the gif stream. The stream is not
485 * closed by this function.
486 * (2) It is static to make this function private.
487 * </pre>
488 */
489 static l_int32
490 pixToGif(PIX *pix,
491 GifFileType *gif)
492 {
493 char *text;
494 l_int32 wpl, i, j, w, h, d, ncolor, rval, gval, bval, valid;
495 l_int32 gif_ncolor = 0;
496 l_uint32 *data, *line;
497 PIX *pixd;
498 PIXCMAP *cmap;
499 ColorMapObject *gif_cmap;
500 GifByteType *gif_line;
501
502 if (!pix)
503 return ERROR_INT("pix not defined", __func__, 1);
504 if (!gif)
505 return ERROR_INT("gif not defined", __func__, 1);
506
507 d = pixGetDepth(pix);
508 if (d == 32) {
509 pixd = pixConvertRGBToColormap(pix, 1);
510 } else if (d > 1) {
511 pixd = pixConvertTo8(pix, TRUE);
512 } else { /* d == 1; make sure there's a colormap */
513 pixd = pixClone(pix);
514 if (!pixGetColormap(pixd)) {
515 cmap = pixcmapCreate(1);
516 pixcmapAddColor(cmap, 255, 255, 255);
517 pixcmapAddColor(cmap, 0, 0, 0);
518 pixSetColormap(pixd, cmap);
519 }
520 }
521
522 if (!pixd)
523 return ERROR_INT("failed to convert to colormapped pix", __func__, 1);
524 d = pixGetDepth(pixd);
525 cmap = pixGetColormap(pixd);
526 if (!cmap) {
527 pixDestroy(&pixd);
528 return ERROR_INT("cmap is missing", __func__, 1);
529 }
530 pixcmapIsValid(cmap, pixd, &valid);
531 if (!valid) {
532 pixDestroy(&pixd);
533 return ERROR_INT("colormap is not valid", __func__, 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", __func__, 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", __func__, 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 __func__, 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", __func__, 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", __func__, 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}", __func__, 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", __func__, 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", __func__, 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", __func__);
633 }
634
635 LEPT_FREE(gif_line);
636 pixDestroy(&pixd);
637 return 0;
638 }
639
640
641 #if 0
642 /*---------------------------------------------------------------------*
643 * Removing interlacing (reference only; not used) *
644 *---------------------------------------------------------------------*/
645 /* GIF supports 4-way interlacing by raster lines.
646 * Before 5.0, it was necessary for leptonica to restore interlaced
647 * data to normal raster order when reading to a pix. With 5.0,
648 * the de-interlacing is done by the library read function.
649 * It is here only as a reference. */
650 static const l_int32 InterlacedOffset[] = {0, 4, 2, 1};
651 static const l_int32 InterlacedJumps[] = {8, 8, 4, 2};
652
653 static PIX *
654 pixUninterlaceGIF(PIX *pixs)
655 {
656 l_int32 w, h, d, wpl, j, k, srow, drow;
657 l_uint32 *datas, *datad, *lines, *lined;
658 PIX *pixd;
659
660 if (!pixs)
661 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
662
663 pixGetDimensions(pixs, &w, &h, &d);
664 wpl = pixGetWpl(pixs);
665 pixd = pixCreateTemplate(pixs);
666 datas = pixGetData(pixs);
667 datad = pixGetData(pixd);
668 for (k = 0, srow = 0; k < 4; k++) {
669 for (drow = InterlacedOffset[k]; drow < h;
670 drow += InterlacedJumps[k], srow++) {
671 lines = datas + srow * wpl;
672 lined = datad + drow * wpl;
673 for (j = 0; j < w; j++)
674 memcpy(lined, lines, 4 * wpl);
675 }
676 }
677
678 return pixd;
679 }
680 #endif
681
682
683 /* -----------------------------------------------------------------*/
684 #endif /* HAVE_LIBGIF || HAVE_LIBUNGIF */