/**
 * libpsd - Photoshop file formats (*.psd) decode library
 * Copyright (C) 2004-2007 Graphest Software.
 *
 * libpsd is the legal property of its developers, whose names are too numerous
 * to list here.  Please refer to the COPYRIGHT file distributed with this
 * source distribution.
 *
 * This program 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 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 Library 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
 *
 * $Id: gradient_blend.c, created by Patrick in 2006.06.22, libpsd@graphest.com Exp $
 */

#include "libpsd.h"
#include "psd_system.h"
#include "psd_color.h"
#include "psd_fixed.h"
#include "psd_bitmap.h"
#include "psd_gradient.h"
#include "psd_math.h"


#define PSD_COLOR_SCALE				4


// from quake3 source code
psd_float psd_carm_sqrt(psd_float x)
{
	union
	{
		psd_int 	intPart;
		psd_float 	floatPart;
	} convertor;
	union
	{
		psd_int 	intPart;
		psd_float 	floatPart;
	} convertor2;
	
	convertor.floatPart = x;
	convertor2.floatPart = x;
	convertor.intPart = 0x1FBCF800 + (convertor.intPart >> 1);
	convertor2.intPart = 0x5f3759df - (convertor2.intPart >> 1);
	
	return 0.5f*(convertor.floatPart + (x * convertor2.floatPart));
}

