mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-31 06:12:22 -06:00
411 lines
13 KiB
C
411 lines
13 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.
|
|
*====================================================================*/
|
|
|
|
/*
|
|
* runlength.c
|
|
*
|
|
* Label pixels by membership in runs
|
|
* PIX *pixRunlengthTransform()
|
|
*
|
|
* Find runs along horizontal and vertical lines
|
|
* l_int32 pixFindHorizontalRuns()
|
|
* l_int32 pixFindVerticalRuns()
|
|
*
|
|
* Compute runlength-to-membership transform on a line
|
|
* l_int32 runlengthMembershipOnLine()
|
|
*
|
|
* Make byte position LUT
|
|
* l_int32 makeMSBitLocTab()
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "allheaders.h"
|
|
|
|
|
|
/*-----------------------------------------------------------------------*
|
|
* Label pixels by membership in runs *
|
|
*-----------------------------------------------------------------------*/
|
|
/*!
|
|
* pixRunlengthTransform()
|
|
*
|
|
* Input: pixs (1 bpp)
|
|
* color (0 for white runs, 1 for black runs)
|
|
* direction (L_HORIZONTAL_RUNS, L_VERTICAL_RUNS)
|
|
* depth (8 or 16 bpp)
|
|
* Return: 0 if OK; 1 on error
|
|
*
|
|
* Notes:
|
|
* (1) The dest Pix is 8 or 16 bpp, with the pixel values
|
|
* equal to the runlength in which it is a member.
|
|
* The length is clipped to the max pixel value if necessary.
|
|
* (2) The color determines if we're labelling white or black runs.
|
|
* (3) A pixel that is not a member of the chosen color gets
|
|
* value 0; it belongs to a run of length 0 of the
|
|
* chosen color.
|
|
* (4) To convert for maximum dynamic range, either linear or
|
|
* log, use pixMaxDynamicRange().
|
|
*/
|
|
PIX *
|
|
pixRunlengthTransform(PIX *pixs,
|
|
l_int32 color,
|
|
l_int32 direction,
|
|
l_int32 depth)
|
|
{
|
|
l_int32 i, j, w, h, wpld, bufsize, maxsize, n;
|
|
l_int32 *start, *end, *buffer;
|
|
l_uint32 *datad, *lined;
|
|
PIX *pixt, *pixd;
|
|
|
|
PROCNAME("pixRunlengthTransform");
|
|
|
|
if (!pixs)
|
|
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
|
|
if (pixGetDepth(pixs) != 1)
|
|
return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
|
|
if (depth != 8 && depth != 16)
|
|
return (PIX *)ERROR_PTR("depth must be 8 or 16 bpp", procName, NULL);
|
|
|
|
w = pixGetWidth(pixs);
|
|
h = pixGetHeight(pixs);
|
|
if (direction == L_HORIZONTAL_RUNS)
|
|
maxsize = 1 + w / 2;
|
|
else if (direction == L_VERTICAL_RUNS)
|
|
maxsize = 1 + h / 2;
|
|
else
|
|
return (PIX *)ERROR_PTR("invalid direction", procName, NULL);
|
|
bufsize = L_MAX(w, h);
|
|
|
|
if ((pixd = pixCreate(w, h, depth)) == NULL)
|
|
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
|
|
datad = pixGetData(pixd);
|
|
wpld = pixGetWpl(pixd);
|
|
|
|
if ((start = (l_int32 *)CALLOC(maxsize, sizeof(l_int32))) == NULL)
|
|
return (PIX *)ERROR_PTR("start not made", procName, NULL);
|
|
if ((end = (l_int32 *)CALLOC(maxsize, sizeof(l_int32))) == NULL)
|
|
return (PIX *)ERROR_PTR("end not made", procName, NULL);
|
|
if ((buffer = (l_int32 *)CALLOC(bufsize, sizeof(l_int32))) == NULL)
|
|
return (PIX *)ERROR_PTR("buffer not made", procName, NULL);
|
|
|
|
/* Use fg runs for evaluation */
|
|
if (color == 0)
|
|
pixt = pixInvert(NULL, pixs);
|
|
else
|
|
pixt = pixClone(pixs);
|
|
|
|
if (direction == L_HORIZONTAL_RUNS) {
|
|
for (i = 0; i < h; i++) {
|
|
pixFindHorizontalRuns(pixt, i, start, end, &n);
|
|
runlengthMembershipOnLine(buffer, w, depth, start, end, n);
|
|
lined = datad + i * wpld;
|
|
if (depth == 8) {
|
|
for (j = 0; j < w; j++)
|
|
SET_DATA_BYTE(lined, j, buffer[j]);
|
|
} else { /* depth == 16 */
|
|
for (j = 0; j < w; j++)
|
|
SET_DATA_TWO_BYTES(lined, j, buffer[j]);
|
|
}
|
|
}
|
|
} else { /* L_VERTICAL_RUNS */
|
|
for (j = 0; j < w; j++) {
|
|
pixFindVerticalRuns(pixt, j, start, end, &n);
|
|
runlengthMembershipOnLine(buffer, h, depth, start, end, n);
|
|
if (depth == 8) {
|
|
for (i = 0; i < h; i++) {
|
|
lined = datad + i * wpld;
|
|
SET_DATA_BYTE(lined, j, buffer[i]);
|
|
}
|
|
} else { /* depth == 16 */
|
|
for (i = 0; i < h; i++) {
|
|
lined = datad + i * wpld;
|
|
SET_DATA_TWO_BYTES(lined, j, buffer[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pixDestroy(&pixt);
|
|
FREE(start);
|
|
FREE(end);
|
|
FREE(buffer);
|
|
return pixd;
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*
|
|
* Find runs along horizontal and vertical lines *
|
|
*-----------------------------------------------------------------------*/
|
|
/*!
|
|
* pixFindHorizontalRuns()
|
|
*
|
|
* Input: pix (1 bpp)
|
|
* y (line to traverse)
|
|
* xstart (returns array of start positions for fg runs)
|
|
* xend (returns array of end positions for fg runs)
|
|
* &n (<return> the number of runs found)
|
|
* Return: 0 if OK; 1 on error
|
|
*
|
|
* Notes:
|
|
* (1) This finds foreground horizontal runs on a single scanline.
|
|
* (2) To find background runs, use pixInvert() before applying
|
|
* this function.
|
|
* (3) The xstart and xend arrays are input. They should be
|
|
* of size w/2 + 1 to insure that they can hold
|
|
* the maximum number of runs in the raster line.
|
|
*/
|
|
l_int32
|
|
pixFindHorizontalRuns(PIX *pix,
|
|
l_int32 y,
|
|
l_int32 *xstart,
|
|
l_int32 *xend,
|
|
l_int32 *pn)
|
|
{
|
|
l_int32 inrun; /* boolean */
|
|
l_int32 index, w, h, d, j, wpl, val;
|
|
l_uint32 *line;
|
|
|
|
PROCNAME("pixFindHorizontalRuns");
|
|
|
|
if (!pn)
|
|
return ERROR_INT("&n not defined", procName, 1);
|
|
*pn = 0;
|
|
if (!pix)
|
|
return ERROR_INT("pix not defined", procName, 1);
|
|
pixGetDimensions(pix, &w, &h, &d);
|
|
if (d != 1)
|
|
return ERROR_INT("pix not 1 bpp", procName, 1);
|
|
if (y < 0 || y >= h)
|
|
return ERROR_INT("y not in [0 ... h - 1]", procName, 1);
|
|
if (!xstart)
|
|
return ERROR_INT("xstart not defined", procName, 1);
|
|
if (!xend)
|
|
return ERROR_INT("xend not defined", procName, 1);
|
|
|
|
wpl = pixGetWpl(pix);
|
|
line = pixGetData(pix) + y * wpl;
|
|
|
|
inrun = FALSE;
|
|
index = 0;
|
|
for (j = 0; j < w; j++) {
|
|
val = GET_DATA_BIT(line, j);
|
|
if (!inrun) {
|
|
if (val) {
|
|
xstart[index] = j;
|
|
inrun = TRUE;
|
|
}
|
|
}
|
|
else {
|
|
if (!val) {
|
|
xend[index++] = j - 1;
|
|
inrun = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Finish last run if necessary */
|
|
if (inrun)
|
|
xend[index++] = w - 1;
|
|
|
|
*pn = index;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* pixFindVerticalRuns()
|
|
*
|
|
* Input: pix (1 bpp)
|
|
* x (line to traverse)
|
|
* ystart (returns array of start positions for fg runs)
|
|
* yend (returns array of end positions for fg runs)
|
|
* &n (<return> the number of runs found)
|
|
* Return: 0 if OK; 1 on error
|
|
*
|
|
* Notes:
|
|
* (1) This finds foreground vertical runs on a single scanline.
|
|
* (2) To find background runs, use pixInvert() before applying
|
|
* this function.
|
|
* (3) The ystart and yend arrays are input. They should be
|
|
* of size h/2 + 1 to insure that they can hold
|
|
* the maximum number of runs in the raster line.
|
|
*/
|
|
l_int32
|
|
pixFindVerticalRuns(PIX *pix,
|
|
l_int32 x,
|
|
l_int32 *ystart,
|
|
l_int32 *yend,
|
|
l_int32 *pn)
|
|
{
|
|
l_int32 inrun; /* boolean */
|
|
l_int32 index, w, h, d, i, wpl, val;
|
|
l_uint32 *data, *line;
|
|
|
|
PROCNAME("pixFindVerticalRuns");
|
|
|
|
if (!pn)
|
|
return ERROR_INT("&n not defined", procName, 1);
|
|
*pn = 0;
|
|
if (!pix)
|
|
return ERROR_INT("pix not defined", procName, 1);
|
|
pixGetDimensions(pix, &w, &h, &d);
|
|
if (d != 1)
|
|
return ERROR_INT("pix not 1 bpp", procName, 1);
|
|
if (x < 0 || x >= w)
|
|
return ERROR_INT("x not in [0 ... w - 1]", procName, 1);
|
|
if (!ystart)
|
|
return ERROR_INT("ystart not defined", procName, 1);
|
|
if (!yend)
|
|
return ERROR_INT("yend not defined", procName, 1);
|
|
|
|
wpl = pixGetWpl(pix);
|
|
data = pixGetData(pix);
|
|
|
|
inrun = FALSE;
|
|
index = 0;
|
|
for (i = 0; i < h; i++) {
|
|
line = data + i * wpl;
|
|
val = GET_DATA_BIT(line, x);
|
|
if (!inrun) {
|
|
if (val) {
|
|
ystart[index] = i;
|
|
inrun = TRUE;
|
|
}
|
|
}
|
|
else {
|
|
if (!val) {
|
|
yend[index++] = i - 1;
|
|
inrun = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Finish last run if necessary */
|
|
if (inrun)
|
|
yend[index++] = h - 1;
|
|
|
|
*pn = index;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*
|
|
* Compute runlength-to-membership transform on a line *
|
|
*-----------------------------------------------------------------------*/
|
|
/*!
|
|
* runlengthMembershipOnLine()
|
|
*
|
|
* Input: buffer (into which full line of data is placed)
|
|
* size (full size of line; w or h)
|
|
* depth (8 or 16 bpp)
|
|
* start (array of start positions for fg runs)
|
|
* end (array of end positions for fg runs)
|
|
* n (the number of runs)
|
|
* Return: 0 if OK; 1 on error
|
|
*
|
|
* Notes:
|
|
* (1) Converts a set of runlengths into a buffer of
|
|
* runlength membership values.
|
|
* (2) Initialization of the array gives pixels that are
|
|
* not within a run the value 0.
|
|
*/
|
|
l_int32
|
|
runlengthMembershipOnLine(l_int32 *buffer,
|
|
l_int32 size,
|
|
l_int32 depth,
|
|
l_int32 *start,
|
|
l_int32 *end,
|
|
l_int32 n)
|
|
{
|
|
l_int32 i, j, first, last, diff, max;
|
|
|
|
PROCNAME("runlengthMembershipOnLine");
|
|
|
|
if (!buffer)
|
|
return ERROR_INT("buffer not defined", procName, 1);
|
|
if (!start)
|
|
return ERROR_INT("start not defined", procName, 1);
|
|
if (!end)
|
|
return ERROR_INT("end not defined", procName, 1);
|
|
|
|
if (depth == 8)
|
|
max = 0xff;
|
|
else /* depth == 16 */
|
|
max = 0xffff;
|
|
|
|
memset(buffer, 0, 4 * size);
|
|
for (i = 0; i < n; i++) {
|
|
first = start[i];
|
|
last = end[i];
|
|
diff = last - first + 1;
|
|
diff = L_MIN(diff, max);
|
|
for (j = first; j <= last; j++)
|
|
buffer[j] = diff;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------*
|
|
* Make byte position LUT *
|
|
*-----------------------------------------------------------------------*/
|
|
/*!
|
|
* makeMSBitLocTab()
|
|
*
|
|
* Input: bitval (either 0 or 1)
|
|
* Return: table (giving, for an input byte, the MS bit location,
|
|
* starting at 0 with the MSBit in the byte),
|
|
* or null on error.
|
|
*
|
|
* Notes:
|
|
* (1) If bitval == 1, it finds the leftmost ON pixel in a byte;
|
|
* otherwise if bitval == 0, it finds the leftmost OFF pixel.
|
|
* (2) If there are no pixels of the indicated color in the byte,
|
|
* this returns 8.
|
|
*/
|
|
l_int32 *
|
|
makeMSBitLocTab(l_int32 bitval)
|
|
{
|
|
l_int32 i, j;
|
|
l_int32 *tab;
|
|
l_uint8 byte, mask;
|
|
|
|
PROCNAME("makeMSBitLocTab");
|
|
|
|
if ((tab = (l_int32 *)CALLOC(256, sizeof(l_int32))) == NULL)
|
|
return (l_int32 *)ERROR_PTR("tab not made", procName, NULL);
|
|
|
|
for (i = 0; i < 256; i++) {
|
|
byte = (l_uint8)i;
|
|
if (bitval == 0)
|
|
byte = ~byte;
|
|
tab[i] = 8;
|
|
mask = 0x80;
|
|
for (j = 0; j < 8; j++) {
|
|
if (byte & mask) {
|
|
tab[i] = j;
|
|
break;
|
|
}
|
|
mask >>= 1;
|
|
}
|
|
}
|
|
|
|
return tab;
|
|
}
|
|
|
|
|