#include "apricot.h"
#include "Icon.h"
#include "Region.h"
#include "img_conv.h"
#include <Icon.inc>

#ifdef __cplusplus
extern "C" {
#endif


#undef  my
#define inherited CImage->
#define my  ((( PIcon) self)-> self)
#define var (( PIcon) self)

#define VAR_MATRIX var->current_state.matrix

static void
produce_mask( Handle self)
{
	Byte * area8 = var-> data;
	Byte * dest = var-> mask;
	Byte * src;
	Byte color = 0;
	RGBColor rgbcolor;
	int i, bpp2;
	int line8Size = LINE_SIZE(var->w, 8), areaLineSize = var-> lineSize;
	int bpp = var-> type & imBPP;
	int w = var-> w, h = var-> h;

	if ( var-> w == 0 || var-> h == 0) return;

	if ( var-> autoMasking == amNone) return;

	if ( var-> autoMasking == amMaskColor) {
		rgbcolor. b = var-> maskColor & 0xFF;
		rgbcolor. g = (var-> maskColor >> 8)  & 0xFF;
		rgbcolor. r = (var-> maskColor >> 16) & 0xFF;
		if ( bpp <= 8)
			color = cm_nearest_color( rgbcolor, var-> palSize, var-> palette);
	} else if ( var-> autoMasking == amMaskIndex) {
		if ( bpp > 8) return;
		color = var-> maskIndex;
		bzero( &rgbcolor, sizeof(rgbcolor));
	}

	if ( bpp == imMono) {
		/* mono case simplifies our task */
		int  j = var-> maskSize;
		Byte * mask = var-> mask;
		memcpy ( var-> mask, var-> data, var-> dataSize);
		if ( color == 0) {
			while ( j--) mask[ j] = ~mask[ j];
		}
		var-> palette[color]. r = var-> palette[color]. g = var-> palette[color]. b = 0;
		if ( color > 0)
			var-> type &= ~imGrayScale;
		return;
	}

	/* convert to 8 bit */
	switch ( bpp)
	{
	case im16:
	case im256:
	case imRGB:
		bpp2 = bpp;
		break;
	default:
		bpp2  = im256;
		areaLineSize = line8Size;
		if (!( area8 = allocb( var-> h * line8Size))) return;
		ic_type_convert( self, area8, var-> palette, im256 | ( var-> type & imGrayScale), &var-> palSize, false);
		break;
	}

	if ( var-> autoMasking == amAuto) {  /* calculate transparent color */
		Byte corners [4];
		Byte counts  [4] = {1, 1, 1, 1};
		RGBColor rgbcorners[4];
		int j = areaLineSize, k;

		/* retrieving corner pixels */
		switch ( bpp2) {
		case im16:
			corners[ 0] = area8[ 0] >> 4;
			corners[ 1] = area8[( w - 1) >> 1];
			corners[ 1] = (( w - 1) & 1) ? corners[ 1] & 0x0f : corners[ 1] >> 4;
			corners[ 2] = area8[ j * ( h - 1)] >> 4;
			corners[ 3] = area8[ j * ( h - 1) + (( w - 1) >> 1)];
			corners[ 3] = (( w - 1) & 1) ? corners[ 3] & 0x0f : corners[ 3] >> 4;
			for ( j = 0; j < 4; j++) {
				rgbcorners[j].r = var-> palette[ corners[ j]]. r;
				rgbcorners[j].g = var-> palette[ corners[ j]]. g;
				rgbcorners[j].b = var-> palette[ corners[ j]]. b;
			}
			break;
		case im256:
			corners[ 0] = area8[ 0];
			corners[ 1] = area8[ w - 1];
			corners[ 2] = area8[ j * ( h - 1)];
			corners[ 3] = area8[ j * ( h - 1) + w - 1];
			for ( j = 0; j < 4; j++) {
				rgbcorners[j].r = var-> palette[ corners[ j]]. r;
				rgbcorners[j].g = var-> palette[ corners[ j]]. g;
				rgbcorners[j].b = var-> palette[ corners[ j]]. b;
			}
			break;
		case imRGB:
			rgbcorners[0] = *(PRGBColor)( area8);
			rgbcorners[1] = *(PRGBColor)( area8 + ( w - 1) * 3);
			rgbcorners[2] = *(PRGBColor)( area8 + j * ( h - 1));
			rgbcorners[3] = *(PRGBColor)( area8 + j * ( h - 1) + ( w - 1) * 3);
			for ( j = 0; j < 4; j++) corners[j] = j;
			#define rgbcmp(x,y) ((rgbcorners[x].r == rgbcorners[y].r) &&\
					(rgbcorners[x].g == rgbcorners[y].g) &&\
					(rgbcorners[x].b == rgbcorners[y].b))
			if ( rgbcmp(1,0)) corners[1] = 0;
			if ( rgbcmp(2,0)) corners[2] = 0;
			if ( rgbcmp(3,0)) corners[3] = 0;
			if ( rgbcmp(2,1)) corners[2] = corners[1];
			if ( rgbcmp(3,1)) corners[3] = corners[1];
			if ( rgbcmp(3,2)) corners[3] = corners[2];
			#undef rgbcmp
			break;
		}

		/* preliminary comparison to a transparent color candidate ( hack ) */
		for ( j = 0; j < 4; j++) {
			if (
				(( rgbcorners[j]. b) == 0) &&
				(( rgbcorners[j]. g) == 128) &&
				(( rgbcorners[j]. r) == 128)) {
				color = corners[ j];
				rgbcolor = rgbcorners[ j];
				goto colorFound;
			}
		}

		color = corners[ 3]; /* our wild (and possibly bad) guess */
		rgbcolor = rgbcorners[ 3];

		/* sorting */
		for ( j = 0; j < 3; j++)
			for (k = 0; k < 3; k++)
				if ( corners[ k] < corners[ k + 1]) {
					Byte l = corners[ k];
					corners[ k] = corners[ k + 1];
					corners[ k + 1] = l;
				}
		/* forming maximum's vector */
		i = 0;
		for (j = 0; j < 3; j++) if ( corners[ j + 1] == corners[ j]) counts[ i]++; else i++;
		for (j = 0; j < 3; j++)
			for (k = 0; k < 3; k++)
				if ( counts[ k] < counts[ k + 1]) {
					Byte l = counts[ k];
					counts[ k] = counts[ k + 1];
					counts[ k + 1] = l;
					l = corners[ k];
					corners[ k] = corners[ k + 1];
					corners[ k + 1] = l;
				}
		if (( counts[0] > 2) || (( counts[0] == 2) && ( counts[1] == 1))) {
			color = corners[ 0];
			rgbcolor = rgbcorners[ 0];
		} else {
			int colorsToCompare = ( counts[0] == 2) ? 2 : 4;

			/* compare to that yellowish hue... */
			for ( j = 0; j < colorsToCompare; j++)
				if (( rgbcorners[j]. b < 20) &&
					( rgbcorners[j]. r > 100) &&
					( rgbcorners[j]. r < 150) &&
					( rgbcorners[j]. g > 100) &&
					( rgbcorners[j]. g < 150))
				{
					color = corners[ j];
					rgbcolor = rgbcorners[ j];
					goto colorFound;
				}

			/* compare to MicroSoft Pink */
			for ( j = 0; j < colorsToCompare; j++)
				if (( rgbcorners[j]. g < 20) &&
					( rgbcorners[j]. r > 200) &&
					( rgbcorners[j]. b > 200))
				{
					color = corners[ j];
					rgbcolor = rgbcorners[ j];
					goto colorFound;
				}
		}
colorFound:;
	}

	/* processing transparency */
	memset( var-> mask, 0, var-> maskSize);
	src  = area8;
	for ( i = 0; i < h; i++, dest += var-> maskLine, src += areaLineSize) {
		register int j;
		switch ( bpp2) {
		case im16:
			{
				int max = ( w >> 1) + ( w & 1);
				register int k = 0;
				for ( j = 0; j < max; j++) {
					if ( color == ( src[ j] >> 4))
						dest[ k >> 3] |= 1 << (7 - ( k & 7));
					if ( color == ( src[ j] & 0x0f))
						dest[ k >> 3] |= 1 << (6 - ( k & 7));
					k += 2;
				}
			}
			break;
		case imRGB:
			{
				register PRGBColor r = ( PRGBColor) src;
				for ( j = 0; j < w; j++) {
					if (( r-> r == rgbcolor.r) &&
						( r-> g == rgbcolor.g) &&
						( r-> b == rgbcolor.b))
						dest[ j >> 3] |= 1 << (7 - ( j & 7));
					r++;
				}
			}
			break;
		default:
			for ( j = 0; j < w; j++)
				if ( src[ j] == color)
					dest[ j >> 3] |= 1 << (7 - ( j & 7));
		}
	}

	/* finalize */
	if ( var-> data != area8) free( area8);
	if ( var-> palSize > color && bpp <= im256) {
		var-> palette[ color]. r = var-> palette[ color]. b = var-> palette[ color]. g = 0;
		if ( color > 0)
			var-> type &= ~imGrayScale;
	}
}

