Mercurial > hgrepos > Python2 > PyMuPDF
diff mupdf-source/thirdparty/leptonica/src/pix2.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/pix2.c Mon Sep 15 11:43:07 2025 +0200 @@ -0,0 +1,3538 @@ +/*====================================================================* + - 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 pix2.c + * <pre> + * + * This file has these basic operations: + * + * (1) Get and set: individual pixels, full image, rectangular region, + * pad pixels, border pixels, and color components for RGB + * (2) Add and remove border pixels + * (3) Endian byte swaps + * (4) Simple method for byte-processing images (instead of words) + * + * Pixel poking + * l_int32 pixGetPixel() + * l_int32 pixSetPixel() + * l_int32 pixGetRGBPixel() + * l_int32 pixSetRGBPixel() + * l_int32 pixSetCmapPixel() + * l_int32 pixGetRandomPixel() + * l_int32 pixClearPixel() + * l_int32 pixFlipPixel() + * void setPixelLow() + * + * Find black or white value + * l_int32 pixGetBlackOrWhiteVal() + * + * Full image clear/set/set-to-arbitrary-value + * l_int32 pixClearAll() + * l_int32 pixSetAll() + * l_int32 pixSetAllGray() + * l_int32 pixSetAllArbitrary() + * l_int32 pixSetBlackOrWhite() + * l_int32 pixSetComponentArbitrary() + * + * Rectangular region clear/set/set-to-arbitrary-value/blend + * l_int32 pixClearInRect() + * l_int32 pixSetInRect() + * l_int32 pixSetInRectArbitrary() + * l_int32 pixBlendInRect() + * + * Set pad bits + * l_int32 pixSetPadBits() + * l_int32 pixSetPadBitsBand() + * + * Assign border pixels + * l_int32 pixSetOrClearBorder() + * l_int32 pixSetBorderVal() + * l_int32 pixSetBorderRingVal() + * l_int32 pixSetMirroredBorder() + * PIX *pixCopyBorder() + * + * Add and remove border + * PIX *pixAddBorder() + * PIX *pixAddBlackOrWhiteBorder() + * PIX *pixAddBorderGeneral() + * PIX *pixAddMultipleBlackWhiteBorders() + * PIX *pixRemoveBorder() + * PIX *pixRemoveBorderGeneral() + * PIX *pixRemoveBorderToSize() + * PIX *pixAddMirroredBorder() + * PIX *pixAddRepeatedBorder() + * PIX *pixAddMixedBorder() + * PIX *pixAddContinuedBorder() + * + * Helper functions using alpha + * l_int32 pixShiftAndTransferAlpha() + * PIX *pixDisplayLayersRGBA() + * + * Color sample setting and extraction + * PIX *pixCreateRGBImage() + * PIX *pixGetRGBComponent() + * l_int32 pixSetRGBComponent() + * PIX *pixGetRGBComponentCmap() + * l_int32 pixCopyRGBComponent() + * l_int32 composeRGBPixel() + * l_int32 composeRGBAPixel() + * void extractRGBValues() + * void extractRGBAValues() + * l_int32 extractMinMaxComponent() + * l_int32 pixGetRGBLine() + * + * Raster line pixel setter + * l_int32 setLineDataVal() + * + * Conversion between big and little endians + * PIX *pixEndianByteSwapNew() + * l_int32 pixEndianByteSwap() + * l_int32 lineEndianByteSwap() + * PIX *pixEndianTwoByteSwapNew() + * l_int32 pixEndianTwoByteSwap() + * + * Extract raster data as binary string + * l_int32 pixGetRasterData() + * + * Test alpha component opaqueness + * l_int32 pixAlphaIsOpaque() + * + * Infer resolution from image size + * l_int32 pixInferResolution() + * + * Setup helpers for 8 bpp byte processing + * l_uint8 **pixSetupByteProcessing() + * l_int32 pixCleanupByteProcessing() + * + * Setting parameters for antialias masking with alpha transforms + * void l_setAlphaMaskBorder() + * </pre> + */ + +#ifdef HAVE_CONFIG_H +#include <config_auto.h> +#endif /* HAVE_CONFIG_H */ + +#include <string.h> +#include "allheaders.h" +#include "pix_internal.h" + +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}; + + /* This is a global that determines the default 8 bpp alpha mask values + * for rings at distance 1 and 2 from the border. Declare extern + * to use. To change the values, use l_setAlphaMaskBorder(). */ +LEPT_DLL l_float32 AlphaMaskBorderVals[2] = {0.0, 0.5}; + + +#ifndef NO_CONSOLE_IO +#define DEBUG_SERIALIZE 0 +#endif /* ~NO_CONSOLE_IO */ + + +/*-------------------------------------------------------------* + * Pixel poking * + *-------------------------------------------------------------*/ +/*! + * \brief pixGetPixel() + * + * \param[in] pix + * \param[in] x,y pixel coords + * \param[out] pval pixel value + * \return 0 if OK; 1 or 2 on error + * + * <pre> + * Notes: + * (1) This returns the value in the data array. If the pix is + * colormapped, it returns the colormap index, not the rgb value. + * (2) Because of the function overhead and the parameter checking, + * this is much slower than using the GET_DATA_*() macros directly. + * Speed on a 1 Mpixel RGB image, using a 3 GHz machine: + * * pixGet/pixSet: ~25 Mpix/sec + * * GET_DATA/SET_DATA: ~350 MPix/sec + * If speed is important and you're doing random access into + * the pix, use pixGetLinePtrs() and the array access macros. + * (3) If the point is outside the image, this returns an error (2), + * with 0 in %pval. To avoid spamming output, it fails silently. + * </pre> + */ +l_ok +pixGetPixel(PIX *pix, + l_int32 x, + l_int32 y, + l_uint32 *pval) +{ +l_int32 w, h, d, wpl, val; +l_uint32 *line, *data; + + if (!pval) + return ERROR_INT("&val not defined", __func__, 1); + *pval = 0; + if (!pix) + return ERROR_INT("pix not defined", __func__, 1); + + pixGetDimensions(pix, &w, &h, &d); + if (x < 0 || x >= w || y < 0 || y >= h) + return 2; + + wpl = pixGetWpl(pix); + data = pixGetData(pix); + line = data + y * wpl; + switch (d) + { + case 1: + val = GET_DATA_BIT(line, x); + break; + case 2: + val = GET_DATA_DIBIT(line, x); + break; + case 4: + val = GET_DATA_QBIT(line, x); + break; + case 8: + val = GET_DATA_BYTE(line, x); + break; + case 16: + val = GET_DATA_TWO_BYTES(line, x); + break; + case 32: + val = line[x]; + break; + default: + return ERROR_INT("depth must be in {1,2,4,8,16,32} bpp", __func__, 1); + } + + *pval = val; + return 0; +} + + +/*! + * \brief pixSetPixel() + * + * \param[in] pix + * \param[in] x,y pixel coords + * \param[in] val value to be inserted + * \return 0 if OK; 1 or 2 on error + * + * <pre> + * Notes: + * (1) Warning: the input value is not checked for overflow with respect + * the the depth of %pix, and the sign bit (if any) is ignored. + * * For d == 1, %val > 0 sets the bit on. + * * For d == 2, 4, 8 and 16, %val is masked to the maximum allowable + * pixel value, and any (invalid) higher order bits are discarded. + * (2) See pixGetPixel() for information on performance. + * (3) If the point is outside the image, this returns an error (2), + * with 0 in %pval. To avoid spamming output, it fails silently. + * </pre> + */ +l_ok +pixSetPixel(PIX *pix, + l_int32 x, + l_int32 y, + l_uint32 val) +{ +l_int32 w, h, d, wpl; +l_uint32 *line, *data; + + if (!pix) + return ERROR_INT("pix not defined", __func__, 1); + pixGetDimensions(pix, &w, &h, &d); + if (x < 0 || x >= w || y < 0 || y >= h) + return 2; + + data = pixGetData(pix); + wpl = pixGetWpl(pix); + line = data + y * wpl; + switch (d) + { + case 1: + if (val) + SET_DATA_BIT(line, x); + else + CLEAR_DATA_BIT(line, x); + break; + case 2: + SET_DATA_DIBIT(line, x, val); + break; + case 4: + SET_DATA_QBIT(line, x, val); + break; + case 8: + SET_DATA_BYTE(line, x, val); + break; + case 16: + SET_DATA_TWO_BYTES(line, x, val); + break; + case 32: + line[x] = val; + break; + default: + return ERROR_INT("depth must be in {1,2,4,8,16,32} bpp", __func__, 1); + } + + return 0; +} + + +/*! + * \brief pixGetRGBPixel() + * + * \param[in] pix 32 bpp rgb, not colormapped + * \param[in] x,y pixel coords + * \param[out] prval [optional] red component + * \param[out] pgval [optional] green component + * \param[out] pbval [optional] blue component + * \return 0 if OK; 1 or 2 on error + * + * <pre> + * Notes: + * (1) If the point is outside the image, this returns an error (2), + * with 0 in %pval. To avoid spamming output, it fails silently. + * </pre> + */ +l_ok +pixGetRGBPixel(PIX *pix, + l_int32 x, + l_int32 y, + l_int32 *prval, + l_int32 *pgval, + l_int32 *pbval) +{ +l_int32 w, h, d, wpl; +l_uint32 *data, *ppixel; + + if (prval) *prval = 0; + if (pgval) *pgval = 0; + if (pbval) *pbval = 0; + if (!prval && !pgval && !pbval) + return ERROR_INT("no output requested", __func__, 1); + if (!pix) + return ERROR_INT("pix not defined", __func__, 1); + pixGetDimensions(pix, &w, &h, &d); + if (d != 32) + return ERROR_INT("pix not 32 bpp", __func__, 1); + if (x < 0 || x >= w || y < 0 || y >= h) + return 2; + + wpl = pixGetWpl(pix); + data = pixGetData(pix); + ppixel = data + y * wpl + x; + if (prval) *prval = GET_DATA_BYTE(ppixel, COLOR_RED); + if (pgval) *pgval = GET_DATA_BYTE(ppixel, COLOR_GREEN); + if (pbval) *pbval = GET_DATA_BYTE(ppixel, COLOR_BLUE); + return 0; +} + + +/*! + * \brief pixSetRGBPixel() + * + * \param[in] pix 32 bpp rgb + * \param[in] x,y pixel coords + * \param[in] rval red component + * \param[in] gval green component + * \param[in] bval blue component + * \return 0 if OK; 1 or 2 on error + * + * <pre> + * Notes: + * (1) If the point is outside the image, this returns an error (2), + * and to avoid spamming output, it fails silently. + * </pre> + */ +l_ok +pixSetRGBPixel(PIX *pix, + l_int32 x, + l_int32 y, + l_int32 rval, + l_int32 gval, + l_int32 bval) +{ +l_int32 w, h, d, wpl; +l_uint32 pixel; +l_uint32 *data, *line; + + if (!pix) + return ERROR_INT("pix not defined", __func__, 1); + pixGetDimensions(pix, &w, &h, &d); + if (d != 32) + return ERROR_INT("pix not 32 bpp", __func__, 1); + if (x < 0 || x >= w || y < 0 || y >= h) + return 2; + + wpl = pixGetWpl(pix); + data = pixGetData(pix); + line = data + y * wpl; + composeRGBPixel(rval, gval, bval, &pixel); + *(line + x) = pixel; + return 0; +} + + +/*! + * \brief pixSetCmapPixel() + * + * \param[in] pix 2, 4 or 8 bpp, colormapped + * \param[in] x,y pixel coords + * \param[in] rval red component + * \param[in] gval green component + * \param[in] bval blue component + * \return 0 if OK; 1 or 2 on error + * + * <pre> + * Notes: + * (1) If the point is outside the image, this returns an error (2), + * and to avoid spamming output, it fails silently. + * (2) - If the color already exists, use it. + * - If the color does not exist in the colormap, it is added + * if possible. + * - If there is not room in the colormap for the new color: + * * if d < 8, return 2 with a warning. + * * if d == 8, find and use the nearest color. + * (3) Note that this operation scales with the number of colors + * in the colormap, and therefore can be very expensive if an + * attempt is made to set many pixels. (In that case, it should + * be implemented with a map:rgb-->index for efficiency.) + * This is best used with very small images. + * </pre> + */ +l_ok +pixSetCmapPixel(PIX *pix, + l_int32 x, + l_int32 y, + l_int32 rval, + l_int32 gval, + l_int32 bval) +{ +l_int32 w, h, d, index; +PIXCMAP *cmap; + + if (!pix) + return ERROR_INT("pix not defined", __func__, 1); + if ((cmap = pixGetColormap(pix)) == NULL) + return ERROR_INT("pix is not colormapped", __func__, 1); + pixGetDimensions(pix, &w, &h, &d); + if (d != 2 && d != 4 && d != 8) + return ERROR_INT("pix depth not 2, 4 or 8", __func__, 1); + if (x < 0 || x >= w || y < 0 || y >= h) + return 2; + + if (d == 8) { /* always add */ + pixcmapAddNearestColor(cmap, rval, gval, bval, &index); + } else { /* d < 8 */ + if (pixcmapAddNewColor(cmap, rval, gval, bval, &index) == 2) + return ERROR_INT("colormap is full", __func__, 2); + } + pixSetPixel(pix, x, y, index); + return 0; +} + + +/*! + * \brief pixGetRandomPixel() + * + * \param[in] pix any depth; can be colormapped + * \param[out] pval [optional] pixel value + * \param[out] px [optional] x coordinate chosen; can be null + * \param[out] py [optional] y coordinate chosen; can be null + * \return 0 if OK; 1 on error + * + * <pre> + * Notes: + * (1) If the pix is colormapped, it returns the rgb value. + * </pre> + */ +l_ok +pixGetRandomPixel(PIX *pix, + l_uint32 *pval, + l_int32 *px, + l_int32 *py) +{ +l_int32 w, h, x, y, rval, gval, bval; +l_uint32 val; +PIXCMAP *cmap; + + if (pval) *pval = 0; + if (px) *px = 0; + if (py) *py = 0; + if (!pval && !px && !py) + return ERROR_INT("no output requested", __func__, 1); + if (!pix) + return ERROR_INT("pix not defined", __func__, 1); + + pixGetDimensions(pix, &w, &h, NULL); + x = rand() % w; + y = rand() % h; + if (px) *px = x; + if (py) *py = y; + if (pval) { + pixGetPixel(pix, x, y, &val); + if ((cmap = pixGetColormap(pix)) != NULL) { + pixcmapGetColor(cmap, val, &rval, &gval, &bval); + composeRGBPixel(rval, gval, bval, pval); + } else { + *pval = val; + } + } + + return 0; +} + + +/*! + * \brief pixClearPixel() + * + * \param[in] pix any depth; warning if colormapped + * \param[in] x,y pixel coords + * \return 0 if OK; 1 or 2 on error. + * + * <pre> + * Notes: + * (1) If the point is outside the image, this returns an error (2), + * with 0 in %pval. To avoid spamming output, it fails silently. + * </pre> + */ +l_ok +pixClearPixel(PIX *pix, + l_int32 x, + l_int32 y) +{ +l_int32 w, h, d, wpl; +l_uint32 *line, *data; + + if (!pix) + return ERROR_INT("pix not defined", __func__, 1); + if (pixGetColormap(pix)) + L_WARNING("cmapped: setting to 0 may not be intended\n", __func__); + pixGetDimensions(pix, &w, &h, &d); + if (x < 0 || x >= w || y < 0 || y >= h) + return 2; + + wpl = pixGetWpl(pix); + data = pixGetData(pix); + line = data + y * wpl; + switch (d) + { + case 1: + CLEAR_DATA_BIT(line, x); + break; + case 2: + CLEAR_DATA_DIBIT(line, x); + break; + case 4: + CLEAR_DATA_QBIT(line, x); + break; + case 8: + SET_DATA_BYTE(line, x, 0); + break; + case 16: + SET_DATA_TWO_BYTES(line, x, 0); + break; + case 32: + line[x] = 0; + break; + default: + return ERROR_INT("depth must be in {1,2,4,8,16,32} bpp", __func__, 1); + } + + return 0; +} + + +/*! + * \brief pixFlipPixel() + * + * \param[in] pix any depth, warning if colormapped + * \param[in] x,y pixel coords + * \return 0 if OK; 1 or 2 on error + * + * <pre> + * Notes: + * (1) If the point is outside the image, this returns an error (2), + * with 0 in %pval. To avoid spamming output, it fails silently. + * </pre> + */ +l_ok +pixFlipPixel(PIX *pix, + l_int32 x, + l_int32 y) +{ +l_int32 w, h, d, wpl; +l_uint32 val; +l_uint32 *line, *data; + + if (!pix) + return ERROR_INT("pix not defined", __func__, 1); + if (pixGetColormap(pix)) + L_WARNING("cmapped: setting to 0 may not be intended\n", __func__); + pixGetDimensions(pix, &w, &h, &d); + if (x < 0 || x >= w || y < 0 || y >= h) + return 2; + + data = pixGetData(pix); + wpl = pixGetWpl(pix); + line = data + y * wpl; + switch (d) + { + case 1: + val = GET_DATA_BIT(line, x); + if (val) + CLEAR_DATA_BIT(line, x); + else + SET_DATA_BIT(line, x); + break; + case 2: + val = GET_DATA_DIBIT(line, x); + val ^= 0x3; + SET_DATA_DIBIT(line, x, val); + break; + case 4: + val = GET_DATA_QBIT(line, x); + val ^= 0xf; + SET_DATA_QBIT(line, x, val); + break; + case 8: + val = GET_DATA_BYTE(line, x); + val ^= 0xff; + SET_DATA_BYTE(line, x, val); + break; + case 16: + val = GET_DATA_TWO_BYTES(line, x); + val ^= 0xffff; + SET_DATA_TWO_BYTES(line, x, val); + break; + case 32: + val = line[x] ^ 0xffffffff; + line[x] = val; + break; + default: + return ERROR_INT("depth must be in {1,2,4,8,16,32} bpp", __func__, 1); + } + + return 0; +} + + +/*! + * \brief setPixelLow() + * + * \param[in] line ptr to beginning of line, + * \param[in] x pixel location in line + * \param[in] depth bpp + * \param[in] val to be inserted + * \return void + * + * <pre> + * Notes: + * (1) Caution: input variables are not checked! + * </pre> + */ +void +setPixelLow(l_uint32 *line, + l_int32 x, + l_int32 depth, + l_uint32 val) +{ + switch (depth) + { + case 1: + if (val) + SET_DATA_BIT(line, x); + else + CLEAR_DATA_BIT(line, x); + break; + case 2: + SET_DATA_DIBIT(line, x, val); + break; + case 4: + SET_DATA_QBIT(line, x, val); + break; + case 8: + SET_DATA_BYTE(line, x, val); + break; + case 16: + SET_DATA_TWO_BYTES(line, x, val); + break; + case 32: + line[x] = val; + break; + default: + lept_stderr("illegal depth in setPixelLow()\n"); + } +} + + +/*-------------------------------------------------------------* + * Find black or white value * + *-------------------------------------------------------------*/ +/*! + * \brief pixGetBlackOrWhiteVal() + * + * \param[in] pixs all depths; cmap ok + * \param[in] op L_GET_BLACK_VAL, L_GET_WHITE_VAL + * \param[out] pval pixel value + * \return 0 if OK; 1 on error + * + * <pre> + * Notes: + * (1) Side effect. For a colormapped image, if the requested + * color is not present and there is room to add it in the cmap, + * it is added and the new index is returned. If there is no room, + * the index of the closest color in intensity is returned. + * </pre> + */ +l_ok +pixGetBlackOrWhiteVal(PIX *pixs, + l_int32 op, + l_uint32 *pval) +{ +l_int32 d, index; +PIXCMAP *cmap; + + if (!pval) + return ERROR_INT("&val not defined", __func__, 1); + *pval = 0; + if (!pixs) + return ERROR_INT("pixs not defined", __func__, 1); + if (op != L_GET_BLACK_VAL && op != L_GET_WHITE_VAL) + return ERROR_INT("invalid op", __func__, 1); + + cmap = pixGetColormap(pixs); + d = pixGetDepth(pixs); + if (!cmap) { + if ((d == 1 && op == L_GET_WHITE_VAL) || + (d > 1 && op == L_GET_BLACK_VAL)) { /* min val */ + *pval = 0; + } else { /* max val */ + *pval = (d == 32) ? 0xffffff00 : (1 << d) - 1; + } + } else { /* handle colormap */ + if (op == L_GET_BLACK_VAL) + pixcmapAddBlackOrWhite(cmap, 0, &index); + else /* L_GET_WHITE_VAL */ + pixcmapAddBlackOrWhite(cmap, 1, &index); + *pval = index; + } + + return 0; +} + + +/*-------------------------------------------------------------* + * Full image clear/set/set-to-arbitrary-value/invert * + *-------------------------------------------------------------*/ +/*! + * \brief pixClearAll() + * + * \param[in] pix all depths; use cmapped with caution + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) Clears all data to 0. For 1 bpp, this is white; for grayscale + * or color, this is black. + * (2) Caution: for colormapped pix, this sets the color to the first + * one in the colormap. Be sure that this is the intended color! + * </pre> + */ +l_ok +pixClearAll(PIX *pix) +{ + if (!pix) + return ERROR_INT("pix not defined", __func__, 1); + + memset(pix->data, 0, 4LL * pix->wpl * pix->h); + return 0; +} + + +/*! + * \brief pixSetAll() + * + * \param[in] pix all depths; use cmapped with caution + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) Sets all data to 1. For 1 bpp, this is black; for grayscale + * or color, this is white. + * (2) Caution: for colormapped pix, this sets the pixel value to the + * maximum value supported by the colormap: 2^d - 1. However, this + * color may not be defined, because the colormap may not be full. + * </pre> + */ +l_ok +pixSetAll(PIX *pix) +{ +l_int32 n; +PIXCMAP *cmap; + + if (!pix) + return ERROR_INT("pix not defined", __func__, 1); + if ((cmap = pixGetColormap(pix)) != NULL) { + n = pixcmapGetCount(cmap); + if (n < cmap->nalloc) /* cmap is not full */ + return ERROR_INT("cmap entry does not exist", __func__, 1); + } + + memset(pix->data, 0xff, 4LL * pix->wpl * pix->h); + return 0; +} + + +/*! + * \brief pixSetAllGray() + * + * \param[in] pix all depths, cmap ok + * \param[in] grayval in range 0 ... 255 + * \return 0 if OK; 1 on error + * + * <pre> + * Notes: + * (1) N.B. For all images, %grayval == 0 represents black and + * %grayval == 255 represents white. + * (2) For depth < 8, we do our best to approximate the gray level. + * For 1 bpp images, any %grayval < 128 is black; >= 128 is white. + * For 32 bpp images, each r,g,b component is set to %grayval, + * and the alpha component is preserved. + * (3) If pix is colormapped, it adds the gray value, replicated in + * all components, to the colormap if it's not there and there + * is room. If the colormap is full, it finds the closest color in + * L2 distance of components. This index is written to all pixels. + * </pre> + */ +l_ok +pixSetAllGray(PIX *pix, + l_int32 grayval) +{ +l_int32 d, spp, index; +l_uint32 val32; +PIX *alpha; +PIXCMAP *cmap; + + if (!pix) + return ERROR_INT("pix not defined", __func__, 1); + if (grayval < 0) { + L_WARNING("grayval < 0; setting to 0\n", __func__); + grayval = 0; + } else if (grayval > 255) { + L_WARNING("grayval > 255; setting to 255\n", __func__); + grayval = 255; + } + + /* Handle the colormap case */ + cmap = pixGetColormap(pix); + if (cmap) { + pixcmapAddNearestColor(cmap, grayval, grayval, grayval, &index); + pixSetAllArbitrary(pix, index); + return 0; + } + + /* Non-cmapped */ + d = pixGetDepth(pix); + spp = pixGetSpp(pix); + if (d == 1) { + if (grayval < 128) /* black */ + pixSetAll(pix); + else + pixClearAll(pix); /* white */ + } else if (d < 8) { + grayval >>= 8 - d; + pixSetAllArbitrary(pix, grayval); + } else if (d == 8) { + pixSetAllArbitrary(pix, grayval); + } else if (d == 16) { + grayval |= (grayval << 8); + pixSetAllArbitrary(pix, grayval); + } else if (d == 32 && spp == 3) { + composeRGBPixel(grayval, grayval, grayval, &val32); + pixSetAllArbitrary(pix, val32); + } else if (d == 32 && spp == 4) { + alpha = pixGetRGBComponent(pix, L_ALPHA_CHANNEL); + composeRGBPixel(grayval, grayval, grayval, &val32); + pixSetAllArbitrary(pix, val32); + pixSetRGBComponent(pix, alpha, L_ALPHA_CHANNEL); + pixDestroy(&alpha); + } else { + L_ERROR("invalid depth: %d\n", __func__, d); + return 1; + } + + return 0; +} + + +/*! + * \brief pixSetAllArbitrary() + * + * \param[in] pix all depths; use cmapped with caution + * \param[in] val value to set all pixels + * \return 0 if OK; 1 on error + * + * <pre> + * Notes: + * (1) Caution 1! For colormapped pix, %val is used as an index + * into a colormap. Be sure that index refers to the intended color. + * If the color is not in the colormap, you should first add it + * and then call this function. + * (2) Caution 2! For 32 bpp pix, the interpretation of the LSB + * of %val depends on whether spp == 3 (RGB) or spp == 4 (RGBA). + * For RGB, the LSB is ignored in image transformations. + * For RGBA, the LSB is interpreted as the alpha (transparency) + * component; full transparency has alpha == 0x0, whereas + * full opacity has alpha = 0xff. An RGBA image with full + * opacity behaves like an RGB image. + * (3) As an example of (2), suppose you want to initialize a 32 bpp + * pix with partial opacity, say 0xee337788. If the pix is 3 spp, + * the 0x88 alpha component will be ignored and may be changed + * in subsequent processing. However, if the pix is 4 spp, the + * alpha component will be retained and used. The function + * pixCreate(w, h, 32) makes an RGB image by default, and + * pixSetSpp(pix, 4) can be used to promote an RGB image to RGBA. + * </pre> + */ +l_ok +pixSetAllArbitrary(PIX *pix, + l_uint32 val) +{ +l_int32 n, i, j, w, h, d, wpl, npix; +l_uint32 maxval, wordval; +l_uint32 *data, *line; +PIXCMAP *cmap; + + if (!pix) + return ERROR_INT("pix not defined", __func__, 1); + + /* If colormapped, make sure that val is less than the size + * of the cmap array. */ + if ((cmap = pixGetColormap(pix)) != NULL) { + n = pixcmapGetCount(cmap); + if (val >= n) { + L_WARNING("index not in colormap; using last color\n", __func__); + val = n - 1; + } + } + + /* Make sure val isn't too large for the pixel depth. + * If it is too large, set the pixel color to white. */ + pixGetDimensions(pix, &w, &h, &d); + if (d < 32) { + maxval = (1 << d) - 1; + if (val > maxval) { + L_WARNING("val = %d too large for depth; using maxval = %d\n", + __func__, val, maxval); + val = maxval; + } + } + + /* Set up word to tile with */ + wordval = 0; + npix = 32 / d; /* number of pixels per 32 bit word */ + for (j = 0; j < npix; j++) + wordval |= (val << (j * d)); + wpl = pixGetWpl(pix); + data = pixGetData(pix); + for (i = 0; i < h; i++) { + line = data + i * wpl; + for (j = 0; j < wpl; j++) { + *(line + j) = wordval; + } + } + return 0; +} + + +/*! + * \brief pixSetBlackOrWhite() + * + * \param[in] pixs all depths; cmap ok + * \param[in] op L_SET_BLACK, L_SET_WHITE + * \return 0 if OK; 1 on error + * + * <pre> + * Notes: + * (1) Function for setting all pixels in an image to either black + * or white. + * (2) If pixs is colormapped, it adds black or white to the + * colormap if it's not there and there is room. If the colormap + * is full, it finds the closest color in intensity. + * This index is written to all pixels. + * </pre> + */ +l_ok +pixSetBlackOrWhite(PIX *pixs, + l_int32 op) +{ +l_int32 d, index; +PIXCMAP *cmap; + + if (!pixs) + return ERROR_INT("pix not defined", __func__, 1); + if (op != L_SET_BLACK && op != L_SET_WHITE) + return ERROR_INT("invalid op", __func__, 1); + + cmap = pixGetColormap(pixs); + d = pixGetDepth(pixs); + if (!cmap) { + if ((d == 1 && op == L_SET_BLACK) || (d > 1 && op == L_SET_WHITE)) + pixSetAll(pixs); + else + pixClearAll(pixs); + } else { /* handle colormap */ + if (op == L_SET_BLACK) + pixcmapAddBlackOrWhite(cmap, 0, &index); + else /* L_SET_WHITE */ + pixcmapAddBlackOrWhite(cmap, 1, &index); + pixSetAllArbitrary(pixs, index); + } + + return 0; +} + + +/*! + * \brief pixSetComponentArbitrary() + * + * \param[in] pix 32 bpp + * \param[in] comp COLOR_RED, COLOR_GREEN, COLOR_BLUE, L_ALPHA_CHANNEL + * \param[in] val value to set this component + * \return 0 if OK; 1 on error + * + * <pre> + * Notes: + * (1) For example, this can be used to set the alpha component to opaque: + * pixSetComponentArbitrary(pix, L_ALPHA_CHANNEL, 255) + * </pre> + */ +l_ok +pixSetComponentArbitrary(PIX *pix, + l_int32 comp, + l_int32 val) +{ +l_int32 i, nwords; +l_uint32 mask1, mask2; +l_uint32 *data; + + if (!pix || pixGetDepth(pix) != 32) + return ERROR_INT("pix not defined or not 32 bpp", __func__, 1); + if (comp != COLOR_RED && comp != COLOR_GREEN && comp != COLOR_BLUE && + comp != L_ALPHA_CHANNEL) + return ERROR_INT("invalid component", __func__, 1); + if (val < 0 || val > 255) + return ERROR_INT("val not in [0 ... 255]", __func__, 1); + + mask1 = ~(255 << (8 * (3 - comp))); + mask2 = val << (8 * (3 - comp)); + nwords = pixGetHeight(pix) * pixGetWpl(pix); + data = pixGetData(pix); + for (i = 0; i < nwords; i++) { + data[i] &= mask1; /* clear out the component */ + data[i] |= mask2; /* insert the new component value */ + } + + return 0; +} + + +/*-------------------------------------------------------------* + * Rectangular region clear/set/set-to-arbitrary-value * + *-------------------------------------------------------------*/ +/*! + * \brief pixClearInRect() + * + * \param[in] pix all depths; can be cmapped + * \param[in] box in which all pixels will be cleared + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) Clears all data in rect to 0. For 1 bpp, this is white; + * for grayscale or color, this is black. + * (2) Caution: for colormapped pix, this sets the color to the first + * one in the colormap. Be sure that this is the intended color! + * </pre> + */ +l_ok +pixClearInRect(PIX *pix, + BOX *box) +{ +l_int32 x, y, w, h; + + if (!pix) + return ERROR_INT("pix not defined", __func__, 1); + if (!box) + return ERROR_INT("box not defined", __func__, 1); + + boxGetGeometry(box, &x, &y, &w, &h); + pixRasterop(pix, x, y, w, h, PIX_CLR, NULL, 0, 0); + return 0; +} + + +/*! + * \brief pixSetInRect() + * + * \param[in] pix all depths, can be cmapped + * \param[in] box in which all pixels will be set + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) Sets all data in rect to 1. For 1 bpp, this is black; + * for grayscale or color, this is white. + * (2) Caution: for colormapped pix, this sets the pixel value to the + * maximum value supported by the colormap: 2^d - 1. However, this + * color may not be defined, because the colormap may not be full. + * </pre> + */ +l_ok +pixSetInRect(PIX *pix, + BOX *box) +{ +l_int32 n, x, y, w, h; +PIXCMAP *cmap; + + if (!pix) + return ERROR_INT("pix not defined", __func__, 1); + if (!box) + return ERROR_INT("box not defined", __func__, 1); + if ((cmap = pixGetColormap(pix)) != NULL) { + n = pixcmapGetCount(cmap); + if (n < cmap->nalloc) /* cmap is not full */ + return ERROR_INT("cmap entry does not exist", __func__, 1); + } + + boxGetGeometry(box, &x, &y, &w, &h); + pixRasterop(pix, x, y, w, h, PIX_SET, NULL, 0, 0); + return 0; +} + + +/*! + * \brief pixSetInRectArbitrary() + * + * \param[in] pix all depths; can be cmapped + * \param[in] box in which all pixels will be set to val + * \param[in] val value to set all pixels + * \return 0 if OK; 1 on error + * + * <pre> + * Notes: + * (1) For colormapped pix, be sure the value is the intended + * one in the colormap. + * (2) Caution: for colormapped pix, this sets each pixel in the + * rect to the color at the index equal to val. Be sure that + * this index exists in the colormap and that it is the intended one! + * </pre> + */ +l_ok +pixSetInRectArbitrary(PIX *pix, + BOX *box, + l_uint32 val) +{ +l_int32 n, x, y, xstart, xend, ystart, yend, bw, bh, w, h, d, wpl; +l_uint32 maxval; +l_uint32 *data, *line; +BOX *boxc; +PIXCMAP *cmap; + + if (!pix) + return ERROR_INT("pix not defined", __func__, 1); + if (!box) + return ERROR_INT("box not defined", __func__, 1); + pixGetDimensions(pix, &w, &h, &d); + if (d != 1 && d != 2 && d != 4 && d !=8 && d != 16 && d != 32) + return ERROR_INT("depth must be in {1,2,4,8,16,32} bpp", __func__, 1); + if ((cmap = pixGetColormap(pix)) != NULL) { + n = pixcmapGetCount(cmap); + if (val >= n) { + L_WARNING("index not in colormap; using last color\n", __func__); + val = n - 1; + } + } + + maxval = (d == 32) ? 0xffffff00 : (1 << d) - 1; + if (val > maxval) val = maxval; + + /* Handle the simple cases: the min and max values */ + if (val == 0) { + pixClearInRect(pix, box); + return 0; + } + if (d == 1 || + (d == 2 && val == 3) || + (d == 4 && val == 0xf) || + (d == 8 && val == 0xff) || + (d == 16 && val == 0xffff) || + (d == 32 && ((val ^ 0xffffff00) >> 8 == 0))) { + pixSetInRect(pix, box); + return 0; + } + + /* Find the overlap of box with the input pix */ + if ((boxc = boxClipToRectangle(box, w, h)) == NULL) + return ERROR_INT("no overlap of box with image", __func__, 1); + boxGetGeometry(boxc, &xstart, &ystart, &bw, &bh); + xend = xstart + bw - 1; + yend = ystart + bh - 1; + boxDestroy(&boxc); + + wpl = pixGetWpl(pix); + data = pixGetData(pix); + for (y = ystart; y <= yend; y++) { + line = data + y * wpl; + for (x = xstart; x <= xend; x++) { + switch(d) + { + case 2: + SET_DATA_DIBIT(line, x, val); + break; + case 4: + SET_DATA_QBIT(line, x, val); + break; + case 8: + SET_DATA_BYTE(line, x, val); + break; + case 16: + SET_DATA_TWO_BYTES(line, x, val); + break; + case 32: + line[x] = val; + break; + default: + return ERROR_INT("depth not 2|4|8|16|32 bpp", __func__, 1); + } + } + } + + return 0; +} + + +/*! + * \brief pixBlendInRect() + * + * \param[in] pixs 32 bpp rgb + * \param[in] box [optional] in which all pixels will be blended + * \param[in] val blend value; 0xrrggbb00 + * \param[in] fract fraction of color to be blended with each pixel in pixs + * \return 0 if OK; 1 on error + * + * <pre> + * Notes: + * (1) This is an in-place function. It blends the input color %val + * with the pixels in pixs in the specified rectangle. + * If no rectangle is specified, it blends over the entire image. + * </pre> + */ +l_ok +pixBlendInRect(PIX *pixs, + BOX *box, + l_uint32 val, + l_float32 fract) +{ +l_int32 i, j, bx, by, bw, bh, w, h, wpls; +l_int32 prval, pgval, pbval, rval, gval, bval; +l_uint32 val32; +l_uint32 *datas, *lines; + + if (!pixs || pixGetDepth(pixs) != 32) + return ERROR_INT("pixs not defined or not 32 bpp", __func__, 1); + + extractRGBValues(val, &rval, &gval, &bval); + pixGetDimensions(pixs, &w, &h, NULL); + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + if (!box) { + for (i = 0; i < h; i++) { /* scan over box */ + lines = datas + i * wpls; + for (j = 0; j < w; j++) { + val32 = *(lines + j); + extractRGBValues(val32, &prval, &pgval, &pbval); + prval = (l_int32)((1. - fract) * prval + fract * rval); + pgval = (l_int32)((1. - fract) * pgval + fract * gval); + pbval = (l_int32)((1. - fract) * pbval + fract * bval); + composeRGBPixel(prval, pgval, pbval, &val32); + *(lines + j) = val32; + } + } + return 0; + } + + boxGetGeometry(box, &bx, &by, &bw, &bh); + for (i = 0; i < bh; i++) { /* scan over box */ + if (by + i < 0 || by + i >= h) continue; + lines = datas + (by + i) * wpls; + for (j = 0; j < bw; j++) { + if (bx + j < 0 || bx + j >= w) continue; + val32 = *(lines + bx + j); + extractRGBValues(val32, &prval, &pgval, &pbval); + prval = (l_int32)((1. - fract) * prval + fract * rval); + pgval = (l_int32)((1. - fract) * pgval + fract * gval); + pbval = (l_int32)((1. - fract) * pbval + fract * bval); + composeRGBPixel(prval, pgval, pbval, &val32); + *(lines + bx + j) = val32; + } + } + return 0; +} + + +/*-------------------------------------------------------------* + * Set pad bits * + *-------------------------------------------------------------*/ +/*! + * \brief pixSetPadBits() + * + * \param[in] pix 1, 2, 4, 8, 16, 32 bpp + * \param[in] val 0 or 1 + * \return 0 if OK; 1 on error + * + * <pre> + * Notes: + * (1) The pad bits are the bits that expand each scanline to a + * multiple of 32 bits. They are usually not used in + * image processing operations. When boundary conditions + * are important, as in seedfill, they must be set properly. + * (2) This sets the value of the pad bits (if any) in the last + * 32-bit word in each scanline. + * (3) For 32 bpp pix, there are no pad bits, so this is a no-op. + * (4) For 24 bpp pix (which are not generally supported in leptonica), + * this operation would affect image components because the pixels + * are not aligned with 32-bit word boundaries. + * (5) When writing formatted output, such as tiff, png or jpeg, + * the pad bits have no effect on the raster image that is + * generated by reading back from the file. However, in some + * cases, the compressed file itself will depend on the pad + * bits. This is seen, for example, in Windows with 2 and 4 bpp + * tiff-compressed images that have pad bits on each scanline. + * It is sometimes convenient to use a golden file with a + * byte-by-byte check to verify invariance. Consequently, + * and because setting the pad bits is cheap, the pad bits are + * set to 0 before writing these compressed files. + * </pre> + */ +l_ok +pixSetPadBits(PIX *pix, + l_int32 val) +{ +l_int32 i, w, h, d, wpl, endbits, fullwords; +l_uint32 mask; +l_uint32 *data, *pword; + + if (!pix) + return ERROR_INT("pix not defined", __func__, 1); + + pixGetDimensions(pix, &w, &h, &d); + if (d == 32) /* no padding exists for 32 bpp */ + return 0; + if (d == 24) { /* pixels not aligned with 32-bit words */ + L_INFO("pix is 24 bpp\n", __func__); + return 1; + } + + data = pixGetData(pix); + wpl = pixGetWpl(pix); + endbits = 32 - (((l_int64)w * d) % 32); + if (endbits == 32) /* no partial word */ + return 0; + fullwords = (1LL * w * d) / 32; + mask = rmask32[endbits]; + if (val == 0) + mask = ~mask; + + for (i = 0; i < h; i++) { + pword = data + i * wpl + fullwords; + if (val == 0) /* clear */ + *pword = *pword & mask; + else /* set */ + *pword = *pword | mask; + } + + return 0; +} + + +/*! + * \brief pixSetPadBitsBand() + * + * \param[in] pix 1, 2, 4, 8, 16, 32 bpp + * \param[in] by starting y value of band + * \param[in] bh height of band + * \param[in] val 0 or 1 + * \return 0 if OK; 1 on error + * + * <pre> + * Notes: + * (1) The pad bits are the bits that expand each scanline to a + * multiple of 32 bits. They are usually not used in + * image processing operations. When boundary conditions + * are important, as in seedfill, they must be set properly. + * (2) This sets the value of the pad bits (if any) in the last + * 32-bit word in each scanline, within the specified + * band of raster lines. + * (3) For 32 bpp pix, there are no pad bits, so this is a no-op. + * For 24 bpp pix, this function would change image components. + * </pre> + */ +l_ok +pixSetPadBitsBand(PIX *pix, + l_int32 by, + l_int32 bh, + l_int32 val) +{ +l_int32 i, w, h, d, wpl, endbits, fullwords; +l_uint32 mask; +l_uint32 *data, *pword; + + if (!pix) + return ERROR_INT("pix not defined", __func__, 1); + + pixGetDimensions(pix, &w, &h, &d); + if (d == 32) /* no padding exists for 32 bpp */ + return 0; + if (d == 24) { /* pixels not aligned with 32-bit words */ + L_INFO("pix is 24 bpp\n", __func__); + return 1; + } + + if (by < 0) + by = 0; + if (by >= h) + return ERROR_INT("start y not in image", __func__, 1); + if (by + bh > h) + bh = h - by; + + data = pixGetData(pix); + wpl = pixGetWpl(pix); + endbits = 32 - (((l_int64)w * d) % 32); + if (endbits == 32) /* no partial word */ + return 0; + fullwords = (l_int64)w * d / 32; + + mask = rmask32[endbits]; + if (val == 0) + mask = ~mask; + + for (i = by; i < by + bh; i++) { + pword = data + i * wpl + fullwords; + if (val == 0) /* clear */ + *pword = *pword & mask; + else /* set */ + *pword = *pword | mask; + } + + return 0; +} + + +/*-------------------------------------------------------------* + * Set border pixels * + *-------------------------------------------------------------*/ +/*! + * \brief pixSetOrClearBorder() + * + * \param[in] pixs all depths + * \param[in] left, right, top, bot border region amount to set or clear: these distances are from outside + * \param[in] op operation PIX_SET or PIX_CLR + * \return 0 if OK; 1 on error + * + * <pre> + * Notes: + * (1) The border region is defined to be the region in the + * image within a specific distance of each edge. Here, we + * allow the pixels within a specified distance of each + * edge to be set independently. This either sets or + * clears all pixels in the border region. + * (2) For binary images, use PIX_SET for black and PIX_CLR for white. + * (3) For grayscale or color images, use PIX_SET for white + * and PIX_CLR for black. + * </pre> + */ +l_ok +pixSetOrClearBorder(PIX *pixs, + l_int32 left, + l_int32 right, + l_int32 top, + l_int32 bot, + l_int32 op) +{ +l_int32 w, h; + + if (!pixs) + return ERROR_INT("pixs not defined", __func__, 1); + if (op != PIX_SET && op != PIX_CLR) + return ERROR_INT("op must be PIX_SET or PIX_CLR", __func__, 1); + + pixGetDimensions(pixs, &w, &h, NULL); + pixRasterop(pixs, 0, 0, left, h, op, NULL, 0, 0); + pixRasterop(pixs, w - right, 0, right, h, op, NULL, 0, 0); + pixRasterop(pixs, 0, 0, w, top, op, NULL, 0, 0); + pixRasterop(pixs, 0, h - bot, w, bot, op, NULL, 0, 0); + + return 0; +} + + +/*! + * \brief pixSetBorderVal() + * + * \param[in] pixs 8, 16 or 32 bpp + * \param[in] left, right, top, bot border region amount to set: these distances are from outside + * \param[in] val value to set at each border pixel + * \return 0 if OK; 1 on error + * + * <pre> + * Notes: + * (1) The border region is defined to be the region in the + * image within a specific distance of each edge. Here, we + * allow the pixels within a specified distance of each + * edge to be set independently. This sets the pixels + * in the border region to the given input value. + * (2) For efficiency, use pixSetOrClearBorder() if + * you're setting the border to either black or white. + * (3) If d != 32, the input value should be masked off + * to the appropriate number of least significant bits. + * (4) The code is easily generalized for 2 or 4 bpp. + * </pre> + */ +l_ok +pixSetBorderVal(PIX *pixs, + l_int32 left, + l_int32 right, + l_int32 top, + l_int32 bot, + l_uint32 val) +{ +l_int32 w, h, d, wpls, i, j, bstart, rstart; +l_uint32 *datas, *lines; + + if (!pixs) + return ERROR_INT("pixs not defined", __func__, 1); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 8 && d != 16 && d != 32) + return ERROR_INT("depth must be 8, 16 or 32 bpp", __func__, 1); + + datas = pixGetData(pixs); + wpls = pixGetWpl(pixs); + if (d == 8) { + val &= 0xff; + for (i = 0; i < top; i++) { + lines = datas + i * wpls; + for (j = 0; j < w; j++) + SET_DATA_BYTE(lines, j, val); + } + rstart = w - right; + bstart = h - bot; + for (i = top; i < bstart; i++) { + lines = datas + i * wpls; + for (j = 0; j < left; j++) + SET_DATA_BYTE(lines, j, val); + for (j = rstart; j < w; j++) + SET_DATA_BYTE(lines, j, val); + } + for (i = bstart; i < h; i++) { + lines = datas + i * wpls; + for (j = 0; j < w; j++) + SET_DATA_BYTE(lines, j, val); + } + } else if (d == 16) { + val &= 0xffff; + for (i = 0; i < top; i++) { + lines = datas + i * wpls; + for (j = 0; j < w; j++) + SET_DATA_TWO_BYTES(lines, j, val); + } + rstart = w - right; + bstart = h - bot; + for (i = top; i < bstart; i++) { + lines = datas + i * wpls; + for (j = 0; j < left; j++) + SET_DATA_TWO_BYTES(lines, j, val); + for (j = rstart; j < w; j++) + SET_DATA_TWO_BYTES(lines, j, val); + } + for (i = bstart; i < h; i++) { + lines = datas + i * wpls; + for (j = 0; j < w; j++) + SET_DATA_TWO_BYTES(lines, j, val); + } + } else { /* d == 32 */ + for (i = 0; i < top; i++) { + lines = datas + i * wpls; + for (j = 0; j < w; j++) + *(lines + j) = val; + } + rstart = w - right; + bstart = h - bot; + for (i = top; i < bstart; i++) { + lines = datas + i * wpls; + for (j = 0; j < left; j++) + *(lines + j) = val; + for (j = rstart; j < w; j++) + *(lines + j) = val; + } + for (i = bstart; i < h; i++) { + lines = datas + i * wpls; + for (j = 0; j < w; j++) + *(lines + j) = val; + } + } + + return 0; +} + + +/*! + * \brief pixSetBorderRingVal() + * + * \param[in] pixs any depth; cmap OK + * \param[in] dist distance from outside; must be > 0; first ring is 1 + * \param[in] val value to set at each border pixel + * \return 0 if OK; 1 on error + * + * <pre> + * Notes: + * (1) The rings are single-pixel-wide rectangular sets of + * pixels at a given distance from the edge of the pix. + * This sets all pixels in a given ring to a value. + * </pre> + */ +l_ok +pixSetBorderRingVal(PIX *pixs, + l_int32 dist, + l_uint32 val) +{ +l_int32 w, h, d, i, j, xend, yend; + + if (!pixs) + return ERROR_INT("pixs not defined", __func__, 1); + if (dist < 1) + return ERROR_INT("dist must be > 0", __func__, 1); + pixGetDimensions(pixs, &w, &h, &d); + if (w < 2 * dist + 1 || h < 2 * dist + 1) + return ERROR_INT("ring doesn't exist", __func__, 1); + if (d < 32 && (val >= (1 << d))) + return ERROR_INT("invalid pixel value", __func__, 1); + + xend = w - dist; + yend = h - dist; + for (j = dist - 1; j <= xend; j++) + pixSetPixel(pixs, j, dist - 1, val); + for (j = dist - 1; j <= xend; j++) + pixSetPixel(pixs, j, yend, val); + for (i = dist - 1; i <= yend; i++) + pixSetPixel(pixs, dist - 1, i, val); + for (i = dist - 1; i <= yend; i++) + pixSetPixel(pixs, xend, i, val); + + return 0; +} + + +/*! + * \brief pixSetMirroredBorder() + * + * \param[in] pixs all depths; colormap ok + * \param[in] left, right, top, bot number of pixels to set + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) This applies what is effectively mirror boundary conditions + * to a border region in the image. It is in-place. + * (2) This is useful for setting pixels near the border to a + * value representative of the near pixels to the interior. + * (3) The general pixRasterop() is used for an in-place operation here + * because there is no overlap between the src and dest rectangles. + * </pre> + */ +l_ok +pixSetMirroredBorder(PIX *pixs, + l_int32 left, + l_int32 right, + l_int32 top, + l_int32 bot) +{ +l_int32 i, j, w, h; + + if (!pixs) + return ERROR_INT("pixs not defined", __func__, 1); + + pixGetDimensions(pixs, &w, &h, NULL); + for (j = 0; j < left; j++) + pixRasterop(pixs, left - 1 - j, top, 1, h - top - bot, PIX_SRC, + pixs, left + j, top); + for (j = 0; j < right; j++) + pixRasterop(pixs, w - right + j, top, 1, h - top - bot, PIX_SRC, + pixs, w - right - 1 - j, top); + for (i = 0; i < top; i++) + pixRasterop(pixs, 0, top - 1 - i, w, 1, PIX_SRC, + pixs, 0, top + i); + for (i = 0; i < bot; i++) + pixRasterop(pixs, 0, h - bot + i, w, 1, PIX_SRC, + pixs, 0, h - bot - 1 - i); + + return 0; +} + + +/*! + * \brief pixCopyBorder() + * + * \param[in] pixd all depths; colormap ok; can be NULL + * \param[in] pixs same depth and size as pixd + * \param[in] left, right, top, bot number of pixels to copy + * \return pixd, or NULL on error if pixd is not defined + * + * <pre> + * Notes: + * (1) pixd can be null, but otherwise it must be the same size + * and depth as pixs. Always returns pixd. + * (2) This is useful in situations where by setting a few border + * pixels we can avoid having to copy all pixels in pixs into + * pixd as an initialization step for some operation. + * Nevertheless, for safety, if making a new pixd, all the + * non-border pixels are initialized to 0. + * </pre> + */ +PIX * +pixCopyBorder(PIX *pixd, + PIX *pixs, + l_int32 left, + l_int32 right, + l_int32 top, + l_int32 bot) +{ +l_int32 w, h; + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); + + if (pixd) { + if (pixd == pixs) { + L_WARNING("same: nothing to do\n", __func__); + return pixd; + } else if (!pixSizesEqual(pixs, pixd)) { + return (PIX *)ERROR_PTR("pixs and pixd sizes differ", + __func__, pixd); + } + } else { + if ((pixd = pixCreateTemplate(pixs)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", __func__, pixd); + } + + pixGetDimensions(pixs, &w, &h, NULL); + pixRasterop(pixd, 0, 0, left, h, PIX_SRC, pixs, 0, 0); + pixRasterop(pixd, w - right, 0, right, h, PIX_SRC, pixs, w - right, 0); + pixRasterop(pixd, 0, 0, w, top, PIX_SRC, pixs, 0, 0); + pixRasterop(pixd, 0, h - bot, w, bot, PIX_SRC, pixs, 0, h - bot); + return pixd; +} + + + +/*-------------------------------------------------------------* + * Add and remove border * + *-------------------------------------------------------------*/ +/*! + * \brief pixAddBorder() + * + * \param[in] pixs all depths; colormap ok + * \param[in] npix number of pixels to be added to each side + * \param[in] val value of added border pixels + * \return pixd with the added exterior pixels, or NULL on error + * + * <pre> + * Notes: + * (1) See pixGetBlackOrWhiteVal() for values of black and white pixels. + * </pre> + */ +PIX * +pixAddBorder(PIX *pixs, + l_int32 npix, + l_uint32 val) +{ + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); + if (npix == 0) + return pixClone(pixs); + return pixAddBorderGeneral(pixs, npix, npix, npix, npix, val); +} + + +/*! + * \brief pixAddBlackOrWhiteBorder() + * + * \param[in] pixs all depths; colormap ok + * \param[in] left, right, top, bot number of pixels added + * \param[in] op L_GET_BLACK_VAL, L_GET_WHITE_VAL + * \return pixd with the added exterior pixels, or NULL on error + * + * <pre> + * Notes: + * (1) See pixGetBlackOrWhiteVal() for possible side effect (adding + * a color to a colormap). + * (2) The only complication is that pixs may have a colormap. + * There are two ways to add the black or white border: + * (a) As done here (simplest, most efficient) + * (b) l_int32 ws, hs, d; + * pixGetDimensions(pixs, &ws, &hs, &d); + * Pix *pixd = pixCreate(ws + left + right, hs + top + bot, d); + * PixColormap *cmap = pixGetColormap(pixs); + * if (cmap != NULL) + * pixSetColormap(pixd, pixcmapCopy(cmap)); + * pixSetBlackOrWhite(pixd, L_SET_WHITE); // uses cmap + * pixRasterop(pixd, left, top, ws, hs, PIX_SET, pixs, 0, 0); + * </pre> + */ +PIX * +pixAddBlackOrWhiteBorder(PIX *pixs, + l_int32 left, + l_int32 right, + l_int32 top, + l_int32 bot, + l_int32 op) +{ +l_uint32 val; + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); + if (op != L_GET_BLACK_VAL && op != L_GET_WHITE_VAL) + return (PIX *)ERROR_PTR("invalid op", __func__, NULL); + + pixGetBlackOrWhiteVal(pixs, op, &val); + return pixAddBorderGeneral(pixs, left, right, top, bot, val); +} + + +/*! + * \brief pixAddBorderGeneral() + * + * \param[in] pixs all depths; colormap ok + * \param[in] left, right, top, bot number of pixels added + * \param[in] val value of added border pixels + * \return pixd with the added exterior pixels, or NULL on error + * + * <pre> + * Notes: + * (1) For binary images: + * white: val = 0 + * black: val = 1 + * For grayscale images: + * white: val = 2 ** d - 1 + * black: val = 0 + * For rgb color images: + * white: val = 0xffffff00 + * black: val = 0 + * For colormapped images, set val to the appropriate colormap index. + * (2) If the added border is either black or white, you can use + * pixAddBlackOrWhiteBorder() + * The black and white values for all images can be found with + * pixGetBlackOrWhiteVal() + * which, if pixs is cmapped, may add an entry to the colormap. + * Alternatively, if pixs has a colormap, you can find the index + * of the pixel whose intensity is closest to white or black: + * white: pixcmapGetRankIntensity(cmap, 1.0, &index); + * black: pixcmapGetRankIntensity(cmap, 0.0, &index); + * and use that for val. + * </pre> + */ +PIX * +pixAddBorderGeneral(PIX *pixs, + l_int32 left, + l_int32 right, + l_int32 top, + l_int32 bot, + l_uint32 val) +{ +l_int32 ws, hs, wd, hd, d, op; +l_uint32 maxval; +PIX *pixd; + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); + if (left < 0 || right < 0 || top < 0 || bot < 0) + return (PIX *)ERROR_PTR("negative border added!", __func__, NULL); + + pixGetDimensions(pixs, &ws, &hs, &d); + wd = ws + left + right; + hd = hs + top + bot; + if ((pixd = pixCreate(wd, hd, d)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); + pixCopyResolution(pixd, pixs); + pixCopyColormap(pixd, pixs); + + /* Set the new border pixels */ + maxval = (d == 32) ? 0xffffff00 : (1 << d) - 1; + op = UNDEF; + if (val == 0) + op = PIX_CLR; + else if (val >= maxval) + op = PIX_SET; + if (op == UNDEF) { + pixSetAllArbitrary(pixd, val); + } else { /* just set or clear the border pixels */ + pixRasterop(pixd, 0, 0, left, hd, op, NULL, 0, 0); + pixRasterop(pixd, wd - right, 0, right, hd, op, NULL, 0, 0); + pixRasterop(pixd, 0, 0, wd, top, op, NULL, 0, 0); + pixRasterop(pixd, 0, hd - bot, wd, bot, op, NULL, 0, 0); + } + + /* Copy pixs into the interior */ + pixRasterop(pixd, left, top, ws, hs, PIX_SRC, pixs, 0, 0); + return pixd; +} + + +/*! + * \brief pixAddMultipleBlackWhiteBorders() + * + * \param[in] pixs all depths; colormap ok + * \param[in] nblack1 width of first black border + * \param[in] nwhite1 width of first white border + * \param[in] nblack2 width of second black border + * \param[in] nwhite2 width of second white border + * \param[in] nblack3 width of third black border + * \param[in] nwhite3 width of third white border + * \return pixd with the added borders, or NULL on error + * + * <pre> + * Notes: + * (1) This is a convenience function for adding up to 3 black and + * 3 white borders, alternating black and white. + * (2) Each of the 6 args gives the width of the next border, starting + * with a black border. Any of the args can be 0, skipping + * the addition of that border. + * (3) Maximum allowed border width is 500 for any border. + * </pre> + */ +PIX * +pixAddMultipleBlackWhiteBorders(PIX *pixs, + l_int32 nblack1, + l_int32 nwhite1, + l_int32 nblack2, + l_int32 nwhite2, + l_int32 nblack3, + l_int32 nwhite3) +{ +l_int32 i, color; +l_int32 w[6]; +PIX *pix1, *pixd; + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); + + w[0] = nblack1; + w[1] = nwhite1; + w[2] = nblack2; + w[3] = nwhite2; + w[4] = nblack3; + w[5] = nwhite3; + pixd = pixClone(pixs); + for (i = 0; i < 6; i++) { + if (w[i] > 500) + L_WARNING("w = %d > 500; skipping\n", __func__, w[i]); + if (w[i] > 0 && w[i] <= 500) { + color = (i % 2 == 0) ? L_GET_BLACK_VAL : L_GET_WHITE_VAL; + pix1 = pixAddBlackOrWhiteBorder(pixd, w[i], w[i], w[i], w[i], + color); + pixDestroy(&pixd); + pixd = pix1; + } + } + + return pixd; +} + + +/*! + * \brief pixRemoveBorder() + * + * \param[in] pixs all depths; colormap ok + * \param[in] npix number to be removed from each of the 4 sides + * \return pixd with pixels removed around border, or NULL on error + */ +PIX * +pixRemoveBorder(PIX *pixs, + l_int32 npix) +{ + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); + if (npix == 0) + return pixClone(pixs); + return pixRemoveBorderGeneral(pixs, npix, npix, npix, npix); +} + + +/*! + * \brief pixRemoveBorderGeneral() + * + * \param[in] pixs all depths; colormap ok + * \param[in] left, right, top, bot number of pixels removed + * \return pixd with pixels removed around border, or NULL on error + */ +PIX * +pixRemoveBorderGeneral(PIX *pixs, + l_int32 left, + l_int32 right, + l_int32 top, + l_int32 bot) +{ +l_int32 ws, hs, wd, hd, d; +PIX *pixd; + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); + if (left < 0 || right < 0 || top < 0 || bot < 0) + return (PIX *)ERROR_PTR("negative border removed!", __func__, NULL); + + pixGetDimensions(pixs, &ws, &hs, &d); + wd = ws - left - right; + hd = hs - top - bot; + if (wd <= 0) + return (PIX *)ERROR_PTR("width must be > 0", __func__, NULL); + if (hd <= 0) + return (PIX *)ERROR_PTR("height must be > 0", __func__, NULL); + if ((pixd = pixCreate(wd, hd, d)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); + pixCopyResolution(pixd, pixs); + pixCopySpp(pixd, pixs); + pixCopyColormap(pixd, pixs); + + pixRasterop(pixd, 0, 0, wd, hd, PIX_SRC, pixs, left, top); + if (pixGetDepth(pixs) == 32 && pixGetSpp(pixs) == 4) + pixShiftAndTransferAlpha(pixd, pixs, -left, -top); + return pixd; +} + + +/*! + * \brief pixRemoveBorderToSize() + * + * \param[in] pixs all depths; colormap ok + * \param[in] wd target width; use 0 if only removing from height + * \param[in] hd target height; use 0 if only removing from width + * \return pixd with pixels removed around border, or NULL on error + * + * <pre> + * Notes: + * (1) Removes pixels as evenly as possible from the sides of the + * image, leaving the central part. + * (2) Returns clone if no pixels requested removed, or the target + * sizes are larger than the image. + * </pre> + */ +PIX * +pixRemoveBorderToSize(PIX *pixs, + l_int32 wd, + l_int32 hd) +{ +l_int32 w, h, top, bot, left, right, delta; + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); + + pixGetDimensions(pixs, &w, &h, NULL); + if ((wd <= 0 || wd >= w) && (hd <= 0 || hd >= h)) + return pixClone(pixs); + + left = right = (w - wd) / 2; + delta = w - 2 * left - wd; + right += delta; + top = bot = (h - hd) / 2; + delta = h - hd - 2 * top; + bot += delta; + if (wd <= 0 || wd > w) + left = right = 0; + else if (hd <= 0 || hd > h) + top = bot = 0; + + return pixRemoveBorderGeneral(pixs, left, right, top, bot); +} + + +/*! + * \brief pixAddMirroredBorder() + * + * \param[in] pixs all depths; colormap ok + * \param[in] left, right, top, bot number of pixels added + * \return pixd, or NULL on error + * + * <pre> + * Notes: + * (1) This applies what is effectively mirror boundary conditions. + * For the added border pixels in pixd, the pixels in pixs + * near the border are mirror-copied into the border region. + * (2) This is useful for avoiding special operations near + * boundaries when doing image processing operations + * such as rank filters and convolution. In use, one first + * adds mirrored pixels to each side of the image. The number + * of pixels added on each side is half the filter dimension. + * Then the image processing operations proceed over a + * region equal to the size of the original image, and + * write directly into a dest pix of the same size as pixs. + * (3) The general pixRasterop() is used for an in-place operation here + * because there is no overlap between the src and dest rectangles. + * </pre> + */ +PIX * +pixAddMirroredBorder(PIX *pixs, + l_int32 left, + l_int32 right, + l_int32 top, + l_int32 bot) +{ +l_int32 i, j, w, h; +PIX *pixd; + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); + pixGetDimensions(pixs, &w, &h, NULL); + if (left > w || right > w || top > h || bot > h) + return (PIX *)ERROR_PTR("border too large", __func__, NULL); + + /* Set pixels on left, right, top and bottom, in that order */ + pixd = pixAddBorderGeneral(pixs, left, right, top, bot, 0); + for (j = 0; j < left; j++) + pixRasterop(pixd, left - 1 - j, top, 1, h, PIX_SRC, + pixd, left + j, top); + for (j = 0; j < right; j++) + pixRasterop(pixd, left + w + j, top, 1, h, PIX_SRC, + pixd, left + w - 1 - j, top); + for (i = 0; i < top; i++) + pixRasterop(pixd, 0, top - 1 - i, left + w + right, 1, PIX_SRC, + pixd, 0, top + i); + for (i = 0; i < bot; i++) + pixRasterop(pixd, 0, top + h + i, left + w + right, 1, PIX_SRC, + pixd, 0, top + h - 1 - i); + + return pixd; +} + + +/*! + * \brief pixAddRepeatedBorder() + * + * \param[in] pixs all depths; colormap ok + * \param[in] left, right, top, bot number of pixels added + * \return pixd, or NULL on error + * + * <pre> + * Notes: + * (1) This applies a repeated border, as if the central part of + * the image is tiled over the plane. So, for example, the + * pixels in the left border come from the right side of the image. + * (2) The general pixRasterop() is used for an in-place operation here + * because there is no overlap between the src and dest rectangles. + * </pre> + */ +PIX * +pixAddRepeatedBorder(PIX *pixs, + l_int32 left, + l_int32 right, + l_int32 top, + l_int32 bot) +{ +l_int32 w, h; +PIX *pixd; + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); + pixGetDimensions(pixs, &w, &h, NULL); + if (left > w || right > w || top > h || bot > h) + return (PIX *)ERROR_PTR("border too large", __func__, NULL); + + pixd = pixAddBorderGeneral(pixs, left, right, top, bot, 0); + + /* Set pixels on left, right, top and bottom, in that order */ + pixRasterop(pixd, 0, top, left, h, PIX_SRC, pixd, w, top); + pixRasterop(pixd, left + w, top, right, h, PIX_SRC, pixd, left, top); + pixRasterop(pixd, 0, 0, left + w + right, top, PIX_SRC, pixd, 0, h); + pixRasterop(pixd, 0, top + h, left + w + right, bot, PIX_SRC, pixd, 0, top); + + return pixd; +} + + +/*! + * \brief pixAddMixedBorder() + * + * \param[in] pixs all depths; colormap ok + * \param[in] left, right, top, bot number of pixels added + * \return pixd, or NULL on error + * + * <pre> + * Notes: + * (1) This applies mirrored boundary conditions (b.c.) horizontally + * and repeated b.c. vertically. + * (2) It is specifically used for avoiding special operations + * near boundaries when convolving a hue-saturation histogram + * with a given window size. The repeated b.c. are used + * vertically for hue, and the mirrored b.c. are used + * horizontally for saturation. The number of pixels added + * on each side is approximately (but not quite) half the + * filter dimension. The image processing operations can + * then proceed over a region equal to the size of the original + * image, and write directly into a dest pix of the same + * size as pixs. + * (3) The general pixRasterop() can be used for an in-place + * operation here because there is no overlap between the + * src and dest rectangles. + * </pre> + */ +PIX * +pixAddMixedBorder(PIX *pixs, + l_int32 left, + l_int32 right, + l_int32 top, + l_int32 bot) +{ +l_int32 j, w, h; +PIX *pixd; + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); + pixGetDimensions(pixs, &w, &h, NULL); + if (left > w || right > w || top > h || bot > h) + return (PIX *)ERROR_PTR("border too large", __func__, NULL); + + /* Set mirrored pixels on left and right; + * then set repeated pixels on top and bottom. */ + pixd = pixAddBorderGeneral(pixs, left, right, top, bot, 0); + for (j = 0; j < left; j++) + pixRasterop(pixd, left - 1 - j, top, 1, h, PIX_SRC, + pixd, left + j, top); + for (j = 0; j < right; j++) + pixRasterop(pixd, left + w + j, top, 1, h, PIX_SRC, + pixd, left + w - 1 - j, top); + pixRasterop(pixd, 0, 0, left + w + right, top, PIX_SRC, pixd, 0, h); + pixRasterop(pixd, 0, top + h, left + w + right, bot, PIX_SRC, pixd, 0, top); + + return pixd; +} + + +/*! + * \brief pixAddContinuedBorder() + * + * \param[in] pixs all depths; colormap ok + * \param[in] left, right, top, bot pixels on each side to be added + * \return pixd, or NULL on error + * + * <pre> + * Notes: + * (1) This adds pixels on each side whose values are equal to + * the value on the closest boundary pixel. + * </pre> + */ +PIX * +pixAddContinuedBorder(PIX *pixs, + l_int32 left, + l_int32 right, + l_int32 top, + l_int32 bot) +{ +l_int32 i, j, w, h; +PIX *pixd; + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); + + pixd = pixAddBorderGeneral(pixs, left, right, top, bot, 0); + pixGetDimensions(pixs, &w, &h, NULL); + for (j = 0; j < left; j++) + pixRasterop(pixd, j, top, 1, h, PIX_SRC, pixd, left, top); + for (j = 0; j < right; j++) + pixRasterop(pixd, left + w + j, top, 1, h, + PIX_SRC, pixd, left + w - 1, top); + for (i = 0; i < top; i++) + pixRasterop(pixd, 0, i, left + w + right, 1, PIX_SRC, pixd, 0, top); + for (i = 0; i < bot; i++) + pixRasterop(pixd, 0, top + h + i, left + w + right, 1, + PIX_SRC, pixd, 0, top + h - 1); + + return pixd; +} + + +/*-------------------------------------------------------------------* + * Helper functions using alpha * + *-------------------------------------------------------------------*/ +/*! + * \brief pixShiftAndTransferAlpha() + * + * \param[in] pixd 32 bpp + * \param[in] pixs 32 bpp + * \param[in] shiftx, shifty + * \return 0 if OK; 1 on error + */ +l_ok +pixShiftAndTransferAlpha(PIX *pixd, + PIX *pixs, + l_float32 shiftx, + l_float32 shifty) +{ +l_int32 w, h; +PIX *pix1, *pix2; + + if (!pixs || !pixd) + return ERROR_INT("pixs and pixd not both defined", __func__, 1); + if (pixGetDepth(pixs) != 32 || pixGetSpp(pixs) != 4) + return ERROR_INT("pixs not 32 bpp and 4 spp", __func__, 1); + if (pixGetDepth(pixd) != 32) + return ERROR_INT("pixd not 32 bpp", __func__, 1); + + if (shiftx == 0 && shifty == 0) { + pixCopyRGBComponent(pixd, pixs, L_ALPHA_CHANNEL); + return 0; + } + + pix1 = pixGetRGBComponent(pixs, L_ALPHA_CHANNEL); + pixGetDimensions(pixd, &w, &h, NULL); + pix2 = pixCreate(w, h, 8); + pixRasterop(pix2, 0, 0, w, h, PIX_SRC, pix1, -shiftx, -shifty); + pixSetRGBComponent(pixd, pix2, L_ALPHA_CHANNEL); + pixDestroy(&pix1); + pixDestroy(&pix2); + return 0; +} + + +/*! + * \brief pixDisplayLayersRGBA() + * + * \param[in] pixs cmap or 32 bpp rgba + * \param[in] val 32 bit unsigned color to use as background + * \param[in] maxw max output image width; 0 for no scaling + * \return pixd showing various image views, or NULL on error + * + * <pre> + * Notes: + * (1) Use %val == 0xffffff00 for white background. + * (2) Three views are given: + * ~ the image with a fully opaque alpha + * ~ the alpha layer + * ~ the image as it would appear with a white background. + * </pre> + */ +PIX * +pixDisplayLayersRGBA(PIX *pixs, + l_uint32 val, + l_int32 maxw) +{ +l_int32 w, width; +l_float32 scalefact; +PIX *pix1, *pix2, *pixd; +PIXA *pixa; +PIXCMAP *cmap; + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); + cmap = pixGetColormap(pixs); + if (!cmap && !(pixGetDepth(pixs) == 32 && pixGetSpp(pixs) == 4)) + return (PIX *)ERROR_PTR("pixs not cmap and not 32 bpp rgba", + __func__, NULL); + if ((w = pixGetWidth(pixs)) == 0) + return (PIX *)ERROR_PTR("pixs width 0 !!", __func__, NULL); + + if (cmap) + pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_WITH_ALPHA); + else + pix1 = pixCopy(NULL, pixs); + + /* Scale if necessary so the output width is not larger than maxw */ + scalefact = (maxw == 0) ? 1.0f : L_MIN(1.0f, (l_float32)(maxw) / w); + width = (l_int32)(scalefact * w); + + pixa = pixaCreate(3); + pixSetSpp(pix1, 3); + pixaAddPix(pixa, pix1, L_INSERT); /* show the rgb values */ + pix1 = pixGetRGBComponent(pixs, L_ALPHA_CHANNEL); + pix2 = pixConvertTo32(pix1); + pixaAddPix(pixa, pix2, L_INSERT); /* show the alpha channel */ + pixDestroy(&pix1); + pix1 = pixAlphaBlendUniform(pixs, (val & 0xffffff00)); + pixaAddPix(pixa, pix1, L_INSERT); /* with %val color bg showing */ + pixd = pixaDisplayTiledInRows(pixa, 32, width, scalefact, 0, 25, 2); + pixaDestroy(&pixa); + return pixd; +} + + +/*-------------------------------------------------------------* + * Color sample setting and extraction * + *-------------------------------------------------------------*/ +/*! + * \brief pixCreateRGBImage() + * + * \param[in] pixr 8 bpp red pix + * \param[in] pixg 8 bpp green pix + * \param[in] pixb 8 bpp blue pix + * \return 32 bpp pix, interleaved with 4 samples/pixel, + * or NULL on error + * + * <pre> + * Notes: + * (1) the 4th byte, sometimes called the "alpha channel", + * and which is often used for blending between different + * images, is left with 0 value (fully opaque). + * (2) see Note (4) in pix.h for details on storage of + * 8-bit samples within each 32-bit word. + * (3) This implementation, setting the r, g and b components + * sequentially, is much faster than setting them in parallel + * by constructing an RGB dest pixel and writing it to dest. + * The reason is there are many more cache misses when reading + * from 3 input images simultaneously. + * </pre> + */ +PIX * +pixCreateRGBImage(PIX *pixr, + PIX *pixg, + PIX *pixb) +{ +l_int32 wr, wg, wb, hr, hg, hb, dr, dg, db; +PIX *pixd; + + if (!pixr) + return (PIX *)ERROR_PTR("pixr not defined", __func__, NULL); + if (!pixg) + return (PIX *)ERROR_PTR("pixg not defined", __func__, NULL); + if (!pixb) + return (PIX *)ERROR_PTR("pixb not defined", __func__, NULL); + pixGetDimensions(pixr, &wr, &hr, &dr); + pixGetDimensions(pixg, &wg, &hg, &dg); + pixGetDimensions(pixb, &wb, &hb, &db); + if (dr != 8 || dg != 8 || db != 8) + return (PIX *)ERROR_PTR("input pix not all 8 bpp", __func__, NULL); + if (wr != wg || wr != wb) + return (PIX *)ERROR_PTR("widths not the same", __func__, NULL); + if (hr != hg || hr != hb) + return (PIX *)ERROR_PTR("heights not the same", __func__, NULL); + + if ((pixd = pixCreate(wr, hr, 32)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); + pixCopyResolution(pixd, pixr); + pixSetRGBComponent(pixd, pixr, COLOR_RED); + pixSetRGBComponent(pixd, pixg, COLOR_GREEN); + pixSetRGBComponent(pixd, pixb, COLOR_BLUE); + + return pixd; +} + + +/*! + * \brief pixGetRGBComponent() + * + * \param[in] pixs 32 bpp, or colormapped + * \param[in] comp one of {COLOR_RED, COLOR_GREEN, COLOR_BLUE, + * L_ALPHA_CHANNEL} + * \return pixd the selected 8 bpp component image of the + * input 32 bpp image or NULL on error + * + * <pre> + * Notes: + * (1) Three calls to this function generate the r, g and b 8 bpp + * component images. This is much faster than generating the + * three images in parallel, by extracting a src pixel and setting + * the pixels of each component image from it. The reason is + * there are many more cache misses when writing to three + * output images simultaneously. + * </pre> + */ +PIX * +pixGetRGBComponent(PIX *pixs, + l_int32 comp) +{ +l_int32 i, j, w, h, wpls, wpld, val; +l_uint32 *lines, *lined; +l_uint32 *datas, *datad; +PIX *pixd; + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); + if (pixGetColormap(pixs)) + return pixGetRGBComponentCmap(pixs, comp); + if (pixGetDepth(pixs) != 32) + return (PIX *)ERROR_PTR("pixs not 32 bpp", __func__, NULL); + if (comp != COLOR_RED && comp != COLOR_GREEN && + comp != COLOR_BLUE && comp != L_ALPHA_CHANNEL) + return (PIX *)ERROR_PTR("invalid comp", __func__, NULL); + + pixGetDimensions(pixs, &w, &h, NULL); + if ((pixd = pixCreate(w, h, 8)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); + pixCopyResolution(pixd, pixs); + wpls = pixGetWpl(pixs); + wpld = pixGetWpl(pixd); + datas = pixGetData(pixs); + datad = pixGetData(pixd); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + val = GET_DATA_BYTE(lines + j, comp); + SET_DATA_BYTE(lined, j, val); + } + } + + return pixd; +} + + +/*! + * \brief pixSetRGBComponent() + * + * \param[in] pixd 32 bpp + * \param[in] pixs 8 bpp + * \param[in] comp one of the set: {COLOR_RED, COLOR_GREEN, + * COLOR_BLUE, L_ALPHA_CHANNEL} + * \return 0 if OK; 1 on error + * + * <pre> + * Notes: + * (1) This places the 8 bpp pixel in pixs into the + * specified component (properly interleaved) in pixd, + * (2) The two images are registered to the UL corner; the sizes + * need not be the same, but a warning is issued if they differ. + * </pre> + */ +l_ok +pixSetRGBComponent(PIX *pixd, + PIX *pixs, + l_int32 comp) +{ +l_uint8 srcbyte; +l_int32 i, j, w, h, ws, hs, wd, hd; +l_int32 wpls, wpld; +l_uint32 *lines, *lined; +l_uint32 *datas, *datad; + + if (!pixd) + return ERROR_INT("pixd not defined", __func__, 1); + if (!pixs) + return ERROR_INT("pixs not defined", __func__, 1); + if (pixGetDepth(pixd) != 32) + return ERROR_INT("pixd not 32 bpp", __func__, 1); + if (pixGetDepth(pixs) != 8) + return ERROR_INT("pixs not 8 bpp", __func__, 1); + if (comp != COLOR_RED && comp != COLOR_GREEN && + comp != COLOR_BLUE && comp != L_ALPHA_CHANNEL) + return ERROR_INT("invalid comp", __func__, 1); + pixGetDimensions(pixs, &ws, &hs, NULL); + pixGetDimensions(pixd, &wd, &hd, NULL); + if (ws != wd || hs != hd) + L_WARNING("images sizes not equal\n", __func__); + w = L_MIN(ws, wd); + h = L_MIN(hs, hd); + if (comp == L_ALPHA_CHANNEL) + pixSetSpp(pixd, 4); + datas = pixGetData(pixs); + datad = pixGetData(pixd); + wpls = pixGetWpl(pixs); + wpld = pixGetWpl(pixd); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + srcbyte = GET_DATA_BYTE(lines, j); + SET_DATA_BYTE(lined + j, comp, srcbyte); + } + } + + return 0; +} + + +/*! + * \brief pixGetRGBComponentCmap() + * + * \param[in] pixs colormapped + * \param[in] comp one of the set: {COLOR_RED, COLOR_GREEN, COLOR_BLUE} + * \return pixd the selected 8 bpp component image of the + * input cmapped image, or NULL on error + * + * <pre> + * Notes: + * (1) In leptonica, we do not support alpha in colormaps. + * </pre> + */ +PIX * +pixGetRGBComponentCmap(PIX *pixs, + l_int32 comp) +{ +l_int32 i, j, w, h, val, index, valid; +l_int32 wplc, wpld; +l_uint32 *linec, *lined; +l_uint32 *datac, *datad; +PIX *pixc, *pixd; +PIXCMAP *cmap; +RGBA_QUAD *cta; + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); + if ((cmap = pixGetColormap(pixs)) == NULL) + return (PIX *)ERROR_PTR("pixs not cmapped", __func__, NULL); + if (comp == L_ALPHA_CHANNEL) + return (PIX *)ERROR_PTR("alpha in cmaps not supported", __func__, NULL); + if (comp != COLOR_RED && comp != COLOR_GREEN && comp != COLOR_BLUE) + return (PIX *)ERROR_PTR("invalid comp", __func__, NULL); + + /* If not 8 bpp, make a cmapped 8 bpp pix */ + if (pixGetDepth(pixs) == 8) + pixc = pixClone(pixs); + else + pixc = pixConvertTo8(pixs, TRUE); + pixcmapIsValid(cmap, pixc, &valid); + if (!valid) { + pixDestroy(&pixc); + return (PIX *)ERROR_PTR("invalid colormap", __func__, NULL); + } + + pixGetDimensions(pixs, &w, &h, NULL); + if ((pixd = pixCreate(w, h, 8)) == NULL) { + pixDestroy(&pixc); + return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); + } + pixCopyResolution(pixd, pixs); + wplc = pixGetWpl(pixc); + wpld = pixGetWpl(pixd); + datac = pixGetData(pixc); + datad = pixGetData(pixd); + cta = (RGBA_QUAD *)cmap->array; + + for (i = 0; i < h; i++) { + linec = datac + i * wplc; + lined = datad + i * wpld; + if (comp == COLOR_RED) { + for (j = 0; j < w; j++) { + index = GET_DATA_BYTE(linec, j); + val = cta[index].red; + SET_DATA_BYTE(lined, j, val); + } + } else if (comp == COLOR_GREEN) { + for (j = 0; j < w; j++) { + index = GET_DATA_BYTE(linec, j); + val = cta[index].green; + SET_DATA_BYTE(lined, j, val); + } + } else if (comp == COLOR_BLUE) { + for (j = 0; j < w; j++) { + index = GET_DATA_BYTE(linec, j); + val = cta[index].blue; + SET_DATA_BYTE(lined, j, val); + } + } + } + + pixDestroy(&pixc); + return pixd; +} + + +/*! + * \brief pixCopyRGBComponent() + * + * \param[in] pixd 32 bpp + * \param[in] pixs 32 bpp + * \param[in] comp one of the set: {COLOR_RED, COLOR_GREEN, + * COLOR_BLUE, L_ALPHA_CHANNEL} + * \return 0 if OK; 1 on error + * + * <pre> + * Notes: + * (1) The two images are registered to the UL corner. The sizes + * are usually the same, and a warning is issued if they differ. + * </pre> + */ +l_ok +pixCopyRGBComponent(PIX *pixd, + PIX *pixs, + l_int32 comp) +{ +l_int32 i, j, w, h, ws, hs, wd, hd, val; +l_int32 wpls, wpld; +l_uint32 *lines, *lined; +l_uint32 *datas, *datad; + + if (!pixd && pixGetDepth(pixd) != 32) + return ERROR_INT("pixd not defined or not 32 bpp", __func__, 1); + if (!pixs && pixGetDepth(pixs) != 32) + return ERROR_INT("pixs not defined or not 32 bpp", __func__, 1); + if (comp != COLOR_RED && comp != COLOR_GREEN && + comp != COLOR_BLUE && comp != L_ALPHA_CHANNEL) + return ERROR_INT("invalid component", __func__, 1); + pixGetDimensions(pixs, &ws, &hs, NULL); + pixGetDimensions(pixd, &wd, &hd, NULL); + if (ws != wd || hs != hd) + L_WARNING("images sizes not equal\n", __func__); + w = L_MIN(ws, wd); + h = L_MIN(hs, hd); + if (comp == L_ALPHA_CHANNEL) + pixSetSpp(pixd, 4); + wpls = pixGetWpl(pixs); + wpld = pixGetWpl(pixd); + datas = pixGetData(pixs); + datad = pixGetData(pixd); + for (i = 0; i < h; i++) { + lines = datas + i * wpls; + lined = datad + i * wpld; + for (j = 0; j < w; j++) { + val = GET_DATA_BYTE(lines + j, comp); + SET_DATA_BYTE(lined + j, comp, val); + } + } + return 0; +} + + +/*! + * \brief composeRGBPixel() + * + * \param[in] rval, gval, bval + * \param[out] ppixel 32-bit pixel + * \return 0 if OK; 1 on error + * + * <pre> + * Notes: + * (1) All channels are 8 bits: the input values must be between + * 0 and 255. For speed, this is not enforced by masking + * with 0xff before shifting. + * (2) A slower implementation uses macros: + * SET_DATA_BYTE(ppixel, COLOR_RED, rval); + * SET_DATA_BYTE(ppixel, COLOR_GREEN, gval); + * SET_DATA_BYTE(ppixel, COLOR_BLUE, bval); + * </pre> + */ +l_ok +composeRGBPixel(l_int32 rval, + l_int32 gval, + l_int32 bval, + l_uint32 *ppixel) +{ + if (!ppixel) + return ERROR_INT("&pixel not defined", __func__, 1); + + *ppixel = ((l_uint32)rval << L_RED_SHIFT) | + ((l_uint32)gval << L_GREEN_SHIFT) | + ((l_uint32)bval << L_BLUE_SHIFT); + return 0; +} + + +/*! + * \brief composeRGBAPixel() + * + * \param[in] rval, gval, bval, aval + * \param[out] ppixel 32-bit pixel + * \return 0 if OK; 1 on error + * + * <pre> + * Notes: + * (1) All channels are 8 bits: the input values must be between + * 0 and 255. For speed, this is not enforced by masking + * with 0xff before shifting. + * </pre> + */ +l_ok +composeRGBAPixel(l_int32 rval, + l_int32 gval, + l_int32 bval, + l_int32 aval, + l_uint32 *ppixel) +{ + if (!ppixel) + return ERROR_INT("&pixel not defined", __func__, 1); + + *ppixel = ((l_uint32)rval << L_RED_SHIFT) | + ((l_uint32)gval << L_GREEN_SHIFT) | + ((l_uint32)bval << L_BLUE_SHIFT) | + aval; + return 0; +} + + +/*! + * \brief extractRGBValues() + * + * \param[in] pixel 32 bit + * \param[out] prval [optional] red component + * \param[out] pgval [optional] green component + * \param[out] pbval [optional] blue component + * \return void + * + * <pre> + * Notes: + * (1) A slower implementation uses macros: + * *prval = GET_DATA_BYTE(&pixel, COLOR_RED); + * *pgval = GET_DATA_BYTE(&pixel, COLOR_GREEN); + * *pbval = GET_DATA_BYTE(&pixel, COLOR_BLUE); + * </pre> + */ +void +extractRGBValues(l_uint32 pixel, + l_int32 *prval, + l_int32 *pgval, + l_int32 *pbval) +{ + if (prval) *prval = (pixel >> L_RED_SHIFT) & 0xff; + if (pgval) *pgval = (pixel >> L_GREEN_SHIFT) & 0xff; + if (pbval) *pbval = (pixel >> L_BLUE_SHIFT) & 0xff; +} + + +/*! + * \brief extractRGBAValues() + * + * \param[in] pixel 32 bit + * \param[out] prval [optional] red component + * \param[out] pgval [optional] green component + * \param[out] pbval [optional] blue component + * \param[out] paval [optional] alpha component + * \return void + */ +void +extractRGBAValues(l_uint32 pixel, + l_int32 *prval, + l_int32 *pgval, + l_int32 *pbval, + l_int32 *paval) +{ + if (prval) *prval = (pixel >> L_RED_SHIFT) & 0xff; + if (pgval) *pgval = (pixel >> L_GREEN_SHIFT) & 0xff; + if (pbval) *pbval = (pixel >> L_BLUE_SHIFT) & 0xff; + if (paval) *paval = (pixel >> L_ALPHA_SHIFT) & 0xff; +} + + +/*! + * \brief extractMinMaxComponent() + * + * \param[in] pixel 32 bpp RGB + * \param[in] type L_CHOOSE_MIN or L_CHOOSE_MAX + * \return component in range [0 ... 255], or NULL on error + */ +l_int32 +extractMinMaxComponent(l_uint32 pixel, + l_int32 type) +{ +l_int32 rval, gval, bval, val; + + extractRGBValues(pixel, &rval, &gval, &bval); + if (type == L_CHOOSE_MIN) { + val = L_MIN(rval, gval); + val = L_MIN(val, bval); + } else { /* type == L_CHOOSE_MAX */ + val = L_MAX(rval, gval); + val = L_MAX(val, bval); + } + return val; +} + + +/*! + * \brief pixGetRGBLine() + * + * \param[in] pixs 32 bpp + * \param[in] row + * \param[in] bufr array of red samples; size w bytes + * \param[in] bufg array of green samples; size w bytes + * \param[in] bufb array of blue samples; size w bytes + * \return 0 if OK; 1 on error + * + * <pre> + * Notes: + * (1) This puts rgb components from the input line in pixs + * into the given buffers. + * </pre> + */ +l_ok +pixGetRGBLine(PIX *pixs, + l_int32 row, + l_uint8 *bufr, + l_uint8 *bufg, + l_uint8 *bufb) +{ +l_uint32 *lines; +l_int32 j, w, h; +l_int32 wpls; + + if (!pixs) + return ERROR_INT("pixs not defined", __func__, 1); + if (pixGetDepth(pixs) != 32) + return ERROR_INT("pixs not 32 bpp", __func__, 1); + if (!bufr || !bufg || !bufb) + return ERROR_INT("buffer not defined", __func__, 1); + + pixGetDimensions(pixs, &w, &h, NULL); + if (row < 0 || row >= h) + return ERROR_INT("row out of bounds", __func__, 1); + wpls = pixGetWpl(pixs); + lines = pixGetData(pixs) + row * wpls; + + for (j = 0; j < w; j++) { + bufr[j] = GET_DATA_BYTE(lines + j, COLOR_RED); + bufg[j] = GET_DATA_BYTE(lines + j, COLOR_GREEN); + bufb[j] = GET_DATA_BYTE(lines + j, COLOR_BLUE); + } + + return 0; +} + + +/*-------------------------------------------------------------* + * Raster line pixel setter * + *-------------------------------------------------------------*/ +/*! + * \brief setLineDataVal() + * + * \param[in] line ptr to first word in raster line data + * \param[in] j index of pixels into the raster line + * \param[in] d depth of the pixel + * \param[in] val pixel value to be set + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) This is a convenience function to set a pixel value in a + * raster line where the depth of the image can have different + * values (1, 2, 4, 8, 16 or 32). + * </pre> + */ +l_ok +setLineDataVal(l_uint32 *line, + l_int32 j, + l_int32 d, + l_uint32 val) +{ + if (!line) + return ERROR_INT("line not defined", __func__, 1); + if (j < 0) + return ERROR_INT("j must be >= 0", __func__, 1); + if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32) + return ERROR_INT("invalid d", __func__, 1); + + if (d == 1) + SET_DATA_BIT_VAL(line, j, val); + else if (d == 2) + SET_DATA_DIBIT(line, j, val); + else if (d == 4) + SET_DATA_QBIT(line, j, val); + else if (d == 8) + SET_DATA_BYTE(line, j, val); + else if (d == 16) + SET_DATA_TWO_BYTES(line, j, val); + else /* d == 32 */ + *(line + j) = val; + return 0; +} + + +/*-------------------------------------------------------------* + * Pixel endian conversion * + *-------------------------------------------------------------*/ +/*! + * \brief pixEndianByteSwapNew() + * + * \param[in] pixs + * \return pixd, or NULL on error + * + * <pre> + * Notes: + * (1) This is used to convert the data in a pix to a + * serialized byte buffer in raster order, and, for RGB, + * in order RGBA. This requires flipping bytes within + * each 32-bit word for little-endian platforms, because the + * words have a MSB-to-the-left rule, whereas byte raster-order + * requires the left-most byte in each word to be byte 0. + * For big-endians, no swap is necessary, so this returns a clone. + * (2) Unlike pixEndianByteSwap(), which swaps the bytes in-place, + * this returns a new pix (or a clone). We provide this + * because often when serialization is done, the source + * pix needs to be restored to canonical little-endian order, + * and this requires a second byte swap. In such a situation, + * it is twice as fast to make a new pix in big-endian order, + * use it, and destroy it. + * </pre> + */ +PIX * +pixEndianByteSwapNew(PIX *pixs) +{ +l_uint32 *datas, *datad; +l_int32 i, j, h, wpl; +l_uint32 word; +PIX *pixd; + +#ifdef L_BIG_ENDIAN + + return pixClone(pixs); + +#else /* L_LITTLE_ENDIAN */ + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); + + datas = pixGetData(pixs); + wpl = pixGetWpl(pixs); + h = pixGetHeight(pixs); + if ((pixd = pixCreateTemplate(pixs)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); + datad = pixGetData(pixd); + for (i = 0; i < h; i++) { + for (j = 0; j < wpl; j++, datas++, datad++) { + word = *datas; + *datad = (word >> 24) | + ((word >> 8) & 0x0000ff00) | + ((word << 8) & 0x00ff0000) | + (word << 24); + } + } + + return pixd; + +#endif /* L_BIG_ENDIAN */ + +} + + +/*! + * \brief pixEndianByteSwap() + * + * \param[in] pixs + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) This is used on little-endian platforms to swap + * the bytes within a word; bytes 0 and 3 are swapped, + * and bytes 1 and 2 are swapped. + * (2) This is required for little-endians in situations + * where we convert from a serialized byte order that is + * in raster order, as one typically has in file formats, + * to one with MSB-to-the-left in each 32-bit word, or v.v. + * See pix.h for a description of the canonical format + * (MSB-to-the left) that is used for both little-endian + * and big-endian platforms. For big-endians, the + * MSB-to-the-left word order has the bytes in raster + * order when serialized, so no byte flipping is required. + * </pre> + */ +l_ok +pixEndianByteSwap(PIX *pixs) +{ +l_uint32 *data; +l_int32 i, j, h, wpl; +l_uint32 word; + +#ifdef L_BIG_ENDIAN + + return 0; + +#else /* L_LITTLE_ENDIAN */ + + if (!pixs) + return ERROR_INT("pixs not defined", __func__, 1); + + data = pixGetData(pixs); + wpl = pixGetWpl(pixs); + h = pixGetHeight(pixs); + for (i = 0; i < h; i++) { + for (j = 0; j < wpl; j++, data++) { + word = *data; + *data = (word >> 24) | + ((word >> 8) & 0x0000ff00) | + ((word << 8) & 0x00ff0000) | + (word << 24); + } + } + + return 0; + +#endif /* L_BIG_ENDIAN */ + +} + + +/*! + * \brief lineEndianByteSwap() + * + * \param[in] datad dest byte array data, reordered on little-endians + * \param[in] datas a src line of pix data) + * \param[in] wpl number of 32 bit words in the line + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) This is used on little-endian platforms to swap + * the bytes within each word in the line of image data. + * Bytes 0 <==> 3 and 1 <==> 2 are swapped in the dest + * byte array data8d, relative to the pix data in datas. + * (2) The bytes represent 8 bit pixel values. They are swapped + * for little endians so that when the dest array datad + * is addressed by bytes, the pixels are chosen sequentially + * from left to right in the image. + * </pre> + */ +l_int32 +lineEndianByteSwap(l_uint32 *datad, + l_uint32 *datas, + l_int32 wpl) +{ +l_int32 j; +l_uint32 word; + + if (!datad || !datas) + return ERROR_INT("datad and datas not both defined", __func__, 1); + +#ifdef L_BIG_ENDIAN + + memcpy(datad, datas, 4 * wpl); + return 0; + +#else /* L_LITTLE_ENDIAN */ + + for (j = 0; j < wpl; j++, datas++, datad++) { + word = *datas; + *datad = (word >> 24) | + ((word >> 8) & 0x0000ff00) | + ((word << 8) & 0x00ff0000) | + (word << 24); + } + return 0; + +#endif /* L_BIG_ENDIAN */ + +} + + +/*! + * \brief pixEndianTwoByteSwapNew() + * + * \param[in] pixs + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) This is used on little-endian platforms to swap the + * 2-byte entities within a 32-bit word. + * (2) This is equivalent to a full byte swap, as performed + * by pixEndianByteSwap(), followed by byte swaps in + * each of the 16-bit entities separately. + * (3) Unlike pixEndianTwoByteSwap(), which swaps the shorts in-place, + * this returns a new pix (or a clone). We provide this + * to avoid having to swap twice in situations where the input + * pix must be restored to canonical little-endian order. + * </pre> + */ +PIX * +pixEndianTwoByteSwapNew(PIX *pixs) +{ +l_uint32 *datas, *datad; +l_int32 i, j, h, wpl; +l_uint32 word; +PIX *pixd; + +#ifdef L_BIG_ENDIAN + + return pixClone(pixs); + +#else /* L_LITTLE_ENDIAN */ + + if (!pixs) + return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); + + datas = pixGetData(pixs); + wpl = pixGetWpl(pixs); + h = pixGetHeight(pixs); + if ((pixd = pixCreateTemplate(pixs)) == NULL) + return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); + datad = pixGetData(pixd); + for (i = 0; i < h; i++) { + for (j = 0; j < wpl; j++, datas++, datad++) { + word = *datas; + *datad = (word << 16) | (word >> 16); + } + } + + return pixd; + +#endif /* L_BIG_ENDIAN */ + +} + + +/*! + * \brief pixEndianTwoByteSwap() + * + * \param[in] pixs + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) This is used on little-endian platforms to swap the + * 2-byte entities within a 32-bit word. + * (2) This is equivalent to a full byte swap, as performed + * by pixEndianByteSwap(), followed by byte swaps in + * each of the 16-bit entities separately. + * </pre> + */ +l_ok +pixEndianTwoByteSwap(PIX *pixs) +{ +l_uint32 *data; +l_int32 i, j, h, wpl; +l_uint32 word; + +#ifdef L_BIG_ENDIAN + + return 0; + +#else /* L_LITTLE_ENDIAN */ + + if (!pixs) + return ERROR_INT("pixs not defined", __func__, 1); + + data = pixGetData(pixs); + wpl = pixGetWpl(pixs); + h = pixGetHeight(pixs); + for (i = 0; i < h; i++) { + for (j = 0; j < wpl; j++, data++) { + word = *data; + *data = (word << 16) | (word >> 16); + } + } + + return 0; + +#endif /* L_BIG_ENDIAN */ + +} + + +/*-------------------------------------------------------------* + * Extract raster data as binary string * + *-------------------------------------------------------------*/ +/*! + * \brief pixGetRasterData() + * + * \param[in] pixs 1, 8, 32 bpp + * \param[out] pdata raster data in memory + * \param[out] pnbytes number of bytes in data string + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) This returns the raster data as a byte string, padded to the + * byte. For 1 bpp, the first pixel is the MSbit in the first byte. + * For rgb, the bytes are in (rgb) order. This is the format + * required for flate encoding of pixels in a PostScript file. + * </pre> + */ +l_ok +pixGetRasterData(PIX *pixs, + l_uint8 **pdata, + size_t *pnbytes) +{ +l_int32 w, h, d, wpl, i, j, rval, gval, bval; +l_int32 databpl; /* bytes for each raster line in returned data */ +l_uint8 *line, *data; /* packed data in returned array */ +l_uint32 *rline, *rdata; /* data in pix raster */ + + if (pdata) *pdata = NULL; + if (pnbytes) *pnbytes = 0; + if (!pdata || !pnbytes) + return ERROR_INT("&data and &nbytes not both defined", __func__, 1); + if (!pixs) + return ERROR_INT("pixs not defined", __func__, 1); + pixGetDimensions(pixs, &w, &h, &d); + if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32) + return ERROR_INT("depth not in {1,2,4,8,16,32}", __func__, 1); + + pixSetPadBits(pixs, 0); + rdata = pixGetData(pixs); + wpl = pixGetWpl(pixs); + if (d == 1) + databpl = (w + 7) / 8; + else if (d == 2) + databpl = (w + 3) / 4; + else if (d == 4) + databpl = (w + 1) / 2; + else if (d == 8 || d == 16) + databpl = w * (d / 8); + else /* d == 32 bpp rgb */ + databpl = 3 * w; + if ((data = (l_uint8 *)LEPT_CALLOC((size_t)databpl * h, sizeof(l_uint8))) + == NULL) + return ERROR_INT("data not allocated", __func__, 1); + *pdata = data; + *pnbytes = (size_t)databpl * h; + + for (i = 0; i < h; i++) { + rline = rdata + i * wpl; + line = data + i * databpl; + if (d <= 8) { + for (j = 0; j < databpl; j++) + line[j] = GET_DATA_BYTE(rline, j); + } else if (d == 16) { + for (j = 0; j < w; j++) + line[2 * j] = GET_DATA_TWO_BYTES(rline, j); + } else { /* d == 32 bpp rgb */ + for (j = 0; j < w; j++) { + extractRGBValues(rline[j], &rval, &gval, &bval); + *(line + 3 * j) = rval; + *(line + 3 * j + 1) = gval; + *(line + 3 * j + 2) = bval; + } + } + } + + return 0; +} + + +/*-------------------------------------------------------------* + * Infer resolution from image size * + *-------------------------------------------------------------*/ +/*! + * \brief pixInferResolution() + * + * \param[in] pix + * \param[in] longside assumed max dimension, in inches + * \param[out] pres resolution (ppi) + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) This finds the resolution, assuming that the longest side + * of the image is %longside. On error, returns 300 ppi. + * (2) This is useful for computing resolution for generating pdfs, + * when the images are scanned from pages of known size. + * There, %longside is typically about 11.0. + * </pre> + */ +l_ok +pixInferResolution(PIX *pix, + l_float32 longside, + l_int32 *pres) +{ +l_int32 w, h, maxdim, res; + + if (!pres) + return ERROR_INT("&res not defined", __func__, 1); + *pres = 300; + if (!pix) + return ERROR_INT("pix not defined", __func__, 1); + if (longside <= 0.0) + return ERROR_INT("longside not > 0", __func__, 1); + + pixGetDimensions(pix, &w, &h, NULL); + maxdim = L_MAX(w, h); + res = (l_int32)(maxdim / longside + 0.5); + res = L_MAX(res, 1); /* don't let it be 0 */ + if (res < 10) + L_WARNING("low inferred resolution: %d ppi\n", __func__, res); + if (res > 10000) + L_WARNING("high inferred resolution: %d ppi\n", __func__, res); + *pres = res; + return 0; +} + + +/*-------------------------------------------------------------* + * Test alpha component opaqueness * + *-------------------------------------------------------------*/ +/*! + * \brief pixAlphaIsOpaque() + * + * \param[in] pix 32 bpp, spp == 4 + * \param[out] popaque 1 if spp == 4 and all alpha component + * values are 255 (opaque); 0 otherwise + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * 1) On error, opaque is returned as 0 (FALSE). + * </pre> + */ +l_ok +pixAlphaIsOpaque(PIX *pix, + l_int32 *popaque) +{ +l_int32 w, h, wpl, i, j, alpha; +l_uint32 *data, *line; + + if (!popaque) + return ERROR_INT("&opaque not defined", __func__, 1); + *popaque = FALSE; + if (!pix) + return ERROR_INT("&pix not defined", __func__, 1); + if (pixGetDepth(pix) != 32) + return ERROR_INT("&pix not 32 bpp", __func__, 1); + if (pixGetSpp(pix) != 4) + return ERROR_INT("&pix not 4 spp", __func__, 1); + + data = pixGetData(pix); + wpl = pixGetWpl(pix); + pixGetDimensions(pix, &w, &h, NULL); + for (i = 0; i < h; i++) { + line = data + i * wpl; + for (j = 0; j < w; j++) { + alpha = GET_DATA_BYTE(line + j, L_ALPHA_CHANNEL); + if (alpha ^ 0xff) /* not opaque */ + return 0; + } + } + + *popaque = TRUE; + return 0; +} + + +/*-------------------------------------------------------------* + * Setup helpers for 8 bpp byte processing * + *-------------------------------------------------------------*/ +/*! + * \brief pixSetupByteProcessing() + * + * \param[in] pix 8 bpp, no colormap + * \param[out] pw [optional] width + * \param[out] ph [optional] height + * \return line ptr array, or NULL on error + * + * <pre> + * Notes: + * (1) This is a simple helper for processing 8 bpp images with + * direct byte access. It can swap byte order within each word. + * (2) After processing, you must call pixCleanupByteProcessing(), + * which frees the lineptr array and restores byte order. + * (3) Usage: + * l_uint8 **lineptrs = pixSetupByteProcessing(pix, &w, &h); + * for (i = 0; i < h; i++) { + * l_uint8 *line = lineptrs[i]; + * for (j = 0; j < w; j++) { + * val = line[j]; + * ... + * } + * } + * pixCleanupByteProcessing(pix, lineptrs); + * </pre> + */ +l_uint8 ** +pixSetupByteProcessing(PIX *pix, + l_int32 *pw, + l_int32 *ph) +{ +l_int32 w, h; + + if (pw) *pw = 0; + if (ph) *ph = 0; + if (!pix || pixGetDepth(pix) != 8) + return (l_uint8 **)ERROR_PTR("pix not defined or not 8 bpp", + __func__, NULL); + pixGetDimensions(pix, &w, &h, NULL); + if (pw) *pw = w; + if (ph) *ph = h; + if (pixGetColormap(pix)) + return (l_uint8 **)ERROR_PTR("pix has colormap", __func__, NULL); + + pixEndianByteSwap(pix); + return (l_uint8 **)pixGetLinePtrs(pix, NULL); +} + + +/*! + * \brief pixCleanupByteProcessing() + * + * \param[in] pix 8 bpp, no colormap + * \param[in] lineptrs ptrs to the beginning of each raster line of data + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) This must be called after processing that was initiated + * by pixSetupByteProcessing() has finished. + * </pre> + */ +l_ok +pixCleanupByteProcessing(PIX *pix, + l_uint8 **lineptrs) +{ + if (!pix) + return ERROR_INT("pix not defined", __func__, 1); + if (!lineptrs) + return ERROR_INT("lineptrs not defined", __func__, 1); + + pixEndianByteSwap(pix); + LEPT_FREE(lineptrs); + return 0; +} + + +/*------------------------------------------------------------------------* + * Setting parameters for antialias masking with alpha transforms * + *------------------------------------------------------------------------*/ +/*! + * \brief l_setAlphaMaskBorder() + * + * \param[in] val1, val2 in [0.0 ... 1.0] + * \return void + * + * <pre> + * Notes: + * (1) This sets the opacity values used to generate the two outer + * boundary rings in the alpha mask associated with geometric + * transforms such as pixRotateWithAlpha(). + * (2) The default values are val1 = 0.0 (completely transparent + * in the outermost ring) and val2 = 0.5 (half transparent + * in the second ring). When the image is blended, this + * completely removes the outer ring (shrinking the image by + * 2 in each direction), and alpha-blends with 0.5 the second ring. + * Using val1 = 0.25 and val2 = 0.75 gives a slightly more + * blurred border, with no perceptual difference at screen resolution. + * (3) The actual mask values are found by multiplying these + * normalized opacity values by 255. + * </pre> + */ +void +l_setAlphaMaskBorder(l_float32 val1, + l_float32 val2) +{ + val1 = L_MAX(0.0f, L_MIN(1.0f, val1)); + val2 = L_MAX(0.0f, L_MIN(1.0f, val2)); + AlphaMaskBorderVals[0] = val1; + AlphaMaskBorderVals[1] = val2; +}
