tesseract/image/imgs.cpp
theraysmith 0048516024 Changes to image for 3.00
git-svn-id: https://tesseract-ocr.googlecode.com/svn/trunk@294 d0cd1f9f-072b-0410-8dd7-cf729c803f20
2009-07-11 02:22:41 +00:00

1766 lines
59 KiB
C++

/**********************************************************************
* File: imgs.c (Formerly images.c)
* Description: Main image manipulation functions.
* Author: Ray Smith
* Created: Thu Jun 07 16:25:02 BST 1990
*
* (C) Copyright 1990, Hewlett-Packard Ltd.
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
** http://www.apache.org/licenses/LICENSE-2.0
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*
**********************************************************************/
#include "mfcpch.h" //precompiled headers
#ifdef __MSW32__
#include <io.h>
#else
#include <unistd.h>
#endif
#include <string.h>
#ifdef __UNIX__
#include <assert.h>
#endif
// Include automatically generated configuration file if running autoconf.
#ifdef HAVE_CONFIG_H
#include "config_auto.h"
#endif
#ifdef HAVE_LIBLEPT
// Include leptonica library only if autoconf (or makefile etc) tell us to.
#include "allheaders.h"
#endif
#include "stderr.h"
#include "tprintf.h"
#include "imgerrs.h"
#include "memry.h"
#include "imgs.h"
#include "imgio.h"
#include "imgunpk.h"
#define FIXED_COLOURS 32 /*number of fixed colours */
#define MIN_4BIT 48 /*4bpp range */
#define MAX_4BIT 64
#define MIN_6BIT 64 /*6bpp range */
#define MAX_6BIT 128
#define BLACK_PIX 0
static uinT8 grey_scales[FIXED_COLOURS] = {
0, 255, 76, 227, 151, 179, 28, 104,
149, 72, 215, 67, 53, 44, 156, 137,
110, 153, 79, 181, 166, 218, 55, 81,
129, 105, 179, 149, 168, 69, 84, 126
};
#undef EXTERN
#define EXTERN
EXTERN INT_VAR (image_default_resolution, 300, "Image resolution dpi");
/**********************************************************************
* IMAGE
*
* Contructor for an IMAGE class. Makes the image definitely illegal.
**********************************************************************/
IMAGE::IMAGE() { //construct an image
bpp = 0; //all illegal
fd = -1;
image = NULL;
photo_interp = 1;
res = image_default_resolution;
}
/**********************************************************************
* IMAGE::operator=
*
* Assign an IMAGE to another. The dest becomes the owner of the memory.
**********************************************************************/
IMAGE & IMAGE::operator= ( //assignment
IMAGE & source //source image
) {
destroy();
bpp = source.bpp;
photo_interp = source.photo_interp;
bps = source.bps;
bytespp = (bpp + 7) / 8;
lineskip = source.lineskip; //copy everything
captured = source.captured;
xsize = source.xsize;
ysize = source.ysize;
res = source.res;
image = source.image;
xdim = source.xdim;
bufheight = source.bufheight;
fd = source.fd;
reader = source.reader;
ymin = source.ymin;
ymax = source.ymax;
source.captured = TRUE; //source now captured
source.fd = -1;
return *this;
}
/**********************************************************************
* create
*
* Create an image (allocate memory) of a specific size and bpp.
**********************************************************************/
inT8 IMAGE::create( //get rest of image
inT32 x, //x size required
inT32 y, //ysize required
inT8 bits_per_pixel //bpp required
) {
uinT8 *pixels; //memory for image
xdim = check_legal_image_size (x, y, bits_per_pixel);
if (xdim < 0)
return -1;
pixels = (uinT8 *) alloc_big_zeros ((size_t) (xdim * y * sizeof (uinT8)));
if (pixels == NULL) {
MEMORY_OUT.error ("IMAGE::create", ABORT, "Size=(%d,%d)", xdim, y);
return -1;
}
//allocate to image
this->capture (pixels, x, y, bits_per_pixel);
captured = FALSE;
res = image_default_resolution;
return 0; //success
}
/**********************************************************************
* destroy
*
* Destroy an image, freeing memory and closing any open file.
**********************************************************************/
void IMAGE::destroy() { //get rid of image
if (image != NULL && !captured) {
free_big_mem(image);
}
image = NULL;
if (fd >= 0) {
close(fd);
fd = -1;
}
bpp = 0;
}
/**********************************************************************
* capture
*
* Assign a given memory area to an image to use as an image of
* given size and bpp.
**********************************************************************/
inT8 IMAGE::capture( //get rest of image
uinT8 *pixels, //image memory
inT32 x, //x size required
inT32 y, //ysize required
inT8 bits_per_pixel //bpp required
) {
destroy();
xdim = check_legal_image_size (x, y, bits_per_pixel);
if (xdim < 0)
return -1;
xsize = x;
ysize = y;
bufheight = y;
bpp = bits_per_pixel;
bps = bpp == 24 ? 8 : bpp;
photo_interp = 1;
bytespp = (bpp + 7) / 8;
image = pixels; //assign image area
ymin = 0;
ymax = bufheight; //read it all
captured = TRUE;
res = image_default_resolution;
return 0; //success
}
/**********************************************************************
* pixel
*
* Get a single pixel out of the image.
**********************************************************************/
uinT8 IMAGE::pixel( //get rest of image
inT32 x, //x coord
inT32 y //y coord
) {
if (x < 0)
x = 0; //silently clip
else if (x >= xsize)
x = xsize - 1;
if (y < 0)
y = 0;
else if (y >= ysize)
y = ysize - 1;
check_legal_access (x, y, 1);
switch (bpp) {
case 5:
case 6:
case 8:
return image[(ymax - 1 - y) * xdim + x];
case 4:
return bpp4table[image[(ymax - 1 - y) * xdim + x / 2]][x & 1];
case 2:
return bpp2table[image[(ymax - 1 - y) * xdim + x / 4]][x & 3];
case 1:
return bpp1table[image[(ymax - 1 - y) * xdim + x / 8]][x & 7];
default:
tprintf ("Unexpected bits per pixel %d\n", bpp);
return 0;
}
}
/**********************************************************************
* check_legal_image_size
*
* Check that the supplied image sizes are legal. If they are,
* the xdim is returned, else -1.
**********************************************************************/
inT32 check_legal_image_size( //get rest of image
inT32 x, //x size required
inT32 y, //ysize required
inT8 bits_per_pixel //bpp required
) {
if (x <= 0 || y <= 0) {
BADIMAGESIZE.error ("check_legal_image_size", TESSLOG, "(%d,%d)", x, y);
return -1; //failed
}
if (bits_per_pixel != 1 && bits_per_pixel != 2
&& bits_per_pixel != 4 && bits_per_pixel != 5
&& bits_per_pixel != 6 && bits_per_pixel != 8 && bits_per_pixel != 24
&& bits_per_pixel != 32) {
BADBPP.error ("check_legal_image_size", TESSLOG, "%d", bits_per_pixel);
return -1;
}
//bytes per line
return COMPUTE_IMAGE_XDIM (x, bits_per_pixel);
}
/**********************************************************************
* copy_sub_image
*
* Copy a portion of one image to a portion of another image.
* If the bpps are different, the position of the most significant
* bit is preserved.
**********************************************************************/
DLLSYM void copy_sub_image( //copy rectangle
IMAGE *source, //source image
inT32 xstart, //start coords
inT32 ystart,
inT32 xext, //extent to copy
inT32 yext,
IMAGE *dest, //destination image
inT32 xdest, //destination coords
inT32 ydest,
BOOL8 adjust_grey //shift to new bpp
) {
IMAGELINE copyline; //copy of line
uinT8 *copy; //source pointer
inT8 shift; //shift factor
inT32 pixel; //pixel index
inT32 y; //line index
inT32 yoffset; //current adjusted offset
inT32 bytesize; //no of bytes to copy
inT32 srcppb; //pixels per byte
BOOL8 aligned;
if (xstart < 0 || ystart < 0 || xdest < 0 || ydest < 0)
return;
if (xext <= 0)
xext = source->xsize; //default to all
if (xext > source->xsize - xstart)
//clip to smallest
xext = source->xsize - xstart;
if (xext > dest->xsize - xdest)
xext = dest->xsize - xdest;
if (yext <= 0)
yext = source->ysize; //default to all
if (yext > source->ysize - ystart)
//clip to smallest
yext = source->ysize - ystart;
if (yext > dest->ysize - ydest)
yext = dest->ysize - ydest;
if (xext <= 0 || yext <= 0)
return; //nothing to do
srcppb = 8 / source->bpp; //pixels per byte
if (source->bpp == dest->bpp || !adjust_grey)
shift = 0; //no adjustment
else {
shift = source->bps - dest->bps;
if (shift < 0)
shift = -shift; //keep positive
}
aligned = source->bpp == dest->bpp;
if (aligned && srcppb != 0) {
aligned = xstart % srcppb == 0
&& xdest % srcppb == 0
&& (xext % srcppb == 0 || xdest + xext == dest->xsize);
}
for (y = 0; y < yext; y++) {
if (ystart >= ydest)
yoffset = y; //top down
else
yoffset = yext - y - 1; //bottom up
source->check_legal_access (xstart, ystart + yoffset, xext);
dest->check_legal_access (xdest, ydest + yoffset, xext);
if (aligned) {
bytesize = COMPUTE_IMAGE_XDIM (xext, source->bpp);
//get bytes per line
if (srcppb == 0)
//do cheap move
memmove (dest->image + (dest->ymax - 1 - ydest - yoffset) * dest->xdim + xdest * 3, source->image + (source->ymax - 1 - ystart - yoffset) * source->xdim + xstart * 3, (unsigned) bytesize);
else
//do cheap move
memmove (dest->image + (dest->ymax - 1 - ydest - yoffset) * dest->xdim + xdest / srcppb, source->image + (source->ymax - 1 - ystart - yoffset) * source->xdim + xstart / srcppb, (unsigned) bytesize);
}
else {
if (shift == 0) {
source->fast_get_line (xstart, ystart + yoffset, xext,
&copyline);
}
else if (source->bpp < dest->bpp) {
source->get_line (xstart, ystart + yoffset, xext, &copyline, 0);
if (source->bpp <= shift
&& (source->bpp == 1 || source->bpp == 4)) {
if (source->bpp == 1) {
for (pixel = 0, copy = copyline.pixels; pixel < xext;
pixel++, copy++)
if (*copy)
*copy = 0xff;
}
else {
for (pixel = 0, copy = copyline.pixels; pixel < xext;
pixel++, copy++)
//scale up
*copy = (*copy << shift) | *copy;
}
}
else {
for (pixel = 0, copy = copyline.pixels; pixel < xext;
pixel++)
*copy++ <<= shift; //scale up
}
}
else {
source->get_line (xstart, ystart + yoffset, xext, &copyline, 0);
if (source->bpp == 24) {
for (pixel = 0, copy = copyline.pixels + 1; pixel < xext;
pixel++) {
*copy >>= shift;
copy += 3;
}
}
else {
for (pixel = 0, copy = copyline.pixels; pixel < xext;
pixel++)
*copy++ >>= shift; //scale down
}
}
dest->put_line (xdest, ydest + yoffset, xext, &copyline, 0);
}
}
}
/**********************************************************************
* enlarge_sub_image
*
* Enlarge a portion of one image to a portion of another image.
* If the bpps are different, the position of the most significant
* bit is preserved.
**********************************************************************/
DLLSYM void enlarge_sub_image( //enlarge rectangle
IMAGE *source, //source image
inT32 xstart, //scaled start coords
inT32 ystart,
IMAGE *dest, //destination image
inT32 xdest, //dest coords
inT32 ydest,
inT32 xext, //destination extent
inT32 yext,
inT32 scale, //scale factor
BOOL8 adjust_grey //shift to new bpp
) {
inT8 shift; //shift factor
uinT8 pixel; //current pixel
inT32 srcext; //source extent
inT32 xoffset; //column index
inT32 yoffset; //line index
inT32 xindex, yindex; //index in super pixel
inT32 startxindex; //initial x index
inT32 xscale; //x scale factor
uinT8 *src; //source pixels
uinT8 *destpix; //dest pixels
IMAGELINE copyline; //copy of line
IMAGELINE bigline; //expanded line
if (xstart < 0 || ystart < 0 || xdest < 0 || ydest < 0)
return;
if (xext <= 0)
xext = dest->xsize; //default to all
if (xext > source->xsize * scale - xstart)
//clip to smallest
xext = source->xsize * scale - xstart;
if (xext > dest->xsize - xdest)
xext = dest->xsize - xdest;
if (yext <= 0)
yext = dest->ysize; //default to all
if (yext > source->ysize * scale - ystart)
yext = source->ysize * scale - ystart;
if (yext > dest->ysize - ydest)
yext = dest->ysize - ydest;
if (xext <= 0 || yext <= 0)
return; //nothing to do
xindex = xstart % scale; //offset in super pixel
startxindex = xindex;
yindex = ystart % scale;
//no of source pixels
srcext = (xext + xindex + scale - 1) / scale;
xstart /= scale; //actual start
ystart /= scale;
if (adjust_grey) {
shift = dest->bps - source->bps;
}
else
shift = 0; //no adjustment
bigline.init (xext * 3);
bigline.bpp = dest->bpp == 24 ? source->bpp : dest->bpp;
for (yoffset = 0; yoffset < yext; ystart++) {
source->check_legal_access (xstart, ystart, srcext);
dest->check_legal_access (xdest, ydest + yoffset, xext);
source->fast_get_line (xstart, ystart, srcext, &copyline);
src = copyline.pixels;
destpix = bigline.pixels;
xscale = scale; //enlargement factor
if (source->bpp == 24 && dest->bpp == 24) {
for (xoffset = 0, xindex = startxindex; xoffset < xext;
src += source->bytespp) {
xoffset += xscale - xindex;
if (xoffset > xext)
xscale -= xoffset - xext;
for (; xindex < xscale; xindex++) {
*destpix++ = *src;
*destpix++ = *(src + 1);
*destpix++ = *(src + 2);
}
xindex = 0;
}
}
else {
if (source->bpp == 24)
src++;
for (xoffset = 0, xindex = startxindex; xoffset < xext;
src += source->bytespp) {
xoffset += xscale - xindex;
if (xoffset > xext)
//clip to dest limit
xscale -= xoffset - xext;
if (shift == 0)
pixel = *src;
else if (shift > 0)
pixel = *src << shift;
else
pixel = *src >> (-shift);
for (; xindex < xscale; xindex++)
*destpix++ = pixel; //duplicate pixel
xindex = 0;
}
}
for (; yoffset < yext && yindex < scale; yindex++, yoffset++) {
dest->put_line (xdest, ydest + yoffset, xext, &bigline, 0);
}
yindex = 0;
}
}
/**********************************************************************
* fast_reduce_sub_image
*
* Reduce a portion of one image to a portion of another image.
* If the bpps are different, the position of the most significant
* bit is preserved.
* This is a fast but dirty version, which simply sub-samples.
* It does not smooth as it reduces.
**********************************************************************/
DLLSYM void fast_reduce_sub_image( //reduce rectangle
IMAGE *source, //source image
inT32 xstart, //start coords
inT32 ystart,
inT32 xext, //extent to copy
inT32 yext,
IMAGE *dest, //destination image
inT32 xdest, //destination coords
inT32 ydest,
inT32 scale, //reduction factor
BOOL8 adjust_grey //shift to new bpp
) {
inT8 shift; //shift factor
inT32 xfactor; //run on x coord
inT32 divisor; //total cell area
inT32 xindex, yindex; //into averaging square
inT32 xcoord; //current x coord
inT32 destext; //destination size
inT32 yoffset; //current adjusted offset
uinT8 *pixel; //ptr to source pixels
inT32 *sums; //ptr to sums array
IMAGELINE copyline; //copy of line
inT32 *linesums; //averaging sums
if (xstart < 0 || ystart < 0 || xdest < 0 || ydest < 0)
return;
if (xext <= 0)
xext = source->xsize; //default to all
if (xext > source->xsize - xstart)
//clip to smallest
xext = source->xsize - xstart;
if (xext > (dest->xsize - xdest) * scale)
xext = (dest->xsize - xdest) * scale;
if (yext <= 0)
yext = source->ysize; //default to all
if (yext > source->ysize - ystart)
//clip to smallest
yext = source->ysize - ystart;
if (yext > (dest->ysize - ydest) * scale)
yext = (dest->ysize - ydest) * scale;
if (xext <= 0 || yext <= 0)
return; //nothing to do
xfactor = xext % scale; //left overs
if (xfactor == 0)
xfactor = scale;
//destination pixels
destext = (xext + scale - 1) / scale;
if (adjust_grey)
//shift factor
shift = dest->bps - source->bps;
else
shift = 0; //no adjustment
linesums = new inT32[destext * source->bytespp];
for (yoffset = 0; yoffset < yext; ydest++) {
source->check_legal_access (xstart, ystart + yoffset, xext);
dest->check_legal_access (xdest, ydest, destext);
for (xindex = destext * source->bytespp - 1; xindex >= 0; xindex--)
linesums[xindex] = 0; //zero sums
for (yindex = 0; yindex < scale
&& ystart + yoffset < source->ysize; yindex += 3) {
source->fast_get_line (xstart, ystart + yoffset, xext, &copyline);
pixel = copyline.pixels; //start of line
if (source->bpp == 24) {
for (xcoord = 1, sums = linesums; xcoord < destext;
xcoord++, sums += 3) {
for (xindex = 0; xindex < scale; xindex += 2) {
*sums += *pixel++;
*(sums + 1) += *pixel++;
*(sums + 2) += *pixel++;
pixel += 3;
}
if (scale & 1)
pixel -= 3; //correct position
}
for (xindex = 0; xindex < xfactor; xindex += 2) {
*sums += *pixel++;
*(sums + 1) += *pixel++;
*(sums + 2) += *pixel++;
pixel += 3;
}
}
else {
for (xcoord = 1, sums = linesums; xcoord < destext;
xcoord++, sums++) {
for (xindex = 0; xindex < scale; xindex += 2) {
*sums += *pixel;
pixel += 2;
}
if (scale & 1)
pixel--; //correct position
}
for (xindex = 0; xindex < xfactor; xindex += 2) {
*sums += *pixel;
pixel += 2;
}
}
yoffset += 3; //every 3 lines
}
if (yindex > scale)
yoffset -= yindex - scale; //back on right scale
copyline.init (); //set pixels back to array
copyline.bpp = source->bpp;
pixel = copyline.pixels;
//pixels in block
divisor = ((yindex + 2) / 3) * ((scale + 1) / 2);
if (shift <= 0) {
divisor <<= (-shift); //do greyscale correction
for (sums = linesums, xindex = (destext - 1) * source->bytespp;
xindex > 0; xindex--)
//turn to destination value
*pixel++ = (uinT8) (*sums++ / divisor);
for (xindex = source->bytespp; xindex > 0; xindex--)
*pixel++ = *sums++
/ (((yindex + 2) / 3) * ((xfactor + 1) / 2) << (-shift));
//lastone different
}
else {
for (sums = linesums, xindex = (destext - 1) * source->bytespp;
xindex > 0; xindex--)
*pixel++ = (uinT8) ((*sums++ << shift) / divisor);
//destination value
for (xindex = source->bytespp; xindex > 0; xindex--)
//last one different
*pixel++ = (*(sums++) << shift) / (((yindex + 2) / 3) * ((xfactor + 1) / 2));
}
//put in destination
dest->put_line (xdest, ydest, destext, &copyline, 0);
}
delete linesums;
}
/**********************************************************************
* reduce_sub_image
*
* Reduce a portion of one image to a portion of another image.
* If the bpps are different, the position of the most significant
* bit is preserved.
**********************************************************************/
DLLSYM void reduce_sub_image( //reduce rectangle
IMAGE *source, //source image
inT32 xstart, //start coords
inT32 ystart,
inT32 xext, //extent to copy
inT32 yext,
IMAGE *dest, //destination image
inT32 xdest, //destination coords
inT32 ydest,
inT32 scale, //reduction factor
BOOL8 adjust_grey //shift to new bpp
) {
inT8 shift; //shift factor
inT32 xfactor; //run on x coord
inT32 divisor; //total cell area
inT32 div2; //total cell area divided by 2
inT32 xindex, yindex; //into averaging square
inT32 xcoord; //current x coord
inT32 destext; //destination size
inT32 yoffset; //current adjusted offset
uinT8 *pixel; //ptr to source pixels
inT32 *sums; //ptr to sums array
IMAGELINE copyline; //copy of line
inT32 *linesums; //averaging sums
if (xstart < 0 || ystart < 0 || xdest < 0 || ydest < 0)
return;
if (xext <= 0)
xext = source->xsize; //default to all
if (xext > source->xsize - xstart)
//clip to smallest
xext = source->xsize - xstart;
if (xext > (dest->xsize - xdest) * scale)
xext = (dest->xsize - xdest) * scale;
if (yext <= 0)
yext = source->ysize; //default to all
if (yext > source->ysize - ystart)
//clip to smallest
yext = source->ysize - ystart;
if (yext > (dest->ysize - ydest) * scale)
yext = (dest->ysize - ydest) * scale;
if (xext <= 0 || yext <= 0)
return; //nothing to do
xfactor = xext % scale; //left overs
if (xfactor == 0)
xfactor = scale;
//destination pixels
destext = (xext + scale - 1) / scale;
if (adjust_grey)
//shift factor
shift = dest->bps - source->bps;
else
shift = 0; //no adjustment
linesums = new inT32[destext * source->bytespp];
for (yoffset = 0; yoffset < yext; ydest++) {
source->check_legal_access (xstart, ystart + yoffset, xext);
dest->check_legal_access (xdest, ydest, destext);
for (xindex = 0; xindex < (destext) * source->bytespp; xindex++)
linesums[xindex] = 0; //zero sums
for (yindex = 0; yindex < scale && ystart + yoffset < source->ysize;
yindex++) {
source->fast_get_line (xstart, ystart + yoffset, xext, &copyline);
pixel = copyline.pixels; //start of line
if (source->bpp == 24) {
for (xcoord = 1, sums = linesums; xcoord < destext;
xcoord++, sums += 3) {
for (xindex = 0; xindex < scale; xindex++) {
*sums += *pixel++;
*(sums + 1) += *pixel++;
*(sums + 2) += *pixel++;
}
}
for (xindex = 0; xindex < xfactor; xindex++) {
*sums += *pixel++;
*(sums + 1) += *pixel++;
*(sums + 2) += *pixel++;
}
}
else {
for (xcoord = 1, sums = linesums; xcoord < destext;
xcoord++, sums++) {
for (xindex = 0; xindex < scale; xindex++)
*sums += *pixel++;
}
for (xindex = 0; xindex < xfactor; xindex++)
*sums += *pixel++;
}
yoffset++; //next line
}
copyline.init (); //set pixels back to array
copyline.set_bpp (source->bpp);
pixel = copyline.pixels;
divisor = yindex * scale;
if (divisor == 0) {
tprintf
("Impossible:divisor=0!, yindex=%d, scale=%d, yoffset=%d,yext=%d\n",
yindex, scale, yoffset, yext);
break;
}
if (shift <= 0) {
divisor <<= (-shift); //do greyscale correction
div2 = divisor / 2;
for (sums = linesums, xindex = (destext - 1) * source->bytespp;
xindex > 0; xindex--)
*pixel++ = (uinT8) ((div2 + *sums++) / divisor);
//turn to destination value
div2 = (yindex * xfactor << (-shift)) / 2;
for (xindex = source->bytespp; xindex > 0; xindex--)
*pixel++ =
(uinT8) ((div2 + *sums++) / (yindex * xfactor << (-shift)));
//lastone different
}
else {
div2 = divisor / 2;
for (sums = linesums, xindex = (destext - 1) * source->bytespp;
xindex > 0; xindex--)
*pixel++ = (uinT8) ((div2 + (*sums++ << shift)) / divisor);
//destination value
div2 = (yindex * xfactor) / 2;
for (xindex = source->bytespp; xindex > 0; xindex--)
*pixel++ =
(uinT8) ((div2 + (*sums++ << shift)) / (yindex * xfactor));
//last one different
}
//put in destination
dest->put_line (xdest, ydest, destext, &copyline, 0);
}
delete linesums;
}
/**********************************************************************
* invert_image
*
* Invert the given image (the slow way.)
**********************************************************************/
DLLSYM void invert_image( /*invert the image */
IMAGE *image /*image ot invert */
) {
uinT8 mask; //bit mask
uinT8 bytespp; //bytes per pixel
inT32 xsize, ysize; /*size of image */
inT32 xindex, yindex; /*index into image */
uinT8 *pixel; /*current pixel */
IMAGELINE line; /*line of image */
bytespp = image->get_bpp () == 24 ? 3 : 1;
xsize = image->get_xsize (); /*find sizes */
ysize = image->get_ysize ();
//pixel mask
mask = (1 << image->get_bpp ()) - 1;
/*do each line */
for (yindex = ysize - 1; yindex >= 0; yindex--) {
image->fast_get_line (0, yindex, xsize, &line);
for (pixel = line.pixels, xindex = xsize * bytespp; xindex > 0;
xindex--) {
*pixel = (*pixel) ^ mask; //invert image only
++pixel;
}
/*put it back */
image->fast_put_line (0, yindex, xsize, &line);
}
}
/**********************************************************************
* bias_sub_image
*
* Add a constant to a portion of an image.
**********************************************************************/
DLLSYM void bias_sub_image( //bias rectangle
IMAGE *source, //source image
inT32 xstart, //start coords
inT32 ystart,
inT32 xext, //extent to copy
inT32 yext,
uinT8 bias //number to add
) {
IMAGELINE copyline; //copy of line
uinT8 *copy; //source pointer
inT32 pixel; //pixel index
inT32 y; //line index
uinT8 bytespp; //bytes per pixel
if (xstart < 0 || ystart < 0)
return;
if (xext <= 0)
xext = source->get_xsize (); //default to all
if (xext > source->get_xsize () - xstart)
//clip to smallest
xext = source->get_xsize () - xstart;
if (yext <= 0)
yext = source->get_ysize (); //default to all
if (yext > source->get_ysize () - ystart)
//clip to smallest
yext = source->get_ysize () - ystart;
if (xext <= 0 || yext <= 0)
return; //nothing to do
bytespp = source->get_bpp () == 24 ? 3 : 1;
for (y = 0; y < yext; y++) {
source->check_legal_access (xstart, ystart + y, xext);
source->fast_get_line (xstart, ystart + y, xext, &copyline);
for (pixel = xext * bytespp, copy = copyline.pixels; pixel > 0;
pixel--, copy++)
*copy += bias; //add bias
source->fast_put_line (xstart, ystart + y, xext, &copyline);
}
}
/**********************************************************************
* starbase_to_normal
*
* Copy a portion of one image to a portion of another image.
* This function maps the colour tables used on the screen to
* greyscale values in the way "normally" expected.
**********************************************************************/
DLLSYM void starbase_to_normal( //copy rectangle
IMAGE *source, //source image
inT32 xstart, //start coords
inT32 ystart,
inT32 xext, //extent to copy
inT32 yext,
IMAGE *dest, //destination image
inT32 xdest, //destination coords
inT32 ydest,
BOOL8 preserve_grey //shift to new bpp
) {
IMAGELINE copyline; //copy of line
uinT8 *copy; //source pointer
inT8 shift4; //shift factor
inT8 shift6; //shift factor
inT8 colour_shift; //shift of colours
uinT8 white_level; //dest white value
inT32 pixel; //pixel index
inT32 y; //line index
inT32 yoffset; //current adjusted offset
inT8 srcppb; //pixels per byte
if (xstart < 0 || ystart < 0 || xdest < 0 || ydest < 0)
return;
if (xext <= 0)
xext = source->get_xsize (); //default to all
if (xext > source->get_xsize () - xstart)
//clip to smallest
xext = source->get_xsize () - xstart;
if (xext > dest->get_xsize () - xdest)
xext = dest->get_xsize () - xdest;
if (yext <= 0)
yext = source->get_ysize (); //default to all
if (yext > source->get_ysize () - ystart)
//clip to smallest
yext = source->get_ysize () - ystart;
if (yext > dest->get_ysize () - ydest)
yext = dest->get_ysize () - ydest;
if (xext <= 0 || yext <= 0)
return; //nothing to do
//pixels per byte
srcppb = 8 / source->get_bpp ();
shift4 = 4 - dest->get_bpp (); //for different bpps
shift6 = 6 - dest->get_bpp ();
//for grey preserve
colour_shift = 8 - dest->get_bpp ();
white_level = dest->get_white_level ();
for (y = 0; y < yext; y++) {
if (ystart >= ydest)
yoffset = y; //top down
else
yoffset = yext - y - 1; //bottom up
source->check_legal_access (xstart, ystart + yoffset, xext);
dest->check_legal_access (xdest, ydest + yoffset, xext);
source->get_line (xstart, ystart + yoffset, xext, &copyline, 0);
for (pixel = 0, copy = copyline.pixels; pixel < xext; pixel++) {
if (*copy < FIXED_COLOURS && preserve_grey)
*copy = grey_scales[*copy] >> colour_shift;
else if (*copy < FIXED_COLOURS) {
if (*copy == BLACK_PIX)
*copy = white_level; //black->white
else
*copy = 0; //others->black
}
else if (*copy >= MIN_4BIT && *copy < MAX_4BIT) {
if (shift4 < 0)
*copy = (*copy - MIN_4BIT) << (-shift4);
else
*copy = (*copy - MIN_4BIT) >> shift4;
}
else if (*copy >= MIN_6BIT && *copy < MAX_6BIT) {
if (shift6 < 0)
*copy = (*copy - MIN_6BIT) << (-shift6);
else
*copy = (*copy - MIN_6BIT) >> shift6;
}
else {
*copy = white_level; //white the rest
}
copy++;
}
dest->put_line (xdest, ydest + yoffset, xext, &copyline, 0);
}
}
/**********************************************************************
* fast_get_line
*
* Get a line of image into the supplied image line buffer.
* The image is converted to 8bpp by simple assignment.
* If the image is aleady 8 or 6bpp, no copy is done and a pointer
* to the correct image section is put in the line buffer.
**********************************************************************/
void IMAGE::fast_get_line( //get image line
inT32 x, //coord to start at
inT32 y, //line to get
inT32 width, //no of pixels to get
IMAGELINE *linebuf //line to copy to
) {
if (width > 0 && bpp > 4) {
check_legal_access(x, y, width);
//get pointer only
linebuf->pixels = image + xdim * (ymax - 1 - y) + x * bytespp;
}
else
//just copy it
this->get_line (x, y, width, linebuf, 0);
linebuf->bpp = bpp;
}
/**********************************************************************
* get_line
*
* Get a line of image into the supplied image line buffer.
* The image is converted to 8bpp by simple assignment.
**********************************************************************/
void IMAGE::get_line( //get image line
inT32 x, //coord to start at
inT32 y, //line to get
inT32 width, //no of pixels to get
IMAGELINE *linebuf, //line to copy to
inT32 margins //size of margins
) {
uinT8 *src; //source pointer
uinT8 *dest; //destination pointer
uinT8 *unpacksrc; //unpacking pointer
inT8 bit; //bit index
inT8 pixperbyte; //pixels per byte
uinT8 white; //white colour
inT32 pixel; //pixel index
//test coords
this->check_legal_access (x, y, width);
if (width > xsize - x)
width = xsize - x; //clip to image
width *= bytespp;
linebuf->init (width + margins * bytespp * 2);
linebuf->bpp = bpp;
//start of line
src = image + xdim * (ymax - 1 - y);
dest = linebuf->line; //destination line
linebuf->pixels = dest;
white = (1 << bpp) - 1; //max value of pixel
for (pixel = margins * bytespp; pixel > 0; pixel--) {
*dest++ = white; //margins are white
}
if (width > 0) {
if (bpp > 4) {
src += x; //offset
//easy way
memmove (dest, src, (unsigned) width);
}
else if (bpp == 4) {
src += x / 2; //offset on line
if (x & 1) {
//get coded nibble
*dest++ = bpp4table[*src++][1];
width--;
}
while (width >= 2) {
//get coded bits
unpacksrc = bpp4table[*src++];
*dest++ = *unpacksrc++;
*dest++ = *unpacksrc++; //copy nibbles
width -= 2;
}
if (width) {
//get coded nibble
*dest++ = bpp4table[*src++][0];
}
}
else if (bpp == 2) {
pixperbyte = 4;
src += x / 4; //offset on line
bit = (inT8) (x % 4); //offset in byte
width += bit;
while (width > 0) { //until all done
if (width < pixperbyte)
//less on last byte
pixperbyte = (inT8) width;
//get coded bits
unpacksrc = &bpp2table[*src++][bit];
for (; bit < pixperbyte; bit++)
*dest++ = *unpacksrc++;//copy bytes
width -= pixperbyte;
bit = 0;
}
}
else {
pixperbyte = 8;
src += x / 8; //offset on line
bit = (inT8) (x % 8); //offset in byte
width += bit;
while (width > 0) { //until all done
if (width < pixperbyte)
//less on last byte
pixperbyte = (inT8) width;
//get coded bits
unpacksrc = &bpp1table[*src++][bit];
for (; bit < pixperbyte; bit++)
*dest++ = *unpacksrc++;//copy bytes
width -= pixperbyte;
bit = 0;
}
}
}
for (pixel = margins * bytespp; pixel > 0; pixel--) {
*dest++ = white; //margins are white
}
}
/**********************************************************************
* get_column
*
* Get a column of image into the supplied image line buffer.
* The image is converted to 8bpp by simple assignment.
**********************************************************************/
void IMAGE::get_column( //get image column
inT32 x, //coord to start at
inT32 y, //line to get
inT32 height, //no of pixels to get
IMAGELINE *linebuf, //line to copy to
inT32 margins //size of margins
) {
uinT8 *src; //source pointer
uinT8 *dest; //destination pointer
inT8 bit; //bit index
inT8 pixperbyte; //pixels per byte
uinT8 white; //white colour
inT32 pixel; //pixel index
//test coords
this->check_legal_access (x, y, 1);
//test coords
this->check_legal_access (x, y + height - 1, 1);
if (height > ysize - y)
height = ysize - y; //clip to image
linebuf->init (height * bytespp + margins * bytespp * 2);
//start of line
src = image + xdim * (ymax - 1 - y);
dest = linebuf->line; //destination line
linebuf->pixels = dest;
white = (1 << bpp) - 1; //max value of pixel
for (pixel = margins * bytespp; pixel > 0; pixel--) {
*dest++ = white; //margins are white
}
if (height > 0) {
if (bpp == 24) {
src += x * bytespp; //offset
for (; height > 0; --height) {
*dest++ = *src; //copy bytes
*dest++ = *(src + 1);
*dest++ = *(src + 2);
src -= xdim;
}
}
else if (bpp > 4) {
src += x;
for (; height > 0; --height) {
*dest++ = *src; //copy bytes
src -= xdim;
}
}
else if (bpp == 4) {
src += x / 2; //offset on line
if (x & 1) {
for (; height > 0; --height) {
//get coded nibble
*dest++ = bpp4table[*src][1];
src -= xdim;
}
}
else {
for (; height > 0; --height) {
//get coded nibble
*dest++ = bpp4table[*src][0];
src -= xdim;
}
}
}
else if (bpp == 2) {
pixperbyte = 4;
src += x / 4; //offset on line
bit = (inT8) (x % 4); //offset in byte
for (; height > 0; --height) {
//get coded bits
*dest++ = bpp2table[*src][bit];
src -= xdim;
}
}
else {
pixperbyte = 8;
src += x / 8; //offset on line
bit = (inT8) (x % 8); //offset in byte
for (; height > 0; --height) {
//get coded bits
*dest++ = bpp1table[*src][bit];
src -= xdim;
}
}
}
for (pixel = margins * bytespp; pixel > 0; pixel--) {
*dest++ = white; //margins are white
}
}
/**********************************************************************
* fast_put_line
*
* Put a line buffer back into the image.
* If the line buffer merely points back into the image, nothing is done.
* Otherwise, put_line is used to copy the line back.
**********************************************************************/
void IMAGE::fast_put_line( //put image line
inT32 x, //coord to start at
inT32 y, //line to get
inT32 width, //no of pixels to put
IMAGELINE *linebuf //line to copy to
) {
if (width > 0 && (bpp <= 4 || linebuf->pixels == linebuf->line))
//just copy it
put_line (x, y, width, linebuf, 0);
}
/**********************************************************************
* put_line
*
* Put the supplied line buffer into the image.
* The image is converted from 8bpp by simple assignment.
**********************************************************************/
void IMAGE::put_line( //put image line
inT32 x, //coord to start at
inT32 y, //line to get
inT32 width, //no of pixels to get
IMAGELINE *linebuf, //line to copy to
inT32 margins //margins in buffer
) {
uinT8 *src; //source pointer
uinT8 *dest; //destination pointer
inT8 bit; //bit index
uinT8 pixel; //collected bits
inT8 pixperbyte; //pixels in a byte
inT8 bytesperpix; //in source
this->check_legal_access (x, y, width);
if (width > xsize - x)
width = xsize - x; //clip to image
if (width <= 0)
return; //nothing to do
//source line
src = linebuf->pixels + margins;
//start of line
dest = image + xdim * (ymax - 1 - y);
if (linebuf->bpp == 24) {
src++;
bytesperpix = 3;
}
else
bytesperpix = 1;
if (bpp == 24 && linebuf->bpp == 24) {
dest += x * bytespp;
width *= bytespp;
memmove (dest, src - 1, (unsigned) width);
}
else if (bpp == 24) {
src--;
dest += x * bytespp;
while (width > 0) {
pixel = *src++;
*dest++ = pixel;
*dest++ = pixel;
*dest++ = pixel;
width--;
}
}
else if (bpp > 4) {
dest += x; //offset
if (linebuf->bpp == 24) {
while (width > 0) {
*dest++ = *src;
src += 3;
width--;
}
}
else
//easy way
memmove (dest, src, (unsigned) width);
}
else if (bpp == 4) {
dest += x / 2; //offset on line
if (x & 1) {
*dest &= 0xf0; //clean odd byte
*dest++ |= *src & 0x0f; //and copy it
src += bytesperpix;
width--;
}
while (width >= 2) {
pixel = *src << 4; //left pixel
src += bytesperpix;
pixel |= *src & 0x0f; //right pixel
src += bytesperpix;
*dest++ = pixel;
width -= 2;
}
if (width) {
*dest &= 0x0f; //clean odd byte
*dest |= *src << 4;
}
}
else if (bpp == 2) {
pixperbyte = 4;
dest += x / 4; //offset on line
bit = (inT8) (x % 4); //offset in byte
width += bit;
pixel = *dest >> (8 - bit - bit);
while (width >= 4) { //until all done
for (; bit < 4; bit++) {
pixel <<= 2; //make space for new one
pixel |= *src & 3;
src += bytesperpix;
}
*dest++ = pixel; //new pixel
width -= 4;
bit = 0;
}
if (width > 0) { //until all done
for (bit = 0; bit < width; bit++) {
pixel <<= 2; //make space for new one
pixel |= *src & 3;
src += bytesperpix;
}
pixel <<= (8 - bit - bit); //shift rest
//keep trainling bits
pixel |= *dest & ((1 << (8 - bit - bit)) - 1);
*dest++ = pixel; //new pixel
}
}
else {
pixperbyte = 8;
dest += x / 8; //offset on line
bit = (inT8) (x % 8); //offset in byte
width += bit;
pixel = *dest >> (8 - bit);
while (width >= 8) { //until all done
for (; bit < 8; bit++) {
pixel <<= 1; //make space for new one
pixel |= *src & 1;
src += bytesperpix;
}
*dest++ = pixel; //new pixel
width -= 8;
bit = 0;
}
width -= bit;
if (width > 0) { //until all done
while (width > 0) {
pixel <<= 1; //make space for new one
pixel |= *src & 1;
src += bytesperpix;
bit++;
width--;
}
pixel <<= (8 - bit); //shift rest
//keep trainling bits
pixel |= *dest & ((1 << (8 - bit)) - 1);
*dest++ = pixel; //new pixel
}
}
}
/**********************************************************************
* put_column
*
* Put the supplied column buffer into the image.
* The image is converted from 8bpp by simple assignment.
**********************************************************************/
void IMAGE::put_column( //put image column
inT32 x, //coord to start at
inT32 y, //line to get
inT32 height, //no of pixels to get
IMAGELINE *linebuf, //line to copy to
inT32 margins //margins in buffer
) {
uinT8 *src; //source pointer
uinT8 *dest; //destination pointer
inT8 bit; //bit index
uinT8 pixel; //collected bits
inT8 bytesperpix; //in source
this->check_legal_access (x, y, 1);
this->check_legal_access (x, y + height - 1, 1);
if (height > ysize - y)
height = ysize - y; //clip to image
if (height <= 0)
return; //nothing to do
//source line
src = linebuf->pixels + margins;
//start of line
dest = image + xdim * (ymax - 1 - y);
if (linebuf->bpp == 24) {
src++;
bytesperpix = 3;
}
else
bytesperpix = 1;
if (bpp == 24 && linebuf->bpp == 24) {
dest += x * bytesperpix;
src--;
for (; height > 0; --height) {
*dest = *src++;
*(dest + 1) = *src++;
*(dest + 2) = *src++;
dest -= xdim;
}
}
else if (bpp == 24) {
src--;
dest += x * bytesperpix;
for (; height > 0; --height) {
pixel = *src++;
*dest = pixel;
*(dest + 1) = pixel;
*(dest + 2) = pixel;
dest -= xdim;
}
}
else if (bpp > 4) {
dest += x; //offset
for (; height > 0; --height) {
*dest = *src;
src += bytesperpix;
dest -= xdim;
}
}
else if (bpp == 4) {
dest += x / 2; //offset on line
if (x & 1) {
for (; height > 0; --height) {
*dest &= 0xf0; //clean odd byte
*dest |= *src & 0x0f; //and copy it
src += bytesperpix;
dest -= xdim;
}
}
else {
for (; height > 0; --height) {
*dest &= 0x0f; //clean odd byte
*dest |= *src << 4;
src += bytesperpix;
dest -= xdim;
}
}
}
else if (bpp == 2) {
dest += x / 4; //offset on line
bit = (inT8) (x % 4); //offset in byte
bit = 6 - bit - bit; //bit shift
pixel = ~(3 << bit); //mask
for (; height > 0; --height) {
//change 2 bits
*dest = (*dest & pixel) | ((*src & 3) << bit);
src += bytesperpix;
dest -= xdim;
}
}
else {
dest += x / 8; //offset on line
bit = (inT8) (x % 8); //offset in byte
bit = 7 - bit;
pixel = ~(1 << bit);
for (; height > 0; --height) {
//change 1 bit
*dest = (*dest & pixel) | ((*src & 1) << bit);
src += bytesperpix;
dest -= xdim;
}
}
}
/**********************************************************************
* check_legal_access
*
* Check that x,y are within the bounds of the image.
* Call bufread if necessary to get the image into memory.
**********************************************************************/
void IMAGE::check_legal_access( //check coords are legal
inT32 x, //coords to check
inT32 y,
inT32 xext //xextent
) {
if (x < 0 || x >= xsize || y < 0 || y >= ysize || x + xext > xsize)
BADIMAGECOORDS.error ("IMAGE::check_legal_access",
ABORT, "(%d+%d,%d)", x, xext, y);
if (y >= ymax)
BADIMAGESEEK.error ("IMAGE::check_legal_access", ABORT, "(%d,%d)", x, y);
if (y < ymin)
bufread(y); //read some more
}
#ifdef HAVE_LIBLEPT
// ONLY available if you have Leptonica installed.
/**********************************************************************
* ToPix
*
* Make a Pix from this image.
**********************************************************************/
Pix* IMAGE::ToPix() {
int width = this->get_xsize();
int height = this->get_ysize();
int bpp = this->get_bpp();
Pix* pix = pixCreate(width, height, bpp == 24 ? 32 : bpp);
l_uint32* data = pixGetData(pix);
IMAGELINE line;
if (bpp == 24) {
line.init(width * 3);
line.set_bpp(24);
} else {
line.init(width);
}
switch (bpp) {
case 1:
for (int y = height - 1 ; y >= 0; --y) {
this->get_line(0, y, width, &line, 0);
for (int x = 0; x < width; ++x) {
if (line.pixels[x])
CLEAR_DATA_BIT(data, x);
else
SET_DATA_BIT(data, x);
}
data += pixGetWpl(pix);
}
break;
case 8:
// Greyscale just copies the bytes in the right order.
for (int y = height - 1 ; y >= 0; --y) {
this->get_line(0, y, width, &line, 0);
for (int x = 0; x < width; ++x)
SET_DATA_BYTE(data, x, line.pixels[x]);
data += pixGetWpl(pix);
}
break;
case 24:
// Put the colors in the correct places in the line buffer.
for (int y = height - 1 ; y >= 0; --y) {
this->get_line(0, y, width, &line, 0);
for (int x = 0; x < width; ++x, ++data) {
SET_DATA_BYTE(data, COLOR_RED, line[x][RED_PIX]);
SET_DATA_BYTE(data, COLOR_GREEN, line[x][GREEN_PIX]);
SET_DATA_BYTE(data, COLOR_BLUE, line[x][BLUE_PIX]);
}
}
break;
default:
tprintf("Cannot convert image to Pix with bpp = %d\n", bpp);
}
return pix;
}
/**********************************************************************
* FromPix
*
* Copy from the given Pix into this image.
**********************************************************************/
void IMAGE::FromPix(const Pix* src_pix) {
// Leptonica doesn't const its inputs, but we don't change the input.
Pix* pix = const_cast<Pix*>(src_pix);
Pix* destroy_this_pix = NULL;
int depth = pixGetDepth(pix);
if (depth > 1 && depth < 8) {
// Convert funny depths to 8 bit.
destroy_this_pix = pixConvertTo8(pix, false);
pix = destroy_this_pix;
depth = pixGetDepth(pix);
}
int width = pixGetWidth(pix);
int height = pixGetHeight(pix);
const l_uint32* data = pixGetData(pix);
this->create(width, height, depth == 32 ? 24 : depth);
// For each line in the image, fill the IMAGELINE class and put it into the
// destination image. Note that Tesseract stores images with the
// bottom at y=0 and 0 is always black in grey and binary.
IMAGELINE line;
if (depth == 32) {
line.init(width * 3);
line.set_bpp(24);
} else {
line.init(width);
}
switch (depth) {
case 1:
// Binary images just flip the data bit.
for (int y = height - 1 ; y >= 0; --y) {
for (int x = 0; x < width; ++x)
line.pixels[x] = GET_DATA_BIT(data, x) ^ 1;
this->put_line(0, y, width, &line, 0);
data += pixGetWpl(pix);
}
break;
case 8:
// Greyscale just copies the bytes in the right order.
for (int y = height - 1 ; y >= 0; --y) {
for (int x = 0; x < width; ++x)
line.pixels[x] = GET_DATA_BYTE(data, x);
this->put_line(0, y, width, &line, 0);
data += pixGetWpl(pix);
}
break;
case 32:
// Put the colors in the correct places in the line buffer.
for (int y = height - 1 ; y >= 0; --y) {
for (int x = 0; x < width; ++x, ++data) {
line[x][RED_PIX] = GET_DATA_BYTE(data, COLOR_RED);
line[x][GREEN_PIX] = GET_DATA_BYTE(data, COLOR_GREEN);
line[x][BLUE_PIX] = GET_DATA_BYTE(data, COLOR_BLUE);
}
this->put_line(0, y, width, &line, 0);
}
break;
default:
tprintf("Cannot convert Pix to image with bpp = %d\n", depth);
}
if (destroy_this_pix != NULL)
pixDestroy(&destroy_this_pix);
}
#endif // HAVE_LIBLEPT
/*************************************************************************
* convolver()
*
* Calls the specified function for each pixel in the image, passing in an m x n
* window of the image, centred on the pixel. The convolution function returns
* a new value for the pixel, based on the window.
*
* At the edges of the image, the window is padded to white pixels.
*************************************************************************/
void
IMAGE::convolver ( //Map fn over window
inT32 win_width, //Window width
inT32 win_height, //Window height
void (*convolve) ( //Conv Function
uinT8 ** pixels, //Of window
uinT8 bytespp, //1 or 3 for colour
inT32 win_wd, //Window width
inT32 win_ht, //Window height
uinT8 ret_white_value, //White value to RETURN
uinT8 * result) //Ptr to result pix
) {
IMAGELINE new_row; //Replacement pixels
IMAGELINE *old_rows; //Rows being processed
inT32 oldest_imline; //Next imline to replace
uinT8 **window; //ptrs to pixel rows
uinT8 **winmax; //ptrs to pixel rows
uinT8 **win; //ptrs to pixel rows
inT32 current_row; //Row being calculated
inT32 current_col; //Col being calculated
inT32 row = 0; //Next row to get
inT32 i, j;
uinT8 *pix;
uinT8 *max;
inT32 xmargin = win_width / 2;
inT32 ymargin = win_height / 2;
uinT8 white = get_white_level ();
const uinT8 max_white = 255;
float white_scale = (float) 255 / get_white_level ();
if (((win_width % 2) == 0) ||
((win_height % 2) == 0) ||
(win_height < 3) ||
(win_width < 3) || (win_height > ysize / 2) || (win_width > xsize / 2))
BADWINDOW.error ("IMAGE::convolver",
ABORT, "(%d x %d)", win_width, win_height);
new_row.init (xsize * bytespp);
new_row.set_bpp (bpp);
old_rows = new IMAGELINE[win_height];
for (i = 0; i < win_height; i++) {
old_rows[i].init ((xsize + 2 * xmargin) * bytespp);
old_rows[i].set_bpp (bpp);
}
window = (uinT8 **) alloc_mem (win_height * sizeof (uinT8 *));
winmax = window + win_height;
/* Make bottom border */
for (oldest_imline = 0; oldest_imline < ymargin; oldest_imline++) {
pix = old_rows[oldest_imline].pixels;
max = pix + (xsize + 2 * xmargin) * bytespp;
while (pix < max)
*pix++ = max_white;
}
/* Initialise remaining rows but one*/
for (; oldest_imline < win_height - 1; oldest_imline++) {
get_line (0, row++, xsize, &old_rows[oldest_imline], xmargin);
if (max_white != white) {
pix = old_rows[oldest_imline].pixels;
max = pix + (xsize + 2 * xmargin) * bytespp;
while (pix < max) {
*pix = (uinT8) (*pix * white_scale);
++pix;
}
}
}
/* Image Processing */
for (current_row = 0; current_row < ysize;) {
/* Get next row and re-initialise window array */
if (row < ysize) {
get_line (0, row++, xsize, &old_rows[oldest_imline], xmargin);
if (max_white != white) {
pix = old_rows[oldest_imline].pixels;
max = pix + (xsize + 2 * xmargin) * bytespp;
while (pix < max) {
*pix = (uinT8) (*pix * white_scale);
++pix;
}
}
}
else {
pix = old_rows[oldest_imline].pixels;
max = pix + (xsize + 2 * xmargin) * bytespp;
while (pix < max)
*pix++ = max_white;
}
oldest_imline++;
if (oldest_imline >= win_height)
oldest_imline = 0;
/* Process line */
pix = new_row.pixels;
for (current_col = 0; current_col < xsize;) {
/* Set up window ptrs */
if (current_col == 0) {
j = oldest_imline;
for (i = 0; i < win_height; i++) {
window[i] = old_rows[j++].pixels;
if (j >= win_height)
j = 0;
}
}
else {
for (win = window; win < winmax; (*win++) += bytespp);
//Move along rows
}
convolve(window, bytespp, win_width, win_height, white, pix);
pix += bytespp;
current_col++;
}
put_line (0, current_row, xsize, &new_row, 0);
new_row.init ();
new_row.set_bpp (bpp);
current_row++;
}
}