void
Icon_init( Handle self, HV * profile)
{
	dPROFILE;
	inherited init( self, profile);
	my-> set_maskType( self, pget_i( maskType));
	my-> update_change(self); /* instantiate mask memory */

	my-> set_maskColor( self, pget_i( maskColor));
	my-> set_maskIndex( self, pget_i( maskIndex));
	my-> set_autoMasking( self, pget_i( autoMasking));
	my-> set_mask( self, pget_sv( mask));
	CORE_INIT_TRANSIENT(Icon);
}

static Bool
copy_mask_from_image( Handle self, SV * svmask)
{
	Handle h;
	PImage i;
	Byte *m, bpp;

	if ( !( h = gimme_the_mate(svmask)) || !kind_of(h, CImage)) {
		warn("Icon.mask: not a Prima::Image object");
		return false;
	}
	i = (PImage) h;
	if ( i-> h != var-> h || i-> w != var-> w ) {
		warn("Icon.mask: mask size doesn't match");
		return false;
	}

	bpp = i-> type & imBPP;
	if (bpp == var-> maskType ) {
		memcpy( var-> mask, i->data, var->maskSize );
		return true;
	}

	if ( bpp != 1 && bpp != 8 ) {
		/* down/upgrade to imByte */
		Handle h2;
		PImage i2;
		if ( !( h2 = CImage(h)->dup(h)))
			return false;
		CImage(h2)->set_type(h2, imByte);
		i2 = (PImage) h2;
		if ( !( m = malloc(i2-> dataSize ))) {
			Object_destroy(h2);
			return false;
		}
		memcpy( m, i2->data, i2->dataSize);
		Object_destroy(h2);
		bpp = 8;
	} else {
		if ( !( m = malloc(i-> dataSize )))
			return false;
		memcpy( m, i->data, i->dataSize);
	}

	free( var-> mask );
	var-> mask     = m;
	var-> maskType = bpp;
	var-> maskLine = LINE_SIZE( var-> w, bpp );
	var-> maskSize = var-> maskLine * var-> h;
	return true;
}

