/*
 * Copyright (c) 2004 Sasha Vasko <sasha at aftercode.net>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#define LOCAL_DEBUG

#undef DEBUG_ELLIPS

#ifdef _WIN32
#include "win32/config.h"
#else
#include "config.h"
#endif

/*#define LOCAL_DEBUG*/
/*#define DO_CLOCKING*/

#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#ifdef _WIN32
# include "win32/afterbase.h"
#else
# include "afterbase.h"
#endif
#include "asvisual.h"
#include "asimage.h"
#include "draw.h"


#define CTX_SELECT_CANVAS(ctx)	(get_flags((ctx)->flags, ASDrawCTX_UsingScratch)?(ctx)->scratch_canvas:(ctx)->canvas)

#define CTX_PUT_PIXEL(ctx,x,y,ratio) (ctx)->apply_tool_func(ctx,x,y,ratio)
#define CTX_FILL_HLINE(ctx,x_from,y,x_to,ratio) (ctx)->fill_hline_func(ctx,x_from,y,x_to,ratio)


#define CTX_DEFAULT_FILL_THRESHOLD	126
#define CTX_ELLIPS_FILL_THRESHOLD	140

CARD8 _round1x1[1] =
{  255 };

CARD8 _round3x3[9] =
{ 64,  80,  64,
  80, 255,  80,
  64,  80,  64 };


CARD8 _round5x5[25] =
{  0,  24,  64,  24,   0,
  24,  64,  80,  64,  24,
  64,  80, 255,  80,  64,
  24,  64,  80,  64,  24,
   0,  24,  64,  24,   0,};

CARD8 _round5x5_solid[25] =
{  0,  255,  255,  255,   0,
  255,  255, 255,  255,  255,
  255,  255, 255,  255,  255,
  255,  255, 255,  255,  255,
   0,  255,  255,  255,   0,};

ASDrawTool StandardBrushes[AS_DRAW_BRUSHES] = 
{
	{1, 1, 0, 0, _round1x1},
	{3, 3, 1, 1, _round3x3},
	{5, 5, 2, 2, _round5x5_solid}
};	 

/*********************************************************************************/
/* auxilary functions : 											 */
/*********************************************************************************/

static void
apply_tool_2D( ASDrawContext *ctx, int curr_x, int curr_y, CARD8 ratio )
{
	if( ratio  !=  0 ) 
	{	
		CARD8 *src = ctx->tool->matrix ;
		int corner_x = curr_x - ctx->tool->center_x ; 
		int corner_y = curr_y - ctx->tool->center_y ; 
		int tw = ctx->tool->width ;
		int th = ctx->tool->height ;
		int cw = ctx->canvas_width ;
		int ch = ctx->canvas_height ;
		int aw = tw ; 
		int ah = th ;
		CARD8 *dst = CTX_SELECT_CANVAS(ctx) ; 
		int x, y ;

		if( corner_x+tw <= 0 || corner_x >= cw || corner_y+th <= 0 || corner_y >= ch ) 
			return ;
		 
		if( corner_y > 0 ) 
			dst += corner_y * cw ;
		else if( corner_y < 0 ) 
		{
			ah -= -corner_y ;
			src += -corner_y * tw ;
		}

		if( corner_x  > 0 ) 
			dst += corner_x ;  
		else if( corner_x < 0 )
		{	
			src += -corner_x ; 
			aw -= -corner_x ;
		}
	
		if( corner_x + tw > cw ) 
			aw = cw - corner_x;

		if( corner_y + th > ch ) 
			ah = ch - corner_y;
		
		if( ratio == 255 ) 
		{
			for( y = 0 ; y < ah ; ++y ) 
			{	
				for( x = 0 ; x < aw ; ++x ) 
				{
					if( dst[x] < src[x] ) 
						dst[x] = src[x] ;
					/*
					register CARD32 t = (CARD32)dst[x] + (CARD32)src[x] ;
					dst[x] = t > 255? 255 : t ;
			 		*/
				}
				src += tw ; 
				dst += cw ; 
			}
		}else
		{		 
			for( y = 0 ; y < ah ; ++y ) 
			{	
				for( x = 0 ; x < aw ; ++x ) 
				{
					CARD32 v = ratio ;
					v = (v*src[x])/255 ;
					if( dst[x] < v ) 
						dst[x] = v ;
					/*
					register CARD32 t = (CARD32)dst[x] + (CARD32)src[x] ;
					dst[x] = t > 255? 255 : t ;
			 		*/
				}
				src += tw ; 
				dst += cw ; 
			}
		}
	}
}	   

static void
apply_tool_point( ASDrawContext *ctx, int curr_x, int curr_y, CARD8 ratio )
{
	int cw = ctx->canvas_width ;
	if( ratio != 0 && curr_x >= 0 && curr_x < cw && curr_y >= 0 && curr_y < ctx->canvas_height ) 
	{	
		CARD32 value = (ctx->tool->matrix[0]*(CARD32)ratio)/255 ;
		CARD8 *dst = CTX_SELECT_CANVAS(ctx) ;
		dst += curr_y * cw ; 

		if( dst[curr_x] < value ) 
			dst[curr_x] = value ;
	}
}	   


static void
fill_hline_notile( ASDrawContext *ctx, int x_from, int y, int x_to, CARD8 ratio )
{
	int cw = ctx->canvas_width ;
	if( ratio != 0 && x_to >= 0 && x_from < cw && y >= 0 && y < ctx->canvas_height ) 
	{	
		CARD32 value = ratio ;
		CARD8 *dst = CTX_SELECT_CANVAS(ctx) + y * cw ; 
		int x1 = x_from, x2 = x_to ; 
		if( x1 < 0 ) 
			x1 = 0 ; 
		if( x2 >= cw ) 
			x2 = cw - 1 ; 

		while( x1 <= x2 ) 
		{
			if( dst[x1] < value ) 
				dst[x1] = value ;
			++x1 ;
		}
	}
}


/*********************************************************************************/
/* drawing functions : 											 */
/*********************************************************************************/
static void
ctx_draw_line_solid( ASDrawContext *ctx, int from_x, int from_y, int to_x, int to_y )
{
	int x, y, end, dir = 1;
	int dx = to_x - from_x ; 
	int dy = to_y - from_y ; 
	if( dx < 0 ) 
		dx = -dx ;
	if( dy < 0 ) 
		dy = -dy ;
	
	if( dx >= dy ) 
	{	
      	int Dy = -dx + 2*dy;
      	int inct = 2*dy;
		int incf = 2*(dy - dx);
		
		if( to_y > from_y ) 
		{	x = from_x ; y = from_y ; end = to_x ; }
		else
		{	x = to_x ; y = to_y ; end = from_x ; 	}
		
		if( end < x ) 
			dir = -1 ;				 
      	
		CTX_PUT_PIXEL( ctx, x, y, 255 );
      	while(x != end)
		{
        	x += dir;
        	if(Dy > 0)
			{
          		Dy += incf;
          		++y;
        	}else 
				Dy += inct;
        	CTX_PUT_PIXEL( ctx, x, y, 255 );
      	}
	}else
	{
      	int Dx = -dy + 2*dx;
      	int inct = 2*dx;
		int incf = 2*(dx - dy);
		
		if( to_x > from_x ) 
		{	y = from_y ; x = from_x ; end = to_y ; }
		else
		{	y = to_y ; x = to_x ; end = from_y ; 	}
		
		if( end < y ) 
			dir = -1 ;				 
      	
		CTX_PUT_PIXEL( ctx, x, y, 255 );
      	while(y != end)
		{
        	y += dir;
        	if(Dx > 0)
			{
          		Dx += incf;
          		++x;
        	}else 
				Dx += inct;
        	CTX_PUT_PIXEL( ctx, x, y, 255 );
      	}
	}		 
}	 

