/**************************************************************************** ** ** Copyright (C) 2015 Timothy Pearson ** ** 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 /*! 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; register TQRgb* xP; register TQRgb* nxP; int rows, cols, rowsread, newrows, newcols; register int row, col, needtoreadrow; const uchar maxval = 255; double xscale, yscale; long sxscale, syscale; register 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; } register long a=0; for ( col = 0, xP = xelrow, nxP = tempxelrow, colswritten = 0; col < cols; ++col, ++xP, ++nxP, ++colswritten ) { if ((rowswritten >= y) && (rowswritten <= (y + h))) { register 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 { register long a, r, g, b; register long fraccoltofill, fraccolleft = 0; register 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; }