SV *
Icon_mask( Handle self, Bool set, SV * svmask)
{
	int am = var-> autoMasking;
	if ( var-> stage > csFrozen) return NULL_SV;
	if ( !set) {
		SV * sv = newSV_type(SVt_PV);
		SvREADONLY_on(sv);
		SvLEN_set(sv, 0); /* So Perl won't free it. */
		SvPV_set(sv, (char*)var-> mask);
		SvCUR_set(sv, var-> maskSize);
		SvPOK_only(sv);
		return sv;
	}

	if ( !SvOK(svmask)) return NULL_SV;

	if ( SvROK(svmask)) {
		if ( !copy_mask_from_image( self, svmask ))
			return NULL_SV;
	} else {
		STRLEN maskSize;
		void * mask;
		mask = SvPV( svmask, maskSize);
		if ( is_opt( optInDraw) || maskSize <= 0) return NULL_SV;
		memcpy( var-> mask, mask, (maskSize > (STRLEN)var-> maskSize) ? (STRLEN)var-> maskSize : maskSize);
	}
	var-> autoMasking = amNone;
	my-> update_change( self);
	var-> autoMasking = am;
	return NULL_SV;
}

int
Icon_autoMasking( Handle self, Bool set, int autoMasking)
{
	if ( !set)
		return var-> autoMasking;
	if ( var-> autoMasking == autoMasking) return 0;
	var-> autoMasking = autoMasking;
	if ( is_opt( optInDraw)) return 0;
	my-> update_change( self);
	return 0;
}

/*

1-bit format is a true AND-mask: 0 is alpha=1, 1 is alpha=0
8-bit format has alpha value from 0 to 255.

Note that inter-conversion between these negates the pixel values

*/
Byte*
Icon_convert_mask( Handle self, int type )
{
	int i;
	int srcLine = LINE_SIZE( var-> w, var-> maskType );
	int dstLine = LINE_SIZE( var-> w, type );
	Byte colorref[256], *src = var-> mask, *dst, *ret;
	RGBColor palette[2];

	if ( type == var-> maskType )
		croak("invalid usage of Icon::convert_mask");
	if ( !( ret = malloc( dstLine * var-> h))) {
		warn("Icon::convert_mask: cannot allocate %d bytes", dstLine * var-> h);
		return NULL;
	}
	bzero( ret, dstLine * var-> h);

	switch (type) {
	case imbpp1:
		/* downgrade */
		memset( colorref,       1, 255);
		memset( colorref + 255, 0, 1  );
		for ( i = 0, dst = ret; i < var->h; i++, src += srcLine, dst += dstLine) {
			memset( dst, 0, dstLine );
			bc_byte_mono_cr( src, dst, var-> w, colorref);
		}
		break;
	case imbpp8:
		/* upgrade */
		memset( &palette[0], 0xff, sizeof(RGBColor));
		memset( &palette[1], 0x00, sizeof(RGBColor));
		for ( i = 0, dst = ret; i < var->h; i++, src += srcLine, dst += dstLine)
			bc_mono_graybyte( src, dst, var-> w, palette);
		break;
	default:
		croak("invalid usage of Icon::convert_mask");
	}
	return ret;
}