static void
ctx_draw_line_solid_aa( ASDrawContext *ctx, int from_x, int from_y, int to_x, int to_y )
{
	int x, y, end, dir = 1;
	int dx = to_x - from_x ; 
	int dy = to_y - from_y ; 
	if( dx < 0 ) 
		dx = -dx ;
	if( dy < 0 ) 
		dy = -dy ;

	if( dx == 0 || dy == 0 ) 
	{	
		ctx_draw_line_solid( ctx, from_x, from_y, to_x, to_y );
		return ;
	}
	
	if( dx >= dy ) 
	{	
		int ratio2 = 0x007FFFFF/dx ; 
		CARD32 value = 0x003FFFFF; 
		CARD32 value_incr = ratio2*dy ; 
		int value_decr = (dx - dy)*ratio2 ;

		
		if( to_y > from_y ) 
		{	x = from_x ; y = from_y ; end = to_x ; }
		else
		{	x = to_x ; y = to_y ; end = from_x ; 	}
		
		if( end < x ) 	dir = -1 ;				 
      	
		CTX_PUT_PIXEL( ctx, x, y, 255 );
/*		LOCAL_DEBUG_OUT( "x = %d, y = %d, dir = %d, value = %d, incr = %d, decr = %d, ratio = %d, value_incr = %d", 
						 x, y, dir, value, incr, decr, ratio, value_incr );
  */			
      	while(x != end)
		{
        	x += dir;
        	if( (int)value > value_decr)
			{
				value = (int)value - value_decr ;
          		++y;
        	}else 
				value += value_incr ; 

			{
				register int above = (value&0x00FF0000)>>16;
				/* LOCAL_DEBUG_OUT( "x = %d, y = %d, value = %d, above = %d",  x, y, value, above ); */
				switch( (above>>5)&0x03 )
				{
					case 0 :  /* 0 - 32 */ 
						above = 128 - above ;
						CTX_PUT_PIXEL( ctx, x, y-1, above ) ;
						CTX_PUT_PIXEL( ctx, x, y, ~(above>>1) ) ; 
						break ;	  
					case 1 :  /* 32 - 64 */ 
						{
							int a1 = (above - 32) ;
							CTX_PUT_PIXEL( ctx, x, y+1, a1 ) ;
							above = (~above)&0x7f ;
							CTX_PUT_PIXEL( ctx, x, y-1, above - a1 ) ;
							CTX_PUT_PIXEL( ctx, x, y, 255 ) ; 
						}
						break ;
					case 2 :  /* 64 - 96 */  
						{
							int a1 = (96 - above) ;	  
							CTX_PUT_PIXEL( ctx, x, y-1, a1 ) ;
							CTX_PUT_PIXEL( ctx, x, y, 255 ) ; 
							CTX_PUT_PIXEL( ctx, x, y+1, above - a1 ) ;
						}
						break ;
					case 3 :  /* 96 - 128 */ 
						{
							above -= ((~above)&0x7f)>>1 ;	  
							CTX_PUT_PIXEL( ctx, x, y, ~(above>>1) ) ; 
							CTX_PUT_PIXEL( ctx, x, y+1, above ) ;
						}
						break ;
				}
			}
      	}
	}else
	{
		int ratio2 = 0x007FFFFF/dy ; 
		CARD32 value = 0x003FFFFF; 
		CARD32 value_incr = ratio2*dx ; 
		int value_decr = (dy - dx)*ratio2 ;

		
		if( to_x > from_x ) 
		{	y = from_y ; x = from_x ; end = to_y ; }
		else
		{	y = to_y ; x = to_x ; end = from_y ; 	}
		
		if( end < y ) 	dir = -1 ;				 
      	
		CTX_PUT_PIXEL( ctx, x, y, 255 );
/*		LOCAL_DEBUG_OUT( "x = %d, y = %d, dir = %d, value = %d, incr = %d, decr = %d, ratio = %d, value_incr = %d", 
						 x, y, dir, value, incr, decr, ratio, value_incr );
  */			
      	while(y != end)
		{
        	y += dir;
        	if( (int)value > value_decr)
			{
				value = (int)value - value_decr ;
          		++x;
        	}else 
				value += value_incr ; 

			{
				register int above = (value&0x00FF0000)>>16;
				/* LOCAL_DEBUG_OUT( "x = %d, y = %d, value = %d, above = %d",  x, y, value, above ); */
				switch( (above>>5)&0x03 )
				{
					case 0 :  /* 0 - 32 */ 
						above = 128 - above ;
						CTX_PUT_PIXEL( ctx, x-1, y, above ) ;
						CTX_PUT_PIXEL( ctx, x, y, ~(above>>1) ) ; 
						break ;	  
					case 1 :  /* 32 - 64 */ 
						{
							int a1 = (above - 32) ;
							CTX_PUT_PIXEL( ctx, x+1, y, a1 ) ;
							above = (~above)&0x7f ;
							CTX_PUT_PIXEL( ctx, x-1, y, above - a1 ) ;
							CTX_PUT_PIXEL( ctx, x, y, 255 ) ; 
						}
						break ;
					case 2 :  /* 64 - 96 */  
						{
							int a1 = (96 - above) ;	  
							CTX_PUT_PIXEL( ctx, x-1, y, a1 ) ;
							CTX_PUT_PIXEL( ctx, x, y, 255 ) ; 
							CTX_PUT_PIXEL( ctx, x+1, y, above - a1 ) ;
						}
						break ;
					case 3 :  /* 96 - 128 */ 
						{
							above -= ((~above)&0x7f)>>1 ;	  
							CTX_PUT_PIXEL( ctx, x, y, ~(above>>1) ) ; 
							CTX_PUT_PIXEL( ctx, x+1, y, above ) ;
						}
						break ;
				}
			}
      	}
	}		 
}	 

#define SUPERSAMPLING_BITS	8
#define SUPERSAMPLING_MASK	0x000000FF

static inline void 
render_supersampled_pixel( ASDrawContext *ctx, int xs, int ys )
{
	if( xs >= 0 && ys >= 0 )
	{	
		unsigned int xe = xs&SUPERSAMPLING_MASK ; 
		unsigned int nxe = (~xs)&SUPERSAMPLING_MASK ; 
		unsigned int x = xs>>SUPERSAMPLING_BITS;
		unsigned int ye = ys&SUPERSAMPLING_MASK ; 
		unsigned int nye = (~ys)&SUPERSAMPLING_MASK ; 
		unsigned int y = ys>>SUPERSAMPLING_BITS; 
		unsigned int v = (nxe*nye)>>8 ;	  
			
		CTX_PUT_PIXEL( ctx, x, y, v ) ; 
/*		LOCAL_DEBUG_OUT( "x = %d, y = %d, xe = %d, ye = %d, v = 0x%x", x, y, xe, ye, v ); */
		v = (xe*(nye))>>8 ;	  
		CTX_PUT_PIXEL( ctx, x+1, y, v ) ; 
		v = ((nxe)*ye)>>8 ;
		CTX_PUT_PIXEL( ctx, x, ++y, v ) ; 
		v = (xe*ye)>>8 ;	  
		CTX_PUT_PIXEL( ctx, x+1, y, v ) ; 
	}
}	

typedef struct ASCubicBezier
{
	int x0, y0;
	int x1, y1;
	int x2, y2;
	int x3, y3;
}ASCubicBezier;

