/*-
* Copyright (c) 1997-2002 The Protein Laboratory, University of Copenhagen
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id$
*/
#include "img.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <limits.h>
#include <math.h>
#include "apricot.h"
#include "Image.h"
#include "img_conv.h"
#include <Image.inc>
#include "Clipboard.h"
#ifdef PerlIO
typedef PerlIO *FileStream;
#else
#define PERLIO_IS_STDIO 1
typedef FILE *FileStream;
#define PerlIO_fileno(f) fileno(f)
#endif
#ifdef __cplusplus
extern "C" {
#endif
#undef my
#define inherited CDrawable->
#define my ((( PImage) self)-> self)
#define var (( PImage) self)
static Bool Image_set_extended_data( Handle self, HV * profile);
static void Image_reset_notifications( Handle self);
void
Image_init( Handle self, HV * profile)
{
dPROFILE;
inherited init( self, profile);
var-> eventMask1 =
( query_method( self, "on_headerready", 0) ? IMG_EVENTS_HEADER_READY : 0) |
( query_method( self, "on_dataready", 0) ? IMG_EVENTS_DATA_READY : 0);
Image_reset_notifications( self);
var->w = pget_i( width);
var->h = pget_i( height);
var->conversion = pget_i( conversion);
opt_assign( optHScaling, pget_B( hScaling));
opt_assign( optVScaling, pget_B( vScaling));
if ( !itype_supported( var-> type = pget_i( type)))
if ( !itype_importable( var-> type, &var-> type, nil, nil)) {
warn( "Image::init: cannot set type %08x", var-> type);
var-> type = imBW;
}
var->lineSize = (( var->w * ( var->type & imBPP) + 31) / 32) * 4;
var->dataSize = ( var->lineSize) * var->h;
if ( var-> dataSize > 0) {
var->data = allocb( var->dataSize);
memset( var-> data, 0, var-> dataSize);
if ( var-> data == nil) {
my-> make_empty( self);
croak("Image::init: cannot allocate %d bytes", var-> dataSize);
}
} else
var-> data = nil;
var->palette = allocn( RGBColor, 256);
if ( var-> palette == nil) {
free( var-> data);
var-> data = nil;
croak("Image::init: cannot allocate %d bytes", 768);
}
if ( !Image_set_extended_data( self, profile))
my-> set_data( self, pget_sv( data));
opt_assign( optPreserveType, pget_B( preserveType));
var->palSize = (1 << (var->type & imBPP)) & 0x1ff;
if (!( var->type & imGrayScale) &&
pexist( palette)) { /* palette might be killed by set_extended_data() */
int ps = apc_img_read_palette( var->palette, pget_sv( palette), true);
if ( ps) var-> palSize = ps;
}
{
Point set;
prima_read_point( pget_sv( resolution), (int*)&set, 2, "Array panic on 'resolution'");
my-> set_resolution( self, set);
}
if ( var->type & imGrayScale) switch ( var->type & imBPP)
{
case imbpp1:
memcpy( var->palette, stdmono_palette, sizeof( stdmono_palette));
break;
case imbpp4:
memcpy( var->palette, std16gray_palette, sizeof( std16gray_palette));
break;
case imbpp8:
memcpy( var->palette, std256gray_palette, sizeof( std256gray_palette));
break;
}
apc_image_create( self);
my->update_change( self);
CORE_INIT_TRANSIENT(Image);
}
void
Image_handle_event( Handle self, PEvent event)
{
inherited handle_event ( self, event);
if ( var-> stage > csNormal) return;
switch ( event-> cmd) {
case cmImageHeaderReady:
my-> notify( self, "<s", "HeaderReady");
break;
case cmImageDataReady:
my-> update_change( self);
my-> notify( self, "<siiii", "DataReady",
event-> gen. R. left,
event-> gen. R. bottom,
event-> gen. R. right - event-> gen. R. left + 1,
event-> gen. R. top - event-> gen. R. bottom + 1);
break;
}
}
void
Image_reset( Handle self, int new_type, RGBColor * palette, int palSize)
{
Bool want_palette;
RGBColor new_palette[256];
Byte * new_data = nil;
int new_pal_size = 0, new_line_size, new_data_size, want_only_palette_colors = 0;
if ( var->stage > csFrozen) return;
want_palette = (!( new_type & imGrayScale)) && ( new_type != imRGB) && (palSize > 0);
if ( want_palette) {
new_pal_size = palSize;
if ( new_pal_size == 0) want_palette = false;
if ( new_pal_size > ( 1 << ( new_type & imBPP)))
new_pal_size = 1 << ( new_type & imBPP);
if ( new_pal_size > 256)
new_pal_size = 256;
if ( palette != nil)
memcpy( new_palette, palette, new_pal_size * 3);
else
want_only_palette_colors = 1;
}
if ( !want_palette && (
((var->type == (imbpp8|imGrayScale)) && (new_type == imbpp8)) ||
((var->type == (imbpp4|imGrayScale)) && (new_type == imbpp4)) ||
((var->type == (imbpp1|imGrayScale)) && (new_type == imbpp1))
)) {
var->type = new_type;
return;
}
if ( var-> type == new_type && (
((new_type != imbpp8 && new_type != imbpp4 && new_type != imbpp1) || !want_palette)
)) return;
new_line_size = (( var-> w * ( new_type & imBPP) + 31) / 32) * 4;
new_data_size = new_line_size * var-> h;
if ( new_data_size > 0) {
if ( !( new_data = allocb( new_data_size))) {
my-> make_empty( self);
croak("Image::reset: cannot allocate %d bytes", new_data_size);
}
memset( new_data, 0, new_data_size);
if ( new_pal_size != 1)
ic_type_convert( self, new_data, new_palette, new_type,
&new_pal_size, want_only_palette_colors);
}
if ( new_pal_size > 0) {
var-> palSize = new_pal_size;
memcpy( var-> palette, new_palette, new_pal_size * 3);
}
free( var-> data);
var-> type = new_type;
var-> data = new_data;
var-> lineSize = new_line_size;
var-> dataSize = new_data_size;
my-> update_change( self);
}
void
Image_stretch( Handle self, int width, int height)
{
Byte * newData = nil;
int lineSize;
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 = (( abs( width) * ( var->type & imBPP) + 31) / 32) * 4;
newData = allocb( lineSize * abs( height));
if ( newData == nil)
croak("Image::stretch: cannot allocate %d bytes", lineSize * abs( height));
memset( newData, 0, lineSize * abs( height));
if ( var-> data)
ic_stretch( var-> type, var-> data, var-> w, var-> h,
newData, width, height,
is_opt( optHScaling), is_opt( optVScaling));
free( var->data);
var->data = newData;
var->lineSize = lineSize;
var->dataSize = lineSize * abs( height);
var->w = abs( width);
var->h = abs( height);
my->update_change( self);
}
static void
Image_reset_sv( Handle self, int new_type, SV * palette, Bool triplets)
{
int colors;
RGBColor pal_buf[256], *pal_ptr;
if ( !palette || palette == nilSV) {
pal_ptr = nil;
colors = 0;
} else if ( SvROK( palette) && ( SvTYPE( SvRV( palette)) == SVt_PVAV)) {
colors = apc_img_read_palette( pal_ptr = pal_buf, palette, triplets);
} else {
pal_ptr = nil;
colors = SvIV( palette);
}
my-> reset( self, new_type, pal_ptr, colors);
}
void
Image_set( Handle self, HV * profile)
{
dPROFILE;
if ( pexist( conversion))
{
my-> set_conversion( self, pget_i( conversion));
pdelete( conversion);
}
if ( pexist( hScaling))
{
my->set_hScaling( self, pget_B( hScaling));
pdelete( hScaling);
}
if ( pexist( vScaling))
{
my->set_vScaling( self, pget_B( vScaling));
pdelete( vScaling);
}
if ( Image_set_extended_data( self, profile))
pdelete( data);
if ( pexist( type))
{
int newType = pget_i( type);
if ( !itype_supported( newType))
warn("Invalid image type requested (%08x) in Image::set_type", newType);
else
if ( !opt_InPaint) {
SV * palette;
Bool triplets;
if ( pexist( palette)) {
palette = pget_sv(palette);
triplets = true;
} else if ( pexist( colormap)) {
palette = pget_sv(colormap);
triplets = false;
} else {
palette = nilSV;
triplets = false;
}
Image_reset_sv( self, newType, palette, triplets);
}
pdelete( colormap);
pdelete( palette);
pdelete( type);
}
if ( pexist( size))
{
int set[2];
prima_read_point( pget_sv( size), set, 2, "Array panic on 'size'");
my-> stretch( self, set[0], set[1]);
pdelete( size);
}
if ( pexist( resolution))
{
Point set;
prima_read_point( pget_sv( resolution), (int*)&set, 2, "Array panic on 'resolution'");
my-> set_resolution( self, set);
pdelete( resolution);
}
inherited set ( self, profile);
}
void
Image_done( Handle self)
{
apc_image_destroy( self);
my->make_empty( self);
inherited done( self);
}
void
Image_make_empty( Handle self)
{
free( var->data);
free( var->palette);
var->w = 0;
var->h = 0;
var->type = 0;
var->palSize = 0;
var->lineSize = 0;
var->dataSize = 0;
var->data = nil;
var->palette = nil;
my->update_change( self);
}
Bool
Image_hScaling( Handle self, Bool set, Bool scaling)
{
if ( !set)
return is_opt( optHScaling);
opt_assign( optHScaling, scaling);
return false;
}
Bool
Image_vScaling( Handle self, Bool set, Bool scaling)
{
if ( !set)
return is_opt( optVScaling);
opt_assign( optVScaling, scaling);
return false;
}
Point
Image_resolution( Handle self, Bool set, Point resolution)
{
if ( !set)
return var-> resolution;
if ( resolution. x <= 0 || resolution. y <= 0)
resolution = apc_gp_get_resolution( application);
var-> resolution = resolution;
return resolution;
}
Point
Image_size( Handle self, Bool set, Point size)
{
if ( !set)
return inherited size( self, set, size);
my-> stretch( self, size.x, size.y);
return size;
}
SV *
Image_get_handle( Handle self)
{
char buf[ 256];
snprintf( buf, 256, "0x%08lx", apc_image_get_handle( self));
return newSVpv( buf, 0);
}
Color
Image_get_nearest_color( Handle self, Color color)
{
Byte pal;
RGBColor rgb, *pcolor;
if ( is_opt( optInDrawInfo) || is_opt( optInDraw))
return inherited get_nearest_color( self, color);
switch ( var-> type & imCategory) {
case imColor:
if (( var-> type & imBPP) > 8)
return color;
rgb. b = color & 0xFF;
rgb. g = (color >> 8) & 0xFF;
rgb. r = (color >> 16) & 0xFF;
break;
case imGrayScale:
rgb. r = rgb. g = rgb. b = (
(color & 0xFF) +
((color >> 8) & 0xFF) +
((color >> 16) & 0xFF)
) / 3;
break;
default:
return clInvalid; /* what else? */
}
pal = cm_nearest_color( rgb, var-> palSize, var-> palette);
pcolor = var->palette + pal;
return ARGB( pcolor-> r, pcolor-> g, pcolor-> b);
}
SV *
Image_data( Handle self, Bool set, SV * svdata)
{
void *data;
STRLEN dataSize;
if ( var->stage > csFrozen) return nilSV;
if ( !set)
return newSVpvn(( char *) var-> data, var-> dataSize);
data = SvPV( svdata, dataSize);
if ( is_opt( optInDraw) || dataSize <= 0) return nilSV;
memcpy( var->data, data, dataSize > var->dataSize ? var->dataSize : dataSize);
my-> update_change( self);
return nilSV;
}
/*
Routine sets image data almost as Image::set_data, but taking into
account 'lineSize', 'type', and 'reverse' fields. To be called from bunch routines,
line ::init or ::set. Returns true if relevant fields were found and
data extracted and set, and false if user data should be set throught ::set_data.
Image itself may undergo conversion during the routine; in that case 'palette'
property may be used also. All these fields, if used, or meant to be used but
erroneously set, will be deleted regardless of routine success.
*/
Bool
Image_set_extended_data( Handle self, HV * profile)
{
dPROFILE;
void *data, *proc;
STRLEN dataSize;
int lineSize = 0, newType = var-> type, fixType, oldType = -1;
Bool pexistType, pexistLine, pexistReverse, supp, reverse = false;
if ( !pexist( data)) {
if ( pexist( lineSize)) {
warn( "Image: lineSize supplied without data property.");
pdelete( lineSize);
}
return false;
}
data = SvPV( pget_sv( data), dataSize);
/* parameters check */
pexistType = pexist( type) && ( newType = pget_i( type)) != var-> type;
pexistLine = pexist( lineSize) && ( lineSize = pget_i( lineSize)) != var-> lineSize;
pexistReverse = pexist( reverse) && ( reverse = pget_B( reverse));
pdelete( lineSize);
pdelete( type);
pdelete( reverse);
if ( !pexistLine && !pexistType && !pexistReverse) return false;
if ( is_opt( optInDraw) || dataSize <= 0)
goto GOOD_RETURN;
/* determine line size, if any */
if ( pexistLine) {
if ( lineSize <= 0) {
warn( "Image::set_data: invalid lineSize:%d passed", lineSize);
goto GOOD_RETURN;
}
if ( !pexistType) { /* plain repadding */
ibc_repad(( Byte*) data, var-> data, lineSize, var-> lineSize, dataSize, var-> dataSize, 1, 1, nil, reverse);
my-> update_change( self);
goto GOOD_RETURN;
}
}
/* pre-fetch auto conversion, if set in same clause */
if ( pexist( preserveType))
opt_assign( optPreserveType, pget_B( preserveType));
if ( is_opt( optPreserveType))
oldType = var-> type;
/* getting closest type */
if (( supp = itype_supported( newType))) {
fixType = newType;
proc = nil;
} else if ( !itype_importable( newType, &fixType, &proc, nil)) {
warn( "Image::set_data: invalid image type %08x", newType);
goto GOOD_RETURN;
}
/* fixing image and maybe palette - for known type it's same code as in ::set, */
/* but here's no sense calling it, just doing what we need. */
if ( fixType != var-> type || pexist( palette) || pexist( colormap)) {
SV * palette;
Bool triplets;
if ( pexist( palette)) {
palette = pget_sv( palette);
triplets = true;
} else if ( pexist( colormap)) {
palette = pget_sv( colormap);
triplets = false;
} else {
palette = nilSV;
triplets = false;
}
Image_reset_sv( self, fixType, palette, triplets);
pdelete( palette);
pdelete( colormap);
}
/* copying user data */
if ( supp && lineSize == 0 && !reverse)
/* same code as in ::set_data */
memcpy( var->data, data, dataSize > var->dataSize ? var->dataSize : dataSize);
else {
/* if no explicit lineSize set, assuming x4 padding */
if ( lineSize == 0)
lineSize = (( var-> w * ( newType & imBPP) + 31) / 32) * 4;
/* copying using repadding routine */
ibc_repad(( Byte*) data, var-> data, lineSize, var-> lineSize, dataSize, var-> dataSize,
( newType & imBPP) / 8, ( var-> type & imBPP) / 8, proc, reverse
);
}
my-> update_change( self);
/* if want to keep original type, restoring */
if ( is_opt( optPreserveType))
my-> set_type( self, oldType);
GOOD_RETURN:
pdelete(data);
return true;
}
static ssize_t
img_perlio_read( void * f, size_t bufsize, void * buffer)
{
#ifdef PerlIO
return PerlIO_read(( FileStream) f, buffer, bufsize);
#else
return fread( buffer, 1, bufsize, ( FileStream) f);
#endif
}
static ssize_t
img_perlio_write( void * f, size_t bufsize, void * buffer)
{
#ifdef PerlIO
return PerlIO_write( ( FileStream) f, buffer, bufsize);
#else
return fwrite( buffer, 1, bufsize, ( FileStream) f);
#endif
}
static int
img_perlio_seek( void * f, long offset, int whence)
{
#ifdef PerlIO
return PerlIO_seek( ( FileStream) f, offset, whence);
#else
return fseek( ( FileStream) f, offset, whence);
#endif
}
static long
img_perlio_tell( void * f)
{
#ifdef PerlIO
return PerlIO_tell( ( FileStream) f);
#else
return ftell( ( FileStream) f);
#endif
}
static int
img_perlio_flush( void * f)
{
#ifdef PerlIO
return PerlIO_flush( ( FileStream) f);
#else
return fflush( ( FileStream) f);
#endif
}
static int
img_perlio_error( void * f)
{
#ifdef PerlIO
return PerlIO_error( ( FileStream) f);
#else
return ferror( ( FileStream) f);
#endif
}
XS( Image_load_FROMPERL)
{
dXSARGS;
Handle self;
SV * sv;
HV *profile;
char *fn;
PList ret;
Bool err = false;
FileStream f = NULL;
ImgIORequest ioreq, *pioreq;
char error[256];
if (( items < 2) || (( items % 2) != 0))
croak("Invalid usage of Prima::Image::load");
self = gimme_the_mate( ST( 0));
sv = ST(1);
if ( SvROK(sv) && SvTYPE( SvRV( sv)) == SVt_PVGV)
f = IoIFP(sv_2io(ST(1)));
if ( f != NULL) {
pioreq = &ioreq;
ioreq. handle = f;
ioreq. read = img_perlio_read;
ioreq. write = img_perlio_write;
ioreq. seek = img_perlio_seek;
ioreq. tell = img_perlio_tell;
ioreq. flush = img_perlio_flush;
ioreq. error = img_perlio_error;
fn = NULL;
} else {
fn = ( char *) SvPV_nolen( ST( 1));
pioreq = NULL;
}
profile = parse_hv( ax, sp, items, mark, 2, "Image::load");
if ( !pexist( className))
pset_c( className, self ? my-> className : ( char*) SvPV_nolen( ST( 0)));
pset_i( eventMask, self ? var-> eventMask2 : 0);
ret = apc_img_load( self, fn, pioreq, profile, error);
sv_free(( SV *) profile);
SPAGAIN;
SP -= items;
if ( ret) {
int i;
for ( i = 0; i < ret-> count; i++) {
PAnyObject o = ( PAnyObject) ret-> items[i];
if ( o && o-> mate && o-> mate != nilSV) {
XPUSHs( sv_mortalcopy( o-> mate));
if (( Handle) o != self)
--SvREFCNT( SvRV( o-> mate));
} else {
XPUSHs( &PL_sv_undef);
err = true;
}
}
plist_destroy( ret);
} else {
XPUSHs( &PL_sv_undef);
err = true;
}
/* This code breaks exception propagation chain
since it uses $@ for its own needs */
if ( err)
sv_setpv( GvSV( PL_errgv), error);
else
sv_setsv( GvSV( PL_errgv), nilSV);
PUTBACK;
return;
}
int
Image_lineSize( Handle self, Bool set, int dummy)
{
if ( set)
croak("Image::lineSize: attempt to write read-only property");
return var-> lineSize;
}
PList
Image_load_REDEFINED( SV * who, char *filename, HV * profile)
{
return nil;
}
PList
Image_load( SV * who, char *filename, HV * profile)
{
PList ret;
Handle self = gimme_the_mate( who);
char error[ 256];
if ( !pexist( className))
pset_c( className, self ? my-> className : ( char*) SvPV_nolen( who));
ret = apc_img_load( self, filename, NULL, profile, error);
return ret;
}
XS( Image_save_FROMPERL)
{
dXSARGS;
Handle self;
HV *profile;
char *fn;
int ret;
char error[256];
FileStream f = NULL;
SV * sv;
ImgIORequest ioreq, *pioreq;
if (( items < 2) || (( items % 2) != 0))
croak("Invalid usage of Prima::Image::save");
self = gimme_the_mate( ST( 0));
sv = ST(1);
if ( SvROK(sv) && SvTYPE( SvRV( sv)) == SVt_PVGV)
f = IoIFP(sv_2io(ST(1)));
if ( f != NULL) {
pioreq = &ioreq;
ioreq. handle = f;
ioreq. read = img_perlio_read;
ioreq. write = img_perlio_write;
ioreq. seek = img_perlio_seek;
ioreq. tell = img_perlio_tell;
ioreq. flush = img_perlio_flush;
ioreq. error = img_perlio_error;
fn = NULL;
} else {
fn = ( char *) SvPV_nolen( ST( 1));
pioreq = NULL;
}
profile = parse_hv( ax, sp, items, mark, 2, "Image::save");
ret = apc_img_save( self, fn, pioreq, profile, error);
sv_free(( SV *) profile);
SPAGAIN;
SP -= items;
XPUSHs( sv_2mortal( newSViv(( ret > 0) ? ret : -ret)));
/* This code breaks exception propagation chain
since it uses $@ for its own needs */
if ( ret <= 0)
sv_setpv( GvSV( PL_errgv), error);
else
sv_setsv( GvSV( PL_errgv), nilSV);
PUTBACK;
return;
}
int
Image_save_REDEFINED( SV * who, char *filename, HV * profile)
{
return 0;
}
int
Image_save( SV * who, char *filename, HV * profile)
{
Handle self = gimme_the_mate( who);
char error[ 256];
if ( !pexist( className))
pset_c( className, self ? my-> className : ( char*) SvPV_nolen( who));
return apc_img_save( self, filename, NULL, profile, error);
}
int
Image_type( Handle self, Bool set, int type)
{
HV * profile;
if ( !set)
return var->type;
profile = newHV();
pset_i( type, type);
my-> set( self, profile);
sv_free(( SV *) profile);
return nilHandle;
}
int
Image_get_bpp( Handle self)
{
return var->type & imBPP;
}
Bool
Image_begin_paint( Handle self)
{
Bool ok;
if ( !inherited begin_paint( self))
return false;
if ( !( ok = apc_image_begin_paint( self))) {
inherited end_paint( self);
perl_error();
}
return ok;
}
Bool
Image_begin_paint_info( Handle self)
{
Bool ok;
if ( is_opt( optInDraw)) return true;
if ( !inherited begin_paint_info( self))
return false;
if ( !( ok = apc_image_begin_paint_info( self))) {
inherited end_paint_info( self);
perl_error();
}
return ok;
}
void
Image_end_paint( Handle self)
{
int oldType = var->type;
if ( !is_opt( optInDraw)) return;
apc_image_end_paint( self);
inherited end_paint( self);
if ( is_opt( optPreserveType) && var->type != oldType) {
my->reset( self, oldType, nil, 0);
} else {
switch( var->type)
{
case imbpp1:
if ( var-> palSize == 2 && memcmp( var->palette, stdmono_palette, sizeof( stdmono_palette)) == 0)
var->type |= imGrayScale;
break;
case imbpp4:
if ( var-> palSize == 16 && memcmp( var->palette, std16gray_palette, sizeof( std16gray_palette)) == 0)
var->type |= imGrayScale;
break;
case imbpp8:
if ( var-> palSize == 256 && memcmp( var->palette, std256gray_palette, sizeof( std256gray_palette)) == 0)
var->type |= imGrayScale;
break;
}
my->update_change( self);
}
}
void
Image_end_paint_info( Handle self)
{
if ( !is_opt( optInDrawInfo)) return;
apc_image_end_paint_info( self);
inherited end_paint_info( self);
}
void
Image_update_change( Handle self)
{
if ( var-> stage <= csNormal) apc_image_update_change( self);
var->statsCache = 0;
}
double
Image_stats( Handle self, Bool set, int index, double value)
{
if ( index < 0 || index > isMaxIndex) return 0;
if ( set) {
var-> stats[ index] = value;
var-> statsCache |= 1 << index;
return 0;
} else {
#define gather_stats(TYP) if ( var->data) { \
TYP *src = (TYP*)var->data, *stop, *s; \
maxv = minv = *src; \
for ( y = 0; y < var->h; y++) { \
s = src; stop = s + var->w; \
while (s != stop) { \
v = (double)*s; \
sum += v; \
sum2 += v*v; \
if ( minv > v) minv = v; \
if ( maxv < v) maxv = v; \
s++; \
} \
src = (TYP*)(((Byte *)src) + var->lineSize); \
} \
}
int y;
double sum = 0.0, sum2 = 0.0, minv = 0.0, maxv = 0.0, v;
if ( var->statsCache & ( 1 << index)) return var->stats[ index];
/* calculate image stats */
switch ( var->type) {
case imByte: gather_stats(uint8_t);break;
case imShort: gather_stats(int16_t); break;
case imLong: gather_stats(int32_t); break;
case imFloat: gather_stats(float); break;
case imDouble: gather_stats(double); break;
default: return 0;
}
if ( var->w * var->h > 0)
{
var->stats[ isSum] = sum;
var->stats[ isSum2] = sum2;
sum /= var->w * var->h;
sum2 /= var->w * var->h;
sum2 = sum2 - sum*sum;
var->stats[ isMean] = sum;
var->stats[ isVariance] = sum2;
var->stats[ isStdDev] = sqrt(sum2);
var->stats[ isRangeLo] = minv;
var->stats[ isRangeHi] = maxv;
} else {
for ( y = 0; y <= isMaxIndex; y++) var->stats[ y] = 0;
}
var->statsCache = (1 << (isMaxIndex + 1)) - 1;
}
return var->stats[ index];
}
void
Image_resample( Handle self, double srcLo, double srcHi, double dstLo, double dstHi)
{
#define RSPARMS self, var->data, var->type, srcLo, srcHi, dstLo, dstHi
switch ( var->type)
{
case imByte: rs_Byte_Byte ( RSPARMS); break;
case imShort: rs_Short_Short ( RSPARMS); break;
case imLong: rs_Long_Long ( RSPARMS); break;
case imFloat: rs_float_float ( RSPARMS); break;
case imDouble: rs_double_double ( RSPARMS); break;
default: return;
}
my->update_change( self);
}
SV *
Image_palette( Handle self, Bool set, SV * palette)
{
if ( var->stage > csFrozen) return nilSV;
if ( set) {
int ps;
if ( var->type & imGrayScale) return nilSV;
if ( !var->palette) return nilSV;
ps = apc_img_read_palette( var->palette, palette, true);
if ( ps)
var-> palSize = ps;
else
warn("Invalid array reference passed to Image::palette");
my-> update_change( self);
} else {
int i;
AV * av = newAV();
int colors = ( 1 << ( var->type & imBPP)) & 0x1ff;
Byte * pal = ( Byte*) var->palette;
if (( var->type & imGrayScale) && (( var->type & imBPP) > imbpp8)) colors = 256;
if ( var-> palSize < colors) colors = var-> palSize;
for ( i = 0; i < colors*3; i++) av_push( av, newSViv( pal[ i]));
return newRV_noinc(( SV *) av);
}
return nilSV;
}
int
Image_conversion( Handle self, Bool set, int conversion)
{
if ( !set)
return var-> conversion;
return var-> conversion = conversion;
}
void
Image_create_empty( Handle self, int width, int height, int type)
{
free( var->data);
var->w = width;
var->h = height;
var->type = type;
var->lineSize = (( var->w * ( var->type & imBPP) + 31) / 32) * 4;
var->dataSize = var->lineSize * var->h;
var->palSize = (1 << (var->type & imBPP)) & 0x1ff;
var->statsCache = 0;
if ( var->dataSize > 0)
{
var->data = allocb( var->dataSize);
if ( var-> data == nil) {
int sz = var-> dataSize;
my-> make_empty( self);
croak("Image::create_empty: cannot allocate %d bytes", sz);
}
memset( var->data, 0, var->dataSize);
} else
var->data = nil;
if ( var->type & imGrayScale) switch ( var->type & imBPP)
{
case imbpp1:
memcpy( var->palette, stdmono_palette, sizeof( stdmono_palette));
break;
case imbpp4:
memcpy( var->palette, std16gray_palette, sizeof( std16gray_palette));
break;
case imbpp8:
memcpy( var->palette, std256gray_palette, sizeof( std256gray_palette));
break;
}
}
Bool
Image_preserveType( Handle self, Bool set, Bool preserveType)
{
if ( !set)
return is_opt( optPreserveType);
opt_assign( optPreserveType, preserveType);
return false;
}
SV *
Image_pixel( Handle self, Bool set, int x, int y, SV * pixel)
{
#define BGRto32(pal) ((var->palette[pal].r<<16) | (var->palette[pal].g<<8) | (var->palette[pal].b))
if (!set) {
if ( opt_InPaint)
return inherited pixel(self,false,x,y,pixel);
if ((x>=var->w) || (x<0) || (y>=var->h) || (y<0))
return newSViv( clInvalid);
if ( var-> type & (imComplexNumber|imTrigComplexNumber)) {
AV * av = newAV();
switch ( var-> type) {
case imComplex:
case imTrigComplex: {
float * f = (float*)(var->data + (var->lineSize*y+x*2*sizeof(float)));
av_push( av, newSVnv( *(f++)));
av_push( av, newSVnv( *f));
break;
}
case imDComplex:
case imTrigDComplex: {
double * f = (double*)(var->data + (var->lineSize*y+x*2*sizeof(double)));
av_push( av, newSVnv( *(f++)));
av_push( av, newSVnv( *f));
break;
}
}
return newRV_noinc(( SV*) av);
} else if ( var-> type & imRealNumber) {
switch ( var-> type) {
case imFloat:
return newSVnv(*(float*)(var->data + (var->lineSize*y+x*sizeof(float))));
case imDouble:
return newSVnv(*(double*)(var->data + (var->lineSize*y+x*sizeof(double))));
default:
return nilSV;
}} else
switch (var->type & imBPP) {
case imbpp1:
{
Byte p=var->data[var->lineSize*y+(x>>3)];
p=(p >> (7-(x & 7))) & 1;
return newSViv(((var->type & imGrayScale) ? (p ? 255 : 0) : BGRto32(p)));
}
case imbpp4:
{
Byte p=var->data[var->lineSize*y+(x>>1)];
p=(x&1) ? p & 0x0f : p>>4;
return newSViv(((var->type & imGrayScale) ? (p*255L)/15 : BGRto32(p)));
}
case imbpp8:
{
Byte p=var->data[var->lineSize*y+x];
return newSViv(((var->type & imGrayScale) ? p : BGRto32(p)));
}
case imbpp16:
{
return newSViv(*(Short*)(var->data + (var->lineSize*y+x*2)));
}
case imbpp24:
{
RGBColor p=*(PRGBColor)(var->data + (var->lineSize*y+x*3));
return newSViv((p.r<<16) | (p.g<<8) | p.b);
}
case imbpp32:
return newSViv(*(Long*)(var->data + (var->lineSize*y+x*4)));
default:
return newSViv(clInvalid);
}
#undef BGRto32
} else {
Color color;
RGBColor rgb;
#define LONGtoBGR(lv,clr) ((clr).b=(lv)&0xff,(clr).g=((lv)>>8)&0xff,(clr).r=((lv)>>16)&0xff,(clr))
if ( is_opt( optInDraw))
return inherited pixel(self,true,x,y,pixel);
if ((x>=var->w) || (x<0) || (y>=var->h) || (y<0))
return nilSV;
if ( var-> type & (imComplexNumber|imTrigComplexNumber)) {
if ( !SvROK( pixel) || ( SvTYPE( SvRV( pixel)) != SVt_PVAV)) {
switch ( var-> type) {
case imComplex:
case imTrigComplex:
*(float*)(var->data+(var->lineSize*y+x*2*sizeof(float)))=SvNV(pixel);
break;
case imDComplex:
case imTrigDComplex:
*(double*)(var->data+(var->lineSize*y+x*2*sizeof(double)))=SvNV(pixel);
break;
default:
return nilSV;
}
} else {
AV * av = (AV *) SvRV( pixel);
SV **sv[2];
sv[0] = av_fetch( av, 0, 0);
sv[1] = av_fetch( av, 1, 0);
switch ( var-> type) {
case imComplex:
case imTrigComplex:
if ( sv[0]) *(float*)(var->data+(var->lineSize*y+x*2*sizeof(float)))=SvNV(*(sv[0]));
if ( sv[1]) *(float*)(var->data+(var->lineSize*y+(x*2+1)*sizeof(float)))=SvNV(*(sv[1]));
break;
case imDComplex:
case imTrigDComplex:
if ( sv[0]) *(double*)(var->data+(var->lineSize*y+x*2*sizeof(double)))=SvNV(*(sv[0]));
if ( sv[1]) *(double*)(var->data+(var->lineSize*y+(x*2+1)*sizeof(double)))=SvNV(*(sv[1]));
break;
default:
return nilSV;
}
}
} else if ( var-> type & imRealNumber) {
switch ( var-> type) {
case imFloat:
*(float*)(var->data+(var->lineSize*y+x*sizeof(float)))=SvNV(pixel);
break;
case imDouble:
*(double*)(var->data+(var->lineSize*y+x*sizeof(double)))=SvNV(pixel);
break;
default:
return nilSV;
}
my->update_change( self);
return nilSV;
}
color = SvIV( pixel);
switch (var->type & imBPP) {
case imbpp1 :
{
int x1=7-(x&7);
Byte p=(((var->type & imGrayScale) ? color/255 : cm_nearest_color(LONGtoBGR(color,rgb),var->palSize,var->palette)) & 1);
Byte *pd=var->data+(var->lineSize*y+(x>>3));
*pd&=~(1 << x1);
*pd|=(p << x1);
}
break;
case imbpp4 :
{
Byte p=((var->type & imGrayScale) ? (color*15)/255 : cm_nearest_color(LONGtoBGR(color,rgb),var->palSize,var->palette));
Byte *pd=var->data+(var->lineSize*y+(x>>1));
if (x&1) {
*pd&=0xf0;
}
else {
p<<=4;
*pd&=0x0f;
}
*pd|=p;
}
break;
case imbpp8:
{
if (var->type & imGrayScale) {
var->data[(var->lineSize)*y+x]=color;
}
else {
var->data[(var->lineSize)*y+x]=cm_nearest_color(LONGtoBGR(color,rgb),(var->palSize),(var->palette));
}
}
break;
case imbpp16 :
*(Short*)(var->data+(var->lineSize*y+(x<<1)))=color;
break;
case imbpp24 :
LONGtoBGR(color,rgb);
memcpy((var->data + (var->lineSize*y+x*3)),&rgb,sizeof(RGBColor));
break;
case imbpp32 :
*(Long*)(var->data+(var->lineSize*y+(x<<2)))=color;
break;
default:
return nilSV;
}
my->update_change( self);
#undef LONGtoBGR
return nilSV;
}
}
Handle
Image_bitmap( Handle self)
{
Handle h;
Point s;
HV * 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( monochrome, (var-> type & imBPP) == 1);
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, ropCopyPut);
--SvREFCNT( SvRV( PDrawable( h)-> mate));
return h;
}
Handle
Image_dup( Handle self)
{
Handle h;
PImage i;
HV * profile = newHV();
pset_H( owner, var->owner);
pset_i( width, var->w);
pset_i( height, var->h);
pset_i( type, var->type);
pset_i( conversion, var->conversion);
pset_i( hScaling, is_opt( optHScaling));
pset_i( vScaling, is_opt( optVScaling));
pset_i( preserveType, is_opt( optPreserveType));
h = Object_create( var->self-> className, profile);
sv_free(( SV *) profile);
i = ( PImage) h;
memcpy( i-> palette, var->palette, 768);
i-> palSize = var-> palSize;
if ( i-> type != var->type)
croak("Image::dup consistency failed");
else
memcpy( i-> data, var->data, var->dataSize);
memcpy( i-> stats, var->stats, sizeof( var->stats));
i-> statsCache = var->statsCache;
if ( hv_exists(( HV*)SvRV( var-> mate), "extras", 6)) {
SV ** sv = hv_fetch(( HV*)SvRV( var-> mate), "extras", 6, 0);
if ( sv && SvOK( *sv) && SvROK( *sv) && SvTYPE( SvRV( *sv)) == SVt_PVHV)
(void) hv_store(( HV*)SvRV( i-> mate), "extras", 6, newSVsv( *sv), 0);
}
--SvREFCNT( SvRV( i-> mate));
return h;
}
Handle
Image_extract( Handle self, int x, int y, int width, int height)
{
Handle h;
PImage i;
HV * profile;
unsigned char * data = var->data;
int ls = var->lineSize;
int nodata = 0;
if ( var->w == 0 || var->h == 0) return my->dup( self);
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 ) {
warn("Requested image width is less than 1");
width = 1;
nodata = 1;
}
if ( height <= 0 ) {
warn("Requested image height is less than 1");
height = 1;
nodata = 1;
}
profile = newHV();
pset_H( owner, var->owner);
pset_i( width, width);
pset_i( height, height);
pset_i( type, var->type);
pset_i( conversion, var->conversion);
pset_i( hScaling, is_opt( optHScaling));
pset_i( vScaling, is_opt( optVScaling));
pset_i( preserveType, is_opt( optPreserveType));
h = Object_create( var->self-> className, profile);
sv_free(( SV *) profile);
i = ( PImage) h;
memcpy( i-> palette, var->palette, 768);
i-> palSize = var-> palSize;
if (nodata) goto NODATA;
if (( var->type & imBPP) >= 8) {
int pixelSize = ( var->type & imBPP) / 8;
while ( height > 0) {
height--;
memcpy( i-> data + height * i-> lineSize,
data + ( y + height) * ls + pixelSize * x,
pixelSize * width);
}
} else if (( var->type & imBPP) == 4) {
while ( height > 0) {
height--;
bc_nibble_copy( data + ( y + height) * ls, i-> data + height * i-> lineSize, x, width);
}
} else if (( var->type & imBPP) == 1) {
while ( height > 0) {
height--;
bc_mono_copy( data + ( y + height) * ls, i-> data + height * i-> lineSize, x, width);
}
}
NODATA:
--SvREFCNT( SvRV( i-> mate));
return h;
}
/*
divide the pixels, by whether they match color or not on two
groups, F and B. Both are converted correspondingly to the settings
of color/backColor and rop/rop2. Possible variations:
rop == rop::NoOper, pixel value remains ths same
rop == rop::CopyPut, use the color value
rop == rop::Blackness, use black pixel
rop == rop::Whiteness, use white pixel
rop == rop::AndPut , result is dest & color value
etc...
*/
void
Image_map( Handle self, Color color)
{
Byte * d, b[2];
RGBColor c;
int type = var-> type, height = var-> h, i, ls;
int rop[2];
RGBColor r[2];
int bc = 0;
if ( var-> data == nil) return;
rop[0] = my-> get_rop( self);
rop[1] = my-> get_rop2( self);
if ( rop[0] == ropNoOper && rop[1] == ropNoOper) return;
for ( i = 0; i < 2; i++) {
int not = 0;
switch( rop[i]) {
case ropBlackness:
r[i]. r = r[i]. g = r[i]. b = 0;
rop[i] = ropCopyPut;
break;
case ropWhiteness:
r[i]. r = r[i]. g = r[i]. b = 0xff;
rop[i] = ropCopyPut;
break;
case ropNoOper:
r[i]. r = r[i]. g = r[i]. b = 0;
break;
default: {
Color c = i ? my-> get_backColor( self) : my-> get_color( self);
r[i]. r = ( c >> 16) & 0xff;
r[i]. g = ( c >> 8) & 0xff;
r[i]. b = c & 0xff;
}}
if (( type & imBPP) <= 8) {
b[i] = cm_nearest_color( r[i], var-> palSize, var-> palette);
}
switch ( rop[i]) {
case ropNotPut:
rop[i] = ropCopyPut; not = 1; break;
case ropNotSrcXor: /* same as ropNotDestXor and ropNotXor */
rop[i] = ropXorPut; not = 1; break;
case ropNotSrcAnd:
rop[i] = ropAndPut; not = 1; break;
case ropNotSrcOr:
rop[i] = ropOrPut; not = 1; break;
}
if ( not) {
r[i]. r = ~ r[i]. r;
r[i]. g = ~ r[i]. g;
r[i]. b = ~ r[i]. b;
b[i] = ~ b[i];
}
}
c. r = ( color >> 16) & 0xff;
c. g = ( color >> 8) & 0xff;
c. b = color & 0xff;
if (( type & imBPP) <= 8) {
Color cc;
bc = cm_nearest_color( c, var-> palSize, var-> palette);
cc = ARGB( var->palette[bc].r, var->palette[bc].g, var->palette[bc].b);
if ( cc != color) bc = 0xffff; /* no exact color found */
}
if (
(( type & imBPP) < 8) ||
(
( type != imRGB) &&
( type != (imRGB | imGrayScale))
)
) {
if ( type & imGrayScale)
my-> set_type( self, imbpp8 | imGrayScale);
else
my-> set_type( self, imbpp8);
}
d = ( Byte * ) var-> data;
ls = var-> lineSize;
while ( height--) {
if (( type & imBPP) == 24) {
PRGBColor data = ( PRGBColor) d;
for ( i = 0; i < var-> w; i++) {
int z = ( data-> r == c.r && data-> g == c.g && data-> b == c.b) ? 0 : 1;
switch( rop[z]) {
case ropAndPut:
data-> r &= r[z]. r; data-> g &= r[z]. g; data-> b &= r[z]. b; break;
case ropXorPut:
data-> r ^= r[z]. r; data-> g ^= r[z]. g; data-> b ^= r[z]. b; break;
case ropOrPut:
data-> r |= r[z]. r; data-> g |= r[z]. g; data-> b |= r[z]. b; break;
case ropNotDestAnd:
data-> r = ( ~data-> r) & r[z].r; data-> g = ( ~data-> g) & r[z].g; data-> b = ( ~data-> b) & r[z].b; break;
case ropNotDestOr:
data-> r = ( ~data-> r) | r[z].r; data-> g = ( ~data-> g) | r[z].g; data-> b = ( ~data-> b) | r[z].b; break;
case ropNotAnd:
data-> r = ~(data-> r & r[z].r); data-> g = ~(data-> g & r[z].g); data-> b = ~(data-> b & r[z].b); break;
case ropNotOr:
data-> r = ~(data-> r | r[z].r); data-> g = ~(data-> g | r[z].g); data-> b = ~(data-> b | r[z].b); break;
case ropNoOper:
break;
case ropInvert:
data-> r = ~data-> r; data-> g = ~data-> g; data-> b = ~data-> b; break;
default:
data-> r = r[z]. r; data-> g = r[z]. g; data-> b = r[z]. b;
}
data++;
}
d += ls;
} else {
Byte * data = d;
for ( i = 0; i < var-> w; i++) {
int z = ( *data == bc) ? 0 : 1;
switch( rop[z]) {
case ropAndPut:
*data &= b[z]; break;
case ropXorPut:
*data ^= b[z]; break;
case ropOrPut:
*data |= b[z]; break;
case ropNotDestAnd:
*data = (~(*data)) & b[z]; break;
case ropNotDestOr:
*data = (~(*data)) | b[z]; break;
case ropNotAnd:
*data = ~(*data & b[z]); break;
case ropNotOr:
*data = ~(*data | b[z]); break;
case ropNoOper:
break;
case ropInvert:
*data = ~(*data); break;
default:
*data = b[z]; break;
}
data++;
}
d += ls;
}
}
if ( is_opt( optPreserveType) && var->type != type)
my-> set_type( self, type);
else
my-> update_change( self);
}
SV *
Image_codecs( SV * dummy)
{
int i;
AV * av = newAV();
PList p = plist_create( 16, 16);
apc_img_codecs( p);
for ( i = 0; i < p-> count; i++) {
PImgCodec c = ( PImgCodec ) p-> items[ i];
HV * profile = apc_img_info2hash( c);
pset_i( codecID, i);
av_push( av, newRV_noinc(( SV *) profile));
}
plist_destroy( p);
return newRV_noinc(( SV *) av);
}
Bool
Image_put_image_indirect( Handle self, Handle image, int x, int y, int xFrom, int yFrom, int xDestLen, int yDestLen, int xLen, int yLen, int rop)
{
Bool ret;
if ( is_opt( optInDrawInfo)) return false;
if ( image == nilHandle) return false;
if ( is_opt( optInDraw))
return inherited put_image_indirect( self, image, x, y, xFrom, yFrom, xDestLen, yDestLen, xLen, yLen, rop);
if ( !kind_of( image, CImage)) return false;
ret = img_put( self, image, x, y, xFrom, yFrom, xDestLen, yDestLen, xLen, yLen, rop);
my-> update_change( self);
return ret;
}
UV
Image_add_notification( Handle self, char * name, SV * subroutine, Handle referer, int index)
{
UV id = inherited add_notification( self, name, subroutine, referer, index);
if ( id != 0) Image_reset_notifications( self);
return id;
}
void
Image_remove_notification( Handle self, UV id)
{
inherited remove_notification( self, id);
Image_reset_notifications( self);
}
static void
Image_reset_notifications( Handle self)
{
int i;
PList list;
void * ret[ 2];
int cmd[ 2] = { IMG_EVENTS_HEADER_READY, IMG_EVENTS_DATA_READY };
var-> eventMask2 = var-> eventMask1;
if ( var-> eventIDs == nil) return;
ret[0] = hash_fetch( var-> eventIDs, "HeaderReady", 11);
ret[1] = hash_fetch( var-> eventIDs, "DataReady", 9);
for ( i = 0; i < 2; i++) {
if ( ret[i] == nil) continue;
list = var-> events + PTR2IV( ret[i]) - 1;
if ( list-> count > 0) var-> eventMask2 |= cmd[ i];
}
}
Bool
Image_bar( Handle self, int x1, int y1, int x2, int y2)
{
if (opt_InPaint) {
return apc_gp_bar( self, x1, y1, x2, y2);
} else {
Byte colorbuf[sizeof(double)*2];
Color color;
RGBColor rgb;
color = my->get_color(self);
rgb.b = color & 0xff;
rgb.g = ((color)>>8) & 0xff;
rgb.r = ((color)>>16) & 0xff;
switch (var->type) {
case imBW:
colorbuf[0] = (int)( (rgb.r + rgb.g + rgb.b) / 768.0 + .5);
break;
case imbpp1:
colorbuf[0] = cm_nearest_color(rgb,var->palSize,var->palette) & 1;
break;
case imbpp4 | imGrayScale :
colorbuf[0] = (int)( (rgb.r + rgb.g + rgb.b) / 48.0);
break;
case imbpp4 :
colorbuf[0] = cm_nearest_color(rgb,var->palSize,var->palette) & 7;
break;
case imByte:
colorbuf[0] = (int)( (rgb.r + rgb.g + rgb.b) / 3.0);
break;
case imbpp8:
colorbuf[0] = cm_nearest_color(rgb,var->palSize,var->palette);
break;
case imShort :
*((Short*)colorbuf) = color;
break;
case imRGB :
*((Color*)colorbuf) = color;
break;
case imLong :
*((Long*)colorbuf) = color;
break;
default:
croak("Not implemented yet");
}
img_bar( self, x1, y1, x2 - x1 + 1, y2 - y1 + 1, my-> get_rop(self), colorbuf);
my-> update_change(self);
return true;
}
}
void
Image_rotate( Handle self, int degrees)
{
Byte * new_data;
int new_line_size;
switch (degrees) {
case 90:
case 270:
case 180:
break;
default:
croak("'degrees' must be 90,180,or 270");
}
if (( var-> type & imBPP) < 8) {
int type = var->type;
my->set_type( self, imbpp8 );
my->rotate( self, degrees );
if ( is_opt( optPreserveType)) {
my-> set_conversion( self, ictNone);
my-> set_type( self, type);
my-> set_conversion( self, var-> conversion);
}
return;
}
switch (degrees) {
case 90:
case 270:
new_line_size = (( var-> h * ( var->type & imBPP) + 31) / 32) * 4 ;
if (( new_data = allocb( new_line_size * var->w )) == NULL )
croak("Image::rotate: cannot allocate %d bytes", new_line_size * var->w);
break;
case 180:
if (( new_data = allocb( var->dataSize )) == NULL )
croak("Image::rotate: cannot allocate %d bytes", var->dataSize );
break;
}
img_rotate( self, new_data, degrees );
if ( degrees != 180 ) {
int h = var->h;
var->h = var->w;
var->w = h;
var->lineSize = new_line_size;
var->dataSize = new_line_size * var->h;
}
free( var->data);
var->data = new_data;
my-> update_change(self);
}
void
Image_mirror( Handle self, Bool vertically)
{
if (!vertically && ( var-> type & imBPP) < 8) {
int type = var->type;
my->set_type( self, imbpp8 );
my->mirror( self, vertically );
if ( is_opt( optPreserveType)) {
my-> set_conversion( self, ictNone);
my-> set_type( self, type);
my-> set_conversion( self, var-> conversion);
}
return;
}
img_mirror( self, vertically );
my-> update_change(self);
}
#ifdef __cplusplus
}
#endif