/*====================================================================* - Copyright (C) 2001 Leptonica. All rights reserved. - This software is distributed in the hope that it will be - useful, but with NO WARRANTY OF ANY KIND. - No author or distributor accepts responsibility to anyone for the - consequences of using this software, or for whether it serves any - particular purpose or works at all, unless he or she says so in - writing. Everyone is granted permission to copy, modify and - redistribute this source code, for commercial or non-commercial - purposes, with the following restrictions: (1) the origin of this - source code must not be misrepresented; (2) modified versions must - be plainly marked as such; and (3) this notice may not be removed - or altered from any source or modified source distribution. *====================================================================*/ /* * colormap.c * * Colormap creation, copy, destruction, addition * PIXCMAP *pixcmapCreate() * PIXCMAP *pixcmapCreateRandom() * PIXCMAP *pixcmapCreateLinear() * PIXCMAP *pixcmapCopy() * void pixcmapDestroy() * l_int32 pixcmapAddColor() * l_int32 pixcmapAddNewColor() * 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 pixcmapResetColor() * l_int32 pixcmapGetIndex() * l_int32 pixcmapHasColor() * l_int32 pixcmapCountGrayColors() * l_int32 pixcmapGetRankIntensity() * l_int32 pixcmapGetNearestIndex() * l_int32 pixcmapGetNearestGrayIndex() * l_int32 pixcmapGetExtremeValue() * * Colormap conversion * PIXCMAP *pixcmapGrayToColor() * PIXCMAP *pixcmapColorToGray() * * Colormap I/O * l_int32 pixcmapReadStream() * l_int32 pixcmapWriteStream() * * Extract colormap arrays * l_int32 pixcmapToArrays() * l_int32 pixcmapToRGBTable() * * Colormap transforms * l_int32 pixcmapGammaTRC() * l_int32 pixcmapContrastTRC() * l_int32 pixcmapShiftIntensity() * l_int32 pixcmapConvertRGBToHSV() * l_int32 pixcmapConvertHSVToRGB() * */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include "allheaders.h" /*-------------------------------------------------------------* * Colormap creation and addition * *-------------------------------------------------------------*/ /*! * pixcmapCreate() * * Input: depth (bpp, of pix) * 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); if ((cmap = (PIXCMAP *)CALLOC(1, sizeof(PIXCMAP))) == NULL) return (PIXCMAP *)ERROR_PTR("cmap not made", procName, NULL); cmap->depth = depth; cmap->nalloc = 1 << depth; if ((cta = (RGBA_QUAD *)CALLOC(cmap->nalloc, sizeof(RGBA_QUAD))) == NULL) return (PIXCMAP *)ERROR_PTR("cta not made", procName, NULL); cmap->array = cta; cmap->n = 0; return cmap; } /*! * pixcmapCreateRandom() * * Input: depth (bpp, of pix; 2, 4 or 8) * hasblack (1 if the first color is black; 0 if no black) * haswhite (1 if the last color is white; 0 if no white) * Return: cmap, or null on error * * 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. */ 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; } /*! * pixcmapCreateLinear() * * Input: d (depth of pix for this colormap; 1, 2, 4 or 8) * nlevels (valid in range [2, 2^d]) * Return: cmap, or null on error * * Notes: * (1) Colormap has equally spaced gray color values * from black (0, 0, 0) to white (255, 255, 255). */ 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; } /*! * pixcmapCopy() * * Input: cmaps * Return: cmapd, or null on error */ PIXCMAP * pixcmapCopy(PIXCMAP *cmaps) { l_int32 nbytes; PIXCMAP *cmapd; PROCNAME("pixcmapCopy"); if (!cmaps) return (PIXCMAP *)ERROR_PTR("cmaps not defined", procName, NULL); if ((cmapd = (PIXCMAP *)CALLOC(1, sizeof(PIXCMAP))) == NULL) return (PIXCMAP *)ERROR_PTR("cmapd not made", procName, NULL); nbytes = cmaps->nalloc * sizeof(RGBA_QUAD); if ((cmapd->array = (void *)CALLOC(1, nbytes)) == NULL) return (PIXCMAP *)ERROR_PTR("cmap array not made", procName, NULL); memcpy(cmapd->array, cmaps->array, nbytes); cmapd->n = cmaps->n; cmapd->nalloc = cmaps->nalloc; cmapd->depth = cmaps->depth; return cmapd; } /*! * pixcmapDestroy() * * Input: &cmap (<set to null>) * Return: void */ void pixcmapDestroy(PIXCMAP **pcmap) { PIXCMAP *cmap; PROCNAME("pixcmapDestroy"); if (pcmap == NULL) { L_WARNING("ptr address is null!", procName); return; } if ((cmap = *pcmap) == NULL) return; FREE(cmap->array); FREE(cmap); *pcmap = NULL; return; } /*! * pixcmapAddColor() * * Input: cmap * rval, gval, bval (colormap entry to be added; each number * is in range [0, ... 255]) * Return: 0 if OK, 1 on error * * Note: always adds the color if there is room. */ l_int32 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; cmap->n++; return 0; } /*! * pixcmapAddNewColor() * * Input: cmap * rval, gval, bval (colormap entry to be added; each number * is in range [0, ... 255]) * &index (<return> index of color) * Return: 0 if OK, 1 on error; 2 if unable to add color * * Notes: * (1) This only adds color if not already there. * (2) This returns the index of the new (or existing) color. * (3) Returns 2 with a warning if unable to add this color; * the caller should check the return value. */ l_int32 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", procName); return 2; } /* There's room. Add it. */ pixcmapAddColor(cmap, rval, gval, bval); *pindex = pixcmapGetCount(cmap) - 1; return 0; } /*! * pixcmapAddBlackOrWhite() * * Input: cmap * color (0 for black, 1 for white) * &index (<optional return> index of color; can be null) * Return: 0 if OK, 1 on error * * Notes: * (1) This only adds color if not already there. * (2) This sets index to the requested color. * (3) If there is no room in the colormap, returns the index * of the closest color. */ l_int32 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; } /*! * pixcmapSetBlackAndWhite() * * Input: cmap * setblack (0 for no operation; 1 to set darkest color to black) * setwhite (0 for no operation; 1 to set lightest color to white) * Return: 0 if OK, 1 on error */ l_int32 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; } /*! * pixcmapGetCount() * * Input: cmap * Return: count, or 0 on error */ l_int32 pixcmapGetCount(PIXCMAP *cmap) { PROCNAME("pixcmapGetCount"); if (!cmap) return ERROR_INT("cmap not defined", procName, 0); return cmap->n; } /*! * pixcmapGetFreeCount() * * Input: 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); } /*! * pixcmapGetDepth() * * Input: 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; } /*! * pixcmapGetMinDepth() * * Input: cmap * &mindepth (<return> minimum depth to support the colormap) * Return: 0 if OK, 1 on error * * Notes: * (1) On error, &mindepth is returned as 0. */ l_int32 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; } /*! * pixcmapClear() * * Input: cmap * Return: 0 if OK, 1 on error * * Note: this removes the colors by setting the count to 0. */ l_int32 pixcmapClear(PIXCMAP *cmap) { PROCNAME("pixcmapClear"); if (!cmap) return ERROR_INT("cmap not defined", procName, 1); cmap->n = 0; return 0; } /*-------------------------------------------------------------* * Colormap random access * *-------------------------------------------------------------*/ /*! * pixcmapGetColor() * * Input: cmap * index * &rval, &gval, &bval (<return> each color value in l_int32) * Return: 0 if OK, 1 if not accessable (caller should check) */ l_int32 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; } /*! * pixcmapResetColor() * * Input: cmap * index * rval, gval, bval (colormap entry to be reset; each number * is in range [0, ... 255]) * Return: 0 if OK, 1 if not accessable (caller should check) * * Note: this resets sets the color of an entry that has already * been set and included in the count of colors */ l_int32 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; return 0; } /*! * pixcmapGetIndex() * * Input: cmap * rval, gval, bval (colormap colors to search for; each number * is in range [0, ... 255]) * &index (<return>) * 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; } /*! * pixcmapHasColor() * * Input: cmap * &color (<return> TRUE if cmap has color; FALSE otherwise) * Return: 0 if OK, 1 on error */ l_int32 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)) 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; } } FREE(rmap); FREE(gmap); FREE(bmap); return 0; } /*! * pixcmapCountGrayColors() * * Input: cmap * &ngray (<return> number of gray colors) * Return: 0 if OK, 1 on error * * Notes: * (1) This counts the unique gray colors, including black and white. */ l_int32 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 *)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++; } } FREE(array); *pngray = count; return 0; } /*! * pixcmapGetRankIntensity() * * Input: cmap * rankval (0.0 for darkest, 1.0 for lightest color) * &index (<return> the index into the colormap that * corresponds to the rank intensity color) * Return: 0 if OK, 1 on error */ l_int32 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; } /*! * pixcmapGetNearestIndex() * * Input: cmap * rval, gval, bval (colormap colors to search for; each number * is in range [0, ... 255]) * &index (<return> the index of the nearest color) * Return: 0 if OK, 1 on error (caller must check) * * 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. */ l_int32 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; } /*! * pixcmapGetNearestGrayIndex() * * Input: cmap * val (gray value to search for; in range [0, ... 255]) * &index (<return> the index of the nearest color) * Return: 0 if OK, 1 on error (caller must check) * * 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. */ l_int32 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; } /*! * pixcmapGetExtremeValue() * * Input: cmap * type (L_CHOOSE_MIN or L_CHOOSE_MAX) * &rval (<optional return> red component) * &gval (<optional return> green component) * &bval (<optional return> blue component) * Return: 0 if OK, 1 on error * * Notes: * (1) Returns for selected components the extreme value * (either min or max) of the color component that is * found in the colormap. */ l_int32 pixcmapGetExtremeValue(PIXCMAP *cmap, l_int32 type, l_int32 *prval, l_int32 *pgval, l_int32 *pbval) { l_int32 i, n, rval, gval, bval, extrval, extgval, extbval; PROCNAME("pixcmapGetExtremeValue"); if (!prval && !pgval && !pbval) return ERROR_INT("no result requested for return", procName, 1); if (prval) *prval = 0; if (pgval) *pgval = 0; if (pbval) *pbval = 0; if (!cmap) return ERROR_INT("cmap not defined", procName, 1); if (type != L_CHOOSE_MIN && type != L_CHOOSE_MAX) return ERROR_INT("invalid type", procName, 1); if (type == L_CHOOSE_MIN) { extrval = 100000; extgval = 100000; extbval = 100000; } else { extrval = 0; extgval = 0; extbval = 0; } n = pixcmapGetCount(cmap); for (i = 0; i < n; i++) { pixcmapGetColor(cmap, i, &rval, &gval, &bval); if ((type == L_CHOOSE_MIN && rval < extrval) || (type == L_CHOOSE_MAX && rval > extrval)) extrval = rval; if ((type == L_CHOOSE_MIN && gval < extgval) || (type == L_CHOOSE_MAX && gval > extgval)) extgval = gval; if ((type == L_CHOOSE_MIN && bval < extbval) || (type == L_CHOOSE_MAX && bval > extbval)) extbval = bval; } if (prval) *prval = extrval; if (pgval) *pgval = extgval; if (pbval) *pbval = extbval; return 0; } /*-------------------------------------------------------------* * Colormap conversion * *-------------------------------------------------------------*/ /*! * pixcmapGrayToColor() * * Input: color * Return: cmap, or null on error * * 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. */ 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; } /*! * pixcmapColorToGray() * * Input: cmap * rwt, gwt, bwt (non-negative; these should add to 1.0) * Return: cmap (gray), or null on error * * 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. */ 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", 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", procName); rwt = rwt / sum; gwt = gwt / sum; bwt = bwt / sum; } cmapd = pixcmapCopy(cmaps); 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; } /*-------------------------------------------------------------* * Colormap I/O * *-------------------------------------------------------------*/ /*! * pixcmapReadStream() * * Input: stream * Return: cmap, or null on error */ PIXCMAP * pixcmapReadStream(FILE *fp) { l_int32 rval, gval, bval; 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); fscanf(fp, "Color R-val G-val B-val\n"); fscanf(fp, "--------------------------------\n"); if ((cmap = pixcmapCreate(depth)) == NULL) return (PIXCMAP *)ERROR_PTR("cmap not made", procName, NULL); for (i = 0; i < ncolors; i++) { fscanf(fp, "%3d %3d %3d %3d\n", &index, &rval, &gval, &bval); pixcmapAddColor(cmap, rval, gval, bval); } return cmap; } /*! * pixcmapWriteStream() * * Input: stream, cmap * Return: 0 if OK, 1 on error */ l_int32 pixcmapWriteStream(FILE *fp, PIXCMAP *cmap) { l_int32 *rmap, *gmap, *bmap; 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)) 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\n"); fprintf(fp, "--------------------------------\n"); for (i = 0; i < cmap->n; i++) fprintf(fp, "%3d %3d %3d %3d\n", i, rmap[i], gmap[i], bmap[i]); fprintf(fp, "\n"); FREE(rmap); FREE(gmap); FREE(bmap); return 0; } /*-------------------------------------------------------------* * Extract colormap arrays * *-------------------------------------------------------------*/ /*! * pixcmapToArrays() * * Input: colormap * &rmap, &gmap, &bmap (<return> colormap arrays) * Return: 0 if OK; 1 on error */ l_int32 pixcmapToArrays(PIXCMAP *cmap, l_int32 **prmap, l_int32 **pgmap, l_int32 **pbmap) { l_int32 *rmap, *gmap, *bmap; 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 (!cmap) return ERROR_INT("cmap not defined", procName, 1); ncolors = pixcmapGetCount(cmap); if (((rmap = (l_int32 *)CALLOC(ncolors, sizeof(l_int32))) == NULL) || ((gmap = (l_int32 *)CALLOC(ncolors, sizeof(l_int32))) == NULL) || ((bmap = (l_int32 *)CALLOC(ncolors, sizeof(l_int32))) == NULL)) return ERROR_INT("calloc fail for *map", procName, 1); *prmap = rmap; *pgmap = gmap; *pbmap = bmap; 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; } return 0; } /*! * pixcmapToRGBTable() * * Input: colormap * &tab (<return> table of rgba values for the colormap) * &ncolors (<optional return> size of table) * Return: 0 if OK; 1 on error */ l_int32 pixcmapToRGBTable(PIXCMAP *cmap, l_uint32 **ptab, l_int32 *pncolors) { l_int32 i, ncolors, rval, gval, bval; 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; if ((tab = (l_uint32 *)CALLOC(ncolors, sizeof(l_uint32))) == NULL) return ERROR_INT("tab not made", procName, 1); *ptab = tab; for (i = 0; i < ncolors; i++) { pixcmapGetColor(cmap, i, &rval, &gval, &bval); composeRGBPixel(rval, gval, bval, &tab[i]); } /* for (i = 0; i < ncolors; i++) fprintf(stderr, "Color[%d] = %x\n", i, tab[i]); */ return 0; } /*-------------------------------------------------------------* * Colormap transforms * *-------------------------------------------------------------*/ /*! * pixcmapGammaTRC() * * Input: colormap * gamma (gamma correction; must be > 0.0) * minval (input value that gives 0 for output; can be < 0) * maxval (input value that gives 255 for output; can be > 255) * Return: 0 if OK; 1 on error * * Notes: * - in-place transform * - see pixGammaTRC() and numaGammaTRC() in enhance.c for * description and use of transform */ l_int32 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", procName); gamma = 1.0; } if (minval >= maxval) return ERROR_INT("minval not < maxval", procName, 1); 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; } /*! * pixcmapContrastTRC() * * Input: colormap * factor (generally between 0.0 (no enhancement) * and 1.0, but can be larger than 1.0) * Return: 0 if OK; 1 on error * * Notes: * - in-place transform * - see pixContrastTRC() and numaContrastTRC() in enhance.c for * description and use of transform */ l_int32 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", 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; } /*! * pixcmapShiftIntensity() * * Input: colormap * fraction (between -1.0 and +1.0) * Return: 0 if OK; 1 on error * * Notes: * - in-place transform * - This does a proportional shift of the intensity for each color. * - 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. * - The equivalent transform can be accomplished with pixcmapGammaTRC(), * but it is considerably more difficult (see numaGammaTRC()). */ l_int32 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; } /*! * pixcmapConvertRGBToHSV() * * Input: colormap * Return: 0 if OK; 1 on error * * Notes: * - in-place transform * - See convertRGBToHSV() for def'n of HSV space. * - replaces: r --> h, g --> s, b --> v */ l_int32 pixcmapConvertRGBToHSV(PIXCMAP *cmap) { l_int32 i, ncolors, rval, gval, bval, hval, sval, vval; PROCNAME("pixcmapConvertRGBToHSV"); 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); convertRGBToHSV(rval, gval, bval, &hval, &sval, &vval); pixcmapResetColor(cmap, i, hval, sval, vval); } return 0; } /*! * pixcmapConvertHSVToRGB() * * Input: colormap * Return: 0 if OK; 1 on error * * Notes: * - in-place transform * - See convertRGBToHSV() for def'n of HSV space. * - replaces: h --> r, s --> g, v --> b */ l_int32 pixcmapConvertHSVToRGB(PIXCMAP *cmap) { l_int32 i, ncolors, rval, gval, bval, hval, sval, vval; PROCNAME("pixcmapConvertHSVToRGB"); if (!cmap) return ERROR_INT("cmap not defined", procName, 1); ncolors = pixcmapGetCount(cmap); for (i = 0; i < ncolors; i++) { pixcmapGetColor(cmap, i, &hval, &sval, &vval); convertHSVToRGB(hval, sval, vval, &rval, &gval, &bval); pixcmapResetColor(cmap, i, rval, gval, bval); } return 0; }