void psd_gradient_color_get_table(psd_gradient_color * gradient_color, psd_argb_color * color_table, psd_int table_count, psd_bool reverse)
{
	psd_int i, j;
	psd_argb_color color;
	psd_int r1, r2, r3, g1, g2, g3, b1, b2, b3;
	psd_fixed_16_16 red, green, blue, red_step, green_step, blue_step;
	psd_int midpoint, location1, location2;
	psd_int o1, o2, o3;
	psd_uint high_opacity;
	psd_fixed_16_16 opacity, opacity_step;

	if(gradient_color == NULL)
		return;

	if(gradient_color->color_stop[0].location > 0)
	{
		location1 = 0;
		location2 = (gradient_color->color_stop[0].location * table_count + 2048) / 4096;
		psd_color_memset(color_table + location1, gradient_color->color_stop[0].actual_color,
			location2 - location1);
	}
	
	for(i = 0; i < gradient_color->number_color_stops - 1; i ++)
	{
		location1 = (gradient_color->color_stop[i].location * table_count + 2048) / 4096;
		location2 = (gradient_color->color_stop[i + 1].location * table_count + 2048) / 4096;
		if(gradient_color->color_stop[i].actual_color == gradient_color->color_stop[i + 1].actual_color)
		{
			psd_color_memset(color_table + location1, gradient_color->color_stop[i].actual_color,
				location2 - location1);
			continue;
		}
		
		color = gradient_color->color_stop[i].actual_color;
		r1 = psd_get_red_component(color);
		g1 = psd_get_green_component(color);
		b1 = psd_get_blue_component(color);
		color = gradient_color->color_stop[i + 1].actual_color;
		r3 = psd_get_red_component(color);
		g3 = psd_get_green_component(color);
		b3 = psd_get_blue_component(color);
		
		if(gradient_color->color_stop[i + 1].midpoint == 50)
		{
			red = PSD_FIXED_16_16_INT(r1);
			green = PSD_FIXED_16_16_INT(g1);
			blue = PSD_FIXED_16_16_INT(b1);
			red_step = PSD_FIXED_16_16_INT(r3 - r1) / (location2 - location1);
			green_step = PSD_FIXED_16_16_INT(g3 - g1) / (location2 - location1);
			blue_step = PSD_FIXED_16_16_INT(b3 - b1) / (location2 - location1);
			for(j = location1; j < location2; j ++)
			{
				color_table[j] = PSD_RGB_TO_COLOR(PSD_FIXED_16_16_ROUND(red), 
					PSD_FIXED_16_16_ROUND(green), PSD_FIXED_16_16_ROUND(blue));
				red += red_step;
				green += green_step;
				blue += blue_step;
			}
		}
		else
		{
			midpoint = location1 + (gradient_color->color_stop[i + 1].midpoint * 
				(location2 - location1) + 50) / 100;
			r2 = (r1 + r3) / 2;
			g2 = (g1 + g3) / 2;
			b2 = (b1 + b3) / 2;
			
			red = PSD_FIXED_16_16_INT(r1);
			green = PSD_FIXED_16_16_INT(g1);
			blue = PSD_FIXED_16_16_INT(b1);
			red_step = PSD_FIXED_16_16_INT(r2 - r1) / (midpoint - location1);
			green_step = PSD_FIXED_16_16_INT(g2 - g1) / (midpoint - location1);
			blue_step = PSD_FIXED_16_16_INT(b2 - b1) / (midpoint - location1);
			for(j = location1; j < midpoint; j ++)
			{
				color_table[j] = PSD_RGB_TO_COLOR(PSD_FIXED_16_16_ROUND(red), 
					PSD_FIXED_16_16_ROUND(green), PSD_FIXED_16_16_ROUND(blue));
				red += red_step;
				green += green_step;
				blue += blue_step;
			}

			red = PSD_FIXED_16_16_INT(r2);
			green = PSD_FIXED_16_16_INT(g2);
			blue = PSD_FIXED_16_16_INT(b2);
			red_step = PSD_FIXED_16_16_INT(r3 - r2) / (location2 - midpoint);
			green_step = PSD_FIXED_16_16_INT(g3 - g2) / (location2 - midpoint);
			blue_step = PSD_FIXED_16_16_INT(b3 - b2) / (location2 - midpoint);
			for(j = midpoint; j < location2; j ++)
			{
				color_table[j] = PSD_RGB_TO_COLOR(PSD_FIXED_16_16_ROUND(red), 
					PSD_FIXED_16_16_ROUND(green), PSD_FIXED_16_16_ROUND(blue));
				red += red_step;
				green += green_step;
				blue += blue_step;
			}
		}
	}

	if(gradient_color->color_stop[gradient_color->number_color_stops - 1].location < 4096)
	{
		location1 = (gradient_color->color_stop[gradient_color->number_color_stops - 1].location * table_count + 2048) / 4096;
		location2 = table_count;
		psd_color_memset(color_table + location1, gradient_color->color_stop[gradient_color->number_color_stops - 1].actual_color,
			location2 - location1);
	}

	if(gradient_color->transparency_stop[0].location > 0)
	{
		if(gradient_color->transparency_stop[0].opacity != 100)
		{
			location1 = 0;
			location2 = (gradient_color->transparency_stop[0].location * table_count + 2048) / 4096;
			high_opacity = (gradient_color->transparency_stop[0].opacity * 255 + 50) / 100;
			high_opacity <<= 24;
			for(j = location1; j < location2; j ++)
			{
				color_table[j] = (color_table[j] & 0x00FFFFFF) | high_opacity;
			}
		}
	}
	
	for(i = 0; i < gradient_color->number_transparency_stops - 1; i ++)
	{
		if(gradient_color->transparency_stop[i].opacity == 100 && 
			gradient_color->transparency_stop[i + 1].opacity == 100)
		{
			continue;
		}

		location1 = (gradient_color->transparency_stop[i].location * table_count + 2048) / 4096;
		location2 = (gradient_color->transparency_stop[i + 1].location * table_count + 2048) / 4096;
		
		if(gradient_color->transparency_stop[i].opacity == 
			gradient_color->transparency_stop[i + 1].opacity)
		{
			high_opacity = (gradient_color->transparency_stop[i].opacity * 255 + 50) / 100;
			high_opacity <<= 24;
			for(j = location1; j < location2; j ++)
			{
				color_table[j] = (color_table[j] & 0x00FFFFFF) | high_opacity;
			}
		}
		else
		{
			o1 = (gradient_color->transparency_stop[i].opacity * 255 + 50) / 100;
			o3 = (gradient_color->transparency_stop[i + 1].opacity * 255 + 50) / 100;

			if(gradient_color->transparency_stop[i + 1].midpoint == 50)
			{
				opacity = PSD_FIXED_16_16_INT(o1);
				opacity_step = PSD_FIXED_16_16_INT(o3 - o1) / (location2 - location1);
				for(j = location1; j < location2 ; j ++)
				{
					color_table[j] = (color_table[j] & 0x00FFFFFF) | 
						(PSD_FIXED_16_16_ROUND(opacity) << 24);
					opacity += opacity_step;
				}
			}
			else
			{
				o2 = (o1 + o3) / 2;
				midpoint = location1 + (gradient_color->transparency_stop[i + 1].midpoint * 
					(location2 - location1) + 50) / 100;
				
				opacity = PSD_FIXED_16_16_INT(o1);
				opacity_step = PSD_FIXED_16_16_INT(o2 - o1) / (midpoint - location1);
				for(j = location1; j < midpoint; j ++)
				{
					color_table[j] = (color_table[j] & 0x00FFFFFF) | 
						(PSD_FIXED_16_16_ROUND(opacity) << 24);
					opacity += opacity_step;
				}

				opacity = PSD_FIXED_16_16_INT(o2);
				opacity_step = PSD_FIXED_16_16_INT(o3 - o2) / (location2 - midpoint);
				for(j = midpoint; j < location2; j ++)
				{
					color_table[j] = (color_table[j] & 0x00FFFFFF) | 
						(PSD_FIXED_16_16_ROUND(opacity) << 24);
					opacity += opacity_step;
				}
			}
		}
	}

	if(gradient_color->transparency_stop[gradient_color->number_transparency_stops - 1].location < 4096)
	{
		if(gradient_color->transparency_stop[gradient_color->number_transparency_stops - 1].opacity != 100)
		{
			location1 = (gradient_color->transparency_stop[gradient_color->number_transparency_stops - 1].location * table_count + 2048) / 4096;
			location2 = table_count;
			high_opacity = (gradient_color->transparency_stop[gradient_color->number_transparency_stops - 1].opacity * 255 + 50) / 100;
			high_opacity <<= 24;
			for(j = location1; j < location2; j ++)
			{
				color_table[j] = (color_table[j] & 0x00FFFFFF) | high_opacity;
			}
		}
	}

	if(reverse == psd_true)
	{
		j = table_count / 2;
		for(i = 0; i < j; i ++)
		{
			color = color_table[i];
			color_table[i] = color_table[table_count - 1 - i];
			color_table[table_count - 1 - i] = color;
		}
	}
}