static void
ctx_draw_bezier( ASDrawContext *ctx, int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3 )
{
	int ch = ctx->canvas_height<<8 ;
	int cw = ctx->canvas_width<<8 ;
	
	ASCubicBezier *bstack = NULL ;
	int bstack_size = 0, bstack_used = 0 ;
	
#define ADD_CubicBezier(X0,Y0,X1,Y1,X2,Y2,X3,Y3)	\
	do{ \
		if( ((X0)>=0 || (X1)>=0 || (X2)>=0 || (X3)>=0) && ((X0)<cw||(X1)<cw||(X2)<cw||(X3)<cw) && \
			((Y0)>=0 || (Y1)>=0 || (Y2)>=0 || (Y3)>=0) && ((Y0)<ch||(Y1)<ch||(Y2)<ch||(Y3)<ch) ){ \
			while( bstack_used >= bstack_size ) { \
				bstack_size += 2048/sizeof(ASCubicBezier); \
				bstack = (ASCubicBezier *)realloc( bstack, bstack_size*sizeof(ASCubicBezier)); \
			} \
			LOCAL_DEBUG_OUT( "(%d,%d),(%d,%d),(%d,%d),(%d,%d)", X0, Y0, X1, Y1, X2, Y2, X3, Y3 ); \
			bstack[bstack_used].x0=X0; \
			bstack[bstack_used].y0=Y0; \
			bstack[bstack_used].x1=X1; \
			bstack[bstack_used].y1=Y1; \
			bstack[bstack_used].x2=X2; \
			bstack[bstack_used].y2=Y2; \
			bstack[bstack_used].x3=X3; \
			bstack[bstack_used].y3=Y3; \
			++bstack_used ; \
		} \
	}while(0)


	ADD_CubicBezier(x0,y0,x1,y1,x2,y2,x3,y3);

	while( bstack_used > 0 )
	{
		--bstack_used ;
		x0 = bstack[bstack_used].x0 ;
		y0 = bstack[bstack_used].y0 ;
		x1 = bstack[bstack_used].x1 ;
		y1 = bstack[bstack_used].y1 ;
		x2 = bstack[bstack_used].x2 ;
		y2 = bstack[bstack_used].y2 ;
		x3 = bstack[bstack_used].x3 ;
		y3 = bstack[bstack_used].y3 ;
		{
			int x01 = x0 + ((x1-x0)>>1) ;  	
			int y01 = y0 + ((y1-y0)>>1) ; 
			int x11 = x1 + ((x2-x1)>>1) ; 	   
			int y11 = y1 + ((y2-y1)>>1) ; 
			int x31 = x3 + ((x2-x3)>>1) ; 	   
			int y31 = y3 + ((y2-y3)>>1) ; 

			int x011 = x01 + ((x11-x01)>>1) ;  	  
			int y011 = y01 + ((y11-y01)>>1) ; 
			int x111 = x11 + ((x31-x11)>>1) ;  	  
			int y111 = y11 + ((y31-y11)>>1) ; 

			int x0111 = x011 + ((x111-x011)>>1) ;  	  
			int y0111 = y011 + ((y111-y011)>>1) ; 

			if( (x0&0xFFFFFF00) == (x0111&0xFFFFFF00) && (y0&0xFFFFFF00) == (y0111&0xFFFFFF00) ) 
			{
			  	render_supersampled_pixel( ctx, x0, y0 );
			}else if( x01 != x1 || y01 != y1 || x011 != x2 || y011 != y2 || x0111 != x3 || y0111 != y3 )
			  	ADD_CubicBezier( x0, y0, x01, y01, x011, y011, x0111, y0111 );

			if( (x3&0xFFFFFF00) == (x0111&0xFFFFFF00) && (y3&0xFFFFFF00) == (y0111&0xFFFFFF00) ) 
			{	
			  	render_supersampled_pixel( ctx, x3, y3 );
			}else if( x0111 != x0 || y0111 != y0 || x111 != x1 || y111 != y1 || x31 != x2 || y31 != y2 ) 	
				ADD_CubicBezier( x0111, y0111, x111, y111, x31, y31, x3, y3 );	
		}
	}
	if( bstack ) 
		free( bstack );
}

typedef struct ASScanlinePart
{
	int y ;
	int x0, x1;
}ASScanlinePart;


static void 
ctx_flood_fill( ASDrawContext *ctx, int x_from, int y, int x_to, CARD8 min_val, CARD8 max_val )
{
	int ch = ctx->canvas_height ;
	int cw = ctx->canvas_width ;
	
	ASScanlinePart *sstack = NULL ;
	int sstack_size = 0, sstack_used = 0 ;
	CARD8 *canvas = CTX_SELECT_CANVAS(ctx);
	
	LOCAL_DEBUG_OUT( "(%d,%d,%d)", x_from, y, x_to );
#define ADD_ScanlinePart(X0,Y0,X1)	\
	do{ \
		if( ((X0)>=0 || (X1)>=0 ) && ((X0)<cw||(X1)<cw) && \
			 (Y0)>=0 && (Y0)<ch ){ \
			while( sstack_used >= sstack_size ) { \
				sstack_size += 2048/sizeof(ASScanlinePart); \
				sstack = realloc( sstack, sstack_size*sizeof(ASScanlinePart)); \
			} \
			LOCAL_DEBUG_OUT( "(%d,%d,%d)", X0, Y0, X1 ); \
			sstack[sstack_used].x0=X0; \
			sstack[sstack_used].y=Y0; \
			sstack[sstack_used].x1=X1; \
			++sstack_used ; \
		} \
	}while(0)

	ADD_ScanlinePart(x_from,y,x_to);

	while( sstack_used > 0 )
	{
		--sstack_used ; 
		x_from = sstack[sstack_used].x0 ; 
		x_to = sstack[sstack_used].x1 ; 
		y = sstack[sstack_used].y ; 
		if( x_from <  0 )
			x_from = 0 ; 
		if( x_to >= cw ) 
			x_to = cw - 1 ; 
		if( x_from <= x_to ) 
		{	
			/* here we have to check for lines below and above */
			if( y > 0 ) 
			{
				CARD8 *data = canvas + (y-1)*cw ; 
				int xc = x_from ; 

				while( xc <= x_to ) 
				{	
					if( data[xc] <= max_val && data[xc] >= min_val ) 
					{	
						int x0 = xc, x1 = xc ; 
						while( x0 >= 0 && data[x0] <= max_val && data[x0]  >= min_val ) --x0;
						++x0 ;
						while( x1 < cw && data[x1] <= max_val && data[x1]  >= min_val ) ++x1;
						--x1 ; 
					
						ADD_ScanlinePart(x0,y-1,x1);					
						LOCAL_DEBUG_OUT( "x = %d, y = %d, data[x] = 0x%X, x0 = %d, x1 = %d", xc, y-1, data[xc], x0, x1 );
						while( xc <= x_to && xc <= x1+1 ) ++xc ; 
					}else
						++xc ;
				}
			}	 
			if( y < ch-1 ) 
			{
				CARD8 *data = canvas + (y+1)*cw ; 
				int xc = x_from ; 

				while( xc <= x_to ) 
				{	
					if( data[xc] <= max_val && data[xc] >= min_val ) 
					{	
						int x0 = xc, x1 = xc ; 
						while( x0 >= 0 && data[x0] <= max_val && data[x0]  >= min_val ) --x0;
						++x0 ;
						while( x1 < cw && data[x1] <= max_val && data[x1]  >= min_val ) ++x1;
						--x1 ; 
						LOCAL_DEBUG_OUT( "x = %d, y = %d, data[x] = 0x%X, x0 = %d, x1 = %d", xc, y+1, data[xc], x0, x1 );
						ADD_ScanlinePart(x0,y+1,x1);					
						while( xc <= x_to && xc <= x1+1 ) ++xc ; 
					}else
						++xc ;
				}
			}	 
 			CTX_FILL_HLINE(ctx,x_from,y,x_to,255);		
		}
	}	 
	if( sstack ) 
		free( sstack );
	
	
}

/*************************************************************************
 * Clip functions 
 *************************************************************************/
Bool 
clip_line( int k, int x0, int y0, int cw, int ch, int *x, int*y ) 	
{
	int new_x = *x ; 
	int new_y = *y ;

	if( new_x < 0 ) 
	{
		new_x = 0 ;
		new_y = (-x0 / k) + y0 ;
	}	 
	if( new_y < 0 ) 
	{
		new_y = 0 ;
		new_x = -y0 * k + x0 ; 
	}	 
	if( new_x < 0 ) 
		return False;

	/* here both new_x and new_y are non-negative */
	if( new_x >= cw ) 
	{
		new_x = cw - 1 ;
		new_y = (new_x - x0)/k  + y0 ; 
		if( new_y < 0 ) 
			return False;	   
	}		 
	if( new_y >= ch ) 
	{
		new_y = ch - 1 ;
		new_x = (new_y - y0) * k  + x0 ; 
		if( new_x < 0 || new_x >= cw ) 
			return False;	   
	}		 
	*x = new_x ; 
	*y = new_y ;
	return True;
}

/*********************************************************************************/
/* context functions : 											 				 */
/*********************************************************************************/
Bool asim_set_brush( ASDrawContext *ctx, int brush );

ASDrawContext *
create_asdraw_context( unsigned int width, unsigned int height )
{
	ASDrawContext *ctx = safecalloc( 1, sizeof(ASDrawContext));
	
	ctx->canvas_width = width == 0 ? 1 : width ; 
	ctx->canvas_height = height == 0 ? 1 : height ;
	ctx->canvas = safecalloc(  ctx->canvas_width*ctx->canvas_height, sizeof(CARD8));

	asim_set_brush( ctx, 0 ); 
	ctx->fill_hline_func = fill_hline_notile ;
				   
	return ctx;
}	   