static void
ic_mask_downgrade( Handle self )
{
	int i, type = var->type & imBPP, bpp = (var->type & imBPP ) / 8;
	Byte *src = var->data, *mask = var->mask;
	for ( i = 0; i < var-> h; i++, src += var->lineSize, mask += var->maskLine) {
		switch (type) {
		/* remove alpha pixels with mask as bits */
		case 1: {
			register Byte *s = src, *m = mask;
			register unsigned int ls = var->lineSize;
			while ( ls-- > 0 )
				*(s++) &= *(m++);
			break;
		}

		/* remove alpha pixels with mask as bytes */
		case 4:
			bc_a8mask_nibble( mask, src, var->w);
			break;
		default:
			bc_a8mask_multibyte( mask, src, var->w, bpp);
		}
	}
}

int
Icon_maskType( Handle self, Bool set, int type)
{
	if ( !set)
		return var-> maskType;

	type &= ~imGrayScale;
	if ( var-> maskType == type) return 0;

	if ( var-> mask && var->maskType == imbpp8 && (var->type & imBPP) != 1)
		ic_mask_downgrade(self);

	switch ( type ) {
	case imbpp1:
	case imbpp8:
		if ( var-> mask ) {
			Byte * new_mask = Icon_convert_mask(self, type);
			free( var-> mask );
			var-> mask = new_mask;
			var-> maskLine = LINE_SIZE( var-> w, type );
			var-> maskSize = var-> maskLine * var-> h;
		}
		break;
	default:
		croak("mask type must be either im::bpp1 or im::bpp8");
	}

	if ( var-> mask && var->maskType == imbpp8 && (var->type & imBPP) == 1)
		ic_mask_downgrade(self);

	var-> maskType = type;
	return 1;
}

Color
Icon_maskColor( Handle self, Bool set, Color color)
{
	if ( !set)
		return var-> maskColor;
	if ( var-> maskColor == color) return 0;
	var-> maskColor = color;
	if ( is_opt( optInDraw)) return 0;
	if ( var-> autoMasking == amMaskColor)
		my-> update_change( self);
	return clInvalid;
}

int
Icon_maskIndex( Handle self, Bool set, int index)
{
	if ( !set)
		return var-> maskIndex;
	var-> maskIndex = index;
	if ( is_opt( optInDraw)) return 0;
	if ( var-> autoMasking == amMaskIndex)
		my-> update_change( self);
	return -1;
}

int
Icon_maskPixel( Handle self, Bool set, int x, int y, int pixel)
{
	Point pt;

	pt = prima_matrix_apply_to_int( VAR_MATRIX, x, y );
	x = pt.x;
	y = pt.y;
	if (x >= var->w || x < 0 || y >= var->h || y < 0)
		return clInvalid;

	if (!set) {
		if ( is_opt( optInDraw) ) {
			if ( var-> maskType == imbpp8 )
				return apc_gp_get_mask_pixel( self, x, y );
		}

		switch (var->maskType) {
		case imbpp1: {
			Byte p = var->mask[ var->maskLine * y + ( x >> 3 )];
			p = (p >> (7 - (x & 7))) & 1;
			return p ? 0xff : 0;
		}
		case imbpp8: {
			Byte p = var->mask[ var->maskLine * y + x];
			return p;
		}
		default:
			return clInvalid;
		}
	} else {
		Bool do_update = true;
		if ( pixel < 0 ) pixel = 0;
		if ( pixel > 255 ) pixel = 255;
		if ( is_opt( optInDraw)) {
			if ( var-> maskType == imbpp8 )
				return apc_gp_set_mask_pixel( self, x, y, pixel );
			else
				do_update = false;
		}

		switch (var->maskType) {
		case imbpp1 : {
			int x1 = 7 - ( x & 7 );
			Byte p = (pixel > 0) ? 1 : 0;
			Byte *pd = var->mask + (var->maskLine * y + ( x >> 3));
			*pd &= ~(1 << x1);
			*pd |= p << x1;
			break;
		}
		case imbpp8:
			var->mask[var->maskLine * y + x] = pixel & 0xff;
			break;
		default:
			return 0;
		}
		if ( do_update ) my->update_change( self);
		return 0;
	}
}

