mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-06-19 14:15:21 -06:00
2679 lines
78 KiB
C
2679 lines
78 KiB
C
/*====================================================================*
|
|
- 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.
|
|
*====================================================================*/
|
|
|
|
/*
|
|
* pix2.c
|
|
*
|
|
* This file has these basic operations:
|
|
*
|
|
* (1) Get and set: individual pixels, full image, rectangular region,
|
|
* pad pixels, border pixels, and color components for RGB
|
|
* (2) Add and remove border pixels
|
|
* (3) Endian byte swaps
|
|
* (4) Fast serialization and special processing
|
|
*
|
|
* Pixel poking
|
|
* l_int32 pixGetPixel()
|
|
* l_int32 pixSetPixel()
|
|
* l_int32 pixGetRGBPixel()
|
|
* l_int32 pixSetRGBPixel()
|
|
* l_int32 pixGetRandomPixel()
|
|
* l_int32 pixClearPixel()
|
|
* l_int32 pixFlipPixel()
|
|
* void setPixelLow()
|
|
*
|
|
* Full image clear/set/set-to-arbitrary-value
|
|
* l_int32 pixClearAll()
|
|
* l_int32 pixSetAll()
|
|
* l_int32 pixSetAllArbitrary()
|
|
* l_int32 pixSetBlackOrWhite()
|
|
*
|
|
* Rectangular region clear/set/set-to-arbitrary-value/blend
|
|
* l_int32 pixClearInRect()
|
|
* l_int32 pixSetInRect()
|
|
* l_int32 pixSetInRectArbitrary()
|
|
* l_int32 pixBlendInRect()
|
|
*
|
|
* Set pad bits
|
|
* l_int32 pixSetPadBits()
|
|
* l_int32 pixSetPadBitsBand()
|
|
*
|
|
* Assign border pixels
|
|
* l_int32 pixSetOrClearBorder()
|
|
* l_int32 pixSetBorderVal()
|
|
* l_int32 pixSetMirroredBorder()
|
|
* PIX *pixCopyBorder()
|
|
*
|
|
* Add and remove border
|
|
* PIX *pixAddBorder()
|
|
* PIX *pixAddBorderGeneral()
|
|
* PIX *pixRemoveBorder()
|
|
* PIX *pixRemoveBorderGeneral()
|
|
* PIX *pixAddMirroredBorder()
|
|
* PIX *pixAddRepeatedBorder()
|
|
* PIX *pixAddMixedBorder()
|
|
*
|
|
* Color sample setting and extraction
|
|
* PIX *pixCreateRGBImage()
|
|
* PIX *pixGetRGBComponent()
|
|
* l_int32 pixSetRGBComponent()
|
|
* PIX *pixGetRGBComponentCmap()
|
|
* l_int32 composeRGBPixel()
|
|
* void extractRGBValues()
|
|
* l_int32 extractMinMaxComponent()
|
|
* l_int32 pixGetRGBLine()
|
|
*
|
|
* Conversion between big and little endians
|
|
* PIX *pixEndianByteSwapNew()
|
|
* l_int32 pixEndianByteSwap()
|
|
* l_int32 lineEndianByteSwap()
|
|
* PIX *pixEndianTwoByteSwapNew()
|
|
* l_int32 pixEndianTwoByteSwap()
|
|
*
|
|
* Extract raster data as binary string
|
|
* l_int32 pixGetRasterData()
|
|
*
|
|
* Serialization of pix to/from memory (uncompressed)
|
|
* l_int32 pixSerializeToMemory()
|
|
* PIX *pixDeserializeFromMemory()
|
|
*
|
|
* Setup helpers for 8 bpp byte processing
|
|
* l_uint8 **pixSetupByteProcessing()
|
|
* l_int32 pixCleanupByteProcessing()
|
|
*
|
|
* *** indicates implicit assumption about RGB component ordering
|
|
*/
|
|
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "allheaders.h"
|
|
|
|
static const l_uint32 rmask32[] = {0x0,
|
|
0x00000001, 0x00000003, 0x00000007, 0x0000000f,
|
|
0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff,
|
|
0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff,
|
|
0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff,
|
|
0x0001ffff, 0x0003ffff, 0x0007ffff, 0x000fffff,
|
|
0x001fffff, 0x003fffff, 0x007fffff, 0x00ffffff,
|
|
0x01ffffff, 0x03ffffff, 0x07ffffff, 0x0fffffff,
|
|
0x1fffffff, 0x3fffffff, 0x7fffffff, 0xffffffff};
|
|
|
|
|
|
#ifndef NO_CONSOLE_IO
|
|
#define DEBUG_SERIALIZE 0
|
|
#endif /* ~NO_CONSOLE_IO */
|
|
|
|
|
|
/*-------------------------------------------------------------*
|
|
* Pixel poking *
|
|
*-------------------------------------------------------------*/
|
|
/*!
|
|
* pixGetPixel()
|
|
*
|
|
* Input: pix
|
|
* (x,y) pixel coords
|
|
* &val (<return> pixel value)
|
|
* Return: 0 if OK; 1 on error
|
|
*
|
|
* Notes:
|
|
* (1) This returns the value in the data array. If the pix is
|
|
* colormapped, it returns the colormap index, not the rgb value.
|
|
*/
|
|
l_int32
|
|
pixGetPixel(PIX *pix,
|
|
l_int32 x,
|
|
l_int32 y,
|
|
l_uint32 *pval)
|
|
{
|
|
l_int32 w, h, d, wpl, val;
|
|
l_uint32 *line, *data;
|
|
|
|
PROCNAME("pixGetPixel");
|
|
|
|
if (!pval)
|
|
return ERROR_INT("pval not defined", procName, 1);
|
|
*pval = 0;
|
|
if (!pix)
|
|
return ERROR_INT("pix not defined", procName, 1);
|
|
|
|
pixGetDimensions(pix, &w, &h, &d);
|
|
if (x < 0 || x >= w)
|
|
return ERROR_INT("x out of bounds", procName, 1);
|
|
if (y < 0 || y >= h)
|
|
return ERROR_INT("y out of bounds", procName, 1);
|
|
|
|
wpl = pixGetWpl(pix);
|
|
data = pixGetData(pix);
|
|
line = data + y * wpl;
|
|
switch (d)
|
|
{
|
|
case 1:
|
|
val = GET_DATA_BIT(line, x);
|
|
break;
|
|
case 2:
|
|
val = GET_DATA_DIBIT(line, x);
|
|
break;
|
|
case 4:
|
|
val = GET_DATA_QBIT(line, x);
|
|
break;
|
|
case 8:
|
|
val = GET_DATA_BYTE(line, x);
|
|
break;
|
|
case 16:
|
|
val = GET_DATA_TWO_BYTES(line, x);
|
|
break;
|
|
case 32:
|
|
val = line[x];
|
|
break;
|
|
default:
|
|
return ERROR_INT("depth must be in {1,2,4,8,16,32} bpp", procName, 1);
|
|
}
|
|
|
|
*pval = val;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* pixSetPixel()
|
|
*
|
|
* Input: pix
|
|
* (x,y) pixel coords
|
|
* val (value to be inserted)
|
|
* Return: 0 if OK; 1 on error
|
|
*
|
|
* Note: the input value is not checked for overflow, and
|
|
* the sign bit (if any) is ignored.
|
|
*/
|
|
l_int32
|
|
pixSetPixel(PIX *pix,
|
|
l_int32 x,
|
|
l_int32 y,
|
|
l_uint32 val)
|
|
{
|
|
l_int32 w, h, d, wpl;
|
|
l_uint32 *line, *data;
|
|
|
|
PROCNAME("pixSetPixel");
|
|
|
|
if (!pix)
|
|
return ERROR_INT("pix not defined", procName, 1);
|
|
|
|
pixGetDimensions(pix, &w, &h, &d);
|
|
if (x < 0 || x >= w)
|
|
return ERROR_INT("x out of bounds", procName, 1);
|
|
if (y < 0 || y >= h)
|
|
return ERROR_INT("y out of bounds", procName, 1);
|
|
|
|
data = pixGetData(pix);
|
|
wpl = pixGetWpl(pix);
|
|
line = data + y * wpl;
|
|
switch (d)
|
|
{
|
|
case 1:
|
|
if (val)
|
|
SET_DATA_BIT(line, x);
|
|
else
|
|
CLEAR_DATA_BIT(line, x);
|
|
break;
|
|
case 2:
|
|
SET_DATA_DIBIT(line, x, val);
|
|
break;
|
|
case 4:
|
|
SET_DATA_QBIT(line, x, val);
|
|
break;
|
|
case 8:
|
|
SET_DATA_BYTE(line, x, val);
|
|
break;
|
|
case 16:
|
|
SET_DATA_TWO_BYTES(line, x, val);
|
|
break;
|
|
case 32:
|
|
line[x] = val;
|
|
break;
|
|
default:
|
|
return ERROR_INT("depth must be in {1,2,4,8,16,32} bpp", procName, 1);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* pixGetRGBPixel()
|
|
*
|
|
* Input: pix (32 bpp rgb, not colormapped)
|
|
* (x,y) pixel coords
|
|
* &rval (<optional return> red component)
|
|
* &gval (<optional return> green component)
|
|
* &bval (<optional return> blue component)
|
|
* Return: 0 if OK; 1 on error
|
|
*/
|
|
l_int32
|
|
pixGetRGBPixel(PIX *pix,
|
|
l_int32 x,
|
|
l_int32 y,
|
|
l_int32 *prval,
|
|
l_int32 *pgval,
|
|
l_int32 *pbval)
|
|
{
|
|
l_int32 w, h, d, wpl;
|
|
l_uint32 *data, *ppixel;
|
|
|
|
PROCNAME("pixGetRGBPixel");
|
|
|
|
if (!pix)
|
|
return ERROR_INT("pix not defined", procName, 1);
|
|
pixGetDimensions(pix, &w, &h, &d);
|
|
if (d != 32)
|
|
return ERROR_INT("pix not 32 bpp", procName, 1);
|
|
if (x < 0 || x >= w)
|
|
return ERROR_INT("x out of bounds", procName, 1);
|
|
if (y < 0 || y >= h)
|
|
return ERROR_INT("y out of bounds", procName, 1);
|
|
|
|
wpl = pixGetWpl(pix);
|
|
data = pixGetData(pix);
|
|
ppixel = data + y * wpl + x;
|
|
if (prval) *prval = GET_DATA_BYTE(ppixel, COLOR_RED);
|
|
if (pgval) *pgval = GET_DATA_BYTE(ppixel, COLOR_GREEN);
|
|
if (pbval) *pbval = GET_DATA_BYTE(ppixel, COLOR_BLUE);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* pixSetRGBPixel()
|
|
*
|
|
* Input: pix (32 bpp rgb)
|
|
* (x,y) pixel coords
|
|
* rval (red component)
|
|
* gval (green component)
|
|
* bval (blue component)
|
|
* Return: 0 if OK; 1 on error
|
|
*/
|
|
l_int32
|
|
pixSetRGBPixel(PIX *pix,
|
|
l_int32 x,
|
|
l_int32 y,
|
|
l_int32 rval,
|
|
l_int32 gval,
|
|
l_int32 bval)
|
|
{
|
|
l_int32 w, h, d, wpl;
|
|
l_uint32 pixel;
|
|
l_uint32 *data, *line;
|
|
|
|
PROCNAME("pixSetRGBPixel");
|
|
|
|
if (!pix)
|
|
return ERROR_INT("pix not defined", procName, 1);
|
|
pixGetDimensions(pix, &w, &h, &d);
|
|
if (d != 32)
|
|
return ERROR_INT("pix not 32 bpp", procName, 1);
|
|
if (x < 0 || x >= w)
|
|
return ERROR_INT("x out of bounds", procName, 1);
|
|
if (y < 0 || y >= h)
|
|
return ERROR_INT("y out of bounds", procName, 1);
|
|
|
|
wpl = pixGetWpl(pix);
|
|
data = pixGetData(pix);
|
|
line = data + y * wpl;
|
|
composeRGBPixel(rval, gval, bval, &pixel);
|
|
*(line + x) = pixel;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* pixGetRandomPixel()
|
|
*
|
|
* Input: pix (any depth; can be colormapped)
|
|
* &val (<return> pixel value)
|
|
* &x (<optional return> x coordinate chosen; can be null)
|
|
* &y (<optional return> y coordinate chosen; can be null)
|
|
* Return: 0 if OK; 1 on error
|
|
*
|
|
* Notes:
|
|
* (1) If the pix is colormapped, it returns the rgb value.
|
|
*/
|
|
l_int32
|
|
pixGetRandomPixel(PIX *pix,
|
|
l_uint32 *pval,
|
|
l_int32 *px,
|
|
l_int32 *py)
|
|
{
|
|
l_int32 w, h, x, y, rval, gval, bval;
|
|
l_uint32 val;
|
|
PIXCMAP *cmap;
|
|
|
|
PROCNAME("pixGetRandomPixel");
|
|
|
|
if (!pval)
|
|
return ERROR_INT("pval not defined", procName, 1);
|
|
*pval = 0;
|
|
if (!pix)
|
|
return ERROR_INT("pix not defined", procName, 1);
|
|
|
|
pixGetDimensions(pix, &w, &h, NULL);
|
|
x = rand() % w;
|
|
y = rand() % h;
|
|
if (px) *px = x;
|
|
if (py) *py = y;
|
|
pixGetPixel(pix, x, y, &val);
|
|
if ((cmap = pixGetColormap(pix)) != NULL) {
|
|
pixcmapGetColor(cmap, val, &rval, &gval, &bval);
|
|
composeRGBPixel(rval, gval, bval, pval);
|
|
}
|
|
else
|
|
*pval = val;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* pixClearPixel()
|
|
*
|
|
* Input: pix
|
|
* (x,y) pixel coords
|
|
* Return: 0 if OK; 1 on error.
|
|
*/
|
|
l_int32
|
|
pixClearPixel(PIX *pix,
|
|
l_int32 x,
|
|
l_int32 y)
|
|
{
|
|
l_int32 w, h, d, wpl;
|
|
l_uint32 *line, *data;
|
|
|
|
PROCNAME("pixClearPixel");
|
|
|
|
if (!pix)
|
|
return ERROR_INT("pix not defined", procName, 1);
|
|
|
|
pixGetDimensions(pix, &w, &h, &d);
|
|
if (x < 0 || x >= w)
|
|
return ERROR_INT("x out of bounds", procName, 1);
|
|
if (y < 0 || y >= h)
|
|
return ERROR_INT("y out of bounds", procName, 1);
|
|
|
|
wpl = pixGetWpl(pix);
|
|
data = pixGetData(pix);
|
|
line = data + y * wpl;
|
|
switch (d)
|
|
{
|
|
case 1:
|
|
CLEAR_DATA_BIT(line, x);
|
|
break;
|
|
case 2:
|
|
CLEAR_DATA_DIBIT(line, x);
|
|
break;
|
|
case 4:
|
|
CLEAR_DATA_QBIT(line, x);
|
|
break;
|
|
case 8:
|
|
SET_DATA_BYTE(line, x, 0);
|
|
break;
|
|
case 16:
|
|
SET_DATA_TWO_BYTES(line, x, 0);
|
|
break;
|
|
case 32:
|
|
line[x] = 0;
|
|
break;
|
|
default:
|
|
return ERROR_INT("depth must be in {1,2,4,8,16,32} bpp", procName, 1);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* pixFlipPixel()
|
|
*
|
|
* Input: pix
|
|
* (x,y) pixel coords
|
|
* Return: 0 if OK; 1 on error
|
|
*/
|
|
l_int32
|
|
pixFlipPixel(PIX *pix,
|
|
l_int32 x,
|
|
l_int32 y)
|
|
{
|
|
l_int32 w, h, d, wpl;
|
|
l_uint32 val;
|
|
l_uint32 *line, *data;
|
|
|
|
PROCNAME("pixFlipPixel");
|
|
|
|
if (!pix)
|
|
return ERROR_INT("pix not defined", procName, 1);
|
|
|
|
pixGetDimensions(pix, &w, &h, &d);
|
|
if (x < 0 || x >= w)
|
|
return ERROR_INT("x out of bounds", procName, 1);
|
|
if (y < 0 || y >= h)
|
|
return ERROR_INT("y out of bounds", procName, 1);
|
|
|
|
data = pixGetData(pix);
|
|
wpl = pixGetWpl(pix);
|
|
line = data + y * wpl;
|
|
switch (d)
|
|
{
|
|
case 1:
|
|
val = GET_DATA_BIT(line, x);
|
|
if (val)
|
|
CLEAR_DATA_BIT(line, x);
|
|
else
|
|
SET_DATA_BIT(line, x);
|
|
break;
|
|
case 2:
|
|
val = GET_DATA_DIBIT(line, x);
|
|
val ^= 0x3;
|
|
SET_DATA_DIBIT(line, x, val);
|
|
break;
|
|
case 4:
|
|
val = GET_DATA_QBIT(line, x);
|
|
val ^= 0xf;
|
|
SET_DATA_QBIT(line, x, val);
|
|
break;
|
|
case 8:
|
|
val = GET_DATA_BYTE(line, x);
|
|
val ^= 0xff;
|
|
SET_DATA_BYTE(line, x, val);
|
|
break;
|
|
case 16:
|
|
val = GET_DATA_TWO_BYTES(line, x);
|
|
val ^= 0xffff;
|
|
SET_DATA_TWO_BYTES(line, x, val);
|
|
break;
|
|
case 32:
|
|
val = line[x] ^ 0xffffffff;
|
|
line[x] = val;
|
|
break;
|
|
default:
|
|
return ERROR_INT("depth must be in {1,2,4,8,16,32} bpp", procName, 1);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* setPixelLow()
|
|
*
|
|
* Input: line (ptr to beginning of line),
|
|
* x (pixel location in line)
|
|
* depth (bpp)
|
|
* val (to be inserted)
|
|
* Return: void
|
|
*
|
|
* Notes:
|
|
* (1) Caution: input variables are not checked!
|
|
*/
|
|
void
|
|
setPixelLow(l_uint32 *line,
|
|
l_int32 x,
|
|
l_int32 depth,
|
|
l_uint32 val)
|
|
{
|
|
switch (depth)
|
|
{
|
|
case 1:
|
|
if (val)
|
|
SET_DATA_BIT(line, x);
|
|
else
|
|
CLEAR_DATA_BIT(line, x);
|
|
break;
|
|
case 2:
|
|
SET_DATA_DIBIT(line, x, val);
|
|
break;
|
|
case 4:
|
|
SET_DATA_QBIT(line, x, val);
|
|
break;
|
|
case 8:
|
|
SET_DATA_BYTE(line, x, val);
|
|
break;
|
|
case 16:
|
|
SET_DATA_TWO_BYTES(line, x, val);
|
|
break;
|
|
case 32:
|
|
line[x] = val;
|
|
break;
|
|
default:
|
|
fprintf(stderr, "illegal depth in setPixelLow()\n");
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------*
|
|
* Full image clear/set/set-to-arbitrary-value/invert *
|
|
*-------------------------------------------------------------*/
|
|
/*!
|
|
* pixClearAll()
|
|
*
|
|
* Input: pix (all depths; use cmapped with caution)
|
|
* Return: 0 if OK, 1 on error
|
|
*
|
|
* Notes:
|
|
* (1) Clears all data to 0. For 1 bpp, this is white; for grayscale
|
|
* or color, this is black.
|
|
* (2) Caution: for colormapped pix, this sets the color to the first
|
|
* one in the colormap. Be sure that this is the intended color!
|
|
*/
|
|
l_int32
|
|
pixClearAll(PIX *pix)
|
|
{
|
|
PROCNAME("pixClearAll");
|
|
|
|
if (!pix)
|
|
return ERROR_INT("pix not defined", procName, 1);
|
|
|
|
pixRasterop(pix, 0, 0, pixGetWidth(pix), pixGetHeight(pix),
|
|
PIX_CLR, NULL, 0, 0);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* pixSetAll()
|
|
*
|
|
* Input: pix (all depths; use cmapped with caution)
|
|
* Return: 0 if OK, 1 on error
|
|
*
|
|
* Notes:
|
|
* (1) Sets all data to 1. For 1 bpp, this is black; for grayscale
|
|
* or color, this is white.
|
|
* (2) Caution: for colormapped pix, this sets the pixel value to the
|
|
* maximum value supported by the colormap: 2^d - 1. However, this
|
|
* color may not be defined, because the colormap may not be full.
|
|
*/
|
|
l_int32
|
|
pixSetAll(PIX *pix)
|
|
{
|
|
l_int32 n;
|
|
PIXCMAP *cmap;
|
|
|
|
PROCNAME("pixSetAll");
|
|
|
|
if (!pix)
|
|
return ERROR_INT("pix not defined", procName, 1);
|
|
if ((cmap = pixGetColormap(pix)) != NULL) {
|
|
n = pixcmapGetCount(cmap);
|
|
if (n < cmap->nalloc) /* cmap is not full */
|
|
return ERROR_INT("cmap entry does not exist", procName, 1);
|
|
}
|
|
|
|
pixRasterop(pix, 0, 0, pixGetWidth(pix), pixGetHeight(pix),
|
|
PIX_SET, NULL, 0, 0);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* pixSetAllArbitrary()
|
|
*
|
|
* Input: pix (all depths; use cmapped with caution)
|
|
* val (value to set all pixels)
|
|
* Return: 0 if OK; 1 on error
|
|
*
|
|
* Notes:
|
|
* (1) For colormapped pix, be sure the value is the intended
|
|
* one in the colormap.
|
|
* (2) Caution: for colormapped pix, this sets each pixel to the
|
|
* color at the index equal to val. Be sure that this index
|
|
* exists in the colormap and that it is the intended one!
|
|
*/
|
|
l_int32
|
|
pixSetAllArbitrary(PIX *pix,
|
|
l_uint32 val)
|
|
{
|
|
l_int32 n, i, j, w, h, d, wpl, npix;
|
|
l_uint32 maxval, wordval;
|
|
l_uint32 *data, *line;
|
|
PIXCMAP *cmap;
|
|
|
|
PROCNAME("pixSetAllArbitrary");
|
|
|
|
if (!pix)
|
|
return ERROR_INT("pix not defined", procName, 1);
|
|
if ((cmap = pixGetColormap(pix)) != NULL) {
|
|
n = pixcmapGetCount(cmap);
|
|
if (val < 0) {
|
|
L_WARNING("index not in colormap; using first color", procName);
|
|
val = 0;
|
|
}
|
|
else if (val >= n) {
|
|
L_WARNING("index not in colormap; using last color", procName);
|
|
val = n - 1;
|
|
}
|
|
}
|
|
|
|
pixGetDimensions(pix, &w, &h, &d);
|
|
if (d == 32)
|
|
maxval = 0xffffffff;
|
|
else
|
|
maxval = (1 << d) - 1;
|
|
if (val < 0) {
|
|
L_WARNING("invalid pixel value; set to 0", procName);
|
|
val = 0;
|
|
}
|
|
if (val > maxval) {
|
|
L_WARNING_INT("invalid pixel val; set to maxval = %d",
|
|
procName, maxval);
|
|
val = maxval;
|
|
}
|
|
|
|
/* Set up word to tile with */
|
|
wordval = 0;
|
|
npix = 32 / d; /* number of pixels per 32 bit word */
|
|
for (j = 0; j < npix; j++)
|
|
wordval |= (val << (j * d));
|
|
|
|
wpl = pixGetWpl(pix);
|
|
data = pixGetData(pix);
|
|
for (i = 0; i < h; i++) {
|
|
line = data + i * wpl;
|
|
for (j = 0; j < wpl; j++) {
|
|
*(line + j) = wordval;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* pixSetBlackOrWhite()
|
|
*
|
|
* Input: pixs (all depths; cmap ok)
|
|
* incolor (L_BRING_IN_BLACK or L_BRING_IN_WHITE)
|
|
* Return: 0 if OK; 1 on error
|
|
*
|
|
* Notes:
|
|
* (1) Function for setting all pixels in an image to either black
|
|
* or white.
|
|
* (2) If pixs is colormapped, it adds black or white to the
|
|
* colormap if it's not there and there is room. If the colormap
|
|
* is full, it finds the closest color in intensity.
|
|
* This index is written to all pixels.
|
|
*/
|
|
l_int32
|
|
pixSetBlackOrWhite(PIX *pixs,
|
|
l_int32 incolor)
|
|
{
|
|
l_int32 d, index;
|
|
PIXCMAP *cmap;
|
|
|
|
PROCNAME("pixSetBlackOrWhite");
|
|
|
|
if (!pixs)
|
|
return ERROR_INT("pix not defined", procName, 1);
|
|
if (incolor != L_BRING_IN_BLACK && incolor != L_BRING_IN_WHITE)
|
|
return ERROR_INT("invalid incolor", procName, 1);
|
|
|
|
cmap = pixGetColormap(pixs);
|
|
d = pixGetDepth(pixs);
|
|
if (!cmap) {
|
|
if ((d == 1 && incolor == L_BRING_IN_BLACK) ||
|
|
(d > 1 && incolor == L_BRING_IN_WHITE))
|
|
pixSetAll(pixs);
|
|
else
|
|
pixClearAll(pixs);
|
|
}
|
|
else { /* handle colormap */
|
|
if (incolor == L_BRING_IN_BLACK)
|
|
pixcmapAddBlackOrWhite(cmap, 0, &index);
|
|
else /* L_BRING_IN_WHITE */
|
|
pixcmapAddBlackOrWhite(cmap, 1, &index);
|
|
pixSetAllArbitrary(pixs, index);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------*
|
|
* Rectangular region clear/set/set-to-arbitrary-value *
|
|
*-------------------------------------------------------------*/
|
|
/*!
|
|
* pixClearInRect()
|
|
*
|
|
* Input: pix (all depths; can be cmapped)
|
|
* box (in which all pixels will be cleared)
|
|
* Return: 0 if OK, 1 on error
|
|
*
|
|
* Notes:
|
|
* (1) Clears all data in rect to 0. For 1 bpp, this is white;
|
|
* for grayscale or color, this is black.
|
|
* (2) Caution: for colormapped pix, this sets the color to the first
|
|
* one in the colormap. Be sure that this is the intended color!
|
|
*/
|
|
l_int32
|
|
pixClearInRect(PIX *pix,
|
|
BOX *box)
|
|
{
|
|
l_int32 x, y, w, h;
|
|
|
|
PROCNAME("pixClearInRect");
|
|
|
|
if (!pix)
|
|
return ERROR_INT("pix not defined", procName, 1);
|
|
if (!box)
|
|
return ERROR_INT("box not defined", procName, 1);
|
|
|
|
boxGetGeometry(box, &x, &y, &w, &h);
|
|
pixRasterop(pix, x, y, w, h, PIX_CLR, NULL, 0, 0);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* pixSetInRect()
|
|
*
|
|
* Input: pix (all depths, can be cmapped)
|
|
* box (in which all pixels will be set)
|
|
* Return: 0 if OK, 1 on error
|
|
*
|
|
* Notes:
|
|
* (1) Sets all data in rect to 1. For 1 bpp, this is black;
|
|
* for grayscale or color, this is white.
|
|
* (2) Caution: for colormapped pix, this sets the pixel value to the
|
|
* maximum value supported by the colormap: 2^d - 1. However, this
|
|
* color may not be defined, because the colormap may not be full.
|
|
*/
|
|
l_int32
|
|
pixSetInRect(PIX *pix,
|
|
BOX *box)
|
|
{
|
|
l_int32 n, x, y, w, h;
|
|
PIXCMAP *cmap;
|
|
|
|
PROCNAME("pixSetInRect");
|
|
|
|
if (!pix)
|
|
return ERROR_INT("pix not defined", procName, 1);
|
|
if (!box)
|
|
return ERROR_INT("box not defined", procName, 1);
|
|
if ((cmap = pixGetColormap(pix)) != NULL) {
|
|
n = pixcmapGetCount(cmap);
|
|
if (n < cmap->nalloc) /* cmap is not full */
|
|
return ERROR_INT("cmap entry does not exist", procName, 1);
|
|
}
|
|
|
|
boxGetGeometry(box, &x, &y, &w, &h);
|
|
pixRasterop(pix, x, y, w, h, PIX_SET, NULL, 0, 0);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* pixSetInRectArbitrary()
|
|
*
|
|
* Input: pix (all depths; can be cmapped)
|
|
* box (in which all pixels will be set to val)
|
|
* val (value to set all pixels)
|
|
* Return: 0 if OK; 1 on error
|
|
*
|
|
* Notes:
|
|
* (1) For colormapped pix, be sure the value is the intended
|
|
* one in the colormap.
|
|
* (2) Caution: for colormapped pix, this sets each pixel in the
|
|
* rect to the color at the index equal to val. Be sure that
|
|
* this index exists in the colormap and that it is the intended one!
|
|
*/
|
|
l_int32
|
|
pixSetInRectArbitrary(PIX *pix,
|
|
BOX *box,
|
|
l_uint32 val)
|
|
{
|
|
l_int32 n, x, y, xstart, xend, ystart, yend, bw, bh, w, h, d, wpl, maxval;
|
|
l_uint32 *data, *line;
|
|
BOX *boxc;
|
|
PIXCMAP *cmap;
|
|
|
|
PROCNAME("pixSetInRectArbitrary");
|
|
|
|
if (!pix)
|
|
return ERROR_INT("pix not defined", procName, 1);
|
|
if (!box)
|
|
return ERROR_INT("box not defined", procName, 1);
|
|
pixGetDimensions(pix, &w, &h, &d);
|
|
if (d != 1 && d != 2 && d != 4 && d !=8 && d != 16 && d != 32)
|
|
return ERROR_INT("depth must be in {1,2,4,8,16,32} bpp", procName, 1);
|
|
if ((cmap = pixGetColormap(pix)) != NULL) {
|
|
n = pixcmapGetCount(cmap);
|
|
if (val < 0) {
|
|
L_WARNING("index not in colormap; using first color", procName);
|
|
val = 0;
|
|
}
|
|
else if (val >= n) {
|
|
L_WARNING("index not in colormap; using last color", procName);
|
|
val = n - 1;
|
|
}
|
|
}
|
|
|
|
if (d == 32)
|
|
maxval = 0xffffffff;
|
|
else
|
|
maxval = (1 << d) - 1;
|
|
if (val < 0) {
|
|
L_WARNING("invalid pixel value; set to 0", procName);
|
|
val = 0;
|
|
}
|
|
if (val > maxval) {
|
|
L_WARNING_INT("invalid pixel val; set to maxval = %d",
|
|
procName, maxval);
|
|
val = maxval;
|
|
}
|
|
|
|
/* Handle the simple cases: the min and max values */
|
|
if (val == 0) {
|
|
pixClearInRect(pix, box);
|
|
return 0;
|
|
}
|
|
if (d == 1 ||
|
|
(d == 2 && val == 3) ||
|
|
(d == 4 && val == 0xf) ||
|
|
(d == 8 && val == 0xff) ||
|
|
(d == 16 && val == 0xffff) ||
|
|
(d == 32 && ((val ^ 0xffffff00) >> 8 == 0))) {
|
|
pixSetInRect(pix, box);
|
|
return 0;
|
|
}
|
|
|
|
/* Find the overlap of box with the input pix */
|
|
if ((boxc = boxClipToRectangle(box, w, h)) == NULL)
|
|
return ERROR_INT("no overlap of box with image", procName, 1);
|
|
boxGetGeometry(boxc, &xstart, &ystart, &bw, &bh);
|
|
xend = xstart + bw - 1;
|
|
yend = ystart + bh - 1;
|
|
boxDestroy(&boxc);
|
|
|
|
wpl = pixGetWpl(pix);
|
|
data = pixGetData(pix);
|
|
for (y = ystart; y <= yend; y++) {
|
|
line = data + y * wpl;
|
|
for (x = xstart; x <= xend; x++) {
|
|
switch(d)
|
|
{
|
|
case 2:
|
|
SET_DATA_DIBIT(line, x, val);
|
|
break;
|
|
case 4:
|
|
SET_DATA_QBIT(line, x, val);
|
|
break;
|
|
case 8:
|
|
SET_DATA_BYTE(line, x, val);
|
|
break;
|
|
case 16:
|
|
SET_DATA_TWO_BYTES(line, x, val);
|
|
break;
|
|
case 32:
|
|
line[x] = val;
|
|
break;
|
|
default:
|
|
return ERROR_INT("depth not 2|4|8|16|32 bpp", procName, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* pixBlendInRect()
|
|
*
|
|
* Input: pixs (32 bpp rgb)
|
|
* box (in which all pixels will be blended)
|
|
* val (blend value; 0xrrggbb00)
|
|
* fract (fraction of color to be blended with each pixel in pixs)
|
|
* Return: 0 if OK; 1 on error
|
|
*
|
|
* Notes:
|
|
* (1) This is an in-place function. It blends the input color @val
|
|
* with the pixels in pixs in the specified rectangle.
|
|
*/
|
|
l_int32
|
|
pixBlendInRect(PIX *pixs,
|
|
BOX *box,
|
|
l_uint32 val,
|
|
l_float32 fract)
|
|
{
|
|
l_int32 i, j, bx, by, bw, bh, w, h, wpls;
|
|
l_int32 prval, pgval, pbval, rval, gval, bval;
|
|
l_uint32 val32;
|
|
l_uint32 *datas, *lines;
|
|
|
|
PROCNAME("pixBlendInRect");
|
|
|
|
if (!pixs || pixGetDepth(pixs) != 32)
|
|
return ERROR_INT("pixs not defined or not 32 bpp", procName, 1);
|
|
if (!box)
|
|
return ERROR_INT("box not defined", procName, 1);
|
|
|
|
boxGetGeometry(box, &bx, &by, &bw, &bh);
|
|
pixGetDimensions(pixs, &w, &h, NULL);
|
|
datas = pixGetData(pixs);
|
|
wpls = pixGetWpl(pixs);
|
|
extractRGBValues(val, &rval, &gval, &bval);
|
|
for (i = 0; i < bh; i++) { /* scan over box */
|
|
if (by + i < 0 || by + i >= h) continue;
|
|
lines = datas + (by + i) * wpls;
|
|
for (j = 0; j < bw; j++) {
|
|
if (bx + j < 0 || bx + j >= w) continue;
|
|
val32 = *(lines + bx + j);
|
|
extractRGBValues(val32, &prval, &pgval, &pbval);
|
|
prval = (l_int32)((1. - fract) * prval + fract * rval);
|
|
pgval = (l_int32)((1. - fract) * pgval + fract * gval);
|
|
pbval = (l_int32)((1. - fract) * pbval + fract * bval);
|
|
composeRGBPixel(prval, pgval, pbval, &val32);
|
|
*(lines + bx + j) = val32;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------*
|
|
* Set pad bits *
|
|
*-------------------------------------------------------------*/
|
|
/*!
|
|
* pixSetPadBits()
|
|
*
|
|
* Input: pix (1, 2, 4, 8, 16, 32 bpp)
|
|
* val (0 or 1)
|
|
* Return: 0 if OK; 1 on error
|
|
*
|
|
* Notes:
|
|
* (1) The pad bits are the bits that expand each scanline to a
|
|
* multiple of 32 bits. They are usually not used in
|
|
* image processing operations. When boundary conditions
|
|
* are important, as in seedfill, they must be set properly.
|
|
* (2) This sets the value of the pad bits (if any) in the last
|
|
* 32-bit word in each scanline.
|
|
* (3) For 32 bpp pix, there are no pad bits, so this is a no-op.
|
|
*/
|
|
l_int32
|
|
pixSetPadBits(PIX *pix,
|
|
l_int32 val)
|
|
{
|
|
l_int32 i, w, h, d, wpl, endbits, fullwords;
|
|
l_uint32 mask;
|
|
l_uint32 *data, *pword;
|
|
|
|
PROCNAME("pixSetPadBits");
|
|
|
|
if (!pix)
|
|
return ERROR_INT("pix not defined", procName, 1);
|
|
|
|
pixGetDimensions(pix, &w, &h, &d);
|
|
if (d == 32) /* no padding exists for 32 bpp */
|
|
return 0;
|
|
|
|
data = pixGetData(pix);
|
|
wpl = pixGetWpl(pix);
|
|
endbits = 32 - ((w * d) % 32);
|
|
if (endbits == 32) /* no partial word */
|
|
return 0;
|
|
fullwords = w * d / 32;
|
|
|
|
mask = rmask32[endbits];
|
|
if (val == 0)
|
|
mask = ~mask;
|
|
|
|
for (i = 0; i < h; i++) {
|
|
pword = data + i * wpl + fullwords;
|
|
if (val == 0) /* clear */
|
|
*pword = *pword & mask;
|
|
else /* set */
|
|
*pword = *pword | mask;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* pixSetPadBitsBand()
|
|
*
|
|
* Input: pix (1, 2, 4, 8, 16, 32 bpp)
|
|
* by (starting y value of band)
|
|
* bh (height of band)
|
|
* val (0 or 1)
|
|
* Return: 0 if OK; 1 on error
|
|
*
|
|
* Notes:
|
|
* (1) The pad bits are the bits that expand each scanline to a
|
|
* multiple of 32 bits. They are usually not used in
|
|
* image processing operations. When boundary conditions
|
|
* are important, as in seedfill, they must be set properly.
|
|
* (2) This sets the value of the pad bits (if any) in the last
|
|
* 32-bit word in each scanline, within the specified
|
|
* band of raster lines.
|
|
* (3) For 32 bpp pix, there are no pad bits, so this is a no-op.
|
|
*/
|
|
l_int32
|
|
pixSetPadBitsBand(PIX *pix,
|
|
l_int32 by,
|
|
l_int32 bh,
|
|
l_int32 val)
|
|
{
|
|
l_int32 i, w, h, d, wpl, endbits, fullwords;
|
|
l_uint32 mask;
|
|
l_uint32 *data, *pword;
|
|
|
|
PROCNAME("pixSetPadBitsBand");
|
|
|
|
if (!pix)
|
|
return ERROR_INT("pix not defined", procName, 1);
|
|
|
|
pixGetDimensions(pix, &w, &h, &d);
|
|
if (d == 32) /* no padding exists for 32 bpp */
|
|
return 0;
|
|
|
|
if (by < 0)
|
|
by = 0;
|
|
if (by >= h)
|
|
return ERROR_INT("start y not in image", procName, 1);
|
|
if (by + bh > h)
|
|
bh = h - by;
|
|
|
|
data = pixGetData(pix);
|
|
wpl = pixGetWpl(pix);
|
|
endbits = 32 - ((w * d) % 32);
|
|
if (endbits == 32) /* no partial word */
|
|
return 0;
|
|
fullwords = w * d / 32;
|
|
|
|
mask = rmask32[endbits];
|
|
if (val == 0)
|
|
mask = ~mask;
|
|
|
|
for (i = by; i < by + bh; i++) {
|
|
pword = data + i * wpl + fullwords;
|
|
if (val == 0) /* clear */
|
|
*pword = *pword & mask;
|
|
else /* set */
|
|
*pword = *pword | mask;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------*
|
|
* Set border pixels *
|
|
*-------------------------------------------------------------*/
|
|
/*!
|
|
* pixSetOrClearBorder()
|
|
*
|
|
* Input: pixs (all depths)
|
|
* left, right, top, bot (amount to set or clear)
|
|
* operation (PIX_SET or PIX_CLR)
|
|
* Return: 0 if OK; 1 on error
|
|
*
|
|
* Notes:
|
|
* (1) The border region is defined to be the region in the
|
|
* image within a specific distance of each edge. Here, we
|
|
* allow the pixels within a specified distance of each
|
|
* edge to be set independently. This either sets or
|
|
* clears all pixels in the border region.
|
|
* (2) For binary images, use PIX_SET for black and PIX_CLR for white.
|
|
* (3) For grayscale or color images, use PIX_SET for white
|
|
* and PIX_CLR for black.
|
|
*/
|
|
l_int32
|
|
pixSetOrClearBorder(PIX *pixs,
|
|
l_int32 left,
|
|
l_int32 right,
|
|
l_int32 top,
|
|
l_int32 bot,
|
|
l_int32 op)
|
|
{
|
|
l_int32 w, h;
|
|
|
|
PROCNAME("pixSetOrClearBorder");
|
|
|
|
if (!pixs)
|
|
return ERROR_INT("pixs not defined", procName, 1);
|
|
if (op != PIX_SET && op != PIX_CLR)
|
|
return ERROR_INT("op must be PIX_SET or PIX_CLR", procName, 1);
|
|
|
|
pixGetDimensions(pixs, &w, &h, NULL);
|
|
pixRasterop(pixs, 0, 0, left, h, op, NULL, 0, 0);
|
|
pixRasterop(pixs, w - right, 0, right, h, op, NULL, 0, 0);
|
|
pixRasterop(pixs, 0, 0, w, top, op, NULL, 0, 0);
|
|
pixRasterop(pixs, 0, h - bot, w, bot, op, NULL, 0, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* pixSetBorderVal()
|
|
*
|
|
* Input: pixs (8, 16 or 32 bpp)
|
|
* left, right, top, bot (amount to set)
|
|
* val (value to set at each border pixel)
|
|
* Return: 0 if OK; 1 on error
|
|
*
|
|
* Notes:
|
|
* (1) The border region is defined to be the region in the
|
|
* image within a specific distance of each edge. Here, we
|
|
* allow the pixels within a specified distance of each
|
|
* edge to be set independently. This sets the pixels
|
|
* in the border region to the given input value.
|
|
* (2) For efficiency, use pixSetOrClearBorder() if
|
|
* you're setting the border to either black or white.
|
|
* (3) If d != 32, the input value should be masked off
|
|
* to the appropriate number of least significant bits.
|
|
* (4) The code is easily generalized for 2 or 4 bpp.
|
|
*/
|
|
l_int32
|
|
pixSetBorderVal(PIX *pixs,
|
|
l_int32 left,
|
|
l_int32 right,
|
|
l_int32 top,
|
|
l_int32 bot,
|
|
l_uint32 val)
|
|
{
|
|
l_int32 w, h, d, wpls, i, j, bstart, rstart;
|
|
l_uint32 *datas, *lines;
|
|
|
|
PROCNAME("pixSetBorderVal");
|
|
|
|
if (!pixs)
|
|
return ERROR_INT("pixs not defined", procName, 1);
|
|
pixGetDimensions(pixs, &w, &h, &d);
|
|
if (d != 8 && d != 16 && d != 32)
|
|
return ERROR_INT("depth must be 8, 16 or 32 bpp", procName, 1);
|
|
|
|
datas = pixGetData(pixs);
|
|
wpls = pixGetWpl(pixs);
|
|
if (d == 8) {
|
|
val &= 0xff;
|
|
for (i = 0; i < top; i++) {
|
|
lines = datas + i * wpls;
|
|
for (j = 0; j < w; j++)
|
|
SET_DATA_BYTE(lines, j, val);
|
|
}
|
|
rstart = w - right;
|
|
bstart = h - bot;
|
|
for (i = top; i < bstart; i++) {
|
|
lines = datas + i * wpls;
|
|
for (j = 0; j < left; j++)
|
|
SET_DATA_BYTE(lines, j, val);
|
|
for (j = rstart; j < w; j++)
|
|
SET_DATA_BYTE(lines, j, val);
|
|
}
|
|
for (i = bstart; i < h; i++) {
|
|
lines = datas + i * wpls;
|
|
for (j = 0; j < w; j++)
|
|
SET_DATA_BYTE(lines, j, val);
|
|
}
|
|
}
|
|
else if (d == 16) {
|
|
val &= 0xffff;
|
|
for (i = 0; i < top; i++) {
|
|
lines = datas + i * wpls;
|
|
for (j = 0; j < w; j++)
|
|
SET_DATA_TWO_BYTES(lines, j, val);
|
|
}
|
|
rstart = w - right;
|
|
bstart = h - bot;
|
|
for (i = top; i < bstart; i++) {
|
|
lines = datas + i * wpls;
|
|
for (j = 0; j < left; j++)
|
|
SET_DATA_TWO_BYTES(lines, j, val);
|
|
for (j = rstart; j < w; j++)
|
|
SET_DATA_TWO_BYTES(lines, j, val);
|
|
}
|
|
for (i = bstart; i < h; i++) {
|
|
lines = datas + i * wpls;
|
|
for (j = 0; j < w; j++)
|
|
SET_DATA_TWO_BYTES(lines, j, val);
|
|
}
|
|
}
|
|
else { /* d == 32 */
|
|
for (i = 0; i < top; i++) {
|
|
lines = datas + i * wpls;
|
|
for (j = 0; j < w; j++)
|
|
*(lines + j) = val;
|
|
}
|
|
rstart = w - right;
|
|
bstart = h - bot;
|
|
for (i = top; i < bstart; i++) {
|
|
lines = datas + i * wpls;
|
|
for (j = 0; j < left; j++)
|
|
*(lines + j) = val;
|
|
for (j = rstart; j < w; j++)
|
|
*(lines + j) = val;
|
|
}
|
|
for (i = bstart; i < h; i++) {
|
|
lines = datas + i * wpls;
|
|
for (j = 0; j < w; j++)
|
|
*(lines + j) = val;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* pixSetMirroredBorder()
|
|
*
|
|
* Input: pixs (all depths; colormap ok)
|
|
* left, right, top, bot (number of pixels to set)
|
|
* Return: 0 if OK, 1 on error
|
|
*
|
|
* Notes:
|
|
* (1) This applies what is effectively mirror boundary conditions
|
|
* to a border region in the image. It is in-place.
|
|
* (2) This is useful for setting pixels near the border to a
|
|
* value representative of the near pixels to the interior.
|
|
* (3) The general pixRasterop() is used for an in-place operation here
|
|
* because there is no overlap between the src and dest rectangles.
|
|
*/
|
|
l_int32
|
|
pixSetMirroredBorder(PIX *pixs,
|
|
l_int32 left,
|
|
l_int32 right,
|
|
l_int32 top,
|
|
l_int32 bot)
|
|
{
|
|
l_int32 i, j, w, h;
|
|
|
|
PROCNAME("pixSetMirroredBorder");
|
|
|
|
if (!pixs)
|
|
return ERROR_INT("pixs not defined", procName, 1);
|
|
|
|
pixGetDimensions(pixs, &w, &h, NULL);
|
|
for (j = 0; j < left; j++)
|
|
pixRasterop(pixs, left - 1 - j, top, 1, h - top - bot, PIX_SRC,
|
|
pixs, left + j, top);
|
|
for (j = 0; j < right; j++)
|
|
pixRasterop(pixs, w - right + j, top, 1, h - top - bot, PIX_SRC,
|
|
pixs, w - right - 1 - j, top);
|
|
for (i = 0; i < top; i++)
|
|
pixRasterop(pixs, 0, top - 1 - i, w, 1, PIX_SRC,
|
|
pixs, 0, top + i);
|
|
for (i = 0; i < bot; i++)
|
|
pixRasterop(pixs, 0, h - bot + i, w, 1, PIX_SRC,
|
|
pixs, 0, h - bot - 1 - i);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* pixCopyBorder()
|
|
*
|
|
* Input: pixd (all depths; colormap ok; can be NULL)
|
|
* pixs (same depth and size as pixd)
|
|
* left, right, top, bot (number of pixels to copy)
|
|
* Return: pixd, or null on error if pixd is not defined
|
|
*
|
|
* Notes:
|
|
* (1) pixd can be null, but otherwise it must be the same size
|
|
* and depth as pixs. Always returns pixd.
|
|
* (1) This is useful in situations where by setting a few border
|
|
* pixels we can avoid having to copy all pixels in pixs into
|
|
* pixd as an initialization step for some operation.
|
|
*/
|
|
PIX *
|
|
pixCopyBorder(PIX *pixd,
|
|
PIX *pixs,
|
|
l_int32 left,
|
|
l_int32 right,
|
|
l_int32 top,
|
|
l_int32 bot)
|
|
{
|
|
l_int32 w, h;
|
|
|
|
PROCNAME("pixCopyBorder");
|
|
|
|
if (!pixs)
|
|
return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
|
|
|
|
if (pixd) {
|
|
if (pixd == pixs) {
|
|
L_WARNING("same: nothing to do", procName);
|
|
return pixd;
|
|
}
|
|
else if (!pixSizesEqual(pixs, pixd))
|
|
return (PIX *)ERROR_PTR("pixs and pixd sizes differ",
|
|
procName, pixd);
|
|
}
|
|
else {
|
|
if ((pixd = pixCreateTemplateNoInit(pixs)) == NULL)
|
|
return (PIX *)ERROR_PTR("pixd not made", procName, pixd);
|
|
}
|
|
|
|
pixGetDimensions(pixs, &w, &h, NULL);
|
|
pixRasterop(pixd, 0, 0, left, h, PIX_SRC, pixs, 0, 0);
|
|
pixRasterop(pixd, w - right, 0, right, h, PIX_SRC, pixs, w - right, 0);
|
|
pixRasterop(pixd, 0, 0, w, top, PIX_SRC, pixs, 0, 0);
|
|
pixRasterop(pixd, 0, h - bot, w, bot, PIX_SRC, pixs, 0, h - bot);
|
|
|
|
return pixd;
|
|
}
|
|
|
|
|
|
|
|
/*-------------------------------------------------------------*
|
|
* Add and remove border *
|
|
*-------------------------------------------------------------*/
|
|
/*!
|
|
* pixAddBorder()
|
|
*
|
|
* Input: pixs (all depths; colormap ok)
|
|
* npix (number of pixels to be added to each side)
|
|
* val (value of added border pixels)
|
|
* Return: pixd (with the input pixs centered), or null on error
|
|
*
|
|
* Notes:
|
|
* (1) See pixAddBorderGeneral() for values of white & black pixels.
|
|
*/
|
|
PIX *
|
|
pixAddBorder(PIX *pixs,
|
|
l_int32 npix,
|
|
l_uint32 val)
|
|
{
|
|
PROCNAME("pixAddBorder");
|
|
|
|
if (!pixs)
|
|
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
|
|
if (npix == 0)
|
|
return pixClone(pixs);
|
|
return pixAddBorderGeneral(pixs, npix, npix, npix, npix, val);
|
|
}
|
|
|
|
|
|
/*!
|
|
* pixAddBorderGeneral()
|
|
*
|
|
* Input: pixs (all depths; colormap ok)
|
|
* left, right, top, bot (number of pixels added)
|
|
* val (value of added border pixels)
|
|
* Return: pixd (with the input pixs inserted), or null on error
|
|
*
|
|
* Notes:
|
|
* (1) For binary images:
|
|
* white: val = 0
|
|
* black: val = 1
|
|
* For grayscale images:
|
|
* white: val = 2 ** d - 1
|
|
* black: val = 0
|
|
* For rgb color images:
|
|
* white: val = 0xffffff00
|
|
* black: val = 0
|
|
* For colormapped images, use 'index' found this way:
|
|
* white: pixcmapGetRankIntensity(cmap, 1.0, &index);
|
|
* black: pixcmapGetRankIntensity(cmap, 0.0, &index);
|
|
*/
|
|
PIX *
|
|
pixAddBorderGeneral(PIX *pixs,
|
|
l_int32 left,
|
|
l_int32 right,
|
|
l_int32 top,
|
|
l_int32 bot,
|
|
l_uint32 val)
|
|
{
|
|
l_int32 ws, hs, wd, hd, d, op;
|
|
PIX *pixd;
|
|
|
|
PROCNAME("pixAddBorderGeneral");
|
|
|
|
if (!pixs)
|
|
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
|
|
if (left < 0 || right < 0 || top < 0 || bot < 0)
|
|
return (PIX *)ERROR_PTR("negative border added!", procName, NULL);
|
|
|
|
pixGetDimensions(pixs, &ws, &hs, &d);
|
|
wd = ws + left + right;
|
|
hd = hs + top + bot;
|
|
if ((pixd = pixCreateNoInit(wd, hd, d)) == NULL)
|
|
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
|
|
pixCopyResolution(pixd, pixs);
|
|
pixCopyColormap(pixd, pixs);
|
|
|
|
/* Set the new border pixels */
|
|
op = UNDEF;
|
|
if (val == 0)
|
|
op = PIX_CLR;
|
|
else if ((d == 1 && val == 1) || (d == 2 && val == 3) ||
|
|
(d == 4 && val == 0xf) || (d == 8 && val == 0xff) ||
|
|
(d == 16 && val == 0xffff) || (d == 32 && (val >> 8) == 0xffffff))
|
|
op = PIX_SET;
|
|
if (op == UNDEF)
|
|
pixSetAllArbitrary(pixd, val); /* a little extra writing ! */
|
|
else {
|
|
pixRasterop(pixd, 0, 0, left, hd, op, NULL, 0, 0);
|
|
pixRasterop(pixd, wd - right, 0, right, hd, op, NULL, 0, 0);
|
|
pixRasterop(pixd, 0, 0, wd, top, op, NULL, 0, 0);
|
|
pixRasterop(pixd, 0, hd - bot, wd, bot, op, NULL, 0, 0);
|
|
}
|
|
|
|
/* Copy pixs into the interior */
|
|
pixRasterop(pixd, left, top, ws, hs, PIX_SRC, pixs, 0, 0);
|
|
return pixd;
|
|
}
|
|
|
|
|
|
/*!
|
|
* pixRemoveBorder()
|
|
*
|
|
* Input: pixs (all depths; colormap ok)
|
|
* npix (number to be removed from each of the 4 sides)
|
|
* Return: pixd (with pixels removed around border), or null on error
|
|
*/
|
|
PIX *
|
|
pixRemoveBorder(PIX *pixs,
|
|
l_int32 npix)
|
|
{
|
|
PROCNAME("pixRemoveBorder");
|
|
|
|
if (!pixs)
|
|
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
|
|
if (npix == 0)
|
|
return pixClone(pixs);
|
|
return pixRemoveBorderGeneral(pixs, npix, npix, npix, npix);
|
|
}
|
|
|
|
|
|
/*!
|
|
* pixRemoveBorderGeneral()
|
|
*
|
|
* Input: pixs (all depths; colormap ok)
|
|
* left, right, top, bot (number of pixels added)
|
|
* Return: pixd (with pixels removed around border), or null on error
|
|
*/
|
|
PIX *
|
|
pixRemoveBorderGeneral(PIX *pixs,
|
|
l_int32 left,
|
|
l_int32 right,
|
|
l_int32 top,
|
|
l_int32 bot)
|
|
{
|
|
l_int32 ws, hs, wd, hd, d;
|
|
PIX *pixd;
|
|
|
|
PROCNAME("pixRemoveBorderGeneral");
|
|
|
|
if (!pixs)
|
|
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
|
|
if (left < 0 || right < 0 || top < 0 || bot < 0)
|
|
return (PIX *)ERROR_PTR("negative border removed!", procName, NULL);
|
|
|
|
pixGetDimensions(pixs, &ws, &hs, &d);
|
|
wd = ws - left - right;
|
|
hd = hs - top - bot;
|
|
if (wd <= 0)
|
|
return (PIX *)ERROR_PTR("width must be > 0", procName, NULL);
|
|
if (hd <= 0)
|
|
return (PIX *)ERROR_PTR("height must be > 0", procName, NULL);
|
|
if ((pixd = pixCreateNoInit(wd, hd, d)) == NULL)
|
|
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
|
|
pixCopyResolution(pixd, pixs);
|
|
pixCopyColormap(pixd, pixs);
|
|
|
|
pixRasterop(pixd, 0, 0, wd, hd, PIX_SRC, pixs, left, top);
|
|
return pixd;
|
|
}
|
|
|
|
|
|
/*!
|
|
* pixAddMirroredBorder()
|
|
*
|
|
* Input: pixs (all depths; colormap ok)
|
|
* left, right, top, bot (number of pixels added)
|
|
* Return: pixd, or null on error
|
|
*
|
|
* Notes:
|
|
* (1) This applies what is effectively mirror boundary conditions.
|
|
* For the added border pixels in pixd, the pixels in pixs
|
|
* near the border are mirror-copied into the border region.
|
|
* (2) This is useful for avoiding special operations near
|
|
* boundaries when doing image processing operations
|
|
* such as rank filters and convolution. In use, one first
|
|
* adds mirrored pixels to each side of the image. The number
|
|
* of pixels added on each side is half the filter dimension.
|
|
* Then the image processing operations proceed over a
|
|
* region equal to the size of the original image, and
|
|
* write directly into a dest pix of the same size as pixs.
|
|
* (3) The general pixRasterop() is used for an in-place operation here
|
|
* because there is no overlap between the src and dest rectangles.
|
|
*/
|
|
PIX *
|
|
pixAddMirroredBorder(PIX *pixs,
|
|
l_int32 left,
|
|
l_int32 right,
|
|
l_int32 top,
|
|
l_int32 bot)
|
|
{
|
|
l_int32 i, j, w, h;
|
|
PIX *pixd;
|
|
|
|
PROCNAME("pixAddMirroredBorder");
|
|
|
|
if (!pixs)
|
|
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
|
|
pixGetDimensions(pixs, &w, &h, NULL);
|
|
if (left > w || right > w || top > h || bot > h)
|
|
return (PIX *)ERROR_PTR("border too large", procName, NULL);
|
|
|
|
/* Set pixels on left, right, top and bottom, in that order */
|
|
pixd = pixAddBorderGeneral(pixs, left, right, top, bot, 0);
|
|
for (j = 0; j < left; j++)
|
|
pixRasterop(pixd, left - 1 - j, top, 1, h, PIX_SRC,
|
|
pixd, left + j, top);
|
|
for (j = 0; j < right; j++)
|
|
pixRasterop(pixd, left + w + j, top, 1, h, PIX_SRC,
|
|
pixd, left + w - 1 - j, top);
|
|
for (i = 0; i < top; i++)
|
|
pixRasterop(pixd, 0, top - 1 - i, left + w + right, 1, PIX_SRC,
|
|
pixd, 0, top + i);
|
|
for (i = 0; i < bot; i++)
|
|
pixRasterop(pixd, 0, top + h + i, left + w + right, 1, PIX_SRC,
|
|
pixd, 0, top + h - 1 - i);
|
|
|
|
return pixd;
|
|
}
|
|
|
|
/*!
|
|
* pixAddRepeatedBorder()
|
|
*
|
|
* Input: pixs (all depths; colormap ok)
|
|
* left, right, top, bot (number of pixels added)
|
|
* Return: pixd, or null on error
|
|
*
|
|
* Notes:
|
|
* (1) This applies a repeated border, as if the central part of
|
|
* the image is tiled over the plane. So, for example, the
|
|
* pixels in the left border come from the right side of the image.
|
|
* (2) The general pixRasterop() is used for an in-place operation here
|
|
* because there is no overlap between the src and dest rectangles.
|
|
*/
|
|
PIX *
|
|
pixAddRepeatedBorder(PIX *pixs,
|
|
l_int32 left,
|
|
l_int32 right,
|
|
l_int32 top,
|
|
l_int32 bot)
|
|
{
|
|
l_int32 w, h;
|
|
PIX *pixd;
|
|
|
|
PROCNAME("pixAddRepeatedBorder");
|
|
|
|
if (!pixs)
|
|
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
|
|
pixGetDimensions(pixs, &w, &h, NULL);
|
|
if (left > w || right > w || top > h || bot > h)
|
|
return (PIX *)ERROR_PTR("border too large", procName, NULL);
|
|
|
|
pixd = pixAddBorderGeneral(pixs, left, right, top, bot, 0);
|
|
|
|
/* Set pixels on left, right, top and bottom, in that order */
|
|
pixRasterop(pixd, 0, top, left, h, PIX_SRC, pixd, w, top);
|
|
pixRasterop(pixd, left + w, top, right, h, PIX_SRC, pixd, left, top);
|
|
pixRasterop(pixd, 0, 0, left + w + right, top, PIX_SRC, pixd, 0, h);
|
|
pixRasterop(pixd, 0, top + h, left + w + right, bot, PIX_SRC, pixd, 0, top);
|
|
|
|
return pixd;
|
|
}
|
|
|
|
|
|
/*!
|
|
* pixAddMixedBorder()
|
|
*
|
|
* Input: pixs (all depths; colormap ok)
|
|
* left, right, top, bot (number of pixels added)
|
|
* Return: pixd, or null on error
|
|
*
|
|
* Notes:
|
|
* (1) This applies mirrored boundary conditions horizontally
|
|
* and repeated b.c. vertically.
|
|
* (2) It is specifically used for avoiding special operations
|
|
* near boundaries when convolving a hue-saturation histogram
|
|
* with a given window size. The repeated b.c. are used
|
|
* vertically for hue, and the mirrored b.c. are used
|
|
* horizontally for saturation. The number of pixels added
|
|
* on each side is approximately (but not quite) half the
|
|
* filter dimension. The image processing operations can
|
|
* then proceed over a region equal to the size of the original
|
|
* image, and write directly into a dest pix of the same
|
|
* size as pixs.
|
|
* (3) The general pixRasterop() can be used for an in-place
|
|
* operation here because there is no overlap between the
|
|
* src and dest rectangles.
|
|
*/
|
|
PIX *
|
|
pixAddMixedBorder(PIX *pixs,
|
|
l_int32 left,
|
|
l_int32 right,
|
|
l_int32 top,
|
|
l_int32 bot)
|
|
{
|
|
l_int32 j, w, h;
|
|
PIX *pixd;
|
|
|
|
PROCNAME("pixAddMixedBorder");
|
|
|
|
if (!pixs)
|
|
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
|
|
pixGetDimensions(pixs, &w, &h, NULL);
|
|
if (left > w || right > w || top > h || bot > h)
|
|
return (PIX *)ERROR_PTR("border too large", procName, NULL);
|
|
|
|
/* Set mirrored pixels on left and right;
|
|
* then set repeated pixels on top and bottom. */
|
|
pixd = pixAddBorderGeneral(pixs, left, right, top, bot, 0);
|
|
for (j = 0; j < left; j++)
|
|
pixRasterop(pixd, left - 1 - j, top, 1, h, PIX_SRC,
|
|
pixd, left + j, top);
|
|
for (j = 0; j < right; j++)
|
|
pixRasterop(pixd, left + w + j, top, 1, h, PIX_SRC,
|
|
pixd, left + w - 1 - j, top);
|
|
pixRasterop(pixd, 0, 0, left + w + right, top, PIX_SRC, pixd, 0, h);
|
|
pixRasterop(pixd, 0, top + h, left + w + right, bot, PIX_SRC, pixd, 0, top);
|
|
|
|
return pixd;
|
|
}
|
|
|
|
|
|
|
|
/*-------------------------------------------------------------*
|
|
* Color sample setting and extraction *
|
|
*-------------------------------------------------------------*/
|
|
/*!
|
|
* pixCreateRGBImage()
|
|
*
|
|
* Input: 8 bpp red pix
|
|
* 8 bpp green pix
|
|
* 8 bpp blue pix
|
|
* Return: 32 bpp pix, interleaved with 4 samples/pixel,
|
|
* or null on error
|
|
*
|
|
* Notes:
|
|
* (1) the 4th byte, sometimes called the "alpha channel",
|
|
* and which is often used for blending between different
|
|
* images, is left with 0 value.
|
|
* (2) see Note (4) in pix.h for details on storage of
|
|
* 8-bit samples within each 32-bit word.
|
|
* (3) This implementation, setting the r, g and b components
|
|
* sequentially, is much faster than setting them in parallel
|
|
* by constructing an RGB dest pixel and writing it to dest.
|
|
* The reason is there are many more cache misses when reading
|
|
* from 3 input images simultaneously.
|
|
*/
|
|
PIX *
|
|
pixCreateRGBImage(PIX *pixr,
|
|
PIX *pixg,
|
|
PIX *pixb)
|
|
{
|
|
l_int32 wr, wg, wb, hr, hg, hb, dr, dg, db;
|
|
PIX *pixd;
|
|
|
|
PROCNAME("pixCreateRGBImage");
|
|
|
|
if (!pixr)
|
|
return (PIX *)ERROR_PTR("pixr not defined", procName, NULL);
|
|
if (!pixg)
|
|
return (PIX *)ERROR_PTR("pixg not defined", procName, NULL);
|
|
if (!pixb)
|
|
return (PIX *)ERROR_PTR("pixb not defined", procName, NULL);
|
|
pixGetDimensions(pixr, &wr, &hr, &dr);
|
|
pixGetDimensions(pixg, &wg, &hg, &dg);
|
|
pixGetDimensions(pixb, &wb, &hb, &db);
|
|
if (dr != 8 || dg != 8 || db != 8)
|
|
return (PIX *)ERROR_PTR("input pix not all 8 bpp", procName, NULL);
|
|
if (wr != wg || wr != wb)
|
|
return (PIX *)ERROR_PTR("widths not the same", procName, NULL);
|
|
if (hr != hg || hr != hb)
|
|
return (PIX *)ERROR_PTR("heights not the same", procName, NULL);
|
|
|
|
if ((pixd = pixCreate(wr, hr, 32)) == NULL)
|
|
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
|
|
pixCopyResolution(pixd, pixr);
|
|
pixSetRGBComponent(pixd, pixr, COLOR_RED);
|
|
pixSetRGBComponent(pixd, pixg, COLOR_GREEN);
|
|
pixSetRGBComponent(pixd, pixb, COLOR_BLUE);
|
|
|
|
return pixd;
|
|
}
|
|
|
|
|
|
/*!
|
|
* pixGetRGBComponent()
|
|
*
|
|
* Input: pixs (32 bpp)
|
|
* color (one of {COLOR_RED, COLOR_GREEN, COLOR_BLUE,
|
|
* L_ALPHA_CHANNEL})
|
|
* Return: pixd, the selected 8 bpp component image of the
|
|
* input 32 bpp image, or null on error
|
|
*
|
|
* Notes:
|
|
* (1) The alpha channel (in the 4th byte of each RGB pixel)
|
|
* is not used in leptonica.
|
|
* (2) Three calls to this function generate the three 8 bpp component
|
|
* images. This is much faster than generating the three
|
|
* images in parallel, by extracting a src pixel and setting
|
|
* the pixels of each component image from it. The reason is
|
|
* there are many more cache misses when writing to three
|
|
* output images simultaneously.
|
|
*/
|
|
PIX *
|
|
pixGetRGBComponent(PIX *pixs,
|
|
l_int32 color)
|
|
{
|
|
l_uint8 srcbyte;
|
|
l_uint32 *lines, *lined;
|
|
l_uint32 *datas, *datad;
|
|
l_int32 i, j, w, h;
|
|
l_int32 wpls, wpld;
|
|
PIX *pixd;
|
|
|
|
PROCNAME("pixGetRGBComponent");
|
|
|
|
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 (color != COLOR_RED && color != COLOR_GREEN &&
|
|
color != COLOR_BLUE && color != L_ALPHA_CHANNEL)
|
|
return (PIX *)ERROR_PTR("invalid color", procName, NULL);
|
|
|
|
pixGetDimensions(pixs, &w, &h, NULL);
|
|
if ((pixd = pixCreate(w, h, 8)) == NULL)
|
|
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
|
|
pixCopyResolution(pixd, pixs);
|
|
wpls = pixGetWpl(pixs);
|
|
wpld = pixGetWpl(pixd);
|
|
datas = pixGetData(pixs);
|
|
datad = pixGetData(pixd);
|
|
|
|
for (i = 0; i < h; i++) {
|
|
lines = datas + i * wpls;
|
|
lined = datad + i * wpld;
|
|
for (j = 0; j < w; j++) {
|
|
srcbyte = GET_DATA_BYTE(lines + j, color);
|
|
SET_DATA_BYTE(lined, j, srcbyte);
|
|
}
|
|
}
|
|
|
|
return pixd;
|
|
}
|
|
|
|
|
|
/*!
|
|
* pixSetRGBComponent()
|
|
*
|
|
* Input: pixd (32 bpp)
|
|
* pixs (8 bpp)
|
|
* color (one of {COLOR_RED, COLOR_GREEN, COLOR_BLUE,
|
|
* L_ALPHA_CHANNEL})
|
|
* Return: 0 if OK; 1 on error
|
|
*
|
|
* Notes:
|
|
* (1) This places the 8 bpp pixel in pixs into the
|
|
* specified color component (properly interleaved) in pixd.
|
|
* (2) The alpha channel component is not used in leptonica.
|
|
*/
|
|
l_int32
|
|
pixSetRGBComponent(PIX *pixd,
|
|
PIX *pixs,
|
|
l_int32 color)
|
|
{
|
|
l_uint8 srcbyte;
|
|
l_int32 i, j, w, h;
|
|
l_int32 wpls, wpld;
|
|
l_uint32 *lines, *lined;
|
|
l_uint32 *datas, *datad;
|
|
|
|
PROCNAME("pixSetRGBComponent");
|
|
|
|
if (!pixd)
|
|
return ERROR_INT("pixd not defined", procName, 1);
|
|
if (!pixs)
|
|
return ERROR_INT("pixs not defined", procName, 1);
|
|
|
|
if (pixGetDepth(pixd) != 32)
|
|
return ERROR_INT("pixd not 32 bpp", procName, 1);
|
|
if (pixGetDepth(pixs) != 8)
|
|
return ERROR_INT("pixs not 8 bpp", procName, 1);
|
|
if (color != COLOR_RED && color != COLOR_GREEN &&
|
|
color != COLOR_BLUE && color != L_ALPHA_CHANNEL)
|
|
return ERROR_INT("invalid color", procName, 1);
|
|
pixGetDimensions(pixs, &w, &h, NULL);
|
|
if (w != pixGetWidth(pixd) || h != pixGetHeight(pixd))
|
|
return ERROR_INT("sizes not commensurate", procName, 1);
|
|
|
|
datas = pixGetData(pixs);
|
|
datad = pixGetData(pixd);
|
|
wpls = pixGetWpl(pixs);
|
|
wpld = pixGetWpl(pixd);
|
|
for (i = 0; i < h; i++) {
|
|
lines = datas + i * wpls;
|
|
lined = datad + i * wpld;
|
|
for (j = 0; j < w; j++) {
|
|
srcbyte = GET_DATA_BYTE(lines, j);
|
|
SET_DATA_BYTE(lined + j, color, srcbyte);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* pixGetRGBComponentCmap()
|
|
*
|
|
* Input: pixs (colormapped)
|
|
* color (one of {COLOR_RED, COLOR_GREEN, COLOR_BLUE})
|
|
* Return: pixd (the selected 8 bpp component image of the
|
|
* input cmapped image), or null on error
|
|
*/
|
|
PIX *
|
|
pixGetRGBComponentCmap(PIX *pixs,
|
|
l_int32 color)
|
|
{
|
|
l_int32 i, j, w, h, val, index;
|
|
l_int32 wplc, wpld;
|
|
l_uint32 *linec, *lined;
|
|
l_uint32 *datac, *datad;
|
|
PIX *pixc, *pixd;
|
|
PIXCMAP *cmap;
|
|
RGBA_QUAD *cta;
|
|
|
|
PROCNAME("pixGetRGBComponentCmap");
|
|
|
|
if (!pixs)
|
|
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
|
|
if ((cmap = pixGetColormap(pixs)) == NULL)
|
|
return (PIX *)ERROR_PTR("pixs not cmapped", procName, NULL);
|
|
if (color != COLOR_RED && color != COLOR_GREEN &&
|
|
color != COLOR_BLUE)
|
|
return (PIX *)ERROR_PTR("invalid color", procName, NULL);
|
|
|
|
/* If not 8 bpp, make a cmapped 8 bpp pix */
|
|
if (pixGetDepth(pixs) == 8)
|
|
pixc = pixClone(pixs);
|
|
else
|
|
pixc = pixConvertTo8(pixs, TRUE);
|
|
|
|
pixGetDimensions(pixs, &w, &h, NULL);
|
|
if ((pixd = pixCreateNoInit(w, h, 8)) == NULL)
|
|
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
|
|
pixCopyResolution(pixd, pixs);
|
|
wplc = pixGetWpl(pixc);
|
|
wpld = pixGetWpl(pixd);
|
|
datac = pixGetData(pixc);
|
|
datad = pixGetData(pixd);
|
|
cta = (RGBA_QUAD *)cmap->array;
|
|
|
|
for (i = 0; i < h; i++) {
|
|
linec = datac + i * wplc;
|
|
lined = datad + i * wpld;
|
|
if (color == COLOR_RED) {
|
|
for (j = 0; j < w; j++) {
|
|
index = GET_DATA_BYTE(linec, j);
|
|
val = cta[index].red;
|
|
SET_DATA_BYTE(lined, j, val);
|
|
}
|
|
}
|
|
else if (color == COLOR_GREEN) {
|
|
for (j = 0; j < w; j++) {
|
|
index = GET_DATA_BYTE(linec, j);
|
|
val = cta[index].green;
|
|
SET_DATA_BYTE(lined, j, val);
|
|
}
|
|
}
|
|
else if (color == COLOR_BLUE) {
|
|
for (j = 0; j < w; j++) {
|
|
index = GET_DATA_BYTE(linec, j);
|
|
val = cta[index].green;
|
|
SET_DATA_BYTE(lined, j, val);
|
|
}
|
|
}
|
|
}
|
|
|
|
pixDestroy(&pixc);
|
|
return pixd;
|
|
}
|
|
|
|
|
|
/*!
|
|
* composeRGBPixel()
|
|
*
|
|
* Input: rval, gval, bval
|
|
* &rgbpixel (<return> 32-bit pixel)
|
|
* Return: 0 if OK; 1 on error
|
|
*
|
|
* Notes:
|
|
* (1) A slower implementation uses macros:
|
|
* SET_DATA_BYTE(ppixel, COLOR_RED, rval);
|
|
* SET_DATA_BYTE(ppixel, COLOR_GREEN, gval);
|
|
* SET_DATA_BYTE(ppixel, COLOR_BLUE, bval);
|
|
*/
|
|
l_int32
|
|
composeRGBPixel(l_int32 rval,
|
|
l_int32 gval,
|
|
l_int32 bval,
|
|
l_uint32 *ppixel)
|
|
{
|
|
PROCNAME("composeRGBPixel");
|
|
|
|
if (!ppixel)
|
|
return ERROR_INT("&pixel not defined", procName, 1);
|
|
|
|
*ppixel = (rval << L_RED_SHIFT) | (gval << L_GREEN_SHIFT) |
|
|
(bval << L_BLUE_SHIFT);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* extractRGBValues()
|
|
*
|
|
* Input: pixel (32 bit)
|
|
* &rval (<optional return> red component)
|
|
* &gval (<optional return> green component)
|
|
* &bval (<optional return> blue component)
|
|
* Return: void
|
|
*
|
|
* Notes:
|
|
* (1) A slower implementation uses macros:
|
|
* *prval = GET_DATA_BYTE(&pixel, COLOR_RED);
|
|
* *pgval = GET_DATA_BYTE(&pixel, COLOR_GREEN);
|
|
* *pbval = GET_DATA_BYTE(&pixel, COLOR_BLUE);
|
|
*/
|
|
void
|
|
extractRGBValues(l_uint32 pixel,
|
|
l_int32 *prval,
|
|
l_int32 *pgval,
|
|
l_int32 *pbval)
|
|
{
|
|
if (prval) *prval = (pixel >> L_RED_SHIFT) & 0xff;
|
|
if (pgval) *pgval = (pixel >> L_GREEN_SHIFT) & 0xff;
|
|
if (pbval) *pbval = (pixel >> L_BLUE_SHIFT) & 0xff;
|
|
return;
|
|
}
|
|
|
|
|
|
/*!
|
|
* extractMinMaxComponent()
|
|
*
|
|
* Input: pixel (32 bpp RGB)
|
|
* type (L_CHOOSE_MIN or L_CHOOSE_MAX)
|
|
* Return: componet (in range [0 ... 255], or null on error
|
|
*/
|
|
l_int32
|
|
extractMinMaxComponent(l_uint32 pixel,
|
|
l_int32 type)
|
|
{
|
|
l_int32 rval, gval, bval, val;
|
|
|
|
extractRGBValues(pixel, &rval, &gval, &bval);
|
|
if (type == L_CHOOSE_MIN) {
|
|
val = L_MIN(rval, gval);
|
|
val = L_MIN(val, bval);
|
|
}
|
|
else { /* type == L_CHOOSE_MAX */
|
|
val = L_MAX(rval, gval);
|
|
val = L_MAX(val, bval);
|
|
}
|
|
return val;
|
|
}
|
|
|
|
|
|
/*!
|
|
* pixGetRGBLine()
|
|
*
|
|
* Input: pixs (32 bpp)
|
|
* row
|
|
* bufr (array of red samples; size w bytes)
|
|
* bufg (array of green samples; size w bytes)
|
|
* bufb (array of blue samples; size w bytes)
|
|
* Return: 0 if OK; 1 on error
|
|
*
|
|
* Notes:
|
|
* (1) This puts rgb components from the input line in pixs
|
|
* into the given buffers.
|
|
*/
|
|
l_int32
|
|
pixGetRGBLine(PIX *pixs,
|
|
l_int32 row,
|
|
l_uint8 *bufr,
|
|
l_uint8 *bufg,
|
|
l_uint8 *bufb)
|
|
{
|
|
l_uint32 *lines;
|
|
l_int32 j, w, h;
|
|
l_int32 wpls;
|
|
|
|
PROCNAME("pixGetRGBLine");
|
|
|
|
if (!pixs)
|
|
return ERROR_INT("pixs not defined", procName, 1);
|
|
if (pixGetDepth(pixs) != 32)
|
|
return ERROR_INT("pixs not 32 bpp", procName, 1);
|
|
if (!bufr || !bufg || !bufb)
|
|
return ERROR_INT("buffer not defined", procName, 1);
|
|
|
|
pixGetDimensions(pixs, &w, &h, NULL);
|
|
if (row < 0 || row >= h)
|
|
return ERROR_INT("row out of bounds", procName, 1);
|
|
wpls = pixGetWpl(pixs);
|
|
lines = pixGetData(pixs) + row * wpls;
|
|
|
|
for (j = 0; j < w; j++) {
|
|
bufr[j] = GET_DATA_BYTE(lines + j, COLOR_RED);
|
|
bufg[j] = GET_DATA_BYTE(lines + j, COLOR_GREEN);
|
|
bufb[j] = GET_DATA_BYTE(lines + j, COLOR_BLUE);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------*
|
|
* Pixel endian conversion *
|
|
*-------------------------------------------------------------*/
|
|
/*!
|
|
* pixEndianByteSwapNew()
|
|
*
|
|
* Input: pixs
|
|
* Return: pixd, or null on error
|
|
*
|
|
* Notes:
|
|
* (1) This is used to convert the data in a pix to a
|
|
* serialized byte buffer in raster order, and, for RGB,
|
|
* in order RGBA. This requires flipping bytes within
|
|
* each 32-bit word for little-endian platforms, because the
|
|
* words have a MSB-to-the-left rule, whereas byte raster-order
|
|
* requires the left-most byte in each word to be byte 0.
|
|
* For big-endians, no swap is necessary, so this returns a clone.
|
|
* (2) Unlike pixEndianByteSwap(), which swaps the bytes in-place,
|
|
* this returns a new pix (or a clone). We provide this
|
|
* because often when serialization is done, the source
|
|
* pix needs to be restored to canonical little-endian order,
|
|
* and this requires a second byte swap. In such a situation,
|
|
* it is twice as fast to make a new pix in big-endian order,
|
|
* use it, and destroy it.
|
|
*/
|
|
PIX *
|
|
pixEndianByteSwapNew(PIX *pixs)
|
|
{
|
|
l_uint32 *datas, *datad;
|
|
l_int32 i, j, h, wpl;
|
|
l_uint32 word;
|
|
PIX *pixd;
|
|
|
|
PROCNAME("pixEndianByteSwapNew");
|
|
|
|
#ifdef L_BIG_ENDIAN
|
|
|
|
return pixClone(pixs);
|
|
|
|
#else /* L_LITTLE_ENDIAN */
|
|
|
|
if (!pixs)
|
|
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
|
|
|
|
datas = pixGetData(pixs);
|
|
wpl = pixGetWpl(pixs);
|
|
h = pixGetHeight(pixs);
|
|
pixd = pixCreateTemplate(pixs);
|
|
datad = pixGetData(pixd);
|
|
for (i = 0; i < h; i++) {
|
|
for (j = 0; j < wpl; j++, datas++, datad++) {
|
|
word = *datas;
|
|
*datad = (word >> 24) |
|
|
((word >> 8) & 0x0000ff00) |
|
|
((word << 8) & 0x00ff0000) |
|
|
(word << 24);
|
|
}
|
|
}
|
|
|
|
return pixd;
|
|
|
|
#endif /* L_BIG_ENDIAN */
|
|
|
|
}
|
|
|
|
|
|
/*!
|
|
* pixEndianByteSwap()
|
|
*
|
|
* Input: pixs
|
|
* Return: 0 if OK, 1 on error
|
|
*
|
|
* Notes:
|
|
* (1) This is used on little-endian platforms to swap
|
|
* the bytes within a word; bytes 0 and 3 are swapped,
|
|
* and bytes 1 and 2 are swapped.
|
|
* (2) This is required for little-endians in situations
|
|
* where we convert from a serialized byte order that is
|
|
* in raster order, as one typically has in file formats,
|
|
* to one with MSB-to-the-left in each 32-bit word, or v.v.
|
|
* See pix.h for a description of the canonical format
|
|
* (MSB-to-the left) that is used for both little-endian
|
|
* and big-endian platforms. For big-endians, the
|
|
* MSB-to-the-left word order has the bytes in raster
|
|
* order when serialized, so no byte flipping is required.
|
|
*/
|
|
l_int32
|
|
pixEndianByteSwap(PIX *pixs)
|
|
{
|
|
l_uint32 *data;
|
|
l_int32 i, j, h, wpl;
|
|
l_uint32 word;
|
|
|
|
PROCNAME("pixEndianByteSwap");
|
|
|
|
#ifdef L_BIG_ENDIAN
|
|
|
|
return 0;
|
|
|
|
#else /* L_LITTLE_ENDIAN */
|
|
|
|
if (!pixs)
|
|
return ERROR_INT("pixs not defined", procName, 1);
|
|
|
|
data = pixGetData(pixs);
|
|
wpl = pixGetWpl(pixs);
|
|
h = pixGetHeight(pixs);
|
|
for (i = 0; i < h; i++) {
|
|
for (j = 0; j < wpl; j++, data++) {
|
|
word = *data;
|
|
*data = (word >> 24) |
|
|
((word >> 8) & 0x0000ff00) |
|
|
((word << 8) & 0x00ff0000) |
|
|
(word << 24);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
#endif /* L_BIG_ENDIAN */
|
|
|
|
}
|
|
|
|
|
|
/*!
|
|
* lineEndianByteSwap()
|
|
*
|
|
* Input datad (dest byte array data, reordered on little-endians)
|
|
* datas (a src line of pix data)
|
|
* wpl (number of 32 bit words in the line)
|
|
* Return: 0 if OK, 1 on error
|
|
*
|
|
* Notes:
|
|
* (1) This is used on little-endian platforms to swap
|
|
* the bytes within each word in the line of image data.
|
|
* Bytes 0 <==> 3 and 1 <==> 2 are swapped in the dest
|
|
* byte array data8d, relative to the pix data in datas.
|
|
* (2) The bytes represent 8 bit pixel values. They are swapped
|
|
* for little endians so that when the dest array (char *)datad
|
|
* is addressed by bytes, the pixels are chosen sequentially
|
|
* from left to right in the image.
|
|
*/
|
|
l_int32
|
|
lineEndianByteSwap(l_uint32 *datad,
|
|
l_uint32 *datas,
|
|
l_int32 wpl)
|
|
{
|
|
l_int32 j;
|
|
l_uint32 word;
|
|
|
|
PROCNAME("lineEndianByteSwap");
|
|
|
|
if (!datad || !datas)
|
|
return ERROR_INT("datad and datas not both defined", procName, 1);
|
|
|
|
#ifdef L_BIG_ENDIAN
|
|
|
|
memcpy((char *)datad, (char *)datas, 4 * wpl);
|
|
return 0;
|
|
|
|
#else /* L_LITTLE_ENDIAN */
|
|
|
|
for (j = 0; j < wpl; j++, datas++, datad++) {
|
|
word = *datas;
|
|
*datad = (word >> 24) |
|
|
((word >> 8) & 0x0000ff00) |
|
|
((word << 8) & 0x00ff0000) |
|
|
(word << 24);
|
|
}
|
|
return 0;
|
|
|
|
#endif /* L_BIG_ENDIAN */
|
|
|
|
}
|
|
|
|
|
|
/*!
|
|
* pixEndianTwoByteSwapNew()
|
|
*
|
|
* Input: pixs
|
|
* Return: 0 if OK, 1 on error
|
|
*
|
|
* Notes:
|
|
* (1) This is used on little-endian platforms to swap the
|
|
* 2-byte entities within a 32-bit word.
|
|
* (2) This is equivalent to a full byte swap, as performed
|
|
* by pixEndianByteSwap(), followed by byte swaps in
|
|
* each of the 16-bit entities separately.
|
|
* (3) Unlike pixEndianTwoByteSwap(), which swaps the shorts in-place,
|
|
* this returns a new pix (or a clone). We provide this
|
|
* to avoid having to swap twice in situations where the input
|
|
* pix must be restored to canonical little-endian order.
|
|
*/
|
|
PIX *
|
|
pixEndianTwoByteSwapNew(PIX *pixs)
|
|
{
|
|
l_uint32 *datas, *datad;
|
|
l_int32 i, j, h, wpl;
|
|
l_uint32 word;
|
|
PIX *pixd;
|
|
|
|
PROCNAME("pixEndianTwoByteSwapNew");
|
|
|
|
#ifdef L_BIG_ENDIAN
|
|
|
|
return pixClone(pixs);
|
|
|
|
#else /* L_LITTLE_ENDIAN */
|
|
|
|
if (!pixs)
|
|
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
|
|
|
|
datas = pixGetData(pixs);
|
|
wpl = pixGetWpl(pixs);
|
|
h = pixGetHeight(pixs);
|
|
pixd = pixCreateTemplate(pixs);
|
|
datad = pixGetData(pixd);
|
|
for (i = 0; i < h; i++) {
|
|
for (j = 0; j < wpl; j++, datas++, datad++) {
|
|
word = *datas;
|
|
*datad = (word << 16) | (word >> 16);
|
|
}
|
|
}
|
|
|
|
return pixd;
|
|
|
|
#endif /* L_BIG_ENDIAN */
|
|
|
|
}
|
|
|
|
|
|
/*!
|
|
* pixEndianTwoByteSwap()
|
|
*
|
|
* Input: pixs
|
|
* Return: 0 if OK, 1 on error
|
|
*
|
|
* Notes:
|
|
* (1) This is used on little-endian platforms to swap the
|
|
* 2-byte entities within a 32-bit word.
|
|
* (2) This is equivalent to a full byte swap, as performed
|
|
* by pixEndianByteSwap(), followed by byte swaps in
|
|
* each of the 16-bit entities separately.
|
|
*/
|
|
l_int32
|
|
pixEndianTwoByteSwap(PIX *pixs)
|
|
{
|
|
l_uint32 *data;
|
|
l_int32 i, j, h, wpl;
|
|
l_uint32 word;
|
|
|
|
PROCNAME("pixEndianTwoByteSwap");
|
|
|
|
#ifdef L_BIG_ENDIAN
|
|
|
|
return 0;
|
|
|
|
#else /* L_LITTLE_ENDIAN */
|
|
|
|
if (!pixs)
|
|
return ERROR_INT("pixs not defined", procName, 1);
|
|
|
|
data = pixGetData(pixs);
|
|
wpl = pixGetWpl(pixs);
|
|
h = pixGetHeight(pixs);
|
|
for (i = 0; i < h; i++) {
|
|
for (j = 0; j < wpl; j++, data++) {
|
|
word = *data;
|
|
*data = (word << 16) | (word >> 16);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
#endif /* L_BIG_ENDIAN */
|
|
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------*
|
|
* Extract raster data as binary string *
|
|
*-------------------------------------------------------------*/
|
|
/*!
|
|
* pixGetRasterData()
|
|
*
|
|
* Input: pixs (1, 8, 32 bpp)
|
|
* &data (<return> raster data in memory)
|
|
* &nbytes (<return> number of bytes in data string)
|
|
* Return: 0 if OK, 1 on error
|
|
*
|
|
* Notes:
|
|
* (1) This returns the raster data as a byte string, padded to the
|
|
* byte. For 1 bpp, the first pixel is the MSbit in the first byte.
|
|
* For rgb, the bytes are in (rgb) order. This is the format
|
|
* required for flate encoding of pixels in a PostScript file.
|
|
*/
|
|
l_int32
|
|
pixGetRasterData(PIX *pixs,
|
|
l_uint8 **pdata,
|
|
l_int32 *pnbytes)
|
|
{
|
|
l_int32 w, h, d, wpl, i, j, rval, gval, bval;
|
|
l_int32 databpl; /* bytes for each raster line in returned data */
|
|
l_uint8 *line, *data; /* packed data in returned array */
|
|
l_uint32 *rline, *rdata; /* data in pix raster */
|
|
|
|
PROCNAME("pixGetRasterData");
|
|
|
|
if (!pdata || !pnbytes)
|
|
return ERROR_INT("&data and &nbytes not both defined", procName, 1);
|
|
*pdata = NULL;
|
|
*pnbytes = 0;
|
|
if (!pixs)
|
|
return ERROR_INT("pixs not defined", procName, 1);
|
|
pixGetDimensions(pixs, &w, &h, &d);
|
|
if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32)
|
|
return ERROR_INT("depth not in {1,2,4,8,16,32}", procName, 1);
|
|
rdata = pixGetData(pixs);
|
|
wpl = pixGetWpl(pixs);
|
|
if (d == 1)
|
|
databpl = (w + 7) / 8;
|
|
else if (d == 2)
|
|
databpl = (w + 3) / 4;
|
|
else if (d == 4)
|
|
databpl = (w + 1) / 2;
|
|
else if (d == 8 || d == 16)
|
|
databpl = w * (d / 8);
|
|
else /* d == 32 bpp rgb */
|
|
databpl = 3 * w;
|
|
if ((data = (l_uint8 *)CALLOC(databpl * h, sizeof(l_uint8))) == NULL)
|
|
return ERROR_INT("data not allocated", procName, 1);
|
|
*pdata = data;
|
|
*pnbytes = databpl * h;
|
|
|
|
for (i = 0; i < h; i++) {
|
|
rline = rdata + i * wpl;
|
|
line = data + i * databpl;
|
|
if (d <= 8) {
|
|
for (j = 0; j < databpl; j++)
|
|
line[j] = GET_DATA_BYTE(rline, j);
|
|
}
|
|
else if (d == 16) {
|
|
for (j = 0; j < w; j++)
|
|
line[2 * j] = GET_DATA_TWO_BYTES(rline, j);
|
|
}
|
|
else { /* d == 32 bpp rgb */
|
|
for (j = 0; j < w; j++) {
|
|
extractRGBValues(rline[j], &rval, &gval, &bval);
|
|
*(line + 3 * j) = rval;
|
|
*(line + 3 * j + 1) = gval;
|
|
*(line + 3 * j + 2) = bval;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------*
|
|
* Serialization of pix to/from memory (uncompressed) *
|
|
*-------------------------------------------------------------*/
|
|
/*!
|
|
* pixSerializeToMemory()
|
|
*
|
|
* Input: pixs (1, 8, 32 bpp)
|
|
* &data (<return> serialized data in memory)
|
|
* &nbytes (<return> number of bytes in data string)
|
|
* Return: 0 if OK, 1 on error
|
|
*
|
|
* Notes:
|
|
* (1) This does a fast serialization of the principal elements
|
|
* of the pix, as follows:
|
|
* w (4 bytes)
|
|
* h (4 bytes)
|
|
* d (4 bytes)
|
|
* wpl (4 bytes)
|
|
* ncolors (4 bytes) -- in colormap; 0 if there is no colormap
|
|
* cdatasize (4 bytes) -- size of serialized colormap
|
|
* = 4 * (num colors)
|
|
* cdata (cdatasize)
|
|
* rdatasize (4 bytes) -- size of serialized raster data
|
|
* = 4 * wpl * h
|
|
* rdata (rdatasize)
|
|
*/
|
|
l_int32
|
|
pixSerializeToMemory(PIX *pixs,
|
|
l_uint32 **pdata,
|
|
l_int32 *pnbytes)
|
|
{
|
|
l_int32 w, h, d, wpl, rdatasize, cdatasize, ncolors, nbytes, index;
|
|
l_uint8 *cdata; /* data in colormap (4 bytes/color table entry) */
|
|
l_uint32 *data;
|
|
l_uint32 *rdata; /* data in pix raster */
|
|
PIXCMAP *cmap;
|
|
|
|
PROCNAME("pixSerializeToMemory");
|
|
|
|
if (!pdata || !pnbytes)
|
|
return ERROR_INT("&data and &nbytes not both defined", procName, 1);
|
|
*pdata = NULL;
|
|
*pnbytes = 0;
|
|
if (!pixs)
|
|
return ERROR_INT("pixs not defined", procName, 1);
|
|
|
|
pixGetDimensions(pixs, &w, &h, &d);
|
|
wpl = pixGetWpl(pixs);
|
|
rdata = pixGetData(pixs);
|
|
rdatasize = 4 * wpl * h;
|
|
cdatasize = 0;
|
|
ncolors = 0;
|
|
cdata = NULL;
|
|
if ((cmap = pixGetColormap(pixs)) != NULL)
|
|
pixcmapSerializeToMemory(cmap, 4, &ncolors, &cdata, &cdatasize);
|
|
|
|
nbytes = 28 + cdatasize + rdatasize;
|
|
if ((data = (l_uint32 *)CALLOC(nbytes / 4, sizeof(l_uint32))) == NULL)
|
|
return ERROR_INT("data not made", procName, 1);
|
|
*pdata = data;
|
|
*pnbytes = nbytes;
|
|
data[0] = w;
|
|
data[1] = h;
|
|
data[2] = d;
|
|
data[3] = wpl;
|
|
data[4] = ncolors;
|
|
data[5] = cdatasize;
|
|
if (cdatasize > 0)
|
|
memcpy((char *)(data + 6), (char *)cdata, cdatasize);
|
|
index = 6 + cdatasize / 4;
|
|
data[index] = rdatasize;
|
|
memcpy((char *)(data + index + 1), (char *)rdata, rdatasize);
|
|
|
|
#if DEBUG_SERIALIZE
|
|
fprintf(stderr, "Serialize: "
|
|
"raster size = %d, ncolors in cmap = %d, total bytes = %d\n",
|
|
rdatasize, ncolors, nbytes);
|
|
#endif /* DEBUG_SERIALIZE */
|
|
|
|
FREE(cdata);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* pixDeserializeFromMemory()
|
|
*
|
|
* Input: data (serialized data in memory)
|
|
* nbytes (number of bytes in data string)
|
|
* Return: pix, or NULL on error
|
|
*
|
|
* Notes:
|
|
* (1) See pixSerializeToMemory() for the binary format.
|
|
*/
|
|
PIX *
|
|
pixDeserializeFromMemory(l_uint32 *data,
|
|
l_int32 nbytes)
|
|
{
|
|
l_int32 w, h, d, wpl, imdatasize, cdatasize, ncolors;
|
|
l_uint32 *imdata; /* data in pix raster */
|
|
PIX *pixd;
|
|
PIXCMAP *cmap;
|
|
|
|
PROCNAME("pixDeserializeFromMemory");
|
|
|
|
if (!data)
|
|
return (PIX *)ERROR_PTR("data not defined", procName, NULL);
|
|
if (nbytes < 28)
|
|
return (PIX *)ERROR_PTR("invalid data", procName, NULL);
|
|
|
|
w = data[0];
|
|
h = data[1];
|
|
d = data[2];
|
|
if ((pixd = pixCreate(w, h, d)) == NULL)
|
|
return (PIX *)ERROR_PTR("pix not made", procName, NULL);
|
|
|
|
wpl = data[3];
|
|
ncolors = data[4];
|
|
if ((cdatasize = data[5]) > 0) {
|
|
cmap = pixcmapDeserializeFromMemory((l_uint8 *)(&data[6]), ncolors,
|
|
cdatasize);
|
|
if (!cmap)
|
|
return (PIX *)ERROR_PTR("cmap not made", procName, NULL);
|
|
pixSetColormap(pixd, cmap);
|
|
}
|
|
|
|
imdata = pixGetData(pixd);
|
|
imdatasize = nbytes - 28 - cdatasize;
|
|
memcpy((char *)imdata, (char *)(data + 7 + cdatasize / 4), imdatasize);
|
|
|
|
#if DEBUG_SERIALIZE
|
|
fprintf(stderr, "Deserialize: "
|
|
"raster size = %d, ncolors in cmap = %d, total bytes = %d\n",
|
|
imdatasize, ncolors, nbytes);
|
|
#endif /* DEBUG_SERIALIZE */
|
|
|
|
return pixd;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------*
|
|
* Setup helpers for 8 bpp byte processing *
|
|
*-------------------------------------------------------------*/
|
|
/*!
|
|
* pixSetupByteProcessing()
|
|
*
|
|
* Input: pix (8 bpp, no colormap)
|
|
* &w (<optional return> width)
|
|
* &h (<optional return> height)
|
|
* Return: line ptr array, or null on error
|
|
*
|
|
* Notes:
|
|
* (1) This is a simple helper for processing 8 bpp images with
|
|
* direct byte access. It can swap byte order within each word.
|
|
* (2) After processing, you must call pixCleanupByteProcessing(),
|
|
* which frees the lineptr array and restores byte order.
|
|
* (3) Usage:
|
|
* l_uint8 **lineptrs = pixSetupByteProcessing(pix, &w, &h);
|
|
* for (i = 0; i < h; i++) {
|
|
* l_uint8 *line = lineptrs[i];
|
|
* for (j = 0; j < w; j++) {
|
|
* val = line[j];
|
|
* ...
|
|
* }
|
|
* }
|
|
* pixCleanupByteProcessing(pix, lineptrs);
|
|
*/
|
|
l_uint8 **
|
|
pixSetupByteProcessing(PIX *pix,
|
|
l_int32 *pw,
|
|
l_int32 *ph)
|
|
{
|
|
l_int32 w, h;
|
|
|
|
PROCNAME("pixSetupByteProcessing");
|
|
|
|
if (pw) *pw = 0;
|
|
if (ph) *ph = 0;
|
|
if (!pix || pixGetDepth(pix) != 8)
|
|
return (l_uint8 **)ERROR_PTR("pix not defined or not 8 bpp",
|
|
procName, NULL);
|
|
if (pixGetColormap(pix))
|
|
return (l_uint8 **)ERROR_PTR("pix has colormap", procName, NULL);
|
|
|
|
pixGetDimensions(pix, &w, &h, NULL);
|
|
if (pw) *pw = w;
|
|
if (ph) *ph = h;
|
|
pixEndianByteSwap(pix);
|
|
return (l_uint8 **)pixGetLinePtrs(pix, NULL);
|
|
}
|
|
|
|
|
|
/*!
|
|
* pixCleanupByteProcessing()
|
|
*
|
|
* Input: pix (8 bpp, no colormap)
|
|
* lineptrs (ptrs to the beginning of each raster line of data)
|
|
* Return: 0 if OK, 1 on error
|
|
*
|
|
* Notes:
|
|
* (1) This must be called after processing that was initiated
|
|
* by pixSetupByteProcessing() has finished.
|
|
*/
|
|
l_int32
|
|
pixCleanupByteProcessing(PIX *pix,
|
|
l_uint8 **lineptrs)
|
|
{
|
|
PROCNAME("pixCleanupByteProcessing");
|
|
|
|
if (!pix)
|
|
return ERROR_INT("pix not defined", procName, 1);
|
|
if (!lineptrs)
|
|
return ERROR_INT("lineptrs not defined", procName, 1);
|
|
|
|
pixEndianByteSwap(pix);
|
|
FREE(lineptrs);
|
|
return 0;
|
|
}
|
|
|