psd_status psd_gradient_fill_linear(psd_bitmap * bitmap, psd_gradient_color * gradient_color, psd_bool reverse, 
	psd_int start_x, psd_int start_y, psd_int end_x, psd_int end_y)
{
	psd_int distance, table_count;
	psd_argb_color * color_table;
	psd_int temp, i, j, width, height, index;
	psd_argb_color * dst_data, cur_color;
	psd_fixed_16_16 delta_x, delta_y, line_x, pixel_x;

	width = bitmap->width;
	height = bitmap->height;

	if(start_x == end_x)
	{
		table_count = PSD_ABS(end_y - start_y);
		color_table = (psd_argb_color *)psd_malloc(table_count * 4);
		if(color_table == NULL)
			return psd_status_malloc_failed;
		if(start_y > end_y)
		{
			temp = start_y;
			start_y = end_y;
			end_y = temp;
			reverse = psd_true - reverse;
		}
		psd_gradient_color_get_table(gradient_color, color_table, table_count, reverse);

		dst_data = bitmap->image_data;
		cur_color = color_table[0];
		for(i = 0; i < PSD_MIN(start_y, height); i ++)
		{
			psd_color_memset(dst_data, cur_color, width);
			dst_data += width;
		}

		for(i = PSD_MAX(start_y, 0); i < PSD_MIN(end_y, height); i ++)
		{
			cur_color = color_table[i - start_y];
			psd_color_memset(dst_data, cur_color, width);
			dst_data += width;
		}

		cur_color = color_table[table_count - 1];
		for(i = PSD_MAX(end_y, 0); i < height; i ++)
		{
			psd_color_memset(dst_data, cur_color, width);
			dst_data += width;
		}
	}
	
	else if(start_y == end_y)
	{
		table_count = PSD_ABS(end_x - start_x);
		color_table = (psd_argb_color *)psd_malloc(table_count * 4);
		if(color_table == NULL)
			return psd_status_malloc_failed;
		if(start_x > end_x)
		{
			temp = start_x;
			start_x = end_x;
			end_x = temp;
			reverse = psd_true - reverse;
		}
		psd_gradient_color_get_table(gradient_color, color_table, table_count, reverse);

		dst_data = bitmap->image_data;
		cur_color = color_table[0];
		for(i = 0; i < PSD_MIN(start_x, width); i ++)
		{
			*dst_data = cur_color;
			dst_data ++;
		}

		for(i = PSD_MAX(start_x, 0); i < PSD_MIN(end_x, width); i ++)
		{
			cur_color = color_table[i - start_x];
			*dst_data = cur_color;
			dst_data ++;
		}

		cur_color = color_table[table_count - 1];
		for(i = PSD_MAX(end_x, 0); i < width; i ++)
		{
			*dst_data = cur_color;
			dst_data ++;
		}

		for(i = 1; i < height; i ++)
		{
			memcpy(dst_data, bitmap->image_data, width * 4);
			dst_data += width;
		}
	}
	
	else
	{
		distance = (psd_int)psd_carm_sqrt((psd_float)((end_x - start_x) * (end_x - start_x) + (end_y - start_y) * (end_y - start_y)) + 0.5f);
		table_count = distance * PSD_COLOR_SCALE;
		color_table = (psd_argb_color *)psd_malloc(table_count * 4);
		if(color_table == NULL)
			return psd_status_malloc_failed;
		psd_gradient_color_get_table(gradient_color, color_table, table_count, reverse);

		delta_x = PSD_FIXED_16_16_INT(end_x - start_x) / distance;
		delta_y = PSD_FIXED_16_16_INT(end_y - start_y) / distance;
		line_x = -delta_y * start_y - delta_x * start_x;
		line_x *= PSD_COLOR_SCALE;
		delta_x *= PSD_COLOR_SCALE;
		delta_y *= PSD_COLOR_SCALE;
		dst_data = bitmap->image_data;
		for(i = 0; i < height; i ++)
		{
			pixel_x = line_x;
			for(j = 0; j < width; j ++)
			{
				index =  PSD_FIXED_16_16_ROUND(pixel_x);
				if(index <= 0)
					cur_color = color_table[0];
				else if(index >= table_count - 1)
					cur_color = color_table[table_count - 1];
				else
					cur_color = color_table[index];
				*dst_data = cur_color;
				dst_data ++;
				pixel_x += delta_x;
			}
			line_x += delta_y;
		}
	}
	
	psd_free(color_table);
	
	return psd_status_done;
}

