#include "apricot.h"
#include "Icon.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)
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);
}
SV *
Icon_mask( Handle self, Bool set, SV * svmask)
{
STRLEN maskSize;
void * mask;
int am = var-> autoMasking;
if ( var-> stage > csFrozen) return nilSV;
if ( !set)
return newSVpvn(( char *) var-> mask, var-> maskSize);
mask = SvPV( svmask, maskSize);
if ( is_opt( optInDraw) || maskSize <= 0) return nilSV;
memcpy( var-> mask, mask, maskSize > var-> maskSize ? var-> maskSize : maskSize);
var-> autoMasking = amNone;
my-> update_change( self);
var-> autoMasking = am;
return nilSV;
}
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;
}
switch (type) {
case imbpp1:
/* downgrade */
memset( colorref, 1, 1 );
memset( colorref + 1, 0, 255 );
for ( i = 0, dst = ret; i < var->h; i++, src += srcLine, dst += 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;
}
int
Icon_maskType( Handle self, Bool set, int type)
{
if ( !set)
return var-> maskType;
type &= ~imGrayScale;
if ( var-> maskType == type) return 0;
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");
}
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;
}
void
Icon_update_change( Handle self)
{
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;
}
produce_mask( self);
if ( oldtype != imbpp1 )
my-> set_maskType( self, oldtype);
}
else
var-> mask = nil;
}
void
Icon_stretch( Handle self, int width, int height)
{
Byte * newMask = nil;
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;
}
lineSize = LINE_SIZE( abs( width), var-> maskType );
newMask = allocb( lineSize * abs( height));
if ( newMask == nil && lineSize > 0) {
my-> make_empty( self);
croak("Icon::stretch: cannot allocate %d bytes", lineSize * abs( height));
}
var-> autoMasking = amNone;
if ( var-> mask) {
Bool hScaling, vScaling;
if ( var-> scaling <= istBox ) {
hScaling = var->scaling & istBoxX;
vScaling = var->scaling & istBoxY;
} else {
hScaling = vScaling = 1;
}
ic_stretch( var->maskType, var-> mask, oldW, oldH, newMask, width, height, hScaling, vScaling);
}
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;
}
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 = nil;
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 nilHandle;
}
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);
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)) {
int maskType = pget_i(maskType);
if ( maskType == var-> maskType ) pdelete( maskType );
}
if ( pexist( maskType) && pexist( mask ))
{
free( var-> mask );
var-> mask = nil;
my-> set_maskType( self, pget_i( maskType));
my-> set_mask( self, pget_sv( mask));
pdelete( maskType);
pdelete( 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;
}
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 || ( SvTYPE( alpha ) == SVt_NULL)) {
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_alpha( Handle self, int alpha, int x1, int y1, int x2, int y2)
{
Image dummy;
Byte alpha_a8;
if (opt_InPaint)
return apc_gp_alpha( self, alpha, x1, y1, x2, y2);
alpha_a8 = alpha;
img_fill_dummy( &dummy, var-> w, var-> h, var-> maskType | imGrayScale, var-> mask, std256gray_palette);
img_bar((Handle) &dummy, x1, y1, x2 - x1 + 1, y2 - y1 + 1, ropCopyPut, (void*)&alpha_a8);
return true;
}
#ifdef __cplusplus
}
#endif