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

2304 lines
62 KiB
C
Raw Permalink Normal View History

2021-11-20 06:24:33 +00:00
/*====================================================================*
- 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 colormap.c
* <pre>
*
* Colormap creation, copy, destruction, addition
* PIXCMAP *pixcmapCreate()
* PIXCMAP *pixcmapCreateRandom()
* PIXCMAP *pixcmapCreateLinear()
* PIXCMAP *pixcmapCopy()
* void pixcmapDestroy()
* l_int32 pixcmapIsValid()
* l_int32 pixcmapAddColor()
* l_int32 pixcmapAddRGBA()
* l_int32 pixcmapAddNewColor()
* l_int32 pixcmapAddNearestColor()
* l_int32 pixcmapUsableColor()
* l_int32 pixcmapAddBlackOrWhite()
* l_int32 pixcmapSetBlackAndWhite()
* l_int32 pixcmapGetCount()
* l_int32 pixcmapGetDepth()
* l_int32 pixcmapGetMinDepth()
* l_int32 pixcmapGetFreeCount()
* l_int32 pixcmapClear()
*
* Colormap random access and test
* l_int32 pixcmapGetColor()
* l_int32 pixcmapGetColor32()
* l_int32 pixcmapGetRGBA()
* l_int32 pixcmapGetRGBA32()
* l_int32 pixcmapResetColor()
* l_int32 pixcmapSetAlpha()
* l_int32 pixcmapGetIndex()
* l_int32 pixcmapHasColor()
* l_int32 pixcmapIsOpaque()
* l_int32 pixcmapIsBlackAndWhite()
* l_int32 pixcmapCountGrayColors()
* l_int32 pixcmapGetRankIntensity()
* l_int32 pixcmapGetNearestIndex()
* l_int32 pixcmapGetNearestGrayIndex()
* l_int32 pixcmapGetDistanceToColor()
* l_int32 pixcmapGetRangeValues()
*
* Colormap conversion
* PIXCMAP *pixcmapGrayToColor()
* PIXCMAP *pixcmapColorToGray()
* PIXCMAP *pixcmapConvertTo4()
* PIXCMAP *pixcmapConvertTo8()
*
* Colormap I/O
* l_int32 pixcmapRead()
* l_int32 pixcmapReadStream()
* l_int32 pixcmapReadMem()
* l_int32 pixcmapWrite()
* l_int32 pixcmapWriteStream()
* l_int32 pixcmapWriteMem()
*
* Extract colormap arrays and serialization
* l_int32 pixcmapToArrays()
* l_int32 pixcmapToRGBTable()
* l_int32 pixcmapSerializeToMemory()
* PIXCMAP *pixcmapDeserializeFromMemory()
* char *pixcmapConvertToHex()
*
* Colormap transforms
* l_int32 pixcmapGammaTRC()
* l_int32 pixcmapContrastTRC()
* l_int32 pixcmapShiftIntensity()
* l_int32 pixcmapShiftByComponent()
*
* Note:
* (1) colormaps in leptonica have a maximum of 256 entries.
* (2) nalloc, the allocated size of the palette array, is related
* to the depth d of the pixels by:
* nalloc = 2^(d)
*
* </pre>
*/
#include <string.h>
#include "allheaders.h"
/*-------------------------------------------------------------*
* Colormap creation and addition *
*-------------------------------------------------------------*/
/*!
* \brief pixcmapCreate()
*
* \param[in] depth of pix, in bpp
* \return cmap, or NULL on error
*/
PIXCMAP *
pixcmapCreate(l_int32 depth)
{
RGBA_QUAD *cta;
PIXCMAP *cmap;
PROCNAME("pixcmapCreate");
if (depth != 1 && depth != 2 && depth !=4 && depth != 8)
return (PIXCMAP *)ERROR_PTR("depth not in {1,2,4,8}", procName, NULL);
cmap = (PIXCMAP *)LEPT_CALLOC(1, sizeof(PIXCMAP));
cmap->depth = depth;
cmap->nalloc = 1 << depth;
cta = (RGBA_QUAD *)LEPT_CALLOC(cmap->nalloc, sizeof(RGBA_QUAD));
cmap->array = cta;
cmap->n = 0;
return cmap;
}
/*!
* \brief pixcmapCreateRandom()
*
* \param[in] depth of pix, in bpp: 2, 4 or 8
* \param[in] hasblack 1 if the first color is black; 0 if no black
* \param[in] haswhite 1 if the last color is white; 0 if no white
* \return cmap, or NULL on error
*
* <pre>
* Notes:
* (1) This sets up a colormap with random colors,
* where the first color is optionally black, the last color
* is optionally white, and the remaining colors are
* chosen randomly.
* (2) The number of randomly chosen colors is:
* 2^(depth) - haswhite - hasblack
* (3) Because rand() is seeded, it might disrupt otherwise
* deterministic results if also used elsewhere in a program.
* (4) rand() is not threadsafe, and will generate garbage if run
* on multiple threads at once -- though garbage is generally
* what you want from a random number generator!
* (5) Modern rand()s have equal randomness in low and high order
* bits, but older ones don't. Here, we're just using rand()
* to choose colors for output.
* </pre>
*/
PIXCMAP *
pixcmapCreateRandom(l_int32 depth,
l_int32 hasblack,
l_int32 haswhite)
{
l_int32 ncolors, i;
l_int32 red[256], green[256], blue[256];
PIXCMAP *cmap;
PROCNAME("pixcmapCreateRandom");
if (depth != 2 && depth != 4 && depth != 8)
return (PIXCMAP *)ERROR_PTR("depth not in {2, 4, 8}", procName, NULL);
if (hasblack != 0) hasblack = 1;
if (haswhite != 0) haswhite = 1;
cmap = pixcmapCreate(depth);
ncolors = 1 << depth;
if (hasblack) /* first color is optionally black */
pixcmapAddColor(cmap, 0, 0, 0);
for (i = hasblack; i < ncolors - haswhite; i++) {
red[i] = (l_uint32)rand() & 0xff;
green[i] = (l_uint32)rand() & 0xff;
blue[i] = (l_uint32)rand() & 0xff;
pixcmapAddColor(cmap, red[i], green[i], blue[i]);
}
if (haswhite) /* last color is optionally white */
pixcmapAddColor(cmap, 255, 255, 255);
return cmap;
}
/*!
* \brief pixcmapCreateLinear()
*
* \param[in] d depth of pix for this colormap; 1, 2, 4 or 8
* \param[in] nlevels valid in range [2, 2^d]
* \return cmap, or NULL on error
*
* <pre>
* Notes:
* (1) Colormap has equally spaced gray color values
* from black (0, 0, 0) to white (255, 255, 255).
* </pre>
*/
PIXCMAP *
pixcmapCreateLinear(l_int32 d,
l_int32 nlevels)
{
l_int32 maxlevels, i, val;
PIXCMAP *cmap;
PROCNAME("pixcmapCreateLinear");
if (d != 1 && d != 2 && d !=4 && d != 8)
return (PIXCMAP *)ERROR_PTR("d not in {1, 2, 4, 8}", procName, NULL);
maxlevels = 1 << d;
if (nlevels < 2 || nlevels > maxlevels)
return (PIXCMAP *)ERROR_PTR("invalid nlevels", procName, NULL);
cmap = pixcmapCreate(d);
for (i = 0; i < nlevels; i++) {
val = (255 * i) / (nlevels - 1);
pixcmapAddColor(cmap, val, val, val);
}
return cmap;
}
/*!
* \brief pixcmapCopy()
*
* \param[in] cmaps
* \return cmapd, or NULL on error
*/
PIXCMAP *
pixcmapCopy(const PIXCMAP *cmaps)
{
l_int32 nbytes, valid;
PIXCMAP *cmapd;
PROCNAME("pixcmapCopy");
if (!cmaps)
return (PIXCMAP *)ERROR_PTR("cmaps not defined", procName, NULL);
pixcmapIsValid(cmaps, &valid);
if (!valid)
return (PIXCMAP *)ERROR_PTR("invalid cmap", procName, NULL);
cmapd = (PIXCMAP *)LEPT_CALLOC(1, sizeof(PIXCMAP));
nbytes = cmaps->nalloc * sizeof(RGBA_QUAD);
cmapd->array = (void *)LEPT_CALLOC(1, nbytes);
memcpy(cmapd->array, cmaps->array, cmaps->n * sizeof(RGBA_QUAD));
cmapd->n = cmaps->n;
cmapd->nalloc = cmaps->nalloc;
cmapd->depth = cmaps->depth;
return cmapd;
}
/*!
* \brief pixcmapDestroy()
*
* \param[in,out] pcmap set to null on return
* \return void
*/
void
pixcmapDestroy(PIXCMAP **pcmap)
{
PIXCMAP *cmap;
PROCNAME("pixcmapDestroy");
if (pcmap == NULL) {
L_WARNING("ptr address is null!\n", procName);
return;
}
if ((cmap = *pcmap) == NULL)
return;
LEPT_FREE(cmap->array);
LEPT_FREE(cmap);
*pcmap = NULL;
return;
}
/*!
* \brief pixcmapIsValid()
*
* \param[in] cmap
* \param[out] pvalid return 1 if valid; 0 if not
* \return 0 if OK, 1 on error or if cmap is not valid
*/
l_ok
pixcmapIsValid(const PIXCMAP *cmap,
l_int32 *pvalid)
{
l_int32 d;
PROCNAME("pixcmapIsValid");
if (!pvalid)
return ERROR_INT("&valid not defined", procName, 1);
*pvalid = 0;
if (!cmap)
return ERROR_INT("cmap not defined", procName, 1);
if (!cmap->array)
return ERROR_INT("cmap array not defined", procName, 1);
d = cmap->depth;
if (d !=1 && d != 2 && d != 4 && d != 8) {
L_ERROR("invalid cmap depth: %d\n", procName, d);
return 1;
}
if (cmap->nalloc < 2 || cmap->nalloc > 256) {
L_ERROR("invalid cmap nalloc: %d\n", procName, cmap->nalloc);
return 1;
}
if (cmap->n < 0 || cmap->n > 256 || cmap->n > cmap->nalloc) {
L_ERROR("invalid cmap n: %d (nalloc = %d)\n", procName,
cmap->n, cmap->nalloc);
return 1;
}
*pvalid = 1;
return 0;
}
/*!
* \brief pixcmapAddColor()
*
* \param[in] cmap
* \param[in] rval, gval, bval colormap entry to be added; each number
* is in range [0, ... 255]
* \return 0 if OK, 1 on error
*
* <pre>
* Notes:
* (1) This always adds the color if there is room.
* (2) The alpha component is 255 (opaque)
* </pre>
*/
l_ok
pixcmapAddColor(PIXCMAP *cmap,
l_int32 rval,
l_int32 gval,
l_int32 bval)
{
RGBA_QUAD *cta;
PROCNAME("pixcmapAddColor");
if (!cmap)
return ERROR_INT("cmap not defined", procName, 1);
if (cmap->n >= cmap->nalloc)
return ERROR_INT("no free color entries", procName, 1);
cta = (RGBA_QUAD *)cmap->array;
cta[cmap->n].red = rval;
cta[cmap->n].green = gval;
cta[cmap->n].blue = bval;
cta[cmap->n].alpha = 255;
cmap->n++;
return 0;
}
/*!
* \brief pixcmapAddRGBA()
*
* \param[in] cmap
* \param[in] rval, gval, bval, aval colormap entry to be added;
* each number is in range [0, ... 255]
* \return 0 if OK, 1 on error
*
* <pre>
* Notes:
* (1) This always adds the color if there is room.
* </pre>
*/
l_ok
pixcmapAddRGBA(PIXCMAP *cmap,
l_int32 rval,
l_int32 gval,
l_int32 bval,
l_int32 aval)
{
RGBA_QUAD *cta;
PROCNAME("pixcmapAddRGBA");
if (!cmap)
return ERROR_INT("cmap not defined", procName, 1);
if (cmap->n >= cmap->nalloc)
return ERROR_INT("no free color entries", procName, 1);
cta = (RGBA_QUAD *)cmap->array;
cta[cmap->n].red = rval;
cta[cmap->n].green = gval;
cta[cmap->n].blue = bval;
cta[cmap->n].alpha = aval;
cmap->n++;
return 0;
}
/*!
* \brief pixcmapAddNewColor()
*
* \param[in] cmap
* \param[in] rval, gval, bval colormap entry to be added; each number
* is in range [0, ... 255]
* \param[out] pindex index of color
* \return 0 if OK, 1 on error; 2 if unable to add color
*
* <pre>
* Notes:
* (1) This only adds color if not already there.
* (2) The alpha component is 255 (opaque)
* (3) This returns the index of the new (or existing) color.
* (4) Returns 2 with a warning if unable to add this color;
* the caller should check the return value.
* </pre>
*/
l_ok
pixcmapAddNewColor(PIXCMAP *cmap,
l_int32 rval,
l_int32 gval,
l_int32 bval,
l_int32 *pindex)
{
PROCNAME("pixcmapAddNewColor");
if (!pindex)
return ERROR_INT("&index not defined", procName, 1);
*pindex = 0;
if (!cmap)
return ERROR_INT("cmap not defined", procName, 1);
/* Check if the color is already present. */
if (!pixcmapGetIndex(cmap, rval, gval, bval, pindex)) /* found */
return 0;
/* We need to add the color. Is there room? */
if (cmap->n >= cmap->nalloc) {
L_WARNING("no free color entries\n", procName);
return 2;
}
/* There's room. Add it. */
pixcmapAddColor(cmap, rval, gval, bval);
*pindex = pixcmapGetCount(cmap) - 1;
return 0;
}
/*!
* \brief pixcmapAddNearestColor()
*
* \param[in] cmap
* \param[in] rval, gval, bval colormap entry to be added; each number
* is in range [0, ... 255]
* \param[out] pindex index of color
* \return 0 if OK, 1 on error
*
* <pre>
* Notes:
* (1) This only adds color if not already there.
* (2) The alpha component is 255 (opaque)
* (3) If it's not in the colormap and there is no room to add
* another color, this returns the index of the nearest color.
* </pre>
*/
l_ok
pixcmapAddNearestColor(PIXCMAP *cmap,
l_int32 rval,
l_int32 gval,
l_int32 bval,
l_int32 *pindex)
{
PROCNAME("pixcmapAddNearestColor");
if (!pindex)
return ERROR_INT("&index not defined", procName, 1);
*pindex = 0;
if (!cmap)
return ERROR_INT("cmap not defined", procName, 1);
/* Check if the color is already present. */
if (!pixcmapGetIndex(cmap, rval, gval, bval, pindex)) /* found */
return 0;
/* We need to add the color. Is there room? */
if (cmap->n < cmap->nalloc) {
pixcmapAddColor(cmap, rval, gval, bval);
*pindex = pixcmapGetCount(cmap) - 1;
return 0;
}
/* There's no room. Return the index of the nearest color */
pixcmapGetNearestIndex(cmap, rval, gval, bval, pindex);
return 0;
}
/*!
* \brief pixcmapUsableColor()
*
* \param[in] cmap
* \param[in] rval, gval, bval colormap entry to be added; each number
* is in range [0, ... 255]
* \param[out] pusable 1 if usable; 0 if not
* \return 0 if OK, 1 on error
*
* <pre>
* Notes:
* (1) This checks if the color already exists or if there is
* room to add it. It makes no change in the colormap.
* </pre>
*/
l_ok
pixcmapUsableColor(PIXCMAP *cmap,
l_int32 rval,
l_int32 gval,
l_int32 bval,
l_int32 *pusable)
{
l_int32 index;
PROCNAME("pixcmapUsableColor");
if (!pusable)
return ERROR_INT("&usable not defined", procName, 1);
*pusable = 0;
if (!cmap)
return ERROR_INT("cmap not defined", procName, 1);
/* Is there room to add it? */
if (cmap->n < cmap->nalloc) {
*pusable = 1;
return 0;
}
/* No room; check if the color is already present. */
if (!pixcmapGetIndex(cmap, rval, gval, bval, &index)) /* found */
*pusable = 1;
return 0;
}
/*!
* \brief pixcmapAddBlackOrWhite()
*
* \param[in] cmap
* \param[in] color 0 for black, 1 for white
* \param[out] pindex [optional] index of color; can be null
* \return 0 if OK, 1 on error
*
* <pre>
* Notes:
* (1) This only adds color if not already there.
* (2) The alpha component is 255 (opaque)
* (3) This sets index to the requested color.
* (4) If there is no room in the colormap, returns the index
* of the closest color.
* </pre>
*/
l_ok
pixcmapAddBlackOrWhite(PIXCMAP *cmap,
l_int32 color,
l_int32 *pindex)
{
l_int32 index;
PROCNAME("pixcmapAddBlackOrWhite");
if (pindex) *pindex = 0;
if (!cmap)
return ERROR_INT("cmap not defined", procName, 1);
if (color == 0) { /* black */
if (pixcmapGetFreeCount(cmap) > 0)
pixcmapAddNewColor(cmap, 0, 0, 0, &index);
else
pixcmapGetRankIntensity(cmap, 0.0, &index);
} else { /* white */
if (pixcmapGetFreeCount(cmap) > 0)
pixcmapAddNewColor(cmap, 255, 255, 255, &index);
else
pixcmapGetRankIntensity(cmap, 1.0, &index);
}
if (pindex)
*pindex = index;
return 0;
}
/*!
* \brief pixcmapSetBlackAndWhite()
*
* \param[in] cmap
* \param[in] setblack 0 for no operation; 1 to set darkest color to black
* \param[in] setwhite 0 for no operation; 1 to set lightest color to white
* \return 0 if OK, 1 on error
*/
l_ok
pixcmapSetBlackAndWhite(PIXCMAP *cmap,
l_int32 setblack,
l_int32 setwhite)
{
l_int32 index;
PROCNAME("pixcmapSetBlackAndWhite");
if (!cmap)
return ERROR_INT("cmap not defined", procName, 1);
if (setblack) {
pixcmapGetRankIntensity(cmap, 0.0, &index);
pixcmapResetColor(cmap, index, 0, 0, 0);
}
if (setwhite) {
pixcmapGetRankIntensity(cmap, 1.0, &index);
pixcmapResetColor(cmap, index, 255, 255, 255);
}
return 0;
}
/*!
* \brief pixcmapGetCount()
*
* \param[in] cmap
* \return count, or 0 on error
*/
l_int32
pixcmapGetCount(const PIXCMAP *cmap)
{
PROCNAME("pixcmapGetCount");
if (!cmap)
return ERROR_INT("cmap not defined", procName, 0);
return cmap->n;
}
/*!
* \brief pixcmapGetFreeCount()
*
* \param[in] cmap
* \return free entries, or 0 on error
*/
l_int32
pixcmapGetFreeCount(PIXCMAP *cmap)
{
PROCNAME("pixcmapGetFreeCount");
if (!cmap)
return ERROR_INT("cmap not defined", procName, 0);
return (cmap->nalloc - cmap->n);
}
/*!
* \brief pixcmapGetDepth()
*
* \param[in] cmap
* \return depth, or 0 on error
*/
l_int32
pixcmapGetDepth(PIXCMAP *cmap)
{
PROCNAME("pixcmapGetDepth");
if (!cmap)
return ERROR_INT("cmap not defined", procName, 0);
return cmap->depth;
}
/*!
* \brief pixcmapGetMinDepth()
*
* \param[in] cmap
* \param[out] pmindepth minimum depth to support the colormap
* \return 0 if OK, 1 on error
*
* <pre>
* Notes:
* (1) On error, &mindepth is returned as 0.
* </pre>
*/
l_ok
pixcmapGetMinDepth(PIXCMAP *cmap,
l_int32 *pmindepth)
{
l_int32 ncolors;
PROCNAME("pixcmapGetMinDepth");
if (!pmindepth)
return ERROR_INT("&mindepth not defined", procName, 1);
*pmindepth = 0;
if (!cmap)
return ERROR_INT("cmap not defined", procName, 1);
ncolors = pixcmapGetCount(cmap);
if (ncolors <= 4)
*pmindepth = 2;
else if (ncolors <= 16)
*pmindepth = 4;
else /* ncolors > 16 */
*pmindepth = 8;
return 0;
}
/*!
* \brief pixcmapClear()
*
* \param[in] cmap
* \return 0 if OK, 1 on error
*
* <pre>
* Notes:
* (1) This removes the colors by setting the count to 0.
* </pre>
*/
l_ok
pixcmapClear(PIXCMAP *cmap)
{
PROCNAME("pixcmapClear");
if (!cmap)
return ERROR_INT("cmap not defined", procName, 1);
cmap->n = 0;
return 0;
}
/*-------------------------------------------------------------*
* Colormap random access *
*-------------------------------------------------------------*/
/*!
* \brief pixcmapGetColor()
*
* \param[in] cmap
* \param[in] index
* \param[out] prval, pgval, pbval each color value
* \return 0 if OK, 1 if not accessible caller should check
*/
l_ok
pixcmapGetColor(PIXCMAP *cmap,
l_int32 index,
l_int32 *prval,
l_int32 *pgval,
l_int32 *pbval)
{
RGBA_QUAD *cta;
PROCNAME("pixcmapGetColor");
if (!prval || !pgval || !pbval)
return ERROR_INT("&rval, &gval, &bval not all defined", procName, 1);
*prval = *pgval = *pbval = 0;
if (!cmap)
return ERROR_INT("cmap not defined", procName, 1);
if (index < 0 || index >= cmap->n)
return ERROR_INT("index out of bounds", procName, 1);
cta = (RGBA_QUAD *)cmap->array;
*prval = cta[index].red;
*pgval = cta[index].green;
*pbval = cta[index].blue;
return 0;
}
/*!
* \brief pixcmapGetColor32()
*
* \param[in] cmap
* \param[in] index
* \param[out] pval32 32-bit rgb color value
* \return 0 if OK, 1 if not accessible caller should check
*
* <pre>
* Notes:
* (1) The returned alpha channel value is 255.
* </pre>
*/
l_ok
pixcmapGetColor32(PIXCMAP *cmap,
l_int32 index,
l_uint32 *pval32)
{
l_int32 rval, gval, bval;
PROCNAME("pixcmapGetColor32");
if (!pval32)
return ERROR_INT("&val32 not defined", procName, 1);
*pval32 = 0;
if (pixcmapGetColor(cmap, index, &rval, &gval, &bval) != 0)
return ERROR_INT("rgb values not found", procName, 1);
composeRGBAPixel(rval, gval, bval, 255, pval32);
return 0;
}
/*!
* \brief pixcmapGetRGBA()
*
* \param[in] cmap
* \param[in] index
* \param[out] prval, pgval, pbval, paval each color value
* \return 0 if OK, 1 if not accessible caller should check
*/
l_ok
pixcmapGetRGBA(PIXCMAP *cmap,
l_int32 index,
l_int32 *prval,
l_int32 *pgval,
l_int32 *pbval,
l_int32 *paval)
{
RGBA_QUAD *cta;
PROCNAME("pixcmapGetRGBA");
if (!prval || !pgval || !pbval || !paval)
return ERROR_INT("&rval, &gval, &bval, &aval not all defined",
procName, 1);
*prval = *pgval = *pbval = *paval = 0;
if (!cmap)
return ERROR_INT("cmap not defined", procName, 1);
if (index < 0 || index >= cmap->n)
return ERROR_INT("index out of bounds", procName, 1);
cta = (RGBA_QUAD *)cmap->array;
*prval = cta[index].red;
*pgval = cta[index].green;
*pbval = cta[index].blue;
*paval = cta[index].alpha;
return 0;
}
/*!
* \brief pixcmapGetRGBA32()
*
* \param[in] cmap
* \param[in] index
* \param[out] pval32 32-bit rgba color value
* \return 0 if OK, 1 if not accessible caller should check
*/
l_ok
pixcmapGetRGBA32(PIXCMAP *cmap,
l_int32 index,
l_uint32 *pval32)
{
l_int32 rval, gval, bval, aval;
PROCNAME("pixcmapGetRGBA32");
if (!pval32)
return ERROR_INT("&val32 not defined", procName, 1);
*pval32 = 0;
if (pixcmapGetRGBA(cmap, index, &rval, &gval, &bval, &aval) != 0)
return ERROR_INT("rgba values not found", procName, 1);
composeRGBAPixel(rval, gval, bval, aval, pval32);
return 0;
}
/*!
* \brief pixcmapResetColor()
*
* \param[in] cmap
* \param[in] index
* \param[in] rval, gval, bval colormap entry to be reset; each number
* is in range [0, ... 255]
* \return 0 if OK, 1 if not accessible caller should check
*
* <pre>
* Notes:
* (1) This resets sets the color of an entry that has already
* been set and included in the count of colors.
* (2) The alpha component is 255 (opaque)
* </pre>
*/
l_ok
pixcmapResetColor(PIXCMAP *cmap,
l_int32 index,
l_int32 rval,
l_int32 gval,
l_int32 bval)
{
RGBA_QUAD *cta;
PROCNAME("pixcmapResetColor");
if (!cmap)
return ERROR_INT("cmap not defined", procName, 1);
if (index < 0 || index >= cmap->n)
return ERROR_INT("index out of bounds", procName, 1);
cta = (RGBA_QUAD *)cmap->array;
cta[index].red = rval;
cta[index].green = gval;
cta[index].blue = bval;
cta[index].alpha = 255;
return 0;
}
/*!
* \brief pixcmapSetAlpha()
*
* \param[in] cmap
* \param[in] index
* \param[in] aval in range [0, ... 255]
* \return 0 if OK, 1 on error
*
* <pre>
* Notes:
* (1) This modifies the transparency of one entry in a colormap.
* The alpha component by default is 255 (opaque).
* This is used when extracting the colormap from a PNG file
* without decoding the image.
* </pre>
*/
l_ok
pixcmapSetAlpha(PIXCMAP *cmap,
l_int32 index,
l_int32 aval)
{
RGBA_QUAD *cta;
PROCNAME("pixcmapSetAlpha");
if (!cmap)
return ERROR_INT("cmap not defined", procName, 1);
if (index < 0 || index >= cmap->n)
return ERROR_INT("index out of bounds", procName, 1);
cta = (RGBA_QUAD *)cmap->array;
cta[index].alpha = aval;
return 0;
}
/*!
* \brief pixcmapGetIndex()
*
* \param[in] cmap
* \param[in] rval, gval, bval colormap colors to search for; each number
* is in range [0, ... 255]
* \param[out] pindex value of index found
* \return 0 if found, 1 if not found caller must check
*/
l_int32
pixcmapGetIndex(PIXCMAP *cmap,
l_int32 rval,
l_int32 gval,
l_int32 bval,
l_int32 *pindex)
{
l_int32 n, i;
RGBA_QUAD *cta;
PROCNAME("pixcmapGetIndex");
if (!pindex)
return ERROR_INT("&index not defined", procName, 1);
*pindex = 0;
if (!cmap)
return ERROR_INT("cmap not defined", procName, 1);
n = pixcmapGetCount(cmap);
cta = (RGBA_QUAD *)cmap->array;
for (i = 0; i < n; i++) {
if (rval == cta[i].red &&
gval == cta[i].green &&
bval == cta[i].blue) {
*pindex = i;
return 0;
}
}
return 1;
}
/*!
* \brief pixcmapHasColor()
*
* \param[in] cmap
* \param[out] pcolor TRUE if cmap has color; FALSE otherwise
* \return 0 if OK, 1 on error
*/
l_ok
pixcmapHasColor(PIXCMAP *cmap,
l_int32 *pcolor)
{
l_int32 n, i;
l_int32 *rmap, *gmap, *bmap;
PROCNAME("pixcmapHasColor");
if (!pcolor)
return ERROR_INT("&color not defined", procName, 1);
*pcolor = FALSE;
if (!cmap)
return ERROR_INT("cmap not defined", procName, 1);
if (pixcmapToArrays(cmap, &rmap, &gmap, &bmap, NULL))
return ERROR_INT("colormap arrays not made", procName, 1);
n = pixcmapGetCount(cmap);
for (i = 0; i < n; i++) {
if ((rmap[i] != gmap[i]) || (rmap[i] != bmap[i])) {
*pcolor = TRUE;
break;
}
}
LEPT_FREE(rmap);
LEPT_FREE(gmap);
LEPT_FREE(bmap);
return 0;
}
/*!
* \brief pixcmapIsOpaque()
*
* \param[in] cmap
* \param[out] popaque TRUE if fully opaque: all entries are 255
* \return 0 if OK, 1 on error
*/
l_ok
pixcmapIsOpaque(PIXCMAP *cmap,
l_int32 *popaque)
{
l_int32 i, n;
RGBA_QUAD *cta;
PROCNAME("pixcmapIsOpaque");
if (!popaque)
return ERROR_INT("&opaque not defined", procName, 1);
*popaque = TRUE;
if (!cmap)
return ERROR_INT("cmap not defined", procName, 1);
n = pixcmapGetCount(cmap);
cta = (RGBA_QUAD *)cmap->array;
for (i = 0; i < n; i++) {
if (cta[i].alpha != 255) {
*popaque = FALSE;
break;
}
}
return 0;
}
/*!
* \brief pixcmapIsBlackAndWhite()
*
* \param[in] cmap
* \param[out] pblackwhite TRUE if the cmap has only two colors:
* black (0,0,0) and white (255,255,255)
* \return 0 if OK, 1 on error
*/
l_ok
pixcmapIsBlackAndWhite(PIXCMAP *cmap,
l_int32 *pblackwhite)
{
l_int32 val0, val1, hascolor;
RGBA_QUAD *cta;
PROCNAME("pixcmapIsBlackAndWhite");
if (!pblackwhite)
return ERROR_INT("&blackwhite not defined", procName, 1);
*pblackwhite = FALSE;
if (!cmap)
return ERROR_INT("cmap not defined", procName, 1);
if (pixcmapGetCount(cmap) != 2)
return 0;
pixcmapHasColor(cmap, &hascolor);
if (hascolor) return 0;
cta = (RGBA_QUAD *)cmap->array;
val0 = cta[0].red;
val1 = cta[1].red;
if ((val0 == 0 && val1 == 255) || (val0 == 255 && val1 == 0))
*pblackwhite = TRUE;
return 0;
}
/*!
* \brief pixcmapCountGrayColors()
*
* \param[in] cmap
* \param[out] pngray number of gray colors
* \return 0 if OK, 1 on error
*
* <pre>
* Notes:
* (1) This counts the unique gray colors, including black and white.
* </pre>
*/
l_ok
pixcmapCountGrayColors(PIXCMAP *cmap,
l_int32 *pngray)
{
l_int32 n, i, rval, gval, bval, count;
l_int32 *array;
PROCNAME("pixcmapCountGrayColors");
if (!pngray)
return ERROR_INT("&ngray not defined", procName, 1);
*pngray = 0;
if (!cmap)
return ERROR_INT("cmap not defined", procName, 1);
array = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32));
n = pixcmapGetCount(cmap);
count = 0;
for (i = 0; i < n; i++) {
pixcmapGetColor(cmap, i, &rval, &gval, &bval);
if ((rval == gval) && (rval == bval) && (array[rval] == 0)) {
array[rval] = 1;
count++;
}
}
LEPT_FREE(array);
*pngray = count;
return 0;
}
/*!
* \brief pixcmapGetRankIntensity()
*
* \param[in] cmap
* \param[in] rankval 0.0 for darkest, 1.0 for lightest color
* \param[out] pindex the index into the colormap that corresponds
* to the rank intensity color
* \return 0 if OK, 1 on error
*/
l_ok
pixcmapGetRankIntensity(PIXCMAP *cmap,
l_float32 rankval,
l_int32 *pindex)
{
l_int32 n, i, rval, gval, bval, rankindex;
NUMA *na, *nasort;
PROCNAME("pixcmapGetRankIntensity");
if (!pindex)
return ERROR_INT("&index not defined", procName, 1);
*pindex = 0;
if (!cmap)
return ERROR_INT("cmap not defined", procName, 1);
if (rankval < 0.0 || rankval > 1.0)
return ERROR_INT("rankval not in [0.0 ... 1.0]", procName, 1);
n = pixcmapGetCount(cmap);
na = numaCreate(n);
for (i = 0; i < n; i++) {
pixcmapGetColor(cmap, i, &rval, &gval, &bval);
numaAddNumber(na, rval + gval + bval);
}
nasort = numaGetSortIndex(na, L_SORT_INCREASING);
rankindex = (l_int32)(rankval * (n - 1) + 0.5);
numaGetIValue(nasort, rankindex, pindex);
numaDestroy(&na);
numaDestroy(&nasort);
return 0;
}
/*!
* \brief pixcmapGetNearestIndex()
*
* \param[in] cmap
* \param[in] rval, gval, bval colormap colors to search for; each number
* is in range [0, ... 255]
* \param[out] pindex the index of the nearest color
* \return 0 if OK, 1 on error caller must check
*
* <pre>
* Notes:
* (1) Returns the index of the exact color if possible, otherwise the
* index of the color closest to the target color.
* (2) Nearest color is that which is the least sum-of-squares distance
* from the target color.
* </pre>
*/
l_ok
pixcmapGetNearestIndex(PIXCMAP *cmap,
l_int32 rval,
l_int32 gval,
l_int32 bval,
l_int32 *pindex)
{
l_int32 i, n, delta, dist, mindist;
RGBA_QUAD *cta;
PROCNAME("pixcmapGetNearestIndex");
if (!pindex)
return ERROR_INT("&index not defined", procName, 1);
*pindex = UNDEF;
if (!cmap)
return ERROR_INT("cmap not defined", procName, 1);
if ((cta = (RGBA_QUAD *)cmap->array) == NULL)
return ERROR_INT("cta not defined(!)", procName, 1);
n = pixcmapGetCount(cmap);
mindist = 3 * 255 * 255 + 1;
for (i = 0; i < n; i++) {
delta = cta[i].red - rval;
dist = delta * delta;
delta = cta[i].green - gval;
dist += delta * delta;
delta = cta[i].blue - bval;
dist += delta * delta;
if (dist < mindist) {
*pindex = i;
if (dist == 0)
break;
mindist = dist;
}
}
return 0;
}
/*!
* \brief pixcmapGetNearestGrayIndex()
*
* \param[in] cmap
* \param[in] val gray value to search for; in range [0, ... 255]
* \param[out] pindex the index of the nearest color
* \return 0 if OK, 1 on error caller must check
*
* <pre>
* Notes:
* (1) This should be used on gray colormaps. It uses only the
* green value of the colormap.
* (2) Returns the index of the exact color if possible, otherwise the
* index of the color closest to the target color.
* </pre>
*/
l_ok
pixcmapGetNearestGrayIndex(PIXCMAP *cmap,
l_int32 val,
l_int32 *pindex)
{
l_int32 i, n, dist, mindist;
RGBA_QUAD *cta;
PROCNAME("pixcmapGetNearestGrayIndex");
if (!pindex)
return ERROR_INT("&index not defined", procName, 1);
*pindex = 0;
if (!cmap)
return ERROR_INT("cmap not defined", procName, 1);
if (val < 0 || val > 255)
return ERROR_INT("val not in [0 ... 255]", procName, 1);
if ((cta = (RGBA_QUAD *)cmap->array) == NULL)
return ERROR_INT("cta not defined(!)", procName, 1);
n = pixcmapGetCount(cmap);
mindist = 256;
for (i = 0; i < n; i++) {
dist = cta[i].green - val;
dist = L_ABS(dist);
if (dist < mindist) {
*pindex = i;
if (dist == 0)
break;
mindist = dist;
}
}
return 0;
}
/*!
* \brief pixcmapGetDistanceToColor()
*
* \param[in] cmap
* \param[in] index
* \param[in] rval, gval, bval target color
* \param[out] pdist the distance from the cmap entry to target
* \return 0 if OK, 1 on error
*
* <pre>
* Notes:
* (1) Returns the L2 distance (squared) between the color at index i
* and the target color.
* </pre>
*/
l_ok
pixcmapGetDistanceToColor(PIXCMAP *cmap,
l_int32 index,
l_int32 rval,
l_int32 gval,
l_int32 bval,
l_int32 *pdist)
{
l_int32 n, delta, dist;
RGBA_QUAD *cta;
PROCNAME("pixcmapGetDistanceToColor");
if (!pdist)
return ERROR_INT("&dist not defined", procName, 1);
*pdist = UNDEF;
if (!cmap)
return ERROR_INT("cmap not defined", procName, 1);
n = pixcmapGetCount(cmap);
if (index >= n)
return ERROR_INT("invalid index", procName, 1);
if ((cta = (RGBA_QUAD *)cmap->array) == NULL)
return ERROR_INT("cta not defined(!)", procName, 1);
delta = cta[index].red - rval;
dist = delta * delta;
delta = cta[index].green - gval;
dist += delta * delta;
delta = cta[index].blue - bval;
dist += delta * delta;
*pdist = dist;
return 0;
}
/*!
* \brief pixcmapGetRangeValues()
*
* \param[in] cmap
* \param[in] select L_SELECT_RED, L_SELECT_GREEN, L_SELECT_BLUE or
* L_SELECT_AVERAGE
* \param[out] pminval [optional] minimum value of component
* \param[out] pmaxval [optional] maximum value of component
* \param[out] pminindex [optional] index of minimum value
* \param[out] pmaxindex [optional] index of maximum value
* \return 0 if OK, 1 on error
*
* <pre>
* Notes:
* (1) Returns, for selected components (or the average), the
* the extreme values (min and/or max) and their indices
* that are found in the cmap.
* </pre>
*/
l_ok
pixcmapGetRangeValues(PIXCMAP *cmap,
l_int32 select,
l_int32 *pminval,
l_int32 *pmaxval,
l_int32 *pminindex,
l_int32 *pmaxindex)
{
l_int32 i, n, imin, imax, minval, maxval, rval, gval, bval, aveval;
PROCNAME("pixcmapGetRangeValues");
if (pminval) *pminval = UNDEF;
if (pmaxval) *pmaxval = UNDEF;
if (pminindex) *pminindex = UNDEF;
if (pmaxindex) *pmaxindex = UNDEF;
if (!pminval && !pmaxval && !pminindex && !pmaxindex)
return ERROR_INT("no result requested", procName, 1);
if (!cmap)
return ERROR_INT("cmap not defined", procName, 1);
imin = UNDEF;
imax = UNDEF;
minval = 100000;
maxval = -1;
n = pixcmapGetCount(cmap);
for (i = 0; i < n; i++) {
pixcmapGetColor(cmap, i, &rval, &gval, &bval);
if (select == L_SELECT_RED) {
if (rval < minval) {
minval = rval;
imin = i;
}
if (rval > maxval) {
maxval = rval;
imax = i;
}
} else if (select == L_SELECT_GREEN) {
if (gval < minval) {
minval = gval;
imin = i;
}
if (gval > maxval) {
maxval = gval;
imax = i;
}
} else if (select == L_SELECT_BLUE) {
if (bval < minval) {
minval = bval;
imin = i;
}
if (bval > maxval) {
maxval = bval;
imax = i;
}
} else if (select == L_SELECT_AVERAGE) {
aveval = (rval + gval + bval) / 3;
if (aveval < minval) {
minval = aveval;
imin = i;
}
if (aveval > maxval) {
maxval = aveval;
imax = i;
}
} else {
return ERROR_INT("invalid selection", procName, 1);
}
}
if (pminval) *pminval = minval;
if (pmaxval) *pmaxval = maxval;
if (pminindex) *pminindex = imin;
if (pmaxindex) *pmaxindex = imax;
return 0;
}
/*-------------------------------------------------------------*
* Colormap conversion *
*-------------------------------------------------------------*/
/*!
* \brief pixcmapGrayToColor()
*
* \param[in] color
* \return cmap, or NULL on error
*
* <pre>
* Notes:
* (1) This creates a colormap that maps from gray to
* a specific color. In the mapping, each component
* is faded to white, depending on the gray value.
* (2) In use, this is simply attached to a grayscale pix
* to give it the input color.
* </pre>
*/
PIXCMAP *
pixcmapGrayToColor(l_uint32 color)
{
l_int32 i, rval, gval, bval;
PIXCMAP *cmap;
extractRGBValues(color, &rval, &gval, &bval);
cmap = pixcmapCreate(8);
for (i = 0; i < 256; i++) {
pixcmapAddColor(cmap, rval + (i * (255 - rval)) / 255,
gval + (i * (255 - gval)) / 255,
bval + (i * (255 - bval)) / 255);
}
return cmap;
}
/*!
* \brief pixcmapColorToGray()
*
* \param[in] cmaps
* \param[in] rwt, gwt, bwt non-negative; these should add to 1.0
* \return cmap gray, or NULL on error
*
* <pre>
* Notes:
* (1) This creates a gray colormap from an arbitrary colormap.
* (2) In use, attach the output gray colormap to the pix
* (or a copy of it) that provided the input colormap.
* </pre>
*/
PIXCMAP *
pixcmapColorToGray(PIXCMAP *cmaps,
l_float32 rwt,
l_float32 gwt,
l_float32 bwt)
{
l_int32 i, n, rval, gval, bval, val;
l_float32 sum;
PIXCMAP *cmapd;
PROCNAME("pixcmapColorToGray");
if (!cmaps)
return (PIXCMAP *)ERROR_PTR("cmaps not defined", procName, NULL);
if (rwt < 0.0 || gwt < 0.0 || bwt < 0.0)
return (PIXCMAP *)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. */
sum = rwt + gwt + bwt;
if (sum == 0.0) {
L_WARNING("all weights zero; setting equal to 1/3\n", procName);
rwt = gwt = bwt = 0.33333;
sum = 1.0;
}
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;
}
if ((cmapd = pixcmapCopy(cmaps)) == NULL)
return (PIXCMAP *)ERROR_PTR("cmapd not made", procName, NULL);
n = pixcmapGetCount(cmapd);
for (i = 0; i < n; i++) {
pixcmapGetColor(cmapd, i, &rval, &gval, &bval);
val = (l_int32)(rwt * rval + gwt * gval + bwt * bval + 0.5);
pixcmapResetColor(cmapd, i, val, val, val);
}
return cmapd;
}
/*!
* \brief pixcmapConvertTo4()
*
* \param[in] cmaps colormap for 2 bpp pix
* \return cmapd (4 bpp)
*
* <pre>
* Notes:
* (1) This converts a 2 bpp colormap to 4 bpp. The colors
* are the same; the output colormap entry array has size 16.
* </pre>
*/
PIXCMAP *
pixcmapConvertTo4(PIXCMAP *cmaps)
{
l_int32 i, n, rval, gval, bval;
PIXCMAP *cmapd;
PROCNAME("pixcmapConvertTo4");
if (!cmaps)
return (PIXCMAP *)ERROR_PTR("cmaps not defined", procName, NULL);
if (pixcmapGetDepth(cmaps) != 2)
return (PIXCMAP *)ERROR_PTR("cmaps not for 2 bpp pix", procName, NULL);
cmapd = pixcmapCreate(4);
n = pixcmapGetCount(cmaps);
for (i = 0; i < n; i++) {
pixcmapGetColor(cmaps, i, &rval, &gval, &bval);
pixcmapAddColor(cmapd, rval, gval, bval);
}
return cmapd;
}
/*!
* \brief pixcmapConvertTo8()
*
* \param[in] cmaps colormap for 2 bpp or 4 bpp pix
* \return cmapd (8 bpp)
*
* <pre>
* Notes:
* (1) This converts a 2 bpp or 4 bpp colormap to 8 bpp. The colors
* are the same; the output colormap entry array has size 256.
* </pre>
*/
PIXCMAP *
pixcmapConvertTo8(PIXCMAP *cmaps)
{
l_int32 i, n, depth, rval, gval, bval;
PIXCMAP *cmapd;
PROCNAME("pixcmapConvertTo8");
if (!cmaps)
return (PIXCMAP *)ERROR_PTR("cmaps not defined", procName, NULL);
depth = pixcmapGetDepth(cmaps);
if (depth == 8) return pixcmapCopy(cmaps);
if (depth != 2 && depth != 4)
return (PIXCMAP *)ERROR_PTR("cmaps not 2 or 4 bpp", procName, NULL);
cmapd = pixcmapCreate(8);
n = pixcmapGetCount(cmaps);
for (i = 0; i < n; i++) {
pixcmapGetColor(cmaps, i, &rval, &gval, &bval);
pixcmapAddColor(cmapd, rval, gval, bval);
}
return cmapd;
}
/*-------------------------------------------------------------*
* Colormap I/O *
*-------------------------------------------------------------*/
/*!
* \brief pixcmapRead()
*
* \param[in] filename
* \return cmap, or NULL on error
*/
PIXCMAP *
pixcmapRead(const char *filename)
{
FILE *fp;
PIXCMAP *cmap;
PROCNAME("pixcmapRead");
if (!filename)
return (PIXCMAP *)ERROR_PTR("filename not defined", procName, NULL);
if ((fp = fopenReadStream(filename)) == NULL)
return (PIXCMAP *)ERROR_PTR("stream not opened", procName, NULL);
cmap = pixcmapReadStream(fp);
fclose(fp);
if (!cmap)
return (PIXCMAP *)ERROR_PTR("cmap not read", procName, NULL);
return cmap;
}
/*!
* \brief pixcmapReadStream()
*
* \param[in] fp file stream
* \return cmap, or NULL on error
*/
PIXCMAP *
pixcmapReadStream(FILE *fp)
{
l_int32 rval, gval, bval, aval, ignore;
l_int32 i, index, ret, depth, ncolors;
PIXCMAP *cmap;
PROCNAME("pixcmapReadStream");
if (!fp)
return (PIXCMAP *)ERROR_PTR("stream not defined", procName, NULL);
ret = fscanf(fp, "\nPixcmap: depth = %d bpp; %d colors\n",
&depth, &ncolors);
if (ret != 2 ||
(depth != 1 && depth != 2 && depth != 4 && depth != 8) ||
(ncolors < 2 || ncolors > 256))
return (PIXCMAP *)ERROR_PTR("invalid cmap size", procName, NULL);
ignore = fscanf(fp, "Color R-val G-val B-val Alpha\n");
ignore = fscanf(fp, "----------------------------------------\n");
cmap = pixcmapCreate(depth);
for (i = 0; i < ncolors; i++) {
if (fscanf(fp, "%3d %3d %3d %3d %3d\n",
&index, &rval, &gval, &bval, &aval) != 5) {
pixcmapDestroy(&cmap);
return (PIXCMAP *)ERROR_PTR("invalid entry", procName, NULL);
}
pixcmapAddRGBA(cmap, rval, gval, bval, aval);
}
return cmap;
}
/*!
* \brief pixcmapReadMem()
*
* \param[in] data serialization of pixcmap; in ascii
* \param[in] size of data in bytes; can use strlen to get it
* \return cmap, or NULL on error
*/
PIXCMAP *
pixcmapReadMem(const l_uint8 *data,
size_t size)
{
FILE *fp;
PIXCMAP *cmap;
PROCNAME("pixcmapReadMem");
if (!data)
return (PIXCMAP *)ERROR_PTR("data not defined", procName, NULL);
if ((fp = fopenReadFromMemory(data, size)) == NULL)
return (PIXCMAP *)ERROR_PTR("stream not opened", procName, NULL);
cmap = pixcmapReadStream(fp);
fclose(fp);
if (!cmap) L_ERROR("cmap not read\n", procName);
return cmap;
}
/*!
* \brief pixcmapWrite()
*
* \param[in] filename
* \param[in] cmap
* \return 0 if OK, 1 on error
*/
l_ok
pixcmapWrite(const char *filename,
const PIXCMAP *cmap)
{
l_int32 ret;
FILE *fp;
PROCNAME("pixcmapWrite");
if (!filename)
return ERROR_INT("filename not defined", procName, 1);
if (!cmap)
return ERROR_INT("cmap not defined", procName, 1);
if ((fp = fopenWriteStream(filename, "w")) == NULL)
return ERROR_INT("stream not opened", procName, 1);
ret = pixcmapWriteStream(fp, cmap);
fclose(fp);
if (ret)
return ERROR_INT("cmap not written to stream", procName, 1);
return 0;
}
/*!
* \brief pixcmapWriteStream()
*
* \param[in] fp file stream
\param[in] cmap
* \return 0 if OK, 1 on error
*/
l_ok
pixcmapWriteStream(FILE *fp,
const PIXCMAP *cmap)
{
l_int32 *rmap, *gmap, *bmap, *amap;
l_int32 i;
PROCNAME("pixcmapWriteStream");
if (!fp)
return ERROR_INT("stream not defined", procName, 1);
if (!cmap)
return ERROR_INT("cmap not defined", procName, 1);
if (pixcmapToArrays(cmap, &rmap, &gmap, &bmap, &amap))
return ERROR_INT("colormap arrays not made", procName, 1);
fprintf(fp, "\nPixcmap: depth = %d bpp; %d colors\n", cmap->depth, cmap->n);
fprintf(fp, "Color R-val G-val B-val Alpha\n");
fprintf(fp, "----------------------------------------\n");
for (i = 0; i < cmap->n; i++)
fprintf(fp, "%3d %3d %3d %3d %3d\n",
i, rmap[i], gmap[i], bmap[i], amap[i]);
fprintf(fp, "\n");
LEPT_FREE(rmap);
LEPT_FREE(gmap);
LEPT_FREE(bmap);
LEPT_FREE(amap);
return 0;
}
/*!
* \brief pixcmapWriteMem()
*
* \param[out] pdata data of serialized pixcmap; ascii
* \param[out] psize size of returned data
* \param[in] cmap
* \return 0 if OK, 1 on error
*
* <pre>
* Notes:
* (1) Serializes a pixcmap in memory and puts the result in a buffer.
* </pre>
*/
l_ok
pixcmapWriteMem(l_uint8 **pdata,
size_t *psize,
const PIXCMAP *cmap)
{
l_int32 ret;
FILE *fp;
PROCNAME("pixcmapWriteMem");
if (pdata) *pdata = NULL;
if (psize) *psize = 0;
if (!pdata)
return ERROR_INT("&data not defined", procName, 1);
if (!psize)
return ERROR_INT("&size not defined", procName, 1);
if (!cmap)
return ERROR_INT("cmap not defined", procName, 1);
#if HAVE_FMEMOPEN
if ((fp = open_memstream((char **)pdata, psize)) == NULL)
return ERROR_INT("stream not opened", procName, 1);
ret = pixcmapWriteStream(fp, cmap);
#else
L_INFO("work-around: writing to a temp file\n", procName);
#ifdef _WIN32
if ((fp = fopenWriteWinTempfile()) == NULL)
return ERROR_INT("tmpfile stream not opened", procName, 1);
#else
if ((fp = tmpfile()) == NULL)
return ERROR_INT("tmpfile stream not opened", procName, 1);
#endif /* _WIN32 */
ret = pixcmapWriteStream(fp, cmap);
rewind(fp);
*pdata = l_binaryReadStream(fp, psize);
#endif /* HAVE_FMEMOPEN */
fclose(fp);
return ret;
}
/*----------------------------------------------------------------------*
* Extract colormap arrays and serialization *
*----------------------------------------------------------------------*/
/*!
* \brief pixcmapToArrays()
*
* \param[in] cmap colormap
* \param[out] prmap, pgmap, pbmap colormap arrays
* \param[out] pamap [optional] alpha array
* \return 0 if OK; 1 on error
*/
l_ok
pixcmapToArrays(const PIXCMAP *cmap,
l_int32 **prmap,
l_int32 **pgmap,
l_int32 **pbmap,
l_int32 **pamap)
{
l_int32 *rmap, *gmap, *bmap, *amap;
l_int32 i, ncolors;
RGBA_QUAD *cta;
PROCNAME("pixcmapToArrays");
if (!prmap || !pgmap || !pbmap)
return ERROR_INT("&rmap, &gmap, &bmap not all defined", procName, 1);
*prmap = *pgmap = *pbmap = NULL;
if (pamap) *pamap = NULL;
if (!cmap)
return ERROR_INT("cmap not defined", procName, 1);
ncolors = pixcmapGetCount(cmap);
rmap = (l_int32 *)LEPT_CALLOC(ncolors, sizeof(l_int32));
gmap = (l_int32 *)LEPT_CALLOC(ncolors, sizeof(l_int32));
bmap = (l_int32 *)LEPT_CALLOC(ncolors, sizeof(l_int32));
*prmap = rmap;
*pgmap = gmap;
*pbmap = bmap;
if (pamap) {
amap = (l_int32 *)LEPT_CALLOC(ncolors, sizeof(l_int32));
*pamap = amap;
}
cta = (RGBA_QUAD *)cmap->array;
for (i = 0; i < ncolors; i++) {
rmap[i] = cta[i].red;
gmap[i] = cta[i].green;
bmap[i] = cta[i].blue;
if (pamap)
amap[i] = cta[i].alpha;
}
return 0;
}
/*!
* \brief pixcmapToRGBTable()
*
* \param[in] cmap colormap
* \param[out] ptab table of rgba values for the colormap
* \param[out] pncolors [optional] size of table
* \return 0 if OK; 1 on error
*/
l_ok
pixcmapToRGBTable(PIXCMAP *cmap,
l_uint32 **ptab,
l_int32 *pncolors)
{
l_int32 i, ncolors, rval, gval, bval, aval;
l_uint32 *tab;
PROCNAME("pixcmapToRGBTable");
if (!ptab)
return ERROR_INT("&tab not defined", procName, 1);
*ptab = NULL;
if (!cmap)
return ERROR_INT("cmap not defined", procName, 1);
ncolors = pixcmapGetCount(cmap);
if (pncolors) *pncolors = ncolors;
tab = (l_uint32 *)LEPT_CALLOC(ncolors, sizeof(l_uint32));
*ptab = tab;
for (i = 0; i < ncolors; i++) {
pixcmapGetRGBA(cmap, i, &rval, &gval, &bval, &aval);
composeRGBAPixel(rval, gval, bval, aval, &tab[i]);
}
return 0;
}
/*!
* \brief pixcmapSerializeToMemory()
*
* \param[in] cmap colormap
* \param[in] cpc components/color: 3 for rgb, 4 for rgba
* \param[out] pncolors number of colors in table
* \param[out] pdata binary string, cpc bytes per color
* \return 0 if OK; 1 on error
*
* <pre>
* Notes:
* (1) When serializing to store in a pdf, use %cpc = 3.
* </pre>
*/
l_ok
pixcmapSerializeToMemory(PIXCMAP *cmap,
l_int32 cpc,
l_int32 *pncolors,
l_uint8 **pdata)
{
l_int32 i, ncolors, rval, gval, bval, aval;
l_uint8 *data;
PROCNAME("pixcmapSerializeToMemory");
if (!pdata)
return ERROR_INT("&data not defined", procName, 1);
*pdata = NULL;
if (!pncolors)
return ERROR_INT("&ncolors not defined", procName, 1);
*pncolors = 0;
if (!cmap)
return ERROR_INT("cmap not defined", procName, 1);
if (cpc != 3 && cpc != 4)
return ERROR_INT("cpc not 3 or 4", procName, 1);
ncolors = pixcmapGetCount(cmap);
*pncolors = ncolors;
data = (l_uint8 *)LEPT_CALLOC((size_t)cpc * ncolors, sizeof(l_uint8));
*pdata = data;
for (i = 0; i < ncolors; i++) {
pixcmapGetRGBA(cmap, i, &rval, &gval, &bval, &aval);
data[cpc * i] = rval;
data[cpc * i + 1] = gval;
data[cpc * i + 2] = bval;
if (cpc == 4)
data[cpc * i + 3] = aval;
}
return 0;
}
/*!
* \brief pixcmapDeserializeFromMemory()
*
* \param[in] data binary string, 3 or 4 bytes per color
* \param[in] cpc components/color: 3 for rgb, 4 for rgba
* \param[in] ncolors
* \return cmap, or NULL on error
*/
PIXCMAP *
pixcmapDeserializeFromMemory(l_uint8 *data,
l_int32 cpc,
l_int32 ncolors)
{
l_int32 i, d, rval, gval, bval, aval;
PIXCMAP *cmap;
PROCNAME("pixcmapDeserializeFromMemory");
if (!data)
return (PIXCMAP *)ERROR_PTR("data not defined", procName, NULL);
if (cpc != 3 && cpc != 4)
return (PIXCMAP *)ERROR_PTR("cpc not 3 or 4", procName, NULL);
if (ncolors == 0)
return (PIXCMAP *)ERROR_PTR("no entries", procName, NULL);
if (ncolors > 256)
return (PIXCMAP *)ERROR_PTR("ncolors > 256", procName, NULL);
if (ncolors > 16)
d = 8;
else if (ncolors > 4)
d = 4;
else if (ncolors > 2)
d = 2;
else
d = 1;
cmap = pixcmapCreate(d);
for (i = 0; i < ncolors; i++) {
rval = data[cpc * i];
gval = data[cpc * i + 1];
bval = data[cpc * i + 2];
if (cpc == 4)
aval = data[cpc * i + 3];
else
aval = 255; /* opaque */
pixcmapAddRGBA(cmap, rval, gval, bval, aval);
}
return cmap;
}
/*!
* \brief pixcmapConvertToHex()
*
* \param[in] data binary serialized data
* \param[in] ncolors in colormap
* \return hexdata bracketed, space-separated ascii hex string,
* or NULL on error.
*
* <pre>
* Notes:
* (1) The number of bytes in %data is 3 * ncolors.
* (2) Output is in form:
* < r0g0b0 r1g1b1 ... rngnbn >
* where r0, g0, b0 ... are each 2 bytes of hex ascii
* (3) This is used in pdf files to express the colormap as an
* array in ascii (human-readable) format.
* </pre>
*/
char *
pixcmapConvertToHex(l_uint8 *data,
l_int32 ncolors)
{
l_int32 i, j, hexbytes;
char *hexdata = NULL;
char buf[4];
PROCNAME("pixcmapConvertToHex");
if (!data)
return (char *)ERROR_PTR("data not defined", procName, NULL);
if (ncolors < 1)
return (char *)ERROR_PTR("no colors", procName, NULL);
hexbytes = 2 + (2 * 3 + 1) * ncolors + 2;
hexdata = (char *)LEPT_CALLOC(hexbytes, sizeof(char));
hexdata[0] = '<';
hexdata[1] = ' ';
for (i = 0; i < ncolors; i++) {
j = 2 + (2 * 3 + 1) * i;
snprintf(buf, sizeof(buf), "%02x", data[3 * i]);
hexdata[j] = buf[0];
hexdata[j + 1] = buf[1];
snprintf(buf, sizeof(buf), "%02x", data[3 * i + 1]);
hexdata[j + 2] = buf[0];
hexdata[j + 3] = buf[1];
snprintf(buf, sizeof(buf), "%02x", data[3 * i + 2]);
hexdata[j + 4] = buf[0];
hexdata[j + 5] = buf[1];
hexdata[j + 6] = ' ';
}
hexdata[j + 7] = '>';
hexdata[j + 8] = '\0';
return hexdata;
}
/*-------------------------------------------------------------*
* Colormap transforms *
*-------------------------------------------------------------*/
/*!
* \brief pixcmapGammaTRC()
*
* \param[in] cmap colormap
* \param[in] gamma gamma correction; must be > 0.0
* \param[in] minval input value that gives 0 for output; can be < 0
* \param[in] maxval input value that gives 255 for output; can be > 255
* \return 0 if OK; 1 on error
*
* <pre>
* Notes:
* (1) This is an in-place transform
* (2) See pixGammaTRC() and numaGammaTRC() in enhance.c
* for description and use of transform
* </pre>
*/
l_ok
pixcmapGammaTRC(PIXCMAP *cmap,
l_float32 gamma,
l_int32 minval,
l_int32 maxval)
{
l_int32 rval, gval, bval, trval, tgval, tbval, i, ncolors;
NUMA *nag;
PROCNAME("pixcmapGammaTRC");
if (!cmap)
return ERROR_INT("cmap not defined", procName, 1);
if (gamma <= 0.0) {
L_WARNING("gamma must be > 0.0; setting to 1.0\n", procName);
gamma = 1.0;
}
if (minval >= maxval)
return ERROR_INT("minval not < maxval", procName, 1);
if (gamma == 1.0 && minval == 0 && maxval == 255) /* no-op */
return 0;
if ((nag = numaGammaTRC(gamma, minval, maxval)) == NULL)
return ERROR_INT("nag not made", procName, 1);
ncolors = pixcmapGetCount(cmap);
for (i = 0; i < ncolors; i++) {
pixcmapGetColor(cmap, i, &rval, &gval, &bval);
numaGetIValue(nag, rval, &trval);
numaGetIValue(nag, gval, &tgval);
numaGetIValue(nag, bval, &tbval);
pixcmapResetColor(cmap, i, trval, tgval, tbval);
}
numaDestroy(&nag);
return 0;
}
/*!
* \brief pixcmapContrastTRC()
*
* \param[in] cmap colormap
* \param[in] factor generally between 0.0 [no enhancement]
* and 1.0, but can be larger than 1.0
* \return 0 if OK; 1 on error
*
* <pre>
* Notes:
* (1) This is an in-place transform
* (2) See pixContrastTRC() and numaContrastTRC() in enhance.c
* for description and use of transform
* </pre>
*/
l_ok
pixcmapContrastTRC(PIXCMAP *cmap,
l_float32 factor)
{
l_int32 i, ncolors, rval, gval, bval, trval, tgval, tbval;
NUMA *nac;
PROCNAME("pixcmapContrastTRC");
if (!cmap)
return ERROR_INT("cmap not defined", procName, 1);
if (factor < 0.0) {
L_WARNING("factor must be >= 0.0; setting to 0.0\n", procName);
factor = 0.0;
}
if ((nac = numaContrastTRC(factor)) == NULL)
return ERROR_INT("nac not made", procName, 1);
ncolors = pixcmapGetCount(cmap);
for (i = 0; i < ncolors; i++) {
pixcmapGetColor(cmap, i, &rval, &gval, &bval);
numaGetIValue(nac, rval, &trval);
numaGetIValue(nac, gval, &tgval);
numaGetIValue(nac, bval, &tbval);
pixcmapResetColor(cmap, i, trval, tgval, tbval);
}
numaDestroy(&nac);
return 0;
}
/*!
* \brief pixcmapShiftIntensity()
*
* \param[in] cmap colormap
* \param[in] fraction between -1.0 and +1.0
* \return 0 if OK; 1 on error
*
* <pre>
* Notes:
* (1) This is an in-place transform
* (2) It does a proportional shift of the intensity for each color.
* (3) If fraction < 0.0, it moves all colors towards (0,0,0).
* This darkens the image.
* If fraction > 0.0, it moves all colors towards (255,255,255)
* This fades the image.
* (4) The equivalent transform can be accomplished with pixcmapGammaTRC(),
* but it is considerably more difficult (see numaGammaTRC()).
* </pre>
*/
l_ok
pixcmapShiftIntensity(PIXCMAP *cmap,
l_float32 fraction)
{
l_int32 i, ncolors, rval, gval, bval;
PROCNAME("pixcmapShiftIntensity");
if (!cmap)
return ERROR_INT("cmap not defined", procName, 1);
if (fraction < -1.0 || fraction > 1.0)
return ERROR_INT("fraction not in [-1.0, 1.0]", procName, 1);
ncolors = pixcmapGetCount(cmap);
for (i = 0; i < ncolors; i++) {
pixcmapGetColor(cmap, i, &rval, &gval, &bval);
if (fraction < 0.0)
pixcmapResetColor(cmap, i,
(l_int32)((1.0 + fraction) * rval),
(l_int32)((1.0 + fraction) * gval),
(l_int32)((1.0 + fraction) * bval));
else
pixcmapResetColor(cmap, i,
rval + (l_int32)(fraction * (255 - rval)),
gval + (l_int32)(fraction * (255 - gval)),
bval + (l_int32)(fraction * (255 - bval)));
}
return 0;
}
/*!
* \brief pixcmapShiftByComponent()
*
* \param[in] cmap colormap
* \param[in] srcval source color: 0xrrggbb00
* \param[in] dstval target color: 0xrrggbb00
* \return 0 if OK; 1 on error
*
* <pre>
* Notes:
* (1) This is an in-place transform
* (2) It implements pixelShiftByComponent() for each color.
* The mapping is specified by srcval and dstval.
* (3) If a component decreases, the component in the colormap
* decreases by the same ratio. Likewise for increasing, except
* all ratios are taken with respect to the distance from 255.
* </pre>
*/
l_ok
pixcmapShiftByComponent(PIXCMAP *cmap,
l_uint32 srcval,
l_uint32 dstval)
{
l_int32 i, ncolors, rval, gval, bval;
l_uint32 newval;
PROCNAME("pixcmapShiftByComponent");
if (!cmap)
return ERROR_INT("cmap not defined", procName, 1);
ncolors = pixcmapGetCount(cmap);
for (i = 0; i < ncolors; i++) {
pixcmapGetColor(cmap, i, &rval, &gval, &bval);
pixelShiftByComponent(rval, gval, bval, srcval, dstval, &newval);
extractRGBValues(newval, &rval, &gval, &bval);
pixcmapResetColor(cmap, i, rval, gval, bval);
}
return 0;
}