psd_status psd_gradient_fill_radial(psd_bitmap * bitmap, psd_gradient_color * gradient_color, psd_bool reverse, 
	psd_int center_x, psd_int center_y, psd_int radius)
{
	psd_int width, height, left, top, right, bottom, i, j, ii, i2, j2;
	psd_int table_count, distance;
	psd_argb_color * color_table;
	psd_argb_color * dst_data, cur_color;

	width = bitmap->width;
	height = bitmap->height;
	left = center_x > radius ? center_x - radius : 0;
	top = center_y > radius ? center_y - radius : 0;
	right = center_x + radius < width ? center_x + radius : width;
	bottom = center_y + radius < height ? center_y + radius : height;

	if(center_x < width / 2)
		left = center_x - (right - center_x);
	else
		right = center_x + (center_x - left);
	if(center_y < height / 2)
		top = center_y - (bottom - center_y);
	else
		bottom = center_y + (center_y - top);

	table_count = radius * PSD_COLOR_SCALE;
	color_table = (psd_argb_color *)psd_malloc(table_count * 4);
	if(color_table == NULL)
		return psd_status_malloc_failed;
	psd_gradient_color_get_table(gradient_color, color_table, table_count, reverse);

	cur_color = color_table[table_count - 1];

	if(top >= 0)
	{
		dst_data = bitmap->image_data;
		for(i = 0; i <= PSD_MIN(top, height - 1); i ++)
		{
			psd_color_memset(dst_data, cur_color, width);
			dst_data += width;
		}
	}

	if(bottom < height)
	{
		dst_data = bitmap->image_data + PSD_MAX(bottom, 0) * width;
		for(i = PSD_MAX(bottom, 0); i < height; i ++)
		{
			psd_color_memset(dst_data, cur_color, width);
			dst_data += width;
		}
	}

	if(left >= 0)
	{
		dst_data = bitmap->image_data + PSD_MAX(top, 0) * width;
		for(i = PSD_MAX(top, 0); i < PSD_MIN(bottom, height); i ++)
		{
			psd_color_memset(dst_data, cur_color, PSD_MIN(left + 1, width));
			dst_data += width;
		}
	}

	if(right < width)
	{
		dst_data = bitmap->image_data + PSD_MAX(top, 0) * width + PSD_MAX(right, 0);
		for(i = PSD_MAX(top, 0); i < PSD_MIN(bottom, height); i ++)
		{
			psd_color_memset(dst_data, cur_color, PSD_MIN(width - right, width));
			dst_data += width;
		}
	}

	if(center_x >= 0 && center_x < width)
	{
		dst_data = bitmap->image_data + PSD_MAX(top + 1, 0) * width + center_x;
		for(i = PSD_MAX(top + 1, 0); i < PSD_MIN(bottom - 1, height); i ++)
		{
			distance = PSD_ABS(i - center_y) * PSD_COLOR_SCALE;
			cur_color = color_table[distance];
			*dst_data = cur_color;
			dst_data += width;
		}
	}

	if(center_y >= 0 && center_y < height)
	{
		dst_data = bitmap->image_data + center_y * width + PSD_MAX(left + 1, 0);
		for(i = PSD_MAX(left + 1, 0); i < PSD_MIN(right - 1, width); i ++)
		{
			distance = PSD_ABS(i - center_x) * PSD_COLOR_SCALE;
			cur_color = color_table[distance];
			*dst_data = cur_color;
			dst_data ++;
		}
	}

	for(i = top + 1; i < center_y; i ++)
	{
		ii = (center_y - i) * (center_y - i);
		i2 = center_y + (center_y - i);
		dst_data = bitmap->image_data + i * width + (left + 1);
		for(j = left + 1; j < center_x; j ++, dst_data ++)
		{
			distance = (psd_int)(psd_carm_sqrt((psd_float)(ii + (center_x - j) * (center_x - j))) * PSD_COLOR_SCALE + 0.5);
			if(distance >= table_count)
				cur_color = color_table[table_count - 1];
			else
				cur_color = color_table[distance];

			j2 = center_x + (center_x - j);
			if(i >= 0 && i < height)
			{
				if(j >= 0 && j < width)
					*dst_data = cur_color;
				if(j2 >= 0 && j2 < width)
					*(dst_data + (j2 - j)) = cur_color;
			}
			if(i2 >= 0 && i2 < height)
			{
				if(j >= 0 && j < width)
					*(dst_data + (i2 - i) * width) = cur_color;
				if(j2 >= 0 && j2 < width)
					*(dst_data + (i2 - i) * width + (j2 - j)) = cur_color;
			}
		}
	}

	psd_free(color_table);
	
	return psd_status_done;
}

