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