/*
------------------------------------------------------------------------------
denef - Decode NEF image files
Copyright (C) 2000 Daniel Stephens (daniel@cheeseplant.org)

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.
------------------------------------------------------------------------------
*/

#include <iostream.h>
#include <math.h>
#include <stdlib.h>

#include "interp.h"

static const char *rcsid="$Id: interp_rgb.cc,v 1.3 2000/09/18 07:51:43 daniel Exp $";

// Very naive interpolator, simply interpolates red, green, and blue values
// independently and sums them together (A great example of how NOT to do it)

// Define the region we're willing to interpolate in a single pass (used to
// determine how large of a 'workspace' area to allocate.

static const int zoneWidth  = 256;
static const int zoneHeight = 256;
static const int zoneSize = zoneWidth * zoneHeight;

rgbInterpolator::rgbInterpolator()
{
    tmpGreen = new double[zoneSize];
    tmpRed = new double[zoneSize];
    tmpBlue = new double[zoneSize];
}

rgbInterpolator::~rgbInterpolator()
{
    delete [] tmpGreen;
    delete [] tmpRed;
    delete [] tmpBlue;
}

int
rgbInterpolator::Fringe() const
{
    return 2;
}

int
rgbInterpolator::Max_Width() const
{
    return zoneWidth - 4;
}

int
rgbInterpolator::Max_Height() const
{
    return zoneHeight - 4;
}

void
rgbInterpolator::Interpolate(const nefImageData &nef,
				   int xofs, int yofs,
				   int width, int height,
				   fullImageData &result,
				   int rxofs, int ryofs)
{
    /* This function takes a chunk of the NEF data and creates the decoded
       image from it.

       Basic independent R,G,B interpolator
    */

    // Interpolate 'intermediate' red and blue values
    
    for (int y=-2; y<(height+2); ++y) {
	const short *pn  = nef.Data(xofs, yofs + y - 1);
	const short *p   = nef.Data(xofs, yofs + y    );
	const short *ps  = nef.Data(xofs, yofs + y + 1);

	int ofs = 0;
	if (y & 1) {ofs=1; ++pn; ++p; ++ps;}

	for (int x=ofs-2; x<(width+2); x+=2, pn+=2, p+=2, ps+=2) {
	    double Y;
	    double gh = fabs(pn[-1]-ps[ 1]);
	    double gv = fabs(pn[ 1]-ps[-1]);

	    if (gh > 2.0 * gv) {
		Y=(double)(pn[ 1]+ps[-1]) * 0.5;
	    } else if (gv > 2.0 * gh) {
		Y=(double)(pn[-1]+ps[ 1]) * 0.5;
	    } else {
		Y=(double)(pn[-1]+pn[1]+ps[-1]+ps[1]) * 0.25;
	    }

	    Y /=  (double)nef.Max_Value();

	    if (y & 1) {
		tmpRed[(y+2)*zoneWidth + (x+2)] = (double)*p  / (double)nef.Max_Value();
		tmpBlue[(y+2)*zoneWidth + (x+2)] = Y;
	    } else {
		tmpRed[(y+2)*zoneWidth + (x+2)] = Y;
		tmpBlue[(y+2)*zoneWidth + (x+2)] = (double)*p  / (double)nef.Max_Value();
	    }
	}
    }

    // Calculate rgb for each pixel

    for (int y=0; y<height; ++y) {
	const short *pn  = nef.Data(xofs, yofs + y - 1);
	const short *p   = nef.Data(xofs, yofs + y    );
	const short *ps  = nef.Data(xofs, yofs + y + 1);

	int ofs = (y+2)*zoneWidth + 2;

	bool green_pixel = (y & 1);

	for (int x=0; x<width;
	     ++x, ++pn, ++p, ++ps, ++ofs, green_pixel = !green_pixel) {
	    double R,G,B;
	    
	    if (green_pixel) {
		G=(double)*p;

		double gh = fabs(tmpRed[ofs-1]-tmpRed[ofs+1]);
		double gv = fabs(tmpRed[ofs-zoneWidth]-tmpRed[ofs+zoneWidth]);

		if (gh > 2.0 * gv) {
		    R=(tmpRed[ofs-zoneWidth]+tmpRed[ofs+zoneWidth]) * 0.5;
		} else if (gv > 2.0 * gh) {
		    R=(tmpRed[ofs-1]+tmpRed[ofs+1]) * 0.5;
		} else {
		    R=(tmpRed[ofs-1] + tmpRed[ofs+1] +
		       tmpRed[ofs-zoneWidth] + tmpRed[ofs+zoneWidth]) * 0.25;
		}

		gh = fabs(tmpBlue[ofs-1]-tmpBlue[ofs+1]);
		gv = fabs(tmpBlue[ofs-zoneWidth]-tmpBlue[ofs+zoneWidth]);

		if (gh > 2.0 * gv) {
		    B=(tmpBlue[ofs-zoneWidth]+tmpBlue[ofs+zoneWidth]) * 0.5;
		} else if (gv > 2.0 * gh) {
		    B=(tmpBlue[ofs-1]+tmpBlue[ofs+1]) * 0.5;
		} else {
		    B=(tmpBlue[ofs-1] + tmpBlue[ofs+1] +
		       tmpBlue[ofs-zoneWidth] + tmpBlue[ofs+zoneWidth]) * 0.25;
		}
	    } else {
		double gh = fabs(p[-1]-p[1]);
		double gv = fabs(pn[0]-ps[0]);

		if (gh > 2.0 * gv) {
		    G=(double)(pn[0]+ps[0]) * 0.5;
		} else if (gv > 2.0 * gh) {
		    G=(double)(p[1]+p[-1]) * 0.5;
		} else {
		    G=(double)(pn[0]+p[1]+p[-1]+ps[0]) * 0.25;
		}

		R = tmpRed[ofs];
		B = tmpBlue[ofs];
	    }

	    G /= (double)nef.Max_Value();

	    if (R < 0) {R = 0;}
	    if (G < 0) {G = 0;}
	    if (B < 0) {B = 0;}

	    rgbTriple &res = result(rxofs+x, ryofs+y);

	    res.r = R;
	    res.g = G;
	    res.b = B;
	}
    }
}