psd_status psd_gradient_fill_angle(psd_bitmap * bitmap, psd_gradient_color * gradient_color, psd_bool reverse, 
	psd_int center_x, psd_int center_y, psd_int angle)
{
	psd_int left, top, width, height, i, j, i2, j2, dx, dy, tan_angle, cur_angle;
	psd_argb_color * color_table;
	psd_argb_color * dst_data, cur_color;

	width = bitmap->width;
	height = bitmap->height;
	left = center_x < width / 2 ? center_x - (width - center_x) : 0;
	top = center_y < height / 2 ? center_y - (height - center_y) : 0;

	color_table = (psd_argb_color *)psd_malloc(360 * 4);
	if(color_table == NULL)
		return psd_status_malloc_failed;
	psd_gradient_color_get_table(gradient_color, color_table, 360, (unsigned char)(psd_true - reverse));

	if(center_x >= 0 && center_x < width)
	{
		if(center_y > 0)
		{
			cur_angle = (90 - angle + 360) % 360;
			cur_color = color_table[cur_angle];
			dst_data = bitmap->image_data + center_x;
			for(i = 0; i < PSD_MIN(center_y, height); i ++)
			{
				*dst_data = cur_color;
				dst_data += width;
			}
		}
		if(center_y + 1 < height)
		{
			cur_angle = (270 - angle + 360) % 360;
			cur_color = color_table[cur_angle];
			dst_data = bitmap->image_data + PSD_MAX(center_y + 1, 0) * width + center_x;
			for(i = PSD_MAX(center_y + 1, 0); i < height; i ++)
			{
				*dst_data = cur_color;
				dst_data += width;
			}
		}
	}

	if(center_y >= 0 && center_y < height)
	{
		if(center_x > 0)
		{
			cur_angle = (180 - angle + 360) % 360;
			cur_color = color_table[cur_angle];
			dst_data = bitmap->image_data + center_y * width;
			psd_color_memset(dst_data, cur_color, PSD_MIN(center_x, width));
		}
		if(center_x + 1 < width)
		{
			cur_angle = (-angle + 360) % 360;
			cur_color = color_table[cur_angle];
			dst_data = bitmap->image_data + center_y * width + PSD_MAX(center_x + 1, 0);
			psd_color_memset(dst_data, cur_color, width - PSD_MAX(center_x + 1, 0));
		}
	}

	if(center_x >= 0 && center_x < width && center_y >= 0 && center_y < height)
	{
		dst_data = bitmap->image_data + center_y * width + center_x;
		*dst_data = color_table[0];
	}

	for(i = top; i < center_y; i ++)
	{
		i2 = center_y + (center_y - i);
		dy = center_y - i;
		dst_data = bitmap->image_data + i * width + left;
		for(j = left; j < center_x; j ++, dst_data ++)
		{
			j2 = center_x + (center_x - j);
			dx = center_x - j;
			if(dx >= dy)
				tan_angle = (dy * 45 + (dx >> 1)) / dx;
			else
				tan_angle = 90 - (dx * 45 + (dy >> 1)) / dy;
			tan_angle = PSD_CONSTRAIN(tan_angle, 1, 89);
			cur_angle = 90 - tan_angle - angle + 90;
			if(cur_angle < 0)
				cur_angle += 360;
			else if(cur_angle >= 360)
				cur_angle -= 360;

			if(j >= 0 && j < width)
			{
				if(i >= 0 && i < height)
					*dst_data = color_table[cur_angle];
				if(i2 >= 0 && i2 < height)
				{
					cur_angle += tan_angle * 2;
					if(cur_angle >= 360)
						cur_angle -= 360;
					*(dst_data + (i2 - i) * width) = color_table[cur_angle];
					cur_angle += 180 - tan_angle * 2;
					if(cur_angle >= 360)
						cur_angle -= 360;
				}
				else
				{
					cur_angle += 180;
					if(cur_angle >= 360)
						cur_angle -= 360;
				}
			}
			else
			{
				cur_angle += 180;
				if(cur_angle >= 360)
					cur_angle -= 360;
			}

			if(j2 >= 0 && j2 < width)
			{
				if(i2 >= 0 && i2 < height)
					*(dst_data + (i2 - i) * width + (j2 - j)) = color_table[cur_angle];
				if(i >= 0 && i < height)
				{
					cur_angle += tan_angle * 2;
					if(cur_angle >= 360)
						cur_angle -= 360;
					*(dst_data + (j2 - j)) = color_table[cur_angle];
				}
			}
		}
	}

	psd_free(color_table);
	
	return psd_status_done;
}

