/****************************************************************************
**
** 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;
}
