#include "img.h"
#define Drawable XDrawable
#define Font XFont
#define Window XWindow
#undef FUNC
#undef Bool
#define Bool I32
#include <X11/xpm.h>
#undef Font
#undef Drawable
#undef Bool
#undef Window
#define ComplexShape 0
#define XBool int
#undef Complex
#undef FUNC
#include "Icon.h"
#ifdef __cplusplus
extern "C" {
#endif
static char * xpmext[] = { "xpm",
#if PRIMA_PLATFORM == apcUnix
"xpm.gz", "xpm.Z", /* libXpm claims that it can run gzip */
#endif
nil };
static int xpmbpp[] = {
imbpp8,
imbpp24,
imbpp4,
imbpp1,
0 };
static char * loadOutput[] = {
"hotSpotX",
"hotSpotY",
"hintsComment",
"colorsComment",
"pixelsComment",
"transparentColors",
"extensions",
nil
};
static ImgCodecInfo codec_info = {
"X Pixmap",
"GROUPE BULL",
XpmFormat, XpmVersion, /* version */
xpmext, /* extension */
"X Pixmap File", /* file type */
"XPM", /* short type */
nil, /* features */
"", /* module */
"", /* package */
IMG_LOAD_FROM_FILE | IMG_SAVE_TO_FILE,
xpmbpp, /* save types */
loadOutput
};
static void *
init( PImgCodecInfo * info, void * param)
{
*info = &codec_info;
return (void*)1;
}
typedef struct _LoadRec {
XpmImage image;
XpmInfo info;
RGBColor *palette;
Byte *trans_table;
Byte xpalette;
} LoadRec;
#define outcm(dd) snprintf( fi-> errbuf, 256, "No enough memory (%d bytes)", (int)(dd))
#define outc(x) strncpy( fi-> errbuf, x, 256)
static void *
open_load( PImgCodec instance, PImgLoadFileInstance fi)
{
LoadRec * l;
XpmImage image;
XpmInfo info;
info. valuemask = XpmComments | XpmColorTable | XpmExtensions;
switch ( XpmReadFileToXpmImage( fi-> fileName, &image, &info)) {
case XpmSuccess:
break;
case XpmNoMemory:
fi-> stop = true;
return nil;
case XpmFileInvalid:
case XpmOpenFailed:
default:
return nil;
}
fi-> frameCount = 1;
fi-> stop = true;
l = malloc( sizeof( LoadRec) + ( sizeof( RGBColor) + 1) * image. ncolors);
if ( !l) {
XpmFreeXpmImage( &image);
XpmFreeXpmInfo( &info);
outcm( sizeof( LoadRec) + ( sizeof( RGBColor) + 1) * image. ncolors);
return nil;
}
memset( l, 0, sizeof( LoadRec) + ( sizeof( RGBColor) + 1) * image. ncolors);
l-> image = image;
l-> info = info;
l-> palette = ( RGBColor*) ( &l-> xpalette);
l-> trans_table = ( Byte*)( l->palette) + sizeof( RGBColor) * image. ncolors;
return l;
}
static Bool
load( PImgCodec instance, PImgLoadFileInstance fi)
{
LoadRec * l = ( LoadRec *) fi-> instance;
HV * profile = fi-> frameProperties;
PIcon i = ( PIcon) fi-> object;
int type, transparent_count = 0;
/* determine type */
if ( l-> image. ncolors <= 2) type = imbpp1; else
if ( l-> image. ncolors <= 16) type = imbpp4; else
if ( l-> image. ncolors <= 256) type = imbpp8; else type = imbpp24;
/* acquire color mapping */
{
int i, j;
PHash c = hash_create();
XpmColor * x = l-> image. colorTable;
unsigned int offsets[5];
offsets[0] = (Handle)(&x-> c_color) - (Handle)x;
offsets[1] = (Handle)(&x-> g_color) - (Handle)x;
offsets[2] = (Handle)(&x-> g4_color) - (Handle)x;
offsets[3] = (Handle)(&x-> m_color) - (Handle)x;
offsets[4] = (Handle)(&x-> symbolic) - (Handle)x;
for ( i = 0; i < l-> image. ncolors; i++, x++) {
for ( j = 0; j < 5; j++) {
char * s = *((char**)((char *)x + offsets[j]));
if ( s) {
Color xc;
if ( s[0] == '#' && strlen(s) < 14 && s[1] != 0) {
char buf[ 14], *e;
int ln = strlen(s)-1, r, g, b;
strcpy( buf, s + 1);
while ( ln % 3) {
strcat( buf, "0");
ln++;
}
ln /= 3;
b = strtol( buf + ln * 2, &e, 16); buf[ ln * 2] = 0;
if ( *e) break;
g = strtol( buf + ln, &e, 16); buf[ ln] = 0;
if ( *e) break;
r = strtol( buf, &e, 16);
if ( *e) break;
switch ( ln) {
case 1:
b *= 16; g *= 16; r *= 16;
break;
case 3:
b /= 16; g /= 16; r /= 16;
break;
case 4:
b /= 256; g /= 256; r /= 256;
break;
}
xc = ARGB( r, g, b);
} else {
xc = (Color) PTR2UV(hash_fetch( c, s, strlen(s)));
if ( xc) {
xc -= 10;
} else if ( stricmp( s, "none") == 0) {
transparent_count++;
l-> trans_table[ i] = 1;
xc = 0;
} else {
xc = apc_lookup_color( s);
hash_store( c, s, strlen(s), INT2PTR(void*, (Color)xc + 10));
if ( xc == clInvalid) xc = 0;
}
}
l-> palette[ i]. b = xc & 0xff;
l-> palette[ i]. g = ( xc >> 8 ) & 0xff;
l-> palette[ i]. r = ( xc >> 16 ) & 0xff;
break;
}
}
}
hash_destroy( c, false);
}
/* extras */
if ( fi-> loadExtras) {
if ( transparent_count) {
int i;
AV * av = newAV();
for ( i = 0; i < l-> image. ncolors; i++)
if ( l-> trans_table[i])
av_push( av, newSViv( i));
pset_sv_noinc( transparentColors, newRV_noinc(( SV*) av));
}
if ( l-> info. valuemask & XpmHotspot) {
pset_i( hotSpotX, l-> info. x_hotspot);
pset_i( hotSpotY, l-> info. y_hotspot);
}
if ( l-> info. valuemask & XpmComments) {
if ( l-> info. hints_cmt)
pset_c( hintsComment, l-> info. hints_cmt);
if ( l-> info. colors_cmt)
pset_c( colorsComment, l-> info. colors_cmt);
if ( l-> info. pixels_cmt)
pset_c( pixelsComment, l-> info. pixels_cmt);
}
if ( l-> info. valuemask & XpmExtensions && l-> info. nextensions > 0) {
int i, j;
HV * hash = newHV();
XpmExtension * e = l-> info. extensions;
for ( i = 0; i < l-> info. nextensions; i++, e++) {
SV * string = newSVpv("", 0);
for ( j = 0; j < e-> nlines; j++) {
sv_catpv( string, e-> lines[j]);
if ( j + 1 < e-> nlines)
sv_catpv( string, "\n");
}
(void) hv_store( hash, e-> name, strlen( e-> name), string, 0);
}
pset_sv_noinc( extensions, newRV_noinc((SV*)hash));
}
}
if ( fi-> noImageData) {
CImage( fi-> object)-> set_type( fi-> object, type);
pset_i( width, l-> image. width);
pset_i( height, l-> image. height);
if ( type < imbpp24) {
memcpy( i-> palette, l-> palette, l-> image. ncolors * sizeof( RGBColor));
i-> palSize = l-> image. ncolors;
}
return true;
}
CImage( fi-> object)-> create_empty( fi-> object, l-> image. width, l-> image. height, type);
if ( type < imbpp24) {
memcpy( i-> palette, l-> palette, l-> image. ncolors * sizeof( RGBColor));
i-> palSize = l-> image. ncolors;
}
/* read data */
if ( i-> h > 0 && i-> w > 0) {
unsigned int x, y, *data = l-> image.data;
Byte * dst = i-> data + ( i-> h - 1 ) * i-> lineSize;
for ( y = 0; y < i-> h; y++) {
Byte * d = dst;
for ( x = 0; x < i-> w; x++, data++) {
switch ( type) {
case imbpp1:
if ( *data) *d |= 0x80 >> ( x % 8 );
if (( x % 8) == 7) d++;
break;
case imbpp4:
if ( x % 2)
*(d++) |= (*data) & 0xf;
else
*d = ((*data) & 0xf) << 4;
break;
case imbpp8:
*(d++) = *data;
break;
case imbpp24:
*(d++) = l-> palette[ *data]. b;
*(d++) = l-> palette[ *data]. g;
*(d++) = l-> palette[ *data]. r;
break;
}
}
dst -= i-> lineSize;
}
}
/* read transparency info */
if ( kind_of( fi-> object, CIcon) && transparent_count) {
unsigned int x, y, *data = l-> image.data;
Byte * dst = i-> mask + ( i-> h - 1 ) * i-> maskLine;
for ( y = 0; y < i-> h; y++) {
Byte * d = dst;
for ( x = 0; x < i-> w; x++, data++) {
if ( l-> trans_table[*data]) *d |= 0x80 >> ( x % 8 );
if (( x % 8) == 7) d++;
}
dst -= i-> maskLine;
}
i-> autoMasking = amNone;
}
return true;
}
static void
close_load( PImgCodec instance, PImgLoadFileInstance fi)
{
LoadRec * l = ( LoadRec *) fi-> instance;
XpmFreeXpmImage( &l-> image);
XpmFreeXpmInfo( &l-> info);
free( fi-> instance);
}
static HV *
save_defaults( PImgCodec c)
{
HV * profile = newHV();
pset_i( hotSpotX, 0);
pset_i( hotSpotY, 0);
pset_c( hintsComment, "");
pset_c( colorsComment, "");
pset_c( pixelsComment, "");
pset_sv_noinc( extensions, newRV_noinc((SV*)newHV()));
return profile;
}
static void *
open_save( PImgCodec instance, PImgSaveFileInstance fi)
{
return (void*)1;
}
typedef struct {
int delta;
XpmImage * image;
} CalcData;
static char * encoder = "qwertyuiop[]';lkjhgfdsazxcvbnm,./`1234567890-=QWERTYUIOP{}ASDFGH";
static char * hex = "0123456789ABCDEF";
static Bool
prepare_xpm_color( void * value, int keyLen, Color * color_ptr, CalcData * cd)
{
Color color = *color_ptr;
IV v = PTR2IV(value) - 1, cpp = cd-> image-> cpp, lpp = 6;
char * c;
c = cd-> image-> colorTable[ v]. c_color =
(char*) cd-> image-> colorTable + cd-> delta;
if ( color != clInvalid) {
/* optimized snprintf( c, 8, "#%06x", (unsigned int)color); */
c += 7;
*(c--) = 0;
while ( lpp--) {
*(c--) = hex[ color % 16];
color /= 16;
}
*c = '#';
} else
strcpy( c, "None");
cd-> delta += 8;
c = cd-> image-> colorTable[ v]. string =
(char*) cd-> image-> colorTable + cd-> delta;
if ( color != clInvalid) {
while ( cpp--) {
*(c++) = encoder[ v % 64];
v /= 64;
}
} else {
while ( cpp--) *(c++) = ' ';
}
*c = 0;
cd-> delta += 5;
return false;
}
static Bool
save( PImgCodec instance, PImgSaveFileInstance fi)
{
dPROFILE;
XpmInfo info;
XpmImage image;
HV * profile = fi-> objectExtras;
PIcon i = ( PIcon) fi-> object;
int ret = XpmOpenFailed;
char * extholder = nil;
Bool icon = kind_of( fi-> object, CIcon);
memset( &info, 0, sizeof(info));
memset( &image, 0, sizeof(image));
info. valuemask = XpmSize;
image. width = i-> w;
image. height = i-> h;
image. ncolors = (( i-> type & imBPP) > imbpp8) ? 16777216 : i-> palSize;
if ( !( image. data = malloc( i-> w * i-> h * sizeof(int)))) {
outcm( i-> w * i-> h * sizeof(int));
return false;
}
memset( image. data, 0, i-> w * i-> h * sizeof(int));
/* prepare pixels and palette */
if ( image. ncolors > 256) { /* reduce colors altogether */
CalcData cd;
PHash hash = hash_create();
int x, y, transcolor = -1;
unsigned int *dest = image. data;
Byte * p = i-> data + ( i-> h - 1) * i-> lineSize;
Byte * mask = icon ? i-> mask + ( i-> h - 1) * i-> maskLine : nil;
for ( y = 0; y < i-> h; y++) {
RGBColor * pp = ( RGBColor *) p;
for ( x = 0; x < i-> w; x++, pp++) {
if ( !icon || !( mask[ x >> 3] & ( 0x80 >> ( x & 7)))) {
Color key = ARGB(pp->r,pp->g,pp->b);
Handle val = (Handle) hash_fetch( hash, &key, sizeof(key));
if ( val == 0)
hash_store( hash, &key, sizeof(key), (void*)(val = (hash_count( hash) + 1)));
*(dest++) = val - 1;
} else {
if ( transcolor < 0) {
Color key = clInvalid;
transcolor = hash_count( hash);
hash_store( hash, &key, sizeof(key), INT2PTR(void*, transcolor + 1));
}
*(dest++) = transcolor;
}
}
p -= i-> lineSize;
if ( icon) mask -= i-> maskLine;
}
image. ncolors = hash_count( hash);
image. cpp = ( image. ncolors <= 4096) ?
(( image. ncolors <= 64) ? 1 : 2) :
(( image. ncolors <= 262144) ? 3 : 4);
if ( !( image. colorTable = malloc( image. ncolors * ( sizeof( XpmColor) + 13)))) {
free( image. data);
hash_destroy( hash, false);
outcm( image. ncolors * ( sizeof( XpmColor) + 13));
return false;
}
memset( image. colorTable, 0, image. ncolors * ( sizeof( XpmColor) + 13));
cd. delta = image. ncolors * sizeof( XpmColor);
cd. image = ℑ
hash_first_that( hash, (void*)prepare_xpm_color, &cd, nil, nil);
hash_destroy( hash, false);
} else {
CalcData cd;
int x, y, type = i-> type & imBPP, val = 0, transcolor = -1, ncolors = image. ncolors;
unsigned int *dest = image. data;
Byte * p = i-> data + ( i-> h - 1) * i-> lineSize;
Byte * mask = icon ? i-> mask + ( i-> h - 1) * i-> maskLine : nil;
if ( mask) transcolor = image. ncolors++;
for ( y = 0; y < i-> h; y++) {
Byte * s = p;
for ( x = 0; x < i-> w; x++) {
switch ( type) {
case imbpp1:
val = ( *s & ( 0x80 >> ( x % 8))) ? 1 : 0;
if ( x % 8 == 7) s++;
break;
case imbpp4:
val = (( x % 2) ? *(s++) : (*s >> 4)) & 0xf;
break;
case imbpp8:
val = *(s++);
break;
}
if ( !icon || !( mask[ x >> 3] & ( 0x80 >> ( x & 7)))) {
*(dest++) = val;
} else {
*(dest++) = transcolor;
}
}
p -= i-> lineSize;
if ( icon) mask -= i-> maskLine;
}
if ( !( image. colorTable = malloc( image. ncolors * ( sizeof( XpmColor) + 13)))) {
free( image. data);
outcm( image. ncolors * ( sizeof( XpmColor) + 13));
return false;
}
memset( image. colorTable, 0, image. ncolors * ( sizeof( XpmColor) + 13));
image. cpp = ( image. ncolors > 64) ? 2 : 1;
cd. delta = image. ncolors * sizeof( XpmColor);
cd. image = ℑ
for ( x = 0; x < ncolors; x++) {
Color c = ARGB(i-> palette[x].r,i-> palette[x].g,i-> palette[x].b);
prepare_xpm_color(INT2PTR(void*,x+1), 0, &c, &cd);
}
if ( icon) {
Color c = clInvalid;
prepare_xpm_color( INT2PTR(void*,transcolor+1), 0, &c, &cd);
}
}
/* extras */
if ( pexist( hintsComment)) {
info. valuemask |= XpmComments;
info. hints_cmt = pget_c( hintsComment);
}
if ( pexist( colorsComment)) {
info. valuemask |= XpmComments;
info. colors_cmt = pget_c( colorsComment);
}
if ( pexist( pixelsComment)) {
info. valuemask |= XpmComments;
info. pixels_cmt = pget_c( pixelsComment);
}
if ( pexist( hotSpotX) || pexist( hotSpotY)) {
info. valuemask |= XpmHotspot;
info. x_hotspot = pexist( hotSpotX) ? pget_i( hotSpotX) : 0;
info. y_hotspot = pexist( hotSpotY) ? pget_i( hotSpotY) : 0;
}
if ( pexist( extensions)) {
HV * hv;
HE * he;
SV * sv = pget_sv( extensions);
char * c, **ptr;
int i = 0, ptrs = 0, strholder = 0;
if ( !SvOK(sv) || !SvROK(sv) || SvTYPE(SvRV(sv)) != SVt_PVHV) {
outc("'extensions' is not a hash");
goto EXIT;
}
hv = ( HV*) SvRV( sv);
info. nextensions = HvKEYS(( HV*) hv);
if ( !( info. extensions = malloc( sizeof( XpmExtension) * info. nextensions))) {
outcm( sizeof( XpmExtension) * info. nextensions);
goto EXIT;
}
memset( info. extensions, 0, sizeof( XpmExtension) * info. nextensions);
hv_iterinit( hv);
for (;;) {
char * c;
STRLEN vlen;
if (( he = hv_iternext( hv)) == nil) break;
info. extensions[i]. name = (char*) HeKEY( he);
c = (char*) SvPV( HeVAL( he), vlen);
info. extensions[i]. lines = (char**)c;
strholder += vlen + 1;
info. extensions[i]. nlines = 1;
while (*c) if ( *(c++) == '\n') info. extensions[i]. nlines++;
ptrs += info. extensions[i]. nlines;
i++;
}
if ( !( extholder = malloc( strholder + ptrs * sizeof( char*)))) {
outcm( strholder + ptrs * sizeof( char*));
goto EXIT;
}
memset( extholder, 0, strholder + ptrs * sizeof( char*));
c = extholder;
ptr = ( char**) extholder;
c = extholder + ptrs * sizeof( char*);
for ( i = 0; i < info. nextensions; i++) {
Bool crlf = false;
char * x = ( char*) info. extensions[i]. lines;
info. extensions[i]. lines = ptr;
*(ptr++) = c;
while ( *x) {
switch ( *x) {
case '\n':
crlf = true;
*(c++) = 0;
*(ptr++) = c;
break;
case '\r':
if ( crlf) {
crlf = false;
break;
}
default:
crlf = false;
*(c++) = *x;
}
x++;
}
*(c++) = 0;
}
info. valuemask |= XpmExtensions;
}
ret = XpmWriteFileFromXpmImage( fi-> fileName, &image, &info);
EXIT:
free( extholder);
free( info. extensions);
free( image. colorTable);
free( image. data);
return ret == XpmSuccess;
}
static void
close_save( PImgCodec instance, PImgSaveFileInstance fi)
{
}
void
apc_img_codec_Xpm( void )
{
struct ImgCodecVMT vmt;
memcpy( &vmt, &CNullImgCodecVMT, sizeof( CNullImgCodecVMT));
vmt. init = init;
vmt. open_load = open_load;
vmt. load = load;
vmt. close_load = close_load;
vmt. save_defaults = save_defaults;
vmt. open_save = open_save;
vmt. save = save;
vmt. close_save = close_save;
apc_img_register( &vmt, nil);
}
#ifdef __cplusplus
}
#endif