void
destroy_asdraw_context( ASDrawContext *ctx )
{
	if( ctx )
	{
		if( ctx->canvas ) 
			free( ctx->canvas );	 
		if( ctx->scratch_canvas ) 
			free( ctx->scratch_canvas );	 
		free( ctx );
	}	 
}	   

Bool
asim_set_brush( ASDrawContext *ctx, int brush ) 
{
	if( brush >= 0 && brush < AS_DRAW_BRUSHES && ctx != NULL ) 
	{
		ctx->tool = &(StandardBrushes[brush]) ;  
		if( ctx->tool->width == 1 && ctx->tool->height == 1 ) 
			ctx->apply_tool_func = apply_tool_point ;
		else 
			ctx->apply_tool_func = apply_tool_2D ; 
		return True;
	}	 
	return False;
}

Bool
asim_set_custom_brush( ASDrawContext *ctx, ASDrawTool *brush) 
{
	if( brush !=NULL && ctx != NULL ) 
	{
		ctx->tool = brush ;  
		if( ctx->tool->width == 1 && ctx->tool->height == 1 ) 
			ctx->apply_tool_func = apply_tool_point ;
		else 
			ctx->apply_tool_func = apply_tool_2D ; 
		return True;
	}	 
	return False;
}


Bool
asim_start_path( ASDrawContext *ctx ) 
{
	if( ctx == NULL ) 
		return False;
	LOCAL_DEBUG_OUT( "scratch_canvas = %p, flags = 0x%lx", ctx->scratch_canvas, ctx->flags );
	if( ctx->scratch_canvas ) 
	{
		if( get_flags( ctx->flags, ASDrawCTX_UsingScratch ) )
			return False;	  
		memset( ctx->scratch_canvas, 0x00, ctx->canvas_width*ctx->canvas_height*sizeof(CARD8) );
	}else
		ctx->scratch_canvas	 = safecalloc(  ctx->canvas_width*ctx->canvas_height, sizeof(CARD8));
	set_flags( ctx->flags, ASDrawCTX_UsingScratch );
	return True;
}

void asim_flood_fill( ASDrawContext *ctx, int x, int y, CARD8 min_val, CARD8 max_val );


Bool
asim_apply_path( ASDrawContext *ctx, int start_x, int start_y, Bool fill, int fill_start_x, int fill_start_y, CARD8 fill_threshold ) 
{
	int i ; 
    if( ctx == NULL || !get_flags( ctx->flags, ASDrawCTX_UsingScratch )) 	
		return False;
	
	LOCAL_DEBUG_CALLER_OUT( "start_x = %d, start_y = %d, fill = %d, fill_start_x = %d, fill_start_y = %d",
							start_x, start_y, fill, fill_start_x, fill_start_y );

	/* TODO : contour tracing functionality : */
	if( fill ) 
		asim_flood_fill( ctx, fill_start_x, fill_start_y, 0, fill_threshold==0?CTX_DEFAULT_FILL_THRESHOLD:fill_threshold );	
	/* actually applying scratch : */
	i = ctx->canvas_width*ctx->canvas_height ;
	while( --i >= 0 ) 
		if( ctx->canvas[i] < ctx->scratch_canvas[i] )
			ctx->canvas[i] = ctx->scratch_canvas[i] ;
	
	clear_flags( ctx->flags, ASDrawCTX_UsingScratch );	 
	return True;
}

Bool
apply_asdraw_context( ASImage *im, ASDrawContext *ctx, ASFlagType filter ) 
{
	int chan ;
	int width, height ;
	if( im == NULL || ctx == NULL || filter == 0 )
		return False;
	
	width = im->width ;
	height = im->height ;
	if( width != ctx->canvas_width || height != ctx->canvas_height )
		return False;
	
	for( chan = 0 ; chan < IC_NUM_CHANNELS;  chan++ )
		if( get_flags( filter, 0x01<<chan) )
		{
			int y;
			register ASStorageID *rows = im->channels[chan] ;
			register CARD8 *canvas_row = ctx->canvas ; 
			for( y = 0 ; y < height ; ++y )
			{	
				if( rows[y] ) 
					forget_data( NULL, rows[y] ); 
				rows[y] = store_data( NULL, (CARD8*)canvas_row, width, ASStorage_RLEDiffCompress, 0);
				canvas_row += width ; 
			}
		}
	return True;
}	   

/**************************************************************************/
/* generic line drawing - uses calls supplied function to actually render 
 * pixels :
 **************************************************************************/
/**************************************************************************/
void asim_move_to( ASDrawContext *ctx, int dst_x, int dst_y );

void
asim_line_to_generic( ASDrawContext *ctx, int dst_x, int dst_y, void (*func)(ASDrawContext*,int,int,int,int))
{
	if( ctx ) 
	{
		int from_x = ctx->curr_x ; 	
		int from_y = ctx->curr_y ; 
		int to_x = dst_x ; 	
		int to_y = dst_y ;
		int cw = ctx->canvas_width ; 
		int ch = ctx->canvas_height ; 
		
		asim_move_to( ctx, dst_x, dst_y );	 				   

		if( to_y == from_y ) 
		{
			if( to_y < 0 || to_y >= ch ) 
				return ; 
			if( from_x < 0 ) 
				from_x = 0 ; 
			else if( from_x >= cw ) 
				from_x = cw - 1 ; 		  
			if( to_x < 0 ) 
				to_x = 0 ; 
			else if( to_x >= cw ) 
				to_x = cw - 1 ; 		  
		}else if( to_x == from_x ) 
		{
			if( to_x < 0 || to_x >= ch ) 
				return ; 
			if( from_y < 0 ) 
				from_y = 0 ; 
			else if( from_y >= ch ) 
				from_y = ch - 1 ; 		  
			if( to_y < 0 ) 
				to_y = 0 ; 
			else if( to_y >= ch ) 
				to_y = ch - 1 ; 		  
		}else
		{			 
			int k = (to_x - from_x)/(to_y - from_y);
			int x0 = from_x ; 
			int y0 = from_y ;

			if( (from_x < 0 && to_x < 0 ) || (from_y < 0 && to_y < 0 ) ||
				(from_x >= cw && to_x >= cw ) || (from_y >= ch && to_y >= ch ))
				return ;
			
			if( !clip_line( k, x0, y0, cw, ch, &from_x, &from_y ) ) 
				return ; 	 
			if( !clip_line( k, x0, y0, cw, ch, &to_x, &to_y ) ) 
				return ; 	 
		}			
		if( from_x != to_x || from_y != to_y ) 
			func( ctx, from_x, from_y, to_x, to_y );
	}	 
} 

/*************************************************************************/
/* Path primitives : *****************************************************/
/*************************************************************************/
void
asim_move_to( ASDrawContext *ctx, int dst_x, int dst_y )
{
	if( ctx ) 
	{
		ctx->curr_x = dst_x ; 	
		ctx->curr_y = dst_y ; 
	}		 
}
	   
void
asim_line_to( ASDrawContext *ctx, int dst_x, int dst_y ) 
{
	asim_line_to_generic( ctx, dst_x, dst_y, ctx_draw_line_solid);	
}	 


void
asim_line_to_aa( ASDrawContext *ctx, int dst_x, int dst_y ) 
{
	asim_line_to_generic( ctx, dst_x, dst_y, ctx_draw_line_solid_aa);	 		
}	 

void
asim_cube_bezier( ASDrawContext *ctx, int x1, int y1, int x2, int y2, int x3, int y3 ) 
{
	if( ctx ) 
	{	
		int x0 = ctx->curr_x;
		int y0 = ctx->curr_y;
		
		asim_move_to( ctx, x3, y3 );
		ctx_draw_bezier( ctx, x0<<8, y0<<8, x1<<8, y1<<8, x2<<8, y2<<8, x3<<8, y3<<8 );
	}		
}