void
Icon_update_change( Handle self)
{
	if ( var-> updateLock > 0 ) return;

	inherited update_change( self);

	if ( var-> maskType == 0) return; /* inside inherited init */

	if ( var-> autoMasking == amNone) {
		int maskLine = LINE_SIZE( var-> w, var-> maskType );
		int maskSize = maskLine * var-> h;
		if ( maskLine != var-> maskLine || maskSize != var-> maskSize) {
			free( var-> mask);
			var-> maskLine = maskLine;
			if (!( var-> mask = allocb( var-> maskSize = maskSize)) && maskSize > 0) {
				my-> make_empty( self);
				warn("Not enough memory: %d bytes", maskSize);
			} else
				memset( var-> mask, 0, maskSize);
		}
		return;
	}

	free( var-> mask);
	if ( var-> data)
	{
		int oldtype = var-> maskType;
		var-> maskType = imbpp1;
		var-> maskLine = LINE_SIZE( var-> w, var-> maskType );
		var-> maskSize = var-> maskLine * var-> h;
		if ( !( var-> mask = allocb( var-> maskSize)) && var-> maskSize > 0) {
			my-> make_empty( self);
			warn("Not enough memory: %d bytes", var-> maskSize);
			return;
		}
		bzero( var-> mask, var-> maskSize );
		produce_mask( self);
		if ( oldtype != imbpp1 )
			my-> set_maskType( self, oldtype);
	}
	else
		var-> mask = NULL;
}

void
Icon_stretch( Handle self, int width, int height)
{
	Byte * newMask = NULL;
	int lineSize, oldW = var-> w, oldH = var-> h, am = var-> autoMasking;
	if ( var->stage > csFrozen) return;
	if ( width  >  65535) width  =	65535;
	if ( height >  65535) height =	65535;
	if ( width  < -65535) width  = -65535;
	if ( height < -65535) height = -65535;
	if (( width == var->w) && ( height == var->h)) return;
	if ( width == 0 || height == 0)
	{
		my->create_empty( self, 0, 0, var->type);
		return;
	}

	if ( var-> mask && var-> maskType == imbpp1 && var-> scaling > istBox ) {
		/* upgrade to bpp8 */
		my-> set_maskType( self, imbpp8 );
	}


	lineSize = LINE_SIZE( abs( width), var-> maskType );
	newMask  = allocb( lineSize * abs( height));
	if ( newMask == NULL && lineSize > 0) {
		my-> make_empty( self);
		croak("Icon::stretch: cannot allocate %d bytes", lineSize * abs( height));
	}
	var-> autoMasking = amNone;
	if ( var-> mask) {
		char error[256];
		if ( !ic_stretch( var->maskType | imGrayScale, var-> mask, oldW, oldH, newMask, width, height, var->scaling, error)) {
			free(newMask);
			my-> make_empty( self);
			croak("%s", error);
		}
	}
	inherited stretch( self, width, height);
	free( var-> mask);


	var->mask = newMask;
	var->maskLine = lineSize;
	var->maskSize = lineSize * abs( height);
	inherited stretch( self, width, height);
	var-> autoMasking = am;
}

Handle
Icon_create_from_image( Handle self, int maskType, SV * mask_fill)
{
	Handle obj;
	PIcon dst;
	PImage src = (PImage) self;

	obj = ( Handle) create_object("Prima::Icon", "");
	CIcon( obj)-> create_empty_icon( obj, var->w, var->h, var->type, maskType);
	dst = (PIcon) obj;

	dst->owner      = src->owner;
	dst->conversion = src->conversion ;
	dst->scaling    = src->scaling;
	dst->palSize    = src-> palSize;
	dst->options.optPreserveType = src->options.optPreserveType;
	dst->autoMasking = amNone;
	memcpy( dst-> palette, src->palette, 768);
	memcpy( dst-> data   , src->data   , src->dataSize);
	memcpy( dst-> stats, src->stats, sizeof( src->stats));

	if ( mask_fill && SvOK(mask_fill) && SvPOK(mask_fill)) {
		STRLEN len;
		int sz;
		Byte *bits;

		bits = (Byte*) SvPV(mask_fill, len);
		sz  = (len > dst->maskSize) ? dst->maskSize : len;
		if ( sz == 0 ) {
			/* well... */
		} else if ( sz == 1 ) {
			memset( dst-> mask, *bits, dst->maskSize );
		} else {
			Byte *fill = dst->mask;
			int left   = dst->maskSize;
			while (left > 0) {
				memcpy( fill, bits, (left > sz) ? left : sz);
				left -= sz;
				fill += sz;
			}
		}
	} else if ( maskType == imbpp8)
		memset( dst-> mask, 0xff, dst-> maskSize );

	return obj;
}