psd_status psd_gradient_fill_reflected(psd_bitmap * bitmap, psd_gradient_color * gradient_color, psd_bool reverse,
	psd_int start_x, psd_int start_y, psd_int end_x, psd_int end_y)
{
	psd_int distance, table_count;
	psd_argb_color * color_table;
	psd_int temp, i, j, width, height, index;
	psd_argb_color * dst_data, cur_color;
	psd_fixed_16_16 delta_x, delta_y, line_x, pixel_x;

	width = bitmap->width;
	height = bitmap->height;

	if(start_x == end_x)
	{
		table_count = PSD_ABS(end_y - start_y);
		color_table = (psd_argb_color *)psd_malloc(table_count * 4);
		if(color_table == NULL)
			return psd_status_malloc_failed;
		if(start_y > end_y)
		{
			temp = start_y;
			start_y = end_y;
			end_y = temp;
		}
		psd_gradient_color_get_table(gradient_color, color_table, table_count / 2, (unsigned char)(psd_true - reverse));
		psd_gradient_color_get_table(gradient_color, color_table + table_count / 2, table_count - table_count / 2, reverse);

		dst_data = bitmap->image_data;
		cur_color = color_table[0];
		for(i = 0; i < PSD_MIN(start_y, height); i ++)
		{
			psd_color_memset(dst_data, cur_color, width);
			dst_data += width;
		}

		for(i = PSD_MAX(start_y, 0); i < PSD_MIN(end_y, height); i ++)
		{
			cur_color = color_table[i - start_y];
			psd_color_memset(dst_data, cur_color, width);
			dst_data += width;
		}

		cur_color = color_table[table_count - 1];
		for(i = PSD_MAX(end_y, 0); i < height; i ++)
		{
			psd_color_memset(dst_data, cur_color, width);
			dst_data += width;
		}
	}
	
	else if(start_y == end_y)
	{
		table_count = PSD_ABS(end_x - start_x);
		color_table = (psd_argb_color *)psd_malloc(table_count * 4);
		if(color_table == NULL)
			return psd_status_malloc_failed;
		if(start_x > end_x)
		{
			temp = start_x;
			start_x = end_x;
			end_x = temp;
		}
		psd_gradient_color_get_table(gradient_color, color_table, table_count / 2, (unsigned char)(psd_true - reverse));
		psd_gradient_color_get_table(gradient_color, color_table + table_count / 2, table_count - table_count / 2, reverse);

		dst_data = bitmap->image_data;
		cur_color = color_table[0];
		for(i = 0; i < PSD_MIN(start_x, width); i ++)
		{
			*dst_data = cur_color;
			dst_data ++;
		}

		for(i = PSD_MAX(start_x, 0); i < PSD_MIN(end_x, width); i ++)
		{
			cur_color = color_table[i - start_x];
			*dst_data = cur_color;
			dst_data ++;
		}

		cur_color = color_table[table_count - 1];
		for(i = PSD_MAX(end_x, 0); i < width; i ++)
		{
			*dst_data = cur_color;
			dst_data ++;
		}

		for(i = 1; i < height; i ++)
		{
			memcpy(dst_data, bitmap->image_data, width * 4);
			dst_data += width;
		}
	}
	
	else
	{
		distance = (psd_int)psd_carm_sqrt((psd_float)((end_x - start_x) * (end_x - start_x) + (end_y - start_y) * (end_y - start_y)) + 0.5f);
		table_count = distance * PSD_COLOR_SCALE;
		color_table = (psd_argb_color *)psd_malloc(table_count * 4);
		if(color_table == NULL)
			return psd_status_malloc_failed;
		psd_gradient_color_get_table(gradient_color, color_table, table_count / 2, (unsigned char)(psd_true - reverse));
		psd_gradient_color_get_table(gradient_color, color_table + table_count / 2, table_count - table_count / 2, reverse);

		delta_x = PSD_FIXED_16_16_INT(end_x - start_x) / distance;
		delta_y = PSD_FIXED_16_16_INT(end_y - start_y) / distance;
		line_x = -delta_y * start_y - delta_x * start_x;
		line_x *= PSD_COLOR_SCALE;
		delta_x *= PSD_COLOR_SCALE;
		delta_y *= PSD_COLOR_SCALE;
		dst_data = bitmap->image_data;
		for(i = 0; i < height; i ++)
		{
			pixel_x = line_x;
			for(j = 0; j < width; j ++)
			{
				index =  PSD_FIXED_16_16_ROUND(pixel_x);
				if(index <= 0)
					cur_color = color_table[0];
				else if(index >= table_count - 1)
					cur_color = color_table[table_count - 1];
				else
					cur_color = color_table[index];
				*dst_data = cur_color;
				dst_data ++;
				pixel_x += delta_x;
			}
			line_x += delta_y;
		}
	}
	
	psd_free(color_table);
	
	return psd_status_done;
}