/*************************************************************************/
/* misc auxilary stuff : *************************************************/
/*************************************************************************/
/* Sinus lookup table */
const signed int ASIM_SIN[91]=
{
	0x00000000,
	0x00000478,0x000008EF,0x00000D66,0x000011DC,0x00001650,0x00001AC2,0x00001F33,0x000023A1,0x0000280C,0x00002C74,
	0x000030D9,0x0000353A,0x00003996,0x00003DEF,0x00004242,0x00004690,0x00004AD9,0x00004F1C,0x00005358,0x0000578F,
	0x00005BBE,0x00005FE6,0x00006407,0x00006820,0x00006C31,0x00007039,0x00007439,0x0000782F,0x00007C1C,0x00008000,
	
	0x000083DA,0x000087A9,0x00008B6D,0x00008F27,0x000092D6,0x00009679,0x00009A11,0x00009D9C,0x0000A11B,0x0000A48E,
	0x0000A7F3,0x0000AB4C,0x0000AE97,0x0000B1D5,0x0000B505,0x0000B827,0x0000BB3A,0x0000BE3F,0x0000C135,0x0000C41B,
	0x0000C6F3,0x0000C9BB,0x0000CC73,0x0000CF1C,0x0000D1B4,0x0000D43C,0x0000D6B3,0x0000D91A,0x0000DB6F,0x0000DDB4,
	0x0000DFE7,0x0000E209,0x0000E419,0x0000E617,0x0000E804,0x0000E9DE,0x0000EBA6,0x0000ED5C,0x0000EEFF,0x0000F090,
	0x0000F20E,0x0000F378,0x0000F4D0,0x0000F615,0x0000F747,0x0000F865,0x0000F970,0x0000FA68,0x0000FB4C,0x0000FC1C,
	0x0000FCD9,0x0000FD82,0x0000FE18,0x0000FE99,0x0000FF07,0x0000FF60,0x0000FFA6,0x0000FFD8,0x0000FFF6,0x00010000
};

static inline int asim_sin( int angle )
{
	while( angle >= 360 ) 
		angle -= 360 ;
	while( angle < 0 ) 
		angle += 360 ;
	if( angle <= 90 ) 
		return ASIM_SIN[angle];
	if( angle <= 180 ) 
		return ASIM_SIN[180-angle];
	if( angle <= 270 ) 
		return -ASIM_SIN[angle-180];
	return -ASIM_SIN[360-angle];
}	 

int 
asim_sqrt( double sval ) 
{
	long long uval = (sval >= 0) ? (long long)sval:-(long long)sval ;
	long long res = uval ; 
	long long t = res*res ;
	
	while( t > uval ) 
	{
		res = res >> 1 ; 
		t = t >> 2 ;	  
	}	 
	if( t == uval ) 
		return res;
	res = (res << 1) + 1 ;
	t = res*res ;
	while( t > uval ) 
	{
		t -= (res<<1)-1 ;
		--res ;
	}		  
	return res;
}	  

/*************************************************************************/
/* some standard closed paths :                                          */
/*************************************************************************/
void
asim_straight_ellips( ASDrawContext *ctx, int x, int y, int rx, int ry, Bool fill ) 
{
	if( ctx && rx > 0 && ry > 0 &&
		x + rx >= 0 && y+ry >= 0 && 
		x - rx  < ctx->canvas_width && y - ry < ctx->canvas_height ) 
	{	 
		int max_y = ry ; 
		int orig_x = x, orig_y = y, orig_rx = rx ; 
#ifdef HAVE_LONG_LONG						   
		long long rx2 = rx*rx, ry2 = ry * ry, d ; 
#else
		long rx2 = rx*rx, ry2 = ry * ry, d ; 
#endif		
		if( y + ry  > ctx->canvas_height ) 
			max_y = ctx->canvas_height - y ; 
		if( y - ry  < 0 && y > max_y ) 
			max_y = y ; 
#if 0
		if( fill ) 
		{
			long y1 = 0; 
			long x1 = rx-1;
			long ty = rx*rx ;
			long tx = x1*x1 ;
			
			do
			{
				while( tx > ty ) 
				{
					tx -= (x1<<1)+1 ;
					--x1 ;
				}
				CTX_FILL_HLINE(ctx,x-x1,y+y1,x+x1,255);
				CTX_FILL_HLINE(ctx,x-x1,y-y1,x+x1,255);
				
				d = (y1<<1)+1 ;
				/* this is likely to break for large radii when there are no 64 bit ints */
				if( rx != ry )
					d = (d*rx2)/ry2 ;
				ty -= (long)d;
			
			}while( ++y1 < max_y ); 
		}
#endif

		asim_start_path( ctx );
		asim_move_to( ctx, x+rx, y );
		LOCAL_DEBUG_OUT( "x = %d, y = %d, rx = %d, ry = %d", x, y, rx, ry );
/* if no 64 bit integers - then tough luck - have to resort to beziers */
#ifndef HAVE_LONG_LONG						   
		if( (rx == ry && (rx > 16000 || ry > 16000 || x < -16000 || y < -16000 || x > 16000 || y > 16000)) ||
			(rx != ry && (rx > 1000 || ry > 1000 || x > 1000 || y > 1000 || x < -1000 || y < -1000)) ) 
		{  /* somewhat imprecise approximation using 4 bezier curves */
			int drx = rx*142 ;       /* that gives us precision of about 0.05% which is 
									* pretty good for integer math */
			int dry = ry*142 ;
			LOCAL_DEBUG_OUT( "drx = %d, dry = %d", drx, dry );
			rx  = rx << 8 ; 
			ry  = ry << 8 ; 
			x  = x << 8 ; 
			y  = y << 8 ; 
			ctx_draw_bezier( ctx, x+rx, y, x+rx, y+dry, x+drx, y+ry, x, y+ry );
			ctx_draw_bezier( ctx, x, y+ry, x-drx, y+ry, x-rx, y+dry, x-rx, y );
			ctx_draw_bezier( ctx, x-rx, y, x-rx, y-dry, x-drx, y-ry, x, y-ry );
			ctx_draw_bezier( ctx, x, y-ry, x+drx, y-ry, x+rx, y-dry, x+rx, y );

	 	}else
#endif			
		{		  
			x = x<<4 ; 
			y = y<<4 ; 
			rx = rx<<4 ;
			ry = ry<<4 ;
			max_y = (max_y << 4) + 4;

			{
				long min_r = rx - 1, max_r = rx + 1 ; 
				long y1 = 0; 
				long x1 = max_r;
				long min_ty = min_r * min_r ;
				long max_ty = max_r * max_r ;
				long tx = max_ty ;
			
				do
				{
					long start_tx, start_x1 ;
					while( tx > max_ty ) 
					{
						--x1; 
						tx -= (x1<<1)+1 ;
					}	 
					start_tx = tx ; 
					start_x1 = x1 ;

					
					while( tx > min_ty && x1 >= 0) 
					{
						render_supersampled_pixel( ctx, (x-x1)<<4, (y+y1)<<4 );
						render_supersampled_pixel( ctx, (x-x1)<<4, (y-y1)<<4 );
						render_supersampled_pixel( ctx, (x+x1)<<4, (y+y1)<<4 );
						render_supersampled_pixel( ctx, (x+x1)<<4, (y-y1)<<4 );
						--x1; 
						tx -= (x1<<1)+1 ;
					}
					tx = start_tx ; 
					x1 = start_x1 ;
					d = ((y1<<1)+1);
					if( rx != ry )
						d = (d*rx2)/ry2 ;
					min_ty -= (long)d;
					max_ty -= (long)d;
				}while( ++y1 <= max_y ); 
			}
		}		
		asim_apply_path( ctx, orig_x+orig_rx, orig_y, fill, orig_x, orig_y, CTX_ELLIPS_FILL_THRESHOLD );
	}		
}	 


void
asim_circle( ASDrawContext *ctx, int x, int y, int r, Bool fill ) 
{
	asim_straight_ellips( ctx, x, y, r, r, fill );
}	 