void
Icon_create_empty( Handle self, int width, int height, int type)
{
	my-> create_empty_icon( self, width, height, type, imbpp1);
}

void
Icon_create_empty_icon( Handle self, int width, int height, int type, int maskType)
{
	inherited create_empty( self, width, height, type);
	free( var-> mask);
	if ( var-> data)
	{
		var-> maskType = maskType;
		var-> maskLine = LINE_SIZE( var-> w, var-> maskType );
		var-> maskSize = var-> maskLine * var-> h;
		if ( !( var-> mask = allocb( var-> maskSize)) && var-> maskSize > 0) {
			my-> make_empty( self);
			warn("Not enough memory: %d bytes", var-> maskSize);
			return;
		}
		memset( var-> mask, 0, var-> maskSize);
	}
	else {
		var-> mask = NULL;
		var-> maskLine = 0;
		var-> maskSize = 0;
	}
}

Handle
Icon_dup( Handle self)
{
	Handle h = inherited dup( self);
	PIcon  i = ( PIcon) h;

	if ( var-> maskType != imbpp1 ) {
		Byte * p;
		if ( !(p = realloc( i-> mask, var-> maskSize ))) {
			warn("Icon::dup: cannot allocate %d bytes", var->maskSize);
			Object_destroy(h);
			return NULL_HANDLE;
		}
		i-> mask = p;
	}

	i-> autoMasking = var-> autoMasking;
	i-> maskType	= var-> maskType;
	i-> maskColor	= var-> maskColor;
	i-> maskIndex	= var-> maskIndex;
	i-> maskSize	= var-> maskSize;
	i-> maskLine	= var-> maskLine;

	memcpy( i-> mask, var-> mask, var-> maskSize);
	return h;
}

IconHandle
Icon_split( Handle self)
{
	IconHandle ret = {0,0};
	PImage i;
	HV * profile = newHV();
	char* className = var-> self-> className;

	pset_H( owner,	      var-> owner);
	pset_i( width,	      var-> w);
	pset_i( height,       var-> h);
	pset_i( type,	      var->maskType|imGrayScale);
	pset_i( conversion,   var->conversion);
	pset_i( scaling,      var->scaling);
	pset_i( preserveType, is_opt( optPreserveType));

	ret. andMask = Object_create( "Prima::Image", profile);
	sv_free(( SV *) profile);
	i = ( PImage) ret. andMask;
	memcpy( i-> data, var-> mask, var-> maskSize);
	i-> self-> update_change(( Handle) i);

	var-> self-> className = inherited className;
	ret. xorMask	     = inherited dup( self);
	(void) hv_delete(( HV*)SvRV( PImage(ret. xorMask)-> mate), "extras", 6, G_DISCARD);
	var-> self-> className = className;

	--SvREFCNT( SvRV( i-> mate));
	return ret;
}

void
Icon_combine( Handle self, Handle xorMask, Handle andMask)
{
	Bool killAM = 0;
	int maskType;

	if ( !kind_of( xorMask, CImage) || !kind_of( andMask, CImage))
		return;

	var-> autoMasking = amNone;
	maskType = PImage( andMask)-> type & imBPP;
	if ( maskType != imbpp1 && maskType != imbpp8) {
		killAM = 1;
		andMask = CImage( andMask)-> dup( andMask);
		CImage( andMask)-> set_type( andMask, imbpp1);
		maskType = imbpp1;
	}

	my-> create_empty_icon( self, PImage( xorMask)-> w, PImage( xorMask)-> h, PImage( xorMask)-> type, maskType);

	if ( var-> w != PImage( andMask)-> w || var-> h != PImage( andMask)-> h) {
		if ( !killAM) {
			killAM = 1;
			andMask = CImage( andMask)-> dup( andMask);
		}
		CImage( andMask)-> set_size( andMask, my-> get_size( self));
	}

	memcpy( var-> data, PImage( xorMask)-> data, var-> dataSize);
	memcpy( var-> mask, PImage( andMask)-> data, var-> maskSize);
	memcpy( var-> palette, PImage( xorMask)-> palette, 768);
	var-> palSize = PImage( xorMask)-> palSize;

	if ( killAM) Object_destroy( andMask);

	my-> update_change( self);
}

void
Icon_set( Handle self, HV * profile)
{
	dPROFILE;

	if ( pexist( maskType) && pexist( mask )) {
		SV *svmask   = pget_sv(mask);
		int maskType = pget_i(maskType);
		if ( svmask && SvOK(svmask) && SvROK(svmask)) {
			if ( !copy_mask_from_image(self, svmask))
				goto NO_MASK;
			my-> set_maskType( self, maskType);
		} else if ( maskType != var->maskType) {
			free( var-> mask );
			var-> mask = NULL;
			my-> set_maskType( self, maskType);
			my-> set_mask( self, svmask);
		} else {
			my-> set_mask( self, svmask);
		}
		pdelete( maskType);
		pdelete( mask);
	NO_MASK:
		;
	}

	inherited set ( self, profile);
}

