blob: 45fb90dc44bcf1c9a879dcedfddfd48324311235 [file] [log] [blame]
/*====================================================================*
- Copyright (C) 2001 Leptonica. All rights reserved.
- This software is distributed in the hope that it will be
- useful, but with NO WARRANTY OF ANY KIND.
- No author or distributor accepts responsibility to anyone for the
- consequences of using this software, or for whether it serves any
- particular purpose or works at all, unless he or she says so in
- writing. Everyone is granted permission to copy, modify and
- redistribute this source code, for commercial or non-commercial
- purposes, with the following restrictions: (1) the origin of this
- source code must not be misrepresented; (2) modified versions must
- be plainly marked as such; and (3) this notice may not be removed
- or altered from any source or modified source distribution.
*====================================================================*/
/*
* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "allheaders.h"
/*--------------------------------------------------------------------*
* General rasterop (basic pix interface) *
*--------------------------------------------------------------------*/
/*!
* pixRasterop()
*
* Input: pixd (dest pix)
* dx (x val of UL corner of dest rectangle)
* dy (y val of UL corner of dest rectangle)
* dw (width of dest rectangle)
* dh (height of dest rectangle)
* op (op code)
* pixs (src pix)
* sx (x val of UL corner of src rectangle)
* sy (y val of UL corner of src rectangle)
* Return: 0 if OK; 1 on error.
*
* 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_int32
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 *
*--------------------------------------------------------------------*/
/*!
* pixRasteropVip()
*
* Input: pixd (in-place)
* x (left edge of vertical band)
* w (width of vertical band)
* vshift (vertical shift of band; vshift > 0 is down)
* 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.
*/
l_int32
pixRasteropVip(PIX *pixd,
l_int32 x,
l_int32 w,
l_int32 vshift,
l_int32 incolor)
{
l_int32 h, d, op;
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 (vshift == 0)
return 0;
rasteropVipLow(pixGetData(pixd),
pixGetWidth(pixd), pixGetHeight(pixd),
pixGetDepth(pixd), pixGetWpl(pixd),
x, w, vshift);
d = pixGetDepth(pixd);
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, x, 0, w, vshift, op, NULL, 0, 0);
else { /* vshift < 0 */
h = pixGetHeight(pixd);
pixRasterop(pixd, x, h + vshift, w, -vshift, op, NULL, 0, 0);
}
return 0;
}
/*!
* pixRasteropHip()
*
* Input: pixd (in-place operation)
* y (top of horizontal band)
* h (height of horizontal band)
* hshift (horizontal shift of band; hshift > 0 is to right)
* 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.
*/
l_int32
pixRasteropHip(PIX *pixd,
l_int32 y,
l_int32 h,
l_int32 hshift,
l_int32 incolor)
{
l_int32 w, d, op;
PROCNAME("pixRasteropHip");
if (!pixd)
return ERROR_INT("pixd not defined", procName, 1);
if (hshift == 0)
return 0;
rasteropHipLow(pixGetData(pixd), pixGetHeight(pixd),
pixGetDepth(pixd), pixGetWpl(pixd),
y, h, hshift);
d = pixGetDepth(pixd);
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, y, hshift, h, op, NULL, 0, 0);
else { /* hshift < 0 */
w = pixGetWidth(pixd);
pixRasterop(pixd, w + hshift, y, -hshift, h, op, NULL, 0, 0);
}
return 0;
}
/*--------------------------------------------------------------------*
* Full image translation (general and in-place) *
*--------------------------------------------------------------------*/
/*!
* pixTranslate()
*
* Input: pixd (<optional> destination: this can be null,
* equal to pixs, or different from pixs)
* pixs
* hshift (horizontal shift; hshift > 0 is to right)
* vshift (vertical shift; vshift > 0 is down)
* 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;
}
/*!
* pixRasteropIP()
*
* Input: pixd (in-place translation)
* hshift (horizontal shift; hshift > 0 is to right)
* vshift (vertical shift; vshift > 0 is down)
* incolor (L_BRING_IN_WHITE, L_BRING_IN_BLACK)
* Return: 0 if OK; 1 on error
*/
l_int32
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);
w = pixGetWidth(pixd);
h = pixGetHeight(pixd);
pixRasteropHip(pixd, 0, h, hshift, incolor);
pixRasteropVip(pixd, 0, w, vshift, incolor);
return 0;
}
/*--------------------------------------------------------------------*
* Full image rasterop with no shifts *
*--------------------------------------------------------------------*/
/*!
* pixRasteropFullImage()
*
* Input: pixd
* pixs
* 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_int32
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;
}