void
asim_ellips( ASDrawContext *ctx, int x, int y, int rx, int ry, int angle, Bool fill ) 
{
	while( angle >= 360 ) 
		angle -= 360 ;
	while( angle < 0 ) 
		angle += 360 ;
	
	if( angle == 0 || angle ==180  || rx == ry ) 
	{	
		asim_straight_ellips( ctx, x, y, rx, ry, False );
		if( angle == 180 ) 
			asim_move_to( ctx, x-rx, y );
		return;
	}
	if( angle == 90 || angle == 270 ) 
	{	
		asim_straight_ellips( ctx, x, y, ry, rx, False );
		asim_move_to( ctx, x, y + (angle == 90?-rx:rx) );
		return;
	}

	if( ctx && rx > 0 && ry > 0 ) 
	{	
		int dx0 = rx, dy0 = ry, dx1 = 0, dy1 = rx*4/3 ;
		int x0, y0, x1down, y1down, x2down, y2down, x3, y3, x2up, y2up, x1up, y1up ; 
		int ry4 = (ry<<2)/3 ;
		int sin_val = asim_sin(angle);
		int cos_val = asim_sin(angle+90);
		if(sin_val < 0) 
			sin_val = -sin_val ;
		if(cos_val < 0) 
			cos_val = -cos_val ;
		dx0 = (rx*cos_val)>>8; 
		dy0 = (rx*sin_val)>>8; 
		dx1 = (ry4*sin_val)>>8;
		dy1 = (ry4*cos_val)>>8; 
		if( angle < 180 )
		{
			dy0 = -dy0 ;
			dx1 = -dx1 ; 
		}	 
		if( angle > 90 && angle < 270 )
		{	
			dx0 = -dx0 ; 
			dy1 = -dy1 ;
		}
		x = x << 8;
		y = y << 8;
		x0 = x + dx0 ;
		y0 = y + dy0 ;
		x3 = x - dx0 ;
		y3 = y - dy0 ; 
		x1down = x0 + dx1 ;
		y1down = y0 - dy1 ;
		x2down = x3 + dx1 ; 
		y2down = y3 - dy1 ;  
		x2up = x3 - dx1 ; 
		y2up = y3 + dy1 ;  
		x1up = x0 - dx1 ;  
		y1up = y0 + dy1 ;  
		

		asim_start_path( ctx );
		asim_move_to( ctx, x0>>8, y0>>8 );
		ctx_draw_bezier( ctx, x0, y0, x1down, y1down, x2down, y2down, x3, y3 );
		ctx_draw_bezier( ctx, x3, y3, x2up, y2up, x1up, y1up, x0, y0 );
		asim_apply_path( ctx, x0>>8, y0>>8, fill, x, y, CTX_ELLIPS_FILL_THRESHOLD );
	}		
}	 