Handle
Icon_extract( Handle self, int x, int y, int width, int height)
{
	int nodata = 0;
	Handle h = inherited extract( self, x, y, width, height);
	PIcon i = (PIcon) h;
	unsigned char * mask = var->mask;
	int ls = var->maskLine;

	if ( var->w == 0 || var->h == 0) return h;
	if ( x < 0) x = 0;
	if ( y < 0) y = 0;
	if ( x >= var->w) x = var->w - 1;
	if ( y >= var->h) y = var->h - 1;
	if ( width  + x > var->w) width  = var->w - x;
	if ( height + y > var->h) height = var->h - y;
	if ( width <= 0 ) {
		width = 1;
		nodata = 1;
	}
	if ( height <= 0 ) {
		height = 1;
		nodata = 1;
	}
	if ( nodata ) return h;

	CIcon(h)->set_autoMasking(h, amNone);
	CIcon(h)->set_maskType(h, var-> maskType);
	CIcon(h)->set_maskColor(h, var-> maskColor);

	if ( var->maskType == imbpp8)
		while ( height-- > 0) {
			memcpy( i-> mask + height * i-> maskLine, mask + ( y + height) * ls + x, width);
	} else {
		while ( height-- > 0)
			bc_mono_copy( mask + ( y + height) * ls, i-> mask + height * i-> maskLine, x, width);
	}
	return h;
}

void
Icon_make_empty( Handle self)
{
	inherited make_empty(self);
	free( var->mask);
	var->mask = NULL;
}

Handle
Icon_bitmap( Handle self)
{
	Handle h;
	Point s;
	HV * profile;

	if ( !apc_sys_get_value(svLayeredWidgets))
		return inherited bitmap(self);

	profile = newHV();
	pset_H( owner,	      var->owner);
	pset_i( width,	      var->w);
	pset_i( height,       var->h);
	pset_sv_noinc( palette,     my->get_palette( self));
	pset_i( type,	      dbtLayered);
	h = Object_create( "Prima::DeviceBitmap", profile);
	sv_free(( SV *) profile);
	s = CDrawable( h)-> get_size( h);
	CDrawable( h)-> put_image_indirect( h, self, 0, 0, 0, 0, s.x, s.y, s.x, s.y, ropSrcCopy);
	--SvREFCNT( SvRV( PDrawable( h)-> mate));

	return h;
}

void
Icon_premultiply_alpha( Handle self, SV * alpha)
{
	if ( !alpha || !SvOK( alpha )) {
		int type = var-> maskType;
		Image dummy;
		/* multiply with self */
		if ( var-> maskType != imbpp8 )
			my-> set_maskType( self, imbpp8 );

		img_fill_dummy( &dummy, var-> w, var-> h, imByte, var-> mask, std256gray_palette);
		img_premultiply_alpha_map( self, (Handle) &dummy);

		if ( is_opt( optPreserveType) && var-> maskType != imbpp8 )
			my-> set_maskType( self, type );
	} else
		inherited premultiply_alpha( self, alpha );
}

