diff mupdf-source/thirdparty/leptonica/src/roplow.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/roplow.c	Mon Sep 15 11:43:07 2025 +0200
@@ -0,0 +1,2506 @@
+/*====================================================================*
+ -  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 roplow.c
+ * <pre>
+ *      Low level dest-only
+ *           void            rasteropUniLow()
+ *           static void     rasteropUniWordAlignedlLow()
+ *           static void     rasteropUniGeneralLow()
+ *
+ *      Low level src and dest
+ *           void            rasteropLow()
+ *           static void     rasteropWordAlignedLow()
+ *           static void     rasteropVAlignedLow()
+ *           static void     rasteropGeneralLow()
+ *
+ *      Low level in-place full height vertical block transfer
+ *           void            rasteropVipLow()
+ *
+ *      Low level in-place full width horizontal block transfer
+ *           void            rasteropHipLow()
+ *           static void     shiftDataHorizontalLow()
+ * </pre>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config_auto.h>
+#endif  /* HAVE_CONFIG_H */
+
+#include <string.h>
+#include "allheaders.h"
+
+    /* Static helpers */
+static void rasteropUniWordAlignedLow(l_uint32 *datad, l_int32 dwpl, l_int32 dx,
+                                      l_int32 dy, l_int32  dw, l_int32 dh,
+                                      l_int32 op);
+static void rasteropUniGeneralLow(l_uint32 *datad, l_int32 dwpl, l_int32 dx,
+                                  l_int32 dy, l_int32 dw, l_int32  dh,
+                                  l_int32 op);
+static void rasteropWordAlignedLow(l_uint32 *datad, l_int32 dwpl, l_int32 dx,
+                                   l_int32 dy, l_int32 dw, l_int32 dh,
+                                   l_int32 op, l_uint32 *datas, l_int32 swpl,
+                                   l_int32 sx, l_int32 sy);
+static void rasteropVAlignedLow(l_uint32 *datad, l_int32 dwpl, l_int32 dx,
+                                l_int32 dy, l_int32 dw, l_int32 dh,
+                                l_int32 op, l_uint32 *datas, l_int32 swpl,
+                                l_int32 sx, l_int32 sy);
+static void rasteropGeneralLow(l_uint32 *datad, l_int32 dwpl, l_int32 dx,
+                               l_int32 dy, l_int32 dw, l_int32 dh,
+                               l_int32 op, l_uint32 *datas, l_int32 swpl,
+                               l_int32 sx, l_int32 sy);
+static void shiftDataHorizontalLow(l_uint32 *datad, l_int32 wpld,
+                                   l_uint32 *datas, l_int32 wpls,
+                                   l_int32 shift);
+
+#define COMBINE_PARTIAL(d, s, m)     ( ((d) & ~(m)) | ((s) & (m)) )
+
+static const l_int32  SHIFT_LEFT  = 0;
+static const l_int32  SHIFT_RIGHT = 1;
+
+static const l_uint32 lmask32[] = {0x0,
+    0x80000000, 0xc0000000, 0xe0000000, 0xf0000000,
+    0xf8000000, 0xfc000000, 0xfe000000, 0xff000000,
+    0xff800000, 0xffc00000, 0xffe00000, 0xfff00000,
+    0xfff80000, 0xfffc0000, 0xfffe0000, 0xffff0000,
+    0xffff8000, 0xffffc000, 0xffffe000, 0xfffff000,
+    0xfffff800, 0xfffffc00, 0xfffffe00, 0xffffff00,
+    0xffffff80, 0xffffffc0, 0xffffffe0, 0xfffffff0,
+    0xfffffff8, 0xfffffffc, 0xfffffffe, 0xffffffff};
+
+static const l_uint32 rmask32[] = {0x0,
+    0x00000001, 0x00000003, 0x00000007, 0x0000000f,
+    0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff,
+    0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff,
+    0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff,
+    0x0001ffff, 0x0003ffff, 0x0007ffff, 0x000fffff,
+    0x001fffff, 0x003fffff, 0x007fffff, 0x00ffffff,
+    0x01ffffff, 0x03ffffff, 0x07ffffff, 0x0fffffff,
+    0x1fffffff, 0x3fffffff, 0x7fffffff, 0xffffffff};
+
+
+/*--------------------------------------------------------------------*
+ *                     Low-level dest-only rasterops                  *
+ *--------------------------------------------------------------------*/
+/*!
+ * \brief   rasteropUniLow()
+ *
+ * \param[in]    datad  ptr to dest image data
+ * \param[in]    dpixw  width of dest
+ * \param[in]    dpixh  height of dest
+ * \param[in]    depth  depth of src and dest
+ * \param[in]    dwpl   wpl of dest
+ * \param[in]    dx     x val of UL corner of dest rectangle
+ * \param[in]    dy     y val of UL corner of dest rectangle
+ * \param[in]    dw     width of dest rectangle
+ * \param[in]    dh     height of dest rectangle
+ * \param[in]    op     op code
+ * \return  void
+ *
+ *  Action: scales width, performs clipping, checks alignment, and
+ *          dispatches for the rasterop.
+ */
+void
+rasteropUniLow(l_uint32  *datad,
+               l_int32    dpixw,
+               l_int32    dpixh,
+               l_int32    depth,
+               l_int32    dwpl,
+               l_int32    dx,
+               l_int32    dy,
+               l_int32    dw,
+               l_int32    dh,
+               l_int32    op)
+{
+l_int32  dhangw, dhangh;
+
+   /* -------------------------------------------------------*
+    *            scale horizontal dimensions by depth
+    * -------------------------------------------------------*/
+    if (depth != 1) {
+        dpixw *= depth;
+        dx *= depth;
+        dw *= depth;
+    }
+
+   /* -------------------------------------------------------*
+    *            clip rectangle to dest image
+    * -------------------------------------------------------*/
+       /* first, clip horizontally (dx, dw) */
+    if (dx < 0) {
+        dw += dx;  /* reduce dw */
+        dx = 0;
+    }
+    dhangw = dx + dw - dpixw;  /* rect ovhang dest to right */
+    if (dhangw > 0)
+        dw -= dhangw;  /* reduce dw */
+
+       /* then, clip vertically (dy, dh) */
+    if (dy < 0) {
+        dh += dy;  /* reduce dh */
+        dy = 0;
+    }
+    dhangh = dy + dh - dpixh;  /* rect ovhang dest below */
+    if (dhangh > 0)
+        dh -= dhangh;  /* reduce dh */
+
+        /* if clipped entirely, quit */
+    if ((dw <= 0) || (dh <= 0))
+        return;
+
+   /* -------------------------------------------------------*
+    *       dispatch to aligned or non-aligned blitters
+    * -------------------------------------------------------*/
+    if ((dx & 31) == 0)
+        rasteropUniWordAlignedLow(datad, dwpl, dx, dy, dw, dh, op);
+    else
+        rasteropUniGeneralLow(datad, dwpl, dx, dy, dw, dh, op);
+}
+
+
+
+/*--------------------------------------------------------------------*
+ *           Static low-level uni rasterop with word alignment        *
+ *--------------------------------------------------------------------*/
+/*!
+ * \brief   rasteropUniWordAlignedLow()
+ *
+ * \param[in]    datad  ptr to dest image data
+ * \param[in]    dwpl   wpl of dest
+ * \param[in]    dx     x val of UL corner of dest rectangle
+ * \param[in]    dy     y val of UL corner of dest rectangle
+ * \param[in]    dw     width of dest rectangle
+ * \param[in]    dh     height of dest rectangle
+ * \param[in]    op     op code
+ * \return  void
+ *
+ *  This is called when the dest rect is left aligned
+ *  on 32-bit word boundaries.   That is: dx & 31 == 0.
+ *
+ *  We make an optimized implementation of this because
+ *  it is a common case: e.g., operating on a full dest image.
+ */
+static void
+rasteropUniWordAlignedLow(l_uint32  *datad,
+                          l_int32    dwpl,
+                          l_int32    dx,
+                          l_int32    dy,
+                          l_int32    dw,
+                          l_int32    dh,
+                          l_int32    op)
+{
+l_int32    nfullw;     /* number of full words */
+l_uint32  *pfword;     /* ptr to first word */
+l_int32    lwbits;     /* number of ovrhang bits in last partial word */
+l_uint32   lwmask;     /* mask for last partial word */
+l_uint32  *lined;
+l_int32    i, j;
+
+    /*--------------------------------------------------------*
+     *                Preliminary calculations                *
+     *--------------------------------------------------------*/
+    nfullw = dw >> 5;
+    lwbits = dw & 31;
+    if (lwbits)
+        lwmask = lmask32[lwbits];
+    pfword = datad + dwpl * dy + (dx >> 5);
+
+
+    /*--------------------------------------------------------*
+     *            Now we're ready to do the ops               *
+     *--------------------------------------------------------*/
+    switch (op)
+    {
+    case PIX_CLR:
+        for (i = 0; i < dh; i++) {
+            lined = pfword + i * dwpl;
+            for (j = 0; j < nfullw; j++)
+                *lined++ = 0x0;
+            if (lwbits)
+                *lined = COMBINE_PARTIAL(*lined, 0x0, lwmask);
+        }
+        break;
+    case PIX_SET:
+        for (i = 0; i < dh; i++) {
+            lined = pfword + i * dwpl;
+            for (j = 0; j < nfullw; j++)
+                *lined++ = 0xffffffff;
+            if (lwbits)
+                *lined = COMBINE_PARTIAL(*lined, 0xffffffff, lwmask);
+        }
+        break;
+    case PIX_NOT(PIX_DST):
+        for (i = 0; i < dh; i++) {
+            lined = pfword + i * dwpl;
+            for (j = 0; j < nfullw; j++) {
+                *lined = ~(*lined);
+                lined++;
+            }
+            if (lwbits)
+                *lined = COMBINE_PARTIAL(*lined, ~(*lined), lwmask);
+        }
+        break;
+    default:
+        lept_stderr("Operation %d not permitted here!\n", op);
+    }
+}
+
+
+/*--------------------------------------------------------------------*
+ *        Static low-level uni rasterop without word alignment        *
+ *--------------------------------------------------------------------*/
+/*!
+ * \brief   rasteropUniGeneralLow()
+ *
+ * \param[in]    datad  ptr to dest image data
+ * \param[in]    dwpl   wpl of dest
+ * \param[in]    dx     x val of UL corner of dest rectangle
+ * \param[in]    dy     y val of UL corner of dest rectangle
+ * \param[in]    dw     width of dest rectangle
+ * \param[in]    dh     height of dest rectangle
+ * \param[in]    op     op code
+ * \return  void
+ */
+static void
+rasteropUniGeneralLow(l_uint32  *datad,
+                      l_int32    dwpl,
+                      l_int32    dx,
+                      l_int32    dy,
+                      l_int32    dw,
+                      l_int32    dh,
+                      l_int32    op)
+{
+l_int32    dfwpartb;   /* boolean (1, 0) if first dest word is partial */
+l_int32    dfwpart2b;  /* boolean (1, 0) if first dest word is doubly partial */
+l_uint32   dfwmask;    /* mask for first partial dest word */
+l_int32    dfwbits;    /* first word dest bits in ovrhang */
+l_uint32  *pdfwpart = NULL;   /* ptr to first partial dest word */
+l_int32    dfwfullb;   /* boolean (1, 0) if there exists a full dest word */
+l_int32    dnfullw;    /* number of full words in dest */
+l_uint32  *pdfwfull = NULL;   /* ptr to first full dest word */
+l_int32    dlwpartb;   /* boolean (1, 0) if last dest word is partial */
+l_uint32   dlwmask;    /* mask for last partial dest word */
+l_int32    dlwbits;    /* last word dest bits in ovrhang */
+l_uint32  *pdlwpart = NULL;   /* ptr to last partial dest word */
+l_int32    i, j;
+
+
+    /*--------------------------------------------------------*
+     *                Preliminary calculations                *
+     *--------------------------------------------------------*/
+        /* is the first word partial? */
+    dfwmask = 0;
+    if ((dx & 31) == 0) {  /* if not */
+        dfwpartb = 0;
+        dfwbits = 0;
+    } else {  /* if so */
+        dfwpartb = 1;
+        dfwbits = 32 - (dx & 31);
+        dfwmask = rmask32[dfwbits];
+        pdfwpart = datad + dwpl * dy + (dx >> 5);
+    }
+
+        /* is the first word doubly partial? */
+    if (dw >= dfwbits) {  /* if not */
+        dfwpart2b = 0;
+    } else {  /* if so */
+        dfwpart2b = 1;
+        dfwmask &= lmask32[32 - dfwbits + dw];
+    }
+
+        /* is there a full dest word? */
+    if (dfwpart2b == 1) {  /* not */
+        dfwfullb = 0;
+        dnfullw = 0;
+    } else {
+        dnfullw = (dw - dfwbits) >> 5;
+        if (dnfullw == 0) {  /* if not */
+            dfwfullb = 0;
+        } else {  /* if so */
+            dfwfullb = 1;
+            if (dfwpartb)
+                pdfwfull = pdfwpart + 1;
+            else
+                pdfwfull = datad + dwpl * dy + (dx >> 5);
+        }
+    }
+
+        /* is the last word partial? */
+    dlwbits = (dx + dw) & 31;
+    if (dfwpart2b == 1 || dlwbits == 0) {  /* if not */
+        dlwpartb = 0;
+    } else {
+        dlwpartb = 1;
+        dlwmask = lmask32[dlwbits];
+        if (dfwpartb)
+            pdlwpart = pdfwpart + 1 + dnfullw;
+        else
+            pdlwpart = datad + dwpl * dy + (dx >> 5) + dnfullw;
+    }
+
+
+    /*--------------------------------------------------------*
+     *            Now we're ready to do the ops               *
+     *--------------------------------------------------------*/
+    switch (op)
+    {
+    case PIX_CLR:
+            /* do the first partial word */
+        if (dfwpartb) {
+            for (i = 0; i < dh; i++) {
+                *pdfwpart = COMBINE_PARTIAL(*pdfwpart, 0x0, dfwmask);
+                pdfwpart += dwpl;
+            }
+        }
+
+            /* do the full words */
+        if (dfwfullb) {
+            for (i = 0; i < dh; i++) {
+                for (j = 0; j < dnfullw; j++)
+                    *(pdfwfull + j) = 0x0;
+                pdfwfull += dwpl;
+            }
+        }
+
+            /* do the last partial word */
+        if (dlwpartb) {
+            for (i = 0; i < dh; i++) {
+                *pdlwpart = COMBINE_PARTIAL(*pdlwpart, 0x0, dlwmask);
+                pdlwpart += dwpl;
+            }
+        }
+        break;
+    case PIX_SET:
+            /* do the first partial word */
+        if (dfwpartb) {
+            for (i = 0; i < dh; i++) {
+                *pdfwpart = COMBINE_PARTIAL(*pdfwpart, 0xffffffff, dfwmask);
+                pdfwpart += dwpl;
+            }
+        }
+
+            /* do the full words */
+        if (dfwfullb) {
+            for (i = 0; i < dh; i++) {
+                for (j = 0; j < dnfullw; j++)
+                    *(pdfwfull + j) = 0xffffffff;
+                pdfwfull += dwpl;
+            }
+        }
+
+            /* do the last partial word */
+        if (dlwpartb) {
+            for (i = 0; i < dh; i++) {
+                *pdlwpart = COMBINE_PARTIAL(*pdlwpart, 0xffffffff, dlwmask);
+                pdlwpart += dwpl;
+            }
+        }
+        break;
+    case PIX_NOT(PIX_DST):
+            /* do the first partial word */
+        if (dfwpartb) {
+            for (i = 0; i < dh; i++) {
+                *pdfwpart = COMBINE_PARTIAL(*pdfwpart, ~(*pdfwpart), dfwmask);
+                pdfwpart += dwpl;
+            }
+        }
+
+            /* do the full words */
+        if (dfwfullb) {
+            for (i = 0; i < dh; i++) {
+                for (j = 0; j < dnfullw; j++)
+                    *(pdfwfull + j) = ~(*(pdfwfull + j));
+                pdfwfull += dwpl;
+            }
+        }
+
+            /* do the last partial word */
+        if (dlwpartb) {
+            for (i = 0; i < dh; i++) {
+                *pdlwpart = COMBINE_PARTIAL(*pdlwpart, ~(*pdlwpart), dlwmask);
+                pdlwpart += dwpl;
+            }
+        }
+        break;
+    default:
+        lept_stderr("Operation %d not permitted here!\n", op);
+    }
+}
+
+
+/*--------------------------------------------------------------------*
+ *                   Low-level src and dest rasterops                 *
+ *--------------------------------------------------------------------*/
+/*!
+ * \brief   rasteropLow()
+ *
+ * \param[in]    datad  ptr to dest image data
+ * \param[in]    dpixw  width of dest
+ * \param[in]    dpixh  height of dest
+ * \param[in]    depth  depth of src and dest
+ * \param[in]    dwpl   wpl of dest
+ * \param[in]    dx     x val of UL corner of dest rectangle
+ * \param[in]    dy     y val of UL corner of dest rectangle
+ * \param[in]    dw     width of dest rectangle
+ * \param[in]    dh     height of dest rectangle
+ * \param[in]    op     op code
+ * \param[in]    datas  ptr to src image data
+ * \param[in]    spixw  width of src
+ * \param[in]    spixh  height of src
+ * \param[in]    swpl   wpl of src
+ * \param[in]    sx     x val of UL corner of src rectangle
+ * \param[in]    sy     y val of UL corner of src rectangle
+ * \return  void
+ *
+ *  Action: Scales width, performs clipping, checks alignment and
+ *          dispatches for the rasterop.
+ *
+ *  Warning: the two images must have equal depth.  This is not checked.
+ */
+void
+rasteropLow(l_uint32  *datad,
+            l_int32    dpixw,
+            l_int32    dpixh,
+            l_int32    depth,
+            l_int32    dwpl,
+            l_int32    dx,
+            l_int32    dy,
+            l_int32    dw,
+            l_int32    dh,
+            l_int32    op,
+            l_uint32  *datas,
+            l_int32    spixw,
+            l_int32    spixh,
+            l_int32    swpl,
+            l_int32    sx,
+            l_int32    sy)
+{
+l_int32  dhangw, shangw, dhangh, shangh;
+
+   /* -------------------------------------------------------*
+    *            Scale horizontal dimensions by depth        *
+    * -------------------------------------------------------*/
+    if (depth != 1) {
+        dpixw *= depth;
+        dx *= depth;
+        dw *= depth;
+        spixw *= depth;
+        sx *= depth;
+    }
+
+   /* -------------------------------------------------------*
+    *      Clip to max rectangle within both src and dest    *
+    * -------------------------------------------------------*/
+       /* Clip horizontally (sx, dx, dw) */
+    if (dx < 0) {
+        sx -= dx;  /* increase sx */
+        dw += dx;  /* reduce dw */
+        dx = 0;
+    }
+    if (sx < 0) {
+        dx -= sx;  /* increase dx */
+        dw += sx;  /* reduce dw */
+        sx = 0;
+    }
+    dhangw = dx + dw - dpixw;  /* rect ovhang dest to right */
+    if (dhangw > 0)
+        dw -= dhangw;  /* reduce dw */
+    shangw = sx + dw - spixw;   /* rect ovhang src to right */
+    if (shangw > 0)
+        dw -= shangw;  /* reduce dw */
+
+       /* Clip vertically (sy, dy, dh) */
+    if (dy < 0) {
+        sy -= dy;  /* increase sy */
+        dh += dy;  /* reduce dh */
+        dy = 0;
+    }
+    if (sy < 0) {
+        dy -= sy;  /* increase dy */
+        dh += sy;  /* reduce dh */
+        sy = 0;
+    }
+    dhangh = dy + dh - dpixh;  /* rect ovhang dest below */
+    if (dhangh > 0)
+        dh -= dhangh;  /* reduce dh */
+    shangh = sy + dh - spixh;  /* rect ovhang src below */
+    if (shangh > 0)
+        dh -= shangh;  /* reduce dh */
+
+        /* If clipped entirely, quit */
+    if ((dw <= 0) || (dh <= 0))
+        return;
+
+#if 0
+    lept_stderr("dx = %d, dy = %d, dw = %d, dh = %d, sx = %d, sy = %d\n",
+                dx, dy, dw, dh, sx, sy);
+#endif
+
+   /* -------------------------------------------------------*
+    *       Dispatch to aligned or non-aligned blitters      *
+    * -------------------------------------------------------*/
+    if (((dx & 31) == 0) && ((sx & 31) == 0))
+        rasteropWordAlignedLow(datad, dwpl, dx, dy, dw, dh, op,
+                               datas, swpl, sx, sy);
+    else if ((dx & 31) == (sx & 31))
+        rasteropVAlignedLow(datad, dwpl, dx, dy, dw, dh, op,
+                            datas, swpl, sx, sy);
+    else
+        rasteropGeneralLow(datad, dwpl, dx, dy, dw, dh, op,
+                           datas, swpl, sx, sy);
+}
+
+
+/*--------------------------------------------------------------------*
+ *        Static low-level rasterop with vertical word alignment      *
+ *--------------------------------------------------------------------*/
+/*!
+ * \brief   rasteropWordAlignedLow()
+ *
+ * \param[in]    datad  ptr to dest image data
+ * \param[in]    dwpl   wpl of dest
+ * \param[in]    dx     x val of UL corner of dest rectangle
+ * \param[in]    dy     y val of UL corner of dest rectangle
+ * \param[in]    dw     width of dest rectangle
+ * \param[in]    dh     height of dest rectangle
+ * \param[in]    op     op code
+ * \param[in]    datas  ptr to src image data
+ * \param[in]    swpl   wpl of src
+ * \param[in]    sx     x val of UL corner of src rectangle
+ * \param[in]    sy     y val of UL corner of src rectangle
+ * \return  void
+ *
+ *  This is called when both the src and dest rects
+ *  are left aligned on 32-bit word boundaries.
+ *  That is: dx & 31 == 0 and sx & 31 == 0
+ *
+ *  We make an optimized implementation of this because
+ *  it is a common case: e.g., two images are rasterop'd
+ *  starting from their UL corners 0,0.
+ */
+static void
+rasteropWordAlignedLow(l_uint32  *datad,
+                       l_int32    dwpl,
+                       l_int32    dx,
+                       l_int32    dy,
+                       l_int32    dw,
+                       l_int32    dh,
+                       l_int32    op,
+                       l_uint32  *datas,
+                       l_int32    swpl,
+                       l_int32    sx,
+                       l_int32    sy)
+{
+l_int32    nfullw;     /* number of full words */
+l_uint32  *psfword;    /* ptr to first src word */
+l_uint32  *pdfword;    /* ptr to first dest word */
+l_int32    lwbits;     /* number of ovrhang bits in last partial word */
+l_uint32   lwmask;     /* mask for last partial word */
+l_uint32  *lines, *lined;
+l_int32    i, j;
+
+
+    /*--------------------------------------------------------*
+     *                Preliminary calculations                *
+     *--------------------------------------------------------*/
+    nfullw = dw >> 5;
+    lwbits = dw & 31;
+    if (lwbits)
+        lwmask = lmask32[lwbits];
+    psfword = datas + swpl * sy + (sx >> 5);
+    pdfword = datad + dwpl * dy + (dx >> 5);
+
+    /*--------------------------------------------------------*
+     *            Now we're ready to do the ops               *
+     *--------------------------------------------------------*/
+    switch (op)
+    {
+    case PIX_SRC:
+        for (i = 0; i < dh; i++) {
+            lines = psfword + i * swpl;
+            lined = pdfword + i * dwpl;
+            for (j = 0; j < nfullw; j++) {
+                *lined = *lines;
+                lined++;
+                lines++;
+            }
+            if (lwbits)
+                *lined = COMBINE_PARTIAL(*lined, *lines, lwmask);
+        }
+        break;
+    case PIX_NOT(PIX_SRC):
+        for (i = 0; i < dh; i++) {
+            lines = psfword + i * swpl;
+            lined = pdfword + i * dwpl;
+            for (j = 0; j < nfullw; j++) {
+                *lined = ~(*lines);
+                lined++;
+                lines++;
+            }
+            if (lwbits)
+                *lined = COMBINE_PARTIAL(*lined, ~(*lines), lwmask);
+        }
+        break;
+    case (PIX_SRC | PIX_DST):
+        for (i = 0; i < dh; i++) {
+            lines = psfword + i * swpl;
+            lined = pdfword + i * dwpl;
+            for (j = 0; j < nfullw; j++) {
+                *lined = (*lines | *lined);
+                lined++;
+                lines++;
+            }
+            if (lwbits)
+                *lined = COMBINE_PARTIAL(*lined, (*lines | *lined), lwmask);
+        }
+        break;
+    case (PIX_SRC & PIX_DST):
+        for (i = 0; i < dh; i++) {
+            lines = psfword + i * swpl;
+            lined = pdfword + i * dwpl;
+            for (j = 0; j < nfullw; j++) {
+                *lined = (*lines & *lined);
+                lined++;
+                lines++;
+            }
+            if (lwbits)
+                *lined = COMBINE_PARTIAL(*lined, (*lines & *lined), lwmask);
+        }
+        break;
+    case (PIX_SRC ^ PIX_DST):
+        for (i = 0; i < dh; i++) {
+            lines = psfword + i * swpl;
+            lined = pdfword + i * dwpl;
+            for (j = 0; j < nfullw; j++) {
+                *lined = (*lines ^ *lined);
+                lined++;
+                lines++;
+            }
+            if (lwbits)
+                *lined = COMBINE_PARTIAL(*lined, (*lines ^ *lined), lwmask);
+        }
+        break;
+    case (PIX_NOT(PIX_SRC) | PIX_DST):
+        for (i = 0; i < dh; i++) {
+            lines = psfword + i * swpl;
+            lined = pdfword + i * dwpl;
+            for (j = 0; j < nfullw; j++) {
+                *lined = (~(*lines) | *lined);
+                lined++;
+                lines++;
+            }
+            if (lwbits)
+                *lined = COMBINE_PARTIAL(*lined, (~(*lines) | *lined), lwmask);
+        }
+        break;
+    case (PIX_NOT(PIX_SRC) & PIX_DST):
+        for (i = 0; i < dh; i++) {
+            lines = psfword + i * swpl;
+            lined = pdfword + i * dwpl;
+            for (j = 0; j < nfullw; j++) {
+                *lined = (~(*lines) & *lined);
+                lined++;
+                lines++;
+            }
+            if (lwbits)
+                *lined = COMBINE_PARTIAL(*lined, (~(*lines) & *lined), lwmask);
+        }
+        break;
+    case (PIX_SRC | PIX_NOT(PIX_DST)):
+        for (i = 0; i < dh; i++) {
+            lines = psfword + i * swpl;
+            lined = pdfword + i * dwpl;
+            for (j = 0; j < nfullw; j++) {
+                *lined = (*lines | ~(*lined));
+                lined++;
+                lines++;
+            }
+            if (lwbits)
+                *lined = COMBINE_PARTIAL(*lined, (*lines | ~(*lined)), lwmask);
+        }
+        break;
+    case (PIX_SRC & PIX_NOT(PIX_DST)):
+        for (i = 0; i < dh; i++) {
+            lines = psfword + i * swpl;
+            lined = pdfword + i * dwpl;
+            for (j = 0; j < nfullw; j++) {
+                *lined = (*lines & ~(*lined));
+                lined++;
+                lines++;
+            }
+            if (lwbits)
+                *lined = COMBINE_PARTIAL(*lined, (*lines & ~(*lined)), lwmask);
+        }
+        break;
+    case (PIX_NOT(PIX_SRC | PIX_DST)):
+        for (i = 0; i < dh; i++) {
+            lines = psfword + i * swpl;
+            lined = pdfword + i * dwpl;
+            for (j = 0; j < nfullw; j++) {
+                *lined = ~(*lines  | *lined);
+                lined++;
+                lines++;
+            }
+            if (lwbits)
+                *lined = COMBINE_PARTIAL(*lined, ~(*lines  | *lined), lwmask);
+        }
+        break;
+    case (PIX_NOT(PIX_SRC & PIX_DST)):
+        for (i = 0; i < dh; i++) {
+            lines = psfword + i * swpl;
+            lined = pdfword + i * dwpl;
+            for (j = 0; j < nfullw; j++) {
+                *lined = ~(*lines  & *lined);
+                lined++;
+                lines++;
+            }
+            if (lwbits)
+                *lined = COMBINE_PARTIAL(*lined, ~(*lines  & *lined), lwmask);
+        }
+        break;
+        /* this is three cases: ~(s ^ d), ~s ^ d, s ^ ~d  */
+    case (PIX_NOT(PIX_SRC ^ PIX_DST)):
+        for (i = 0; i < dh; i++) {
+            lines = psfword + i * swpl;
+            lined = pdfword + i * dwpl;
+            for (j = 0; j < nfullw; j++) {
+                *lined = ~(*lines ^ *lined);
+                lined++;
+                lines++;
+            }
+            if (lwbits)
+                *lined = COMBINE_PARTIAL(*lined, ~(*lines ^ *lined), lwmask);
+        }
+        break;
+    default:
+        lept_stderr("Operation %d invalid\n", op);
+    }
+}
+
+
+
+/*--------------------------------------------------------------------*
+ *        Static low-level rasterop with vertical word alignment      *
+ *--------------------------------------------------------------------*/
+/*!
+ * \brief   rasteropVAlignedLow()
+ *
+ * \param[in]    datad  ptr to dest image data
+ * \param[in]    dwpl   wpl of dest
+ * \param[in]    dx     x val of UL corner of dest rectangle
+ * \param[in]    dy     y val of UL corner of dest rectangle
+ * \param[in]    dw     width of dest rectangle
+ * \param[in]    dh     height of dest rectangle
+ * \param[in]    op     op code
+ * \param[in]    datas  ptr to src image data
+ * \param[in]    swpl   wpl of src
+ * \param[in]    sx     x val of UL corner of src rectangle
+ * \param[in]    sy     y val of UL corner of src rectangle
+ * \return  void
+ *
+ *  This is called when the left side of the src and dest
+ *  rects have the same alignment relative to 32-bit word
+ *  boundaries; i.e., dx & 31) == (sx & 31
+ */
+static void
+rasteropVAlignedLow(l_uint32  *datad,
+                    l_int32    dwpl,
+                    l_int32    dx,
+                    l_int32    dy,
+                    l_int32    dw,
+                    l_int32    dh,
+                    l_int32    op,
+                    l_uint32  *datas,
+                    l_int32    swpl,
+                    l_int32    sx,
+                    l_int32    sy)
+{
+l_int32    dfwpartb;   /* boolean (1, 0) if first dest word is partial */
+l_int32    dfwpart2b;  /* boolean (1, 0) if first dest word is doubly partial */
+l_uint32   dfwmask;    /* mask for first partial dest word */
+l_int32    dfwbits;    /* first word dest bits in ovrhang */
+l_uint32  *pdfwpart = NULL;   /* ptr to first partial dest word */
+l_uint32  *psfwpart = NULL;   /* ptr to first partial src word */
+l_int32    dfwfullb;   /* boolean (1, 0) if there exists a full dest word */
+l_int32    dnfullw;    /* number of full words in dest */
+l_uint32  *pdfwfull = NULL;   /* ptr to first full dest word */
+l_uint32  *psfwfull = NULL;   /* ptr to first full src word */
+l_int32    dlwpartb;   /* boolean (1, 0) if last dest word is partial */
+l_uint32   dlwmask;    /* mask for last partial dest word */
+l_int32    dlwbits;    /* last word dest bits in ovrhang */
+l_uint32  *pdlwpart = NULL;   /* ptr to last partial dest word */
+l_uint32  *pslwpart = NULL;   /* ptr to last partial src word */
+l_int32    i, j;
+
+
+    /*--------------------------------------------------------*
+     *                Preliminary calculations                *
+     *--------------------------------------------------------*/
+        /* is the first word partial? */
+    dfwmask = 0;
+    if ((dx & 31) == 0) {  /* if not */
+        dfwpartb = 0;
+        dfwbits = 0;
+    } else {  /* if so */
+        dfwpartb = 1;
+        dfwbits = 32 - (dx & 31);
+        dfwmask = rmask32[dfwbits];
+        pdfwpart = datad + dwpl * dy + (dx >> 5);
+        psfwpart = datas + swpl * sy + (sx >> 5);
+    }
+
+        /* is the first word doubly partial? */
+    if (dw >= dfwbits) {  /* if not */
+        dfwpart2b = 0;
+    } else {  /* if so */
+        dfwpart2b = 1;
+        dfwmask &= lmask32[32 - dfwbits + dw];
+    }
+
+        /* is there a full dest word? */
+    if (dfwpart2b == 1) {  /* not */
+        dfwfullb = 0;
+        dnfullw = 0;
+    } else {
+        dnfullw = (dw - dfwbits) >> 5;
+        if (dnfullw == 0) {  /* if not */
+            dfwfullb = 0;
+        } else {  /* if so */
+            dfwfullb = 1;
+            if (dfwpartb) {
+                pdfwfull = pdfwpart + 1;
+                psfwfull = psfwpart + 1;
+            } else {
+                pdfwfull = datad + dwpl * dy + (dx >> 5);
+                psfwfull = datas + swpl * sy + (sx >> 5);
+            }
+        }
+    }
+
+        /* is the last word partial? */
+    dlwbits = (dx + dw) & 31;
+    if (dfwpart2b == 1 || dlwbits == 0) {  /* if not */
+        dlwpartb = 0;
+    } else {
+        dlwpartb = 1;
+        dlwmask = lmask32[dlwbits];
+        if (dfwpartb) {
+            pdlwpart = pdfwpart + 1 + dnfullw;
+            pslwpart = psfwpart + 1 + dnfullw;
+        } else {
+            pdlwpart = datad + dwpl * dy + (dx >> 5) + dnfullw;
+            pslwpart = datas + swpl * sy + (sx >> 5) + dnfullw;
+        }
+    }
+
+
+    /*--------------------------------------------------------*
+     *            Now we're ready to do the ops               *
+     *--------------------------------------------------------*/
+    switch (op)
+    {
+    case PIX_SRC:
+            /* do the first partial word */
+        if (dfwpartb) {
+            for (i = 0; i < dh; i++) {
+                *pdfwpart = COMBINE_PARTIAL(*pdfwpart, *psfwpart, dfwmask);
+                pdfwpart += dwpl;
+                psfwpart += swpl;
+            }
+        }
+
+            /* do the full words */
+        if (dfwfullb) {
+            for (i = 0; i < dh; i++) {
+                for (j = 0; j < dnfullw; j++)
+                    *(pdfwfull + j) = *(psfwfull + j);
+                pdfwfull += dwpl;
+                psfwfull += swpl;
+            }
+        }
+
+            /* do the last partial word */
+        if (dlwpartb) {
+            for (i = 0; i < dh; i++) {
+                *pdlwpart = COMBINE_PARTIAL(*pdlwpart, *pslwpart, dlwmask);
+                pdlwpart += dwpl;
+                pslwpart += swpl;
+            }
+        }
+        break;
+    case PIX_NOT(PIX_SRC):
+            /* do the first partial word */
+        if (dfwpartb) {
+            for (i = 0; i < dh; i++) {
+                *pdfwpart = COMBINE_PARTIAL(*pdfwpart, ~(*psfwpart), dfwmask);
+                pdfwpart += dwpl;
+                psfwpart += swpl;
+            }
+        }
+
+            /* do the full words */
+        if (dfwfullb) {
+            for (i = 0; i < dh; i++) {
+                for (j = 0; j < dnfullw; j++)
+                    *(pdfwfull + j) = ~(*(psfwfull + j));
+                pdfwfull += dwpl;
+                psfwfull += swpl;
+            }
+        }
+
+            /* do the last partial word */
+        if (dlwpartb) {
+            for (i = 0; i < dh; i++) {
+                *pdlwpart = COMBINE_PARTIAL(*pdlwpart, ~(*pslwpart), dlwmask);
+                pdlwpart += dwpl;
+                pslwpart += swpl;
+            }
+        }
+        break;
+    case (PIX_SRC | PIX_DST):
+            /* do the first partial word */
+        if (dfwpartb) {
+            for (i = 0; i < dh; i++) {
+                *pdfwpart = COMBINE_PARTIAL(*pdfwpart,
+                                    (*psfwpart | *pdfwpart), dfwmask);
+                pdfwpart += dwpl;
+                psfwpart += swpl;
+            }
+        }
+
+            /* do the full words */
+        if (dfwfullb) {
+            for (i = 0; i < dh; i++) {
+                for (j = 0; j < dnfullw; j++)
+                    *(pdfwfull + j) |= *(psfwfull + j);
+                pdfwfull += dwpl;
+                psfwfull += swpl;
+            }
+        }
+
+            /* do the last partial word */
+        if (dlwpartb) {
+            for (i = 0; i < dh; i++) {
+                *pdlwpart = COMBINE_PARTIAL(*pdlwpart,
+                                     (*pslwpart | *pdlwpart), dlwmask);
+                pdlwpart += dwpl;
+                pslwpart += swpl;
+            }
+        }
+        break;
+    case (PIX_SRC & PIX_DST):
+            /* do the first partial word */
+        if (dfwpartb) {
+            for (i = 0; i < dh; i++) {
+                *pdfwpart = COMBINE_PARTIAL(*pdfwpart,
+                                    (*psfwpart & *pdfwpart), dfwmask);
+                pdfwpart += dwpl;
+                psfwpart += swpl;
+            }
+        }
+
+            /* do the full words */
+        if (dfwfullb) {
+            for (i = 0; i < dh; i++) {
+                for (j = 0; j < dnfullw; j++)
+                    *(pdfwfull + j) &= *(psfwfull + j);
+                pdfwfull += dwpl;
+                psfwfull += swpl;
+            }
+        }
+
+            /* do the last partial word */
+        if (dlwpartb) {
+            for (i = 0; i < dh; i++) {
+                *pdlwpart = COMBINE_PARTIAL(*pdlwpart,
+                                     (*pslwpart & *pdlwpart), dlwmask);
+                pdlwpart += dwpl;
+                pslwpart += swpl;
+            }
+        }
+        break;
+    case (PIX_SRC ^ PIX_DST):
+            /* do the first partial word */
+        if (dfwpartb) {
+            for (i = 0; i < dh; i++) {
+                *pdfwpart = COMBINE_PARTIAL(*pdfwpart,
+                                    (*psfwpart ^ *pdfwpart), dfwmask);
+                pdfwpart += dwpl;
+                psfwpart += swpl;
+            }
+        }
+
+            /* do the full words */
+        if (dfwfullb) {
+            for (i = 0; i < dh; i++) {
+                for (j = 0; j < dnfullw; j++)
+                    *(pdfwfull + j) ^= *(psfwfull + j);
+                pdfwfull += dwpl;
+                psfwfull += swpl;
+            }
+        }
+
+            /* do the last partial word */
+        if (dlwpartb) {
+            for (i = 0; i < dh; i++) {
+                *pdlwpart = COMBINE_PARTIAL(*pdlwpart,
+                                     (*pslwpart ^ *pdlwpart), dlwmask);
+                pdlwpart += dwpl;
+                pslwpart += swpl;
+            }
+        }
+        break;
+    case (PIX_NOT(PIX_SRC) | PIX_DST):
+            /* do the first partial word */
+        if (dfwpartb) {
+            for (i = 0; i < dh; i++) {
+                *pdfwpart = COMBINE_PARTIAL(*pdfwpart,
+                                    (~(*psfwpart) | *pdfwpart), dfwmask);
+                pdfwpart += dwpl;
+                psfwpart += swpl;
+            }
+        }
+
+            /* do the full words */
+        if (dfwfullb) {
+            for (i = 0; i < dh; i++) {
+                for (j = 0; j < dnfullw; j++)
+                    *(pdfwfull + j) |= ~(*(psfwfull + j));
+                pdfwfull += dwpl;
+                psfwfull += swpl;
+            }
+        }
+
+            /* do the last partial word */
+        if (dlwpartb) {
+            for (i = 0; i < dh; i++) {
+                *pdlwpart = COMBINE_PARTIAL(*pdlwpart,
+                                     (~(*pslwpart) | *pdlwpart), dlwmask);
+                pdlwpart += dwpl;
+                pslwpart += swpl;
+            }
+        }
+        break;
+    case (PIX_NOT(PIX_SRC) & PIX_DST):
+            /* do the first partial word */
+        if (dfwpartb) {
+            for (i = 0; i < dh; i++) {
+                *pdfwpart = COMBINE_PARTIAL(*pdfwpart,
+                                    (~(*psfwpart) & *pdfwpart), dfwmask);
+                pdfwpart += dwpl;
+                psfwpart += swpl;
+            }
+        }
+
+            /* do the full words */
+        if (dfwfullb) {
+            for (i = 0; i < dh; i++) {
+                for (j = 0; j < dnfullw; j++)
+                    *(pdfwfull + j) &= ~(*(psfwfull + j));
+                pdfwfull += dwpl;
+                psfwfull += swpl;
+            }
+        }
+
+            /* do the last partial word */
+        if (dlwpartb) {
+            for (i = 0; i < dh; i++) {
+                *pdlwpart = COMBINE_PARTIAL(*pdlwpart,
+                                     (~(*pslwpart) & *pdlwpart), dlwmask);
+                pdlwpart += dwpl;
+                pslwpart += swpl;
+            }
+        }
+        break;
+    case (PIX_SRC | PIX_NOT(PIX_DST)):
+            /* do the first partial word */
+        if (dfwpartb) {
+            for (i = 0; i < dh; i++) {
+                *pdfwpart = COMBINE_PARTIAL(*pdfwpart,
+                                    (*psfwpart | ~(*pdfwpart)), dfwmask);
+                pdfwpart += dwpl;
+                psfwpart += swpl;
+            }
+        }
+
+            /* do the full words */
+        if (dfwfullb) {
+            for (i = 0; i < dh; i++) {
+                for (j = 0; j < dnfullw; j++)
+                    *(pdfwfull + j) = *(psfwfull + j) | ~(*(pdfwfull + j));
+                pdfwfull += dwpl;
+                psfwfull += swpl;
+            }
+        }
+
+            /* do the last partial word */
+        if (dlwpartb) {
+            for (i = 0; i < dh; i++) {
+                *pdlwpart = COMBINE_PARTIAL(*pdlwpart,
+                                     (*pslwpart | ~(*pdlwpart)), dlwmask);
+                pdlwpart += dwpl;
+                pslwpart += swpl;
+            }
+        }
+        break;
+    case (PIX_SRC & PIX_NOT(PIX_DST)):
+            /* do the first partial word */
+        if (dfwpartb) {
+            for (i = 0; i < dh; i++) {
+                *pdfwpart = COMBINE_PARTIAL(*pdfwpart,
+                                    (*psfwpart & ~(*pdfwpart)), dfwmask);
+                pdfwpart += dwpl;
+                psfwpart += swpl;
+            }
+        }
+
+            /* do the full words */
+        if (dfwfullb) {
+            for (i = 0; i < dh; i++) {
+                for (j = 0; j < dnfullw; j++)
+                    *(pdfwfull + j) = *(psfwfull + j) & ~(*(pdfwfull + j));
+                pdfwfull += dwpl;
+                psfwfull += swpl;
+            }
+        }
+
+            /* do the last partial word */
+        if (dlwpartb) {
+            for (i = 0; i < dh; i++) {
+                *pdlwpart = COMBINE_PARTIAL(*pdlwpart,
+                                     (*pslwpart & ~(*pdlwpart)), dlwmask);
+                pdlwpart += dwpl;
+                pslwpart += swpl;
+            }
+        }
+        break;
+    case (PIX_NOT(PIX_SRC | PIX_DST)):
+            /* do the first partial word */
+        if (dfwpartb) {
+            for (i = 0; i < dh; i++) {
+                *pdfwpart = COMBINE_PARTIAL(*pdfwpart,
+                                    ~(*psfwpart | *pdfwpart), dfwmask);
+                pdfwpart += dwpl;
+                psfwpart += swpl;
+            }
+        }
+
+            /* do the full words */
+        if (dfwfullb) {
+            for (i = 0; i < dh; i++) {
+                for (j = 0; j < dnfullw; j++)
+                    *(pdfwfull + j) = ~(*(psfwfull + j) | *(pdfwfull + j));
+                pdfwfull += dwpl;
+                psfwfull += swpl;
+            }
+        }
+
+            /* do the last partial word */
+        if (dlwpartb) {
+            for (i = 0; i < dh; i++) {
+                *pdlwpart = COMBINE_PARTIAL(*pdlwpart,
+                                     ~(*pslwpart | *pdlwpart), dlwmask);
+                pdlwpart += dwpl;
+                pslwpart += swpl;
+            }
+        }
+        break;
+    case (PIX_NOT(PIX_SRC & PIX_DST)):
+            /* do the first partial word */
+        if (dfwpartb) {
+            for (i = 0; i < dh; i++) {
+                *pdfwpart = COMBINE_PARTIAL(*pdfwpart,
+                                    ~(*psfwpart & *pdfwpart), dfwmask);
+                pdfwpart += dwpl;
+                psfwpart += swpl;
+            }
+        }
+
+            /* do the full words */
+        if (dfwfullb) {
+            for (i = 0; i < dh; i++) {
+                for (j = 0; j < dnfullw; j++)
+                    *(pdfwfull + j) = ~(*(psfwfull + j) & *(pdfwfull + j));
+                pdfwfull += dwpl;
+                psfwfull += swpl;
+            }
+        }
+
+            /* do the last partial word */
+        if (dlwpartb) {
+            for (i = 0; i < dh; i++) {
+                *pdlwpart = COMBINE_PARTIAL(*pdlwpart,
+                                     ~(*pslwpart & *pdlwpart), dlwmask);
+                pdlwpart += dwpl;
+                pslwpart += swpl;
+            }
+        }
+        break;
+        /* this is three cases: ~(s ^ d), ~s ^ d, s ^ ~d  */
+    case (PIX_NOT(PIX_SRC ^ PIX_DST)):
+            /* do the first partial word */
+        if (dfwpartb) {
+            for (i = 0; i < dh; i++) {
+                *pdfwpart = COMBINE_PARTIAL(*pdfwpart,
+                                    ~(*psfwpart ^ *pdfwpart), dfwmask);
+                pdfwpart += dwpl;
+                psfwpart += swpl;
+            }
+        }
+
+            /* do the full words */
+        if (dfwfullb) {
+            for (i = 0; i < dh; i++) {
+                for (j = 0; j < dnfullw; j++)
+                    *(pdfwfull + j) = ~(*(psfwfull + j) ^ *(pdfwfull + j));
+                pdfwfull += dwpl;
+                psfwfull += swpl;
+            }
+        }
+
+            /* do the last partial word */
+        if (dlwpartb) {
+            for (i = 0; i < dh; i++) {
+                *pdlwpart = COMBINE_PARTIAL(*pdlwpart,
+                                     ~(*pslwpart ^ *pdlwpart), dlwmask);
+                pdlwpart += dwpl;
+                pslwpart += swpl;
+            }
+        }
+        break;
+    default:
+        lept_stderr("Operation %x invalid\n", op);
+    }
+}
+
+
+/*--------------------------------------------------------------------*
+ *     Static low-level rasterop without vertical word alignment      *
+ *--------------------------------------------------------------------*/
+/*!
+ * \brief   rasteropGeneralLow()
+ *
+ * \param[in]    datad  ptr to dest image data
+ * \param[in]    dwpl   wpl of dest
+ * \param[in]    dx     x val of UL corner of dest rectangle
+ * \param[in]    dy     y val of UL corner of dest rectangle
+ * \param[in]    dw     width of dest rectangle
+ * \param[in]    dh     height of dest rectangle
+ * \param[in]    op     op code
+ * \param[in]    datas  ptr to src image data
+ * \param[in]    swpl   wpl of src
+ * \param[in]    sx     x val of UL corner of src rectangle
+ * \param[in]    sy     y val of UL corner of src rectangle
+ * \return  void
+ *
+ *  This is called when the src and dest rects are
+ *  do not have the same 32-bit word alignment.
+ *
+ *  The method is a generalization of rasteropVAlignLow.
+ *  There, the src image pieces were directly merged
+ *  with the dest.  Here, we shift the source bits
+ *  to fill words that are aligned with the dest, and
+ *  then use those "source words" exactly in place
+ *  of the source words that were used in rasteropVAlignLow.
+ *
+ *  The critical parameter is thus the shift required
+ *  for the src.  Consider the left edge of the rectangle.
+ *  The overhang into the src and dest words are found,
+ *  and the difference is exactly this shift.  There are
+ *  two separate cases, depending on whether the src pixels
+ *  are shifted left or right.  If the src overhang is
+ *  larger than the dest overhang, the src is shifted to
+ *  the right, and a number of pixels equal to the shift are
+ *  left over for filling the next dest word, if necessary.
+ *
+ *  But if the dest overhang is larger than the src overhang,
+ *  the src is shifted to the left, and depending on the width of
+ *  transferred pixels, it may also be necessary to shift pixels
+ *  in from the next src word, in order to fill the dest word.
+ *  An interesting case is where the src overhang equals the width,
+ *  dw, of the block.  Then all the pixels necessary to fill the first
+ *  dest word can be taken from the first src word, up to the last
+ *  src pixel in the word, and no pixels from the next src word are
+ *  required.  Consider this simple example, where a single pixel from
+ *  the src is transferred to the dest:
+ *     pix1 = pixCreate(32, 1, 1);
+ *     pix2 = pixCreate(32, 1, 1);
+ *     pixRasterop(pix1, 30, 0, 1, 1, PIX_SRC, pix2, 31, 0);
+ *  Here, the pixel at the right end of the src image (sx = 31)
+ *  is shifted one bit to the left (to dx = 30).  The width (1) equals
+ *  the src overhang (1), and no pixels from the next word are required.
+ *  (This must be true because there is only one src word.)
+ */
+static void
+rasteropGeneralLow(l_uint32  *datad,
+                   l_int32    dwpl,
+                   l_int32    dx,
+                   l_int32    dy,
+                   l_int32    dw,
+                   l_int32    dh,
+                   l_int32    op,
+                   l_uint32  *datas,
+                   l_int32    swpl,
+                   l_int32    sx,
+                   l_int32    sy)
+{
+l_int32    dfwpartb;    /* boolean (1, 0) if first dest word is partial      */
+l_int32    dfwpart2b;   /* boolean (1, 0) if 1st dest word is doubly partial */
+l_uint32   dfwmask;     /* mask for first partial dest word                  */
+l_int32    dfwbits;     /* first word dest bits in overhang; 0-31            */
+l_int32    dhang;       /* dest overhang in first partial word,              */
+                        /* or 0 if dest is word aligned (same as dfwbits)    */
+l_uint32  *pdfwpart = NULL;    /* ptr to first partial dest word                    */
+l_uint32  *psfwpart = NULL;    /* ptr to first partial src word                     */
+l_int32    dfwfullb;    /* boolean (1, 0) if there exists a full dest word   */
+l_int32    dnfullw;     /* number of full words in dest                      */
+l_uint32  *pdfwfull = NULL;    /* ptr to first full dest word                       */
+l_uint32  *psfwfull = NULL;    /* ptr to first full src word                        */
+l_int32    dlwpartb;    /* boolean (1, 0) if last dest word is partial       */
+l_uint32   dlwmask;     /* mask for last partial dest word                   */
+l_int32    dlwbits;     /* last word dest bits in ovrhang                    */
+l_uint32  *pdlwpart = NULL;    /* ptr to last partial dest word                     */
+l_uint32  *pslwpart = NULL;    /* ptr to last partial src word                      */
+l_uint32   sword;       /* compose src word aligned with the dest words      */
+l_int32    sfwbits;     /* first word src bits in overhang (1-32),           */
+                        /* or 32 if src is word aligned                      */
+l_int32    shang;       /* source overhang in the first partial word,        */
+                        /* or 0 if src is word aligned (not same as sfwbits) */
+l_int32    sleftshift;  /* bits to shift left for source word to align       */
+                        /* with the dest.  Also the number of bits that      */
+                        /* get shifted to the right to align with the dest.  */
+l_int32    srightshift; /* bits to shift right for source word to align      */
+                        /* with dest.  Also, the number of bits that get     */
+                        /* shifted left to align with the dest.              */
+l_int32    srightmask;  /* mask for selecting sleftshift bits that have      */
+                        /* been shifted right by srightshift bits            */
+l_int32    sfwshiftdir; /* either SHIFT_LEFT or SHIFT_RIGHT                  */
+l_int32    sfwaddb;     /* boolean: do we need an additional sfw right shift? */
+l_int32    slwaddb;     /* boolean: do we need an additional slw right shift? */
+l_int32    i, j;
+
+
+    /*--------------------------------------------------------*
+     *                Preliminary calculations                *
+     *--------------------------------------------------------*/
+        /* To get alignment of src with dst (e.g., in the
+         * full words) the src must do a left shift of its
+         * relative overhang in the current src word,
+         * and OR that with a right shift of
+         * (31 -  relative overhang) from the next src word.
+         * We find the absolute overhangs, the relative overhangs,
+         * the required shifts and the src mask */
+    if ((sx & 31) == 0)
+        shang = 0;
+    else
+        shang = 32 - (sx & 31);
+    if ((dx & 31) == 0)
+        dhang = 0;
+    else
+        dhang = 32 - (dx & 31);
+#if 0
+    lept_stderr("shang = %d, dhang = %d\n", shang, dhang);
+#endif
+
+    if (shang == 0 && dhang == 0) {  /* this should be treated by an
+                                        aligned operation, not by
+                                        this general rasterop! */
+        sleftshift = 0;
+        srightshift = 0;
+        srightmask = rmask32[0];
+    } else {
+        if (dhang > shang)
+            sleftshift = dhang - shang;
+        else
+            sleftshift = 32 - (shang - dhang);
+        srightshift = 32 - sleftshift;
+        srightmask = rmask32[sleftshift];
+    }
+
+#if 0
+    lept_stderr("sleftshift = %d, srightshift = %d\n", sleftshift, srightshift);
+#endif
+
+        /* Is the first dest word partial? */
+    dfwmask = 0;
+    if ((dx & 31) == 0) {  /* if not */
+        dfwpartb = 0;
+        dfwbits = 0;
+    } else {  /* if so */
+        dfwpartb = 1;
+        dfwbits = 32 - (dx & 31);
+        dfwmask = rmask32[dfwbits];
+        pdfwpart = datad + dwpl * dy + (dx >> 5);
+        psfwpart = datas + swpl * sy + (sx >> 5);
+        sfwbits = 32 - (sx & 31);
+        if (dfwbits > sfwbits) {
+            sfwshiftdir = SHIFT_LEFT;  /* shift by sleftshift */
+                /* Do we have enough bits from the current src word? */
+            if (dw <= shang)
+                sfwaddb = 0;  /* yes: we have enough bits */
+            else
+                sfwaddb = 1;  /* no: rshift in next src word by srightshift */
+        } else {
+            sfwshiftdir = SHIFT_RIGHT;  /* shift by srightshift */
+        }
+    }
+
+        /* Is the first dest word doubly partial? */
+    if (dw >= dfwbits) {  /* if not */
+        dfwpart2b = 0;
+    } else {  /* if so */
+        dfwpart2b = 1;
+        dfwmask &= lmask32[32 - dfwbits + dw];
+    }
+
+        /* Is there a full dest word? */
+    if (dfwpart2b == 1) {  /* not */
+        dfwfullb = 0;
+        dnfullw = 0;
+    } else {
+        dnfullw = (dw - dfwbits) >> 5;
+        if (dnfullw == 0) {  /* if not */
+            dfwfullb = 0;
+        } else {  /* if so */
+            dfwfullb = 1;
+            pdfwfull = datad + dwpl * dy + ((dx + dhang) >> 5);
+            psfwfull = datas + swpl * sy + ((sx + dhang) >> 5); /* yes, dhang */
+        }
+    }
+
+        /* Is the last dest word partial? */
+    dlwbits = (dx + dw) & 31;
+    if (dfwpart2b == 1 || dlwbits == 0) {  /* if not */
+        dlwpartb = 0;
+    } else {
+        dlwpartb = 1;
+        dlwmask = lmask32[dlwbits];
+        pdlwpart = datad + dwpl * dy + ((dx + dhang) >> 5) + dnfullw;
+        pslwpart = datas + swpl * sy + ((sx + dhang) >> 5) + dnfullw;
+        if (dlwbits <= srightshift)   /* must be <= here !!! */
+            slwaddb = 0;  /* we got enough bits from current src word */
+        else
+            slwaddb = 1;  /* must rshift in next src word by srightshift */
+    }
+
+
+    /*--------------------------------------------------------*
+     *            Now we're ready to do the ops               *
+     *--------------------------------------------------------*/
+    switch (op)
+    {
+    case PIX_SRC:
+            /* do the first partial word */
+        if (dfwpartb) {
+            for (i = 0; i < dh; i++)
+            {
+                if (sfwshiftdir == SHIFT_LEFT) {
+                    sword = *psfwpart << sleftshift;
+                    if (sfwaddb)
+                        sword = COMBINE_PARTIAL(sword,
+                                      *(psfwpart + 1) >> srightshift,
+                                       srightmask);
+                } else {  /* shift right */
+                    sword = *psfwpart >> srightshift;
+                }
+
+                *pdfwpart = COMBINE_PARTIAL(*pdfwpart, sword, dfwmask);
+                pdfwpart += dwpl;
+                psfwpart += swpl;
+            }
+        }
+
+            /* do the full words */
+        if (dfwfullb) {
+            for (i = 0; i < dh; i++) {
+                for (j = 0; j < dnfullw; j++) {
+                    sword = COMBINE_PARTIAL(*(psfwfull + j) << sleftshift,
+                                   *(psfwfull + j + 1) >> srightshift,
+                                   srightmask);
+                    *(pdfwfull + j) = sword;
+                }
+                pdfwfull += dwpl;
+                psfwfull += swpl;
+            }
+        }
+
+            /* do the last partial word */
+        if (dlwpartb) {
+            for (i = 0; i < dh; i++) {
+                sword = *pslwpart << sleftshift;
+                if (slwaddb)
+                    sword = COMBINE_PARTIAL(sword,
+                                  *(pslwpart + 1) >> srightshift,
+                                  srightmask);
+
+                *pdlwpart = COMBINE_PARTIAL(*pdlwpart, sword, dlwmask);
+                pdlwpart += dwpl;
+                pslwpart += swpl;
+            }
+        }
+        break;
+    case PIX_NOT(PIX_SRC):
+            /* do the first partial word */
+        if (dfwpartb) {
+            for (i = 0; i < dh; i++)
+            {
+                if (sfwshiftdir == SHIFT_LEFT) {
+                    sword = *psfwpart << sleftshift;
+                    if (sfwaddb)
+                        sword = COMBINE_PARTIAL(sword,
+                                      *(psfwpart + 1) >> srightshift,
+                                       srightmask);
+                } else {  /* shift right */
+                    sword = *psfwpart >> srightshift;
+                }
+
+                *pdfwpart = COMBINE_PARTIAL(*pdfwpart, ~sword, dfwmask);
+                pdfwpart += dwpl;
+                psfwpart += swpl;
+            }
+        }
+
+            /* do the full words */
+        if (dfwfullb) {
+            for (i = 0; i < dh; i++) {
+                for (j = 0; j < dnfullw; j++) {
+                    sword = COMBINE_PARTIAL(*(psfwfull + j) << sleftshift,
+                                   *(psfwfull + j + 1) >> srightshift,
+                                   srightmask);
+                    *(pdfwfull + j) = ~sword;
+                }
+                pdfwfull += dwpl;
+                psfwfull += swpl;
+            }
+        }
+
+            /* do the last partial word */
+        if (dlwpartb) {
+            for (i = 0; i < dh; i++) {
+                sword = *pslwpart << sleftshift;
+                if (slwaddb)
+                    sword = COMBINE_PARTIAL(sword,
+                                  *(pslwpart + 1) >> srightshift,
+                                  srightmask);
+
+                *pdlwpart = COMBINE_PARTIAL(*pdlwpart, ~sword, dlwmask);
+                pdlwpart += dwpl;
+                pslwpart += swpl;
+            }
+        }
+        break;
+    case (PIX_SRC | PIX_DST):
+            /* do the first partial word */
+        if (dfwpartb) {
+            for (i = 0; i < dh; i++)
+            {
+                if (sfwshiftdir == SHIFT_LEFT) {
+                    sword = *psfwpart << sleftshift;
+                    if (sfwaddb)
+                        sword = COMBINE_PARTIAL(sword,
+                                      *(psfwpart + 1) >> srightshift,
+                                       srightmask);
+                } else {  /* shift right */
+                    sword = *psfwpart >> srightshift;
+                }
+
+                *pdfwpart = COMBINE_PARTIAL(*pdfwpart,
+                                 (sword | *pdfwpart), dfwmask);
+                pdfwpart += dwpl;
+                psfwpart += swpl;
+            }
+        }
+
+            /* do the full words */
+        if (dfwfullb) {
+            for (i = 0; i < dh; i++) {
+                for (j = 0; j < dnfullw; j++) {
+                    sword = COMBINE_PARTIAL(*(psfwfull + j) << sleftshift,
+                                   *(psfwfull + j + 1) >> srightshift,
+                                   srightmask);
+                    *(pdfwfull + j) |= sword;
+                }
+                pdfwfull += dwpl;
+                psfwfull += swpl;
+            }
+        }
+
+            /* do the last partial word */
+        if (dlwpartb) {
+            for (i = 0; i < dh; i++) {
+                sword = *pslwpart << sleftshift;
+                if (slwaddb)
+                    sword = COMBINE_PARTIAL(sword,
+                                  *(pslwpart + 1) >> srightshift,
+                                  srightmask);
+
+                *pdlwpart = COMBINE_PARTIAL(*pdlwpart,
+                               (sword | *pdlwpart), dlwmask);
+                pdlwpart += dwpl;
+                pslwpart += swpl;
+            }
+        }
+        break;
+    case (PIX_SRC & PIX_DST):
+            /* do the first partial word */
+        if (dfwpartb) {
+            for (i = 0; i < dh; i++)
+            {
+                if (sfwshiftdir == SHIFT_LEFT) {
+                    sword = *psfwpart << sleftshift;
+                    if (sfwaddb)
+                        sword = COMBINE_PARTIAL(sword,
+                                      *(psfwpart + 1) >> srightshift,
+                                       srightmask);
+                } else {  /* shift right */
+                    sword = *psfwpart >> srightshift;
+                }
+
+                *pdfwpart = COMBINE_PARTIAL(*pdfwpart,
+                                 (sword & *pdfwpart), dfwmask);
+                pdfwpart += dwpl;
+                psfwpart += swpl;
+            }
+        }
+
+            /* do the full words */
+        if (dfwfullb) {
+            for (i = 0; i < dh; i++) {
+                for (j = 0; j < dnfullw; j++) {
+                    sword = COMBINE_PARTIAL(*(psfwfull + j) << sleftshift,
+                                   *(psfwfull + j + 1) >> srightshift,
+                                   srightmask);
+                    *(pdfwfull + j) &= sword;
+                }
+                pdfwfull += dwpl;
+                psfwfull += swpl;
+            }
+        }
+
+            /* do the last partial word */
+        if (dlwpartb) {
+            for (i = 0; i < dh; i++) {
+                sword = *pslwpart << sleftshift;
+                if (slwaddb)
+                    sword = COMBINE_PARTIAL(sword,
+                                  *(pslwpart + 1) >> srightshift,
+                                  srightmask);
+
+                *pdlwpart = COMBINE_PARTIAL(*pdlwpart,
+                               (sword & *pdlwpart), dlwmask);
+                pdlwpart += dwpl;
+                pslwpart += swpl;
+            }
+        }
+        break;
+    case (PIX_SRC ^ PIX_DST):
+            /* do the first partial word */
+        if (dfwpartb) {
+            for (i = 0; i < dh; i++)
+            {
+                if (sfwshiftdir == SHIFT_LEFT) {
+                    sword = *psfwpart << sleftshift;
+                    if (sfwaddb)
+                        sword = COMBINE_PARTIAL(sword,
+                                      *(psfwpart + 1) >> srightshift,
+                                       srightmask);
+                } else {  /* shift right */
+                    sword = *psfwpart >> srightshift;
+                }
+
+                *pdfwpart = COMBINE_PARTIAL(*pdfwpart,
+                                 (sword ^ *pdfwpart), dfwmask);
+                pdfwpart += dwpl;
+                psfwpart += swpl;
+            }
+        }
+
+            /* do the full words */
+        if (dfwfullb) {
+            for (i = 0; i < dh; i++) {
+                for (j = 0; j < dnfullw; j++) {
+                    sword = COMBINE_PARTIAL(*(psfwfull + j) << sleftshift,
+                                   *(psfwfull + j + 1) >> srightshift,
+                                   srightmask);
+                    *(pdfwfull + j) ^= sword;
+                }
+                pdfwfull += dwpl;
+                psfwfull += swpl;
+            }
+        }
+
+            /* do the last partial word */
+        if (dlwpartb) {
+            for (i = 0; i < dh; i++) {
+                sword = *pslwpart << sleftshift;
+                if (slwaddb)
+                    sword = COMBINE_PARTIAL(sword,
+                                  *(pslwpart + 1) >> srightshift,
+                                  srightmask);
+
+                *pdlwpart = COMBINE_PARTIAL(*pdlwpart,
+                               (sword ^ *pdlwpart), dlwmask);
+                pdlwpart += dwpl;
+                pslwpart += swpl;
+            }
+        }
+        break;
+    case (PIX_NOT(PIX_SRC) | PIX_DST):
+            /* do the first partial word */
+        if (dfwpartb) {
+            for (i = 0; i < dh; i++)
+            {
+                if (sfwshiftdir == SHIFT_LEFT) {
+                    sword = *psfwpart << sleftshift;
+                    if (sfwaddb)
+                        sword = COMBINE_PARTIAL(sword,
+                                      *(psfwpart + 1) >> srightshift,
+                                       srightmask);
+                } else {  /* shift right */
+                    sword = *psfwpart >> srightshift;
+                }
+
+                *pdfwpart = COMBINE_PARTIAL(*pdfwpart,
+                                 (~sword | *pdfwpart), dfwmask);
+                pdfwpart += dwpl;
+                psfwpart += swpl;
+            }
+        }
+
+            /* do the full words */
+        if (dfwfullb) {
+            for (i = 0; i < dh; i++) {
+                for (j = 0; j < dnfullw; j++) {
+                    sword = COMBINE_PARTIAL(*(psfwfull + j) << sleftshift,
+                                   *(psfwfull + j + 1) >> srightshift,
+                                   srightmask);
+                    *(pdfwfull + j) |= ~sword;
+                }
+                pdfwfull += dwpl;
+                psfwfull += swpl;
+            }
+        }
+
+            /* do the last partial word */
+        if (dlwpartb) {
+            for (i = 0; i < dh; i++) {
+                sword = *pslwpart << sleftshift;
+                if (slwaddb)
+                    sword = COMBINE_PARTIAL(sword,
+                                  *(pslwpart + 1) >> srightshift,
+                                  srightmask);
+
+                *pdlwpart = COMBINE_PARTIAL(*pdlwpart,
+                               (~sword | *pdlwpart), dlwmask);
+                pdlwpart += dwpl;
+                pslwpart += swpl;
+            }
+        }
+        break;
+    case (PIX_NOT(PIX_SRC) & PIX_DST):
+            /* do the first partial word */
+        if (dfwpartb) {
+            for (i = 0; i < dh; i++)
+            {
+                if (sfwshiftdir == SHIFT_LEFT) {
+                    sword = *psfwpart << sleftshift;
+                    if (sfwaddb)
+                        sword = COMBINE_PARTIAL(sword,
+                                      *(psfwpart + 1) >> srightshift,
+                                       srightmask);
+                } else {  /* shift right */
+                    sword = *psfwpart >> srightshift;
+                }
+
+                *pdfwpart = COMBINE_PARTIAL(*pdfwpart,
+                                 (~sword & *pdfwpart), dfwmask);
+                pdfwpart += dwpl;
+                psfwpart += swpl;
+            }
+        }
+
+            /* do the full words */
+        if (dfwfullb) {
+            for (i = 0; i < dh; i++) {
+                for (j = 0; j < dnfullw; j++) {
+                    sword = COMBINE_PARTIAL(*(psfwfull + j) << sleftshift,
+                                   *(psfwfull + j + 1) >> srightshift,
+                                   srightmask);
+                    *(pdfwfull + j) &= ~sword;
+                }
+                pdfwfull += dwpl;
+                psfwfull += swpl;
+            }
+        }
+
+            /* do the last partial word */
+        if (dlwpartb) {
+            for (i = 0; i < dh; i++) {
+                sword = *pslwpart << sleftshift;
+                if (slwaddb)
+                    sword = COMBINE_PARTIAL(sword,
+                                  *(pslwpart + 1) >> srightshift,
+                                  srightmask);
+
+                *pdlwpart = COMBINE_PARTIAL(*pdlwpart,
+                               (~sword & *pdlwpart), dlwmask);
+                pdlwpart += dwpl;
+                pslwpart += swpl;
+            }
+        }
+        break;
+    case (PIX_SRC | PIX_NOT(PIX_DST)):
+            /* do the first partial word */
+        if (dfwpartb) {
+            for (i = 0; i < dh; i++)
+            {
+                if (sfwshiftdir == SHIFT_LEFT) {
+                    sword = *psfwpart << sleftshift;
+                    if (sfwaddb)
+                        sword = COMBINE_PARTIAL(sword,
+                                      *(psfwpart + 1) >> srightshift,
+                                       srightmask);
+                } else {  /* shift right */
+                    sword = *psfwpart >> srightshift;
+                }
+
+                *pdfwpart = COMBINE_PARTIAL(*pdfwpart,
+                                 (sword | ~(*pdfwpart)), dfwmask);
+                pdfwpart += dwpl;
+                psfwpart += swpl;
+            }
+        }
+
+            /* do the full words */
+        if (dfwfullb) {
+            for (i = 0; i < dh; i++) {
+                for (j = 0; j < dnfullw; j++) {
+                    sword = COMBINE_PARTIAL(*(psfwfull + j) << sleftshift,
+                                   *(psfwfull + j + 1) >> srightshift,
+                                   srightmask);
+                    *(pdfwfull + j) = sword | ~(*(pdfwfull + j));
+                }
+                pdfwfull += dwpl;
+                psfwfull += swpl;
+            }
+        }
+
+            /* do the last partial word */
+        if (dlwpartb) {
+            for (i = 0; i < dh; i++) {
+                sword = *pslwpart << sleftshift;
+                if (slwaddb)
+                    sword = COMBINE_PARTIAL(sword,
+                                  *(pslwpart + 1) >> srightshift,
+                                  srightmask);
+
+                *pdlwpart = COMBINE_PARTIAL(*pdlwpart,
+                               (sword | ~(*pdlwpart)), dlwmask);
+                pdlwpart += dwpl;
+                pslwpart += swpl;
+            }
+        }
+        break;
+    case (PIX_SRC & PIX_NOT(PIX_DST)):
+            /* do the first partial word */
+        if (dfwpartb) {
+            for (i = 0; i < dh; i++)
+            {
+                if (sfwshiftdir == SHIFT_LEFT) {
+                    sword = *psfwpart << sleftshift;
+                    if (sfwaddb)
+                        sword = COMBINE_PARTIAL(sword,
+                                      *(psfwpart + 1) >> srightshift,
+                                       srightmask);
+                } else {  /* shift right */
+                    sword = *psfwpart >> srightshift;
+                }
+
+                *pdfwpart = COMBINE_PARTIAL(*pdfwpart,
+                                 (sword & ~(*pdfwpart)), dfwmask);
+                pdfwpart += dwpl;
+                psfwpart += swpl;
+            }
+        }
+
+            /* do the full words */
+        if (dfwfullb) {
+            for (i = 0; i < dh; i++) {
+                for (j = 0; j < dnfullw; j++) {
+                    sword = COMBINE_PARTIAL(*(psfwfull + j) << sleftshift,
+                                   *(psfwfull + j + 1) >> srightshift,
+                                   srightmask);
+                    *(pdfwfull + j) = sword & ~(*(pdfwfull + j));
+                }
+                pdfwfull += dwpl;
+                psfwfull += swpl;
+            }
+        }
+
+            /* do the last partial word */
+        if (dlwpartb) {
+            for (i = 0; i < dh; i++) {
+                sword = *pslwpart << sleftshift;
+                if (slwaddb)
+                    sword = COMBINE_PARTIAL(sword,
+                                  *(pslwpart + 1) >> srightshift,
+                                  srightmask);
+
+                *pdlwpart = COMBINE_PARTIAL(*pdlwpart,
+                               (sword & ~(*pdlwpart)), dlwmask);
+                pdlwpart += dwpl;
+                pslwpart += swpl;
+            }
+        }
+        break;
+    case (PIX_NOT(PIX_SRC | PIX_DST)):
+            /* do the first partial word */
+        if (dfwpartb) {
+            for (i = 0; i < dh; i++)
+            {
+                if (sfwshiftdir == SHIFT_LEFT) {
+                    sword = *psfwpart << sleftshift;
+                    if (sfwaddb)
+                        sword = COMBINE_PARTIAL(sword,
+                                      *(psfwpart + 1) >> srightshift,
+                                       srightmask);
+                } else {  /* shift right */
+                    sword = *psfwpart >> srightshift;
+                }
+
+                *pdfwpart = COMBINE_PARTIAL(*pdfwpart,
+                                 ~(sword | *pdfwpart), dfwmask);
+                pdfwpart += dwpl;
+                psfwpart += swpl;
+            }
+        }
+
+            /* do the full words */
+        if (dfwfullb) {
+            for (i = 0; i < dh; i++) {
+                for (j = 0; j < dnfullw; j++) {
+                    sword = COMBINE_PARTIAL(*(psfwfull + j) << sleftshift,
+                                   *(psfwfull + j + 1) >> srightshift,
+                                   srightmask);
+                    *(pdfwfull + j) = ~(sword | *(pdfwfull + j));
+                }
+                pdfwfull += dwpl;
+                psfwfull += swpl;
+            }
+        }
+
+            /* do the last partial word */
+        if (dlwpartb) {
+            for (i = 0; i < dh; i++) {
+                sword = *pslwpart << sleftshift;
+                if (slwaddb)
+                    sword = COMBINE_PARTIAL(sword,
+                                  *(pslwpart + 1) >> srightshift,
+                                  srightmask);
+
+                *pdlwpart = COMBINE_PARTIAL(*pdlwpart,
+                               ~(sword | *pdlwpart), dlwmask);
+                pdlwpart += dwpl;
+                pslwpart += swpl;
+            }
+        }
+        break;
+    case (PIX_NOT(PIX_SRC & PIX_DST)):
+            /* do the first partial word */
+        if (dfwpartb) {
+            for (i = 0; i < dh; i++)
+            {
+                if (sfwshiftdir == SHIFT_LEFT) {
+                    sword = *psfwpart << sleftshift;
+                    if (sfwaddb)
+                        sword = COMBINE_PARTIAL(sword,
+                                      *(psfwpart + 1) >> srightshift,
+                                       srightmask);
+                } else {  /* shift right */
+                    sword = *psfwpart >> srightshift;
+                }
+
+                *pdfwpart = COMBINE_PARTIAL(*pdfwpart,
+                                 ~(sword & *pdfwpart), dfwmask);
+                pdfwpart += dwpl;
+                psfwpart += swpl;
+            }
+        }
+
+            /* do the full words */
+        if (dfwfullb) {
+            for (i = 0; i < dh; i++) {
+                for (j = 0; j < dnfullw; j++) {
+                    sword = COMBINE_PARTIAL(*(psfwfull + j) << sleftshift,
+                                   *(psfwfull + j + 1) >> srightshift,
+                                   srightmask);
+                    *(pdfwfull + j) = ~(sword & *(pdfwfull + j));
+                }
+                pdfwfull += dwpl;
+                psfwfull += swpl;
+            }
+        }
+
+            /* do the last partial word */
+        if (dlwpartb) {
+            for (i = 0; i < dh; i++) {
+                sword = *pslwpart << sleftshift;
+                if (slwaddb)
+                    sword = COMBINE_PARTIAL(sword,
+                                  *(pslwpart + 1) >> srightshift,
+                                  srightmask);
+
+                *pdlwpart = COMBINE_PARTIAL(*pdlwpart,
+                               ~(sword & *pdlwpart), dlwmask);
+                pdlwpart += dwpl;
+                pslwpart += swpl;
+            }
+        }
+        break;
+        /* this is three cases: ~(s ^ d), ~s ^ d, s ^ ~d  */
+    case (PIX_NOT(PIX_SRC ^ PIX_DST)):
+            /* do the first partial word */
+        if (dfwpartb) {
+            for (i = 0; i < dh; i++)
+            {
+                if (sfwshiftdir == SHIFT_LEFT) {
+                    sword = *psfwpart << sleftshift;
+                    if (sfwaddb)
+                        sword = COMBINE_PARTIAL(sword,
+                                      *(psfwpart + 1) >> srightshift,
+                                       srightmask);
+                } else {  /* shift right */
+                    sword = *psfwpart >> srightshift;
+                }
+
+                *pdfwpart = COMBINE_PARTIAL(*pdfwpart,
+                                 ~(sword ^ *pdfwpart), dfwmask);
+                pdfwpart += dwpl;
+                psfwpart += swpl;
+            }
+        }
+
+            /* do the full words */
+        if (dfwfullb) {
+            for (i = 0; i < dh; i++) {
+                for (j = 0; j < dnfullw; j++) {
+                    sword = COMBINE_PARTIAL(*(psfwfull + j) << sleftshift,
+                                   *(psfwfull + j + 1) >> srightshift,
+                                   srightmask);
+                    *(pdfwfull + j) = ~(sword ^ *(pdfwfull + j));
+                }
+                pdfwfull += dwpl;
+                psfwfull += swpl;
+            }
+        }
+
+            /* do the last partial word */
+        if (dlwpartb) {
+            for (i = 0; i < dh; i++) {
+                sword = *pslwpart << sleftshift;
+                if (slwaddb)
+                    sword = COMBINE_PARTIAL(sword,
+                                  *(pslwpart + 1) >> srightshift,
+                                  srightmask);
+
+                *pdlwpart = COMBINE_PARTIAL(*pdlwpart,
+                               ~(sword ^ *pdlwpart), dlwmask);
+                pdlwpart += dwpl;
+                pslwpart += swpl;
+            }
+        }
+        break;
+    default:
+        lept_stderr("Operation %x invalid\n", op);
+    }
+}
+
+
+/*--------------------------------------------------------------------*
+ *        Low level in-place full height vertical block transfer      *
+ *--------------------------------------------------------------------*/
+/*!
+ * \brief   rasteropVipLow()
+ *
+ * \param[in]    data   ptr to image data
+ * \param[in]    pixw   width
+ * \param[in]    pixh   height
+ * \param[in]    depth  depth
+ * \param[in]    wpl    wpl
+ * \param[in]    x      x val of UL corner of rectangle
+ * \param[in]    w      width of rectangle
+ * \param[in]    shift  + shifts data downward in vertical column
+ * \return  0 if OK; 1 on error.
+ *
+ * <pre>
+ * Notes:
+ *      (1) This clears the pixels that are left exposed after the
+ *          translation.  You can consider them as pixels that are
+ *          shifted in from outside the image.  This can be later
+ *          overridden by the incolor parameter in higher-level functions
+ *          that call this.  For example, for images with depth > 1,
+ *          these pixels are cleared to black; to be white they
+ *          must later be SET to white.  See, e.g., pixRasteropVip().
+ *      (2) This function scales the width to accommodate any depth,
+ *          performs clipping, and then does the in-place rasterop.
+ * </pre>
+ */
+void
+rasteropVipLow(l_uint32  *data,
+               l_int32    pixw,
+               l_int32    pixh,
+               l_int32    depth,
+               l_int32    wpl,
+               l_int32    x,
+               l_int32    w,
+               l_int32    shift)
+{
+l_int32    fwpartb;    /* boolean (1, 0) if first word is partial */
+l_int32    fwpart2b;   /* boolean (1, 0) if first word is doubly partial */
+l_uint32   fwmask;     /* mask for first partial word */
+l_int32    fwbits;     /* first word bits in ovrhang */
+l_uint32  *pdfwpart = NULL;   /* ptr to first partial dest word */
+l_uint32  *psfwpart = NULL;   /* ptr to first partial src word */
+l_int32    fwfullb;    /* boolean (1, 0) if there exists a full word */
+l_int32    nfullw;     /* number of full words */
+l_uint32  *pdfwfull = NULL;   /* ptr to first full dest word */
+l_uint32  *psfwfull = NULL;   /* ptr to first full src word */
+l_int32    lwpartb;    /* boolean (1, 0) if last word is partial */
+l_uint32   lwmask;     /* mask for last partial word */
+l_int32    lwbits;     /* last word bits in ovrhang */
+l_uint32  *pdlwpart = NULL;   /* ptr to last partial dest word */
+l_uint32  *pslwpart = NULL;   /* ptr to last partial src word */
+l_int32    dirwpl;     /* directed wpl (-wpl * sign(shift)) */
+l_int32    absshift;   /* absolute value of shift; for use in iterator */
+l_int32    vlimit;     /* vertical limit value for iterations */
+l_int32    i, j;
+
+
+   /*--------------------------------------------------------*
+    *            Scale horizontal dimensions by depth        *
+    *--------------------------------------------------------*/
+    if (depth != 1) {
+        pixw *= depth;
+        x *= depth;
+        w *= depth;
+    }
+
+
+   /*--------------------------------------------------------*
+    *                   Clip horizontally                    *
+    *--------------------------------------------------------*/
+    if (x < 0) {
+        w += x;    /* reduce w */
+        x = 0;     /* clip to x = 0 */
+    }
+    if (x >= pixw || w <= 0)  /* no part of vertical slice is in the image */
+        return;
+
+    if (x + w > pixw)
+        w = pixw - x;   /* clip to x + w = pixw */
+
+    /*--------------------------------------------------------*
+     *                Preliminary calculations                *
+     *--------------------------------------------------------*/
+        /* is the first word partial? */
+    if ((x & 31) == 0) {  /* if not */
+        fwpartb = 0;
+        fwbits = 0;
+    } else {  /* if so */
+        fwpartb = 1;
+        fwbits = 32 - (x & 31);
+        fwmask = rmask32[fwbits];
+        if (shift >= 0) { /* go up from bottom */
+            pdfwpart = data + wpl * (pixh - 1) + (x >> 5);
+            psfwpart = data + wpl * (pixh - 1 - shift) + (x >> 5);
+        } else {  /* go down from top */
+            pdfwpart = data + (x >> 5);
+            psfwpart = data - wpl * shift + (x >> 5);
+        }
+    }
+
+        /* is the first word doubly partial? */
+    if (w >= fwbits) {  /* if not */
+        fwpart2b = 0;
+    } else {  /* if so */
+        fwpart2b = 1;
+        fwmask &= lmask32[32 - fwbits + w];
+    }
+
+        /* is there a full dest word? */
+    if (fwpart2b == 1) {  /* not */
+        fwfullb = 0;
+        nfullw = 0;
+    } else {
+        nfullw = (w - fwbits) >> 5;
+        if (nfullw == 0) {  /* if not */
+            fwfullb = 0;
+        } else {  /* if so */
+            fwfullb = 1;
+            if (fwpartb) {
+                pdfwfull = pdfwpart + 1;
+                psfwfull = psfwpart + 1;
+            } else if (shift >= 0) { /* go up from bottom */
+                pdfwfull = data + wpl * (pixh - 1) + (x >> 5);
+                psfwfull = data + wpl * (pixh - 1 - shift) + (x >> 5);
+            } else {  /* go down from top */
+                pdfwfull = data + (x >> 5);
+                psfwfull = data - wpl * shift + (x >> 5);
+            }
+        }
+    }
+
+        /* is the last word partial? */
+    lwbits = (x + w) & 31;
+    if (fwpart2b == 1 || lwbits == 0) {  /* if not */
+        lwpartb = 0;
+    } else {
+        lwpartb = 1;
+        lwmask = lmask32[lwbits];
+        if (fwpartb) {
+            pdlwpart = pdfwpart + 1 + nfullw;
+            pslwpart = psfwpart + 1 + nfullw;
+        } else if (shift >= 0) { /* go up from bottom */
+            pdlwpart = data + wpl * (pixh - 1) + (x >> 5) + nfullw;
+            pslwpart = data + wpl * (pixh - 1 - shift) + (x >> 5) + nfullw;
+        } else {  /* go down from top */
+            pdlwpart = data + (x >> 5) + nfullw;
+            pslwpart = data - wpl * shift + (x >> 5) + nfullw;
+        }
+    }
+
+        /* determine the direction of flow from the shift
+         * If the shift >= 0, data flows downard from src
+         * to dest, starting at the bottom and working up.
+         * If shift < 0, data flows upward from src to
+         * dest, starting at the top and working down. */
+    dirwpl = (shift >= 0) ? -wpl : wpl;
+    absshift = L_ABS(shift);
+    vlimit = L_MAX(0, pixh - absshift);
+
+
+    /*--------------------------------------------------------*
+     *            Now we're ready to do the ops               *
+     *--------------------------------------------------------*/
+
+        /* Do the first partial word */
+    if (fwpartb) {
+        for (i = 0; i < vlimit; i++) {
+            *pdfwpart = COMBINE_PARTIAL(*pdfwpart, *psfwpart, fwmask);
+            pdfwpart += dirwpl;
+            psfwpart += dirwpl;
+        }
+
+            /* Clear the incoming pixels */
+        for (i = vlimit; i < pixh; i++) {
+            *pdfwpart = COMBINE_PARTIAL(*pdfwpart, 0x0, fwmask);
+            pdfwpart += dirwpl;
+        }
+    }
+
+        /* Do the full words */
+    if (fwfullb) {
+        for (i = 0; i < vlimit; i++) {
+            for (j = 0; j < nfullw; j++)
+                *(pdfwfull + j) = *(psfwfull + j);
+            pdfwfull += dirwpl;
+            psfwfull += dirwpl;
+        }
+
+            /* Clear the incoming pixels */
+        for (i = vlimit; i < pixh; i++) {
+            for (j = 0; j < nfullw; j++)
+                *(pdfwfull + j) = 0x0;
+            pdfwfull += dirwpl;
+        }
+    }
+
+        /* Do the last partial word */
+    if (lwpartb) {
+        for (i = 0; i < vlimit; i++) {
+            *pdlwpart = COMBINE_PARTIAL(*pdlwpart, *pslwpart, lwmask);
+            pdlwpart += dirwpl;
+            pslwpart += dirwpl;
+        }
+
+            /* Clear the incoming pixels */
+        for (i = vlimit; i < pixh; i++) {
+            *pdlwpart = COMBINE_PARTIAL(*pdlwpart, 0x0, lwmask);
+            pdlwpart += dirwpl;
+        }
+    }
+}
+
+
+
+/*--------------------------------------------------------------------*
+ *       Low level in-place full width horizontal block transfer      *
+ *--------------------------------------------------------------------*/
+/*!
+ * \brief   rasteropHipLow()
+ *
+ * \param[in]    data   ptr to image data
+ * \param[in]    pixh   height
+ * \param[in]    depth  depth
+ * \param[in]    wpl    wpl
+ * \param[in]    y      y val of UL corner of rectangle
+ * \param[in]    h      height of rectangle
+ * \param[in]    shift  + shifts data to the left in a horizontal column
+ * \return  0 if OK; 1 on error.
+ *
+ * <pre>
+ * Notes:
+ *      (1) This clears the pixels that are left exposed after the rasterop.
+ *          Therefore, for Pix with depth > 1, these pixels become black,
+ *          and must be subsequently SET if they are to be white.
+ *          For example, see pixRasteropHip().
+ *      (2) This function performs clipping and calls shiftDataHorizontalLow()
+ *          to do the in-place rasterop on each line.
+ * </pre>
+ */
+void
+rasteropHipLow(l_uint32  *data,
+               l_int32    pixh,
+               l_int32    depth,
+               l_int32    wpl,
+               l_int32    y,
+               l_int32    h,
+               l_int32    shift)
+{
+l_int32    i;
+l_uint32  *line;
+
+        /* clip band if necessary */
+    if (y < 0) {
+        h += y;  /* reduce h */
+        y = 0;   /* clip to y = 0 */
+    }
+    if (h <= 0 || y > pixh)  /* no part of horizontal slice is in the image */
+        return;
+
+    if (y + h > pixh)
+        h = pixh - y;   /* clip to y + h = pixh */
+
+    for (i = y; i < y + h; i++) {
+        line = data + i * wpl;
+        shiftDataHorizontalLow(line, wpl, line, wpl, shift * depth);
+    }
+}
+
+
+/*!
+ * \brief   shiftDataHorizontalLow()
+ *
+ * \param[in]    datad  ptr to beginning of dest line
+ * \param[in]    wpld   wpl of dest
+ * \param[in]    datas  ptr to beginning of src line
+ * \param[in]    wpls   wpl of src
+ * \param[in]    shift  horizontal shift of block; >0 is to right
+ * \return  void
+ *
+ * <pre>
+ * Notes:
+ *      (1) This can also be used for in-place operation; see, e.g.,
+ *          rasteropHipLow().
+ *      (2) We are clearing the pixels that are shifted in from
+ *          outside the image.  This can be overridden by the
+ *          incolor parameter in higher-level functions that call this.
+ * </pre>
+ */
+static void
+shiftDataHorizontalLow(l_uint32  *datad,
+                       l_int32    wpld,
+                       l_uint32  *datas,
+                       l_int32    wpls,
+                       l_int32    shift)
+{
+l_int32    j, firstdw, wpl, rshift, lshift;
+l_uint32  *lined, *lines;
+
+    lined = datad;
+    lines = datas;
+
+    if (shift >= 0) {   /* src shift to right; data flows to
+                         * right, starting at right edge and
+                         * progressing leftward. */
+        firstdw = shift / 32;
+        wpl = L_MIN(wpls, wpld - firstdw);
+        lined += firstdw + wpl - 1;
+        lines += wpl - 1;
+        rshift = shift & 31;
+        if (rshift == 0) {
+            for (j = 0; j < wpl; j++)
+                *lined-- = *lines--;
+
+                /* clear out the rest to the left edge */
+            for (j = 0; j < firstdw; j++)
+                *lined-- = 0;
+        } else {
+            lshift = 32 - rshift;
+            for (j = 1; j < wpl; j++) {
+                *lined-- = *(lines - 1) << lshift | *lines >> rshift;
+                lines--;
+            }
+            *lined = *lines >> rshift;  /* partial first */
+
+                /* clear out the rest to the left edge */
+            *lined &= ~lmask32[rshift];
+            lined--;
+            for (j = 0; j < firstdw; j++)
+                *lined-- = 0;
+        }
+    } else {  /* src shift to left; data flows to left, starting
+             * at left edge and progressing rightward. */
+        firstdw = (-shift) / 32;
+        wpl = L_MIN(wpls - firstdw, wpld);
+        lines += firstdw;
+        lshift = (-shift) & 31;
+        if (lshift == 0) {
+            for (j = 0; j < wpl; j++)
+                *lined++ = *lines++;
+
+                /* clear out the rest to the right edge */
+            for (j = 0; j < firstdw; j++)
+                *lined++ = 0;
+        } else {
+            rshift = 32 - lshift;
+            for (j = 1; j < wpl; j++) {
+                *lined++ = *lines << lshift | *(lines + 1) >> rshift;
+                lines++;
+            }
+            *lined = *lines << lshift;  /* partial last */
+
+                /* clear out the rest to the right edge */
+                /* first clear the lshift pixels of this partial word */
+            *lined &= ~rmask32[lshift];
+            lined++;
+                /* then the remaining words to the right edge */
+            for (j = 0; j < firstdw; j++)
+                *lined++ = 0;
+        }
+    }
+}