diff mupdf-source/thirdparty/leptonica/src/pix1.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/pix1.c	Mon Sep 15 11:43:07 2025 +0200
@@ -0,0 +1,1971 @@
+/*====================================================================*
+ -  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 pix1.c
+ * <pre>
+ *
+ *    The pixN.c {N = 1,2,3,4,5} files are sorted by the type of operation.
+ *    The primary functions in these files are:
+ *
+ *        pix1.c: constructors, destructors and field accessors
+ *        pix2.c: pixel poking of image, pad and border pixels
+ *        pix3.c: masking and logical ops, counting, mirrored tiling
+ *        pix4.c: histograms, statistics, fg/bg estimation
+ *        pix5.c: property measurements, rectangle extraction
+ *
+ *
+ *    This file has the basic constructors, destructors and field accessors
+ *
+ *    Pix memory management (allows custom allocator and deallocator)
+ *          static void  *pixdata_malloc()
+ *          static void   pixdata_free()
+ *          void          setPixMemoryManager()
+ *
+ *    Pix creation
+ *          PIX          *pixCreate()
+ *          PIX          *pixCreateNoInit()
+ *          PIX          *pixCreateTemplate()
+ *          PIX          *pixCreateTemplateNoInit()
+ *          PIX          *pixCreateWithCmap()
+ *          PIX          *pixCreateHeader()
+ *          PIX          *pixClone()
+ *
+ *    Pix destruction
+ *          void          pixDestroy()
+ *          static void   pixFree()
+ *
+ *    Pix copy
+ *          PIX          *pixCopy()
+ *          l_int32       pixResizeImageData()
+ *          l_int32       pixCopyColormap()
+ *          l_int32       pixTransferAllData()
+ *          l_int32       pixSwapAndDestroy()
+ *
+ *    Pix accessors
+ *          l_int32       pixGetWidth()
+ *          l_int32       pixSetWidth()
+ *          l_int32       pixGetHeight()
+ *          l_int32       pixSetHeight()
+ *          l_int32       pixGetDepth()
+ *          l_int32       pixSetDepth()
+ *          l_int32       pixGetDimensions()
+ *          l_int32       pixSetDimensions()
+ *          l_int32       pixCopyDimensions()
+ *          l_int32       pixGetSpp()
+ *          l_int32       pixSetSpp()
+ *          l_int32       pixCopySpp()
+ *          l_int32       pixGetWpl()
+ *          l_int32       pixSetWpl()
+ *          l_uint32      pixGetXRes()
+ *          l_int32       pixSetXRes()
+ *          l_uint32      pixGetYRes()
+ *          l_int32       pixSetYRes()
+ *          l_int32       pixGetResolution()
+ *          l_int32       pixSetResolution()
+ *          l_int32       pixCopyResolution()
+ *          l_int32       pixScaleResolution()
+ *          l_int32       pixGetInputFormat()
+ *          l_int32       pixSetInputFormat()
+ *          l_int32       pixCopyInputFormat()
+ *          l_int32       pixSetSpecial()
+ *          char         *pixGetText()
+ *          l_int32       pixSetText()
+ *          l_int32       pixAddText()
+ *          l_int32       pixCopyText()
+ *          l_uint8      *pixGetTextCompNew()
+ *          l_int32      *pixSetTextCompNew()
+ *          PIXCMAP      *pixGetColormap()
+ *          l_int32       pixSetColormap()
+ *          l_int32       pixDestroyColormap()
+ *          l_uint32     *pixGetData()
+ *          l_int32       pixFreeAndSetData()
+ *          l_int32       pixSetData()
+ *          l_int32       pixFreeData()
+ *          l_uint32     *pixExtractData()
+ *
+ *    Pix line ptrs
+ *          void        **pixGetLinePtrs()
+ *
+ *    Pix size comparisons
+ *          l_int32       pixSizesEqual()
+ *          l_int32       pixMaxAspectRatio()
+ *
+ *    Pix debug
+ *          l_int32       pixPrintStreamInfo()
+ *
+ *
+ *  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ *      Important notes on direct management of pix image data
+ *  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ *
+ *  Custom allocator and deallocator
+ *  --------------------------------
+ *
+ *  At the lowest level, you can specify the function that does the
+ *  allocation and deallocation of the data field in the pix.
+ *  By default, this is malloc and free.  However, by calling
+ *  setPixMemoryManager(), custom functions can be substituted.
+ *  When using this, keep two things in mind:
+ *
+ *   (1) Call setPixMemoryManager() before any pix have been allocated
+ *   (2) Destroy all pix as usual, in order to prevent leaks.
+ *
+ *  In pixalloc.c, we provide an example custom allocator and deallocator.
+ *  To use it, you must call pmsCreate() before any pix have been allocated
+ *  and pmsDestroy() at the end after all pix have been destroyed.
+ *
+ *
+ *  Direct manipulation of the pix data field
+ *  -----------------------------------------
+ *
+ *  Memory management of the (image) data field in the pix is
+ *  handled differently from that in the colormap and text fields.
+ *  For colormap and text, the functions pixSetColormap() and
+ *  pixSetText() remove the existing heap data and insert the
+ *  new data.  For the image data, pixSetData() just reassigns the
+ *  data field; any existing data will be lost if there isn't
+ *  another handle for it.
+ *
+ *  Why is pixSetData() limited in this way?  Because the image
+ *  data can be very large, we need flexible ways to handle it,
+ *  particularly when you want to re-use the data in a different
+ *  context without making a copy.  Here are some different
+ *  things you might want to do:
+ *
+ *  (1) Use pixCopy(pixd, pixs) where pixd is not the same size
+ *      as pixs.  This will remove the data in pixd, allocate a
+ *      new data field in pixd, and copy the data from pixs, leaving
+ *      pixs unchanged.
+ *
+ *  (2) Use pixTransferAllData(pixd, &pixs, ...) to transfer the
+ *      data from pixs to pixd without making a copy of it.  If
+ *      pixs is not cloned, this will do the transfer and destroy pixs.
+ *      But if the refcount of pixs is greater than 1, it just copies
+ *      the data and decrements the ref count.
+ *
+ *  (3) Use pixSwapAndDestroy(pixd, &pixs) to replace pixs by an
+ *      existing pixd.  This is similar to pixTransferAllData(), but
+ *      simpler, in that it never makes any copies and if pixs is
+ *      cloned, the other references are not changed by this operation.
+ *
+ *  (4) Use pixExtractData() to extract the image data from the pix
+ *      without copying if possible.  This could be used, for example,
+ *      to convert from a pix to some other data structure with minimal
+ *      heap allocation.  After the data is extracated, the pixels can
+ *      be munged and used in another context.  However, the danger
+ *      here is that the pix might have a refcount > 1, in which case
+ *      a copy of the data must be made and the input pix left unchanged.
+ *      If there are no clones, the image data can be extracted without
+ *      a copy, and the data ptr in the pix must be nulled before
+ *      destroying it because the pix will no longer 'own' the data.
+ *
+ *  We have provided accessors and functions here that should be
+ *  sufficient so that you can do anything you want without
+ *  explicitly referencing any of the pix member fields.
+ *
+ *  However, to avoid memory smashes and leaks when doing special operations
+ *  on the pix data field, look carefully at the behavior of the image
+ *  data accessors and keep in mind that when you invoke pixDestroy(),
+ *  the pix considers itself the owner of all its heap data.
+ * </pre>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config_auto.h>
+#endif  /* HAVE_CONFIG_H */
+
+#include <string.h>
+#include "allheaders.h"
+#include "pix_internal.h"
+
+static void pixFree(PIX *pix);
+
+/*-------------------------------------------------------------------------*
+ *                        Pix Memory Management                            *
+ *                                                                         *
+ *  These functions give you the freedom to specify at compile or run      *
+ *  time the allocator and deallocator to be used for the pix raster       *
+ *  image data.  They have no effect on any other heap allocation,         *
+ *  including the pix struct itself, which is controlled by the            *
+ *  #defines in environ.h.                                                 *
+ *                                                                         *
+ *  The default functions for allocating pix raster data are malloc and    *
+ *  free (or leptonica_* custom allocators if LEPTONICA_INTERCEPT_ALLOC    *
+ *  is defined).  Use setPixMemoryManager() to specify other functions     *
+ *  to use specifically for pix raster image data.                         *
+ *-------------------------------------------------------------------------*/
+/*! Pix memory manager */
+    /*
+     * <pre>
+     * Notes:
+     *      (1) The allocator and deallocator function types,
+     *          alloc_fn and dealloc_fn, are defined in pix.h.
+     * </pre>
+     */
+struct PixMemoryManager
+{
+    alloc_fn    allocator;
+    dealloc_fn  deallocator;
+};
+
+/*! Default Pix memory manager */
+static struct PixMemoryManager  pix_mem_manager = {
+#ifdef LEPTONICA_INTERCEPT_ALLOC
+    &leptonica_malloc,
+    &leptonica_free
+#else
+    &malloc,
+    &free
+#endif  /* LEPTONICA_INTERCEPT_ALLOC */
+};
+
+static void *
+pixdata_malloc(size_t  size)
+{
+#ifndef _MSC_VER
+    return (*pix_mem_manager.allocator)(size);
+#else  /* _MSC_VER */
+    /* Under MSVC++, pix_mem_manager is initialized after a call to
+     * pixdata_malloc.  Just ignore the custom allocator feature. */
+    return LEPT_MALLOC(size);
+#endif  /* _MSC_VER */
+}
+
+static void
+pixdata_free(void  *ptr)
+{
+#ifndef _MSC_VER
+    (*pix_mem_manager.deallocator)(ptr);
+#else  /* _MSC_VER */
+    /* Under MSVC++, pix_mem_manager is initialized after a call to
+     * pixdata_malloc.  Just ignore the custom allocator feature. */
+    LEPT_FREE(ptr);
+#endif  /* _MSC_VER */
+}
+
+/*!
+ * \brief   setPixMemoryManager()
+ *
+ * \param[in]   allocator    [optional] use NULL to skip
+ * \param[in]   deallocator  [optional] use NULL to skip
+ * \return  void
+ *
+ * <pre>
+ * Notes:
+ *      (1) Use this to change the alloc and/or dealloc functions;
+ *          e.g., setPixMemoryManager(my_malloc, my_free).
+ *      (2) The C99 standard (section 6.7.5.3, par. 8) says:
+ *            A declaration of a parameter as "function returning type"
+ *            shall be adjusted to "pointer to function returning type"
+ *          so that it can be in either of these two forms:
+ *            (a) type (function-ptr(type, ...))
+ *            (b) type ((*function-ptr)(type, ...))
+ *          because form (a) is implicitly converted to form (b), as in the
+ *          definition of struct PixMemoryManager above.  So, for example,
+ *          we should be able to declare either of these:
+ *            (a) void *(allocator(size_t))
+ *            (b) void *((*allocator)(size_t))
+ *          However, MSVC++ only accepts the second version.
+ * </pre>
+ */
+void
+setPixMemoryManager(alloc_fn   allocator,
+                    dealloc_fn deallocator)
+{
+    if (allocator) pix_mem_manager.allocator = allocator;
+    if (deallocator) pix_mem_manager.deallocator = deallocator;
+}
+
+
+/*--------------------------------------------------------------------*
+ *                             Pix Creation                           *
+ *--------------------------------------------------------------------*/
+/*!
+ * \brief   pixCreate()
+ *
+ * \param[in]    width, height, depth
+ * \return  pixd with data allocated and initialized to 0,
+ *                    or NULL on error
+ */
+PIX *
+pixCreate(l_int32  width,
+          l_int32  height,
+          l_int32  depth)
+{
+PIX  *pixd;
+
+    if ((pixd = pixCreateNoInit(width, height, depth)) == NULL)
+        return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
+    memset(pixd->data, 0, 4LL * pixd->wpl * pixd->h);
+    return pixd;
+}
+
+
+/*!
+ * \brief   pixCreateNoInit()
+ *
+ * \param[in]    width, height, depth
+ * \return  pixd with data allocated but not initialized,
+ *                    or NULL on error
+ *
+ * <pre>
+ * Notes:
+ *      (1) Pad bits are set to avoid reading uninitialized data, because
+ *          some optimized routines read from pad bits.
+ *      (2) Initializing memory is very fast, so this optimization is
+ *          not used in the library.
+ * </pre>
+ */
+PIX *
+pixCreateNoInit(l_int32  width,
+                l_int32  height,
+                l_int32  depth)
+{
+l_int32    wpl;
+PIX       *pixd;
+l_uint32  *data;
+
+    if ((pixd = pixCreateHeader(width, height, depth)) == NULL)
+        return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
+    wpl = pixGetWpl(pixd);
+    if ((data = (l_uint32 *)pixdata_malloc(4LL * wpl * height)) == NULL) {
+        pixDestroy(&pixd);
+        return (PIX *)ERROR_PTR("pixdata_malloc fail for data",
+                                __func__, NULL);
+    }
+    pixSetData(pixd, data);
+    pixSetPadBits(pixd, 0);
+    return pixd;
+}
+
+
+/*!
+ * \brief   pixCreateTemplate()
+ *
+ * \param[in]    pixs
+ * \return  pixd, or NULL on error
+ *
+ * <pre>
+ * Notes:
+ *      (1) Makes a Pix of the same size as the input Pix, with the
+ *          data array allocated and initialized to 0.
+ *      (2) Copies the other fields, including colormap if it exists.
+ * </pre>
+ */
+PIX *
+pixCreateTemplate(const PIX  *pixs)
+{
+PIX  *pixd;
+
+    if (!pixs)
+        return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
+
+    if ((pixd = pixCreateTemplateNoInit(pixs)) == NULL)
+        return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
+    memset(pixd->data, 0, 4LL * pixd->wpl * pixd->h);
+    return pixd;
+}
+
+
+/*!
+ * \brief   pixCreateTemplateNoInit()
+ *
+ * \param[in]    pixs
+ * \return  pixd, or NULL on error
+ *
+ * <pre>
+ * Notes:
+ *      (1) Makes a Pix of the same size as the input Pix, with
+ *          the data array allocated but not initialized to 0.
+ *      (2) Copies the other fields, including colormap if it exists.
+ *      (3) Pad bits are set to avoid reading uninitialized data, because
+ *          some optimized routines read from pad bits.
+ *      (4) Initializing memory is very fast, so this optimization is
+ *          not used in the library.
+ * </pre>
+ */
+PIX *
+pixCreateTemplateNoInit(const PIX  *pixs)
+{
+l_int32  w, h, d;
+PIX     *pixd;
+
+    if (!pixs)
+        return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
+
+    pixGetDimensions(pixs, &w, &h, &d);
+    if ((pixd = pixCreateNoInit(w, h, d)) == NULL)
+        return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
+    pixCopySpp(pixd, pixs);
+    pixCopyResolution(pixd, pixs);
+    pixCopyColormap(pixd, pixs);
+    pixCopyText(pixd, pixs);
+    pixCopyInputFormat(pixd, pixs);
+    pixSetPadBits(pixd, 0);
+    return pixd;
+}
+
+
+/*!
+ * \brief   pixCreateWithCmap()
+ *
+ * \param[in]    width
+ * \param[in]    height
+ * \param[in]    depth        2, 4 or 8 bpp
+ * \param[in]    initcolor    L_SET_BLACK, L_SET_WHITE
+ * \return  pixd   with the initialization color assigned to all pixels,
+ *                 or NULL on error.
+ *
+ * <pre>
+ * Notes:
+ *      (1) Creates a pix with a cmap, initialized to value 0.
+ *      (2) Initializes the pix black or white by adding that color
+ *          to the cmap at index 0.
+ * </pre>
+ */
+PIX *
+pixCreateWithCmap(l_int32  width,
+                 l_int32  height,
+                 l_int32  depth,
+                 l_int32  initcolor)
+{
+PIX       *pix;
+PIXCMAP   *cmap;
+
+    if (depth != 2 && depth != 4 && depth != 8)
+        return (PIX *)ERROR_PTR("depth not 2, 4 or 8 bpp", __func__, NULL);
+
+    if ((pix = pixCreate(width, height, depth)) == NULL)
+        return (PIX *)ERROR_PTR("pix not made", __func__, NULL);
+    cmap = pixcmapCreate(depth);
+    pixSetColormap(pix, cmap);
+    if (initcolor == L_SET_BLACK)
+         pixcmapAddColor(cmap, 0, 0, 0);
+    else  /* L_SET_WHITE */
+         pixcmapAddColor(cmap, 255, 255, 255);
+    return pix;
+}
+
+
+/*!
+ * \brief   pixCreateHeader()
+ *
+ * \param[in]    width, height, depth
+ * \return  pixd with no data allocated, or NULL on error
+ *
+ * <pre>
+ * Notes:
+ *      (1) It is assumed that all 32 bit pix have 3 spp.  If there is
+ *          a valid alpha channel, this will be set to 4 spp later.
+ *      (2) All pixCreate*() functions call pixCreateHeader().
+            If the number of bytes to be allocated is larger than the
+ *          maximum value in an int32, we can get overflow, resulting
+ *          in a smaller amount of memory actually being allocated.
+ *          Later, an attempt to access memory that wasn't allocated will
+ *          cause a crash.  So to avoid crashing a program (or worse)
+ *          with bad (or malicious) input, we limit the requested
+ *          allocation of image data in a typesafe way.
+ * </pre>
+ */
+PIX *
+pixCreateHeader(l_int32  width,
+                l_int32  height,
+                l_int32  depth)
+{
+l_int32   wpl;
+l_uint64  wpl64, bignum;
+PIX      *pixd;
+
+    if ((depth != 1) && (depth != 2) && (depth != 4) && (depth != 8)
+         && (depth != 16) && (depth != 24) && (depth != 32))
+        return (PIX *)ERROR_PTR("depth must be {1, 2, 4, 8, 16, 24, 32}",
+                                __func__, NULL);
+    if (width <= 0)
+        return (PIX *)ERROR_PTR("width must be > 0", __func__, NULL);
+    if (height <= 0)
+        return (PIX *)ERROR_PTR("height must be > 0", __func__, NULL);
+
+        /* Avoid overflow in malloc, malicious or otherwise */
+    wpl64 = ((l_uint64)width * (l_uint64)depth + 31) / 32;
+    if (wpl64 > ((1LL << 24) - 1)) {
+        L_ERROR("requested w = %d, h = %d, d = %d\n",
+                __func__, width, height, depth);
+        return (PIX *)ERROR_PTR("wpl >= 2^24", __func__, NULL);
+    }
+    wpl = (l_int32)wpl64;
+    bignum = 4LL * wpl * height;   /* number of bytes to be requested */
+    if (bignum > ((1LL << 31) - 1)) {
+        L_ERROR("requested w = %d, h = %d, d = %d\n",
+                __func__, width, height, depth);
+        return (PIX *)ERROR_PTR("requested bytes >= 2^31", __func__, NULL);
+    }
+
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+    if (bignum > (1LL << 26)) {
+        L_ERROR("fuzzer requested > 64 MB; refused\n", __func__);
+        return NULL;
+    }
+    if (width > 20000) {
+        L_ERROR("fuzzer requested width > 20K; refused\n", __func__);
+        return NULL;
+    }
+    if (height > 20000) {
+        L_ERROR("fuzzer requested height > 20K; refused\n", __func__);
+        return NULL;
+    }
+#endif   /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */
+
+    if ((pixd = (PIX *)LEPT_CALLOC(1, sizeof(PIX))) == NULL)
+        return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
+    pixSetWidth(pixd, width);
+    pixSetHeight(pixd, height);
+    pixSetDepth(pixd, depth);
+    pixSetWpl(pixd, wpl);
+    if (depth == 24 || depth == 32)
+        pixSetSpp(pixd, 3);
+    else
+        pixSetSpp(pixd, 1);
+    pixd->refcount = 1;
+    pixd->informat = IFF_UNKNOWN;
+    return pixd;
+}
+
+
+/*!
+ * \brief   pixClone()
+ *
+ * \param[in]    pixs
+ * \return  same pix ptr, or NULL on error
+ *
+ * <pre>
+ * Notes:
+ *      (1) A "clone" is simply a handle (ptr) to an existing pix.
+ *          It is implemented because (a) images can be large and
+ *          hence expensive to copy, and (b) extra handles to a data
+ *          structure need to be made with a simple policy to avoid
+ *          both double frees and memory leaks.  Pix are reference
+ *          counted.  The side effect of pixClone() is an increase
+ *          by 1 in the ref count.
+ *      (2) The protocol to be used is:
+ *          (a) Whenever you want a new handle to an existing image,
+ *              call pixClone(), which just bumps a ref count.
+ *          (b) Always call pixDestroy() on all handles.  This
+ *              decrements the ref count, nulls the handle, and
+ *              only destroys the pix when pixDestroy() has been
+ *              called on all handles.
+ * </pre>
+ */
+PIX *
+pixClone(PIX  *pixs)
+{
+    if (!pixs)
+        return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
+    ++pixs->refcount;
+
+    return pixs;
+}
+
+
+/*--------------------------------------------------------------------*
+ *                           Pix Destruction                          *
+ *--------------------------------------------------------------------*/
+/*!
+ * \brief   pixDestroy()
+ *
+ * \param[in,out]   ppix     will be set to null before returning
+ * \return  void
+ *
+ * <pre>
+ * Notes:
+ *      (1) Decrements the ref count and, if 0, destroys the pix.
+ *      (2) Always nulls the input ptr.
+ * </pre>
+ */
+void
+pixDestroy(PIX  **ppix)
+{
+PIX  *pix;
+
+    if (!ppix) {
+        L_WARNING("ptr address is null!\n", __func__);
+        return;
+    }
+
+    if ((pix = *ppix) == NULL)
+        return;
+    pixFree(pix);
+    *ppix = NULL;
+}
+
+
+/*!
+ * \brief   pixFree()
+ *
+ * \param[in]    pix
+ * \return  void
+ *
+ * <pre>
+ * Notes:
+ *      (1) Decrements the ref count and, if 0, destroys the pix.
+ * </pre>
+ */
+static void
+pixFree(PIX  *pix)
+{
+l_uint32  *data;
+char      *text;
+
+    if (!pix) return;
+
+    if (--pix->refcount == 0) {
+        if ((data = pixGetData(pix)) != NULL)
+            pixdata_free(data);
+        if ((text = pixGetText(pix)) != NULL)
+            LEPT_FREE(text);
+        pixDestroyColormap(pix);
+        LEPT_FREE(pix);
+    }
+    return;
+}
+
+
+/*-------------------------------------------------------------------------*
+ *                                 Pix Copy                                *
+ *-------------------------------------------------------------------------*/
+/*!
+ * \brief   pixCopy()
+ *
+ * \param[in]    pixd   [optional] can be null, equal to pixs,
+ *                      different from pixs
+ * \param[in]    pixs
+ * \return  pixd, or NULL on error
+ *
+ * <pre>
+ * Notes:
+ *      (1) There are three cases:
+ *            (a) pixd == null  (makes a new pix; refcount = 1)
+ *            (b) pixd == pixs  (no-op)
+ *            (c) pixd != pixs  (data copy; no change in refcount)
+ *          If the refcount of pixd > 1, case (c) will side-effect
+ *          these handles.
+ *      (2) The general pattern of use is:
+ *             pixd = pixCopy(pixd, pixs);
+ *          This will work for all three cases.
+ *          For clarity when the case is known, you can use:
+ *            (a) pixd = pixCopy(NULL, pixs);
+ *            (c) pixCopy(pixd, pixs);
+ *      (3) For case (c), we check if pixs and pixd are the same
+ *          size (w,h,d).  If so, the data is copied directly.
+ *          Otherwise, the data is reallocated to the correct size
+ *          and the copy proceeds.  The refcount of pixd is unchanged.
+ *      (4) This operation, like all others that may involve a pre-existing
+ *          pixd, will side-effect any existing clones of pixd.
+ * </pre>
+ */
+PIX *
+pixCopy(PIX        *pixd,   /* can be null */
+        const PIX  *pixs)
+{
+l_int32  bytes;
+
+    if (!pixs)
+        return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
+    if (pixs == pixd)
+        return pixd;
+
+        /* Total bytes in image data */
+    bytes = 4 * pixGetWpl(pixs) * pixGetHeight(pixs);
+
+        /* If we're making a new pix ... */
+    if (!pixd) {
+        if ((pixd = pixCreateTemplate(pixs)) == NULL)
+            return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
+        memcpy(pixd->data, pixs->data, bytes);
+        return pixd;
+    }
+
+        /* Reallocate image data if sizes are different.  If this fails,
+         * pixd hasn't been changed.  But we want to signal that the copy
+         * failed, so return NULL.  This will cause a memory leak if the
+         * return ptr is assigned to pixd, but that is preferred to proceeding
+         * with an incorrect pixd, and in any event this use case of
+         * pixCopy() -- reallocating into an existing pix -- is infrequent.  */
+    if (pixResizeImageData(pixd, pixs) == 1)
+        return (PIX *)ERROR_PTR("reallocation of data failed", __func__, NULL);
+
+        /* Copy non-image data fields */
+    pixCopyColormap(pixd, pixs);
+    pixCopySpp(pixd, pixs);
+    pixCopyResolution(pixd, pixs);
+    pixCopyInputFormat(pixd, pixs);
+    pixCopyText(pixd, pixs);
+
+        /* Copy image data */
+    memcpy(pixd->data, pixs->data, bytes);
+    return pixd;
+}
+
+
+/*!
+ * \brief   pixResizeImageData()
+ *
+ * \param[in]   pixd   gets new uninitialized buffer for image data
+ * \param[in]   pixs   determines the size of the buffer; not changed
+ * \return  0 if OK, 1 on error
+ *
+ * <pre>
+ * Notes:
+ *      (1) If the sizes of data in pixs and pixd are unequal, this
+ *          frees the existing image data in pixd and allocates
+ *          an uninitialized buffer that will hold the required amount
+ *          of image data in pixs.  The image data from pixs is not
+ *          copied into the new buffer.
+ *      (2) On failure to allocate, pixd is unchanged.
+ * </pre>
+ */
+l_ok
+pixResizeImageData(PIX        *pixd,
+                   const PIX  *pixs)
+{
+l_int32    w, h, d, wpl, bytes;
+l_uint32  *data;
+
+    if (!pixs)
+        return ERROR_INT("pixs not defined", __func__, 1);
+    if (!pixd)
+        return ERROR_INT("pixd not defined", __func__, 1);
+
+    if (pixSizesEqual(pixs, pixd))  /* nothing to do */
+        return 0;
+
+        /* Make sure we can copy the data */
+    pixGetDimensions(pixs, &w, &h, &d);
+    wpl = pixGetWpl(pixs);
+    bytes = 4 * wpl * h;
+    if ((data = (l_uint32 *)pixdata_malloc(bytes)) == NULL)
+        return ERROR_INT("pixdata_malloc fail for data", __func__, 1);
+
+        /* OK, do it */
+    pixSetWidth(pixd, w);
+    pixSetHeight(pixd, h);
+    pixSetDepth(pixd, d);
+    pixSetWpl(pixd, wpl);
+    pixFreeAndSetData(pixd, data);  /* free old data and assign new data */
+    pixCopyResolution(pixd, pixs);
+    return 0;
+}
+
+
+/*!
+ * \brief   pixCopyColormap()
+ *
+ * \param[in]   pixd
+ * \param[in]   pixs   copies the colormap to %pixd
+ * \return  0 if OK, 1 on error
+ *
+ * <pre>
+ * Notes:
+ *      (1) This destroys the colormap in pixd, unless the operation is a no-op
+ * </pre>
+ */
+l_ok
+pixCopyColormap(PIX        *pixd,
+                const PIX  *pixs)
+{
+l_int32         valid;
+const PIXCMAP  *cmaps;
+PIXCMAP        *cmapd;
+
+    if (!pixs)
+        return ERROR_INT("pixs not defined", __func__, 1);
+    if (!pixd)
+        return ERROR_INT("pixd not defined", __func__, 1);
+    if (pixs == pixd)
+        return 0;   /* no-op */
+    if (pixGetDepth(pixs) != pixGetDepth(pixd))
+        return ERROR_INT("depths of pixs and pixd differ", __func__, 1);
+
+    pixDestroyColormap(pixd);
+    if ((cmaps = pixs->colormap) == NULL)  /* not an error */
+        return 0;
+    pixcmapIsValid(cmaps, NULL, &valid);
+    if (!valid)
+        return ERROR_INT("cmap not valid", __func__, 1);
+
+    if ((cmapd = pixcmapCopy(cmaps)) == NULL)
+        return ERROR_INT("cmapd not made", __func__, 1);
+    pixSetColormap(pixd, cmapd);
+    return 0;
+}
+
+
+/*!
+ * \brief   pixTransferAllData()
+ *
+ * \param[in]      pixd        must be different from pixs
+ * \param[in,out]  ppixs       will be nulled if refcount goes to 0
+ * \param[in]      copytext    1 to copy the text field; 0 to skip
+ * \param[in]      copyformat  1 to copy the informat field; 0 to skip
+ * \return  0 if OK, 1 on error
+ *
+ * <pre>
+ * Notes:
+ *      (1) This does a complete data transfer from pixs to pixd,
+ *          followed by the destruction of pixs (refcount permitting).
+ *      (2) If the refcount of pixs is 1, pixs is destroyed.  Otherwise,
+ *          the data in pixs is copied (rather than transferred) to pixd.
+ *      (3) This operation, like all others with a pre-existing pixd,
+ *          will side-effect any existing clones of pixd.  The pixd
+ *          refcount does not change.
+ *      (4) When might you use this?  Suppose you have an in-place Pix
+ *          function (returning void) with the typical signature:
+ *              void function-inplace(PIX *pix, ...)
+ *          where "..." are non-pointer input parameters, and suppose
+ *          further that you sometimes want to return an arbitrary Pix
+ *          in place of the input Pix.  There are two ways you can do this:
+ *          (a) The straightforward way is to change the function
+ *              signature to take the address of the Pix ptr:
+ * \code
+ *                  void function-inplace(PIX **ppix, ...) {
+ *                      PIX *pixt = function-makenew(*ppix);
+ *                      pixDestroy(ppix);
+ *                      *ppix = pixt;
+ *                      return;
+ *                  }
+ * \endcode
+ *              Here, the input and returned pix are different, as viewed
+ *              by the calling function, and the inplace function is
+ *              expected to destroy the input pix to avoid a memory leak.
+ *          (b) Keep the signature the same and use pixTransferAllData()
+ *              to return the new Pix in the input Pix struct:
+ * \code
+ *                  void function-inplace(PIX *pix, ...) {
+ *                      PIX *pixt = function-makenew(pix);
+ *                      pixTransferAllData(pix, &pixt, 0, 0);
+ *                               // pixDestroy() is called on pixt
+ *                      return;
+ *                  }
+ * \endcode
+ *              Here, the input and returned pix are the same, as viewed
+ *              by the calling function, and the inplace function must
+ *              never destroy the input pix, because the calling function
+ *              maintains an unchanged handle to it.
+ * </pre>
+ */
+l_ok
+pixTransferAllData(PIX     *pixd,
+                   PIX    **ppixs,
+                   l_int32  copytext,
+                   l_int32  copyformat)
+{
+l_int32  nbytes;
+PIX     *pixs;
+
+    if (!ppixs)
+        return ERROR_INT("&pixs not defined", __func__, 1);
+    if ((pixs = *ppixs) == NULL)
+        return ERROR_INT("pixs not defined", __func__, 1);
+    if (!pixd)
+        return ERROR_INT("pixd not defined", __func__, 1);
+    if (pixs == pixd)  /* no-op */
+        return ERROR_INT("pixd == pixs", __func__, 1);
+
+    if (pixs->refcount == 1) {  /* transfer the data, cmap, text */
+        pixFreeData(pixd);  /* dealloc any existing data */
+        pixSetData(pixd, pixGetData(pixs));  /* transfer new data from pixs */
+        pixs->data = NULL;  /* pixs no longer owns data */
+        pixDestroyColormap(pixd);  /* free the old one, if it exists */
+        pixd->colormap = pixGetColormap(pixs);  /* transfer to pixd */
+        pixs->colormap = NULL;  /* pixs no longer owns colormap */
+        if (copytext) {
+            pixSetText(pixd, pixGetText(pixs));
+            pixSetText(pixs, NULL);
+        }
+    } else {  /* preserve pixs by making a copy of the data, cmap, text */
+        pixResizeImageData(pixd, pixs);
+        nbytes = 4 * pixGetWpl(pixs) * pixGetHeight(pixs);
+        memcpy(pixGetData(pixd), pixGetData(pixs), nbytes);
+        pixCopyColormap(pixd, pixs);
+        if (copytext)
+            pixCopyText(pixd, pixs);
+    }
+
+    pixCopySpp(pixd, pixs);
+    pixCopyResolution(pixd, pixs);
+    pixCopyDimensions(pixd, pixs);
+    if (copyformat)
+        pixCopyInputFormat(pixd, pixs);
+
+        /* This will destroy pixs if data was transferred;
+         * otherwise, it just decrements its refcount. */
+    pixDestroy(ppixs);
+    return 0;
+}
+
+
+/*!
+ * \brief   pixSwapAndDestroy()
+ *
+ * \param[out]     ppixd   [optional] input pixd can be null,
+ *                         and it must be different from pixs
+ * \param[in,out]  ppixs   will be nulled after the swap
+ * \return  0 if OK, 1 on error
+ *
+ * <pre>
+ * Notes:
+ *      (1) Simple operation to change the handle name safely.
+ *          After this operation, the original image in pixd has
+ *          been destroyed, pixd points to what was pixs, and
+ *          the input pixs ptr has been nulled.
+ *      (2) This works safely whether or not pixs and pixd are cloned.
+ *          If pixs is cloned, the other handles still point to
+ *          the original image, with the ref count reduced by 1.
+ *      (3) Usage example:
+ * \code
+ *            Pix *pix1 = pixRead("...");
+ *            Pix *pix2 = function(pix1, ...);
+ *            pixSwapAndDestroy(&pix1, &pix2);
+ *            pixDestroy(&pix1);  // holds what was in pix2
+ * \endcode
+ *          Example with clones ([] shows ref count of image generated
+ *                               by the function):
+ * \code
+ *            Pix *pixs = pixRead("...");
+ *            Pix *pix1 = pixClone(pixs);
+ *            Pix *pix2 = function(pix1, ...);   [1]
+ *            Pix *pix3 = pixClone(pix2);   [1] --> [2]
+ *            pixSwapAndDestroy(&pix1, &pix2);
+ *            pixDestroy(&pixs);  // still holds read image
+ *            pixDestroy(&pix1);  // holds what was in pix2  [2] --> [1]
+ *            pixDestroy(&pix3);  // holds what was in pix2  [1] --> [0]
+ * \endcode
+ * </pre>
+ */
+l_ok
+pixSwapAndDestroy(PIX  **ppixd,
+                  PIX  **ppixs)
+{
+    if (!ppixd)
+        return ERROR_INT("&pixd not defined", __func__, 1);
+    if (!ppixs)
+        return ERROR_INT("&pixs not defined", __func__, 1);
+    if (*ppixs == NULL)
+        return ERROR_INT("pixs not defined", __func__, 1);
+    if (ppixs == ppixd)  /* no-op */
+        return ERROR_INT("&pixd == &pixs", __func__, 1);
+
+    pixDestroy(ppixd);
+    *ppixd = pixClone(*ppixs);
+    pixDestroy(ppixs);
+    return 0;
+}
+
+
+/*--------------------------------------------------------------------*
+ *                              Pix Accessors                         *
+ *--------------------------------------------------------------------*/
+l_int32
+pixGetWidth(const PIX  *pix)
+{
+    if (!pix)
+        return ERROR_INT("pix not defined", __func__, 0);
+
+    return pix->w;
+}
+
+
+l_int32
+pixSetWidth(PIX     *pix,
+            l_int32  width)
+{
+    if (!pix)
+        return ERROR_INT("pix not defined", __func__, 1);
+    if (width < 0) {
+        pix->w = 0;
+        return ERROR_INT("width must be >= 0", __func__, 1);
+    }
+
+    pix->w = width;
+    return 0;
+}
+
+
+l_int32
+pixGetHeight(const PIX  *pix)
+{
+    if (!pix)
+        return ERROR_INT("pix not defined", __func__, 0);
+
+    return pix->h;
+}
+
+
+l_int32
+pixSetHeight(PIX     *pix,
+             l_int32  height)
+{
+    if (!pix)
+        return ERROR_INT("pix not defined", __func__, 1);
+    if (height < 0) {
+        pix->h = 0;
+        return ERROR_INT("h must be >= 0", __func__, 1);
+    }
+
+    pix->h = height;
+    return 0;
+}
+
+
+l_int32
+pixGetDepth(const PIX  *pix)
+{
+    if (!pix)
+        return ERROR_INT("pix not defined", __func__, 0);
+
+    return pix->d;
+}
+
+
+l_int32
+pixSetDepth(PIX     *pix,
+            l_int32  depth)
+{
+    if (!pix)
+        return ERROR_INT("pix not defined", __func__, 1);
+    if (depth < 1)
+        return ERROR_INT("d must be >= 1", __func__, 1);
+
+    pix->d = depth;
+    return 0;
+}
+
+
+/*!
+ * \brief   pixGetDimensions()
+ *
+ * \param[in]    pix
+ * \param[out]   pw, ph, pd    [optional] each can be null
+ * \return  0 if OK, 1 on error
+ */
+l_ok
+pixGetDimensions(const PIX  *pix,
+                 l_int32    *pw,
+                 l_int32    *ph,
+                 l_int32    *pd)
+{
+    if (pw) *pw = 0;
+    if (ph) *ph = 0;
+    if (pd) *pd = 0;
+    if (!pix)
+        return ERROR_INT("pix not defined", __func__, 1);
+    if (pw) *pw = pix->w;
+    if (ph) *ph = pix->h;
+    if (pd) *pd = pix->d;
+    return 0;
+}
+
+
+/*!
+ * \brief   pixSetDimensions()
+ *
+ * \param[in]    pix
+ * \param[in]    w, h, d   use 0 to skip the setting for any of these
+ * \return  0 if OK, 1 on error
+ */
+l_ok
+pixSetDimensions(PIX     *pix,
+                 l_int32  w,
+                 l_int32  h,
+                 l_int32  d)
+{
+    if (!pix)
+        return ERROR_INT("pix not defined", __func__, 1);
+    if (w > 0) pixSetWidth(pix, w);
+    if (h > 0) pixSetHeight(pix, h);
+    if (d > 0) pixSetDepth(pix, d);
+    return 0;
+}
+
+
+/*!
+ * \brief   pixCopyDimensions()
+ *
+ * \param[in]   pixd
+ * \param[in]   pixs
+ * \return  0 if OK, 1 on error
+ */
+l_ok
+pixCopyDimensions(PIX        *pixd,
+                  const PIX  *pixs)
+{
+    if (!pixd)
+        return ERROR_INT("pixd not defined", __func__, 1);
+    if (!pixs)
+        return ERROR_INT("pixs not defined", __func__, 1);
+    if (pixs == pixd)
+        return 0;   /* no-op */
+
+    pixSetWidth(pixd, pixGetWidth(pixs));
+    pixSetHeight(pixd, pixGetHeight(pixs));
+    pixSetDepth(pixd, pixGetDepth(pixs));
+    pixSetWpl(pixd, pixGetWpl(pixs));
+    return 0;
+}
+
+
+l_int32
+pixGetSpp(const PIX  *pix)
+{
+    if (!pix)
+        return ERROR_INT("pix not defined", __func__, 0);
+
+    return pix->spp;
+}
+
+
+/*
+ * \brief   pixSetSpp()
+ *
+ * \param[in]   pix
+ * \param[in]   spp   1, 3 or 4 samples
+ * \return  0 if OK, 1 on error
+ *
+ * <pre>
+ * Notes:
+ *      (1) For a 32 bpp pix, this can be used to ignore the
+ *          alpha sample (spp == 3) or to use it (spp == 4).
+ *          For example, to write a spp == 4 image without the alpha
+ *          sample (as an rgb pix), call pixSetSpp(pix, 3) and
+ *          then write it out as a png.
+ * </pre>
+ */
+l_int32
+pixSetSpp(PIX     *pix,
+          l_int32  spp)
+{
+    if (!pix)
+        return ERROR_INT("pix not defined", __func__, 1);
+    if (spp < 1)
+        return ERROR_INT("spp must be >= 1", __func__, 1);
+
+    pix->spp = spp;
+    return 0;
+}
+
+
+/*!
+ * \brief   pixCopySpp()
+ *
+ * \param[in]   pixd
+ * \param[in]   pixs
+ * \return  0 if OK, 1 on error
+ */
+l_ok
+pixCopySpp(PIX        *pixd,
+           const PIX  *pixs)
+{
+    if (!pixd)
+        return ERROR_INT("pixd not defined", __func__, 1);
+    if (!pixs)
+        return ERROR_INT("pixs not defined", __func__, 1);
+    if (pixs == pixd)
+        return 0;   /* no-op */
+
+    pixSetSpp(pixd, pixGetSpp(pixs));
+    return 0;
+}
+
+
+l_int32
+pixGetWpl(const PIX  *pix)
+{
+    if (!pix)
+        return ERROR_INT("pix not defined", __func__, 0);
+    return pix->wpl;
+}
+
+
+l_int32
+pixSetWpl(PIX     *pix,
+          l_int32  wpl)
+{
+    if (!pix)
+        return ERROR_INT("pix not defined", __func__, 1);
+
+    pix->wpl = wpl;
+    return 0;
+}
+
+
+l_int32
+pixGetXRes(const PIX  *pix)
+{
+    if (!pix)
+        return ERROR_INT("pix not defined", __func__, 0);
+    return pix->xres;
+}
+
+
+l_int32
+pixSetXRes(PIX     *pix,
+           l_int32  res)
+{
+    if (!pix)
+        return ERROR_INT("pix not defined", __func__, 1);
+
+    pix->xres = res;
+    return 0;
+}
+
+
+l_int32
+pixGetYRes(const PIX  *pix)
+{
+    if (!pix)
+        return ERROR_INT("pix not defined", __func__, 0);
+    return pix->yres;
+}
+
+
+l_int32
+pixSetYRes(PIX     *pix,
+           l_int32  res)
+{
+    if (!pix)
+        return ERROR_INT("pix not defined", __func__, 1);
+
+    pix->yres = res;
+    return 0;
+}
+
+
+/*!
+ * \brief   pixGetResolution()
+ *
+ * \param[in]    pix
+ * \param[out]   pxres, pyres   [optional] each can be null
+ * \return  0 if OK, 1 on error
+ */
+l_ok
+pixGetResolution(const PIX  *pix,
+                 l_int32    *pxres,
+                 l_int32    *pyres)
+{
+    if (pxres) *pxres = 0;
+    if (pyres) *pyres = 0;
+    if (!pxres && !pyres)
+        return ERROR_INT("no output requested", __func__, 1);
+    if (!pix)
+        return ERROR_INT("pix not defined", __func__, 1);
+    if (pxres) *pxres = pix->xres;
+    if (pyres) *pyres = pix->yres;
+    return 0;
+}
+
+
+/*!
+ * \brief   pixSetResolution()
+ *
+ * \param[in]   pix
+ * \param[in]   xres, yres   use 0 to skip setting a value for either of these
+ * \return  0 if OK, 1 on error
+ */
+l_ok
+pixSetResolution(PIX     *pix,
+                 l_int32  xres,
+                 l_int32  yres)
+{
+    if (!pix)
+        return ERROR_INT("pix not defined", __func__, 1);
+    if (xres > 0) pix->xres = xres;
+    if (yres > 0) pix->yres = yres;
+    return 0;
+}
+
+
+l_int32
+pixCopyResolution(PIX        *pixd,
+                  const PIX  *pixs)
+{
+    if (!pixs)
+        return ERROR_INT("pixs not defined", __func__, 1);
+    if (!pixd)
+        return ERROR_INT("pixd not defined", __func__, 1);
+    if (pixs == pixd)
+        return 0;   /* no-op */
+
+    pixSetXRes(pixd, pixGetXRes(pixs));
+    pixSetYRes(pixd, pixGetYRes(pixs));
+    return 0;
+}
+
+
+l_int32
+pixScaleResolution(PIX       *pix,
+                   l_float32  xscale,
+                   l_float32  yscale)
+{
+l_float64  xres, yres;
+l_float64  maxres = 100000000.0;
+
+    if (!pix)
+        return ERROR_INT("pix not defined", __func__, 1);
+    if (xscale <= 0 || yscale <= 0)
+        return ERROR_INT("invalid scaling ratio", __func__, 1);
+
+    xres = (l_float64)xscale * (l_float32)(pix->xres) + 0.5;
+    yres = (l_float64)yscale * (l_float32)(pix->yres) + 0.5;
+    pix->xres = (l_uint32)L_MIN(xres, maxres);
+    pix->yres = (l_uint32)L_MIN(yres, maxres);
+    return 0;
+}
+
+
+l_int32
+pixGetInputFormat(const PIX  *pix)
+{
+    if (!pix)
+        return ERROR_INT("pix not defined", __func__, 0);
+    return pix->informat;
+}
+
+
+l_int32
+pixSetInputFormat(PIX     *pix,
+                  l_int32  informat)
+{
+    if (!pix)
+        return ERROR_INT("pix not defined", __func__, 1);
+    pix->informat = informat;
+    return 0;
+}
+
+
+l_int32
+pixCopyInputFormat(PIX        *pixd,
+                   const PIX  *pixs)
+{
+    if (!pixs)
+        return ERROR_INT("pixs not defined", __func__, 1);
+    if (!pixd)
+        return ERROR_INT("pixd not defined", __func__, 1);
+    if (pixs == pixd)
+        return 0;   /* no-op */
+
+    pixSetInputFormat(pixd, pixGetInputFormat(pixs));
+    return 0;
+}
+
+
+l_int32
+pixSetSpecial(PIX     *pix,
+              l_int32  special)
+{
+    if (!pix)
+        return ERROR_INT("pix not defined", __func__, 1);
+    pix->special = special;
+    return 0;
+}
+
+
+/*!
+ * \brief   pixGetText()
+ *
+ * \param[in]   pix
+ * \return  ptr to existing text string
+ *
+ * <pre>
+ * Notes:
+ *      (1) The text string belongs to the pix:
+ *          * the caller must NOT free it
+ *          * it must not be used after the pix is destroyed
+ * </pre>
+ */
+char *
+pixGetText(PIX  *pix)
+{
+    if (!pix)
+        return (char *)ERROR_PTR("pix not defined", __func__, NULL);
+    return pix->text;
+}
+
+
+/*!
+ * \brief   pixSetText()
+ *
+ * \param[in]   pix
+ * \param[in]   textstring   can be null
+ * \return  0 if OK, 1 on error
+ *
+ * <pre>
+ * Notes:
+ *      (1) This removes any existing textstring and puts a copy of
+ *          the input textstring there.
+ * </pre>
+ */
+l_ok
+pixSetText(PIX         *pix,
+           const char  *textstring)
+{
+    if (!pix)
+        return ERROR_INT("pix not defined", __func__, 1);
+
+    stringReplace(&pix->text, textstring);
+    return 0;
+}
+
+
+/*!
+ * \brief   pixAddText()
+ *
+ * \param[in]    pix
+ * \param[in]    textstring   can be null
+ * \return  0 if OK, 1 on error
+ *
+ * <pre>
+ * Notes:
+ *      (1) This adds the new textstring to any existing text.
+ *      (2) Either or both the existing text and the new text
+ *          string can be null.
+ * </pre>
+ */
+l_ok
+pixAddText(PIX         *pix,
+           const char  *textstring)
+{
+char  *newstring;
+
+    if (!pix)
+        return ERROR_INT("pix not defined", __func__, 1);
+
+    newstring = stringJoin(pixGetText(pix), textstring);
+    stringReplace(&pix->text, newstring);
+    LEPT_FREE(newstring);
+    return 0;
+}
+
+
+l_int32
+pixCopyText(PIX        *pixd,
+            const PIX  *pixs)
+{
+    if (!pixs)
+        return ERROR_INT("pixs not defined", __func__, 1);
+    if (!pixd)
+        return ERROR_INT("pixd not defined", __func__, 1);
+    if (pixs == pixd)
+        return 0;   /* no-op */
+
+    pixSetText(pixd, pixs->text);
+    return 0;
+}
+
+
+/*!
+ * \brief   pixGetTextCompNew()
+ *
+ * \param[in]   pix
+ * \param[out]  psize    this number of bytes of returned binary data
+ * \return  ptr to binary data derived from the text string in the pix,
+ *          after decoding and uncompressing
+ *
+ * <pre>
+ * Notes:
+ *      (1) The ascii string in the text field of the input pix was
+ *          previously stored there using pixSetTextCompNew().
+ *      (2) This retrieves the string and performs ascii85 decoding
+ *          followed by decompression on it.  The returned binary data
+ *          is owned by the caller and must be freed.
+ * </pre>
+ */
+l_uint8 *
+pixGetTextCompNew(PIX     *pix,
+                  size_t  *psize)
+{
+char  *str;
+
+    if (!pix)
+        return (l_uint8 *)ERROR_PTR("pix not defined", __func__, NULL);
+    str = pixGetText(pix);
+    return decodeAscii85WithComp(str, strlen(str), psize);
+}
+
+
+/*!
+ * \brief   pixSetTextCompNew()
+ *
+ * \param[in]   pix
+ * \param[in]   data    binary data
+ * \param[in]   size    number of bytes of binary data
+ * \return  0 if OK, 1 on error
+ *
+ * <pre>
+ * Notes:
+ *      (1) This receives binary data and performs compression and ascii85
+ *          encoding on it.  The ascii result is stored in the input pix,
+ *          replacing any string that may be there.
+ *      (2) The input %data can be reconstructed using pixGetTextCompNew().
+ * </pre>
+ */
+l_ok
+pixSetTextCompNew(PIX            *pix,
+                  const l_uint8  *data,
+                  size_t          size)
+{
+size_t  encodesize;  /* ignored */
+
+    if (!pix)
+        return ERROR_INT("pix not defined", __func__, 1);
+
+    LEPT_FREE(pix->text);
+    pix->text = encodeAscii85WithComp(data, size, &encodesize);
+    return 0;
+}
+
+
+PIXCMAP *
+pixGetColormap(PIX  *pix)
+{
+    if (!pix)
+        return (PIXCMAP *)ERROR_PTR("pix not defined", __func__, NULL);
+    return pix->colormap;
+}
+
+
+/*!
+ * \brief   pixSetColormap()
+ *
+ * \param[in]   pix
+ * \param[in]   colormap   optional; can be null.
+ * \return  0 if OK, 1 on error.
+ *
+ * <pre>
+ * Notes:
+ *      (1) If %colormap is not defined, this is a no-op.
+ *      (2) This destroys any existing colormap before assigning the
+ *          new %colormap to %pix.
+ *      (3) If the colormap is not valid, this returns 1.  The caller
+ *          should check if there is a possibility that the pix and
+ *          colormap depths differ.
+ *      (4) This does not do the work of checking pixs for a pixel value
+ *          that is out of bounds for the colormap -- that only needs to
+ *          be done when reading and writing with an I/O library like
+ *          png and gif.
+ *      (5) Because colormaps are not ref counted, the new colormap
+ *          must not belong to any other pix.
+ * </pre>
+ */
+l_ok
+pixSetColormap(PIX      *pix,
+               PIXCMAP  *colormap)
+{
+l_int32  valid;
+
+    if (!pix)
+        return ERROR_INT("pix not defined", __func__, 1);
+    if (!colormap) return 0;
+
+        /* Make sure the colormap doesn't get lost */
+    pixDestroyColormap(pix);
+    pix->colormap = colormap;
+
+    pixcmapIsValid(colormap, NULL, &valid);
+    if (!valid)
+        return ERROR_INT("colormap is not valid", __func__, 1);
+    return 0;
+}
+
+
+/*!
+ * \brief   pixDestroyColormap()
+ *
+ * \param[in]   pix
+ * \return  0 if OK, 1 on error
+ */
+l_ok
+pixDestroyColormap(PIX  *pix)
+{
+PIXCMAP  *cmap;
+
+    if (!pix)
+        return ERROR_INT("pix not defined", __func__, 1);
+
+    if ((cmap = pix->colormap) != NULL) {
+        pixcmapDestroy(&cmap);
+        pix->colormap = NULL;
+    }
+    return 0;
+}
+
+
+/*!
+ * \brief   pixGetData()
+ *
+ * \param[in]    pix
+ * \return  ptr to image data
+ *
+ * <pre>
+ * Notes:
+ *      (1) This gives a new handle for the data.  The data is still
+ *          owned by the pix, so do not call LEPT_FREE() on it.
+ *      (2) This cannot guarantee that the pix data returned will not
+ *          be changed, so %pix cannot be declared const.  And because
+ *          most imaging operations call this for access to the data,
+ *          this prevents them from declaring %pix to be const, even if
+ *          they only use the data for inspection.
+ * </pre>
+ */
+l_uint32 *
+pixGetData(PIX  *pix)
+{
+    if (!pix)
+        return (l_uint32 *)ERROR_PTR("pix not defined", __func__, NULL);
+    return pix->data;
+}
+
+
+/*!
+ * \brief   pixFreeAndSetData()
+ *
+ * \param[in]   pix
+ * \param[in]   data
+ * \return  0 if OK, 1 on error
+ *
+ * <pre>
+ * Notes:
+ *      (1) This frees the existing raster data in the pix and assigns %data.
+ * </pre>
+ */
+l_int32
+pixFreeAndSetData(PIX       *pix,
+                  l_uint32  *data)
+{
+    if (!pix)
+        return ERROR_INT("pix not defined", __func__, 1);
+
+    pixFreeData(pix);
+    pix->data = data;
+    return 0;
+}
+
+
+/*!
+ * \brief   pixSetData()
+ *
+ * \param[in]   pix
+ * \param[in]   data
+ * \return  0 if OK, 1 on error
+ *
+ * <pre>
+ * Notes:
+ *      (1) This does not free any existing data.  To free existing
+ *          data, use pixFreeAndSetData() instead.
+ * </pre>
+ */
+l_int32
+pixSetData(PIX       *pix,
+           l_uint32  *data)
+{
+    if (!pix)
+        return ERROR_INT("pix not defined", __func__, 1);
+
+    pix->data = data;
+    return 0;
+}
+
+
+/*!
+ * \brief   pixFreeData()
+ *
+ * \param[in]   pix
+ * \return  0 if OK, 1 on error
+ *
+ * <pre>
+ * Notes:
+ *      (1) This frees the data and sets the pix data ptr to null.
+ *          It should be used before pixSetData() in the situation where
+ *          you want to free any existing data before doing
+ *          a subsequent assignment with pixSetData().
+ * </pre>
+ */
+l_int32
+pixFreeData(PIX  *pix)
+{
+l_uint32  *data;
+
+    if (!pix)
+        return ERROR_INT("pix not defined", __func__, 1);
+
+    if ((data = pixGetData(pix)) != NULL) {
+        pixdata_free(data);
+        pix->data = NULL;
+    }
+    return 0;
+}
+
+
+/*!
+ * \brief   pixExtractData()
+ *
+ * \param[in]   pix
+ * \return  ptr to data, or null on error
+ *
+ * <pre>
+ * Notes:
+ *      (1) This extracts the pix image data for use in another context.
+ *          The caller still needs to use pixDestroy() on the input pix.
+ *      (2) If refcount == 1, the data is extracted and the
+ *          pix->data ptr is set to NULL.
+ *      (3) If refcount > 1, this simply returns a copy of the data,
+ *          using the pix allocator, and leaving the input pix unchanged.
+ * </pre>
+ */
+l_uint32 *
+pixExtractData(PIX  *pixs)
+{
+l_int32    bytes;
+l_uint32  *data, *datas;
+
+    if (!pixs)
+        return (l_uint32 *)ERROR_PTR("pixs not defined", __func__, NULL);
+
+    if (pixs->refcount == 1) {  /* extract */
+        data = pixGetData(pixs);
+        pixSetData(pixs, NULL);
+    } else {  /* refcount > 1; copy */
+        bytes = 4 * pixGetWpl(pixs) * pixGetHeight(pixs);
+        datas = pixGetData(pixs);
+        if ((data = (l_uint32 *)pixdata_malloc(bytes)) == NULL)
+            return (l_uint32 *)ERROR_PTR("data not made", __func__, NULL);
+        memcpy(data, datas, bytes);
+    }
+
+    return data;
+}
+
+
+/*--------------------------------------------------------------------*
+ *                          Pix line ptrs                             *
+ *--------------------------------------------------------------------*/
+/*!
+ * \brief   pixGetLinePtrs()
+ *
+ * \param[in]    pix
+ * \param[out]   psize   [optional] array size, which is the pix height
+ * \return  array of line ptrs, or NULL on error
+ *
+ * <pre>
+ * Notes:
+ *      (1) This is intended to be used for fast random pixel access.
+ *          For example, for an 8 bpp image,
+ *              val = GET_DATA_BYTE(lines8[i], j);
+ *          is equivalent to, but much faster than,
+ *              pixGetPixel(pix, j, i, &val);
+ *      (2) How much faster?  For 1 bpp, it's from 6 to 10x faster.
+ *          For 8 bpp, it's an amazing 30x faster.  So if you are
+ *          doing random access over a substantial part of the image,
+ *          use this line ptr array.
+ *      (3) When random access is used in conjunction with a stack,
+ *          queue or heap, the overall computation time depends on
+ *          the operations performed on each struct that is popped
+ *          or pushed, and whether we are using a priority queue (O(logn))
+ *          or a queue or stack (O(1)).  For example, for maze search,
+ *          the overall ratio of time for line ptrs vs. pixGet/Set* is
+ *             Maze type     Type                   Time ratio
+ *               binary      queue                     0.4
+ *               gray        heap (priority queue)     0.6
+ *      (4) Because this returns a void** and the accessors take void*,
+ *          the compiler cannot check the pointer types.  It is
+ *          strongly recommended that you adopt a naming scheme for
+ *          the returned ptr arrays that indicates the pixel depth.
+ *          (This follows the original intent of Simonyi's "Hungarian"
+ *          application notation, where naming is used proactively
+ *          to make errors visibly obvious.)  By doing this, you can
+ *          tell by inspection if the correct accessor is used.
+ *          For example, for an 8 bpp pixg:
+ *              void **lineg8 = pixGetLinePtrs(pixg, NULL);
+ *              val = GET_DATA_BYTE(lineg8[i], j);  // fast access; BYTE, 8
+ *              ...
+ *              LEPT_FREE(lineg8);  // don't forget this
+ *      (5) These are convenient for accessing bytes sequentially in an
+ *          8 bpp grayscale image.  People who write image processing code
+ *          on 8 bpp images are accustomed to grabbing pixels directly out
+ *          of the raster array.  Note that for little endians, you first
+ *          need to reverse the byte order in each 32-bit word.
+ *          Here's a typical usage pattern:
+ *              pixEndianByteSwap(pix);   // always safe; no-op on big-endians
+ *              l_uint8 **lineptrs = (l_uint8 **)pixGetLinePtrs(pix, NULL);
+ *              pixGetDimensions(pix, &w, &h, NULL);
+ *              for (i = 0; i < h; i++) {
+ *                  l_uint8 *line = lineptrs[i];
+ *                  for (j = 0; j < w; j++) {
+ *                      val = line[j];
+ *                      ...
+ *                  }
+ *              }
+ *              pixEndianByteSwap(pix);  // restore big-endian order
+ *              LEPT_FREE(lineptrs);
+ *          This can be done even more simply as follows:
+ *              l_uint8 **lineptrs = pixSetupByteProcessing(pix, &w, &h);
+ *              for (i = 0; i < h; i++) {
+ *                  l_uint8 *line = lineptrs[i];
+ *                  for (j = 0; j < w; j++) {
+ *                      val = line[j];
+ *                      ...
+ *                  }
+ *              }
+ *              pixCleanupByteProcessing(pix, lineptrs);
+ * </pre>
+ */
+void **
+pixGetLinePtrs(PIX      *pix,
+               l_int32  *psize)
+{
+l_int32    i, h, wpl;
+l_uint32  *data;
+void     **lines;
+
+    if (psize) *psize = 0;
+    if (!pix)
+        return (void **)ERROR_PTR("pix not defined", __func__, NULL);
+
+    h = pixGetHeight(pix);
+    if (psize) *psize = h;
+    if ((lines = (void **)LEPT_CALLOC(h, sizeof(void *))) == NULL)
+        return (void **)ERROR_PTR("lines not made", __func__, NULL);
+    wpl = pixGetWpl(pix);
+    data = pixGetData(pix);
+    for (i = 0; i < h; i++)
+        lines[i] = (void *)(data + i * wpl);
+
+    return lines;
+}
+
+
+/*--------------------------------------------------------------------*
+ *                         Pix Size Comparisons                       *
+ *--------------------------------------------------------------------*/
+/*!
+ * \brief   pixSizesEqual()
+ *
+ * \param[in]    pix1, pix2
+ * \return  1 if the two pix have same {h, w, d}; 0 otherwise.
+ */
+l_int32
+pixSizesEqual(const PIX  *pix1,
+              const PIX  *pix2)
+{
+    if (!pix1 || !pix2)
+        return ERROR_INT("pix1 and pix2 not both defined", __func__, 0);
+
+    if (pix1 == pix2)
+        return 1;
+
+    if ((pixGetWidth(pix1) != pixGetWidth(pix2)) ||
+        (pixGetHeight(pix1) != pixGetHeight(pix2)) ||
+        (pixGetDepth(pix1) != pixGetDepth(pix2)))
+        return 0;
+    else
+        return 1;
+}
+
+
+/*!
+ * \brief   pixMaxAspectRatio()
+ *
+ * \param[in]    pixs      32 bpp rgb
+ * \param[out]   pratio    max aspect ratio, >= 1.0; -1.0 on error
+ * \return  0 if OK, 1 on error
+ */
+l_ok
+pixMaxAspectRatio(PIX        *pixs,
+                  l_float32  *pratio)
+{
+l_int32  w, h;
+
+    if (!pratio)
+        return ERROR_INT("&ratio not defined", __func__, 1);
+    *pratio = -1.0;
+    if (!pixs)
+        return ERROR_INT("pixs not defined", __func__, 1);
+    pixGetDimensions(pixs, &w, &h, NULL);
+    if (w == 0 || h == 0) {
+        L_ERROR("invalid size: w = %d, h = %d\n", __func__, w, h);
+        return 1;
+    }
+
+    *pratio = L_MAX((l_float32)h / (l_float32)w, (l_float32)w / (l_float32)h);
+    return 0;
+}
+
+
+/*--------------------------------------------------------------------*
+ *                    Print output for debugging                      *
+ *--------------------------------------------------------------------*/
+extern const char *ImageFileFormatExtensions[];
+
+/*!
+ * \brief   pixPrintStreamInfo()
+ *
+ * \param[in]    fp    file stream
+ * \param[in]    pix
+ * \param[in]    text  [optional] identifying string; can be null
+ * \return  0 if OK, 1 on error
+ */
+l_ok
+pixPrintStreamInfo(FILE        *fp,
+                   const PIX   *pix,
+                   const char  *text)
+{
+l_int32         informat;
+const PIXCMAP  *cmap;
+
+    if (!fp)
+        return ERROR_INT("fp not defined", __func__, 1);
+    if (!pix)
+        return ERROR_INT("pix not defined", __func__, 1);
+
+    if (text)
+        fprintf(fp, "  Pix Info for %s:\n", text);
+    fprintf(fp, "    width = %d, height = %d, depth = %d, spp = %d\n",
+            pixGetWidth(pix), pixGetHeight(pix), pixGetDepth(pix),
+            pixGetSpp(pix));
+    fprintf(fp, "    wpl = %d, data = %p, refcount = %d\n",
+            pixGetWpl(pix), pix->data, pix->refcount);
+    fprintf(fp, "    xres = %d, yres = %d\n", pixGetXRes(pix), pixGetYRes(pix));
+    if ((cmap = pix->colormap) != NULL)
+        pixcmapWriteStream(fp, cmap);
+    else
+        fprintf(fp, "    no colormap\n");
+    informat = pixGetInputFormat(pix);
+    fprintf(fp, "    input format: %d (%s)\n", informat,
+            ImageFileFormatExtensions[informat]);
+    if (pix->text != NULL)
+        fprintf(fp, "    text: %s\n", pix->text);
+
+    return 0;
+}