Bool
Icon_bar_alpha( Handle self, int alpha, int x1, int y1, int x2, int y2)
{
	Image dummy;
	ImgPaintContext ctx;
	Bool free_rgn = false;
	PRegionRec rgn = var->regionData;

	if (opt_InPaint)
		return apc_gp_alpha( self, alpha, x1, y1, x2, y2);

	if ( x1 < 0 && y1 < 0 && x2 < 0 && y2 < 0 ) {
		x1 = 0;
		y1 = 0;
		x2 = var-> w - 1;
		y2 = var-> h - 1;
	} else {
		NRect nrect = {x1,y1,x2,y2};
		NPoint npoly[4];

		if ( prima_matrix_is_square_rectangular( VAR_MATRIX, &nrect, npoly)) {
			x1 = floor(nrect.left   + .5);
			y1 = floor(nrect.bottom + .5);
			x2 = floor(nrect.right  + .5);
			y2 = floor(nrect.top    + .5);
		} else {
			Handle rgn1;
			PRegionRec rgndata;
			int i;
			Point poly[4];

			prima_matrix_apply2_to_int( VAR_MATRIX, npoly, poly, 4 );
			x1 = x2 = poly[0].x;
			y1 = y2 = poly[0].y;
			for ( i = 1; i < 4; i++) {
				if ( x1 > poly[i].x ) x1 = poly[i].x;
				if ( y1 > poly[i].y ) y1 = poly[i].y;
				if ( x2 < poly[i].x ) x2 = poly[i].x;
				if ( y2 < poly[i].y ) y2 = poly[i].y;
			}
			rgndata = img_region_polygon( poly, 4, fmWinding | fmOverlay );
			rgn1 = Region_create_from_data( NULL_HANDLE, rgndata );
			free( rgndata );

			if ( var-> regionData ) {
				Handle rgn2 = Region_create_from_data( NULL_HANDLE, var->regionData );
				Region_combine(rgn1, rgn2, rgnopUnion);
				Object_destroy(rgn2);
			}
			rgn = Region_update_change(rgn1, true);
			Object_destroy(rgn1);
			free_rgn = true;
		}
	}

	img_fill_dummy( &dummy, var-> w, var-> h, var-> maskType | imGrayScale, var-> mask, std256gray_palette);

	bzero(&ctx, sizeof(ctx));
	memset( ctx.pattern, 0xff, sizeof(ctx.pattern));
	ctx.patternOffset.x = ctx.patternOffset.y = 0;
	ctx.transparent     = false;
	ctx. color[0]       = alpha & 0xff;
	ctx. rop            = ropCopyPut;
	ctx. region         = rgn;
	img_bar((Handle) &dummy, x1, y1, x2 - x1 + 1, y2 - y1 + 1, &ctx);

	if ( free_rgn ) free(rgn);

	return true;
}

Bool
Icon_rotate( Handle self, double degrees, SV * svfill)
{
	Bool ok;
	Image dummy;
	int autoMasking = var->autoMasking, maskType = var->maskType;
	var->autoMasking = amNone;

	var->updateLock++;

	my->set_maskType(self, imbpp8);

	img_fill_dummy( &dummy, var->w, var->h, imByte, var->mask, NULL);
	dummy.scaling = var->scaling;
	dummy.mate    = var->mate;

	ok = inherited rotate(self, degrees, NULL_SV);
	if ( ok ) {
		ok = Image_rotate((Handle) &dummy, degrees, NULL_SV);
		if ( ok ) {
			var-> mask     = dummy.data;
			var-> maskLine = dummy. lineSize;
			var-> maskSize = dummy. dataSize;
			if ( var->w != dummy.w || var->h != dummy.h)
				croak("panic: icon object inconsistent after rotation");
		}
	}

	if (maskType != imbpp8 && is_opt( optPreserveType))
		my-> set_maskType( self, maskType);
	var->updateLock--;
	my->update_change(self);
	var->autoMasking = autoMasking;
	return ok;
}

Bool
Icon_matrix_transform( Handle self, Matrix matrix, ColorPixel fill, Point *aperture)
{
	Bool ok;
	Image dummy;
	int autoMasking = var->autoMasking, maskType = var->maskType;
	ColorPixel icon_fill;
	var->autoMasking = amNone;

	var->updateLock++;

	my->set_maskType(self, imbpp8);

	img_fill_dummy( &dummy, var->w, var->h, imByte, var->mask, NULL);
	dummy.scaling = var->scaling;
	dummy.mate    = var->mate;

	bzero(&icon_fill, sizeof(icon_fill));

	ok = inherited matrix_transform(self, matrix, icon_fill, aperture);
	if ( ok ) {
		Image i;
		ok = img_2d_transform((Handle) &dummy, matrix, icon_fill, &i, NULL );
		if ( ok ) {
			free( var-> mask );
			var-> mask     = i.data;
			var-> maskLine = i.lineSize;
			var-> maskSize = i.dataSize;
			if ( var->w != i.w || var->h != i.h)
				croak("panic: icon object inconsistent after 2d transform");
		}
		return false;
	}

	if (maskType != imbpp8 && is_opt( optPreserveType))
		my-> set_maskType( self, maskType);
	var->updateLock--;
	my->update_change(self);
	var->autoMasking = autoMasking;
	return ok;
}

int
Icon_get_effective_rop( Handle self, int rop)
{
	if ( rop == ropDefault )
		rop = (var->maskType == 1) ? ropCopyPut : ropBlend;
	return inherited get_effective_rop(self, rop);
}

void
Icon_begin_preserve_type( Handle self, PImagePreserveTypeRec save)
{
	inherited begin_preserve_type(self, save);
	save->mask   = var->maskType;

}

void
Icon_end_preserve_type( Handle self, PImagePreserveTypeRec save)
{
	inherited end_preserve_type(self, save);
	if ( save->enabled && var->maskType != save->mask)
		my->set_maskType( self, save->mask);
}

#ifdef __cplusplus
}
#endif
