twain3.0/3rdparty/hgOCR/leptonica/pixconv.c

4261 lines
140 KiB
C

/*====================================================================*
- 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 pixconv.c
* <pre>
*
* These functions convert between images of different types
* without scaling.
*
* Conversion from 8 bpp grayscale to 1, 2, 4 and 8 bpp
* PIX *pixThreshold8()
*
* Conversion from colormap to full color or grayscale
* PIX *pixRemoveColormapGeneral()
* PIX *pixRemoveColormap()
*
* Add colormap losslessly (8 to 8)
* l_int32 pixAddGrayColormap8()
* PIX *pixAddMinimalGrayColormap8()
*
* Conversion from RGB color to grayscale
* PIX *pixConvertRGBToLuminance()
* PIX *pixConvertRGBToGray()
* PIX *pixConvertRGBToGrayFast()
* PIX *pixConvertRGBToGrayMinMax()
* PIX *pixConvertRGBToGraySatBoost()
* PIX *pixConvertRGBToGrayArb()
* PIX *pixConvertRGBToBinaryArb()
*
* Conversion from grayscale to colormap
* PIX *pixConvertGrayToColormap() -- 2, 4, 8 bpp
* PIX *pixConvertGrayToColormap8() -- 8 bpp only
*
* Colorizing conversion from grayscale to color
* PIX *pixColorizeGray() -- 8 bpp or cmapped
*
* Conversion from RGB color to colormap
* PIX *pixConvertRGBToColormap()
*
* Conversion from colormap to 1 bpp
* PIX *pixConvertCmapTo1()
*
* Quantization for relatively small number of colors in source
* l_int32 pixQuantizeIfFewColors()
*
* Conversion from 16 bpp to 8 bpp
* PIX *pixConvert16To8()
*
* Conversion from grayscale to false color
* PIX *pixConvertGrayToFalseColor()
*
* Unpacking conversion from 1 bpp to 2, 4, 8, 16 and 32 bpp
* PIX *pixUnpackBinary()
* PIX *pixConvert1To16()
* PIX *pixConvert1To32()
*
* Unpacking conversion from 1 bpp to 2 bpp
* PIX *pixConvert1To2Cmap()
* PIX *pixConvert1To2()
*
* Unpacking conversion from 1 bpp to 4 bpp
* PIX *pixConvert1To4Cmap()
* PIX *pixConvert1To4()
*
* Unpacking conversion from 1, 2 and 4 bpp to 8 bpp
* PIX *pixConvert1To8()
* PIX *pixConvert2To8()
* PIX *pixConvert4To8()
*
* Unpacking conversion from 8 bpp to 16 bpp
* PIX *pixConvert8To16()
*
* Top-level conversion to 1 bpp
* PIX *pixConvertTo1Adaptive()
* PIX *pixConvertTo1()
* PIX *pixConvertTo1BySampling()
*
* Top-level conversion to 2 bpp
* PIX *pixConvertTo2()
* PIX *pixConvert8To2()
*
* Top-level conversion to 4 bpp
* PIX *pixConvertTo4()
* PIX *pixConvert8To4()
*
* Top-level conversion to 8 bpp
* PIX *pixConvertTo8()
* PIX *pixConvertTo8BySampling()
* PIX *pixConvertTo8Colormap()
*
* Top-level conversion to 16 bpp
* PIX *pixConvertTo16()
*
* Top-level conversion to 32 bpp (RGB)
* PIX *pixConvertTo32() ***
* PIX *pixConvertTo32BySampling() ***
* PIX *pixConvert8To32() ***
*
* Top-level conversion to 8 or 32 bpp, without colormap
* PIX *pixConvertTo8Or32
*
* Conversion between 24 bpp and 32 bpp rgb
* PIX *pixConvert24To32()
* PIX *pixConvert32To24()
*
* Conversion between 32 bpp (1 spp) and 16 or 8 bpp
* PIX *pixConvert32To16()
* PIX *pixConvert32To8()
*
* Removal of alpha component by blending with white background
* PIX *pixRemoveAlpha()
*
* Addition of alpha component to 1 bpp
* PIX *pixAddAlphaTo1bpp()
*
* Lossless depth conversion (unpacking)
* PIX *pixConvertLossless()
*
* Conversion for printing in PostScript
* PIX *pixConvertForPSWrap()
*
* Scaling conversion to subpixel RGB
* PIX *pixConvertToSubpixelRGB()
* PIX *pixConvertGrayToSubpixelRGB()
* PIX *pixConvertColorToSubpixelRGB()
*
* Setting neutral point for min/max boost conversion to gray
* void l_setNeutralBoostVal()
* </pre>
*/
#include <string.h>
#include <math.h>
#include "allheaders.h"
/* ------- Set neutral point for min/max boost conversion to gray ------ */
/* Call l_setNeutralBoostVal() to change this */
static l_int32 var_NEUTRAL_BOOST_VAL = 180;
#ifndef NO_CONSOLE_IO
#define DEBUG_CONVERT_TO_COLORMAP 0
#define DEBUG_UNROLLING 0
#endif /* ~NO_CONSOLE_IO */
/*-------------------------------------------------------------*
* Conversion from 8 bpp grayscale to 1, 2 4 and 8 bpp *
*-------------------------------------------------------------*/
/*!
* \brief pixThreshold8()
*
* \param[in] pixs 8 bpp grayscale
* \param[in] d destination depth: 1, 2, 4 or 8
* \param[in] nlevels number of levels to be used for colormap
* \param[in] cmapflag 1 if makes colormap; 0 otherwise
* \return pixd thresholded with standard dest thresholds,
* or NULL on error
*
* <pre>
* Notes:
* (1) This uses, by default, equally spaced "target" values
* that depend on the number of levels, with thresholds
* halfway between. For N levels, with separation (N-1)/255,
* there are N-1 fixed thresholds.
* (2) For 1 bpp destination, the number of levels can only be 2
* and if a cmap is made, black is (0,0,0) and white
* is (255,255,255), which is opposite to the convention
* without a colormap.
* (3) For 1, 2 and 4 bpp, the nlevels arg is used if a colormap
* is made; otherwise, we take the most significant bits
* from the src that will fit in the dest.
* (4) For 8 bpp, the input pixs is quantized to nlevels. The
* dest quantized with that mapping, either through a colormap
* table or directly with 8 bit values.
* (5) Typically you should not use make a colormap for 1 bpp dest.
* (6) This is not dithering. Each pixel is treated independently.
* </pre>
*/
PIX *
pixThreshold8(PIX *pixs,
l_int32 d,
l_int32 nlevels,
l_int32 cmapflag)
{
PIX *pixd;
PIXCMAP *cmap;
PROCNAME("pixThreshold8");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
if (pixGetDepth(pixs) != 8)
return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL);
if (cmapflag && nlevels < 2)
return (PIX *)ERROR_PTR("nlevels must be at least 2", procName, NULL);
switch (d) {
case 1:
pixd = pixThresholdToBinary(pixs, 128);
if (cmapflag) {
cmap = pixcmapCreateLinear(1, 2);
pixSetColormap(pixd, cmap);
}
break;
case 2:
pixd = pixThresholdTo2bpp(pixs, nlevels, cmapflag);
break;
case 4:
pixd = pixThresholdTo4bpp(pixs, nlevels, cmapflag);
break;
case 8:
pixd = pixThresholdOn8bpp(pixs, nlevels, cmapflag);
break;
default:
return (PIX *)ERROR_PTR("d must be in {1,2,4,8}", procName, NULL);
}
if (!pixd)
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
pixCopyInputFormat(pixd, pixs);
return pixd;
}
/*-------------------------------------------------------------*
* Conversion from colormapped pix *
*-------------------------------------------------------------*/
/*!
* \brief pixRemoveColormapGeneral()
*
* \param[in] pixs any depth, with or without colormap
* \param[in] type REMOVE_CMAP_TO_BINARY,
* REMOVE_CMAP_TO_GRAYSCALE,
* REMOVE_CMAP_TO_FULL_COLOR,
* REMOVE_CMAP_WITH_ALPHA,
* REMOVE_CMAP_BASED_ON_SRC
* \param[in] ifnocmap L_CLONE, L_COPY
* \return pixd always a new pix; without colormap, or NULL on error
*
* <pre>
* Notes:
* (1) Convenience function that allows choice between returning
* a clone or a copy if pixs does not have a colormap.
* (2) See pixRemoveColormap().
* </pre>
*/
PIX *
pixRemoveColormapGeneral(PIX *pixs,
l_int32 type,
l_int32 ifnocmap)
{
PROCNAME("pixRemoveColormapGeneral");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
if (ifnocmap != L_CLONE && ifnocmap != L_COPY)
return (PIX *)ERROR_PTR("invalid value for ifnocmap", procName, NULL);
if (pixGetColormap(pixs))
return pixRemoveColormap(pixs, type);
if (ifnocmap == L_CLONE)
return pixClone(pixs);
else
return pixCopy(NULL, pixs);
}
/*!
* \brief pixRemoveColormap()
*
* \param[in] pixs see restrictions below
* \param[in] type REMOVE_CMAP_TO_BINARY,
* REMOVE_CMAP_TO_GRAYSCALE,
* REMOVE_CMAP_TO_FULL_COLOR,
* REMOVE_CMAP_WITH_ALPHA,
* REMOVE_CMAP_BASED_ON_SRC
* \return pixd without colormap, or NULL on error
*
* <pre>
* Notes:
* (1) If pixs does not have a colormap, a clone is returned.
* (2) Otherwise, the input pixs is restricted to 1, 2, 4 or 8 bpp.
* (3) Use REMOVE_CMAP_TO_BINARY only on 1 bpp pix.
* (4) For grayscale conversion from RGB, use a weighted average
* of RGB values, and always return an 8 bpp pix, regardless
* of whether the input pixs depth is 2, 4 or 8 bpp.
* (5) REMOVE_CMAP_TO_FULL_COLOR ignores the alpha component and
* returns a 32 bpp pix with spp == 3 and the alpha bytes are 0.
* (6) For REMOVE_CMAP_BASED_ON_SRC, if there is no color, this
* returns either a 1 bpp or 8 bpp grayscale pix.
* If there is color, this returns a 32 bpp pix, with either:
* * 3 spp, if the alpha values are all 255 (opaque), or
* * 4 spp (preserving the alpha), if any alpha values are not 255.
* </pre>
*/
PIX *
pixRemoveColormap(PIX *pixs,
l_int32 type)
{
l_int32 sval, rval, gval, bval, val0, val1;
l_int32 i, j, k, w, h, d, wpls, wpld, ncolors, nalloc, count;
l_int32 opaque, colorfound, blackwhite;
l_int32 *rmap, *gmap, *bmap, *amap;
l_uint32 *datas, *lines, *datad, *lined, *lut, *graymap;
l_uint32 sword, dword;
PIXCMAP *cmap;
PIX *pixd;
PROCNAME("pixRemoveColormap");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
if ((cmap = pixGetColormap(pixs)) == NULL)
return pixClone(pixs);
if (type != REMOVE_CMAP_TO_BINARY &&
type != REMOVE_CMAP_TO_GRAYSCALE &&
type != REMOVE_CMAP_TO_FULL_COLOR &&
type != REMOVE_CMAP_WITH_ALPHA &&
type != REMOVE_CMAP_BASED_ON_SRC) {
L_WARNING("Invalid type; converting based on src\n", procName);
type = REMOVE_CMAP_BASED_ON_SRC;
}
pixGetDimensions(pixs, &w, &h, &d);
if (d != 1 && d != 2 && d != 4 && d != 8)
return (PIX *)ERROR_PTR("pixs must be {1,2,4,8} bpp", procName, NULL);
ncolors = pixcmapGetCount(cmap);
nalloc = 1 << d; /* allocate for max size in case of pixel corruption */
if (ncolors > nalloc)
return (PIX *)ERROR_PTR("too many colors for pixel depth",
procName, NULL);
if (pixcmapToArrays(cmap, &rmap, &gmap, &bmap, &amap))
return (PIX *)ERROR_PTR("colormap arrays not made", procName, NULL);
if (d != 1 && type == REMOVE_CMAP_TO_BINARY) {
L_WARNING("not 1 bpp; can't remove cmap to binary\n", procName);
type = REMOVE_CMAP_BASED_ON_SRC;
}
/* Select output type depending on colormap content */
if (type == REMOVE_CMAP_BASED_ON_SRC) {
pixcmapIsOpaque(cmap, &opaque);
pixcmapHasColor(cmap, &colorfound);
pixcmapIsBlackAndWhite(cmap, &blackwhite);
if (!opaque) { /* save the alpha */
type = REMOVE_CMAP_WITH_ALPHA;
} else if (colorfound) {
type = REMOVE_CMAP_TO_FULL_COLOR;
} else { /* opaque and no color */
if (d == 1 && blackwhite) /* can binarize without loss */
type = REMOVE_CMAP_TO_BINARY;
else
type = REMOVE_CMAP_TO_GRAYSCALE;
}
}
datas = pixGetData(pixs);
wpls = pixGetWpl(pixs);
if (type == REMOVE_CMAP_TO_BINARY) {
if ((pixd = pixCopy(NULL, pixs)) == NULL) {
L_ERROR("pixd not made\n", procName);
goto cleanup_arrays;
}
pixcmapGetColor(cmap, 0, &rval, &gval, &bval);
val0 = rval + gval + bval;
pixcmapGetColor(cmap, 1, &rval, &gval, &bval);
val1 = rval + gval + bval;
if (val0 < val1) /* photometrically inverted from standard */
pixInvert(pixd, pixd);
pixDestroyColormap(pixd);
} else if (type == REMOVE_CMAP_TO_GRAYSCALE) {
if ((pixd = pixCreate(w, h, 8)) == NULL) {
L_ERROR("pixd not made\n", procName);
goto cleanup_arrays;
}
pixCopyResolution(pixd, pixs);
pixCopyInputFormat(pixd, pixs);
datad = pixGetData(pixd);
wpld = pixGetWpl(pixd);
graymap = (l_uint32 *)LEPT_CALLOC(nalloc, sizeof(l_uint32));
for (i = 0; i < ncolors; i++) {
graymap[i] = (l_uint32)(L_RED_WEIGHT * rmap[i] +
L_GREEN_WEIGHT * gmap[i] +
L_BLUE_WEIGHT * bmap[i] + 0.5);
}
for (i = 0; i < h; i++) {
lines = datas + i * wpls;
lined = datad + i * wpld;
switch (d) /* depth test above; no default permitted */
{
case 8:
/* Unrolled 4x */
for (j = 0, count = 0; j + 3 < w; j += 4, count++) {
sword = lines[count];
dword = (graymap[(sword >> 24) & 0xff] << 24) |
(graymap[(sword >> 16) & 0xff] << 16) |
(graymap[(sword >> 8) & 0xff] << 8) |
graymap[sword & 0xff];
lined[count] = dword;
}
/* Cleanup partial word */
for (; j < w; j++) {
sval = GET_DATA_BYTE(lines, j);
gval = graymap[sval];
SET_DATA_BYTE(lined, j, gval);
}
#if DEBUG_UNROLLING
#define CHECK_VALUE(a, b, c) if (GET_DATA_BYTE(a, b) != c) { \
fprintf(stderr, "Error: mismatch at %d, %d vs %d\n", \
j, GET_DATA_BYTE(a, b), c); }
for (j = 0; j < w; j++) {
sval = GET_DATA_BYTE(lines, j);
gval = graymap[sval];
CHECK_VALUE(lined, j, gval);
}
#endif
break;
case 4:
/* Unrolled 8x */
for (j = 0, count = 0; j + 7 < w; j += 8, count++) {
sword = lines[count];
dword = (graymap[(sword >> 28) & 0xf] << 24) |
(graymap[(sword >> 24) & 0xf] << 16) |
(graymap[(sword >> 20) & 0xf] << 8) |
graymap[(sword >> 16) & 0xf];
lined[2 * count] = dword;
dword = (graymap[(sword >> 12) & 0xf] << 24) |
(graymap[(sword >> 8) & 0xf] << 16) |
(graymap[(sword >> 4) & 0xf] << 8) |
graymap[sword & 0xf];
lined[2 * count + 1] = dword;
}
/* Cleanup partial word */
for (; j < w; j++) {
sval = GET_DATA_QBIT(lines, j);
gval = graymap[sval];
SET_DATA_BYTE(lined, j, gval);
}
#if DEBUG_UNROLLING
for (j = 0; j < w; j++) {
sval = GET_DATA_QBIT(lines, j);
gval = graymap[sval];
CHECK_VALUE(lined, j, gval);
}
#endif
break;
case 2:
/* Unrolled 16x */
for (j = 0, count = 0; j + 15 < w; j += 16, count++) {
sword = lines[count];
dword = (graymap[(sword >> 30) & 0x3] << 24) |
(graymap[(sword >> 28) & 0x3] << 16) |
(graymap[(sword >> 26) & 0x3] << 8) |
graymap[(sword >> 24) & 0x3];
lined[4 * count] = dword;
dword = (graymap[(sword >> 22) & 0x3] << 24) |
(graymap[(sword >> 20) & 0x3] << 16) |
(graymap[(sword >> 18) & 0x3] << 8) |
graymap[(sword >> 16) & 0x3];
lined[4 * count + 1] = dword;
dword = (graymap[(sword >> 14) & 0x3] << 24) |
(graymap[(sword >> 12) & 0x3] << 16) |
(graymap[(sword >> 10) & 0x3] << 8) |
graymap[(sword >> 8) & 0x3];
lined[4 * count + 2] = dword;
dword = (graymap[(sword >> 6) & 0x3] << 24) |
(graymap[(sword >> 4) & 0x3] << 16) |
(graymap[(sword >> 2) & 0x3] << 8) |
graymap[sword & 0x3];
lined[4 * count + 3] = dword;
}
/* Cleanup partial word */
for (; j < w; j++) {
sval = GET_DATA_DIBIT(lines, j);
gval = graymap[sval];
SET_DATA_BYTE(lined, j, gval);
}
#if DEBUG_UNROLLING
for (j = 0; j < w; j++) {
sval = GET_DATA_DIBIT(lines, j);
gval = graymap[sval];
CHECK_VALUE(lined, j, gval);
}
#endif
break;
case 1:
/* Unrolled 8x */
for (j = 0, count = 0; j + 31 < w; j += 32, count++) {
sword = lines[count];
for (k = 0; k < 4; k++) {
/* The top byte is always the relevant one */
dword = (graymap[(sword >> 31) & 0x1] << 24) |
(graymap[(sword >> 30) & 0x1] << 16) |
(graymap[(sword >> 29) & 0x1] << 8) |
graymap[(sword >> 28) & 0x1];
lined[8 * count + 2 * k] = dword;
dword = (graymap[(sword >> 27) & 0x1] << 24) |
(graymap[(sword >> 26) & 0x1] << 16) |
(graymap[(sword >> 25) & 0x1] << 8) |
graymap[(sword >> 24) & 0x1];
lined[8 * count + 2 * k + 1] = dword;
sword <<= 8; /* Move up the next byte */
}
}
/* Cleanup partial word */
for (; j < w; j++) {
sval = GET_DATA_BIT(lines, j);
gval = graymap[sval];
SET_DATA_BYTE(lined, j, gval);
}
#if DEBUG_UNROLLING
for (j = 0; j < w; j++) {
sval = GET_DATA_BIT(lines, j);
gval = graymap[sval];
CHECK_VALUE(lined, j, gval);
}
#undef CHECK_VALUE
#endif
break;
default:
return NULL;
}
}
if (graymap)
LEPT_FREE(graymap);
} else { /* type == REMOVE_CMAP_TO_FULL_COLOR or REMOVE_CMAP_WITH_ALPHA */
if ((pixd = pixCreate(w, h, 32)) == NULL) {
L_ERROR("pixd not made\n", procName);
goto cleanup_arrays;
}
pixCopyInputFormat(pixd, pixs);
pixCopyResolution(pixd, pixs);
if (type == REMOVE_CMAP_WITH_ALPHA)
pixSetSpp(pixd, 4);
datad = pixGetData(pixd);
wpld = pixGetWpl(pixd);
lut = (l_uint32 *)LEPT_CALLOC(nalloc, sizeof(l_uint32));
for (i = 0; i < ncolors; i++) {
if (type == REMOVE_CMAP_TO_FULL_COLOR)
composeRGBPixel(rmap[i], gmap[i], bmap[i], lut + i);
else /* full color plus alpha */
composeRGBAPixel(rmap[i], gmap[i], bmap[i], amap[i], lut + i);
}
for (i = 0; i < h; i++) {
lines = datas + i * wpls;
lined = datad + i * wpld;
for (j = 0; j < w; j++) {
if (d == 8)
sval = GET_DATA_BYTE(lines, j);
else if (d == 4)
sval = GET_DATA_QBIT(lines, j);
else if (d == 2)
sval = GET_DATA_DIBIT(lines, j);
else /* (d == 1) */
sval = GET_DATA_BIT(lines, j);
if (sval >= ncolors)
L_WARNING("pixel value out of bounds\n", procName);
else
lined[j] = lut[sval];
}
}
LEPT_FREE(lut);
}
cleanup_arrays:
LEPT_FREE(rmap);
LEPT_FREE(gmap);
LEPT_FREE(bmap);
LEPT_FREE(amap);
return pixd;
}
/*-------------------------------------------------------------*
* Add colormap losslessly (8 to 8) *
*-------------------------------------------------------------*/
/*!
* \brief pixAddGrayColormap8()
*
* \param[in] pixs 8 bpp
* \return 0 if OK, 1 on error
*
* <pre>
* Notes:
* (1) If pixs has a colormap, this is a no-op.
* </pre>
*/
l_ok
pixAddGrayColormap8(PIX *pixs)
{
PIXCMAP *cmap;
PROCNAME("pixAddGrayColormap8");
if (!pixs || pixGetDepth(pixs) != 8)
return ERROR_INT("pixs not defined or not 8 bpp", procName, 1);
if (pixGetColormap(pixs))
return 0;
cmap = pixcmapCreateLinear(8, 256);
pixSetColormap(pixs, cmap);
return 0;
}
/*!
* \brief pixAddMinimalGrayColormap8()
*
* \param[in] pixs 8 bpp
* \return 0 if OK, 1 on error
*
* <pre>
* Notes:
* (1) This generates a colormapped version of the input image
* that has the same number of colormap entries as the
* input image has unique gray levels.
* </pre>
*/
PIX *
pixAddMinimalGrayColormap8(PIX *pixs)
{
l_int32 ncolors, w, h, i, j, wpl1, wpld, index, val;
l_int32 *inta, *revmap;
l_uint32 *data1, *datad, *line1, *lined;
PIX *pix1, *pixd;
PIXCMAP *cmap;
PROCNAME("pixAddMinimalGrayColormap8");
if (!pixs || pixGetDepth(pixs) != 8)
return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL);
/* Eliminate the easy cases */
pixNumColors(pixs, 1, &ncolors);
cmap = pixGetColormap(pixs);
if (cmap) {
if (pixcmapGetCount(cmap) == ncolors) /* irreducible */
return pixCopy(NULL, pixs);
else
pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE);
} else {
if (ncolors == 256) {
pix1 = pixCopy(NULL, pixs);
pixAddGrayColormap8(pix1);
return pix1;
}
pix1 = pixClone(pixs);
}
/* Find the gray levels and make a reverse map */
pixGetDimensions(pix1, &w, &h, NULL);
data1 = pixGetData(pix1);
wpl1 = pixGetWpl(pix1);
inta = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32));
for (i = 0; i < h; i++) {
line1 = data1 + i * wpl1;
for (j = 0; j < w; j++) {
val = GET_DATA_BYTE(line1, j);
inta[val] = 1;
}
}
cmap = pixcmapCreate(8);
revmap = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32));
for (i = 0, index = 0; i < 256; i++) {
if (inta[i]) {
pixcmapAddColor(cmap, i, i, i);
revmap[i] = index++;
}
}
/* Set all pixels in pixd to the colormap index */
pixd = pixCreateTemplate(pix1);
pixSetColormap(pixd, cmap);
pixCopyInputFormat(pixd, pixs);
pixCopyResolution(pixd, pixs);
datad = pixGetData(pixd);
wpld = pixGetWpl(pixd);
for (i = 0; i < h; i++) {
line1 = data1 + i * wpl1;
lined = datad + i * wpld;
for (j = 0; j < w; j++) {
val = GET_DATA_BYTE(line1, j);
SET_DATA_BYTE(lined, j, revmap[val]);
}
}
pixDestroy(&pix1);
LEPT_FREE(inta);
LEPT_FREE(revmap);
return pixd;
}
/*-------------------------------------------------------------*
* Conversion from RGB color to grayscale *
*-------------------------------------------------------------*/
/*!
* \brief pixConvertRGBToLuminance()
*
* \param[in] pixs 32 bpp RGB
* \return 8 bpp pix, or NULL on error
*
* <pre>
* Notes:
* (1) Use a standard luminance conversion.
* </pre>
*/
PIX *
pixConvertRGBToLuminance(PIX *pixs)
{
return pixConvertRGBToGray(pixs, 0.0, 0.0, 0.0);
}
/*!
* \brief pixConvertRGBToGray()
*
* \param[in] pixs 32 bpp RGB
* \param[in] rwt, gwt, bwt non-negative; these should add to 1.0,
* or use 0.0 for default
* \return 8 bpp pix, or NULL on error
*
* <pre>
* Notes:
* (1) Use a weighted average of the RGB values.
* </pre>
*/
PIX *
pixConvertRGBToGray(PIX *pixs,
l_float32 rwt,
l_float32 gwt,
l_float32 bwt)
{
l_int32 i, j, w, h, wpls, wpld, val;
l_uint32 word;
l_uint32 *datas, *lines, *datad, *lined;
l_float32 sum;
PIX *pixd;
PROCNAME("pixConvertRGBToGray");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
if (pixGetDepth(pixs) != 32)
return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL);
if (rwt < 0.0 || gwt < 0.0 || bwt < 0.0)
return (PIX *)ERROR_PTR("weights not all >= 0.0", procName, NULL);
/* Make sure the sum of weights is 1.0; otherwise, you can get
* overflow in the gray value. */
if (rwt == 0.0 && gwt == 0.0 && bwt == 0.0) {
rwt = L_RED_WEIGHT;
gwt = L_GREEN_WEIGHT;
bwt = L_BLUE_WEIGHT;
}
sum = rwt + gwt + bwt;
if (L_ABS(sum - 1.0) > 0.0001) { /* maintain ratios with sum == 1.0 */
L_WARNING("weights don't sum to 1; maintaining ratios\n", procName);
rwt = rwt / sum;
gwt = gwt / sum;
bwt = bwt / sum;
}
pixGetDimensions(pixs, &w, &h, NULL);
datas = pixGetData(pixs);
wpls = pixGetWpl(pixs);
if ((pixd = pixCreate(w, h, 8)) == NULL)
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
pixCopyResolution(pixd, pixs);
pixCopyInputFormat(pixd, pixs);
datad = pixGetData(pixd);
wpld = pixGetWpl(pixd);
for (i = 0; i < h; i++) {
lines = datas + i * wpls;
lined = datad + i * wpld;
for (j = 0; j < w; j++) {
word = *(lines + j);
val = (l_int32)(rwt * ((word >> L_RED_SHIFT) & 0xff) +
gwt * ((word >> L_GREEN_SHIFT) & 0xff) +
bwt * ((word >> L_BLUE_SHIFT) & 0xff) + 0.5);
SET_DATA_BYTE(lined, j, val);
}
}
return pixd;
}
/*!
* \brief pixConvertRGBToGrayFast()
*
* \param[in] pixs 32 bpp RGB
* \return 8 bpp pix, or NULL on error
*
* <pre>
* Notes:
* (1) This function should be used if speed of conversion
* is paramount, and the green channel can be used as
* a fair representative of the RGB intensity. It is
* several times faster than pixConvertRGBToGray().
* (2) To combine RGB to gray conversion with subsampling,
* use pixScaleRGBToGrayFast() instead.
* </pre>
*/
PIX *
pixConvertRGBToGrayFast(PIX *pixs)
{
l_int32 i, j, w, h, wpls, wpld, val;
l_uint32 *datas, *lines, *datad, *lined;
PIX *pixd;
PROCNAME("pixConvertRGBToGrayFast");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
if (pixGetDepth(pixs) != 32)
return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL);
pixGetDimensions(pixs, &w, &h, NULL);
datas = pixGetData(pixs);
wpls = pixGetWpl(pixs);
if ((pixd = pixCreate(w, h, 8)) == NULL)
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
pixCopyResolution(pixd, pixs);
pixCopyInputFormat(pixd, pixs);
datad = pixGetData(pixd);
wpld = pixGetWpl(pixd);
for (i = 0; i < h; i++) {
lines = datas + i * wpls;
lined = datad + i * wpld;
for (j = 0; j < w; j++, lines++) {
val = ((*lines) >> L_GREEN_SHIFT) & 0xff;
SET_DATA_BYTE(lined, j, val);
}
}
return pixd;
}
/*!
* \brief pixConvertRGBToGrayMinMax()
*
* \param[in] pixs 32 bpp RGB
* \param[in] type L_CHOOSE_MIN, L_CHOOSE_MAX, L_CHOOSE_MAXDIFF,
* L_CHOOSE_MIN_BOOST, L_CHOOSE_MAX_BOOST
* \return 8 bpp pix, or NULL on error
*
* <pre>
* Notes:
* (1) This chooses various components or combinations of them,
* from the three RGB sample values. In addition to choosing
* the min, max, and maxdiff (difference between max and min),
* this also allows boosting the min and max about a reference
* value.
* (2) The default reference value for boosting the min and max
* is 200. This can be changed with l_setNeutralBoostVal()
* </pre>
*/
PIX *
pixConvertRGBToGrayMinMax(PIX *pixs,
l_int32 type)
{
l_int32 i, j, w, h, wpls, wpld, rval, gval, bval, val, minval, maxval;
l_uint32 *datas, *lines, *datad, *lined;
PIX *pixd;
PROCNAME("pixConvertRGBToGrayMinMax");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
if (pixGetDepth(pixs) != 32)
return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL);
if (type != L_CHOOSE_MIN && type != L_CHOOSE_MAX &&
type != L_CHOOSE_MAXDIFF && type != L_CHOOSE_MIN_BOOST &&
type != L_CHOOSE_MAX_BOOST)
return (PIX *)ERROR_PTR("invalid type", procName, NULL);
pixGetDimensions(pixs, &w, &h, NULL);
datas = pixGetData(pixs);
wpls = pixGetWpl(pixs);
if ((pixd = pixCreate(w, h, 8)) == NULL)
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
pixCopyResolution(pixd, pixs);
pixCopyInputFormat(pixd, pixs);
datad = pixGetData(pixd);
wpld = pixGetWpl(pixd);
for (i = 0; i < h; i++) {
lines = datas + i * wpls;
lined = datad + i * wpld;
for (j = 0; j < w; j++) {
extractRGBValues(lines[j], &rval, &gval, &bval);
if (type == L_CHOOSE_MIN || type == L_CHOOSE_MIN_BOOST) {
val = L_MIN(rval, gval);
val = L_MIN(val, bval);
if (type == L_CHOOSE_MIN_BOOST)
val = L_MIN(255, (val * val) / var_NEUTRAL_BOOST_VAL);
} else if (type == L_CHOOSE_MAX || type == L_CHOOSE_MAX_BOOST) {
val = L_MAX(rval, gval);
val = L_MAX(val, bval);
if (type == L_CHOOSE_MAX_BOOST)
val = L_MIN(255, (val * val) / var_NEUTRAL_BOOST_VAL);
} else { /* L_CHOOSE_MAXDIFF */
minval = L_MIN(rval, gval);
minval = L_MIN(minval, bval);
maxval = L_MAX(rval, gval);
maxval = L_MAX(maxval, bval);
val = maxval - minval;
}
SET_DATA_BYTE(lined, j, val);
}
}
return pixd;
}
/*!
* \brief pixConvertRGBToGraySatBoost()
*
* \param[in] pixs 32 bpp rgb
* \param[in] refval between 1 and 255; typ. less than 128
* \return pixd 8 bpp, or NULL on error
*
* <pre>
* Notes:
* (1) This returns the max component value, boosted by
* the saturation. The maximum boost occurs where
* the maximum component value is equal to some reference value.
* This particular weighting is due to Dany Qumsiyeh.
* (2) For gray pixels (zero saturation), this returns
* the intensity of any component.
* (3) For fully saturated pixels ('fullsat'), this rises linearly
* with the max value and has a slope equal to 255 divided
* by the reference value; for a max value greater than
* the reference value, it is clipped to 255.
* (4) For saturation values in between, the output is a linear
* combination of (2) and (3), weighted by saturation.
* It falls between these two curves, and does not exceed 255.
* (5) This can be useful for distinguishing an object that has nonzero
* saturation from a gray background. For this, the refval
* should be chosen near the expected value of the background,
* to achieve maximum saturation boost there.
* </pre>
*/
PIX *
pixConvertRGBToGraySatBoost(PIX *pixs,
l_int32 refval)
{
l_int32 w, h, d, i, j, wplt, wpld;
l_int32 rval, gval, bval, sval, minrg, maxrg, min, max, delta;
l_int32 fullsat, newval;
l_float32 *invmax, *ratio;
l_uint32 *linet, *lined, *datat, *datad;
PIX *pixt, *pixd;
PROCNAME("pixConvertRGBToGraySatBoost");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
pixGetDimensions(pixs, &w, &h, &d);
if (d != 32 && !pixGetColormap(pixs))
return (PIX *)ERROR_PTR("pixs not cmapped or rgb", procName, NULL);
if (refval < 1 || refval > 255)
return (PIX *)ERROR_PTR("refval not in [1 ... 255]", procName, NULL);
pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR);
pixd = pixCreate(w, h, 8);
pixCopyResolution(pixd, pixs);
pixCopyInputFormat(pixd, pixs);
wplt = pixGetWpl(pixt);
datat = pixGetData(pixt);
wpld = pixGetWpl(pixd);
datad = pixGetData(pixd);
invmax = (l_float32 *)LEPT_CALLOC(256, sizeof(l_float32));
ratio = (l_float32 *)LEPT_CALLOC(256, sizeof(l_float32));
for (i = 1; i < 256; i++) { /* i == 0 --> delta = sval = newval = 0 */
invmax[i] = 1.0 / (l_float32)i;
ratio[i] = (l_float32)i / (l_float32)refval;
}
for (i = 0; i < h; i++) {
linet = datat + i * wplt;
lined = datad + i * wpld;
for (j = 0; j < w; j++) {
extractRGBValues(linet[j], &rval, &gval, &bval);
minrg = L_MIN(rval, gval);
min = L_MIN(minrg, bval);
maxrg = L_MAX(rval, gval);
max = L_MAX(maxrg, bval);
delta = max - min;
if (delta == 0) /* gray; no chroma */
sval = 0;
else
sval = (l_int32)(255. * (l_float32)delta * invmax[max] + 0.5);
fullsat = L_MIN(255, 255 * ratio[max]);
newval = (sval * fullsat + (255 - sval) * max) / 255;
SET_DATA_BYTE(lined, j, newval);
}
}
pixDestroy(&pixt);
LEPT_FREE(invmax);
LEPT_FREE(ratio);
return pixd;
}
/*!
* \brief pixConvertRGBToGrayArb()
*
* \param[in] pixs 32 bpp RGB
* \param[in] rc, gc, bc arithmetic factors; can be negative
* \return 8 bpp pix, or NULL on error
*
* <pre>
* Notes:
* (1) This converts to gray using an arbitrary linear combination
* of the rgb color components. It differs from pixConvertToGray(),
* which uses only positive coefficients that sum to 1.
* (2) The gray output values are clipped to 0 and 255.
* </pre>
*/
PIX *
pixConvertRGBToGrayArb(PIX *pixs,
l_float32 rc,
l_float32 gc,
l_float32 bc)
{
l_int32 i, j, w, h, wpls, wpld, rval, gval, bval, val;
l_uint32 *datas, *lines, *datad, *lined;
PIX *pixd;
PROCNAME("pixConvertRGBToGrayArb");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
if (pixGetDepth(pixs) != 32)
return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL);
if (rc <= 0 && gc <= 0 && bc <= 0)
return (PIX *)ERROR_PTR("all coefficients <= 0", procName, NULL);
pixGetDimensions(pixs, &w, &h, NULL);
datas = pixGetData(pixs);
wpls = pixGetWpl(pixs);
if ((pixd = pixCreate(w, h, 8)) == NULL)
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
pixCopyResolution(pixd, pixs);
pixCopyInputFormat(pixd, pixs);
datad = pixGetData(pixd);
wpld = pixGetWpl(pixd);
for (i = 0; i < h; i++) {
lines = datas + i * wpls;
lined = datad + i * wpld;
for (j = 0; j < w; j++) {
extractRGBValues(lines[j], &rval, &gval, &bval);
val = (l_int32)(rc * rval + gc * gval + bc * bval);
val = L_MIN(255, L_MAX(0, val));
SET_DATA_BYTE(lined, j, val);
}
}
return pixd;
}
/*!
* \brief pixConvertRGBToBinaryArb()
*
* \param[in] pixs 32 bpp RGB
* \param[in] rc, gc, bc arithmetic factors; can be negative
* \param[in] thresh binarization threshold
* \param[in] relation L_SELECT_IF_LT, L_SELECT_IF_GT
* L_SELECT_IF_LTE, L_SELECT_IF_GTE
* \return 1 bpp pix, or NULL on error
*
* <pre>
* Notes:
* (1) This makes a 1 bpp mask from an RGB image, using an arbitrary
* linear combination of the rgb color components, along with
* a threshold and a selection choice of the gray value relative
* to %thresh.
* </pre>
*/
PIX *
pixConvertRGBToBinaryArb(PIX *pixs,
l_float32 rc,
l_float32 gc,
l_float32 bc,
l_int32 thresh,
l_int32 relation)
{
l_int32 threshold;
PIX *pix1, *pix2;
PROCNAME("pixConvertRGBToBinaryArb");
if (!pixs || pixGetDepth(pixs) != 32)
return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL);
if (rc <= 0 && gc <= 0 && bc <= 0)
return (PIX *)ERROR_PTR("all coefficients <= 0", procName, NULL);
if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT &&
relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE)
return (PIX *)ERROR_PTR("invalid relation", procName, NULL);
pix1 = pixConvertRGBToGrayArb(pixs, rc, gc, bc);
threshold = (relation == L_SELECT_IF_LTE || relation == L_SELECT_IF_GT) ?
thresh : thresh + 1;
pix2 = pixThresholdToBinary(pix1, threshold);
if (relation == L_SELECT_IF_GT || relation == L_SELECT_IF_GTE)
pixInvert(pix2, pix2);
pixDestroy(&pix1);
return pix2;
}
/*---------------------------------------------------------------------------*
* Conversion from grayscale to colormap *
*---------------------------------------------------------------------------*/
/*!
* \brief pixConvertGrayToColormap()
*
* \param[in] pixs 2, 4 or 8 bpp grayscale
* \return pixd 2, 4 or 8 bpp with colormap, or NULL on error
*
* <pre>
* Notes:
* (1) This is a simple interface for adding a colormap to a
* 2, 4 or 8 bpp grayscale image without causing any
* quantization. There is some similarity to operations
* in grayquant.c, such as pixThresholdOn8bpp(), where
* the emphasis is on quantization with an arbitrary number
* of levels, and a colormap is an option.
* (2) Returns a copy if pixs already has a colormap.
* (3) For 8 bpp src, this is a lossless transformation.
* (4) For 2 and 4 bpp src, this generates a colormap that
* assumes full coverage of the gray space, with equally spaced
* levels: 4 levels for d = 2 and 16 levels for d = 4.
* (5) In all cases, the depth of the dest is the same as the src.
* </pre>
*/
PIX *
pixConvertGrayToColormap(PIX *pixs)
{
l_int32 d;
PIX *pixd;
PIXCMAP *cmap;
PROCNAME("pixConvertGrayToColormap");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
d = pixGetDepth(pixs);
if (d != 2 && d != 4 && d != 8)
return (PIX *)ERROR_PTR("pixs not 2, 4 or 8 bpp", procName, NULL);
if (pixGetColormap(pixs)) {
L_INFO("pixs already has a colormap\n", procName);
return pixCopy(NULL, pixs);
}
if (d == 8) /* lossless conversion */
return pixConvertGrayToColormap8(pixs, 2);
/* Build a cmap with equally spaced target values over the
* full 8 bpp range. */
pixd = pixCopy(NULL, pixs);
cmap = pixcmapCreateLinear(d, 1 << d);
pixSetColormap(pixd, cmap);
pixCopyInputFormat(pixd, pixs);
return pixd;
}
/*!
* \brief pixConvertGrayToColormap8()
*
* \param[in] pixs 8 bpp grayscale
* \param[in] mindepth of pixd; valid values are 2, 4 and 8
* \return pixd 2, 4 or 8 bpp with colormap, or NULL on error
*
* <pre>
* Notes:
* (1) Returns a copy if pixs already has a colormap.
* (2) This is a lossless transformation; there is no quantization.
* We compute the number of different gray values in pixs,
* and construct a colormap that has exactly these values.
* (3) 'mindepth' is the minimum depth of pixd. If mindepth == 8,
* pixd will always be 8 bpp. Let the number of different
* gray values in pixs be ngray. If mindepth == 4, we attempt
* to save pixd as a 4 bpp image, but if ngray > 16,
* pixd must be 8 bpp. Likewise, if mindepth == 2,
* the depth of pixd will be 2 if ngray <= 4 and 4 if ngray > 4
* but <= 16.
* </pre>
*/
PIX *
pixConvertGrayToColormap8(PIX *pixs,
l_int32 mindepth)
{
l_int32 ncolors, w, h, depth, i, j, wpls, wpld;
l_int32 index, num, val, newval;
l_int32 array[256];
l_uint32 *lines, *lined, *datas, *datad;
NUMA *na;
PIX *pixd;
PIXCMAP *cmap;
PROCNAME("pixConvertGrayToColormap8");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
if (pixGetDepth(pixs) != 8)
return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL);
if (mindepth != 2 && mindepth != 4 && mindepth != 8) {
L_WARNING("invalid value of mindepth; setting to 8\n", procName);
mindepth = 8;
}
if (pixGetColormap(pixs)) {
L_INFO("pixs already has a colormap\n", procName);
return pixCopy(NULL, pixs);
}
na = pixGetGrayHistogram(pixs, 1);
numaGetCountRelativeToZero(na, L_GREATER_THAN_ZERO, &ncolors);
if (mindepth == 8 || ncolors > 16)
depth = 8;
else if (mindepth == 4 || ncolors > 4)
depth = 4;
else
depth = 2;
pixGetDimensions(pixs, &w, &h, NULL);
pixd = pixCreate(w, h, depth);
cmap = pixcmapCreate(depth);
pixSetColormap(pixd, cmap);
pixCopyInputFormat(pixd, pixs);
pixCopyResolution(pixd, pixs);
index = 0;
for (i = 0; i < 256; i++) {
array[i] = 0; /* only to quiet the static checker */
numaGetIValue(na, i, &num);
if (num > 0) {
pixcmapAddColor(cmap, i, i, i);
array[i] = index;
index++;
}
}
datas = pixGetData(pixs);
wpls = pixGetWpl(pixs);
datad = pixGetData(pixd);
wpld = pixGetWpl(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);
newval = array[val];
if (depth == 2)
SET_DATA_DIBIT(lined, j, newval);
else if (depth == 4)
SET_DATA_QBIT(lined, j, newval);
else /* depth == 8 */
SET_DATA_BYTE(lined, j, newval);
}
}
numaDestroy(&na);
return pixd;
}
/*---------------------------------------------------------------------------*
* Colorizing conversion from grayscale to color *
*---------------------------------------------------------------------------*/
/*!
* \brief pixColorizeGray()
*
* \param[in] pixs 8 bpp gray; 2, 4 or 8 bpp colormapped
* \param[in] color 32 bit rgba pixel
* \param[in] cmapflag 1 for result to have colormap; 0 for RGB
* \return pixd 8 bpp colormapped or 32 bpp rgb, or NULL on error
*
* <pre>
* Notes:
* (1) This applies the specific color to the grayscale image.
* (2) If pixs already has a colormap, it is removed to gray
* before colorizing.
* </pre>
*/
PIX *
pixColorizeGray(PIX *pixs,
l_uint32 color,
l_int32 cmapflag)
{
l_int32 i, j, w, h, wplt, wpld, val8;
l_uint32 *datad, *datat, *lined, *linet, *tab;
PIX *pixt, *pixd;
PIXCMAP *cmap;
PROCNAME("pixColorizeGray");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
if (pixGetDepth(pixs) != 8 && !pixGetColormap(pixs))
return (PIX *)ERROR_PTR("pixs not 8 bpp or cmapped", procName, NULL);
if (pixGetColormap(pixs))
pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE);
else
pixt = pixClone(pixs);
cmap = pixcmapGrayToColor(color);
if (cmapflag) {
pixd = pixCopy(NULL, pixt);
pixSetColormap(pixd, cmap);
pixDestroy(&pixt);
return pixd;
}
/* Make an RGB pix */
pixcmapToRGBTable(cmap, &tab, NULL);
pixGetDimensions(pixt, &w, &h, NULL);
pixd = pixCreate(w, h, 32);
pixCopyResolution(pixd, pixs);
pixCopyInputFormat(pixd, pixs);
datad = pixGetData(pixd);
wpld = pixGetWpl(pixd);
datat = pixGetData(pixt);
wplt = pixGetWpl(pixt);
for (i = 0; i < h; i++) {
lined = datad + i * wpld;
linet = datat + i * wplt;
for (j = 0; j < w; j++) {
val8 = GET_DATA_BYTE(linet, j);
lined[j] = tab[val8];
}
}
pixDestroy(&pixt);
pixcmapDestroy(&cmap);
LEPT_FREE(tab);
return pixd;
}
/*---------------------------------------------------------------------------*
* Conversion from RGB color to colormap *
*---------------------------------------------------------------------------*/
/*!
* \brief pixConvertRGBToColormap()
*
* \param[in] pixs 32 bpp rgb
* \param[in] ditherflag 1 to dither, 0 otherwise
* \return pixd 2, 4 or 8 bpp with colormap, or NULL on error
*
* <pre>
* Notes:
* (1) This function has two relatively simple modes of color
* quantization:
* (a) If the image is made orthographically and has not more
* than 256 'colors' at the level 4 octcube leaves,
* it is quantized nearly exactly. The ditherflag
* is ignored.
* (b) Most natural images have more than 256 different colors;
* in that case we use adaptive octree quantization,
* with dithering if requested.
* (2) If there are not more than 256 occupied level 4 octcubes,
* the color in the colormap that represents all pixels in
* one of those octcubes is given by the first pixel that
* falls into that octcube.
* (3) If there are more than 256 colors, we use adaptive octree
* color quantization.
* (4) Dithering gives better visual results on images where
* there is a color wash (a slow variation of color), but it
* is about twice as slow and results in significantly larger
* files when losslessly compressed (e.g., into png).
* </pre>
*/
PIX *
pixConvertRGBToColormap(PIX *pixs,
l_int32 ditherflag)
{
l_int32 ncolors;
NUMA *na;
PIX *pixd;
PROCNAME("pixConvertRGBToColormap");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
if (pixGetDepth(pixs) != 32)
return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL);
if (pixGetSpp(pixs) == 4)
L_WARNING("pixs has alpha; removing\n", procName);
/* Get the histogram and count the number of occupied level 4
* leaf octcubes. We don't yet know if this is the number of
* actual colors, but if it's not, all pixels falling into
* the same leaf octcube will be assigned to the color of the
* first pixel that lands there. */
na = pixOctcubeHistogram(pixs, 4, &ncolors);
/* If there are too many occupied leaf octcubes to be
* represented directly in a colormap, fall back to octree
* quantization, optionally with dithering. */
if (ncolors > 256) {
numaDestroy(&na);
if (ditherflag)
L_INFO("More than 256 colors; using octree quant with dithering\n",
procName);
else
L_INFO("More than 256 colors; using octree quant; no dithering\n",
procName);
return pixOctreeColorQuant(pixs, 240, ditherflag);
}
/* There are not more than 256 occupied leaf octcubes.
* Quantize to those octcubes. */
pixd = pixFewColorsOctcubeQuant2(pixs, 4, na, ncolors, NULL);
pixCopyInputFormat(pixd, pixs);
numaDestroy(&na);
return pixd;
}
/*---------------------------------------------------------------------------*
* Conversion from colormap to 1 bpp *
*---------------------------------------------------------------------------*/
/*!
* \brief pixConvertCmapTo1()
*
* \param[in] pixs cmapped
* \return pixd 1 bpp, or NULL on error
*
* <pre>
* Notes:
* (1) This is an extreme color quantizer. It decides which
* colors map to FG (black) and which to BG (white).
* (2) This uses two heuristics to make the decision:
* (a) colors similar to each other are likely to be in the same class
* (b) there is usually much less FG than BG.
* </pre>
*/
PIX *
pixConvertCmapTo1(PIX *pixs)
{
l_int32 i, j, nc, w, h, imin, imax, factor, wpl1, wpld;
l_int32 index, rmin, gmin, bmin, rmax, gmax, bmax, dmin, dmax;
l_float32 minfract, ifract;
l_int32 *lut;
l_uint32 *line1, *lined, *data1, *datad;
NUMA *na1, *na2; /* histograms */
PIX *pix1, *pixd;
PIXCMAP *cmap;
PROCNAME("pixConvertCmapTo1");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
if ((cmap = pixGetColormap(pixs)) == NULL)
return (PIX *)ERROR_PTR("no colormap", procName, NULL);
/* Select target colors for the two classes. Find the
* colors with smallest and largest average component values.
* The smallest is class 0 and the largest is class 1. */
pixcmapGetRangeValues(cmap, L_SELECT_AVERAGE, NULL, NULL, &imin, &imax);
pixcmapGetColor(cmap, imin, &rmin, &gmin, &bmin);
pixcmapGetColor(cmap, imax, &rmax, &gmax, &bmax);
nc = pixcmapGetCount(cmap);
/* Assign colors to the two classes. The histogram is
* initialized to 0, so any colors not found when computing
* the sampled histogram will get zero weight in minfract. */
if ((lut = (l_int32 *)LEPT_CALLOC(nc, sizeof(l_int32))) == NULL)
return (PIX *)ERROR_PTR("calloc fail for lut", procName, NULL);
pixGetDimensions(pixs, &w, &h, NULL);
factor = L_MAX(1, (l_int32)sqrt((l_float64)(w * h) / 50000. + 0.5));
na1 = pixGetCmapHistogram(pixs, factor);
na2 = numaNormalizeHistogram(na1, 1.0);
minfract = 0.0;
for (i = 0; i < nc; i++) {
numaGetFValue(na2, i, &ifract);
pixcmapGetDistanceToColor(cmap, i, rmin, gmin, bmin, &dmin);
pixcmapGetDistanceToColor(cmap, i, rmax, gmax, bmax, &dmax);
if (dmin < dmax) { /* closer to dark extreme value */
lut[i] = 1; /* black pixel in 1 bpp image */
minfract += ifract;
}
}
numaDestroy(&na1);
numaDestroy(&na2);
/* Generate the output binarized image */
pix1 = pixConvertTo8(pixs, 1);
pixd = pixCreate(w, h, 1);
data1 = pixGetData(pix1);
datad = pixGetData(pixd);
wpl1 = pixGetWpl(pix1);
wpld = pixGetWpl(pixd);
for (i = 0; i < h; i++) {
line1 = data1 + i * wpl1;
lined = datad + i * wpld;
for (j = 0; j < w; j++) {
index = GET_DATA_BYTE(line1, j);
if (lut[index] == 1) SET_DATA_BIT(lined, j);
}
}
pixDestroy(&pix1);
LEPT_FREE(lut);
/* We expect minfract (the dark colors) to be less than 0.5.
* If that is not the case, invert pixd. */
if (minfract > 0.5) {
L_INFO("minfract = %5.3f; inverting\n", procName, minfract);
pixInvert(pixd, pixd);
}
return pixd;
}
/*---------------------------------------------------------------------------*
* Quantization for relatively small number of colors in source *
*---------------------------------------------------------------------------*/
/*!
* \brief pixQuantizeIfFewColors()
*
* \param[in] pixs 8 bpp gray or 32 bpp rgb
* \param[in] maxcolors max number of colors allowed to be returned
* from pixColorsForQuantization();
* use 0 for default
* \param[in] mingraycolors min number of gray levels that a grayscale
* image is quantized to; use 0 for default
* \param[in] octlevel for octcube quantization: 3 or 4
* \param[out] ppixd 2,4 or 8 bpp quantized; null if too many colors
* \return 0 if OK, 1 on error or if pixs can't be quantized into
* a small number of colors.
*
* <pre>
* Notes:
* (1) This is a wrapper that tests if the pix can be quantized
* with good quality using a small number of colors. If so,
* it does the quantization, defining a colormap and using
* pixels whose value is an index into the colormap.
* (2) If the image has color, it is quantized with 8 bpp pixels.
* If the image is essentially grayscale, the pixels are
* either 4 or 8 bpp, depending on the size of the required
* colormap.
* (3) %octlevel = 4 generates a larger colormap and larger
* compressed image than %octlevel = 3. If image quality is
* important, you should use %octlevel = 4.
* (4) If the image already has a colormap, it returns a clone.
* </pre>
*/
l_ok
pixQuantizeIfFewColors(PIX *pixs,
l_int32 maxcolors,
l_int32 mingraycolors,
l_int32 octlevel,
PIX **ppixd)
{
l_int32 d, ncolors, iscolor, graycolors;
PIX *pixg, *pixd;
PROCNAME("pixQuantizeIfFewColors");
if (!ppixd)
return ERROR_INT("&pixd not defined", procName, 1);
*ppixd = NULL;
if (!pixs)
return ERROR_INT("pixs not defined", procName, 1);
d = pixGetDepth(pixs);
if (d != 8 && d != 32)
return ERROR_INT("pixs not defined", procName, 1);
if (pixGetColormap(pixs) != NULL) {
*ppixd = pixClone(pixs);
return 0;
}
if (maxcolors <= 0)
maxcolors = 15; /* default */
if (maxcolors > 50)
L_WARNING("maxcolors > 50; very large!\n", procName);
if (mingraycolors <= 0)
mingraycolors = 10; /* default */
if (mingraycolors > 30)
L_WARNING("mingraycolors > 30; very large!\n", procName);
if (octlevel != 3 && octlevel != 4) {
L_WARNING("invalid octlevel; setting to 3\n", procName);
octlevel = 3;
}
/* Test the number of colors. For color, the octcube leaves
* are at level 4. */
pixColorsForQuantization(pixs, 0, &ncolors, &iscolor, 0);
if (ncolors > maxcolors)
return ERROR_INT("too many colors", procName, 1);
/* Quantize!
* (1) For color:
* If octlevel == 4, try to quantize to an octree where
* the octcube leaves are at level 4. If that fails,
* back off to level 3.
* If octlevel == 3, quantize to level 3 directly.
* For level 3, the quality is usually good enough and there
* is negligible chance of getting more than 256 colors.
* (2) For grayscale, multiply ncolors by 1.5 for extra quality,
* but use at least mingraycolors and not more than 256. */
if (iscolor) {
pixd = pixFewColorsOctcubeQuant1(pixs, octlevel);
if (!pixd) { /* backoff */
pixd = pixFewColorsOctcubeQuant1(pixs, octlevel - 1);
if (octlevel == 3) /* shouldn't happen */
L_WARNING("quantized at level 2; low quality\n", procName);
}
} else { /* image is really grayscale */
if (d == 32)
pixg = pixConvertRGBToLuminance(pixs);
else
pixg = pixClone(pixs);
graycolors = L_MAX(mingraycolors, (l_int32)(1.5 * ncolors));
graycolors = L_MIN(graycolors, 256);
if (graycolors < 16)
pixd = pixThresholdTo4bpp(pixg, graycolors, 1);
else
pixd = pixThresholdOn8bpp(pixg, graycolors, 1);
pixDestroy(&pixg);
}
*ppixd = pixd;
if (!pixd)
return ERROR_INT("pixd not made", procName, 1);
pixCopyInputFormat(pixd, pixs);
return 0;
}
/*---------------------------------------------------------------------------*
* Conversion from 16 bpp to 8 bpp *
*---------------------------------------------------------------------------*/
/*!
* \brief pixConvert16To8()
*
* \param[in] pixs 16 bpp
* \param[in] type L_LS_BYTE, L_MS_BYTE, L_AUTO_BYTE, L_CLIP_TO_FF
* \return pixd 8 bpp, or NULL on error
*
* <pre>
* Notes:
* (1) With L_AUTO_BYTE, if the max pixel value is greater than 255,
* use the MSB; otherwise, use the LSB.
* (2) With L_CLIP_TO_FF, use min(pixel-value, 0xff) for each
* 16-bit src pixel.
* </pre>
*/
PIX *
pixConvert16To8(PIX *pixs,
l_int32 type)
{
l_uint16 dword;
l_int32 w, h, wpls, wpld, i, j, val, use_lsb;
l_uint32 sword, first, second;
l_uint32 *datas, *datad, *lines, *lined;
PIX *pixd;
PROCNAME("pixConvert16To8");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
if (pixGetDepth(pixs) != 16)
return (PIX *)ERROR_PTR("pixs not 16 bpp", procName, NULL);
if (type != L_LS_BYTE && type != L_MS_BYTE &&
type != L_AUTO_BYTE && type != L_CLIP_TO_FF)
return (PIX *)ERROR_PTR("invalid type", procName, NULL);
pixGetDimensions(pixs, &w, &h, NULL);
if ((pixd = pixCreate(w, h, 8)) == NULL)
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
pixCopyInputFormat(pixd, pixs);
pixCopyResolution(pixd, pixs);
wpls = pixGetWpl(pixs);
datas = pixGetData(pixs);
wpld = pixGetWpl(pixd);
datad = pixGetData(pixd);
if (type == L_AUTO_BYTE) {
use_lsb = TRUE;
for (i = 0; i < h; i++) {
lines = datas + i * wpls;
for (j = 0; j < wpls; j++) {
val = GET_DATA_TWO_BYTES(lines, j);
if (val > 255) {
use_lsb = FALSE;
break;
}
}
if (!use_lsb) break;
}
type = (use_lsb) ? L_LS_BYTE : L_MS_BYTE;
}
/* Convert 2 pixels at a time */
for (i = 0; i < h; i++) {
lines = datas + i * wpls;
lined = datad + i * wpld;
if (type == L_LS_BYTE) {
for (j = 0; j < wpls; j++) {
sword = *(lines + j);
dword = ((sword >> 8) & 0xff00) | (sword & 0xff);
SET_DATA_TWO_BYTES(lined, j, dword);
}
} else if (type == L_MS_BYTE) {
for (j = 0; j < wpls; j++) {
sword = *(lines + j);
dword = ((sword >> 16) & 0xff00) | ((sword >> 8) & 0xff);
SET_DATA_TWO_BYTES(lined, j, dword);
}
} else { /* type == L_CLIP_TO_FF */
for (j = 0; j < wpls; j++) {
sword = *(lines + j);
first = (sword >> 24) ? 255 : ((sword >> 16) & 0xff);
second = ((sword >> 8) & 0xff) ? 255 : (sword & 0xff);
dword = (first << 8) | second;
SET_DATA_TWO_BYTES(lined, j, dword);
}
}
}
return pixd;
}
/*---------------------------------------------------------------------------*
* Conversion from grayscale to false color
*---------------------------------------------------------------------------*/
/*!
* \brief pixConvertGrayToFalseColor()
*
* \param[in] pixs 8 or 16 bpp grayscale
* \param[in] gamma (factor) 0.0 or 1.0 for default; > 1.0 for brighter;
* 2.0 is quite nice
* \return pixd 8 bpp with colormap, or NULL on error
*
* <pre>
* Notes:
* (1) For 8 bpp input, this simply adds a colormap to the input image.
* (2) For 16 bpp input, it first converts to 8 bpp, using the MSB,
* and then adds the colormap.
* (3) The colormap is modeled after the Matlab "jet" configuration.
* </pre>
*/
PIX *
pixConvertGrayToFalseColor(PIX *pixs,
l_float32 gamma)
{
l_int32 d, i, rval, bval, gval;
l_int32 *curve;
l_float32 invgamma, x;
PIX *pixd;
PIXCMAP *cmap;
PROCNAME("pixConvertGrayToFalseColor");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
d = pixGetDepth(pixs);
if (d != 8 && d != 16)
return (PIX *)ERROR_PTR("pixs not 8 or 16 bpp", procName, NULL);
if (d == 16) {
pixd = pixConvert16To8(pixs, L_MS_BYTE);
} else { /* d == 8 */
if (pixGetColormap(pixs))
pixd = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE);
else
pixd = pixCopy(NULL, pixs);
}
if (!pixd)
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
cmap = pixcmapCreate(8);
pixSetColormap(pixd, cmap);
pixCopyResolution(pixd, pixs);
pixCopyInputFormat(pixd, pixs);
/* Generate curve for transition part of color map */
curve = (l_int32 *)LEPT_CALLOC(64, sizeof(l_int32));
if (gamma == 0.0) gamma = 1.0;
invgamma = 1. / gamma;
for (i = 0; i < 64; i++) {
x = (l_float32)i / 64.;
curve[i] = (l_int32)(255. * powf(x, invgamma) + 0.5);
}
for (i = 0; i < 256; i++) {
if (i < 32) {
rval = 0;
gval = 0;
bval = curve[i + 32];
} else if (i < 96) { /* 32 - 95 */
rval = 0;
gval = curve[i - 32];
bval = 255;
} else if (i < 160) { /* 96 - 159 */
rval = curve[i - 96];
gval = 255;
bval = curve[159 - i];
} else if (i < 224) { /* 160 - 223 */
rval = 255;
gval = curve[223 - i];
bval = 0;
} else { /* 224 - 255 */
rval = curve[287 - i];
gval = 0;
bval = 0;
}
pixcmapAddColor(cmap, rval, gval, bval);
}
LEPT_FREE(curve);
return pixd;
}
/*---------------------------------------------------------------------------*
* Unpacking conversion from 1 bpp to 2, 4, 8, 16 and 32 bpp *
*---------------------------------------------------------------------------*/
/*!
* \brief pixUnpackBinary()
*
* \param[in] pixs 1 bpp
* \param[in] depth of destination: 2, 4, 8, 16 or 32 bpp
* \param[in] invert 0: binary 0 --> grayscale 0
* binary 1 --> grayscale 0xff...
* 1: binary 0 --> grayscale 0xff...
* binary 1 --> grayscale 0
* \return pixd 2, 4, 8, 16 or 32 bpp, or NULL on error
*
* <pre>
* Notes:
* (1) This function calls special cases of pixConvert1To*(),
* for 2, 4, 8, 16 and 32 bpp destinations.
* </pre>
*/
PIX *
pixUnpackBinary(PIX *pixs,
l_int32 depth,
l_int32 invert)
{
PIX *pixd;
PROCNAME("pixUnpackBinary");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
if (pixGetDepth(pixs) != 1)
return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
if (depth != 2 && depth != 4 && depth != 8 && depth != 16 && depth != 32)
return (PIX *)ERROR_PTR("depth not 2, 4, 8, 16 or 32 bpp",
procName, NULL);
if (depth == 2) {
if (invert == 0)
pixd = pixConvert1To2(NULL, pixs, 0, 3);
else /* invert bits */
pixd = pixConvert1To2(NULL, pixs, 3, 0);
} else if (depth == 4) {
if (invert == 0)
pixd = pixConvert1To4(NULL, pixs, 0, 15);
else /* invert bits */
pixd = pixConvert1To4(NULL, pixs, 15, 0);
} else if (depth == 8) {
if (invert == 0)
pixd = pixConvert1To8(NULL, pixs, 0, 255);
else /* invert bits */
pixd = pixConvert1To8(NULL, pixs, 255, 0);
} else if (depth == 16) {
if (invert == 0)
pixd = pixConvert1To16(NULL, pixs, 0, 0xffff);
else /* invert bits */
pixd = pixConvert1To16(NULL, pixs, 0xffff, 0);
} else {
if (invert == 0)
pixd = pixConvert1To32(NULL, pixs, 0, 0xffffffff);
else /* invert bits */
pixd = pixConvert1To32(NULL, pixs, 0xffffffff, 0);
}
pixCopyInputFormat(pixd, pixs);
return pixd;
}
/*!
* \brief pixConvert1To16()
*
* \param[in] pixd [optional] 16 bpp, can be null
* \param[in] pixs 1 bpp
* \param[in] val0 16 bit value to be used for 0s in pixs
* \param[in] val1 16 bit value to be used for 1s in pixs
* \return pixd 16 bpp
*
* <pre>
* Notes:
* (1) If pixd is null, a new pix is made.
* (2) If pixd is not null, it must be of equal width and height
* as pixs. It is always returned.
* </pre>
*/
PIX *
pixConvert1To16(PIX *pixd,
PIX *pixs,
l_uint16 val0,
l_uint16 val1)
{
l_int32 w, h, i, j, dibit, ndibits, wpls, wpld;
l_uint16 val[2];
l_uint32 index;
l_uint32 *tab, *datas, *datad, *lines, *lined;
PROCNAME("pixConvert1To16");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
if (pixGetDepth(pixs) != 1)
return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
pixGetDimensions(pixs, &w, &h, NULL);
if (pixd) {
if (w != pixGetWidth(pixd) || h != pixGetHeight(pixd))
return (PIX *)ERROR_PTR("pix sizes unequal", procName, pixd);
if (pixGetDepth(pixd) != 16)
return (PIX *)ERROR_PTR("pixd not 16 bpp", procName, pixd);
} else {
if ((pixd = pixCreate(w, h, 16)) == NULL)
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
}
pixCopyResolution(pixd, pixs);
pixCopyInputFormat(pixd, pixs);
/* Use a table to convert 2 src bits at a time */
tab = (l_uint32 *)LEPT_CALLOC(4, sizeof(l_uint32));
val[0] = val0;
val[1] = val1;
for (index = 0; index < 4; index++) {
tab[index] = (val[(index >> 1) & 1] << 16) | val[index & 1];
}
datas = pixGetData(pixs);
wpls = pixGetWpl(pixs);
datad = pixGetData(pixd);
wpld = pixGetWpl(pixd);
ndibits = (w + 1) / 2;
for (i = 0; i < h; i++) {
lines = datas + i * wpls;
lined = datad + i * wpld;
for (j = 0; j < ndibits; j++) {
dibit = GET_DATA_DIBIT(lines, j);
lined[j] = tab[dibit];
}
}
LEPT_FREE(tab);
return pixd;
}
/*!
* \brief pixConvert1To32()
*
* \param[in] pixd [optional] 32 bpp, can be null
* \param[in] pixs 1 bpp
* \param[in] val0 32 bit value to be used for 0s in pixs
* \param[in] val1 32 bit value to be used for 1s in pixs
* \return pixd 32 bpp
*
* <pre>
* Notes:
* (1) If pixd is null, a new pix is made.
* (2) If pixd is not null, it must be of equal width and height
* as pixs. It is always returned.
* </pre>
*/
PIX *
pixConvert1To32(PIX *pixd,
PIX *pixs,
l_uint32 val0,
l_uint32 val1)
{
l_int32 w, h, i, j, wpls, wpld, bit;
l_uint32 val[2];
l_uint32 *datas, *datad, *lines, *lined;
PROCNAME("pixConvert1To32");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
if (pixGetDepth(pixs) != 1)
return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
pixGetDimensions(pixs, &w, &h, NULL);
if (pixd) {
if (w != pixGetWidth(pixd) || h != pixGetHeight(pixd))
return (PIX *)ERROR_PTR("pix sizes unequal", procName, pixd);
if (pixGetDepth(pixd) != 32)
return (PIX *)ERROR_PTR("pixd not 32 bpp", procName, pixd);
} else {
if ((pixd = pixCreate(w, h, 32)) == NULL)
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
}
pixCopyResolution(pixd, pixs);
pixCopyInputFormat(pixd, pixs);
val[0] = val0;
val[1] = val1;
datas = pixGetData(pixs);
wpls = pixGetWpl(pixs);
datad = pixGetData(pixd);
wpld = pixGetWpl(pixd);
for (i = 0; i < h; i++) {
lines = datas + i * wpls;
lined = datad + i * wpld;
for (j = 0; j <w; j++) {
bit = GET_DATA_BIT(lines, j);
lined[j] = val[bit];
}
}
return pixd;
}
/*---------------------------------------------------------------------------*
* Conversion from 1 bpp to 2 bpp *
*---------------------------------------------------------------------------*/
/*!
* \brief pixConvert1To2Cmap()
*
* \param[in] pixs 1 bpp
* \return pixd 2 bpp, cmapped
*
* <pre>
* Notes:
* (1) Input 0 is mapped to (255, 255, 255); 1 is mapped to (0, 0, 0)
* </pre>
*/
PIX *
pixConvert1To2Cmap(PIX *pixs)
{
PIX *pixd;
PIXCMAP *cmap;
PROCNAME("pixConvert1To2Cmap");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
if (pixGetDepth(pixs) != 1)
return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
if ((pixd = pixConvert1To2(NULL, pixs, 0, 1)) == NULL)
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
cmap = pixcmapCreate(2);
pixcmapAddColor(cmap, 255, 255, 255);
pixcmapAddColor(cmap, 0, 0, 0);
pixSetColormap(pixd, cmap);
pixCopyInputFormat(pixd, pixs);
return pixd;
}
/*!
* \brief pixConvert1To2()
*
* \param[in] pixd [optional] 2 bpp, can be null
* \param[in] pixs 1 bpp
* \param[in] val0 2 bit value to be used for 0s in pixs
* \param[in] val1 2 bit value to be used for 1s in pixs
* \return pixd 2 bpp
*
* <pre>
* Notes:
* (1) If pixd is null, a new pix is made.
* (2) If pixd is not null, it must be of equal width and height
* as pixs. It is always returned.
* (3) A simple unpacking might use val0 = 0 and val1 = 3.
* (4) If you want a colormapped pixd, use pixConvert1To2Cmap().
* </pre>
*/
PIX *
pixConvert1To2(PIX *pixd,
PIX *pixs,
l_int32 val0,
l_int32 val1)
{
l_int32 w, h, i, j, byteval, nbytes, wpls, wpld;
l_uint8 val[2];
l_uint32 index;
l_uint16 *tab;
l_uint32 *datas, *datad, *lines, *lined;
PROCNAME("pixConvert1To2");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
if (pixGetDepth(pixs) != 1)
return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);
pixGetDimensions(pixs, &w, &h, NULL);
if (pixd) {
if (w != pixGetWidth(pixd) || h != pixGetHeight(pixd))
return (PIX *)ERROR_PTR("pix sizes unequal", procName, pixd);
if (pixGetDepth(pixd) != 2)
return (PIX *)ERROR_PTR("pixd not 2 bpp", procName, pixd);
} else {
if ((pixd = pixCreate(w, h, 2)) == NULL)
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
}
pixCopyResolution(pixd, pixs);
pixCopyInputFormat(pixd, pixs);
/* Use a table to convert 8 src bits to 16 dest bits */
tab = (l_uint16 *)LEPT_CALLOC(256, sizeof(l_uint16));
val[0] = val0;
val[1] = val1;
for (index = 0; index < 256; index++) {
tab[index] = (val[(index >> 7) & 1] << 14) |
(val[(index >> 6) & 1] << 12) |
(val[(index >> 5) & 1] << 10) |
(val[(index >> 4) & 1] << 8) |
(val[(index >> 3) & 1] << 6) |
(val[(index >> 2) & 1] << 4) |
(val[(index >> 1) & 1] << 2) | val[index & 1];
}
datas = pixGetData(pixs);
wpls = pixGetWpl(pixs);
datad = pixGetData(pixd);
wpld = pixGetWpl(pixd);
nbytes = (w + 7) / 8;
for (i = 0; i < h; i++) {
lines = datas + i * wpls;
lined = datad + i * wpld;
for (j = 0; j < nbytes; j++) {
byteval = GET_DATA_BYTE(lines, j);
SET_DATA_TWO_BYTES(lined, j, tab[byteval]);
}
}
LEPT_FREE(tab);
return pixd;
}
/*---------------------------------------------------------------------------*
* Conversion from 1 bpp to 4 bpp *
*---------------------------------------------------------------------------*/
/*!
* \brief pixConvert1To4Cmap()
*
* \param[in] pixs 1 bpp
* \return pixd 4 bpp, cmapped
*
* <pre>
* Notes:
* (1) Input 0 is mapped to (255, 255, 255); 1 is mapped to (0, 0, 0)
* </pre>
*/
PIX *
pixConvert1To4Cmap(PIX *pixs)
{
PIX *pixd;
PIXCMAP *cmap;
PROCNAME("pixConvert1To4Cmap");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
if (pixGetDepth(pixs) != 1)
return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
if ((pixd = pixConvert1To4(NULL, pixs, 0, 1)) == NULL)
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
cmap = pixcmapCreate(4);
pixcmapAddColor(cmap, 255, 255, 255);
pixcmapAddColor(cmap, 0, 0, 0);
pixSetColormap(pixd, cmap);
pixCopyInputFormat(pixd, pixs);
return pixd;
}
/*!
* \brief pixConvert1To4()
*
* \param[in] pixd [optional] 4 bpp, can be null
* \param[in] pixs 1 bpp
* \param[in] val0 4 bit value to be used for 0s in pixs
* \param[in] val1 4 bit value to be used for 1s in pixs
* \return pixd 4 bpp
*
* <pre>
* Notes:
* (1) If pixd is null, a new pix is made.
* (2) If pixd is not null, it must be of equal width and height
* as pixs. It is always returned.
* (3) A simple unpacking might use val0 = 0 and val1 = 15, or v.v.
* (4) If you want a colormapped pixd, use pixConvert1To4Cmap().
* </pre>
*/
PIX *
pixConvert1To4(PIX *pixd,
PIX *pixs,
l_int32 val0,
l_int32 val1)
{
l_int32 w, h, i, j, byteval, nbytes, wpls, wpld;
l_uint8 val[2];
l_uint32 index;
l_uint32 *tab, *datas, *datad, *lines, *lined;
PROCNAME("pixConvert1To4");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
if (pixGetDepth(pixs) != 1)
return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);
pixGetDimensions(pixs, &w, &h, NULL);
if (pixd) {
if (w != pixGetWidth(pixd) || h != pixGetHeight(pixd))
return (PIX *)ERROR_PTR("pix sizes unequal", procName, pixd);
if (pixGetDepth(pixd) != 4)
return (PIX *)ERROR_PTR("pixd not 4 bpp", procName, pixd);
} else {
if ((pixd = pixCreate(w, h, 4)) == NULL)
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
}
pixCopyResolution(pixd, pixs);
pixCopyInputFormat(pixd, pixs);
/* Use a table to convert 8 src bits to 32 bit dest word */
tab = (l_uint32 *)LEPT_CALLOC(256, sizeof(l_uint32));
val[0] = val0;
val[1] = val1;
for (index = 0; index < 256; index++) {
tab[index] = (val[(index >> 7) & 1] << 28) |
(val[(index >> 6) & 1] << 24) |
(val[(index >> 5) & 1] << 20) |
(val[(index >> 4) & 1] << 16) |
(val[(index >> 3) & 1] << 12) |
(val[(index >> 2) & 1] << 8) |
(val[(index >> 1) & 1] << 4) | val[index & 1];
}
datas = pixGetData(pixs);
wpls = pixGetWpl(pixs);
datad = pixGetData(pixd);
wpld = pixGetWpl(pixd);
nbytes = (w + 7) / 8;
for (i = 0; i < h; i++) {
lines = datas + i * wpls;
lined = datad + i * wpld;
for (j = 0; j < nbytes; j++) {
byteval = GET_DATA_BYTE(lines, j);
lined[j] = tab[byteval];
}
}
LEPT_FREE(tab);
return pixd;
}
/*---------------------------------------------------------------------------*
* Conversion from 1, 2 and 4 bpp to 8 bpp *
*---------------------------------------------------------------------------*/
/*!
* \brief pixConvert1To8Cmap()
*
* \param[in] pixs 1 bpp
* \return pixd 8 bpp, cmapped
*
* <pre>
* Notes:
* (1) Input 0 is mapped to (255, 255, 255); 1 is mapped to (0, 0, 0)
* </pre>
*/
PIX *
pixConvert1To8Cmap(PIX *pixs)
{
PIX *pixd;
PIXCMAP *cmap;
PROCNAME("pixConvert1To8Cmap");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
if (pixGetDepth(pixs) != 1)
return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
if ((pixd = pixConvert1To8(NULL, pixs, 0, 1)) == NULL)
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
cmap = pixcmapCreate(8);
pixcmapAddColor(cmap, 255, 255, 255);
pixcmapAddColor(cmap, 0, 0, 0);
pixSetColormap(pixd, cmap);
pixCopyInputFormat(pixd, pixs);
return pixd;
}
/*!
* \brief pixConvert1To8()
*
* \param[in] pixd [optional] 8 bpp, can be null
* \param[in] pixs 1 bpp
* \param[in] val0 8 bit value to be used for 0s in pixs
* \param[in] val1 8 bit value to be used for 1s in pixs
* \return pixd 8 bpp
*
* <pre>
* Notes:
* (1) If pixd is null, a new pix is made.
* (2) If pixd is not null, it must be of equal width and height
* as pixs. It is always returned.
* (3) A simple unpacking might use val0 = 0 and val1 = 255, or v.v.
* (4) To have a colormap associated with the 8 bpp pixd,
* use pixConvert1To8Cmap().
* </pre>
*/
PIX *
pixConvert1To8(PIX *pixd,
PIX *pixs,
l_uint8 val0,
l_uint8 val1)
{
l_int32 w, h, i, j, qbit, nqbits, wpls, wpld;
l_uint8 val[2];
l_uint32 index;
l_uint32 *tab, *datas, *datad, *lines, *lined;
PROCNAME("pixConvert1To8");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
if (pixGetDepth(pixs) != 1)
return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);
pixGetDimensions(pixs, &w, &h, NULL);
if (pixd) {
if (w != pixGetWidth(pixd) || h != pixGetHeight(pixd))
return (PIX *)ERROR_PTR("pix sizes unequal", procName, pixd);
if (pixGetDepth(pixd) != 8)
return (PIX *)ERROR_PTR("pixd not 8 bpp", procName, pixd);
} else {
if ((pixd = pixCreate(w, h, 8)) == NULL)
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
}
pixCopyResolution(pixd, pixs);
pixCopyInputFormat(pixd, pixs);
pixSetPadBits(pixs, 0);
/* Use a table to convert 4 src bits at a time */
tab = (l_uint32 *)LEPT_CALLOC(16, sizeof(l_uint32));
val[0] = val0;
val[1] = val1;
for (index = 0; index < 16; index++) {
tab[index] = ((l_uint32)val[(index >> 3) & 1] << 24) |
(val[(index >> 2) & 1] << 16) |
(val[(index >> 1) & 1] << 8) | val[index & 1];
}
datas = pixGetData(pixs);
wpls = pixGetWpl(pixs);
datad = pixGetData(pixd);
wpld = pixGetWpl(pixd);
nqbits = (w + 3) / 4;
for (i = 0; i < h; i++) {
lines = datas + i * wpls;
lined = datad + i * wpld;
for (j = 0; j < nqbits; j++) {
qbit = GET_DATA_QBIT(lines, j);
lined[j] = tab[qbit];
}
}
LEPT_FREE(tab);
return pixd;
}
/*!
* \brief pixConvert2To8()
*
* \param[in] pixs 2 bpp
* \param[in] val0 8 bit value to be used for 00 in pixs
* \param[in] val1 8 bit value to be used for 01 in pixs
* \param[in] val2 8 bit value to be used for 10 in pixs
* \param[in] val3 8 bit value to be used for 11 in pixs
* \param[in] cmapflag TRUE if pixd is to have a colormap; FALSE otherwise
* \return pixd 8 bpp, or NULL on error
*
* <pre>
* Notes:
* ~ A simple unpacking might use val0 = 0,
* val1 = 85 (0x55), val2 = 170 (0xaa), val3 = 255.
* ~ If cmapflag is TRUE:
* ~ The 8 bpp image is made with a colormap.
* ~ If pixs has a colormap, the input values are ignored and
* the 8 bpp image is made using the colormap
* ~ If pixs does not have a colormap, the input values are
* used to build the colormap.
* ~ If cmapflag is FALSE:
* ~ The 8 bpp image is made without a colormap.
* ~ If pixs has a colormap, the input values are ignored,
* the colormap is removed, and the values stored in the 8 bpp
* image are from the colormap.
* ~ If pixs does not have a colormap, the input values are
* used to populate the 8 bpp image.
* </pre>
*/
PIX *
pixConvert2To8(PIX *pixs,
l_uint8 val0,
l_uint8 val1,
l_uint8 val2,
l_uint8 val3,
l_int32 cmapflag)
{
l_int32 w, h, i, j, nbytes, wpls, wpld, dibit, byte;
l_uint8 val[4];
l_uint32 index;
l_uint32 *tab, *datas, *datad, *lines, *lined;
PIX *pixd;
PIXCMAP *cmaps, *cmapd;
PROCNAME("pixConvert2To8");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
if (pixGetDepth(pixs) != 2)
return (PIX *)ERROR_PTR("pixs not 2 bpp", procName, NULL);
cmaps = pixGetColormap(pixs);
if (cmaps && cmapflag == FALSE)
return pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE);
pixGetDimensions(pixs, &w, &h, NULL);
if ((pixd = pixCreate(w, h, 8)) == NULL)
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
pixSetPadBits(pixs, 0);
pixCopyResolution(pixd, pixs);
pixCopyInputFormat(pixd, pixs);
datas = pixGetData(pixs);
wpls = pixGetWpl(pixs);
datad = pixGetData(pixd);
wpld = pixGetWpl(pixd);
if (cmapflag == TRUE) { /* pixd will have a colormap */
if (cmaps) { /* use the existing colormap from pixs */
cmapd = pixcmapConvertTo8(cmaps);
} else { /* make a colormap from the input values */
cmapd = pixcmapCreate(8);
pixcmapAddColor(cmapd, val0, val0, val0);
pixcmapAddColor(cmapd, val1, val1, val1);
pixcmapAddColor(cmapd, val2, val2, val2);
pixcmapAddColor(cmapd, val3, val3, val3);
}
pixSetColormap(pixd, cmapd);
for (i = 0; i < h; i++) {
lines = datas + i * wpls;
lined = datad + i * wpld;
for (j = 0; j < w; j++) {
dibit = GET_DATA_DIBIT(lines, j);
SET_DATA_BYTE(lined, j, dibit);
}
}
return pixd;
}
/* Last case: no colormap in either pixs or pixd.
* Use input values and build a table to convert 1 src byte
* (4 src pixels) at a time */
tab = (l_uint32 *)LEPT_CALLOC(256, sizeof(l_uint32));
val[0] = val0;
val[1] = val1;
val[2] = val2;
val[3] = val3;
for (index = 0; index < 256; index++) {
tab[index] = (val[(index >> 6) & 3] << 24) |
(val[(index >> 4) & 3] << 16) |
(val[(index >> 2) & 3] << 8) | val[index & 3];
}
nbytes = (w + 3) / 4;
for (i = 0; i < h; i++) {
lines = datas + i * wpls;
lined = datad + i * wpld;
for (j = 0; j < nbytes; j++) {
byte = GET_DATA_BYTE(lines, j);
lined[j] = tab[byte];
}
}
LEPT_FREE(tab);
return pixd;
}
/*!
* \brief pixConvert4To8()
*
* \param[in] pixs 4 bpp
* \param[in] cmapflag TRUE if pixd is to have a colormap; FALSE otherwise
* \return pixd 8 bpp, or NULL on error
*
* <pre>
* Notes:
* ~ If cmapflag is TRUE:
* ~ pixd is made with a colormap.
* ~ If pixs has a colormap, it is copied and the colormap
* index values are placed in pixd.
* ~ If pixs does not have a colormap, a colormap with linear
* trc is built and the pixel values in pixs are placed in
* pixd as colormap index values.
* ~ If cmapflag is FALSE:
* ~ pixd is made without a colormap.
* ~ If pixs has a colormap, it is removed and the values stored
* in pixd are from the colormap (converted to gray).
* ~ If pixs does not have a colormap, the pixel values in pixs
* are used, with shift replication, to populate pixd.
* </pre>
*/
PIX *
pixConvert4To8(PIX *pixs,
l_int32 cmapflag)
{
l_int32 w, h, i, j, wpls, wpld, byte, qbit;
l_uint32 *datas, *datad, *lines, *lined;
PIX *pixd;
PIXCMAP *cmaps, *cmapd;
PROCNAME("pixConvert4To8");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
if (pixGetDepth(pixs) != 4)
return (PIX *)ERROR_PTR("pixs not 4 bpp", procName, NULL);
cmaps = pixGetColormap(pixs);
if (cmaps && cmapflag == FALSE)
return pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE);
pixGetDimensions(pixs, &w, &h, NULL);
if ((pixd = pixCreate(w, h, 8)) == NULL)
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
pixCopyResolution(pixd, pixs);
pixCopyInputFormat(pixd, pixs);
datas = pixGetData(pixs);
wpls = pixGetWpl(pixs);
datad = pixGetData(pixd);
wpld = pixGetWpl(pixd);
if (cmapflag == TRUE) { /* pixd will have a colormap */
if (cmaps) { /* use the existing colormap from pixs */
cmapd = pixcmapConvertTo8(cmaps);
} else { /* make a colormap with a linear trc */
cmapd = pixcmapCreate(8);
for (i = 0; i < 16; i++)
pixcmapAddColor(cmapd, 17 * i, 17 * i, 17 * i);
}
pixSetColormap(pixd, cmapd);
for (i = 0; i < h; i++) {
lines = datas + i * wpls;
lined = datad + i * wpld;
for (j = 0; j < w; j++) {
qbit = GET_DATA_QBIT(lines, j);
SET_DATA_BYTE(lined, j, qbit);
}
}
return pixd;
}
/* Last case: no colormap in either pixs or pixd.
* Replicate the qbit value into 8 bits. */
for (i = 0; i < h; i++) {
lines = datas + i * wpls;
lined = datad + i * wpld;
for (j = 0; j < w; j++) {
qbit = GET_DATA_QBIT(lines, j);
byte = (qbit << 4) | qbit;
SET_DATA_BYTE(lined, j, byte);
}
}
return pixd;
}
/*---------------------------------------------------------------------------*
* Unpacking conversion from 8 bpp to 16 bpp *
*---------------------------------------------------------------------------*/
/*!
* \brief pixConvert8To16()
*
* \param[in] pixs 8 bpp; colormap removed to gray
* \param[in] leftshift number of bits: 0 is no shift;
* 8 replicates in MSB and LSB of dest
* \return pixd 16 bpp, or NULL on error
*
* <pre>
* Notes:
* (1) For left shift of 8, the 8 bit value is replicated in both
* the MSB and the LSB of the pixels in pixd. That way, we get
* proportional mapping, with a correct map from 8 bpp white
* (0xff) to 16 bpp white (0xffff).
* </pre>
*/
PIX *
pixConvert8To16(PIX *pixs,
l_int32 leftshift)
{
l_int32 i, j, w, h, d, wplt, wpld, val;
l_uint32 *datat, *datad, *linet, *lined;
PIX *pixt, *pixd;
PROCNAME("pixConvert8To16");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
pixGetDimensions(pixs, &w, &h, &d);
if (d != 8)
return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL);
if (leftshift < 0 || leftshift > 8)
return (PIX *)ERROR_PTR("leftshift not in [0 ... 8]", procName, NULL);
if (pixGetColormap(pixs) != NULL)
pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE);
else
pixt = pixClone(pixs);
pixd = pixCreate(w, h, 16);
pixCopyResolution(pixd, pixs);
pixCopyInputFormat(pixd, pixs);
datat = pixGetData(pixt);
datad = pixGetData(pixd);
wplt = pixGetWpl(pixt);
wpld = pixGetWpl(pixd);
for (i = 0; i < h; i++) {
linet = datat + i * wplt;
lined = datad + i * wpld;
for (j = 0; j < w; j++) {
val = GET_DATA_BYTE(linet, j);
if (leftshift == 8)
val = val | (val << leftshift);
else
val <<= leftshift;
SET_DATA_TWO_BYTES(lined, j, val);
}
}
pixDestroy(&pixt);
return pixd;
}
/*---------------------------------------------------------------------------*
* Top-level conversion to 2 bpp *
*---------------------------------------------------------------------------*/
/*!
* \brief pixConvertTo2()
*
* \param[in] pixs 1, 2, 4, 8, 32 bpp; colormap OK but will be removed
* \return pixd 2 bpp, or NULL on error
*
* <pre>
* Notes:
* (1) This is a top-level function, with simple default values
* used in pixConvertTo8() if unpacking is necessary.
* (2) Any existing colormap is removed; the result is always gray.
* (3) If the input image has 2 bpp and no colormap, the operation is
* lossless and a copy is returned.
* </pre>
*/
PIX *
pixConvertTo2(PIX *pixs)
{
l_int32 d;
PIX *pix1, *pix2, *pix3, *pixd;
PROCNAME("pixConvertTo2");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
d = pixGetDepth(pixs);
if (d != 1 && d != 2 && d != 4 && d != 8 && d != 32)
return (PIX *)ERROR_PTR("depth not {1,2,4,8,32}", procName, NULL);
if (pixGetColormap(pixs) != NULL) {
pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE);
d = pixGetDepth(pix1);
} else {
pix1 = pixCopy(NULL, pixs);
}
if (d == 32)
pix2 = pixConvertTo8(pix1, FALSE);
else
pix2 = pixClone(pix1);
pixDestroy(&pix1);
if (d == 1) {
pixd = pixConvert1To2(NULL, pix2, 3, 0);
} else if (d == 2) {
pixd = pixClone(pix2);
} else if (d == 4) {
pix3 = pixConvert4To8(pix2, FALSE); /* unpack to 8 */
pixd = pixConvert8To2(pix3);
pixDestroy(&pix3);
} else { /* d == 8 */
pixd = pixConvert8To2(pix2);
}
pixDestroy(&pix2);
return pixd;
}
/*!
* \brief pixConvert8To2()
*
* \param[in] pix 8 bpp; colormap OK
* \return pixd 2 bpp, or NULL on error
*
* <pre>
* Notes:
* (1) Any existing colormap is removed to gray.
* </pre>
*/
PIX *
pixConvert8To2(PIX *pix)
{
l_int32 i, j, w, h, wpls, wpld;
l_uint32 word;
l_uint32 *datas, *lines, *datad, *lined;
PIX *pixs, *pixd;
PROCNAME("pixConvert8To2");
if (!pix || pixGetDepth(pix) != 8)
return (PIX *)ERROR_PTR("pix undefined or not 8 bpp", procName, NULL);
if (pixGetColormap(pix) != NULL)
pixs = pixRemoveColormap(pix, REMOVE_CMAP_TO_GRAYSCALE);
else
pixs = pixClone(pix);
pixGetDimensions(pixs, &w, &h, NULL);
datas = pixGetData(pixs);
wpls = pixGetWpl(pixs);
pixd = pixCreate(w, h, 2);
datad = pixGetData(pixd);
wpld = pixGetWpl(pixd);
for (i = 0; i < h; i++) {
lines = datas + i * wpls;
lined = datad + i * wpld;
for (j = 0; j < wpls; j++) { /* march through 4 pixels at a time */
word = lines[j] & 0xc0c0c0c0; /* top 2 bits of each byte */
word = (word >> 24) | ((word & 0xff0000) >> 18) |
((word & 0xff00) >> 12) | ((word & 0xff) >> 6);
SET_DATA_BYTE(lined, j, word); /* only LS byte is filled */
}
}
pixDestroy(&pixs);
return pixd;
}
/*---------------------------------------------------------------------------*
* Top-level conversion to 4 bpp *
*---------------------------------------------------------------------------*/
/*!
* \brief pixConvertTo4()
*
* \param[in] pixs 1, 2, 4, 8, 32 bpp; colormap OK but will be removed
* \return pixd 4 bpp, or NULL on error
*
* <pre>
* Notes:
* (1) This is a top-level function, with simple default values
* used in pixConvertTo8() if unpacking is necessary.
* (2) Any existing colormap is removed; the result is always gray.
* (3) If the input image has 4 bpp and no colormap, the operation is
* lossless and a copy is returned.
* </pre>
*/
PIX *
pixConvertTo4(PIX *pixs)
{
l_int32 d;
PIX *pix1, *pix2, *pix3, *pixd;
PROCNAME("pixConvertTo4");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
d = pixGetDepth(pixs);
if (d != 1 && d != 2 && d != 4 && d != 8 && d != 32)
return (PIX *)ERROR_PTR("depth not {1,2,4,8,32}", procName, NULL);
if (pixGetColormap(pixs) != NULL) {
pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE);
d = pixGetDepth(pix1);
} else {
pix1 = pixCopy(NULL, pixs);
}
if (d == 32)
pix2 = pixConvertTo8(pix1, FALSE);
else
pix2 = pixClone(pix1);
pixDestroy(&pix1);
if (d == 1) {
pixd = pixConvert1To4(NULL, pix2, 15, 0);
} else if (d == 2) {
pix3 = pixConvert2To8(pix2, 0, 0x55, 0xaa, 0xff, FALSE);
pixd = pixConvert8To4(pix3);
pixDestroy(&pix3);
} else if (d == 4) {
pixd = pixClone(pix2);
} else { /* d == 8 */
pixd = pixConvert8To4(pix2);
}
pixDestroy(&pix2);
return pixd;
}
/*!
* \brief pixConvert8To4()
*
* \param[in] pix 8 bpp; colormap OK
* \return pixd 4 bpp, or NULL on error
*
* <pre>
* Notes:
* (1) Any existing colormap is removed to gray.
* </pre>
*/
PIX *
pixConvert8To4(PIX *pix)
{
l_int32 i, j, w, h, wpls, wpld, val;
l_uint32 *datas, *lines, *datad, *lined;
PIX *pixs, *pixd;
PROCNAME("pixConvert8To4");
if (!pix || pixGetDepth(pix) != 8)
return (PIX *)ERROR_PTR("pix undefined or not 8 bpp", procName, NULL);
if (pixGetColormap(pix) != NULL)
pixs = pixRemoveColormap(pix, REMOVE_CMAP_TO_GRAYSCALE);
else
pixs = pixClone(pix);
pixGetDimensions(pixs, &w, &h, NULL);
datas = pixGetData(pixs);
wpls = pixGetWpl(pixs);
pixd = pixCreate(w, h, 4);
datad = pixGetData(pixd);
wpld = pixGetWpl(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);
val = val >> 4; /* take top 4 bits */
SET_DATA_QBIT(lined, j, val);
}
}
pixDestroy(&pixs);
return pixd;
}
/*---------------------------------------------------------------------------*
* Top-level conversion to 1 bpp *
*---------------------------------------------------------------------------*/
/*!
* \brief pixConvertTo1Adaptive()
*
* \param[in] pixs 1, 2, 4, 8, 16 or 32 bpp
* \return pixd 1 bpp, or NULL on error
*
* <pre>
* Notes:
* (1) This is a top-level function, that uses default values for
* adaptive thresholding, if necessary. Otherwise, it is the same as
* pixConvertTo1(), which uses a global threshold for binarization.
* </pre>
*/
PIX *
pixConvertTo1Adaptive(PIX *pixs)
{
l_int32 d, color0, color1, rval, gval, bval;
PIX *pix1, *pix2, *pixd;
PIXCMAP *cmap;
PROCNAME("pixConvertTo1Adaptive");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
d = pixGetDepth(pixs);
if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32)
return (PIX *)ERROR_PTR("depth not {1,2,4,8,16,32}", procName, NULL);
cmap = pixGetColormap(pixs);
if (d == 1) {
if (!cmap) {
return pixCopy(NULL, pixs);
} else { /* strip the colormap off, and invert if reasonable
for standard binary photometry. */
pixcmapGetColor(cmap, 0, &rval, &gval, &bval);
color0 = rval + gval + bval;
pixcmapGetColor(cmap, 1, &rval, &gval, &bval);
color1 = rval + gval + bval;
pixd = pixCopy(NULL, pixs);
pixDestroyColormap(pixd);
if (color1 > color0)
pixInvert(pixd, pixd);
return pixd;
}
}
/* For all other depths, use 8 bpp as an intermediary */
pix1 = pixConvertTo8(pixs, FALSE);
pix2 = pixBackgroundNormSimple(pix1, NULL, NULL);
pixd = pixThresholdToBinary(pix2, 180);
pixDestroy(&pix1);
pixDestroy(&pix2);
return pixd;
}
/*!
* \brief pixConvertTo1()
*
* \param[in] pixs 1, 2, 4, 8, 16 or 32 bpp
* \param[in] threshold for final binarization, relative to 8 bpp
* \return pixd 1 bpp, or NULL on error
*
* <pre>
* Notes:
* (1) This is a top-level function, with simple default values
* used in pixConvertTo8() if unpacking is necessary.
* (2) Any existing colormap is removed.
* (3) If the input image has 1 bpp and no colormap, the operation is
* lossless and a copy is returned.
* </pre>
*/
PIX *
pixConvertTo1(PIX *pixs,
l_int32 threshold)
{
l_int32 d, color0, color1, rval, gval, bval;
PIX *pixg, *pixd;
PIXCMAP *cmap;
PROCNAME("pixConvertTo1");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
d = pixGetDepth(pixs);
if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32)
return (PIX *)ERROR_PTR("depth not {1,2,4,8,16,32}", procName, NULL);
cmap = pixGetColormap(pixs);
if (d == 1) {
if (!cmap) {
return pixCopy(NULL, pixs);
} else { /* strip the colormap off, and invert if reasonable
for standard binary photometry. */
pixcmapGetColor(cmap, 0, &rval, &gval, &bval);
color0 = rval + gval + bval;
pixcmapGetColor(cmap, 1, &rval, &gval, &bval);
color1 = rval + gval + bval;
pixd = pixCopy(NULL, pixs);
pixDestroyColormap(pixd);
if (color1 > color0)
pixInvert(pixd, pixd);
return pixd;
}
}
/* For all other depths, use 8 bpp as an intermediary */
pixg = pixConvertTo8(pixs, FALSE);
pixd = pixThresholdToBinary(pixg, threshold);
pixDestroy(&pixg);
return pixd;
}
/*!
* \brief pixConvertTo1BySampling()
*
* \param[in] pixs 1, 2, 4, 8, 16 or 32 bpp
* \param[in] factor submsampling factor; integer >= 1
* \param[in] threshold for final binarization, relative to 8 bpp
* \return pixd 1 bpp, or NULL on error
*
* <pre>
* Notes:
* (1) This is a quick and dirty, top-level converter.
* (2) See pixConvertTo1() for default values.
* </pre>
*/
PIX *
pixConvertTo1BySampling(PIX *pixs,
l_int32 factor,
l_int32 threshold)
{
l_float32 scalefactor;
PIX *pixt, *pixd;
PROCNAME("pixConvertTo1BySampling");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
if (factor < 1)
return (PIX *)ERROR_PTR("factor must be >= 1", procName, NULL);
scalefactor = 1. / (l_float32)factor;
pixt = pixScaleBySampling(pixs, scalefactor, scalefactor);
pixd = pixConvertTo1(pixt, threshold);
pixDestroy(&pixt);
return pixd;
}
/*---------------------------------------------------------------------------*
* Top-level conversion to 8 bpp *
*---------------------------------------------------------------------------*/
/*!
* \brief pixConvertTo8()
*
* \param[in] pixs 1, 2, 4, 8, 16 or 32 bpp
* \param[in] cmapflag TRUE if pixd is to have a colormap; FALSE otherwise
* \return pixd 8 bpp, or NULL on error
*
* <pre>
* Notes:
* (1) This is a top-level function, with simple default values
* for unpacking.
* (2) The result, pixd, is made with a colormap if specified.
* It is always a new image -- never a clone. For example,
* if d == 8, and cmapflag matches the existence of a cmap
* in pixs, the operation is lossless and it returns a copy.
* (3) The default values used are:
* ~ 1 bpp: val0 = 255, val1 = 0
* ~ 2 bpp: 4 bpp: even increments over dynamic range
* ~ 8 bpp: lossless if cmap matches cmapflag
* ~ 16 bpp: use most significant byte
* (4) If 32 bpp RGB, this is converted to gray. If you want
* to do color quantization, you must specify the type
* explicitly, using the color quantization code.
* </pre>
*/
PIX *
pixConvertTo8(PIX *pixs,
l_int32 cmapflag)
{
l_int32 d;
PIX *pixd;
PIXCMAP *cmap;
PROCNAME("pixConvertTo8");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
d = pixGetDepth(pixs);
if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32)
return (PIX *)ERROR_PTR("depth not {1,2,4,8,16,32}", procName, NULL);
if (d == 1) {
if (cmapflag)
return pixConvert1To8Cmap(pixs);
else
return pixConvert1To8(NULL, pixs, 255, 0);
} else if (d == 2) {
return pixConvert2To8(pixs, 0, 85, 170, 255, cmapflag);
} else if (d == 4) {
return pixConvert4To8(pixs, cmapflag);
} else if (d == 8) {
cmap = pixGetColormap(pixs);
if ((cmap && cmapflag) || (!cmap && !cmapflag)) {
return pixCopy(NULL, pixs);
} else if (cmap) { /* !cmapflag */
return pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE);
} else { /* !cmap && cmapflag; add colormap to pixd */
pixd = pixCopy(NULL, pixs);
pixAddGrayColormap8(pixd);
return pixd;
}
} else if (d == 16) {
pixd = pixConvert16To8(pixs, L_MS_BYTE);
if (cmapflag)
pixAddGrayColormap8(pixd);
return pixd;
} else { /* d == 32 */
pixd = pixConvertRGBToLuminance(pixs);
if (cmapflag)
pixAddGrayColormap8(pixd);
return pixd;
}
}
/*!
* \brief pixConvertTo8BySampling()
*
* \param[in] pixs 1, 2, 4, 8, 16 or 32 bpp
* \param[in] factor submsampling factor; integer >= 1
* \param[in] cmapflag TRUE if pixd is to have a colormap; FALSE otherwise
* \return pixd 8 bpp, or NULL on error
*
* <pre>
* Notes:
* (1) This is a fast, quick/dirty, top-level converter.
* (2) See pixConvertTo8() for default values.
* </pre>
*/
PIX *
pixConvertTo8BySampling(PIX *pixs,
l_int32 factor,
l_int32 cmapflag)
{
l_float32 scalefactor;
PIX *pixt, *pixd;
PROCNAME("pixConvertTo8BySampling");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
if (factor < 1)
return (PIX *)ERROR_PTR("factor must be >= 1", procName, NULL);
scalefactor = 1. / (l_float32)factor;
pixt = pixScaleBySampling(pixs, scalefactor, scalefactor);
pixd = pixConvertTo8(pixt, cmapflag);
pixDestroy(&pixt);
return pixd;
}
/*!
* \brief pixConvertTo8Colormap()
*
* \param[in] pixs 1, 2, 4, 8, 16 or 32 bpp
* \param[in] dither 1 to dither if necessary; 0 otherwise
* \return pixd 8 bpp, cmapped, or NULL on error
*
* <pre>
* Notes:
* (1) This is a top-level function, with simple default values
* for unpacking.
* (2) The result, pixd, is always made with a colormap.
* (3) If d == 8, the operation is lossless and it returns a copy.
* (4) The default values used for increasing depth are:
* ~ 1 bpp: val0 = 255, val1 = 0
* ~ 2 bpp: 4 bpp: even increments over dynamic range
* (5) For 16 bpp, use the most significant byte.
* (6) For 32 bpp RGB, use octcube quantization with optional dithering.
* </pre>
*/
PIX *
pixConvertTo8Colormap(PIX *pixs,
l_int32 dither)
{
l_int32 d;
PROCNAME("pixConvertTo8Colormap");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
d = pixGetDepth(pixs);
if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32)
return (PIX *)ERROR_PTR("depth not {1,2,4,8,16,32}", procName, NULL);
if (d != 32)
return pixConvertTo8(pixs, 1);
return pixConvertRGBToColormap(pixs, dither);
}
/*---------------------------------------------------------------------------*
* Top-level conversion to 16 bpp *
*---------------------------------------------------------------------------*/
/*!
* \brief pixConvertTo16()
*
* \param[in] pixs 1, 8 bpp
* \return pixd 16 bpp, or NULL on error
*
* Usage: Top-level function, with simple default values for unpacking.
* 1 bpp: val0 = 0xffff, val1 = 0
* 8 bpp: replicates the 8 bit value in both the MSB and LSB
* of the 16 bit pixel.
*/
PIX *
pixConvertTo16(PIX *pixs)
{
l_int32 d;
PROCNAME("pixConvertTo16");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
d = pixGetDepth(pixs);
if (d == 1)
return pixConvert1To16(NULL, pixs, 0xffff, 0);
else if (d == 8)
return pixConvert8To16(pixs, 8);
else
return (PIX *)ERROR_PTR("src depth not 1 or 8 bpp", procName, NULL);
}
/*---------------------------------------------------------------------------*
* Top-level conversion to 32 bpp *
*---------------------------------------------------------------------------*/
/*!
* \brief pixConvertTo32()
*
* \param[in] pixs 1, 2, 4, 8, 16, 24 or 32 bpp
* \return pixd 32 bpp, or NULL on error
*
* Usage: Top-level function, with simple default values for unpacking.
* 1 bpp: val0 = 255, val1 = 0
* and then replication into R, G and B components
* 2 bpp: if colormapped, use the colormap values; otherwise,
* use val0 = 0, val1 = 0x55, val2 = 0xaa, val3 = 255
* and replicate gray into R, G and B components
* 4 bpp: if colormapped, use the colormap values; otherwise,
* replicate 2 nybs into a byte, and then into R,G,B components
* 8 bpp: if colormapped, use the colormap values; otherwise,
* replicate gray values into R, G and B components
* 16 bpp: replicate MSB into R, G and B components
* 24 bpp: unpack the pixels, maintaining word alignment on each scanline
* 32 bpp: makes a copy
*
* <pre>
* Notes:
* (1) Never returns a clone of pixs.
* </pre>
*/
PIX *
pixConvertTo32(PIX *pixs)
{
l_int32 d;
PIX *pix1, *pixd;
PROCNAME("pixConvertTo32");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
d = pixGetDepth(pixs);
if (d == 1) {
return pixConvert1To32(NULL, pixs, 0xffffffff, 0);
} else if (d == 2) {
pix1 = pixConvert2To8(pixs, 0, 85, 170, 255, TRUE);
pixd = pixConvert8To32(pix1);
pixDestroy(&pix1);
return pixd;
} else if (d == 4) {
pix1 = pixConvert4To8(pixs, TRUE);
pixd = pixConvert8To32(pix1);
pixDestroy(&pix1);
return pixd;
} else if (d == 8) {
return pixConvert8To32(pixs);
} else if (d == 16) {
pix1 = pixConvert16To8(pixs, L_MS_BYTE);
pixd = pixConvert8To32(pix1);
pixDestroy(&pix1);
return pixd;
} else if (d == 24) {
return pixConvert24To32(pixs);
} else if (d == 32) {
return pixCopy(NULL, pixs);
} else {
return (PIX *)ERROR_PTR("depth not 1, 2, 4, 8, 16, 32 bpp",
procName, NULL);
}
}
/*!
* \brief pixConvertTo32BySampling()
*
* \param[in] pixs 1, 2, 4, 8, 16, 24 or 32 bpp
* \param[in] factor submsampling factor; integer >= 1
* \return pixd 32 bpp, or NULL on error
*
* <pre>
* Notes:
* (1) This is a fast, quick/dirty, top-level converter.
* (2) See pixConvertTo32() for default values.
* </pre>
*/
PIX *
pixConvertTo32BySampling(PIX *pixs,
l_int32 factor)
{
l_float32 scalefactor;
PIX *pix1, *pixd;
PROCNAME("pixConvertTo32BySampling");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
if (factor < 1)
return (PIX *)ERROR_PTR("factor must be >= 1", procName, NULL);
scalefactor = 1. / (l_float32)factor;
pix1 = pixScaleBySampling(pixs, scalefactor, scalefactor);
pixd = pixConvertTo32(pix1);
pixDestroy(&pix1);
return pixd;
}
/*!
* \brief pixConvert8To32()
*
* \param[in] pixs 8 bpp
* \return 32 bpp rgb pix, or NULL on error
*
* <pre>
* Notes:
* (1) If there is no colormap, replicates the gray value
* into the 3 MSB of the dest pixel.
* </pre>
*/
PIX *
pixConvert8To32(PIX *pixs)
{
l_int32 i, j, w, h, wpls, wpld, val;
l_uint32 *datas, *datad, *lines, *lined;
l_uint32 *tab;
PIX *pixd;
PROCNAME("pixConvert8To32");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
if (pixGetDepth(pixs) != 8)
return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL);
if (pixGetColormap(pixs))
return pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR);
pixGetDimensions(pixs, &w, &h, NULL);
datas = pixGetData(pixs);
wpls = pixGetWpl(pixs);
if ((pixd = pixCreate(w, h, 32)) == NULL)
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
pixCopyResolution(pixd, pixs);
pixCopyInputFormat(pixd, pixs);
datad = pixGetData(pixd);
wpld = pixGetWpl(pixd);
/* Replication table gray --> rgb */
tab = (l_uint32 *)LEPT_CALLOC(256, sizeof(l_uint32));
for (i = 0; i < 256; i++)
tab[i] = (i << 24) | (i << 16) | (i << 8);
/* Replicate 1 --> 4 bytes (alpha byte not set) */
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);
lined[j] = tab[val];
}
}
LEPT_FREE(tab);
return pixd;
}
/*---------------------------------------------------------------------------*
* Top-level conversion to 8 or 32 bpp, without colormap *
*---------------------------------------------------------------------------*/
/*!
* \brief pixConvertTo8Or32()
*
* \param[in] pixs 1, 2, 4, 8, 16, with or without colormap;
* or 32 bpp rgb
* \param[in] copyflag L_CLONE or L_COPY
* \param[in] warnflag 1 to issue warning if colormap is removed; else 0
* \return pixd 8 bpp grayscale or 32 bpp rgb, or NULL on error
*
* <pre>
* Notes:
* (1) If there is a colormap, the colormap is removed to 8 or 32 bpp,
* depending on whether the colors in the colormap are all gray.
* (2) If the input is either rgb or 8 bpp without a colormap,
* this returns either a clone or a copy, depending on %copyflag.
* (3) Otherwise, the pix is converted to 8 bpp grayscale.
* In all cases, pixd does not have a colormap.
* </pre>
*/
PIX *
pixConvertTo8Or32(PIX *pixs,
l_int32 copyflag,
l_int32 warnflag)
{
l_int32 d;
PIX *pixd;
PROCNAME("pixConvertTo8Or32");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
if (copyflag != L_CLONE && copyflag != L_COPY)
return (PIX *)ERROR_PTR("invalid copyflag", procName, NULL);
d = pixGetDepth(pixs);
if (pixGetColormap(pixs)) {
if (warnflag) L_WARNING("pix has colormap; removing\n", procName);
pixd = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC);
} else if (d == 8 || d == 32) {
if (copyflag == L_CLONE)
pixd = pixClone(pixs);
else /* copyflag == L_COPY */
pixd = pixCopy(NULL, pixs);
} else {
pixd = pixConvertTo8(pixs, 0);
}
/* Sanity check on result */
d = pixGetDepth(pixd);
if (d != 8 && d != 32) {
pixDestroy(&pixd);
return (PIX *)ERROR_PTR("depth not 8 or 32 bpp", procName, NULL);
}
return pixd;
}
/*---------------------------------------------------------------------------*
* Conversion between 24 bpp and 32 bpp rgb *
*---------------------------------------------------------------------------*/
/*!
* \brief pixConvert24To32()
*
* \param[in] pixs 24 bpp rgb
* \return pixd 32 bpp rgb, or NULL on error
*
* <pre>
* Notes:
* (1) 24 bpp rgb pix are not supported in leptonica, except for a small
* number of formatted write operations. The data is a byte array,
* with pixels in order r,g,b, and padded to 32 bit boundaries
* in each line.
* (2) Because 24 bpp rgb pix are conveniently generated by programs
* such as xpdf (which has SplashBitmaps that store the raster
* data in consecutive 24-bit rgb pixels), it is useful to provide
* 24 bpp pix that simply incorporate that data. The only things
* we can do with these are:
* (a) write them to file in png, jpeg, tiff and pnm
* (b) interconvert between 24 and 32 bpp in memory (for testing).
* </pre>
*/
PIX *
pixConvert24To32(PIX *pixs)
{
l_uint8 *lines;
l_int32 w, h, d, i, j, wpls, wpld, rval, gval, bval;
l_uint32 pixel;
l_uint32 *datas, *datad, *lined;
PIX *pixd;
PROCNAME("pixConvert24to32");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
pixGetDimensions(pixs, &w, &h, &d);
if (d != 24)
return (PIX *)ERROR_PTR("pixs not 24 bpp", procName, NULL);
pixd = pixCreateNoInit(w, h, 32);
datas = pixGetData(pixs);
datad = pixGetData(pixd);
wpls = pixGetWpl(pixs);
wpld = pixGetWpl(pixd);
for (i = 0; i < h; i++) {
lines = (l_uint8 *)(datas + i * wpls);
lined = datad + i * wpld;
for (j = 0; j < w; j++) {
rval = *lines++;
gval = *lines++;
bval = *lines++;
composeRGBPixel(rval, gval, bval, &pixel);
lined[j] = pixel;
}
}
pixCopyResolution(pixd, pixs);
pixCopyInputFormat(pixd, pixs);
return pixd;
}
/*!
* \brief pixConvert32To24()
*
* \param[in] pixs 32 bpp rgb
* \return pixd 24 bpp rgb, or NULL on error
*
* <pre>
* Notes:
* (1) See pixconvert24To32().
* </pre>
*/
PIX *
pixConvert32To24(PIX *pixs)
{
l_uint8 *rgbdata8;
l_int32 w, h, d, i, j, wpls, wpld, rval, gval, bval;
l_uint32 *datas, *lines, *rgbdata;
PIX *pixd;
PROCNAME("pixConvert32to24");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
pixGetDimensions(pixs, &w, &h, &d);
if (d != 32)
return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL);
datas = pixGetData(pixs);
wpls = pixGetWpl(pixs);
pixd = pixCreateNoInit(w, h, 24);
rgbdata = pixGetData(pixd);
wpld = pixGetWpl(pixd);
for (i = 0; i < h; i++) {
lines = datas + i * wpls;
rgbdata8 = (l_uint8 *)(rgbdata + i * wpld);
for (j = 0; j < w; j++) {
extractRGBValues(lines[j], &rval, &gval, &bval);
*rgbdata8++ = rval;
*rgbdata8++ = gval;
*rgbdata8++ = bval;
}
}
pixCopyResolution(pixd, pixs);
pixCopyInputFormat(pixd, pixs);
return pixd;
}
/*---------------------------------------------------------------------------*
* Conversion between 32 bpp (1 spp) and 16 or 8 bpp *
*---------------------------------------------------------------------------*/
/*!
* \brief pixConvert32To16()
*
* \param[in] pixs 32 bpp, single component
* \param[in] type L_LS_TWO_BYTES, L_MS_TWO_BYTES, L_CLIP_TO_FFFF
* \return pixd 16 bpp , or NULL on error
*
* <pre>
* Notes:
* (1) The data in pixs is typically used for labelling.
* It is an array of l_uint32 values, not rgb or rgba.
* </pre>
*/
PIX *
pixConvert32To16(PIX *pixs,
l_int32 type)
{
l_uint16 dword;
l_int32 w, h, i, j, wpls, wpld;
l_uint32 sword;
l_uint32 *datas, *lines, *datad, *lined;
PIX *pixd;
PROCNAME("pixConvert32to16");
if (!pixs || pixGetDepth(pixs) != 32)
return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL);
if (type != L_LS_TWO_BYTES && type != L_MS_TWO_BYTES &&
type != L_CLIP_TO_FFFF)
return (PIX *)ERROR_PTR("invalid type", procName, NULL);
pixGetDimensions(pixs, &w, &h, NULL);
if ((pixd = pixCreate(w, h, 16)) == NULL)
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
pixCopyResolution(pixd, pixs);
pixCopyInputFormat(pixd, pixs);
wpls = pixGetWpl(pixs);
datas = pixGetData(pixs);
wpld = pixGetWpl(pixd);
datad = pixGetData(pixd);
for (i = 0; i < h; i++) {
lines = datas + i * wpls;
lined = datad + i * wpld;
if (type == L_LS_TWO_BYTES) {
for (j = 0; j < wpls; j++) {
sword = *(lines + j);
dword = sword & 0xffff;
SET_DATA_TWO_BYTES(lined, j, dword);
}
} else if (type == L_MS_TWO_BYTES) {
for (j = 0; j < wpls; j++) {
sword = *(lines + j);
dword = sword >> 16;
SET_DATA_TWO_BYTES(lined, j, dword);
}
} else { /* type == L_CLIP_TO_FFFF */
for (j = 0; j < wpls; j++) {
sword = *(lines + j);
dword = (sword >> 16) ? 0xffff : (sword & 0xffff);
SET_DATA_TWO_BYTES(lined, j, dword);
}
}
}
return pixd;
}
/*!
* \brief pixConvert32To8()
*
* \param[in] pixs 32 bpp, single component
* \param[in] type16 L_LS_TWO_BYTES, L_MS_TWO_BYTES, L_CLIP_TO_FFFF
* \param[in] type8 L_LS_BYTE, L_MS_BYTE, L_CLIP_TO_FF
* \return pixd 8 bpp, or NULL on error
*/
PIX *
pixConvert32To8(PIX *pixs,
l_int32 type16,
l_int32 type8)
{
PIX *pix1, *pixd;
PROCNAME("pixConvert32to8");
if (!pixs || pixGetDepth(pixs) != 32)
return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL);
if (type16 != L_LS_TWO_BYTES && type16 != L_MS_TWO_BYTES &&
type16 != L_CLIP_TO_FFFF)
return (PIX *)ERROR_PTR("invalid type16", procName, NULL);
if (type8 != L_LS_BYTE && type8 != L_MS_BYTE && type8 != L_CLIP_TO_FF)
return (PIX *)ERROR_PTR("invalid type8", procName, NULL);
pix1 = pixConvert32To16(pixs, type16);
pixd = pixConvert16To8(pix1, type8);
pixDestroy(&pix1);
return pixd;
}
/*---------------------------------------------------------------------------*
* Removal of alpha component by blending with white background *
*---------------------------------------------------------------------------*/
/*!
* \brief pixRemoveAlpha()
*
* \param[in] pixs any depth
* \return pixd if 32 bpp rgba, pixs blended over a white background;
* a clone of pixs otherwise, and NULL on error
*
* <pre>
* Notes:
* (1) This is a wrapper on pixAlphaBlendUniform()
* </pre>
*/
PIX *
pixRemoveAlpha(PIX *pixs)
{
PROCNAME("pixRemoveAlpha");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
if (pixGetDepth(pixs) == 32 && pixGetSpp(pixs) == 4)
return pixAlphaBlendUniform(pixs, 0xffffff00);
else
return pixClone(pixs);
}
/*---------------------------------------------------------------------------*
* Addition of alpha component to 1 bpp *
*---------------------------------------------------------------------------*/
/*!
* \brief pixAddAlphaTo1bpp()
*
* \param[in] pixd [optional] 1 bpp, can be null or equal to pixs
* \param[in] pixs 1 bpp
* \return pixd 1 bpp with colormap and non-opaque alpha,
* or NULL on error
*
* <pre>
* Notes:
* (1) We don't use 1 bpp colormapped images with alpha in leptonica,
* but we support generating them (here), writing to png, and reading
* the png. On reading, they are converted to 32 bpp RGBA.
* (2) The background (0) pixels in pixs become fully transparent, and the
* foreground (1) pixels are fully opaque. Thus, pixd is a 1 bpp
* representation of a stencil, that can be used to paint over pixels
* of a backing image that are masked by the foreground in pixs.
* </pre>
*/
PIX *
pixAddAlphaTo1bpp(PIX *pixd,
PIX *pixs)
{
PIXCMAP *cmap;
PROCNAME("pixAddAlphaTo1bpp");
if (!pixs || (pixGetDepth(pixs) != 1))
return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL);
if (pixd && (pixd != pixs))
return (PIX *)ERROR_PTR("pixd defined but != pixs", procName, NULL);
pixd = pixCopy(pixd, pixs);
cmap = pixcmapCreate(1);
pixSetColormap(pixd, cmap);
pixcmapAddRGBA(cmap, 255, 255, 255, 0); /* 0 ==> white + transparent */
pixcmapAddRGBA(cmap, 0, 0, 0, 255); /* 1 ==> black + opaque */
return pixd;
}
/*---------------------------------------------------------------------------*
* Lossless depth conversion (unpacking) *
*---------------------------------------------------------------------------*/
/*!
* \brief pixConvertLossless()
*
* \param[in] pixs 1, 2, 4, 8 bpp, not cmapped
* \param[in] d destination depth: 2, 4 or 8
* \return pixd 2, 4 or 8 bpp, or NULL on error
*
* <pre>
* Notes:
* (1) This is a lossless unpacking (depth-increasing)
* conversion. If ds is the depth of pixs, then
* ~ if d < ds, returns NULL
* ~ if d == ds, returns a copy
* ~ if d > ds, does the unpacking conversion
* (2) If pixs has a colormap, this is an error.
* </pre>
*/
PIX *
pixConvertLossless(PIX *pixs,
l_int32 d)
{
l_int32 w, h, ds, wpls, wpld, i, j, val;
l_uint32 *datas, *datad, *lines, *lined;
PIX *pixd;
PROCNAME("pixConvertLossless");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
if (pixGetColormap(pixs))
return (PIX *)ERROR_PTR("pixs has colormap", procName, NULL);
if (d != 2 && d != 4 && d != 8)
return (PIX *)ERROR_PTR("invalid dest depth", procName, NULL);
pixGetDimensions(pixs, &w, &h, &ds);
if (d < ds)
return (PIX *)ERROR_PTR("depth > d", procName, NULL);
else if (d == ds)
return pixCopy(NULL, pixs);
if ((pixd = pixCreate(w, h, d)) == NULL)
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
pixCopyResolution(pixd, pixs);
pixCopyInputFormat(pixd, pixs);
/* Unpack the bits */
datas = pixGetData(pixs);
wpls = pixGetWpl(pixs);
datad = pixGetData(pixd);
wpld = pixGetWpl(pixd);
for (i = 0; i < h; i++) {
lines = datas + i * wpls;
lined = datad + i * wpld;
switch (ds)
{
case 1:
for (j = 0; j < w; j++) {
val = GET_DATA_BIT(lines, j);
if (d == 8)
SET_DATA_BYTE(lined, j, val);
else if (d == 4)
SET_DATA_QBIT(lined, j, val);
else /* d == 2 */
SET_DATA_DIBIT(lined, j, val);
}
break;
case 2:
for (j = 0; j < w; j++) {
val = GET_DATA_DIBIT(lines, j);
if (d == 8)
SET_DATA_BYTE(lined, j, val);
else /* d == 4 */
SET_DATA_QBIT(lined, j, val);
}
break;
case 4:
for (j = 0; j < w; j++) {
val = GET_DATA_DIBIT(lines, j);
SET_DATA_BYTE(lined, j, val);
}
break;
}
}
return pixd;
}
/*---------------------------------------------------------------------------*
* Conversion for printing in PostScript *
*---------------------------------------------------------------------------*/
/*!
* \brief pixConvertForPSWrap()
*
* \param[in] pixs 1, 2, 4, 8, 16, 32 bpp
* \return pixd 1, 8, or 32 bpp, or NULL on error
*
* <pre>
* Notes:
* (1) For wrapping in PostScript, we convert pixs to
* 1 bpp, 8 bpp (gray) and 32 bpp (RGB color).
* (2) Colormaps are removed. For pixs with colormaps, the
* images are converted to either 8 bpp gray or 32 bpp
* RGB, depending on whether the colormap has color content.
* (3) Images without colormaps, that are not 1 bpp or 32 bpp,
* are converted to 8 bpp gray.
* </pre>
*/
PIX *
pixConvertForPSWrap(PIX *pixs)
{
l_int32 d;
PIX *pixd;
PIXCMAP *cmap;
PROCNAME("pixConvertForPSWrap");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
cmap = pixGetColormap(pixs);
d = pixGetDepth(pixs);
switch (d)
{
case 1:
case 32:
pixd = pixClone(pixs);
break;
case 2:
if (cmap)
pixd = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC);
else
pixd = pixConvert2To8(pixs, 0, 0x55, 0xaa, 0xff, FALSE);
break;
case 4:
if (cmap)
pixd = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC);
else
pixd = pixConvert4To8(pixs, FALSE);
break;
case 8:
pixd = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC);
break;
case 16:
pixd = pixConvert16To8(pixs, L_MS_BYTE);
break;
default:
fprintf(stderr, "depth not in {1, 2, 4, 8, 16, 32}");
return NULL;
}
return pixd;
}
/*---------------------------------------------------------------------------*
* Scaling conversion to subpixel RGB *
*---------------------------------------------------------------------------*/
/*!
* \brief pixConvertToSubpixelRGB()
*
* \param[in] pixs 8 bpp grayscale, 32 bpp rgb, or colormapped
* \param[in] scalex, scaley anisotropic scaling permitted between
* source and destination
* \param[in] order of subpixel rgb color components in
* composition of pixd:
* L_SUBPIXEL_ORDER_RGB, L_SUBPIXEL_ORDER_BGR,
* L_SUBPIXEL_ORDER_VRGB, L_SUBPIXEL_ORDER_VBGR
* \return pixd 32 bpp, or NULL on error
*
* <pre>
* Notes:
* (1) If pixs has a colormap, it is removed based on its contents
* to either 8 bpp gray or rgb.
* (2) For horizontal subpixel splitting, the input image
* is rescaled by %scaley vertically and by 3.0 times
* %scalex horizontally. Then each horizontal triplet
* of pixels is mapped back to a single rgb pixel, with the
* r, g and b values being assigned based on the pixel triplet.
* For gray triplets, the r, g, and b values are set equal to
* the three gray values. For color triplets, the r, g and b
* values are set equal to the components from the appropriate
* subpixel. Vertical subpixel splitting is handled similarly.
* (3) See pixConvertGrayToSubpixelRGB() and
* pixConvertColorToSubpixelRGB() for further details.
* </pre>
*/
PIX *
pixConvertToSubpixelRGB(PIX *pixs,
l_float32 scalex,
l_float32 scaley,
l_int32 order)
{
l_int32 d;
PIX *pix1, *pixd;
PIXCMAP *cmap;
PROCNAME("pixConvertToSubpixelRGB");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
d = pixGetDepth(pixs);
cmap = pixGetColormap(pixs);
if (d != 8 && d != 32 && !cmap)
return (PIX *)ERROR_PTR("pix not 8 or 32 bpp and not cmapped",
procName, NULL);
if (scalex <= 0.0 || scaley <= 0.0)
return (PIX *)ERROR_PTR("scale factors must be > 0", procName, NULL);
if (order != L_SUBPIXEL_ORDER_RGB && order != L_SUBPIXEL_ORDER_BGR &&
order != L_SUBPIXEL_ORDER_VRGB && order != L_SUBPIXEL_ORDER_VBGR)
return (PIX *)ERROR_PTR("invalid subpixel order", procName, NULL);
if ((pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC)) == NULL)
return (PIX *)ERROR_PTR("pix1 not made", procName, NULL);
d = pixGetDepth(pix1);
pixd = NULL;
if (d == 8)
pixd = pixConvertGrayToSubpixelRGB(pix1, scalex, scaley, order);
else if (d == 32)
pixd = pixConvertColorToSubpixelRGB(pix1, scalex, scaley, order);
else
L_ERROR("invalid depth %d\n", procName, d);
pixDestroy(&pix1);
return pixd;
}
/*!
* \brief pixConvertGrayToSubpixelRGB()
*
* \param[in] pixs 8 bpp or colormapped
* \param[in] scalex, scaley
* \param[in] order of subpixel rgb color components in
* composition of pixd:
* L_SUBPIXEL_ORDER_RGB, L_SUBPIXEL_ORDER_BGR,
* L_SUBPIXEL_ORDER_VRGB, L_SUBPIXEL_ORDER_VBGR
* \return pixd 32 bpp, or NULL on error
*
* <pre>
* Notes:
* (1) If pixs has a colormap, it is removed to 8 bpp.
* (2) For horizontal subpixel splitting, the input gray image
* is rescaled by %scaley vertically and by 3.0 times
* %scalex horizontally. Then each horizontal triplet
* of pixels is mapped back to a single rgb pixel, with the
* r, g and b values being assigned from the triplet of gray values.
* Similar operations are used for vertical subpixel splitting.
* (3) This is a form of subpixel rendering that tends to give the
* resulting text a sharper and somewhat chromatic display.
* For horizontal subpixel splitting, the observable difference
* between %order=L_SUBPIXEL_ORDER_RGB and
* %order=L_SUBPIXEL_ORDER_BGR is reduced by optical diffusers
* in the display that make the pixel color appear to emerge
* from the entire pixel.
* </pre>
*/
PIX *
pixConvertGrayToSubpixelRGB(PIX *pixs,
l_float32 scalex,
l_float32 scaley,
l_int32 order)
{
l_int32 w, h, d, wd, hd, wplt, wpld, i, j, rval, gval, bval, direction;
l_uint32 *datat, *datad, *linet, *lined;
PIX *pix1, *pix2, *pixd;
PIXCMAP *cmap;
PROCNAME("pixConvertGrayToSubpixelRGB");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
d = pixGetDepth(pixs);
cmap = pixGetColormap(pixs);
if (d != 8 && !cmap)
return (PIX *)ERROR_PTR("pix not 8 bpp & not cmapped", procName, NULL);
if (scalex <= 0.0 || scaley <= 0.0)
return (PIX *)ERROR_PTR("scale factors must be > 0", procName, NULL);
if (order != L_SUBPIXEL_ORDER_RGB && order != L_SUBPIXEL_ORDER_BGR &&
order != L_SUBPIXEL_ORDER_VRGB && order != L_SUBPIXEL_ORDER_VBGR)
return (PIX *)ERROR_PTR("invalid subpixel order", procName, NULL);
direction =
(order == L_SUBPIXEL_ORDER_RGB || order == L_SUBPIXEL_ORDER_BGR)
? L_HORIZ : L_VERT;
pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE);
if (direction == L_HORIZ)
pix2 = pixScale(pix1, 3.0 * scalex, scaley);
else /* L_VERT */
pix2 = pixScale(pix1, scalex, 3.0 * scaley);
pixGetDimensions(pix2, &w, &h, NULL);
wd = (direction == L_HORIZ) ? w / 3 : w;
hd = (direction == L_VERT) ? h / 3 : h;
pixd = pixCreate(wd, hd, 32);
datad = pixGetData(pixd);
wpld = pixGetWpl(pixd);
datat = pixGetData(pix2);
wplt = pixGetWpl(pix2);
if (direction == L_HORIZ) {
for (i = 0; i < hd; i++) {
linet = datat + i * wplt;
lined = datad + i * wpld;
for (j = 0; j < wd; j++) {
rval = GET_DATA_BYTE(linet, 3 * j);
gval = GET_DATA_BYTE(linet, 3 * j + 1);
bval = GET_DATA_BYTE(linet, 3 * j + 2);
if (order == L_SUBPIXEL_ORDER_RGB)
composeRGBPixel(rval, gval, bval, &lined[j]);
else /* order BGR */
composeRGBPixel(bval, gval, rval, &lined[j]);
}
}
} else { /* L_VERT */
for (i = 0; i < hd; i++) {
linet = datat + 3 * i * wplt;
lined = datad + i * wpld;
for (j = 0; j < wd; j++) {
rval = GET_DATA_BYTE(linet, j);
gval = GET_DATA_BYTE(linet + wplt, j);
bval = GET_DATA_BYTE(linet + 2 * wplt, j);
if (order == L_SUBPIXEL_ORDER_VRGB)
composeRGBPixel(rval, gval, bval, &lined[j]);
else /* order VBGR */
composeRGBPixel(bval, gval, rval, &lined[j]);
}
}
}
pixDestroy(&pix1);
pixDestroy(&pix2);
return pixd;
}
/*!
* \brief pixConvertColorToSubpixelRGB()
*
* \param[in] pixs 32 bpp or colormapped
* \param[in] scalex, scaley
* \param[in] order of subpixel rgb color components in
* composition of pixd:
* L_SUBPIXEL_ORDER_RGB, L_SUBPIXEL_ORDER_BGR,
* L_SUBPIXEL_ORDER_VRGB, L_SUBPIXEL_ORDER_VBGR
* \return pixd 32 bpp, or NULL on error
*
* <pre>
* Notes:
* (1) If pixs has a colormap, it is removed to 32 bpp rgb.
* If the colormap has no color, pixConvertGrayToSubpixelRGB()
* should be called instead, because it will give the same result
* more efficiently. The function pixConvertToSubpixelRGB()
* will do the best thing for all cases.
* (2) For horizontal subpixel splitting, the input rgb image
* is rescaled by %scaley vertically and by 3.0 times
* %scalex horizontally. Then for each horizontal triplet
* of pixels, the r component of the final pixel is selected
* from the r component of the appropriate pixel in the triplet,
* and likewise for g and b. Vertical subpixel splitting is
* handled similarly.
* </pre>
*/
PIX *
pixConvertColorToSubpixelRGB(PIX *pixs,
l_float32 scalex,
l_float32 scaley,
l_int32 order)
{
l_int32 w, h, d, wd, hd, wplt, wpld, i, j, rval, gval, bval, direction;
l_uint32 *datat, *datad, *linet, *lined;
PIX *pix1, *pix2, *pixd;
PIXCMAP *cmap;
PROCNAME("pixConvertColorToSubpixelRGB");
if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
d = pixGetDepth(pixs);
cmap = pixGetColormap(pixs);
if (d != 32 && !cmap)
return (PIX *)ERROR_PTR("pix not 32 bpp & not cmapped", procName, NULL);
if (scalex <= 0.0 || scaley <= 0.0)
return (PIX *)ERROR_PTR("scale factors must be > 0", procName, NULL);
if (order != L_SUBPIXEL_ORDER_RGB && order != L_SUBPIXEL_ORDER_BGR &&
order != L_SUBPIXEL_ORDER_VRGB && order != L_SUBPIXEL_ORDER_VBGR)
return (PIX *)ERROR_PTR("invalid subpixel order", procName, NULL);
direction =
(order == L_SUBPIXEL_ORDER_RGB || order == L_SUBPIXEL_ORDER_BGR)
? L_HORIZ : L_VERT;
pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR);
if (direction == L_HORIZ)
pix2 = pixScale(pix1, 3.0 * scalex, scaley);
else /* L_VERT */
pix2 = pixScale(pix1, scalex, 3.0 * scaley);
pixGetDimensions(pix2, &w, &h, NULL);
wd = (direction == L_HORIZ) ? w / 3 : w;
hd = (direction == L_VERT) ? h / 3 : h;
pixd = pixCreate(wd, hd, 32);
pixCopyInputFormat(pixd, pixs);
datad = pixGetData(pixd);
wpld = pixGetWpl(pixd);
datat = pixGetData(pix2);
wplt = pixGetWpl(pix2);
if (direction == L_HORIZ) {
for (i = 0; i < hd; i++) {
linet = datat + i * wplt;
lined = datad + i * wpld;
for (j = 0; j < wd; j++) {
if (order == L_SUBPIXEL_ORDER_RGB) {
extractRGBValues(linet[3 * j], &rval, NULL, NULL);
extractRGBValues(linet[3 * j + 1], NULL, &gval, NULL);
extractRGBValues(linet[3 * j + 2], NULL, NULL, &bval);
} else { /* order BGR */
extractRGBValues(linet[3 * j], NULL, NULL, &bval);
extractRGBValues(linet[3 * j + 1], NULL, &gval, NULL);
extractRGBValues(linet[3 * j + 2], &rval, NULL, NULL);
}
composeRGBPixel(rval, gval, bval, &lined[j]);
}
}
} else { /* L_VERT */
for (i = 0; i < hd; i++) {
linet = datat + 3 * i * wplt;
lined = datad + i * wpld;
for (j = 0; j < wd; j++) {
if (order == L_SUBPIXEL_ORDER_VRGB) {
extractRGBValues(linet[j], &rval, NULL, NULL);
extractRGBValues((linet + wplt)[j], NULL, &gval, NULL);
extractRGBValues((linet + 2 * wplt)[j], NULL, NULL, &bval);
} else { /* order VBGR */
extractRGBValues(linet[j], NULL, NULL, &bval);
extractRGBValues((linet + wplt)[j], NULL, &gval, NULL);
extractRGBValues((linet + 2 * wplt)[j], &rval, NULL, NULL);
}
composeRGBPixel(rval, gval, bval, &lined[j]);
}
}
}
if (pixGetSpp(pixs) == 4)
pixScaleAndTransferAlpha(pixd, pixs, scalex, scaley);
pixDestroy(&pix1);
pixDestroy(&pix2);
return pixd;
}
/*---------------------------------------------------------------------*
* Setting neutral point for min/max boost conversion to gray *
*---------------------------------------------------------------------*/
/*!
* \brief l_setNeutralBoostVal()
*
* \param[in] val between 1 and 255; typical value is 180
* \return void
*
* <pre>
* Notes:
* (1) This raises or lowers the selected min or max RGB component value,
* depending on if that component is above or below this value.
* </pre>
*/
void
l_setNeutralBoostVal(l_int32 val)
{
PROCNAME("l_setNeutralBoostVal");
if (val <= 0) {
L_ERROR("invalid reference value for neutral boost\n", procName);
return;
}
var_NEUTRAL_BOOST_VAL = val;
}