// FreeImage Display Demo v4.0
// Copyright (C) 2008 monday2000@yandex.ru
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// http://www.gnu.org/copyleft/gpl.html
//
//
// Uses pnmscalefixed scaling algorithm from netpbm project
// Copyright (C) 1989, 1991 by Jef Poskanzer
//
//
// This code is taken (and then adopted) from the WinDjView Project
// "Scaling.cpp" file
//
// Copyright (C) 2004-2006 Andrew Zhezherun
#include "stdafx.h"
#include "FreeImage.h"
#include "Utilities.h"
#include "Resize2.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
#define FULLSCALE 4096
#define SCALESHIFT 12
#define HALFSCALE 2048
void horizontal_scale(const BYTE* inputxelrow, BYTE* newxelrow, unsigned cols, unsigned newcols, unsigned sxscale, int btpp)
{
// Take the input row inputxelrow[], which is 'cols' columns wide, and
// scale it by a factor of 'sxcale', which is in SCALEths to create
// the output row newxelrow[], which is 'newcols' columns wide.
//
// 'format' and 'maxval' describe the Netpbm format of the both input and
// output rows.
//
// *stretchP is the number of columns (could be fractional) on the right
// that we had to fill by stretching due to rounding problems.
unsigned r = 0, g = 0, b = 0;
unsigned newcol = 0;
unsigned fraccoltofill = FULLSCALE; // Output column is "empty" now
const BYTE* piP = inputxelrow;
BYTE* piN = newxelrow;
for (unsigned col = 0; col < cols; ++col)
{
// Process one pixel from input ('inputxelrow')
unsigned fraccolleft = sxscale;
// Output all columns, if any, that can be filled using information
// from this input column, in addition what's already in the output column.
while (fraccolleft >= fraccoltofill)
{
// Generate one output pixel in 'newxelrow'. It will consist
// of anything accumulated from prior input pixels in 'r','g',
// and 'b', plus a fraction of the current input pixel.
r += fraccoltofill * piP[FI_RGBA_RED];
g += fraccoltofill * piP[FI_RGBA_GREEN];
b += fraccoltofill * piP[FI_RGBA_BLUE];
piN[FI_RGBA_RED] = (BYTE) (r >> SCALESHIFT);
piN[FI_RGBA_GREEN] = (BYTE) (g >> SCALESHIFT);
piN[FI_RGBA_BLUE] = (BYTE) (b >> SCALESHIFT);
r = g = b = 0;
// Set up to start filling next output column
piN += btpp;
++newcol;
fraccolleft -= fraccoltofill;
fraccoltofill = FULLSCALE;
}
// There's not enough left in the current input pixel to fill up
// a whole output column, so just accumulate the remainder of the
// pixel into the current output column.
if (fraccolleft > 0)
{
r += fraccolleft * piP[FI_RGBA_RED];
g += fraccolleft * piP[FI_RGBA_GREEN];
b += fraccolleft * piP[FI_RGBA_BLUE];
fraccoltofill -= fraccolleft;
}
piP += btpp;
}
while (newcol < newcols)
{
// We ran out of input columns before we filled up the output
// columns. This would be because of rounding down. For small
// images, we're probably missing only a tiny fraction of a column,
// but for large images, it could be multiple columns.
// So we fake the remaining output columns by copying the rightmost
// legitimate pixel. We call this stretching.
r += fraccoltofill * (piP - 1)[FI_RGBA_RED] + HALFSCALE; // for rounding
g += fraccoltofill * (piP - 1)[FI_RGBA_GREEN] + HALFSCALE; // for rounding
b += fraccoltofill * (piP - 1)[FI_RGBA_BLUE] + HALFSCALE; // for rounding
piN[FI_RGBA_RED] = (BYTE) (r >>= SCALESHIFT);
piN[FI_RGBA_GREEN] = (BYTE) (g >>= SCALESHIFT);
piN[FI_RGBA_BLUE] = (BYTE) (b >>= SCALESHIFT);
piN += btpp;
++newcol;
fraccoltofill = FULLSCALE;
}
}
/////////////////////////////////////////////
BOOL RescalePnm(FIBITMAP* src_dib, unsigned nWidth, unsigned nHeight, CPoint OffscreenSize, BYTE* pBits)
{
int bpp = FreeImage_GetBPP(src_dib);
int btpp = bpp/8; // bytes per pixel
unsigned cols = FreeImage_GetWidth(src_dib);
unsigned rows = FreeImage_GetHeight(src_dib);
unsigned newcols = nWidth;
unsigned newrows = nHeight;
// The number of rows we had to fill by stretching because of
// rounding error, which made us run out of input rows before we
// had filled up the output rows.
// We round the scale factor down so that we never fill up the
// output while (a fractional pixel of) input remains unused.
// Instead, we will run out of input while some of the output is
// unfilled. We can address that by stretching, whereas the other
// case would require throwing away some of the input.
unsigned sxscale = FULLSCALE * newcols / cols;
unsigned syscale = FULLSCALE * newrows / rows;
BYTE* tempxelrow = (BYTE*) malloc(CalculateLine(cols, bpp));
unsigned int* rs = (unsigned*) malloc(cols * sizeof(unsigned) * 3);
unsigned int* gs = rs + cols;
unsigned int* bs = gs + cols;
unsigned wpld = CalculatePitch(CalculateLine(nWidth, 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 - nHeight)/2 * wpld_big;
if (tempxelrow == NULL || rs == NULL)
{
if (tempxelrow != NULL)
free(tempxelrow);
if (rs != NULL)
free(rs);
return false;
}
unsigned rowsread = 0;
unsigned fracrowleft = syscale;
unsigned needtoreadrow = 1;
unsigned fracrowtofill = FULLSCALE;
for (unsigned col = 0; col < cols; ++col)
rs[col] = gs[col] = bs[col] = HALFSCALE;
int vertical_stretch = 0;
BYTE* xelrow = 0;
BYTE* xP;
BYTE* nxP;
for (unsigned row = 0; row < newrows; ++row)
{
// First scale vertically from xelrow into tempxelrow.
while (fracrowleft < fracrowtofill)
{
if (needtoreadrow)
{
if (rowsread < rows)
{
xelrow = FreeImage_GetScanLine(src_dib, rowsread);
++rowsread;
}
}
unsigned* prs = rs;
unsigned* pgs = gs;
unsigned* pbs = bs;
unsigned col;
for (col = 0, xP = xelrow; col < cols; ++col)
{
*prs++ += fracrowleft * xP[FI_RGBA_RED];
*pgs++ += fracrowleft * xP[FI_RGBA_GREEN];
*pbs++ += fracrowleft * xP[FI_RGBA_BLUE];
xP += btpp;
}
fracrowtofill -= fracrowleft;
fracrowleft = syscale;
needtoreadrow = 1;
}
// Now fracrowleft is >= fracrowtofill, so we can produce a row.
if (needtoreadrow)
{
if (rowsread < rows)
{
xelrow = FreeImage_GetScanLine(src_dib, rowsread);
++rowsread;
needtoreadrow = 0;
}
else
{
// We need another input row to fill up this output row,
// but there aren't any more. That's because of rounding
// down on our scaling arithmetic. So we go ahead with
// the data from the last row we read, which amounts to
// stretching out the last output row.
vertical_stretch += fracrowtofill;
}
}
unsigned* prs = rs;
unsigned* pgs = gs;
unsigned* pbs = bs;
unsigned col;
for (col = 0, xP = xelrow, nxP = tempxelrow; col < cols; ++col)
{
unsigned r = *prs + fracrowtofill * xP[FI_RGBA_RED];
unsigned g = *pgs + fracrowtofill * xP[FI_RGBA_GREEN];
unsigned b = *pbs + fracrowtofill * xP[FI_RGBA_BLUE];
nxP[FI_RGBA_RED] = (BYTE) (r >> SCALESHIFT);
nxP[FI_RGBA_GREEN] = (BYTE) (g >> SCALESHIFT);
nxP[FI_RGBA_BLUE] = (BYTE) (b >> SCALESHIFT);
*prs++ = HALFSCALE;
*pgs++ = HALFSCALE;
*pbs++ = HALFSCALE;
xP += btpp;
nxP += btpp;
}
fracrowleft -= fracrowtofill;
if (fracrowleft == 0)
{
fracrowleft = syscale;
needtoreadrow = 1;
}
fracrowtofill = FULLSCALE;
horizontal_scale(tempxelrow, FI_GetScanLine(pBits, wpld_big, row), cols, newcols, sxscale, btpp);
}
free(tempxelrow);
free(rs);
return true;
}
/////////////////////////////////////////////
BOOL RescalePnm8(FIBITMAP* src_dib, unsigned nWidth, unsigned nHeight,
CPoint OffscreenSize, BYTE* pBits, RGBQUAD *dst_pal)
{
int bpp = FreeImage_GetBPP(src_dib);
int btpp = bpp/8; // bytes per pixel
unsigned cols = FreeImage_GetWidth(src_dib);
unsigned rows = FreeImage_GetHeight(src_dib);
unsigned newcols = nWidth;
unsigned newrows = nHeight;
// The number of rows we had to fill by stretching because of
// rounding error, which made us run out of input rows before we
// had filled up the output rows.
// We round the scale factor down so that we never fill up the
// output while (a fractional pixel of) input remains unused.
// Instead, we will run out of input while some of the output is
// unfilled. We can address that by stretching, whereas the other
// case would require throwing away some of the input.
unsigned sxscale = FULLSCALE * newcols / cols;
unsigned syscale = FULLSCALE * newrows / rows;
BYTE* tempxelrow = (BYTE*) malloc(cols * sizeof(BYTE));
unsigned* rs = (unsigned*) malloc(cols * sizeof(unsigned));
unsigned wpld = CalculatePitch(CalculateLine(nWidth, 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 - nHeight)/2 * wpld_big;
if (tempxelrow == NULL || rs == NULL)
{
if (tempxelrow != NULL)
free(tempxelrow);
if (rs != NULL)
free(rs);
return false;
}
unsigned rowsread = 0;
unsigned fracrowleft = syscale;
unsigned needtoreadrow = 1;
unsigned fracrowtofill = FULLSCALE;
for (unsigned col = 0; col < cols; ++col)
rs[col] = HALFSCALE;
int vertical_stretch = 0;
BYTE* xelrow = 0;
BYTE* xP;
BYTE* nxP;
BYTE val;
for (unsigned row = 0; row < newrows; ++row)
{
// First scale vertically from xelrow into tempxelrow.
while (fracrowleft < fracrowtofill) // e.g. if minification
{
if (needtoreadrow)
{
if (rowsread < rows)
{
xelrow = FreeImage_GetScanLine(src_dib, rowsread);
++rowsread;
}
}
unsigned* prs = rs;
unsigned col;
for (col = 0, xP = xelrow; col < cols; ++col, ++xP)
{
if (bpp == 1)
{
Get1BitPixel(xelrow, col, color_mode, &val);
*prs += fracrowleft * dst_pal[val].rgbRed;
}
else
*prs += fracrowleft * dst_pal[*xP].rgbRed;
++prs;
}
fracrowtofill -= fracrowleft;
fracrowleft = syscale;
needtoreadrow = 1;
}
// Now fracrowleft is >= fracrowtofill, so we can produce a row.
if (needtoreadrow)
{
if (rowsread < rows)
{
xelrow = FreeImage_GetScanLine(src_dib, rowsread);
++rowsread;
needtoreadrow = 0;
}
else
{
// We need another input row to fill up this output row,
// but there aren't any more. That's because of rounding
// down on our scaling arithmetic. So we go ahead with
// the data from the last row we read, which amounts to
// stretching out the last output row.
vertical_stretch += fracrowtofill;
}
}
unsigned* prs = rs;
unsigned col;
for (col = 0, xP = xelrow, nxP = tempxelrow; col < cols; ++col, ++xP, ++nxP, ++prs)
{
unsigned r;
if (bpp == 1)
{
Get1BitPixel(xelrow, col, color_mode, &val);
r = *prs + fracrowtofill * dst_pal[val].rgbRed;
}
else
r = *prs + fracrowtofill * dst_pal[*xP].rgbRed;
*nxP = (BYTE) (r >> SCALESHIFT);
*prs = HALFSCALE;
}
fracrowleft -= fracrowtofill;
if (fracrowleft == 0)
{
fracrowleft = syscale;
needtoreadrow = 1;
}
fracrowtofill = FULLSCALE;
horizontal_scale8(tempxelrow, FI_GetScanLine(pBits, wpld_big, row), cols, newcols, sxscale);
}
free(tempxelrow);
free(rs);
return true;
}
//////////////////////////////////////////////////////////
void horizontal_scale8(const BYTE* inputxelrow, BYTE* newxelrow, unsigned cols, unsigned newcols, unsigned sxscale)
{
// Take the input row inputxelrow[], which is 'cols' columns wide, and
// scale it by a factor of 'sxcale', which is in SCALEths to create
// the output row newxelrow[], which is 'newcols' columns wide.
//
// 'format' and 'maxval' describe the Netpbm format of the both input and
// output rows.
//
// *stretchP is the number of columns (could be fractional) on the right
// that we had to fill by stretching due to rounding problems.
unsigned newcol = 0;
unsigned fraccoltofill = FULLSCALE;
unsigned r = 0;
const BYTE* piP = inputxelrow;
BYTE* piN = newxelrow;
for (unsigned col = 0; col < cols; ++col, ++piP)
{
// Process one pixel from input ('inputxelrow')
unsigned fraccolleft = sxscale;
// Output all columns, if any, that can be filled using information
// from this input column, in addition what's already in the output column.
while (fraccolleft >= fraccoltofill)
{
// Generate one output pixel in 'newxelrow'. It will consist
// of anything accumulated from prior input pixels
// plus a fraction of the current input pixel.
r += fraccoltofill * (*piP);
*piN = (BYTE) (r >> SCALESHIFT);
r = 0;
// Set up to start filling next output column
++piN;
++newcol;
fraccolleft -= fraccoltofill;
fraccoltofill = FULLSCALE;
}
// There's not enough left in the current input pixel to fill up
// a whole output column, so just accumulate the remainder of the
// pixel into the current output column.
if (fraccolleft > 0)
{
r += fraccolleft * (*piP);
fraccoltofill -= fraccolleft;
}
}
while (newcol < newcols)
{
// We ran out of input columns before we filled up the output
// columns. This would be because of rounding down. For small
// images, we're probably missing only a tiny fraction of a column,
// but for large images, it could be multiple columns.
// So we fake the remaining output columns by copying the rightmost
// legitimate pixel. We call this stretching.
r += fraccoltofill * (*(piP - 1)) + HALFSCALE;
*piN = (BYTE) (r >> SCALESHIFT);
++piN;
++newcol;
fraccoltofill = FULLSCALE;
}
}
/////////////////////////////////////////////
BYTE* FI_GetScanLine(BYTE* pBits, unsigned pitch, int scanline)
{
return CalculateScanLine(pBits, pitch, scanline);
}
//////////////////////////////////////////////////////////
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;
}