1 #include "img.h"
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <limits.h>
5 #include <math.h>
6 #include "apricot.h"
7 #include "Image.h"
8 #include "Region.h"
9 #include "img_conv.h"
10 #include <Image.inc>
11 #include "Clipboard.h"
12
13 #ifdef PerlIO
14 typedef PerlIO *FileStream;
15 #else
16 #define PERLIO_IS_STDIO 1
17 typedef FILE *FileStream;
18 #define PerlIO_fileno(f) fileno(f)
19 #endif
20
21
22 #ifdef __cplusplus
23 extern "C" {
24 #endif
25
26
27 #undef my
28 #define inherited CDrawable->
29 #define my ((( PImage) self)-> self)
30 #define var (( PImage) self)
31
32 static Bool Image_set_extended_data( Handle self, HV * profile);
33 static void Image_reset_notifications( Handle self);
34
35 void
Image_init(Handle self,HV * profile)36 Image_init( Handle self, HV * profile)
37 {
38 dPROFILE;
39 var-> updateLock = 0;
40 opt_set(optSystemDrawable);
41 inherited init( self, profile);
42 var-> eventMask1 =
43 ( query_method( self, "on_headerready", 0) ? IMG_EVENTS_HEADER_READY : 0) |
44 ( query_method( self, "on_dataready", 0) ? IMG_EVENTS_DATA_READY : 0);
45 Image_reset_notifications( self);
46 var->w = pget_i( width);
47 var->h = pget_i( height);
48 if ( var-> w < 0 ) var-> w = 0;
49 if ( var-> h < 0 ) var-> h = 0;
50 if ( !iconvtype_supported( var->conversion = pget_i( conversion) )) {
51 warn("Invalid conversion: %d\n", var->conversion);
52 var->conversion = ictNone;
53 }
54 var->scaling = pget_i( scaling);
55 if ( var->scaling < istNone || var-> scaling > istMax) {
56 warn("Invalid scaling: %d\n", var->scaling);
57 var-> scaling = istNone;
58 }
59 if ( !itype_supported( var-> type = pget_i( type)))
60 if ( !itype_importable( var-> type, &var-> type, NULL, NULL)) {
61 warn( "Image::init: cannot set type %08x", var-> type);
62 var-> type = imBW;
63 }
64
65 var->lineSize = LINE_SIZE(var->w, var->type);
66 var->dataSize = ( var->lineSize) * var->h;
67 if ( var-> dataSize > 0) {
68 var->data = allocb( var->dataSize);
69 memset( var-> data, 0, var-> dataSize);
70 if ( var-> data == NULL) {
71 my-> make_empty( self);
72 croak("Image::init: cannot allocate %d bytes", var-> dataSize);
73 }
74 } else
75 var-> data = NULL;
76 var->palette = allocn( RGBColor, 256);
77 if ( var-> palette == NULL) {
78 free( var-> data);
79 var-> data = NULL;
80 croak("Image::init: cannot allocate %d bytes", 768);
81 }
82 if ( !Image_set_extended_data( self, profile))
83 my-> set_data( self, pget_sv( data));
84 opt_assign( optPreserveType, pget_B( preserveType));
85 var->palSize = (1 << (var->type & imBPP)) & 0x1ff;
86 if (!( var->type & imGrayScale) &&
87 pexist( palette)) { /* palette might be killed by set_extended_data() */
88 int ps = apc_img_read_palette( var->palette, pget_sv( palette), true);
89 if ( ps) var-> palSize = ps;
90 }
91
92 {
93 Point set;
94 prima_read_point( pget_sv( resolution), (int*)&set, 2, "Array panic on 'resolution'");
95 my-> set_resolution( self, set);
96 }
97 if ( var->type & imGrayScale) switch ( var->type & imBPP)
98 {
99 case imbpp1:
100 memcpy( var->palette, stdmono_palette, sizeof( stdmono_palette));
101 break;
102 case imbpp4:
103 memcpy( var->palette, std16gray_palette, sizeof( std16gray_palette));
104 break;
105 case imbpp8:
106 memcpy( var->palette, std256gray_palette, sizeof( std256gray_palette));
107 break;
108 }
109 apc_image_create( self);
110 my->update_change( self);
111 CORE_INIT_TRANSIENT(Image);
112 }
113
114 void
Image_handle_event(Handle self,PEvent event)115 Image_handle_event( Handle self, PEvent event)
116 {
117 inherited handle_event ( self, event);
118 if ( var-> stage > csNormal) return;
119 switch ( event-> cmd) {
120 case cmImageHeaderReady:
121 my-> notify( self, "<sS", "HeaderReady", sv_2mortal(newRV((SV*) event-> gen. p)));
122 break;
123 case cmImageDataReady:
124 my-> update_change( self);
125 my-> notify( self, "<siiii", "DataReady",
126 event-> gen. R. left,
127 event-> gen. R. bottom,
128 event-> gen. R. right - event-> gen. R. left + 1,
129 event-> gen. R. top - event-> gen. R. bottom + 1);
130 break;
131 }
132 }
133
134 void
Image_reset(Handle self,int new_type,RGBColor * palette,int palSize)135 Image_reset( Handle self, int new_type, RGBColor * palette, int palSize)
136 {
137 Bool want_palette;
138 RGBColor new_palette[256];
139 Byte * new_data = NULL;
140 int new_pal_size = 0, new_line_size, new_data_size, want_only_palette_colors = 0;
141
142 if ( var->stage > csFrozen) return;
143
144 want_palette = (!( new_type & imGrayScale)) && ( new_type != imRGB) && (palSize > 0);
145 if ( want_palette) {
146 new_pal_size = palSize;
147 if ( new_pal_size == 0) want_palette = false;
148 if ( new_pal_size > ( 1 << ( new_type & imBPP)))
149 new_pal_size = 1 << ( new_type & imBPP);
150 if ( new_pal_size > 256)
151 new_pal_size = 256;
152 if ( palette != NULL)
153 memcpy( new_palette, palette, new_pal_size * 3);
154 else
155 want_only_palette_colors = 1;
156 }
157 if ( !want_palette && (
158 ((var->type == (imbpp8|imGrayScale)) && (new_type == imbpp8)) ||
159 ((var->type == (imbpp4|imGrayScale)) && (new_type == imbpp4)) ||
160 ((var->type == (imbpp1|imGrayScale)) && (new_type == imbpp1))
161 )) {
162 var->type = new_type;
163 return;
164 }
165 if (( var->conversion & ictpMask) == ictpCubic)
166 want_palette = true;
167 if ( var-> type == new_type && (
168 ((new_type != imbpp8 && new_type != imbpp4 && new_type != imbpp1) || !want_palette)
169 )) return;
170
171 new_line_size = LINE_SIZE(var->w, new_type);
172 new_data_size = new_line_size * var-> h;
173 if ( new_data_size > 0) {
174 if ( !( new_data = allocb( new_data_size))) {
175 my-> make_empty( self);
176 croak("Image::reset: cannot allocate %d bytes", new_data_size);
177 }
178 memset( new_data, 0, new_data_size);
179 if ( new_pal_size != 1)
180 ic_type_convert( self, new_data, new_palette, new_type,
181 &new_pal_size, want_only_palette_colors);
182 }
183 if ( new_pal_size > 0) {
184 var-> palSize = new_pal_size;
185 memcpy( var-> palette, new_palette, new_pal_size * 3);
186 }
187 free( var-> data);
188 var-> type = new_type;
189 var-> data = new_data;
190 var-> lineSize = new_line_size;
191 var-> dataSize = new_data_size;
192 my-> update_change( self);
193 }
194
195 void
Image_stretch(Handle self,int width,int height)196 Image_stretch( Handle self, int width, int height)
197 {
198 Byte * newData = NULL;
199 int lineSize, oldType, newType;
200 if ( var->stage > csFrozen) return;
201 if ( width > 65535) width = 65535;
202 if ( height > 65535) height = 65535;
203 if ( width < -65535) width = -65535;
204 if ( height < -65535) height = -65535;
205 if (( width == var->w) && ( height == var->h)) return;
206 if ( width == 0 || height == 0)
207 {
208 my->create_empty( self, 0, 0, var->type);
209 return;
210 }
211
212 oldType = var->type;
213 newType = ic_stretch_suggest_type( var-> type, var-> scaling );
214 if ( newType != var->type)
215 my->set_type(self, newType);
216
217 lineSize = LINE_SIZE( abs( width) , var->type);
218 newData = allocb( lineSize * abs( height));
219 if ( newData == NULL)
220 croak("Image::stretch: cannot allocate %d bytes", lineSize * abs( height));
221 memset( newData, 0, lineSize * abs( height));
222 if ( var-> data) {
223 char error[256];
224 if (!ic_stretch( var-> type, var-> data, var-> w, var-> h, newData, width, height, var->scaling, error)) {
225 free( var->data);
226 my-> make_empty( self);
227 croak("%s", error);
228 }
229 }
230
231 free( var->data);
232 var->data = newData;
233 var->lineSize = lineSize;
234 var->dataSize = lineSize * abs( height);
235 var->w = abs( width);
236 var->h = abs( height);
237
238 if ( is_opt( optPreserveType) && var-> type != oldType )
239 my-> set_type( self, oldType );
240
241 my->update_change( self);
242 }
243
244 static void
Image_reset_sv(Handle self,int new_type,SV * palette,Bool triplets)245 Image_reset_sv( Handle self, int new_type, SV * palette, Bool triplets)
246 {
247 int colors;
248 RGBColor pal_buf[256], *pal_ptr;
249 if ( !palette || palette == NULL_SV) {
250 pal_ptr = NULL;
251 colors = 0;
252 } else if ( SvROK( palette) && ( SvTYPE( SvRV( palette)) == SVt_PVAV)) {
253 colors = apc_img_read_palette( pal_ptr = pal_buf, palette, triplets);
254 } else {
255 pal_ptr = NULL;
256 colors = SvIV( palette);
257 }
258 my-> reset( self, new_type, pal_ptr, colors);
259 }
260
261 void
Image_set(Handle self,HV * profile)262 Image_set( Handle self, HV * profile)
263 {
264 dPROFILE;
265 if ( pexist( conversion))
266 {
267 my-> set_conversion( self, pget_i( conversion));
268 pdelete( conversion);
269 }
270 if ( pexist( scaling))
271 {
272 my->set_scaling( self, pget_i( scaling));
273 pdelete( scaling);
274 }
275
276 if ( Image_set_extended_data( self, profile))
277 pdelete( data);
278
279 if ( pexist( type))
280 {
281 int newType = pget_i( type);
282 if ( !itype_supported( newType))
283 warn("Invalid image type requested (%08x) in Image::set_type", newType);
284 else
285 if ( !opt_InPaint) {
286 SV * palette;
287 Bool triplets;
288 if ( pexist( palette)) {
289 palette = pget_sv(palette);
290 triplets = true;
291 } else if ( pexist( colormap)) {
292 palette = pget_sv(colormap);
293 triplets = false;
294 } else {
295 palette = NULL_SV;
296 triplets = false;
297 }
298 Image_reset_sv( self, newType, palette, triplets);
299 }
300 pdelete( colormap);
301 pdelete( palette);
302 pdelete( type);
303 }
304
305 if ( pexist( size))
306 {
307 int set[2];
308 prima_read_point( pget_sv( size), set, 2, "Array panic on 'size'");
309 my-> stretch( self, set[0], set[1]);
310 pdelete( size);
311 }
312
313 if ( pexist( resolution))
314 {
315 Point set;
316 prima_read_point( pget_sv( resolution), (int*)&set, 2, "Array panic on 'resolution'");
317 my-> set_resolution( self, set);
318 pdelete( resolution);
319 }
320
321 inherited set ( self, profile);
322 }
323
324
325 void
Image_done(Handle self)326 Image_done( Handle self)
327 {
328 if ( var-> regionData ) {
329 free(var->regionData);
330 var->regionData = NULL;
331 }
332 apc_image_destroy( self);
333 my->make_empty( self);
334 inherited done( self);
335 }
336
337 void
Image_make_empty(Handle self)338 Image_make_empty( Handle self)
339 {
340 free( var->data);
341 free( var->palette);
342 var->w = 0;
343 var->h = 0;
344 var->type = 0;
345 var->palSize = 0;
346 var->lineSize = 0;
347 var->dataSize = 0;
348 var->data = NULL;
349 var->palette = NULL;
350 my->update_change( self);
351 }
352
353 Point
Image_resolution(Handle self,Bool set,Point resolution)354 Image_resolution( Handle self, Bool set, Point resolution)
355 {
356 if ( !set)
357 return var-> resolution;
358 if ( resolution. x <= 0 || resolution. y <= 0)
359 resolution = apc_gp_get_resolution( application);
360 var-> resolution = resolution;
361 return resolution;
362 }
363
364 int
Image_scaling(Handle self,Bool set,int scaling)365 Image_scaling( Handle self, Bool set, int scaling)
366 {
367 if ( !set)
368 return var->scaling;
369 if ( scaling < istNone || scaling > istMax ) {
370 warn("Invalid scaling: %d", scaling);
371 return false;
372 }
373 var->scaling = scaling;
374 return false;
375 }
376
377 Point
Image_size(Handle self,Bool set,Point size)378 Image_size( Handle self, Bool set, Point size)
379 {
380 if ( !set)
381 return inherited size( self, set, size);
382 my-> stretch( self, size.x, size.y);
383 return size;
384 }
385
386
387 Bool
Image_can_draw_alpha(Handle self)388 Image_can_draw_alpha( Handle self)
389 {
390 if ( is_opt( optInDrawInfo) )
391 return false;
392 else if ( is_opt( optInDraw))
393 return apc_gp_can_draw_alpha(self);
394 else
395 return var->type == imByte || var->type == imRGB;
396 }
397
398 SV *
Image_get_handle(Handle self)399 Image_get_handle( Handle self)
400 {
401 char buf[ 256];
402 snprintf( buf, 256, PR_HANDLE_FMT, apc_image_get_handle( self));
403 return newSVpv( buf, 0);
404 }
405
406 Color
Image_get_nearest_color(Handle self,Color color)407 Image_get_nearest_color( Handle self, Color color)
408 {
409 Byte pal;
410 RGBColor rgb, *pcolor;
411
412 if ( is_opt( optInDrawInfo) || is_opt( optInDraw))
413 return inherited get_nearest_color( self, color);
414
415 switch ( var-> type & imCategory) {
416 case imColor:
417 if (( var-> type & imBPP) > 8)
418 return color;
419 rgb. b = color & 0xFF;
420 rgb. g = (color >> 8) & 0xFF;
421 rgb. r = (color >> 16) & 0xFF;
422 break;
423 case imGrayScale:
424 rgb. r = rgb. g = rgb. b = (
425 (color & 0xFF) +
426 ((color >> 8) & 0xFF) +
427 ((color >> 16) & 0xFF)
428 ) / 3;
429 break;
430 default:
431 return clInvalid; /* what else? */
432 }
433
434 pal = cm_nearest_color( rgb, var-> palSize, var-> palette);
435 pcolor = var->palette + pal;
436 return ARGB( pcolor-> r, pcolor-> g, pcolor-> b);
437 }
438
439 SV *
Image_data(Handle self,Bool set,SV * svdata)440 Image_data( Handle self, Bool set, SV * svdata)
441 {
442 void *data;
443 STRLEN dataSize;
444
445 if ( var->stage > csFrozen) return NULL_SV;
446
447 if ( !set) {
448 SV * sv = newSV_type(SVt_PV);
449 SvREADONLY_on(sv);
450 SvLEN_set(sv, 0); /* So Perl won't free it. */
451 SvPV_set(sv, (char*)var-> data);
452 SvCUR_set(sv, var-> dataSize);
453 SvPOK_only(sv);
454 return sv;
455 }
456
457 data = SvPV( svdata, dataSize);
458 if ( is_opt( optInDraw) || dataSize <= 0) return NULL_SV;
459
460 memcpy( var->data, data, (dataSize > (STRLEN)var->dataSize) ? (STRLEN)var->dataSize : dataSize);
461 my-> update_change( self);
462 return NULL_SV;
463 }
464
465 /*
466 Routine sets image data almost as Image::set_data, but taking into
467 account 'lineSize', 'type', and 'reverse' fields. To be called from bunch routines,
468 line ::init or ::set. Returns true if relevant fields were found and
469 data extracted and set, and false if user data should be set throught ::set_data.
470 Image itself may undergo conversion during the routine; in that case 'palette'
471 property may be used also. All these fields, if used, or meant to be used but
472 erroneously set, will be deleted regardless of routine success.
473 */
474 Bool
Image_set_extended_data(Handle self,HV * profile)475 Image_set_extended_data( Handle self, HV * profile)
476 {
477 dPROFILE;
478 void *data, *proc;
479 STRLEN dataSize;
480 int lineSize = 0, newType = var-> type, fixType, oldType = -1;
481 Bool pexistType, pexistLine, pexistReverse, supp, reverse = false;
482
483 if ( !pexist( data)) {
484 if ( pexist( lineSize)) {
485 warn( "Image: lineSize supplied without data property.");
486 pdelete( lineSize);
487 }
488 return false;
489 }
490
491 data = SvPV( pget_sv( data), dataSize);
492
493 /* parameters check */
494 pexistType = pexist( type) && ( newType = pget_i( type)) != var-> type;
495 pexistLine = pexist( lineSize) && ( lineSize = pget_i( lineSize)) != var-> lineSize;
496 pexistReverse = pexist( reverse) && ( reverse = pget_B( reverse));
497
498 pdelete( lineSize);
499 pdelete( type);
500 pdelete( reverse);
501
502 if ( !pexistLine && !pexistType && !pexistReverse) return false;
503
504 if ( is_opt( optInDraw) || dataSize <= 0)
505 goto GOOD_RETURN;
506
507 /* determine line size, if any */
508 if ( pexistLine) {
509 if ( lineSize <= 0) {
510 warn( "Image::set_data: invalid lineSize:%d passed", lineSize);
511 goto GOOD_RETURN;
512 }
513 if ( !pexistType) { /* plain repadding */
514 ibc_repad(( Byte*) data, var-> data, lineSize, var-> lineSize, dataSize, var-> dataSize, 1, 1, NULL, reverse);
515 my-> update_change( self);
516 goto GOOD_RETURN;
517 }
518 }
519
520 /* pre-fetch auto conversion, if set in same clause */
521 if ( pexist( preserveType))
522 opt_assign( optPreserveType, pget_B( preserveType));
523 if ( is_opt( optPreserveType))
524 oldType = var-> type;
525
526 /* getting closest type */
527 if (( supp = itype_supported( newType))) {
528 fixType = newType;
529 proc = NULL;
530 } else if ( !itype_importable( newType, &fixType, &proc, NULL)) {
531 warn( "Image::set_data: invalid image type %08x", newType);
532 goto GOOD_RETURN;
533 }
534
535 /* fixing image and maybe palette - for known type it's same code as in ::set, */
536 /* but here's no sense calling it, just doing what we need. */
537 if ( fixType != var-> type || pexist( palette) || pexist( colormap)) {
538 SV * palette;
539 Bool triplets;
540 if ( pexist( palette)) {
541 palette = pget_sv( palette);
542 triplets = true;
543 } else if ( pexist( colormap)) {
544 palette = pget_sv( colormap);
545 triplets = false;
546 } else {
547 palette = NULL_SV;
548 triplets = false;
549 }
550 Image_reset_sv( self, fixType, palette, triplets);
551 pdelete( palette);
552 pdelete( colormap);
553 }
554
555 /* copying user data */
556 if ( supp && lineSize == 0 && !reverse)
557 /* same code as in ::set_data */
558 memcpy( var->data, data, (dataSize > (STRLEN)var->dataSize) ? (STRLEN)var->dataSize : dataSize);
559 else {
560 /* if no explicit lineSize set, assuming x4 padding */
561 if ( lineSize == 0)
562 lineSize = LINE_SIZE( var-> w , newType);
563 /* copying using repadding routine */
564 ibc_repad(( Byte*) data, var-> data, lineSize, var-> lineSize, dataSize, var-> dataSize,
565 ( newType & imBPP) / 8, ( var-> type & imBPP) / 8, proc, reverse
566 );
567 }
568 my-> update_change( self);
569 /* if want to keep original type, restoring */
570 if ( is_opt( optPreserveType))
571 my-> set_type( self, oldType);
572
573 GOOD_RETURN:
574 pdelete(data);
575 return true;
576 }
577
578 static ssize_t
img_perlio_read(void * f,size_t bufsize,void * buffer)579 img_perlio_read( void * f, size_t bufsize, void * buffer)
580 {
581 #ifdef PerlIO
582 return PerlIO_read(( FileStream) f, buffer, bufsize);
583 #else
584 return fread( buffer, 1, bufsize, ( FileStream) f);
585 #endif
586 }
587
588 static ssize_t
img_perlio_write(void * f,size_t bufsize,void * buffer)589 img_perlio_write( void * f, size_t bufsize, void * buffer)
590 {
591 #ifdef PerlIO
592 return PerlIO_write( ( FileStream) f, buffer, bufsize);
593 #else
594 return fwrite( buffer, 1, bufsize, ( FileStream) f);
595 #endif
596 }
597
598 static int
img_perlio_seek(void * f,long offset,int whence)599 img_perlio_seek( void * f, long offset, int whence)
600 {
601 #ifdef PerlIO
602 return PerlIO_seek( ( FileStream) f, offset, whence);
603 #else
604 return fseek( ( FileStream) f, offset, whence);
605 #endif
606 }
607
608 static long
img_perlio_tell(void * f)609 img_perlio_tell( void * f)
610 {
611 #ifdef PerlIO
612 return PerlIO_tell( ( FileStream) f);
613 #else
614 return ftell( ( FileStream) f);
615 #endif
616 }
617
618 static int
img_perlio_flush(void * f)619 img_perlio_flush( void * f)
620 {
621 #ifdef PerlIO
622 return PerlIO_flush( ( FileStream) f);
623 #else
624 return fflush( ( FileStream) f);
625 #endif
626 }
627
628 static int
img_perlio_error(void * f)629 img_perlio_error( void * f)
630 {
631 #ifdef PerlIO
632 return PerlIO_error( ( FileStream) f);
633 #else
634 return ferror( ( FileStream) f);
635 #endif
636 }
637
XS(Image_load_FROMPERL)638 XS( Image_load_FROMPERL)
639 {
640 dXSARGS;
641 Handle self;
642 SV * sv;
643 HV *profile;
644 char *fn;
645 PList ret;
646 Bool err = false, is_utf8;
647 FileStream f = NULL;
648 ImgIORequest ioreq, *pioreq;
649 char error[256];
650
651 if (( items < 2) || (( items % 2) != 0))
652 croak("Invalid usage of Prima::Image::load");
653
654 self = gimme_the_mate( ST( 0));
655
656 sv = ST(1);
657 if ( SvROK(sv) && SvTYPE( SvRV( sv)) == SVt_PVGV)
658 f = IoIFP(sv_2io(ST(1)));
659
660 if ( f != NULL) {
661 pioreq = &ioreq;
662 ioreq. handle = f;
663 ioreq. read = img_perlio_read;
664 ioreq. write = img_perlio_write;
665 ioreq. seek = img_perlio_seek;
666 ioreq. tell = img_perlio_tell;
667 ioreq. flush = img_perlio_flush;
668 ioreq. error = img_perlio_error;
669 fn = NULL;
670 is_utf8 = false;
671 } else {
672 fn = ( char *) SvPV_nolen( ST( 1));
673 is_utf8 = prima_is_utf8_sv(ST(1));
674 pioreq = NULL;
675 }
676
677 profile = parse_hv( ax, sp, items, mark, 2, "Image::load");
678 if ( !pexist( className))
679 pset_c( className, self ? my-> className : ( char*) SvPV_nolen( ST( 0)));
680 pset_i( eventMask, self ? var-> eventMask2 : 0);
681 ret = apc_img_load( self, fn, is_utf8, pioreq, profile, error);
682 sv_free(( SV *) profile);
683 SPAGAIN;
684 SP -= items;
685 if ( ret) {
686 int i;
687 for ( i = 0; i < ret-> count; i++) {
688 PAnyObject o = ( PAnyObject) ret-> items[i];
689 if ( o && o-> mate && o-> mate != NULL_SV) {
690 XPUSHs( sv_mortalcopy( o-> mate));
691 if (( Handle) o != self)
692 --SvREFCNT( SvRV( o-> mate));
693 } else {
694 XPUSHs( &PL_sv_undef);
695 err = true;
696 }
697 }
698 plist_destroy( ret);
699 } else {
700 XPUSHs( &PL_sv_undef);
701 err = true;
702 }
703
704 /* This code breaks exception propagation chain
705 since it uses $@ for its own needs */
706 if ( err)
707 sv_setpv( GvSV( PL_errgv), error);
708 else
709 sv_setsv( GvSV( PL_errgv), NULL_SV);
710
711 PUTBACK;
712 return;
713 }
714
715 int
Image_lineSize(Handle self,Bool set,int dummy)716 Image_lineSize( Handle self, Bool set, int dummy)
717 {
718 if ( set)
719 croak("Image::lineSize: attempt to write read-only property");
720
721 return var-> lineSize;
722 }
723
724 PList
Image_load_REDEFINED(SV * who,SV * filename,HV * profile)725 Image_load_REDEFINED( SV * who, SV *filename, HV * profile)
726 {
727 return NULL;
728 }
729
730 PList
Image_load(SV * who,SV * filename,HV * profile)731 Image_load( SV * who, SV *filename, HV * profile)
732 {
733 PList ret;
734 Handle self = gimme_the_mate( who);
735 char error[ 256];
736 if ( !pexist( className))
737 pset_c( className, self ? my-> className : ( char*) SvPV_nolen( who));
738 ret = apc_img_load( self, SvPV_nolen(filename), prima_is_utf8_sv(filename), NULL, profile, error);
739 return ret;
740 }
741
742
XS(Image_save_FROMPERL)743 XS( Image_save_FROMPERL)
744 {
745 dXSARGS;
746 Handle self;
747 HV *profile;
748 char *fn;
749 int ret;
750 char error[256];
751 FileStream f = NULL;
752 SV * sv;
753 Bool is_utf8;
754 ImgIORequest ioreq, *pioreq;
755
756 if (( items < 2) || (( items % 2) != 0))
757 croak("Invalid usage of Prima::Image::save");
758
759 self = gimme_the_mate( ST( 0));
760
761 sv = ST(1);
762 if ( SvROK(sv) && SvTYPE( SvRV( sv)) == SVt_PVGV)
763 f = IoIFP(sv_2io(ST(1)));
764
765 if ( f != NULL) {
766 pioreq = &ioreq;
767 ioreq. handle = f;
768 ioreq. read = img_perlio_read;
769 ioreq. write = img_perlio_write;
770 ioreq. seek = img_perlio_seek;
771 ioreq. tell = img_perlio_tell;
772 ioreq. flush = img_perlio_flush;
773 ioreq. error = img_perlio_error;
774 fn = NULL;
775 is_utf8 = false;
776 } else {
777 fn = ( char *) SvPV_nolen( ST( 1));
778 is_utf8 = prima_is_utf8_sv( ST(1) );
779 pioreq = NULL;
780 }
781
782 profile = parse_hv( ax, sp, items, mark, 2, "Image::save");
783 ret = apc_img_save( self, fn, is_utf8, pioreq, profile, error);
784 sv_free(( SV *) profile);
785 SPAGAIN;
786 SP -= items;
787 XPUSHs( sv_2mortal( newSViv(( ret > 0) ? ret : -ret)));
788
789 /* This code breaks exception propagation chain
790 since it uses $@ for its own needs */
791 if ( ret <= 0)
792 sv_setpv( GvSV( PL_errgv), error);
793 else
794 sv_setsv( GvSV( PL_errgv), NULL_SV);
795 PUTBACK;
796 return;
797 }
798
799 int
Image_save_REDEFINED(SV * who,SV * filename,HV * profile)800 Image_save_REDEFINED( SV * who, SV *filename, HV * profile)
801 {
802 return 0;
803 }
804
805 int
Image_save(SV * who,SV * filename,HV * profile)806 Image_save( SV * who, SV *filename, HV * profile)
807 {
808 Handle self = gimme_the_mate( who);
809 char error[ 256];
810 if ( !pexist( className))
811 pset_c( className, self ? my-> className : ( char*) SvPV_nolen( who));
812 return apc_img_save( self, SvPV_nolen(filename), prima_is_utf8_sv(filename), NULL, profile, error);
813 }
814
815 int
Image_type(Handle self,Bool set,int type)816 Image_type( Handle self, Bool set, int type)
817 {
818 HV * profile;
819 if ( !set)
820 return var->type;
821 profile = newHV();
822 pset_i( type, type);
823 my-> set( self, profile);
824 sv_free(( SV *) profile);
825 return NULL_HANDLE;
826 }
827
828 int
Image_get_bpp(Handle self)829 Image_get_bpp( Handle self)
830 {
831 return var->type & imBPP;
832 }
833
834
835 Bool
Image_begin_paint(Handle self)836 Image_begin_paint( Handle self)
837 {
838 Bool ok;
839 if ( var-> regionData ) {
840 free(var->regionData);
841 var->regionData = NULL;
842 }
843 if ( !inherited begin_paint( self))
844 return false;
845 if ( !( ok = apc_image_begin_paint( self))) {
846 inherited end_paint( self);
847 perl_error();
848 }
849 if (ok)
850 apc_gp_set_antialias( self, var->antialias );
851 return ok;
852 }
853
854 Bool
Image_begin_paint_info(Handle self)855 Image_begin_paint_info( Handle self)
856 {
857 Bool ok;
858 if ( is_opt( optInDraw)) return true;
859 if ( var-> regionData ) {
860 free(var->regionData);
861 var->regionData = NULL;
862 }
863 if ( !inherited begin_paint_info( self))
864 return false;
865 if ( !( ok = apc_image_begin_paint_info( self))) {
866 inherited end_paint_info( self);
867 perl_error();
868 }
869 if (ok)
870 apc_gp_set_antialias( self, var->antialias );
871 return ok;
872 }
873
874
875 void
Image_end_paint(Handle self)876 Image_end_paint( Handle self)
877 {
878 int oldType = var->type;
879 if ( !is_opt( optInDraw)) return;
880 apc_image_end_paint( self);
881 inherited end_paint( self);
882 if ( is_opt( optPreserveType) && var->type != oldType) {
883 my->reset( self, oldType, NULL, 0);
884 } else {
885 switch( var->type)
886 {
887 case imbpp1:
888 if ( var-> palSize == 2 && memcmp( var->palette, stdmono_palette, sizeof( stdmono_palette)) == 0)
889 var->type |= imGrayScale;
890 break;
891 case imbpp4:
892 if ( var-> palSize == 16 && memcmp( var->palette, std16gray_palette, sizeof( std16gray_palette)) == 0)
893 var->type |= imGrayScale;
894 break;
895 case imbpp8:
896 if ( var-> palSize == 256 && memcmp( var->palette, std256gray_palette, sizeof( std256gray_palette)) == 0)
897 var->type |= imGrayScale;
898 break;
899 }
900 my->update_change( self);
901 }
902 }
903
904 void
Image_end_paint_info(Handle self)905 Image_end_paint_info( Handle self)
906 {
907 if ( !is_opt( optInDrawInfo)) return;
908 apc_image_end_paint_info( self);
909 inherited end_paint_info( self);
910 }
911
912 void
Image_update_change(Handle self)913 Image_update_change( Handle self)
914 {
915 if ( var-> updateLock ) return;
916 if ( var-> stage <= csNormal) apc_image_update_change( self);
917 var->statsCache = 0;
918 }
919
920 double
Image_stats(Handle self,Bool set,int index,double value)921 Image_stats( Handle self, Bool set, int index, double value)
922 {
923 if ( index < 0 || index > isMaxIndex) return 0;
924 if ( set) {
925 var-> stats[ index] = value;
926 var-> statsCache |= 1 << index;
927 return 0;
928 } else {
929 #define gather_stats(TYP) if ( var->data) { \
930 TYP *src = (TYP*)var->data, *stop, *s; \
931 maxv = minv = *src; \
932 for ( y = 0; y < var->h; y++) { \
933 s = src; stop = s + var->w; \
934 while (s != stop) { \
935 v = (double)*s; \
936 sum += v; \
937 sum2 += v*v; \
938 if ( minv > v) minv = v; \
939 if ( maxv < v) maxv = v; \
940 s++; \
941 } \
942 src = (TYP*)(((Byte *)src) + var->lineSize); \
943 } \
944 }
945 int y;
946 double sum = 0.0, sum2 = 0.0, minv = 0.0, maxv = 0.0, v;
947
948 if ( var->statsCache & ( 1 << index)) return var->stats[ index];
949 /* calculate image stats */
950 switch ( var->type) {
951 case imByte: gather_stats(uint8_t);break;
952 case imShort: gather_stats(int16_t); break;
953 case imLong: gather_stats(int32_t); break;
954 case imFloat: gather_stats(float); break;
955 case imDouble: gather_stats(double); break;
956 default: return 0;
957 }
958 if ( var->w * var->h > 0)
959 {
960 var->stats[ isSum] = sum;
961 var->stats[ isSum2] = sum2;
962 sum /= var->w * var->h;
963 sum2 /= var->w * var->h;
964 sum2 = sum2 - sum*sum;
965 var->stats[ isMean] = sum;
966 var->stats[ isVariance] = sum2;
967 var->stats[ isStdDev] = sqrt(sum2);
968 var->stats[ isRangeLo] = minv;
969 var->stats[ isRangeHi] = maxv;
970 } else {
971 for ( y = 0; y <= isMaxIndex; y++) var->stats[ y] = 0;
972 }
973 var->statsCache = (1 << (isMaxIndex + 1)) - 1;
974 }
975 return var->stats[ index];
976 }
977
978 void
Image_resample(Handle self,double srcLo,double srcHi,double dstLo,double dstHi)979 Image_resample( Handle self, double srcLo, double srcHi, double dstLo, double dstHi)
980 {
981 #define RSPARMS self, var->data, var->type, srcLo, srcHi, dstLo, dstHi
982 switch ( var->type)
983 {
984 case imByte: rs_Byte_Byte ( RSPARMS); break;
985 case imShort: rs_Short_Short ( RSPARMS); break;
986 case imLong: rs_Long_Long ( RSPARMS); break;
987 case imFloat: rs_float_float ( RSPARMS); break;
988 case imDouble: rs_double_double ( RSPARMS); break;
989 default: return;
990 }
991 my->update_change( self);
992 }
993
994 SV *
Image_palette(Handle self,Bool set,SV * palette)995 Image_palette( Handle self, Bool set, SV * palette)
996 {
997 if ( var->stage > csFrozen) return NULL_SV;
998 if ( set) {
999 int ps;
1000 if ( var->type & imGrayScale) return NULL_SV;
1001 if ( !var->palette) return NULL_SV;
1002 ps = apc_img_read_palette( var->palette, palette, true);
1003
1004 if ( ps)
1005 var-> palSize = ps;
1006 else
1007 warn("Invalid array reference passed to Image::palette");
1008 my-> update_change( self);
1009 } else {
1010 int i;
1011 AV * av = newAV();
1012 int colors = ( 1 << ( var->type & imBPP)) & 0x1ff;
1013 Byte * pal = ( Byte*) var->palette;
1014 if (( var->type & imGrayScale) && (( var->type & imBPP) > imbpp8)) colors = 256;
1015 if ( var-> palSize < colors) colors = var-> palSize;
1016 for ( i = 0; i < colors*3; i++) av_push( av, newSViv( pal[ i]));
1017 return newRV_noinc(( SV *) av);
1018 }
1019 return NULL_SV;
1020 }
1021
1022 int
Image_conversion(Handle self,Bool set,int conversion)1023 Image_conversion( Handle self, Bool set, int conversion)
1024 {
1025 if ( !set)
1026 return var-> conversion;
1027 if ( !iconvtype_supported(conversion))
1028 return var-> conversion;
1029 return var-> conversion = conversion;
1030 }
1031
1032 void
Image_create_empty(Handle self,int width,int height,int type)1033 Image_create_empty( Handle self, int width, int height, int type)
1034 {
1035 free( var->data);
1036 var->w = width;
1037 var->h = height;
1038 var->type = type;
1039 var->lineSize = LINE_SIZE(var->w, var->type);
1040 var->dataSize = var->lineSize * var->h;
1041 var->palSize = (1 << (var->type & imBPP)) & 0x1ff;
1042 var->statsCache = 0;
1043 if ( var->dataSize > 0)
1044 {
1045 var->data = allocb( var->dataSize);
1046 if ( var-> data == NULL) {
1047 int sz = var-> dataSize;
1048 my-> make_empty( self);
1049 croak("Image::create_empty: cannot allocate %d bytes", sz);
1050 }
1051 memset( var->data, 0, var->dataSize);
1052 } else
1053 var->data = NULL;
1054 if ( var->type & imGrayScale) switch ( var->type & imBPP)
1055 {
1056 case imbpp1:
1057 memcpy( var->palette, stdmono_palette, sizeof( stdmono_palette));
1058 break;
1059 case imbpp4:
1060 memcpy( var->palette, std16gray_palette, sizeof( std16gray_palette));
1061 break;
1062 case imbpp8:
1063 memcpy( var->palette, std256gray_palette, sizeof( std256gray_palette));
1064 break;
1065 }
1066 }
1067
1068 Bool
Image_preserveType(Handle self,Bool set,Bool preserveType)1069 Image_preserveType( Handle self, Bool set, Bool preserveType)
1070 {
1071 if ( !set)
1072 return is_opt( optPreserveType);
1073 opt_assign( optPreserveType, preserveType);
1074 return false;
1075 }
1076
1077 SV *
Image_pixel(Handle self,Bool set,int x,int y,SV * pixel)1078 Image_pixel( Handle self, Bool set, int x, int y, SV * pixel)
1079 {
1080 #define BGRto32(pal) ((var->palette[pal].r<<16) | (var->palette[pal].g<<8) | (var->palette[pal].b))
1081 if (!set) {
1082 if ( opt_InPaint)
1083 return inherited pixel(self,false,x,y,pixel);
1084
1085 if ((x>=var->w) || (x<0) || (y>=var->h) || (y<0))
1086 return newSViv( clInvalid);
1087
1088 if ( var-> type & (imComplexNumber|imTrigComplexNumber)) {
1089 AV * av = newAV();
1090 switch ( var-> type) {
1091 case imComplex:
1092 case imTrigComplex: {
1093 float * f = (float*)(var->data + (var->lineSize*y+x*2*sizeof(float)));
1094 av_push( av, newSVnv( *(f++)));
1095 av_push( av, newSVnv( *f));
1096 break;
1097 }
1098 case imDComplex:
1099 case imTrigDComplex: {
1100 double * f = (double*)(var->data + (var->lineSize*y+x*2*sizeof(double)));
1101 av_push( av, newSVnv( *(f++)));
1102 av_push( av, newSVnv( *f));
1103 break;
1104 }
1105 }
1106 return newRV_noinc(( SV*) av);
1107 } else if ( var-> type & imRealNumber) {
1108 switch ( var-> type) {
1109 case imFloat:
1110 return newSVnv(*(float*)(var->data + (var->lineSize*y+x*sizeof(float))));
1111 case imDouble:
1112 return newSVnv(*(double*)(var->data + (var->lineSize*y+x*sizeof(double))));
1113 default:
1114 return NULL_SV;
1115 }} else
1116 switch (var->type & imBPP) {
1117 case imbpp1: {
1118 Byte p=var->data[var->lineSize*y+(x>>3)];
1119 p=(p >> (7-(x & 7))) & 1;
1120 return newSViv(((var->type & imGrayScale) ? (p ? 255 : 0) : BGRto32(p)));
1121 }
1122 case imbpp4: {
1123 Byte p=var->data[var->lineSize*y+(x>>1)];
1124 p=(x&1) ? p & 0x0f : p>>4;
1125 return newSViv(((var->type & imGrayScale) ? (p*255L)/15 : BGRto32(p)));
1126 }
1127 case imbpp8: {
1128 Byte p=var->data[var->lineSize*y+x];
1129 return newSViv(((var->type & imGrayScale) ? p : BGRto32(p)));
1130 }
1131 case imbpp16: {
1132 return newSViv(*(Short*)(var->data + (var->lineSize*y+x*2)));
1133 }
1134 case imbpp24: {
1135 RGBColor p=*(PRGBColor)(var->data + (var->lineSize*y+x*3));
1136 return newSViv((p.r<<16) | (p.g<<8) | p.b);
1137 }
1138 case imbpp32:
1139 return newSViv(*(Long*)(var->data + (var->lineSize*y+x*4)));
1140 default:
1141 return newSViv(clInvalid);
1142 }
1143 #undef BGRto32
1144 } else {
1145 Color color;
1146 RGBColor rgb;
1147 #define LONGtoBGR(lv,clr) ((clr).b=(lv)&0xff,(clr).g=((lv)>>8)&0xff,(clr).r=((lv)>>16)&0xff,(clr))
1148 if ( is_opt( optInDraw))
1149 return inherited pixel(self,true,x,y,pixel);
1150
1151 if ((x>=var->w) || (x<0) || (y>=var->h) || (y<0))
1152 return NULL_SV;
1153
1154 if ( var-> type & (imComplexNumber|imTrigComplexNumber)) {
1155 if ( !SvROK( pixel) || ( SvTYPE( SvRV( pixel)) != SVt_PVAV)) {
1156 switch ( var-> type) {
1157 case imComplex:
1158 case imTrigComplex:
1159 *(float*)(var->data+(var->lineSize*y+x*2*sizeof(float)))=SvNV(pixel);
1160 break;
1161 case imDComplex:
1162 case imTrigDComplex:
1163 *(double*)(var->data+(var->lineSize*y+x*2*sizeof(double)))=SvNV(pixel);
1164 break;
1165 default:
1166 return NULL_SV;
1167 }
1168 } else {
1169 AV * av = (AV *) SvRV( pixel);
1170 SV **sv[2];
1171 sv[0] = av_fetch( av, 0, 0);
1172 sv[1] = av_fetch( av, 1, 0);
1173
1174 switch ( var-> type) {
1175 case imComplex:
1176 case imTrigComplex:
1177 if ( sv[0]) *(float*)(var->data+(var->lineSize*y+x*2*sizeof(float)))=SvNV(*(sv[0]));
1178 if ( sv[1]) *(float*)(var->data+(var->lineSize*y+(x*2+1)*sizeof(float)))=SvNV(*(sv[1]));
1179 break;
1180 case imDComplex:
1181 case imTrigDComplex:
1182 if ( sv[0]) *(double*)(var->data+(var->lineSize*y+x*2*sizeof(double)))=SvNV(*(sv[0]));
1183 if ( sv[1]) *(double*)(var->data+(var->lineSize*y+(x*2+1)*sizeof(double)))=SvNV(*(sv[1]));
1184 break;
1185 default:
1186 return NULL_SV;
1187 }
1188 }
1189 } else if ( var-> type & imRealNumber) {
1190 switch ( var-> type) {
1191 case imFloat:
1192 *(float*)(var->data+(var->lineSize*y+x*sizeof(float)))=SvNV(pixel);
1193 break;
1194 case imDouble:
1195 *(double*)(var->data+(var->lineSize*y+x*sizeof(double)))=SvNV(pixel);
1196 break;
1197 default:
1198 return NULL_SV;
1199 }
1200 my->update_change( self);
1201 return NULL_SV;
1202 }
1203
1204 color = SvIV( pixel);
1205 switch (var->type & imBPP) {
1206 case imbpp1 :
1207 {
1208 int x1=7-(x&7);
1209 Byte p=(((var->type & imGrayScale) ? color/255 : cm_nearest_color(LONGtoBGR(color,rgb),var->palSize,var->palette)) & 1);
1210 Byte *pd=var->data+(var->lineSize*y+(x>>3));
1211 *pd&=~(1 << x1);
1212 *pd|=(p << x1);
1213 }
1214 break;
1215 case imbpp4 :
1216 {
1217 Byte p=((var->type & imGrayScale) ? (color*15)/255 : cm_nearest_color(LONGtoBGR(color,rgb),var->palSize,var->palette));
1218 Byte *pd=var->data+(var->lineSize*y+(x>>1));
1219 if (x&1) {
1220 *pd&=0xf0;
1221 }
1222 else {
1223 p<<=4;
1224 *pd&=0x0f;
1225 }
1226 *pd|=p;
1227 }
1228 break;
1229 case imbpp8:
1230 {
1231 if (var->type & imGrayScale) {
1232 var->data[(var->lineSize)*y+x]=color;
1233 }
1234 else {
1235 var->data[(var->lineSize)*y+x]=cm_nearest_color(LONGtoBGR(color,rgb),(var->palSize),(var->palette));
1236 }
1237 }
1238 break;
1239 case imbpp16 :
1240 *(Short*)(var->data+(var->lineSize*y+(x<<1)))=color;
1241 break;
1242 case imbpp24 :
1243 (void) LONGtoBGR(color,rgb);
1244 memcpy((var->data + (var->lineSize*y+x*3)),&rgb,sizeof(RGBColor));
1245 break;
1246 case imbpp32 :
1247 *(Long*)(var->data+(var->lineSize*y+(x<<2)))=color;
1248 break;
1249 default:
1250 return NULL_SV;
1251 }
1252 my->update_change( self);
1253 #undef LONGtoBGR
1254 return NULL_SV;
1255 }
1256 }
1257
1258 Handle
Image_bitmap(Handle self)1259 Image_bitmap( Handle self)
1260 {
1261 Handle h;
1262 Point s;
1263 HV * profile = newHV();
1264
1265 pset_H( owner, var->owner);
1266 pset_i( width, var->w);
1267 pset_i( height, var->h);
1268 pset_sv_noinc( palette, my->get_palette( self));
1269 pset_i( type, (var-> type == imBW) ? dbtBitmap : dbtPixmap);
1270 h = Object_create( "Prima::DeviceBitmap", profile);
1271 sv_free(( SV *) profile);
1272 s = CDrawable( h)-> get_size( h);
1273 CDrawable( h)-> put_image_indirect( h, self, 0, 0, 0, 0, s.x, s.y, s.x, s.y, ropCopyPut);
1274 --SvREFCNT( SvRV( PDrawable( h)-> mate));
1275 return h;
1276 }
1277
1278
1279 Handle
Image_dup(Handle self)1280 Image_dup( Handle self)
1281 {
1282 Handle h;
1283 PImage i;
1284 HV * profile = newHV();
1285
1286 pset_H( owner, var->owner);
1287 pset_i( width, var->w);
1288 pset_i( height, var->h);
1289 pset_i( type, var->type);
1290 pset_i( conversion, var->conversion);
1291 pset_i( scaling, var->scaling);
1292 pset_i( preserveType, is_opt( optPreserveType));
1293
1294 h = Object_create( var->self-> className, profile);
1295 sv_free(( SV *) profile);
1296 i = ( PImage) h;
1297 memcpy( i-> palette, var->palette, 768);
1298 i-> palSize = var-> palSize;
1299 if ( i-> type != var->type)
1300 croak("Image::dup consistency failed");
1301 else
1302 memcpy( i-> data, var->data, var->dataSize);
1303 memcpy( i-> stats, var->stats, sizeof( var->stats));
1304 i-> statsCache = var->statsCache;
1305
1306 if ( var->mate && hv_exists(( HV*)SvRV( var-> mate), "extras", 6)) {
1307 SV ** sv = hv_fetch(( HV*)SvRV( var-> mate), "extras", 6, 0);
1308 if ( sv && SvOK( *sv) && SvROK( *sv) && SvTYPE( SvRV( *sv)) == SVt_PVHV)
1309 (void) hv_store(( HV*)SvRV( i-> mate), "extras", 6, newSVsv( *sv), 0);
1310 }
1311
1312 --SvREFCNT( SvRV( i-> mate));
1313 return h;
1314 }
1315
1316 Handle
Image_extract(Handle self,int x,int y,int width,int height)1317 Image_extract( Handle self, int x, int y, int width, int height)
1318 {
1319 Handle h;
1320 PImage i;
1321 HV * profile;
1322 unsigned char * data = var->data;
1323 int ls = var->lineSize;
1324 int nodata = 0;
1325
1326 if ( var->w == 0 || var->h == 0) return my->dup( self);
1327 if ( x < 0) x = 0;
1328 if ( y < 0) y = 0;
1329 if ( x >= var->w) x = var->w - 1;
1330 if ( y >= var->h) y = var->h - 1;
1331 if ( width + x > var->w) width = var->w - x;
1332 if ( height + y > var->h) height = var->h - y;
1333 if ( width <= 0 ) {
1334 warn("Requested image width is less than 1");
1335 width = 1;
1336 nodata = 1;
1337 }
1338 if ( height <= 0 ) {
1339 warn("Requested image height is less than 1");
1340 height = 1;
1341 nodata = 1;
1342 }
1343
1344 profile = newHV();
1345 pset_H( owner, var->owner);
1346 pset_i( width, width);
1347 pset_i( height, height);
1348 pset_i( type, var->type);
1349 pset_i( conversion, var->conversion);
1350 pset_i( scaling, var->scaling);
1351 pset_i( preserveType, is_opt( optPreserveType));
1352
1353 h = Object_create( var->self-> className, profile);
1354 sv_free(( SV *) profile);
1355
1356 i = ( PImage) h;
1357 memcpy( i-> palette, var->palette, 768);
1358 i-> palSize = var-> palSize;
1359 if (nodata) goto NODATA;
1360
1361 if (( var->type & imBPP) >= 8) {
1362 int pixelSize = ( var->type & imBPP) / 8;
1363 while ( height > 0) {
1364 height--;
1365 memcpy( i-> data + height * i-> lineSize,
1366 data + ( y + height) * ls + pixelSize * x,
1367 pixelSize * width);
1368 }
1369 } else if (( var->type & imBPP) == 4) {
1370 while ( height > 0) {
1371 height--;
1372 bc_nibble_copy( data + ( y + height) * ls, i-> data + height * i-> lineSize, x, width);
1373 }
1374 } else if (( var->type & imBPP) == 1) {
1375 while ( height > 0) {
1376 height--;
1377 bc_mono_copy( data + ( y + height) * ls, i-> data + height * i-> lineSize, x, width);
1378 }
1379 }
1380 NODATA:
1381 --SvREFCNT( SvRV( i-> mate));
1382 return h;
1383 }
1384
1385 /*
1386 divide the pixels, by whether they match color or not on two
1387 groups, F and B. Both are converted correspondingly to the settings
1388 of color/backColor and rop/rop2. Possible variations:
1389 rop == rop::NoOper, pixel value remains ths same
1390 rop == rop::CopyPut, use the color value
1391 rop == rop::Blackness, use black pixel
1392 rop == rop::Whiteness, use white pixel
1393 rop == rop::AndPut , result is dest & color value
1394 etc...
1395 */
1396
1397 void
Image_map(Handle self,Color color)1398 Image_map( Handle self, Color color)
1399 {
1400 Byte * d, b[2];
1401 RGBColor c;
1402 int type = var-> type, height = var-> h, i, ls;
1403 int rop[2];
1404 RGBColor r[2];
1405 int bc = 0;
1406
1407 if ( var-> data == NULL) return;
1408
1409 rop[0] = my-> get_rop( self);
1410 rop[1] = my-> get_rop2( self);
1411 if ( rop[0] == ropNoOper && rop[1] == ropNoOper) return;
1412
1413 for ( i = 0; i < 2; i++) {
1414 int not = 0;
1415
1416 switch( rop[i]) {
1417 case ropBlackness:
1418 r[i]. r = r[i]. g = r[i]. b = 0;
1419 rop[i] = ropCopyPut;
1420 break;
1421 case ropWhiteness:
1422 r[i]. r = r[i]. g = r[i]. b = 0xff;
1423 rop[i] = ropCopyPut;
1424 break;
1425 case ropNoOper:
1426 r[i]. r = r[i]. g = r[i]. b = 0;
1427 break;
1428 default: {
1429 Color c = i ? my-> get_backColor( self) : my-> get_color( self);
1430 r[i]. r = ( c >> 16) & 0xff;
1431 r[i]. g = ( c >> 8) & 0xff;
1432 r[i]. b = c & 0xff;
1433 }}
1434
1435 if (( type & imBPP) <= 8) {
1436 b[i] = cm_nearest_color( r[i], var-> palSize, var-> palette);
1437 }
1438
1439 switch ( rop[i]) {
1440 case ropNotPut:
1441 rop[i] = ropCopyPut; not = 1; break;
1442 case ropNotSrcXor: /* same as ropNotDestXor and ropNotXor */
1443 rop[i] = ropXorPut; not = 1; break;
1444 case ropNotSrcAnd:
1445 rop[i] = ropAndPut; not = 1; break;
1446 case ropNotSrcOr:
1447 rop[i] = ropOrPut; not = 1; break;
1448 }
1449
1450 if ( not) {
1451 r[i]. r = ~ r[i]. r;
1452 r[i]. g = ~ r[i]. g;
1453 r[i]. b = ~ r[i]. b;
1454 b[i] = ~ b[i];
1455 }
1456 }
1457
1458 c. r = ( color >> 16) & 0xff;
1459 c. g = ( color >> 8) & 0xff;
1460 c. b = color & 0xff;
1461 if (( type & imBPP) <= 8) {
1462 Color cc;
1463 bc = cm_nearest_color( c, var-> palSize, var-> palette);
1464 cc = ARGB( var->palette[bc].r, var->palette[bc].g, var->palette[bc].b);
1465 if ( cc != color) bc = 0xffff; /* no exact color found */
1466 }
1467
1468 if (
1469 (( type & imBPP) < 8) ||
1470 (
1471 ( type != imRGB) &&
1472 ( type != (imRGB | imGrayScale))
1473 )
1474 ) {
1475 if ( type & imGrayScale)
1476 my-> set_type( self, imbpp8 | imGrayScale);
1477 else
1478 my-> set_type( self, imbpp8);
1479 }
1480
1481 d = ( Byte * ) var-> data;
1482 ls = var-> lineSize;
1483
1484 while ( height--) {
1485 if (( type & imBPP) == 24) {
1486 PRGBColor data = ( PRGBColor) d;
1487 for ( i = 0; i < var-> w; i++) {
1488 int z = ( data-> r == c.r && data-> g == c.g && data-> b == c.b) ? 0 : 1;
1489 switch( rop[z]) {
1490 case ropAndPut:
1491 data-> r &= r[z]. r; data-> g &= r[z]. g; data-> b &= r[z]. b; break;
1492 case ropXorPut:
1493 data-> r ^= r[z]. r; data-> g ^= r[z]. g; data-> b ^= r[z]. b; break;
1494 case ropOrPut:
1495 data-> r |= r[z]. r; data-> g |= r[z]. g; data-> b |= r[z]. b; break;
1496 case ropNotDestAnd:
1497 data-> r = ( ~data-> r) & r[z].r; data-> g = ( ~data-> g) & r[z].g; data-> b = ( ~data-> b) & r[z].b; break;
1498 case ropNotDestOr:
1499 data-> r = ( ~data-> r) | r[z].r; data-> g = ( ~data-> g) | r[z].g; data-> b = ( ~data-> b) | r[z].b; break;
1500 case ropNotAnd:
1501 data-> r = ~(data-> r & r[z].r); data-> g = ~(data-> g & r[z].g); data-> b = ~(data-> b & r[z].b); break;
1502 case ropNotOr:
1503 data-> r = ~(data-> r | r[z].r); data-> g = ~(data-> g | r[z].g); data-> b = ~(data-> b | r[z].b); break;
1504 case ropNoOper:
1505 break;
1506 case ropInvert:
1507 data-> r = ~data-> r; data-> g = ~data-> g; data-> b = ~data-> b; break;
1508 default:
1509 data-> r = r[z]. r; data-> g = r[z]. g; data-> b = r[z]. b;
1510 }
1511 data++;
1512 }
1513 d += ls;
1514 } else {
1515 Byte * data = d;
1516 for ( i = 0; i < var-> w; i++) {
1517 int z = ( *data == bc) ? 0 : 1;
1518 switch( rop[z]) {
1519 case ropAndPut:
1520 *data &= b[z]; break;
1521 case ropXorPut:
1522 *data ^= b[z]; break;
1523 case ropOrPut:
1524 *data |= b[z]; break;
1525 case ropNotDestAnd:
1526 *data = (~(*data)) & b[z]; break;
1527 case ropNotDestOr:
1528 *data = (~(*data)) | b[z]; break;
1529 case ropNotAnd:
1530 *data = ~(*data & b[z]); break;
1531 case ropNotOr:
1532 *data = ~(*data | b[z]); break;
1533 case ropNoOper:
1534 break;
1535 case ropInvert:
1536 *data = ~(*data); break;
1537 default:
1538 *data = b[z]; break;
1539 }
1540 data++;
1541 }
1542 d += ls;
1543 }
1544 }
1545
1546 if ( is_opt( optPreserveType) && var->type != type)
1547 my-> set_type( self, type);
1548 else
1549 my-> update_change( self);
1550 }
1551
1552 SV *
Image_codecs(SV * dummy,int codecID)1553 Image_codecs( SV * dummy, int codecID)
1554 {
1555 PList p = plist_create( 16, 16);
1556 apc_img_codecs( p);
1557 if ( codecID < 0 ) {
1558 int i;
1559 AV * av = newAV();
1560 for ( i = 0; i < p-> count; i++) {
1561 PImgCodec c = ( PImgCodec ) p-> items[ i];
1562 HV * profile = apc_img_info2hash( c);
1563 pset_i( codecID, i);
1564 av_push( av, newRV_noinc(( SV *) profile));
1565 }
1566 plist_destroy( p);
1567 return newRV_noinc(( SV *) av);
1568 } else if ( codecID < p-> count ) {
1569 PImgCodec c = ( PImgCodec ) p-> items[ codecID];
1570 HV * profile = apc_img_info2hash( c);
1571 pset_i( codecID, codecID);
1572 return newRV_noinc(( SV *) profile);
1573 } else {
1574 return &PL_sv_undef;
1575 }
1576 }
1577
1578 static void
1579 color2pixel( Handle self, Color color, Byte * pixel);
1580
1581 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)1582 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)
1583 {
1584 Bool ret;
1585 Byte * color = NULL, colorbuf[ MAX_SIZEOF_PIXEL ];
1586
1587 if ( is_opt( optInDrawInfo)) return false;
1588 if ( image == NULL_HANDLE) return false;
1589 if ( is_opt( optInDraw))
1590 return inherited put_image_indirect( self, image, x, y, xFrom, yFrom, xDestLen, yDestLen, xLen, yLen, rop);
1591 if ( !kind_of( image, CImage)) return false;
1592
1593 if ( rop & ropConstantColor ) {
1594 bzero( colorbuf, MAX_SIZEOF_PIXEL );
1595 color2pixel( self, my->get_color(self), colorbuf );
1596 color = colorbuf;
1597 }
1598
1599 ret = img_put( self, image, x, y, xFrom, yFrom, xDestLen, yDestLen, xLen, yLen, rop,
1600 var->regionData ? &var->regionData-> data. box : NULL, color);
1601 my-> update_change( self);
1602 return ret;
1603 }
1604
1605 UV
Image_add_notification(Handle self,char * name,SV * subroutine,Handle referer,int index)1606 Image_add_notification( Handle self, char * name, SV * subroutine, Handle referer, int index)
1607 {
1608 UV id = inherited add_notification( self, name, subroutine, referer, index);
1609 if ( id != 0) Image_reset_notifications( self);
1610 return id;
1611 }
1612
1613 void
Image_remove_notification(Handle self,UV id)1614 Image_remove_notification( Handle self, UV id)
1615 {
1616 inherited remove_notification( self, id);
1617 Image_reset_notifications( self);
1618 }
1619
1620 static void
Image_reset_notifications(Handle self)1621 Image_reset_notifications( Handle self)
1622 {
1623 int i;
1624 PList list;
1625 void * ret[ 2];
1626 int cmd[ 2] = { IMG_EVENTS_HEADER_READY, IMG_EVENTS_DATA_READY };
1627 var-> eventMask2 = var-> eventMask1;
1628 if ( var-> eventIDs == NULL) return;
1629
1630 ret[0] = hash_fetch( var-> eventIDs, "HeaderReady", 11);
1631 ret[1] = hash_fetch( var-> eventIDs, "DataReady", 9);
1632
1633 for ( i = 0; i < 2; i++) {
1634 if ( ret[i] == NULL) continue;
1635 list = var-> events + PTR2IV( ret[i]) - 1;
1636 if ( list-> count > 0) var-> eventMask2 |= cmd[ i];
1637 }
1638 }
1639
1640 static void
color2pixel(Handle self,Color color,Byte * pixel)1641 color2pixel( Handle self, Color color, Byte * pixel)
1642 {
1643 RGBColor rgb;
1644 rgb.b = color & 0xff;
1645 rgb.g = ((color)>>8) & 0xff;
1646 rgb.r = ((color)>>16) & 0xff;
1647
1648 switch (var->type) {
1649 case imBW:
1650 pixel[0] = (int)( (rgb.r + rgb.g + rgb.b) / 768.0 + .5);
1651 break;
1652 case imbpp1:
1653 pixel[0] = cm_nearest_color(rgb,var->palSize,var->palette) & 1;
1654 break;
1655 case imbpp4 | imGrayScale :
1656 pixel[0] = (int)( (rgb.r + rgb.g + rgb.b) / 48.0);
1657 break;
1658 case imbpp4 :
1659 pixel[0] = cm_nearest_color(rgb,var->palSize,var->palette) & 7;
1660 break;
1661 case imByte:
1662 pixel[0] = (int)( (rgb.r + rgb.g + rgb.b) / 3.0);
1663 break;
1664 case imbpp8:
1665 pixel[0] = cm_nearest_color(rgb,var->palSize,var->palette);
1666 break;
1667 case imShort :
1668 *((Short*)pixel) = color;
1669 break;
1670 case imRGB :
1671 memcpy( pixel, &rgb, 3);
1672 break;
1673 case imLong :
1674 *((Long*)pixel) = color;
1675 break;
1676 default:
1677 croak("Not implemented yet");
1678 }
1679 }
1680
1681 static void
prepare_fill_context(Handle self,Point translate,PImgPaintContext ctx)1682 prepare_fill_context(Handle self, Point translate, PImgPaintContext ctx)
1683 {
1684 FillPattern * p = &ctx->pattern;
1685
1686 color2pixel( self, my->get_color(self), ctx->color);
1687 color2pixel( self, my->get_backColor(self), ctx->backColor);
1688
1689 ctx-> rop = var-> extraROP |
1690 (( var-> alpha < 255 ) ?
1691 ropSrcAlpha | ( var-> alpha << ropSrcAlphaShift ) :
1692 0
1693 );
1694 ctx-> region = var->regionData ? &var->regionData-> data. box : NULL;
1695 ctx-> patternOffset = my->get_fillPatternOffset(self);
1696 ctx-> patternOffset.x -= translate.x;
1697 ctx-> patternOffset.y -= translate.y;
1698 ctx-> transparent = my->get_rop2(self) == ropNoOper;
1699
1700 if ( my-> fillPattern == Drawable_fillPattern) {
1701 FillPattern * fp = apc_gp_get_fill_pattern( self);
1702 if ( fp )
1703 memcpy( p, fp, sizeof(FillPattern));
1704 else
1705 memset( p, 0xff, sizeof(FillPattern));
1706 } else {
1707 AV * av;
1708 SV * fp;
1709 fp = my->get_fillPattern( self);
1710 if ( fp && SvOK(fp) && SvROK(fp) && SvTYPE(av = (AV*)SvRV(fp)) == SVt_PVAV && av_len(av) == sizeof(FillPattern) - 1) {
1711 int i;
1712 for ( i = 0; i < 8; i++) {
1713 SV ** sv = av_fetch( av, i, 0);
1714 (*p)[i] = (sv && *sv && SvOK(*sv)) ? SvIV(*sv) : 0;
1715 }
1716 } else {
1717 warn("Bad array returned by .fillPattern");
1718 memset( p, 0xff, sizeof(FillPattern));
1719 }
1720 }
1721 }
1722
1723 static void
prepare_line_context(Handle self,unsigned char * lp,ImgPaintContext * ctx)1724 prepare_line_context( Handle self, unsigned char * lp, ImgPaintContext * ctx)
1725 {
1726 color2pixel( self, my->get_color(self), ctx->color);
1727 color2pixel( self, my->get_backColor(self), ctx->backColor);
1728 ctx-> rop = var-> extraROP |
1729 (( var-> alpha < 255 ) ?
1730 ropSrcAlpha | ( var-> alpha << ropSrcAlphaShift ) :
1731 0
1732 );
1733 ctx->region = var->regionData ? &var->regionData-> data. box : NULL;
1734 ctx->transparent = my->get_rop2(self) == ropNoOper;
1735 ctx->translate = my->get_translate(self);
1736 if ( my-> linePattern == Drawable_linePattern) {
1737 int lplen;
1738 lplen = apc_gp_get_line_pattern( self, lp);
1739 lp[lplen] = 0;
1740 } else {
1741 SV * sv = my->get_linePattern(self);
1742 if ( sv && SvOK(sv)) {
1743 STRLEN lplen;
1744 char * lpsv = SvPV(sv, lplen);
1745 if ( lplen > 255 ) lplen = 255;
1746 memcpy(lp, lpsv, lplen);
1747 lp[lplen] = 0;
1748 } else
1749 strcpy((char*) lp, (const char*) lpSolid);
1750 }
1751 ctx->linePattern = lp;
1752 }
1753
1754 static Bool
integral_rotate(Handle self,int degrees)1755 integral_rotate( Handle self, int degrees)
1756 {
1757 Byte * new_data = NULL;
1758 int new_line_size = 0;
1759
1760 if (( var-> type & imBPP) < 8) {
1761 Bool ok;
1762 int type = var->type;
1763 my->set_type( self, imbpp8 );
1764 ok = integral_rotate( self, degrees );
1765 if ( is_opt( optPreserveType)) {
1766 int conv = var-> conversion;
1767 my-> set_conversion( self, ictNone);
1768 my-> set_type( self, type);
1769 my-> set_conversion( self, conv);
1770 }
1771 return ok;
1772 }
1773
1774 switch (degrees) {
1775 case 90:
1776 case 270:
1777 new_line_size = LINE_SIZE( var-> h , var->type);
1778 if (( new_data = allocb( new_line_size * var->w )) == NULL ) {
1779 warn("Image::rotate: cannot allocate %d bytes", new_line_size * var->w);
1780 return false;
1781 }
1782 break;
1783 case 180:
1784 if (( new_data = allocb( var->dataSize )) == NULL ) {
1785 warn("Image::rotate: cannot allocate %d bytes", var->dataSize );
1786 return false;
1787 }
1788 break;
1789 default:
1790 croak("'degrees' must be 90,180,or 270");
1791 }
1792
1793 img_integral_rotate( self, new_data, new_line_size, degrees );
1794 if ( degrees != 180 ) {
1795 int h = var->h;
1796 var->h = var->w;
1797 var->w = h;
1798 var->lineSize = new_line_size;
1799 var->dataSize = new_line_size * var->h;
1800 }
1801 free( var->data);
1802 var->data = new_data;
1803
1804 my-> update_change(self);
1805 return true;
1806 }
1807
1808 static Bool
generic_rotate(Handle self,float degrees)1809 generic_rotate( Handle self, float degrees)
1810 {
1811 Image i;
1812 int desired_type = var->type;
1813
1814 if (( desired_type & imBPP) <= 8)
1815 desired_type = (desired_type & imGrayScale) ? imByte : imRGB;
1816
1817 if (var->type != desired_type) {
1818 Bool ok;
1819 int type = var->type;
1820 my->set_type( self, desired_type );
1821 ok = generic_rotate( self, degrees );
1822 if ( is_opt( optPreserveType)) {
1823 int conv = var-> conversion;
1824 my-> set_conversion( self, ictNone);
1825 my-> set_type( self, type);
1826 my-> set_conversion( self, conv);
1827 }
1828 return ok;
1829 }
1830
1831 if (!img_generic_rotate( self, degrees, &i ))
1832 return false;
1833
1834 free( var->data);
1835
1836 var->h = i. h;
1837 var->w = i. w;
1838 var->lineSize = i. lineSize;
1839 var->dataSize = i. dataSize;
1840 var->data = i. data;
1841
1842 my-> update_change(self);
1843 return true;
1844 }
1845
1846 Bool
Image_rotate(Handle self,double degrees)1847 Image_rotate( Handle self, double degrees)
1848 {
1849 degrees = fmod(degrees, 360.0);
1850 if ( degrees < 0 ) degrees += 360.0;
1851
1852 if ( degrees == 0.0 )
1853 return true;
1854
1855 if ( degrees == 90.0 || degrees == 180.0 || degrees == 270.0 )
1856 return integral_rotate(self, (int)degrees);
1857 else
1858 return generic_rotate(self, degrees);
1859 }
1860
1861 Bool
Image_transform(Handle self,double a,double b,double c,double d)1862 Image_transform( Handle self, double a, double b, double c, double d)
1863 {
1864 Image i;
1865 int desired_type = var->type;
1866 float matrix[4] = { a, b, c, d };
1867
1868 if (( desired_type & imBPP) <= 8)
1869 desired_type = (desired_type & imGrayScale) ? imByte : imRGB;
1870
1871 if (var->type != desired_type) {
1872 Bool ok;
1873 int type = var->type;
1874 my->set_type( self, desired_type );
1875 ok = my->transform( self, a, b, c, d);
1876 if ( is_opt( optPreserveType)) {
1877 int conv = var-> conversion;
1878 my-> set_conversion( self, ictNone);
1879 my-> set_type( self, type);
1880 my-> set_conversion( self, conv);
1881 }
1882 return ok;
1883 }
1884
1885 if (!img_2d_transform( self, matrix, &i ))
1886 return false;
1887
1888 if ( i.data != NULL ) {
1889 free( var->data);
1890
1891 var->h = i. h;
1892 var->w = i. w;
1893 var->lineSize = i. lineSize;
1894 var->dataSize = i. dataSize;
1895 var->data = i. data;
1896
1897 my-> update_change(self);
1898 }
1899
1900 return true;
1901 }
1902
1903 void
Image_mirror(Handle self,Bool vertically)1904 Image_mirror( Handle self, Bool vertically)
1905 {
1906 if (!vertically && ( var-> type & imBPP) < 8) {
1907 int type = var->type;
1908 my->set_type( self, imbpp8 );
1909 my->mirror( self, vertically );
1910 if ( is_opt( optPreserveType)) {
1911 int conv = var-> conversion;
1912 my-> set_conversion( self, ictNone);
1913 my-> set_type( self, type);
1914 my-> set_conversion( self, conv );
1915 }
1916 return;
1917 }
1918
1919 img_mirror( self, vertically );
1920 my-> update_change(self);
1921 }
1922
1923 void
Image_premultiply_alpha(Handle self,SV * alpha)1924 Image_premultiply_alpha( Handle self, SV * alpha)
1925 {
1926 int oldType;
1927
1928 oldType = var-> type;
1929 if ( var-> type & imGrayScale ) {
1930 if ( var-> type != imByte )
1931 my-> set_type( self, imByte );
1932 } else {
1933 if ( var-> type != imRGB )
1934 my-> set_type( self, imRGB );
1935 }
1936
1937 if ( SvROK( alpha )) {
1938 Handle a = gimme_the_mate( alpha), dup = NULL_HANDLE;
1939 if ( !a || !kind_of( a, CImage) || PImage(a)-> w != var-> w || PImage(a)-> h != var-> h )
1940 croak( "Illegal object reference passed to Prima::Image::%s", "premultiply_alpha");
1941 if ( PImage(a)->type != imByte)
1942 a = dup = CImage(a)->dup(a);
1943 img_premultiply_alpha_map( self, a);
1944 if (dup)
1945 Object_destroy(dup);
1946 } else
1947 img_premultiply_alpha_constant( self, SvIV( alpha ));
1948
1949 if ( is_opt( optPreserveType ) && var-> type != oldType )
1950 my-> set_type( self, oldType );
1951 else
1952 my-> update_change( self );
1953 }
1954
1955 int
Image_alpha(Handle self,Bool set,int alpha)1956 Image_alpha( Handle self, Bool set, int alpha)
1957 {
1958 if ( is_opt(optInDraw) || is_opt(optInDrawInfo))
1959 return inherited alpha(self,set,alpha);
1960
1961 if (set) {
1962 if ( alpha < 0 ) alpha = 0;
1963 if ( alpha > 255 ) alpha = 255;
1964 if (( alpha < 255 ) && !my->can_draw_alpha(self))
1965 alpha = 255;
1966 inherited alpha(self,set,alpha);
1967 }
1968 return var->alpha;
1969 }
1970
1971 Bool
Image_antialias(Handle self,Bool set,Bool aa)1972 Image_antialias( Handle self, Bool set, Bool aa)
1973 {
1974 if ( is_opt(optInDraw) || is_opt(optInDrawInfo))
1975 return inherited antialias(self,set,aa);
1976
1977 if (set) {
1978 if ( aa && !my->can_draw_alpha(self))
1979 aa = false;
1980 var-> antialias = aa;
1981 }
1982 return var->antialias;
1983 }
1984
1985 Rect
Image_clipRect(Handle self,Bool set,Rect r)1986 Image_clipRect( Handle self, Bool set, Rect r)
1987 {
1988 if ( is_opt(optInDraw) || is_opt(optInDrawInfo))
1989 return inherited clipRect(self,set,r);
1990
1991 if ( var-> stage > csFrozen) return r;
1992
1993 if ( set) {
1994 PRegionRec reg;
1995 if ( var-> regionData ) {
1996 free(var->regionData);
1997 var->regionData = NULL;
1998 }
1999 if ((reg = malloc(sizeof(RegionRec) + sizeof(Box))) != NULL) {
2000 Box *box;
2001 reg->type = rgnRectangle;
2002 reg-> data. box. n_boxes = 1;
2003 box = reg-> data. box. boxes = (Box*) (((Byte*)reg) + sizeof(RegionRec));
2004 box-> x = r.left;
2005 box-> y = r.bottom;
2006 box-> width = r.right - r.left + 1;
2007 box-> height = r.top - r.bottom + 1;
2008 var->regionData = reg;
2009 }
2010 } else if ( var-> regionData ) {
2011 Box box = img_region_box( &var->regionData->data.box);
2012 r.left = box.x;
2013 r.bottom = box.y;
2014 r.right = box.x + box.width - 1;
2015 r.top = box.y + box.height - 1;
2016 } else {
2017 r.left = r.bottom = 0;
2018 r.right = var-> w - 1;
2019 r.top = var-> h - 1;
2020 }
2021
2022 return r;
2023 }
2024
2025 Handle
Image_region(Handle self,Bool set,Handle mask)2026 Image_region( Handle self, Bool set, Handle mask)
2027 {
2028 if ( is_opt(optInDraw) || is_opt(optInDrawInfo))
2029 return inherited region(self,set,mask);
2030
2031 if ( var-> stage > csFrozen) return NULL_HANDLE;
2032
2033 if ( set) {
2034 if ( var-> regionData ) {
2035 free(var->regionData);
2036 var->regionData = NULL;
2037 }
2038
2039 if ( mask && kind_of( mask, CRegion)) {
2040 var->regionData = CRegion(mask)->update_change(mask, true);
2041 return NULL_HANDLE;
2042 }
2043
2044 if ( mask && !kind_of( mask, CImage)) {
2045 warn("Illegal object reference passed to Image::region");
2046 return NULL_HANDLE;
2047 }
2048
2049 if ( mask ) {
2050 Handle region;
2051 HV * profile = newHV();
2052 pset_H( image, mask );
2053 region = Object_create("Prima::Region", profile);
2054 sv_free(( SV *) profile);
2055 var->regionData = CRegion(region)->update_change(region, true);
2056 Object_destroy(region);
2057 }
2058
2059 } else if ( var-> regionData )
2060 return Region_create_from_data( NULL_HANDLE, var->regionData);
2061
2062 return NULL_HANDLE;
2063 }
2064
2065 int
Image_rop(Handle self,Bool set,int rop)2066 Image_rop( Handle self, Bool set, int rop)
2067 {
2068 if (!set) return var-> extraROP;
2069 if ( rop < 0 ) rop = 0;
2070 var-> extraROP = rop;
2071 if ( rop > ropNoOper ) rop = ropNoOper;
2072 apc_gp_set_rop( self, rop);
2073 return var-> extraROP;
2074 }
2075
2076 static Bool
primitive(Handle self,Bool fill,char * method,...)2077 primitive( Handle self, Bool fill, char * method, ...)
2078 {
2079 Bool r;
2080 SV * ret;
2081 char format[256];
2082 va_list args;
2083 va_start( args, method);
2084 ENTER;
2085 SAVETMPS;
2086 strcpy(format, "<");
2087 strncat(format, method, 255);
2088 ret = call_perl_indirect( self,
2089 var->antialias ?
2090 (fill ? "fill_imgaa_primitive" : "stroke_imgaa_primitive") :
2091 (fill ? "fill_img_primitive" : "stroke_img_primitive"),
2092 format, true, false, args);
2093 va_end( args);
2094 r = ret ? SvTRUE( ret) : false;
2095 FREETMPS;
2096 LEAVE;
2097 return r;
2098 }
2099
2100 Bool
Image_arc(Handle self,double x,double y,double dX,double dY,double startAngle,double endAngle)2101 Image_arc( Handle self, double x, double y, double dX, double dY, double startAngle, double endAngle)
2102 {
2103 if ( opt_InPaint) return inherited arc(self, x, y, dX, dY, startAngle, endAngle);
2104 while ( startAngle > endAngle ) endAngle += 360.0;
2105 return primitive( self, 0, "snnnnnn", "arc", x, y, dX-1, dY-1, startAngle, endAngle);
2106 }
2107
2108 Bool
Image_bar(Handle self,double x1,double y1,double x2,double y2)2109 Image_bar( Handle self, double x1, double y1, double x2, double y2)
2110 {
2111 Point t;
2112 Bool ok;
2113 int _x1, _y1, _x2, _y2;
2114 ImgPaintContext ctx;
2115 if (opt_InPaint)
2116 return inherited bar( self, x1, y1, x2, y2);
2117 else if ( var-> antialias ) {
2118 ok = primitive( self, 1, "snnnn", "rectangle", x1, y1, x2, y2);
2119 my-> update_change(self);
2120 return ok;
2121 }
2122
2123 _x1 = x1;
2124 _x2 = x2;
2125 _y1 = y1;
2126 _y2 = y2;
2127
2128 t = my->get_translate(self);
2129 _x1 += t.x;
2130 _y1 += t.y;
2131
2132 prepare_fill_context(self, t, &ctx);
2133 ok = img_bar( self, _x1, _y1, _x2 - _x1 + 1, _y2 - _y1 + 1, &ctx);
2134 my-> update_change(self);
2135 return ok;
2136 }
2137
2138 Bool
Image_bars(Handle self,SV * rects)2139 Image_bars( Handle self, SV * rects)
2140 {
2141 Point t;
2142 ImgPaintContext ctx;
2143 int i, count;
2144 Bool ok = true, do_free;
2145 Rect *p, *r;
2146 if (opt_InPaint)
2147 return inherited bars( self, rects);
2148 else if ( var-> antialias ) {
2149 NRect *p, *r;
2150 if (( p = prima_read_array( rects, "Image::bars", 'd', 4, 0, -1, &count, &do_free)) == NULL)
2151 return false;
2152 for ( i = 0, r = p; i < count; i++, r++) {
2153 ok = primitive( self, 1, "snnnn", "rectangle",
2154 r->left,
2155 r->bottom,
2156 r->right - r->left,
2157 r->top - r->bottom
2158 );
2159 if ( !ok ) break;
2160 }
2161 if ( do_free ) free( p);
2162 } else {
2163 if (( p = prima_read_array( rects, "Image::bars", 'i', 4, 0, -1, &count, &do_free)) == NULL)
2164 return false;
2165 t = my->get_translate(self);
2166 prepare_fill_context(self, t, &ctx);
2167 for ( i = 0, r = p; i < count; i++, r++) {
2168 ImgPaintContext ctx2 = ctx;
2169 if ( !( ok &= img_bar( self,
2170 r->left,
2171 r->bottom,
2172 r->right - r->left + 1,
2173 r->top - r->bottom + 1,
2174 &ctx2))) break;
2175 }
2176 if ( do_free ) free( p);
2177 }
2178 my-> update_change(self);
2179 return ok;
2180 }
2181
2182 Bool
Image_chord(Handle self,double x,double y,double dX,double dY,double startAngle,double endAngle)2183 Image_chord( Handle self, double x, double y, double dX, double dY, double startAngle, double endAngle)
2184 {
2185 if ( opt_InPaint) return inherited chord(self, x, y, dX, dY, startAngle, endAngle);
2186 return primitive( self, 0, "snnnnnn", "chord", x, y, dX-1, dY-1, startAngle, endAngle);
2187 }
2188
2189 Bool
Image_clear(Handle self,double x1,double y1,double x2,double y2)2190 Image_clear(Handle self, double x1, double y1, double x2, double y2)
2191 {
2192 Point t;
2193 Bool ok;
2194 ImgPaintContext ctx;
2195 int _x1, _y1, _x2, _y2;
2196 Bool full;
2197
2198 full = x1 < 0 && y1 < 0 && x2 < 0 && y2 < 0;
2199 if (opt_InPaint)
2200 return inherited clear( self, x1, y1, x2, y2);
2201 else if ( !full && var->antialias ) {
2202 Bool ok;
2203 Color color;
2204 FillPattern fp;
2205 color = apc_gp_get_color(self);
2206 memcpy(&fp, apc_gp_get_fill_pattern(self), sizeof(FillPattern));
2207 apc_gp_set_color(self, apc_gp_get_back_color(self));
2208 apc_gp_set_fill_pattern(self, fillPatterns[fpSolid]);
2209 ok = primitive( self, 1, "snnnn", "rectangle", x1, y1, x2, y2);
2210 apc_gp_set_fill_pattern(self, fp);
2211 apc_gp_set_color(self, color);
2212 return ok;
2213 } else {
2214 _x1 = x1;
2215 _x2 = x2;
2216 _y1 = y1;
2217 _y2 = y2;
2218 if ( _x1 < 0 && _y1 < 0 && _x2 < 0 && _y2 < 0) {
2219 _x1 = 0;
2220 _y1 = 0;
2221 _x2 = var-> w - 1;
2222 _y2 = var-> h - 1;
2223 }
2224 t = my->get_translate(self);
2225 _x1 += t.x;
2226 _y1 += t.y;
2227 color2pixel( self, my->get_backColor(self), ctx.color);
2228 *ctx.backColor = *ctx.color;
2229 ctx.rop = my->get_rop(self);
2230 ctx.region = var->regionData ? &var->regionData-> data. box : NULL;
2231 memset( ctx.pattern, 0xff, sizeof(ctx.pattern));
2232 ctx.patternOffset.x = ctx.patternOffset.y = 0;
2233 ctx.patternOffset.x -= t.x;
2234 ctx.patternOffset.y -= t.y;
2235 ctx.transparent = false;
2236 ok = img_bar( self, _x1, _y1, _x2 - _x1 + 1, _y2 - _y1 + 1, &ctx);
2237 }
2238 my-> update_change(self);
2239 return ok;
2240 }
2241
2242 Bool
Image_ellipse(Handle self,double x,double y,double dX,double dY)2243 Image_ellipse( Handle self, double x, double y, double dX, double dY)
2244 {
2245 if ( opt_InPaint) return inherited ellipse(self, x, y, dX, dY);
2246 return primitive( self, 0, "snnnn", "ellipse", x, y, dX-1, dY-1);
2247 }
2248
2249 Bool
Image_fill_chord(Handle self,double x,double y,double dX,double dY,double startAngle,double endAngle)2250 Image_fill_chord( Handle self, double x, double y, double dX, double dY, double startAngle, double endAngle)
2251 {
2252 if ( opt_InPaint) return inherited fill_chord(self, x, y, dX, dY, startAngle, endAngle);
2253 return primitive( self, 1, "snnnnnn", "chord", x, y, dX-1, dY-1, startAngle, endAngle);
2254 }
2255
2256 Bool
Image_fill_ellipse(Handle self,double x,double y,double dX,double dY)2257 Image_fill_ellipse( Handle self, double x, double y, double dX, double dY)
2258 {
2259 if ( opt_InPaint) return inherited fill_ellipse(self, x, y, dX, dY);
2260 return primitive( self, 1, "snnnn", "ellipse", x, y, dX-1, dY-1);
2261 }
2262
2263 Bool
Image_fillpoly(Handle self,SV * points)2264 Image_fillpoly( Handle self, SV * points)
2265 {
2266 if ( opt_InPaint) return inherited fillpoly(self, points);
2267 return primitive( self, 1, "sS", "line", points );
2268 }
2269
2270 Bool
Image_fill_sector(Handle self,double x,double y,double dX,double dY,double startAngle,double endAngle)2271 Image_fill_sector( Handle self, double x, double y, double dX, double dY, double startAngle, double endAngle)
2272 {
2273 if ( opt_InPaint) return inherited fill_sector(self, x, y, dX, dY, startAngle, endAngle);
2274 return primitive( self, 1, "snnnnnn", "sector", x, y, dX-1, dY-1, startAngle, endAngle);
2275 }
2276
2277 Bool
Image_flood_fill(Handle self,int x,int y,Color color,Bool singleBorder)2278 Image_flood_fill( Handle self, int x, int y, Color color, Bool singleBorder)
2279 {
2280 Point t;
2281 Bool ok;
2282 ImgPaintContext ctx;
2283 ColorPixel px;
2284 if (opt_InPaint)
2285 return inherited flood_fill(self, x, y, color, singleBorder);
2286
2287 t = my->get_translate(self);
2288 x += t.x;
2289 y += t.y;
2290
2291 prepare_fill_context(self, t, &ctx);
2292 color2pixel( self, color, (Byte*)&px);
2293 ok = img_flood_fill( self, x, y, px, singleBorder, &ctx);
2294 my-> update_change(self);
2295 return ok;
2296 }
2297
2298 Bool
Image_line(Handle self,double x1,double y1,double x2,double y2)2299 Image_line(Handle self, double x1, double y1, double x2, double y2)
2300 {
2301 if ( opt_InPaint) {
2302 return inherited line(self, x1, y1, x2, y2);
2303 } else if ( !var->antialias && (int)(my->get_lineWidth(self) + .5) == 0) {
2304 ImgPaintContext ctx;
2305 unsigned char lp[256];
2306 Point poly[2];
2307 prepare_line_context( self, lp, &ctx);
2308 poly[0].x = x1;
2309 poly[0].y = y1;
2310 poly[1].x = x2;
2311 poly[1].y = y2;
2312 return img_polyline(self, 2, poly, &ctx);
2313 } else {
2314 return primitive( self, 0, "snnnn", "line", x1, y1, x2, y2);
2315 }
2316 }
2317
2318 Bool
Image_lines(Handle self,SV * points)2319 Image_lines( Handle self, SV * points)
2320 {
2321 if ( opt_InPaint) {
2322 return inherited lines(self, points);
2323 } else if ( !var->antialias && (int)(my->get_lineWidth(self) + .5) == 0) {
2324 Point * lines, *p;
2325 int i, count;
2326 Bool ok = true, do_free;
2327 ImgPaintContext ctx, ctx2;
2328 unsigned char lp[256];
2329 if (( lines = prima_read_array( points, "Image::lines", 'i', 4, 0, -1, &count, &do_free)) == NULL)
2330 return false;
2331 prepare_line_context( self, lp, &ctx);
2332 for (i = 0, p = lines; i < count; i++, p+=2) {
2333 ctx2 = ctx;
2334 if ( !( ok &= img_polyline(self, 2, p, &ctx2))) break;
2335 }
2336 if (do_free) free(lines);
2337 return ok;
2338 } else {
2339 return primitive( self, 0, "sS", "lines", points );
2340 }
2341 }
2342
2343 Bool
Image_polyline(Handle self,SV * points)2344 Image_polyline( Handle self, SV * points)
2345 {
2346 if ( opt_InPaint) {
2347 return inherited polyline(self, points);
2348 } else if ( !var->antialias && (int)(my->get_lineWidth(self) + .5) == 0) {
2349 Point * lines;
2350 int count;
2351 Bool ok, do_free;
2352 ImgPaintContext ctx;
2353 unsigned char lp[256];
2354 if (( lines = prima_read_array( points, "Image::polyline", 'i', 2, 2, -1, &count, &do_free)) == NULL)
2355 return false;
2356 prepare_line_context( self, lp, &ctx);
2357 ok = img_polyline(self, count, lines, &ctx);
2358 if ( do_free ) free(lines);
2359 return ok;
2360 } else {
2361 return primitive( self, 0, "sS", "line", points );
2362 }
2363 }
2364
2365 Bool
Image_rectangle(Handle self,double x1,double y1,double x2,double y2)2366 Image_rectangle(Handle self, double x1, double y1, double x2, double y2)
2367 {
2368 if ( opt_InPaint) {
2369 return inherited rectangle(self, x1, y1, x2, y2);
2370 } else if ( !var->antialias && (int)(my->get_lineWidth(self) + .5) == 0) {
2371 ImgPaintContext ctx;
2372 unsigned char lp[256];
2373 Point r[5] = { {x1,y1}, {x2,y1}, {x2,y2}, {x1,y2}, {x1,y1} };
2374 prepare_line_context( self, lp, &ctx);
2375 return img_polyline(self, 5, r, &ctx);
2376 } else {
2377 return primitive( self, 0, "snnnn", "rectangle", x1, y1, x2, y2);
2378 }
2379 }
2380
2381 Bool
Image_sector(Handle self,double x,double y,double dX,double dY,double startAngle,double endAngle)2382 Image_sector( Handle self, double x, double y, double dX, double dY, double startAngle, double endAngle)
2383 {
2384 if ( opt_InPaint) return inherited sector(self, x, y, dX, dY, startAngle, endAngle);
2385 return primitive( self, 0, "snnnnnn", "sector", x, y, dX-1, dY-1, startAngle, endAngle);
2386 }
2387
2388 #ifdef __cplusplus
2389 }
2390 #endif
2391