/*====================================================================*
- 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 v3.1
// 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"
#include "fi_testDoc.h"
/////////////////////////////////////////////////////////////////////
/*------------------------------------------------------------------*
* 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 CFi_testDoc::LeptonicaScaleBySamplingLow(FIBITMAP* src_dib,
unsigned wd, unsigned hd, CPoint OffscreenSize,
BYTE* pBits, RGBQUAD *dst_pal)
{
unsigned i, j;
unsigned xs, prevxs;
BYTE sval;
unsigned *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 + 6 - dif2_w % 6; // why is it needed here to align
// the image at the 6-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 = (unsigned *)calloc(hd, sizeof(unsigned))) == NULL)
return false;
if ((scol = (unsigned *)calloc(wd, sizeof(unsigned))) == 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 * i + 0.5), hs - 1);
for (j = 0; j < wd; j++)
scol[j] = MIN<unsigned>((unsigned)(wratio * 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 CFi_testDoc::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 + 6 - dif2_w % 6; // why is it needed here to align
// the image at the 6-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 CFi_testDoc::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 + 6 - dif2_w % 6; // why is it needed here to align
// the image at the 6-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;
}
/////////////////////////////////////////////////////////////////////
void CFi_testDoc::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;
}
/////////////////////////////////////////////////////////////////////