/*====================================================================* - 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 rop.c *
* General rasterop * l_int32 pixRasterop() * * In-place full band translation * l_int32 pixRasteropVip() * l_int32 pixRasteropHip() * * Full image translation (general and in-place) * l_int32 pixTranslate() * l_int32 pixRasteropIP() * * Full image rasterop with no translation * l_int32 pixRasteropFullImage() **/ #include
* Notes: * (1) This has the standard set of 9 args for rasterop. * This function is your friend; it is worth memorizing! * (2) If the operation involves only dest, this calls * rasteropUniLow(). Otherwise, checks depth of the * src and dest, and if they match, calls rasteropLow(). * (3) For the two-image operation, where both pixs and pixd * are defined, they are typically different images. However * there are cases, such as pixSetMirroredBorder(), where * in-place operations can be done, blitting pixels from * one part of pixd to another. Consequently, we permit * such operations. If you use them, be sure that there * is no overlap between the source and destination rectangles * in pixd (!) * * Background: * ----------- * * There are 18 operations, described by the op codes in pix.h. * * One, PIX_DST, is a no-op. * * Three, PIX_CLR, PIX_SET, and PIX_NOT(PIX_DST) operate only on the dest. * These are handled by the low-level rasteropUniLow(). * * The other 14 involve the both the src and the dest, and depend on * the bit values of either just the src or the bit values of both * src and dest. They are handled by rasteropLow(): * * PIX_SRC s * PIX_NOT(PIX_SRC) ~s * PIX_SRC | PIX_DST s | d * PIX_SRC & PIX_DST s & d * PIX_SRC ^ PIX_DST s ^ d * PIX_NOT(PIX_SRC) | PIX_DST ~s | d * PIX_NOT(PIX_SRC) & PIX_DST ~s & d * PIX_NOT(PIX_SRC) ^ PIX_DST ~s ^ d * PIX_SRC | PIX_NOT(PIX_DST) s | ~d * PIX_SRC & PIX_NOT(PIX_DST) s & ~d * PIX_SRC ^ PIX_NOT(PIX_DST) s ^ ~d * PIX_NOT(PIX_SRC | PIX_DST) ~(s | d) * PIX_NOT(PIX_SRC & PIX_DST) ~(s & d) * PIX_NOT(PIX_SRC ^ PIX_DST) ~(s ^ d) * * Each of these is implemented with one of three low-level * functions, depending on the alignment of the left edge * of the src and dest rectangles: * * a fastest implementation if both left edges are * (32-bit) word aligned * * a very slightly slower implementation if both left * edges have the same relative (32-bit) word alignment * * the general routine that is invoked when * both left edges have different word alignment * * Of the 14 binary rasterops above, only 12 are unique * logical combinations (out of a possible 16) of src * and dst bits: * * (sd) (11) (10) (01) (00) * ----------------------------------------------- * s 1 1 0 0 * ~s 0 1 0 1 * s | d 1 1 1 0 * s & d 1 0 0 0 * s ^ d 0 1 1 0 * ~s | d 1 0 1 1 * ~s & d 0 0 1 0 * ~s ^ d 1 0 0 1 * s | ~d 1 1 0 1 * s & ~d 0 1 0 0 * s ^ ~d 1 0 0 1 * ~(s | d) 0 0 0 1 * ~(s & d) 0 1 1 1 * ~(s ^ d) 1 0 0 1 * * Note that the following three operations are equivalent: * ~(s ^ d) * ~s ^ d * s ^ ~d * and in the implementation, we call them out with the first form; * namely, ~(s ^ d). * * Of the 16 possible binary combinations of src and dest bits, * the remaining 4 unique ones are independent of the src bit. * They depend on either just the dest bit or on neither * the src nor dest bits: * * d 1 0 1 0 (indep. of s) * ~d 0 1 0 1 (indep. of s) * CLR 0 0 0 0 (indep. of both s & d) * SET 1 1 1 1 (indep. of both s & d) * * As mentioned above, three of these are implemented by * rasteropUniLow(), and one is a no-op. * * How can these operation codes be represented by bits * in such a way that when the basic operations are performed * on the bits the results are unique for unique * operations, and mimic the logic table given above? * * The answer is to choose a particular order of the pairings: * (sd) (11) (10) (01) (00) * (which happens to be the same as in the above table) * and to translate the result into 4-bit representations * of s and d. For example, the Sun rasterop choice * (omitting the extra bit for clipping) is * * PIX_SRC 0xc * PIX_DST 0xa * * This corresponds to our pairing order given above: * (sd) (11) (10) (01) (00) * where for s = 1 we get the bit pattern * PIX_SRC: 1 1 0 0 (0xc) * and for d = 1 we get the pattern * PIX_DST: 1 0 1 0 (0xa) * * OK, that's the pairing order that Sun chose. How many different * ways can we assign bit patterns to PIX_SRC and PIX_DST to get * the boolean ops to work out? Any of the 4 pairs can be put * in the first position, any of the remaining 3 pairs can go * in the second; and one of the remaining 2 pairs can go the the third. * There is a total of 4*3*2 = 24 ways these pairs can be permuted. **/ l_ok pixRasterop(PIX *pixd, l_int32 dx, l_int32 dy, l_int32 dw, l_int32 dh, l_int32 op, PIX *pixs, l_int32 sx, l_int32 sy) { l_int32 dd; PROCNAME("pixRasterop"); if (!pixd) return ERROR_INT("pixd not defined", procName, 1); if (op == PIX_DST) /* no-op */ return 0; /* Check if operation is only on dest */ dd = pixGetDepth(pixd); if (op == PIX_CLR || op == PIX_SET || op == PIX_NOT(PIX_DST)) { rasteropUniLow(pixGetData(pixd), pixGetWidth(pixd), pixGetHeight(pixd), dd, pixGetWpl(pixd), dx, dy, dw, dh, op); return 0; } if (!pixs) return ERROR_INT("pixs not defined", procName, 1); /* Check depth of src and dest; these must agree */ if (dd != pixGetDepth(pixs)) return ERROR_INT("depths of pixs and pixd differ", procName, 1); rasteropLow(pixGetData(pixd), pixGetWidth(pixd), pixGetHeight(pixd), dd, pixGetWpl(pixd), dx, dy, dw, dh, op, pixGetData(pixs), pixGetWidth(pixs), pixGetHeight(pixs), pixGetWpl(pixs), sx, sy); return 0; } /*--------------------------------------------------------------------* * In-place full band translation * *--------------------------------------------------------------------*/ /*! * \brief pixRasteropVip() * * \param[in] pixd in-place * \param[in] bx left edge of vertical band * \param[in] bw width of vertical band * \param[in] vshift vertical shift of band; vshift > 0 is down * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK * \return 0 if OK; 1 on error * *
* Notes: * (1) This rasterop translates a vertical band of the * image either up or down, bringing in either white * or black pixels from outside the image. * (2) The vertical band extends the full height of pixd. * (3) If a colormap exists, the nearest color to white or black * is brought in. **/ l_ok pixRasteropVip(PIX *pixd, l_int32 bx, l_int32 bw, l_int32 vshift, l_int32 incolor) { l_int32 w, h, d, index, op; PIX *pixt; PIXCMAP *cmap; PROCNAME("pixRasteropVip"); if (!pixd) return ERROR_INT("pixd not defined", procName, 1); if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) return ERROR_INT("invalid value for incolor", procName, 1); if (bw <= 0) return ERROR_INT("bw must be > 0", procName, 1); if (vshift == 0) return 0; pixGetDimensions(pixd, &w, &h, &d); rasteropVipLow(pixGetData(pixd), w, h, d, pixGetWpl(pixd), bx, bw, vshift); cmap = pixGetColormap(pixd); if (!cmap) { if ((d == 1 && incolor == L_BRING_IN_BLACK) || (d > 1 && incolor == L_BRING_IN_WHITE)) op = PIX_SET; else op = PIX_CLR; /* Set the pixels brought in at top or bottom */ if (vshift > 0) pixRasterop(pixd, bx, 0, bw, vshift, op, NULL, 0, 0); else /* vshift < 0 */ pixRasterop(pixd, bx, h + vshift, bw, -vshift, op, NULL, 0, 0); return 0; } /* Get the nearest index and fill with that */ if (incolor == L_BRING_IN_BLACK) pixcmapGetRankIntensity(cmap, 0.0, &index); else /* white */ pixcmapGetRankIntensity(cmap, 1.0, &index); pixt = pixCreate(bw, L_ABS(vshift), d); pixSetAllArbitrary(pixt, index); if (vshift > 0) pixRasterop(pixd, bx, 0, bw, vshift, PIX_SRC, pixt, 0, 0); else /* vshift < 0 */ pixRasterop(pixd, bx, h + vshift, bw, -vshift, PIX_SRC, pixt, 0, 0); pixDestroy(&pixt); return 0; } /*! * \brief pixRasteropHip() * * \param[in] pixd in-place operation * \param[in] by top of horizontal band * \param[in] bh height of horizontal band * \param[in] hshift horizontal shift of band; hshift > 0 is to right * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK * \return 0 if OK; 1 on error * *
* Notes: * (1) This rasterop translates a horizontal band of the * image either left or right, bringing in either white * or black pixels from outside the image. * (2) The horizontal band extends the full width of pixd. * (3) If a colormap exists, the nearest color to white or black * is brought in. **/ l_ok pixRasteropHip(PIX *pixd, l_int32 by, l_int32 bh, l_int32 hshift, l_int32 incolor) { l_int32 w, h, d, index, op; PIX *pixt; PIXCMAP *cmap; PROCNAME("pixRasteropHip"); if (!pixd) return ERROR_INT("pixd not defined", procName, 1); if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) return ERROR_INT("invalid value for incolor", procName, 1); if (bh <= 0) return ERROR_INT("bh must be > 0", procName, 1); if (hshift == 0) return 0; pixGetDimensions(pixd, &w, &h, &d); rasteropHipLow(pixGetData(pixd), h, d, pixGetWpl(pixd), by, bh, hshift); cmap = pixGetColormap(pixd); if (!cmap) { if ((d == 1 && incolor == L_BRING_IN_BLACK) || (d > 1 && incolor == L_BRING_IN_WHITE)) op = PIX_SET; else op = PIX_CLR; /* Set the pixels brought in at left or right */ if (hshift > 0) pixRasterop(pixd, 0, by, hshift, bh, op, NULL, 0, 0); else /* hshift < 0 */ pixRasterop(pixd, w + hshift, by, -hshift, bh, op, NULL, 0, 0); return 0; } /* Get the nearest index and fill with that */ if (incolor == L_BRING_IN_BLACK) pixcmapGetRankIntensity(cmap, 0.0, &index); else /* white */ pixcmapGetRankIntensity(cmap, 1.0, &index); pixt = pixCreate(L_ABS(hshift), bh, d); pixSetAllArbitrary(pixt, index); if (hshift > 0) pixRasterop(pixd, 0, by, hshift, bh, PIX_SRC, pixt, 0, 0); else /* hshift < 0 */ pixRasterop(pixd, w + hshift, by, -hshift, bh, PIX_SRC, pixt, 0, 0); pixDestroy(&pixt); return 0; } /*--------------------------------------------------------------------* * Full image translation (general and in-place) * *--------------------------------------------------------------------*/ /*! * \brief pixTranslate() * * \param[in] pixd [optional] destination: this can be null, * equal to pixs, or different from pixs * \param[in] pixs * \param[in] hshift horizontal shift; hshift > 0 is to right * \param[in] vshift vertical shift; vshift > 0 is down * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK * \return pixd, or NULL on error. * *
* Notes: * (1) The general pattern is: * pixd = pixTranslate(pixd, pixs, ...); * For clarity, when you know the case, use one of these: * pixd = pixTranslate(NULL, pixs, ...); // new * pixTranslate(pixs, pixs, ...); // in-place * pixTranslate(pixd, pixs, ...); // to existing pixd * (2) If an existing pixd is not the same size as pixs, the * image data will be reallocated. **/ PIX * pixTranslate(PIX *pixd, PIX *pixs, l_int32 hshift, l_int32 vshift, l_int32 incolor) { PROCNAME("pixTranslate"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); /* Prepare pixd for in-place operation */ if ((pixd = pixCopy(pixd, pixs)) == NULL) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); pixRasteropIP(pixd, hshift, vshift, incolor); return pixd; } /*! * \brief pixRasteropIP() * * \param[in] pixd in-place translation * \param[in] hshift horizontal shift; hshift > 0 is to right * \param[in] vshift vertical shift; vshift > 0 is down * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK * \return 0 if OK; 1 on error */ l_ok pixRasteropIP(PIX *pixd, l_int32 hshift, l_int32 vshift, l_int32 incolor) { l_int32 w, h; PROCNAME("pixRasteropIP"); if (!pixd) return ERROR_INT("pixd not defined", procName, 1); pixGetDimensions(pixd, &w, &h, NULL); pixRasteropHip(pixd, 0, h, hshift, incolor); pixRasteropVip(pixd, 0, w, vshift, incolor); return 0; } /*--------------------------------------------------------------------* * Full image rasterop with no shifts * *--------------------------------------------------------------------*/ /*! * \brief pixRasteropFullImage() * * \param[in] pixd * \param[in] pixs * \param[in] op any of the op-codes * \return 0 if OK; 1 on error * *
* Notes: * ~ this is a wrapper for a common 2-image raster operation * ~ both pixs and pixd must be defined * ~ the operation is performed with aligned UL corners of pixs and pixd * ~ the operation clips to the smallest pix; if the width or height * of pixd is larger than pixs, some pixels in pixd will be unchanged **/ l_ok pixRasteropFullImage(PIX *pixd, PIX *pixs, l_int32 op) { PROCNAME("pixRasteropFullImage"); if (!pixd) return ERROR_INT("pixd not defined", procName, 1); if (!pixs) return ERROR_INT("pixs not defined", procName, 1); pixRasterop(pixd, 0, 0, pixGetWidth(pixd), pixGetHeight(pixd), op, pixs, 0, 0); return 0; }