/* gradient.c - renders gradients
 *
 *  Raster graphics library
 *
 *  Copyright (c) 1997 Alfredo K. Kojima
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *  
 *  This library 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
 *  Library General Public License for more details.
 *  
 *  You should have received a copy of the GNU Library General Public
 *  License along with this library; if not, write to the Free
 *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "wraster.h"


RImage *renderHGradient(int width, int height, int r0, int g0, int b0, 
			int rf, int gf, int bf);
RImage *renderVGradient(int width, int height, int r0, int g0, int b0, 
			int rf, int gf, int bf);
RImage *renderDGradient(int width, int height, int r0, int g0, int b0, 
			int rf, int gf, int bf);


RImage*
RRenderGradient(int width, int height, RColor *from, RColor *to, int style)
{
    switch (style) {
     case WGRD_HORIZONTAL:
	return renderHGradient(width, height, from->red, from->green, 
			       from->blue, to->red, to->green, to->blue);
     case WGRD_VERTICAL:
	return renderVGradient(width, height, from->red, from->green, 
			       from->blue, to->red, to->green, to->blue);

     case WGRD_DIAGONAL:
	return renderDGradient(width, height, from->red, from->green, 
			       from->blue, to->red, to->green, to->blue);
    }
#ifdef DEBUG
    printf("invalid gradient style in RRenderGradient()");
#endif
    return NULL;
}


/*
 *----------------------------------------------------------------------
 * renderHGradient--
 * 	Renders a horizontal linear gradient of the specified size in the
 * RImage format with a border of the specified type. 
 * 
 * Returns:
 * 	A 24bit RImage with the gradient (no alpha channel).
 * 
 * Side effects:
 * 	None
 *---------------------------------------------------------------------- 
 */
RImage*
renderHGradient(int width, int height, int r0, int g0, int b0, 
		int rf, int gf, int bf)
{    
    int i;
    unsigned long r, g, b, dr, dg, db;
    RImage *image;
    unsigned char *rp, *gp, *bp;

    image = RCreateImage(width, height, False);
    if (!image) {
	return NULL;
    }
    rp = image->data[0];
    gp = image->data[1];
    bp = image->data[2];

    r = r0 << 16;
    g = g0 << 16;
    b = b0 << 16;
    
    dr = ((rf-r0)<<16)/width;
    dg = ((gf-g0)<<16)/width;
    db = ((bf-b0)<<16)/width;
    /* render the first line */
    for (i=0; i<width; i++) {
	*(rp++) = (unsigned char)(r>>16);
	*(gp++) = (unsigned char)(g>>16);
	*(bp++) = (unsigned char)(b>>16);
	r += dr;
	g += dg;
	b += db;
    }

    
    /* copy the first line to the other lines */
    for (i=1; i<height; i++) {
	memcpy(&(image->data[0][i*width]), image->data[0], width);
	memcpy(&(image->data[1][i*width]), image->data[1], width);
	memcpy(&(image->data[2][i*width]), image->data[2], width);
    }
    return image;
}

/*
 *----------------------------------------------------------------------
 * renderVGradient--
 *      Renders a vertical linear gradient of the specified size in the
 * RImage format with a border of the specified type.
 *
 * Returns:
 *      A 24bit RImage with the gradient (no alpha channel).
 *
 * Side effects:
 *      None
 *----------------------------------------------------------------------
 */
RImage*
renderVGradient(int width, int height, int r0, int g0, int b0,
		int rf, int gf, int bf)
{
    int i;
    unsigned long r, g, b, dr, dg, db;
    RImage *image;
    unsigned char *rp, *gp, *bp;

    image = RCreateImage(width, height, False);
    if (!image) {
	return NULL;
    }    
    rp = image->data[0];
    gp = image->data[1];
    bp = image->data[2];
    
    r = r0<<16;
    g = g0<<16;
    b = b0<<16;

    dr = ((rf-r0)<<16)/height;
    dg = ((gf-g0)<<16)/height;
    db = ((bf-b0)<<16)/height;

    for (i=0; i<height; i++) {
	memset(rp, (unsigned char)(r>>16), width);
	memset(gp, (unsigned char)(g>>16), width);
	memset(bp, (unsigned char)(b>>16), width);
	rp+=width;
	gp+=width;
	bp+=width;
        r+=dr;
        g+=dg;
        b+=db;
    }
    return image;
}


/*
 *----------------------------------------------------------------------
 * renderDGradient--
 *      Renders a diagonal linear gradient of the specified size in the
 * RImage format with a border of the specified type.
 *
 * Returns:
 *      A 24bit RImage with the gradient (no alpha channel).
 *
 * Side effects:
 *      None
 *----------------------------------------------------------------------
 */
RImage*
renderDGradient(int width, int height, int r0, int g0, int b0,
		int rf, int gf, int bf)
{
    int x, y;
    float from_red,from_green,from_blue;
    float to_red,to_green,to_blue;
    float dr,dg,db,dx,dy,w,h,xred,yred,xgreen,ygreen,xblue,yblue;
    RImage *image;
    unsigned char *rp, *gp, *bp;

    image = RCreateImage(width, height, False);
    if (!image) {
	return NULL;
    }
    rp = image->data[0];
    gp = image->data[1];
    bp = image->data[2];
    
    from_red = (float)r0;
    from_green = (float)g0;
    from_blue = (float)b0;
    
    to_red = (float)rf;
    to_green = (float)gf;
    to_blue = (float)bf;
    
    w = (float) width;
    h = (float) height;
    
    dr = (to_red - from_red);
    dg = (to_green - from_green);
    db = (to_blue - from_blue);
    
    for (y=0; y<height; y++) {
	
	dy = y / h;
	
	yred   = dr * dy + from_red;
	ygreen = dg * dy + from_green;
	yblue  = db * dy + from_blue;
	
	for (x=0; x<width; x++) { 
	    dx = x / w;
	    
	    xred   = dr * dx + from_red;
	    xgreen = dg * dx + from_green;
	    xblue  = db * dx + from_blue;
	    
	    *(rp++) = (unsigned char)((xred + yred)/2);
	    *(gp++) = (unsigned char)((xgreen + ygreen)/2);
	    *(bp++) = (unsigned char)((xblue + yblue)/2);
	}
    }
    return image;
}

