|
|
|
/****************************************************************************
|
|
|
|
**
|
|
|
|
** Copyright (C) 2015 Timothy Pearson <kb9vqf@pearsoncomputing.net>
|
|
|
|
**
|
|
|
|
** This file is part of TDE.
|
|
|
|
**
|
|
|
|
** 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; see the file COPYING. If not, write to
|
|
|
|
** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
|
|
** Boston, MA 02110-1301, USA.
|
|
|
|
**
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#include <tqimage.h>
|
|
|
|
|
|
|
|
/*!
|
|
|
|
|
|
|
|
Smooth scaling function with ability to limit scaled region
|
|
|
|
The selection rectangle is given in terms of destination coordinates
|
|
|
|
It leaves areas outside of the selection rectangle undefined...
|
|
|
|
|
|
|
|
Function code originally taken from qimage.cpp pnmscale () and modified
|
|
|
|
to only scale a section of the source
|
|
|
|
|
|
|
|
This function uses code based on pnmscale.c by Jef Poskanzer.
|
|
|
|
|
|
|
|
pnmscale.c - read a portable anymap and scale it
|
|
|
|
|
|
|
|
\legalese
|
|
|
|
|
|
|
|
Copyright (C) 1989, 1991 by Jef Poskanzer.
|
|
|
|
|
|
|
|
Permission to use, copy, modify, and distribute this software and
|
|
|
|
its documentation for any purpose and without fee is hereby
|
|
|
|
granted, provided that the above copyright notice appear in all
|
|
|
|
copies and that both that copyright notice and this permission
|
|
|
|
notice appear in supporting documentation. This software is
|
|
|
|
provided "as is" without express or implied warranty.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
void pnmscale_fractional(const TQImage& src, TQImage& dst, int x, int y, int w, int h)
|
|
|
|
{
|
|
|
|
TQRgb* xelrow = 0;
|
|
|
|
TQRgb* tempxelrow = 0;
|
|
|
|
TQRgb* xP;
|
|
|
|
TQRgb* nxP;
|
|
|
|
int rows, cols, rowsread, newrows, newcols;
|
|
|
|
int row, col, needtoreadrow;
|
|
|
|
const uchar maxval = 255;
|
|
|
|
double xscale, yscale;
|
|
|
|
long sxscale, syscale;
|
|
|
|
long fracrowtofill, fracrowleft;
|
|
|
|
long* as;
|
|
|
|
long* rs;
|
|
|
|
long* gs;
|
|
|
|
long* bs;
|
|
|
|
int rowswritten = 0;
|
|
|
|
int colswritten = 0;
|
|
|
|
|
|
|
|
cols = src.width();
|
|
|
|
rows = src.height();
|
|
|
|
newcols = dst.width();
|
|
|
|
newrows = dst.height();
|
|
|
|
|
|
|
|
long SCALE;
|
|
|
|
long HALFSCALE;
|
|
|
|
|
|
|
|
if (cols > 4096)
|
|
|
|
{
|
|
|
|
SCALE = 4096;
|
|
|
|
HALFSCALE = 2048;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int fac = 4096;
|
|
|
|
|
|
|
|
while (cols * fac > 4096)
|
|
|
|
{
|
|
|
|
fac /= 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
SCALE = fac * cols;
|
|
|
|
HALFSCALE = fac * cols / 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
xscale = (double) newcols / (double) cols;
|
|
|
|
yscale = (double) newrows / (double) rows;
|
|
|
|
|
|
|
|
sxscale = (long)(xscale * SCALE);
|
|
|
|
syscale = (long)(yscale * SCALE);
|
|
|
|
|
|
|
|
if ( newrows != rows ) /* shortcut Y scaling if possible */
|
|
|
|
tempxelrow = new TQRgb[cols];
|
|
|
|
|
|
|
|
if ( src.hasAlphaBuffer() ) {
|
|
|
|
dst.setAlphaBuffer(TRUE);
|
|
|
|
as = new long[cols];
|
|
|
|
for ( col = 0; col < cols; ++col )
|
|
|
|
as[col] = HALFSCALE;
|
|
|
|
} else {
|
|
|
|
as = 0;
|
|
|
|
}
|
|
|
|
rs = new long[cols];
|
|
|
|
gs = new long[cols];
|
|
|
|
bs = new long[cols];
|
|
|
|
rowsread = 0;
|
|
|
|
fracrowleft = syscale;
|
|
|
|
needtoreadrow = 1;
|
|
|
|
for ( col = 0; col < cols; ++col )
|
|
|
|
rs[col] = gs[col] = bs[col] = HALFSCALE;
|
|
|
|
fracrowtofill = SCALE;
|
|
|
|
|
|
|
|
for ( row = 0; row < newrows; ++row ) {
|
|
|
|
/* First scale Y from xelrow into tempxelrow. */
|
|
|
|
if ( newrows == rows ) {
|
|
|
|
/* shortcut Y scaling if possible */
|
|
|
|
tempxelrow = xelrow = (TQRgb*)src.scanLine(rowsread++);
|
|
|
|
} else {
|
|
|
|
while ( fracrowleft < fracrowtofill ) {
|
|
|
|
if ( needtoreadrow && rowsread < rows )
|
|
|
|
xelrow = (TQRgb*)src.scanLine(rowsread++);
|
|
|
|
for ( col = 0, xP = xelrow; col < cols; ++col, ++xP ) {
|
|
|
|
if ((rowswritten >= y) && (rowswritten <= (y + h))) {
|
|
|
|
if (as) {
|
|
|
|
as[col] += fracrowleft * tqAlpha( *xP );
|
|
|
|
rs[col] += fracrowleft * tqRed( *xP ) * tqAlpha( *xP ) / 255;
|
|
|
|
gs[col] += fracrowleft * tqGreen( *xP ) * tqAlpha( *xP ) / 255;
|
|
|
|
bs[col] += fracrowleft * tqBlue( *xP ) * tqAlpha( *xP ) / 255;
|
|
|
|
} else {
|
|
|
|
rs[col] += fracrowleft * tqRed( *xP );
|
|
|
|
gs[col] += fracrowleft * tqGreen( *xP );
|
|
|
|
bs[col] += fracrowleft * tqBlue( *xP );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fracrowtofill -= fracrowleft;
|
|
|
|
fracrowleft = syscale;
|
|
|
|
needtoreadrow = 1;
|
|
|
|
}
|
|
|
|
/* Now fracrowleft is >= fracrowtofill, so we can produce a row. */
|
|
|
|
if ( needtoreadrow && rowsread < rows ) {
|
|
|
|
xelrow = (TQRgb*)src.scanLine(rowsread++);
|
|
|
|
needtoreadrow = 0;
|
|
|
|
}
|
|
|
|
long a=0;
|
|
|
|
for ( col = 0, xP = xelrow, nxP = tempxelrow, colswritten = 0;
|
|
|
|
col < cols; ++col, ++xP, ++nxP, ++colswritten )
|
|
|
|
{
|
|
|
|
if ((rowswritten >= y) && (rowswritten <= (y + h))) {
|
|
|
|
long r, g, b;
|
|
|
|
|
|
|
|
if ( as ) {
|
|
|
|
r = rs[col] + fracrowtofill * tqRed( *xP ) * tqAlpha( *xP ) / 255;
|
|
|
|
g = gs[col] + fracrowtofill * tqGreen( *xP ) * tqAlpha( *xP ) / 255;
|
|
|
|
b = bs[col] + fracrowtofill * tqBlue( *xP ) * tqAlpha( *xP ) / 255;
|
|
|
|
a = as[col] + fracrowtofill * tqAlpha( *xP );
|
|
|
|
if ( a ) {
|
|
|
|
r = r * 255 / a * SCALE;
|
|
|
|
g = g * 255 / a * SCALE;
|
|
|
|
b = b * 255 / a * SCALE;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
r = rs[col] + fracrowtofill * tqRed( *xP );
|
|
|
|
g = gs[col] + fracrowtofill * tqGreen( *xP );
|
|
|
|
b = bs[col] + fracrowtofill * tqBlue( *xP );
|
|
|
|
}
|
|
|
|
r /= SCALE;
|
|
|
|
if ( r > maxval ) r = maxval;
|
|
|
|
g /= SCALE;
|
|
|
|
if ( g > maxval ) g = maxval;
|
|
|
|
b /= SCALE;
|
|
|
|
if ( b > maxval ) b = maxval;
|
|
|
|
if ( as ) {
|
|
|
|
a /= SCALE;
|
|
|
|
if ( a > maxval ) a = maxval;
|
|
|
|
*nxP = tqRgba( (int)r, (int)g, (int)b, (int)a );
|
|
|
|
as[col] = HALFSCALE;
|
|
|
|
} else {
|
|
|
|
*nxP = tqRgb( (int)r, (int)g, (int)b );
|
|
|
|
}
|
|
|
|
rs[col] = gs[col] = bs[col] = HALFSCALE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fracrowleft -= fracrowtofill;
|
|
|
|
if ( fracrowleft == 0 ) {
|
|
|
|
fracrowleft = syscale;
|
|
|
|
needtoreadrow = 1;
|
|
|
|
}
|
|
|
|
fracrowtofill = SCALE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Now scale X from tempxelrow into dst and write it out. */
|
|
|
|
if ( newcols == cols ) {
|
|
|
|
/* shortcut X scaling if possible */
|
|
|
|
memcpy(dst.scanLine(rowswritten++), tempxelrow, newcols*4);
|
|
|
|
} else {
|
|
|
|
long a, r, g, b;
|
|
|
|
long fraccoltofill, fraccolleft = 0;
|
|
|
|
int needcol;
|
|
|
|
|
|
|
|
nxP = (TQRgb*)dst.scanLine(rowswritten++);
|
|
|
|
colswritten = 0;
|
|
|
|
fraccoltofill = SCALE;
|
|
|
|
a = r = g = b = HALFSCALE;
|
|
|
|
needcol = 0;
|
|
|
|
for ( col = 0, xP = tempxelrow; col < cols; ++col, ++xP ) {
|
|
|
|
fraccolleft = sxscale;
|
|
|
|
while ( fraccolleft >= fraccoltofill ) {
|
|
|
|
if ( needcol ) {
|
|
|
|
++nxP;
|
|
|
|
++colswritten;
|
|
|
|
a = r = g = b = HALFSCALE;
|
|
|
|
}
|
|
|
|
if ((colswritten >= x) && (colswritten <= (x + w)) && (rowswritten >= y) && (rowswritten <= (y + h))) {
|
|
|
|
if ( as ) {
|
|
|
|
r += fraccoltofill * tqRed( *xP ) * tqAlpha( *xP ) / 255;
|
|
|
|
g += fraccoltofill * tqGreen( *xP ) * tqAlpha( *xP ) / 255;
|
|
|
|
b += fraccoltofill * tqBlue( *xP ) * tqAlpha( *xP ) / 255;
|
|
|
|
a += fraccoltofill * tqAlpha( *xP );
|
|
|
|
if ( a ) {
|
|
|
|
r = r * 255 / a * SCALE;
|
|
|
|
g = g * 255 / a * SCALE;
|
|
|
|
b = b * 255 / a * SCALE;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
r += fraccoltofill * tqRed( *xP );
|
|
|
|
g += fraccoltofill * tqGreen( *xP );
|
|
|
|
b += fraccoltofill * tqBlue( *xP );
|
|
|
|
}
|
|
|
|
r /= SCALE;
|
|
|
|
if ( r > maxval ) r = maxval;
|
|
|
|
g /= SCALE;
|
|
|
|
if ( g > maxval ) g = maxval;
|
|
|
|
b /= SCALE;
|
|
|
|
if ( b > maxval ) b = maxval;
|
|
|
|
if (as) {
|
|
|
|
a /= SCALE;
|
|
|
|
if ( a > maxval ) a = maxval;
|
|
|
|
*nxP = tqRgba( (int)r, (int)g, (int)b, (int)a );
|
|
|
|
} else {
|
|
|
|
*nxP = tqRgb( (int)r, (int)g, (int)b );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fraccolleft -= fraccoltofill;
|
|
|
|
fraccoltofill = SCALE;
|
|
|
|
needcol = 1;
|
|
|
|
}
|
|
|
|
if ( fraccolleft > 0 ) {
|
|
|
|
if ( needcol ) {
|
|
|
|
++nxP;
|
|
|
|
++colswritten;
|
|
|
|
a = r = g = b = HALFSCALE;
|
|
|
|
needcol = 0;
|
|
|
|
}
|
|
|
|
if ((rowswritten >= y) && (rowswritten <= (y + h))) {
|
|
|
|
if (as) {
|
|
|
|
a += fraccolleft * tqAlpha( *xP );
|
|
|
|
r += fraccolleft * tqRed( *xP ) * tqAlpha( *xP ) / 255;
|
|
|
|
g += fraccolleft * tqGreen( *xP ) * tqAlpha( *xP ) / 255;
|
|
|
|
b += fraccolleft * tqBlue( *xP ) * tqAlpha( *xP ) / 255;
|
|
|
|
} else {
|
|
|
|
r += fraccolleft * tqRed( *xP );
|
|
|
|
g += fraccolleft * tqGreen( *xP );
|
|
|
|
b += fraccolleft * tqBlue( *xP );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fraccoltofill -= fraccolleft;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( fraccoltofill > 0 ) {
|
|
|
|
--xP;
|
|
|
|
if ((rowswritten >= y) && (rowswritten <= (y + h))) {
|
|
|
|
if (as) {
|
|
|
|
a += fraccolleft * tqAlpha( *xP );
|
|
|
|
r += fraccoltofill * tqRed( *xP ) * tqAlpha( *xP ) / 255;
|
|
|
|
g += fraccoltofill * tqGreen( *xP ) * tqAlpha( *xP ) / 255;
|
|
|
|
b += fraccoltofill * tqBlue( *xP ) * tqAlpha( *xP ) / 255;
|
|
|
|
if ( a ) {
|
|
|
|
r = r * 255 / a * SCALE;
|
|
|
|
g = g * 255 / a * SCALE;
|
|
|
|
b = b * 255 / a * SCALE;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
r += fraccoltofill * tqRed( *xP );
|
|
|
|
g += fraccoltofill * tqGreen( *xP );
|
|
|
|
b += fraccoltofill * tqBlue( *xP );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( ! needcol ) {
|
|
|
|
if ((rowswritten >= y) && (rowswritten <= (y + h))) {
|
|
|
|
r /= SCALE;
|
|
|
|
if ( r > maxval ) r = maxval;
|
|
|
|
g /= SCALE;
|
|
|
|
if ( g > maxval ) g = maxval;
|
|
|
|
b /= SCALE;
|
|
|
|
if ( b > maxval ) b = maxval;
|
|
|
|
if (as) {
|
|
|
|
a /= SCALE;
|
|
|
|
if ( a > maxval ) a = maxval;
|
|
|
|
*nxP = tqRgba( (int)r, (int)g, (int)b, (int)a );
|
|
|
|
} else {
|
|
|
|
*nxP = tqRgb( (int)r, (int)g, (int)b );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( newrows != rows && tempxelrow )// Robust, tempxelrow might be 0 1 day
|
|
|
|
delete [] tempxelrow;
|
|
|
|
if ( as ) // Avoid purify complaint
|
|
|
|
delete [] as;
|
|
|
|
if ( rs ) // Robust, rs might be 0 one day
|
|
|
|
delete [] rs;
|
|
|
|
if ( gs ) // Robust, gs might be 0 one day
|
|
|
|
delete [] gs;
|
|
|
|
if ( bs ) // Robust, bs might be 0 one day
|
|
|
|
delete [] bs;
|
|
|
|
}
|