psd_status psd_gradient_fill_diamond(psd_bitmap * bitmap, psd_gradient_color * gradient_color, psd_bool reverse,
	psd_int center_x, psd_int center_y, psd_int radius, psd_int angle)
{
	psd_int width, height, i, j, dx, dy;
	psd_float ax, ay, xy;
	psd_int distance, table_count;
	psd_argb_color * color_table;
	psd_argb_color * dst_data, cur_color;
	psd_fixed_16_16 angle_sin, angle_cos;
	
	angle = (angle + 360) % 90;
	width = bitmap->width;
	height = bitmap->height;

	if(angle == 0)
	{
		table_count = radius;
		color_table = (psd_argb_color *)psd_malloc(table_count * 4);
		if(color_table == NULL)
			return psd_status_malloc_failed;
		psd_gradient_color_get_table(gradient_color, color_table, table_count, reverse);

		dst_data = bitmap->image_data;
		for(i = 0; i < height; i ++)
		{
			for(j = 0; j < width; j ++, dst_data ++)
			{
				distance = PSD_ABS(j - center_x) + PSD_ABS(i - center_y);
				if(distance >= table_count)
					cur_color = color_table[table_count - 1];
				else
					cur_color = color_table[distance];
				*dst_data = cur_color;
			}
		}
	}
	else
	{
		table_count = radius * PSD_COLOR_SCALE;
		color_table = (psd_argb_color *)psd_malloc(table_count * 4);
		if(color_table == NULL)
			return psd_status_malloc_failed;
		psd_gradient_color_get_table(gradient_color, color_table, table_count, reverse);

		if(angle <= 45)
		{
			ax = 1;
			ay = angle / 45.0f;
		}
		else
		{
			ay = 1;
			ax = (90 - angle) / 45.0f;
		}
		xy = psd_carm_sqrt(ax * ax + ay * ay);
		angle_sin = psd_fixed_16_16_float(ax / xy);
		angle_cos = psd_fixed_16_16_float(ay / xy);

		dst_data = bitmap->image_data;
		for(i = 0; i < height; i ++)
		{
			for(j = 0; j < width; j ++, dst_data ++)
			{
				dx = (j - center_x) * angle_cos + (i - center_y) * angle_sin;
				dy = -(j - center_x) * angle_sin + (i - center_y) * angle_cos;
				distance = PSD_FIXED_16_16_ROUND((PSD_ABS(dx) + PSD_ABS(dy)) * PSD_COLOR_SCALE);
				if(distance >= table_count)
					cur_color = color_table[table_count - 1];
				else
					cur_color = color_table[distance];
				*dst_data = cur_color;
			}
		}
	}
	
	psd_free(color_table);
	
	return psd_status_done;
}