void
asim_ellips2( ASDrawContext *ctx, int x, int y, int rx, int ry, int angle, Bool fill ) 
{
	Bool direction = 1 ;

	while( angle >= 360 ) 
		angle -= 360 ;
	while( angle < 0 ) 
		angle += 360 ;
	
	if( angle == 0 || angle ==180  || rx == ry ) 
	{	
		asim_straight_ellips( ctx, x, y, rx, ry, fill );
		if( angle == 180 ) 
			asim_move_to( ctx, x-rx, y );
		return;
	}
	if( angle == 90 || angle == 270 ) 
	{	
		asim_straight_ellips( ctx, x, y, ry, rx, fill );
		asim_move_to( ctx, x, y + (angle == 90?-rx:rx) );
		return;
	}

	if( angle > 180 ) 
		angle -= 180 ; 
	if( angle > 90 ) 
	{	
		angle = 180-angle ; 
		direction = -1 ;
	}


	if( ctx && rx > 0 && ry > 0 ) 
	{	
		int sin90 = 0x00010000 ;
		double sin_val = (double)asim_sin(angle)/(double)sin90;
		double cos_val = (double)asim_sin(angle+90)/(double)sin90;
		double rx2 = rx*rx ; 
		double ry2 = ry*ry ;
		double c2 = rx2 - ry2 ; 
		double xc2 = (c2 * cos_val)*cos_val ;
		double yc2 = (c2 * sin_val)*sin_val ;
		double A = rx2 - xc2 ;
		double B = rx2 - yc2 ;
		double C = -c2*sin_val*cos_val ;
		double F = -rx2*ry2 ;
		int yt = asim_sqrt(A);
		int xt = -C/yt ;
		int xr = asim_sqrt(B);
		int yr = -C/xr ;
		int x1 ; 
		int x2 ; 
		int line = yt; 
		int last_med_dd1 = 0 ; 
		int last_med_dd2 = 0 ; 
		int y1 ;
		double A2 = 2.*A ;
		double BB; 
		double CC = C*(double)((line<<1)-1);


		xt = (A-CC)/A2  ;
		y1 = line*direction ;
		BB = B*(double)line*(double)line + F - B*(double)line - (B/4.);
		x1 = xt+1 ;
		x2 = xt-1 ;
		--yr ; 

		while( line >= -1 ) 
		{
			double d ; 
			int dx1 = 0, dx2 = 0 ;
			d = A*(double)x1*(double)x1 + BB +CC*(double)x1;
#ifdef DEBUG_ELLIPS					 						   
			fprintf( stderr, "line = %d, d1 = %f", y-line, d ); 
#endif
			if( d < 0 ) 
			{       
				int aa = (double)((A-A2*(double)x1-CC)*255/A2);
				int dd = -aa-(double)(d*255/A2)  ; 
				int med_dd = (dd>>1)+1; 
	
				if( last_med_dd1 > 0 ) 
				{ /* this prevents suddent jags on the line and makes it as 
				   * smooth as it gets  */
					med_dd = (2*med_dd+last_med_dd1)/3 ;
				}
				++dx1 ;
#ifdef DEBUG_ELLIPS					 						   
				fprintf( stderr, " -> d1 = %f, dx1 = %d, aa = %d, dd = %d, med_dd = %d\n", d, dx1, aa, dd, med_dd ); 
#endif
				if( med_dd+aa > dd ) 
				{
					int v = (dd-med_dd)*255/(aa+255) ;	
					CTX_PUT_PIXEL( ctx, x+(x1-1), y-y1, 255-v ) ;
					CTX_PUT_PIXEL( ctx, x-(x1-1), y+y1, 255-v ) ;
					CTX_PUT_PIXEL( ctx, x+(x1-2), y-y1, v ) ;
					CTX_PUT_PIXEL( ctx, x-(x1-2), y+y1, v ) ;
					dx1 = 2 ;
#ifdef DEBUG_ELLIPS					 						   
					fprintf( stderr, "\t\t dx1 = %d, v = %d\n", dx1, v ); 
#endif
				}else	 
					while( dd > -(aa>>1) ) 
					{ 	
						int v ; 
						v = ( dd >= med_dd )? dd-med_dd: med_dd-dd ;  
						v = v*255/med_dd ; 
						if( v < 0 || v > 255) 
							v = 250 ;

						CTX_PUT_PIXEL( ctx, x+(x1-dx1), y-y1, 255-v ) ;
						CTX_PUT_PIXEL( ctx, x-(x1-dx1), y+y1, 255-v ) ;
						if( x1 >= xt && dd > med_dd ) 
						{	
							CTX_PUT_PIXEL( ctx, x+(x1-dx1), y-(y1+direction), v ) ;
							CTX_PUT_PIXEL( ctx, x-(x1-dx1), y+(y1+direction), v ) ;
						}
						++dx1 ; 
#ifdef DEBUG_ELLIPS					 						   
						fprintf( stderr, "\t\t dx1 = %d, aa = %d, dd = %d, med_dd = %d, v = %d, x = %d\n", dx1, aa, dd, med_dd, v, x+(x1-dx1) ); 
#endif
						aa += 255 ;
						dd -= aa ; 
					}
				x1 -= (dx1>>1)-1 ;
				last_med_dd1 = med_dd ; 
			}

			d = A*(double)(x2+1)*(double)(x2+1) + BB +CC*(double)(x2+1) ;
			if( line > yr ) 
			{
#ifdef DEBUG_ELLIPS					 						   
				fprintf( stderr, "line = %d, d2 = %f\n", y-line, d ); 
#endif
			 	if( d < 0 ) 	
				{
					int aa = (double)((A+A2*(double)x2+CC)*255/A2);
					int dd = aa+(double)(d*255/A2)  ; 
					int med_dd = dd/2 - 1; 

					if( last_med_dd2 > 0 ) 
					{ /* this prevents suddent jags on the line and makes it as 
					   * smooth as it gets  */
						med_dd = (med_dd*2+last_med_dd2)/3 ;
					}
					dx2 = 1;
#ifdef DEBUG_ELLIPS					 						   
					fprintf( stderr, " -> d2 = %f, x2 = %d, dx2 = %d, aa = %d, dd = %d, med_dd = %d\n", d, x2, dx2, aa, dd, med_dd ); 
#endif
					if( med_dd-aa < dd ) 
					{
						int v = (med_dd-dd)*255/(aa+255) ;	
						CTX_PUT_PIXEL( ctx, x+(x2+2), y-y1, 255-v ) ;
						CTX_PUT_PIXEL( ctx, x-(x2+2), y+y1, 255-v ) ;
						CTX_PUT_PIXEL( ctx, x+(x2+3), y-y1, v ) ;
						CTX_PUT_PIXEL( ctx, x-(x2+3), y+y1, v ) ;
						dx2 = 2 ;
#ifdef DEBUG_ELLIPS					 						   
						fprintf( stderr, "\t\t dx2 = %d, v = %d\n", dx2, v ); 
#endif
					}else while( dd < aa/2 ) 
					{ 
						int v ; 
						v = ( dd >= med_dd )? dd-med_dd: med_dd-dd ;  
						v = (med_dd == 0)?250: (v*255/-med_dd) ; 
						if( v < 0 || v > 255) 
							v = 250 ;
#ifdef DEBUG_ELLIPS					 						   
						fprintf( stderr, "\t\t dx2 = %d, aa = %d, dd = %d, med_dd = %d, v = %d, x = %d\n", dx2, aa, dd, med_dd, v, x+(x2+dx2) ); 						
#endif
						++dx2; 
						CTX_PUT_PIXEL( ctx, x+(x2+dx2), y-y1, 255-v ) ;
						CTX_PUT_PIXEL( ctx, x-(x2+dx2), y+y1, 255-v ) ;
						if( x2 <= xt && dd < med_dd ) 
						{	
							CTX_PUT_PIXEL( ctx, x+(x2+dx2), y-(y1+direction), v ) ;
							CTX_PUT_PIXEL( ctx, x-(x2+dx2), y+(y1+direction), v ) ;
						}
						aa += 255 ;
						dd += aa ; 
					}
					x2 += (dx2>>1)-1 ;
					last_med_dd2 = med_dd ; 
				}	 
			}else if( line < yr ) 
			{
#ifdef DEBUG_ELLIPS					 						   
				fprintf( stderr, "line = %d, BELOW: d2 = %f\n", y-line, d ); 
#endif
				if( d > 0. )
				{	
					int aa = (double)((A-A2*(double)(x2)-CC)*255./A2);
					int dd = aa+(double)(d*255./A2)  ; 
					int med_dd = dd/2+1; 
					
					if( last_med_dd2 > 0 ) 
						med_dd = (med_dd*2+last_med_dd2)/3 ;

					dx2 = -1 ;
#ifdef DEBUG_ELLIPS					 						   
					fprintf( stderr, " ->BELOW: d2 = %f, dx2 = %d, aa = %d, dd = %d, med_dd = %d\n", d, dx2, aa, dd, med_dd ); 					
#endif
					if( med_dd-aa > dd ) 
					{
						int v = (dd-med_dd)*255/(-aa+255) ;	
#ifdef DEBUG_ELLIPS					 						   
						fprintf( stderr, "\t\t BELOW: dx2 = %d, v = %d\n", dx2, v ); 
#endif
						CTX_PUT_PIXEL( ctx, x+(x2-1), y-y1, 255-v ) ;
						CTX_PUT_PIXEL( ctx, x-(x2-1), y+y1, 255-v ) ;
						CTX_PUT_PIXEL( ctx, x+(x2-2), y-y1, v ) ;
						CTX_PUT_PIXEL( ctx, x-(x2-2), y+y1, v ) ;
						dx2 = -2 ;
					}else while( dd > aa/2 )	
					{	
						int v ; 
						v = ( dd >= med_dd )? dd-med_dd: med_dd-dd ;  
						v = v*255/med_dd ; 
						if( v < 0 || v > 255) 
							v = 250 ;
#ifdef DEBUG_ELLIPS					 						
						fprintf( stderr, "\t\tBELOW:  dx2 = %d, aa = %d, dd = %d, med_dd = %d, v = %d, x = %d\n", dx2, aa, dd, med_dd, v, x+(x2+dx2) ); 						   
#endif
						CTX_PUT_PIXEL( ctx, x+(x2+dx2), y-y1, 255-v ) ;
						CTX_PUT_PIXEL( ctx, x-(x2+dx2), y+y1, 255-v ) ;
						--dx2; 
						aa += 255 ;
						dd += aa ; 
					}
					x2 += (dx2/2)+1 ;
					last_med_dd2 = med_dd ; 
				}			
			}else
			{	
    			x2 = xr+2 ; 	
#ifdef DEBUG_ELLIPS					 
				fprintf( stderr, "\t\t BELOW: x2 = %d, xr = %d\n", x2,xr); 
#endif
				CTX_PUT_PIXEL( ctx, x+xr, y-y1, 255 ) ;
				CTX_PUT_PIXEL( ctx, x-xr, y+y1, 255 ) ;
				last_med_dd2 = 0 ;
			}
#ifdef DEBUG_ELLIPS					 
			fprintf( stderr, "dx1 = %d, x1 = %d, dx2 = %d, x2 = %d\n", dx1, x1, dx2, x2 ); 
#endif
			if( fill ) 
			{	
				CTX_FILL_HLINE(ctx,x+(x1-2),y-y1,x+x2-1,255);
				CTX_FILL_HLINE(ctx,x-x2-1,y+y1,x-(x1-2),255);
			}
			
			CC -= 2.*C ;
			BB -= B*(double)((line-1)<<1) ;
			y1 -= direction ;
			--line ; 
		}	 
	}		
}	 

/*************************************************************************/
/* Flood fill functionality **********************************************/
/*************************************************************************/

void 
asim_flood_fill( ASDrawContext *ctx, int x, int y, CARD8 min_val, CARD8 max_val ) 
{
	if( ctx && x >= 0 && x < ctx->canvas_width && y >= 0 && y < ctx->canvas_height )
	{
		int x0 = x, x1 = x ; 
		int cw = ctx->canvas_width ;
		CARD8 *data = CTX_SELECT_CANVAS(ctx) + y*cw ; 
		while( x0 >= 0 && data[x0] <= max_val && data[x0]  >= min_val ) --x0;
		++x0 ;
		while( x1 < cw && data[x1] <= max_val && data[x1]  >= min_val ) ++x1;
		--x1 ; 
		LOCAL_DEBUG_OUT( "x = %d, y = %d, data[x] = 0x%X, x0 = %d, x1 = %d", x, y, data[x], x0, x1 );
		
		if( x0 <= x1 ) 
			ctx_flood_fill( ctx, x0, y, x1, min_val, max_val );			
	}		   
}	 

void
asim_rectangle( ASDrawContext *ctx, int x, int y, int width, int height ) 
{
	asim_move_to( ctx, x, y );
	asim_line_to_generic( ctx, x+width, y, ctx_draw_line_solid);	 		
	asim_line_to_generic( ctx, x+width, y+height, ctx_draw_line_solid);	 		   
	asim_line_to_generic( ctx, x, y+height, ctx_draw_line_solid);	 		   
	asim_line_to_generic( ctx, x, y, ctx_draw_line_solid);	 		   
}	 




/*********************************************************************************/
/* The end !!!! 																 */
/*********************************************************************************/
/*********************************************************************************/
/* Test container : 																 */
/*********************************************************************************/

#ifdef TEST_ASDRAW
#include "afterimage.h"

