/*====================================================================*
- 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.
*====================================================================*/
// See http://www.leptonica.com/scaling.html
// (2) This Leptonica code is adopted/modified for the FreeImage Library
// FreeImage Display Demo v4.0
// Copyright (C) 2008 monday2000@yandex.ru
/////////////////////////////////////////////////////////////////////
#include "stdafx.h" // added to the original file to allow the MFC compilation
#include "FreeImage.h"
#include "Utilities.h"
/////////////////////////////////////////////////////////////////////
inline void Get1BitPixel(BYTE* line, int x, int color_mode, BYTE* value)
{
if (color_mode == FIC_MINISWHITE)
*value = (line[x >> 3] & (0x80 >> (x & 0x07))) != 0 ? 0 : 255;
else
*value = (line[x >> 3] & (0x80 >> (x & 0x07))) != 0 ? 255 : 0;
}
/////////////////////////////////////////////////////////////////////
/*!
* scaleGrayAreaMapLow()
*
* This should only be used for downscaling.
* We choose to divide each pixel into 16 x 16 sub-pixels.
* This is about 2x slower than scaleSmoothLow(), but the results
* are significantly better on small text, esp. for downscaling
* factors between 1.5 and 5. All src pixels are subdivided
* into 256 sub-pixels, and are weighted by the number of
* sub-pixels covered by the dest pixel.
*/
BOOL LeptonicaScaleGrayAreaMapLow(FIBITMAP* src_dib,
unsigned wd, unsigned hd, CPoint OffscreenSize,
BYTE* pBits, RGBQUAD *dst_pal)
{
unsigned i, j, k, m, wm2, hm2;
unsigned xu, yu; // UL corner in src image, to 1/16 of a pixel
unsigned xl, yl; // LR corner in src image, to 1/16 of a pixel
unsigned xup, yup, xuf, yuf; // UL src pixel: integer and fraction
unsigned xlp, ylp, xlf, ylf; // LR src pixel: integer and fraction
unsigned delx, dely, area;
unsigned v00; // contrib. from UL src pixel
unsigned v01; // contrib. from LL src pixel
unsigned v10; // contrib from UR src pixel
unsigned v11; // contrib from LR src pixel
unsigned vin; // contrib from all full interior src pixels
unsigned vmid; // contrib from side parts that are full in 1 direction
unsigned val;
BYTE *lines, *lined;
double scx, scy;
BYTE *datas = FreeImage_GetBits(src_dib);
unsigned ws = FreeImage_GetWidth(src_dib);
unsigned hs = FreeImage_GetHeight(src_dib);
int bpp = FreeImage_GetBPP(src_dib);
int btpp = bpp/8;
unsigned wpls = FreeImage_GetPitch(src_dib);
unsigned wpld = CalculatePitch(CalculateLine(wd, bpp == 1 ? 8 : bpp));
int color_mode = FreeImage_GetColorType(src_dib);
unsigned wpld_big = CalculatePitch(CalculateLine(OffscreenSize.x,
bpp == 1 ? 8 : bpp));
unsigned dif2_w = (wpld_big - wpld)/2;
// center the bitmap horizontally (on the background bitmap)
pBits += dif2_w;
// center the bitmap vertically (on the background bitmap)
pBits += (OffscreenSize.y - hd)/2 * wpld_big;
BYTE sval;
// (scx, scy) are scaling factors that are applied to the
// dest coords to get the corresponding src coords.
// We need them because we iterate over dest pixels
// and must find the corresponding set of src pixels.
scx = 16. * (double)ws / (double)wd;
scy = 16. * (double)hs / (double)hd;
wm2 = ws - 2;
hm2 = hs - 2;
unsigned *srow_u, *scol_u;
if ((srow_u = (unsigned *)calloc(hd, sizeof(unsigned))) == NULL)
return false;
if ((scol_u = (unsigned *)calloc(wd, sizeof(unsigned))) == NULL)
return false;
for (i = 0; i < hd; i++)
srow_u[i] = MIN<unsigned>((unsigned)(scy * i + 0.5), (unsigned)(16. * (hs - 1)));
for (j = 0; j < wd; j++)
scol_u[j] = MIN<unsigned>((unsigned)(scy * j + 0.5), (unsigned)(16. * (ws - 1)));
unsigned xuf16, yuf16;
unsigned temp_val;
// Iterate over the destination pixels
for (i = 0; i < hd; i++)
{
yu = srow_u[i];
yl = yu + (unsigned)scy;
yup = yu >> 4;
yuf = yu & 0x0f;
ylp = yl >> 4;
ylf = yl & 0x0f;
dely = ylp - yup;
lined = pBits + i * wpld_big;
lines = datas + yup * wpls;
yuf16 = 16 - yuf;
for (j = 0; j < wd; j++)
{
xu = scol_u[j];
xl = xu + (unsigned)scx;
xup = xu >> 4;
xuf = xu & 0x0f;
xlp = xl >> 4;
xlf = xl & 0x0f;
delx = xlp - xup;
xuf16 = 16 - xuf;
switch (bpp)
{
case 1:
// If near the edge, just use a src pixel value
if (xlp > wm2 || ylp > hm2)
{
Get1BitPixel(lines, xup, color_mode, &sval);
lined[j] = sval;
continue;
}
// Area summed over, in subpixels. This varies
// due to the quantization, so we can't simply take
// the area to be a constant: area = scx * scy.
area = (xuf16 + ((delx - 1) << 4) + xlf) *
(yuf16 + ((dely - 1) << 4) + ylf);
// Do area map summation
Get1BitPixel(lines, xup, color_mode, &sval);
temp_val = xuf16 * yuf16;
v00 = sval ? (temp_val << 8) - temp_val : 0;
Get1BitPixel(lines, xlp, color_mode, &sval);
temp_val = xlf * yuf16;
v10 = sval ? (temp_val << 8) - temp_val : 0;
Get1BitPixel(lines + dely * wpls, xup, color_mode, &sval);
temp_val = xuf16 * ylf;
v01 = sval ? (temp_val << 8) - temp_val : 0;
Get1BitPixel(lines + dely * wpls, xlp, color_mode, &sval);
temp_val = xlf * ylf;
v11 = sval ? (temp_val << 8) - temp_val : 0;
for (vin = 0, k = 1; k < dely; k++)
{ // for full src pixels
for (m = 1; m < delx; m++)
{
Get1BitPixel(lines + k * wpls, xup + m, color_mode, &sval);
vin += sval ? 65280 : 0; // 255 * 256
}
}
for (vmid = 0, k = 1; k < dely; k++) // for left side
{
Get1BitPixel(lines + k * wpls, xup, color_mode, &sval);
vmid += sval ? (xuf16 << 4) * sval : 0;
}
for (k = 1; k < dely; k++) // for right side
{
Get1BitPixel(lines + k * wpls, xlp, color_mode, &sval);
vmid += sval ? (xlf << 4) * sval : 0;
}
for (m = 1; m < delx; m++) // for top side
{
Get1BitPixel(lines, xup + m, color_mode, &sval);
vmid += sval ? (yuf16 << 4) * sval : 0;
}
for (m = 1; m < delx; m++) // for bottom side
{
Get1BitPixel(lines + dely * wpls, xup + m, color_mode, &sval);
vmid += sval ? (ylf << 4) * sval : 0;
}
val = (v00 + v01 + v10 + v11 + vin + vmid + 128) / area;
lined[j] = val;
break;
//*************************************
case 8:
// If near the edge, just use a src pixel value
if (xlp > wm2 || ylp > hm2)
{
lined[j] = dst_pal[*(lines + xup)].rgbRed;
continue;
}
// Area summed over, in subpixels. This varies
// due to the quantization, so we can't simply take
// the area to be a constant: area = scx * scy.
area = (xuf16 + 16 * (delx - 1) + xlf) *
(yuf16 + 16 * (dely - 1) + ylf);
// Do area map summation
v00 = xuf16 * yuf16 * dst_pal[*(lines + xup)].rgbRed;
v10 = xlf * yuf16 * dst_pal[*(lines + xlp)].rgbRed;
v01 = xuf16 * ylf * dst_pal[*(lines + dely * wpls + xup)].rgbRed;
v11 = xlf * ylf * dst_pal[*(lines + dely * wpls + xlp)].rgbRed;
for (vin = 0, k = 1; k < dely; k++)
{ // for full src pixels
for (m = 1; m < delx; m++)
{
vin += 256 * dst_pal[*(lines + k * wpls + xup + m)].rgbRed;
}
}
for (vmid = 0, k = 1; k < dely; k++) // for left side
vmid += xuf16 * 16 * dst_pal[*(lines + k * wpls + xup)].rgbRed;
for (k = 1; k < dely; k++) // for right side
vmid += xlf * 16 * dst_pal[*(lines + k * wpls + xlp)].rgbRed;
for (m = 1; m < delx; m++) // for top side
vmid += 16 * yuf16 * dst_pal[*(lines + xup + m)].rgbRed;
for (m = 1; m < delx; m++) // for bottom side
vmid += 16 * ylf * dst_pal[*(lines + dely * wpls + xup + m)].rgbRed;
val = (v00 + v01 + v10 + v11 + vin + vmid + 128) / area;
lined[j] = val;
break;
} // end switch
}
}
free(srow_u);
free(scol_u);
return true;
}
/////////////////////////////////////////////////////////////////////
/*------------------------------------------------------------------*
* General area mapped gray scaling *
*------------------------------------------------------------------*/
/*!
* scaleColorAreaMapLow()
*
* This should only be used for downscaling.
* We choose to divide each pixel into 16 x 16 sub-pixels.
* This is much slower than scaleSmoothLow(), but it gives a
* better representation, esp. for downscaling factors between
* 1.5 and 5. All src pixels are subdivided into 256 sub-pixels,
* and are weighted by the number of sub-pixels covered by
* the dest pixel. This is about 2x slower than scaleSmoothLow(),
* but the results are significantly better on small text.
*
* *** Warning: implicit assumption about RGB component ordering ***
*/
BOOL LeptonicaScaleColorAreaMapLow(FIBITMAP* src_dib,
unsigned wd, unsigned hd, CPoint OffscreenSize,
BYTE* pBits, RGBQUAD *dst_pal)
{
unsigned i, j, k, m, wm2, hm2;
unsigned area00, area10, area01, area11, areal, arear, areat, areab;
unsigned xu, yu; // UL corner in src image, to 1/16 of a pixel
unsigned xl, yl; // LR corner in src image, to 1/16 of a pixel
unsigned xup, yup, xuf, yuf; // UL src pixel: integer and fraction
unsigned xlp, ylp, xlf, ylf; // LR src pixel: integer and fraction
unsigned delx, dely, area;
unsigned v00r, v00g, v00b; // contrib. from UL src pixel
unsigned v01r, v01g, v01b; // contrib. from LL src pixel
unsigned v10r, v10g, v10b; // contrib from UR src pixel
unsigned v11r, v11g, v11b; // contrib from LR src pixel
unsigned vinr, ving, vinb; // contrib from all full interior src pixels
unsigned vmidr, vmidg, vmidb; // contrib from side parts
unsigned valr, valg, valb;
BYTE pixel00[4], pixel10[4], pixel01[4], pixel11[4], pixel[4];
BYTE *lines, *lined;
double scx, scy;
BYTE *datas = FreeImage_GetBits(src_dib);
unsigned ws = FreeImage_GetWidth(src_dib);
unsigned hs = FreeImage_GetHeight(src_dib);
int bpp = FreeImage_GetBPP(src_dib);
int btpp = bpp/8;
unsigned wpls = FreeImage_GetPitch(src_dib);
unsigned wpld = CalculatePitch(CalculateLine(wd, bpp == 1 ? 8 : bpp));
int color_mode = FreeImage_GetColorType(src_dib);
unsigned wpld_big = CalculatePitch(CalculateLine(OffscreenSize.x,
bpp == 1 ? 8 : bpp));
unsigned dif2_w = (wpld_big - wpld)/2;
// center the bitmap horizontally (on the background bitmap)
pBits += dif2_w - dif2_w % 3; // why is it needed here to align
// the image at the 3-byte boundary ???
// center the bitmap vertically (on the background bitmap)
pBits += (OffscreenSize.y - hd)/2 * wpld_big;
// (scx, scy) are scaling factors that are applied to the
// dest coords to get the corresponding src coords.
// We need them because we iterate over dest pixels
// and must find the corresponding set of src pixels.
scx = 16. * (double)ws / (double)wd;
scy = 16. * (double)hs / (double)hd;
wm2 = ws - 2;
hm2 = hs - 2;
/* Iterate over the destination pixels */
for (i = 0; i < hd; i++)
{
yu = (unsigned)(scy * i + 0.5);
yl = (unsigned)(scy * (i + 1.0) + 0.5);
yup = yu >> 4;
yuf = yu & 0x0f;
ylp = yl >> 4;
ylf = yl & 0x0f;
dely = ylp - yup;
lined = pBits + i * wpld_big;
lines = datas + yup * wpls;
for (j = 0; j < wd; j++)
{
xu = (unsigned)(scx * j + 0.5);
xl = (unsigned)(scx * (j + 1.0) + 0.5);
xup = xu >> 4;
xuf = xu & 0x0f;
xlp = xl >> 4;
xlf = xl & 0x0f;
delx = xlp - xup;
// If near the edge, just use a src pixel value
if (xlp > wm2 || ylp > hm2)
{
//*(lined + j*btpp) = *(lines + xup*btpp);
(lined + j*btpp)[FI_RGBA_RED] = (lines + xup*btpp)[FI_RGBA_RED];
(lined + j*btpp)[FI_RGBA_GREEN] = (lines + xup*btpp)[FI_RGBA_GREEN];
(lined + j*btpp)[FI_RGBA_BLUE] = (lines + xup*btpp)[FI_RGBA_BLUE];
continue;
}
// Area summed over, in subpixels. This varies
// due to the quantization, so we can't simply take
// the area to be a constant: area = scx * scy.
area = ((16 - xuf) + 16 * (delx - 1) + xlf) *
((16 - yuf) + 16 * (dely - 1) + ylf);
// Do area map summation
//pixel00 = *(lines + xup*btpp);
pixel00[FI_RGBA_RED] = (lines + xup*btpp)[FI_RGBA_RED];
pixel00[FI_RGBA_GREEN] = (lines + xup*btpp)[FI_RGBA_GREEN];
pixel00[FI_RGBA_BLUE] = (lines + xup*btpp)[FI_RGBA_BLUE];
//pixel10 = *(lines + xlp*btpp);
pixel10[FI_RGBA_RED] = (lines + xlp*btpp)[FI_RGBA_RED];
pixel10[FI_RGBA_GREEN] = (lines + xlp*btpp)[FI_RGBA_GREEN];
pixel10[FI_RGBA_BLUE] = (lines + xlp*btpp)[FI_RGBA_BLUE];
//pixel01 = *(lines + dely * wpls + xup*btpp);
pixel01[FI_RGBA_RED] = (lines + dely * wpls + xup*btpp)[FI_RGBA_RED];
pixel01[FI_RGBA_GREEN] = (lines + dely * wpls + xup*btpp)[FI_RGBA_GREEN];
pixel01[FI_RGBA_BLUE] = (lines + dely * wpls + xup*btpp)[FI_RGBA_BLUE];
//pixel11 = *(lines + dely * wpls + xlp*btpp);
pixel11[FI_RGBA_RED] = (lines + dely * wpls + xlp*btpp)[FI_RGBA_RED];
pixel11[FI_RGBA_GREEN] = (lines + dely * wpls + xlp*btpp)[FI_RGBA_GREEN];
pixel11[FI_RGBA_BLUE] = (lines + dely * wpls + xlp*btpp)[FI_RGBA_BLUE];
area00 = (16 - xuf) * (16 - yuf);
area10 = xlf * (16 - yuf);
area01 = (16 - xuf) * ylf;
area11 = xlf * ylf;
//v00r = area00 * (pixel00 >> 24);
v00r = area00 * pixel00[FI_RGBA_RED];
//v00g = area00 * ((pixel00 >> 16) & 0xff);
v00g = area00 * pixel00[FI_RGBA_GREEN];
//v00b = area00 * ((pixel00 >> 8) & 0xff);
v00b = area00 * pixel00[FI_RGBA_BLUE];
//v10r = area10 * (pixel10 >> 24);
v10r = area10 * pixel10[FI_RGBA_RED];
//v10g = area10 * ((pixel10 >> 16) & 0xff);
v10g = area10 * pixel10[FI_RGBA_GREEN];
//v10b = area10 * ((pixel10 >> 8) & 0xff);
v10b = area10 * pixel10[FI_RGBA_BLUE];
//v01r = area01 * (pixel01 >> 24);
v01r = area01 * pixel01[FI_RGBA_RED];
//v01g = area01 * ((pixel01 >> 16) & 0xff);
v01g = area01 * pixel01[FI_RGBA_GREEN];
//v01b = area01 * ((pixel01 >> 8) & 0xff);
v01b = area01 * pixel01[FI_RGBA_BLUE];
//v11r = area11 * (pixel11 >> 24);
v11r = area11 * pixel11[FI_RGBA_RED];
//v11g = area11 * ((pixel11 >> 16) & 0xff);
v11g = area11 * pixel11[FI_RGBA_GREEN];
//v11b = area11 * ((pixel11 >> 8) & 0xff);
v11b = area11 * pixel11[FI_RGBA_BLUE];
vinr = ving = vinb = 0;
for (k = 1; k < dely; k++)
{ // for full src pixels
for (m = 1; m < delx; m++)
{
//pixel = *(lines + k * wpls + (xup + m)*btpp);
pixel[FI_RGBA_RED] = (lines + k * wpls + (xup + m)*btpp)[FI_RGBA_RED];
pixel[FI_RGBA_GREEN] = (lines + k * wpls + (xup + m)*btpp)[FI_RGBA_GREEN];
pixel[FI_RGBA_BLUE] = (lines + k * wpls + (xup + m)*btpp)[FI_RGBA_BLUE];
//vinr += 256 * (pixel >> 24);
vinr += 256 * pixel[FI_RGBA_RED];
//ving += 256 * ((pixel >> 16) & 0xff);
ving += 256 * pixel[FI_RGBA_GREEN];
//vinb += 256 * ((pixel >> 8) & 0xff);
vinb += 256 * pixel[FI_RGBA_BLUE];
}
}
vmidr = vmidg = vmidb = 0;
areal = (16 - xuf) * 16;
arear = xlf * 16;
areat = 16 * (16 - yuf);
areab = 16 * ylf;
for (k = 1; k < dely; k++)
{ // for left side
//pixel = *(lines + k * wpls + xup*btpp);
pixel[FI_RGBA_RED] = (lines + k * wpls + xup*btpp)[FI_RGBA_RED];
pixel[FI_RGBA_GREEN] = (lines + k * wpls + xup*btpp)[FI_RGBA_GREEN];
pixel[FI_RGBA_BLUE] = (lines + k * wpls + xup*btpp)[FI_RGBA_BLUE];
//vmidr += areal * (pixel >> 24);
vmidr += areal * pixel[FI_RGBA_RED];
//vmidg += areal * ((pixel >> 16) & 0xff);
vmidg += areal * pixel[FI_RGBA_GREEN];
//vmidb += areal * ((pixel >> 8) & 0xff);
vmidb += areal * pixel[FI_RGBA_BLUE];
}
for (k = 1; k < dely; k++)
{ // for right side
//pixel = *(lines + k * wpls + xlp*btpp);
pixel[FI_RGBA_RED] = (lines + k * wpls + xlp*btpp)[FI_RGBA_RED];
pixel[FI_RGBA_GREEN] = (lines + k * wpls + xlp*btpp)[FI_RGBA_GREEN];
pixel[FI_RGBA_BLUE] = (lines + k * wpls + xlp*btpp)[FI_RGBA_BLUE];
//vmidr += arear * (pixel >> 24);
vmidr += arear * pixel[FI_RGBA_RED];
//vmidg += arear * ((pixel >> 16) & 0xff);
vmidg += arear * pixel[FI_RGBA_GREEN];
//vmidb += arear * ((pixel >> 8) & 0xff);
vmidb += arear * pixel[FI_RGBA_BLUE];
}
for (m = 1; m < delx; m++)
{ // for top side
//pixel = *(lines + (xup + m)*btpp);
pixel[FI_RGBA_RED] = (lines + (xup + m)*btpp)[FI_RGBA_RED];
pixel[FI_RGBA_GREEN] = (lines + (xup + m)*btpp)[FI_RGBA_GREEN];
pixel[FI_RGBA_BLUE] = (lines + (xup + m)*btpp)[FI_RGBA_BLUE];
//vmidr += areat * (pixel >> 24);
vmidr += areat * pixel[FI_RGBA_RED];
//vmidg += areat * ((pixel >> 16) & 0xff);
vmidg += areat * pixel[FI_RGBA_GREEN];
//vmidb += areat * ((pixel >> 8) & 0xff);
vmidb += areat * pixel[FI_RGBA_BLUE];
}
for (m = 1; m < delx; m++)
{ // for bottom side
//pixel = *(lines + dely * wpls + (xup + m)*btpp);
pixel[FI_RGBA_RED] = (lines + dely * wpls + (xup + m)*btpp)[FI_RGBA_RED];
pixel[FI_RGBA_GREEN] = (lines + dely * wpls + (xup + m)*btpp)[FI_RGBA_GREEN];
pixel[FI_RGBA_BLUE] = (lines + dely * wpls + (xup + m)*btpp)[FI_RGBA_BLUE];
vmidr += areab * pixel[FI_RGBA_RED];
vmidg += areab * pixel[FI_RGBA_GREEN];
vmidb += areab * pixel[FI_RGBA_BLUE];
}
// Sum all the contributions
valr = (v00r + v01r + v10r + v11r + vinr + vmidr + 128) / area;
valg = (v00g + v01g + v10g + v11g + ving + vmidg + 128) / area;
valb = (v00b + v01b + v10b + v11b + vinb + vmidb + 128) / area;
//*(lined + j*btpp) = (valr << 24 ) | (valg << 16) | (valb << 8);
(lined + j*btpp)[FI_RGBA_RED] = valr;
(lined + j*btpp)[FI_RGBA_GREEN] = valg;
(lined + j*btpp)[FI_RGBA_BLUE] = valb;
}
}
return true;
}
/////////////////////////////////////////////////////////////////////
/*------------------------------------------------------------------*
* Binary, grayscale and color scaling by closest pixel sampling *
*------------------------------------------------------------------*/
/*!
* scaleBySamplingLow() and scaleBinaryLow() - united together
*
* Notes:
* (1) The dest must be cleared prior to this operation,
* and we clear it here in the low-level code.
* (2) We reuse dest pixels and dest pixel rows whenever
* possible. This speeds the upscaling; downscaling
* is done by strict subsampling and is unaffected.
* (3) Because we are sampling and not interpolating, this
* routine works directly, without conversion to full
* RGB color, for 2, 4 or 8 bpp palette color images.
*/
// This is a "nearest neighbor" sampling algorithm (without filtering)
BOOL LeptonicaScaleBySamplingLow(FIBITMAP* src_dib,
unsigned wd, unsigned hd, CPoint OffscreenSize,
BYTE* pBits, RGBQUAD *dst_pal)
{
unsigned i, j;
unsigned xs, prevxs;
BYTE sval;
long *srow, *scol;
BYTE csval;
BYTE sval4[4] = {0};
BYTE *lines, *prevlines, *lined, *prevlined;
double wratio, hratio;
BYTE *datas = FreeImage_GetBits(src_dib);
unsigned ws = FreeImage_GetWidth(src_dib);
unsigned hs = FreeImage_GetHeight(src_dib);
int bpp = FreeImage_GetBPP(src_dib);
int btpp = bpp/8;
unsigned wpls = FreeImage_GetPitch(src_dib);
unsigned wpld = CalculatePitch(CalculateLine(wd, bpp == 1 ? 8 : bpp));
int color_mode = FreeImage_GetColorType(src_dib);
unsigned wpld_big = CalculatePitch(CalculateLine(OffscreenSize.x,
bpp == 1 ? 8 : bpp));
unsigned dif2_w = (wpld_big - wpld)/2;
// center the bitmap horizontally (on the background bitmap)
pBits += dif2_w - dif2_w % 3; // why is it needed here to align
// the image at the 3-byte boundary ???
// center the bitmap vertically (on the background bitmap)
pBits += (OffscreenSize.y - hd)/2 * wpld_big;
// the source row corresponding to dest row i ==> srow[i]
// the source col corresponding to dest col j ==> scol[j]
if ((srow = (long *)calloc(hd, sizeof(long))) == NULL)
return false;
if ((scol = (long *)calloc(wd, sizeof(long))) == NULL)
return false;
wratio = (double)ws / (double)wd;
hratio = (double)hs / (double)hd;
for (i = 0; i < hd; i++)
srow[i] = MIN<unsigned>((unsigned)(hratio * (double)i + 0.5), hs - 1);
for (j = 0; j < wd; j++)
scol[j] = MIN<unsigned>((unsigned)(wratio * (double)j + 0.5), ws - 1);
prevlines = NULL;
for (i = 0; i < hd; i++)
{
lines = datas + srow[i] * wpls;
lined = pBits + i * wpld_big;
if (lines != prevlines)
{ // make dest from new source row
prevxs = -1;
sval = 0;
csval = 0;
switch (bpp)
{
case 1:
for (j = 0; j < wd; j++)
{
xs = scol[j];
if (xs != prevxs)
{ // get dest pix from source col
Get1BitPixel(lines, xs, color_mode, &sval);
sval = dst_pal[sval].rgbRed;
lined[j] = sval;
prevxs = xs;
}
else // copy prev dest pix
lined[j] = sval;
}
break;
case 8:
for (j = 0; j < wd; j++)
{
xs = scol[j];
if (xs != prevxs)
{ // get dest pix from source col
sval = dst_pal[*(lines + xs)].rgbRed;
lined[j] = sval;
prevxs = xs;
}
else // copy prev dest pix
lined[j] = sval;
}
break;
case 24:
for (j = 0; j < wd; j++)
{
xs = scol[j];
if (xs != prevxs)
{ // get dest pix from source col
memcpy((char *)sval4, (char *)(lines + xs*btpp), 3);
memcpy((char *)(lined + j*btpp), (char *)sval4, 3);
prevxs = xs;
}
else // copy prev dest pix
memcpy((char *)(lined + j*btpp), (char *)sval4, 3);
}
break;
default:
return false;
break;
}
}
else
{ // lines == prevlines; copy prev dest row
prevlined = lined - wpld_big;
memcpy((char *)lined, (char *)prevlined, wpld);
}
prevlines = lines;
}
free(srow);
free(scol);
return true;
}
//////////////////////////////////////////////////////////////////////
/*------------------------------------------------------------------*
* Color, grayscale and binary downsampling with (antialias) smoothing *
*------------------------------------------------------------------*/
/*!
* scaleSmoothLow()
*
* Notes:
* (1) This function is called on 1-, 8- or 24 bpp src and dest images.
* (2) size is the full width of the lowpass smoothing filter.
* It is correlated with the reduction ratio, being the
* nearest integer such that size is approximately equal to hs / hd.
*
*/
BOOL LeptonicaScaleSmoothLow(FIBITMAP* src_dib,
unsigned wd, unsigned hd, CPoint OffscreenSize,
unsigned size, BYTE* pBits,
RGBQUAD *dst_pal)
{
unsigned i, j, m, n, xstart;
unsigned val, rval, gval, bval;
unsigned *srow, *scol;
BYTE *lines, *lined, *line, *mpixels, sval;
BYTE* pixel;
double wratio, hratio, norm;
BYTE *datas = FreeImage_GetBits(src_dib);
unsigned ws = FreeImage_GetWidth(src_dib);
unsigned hs = FreeImage_GetHeight(src_dib);
int bpp = FreeImage_GetBPP(src_dib);
int btpp = bpp/8;
unsigned wpls = FreeImage_GetPitch(src_dib);
unsigned wpld = CalculatePitch(CalculateLine(wd, bpp == 1 ? 8 : bpp));
int color_mode = FreeImage_GetColorType(src_dib);
unsigned wpld_big = CalculatePitch(CalculateLine(OffscreenSize.x,
bpp == 1 ? 8 : bpp));
unsigned dif2_w = (wpld_big - wpld)/2;
// center the bitmap horizontally (on the background bitmap)
pBits += dif2_w - dif2_w % 3; // why is it needed here to align
// the image at the 3-byte boundary ???
// center the bitmap vertically (on the background bitmap)
pBits += (OffscreenSize.y - hd)/2 * wpld_big;
// Each dest pixel at (j,i) is computed as the average
// of size^2 corresponding src pixels.
// We store the UL corner location of the square of
// src pixels that correspond to dest pixel (j,i).
// The are labelled by the arrays srow[i] and scol[j].
if ((srow = (unsigned *)calloc(hd, sizeof(unsigned))) == NULL)
return false;
if ((scol = (unsigned *)calloc(wd, sizeof(unsigned))) == NULL)
return false;
norm = 1. / (double)(size * size);
wratio = (double)ws / (double)wd;
hratio = (double)hs / (double)hd;
for (i = 0; i < hd; i++)
srow[i] = MIN<unsigned>((unsigned)(hratio * i + 0.5), hs - size);
for (j = 0; j < wd; j++)
scol[j] = MIN<unsigned>((unsigned)(wratio * j + 0.5), ws - size);
// For each dest pixel, compute average
switch (bpp)
{
case 1:
for (i = 0; i < hd; i++)
{
lines = datas + srow[i] * wpls;
lined = pBits + i * wpld_big;
for (j = 0; j < wd; j++)
{
xstart = scol[j];
val = 0;
for (m = 0; m < size; m++)
{
line = lines + m * wpls;
for (n = 0; n < size; n++)
{
Get1BitPixel(line, n + xstart, color_mode, &sval);
val += dst_pal[sval].rgbRed;
}
}
val = (unsigned)((double)val * norm);
lined[j] = val;
}
}
break;
case 8:
for (i = 0; i < hd; i++)
{
lines = datas + srow[i] * wpls;
lined = pBits + i * wpld_big;
for (j = 0; j < wd; j++)
{
xstart = scol[j];
val = 0;
for (m = 0; m < size; m++)
{
line = lines + m * wpls;
for (n = 0; n < size; n++)
{
val += dst_pal[*(line + xstart + n)].rgbRed;
}
}
val = (unsigned)((double)val * norm);
lined[j] = val;
}
}
break;
case 24:
for (i = 0; i < hd; i++)
{
lines = datas + srow[i] * wpls;
lined = pBits + i * wpld_big;
for (j = 0; j < wd; j++)
{
xstart = scol[j]*btpp;
rval = gval = bval = 0;
for (m = 0; m < size; m++)
{
mpixels = lines + m * wpls + xstart;
for (n = 0; n < size; n++)
{
pixel = (mpixels + n*btpp);
rval += pixel[FI_RGBA_RED];
gval += pixel[FI_RGBA_GREEN];
bval += pixel[FI_RGBA_BLUE];
}
}
rval = (unsigned)((double)rval * norm);
gval = (unsigned)((double)gval * norm);
bval = (unsigned)((double)bval * norm);
(lined + j*btpp)[FI_RGBA_RED] = rval;
(lined + j*btpp)[FI_RGBA_GREEN] = gval;
(lined + j*btpp)[FI_RGBA_BLUE] = bval;
}
}
}
free(srow);
free(scol);
return true;
}
/////////////////////////////////////////////////////////////////////
/*------------------------------------------------------------------*
* General linear interpolated color, binary and gray scaling *
*------------------------------------------------------------------*/
/*!
* scaleGrayLILow() and scaleColorLILow() - united together
*
* We choose to divide each pixel into 16 x 16 sub-pixels.
* Linear interpolation is equivalent to finding the
* fractional area (i.e., number of sub-pixels divided
* by 256) associated with each of the four nearest src pixels,
* and weighting each pixel value by this fractional area.
*/
BOOL LeptonicaScaleLILow(FIBITMAP* src_dib,
unsigned wd, unsigned hd, CPoint OffscreenSize,
BYTE *pBits, RGBQUAD *dst_pal, BOOL bInterpolate)
{
long i, j, wm2, hm2;
long xpm, ypm; // location in src image, to 1/16 of a pixel
long xp, yp, xf, yf; // src pixel and pixel fraction coordinates
long v00, v01, v10, v11;
BYTE val;
BYTE *lines, *lined;
double scx, scy;
BYTE *datas = FreeImage_GetBits(src_dib);
long ws = FreeImage_GetWidth(src_dib);
long hs = FreeImage_GetHeight(src_dib);
long bpp = FreeImage_GetBPP(src_dib);
int btpp = bpp/8;
long wpls = FreeImage_GetPitch(src_dib);
long wpld = CalculatePitch(CalculateLine(wd, bpp == 1 ? 8 : bpp));
int color_mode = FreeImage_GetColorType(src_dib);
unsigned wpld_big = CalculatePitch(CalculateLine(OffscreenSize.x,
bpp == 1 ? 8 : bpp));
unsigned dif2_w = (wpld_big - wpld)/2;
// center the bitmap horizontally (on the background bitmap)
pBits += dif2_w - dif2_w % 3; // why is it needed here to align
// the image at the 3-byte boundary ???
// center the bitmap vertically (on the background bitmap)
pBits += (OffscreenSize.y - hd)/2 * wpld_big;
// (scx, scy) are scaling factors that are applied to the
// dest coords to get the corresponding src coords.
// We need them because we iterate over dest pixels
// and must find the corresponding set of src pixels.
scx = 16. * (double)ws / (double)wd;
scy = 16. * (double)hs / (double)hd;
wm2 = ws - 2;
hm2 = hs - 2;
switch (bpp)
{
case 1:
// iterate over the destination pixels
for (i = 0; i < hd; i++)
{
ypm = (long)(scy * (double)i + 0.5);
yp = ypm >> 4;
yf = ypm & 0x0f;
lined = pBits + i * wpld_big;
lines = datas + yp * wpls;
for (j = 0; j < wd; j++)
{
xpm = (long)(scx * (double)j + 0.5);
xp = xpm >> 4;
xf = xpm & 0x0f;
// if near the edge, just use the src pixel value
if (xp > wm2 || yp > hm2)
{
Get1BitPixel(lines, xp, color_mode, &val);
lined[j] = dst_pal[val].rgbRed;
continue;
}
if (!bInterpolate)
{// simply subsample (which is faster but gives lousy results!)
Get1BitPixel(lines, xp, color_mode, &val);
lined[j] = dst_pal[val].rgbRed;
}
else
{ //do bilinear interpolation
Get1BitPixel(lines, xp, color_mode, &val);
v00 = (16 - xf) * (16 - yf) * dst_pal[val].rgbRed;
Get1BitPixel(lines, xp + 1, color_mode, &val);
v10 = xf * (16 - yf) * dst_pal[val].rgbRed;
Get1BitPixel(lines + wpls, + xp, color_mode, &val);
v01 = (16 - xf) * yf * dst_pal[val].rgbRed;
Get1BitPixel(lines + wpls, xp + 1, color_mode, &val);
v11 = xf * yf * dst_pal[val].rgbRed;
val = (BYTE)((v00 + v01 + v10 + v11 + 128) / 256);
lined[j] = val;
}
}
}
break;
case 8:
// iterate over the destination pixels
for (i = 0; i < hd; i++)
{
ypm = (long)(scy * (double)i + 0.5);
yp = ypm >> 4;
yf = ypm & 0x0f;
lined = pBits + i * wpld_big;
lines = datas + yp * wpls;
for (j = 0; j < wd; j++)
{
xpm = (long)(scx * (double)j + 0.5);
xp = xpm >> 4;
xf = xpm & 0x0f;
// if near the edge, just use the src pixel value
if (xp > wm2 || yp > hm2)
{
lined[j] = dst_pal[*(lines + xp)].rgbRed;
continue;
}
if (!bInterpolate)
{// simply subsample (which is faster but gives lousy results!)
lined[j] = dst_pal[*(lines + xp)].rgbRed;
}
else
{ //do bilinear interpolation
v00 = (16 - xf) * (16 - yf) * dst_pal[*(lines + xp)].rgbRed;
v10 = xf * (16 - yf) * dst_pal[*(lines + xp + 1)].rgbRed;
v01 = (16 - xf) * yf * dst_pal[*(lines + wpls + xp)].rgbRed;
v11 = xf * yf * dst_pal[*(lines + wpls + xp + 1)].rgbRed;
val = (BYTE)((v00 + v01 + v10 + v11 + 128) / 256);
lined[j] = val;
}
}
}
break;
case 24:
long v00r, v01r, v10r, v11r, v00g, v01g, v10g, v11g;
long v00b, v01b, v10b, v11b, area00, area01, area10, area11;
BYTE *pixels1, *pixels2, *pixels3, *pixels4;
// iterate over the destination pixels
for (i = 0; i < hd; i++)
{
ypm = (long)(scy * (double)i + 0.5);
yp = ypm >> 4;
yf = ypm & 0x0f;
lined = pBits + i * wpld_big;
lines = datas + yp * wpls;
for (j = 0; j < wd; j++)
{
xpm = (long)(scx * (double)j + 0.5);
xp = xpm >> 4;
xf = xpm & 0x0f;
// if near the edge, just use the src pixel value
if (xp > wm2 || yp > hm2)
{
(lined + j*btpp)[FI_RGBA_RED] = (lines + xp*btpp)[FI_RGBA_RED];
(lined + j*btpp)[FI_RGBA_GREEN] = (lines + xp*btpp)[FI_RGBA_GREEN];
(lined + j*btpp)[FI_RGBA_BLUE] = (lines + xp*btpp)[FI_RGBA_BLUE];
continue;
}
if (!bInterpolate) // simply subsample (which is faster but gives lousy results!)
{
(lined + j*btpp)[FI_RGBA_RED] = (lines + xp*btpp)[FI_RGBA_RED];
(lined + j*btpp)[FI_RGBA_GREEN] = (lines + xp*btpp)[FI_RGBA_GREEN];
(lined + j*btpp)[FI_RGBA_BLUE] = (lines + xp*btpp)[FI_RGBA_BLUE];
}
else
{
// do bilinear interpolation. This is a simple
// generalization of the calculation in scaleGrayLILow()
pixels1 = lines + xp*btpp;
pixels2 = lines + xp*btpp + 1;
pixels3 = lines + wpls + xp*btpp;
pixels4 = lines + wpls + xp*btpp + 1;
area00 = (16 - xf) * (16 - yf);
area10 = xf * (16 - yf);
area01 = (16 - xf) * yf;
area11 = xf * yf;
v00r = area00 * pixels1[FI_RGBA_RED];
v00g = area00 * pixels1[FI_RGBA_GREEN];
v00b = area00 * pixels1[FI_RGBA_BLUE];
v10r = area10 * pixels1[FI_RGBA_RED];
v10g = area10 * pixels1[FI_RGBA_GREEN];
v10b = area10 * pixels1[FI_RGBA_BLUE];
v01r = area01 * pixels1[FI_RGBA_RED];
v01g = area01 * pixels1[FI_RGBA_GREEN];
v01b = area01 * pixels1[FI_RGBA_BLUE];
v11r = area11 * pixels1[FI_RGBA_RED];
v11g = area11 * pixels1[FI_RGBA_GREEN];
v11b = area11 * pixels1[FI_RGBA_BLUE];
(lined + j*btpp)[FI_RGBA_RED] = (BYTE)((v00r + v10r + v01r + v11r + 128) / 256);
(lined + j*btpp)[FI_RGBA_GREEN] = (BYTE)((v00g + v10g + v01g + v11g + 128) / 256);
(lined + j*btpp)[FI_RGBA_BLUE] = (BYTE)((v00b + v10b + v01b + v11b + 128) / 256);
}
}
}
}
return true;
}