diff 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
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mupdf-source/thirdparty/leptonica/src/gifio.c	Mon Sep 15 11:43:07 2025 +0200
@@ -0,0 +1,684 @@
+/*====================================================================*
+ -  Copyright (C) 2001 Leptonica.  All rights reserved.
+ -
+ -  Redistribution and use in source and binary forms, with or without
+ -  modification, are permitted provided that the following conditions
+ -  are met:
+ -  1. Redistributions of source code must retain the above copyright
+ -     notice, this list of conditions and the following disclaimer.
+ -  2. Redistributions in binary form must reproduce the above
+ -     copyright notice, this list of conditions and the following
+ -     disclaimer in the documentation and/or other materials
+ -     provided with the distribution.
+ -
+ -  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ -  ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ -  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ -  A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL ANY
+ -  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ -  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ -  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ -  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ -  OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ -  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ -  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *====================================================================*/
+
+/*!
+ * \file gifio.c
+ * <pre>
+ *
+ *    Reading gif
+ *          PIX            *pixReadStreamGif()
+ *          PIX            *pixReadMemGif()
+ *          static l_int32  gifReadFunc()
+ *          static PIX     *gifToPix()
+ *
+ *    Writing gif
+ *          l_int32         pixWriteStreamGif()
+ *          l_int32         pixWriteMemGif()
+ *          static l_int32  gifWriteFunc()
+ *          static l_int32  pixToGif()
+ *
+ *    The initial version of this module was generously contribued by
+ *    Antony Dovgal.
+ *
+ *    The functions that read and write from pix to gif-compressed memory,
+ *    using gif internal functions DGifOpen() and EGifOpen() that are
+ *    available in 5.1 and later, were contributed by Tobias Peirick.
+ *
+ *    Version information:
+ *
+ *    (1) This supports the gif library, version 5.1 or later, for which
+ *        gif read-from-mem and write-to-mem allow these operations
+ *        without writing temporary files.
+ *    (2) There has never been a gif stream interface.  For versions
+ *        before 5.1, it was necessary to use a file descriptor, and to
+ *        generate a file stream from the low-level descriptor.  With the
+ *        memory interface in 5.1 that can be used on all platforms, it
+ *        is no longer necessary to use any API code with file descriptors.
+ *    (3) The public interface changed with 5.0 and with 5.1, and we
+ *        no longer support 4.6.1 and 5.0.
+ *    (4) Version 5.1.2 came out on Jan 7, 2016.  Leptonica cannot
+ *        successfully read gif files that it writes with this version;
+ *        DGifSlurp() gets an internal error from an uninitialized array
+ *        and returns failure.  The problem was fixed in 5.1.3.
+ *
+ *    Limitations:
+ *
+ *    (1) We do not support animated gif.  If the gif has more than one image,
+ *        an error message is returned.
+ *
+ * </pre>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config_auto.h>
+#endif  /* HAVE_CONFIG_H */
+
+#include <string.h>
+#include "allheaders.h"
+
+/* --------------------------------------------------------------------*/
+#if  HAVE_LIBGIF  || HAVE_LIBUNGIF             /* defined in environ.h */
+/* --------------------------------------------------------------------*/
+
+#include "gif_lib.h"
+
+    /* Interface that enables low-level GIF support for reading from memory */
+static PIX * gifToPix(GifFileType *gif);
+    /* Interface that enables low-level GIF support for writing to memory */
+static l_int32 pixToGif(PIX *pix, GifFileType *gif);
+
+    /*! For in-memory decoding of GIF; 5.1+ */
+typedef struct GifReadBuffer
+{
+    size_t            size;    /*!< size of buffer                           */
+    size_t            pos;     /*!< position relative to beginning of buffer */
+    const l_uint8    *cdata;   /*!< data in the buffer                       */
+} GifReadBuffer;
+
+    /*! Low-level callback for in-memory decoding */
+static l_int32  gifReadFunc(GifFileType *gif, GifByteType *dest,
+                            l_int32 bytesToRead);
+    /*! Low-level callback for in-memory encoding */
+static l_int32  gifWriteFunc(GifFileType *gif, const GifByteType *src,
+                             l_int32 bytesToWrite);
+
+
+/*---------------------------------------------------------------------*
+ *                            Reading gif                              *
+ *---------------------------------------------------------------------*/
+/*!
+ * \brief   pixReadStreamGif()
+ *
+ * \param[in]  fp   file stream opened for reading
+ * \return  pix, or NULL on error
+ */
+PIX *
+pixReadStreamGif(FILE  *fp)
+{
+l_uint8  *filedata;
+size_t    filesize;
+PIX      *pix;
+
+    if (!fp)
+        return (PIX *)ERROR_PTR("fp not defined", __func__, NULL);
+
+        /* Read data into memory from file */
+    rewind(fp);
+    if ((filedata = l_binaryReadStream(fp, &filesize)) == NULL)
+        return (PIX *)ERROR_PTR("filedata not read", __func__, NULL);
+
+        /* Uncompress from memory */
+    pix = pixReadMemGif(filedata, filesize);
+    LEPT_FREE(filedata);
+    if (!pix)
+        L_ERROR("failed to read gif from file data\n", __func__);
+    return pix;
+}
+
+
+/*!
+ * \brief   pixReadMemGif()
+ *
+ * \param[in]  cdata    const; gif-encoded
+ * \param[in]  size     bytes data
+ * \return  pix, or NULL on error
+ *
+ * <pre>
+ * Notes:
+ *     (1) For libgif version >= 5.1, this uses the DGifOpen() buffer
+ *         interface.  No temp files are required.
+ *     (2) For libgif version < 5.1, it was necessary to write the compressed
+ *         data to file and read it back, and we couldn't use the GNU
+ *         runtime extension fmemopen() because libgif doesn't have a file
+ *         stream interface.
+ * </pre>
+ */
+PIX *
+pixReadMemGif(const l_uint8  *cdata,
+              size_t          size)
+{
+GifFileType   *gif;
+GifReadBuffer  buffer;
+
+        /* 5.1+ and not 5.1.2 */
+#if (GIFLIB_MAJOR < 5 || (GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 0))
+    L_ERROR("Require giflib-5.1 or later\n", __func__);
+    return NULL;
+#endif  /* < 5.1 */
+#if GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 1 && GIFLIB_RELEASE == 2  /* 5.1.2 */
+    L_ERROR("Can't use giflib-5.1.2; suggest 5.1.3 or later\n", __func__);
+    return NULL;
+#endif  /* 5.1.2 */
+
+    if (!cdata)
+        return (PIX *)ERROR_PTR("cdata not defined", __func__, NULL);
+
+    buffer.cdata = cdata;
+    buffer.size = size;
+    buffer.pos = 0;
+    if ((gif = DGifOpen((void*)&buffer, gifReadFunc, NULL)) == NULL)
+        return (PIX *)ERROR_PTR("could not open gif stream from memory",
+                                __func__, NULL);
+
+    return gifToPix(gif);
+}
+
+
+static l_int32
+gifReadFunc(GifFileType  *gif,
+            GifByteType  *dest,
+            l_int32       bytesToRead)
+{
+GifReadBuffer  *buffer;
+l_int32         bytesRead;
+
+    if ((buffer = (GifReadBuffer*)gif->UserData) == NULL)
+        return ERROR_INT("UserData not set", __func__, -1);
+
+    if(buffer->pos >= buffer->size || bytesToRead > buffer->size)
+        return -1;
+
+    bytesRead = (buffer->pos < buffer->size - bytesToRead)
+              ? bytesToRead : buffer->size - buffer->pos;
+    memcpy(dest, buffer->cdata + buffer->pos, bytesRead);
+    buffer->pos += bytesRead;
+    return bytesRead;
+}
+
+
+/*!
+ * \brief   gifToPix()
+ *
+ * \param[in]  gif   opened gif stream
+ * \return  pix, or NULL on error
+ *
+ * <pre>
+ * Notes:
+ *      (1) This decodes the pix from the compressed gif stream and
+ *          closes the stream.
+ *      (2) It is static so that the stream is not exposed to clients.
+ *      (3) Leptonica does not support gifanim (more than 1 image in the file).
+ * </pre>
+ */
+static PIX *
+gifToPix(GifFileType  *gif)
+{
+l_int32          wpl, i, j, w, h, d, cindex, ncolors, valid, nimages;
+l_int32          rval, gval, bval;
+l_uint32        *data, *line;
+PIX             *pixd;
+PIXCMAP         *cmap;
+ColorMapObject  *gif_cmap;
+SavedImage       si;
+int              giferr;
+
+        /* Read all the data, but use only the first image found */
+    if (DGifSlurp(gif) != GIF_OK) {
+        DGifCloseFile(gif, &giferr);
+        return (PIX *)ERROR_PTR("failed to read GIF data", __func__, NULL);
+    }
+
+    if (gif->SavedImages == NULL) {
+        DGifCloseFile(gif, &giferr);
+        return (PIX *)ERROR_PTR("no images found in GIF", __func__, NULL);
+    }
+
+    nimages = gif->ImageCount;
+    if (nimages > 1) {
+        DGifCloseFile(gif, &giferr);
+        L_ERROR("There are %d images in the file; gifanim is not supported\n",
+                __func__, nimages);
+        return NULL;
+    }
+
+    si = gif->SavedImages[0];
+    w = si.ImageDesc.Width;
+    h = si.ImageDesc.Height;
+    if (w <= 0 || h <= 0) {
+        DGifCloseFile(gif, &giferr);
+        return (PIX *)ERROR_PTR("invalid image dimensions", __func__, NULL);
+    }
+
+    if (si.RasterBits == NULL) {
+        DGifCloseFile(gif, &giferr);
+        return (PIX *)ERROR_PTR("no raster data in GIF", __func__, NULL);
+    }
+
+    if (si.ImageDesc.ColorMap) {
+            /* private cmap for this image */
+        gif_cmap = si.ImageDesc.ColorMap;
+    } else if (gif->SColorMap) {
+            /* global cmap for whole picture */
+        gif_cmap = gif->SColorMap;
+    } else {
+            /* don't know where to take cmap from */
+        DGifCloseFile(gif, &giferr);
+        return (PIX *)ERROR_PTR("color map is missing", __func__, NULL);
+    }
+
+    ncolors = gif_cmap->ColorCount;
+    if (ncolors <= 0 || ncolors > 256) {
+        DGifCloseFile(gif, &giferr);
+        return (PIX *)ERROR_PTR("ncolors is invalid", __func__, NULL);
+    }
+    if (ncolors <= 2)
+        d = 1;
+    else if (ncolors <= 4)
+        d = 2;
+    else if (ncolors <= 16)
+        d = 4;
+    else  /* [17 ... 256] */
+        d = 8;
+    cmap = pixcmapCreate(d);
+    for (cindex = 0; cindex < ncolors; cindex++) {
+        rval = gif_cmap->Colors[cindex].Red;
+        gval = gif_cmap->Colors[cindex].Green;
+        bval = gif_cmap->Colors[cindex].Blue;
+        pixcmapAddColor(cmap, rval, gval, bval);
+    }
+
+    if ((pixd = pixCreate(w, h, d)) == NULL) {
+        DGifCloseFile(gif, &giferr);
+        pixcmapDestroy(&cmap);
+        return (PIX *)ERROR_PTR("failed to allocate pixd", __func__, NULL);
+    }
+    pixSetInputFormat(pixd, IFF_GIF);
+    pixSetColormap(pixd, cmap);
+    pixcmapIsValid(cmap, pixd, &valid);
+    if (!valid) {
+        DGifCloseFile(gif, &giferr);
+        pixDestroy(&pixd);
+        pixcmapDestroy(&cmap);
+        return (PIX *)ERROR_PTR("colormap is invalid", __func__, NULL);
+    }
+
+    wpl = pixGetWpl(pixd);
+    data = pixGetData(pixd);
+    for (i = 0; i < h; i++) {
+        line = data + i * wpl;
+        if (d == 1) {
+            for (j = 0; j < w; j++) {
+                if (si.RasterBits[i * w + j])
+                    SET_DATA_BIT(line, j);
+            }
+        } else if (d == 2) {
+            for (j = 0; j < w; j++)
+                SET_DATA_DIBIT(line, j, si.RasterBits[i * w + j]);
+        } else if (d == 4) {
+            for (j = 0; j < w; j++)
+                SET_DATA_QBIT(line, j, si.RasterBits[i * w + j]);
+        } else {  /* d == 8 */
+            for (j = 0; j < w; j++)
+                SET_DATA_BYTE(line, j, si.RasterBits[i * w + j]);
+        }
+    }
+
+    /* Versions before 5.0 required un-interlacing to restore
+     * the raster lines to normal order if the image
+     * had been interlaced (for viewing in a browser):
+         if (gif->Image.Interlace) {
+             PIX *pixdi = pixUninterlaceGIF(pixd);
+             pixTransferAllData(pixd, &pixdi, 0, 0);
+         }
+     * This is no longer required. */
+
+    DGifCloseFile(gif, &giferr);
+    return pixd;
+}
+
+
+/*---------------------------------------------------------------------*
+ *                            Writing gif                              *
+ *---------------------------------------------------------------------*/
+/*!
+ * \brief   pixWriteStreamGif()
+ *
+ * \param[in]  fp    file stream opened for writing
+ * \param[in]  pix   1, 2, 4, 8, 16 or 32 bpp
+ * \return  0 if OK, 1 on error
+ *
+ * <pre>
+ * Notes:
+ *      (1) All output gif have colormaps.  If the pix is 32 bpp rgb,
+ *          this quantizes the colors and writes out 8 bpp.
+ *          If the pix is 16 bpp grayscale, it converts to 8 bpp first.
+ * </pre>
+ */
+l_ok
+pixWriteStreamGif(FILE  *fp,
+                  PIX   *pix)
+{
+l_uint8  *filedata;
+size_t    filebytes, nbytes;
+
+    if (!fp)
+        return ERROR_INT("stream not open", __func__, 1);
+    if (!pix)
+        return ERROR_INT("pix not defined", __func__, 1);
+
+    pixSetPadBits(pix, 0);
+    if (pixWriteMemGif(&filedata, &filebytes, pix) != 0) {
+        LEPT_FREE(filedata);
+        return ERROR_INT("failure to gif encode pix", __func__, 1);
+    }
+
+    rewind(fp);
+    nbytes = fwrite(filedata, 1, filebytes, fp);
+    LEPT_FREE(filedata);
+    if (nbytes != filebytes)
+        return ERROR_INT("write error", __func__, 1);
+    return 0;
+}
+
+
+/*!
+ * \brief   pixWriteMemGif()
+ *
+ * \param[out]   pdata data of gif compressed image
+ * \param[out]   psize size of returned data
+ * \param[in]    pix
+ * \return  0 if OK, 1 on error
+ *
+ * <pre>
+ * Notes:
+ *      (1) See comments in pixReadMemGif()
+ * </pre>
+ */
+l_ok
+pixWriteMemGif(l_uint8  **pdata,
+               size_t    *psize,
+               PIX       *pix)
+{
+int           giferr;
+l_int32       result;
+GifFileType  *gif;
+L_BBUFFER    *buffer;
+
+        /* 5.1+ and not 5.1.2 */
+#if (GIFLIB_MAJOR < 5 || (GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 0))
+    L_ERROR("Require giflib-5.1 or later\n", __func__);
+    return 1;
+#endif  /* < 5.1 */
+#if GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 1 && GIFLIB_RELEASE == 2  /* 5.1.2 */
+    L_ERROR("Can't use giflib-5.1.2; suggest 5.1.3 or later\n", __func__);
+    return 1;
+#endif  /* 5.1.2 */
+
+    if (!pdata)
+        return ERROR_INT("&data not defined", __func__, 1 );
+    *pdata = NULL;
+    if (!psize)
+        return ERROR_INT("&size not defined", __func__, 1 );
+    *psize = 0;
+    if (!pix)
+        return ERROR_INT("&pix not defined", __func__, 1 );
+
+    if ((buffer = bbufferCreate(NULL, 0)) == NULL)
+        return ERROR_INT("failed to create buffer", __func__, 1);
+
+    if ((gif = EGifOpen((void*)buffer, gifWriteFunc, NULL)) == NULL) {
+        bbufferDestroy(&buffer);
+        return ERROR_INT("failed to create GIF image handle", __func__, 1);
+    }
+
+    result = pixToGif(pix, gif);
+    EGifCloseFile(gif, &giferr);
+
+    if (result == 0) {
+        *pdata = bbufferDestroyAndSaveData(&buffer, psize);
+    } else {
+        bbufferDestroy(&buffer);
+    }
+    return result;
+}
+
+
+static l_int32
+gifWriteFunc(GifFileType        *gif,
+             const GifByteType  *src,
+             l_int32             bytesToWrite)
+{
+L_BBUFFER  *buffer;
+
+    if ((buffer = (L_BBUFFER*)gif->UserData) == NULL)
+        return ERROR_INT("UserData not set", __func__, -1);
+
+    if(bbufferRead(buffer, (l_uint8*)src, bytesToWrite) == 0)
+        return bytesToWrite;
+    return 0;
+}
+
+
+/*!
+ * \brief   pixToGif()
+ *
+ * \param[in]  pix    1, 2, 4, 8, 16 or 32 bpp
+ * \param[in]  gif    opened gif stream
+ * \return  0 if OK, 1 on error
+ *
+ * <pre>
+ * Notes:
+ *      (1) This encodes the pix to the gif stream. The stream is not
+ *          closed by this function.
+ *      (2) It is static to make this function private.
+ * </pre>
+ */
+static l_int32
+pixToGif(PIX          *pix,
+         GifFileType  *gif)
+{
+char            *text;
+l_int32          wpl, i, j, w, h, d, ncolor, rval, gval, bval, valid;
+l_int32          gif_ncolor = 0;
+l_uint32        *data, *line;
+PIX             *pixd;
+PIXCMAP         *cmap;
+ColorMapObject  *gif_cmap;
+GifByteType     *gif_line;
+
+    if (!pix)
+        return ERROR_INT("pix not defined", __func__, 1);
+    if (!gif)
+        return ERROR_INT("gif not defined", __func__, 1);
+
+    d = pixGetDepth(pix);
+    if (d == 32) {
+        pixd = pixConvertRGBToColormap(pix, 1);
+    } else if (d > 1) {
+        pixd = pixConvertTo8(pix, TRUE);
+    } else {  /* d == 1; make sure there's a colormap */
+        pixd = pixClone(pix);
+        if (!pixGetColormap(pixd)) {
+            cmap = pixcmapCreate(1);
+            pixcmapAddColor(cmap, 255, 255, 255);
+            pixcmapAddColor(cmap, 0, 0, 0);
+            pixSetColormap(pixd, cmap);
+        }
+    }
+
+    if (!pixd)
+        return ERROR_INT("failed to convert to colormapped pix", __func__, 1);
+    d = pixGetDepth(pixd);
+    cmap = pixGetColormap(pixd);
+    if (!cmap) {
+        pixDestroy(&pixd);
+        return ERROR_INT("cmap is missing", __func__, 1);
+    }
+    pixcmapIsValid(cmap, pixd, &valid);
+    if (!valid) {
+        pixDestroy(&pixd);
+        return ERROR_INT("colormap is not valid", __func__, 1);
+    }
+
+        /* 'Round' the number of gif colors up to a power of 2 */
+    ncolor = pixcmapGetCount(cmap);
+    for (i = 0; i <= 8; i++) {
+        if ((1 << i) >= ncolor) {
+            gif_ncolor = (1 << i);
+            break;
+        }
+    }
+    if (gif_ncolor < 1) {
+        pixDestroy(&pixd);
+        return ERROR_INT("number of colors is invalid", __func__, 1);
+    }
+
+        /* Save the cmap colors in a gif_cmap */
+    if ((gif_cmap = GifMakeMapObject(gif_ncolor, NULL)) == NULL) {
+        pixDestroy(&pixd);
+        return ERROR_INT("failed to create GIF color map", __func__, 1);
+    }
+    for (i = 0; i < gif_ncolor; i++) {
+        rval = gval = bval = 0;
+        if (ncolor > 0) {
+            if (pixcmapGetColor(cmap, i, &rval, &gval, &bval) != 0) {
+                pixDestroy(&pixd);
+                GifFreeMapObject(gif_cmap);
+                return ERROR_INT("failed to get color from color map",
+                                 __func__, 1);
+            }
+            ncolor--;
+        }
+        gif_cmap->Colors[i].Red = rval;
+        gif_cmap->Colors[i].Green = gval;
+        gif_cmap->Colors[i].Blue = bval;
+    }
+
+    pixGetDimensions(pixd, &w, &h, NULL);
+    if (EGifPutScreenDesc(gif, w, h, gif_cmap->BitsPerPixel, 0, gif_cmap)
+        != GIF_OK) {
+        pixDestroy(&pixd);
+        GifFreeMapObject(gif_cmap);
+        return ERROR_INT("failed to write screen description", __func__, 1);
+    }
+    GifFreeMapObject(gif_cmap); /* not needed after this point */
+
+    if (EGifPutImageDesc(gif, 0, 0, w, h, FALSE, NULL) != GIF_OK) {
+        pixDestroy(&pixd);
+        return ERROR_INT("failed to image screen description", __func__, 1);
+    }
+
+    data = pixGetData(pixd);
+    wpl = pixGetWpl(pixd);
+    if (d != 1 && d != 2 && d != 4 && d != 8) {
+        pixDestroy(&pixd);
+        return ERROR_INT("image depth is not in {1, 2, 4, 8}", __func__, 1);
+    }
+
+    if ((gif_line = (GifByteType *)LEPT_CALLOC(sizeof(GifByteType), w))
+        == NULL) {
+        pixDestroy(&pixd);
+        return ERROR_INT("mem alloc fail for data line", __func__, 1);
+    }
+
+    for (i = 0; i < h; i++) {
+        line = data + i * wpl;
+            /* Gif's way of setting the raster line up for compression */
+        for (j = 0; j < w; j++) {
+            switch(d)
+            {
+            case 8:
+                gif_line[j] = GET_DATA_BYTE(line, j);
+                break;
+            case 4:
+                gif_line[j] = GET_DATA_QBIT(line, j);
+                break;
+            case 2:
+                gif_line[j] = GET_DATA_DIBIT(line, j);
+                break;
+            case 1:
+                gif_line[j] = GET_DATA_BIT(line, j);
+                break;
+            }
+        }
+
+            /* Compress and save the line */
+        if (EGifPutLine(gif, gif_line, w) != GIF_OK) {
+            LEPT_FREE(gif_line);
+            pixDestroy(&pixd);
+            return ERROR_INT("failed to write data line into GIF", __func__, 1);
+        }
+    }
+
+        /* Write a text comment.  This must be placed after writing the
+         * data (!!)  Note that because libgif does not provide a function
+         * for reading comments from file, you will need another way
+         * to read comments. */
+    if ((text = pixGetText(pix)) != NULL) {
+        if (EGifPutComment(gif, text) != GIF_OK)
+            L_WARNING("gif comment not written\n", __func__);
+    }
+
+    LEPT_FREE(gif_line);
+    pixDestroy(&pixd);
+    return 0;
+}
+
+
+#if 0
+/*---------------------------------------------------------------------*
+ *         Removing interlacing (reference only; not used)             *
+ *---------------------------------------------------------------------*/
+    /* GIF supports 4-way interlacing by raster lines.
+     * Before 5.0, it was necessary for leptonica to restore interlaced
+     * data to normal raster order when reading to a pix. With 5.0,
+     * the de-interlacing is done by the library read function.
+     * It is here only as a reference. */
+static const l_int32 InterlacedOffset[] = {0, 4, 2, 1};
+static const l_int32 InterlacedJumps[] = {8, 8, 4, 2};
+
+static PIX *
+pixUninterlaceGIF(PIX  *pixs)
+{
+l_int32    w, h, d, wpl, j, k, srow, drow;
+l_uint32  *datas, *datad, *lines, *lined;
+PIX       *pixd;
+
+    if (!pixs)
+        return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
+
+    pixGetDimensions(pixs, &w, &h, &d);
+    wpl = pixGetWpl(pixs);
+    pixd = pixCreateTemplate(pixs);
+    datas = pixGetData(pixs);
+    datad = pixGetData(pixd);
+    for (k = 0, srow = 0; k < 4; k++) {
+        for (drow = InterlacedOffset[k]; drow < h;
+             drow += InterlacedJumps[k], srow++) {
+            lines = datas + srow * wpl;
+            lined = datad + drow * wpl;
+            for (j = 0; j < w; j++)
+                memcpy(lined, lines, 4 * wpl);
+        }
+    }
+
+    return pixd;
+}
+#endif
+
+
+/* -----------------------------------------------------------------*/
+#endif    /* HAVE_LIBGIF || HAVE_LIBUNGIF  */