int main(int argc, char **argv )
{
	ASVisual *asv ;
	ASImage *back = NULL ;
	ASImage *drawing1 = NULL ;
	ASImage *merged_im = NULL ;
	ASImageLayer *layers ;
	int layers_num = 0, i, k;
	int screen = 0, depth = 0;

	ASDrawContext *ctx ;

#define DRAW_TEST_SIZE 800	
	
	set_output_threshold( 10 );

#ifndef X_DISPLAY_MISSING
	dpy = XOpenDisplay(NULL);
	screen = DefaultScreen(dpy);
	depth = DefaultDepth( dpy, screen );
#endif

	asv = create_asvisual( dpy, screen, depth, NULL );


	back = create_asimage( DRAW_TEST_SIZE, DRAW_TEST_SIZE, 100 );
	back->back_color = ARGB32_White ;
	drawing1 = create_asimage( DRAW_TEST_SIZE, DRAW_TEST_SIZE, 100 );
	drawing1->back_color = ARGB32_Black ;


	ctx = create_asdraw_context(DRAW_TEST_SIZE, DRAW_TEST_SIZE);
	/* actuall drawing starts here */
#if 1
/*	for( i = 0 ; i < 50000 ; ++i ) */
	asim_move_to( ctx, 0, 0 ); 
	asim_line_to_aa( ctx, 200, 200 ); 
	asim_line_to_aa( ctx, 100, 10 );
	asim_line_to_aa( ctx, 10, 300 );
	asim_line_to_aa( ctx, 15, 400 );
	asim_move_to(ctx, 15, 420);  
	asim_line_to_aa( ctx, 200, 410 );
	asim_line_to_aa( ctx, 400, 440 );
		
	asim_move_to(ctx, 10, 0); 	  
	asim_set_brush( ctx, 1 ); 
	asim_line_to_aa( ctx, 210, 200 ); 
	asim_line_to_aa( ctx, 110, 10 );
	asim_line_to_aa( ctx, 20, 300 );
	asim_line_to_aa( ctx, 25, 400 );
	asim_move_to(ctx, 15, 430);  
	asim_line_to_aa( ctx, 200, 420 );
	asim_line_to_aa( ctx, 400, 450 );

	asim_move_to(ctx, 20, 0); 	  
	asim_set_brush( ctx, 2 ); 
	asim_line_to_aa( ctx, 220, 200 ); 
	asim_line_to_aa( ctx, 120, 10 );
	asim_line_to_aa( ctx, 30, 300 );
	asim_line_to_aa( ctx, 35, 400 );
	asim_move_to(ctx, 15, 440);    
	asim_line_to_aa( ctx, 200, 430 );
	asim_line_to_aa( ctx, 400, 460 );

    /****************************************/
	/* non-antialiased : */

	asim_move_to(ctx,  200, 0 ); 
	asim_set_brush( ctx, 0 ); 
	asim_line_to( ctx, 400, 200 ); 
	asim_line_to( ctx, 300, 10 );
	asim_line_to( ctx, 210, 300 );
	asim_line_to( ctx, 215, 400 );
	asim_move_to(ctx, 15, 450);  
	asim_line_to( ctx, 200, 440 );
	asim_line_to( ctx, 400, 470 );
		
	asim_move_to(ctx, 210, 0); 	  
	asim_set_brush( ctx, 1 ); 
	asim_line_to( ctx, 410, 200 ); 
	asim_line_to( ctx, 310, 10 );
	asim_line_to( ctx, 220, 300 );
	asim_line_to( ctx, 225, 400 );
	asim_move_to(ctx, 15, 460);  
	asim_line_to( ctx, 200, 450 );
	asim_line_to( ctx, 400, 480 );

	asim_move_to(ctx, 220, 0); 	  
	asim_set_brush( ctx, 2 ); 
	asim_line_to( ctx, 420, 200 ); 
	asim_line_to( ctx, 320, 10 );
	asim_line_to( ctx, 230, 300 );
	asim_line_to( ctx, 235, 400 );
	asim_move_to(ctx, 15, 470);    
	asim_line_to( ctx, 200, 460 );
	asim_line_to( ctx, 400, 490 );

#if 0
	asim_move_to(ctx, 10, 600); 	  
	asim_set_brush( ctx, 0 ); 
	asim_cube_bezier( ctx, 10, 400, 100, 570, 400, 600 ); 
	asim_move_to(ctx, 20, 610); 	  
	asim_set_brush( ctx, 1 ); 
	asim_cube_bezier( ctx, 20, 410, 110, 580, 410, 610 ); 
	asim_move_to(ctx, 30, 640); 	  
	asim_set_brush( ctx, 2 ); 
	asim_cube_bezier( ctx, 30, 440, 120, 610, 420, 640 ); 
	
	asim_move_to(ctx, 50, 660); 	  
	asim_set_brush( ctx, 0 ); 
	asim_cube_bezier( ctx, -20, 940, 350, 490, 300, 690 ); 
	asim_move_to(ctx, 40, 680); 	  
	asim_set_brush( ctx, 1 ); 
	asim_cube_bezier( ctx, -30, 960, 340, 510, 290, 710 ); 
	asim_move_to(ctx, 30, 700); 	  
	asim_set_brush( ctx, 2 ); 
	asim_cube_bezier( ctx, -40, 980, 330, 530, 280, 730 ); 
#endif
	asim_set_brush( ctx, 0 ); 
	asim_circle( ctx, 600, 110, 80, False );
	asim_circle( ctx, 600, 110, 50, False );
	asim_circle( ctx, 600, 110, 20, False );
	asim_circle( ctx, 600, 110, 1, False );

	asim_set_brush( ctx, 1 ); 
	asim_circle( ctx, 600, 110, 90, False );
	asim_circle( ctx, 600, 110, 60, False );
	asim_circle( ctx, 600, 110, 30, False );
	asim_circle( ctx, 600, 110, 5, False );
	
	asim_set_brush( ctx, 2 ); 
	asim_circle( ctx, 600, 110, 100, False );
	asim_circle( ctx, 600, 110, 70, False );
	asim_circle( ctx, 600, 110, 40, False );
	asim_circle( ctx, 600, 110, 10, False );

	asim_set_brush( ctx, 0 ); 
/*	asim_circle( ctx, -1000, -1000, 2000, False );
	asim_ellips( ctx, -1000, -1000, 2000, 500, -45, False );
 */
	asim_circle( ctx, 595, 550, 200, False );
	i = 30 ;

/*	for( k = 0 ; k < 100 ; ++k )  */
		for( i = 3 ; i < 180 ; i+=5 ) 
		{	
/*			asim_ellips2( ctx, 595, 550, 198, 40, i, False );
			asim_ellips( ctx, 595, 540, 198, 40, i, False );
			asim_ellips2( ctx, 595, 550, 198, 40, i+30, False );
			asim_ellips( ctx, 595, 540, 198, 40, i+30, False );
 */
			asim_ellips2( ctx, 595, 550, 198, 60, i, False );
/*			asim_ellips( ctx, 585, 550, 198, 40, i+50, False );
			asim_ellips( ctx, 595, 550, 198, 40, i, False ); 
 */
		}
#endif
	asim_circle( ctx, 705, 275, 90, True );


/*	asim_circle( ctx, -40000, 500, 40500, False );
	asim_circle( ctx, -10000, 500, 10499, False );

	asim_ellips2( ctx, -1000, 500, 1500, 100, 2, False );

	asim_flood_fill( ctx, 664, 166, 0, 126 ); 
	asim_flood_fill( ctx, 670, 77, 0, 126 ); 
	asim_flood_fill( ctx, 120, 80, 0, 126 ); 
	asim_flood_fill( ctx, 300, 460, 0, 126 ); 
*/
#if 1
	/* commit drawing : */
	apply_asdraw_context( drawing1, ctx, SCL_DO_ALPHA ); 
	
	layers_num = 2 ;
	layers = create_image_layers( layers_num );
	
	layers[0].im = back ;
	layers[1].im = drawing1 ;
	layers[0].clip_width = DRAW_TEST_SIZE ;
	layers[0].clip_height = DRAW_TEST_SIZE ;
	layers[1].clip_width = DRAW_TEST_SIZE ;
	layers[1].clip_height = DRAW_TEST_SIZE ;
		
	merged_im = merge_layers( asv, layers, layers_num,
		                      DRAW_TEST_SIZE, DRAW_TEST_SIZE,
							  ASA_ASImage,
							  0, ASIMAGE_QUALITY_DEFAULT );


	ASImage2file( merged_im, NULL, "test_asdraw.png", ASIT_Png, NULL );
	destroy_asimage( &merged_im );
#endif

	return 0;
}
	

#endif

