1 #include "apricot.h"
2 #include "Drawable.h"
3 #include "Icon.h"
4 #include "Region.h"
5 #include <Drawable.inc>
6 
7 #ifdef __cplusplus
8 extern "C" {
9 #endif
10 
11 
12 #undef my
13 #define inherited CComponent->
14 #define my  ((( PDrawable) self)-> self)
15 #define var (( PDrawable) self)
16 
17 #define gpARGS            Bool inPaint = opt_InPaint
18 #define gpENTER(fail)     if ( !inPaint) if ( !my-> begin_paint_info( self)) return (fail)
19 #define gpLEAVE           if ( !inPaint) my-> end_paint_info( self)
20 
21 #define CHECK_GP(ret) \
22 	if ( !is_opt(optSystemDrawable)) { \
23 		warn("This method is not available because %s is not a system Drawable object. You need to implement your own (ref:%d)", my->className, __LINE__);\
24 		return ret; \
25 	}
26 
27 void
Drawable_init(Handle self,HV * profile)28 Drawable_init( Handle self, HV * profile)
29 {
30 	dPROFILE;
31 	inherited init( self, profile);
32 	apc_gp_init( self);
33 	var-> w = var-> h = 0;
34 	my-> set_alpha        ( self, pget_i ( alpha));
35 	my-> set_antialias    ( self, pget_B ( antialias));
36 	my-> set_color        ( self, pget_i ( color));
37 	my-> set_backColor    ( self, pget_i ( backColor));
38 	my-> set_fillMode     ( self, pget_i ( fillMode));
39 	my-> set_fillPattern  ( self, pget_sv( fillPattern));
40 	my-> set_lineEnd      ( self, pget_i ( lineEnd));
41 	my-> set_lineJoin     ( self, pget_i ( lineJoin));
42 	my-> set_linePattern  ( self, pget_sv( linePattern));
43 	my-> set_lineWidth    ( self, pget_f ( lineWidth));
44 	my-> set_miterLimit   ( self, pget_i ( miterLimit));
45 	my-> set_region       ( self, pget_H ( region));
46 	my-> set_rop          ( self, pget_i ( rop));
47 	my-> set_rop2         ( self, pget_i ( rop2));
48 	my-> set_textOpaque   ( self, pget_B ( textOpaque));
49 	my-> set_textOutBaseline( self, pget_B ( textOutBaseline));
50 	if ( pexist( translate))
51 	{
52 		AV * av;
53 		Point tr;
54 		SV ** holder, *sv;
55 
56 		sv = pget_sv( translate);
57 		if ( sv && SvOK(sv) && SvROK(sv) && SvTYPE(av = (AV*)SvRV(sv)) == SVt_PVAV && av_len(av) == 1) {
58 			tr.x = tr.y = 0;
59 			holder = av_fetch( av, 0, 0);
60 			if ( holder) tr.x = SvIV( *holder); else warn("Array panic on 'translate'");
61 			holder = av_fetch( av, 1, 0);
62 			if ( holder) tr.y = SvIV( *holder); else warn("Array panic on 'translate'");
63 			my-> set_translate( self, tr);
64 		} else
65 			warn("Array panic on 'translate'");
66 
67 		sv = pget_sv( fillPatternOffset);
68 		if ( sv && SvOK(sv) && SvROK(sv) && SvTYPE(av = (AV*)SvRV(sv)) == SVt_PVAV && av_len(av) == 1) {
69 			tr.x = tr.y = 0;
70 			holder = av_fetch( av, 0, 0);
71 			if ( holder) tr.x = SvIV( *holder); else warn("Array panic on 'fillPatternOffset'");
72 			holder = av_fetch( av, 1, 0);
73 			if ( holder) tr.y = SvIV( *holder); else warn("Array panic on 'fillPatternOffset'");
74 			my-> set_fillPatternOffset( self, tr );
75 		} else
76 			warn("Array panic on 'fillPatternOffset'");
77 	}
78 	SvHV_Font( pget_sv( font), &Font_buffer, "Drawable::init");
79 	my-> set_font( self, Font_buffer);
80 	my-> set_palette( self, pget_sv( palette));
81 	CORE_INIT_TRANSIENT(Drawable);
82 }
83 
84 static void
clear_font_abc_caches(Handle self)85 clear_font_abc_caches( Handle self)
86 {
87 	PList u;
88 	if (( u = var-> font_abc_glyphs)) {
89 		int i;
90 		for ( i = 0; i < u-> count; i += 2)
91 			free(( void*) u-> items[ i + 1]);
92 		plist_destroy( u);
93 		var-> font_abc_glyphs = NULL;
94 	}
95 	if (( u = var-> font_abc_unicode)) {
96 		int i;
97 		for ( i = 0; i < u-> count; i += 2)
98 			free(( void*) u-> items[ i + 1]);
99 		plist_destroy( u);
100 		var-> font_abc_unicode = NULL;
101 	}
102 	if ( var-> font_abc_ascii) {
103 		free( var-> font_abc_ascii);
104 		var-> font_abc_ascii = NULL;
105 	}
106 	if ( var-> font_abc_glyphs_ranges ) {
107 		free(var-> font_abc_glyphs_ranges);
108 		var-> font_abc_glyphs_ranges = NULL;
109 		var-> font_abc_glyphs_n_ranges = 0;
110 	}
111 }
112 
113 void
Drawable_done(Handle self)114 Drawable_done( Handle self)
115 {
116 	clear_font_abc_caches( self);
117 	apc_gp_done( self);
118 	inherited done( self);
119 }
120 
121 void
Drawable_cleanup(Handle self)122 Drawable_cleanup( Handle self)
123 {
124 	if ( is_opt( optInDrawInfo))
125 		my-> end_paint_info( self);
126 	if ( is_opt( optInDraw))
127 		my-> end_paint( self);
128 	inherited cleanup( self);
129 }
130 
131 Bool
Drawable_begin_paint(Handle self)132 Drawable_begin_paint( Handle self)
133 {
134 	if ( var-> stage > csFrozen) return false;
135 	if ( is_opt( optInDrawInfo)) my-> end_paint_info( self);
136 	opt_set( optInDraw);
137 	return true;
138 }
139 
140 void
Drawable_end_paint(Handle self)141 Drawable_end_paint( Handle self)
142 {
143 	clear_font_abc_caches( self);
144 	opt_clear( optInDraw);
145 }
146 
147 Bool
Drawable_begin_paint_info(Handle self)148 Drawable_begin_paint_info( Handle self)
149 {
150 	if ( var-> stage > csFrozen) return false;
151 	if ( is_opt( optInDraw))     return true;
152 	if ( is_opt( optInDrawInfo)) return false;
153 	opt_set( optInDrawInfo);
154 	return true;
155 }
156 
157 void
Drawable_end_paint_info(Handle self)158 Drawable_end_paint_info( Handle self)
159 {
160 	clear_font_abc_caches( self);
161 	opt_clear( optInDrawInfo);
162 }
163 
164 void
Drawable_set(Handle self,HV * profile)165 Drawable_set( Handle self, HV * profile)
166 {
167 	dPROFILE;
168 	if ( pexist( font))
169 	{
170 		SvHV_Font( pget_sv( font), &Font_buffer, "Drawable::set");
171 		my-> set_font( self, Font_buffer);
172 		pdelete( font);
173 	}
174 	if ( pexist( translate))
175 	{
176 		AV * av = ( AV *) SvRV( pget_sv( translate));
177 		Point tr = {0,0};
178 		SV ** holder = av_fetch( av, 0, 0);
179 		if ( holder) tr.x = SvIV( *holder); else warn("Array panic on 'translate'");
180 		holder = av_fetch( av, 1, 0);
181 		if ( holder) tr.y = SvIV( *holder); else warn("Array panic on 'translate'");
182 		my-> set_translate( self, tr);
183 		pdelete( translate);
184 	}
185 	if ( pexist( width) && pexist( height)) {
186 		Point size;
187 		size. x = pget_i( width);
188 		size. y = pget_i( height);
189 		my-> set_size( self, size);
190 		pdelete( width);
191 		pdelete( height);
192 	}
193 	if ( pexist( fillPatternOffset))
194 	{
195 		AV * av = ( AV *) SvRV( pget_sv( fillPatternOffset));
196 		Point fpo = {0,0};
197 		SV ** holder = av_fetch( av, 0, 0);
198 		if ( holder) fpo.x = SvIV( *holder); else warn("Array panic on 'fillPatternOffset'");
199 		holder = av_fetch( av, 1, 0);
200 		if ( holder) fpo.y = SvIV( *holder); else warn("Array panic on 'fillPatternOffset'");
201 		my-> set_fillPatternOffset( self, fpo);
202 		pdelete( fillPatternOffset);
203 	}
204 	inherited set( self, profile);
205 }
206 
207 
208 Font *
Drawable_font_match(char * dummy,Font * source,Font * dest,Bool pick)209 Drawable_font_match( char * dummy, Font * source, Font * dest, Bool pick)
210 {
211 	if ( pick)
212 		apc_font_pick( NULL_HANDLE, source, dest);
213 	else
214 		Drawable_font_add( NULL_HANDLE, source, dest);
215 	return dest;
216 }
217 
218 Bool
Drawable_font_add(Handle self,Font * source,Font * dest)219 Drawable_font_add( Handle self, Font * source, Font * dest)
220 {
221 	Bool useHeight = !source-> undef. height;
222 	Bool useWidth  = !source-> undef. width;
223 	Bool useSize   = !source-> undef. size;
224 	Bool usePitch  = !source-> undef. pitch;
225 	Bool useStyle  = !source-> undef. style;
226 	Bool useDir    = !source-> undef. direction;
227 	Bool useName   = !source-> undef. name;
228 	Bool useVec    = !source-> undef. vector;
229 	Bool useEnc    = !source-> undef. encoding;
230 
231 	/* assignning values */
232 	if ( dest != source) {
233 		dest-> undef = source-> undef;
234 		if ( useHeight) dest-> height    = source-> height;
235 		if ( useWidth ) dest-> width     = source-> width;
236 		if ( useDir   ) dest-> direction = source-> direction;
237 		if ( useStyle ) dest-> style     = source-> style;
238 		if ( usePitch ) dest-> pitch     = source-> pitch;
239 		if ( useSize  ) dest-> size      = source-> size;
240 		if ( useVec   ) dest-> vector    = source-> vector;
241 		if ( useName  ) {
242 			strcpy( dest-> name, source-> name);
243 			dest->is_utf8.name = source->is_utf8.name;
244 		}
245 		if ( useEnc   ) {
246 			strcpy( dest-> encoding, source-> encoding);
247 			dest->is_utf8.encoding = source->is_utf8.encoding;
248 		}
249 	}
250 
251 	/* nulling dependencies */
252 	if ( !useHeight && useSize)
253 		dest-> height = 0;
254 	if ( !useWidth && ( usePitch || useHeight || useName || useSize || useDir || useStyle))
255 		dest-> width = 0;
256 	if ( !usePitch && ( useStyle || useName || useDir || useWidth))
257 		dest-> pitch = fpDefault;
258 	if ( useHeight)
259 		dest-> size = 0;
260 	if ( !useHeight && !useSize && ( dest-> height <= 0 || dest-> height > 16383))
261 		useSize = 1;
262 
263 	/* validating entries */
264 	if ( dest-> height <= 0) dest-> height = 1;
265 		else if ( dest-> height > 16383 ) dest-> height = 16383;
266 	if ( dest-> width  <  0) dest-> width  = 1;
267 		else if ( dest-> width  > 16383 ) dest-> width  = 16383;
268 	if ( dest-> size   <= 0) dest-> size   = 1;
269 		else if ( dest-> size   > 16383 ) dest-> size   = 16383;
270 	if ( dest-> name[0] == 0) {
271 		strcpy( dest-> name, "Default");
272 		dest->is_utf8.name = false;
273 	}
274 	if ( dest-> undef.pitch || dest-> pitch < fpDefault || dest-> pitch > fpFixed)
275 		dest-> pitch = fpDefault;
276 	if ( dest-> undef. direction )
277 		dest-> direction = 0;
278 	if ( dest-> undef. style )
279 		dest-> style = 0;
280 	if ( dest-> undef. vector || dest-> vector < fvBitmap || dest-> vector > fvDefault)
281 		dest-> vector = fvDefault;
282 	if ( dest-> undef. encoding )
283 		dest-> encoding[0] = 0;
284 	memset(&dest->undef, 0, sizeof(dest->undef));
285 
286 	return useSize && !useHeight;
287 }
288 
289 int
Drawable_get_bpp(Handle self)290 Drawable_get_bpp( Handle self)
291 {
292 	gpARGS;
293 	int ret;
294 	CHECK_GP(0);
295 	gpENTER(0);
296 	ret = apc_gp_get_bpp( self);
297 	gpLEAVE;
298 	return ret;
299 }
300 
301 int
Drawable_get_paint_state(Handle self)302 Drawable_get_paint_state( Handle self)
303 {
304 	if ( is_opt( optInDraw))
305 		return psEnabled;
306 	else if ( is_opt( optInDrawInfo))
307 		return psInformation;
308 	else
309 		return psDisabled;
310 }
311 
312 Color
Drawable_get_nearest_color(Handle self,Color color)313 Drawable_get_nearest_color( Handle self, Color color)
314 {
315 	gpARGS;
316 	CHECK_GP(0);
317 	gpENTER(clInvalid);
318 	color = apc_gp_get_nearest_color( self, color);
319 	gpLEAVE;
320 	return color;
321 }
322 
323 SV *
Drawable_get_physical_palette(Handle self)324 Drawable_get_physical_palette( Handle self)
325 {
326 	gpARGS;
327 	int i, nCol;
328 	AV * av = newAV();
329 	PRGBColor r;
330 
331 	CHECK_GP(NULL_SV);
332 	gpENTER(newRV_noinc(( SV *) av));
333 	r = apc_gp_get_physical_palette( self, &nCol);
334 	gpLEAVE;
335 
336 	for ( i = 0; i < nCol; i++) {
337 		av_push( av, newSViv( r[ i].b));
338 		av_push( av, newSViv( r[ i].g));
339 		av_push( av, newSViv( r[ i].r));
340 	}
341 	free( r);
342 	return newRV_noinc(( SV *) av);
343 }
344 
345 SV *
Drawable_get_font_abcdef(Handle self,int first,int last,int flags,PFontABC (* func)(Handle,int,int,int))346 Drawable_get_font_abcdef( Handle self, int first, int last, int flags, PFontABC (*func)(Handle, int, int, int))
347 {
348 	int i;
349 	AV * av;
350 	PFontABC abc;
351 
352 	if ( first < 0) first = 0;
353 	if ( last  < 0) last  = 255;
354 
355 	if ( flags & toGlyphs )
356 		flags &= ~toUTF8;
357 	else if ( !(flags & toUTF8)) {
358 		if ( first > 255) first = 255;
359 		if ( last  > 255) last  = 255;
360 	}
361 
362 	if ( first > last)
363 		abc = NULL;
364 	else {
365 		gpARGS;
366 		gpENTER( newRV_noinc(( SV *) newAV()));
367 		abc = func( self, first, last, flags );
368 		gpLEAVE;
369 	}
370 
371 	av = newAV();
372 	if ( abc != NULL) {
373 		for ( i = 0; i <= last - first; i++) {
374 			av_push( av, newSVnv( abc[ i]. a));
375 			av_push( av, newSVnv( abc[ i]. b));
376 			av_push( av, newSVnv( abc[ i]. c));
377 		}
378 		free( abc);
379 	}
380 	return newRV_noinc(( SV *) av);
381 }
382 
383 SV *
Drawable_get_font_abc(Handle self,int first,int last,int flags)384 Drawable_get_font_abc( Handle self, int first, int last, int flags)
385 {
386 	CHECK_GP(NULL_SV);
387 	return Drawable_get_font_abcdef( self, first, last, flags, apc_gp_get_font_abc);
388 }
389 
390 SV *
Drawable_get_font_def(Handle self,int first,int last,int flags)391 Drawable_get_font_def( Handle self, int first, int last, int flags)
392 {
393 	CHECK_GP(NULL_SV);
394 	return Drawable_get_font_abcdef( self, first, last, flags, apc_gp_get_font_def);
395 }
396 
397 SV *
Drawable_get_font_languages(Handle self)398 Drawable_get_font_languages( Handle self)
399 {
400 	char *buf, *p;
401 	AV * av = newAV();
402 	gpARGS;
403 
404 	CHECK_GP(NULL_SV);
405 	gpENTER( newRV_noinc(( SV *) av));
406 	p = buf = apc_gp_get_font_languages( self);
407 	gpLEAVE;
408 	if (p) {
409 		while (*p) {
410 			int len = strlen(p);
411 			av_push(av, newSVpv(p, len));
412 			p += len + 1;
413 		}
414 		free(buf);
415 	}
416 	return newRV_noinc(( SV *) av);
417 }
418 
419 SV *
Drawable_get_font_ranges(Handle self)420 Drawable_get_font_ranges( Handle self)
421 {
422 	int count = 0;
423 	unsigned long * ret;
424 	AV * av = newAV();
425 	gpARGS;
426 
427 	CHECK_GP(NULL_SV);
428 	gpENTER( newRV_noinc(( SV *) av));
429 	ret = apc_gp_get_font_ranges( self, &count);
430 	gpLEAVE;
431 	if ( ret) {
432 		int i;
433 		for ( i = 0; i < count; i++)
434 			av_push( av, newSViv( ret[i]));
435 		free( ret);
436 	}
437 	return newRV_noinc(( SV *) av);
438 }
439 
440 
441 SV *
Drawable_get_handle(Handle self)442 Drawable_get_handle( Handle self)
443 {
444 	char buf[ 256];
445 	CHECK_GP(NULL_SV);
446 	snprintf( buf, 256, PR_HANDLE_FMT, apc_gp_get_handle( self));
447 	return newSVpv( buf, 0);
448 }
449 
450 int
Drawable_height(Handle self,Bool set,int height)451 Drawable_height( Handle self, Bool set, int height)
452 {
453 	Point p = my-> get_size( self);
454 	if ( !set)
455 		return p. y;
456 	p. y = height;
457 	my-> set_size( self, p);
458 	return height;
459 }
460 
461 Point
Drawable_resolution(Handle self,Bool set,Point resolution)462 Drawable_resolution( Handle self, Bool set, Point resolution)
463 {
464 	CHECK_GP(resolution);
465 	if ( set)
466 		croak("Attempt to write read-only property %s", "Drawable::resolution");
467 	return apc_gp_get_resolution( self);
468 }
469 
470 Point
Drawable_size(Handle self,Bool set,Point size)471 Drawable_size ( Handle self, Bool set, Point size)
472 {
473 	if ( set)
474 		croak("Attempt to write read-only property %s", "Drawable::size");
475 	size. x = var-> w;
476 	size. y = var-> h;
477 	return size;
478 }
479 
480 int
Drawable_width(Handle self,Bool set,int width)481 Drawable_width( Handle self, Bool set, int width)
482 {
483 	Point p = my-> get_size( self);
484 	if ( !set)
485 		return p. x;
486 	p. x = width;
487 	my-> set_size( self, p);
488 	return width;
489 }
490 
491 Bool
Drawable_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)492 Drawable_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)
493 {
494 	Bool ok;
495 	CHECK_GP(false);
496 	if ( image == NULL_HANDLE) return false;
497 	if ( !(PObject(image)-> options.optSystemDrawable)) {
498 		warn("This method is not available on this class because it is not a system Drawable object. You need to implement your own");
499 		return false;
500 	}
501 	if ( xLen == xDestLen && yLen == yDestLen)
502 		ok = apc_gp_put_image( self, image, x, y, xFrom, yFrom, xLen, yLen, rop);
503 	else
504 		ok = apc_gp_stretch_image( self, image, x, y, xFrom, yFrom, xDestLen, yDestLen, xLen, yLen, rop);
505 	if ( !ok) perl_error();
506 	return ok;
507 }
508 
509 static Bool
primitive(Handle self,Bool fill,char * method,...)510 primitive( Handle self, Bool fill, char * method, ...)
511 {
512 	Bool r;
513 	SV * ret;
514 	char format[256];
515 	va_list args;
516 	va_start( args, method);
517 	ENTER;
518 	SAVETMPS;
519 	strcpy(format, "<");
520 	strncat(format, method, 255);
521 	ret = call_perl_indirect( self, fill ? "fill_aa_primitive" : "stroke_aa_primitive", format, true, false, args);
522 	va_end( args);
523 	r = ret ? SvTRUE( ret) : false;
524 	FREETMPS;
525 	LEAVE;
526 	return r;
527 }
528 
529 #define IS_AA (var->antialias || (var->alpha < 255))
530 #define TRUNC(x) x=trunc(x)
531 #define TRUNC2(x,y) {TRUNC(x);TRUNC(y);}
532 #define TRUNC4(x,y,z,t) {TRUNC(x);TRUNC(y);TRUNC(z);TRUNC(t);}
533 
534 
535 Bool
Drawable_arc(Handle self,double x,double y,double dX,double dY,double startAngle,double endAngle)536 Drawable_arc( Handle self, double x, double y, double dX, double dY, double startAngle, double endAngle)
537 {
538 	CHECK_GP(false);
539 	while ( startAngle > endAngle ) endAngle += 360.0;
540 	return IS_AA ?
541 		primitive( self, 0, "snnnnnn", "arc", x, y, dX-1, dY-1, startAngle, endAngle) :
542 		apc_gp_arc(self, x, y, dX, dY, startAngle, endAngle);
543 }
544 
545 Bool
Drawable_chord(Handle self,double x,double y,double dX,double dY,double startAngle,double endAngle)546 Drawable_chord( Handle self, double x, double y, double dX, double dY, double startAngle, double endAngle)
547 {
548 	CHECK_GP(false);
549 	return IS_AA ?
550 		primitive( self, 0, "snnnnnn", "chord", x, y, dX-1, dY-1, startAngle, endAngle) :
551 		apc_gp_chord(self, x, y, dX, dY, startAngle, endAngle);
552 }
553 
554 Bool
Drawable_ellipse(Handle self,double x,double y,double dX,double dY)555 Drawable_ellipse( Handle self, double x, double y,  double dX, double dY)
556 {
557 	CHECK_GP(false);
558 	return IS_AA ?
559 		primitive( self, 0, "snnnn", "ellipse", x, y, dX-1, dY-1) :
560 		apc_gp_ellipse(self, x, y, dX, dY);
561 }
562 
563 Bool
Drawable_bar(Handle self,double x1,double y1,double x2,double y2)564 Drawable_bar( Handle self, double x1, double y1, double x2, double y2)
565 {
566 	CHECK_GP(false);
567 
568 	if ( !var->antialias ) TRUNC4(x1,y1,x2,y2);
569 
570 	if (IS_AA) {
571 		NPoint r[5] = { {x1,y1}, {x2,y1}, {x2,y2}, {x1,y2}, {x1,y1} };
572 		return apc_gp_aa_fill_poly( self, 5, r);
573 	} else
574 		return apc_gp_bar(self, x1, y1, x2, y2);
575 }
576 
577 Bool
Drawable_bars(Handle self,SV * rects)578 Drawable_bars( Handle self, SV * rects)
579 {
580 	int count;
581 	Rect * p;
582 	Bool ret = false, do_free;
583 	CHECK_GP(false);
584 	if (( p = prima_read_array( rects, "Drawable::bars",
585 		IS_AA ? 'd' : 'i',
586 		4, 0, -1, &count, &do_free)) == NULL)
587 		return false;
588 
589 	if ( IS_AA ) {
590 		int i;
591 		NRect *r;
592 		for ( i = 0, r = (NRect*)p; i < count; i++, r++) {
593 			NPoint xr[5] = {
594 				{r->left,r->bottom},
595 				{r->left,r->top},
596 				{r->right,r->top},
597 				{r->right,r->bottom},
598 				{r->left,r->bottom}
599 			};
600 			if ( !var->antialias) {
601 				int j;
602 				for ( j = 0; j < 5; j++)
603 					TRUNC2(xr[j].x,xr[j].y);
604 			}
605 			if ( !( ret = apc_gp_aa_fill_poly( self, 5, xr)))
606 				break;
607 		}
608 	} else
609 		ret = apc_gp_bars( self, count, p);
610 	if ( !ret) perl_error();
611 	if ( do_free ) free( p);
612 	return ret;
613 }
614 
615 Bool
Drawable_clear(Handle self,double x1,double y1,double x2,double y2)616 Drawable_clear( Handle self, double x1, double y1, double x2, double y2)
617 {
618 	Bool full;
619 	CHECK_GP(false);
620 
621 	full = x1 < 0 && y1 < 0 && x2 < 0 && y2 < 0;
622 	if ( !var->antialias ) TRUNC4(x1,y1,x2,y2);
623 	if ( !full && IS_AA) {
624 		Bool ok;
625 		Color color;
626 		FillPattern fp;
627 		NPoint r[5] = { {x1,y1}, {x2,y1}, {x2,y2}, {x1,y2}, {x1,y1} };
628 		color = apc_gp_get_color(self);
629 		memcpy(&fp, apc_gp_get_fill_pattern(self), sizeof(FillPattern));
630 		apc_gp_set_color(self, apc_gp_get_back_color(self));
631 		apc_gp_set_fill_pattern(self, fillPatterns[fpSolid]);
632 		ok = apc_gp_aa_fill_poly( self, 5, r);
633 		apc_gp_set_fill_pattern(self, fp);
634 		apc_gp_set_color(self, color);
635 		return ok;
636 	} else return apc_gp_clear(self,
637 		x1,y1,x2,y2
638 	);
639 }
640 
641 Bool
Drawable_fill_chord(Handle self,double x,double y,double dX,double dY,double startAngle,double endAngle)642 Drawable_fill_chord( Handle self, double x, double y, double dX, double dY, double startAngle, double endAngle)
643 {
644 	CHECK_GP(false);
645 	if (IS_AA) {
646 		return primitive( self, 1, "snnnnnn", "chord", x, y, dX, dY, startAngle, endAngle);
647 	} else return apc_gp_fill_chord(self,
648 		x, y, dX, dY, startAngle, endAngle
649 	);
650 }
651 
652 Bool
Drawable_fill_ellipse(Handle self,double x,double y,double dX,double dY)653 Drawable_fill_ellipse( Handle self, double x, double y,  double dX, double dY)
654 {
655 	if (IS_AA) {
656 		return primitive( self, 1, "snnnn", "ellipse", x, y, dX, dY);
657 	} else return apc_gp_fill_ellipse(self,
658 		x, y, dX, dY
659 	);
660 }
661 
662 Bool
Drawable_fill_sector(Handle self,double x,double y,double dX,double dY,double startAngle,double endAngle)663 Drawable_fill_sector( Handle self, double x, double y, double dX, double dY, double startAngle, double endAngle)
664 {
665 	CHECK_GP(false);
666 	if (IS_AA) {
667 		return primitive( self, 1, "snnnnnn", "sector", x, y, dX, dY, startAngle, endAngle);
668 	} else return apc_gp_fill_sector(self,
669 		x, y, dX, dY, startAngle, endAngle
670 	);
671 }
672 
673 Bool
Drawable_fillpoly(Handle self,SV * points)674 Drawable_fillpoly(Handle self, SV * points)
675 {
676 	int count;
677 	void *p;
678 	Bool ret = false;
679 	Bool do_free = true;
680 	CHECK_GP(false);
681 
682 	if (( p = prima_read_array(
683 		points, "fillpoly",
684 		IS_AA ? 'd' : 'i',
685 		2, 2, -1, &count,
686 		(var->alpha < 255 && !var->antialias) ? NULL : &do_free
687 	)) == NULL)
688 		return false;
689 
690 	if ( var->alpha < 255 && !var->antialias ) {
691 		int i;
692 		NPoint *pp = (NPoint*)p;
693 		for ( i = 0; i < count; i++, pp++) TRUNC2(pp->x,pp->y);
694 	}
695 
696 	ret = IS_AA ?
697 		apc_gp_aa_fill_poly( self, count, (NPoint*) p) :
698 		apc_gp_fill_poly( self, count, (Point*) p);
699 	if ( !ret) perl_error();
700 	if ( do_free ) free(p);
701 
702 	return ret;
703 }
704 
705 Bool
Drawable_line(Handle self,double x1,double y1,double x2,double y2)706 Drawable_line(Handle self, double x1, double y1, double x2, double y2)
707 {
708 	CHECK_GP(false);
709 	if (IS_AA)
710 		return primitive( self, 0, "snnnn", "line", x1, y1, x2, y2);
711 	else return apc_gp_line(self,
712 		x1, y1, x2, y2
713 	);
714 }
715 
716 static Bool
read_polypoints(Handle self,SV * points,char * procName,int min,Bool (* procPtr)(Handle,int,Point *))717 read_polypoints( Handle self, SV * points, char * procName, int min, Bool (*procPtr)(Handle,int,Point*))
718 {
719 	int count;
720 	Point * p;
721 	Bool ret = false;
722 	Bool do_free;
723 	if (( p = (Point*) prima_read_array( points, procName, 'i', 2, min, -1, &count, &do_free)) != NULL) {
724 		ret = procPtr( self, count, p);
725 		if ( !ret) perl_error();
726 		if ( do_free ) free(p);
727 	}
728 	return ret;
729 }
730 
731 Bool
Drawable_lines(Handle self,SV * lines)732 Drawable_lines(Handle self, SV * lines)
733 {
734 	CHECK_GP(false);
735 
736 	if (IS_AA)
737 		return primitive( self, 0, "sS", "lines", lines);
738 	else
739 		return read_polypoints( self, lines, "Drawable::lines", 2, apc_gp_draw_poly2);
740 }
741 
742 Bool
Drawable_polyline(Handle self,SV * lines)743 Drawable_polyline(Handle self, SV * lines)
744 {
745 	CHECK_GP(false);
746 
747 	if (IS_AA)
748 		return primitive( self, 0, "sS", "line", lines);
749 	else
750 		return read_polypoints( self, lines, "Drawable::polyline", 2, apc_gp_draw_poly);
751 }
752 
753 Bool
Drawable_rectangle(Handle self,double x1,double y1,double x2,double y2)754 Drawable_rectangle( Handle self, double x1, double y1, double x2, double y2)
755 {
756 	CHECK_GP(false);
757 	return IS_AA ?
758 		primitive( self, 0, "snnnn", "rectangle", x1,y1,x2,y2) :
759 		apc_gp_rectangle(self, x1, y1, x2, y2);
760 }
761 
762 SV *
Drawable_render_glyph(Handle self,int index,HV * profile)763 Drawable_render_glyph( Handle self, int index, HV * profile)
764 {
765 	int flags, *buffer, count;
766 	SV * ret;
767 	dPROFILE;
768 	gpARGS;
769 	CHECK_GP(NULL_SV);
770 	gpENTER(NULL_SV);
771 
772 	flags = ggoUseHints;
773 	if ( pexist(glyph)   && pget_B(glyph))   flags |= ggoGlyphIndex;
774 	if ( pexist(hints)   && !pget_B(hints))  flags &= ~ggoUseHints;
775 	if ( pexist(unicode) && pget_B(unicode)) flags |= ggoUnicode;
776 	count = apc_gp_get_glyph_outline( self, index, flags, &buffer);
777 	hv_clear(profile); /* old gencls bork */
778 	gpLEAVE;
779 
780 	if ( count < 0 ) return NULL_SV;
781 	ret = prima_array_new(sizeof(int) * count);
782 	memcpy( prima_array_get_storage(ret), buffer, sizeof(int) * count);
783 	if ( buffer ) free( buffer );
784 	return prima_array_tie( ret, sizeof(int), "i");
785 }
786 
787 /*
788 
789 Render B-spline
790 
791 thanks to :
792 
793 Marcel Steinbeck for tinyspline.c
794 Thibaut Séguy for bspline.js
795 Carl-Wilhelm Reinhold de Boor for the rendering algorithm
796 
797 */
798 
799 /* fill default set of knots for curve */
800 static double *
default_knots(int n_points,int degree,Bool clamped)801 default_knots( int n_points, int degree, Bool clamped )
802 {
803 	double * ret, * ptr;
804 	int i, n_knots, order;
805 
806 	order = degree + 1;
807 	n_knots = n_points + order;
808 	if ( !( ret = malloc( sizeof(double) * n_knots )))
809 		return NULL;
810 	ptr = ret;
811 
812 	if ( clamped ) {
813 		int slope = n_knots - order * 2;
814 		double fac = 1.0 / (n_knots - 2 * degree - 1);
815 		for ( i = 0; i < order; i++, ptr++) *ptr = 0.0;
816 		for ( i = 0; i < slope; i++, ptr++) *ptr = fac * (i + 1);
817 		for ( i = 0; i < order; i++, ptr++) *ptr = 1.0;
818 	} else {
819 		for ( i = 0; i < n_knots; i++, ptr++) *ptr = (double) i;
820 	}
821 
822 	return ret;
823 }
824 
825 /* render single point on a spline with de Boor's algorithm */
826 static Bool
render_point(double t,int degree,int n_points,int dimensions,double * v,double * knots,int * last_found_knot,Point * result,NPoint * nresult)827 render_point(
828 	double t,
829 	int degree, int n_points, int dimensions, double * v,
830 	double * knots, int * last_found_knot, Point * result, NPoint * nresult
831 ) {
832 	double lo, hi;
833 	int l, i, n_knots = n_points + degree + 1, s, found = false;
834 
835 	lo = knots[degree];
836 	hi = knots[n_knots - degree - 1];
837 	t = t * ( hi - lo ) + lo;
838 
839 	/* find which pair of knots t belongs to */
840 	s = ( *last_found_knot < 0 ) ? degree : *last_found_knot;
841 	for ( ; s < n_points; s++ ) {
842 		if ( t >= knots[s] && t <= knots[s + 1] ) {
843 			*last_found_knot = s;
844 			found = true;
845 			break;
846 		}
847 	}
848 	if ( !found ) {
849 		warn("badly formed knot vector: outside curve definition");
850 		return false;
851 	}
852 
853 	for ( l = 1; l <= degree + 1; l++) {
854 		for ( i = s; i > s - degree - 1 + l; i-- ) {
855 			int j, ix;
856 			double dkt, a, a_hat;
857 			dkt = knots[i + degree + 1 - l] - knots[i];
858 			if ( dkt == 0.0 ) {
859 				warn("badly formed knot vector: not increasing");
860 				return false;
861 			}
862 			a = ( t - knots[i] ) / dkt;
863 			a_hat = 1.0 - a;
864 			ix = i * 3;
865       			/* interpolate each component */
866 			for ( j = 0; j < dimensions; j++, ix++)
867 				v[ix] = a_hat * v[ix - 3] + a * v[ix];
868 		}
869 	}
870 
871 	/* convert back to cartesian and return */
872 	s *= 3;
873 	if ( dimensions == 3 ) {
874 		double f;
875 		f = v[s] / v[s+2];
876 		if ( result )
877 			result-> x = ( f < 0 ) ? (f - .5) : (f + .5);
878 		else
879 			nresult-> x = f;
880 		f = v[s+1] / v[s+2];
881 		if ( result )
882 			result-> y = ( f < 0 ) ? (f - .5) : (f + .5);
883 		else
884 			nresult-> y = f;
885 	} else if ( result ) {
886 		result-> x = (v[s] < 0) ? (v[s] - .5) : (v[s] + .5);
887 		result-> y = (v[s+1] < 0) ? (v[s+1] - .5) : (v[s+1] + .5);
888 	} else {
889 		nresult-> x = v[s];
890 		nresult-> y = v[s+1];
891 	}
892 
893 	return true;
894 }
895 
896 static int
tangent_detect(Point * a,Point * b)897 tangent_detect( Point * a, Point * b)
898 {
899 	register int x = a->x - b->x;
900 	register int y = a->y - b->y;
901 	if ( x == 0 ) {
902 		if ( y ==  0) return 0;
903 		if ( y == -1) return 1;
904 		if ( y ==  1) return 2;
905 	} else if ( y == 0 ) {
906 		if ( x == -1) return 3;
907 		if ( x ==  1) return 4;
908 	} else if ( x < 2 && x > -2 && y < 2 && y > -2 )
909 		return (x * 10) + y;
910 	return -1;
911 }
912 
913 static void
tangent_apply(int tangent,Point * b)914 tangent_apply( int tangent,  Point * b)
915 {
916 	switch(tangent) {
917 	case 1:
918 		b->y++;
919 		break;
920 	case 2:
921 		b->y--;
922 		break;
923 	case 3:
924 		b->x++;
925 		break;
926 	case 4:
927 		b->x--;
928 		break;
929 	case 10 - 1:
930 		b->x--;
931 		b->y++;
932 		break;
933 	case 10 + 1:
934 		b->x--;
935 		b->y--;
936 		break;
937 	case - 10 - 1:
938 		b->x++;
939 		b->y++;
940 		break;
941 	case - 10 + 1:
942 		b->x++;
943 		b->y--;
944 		break;
945 	}
946 }
947 
948 static Bool
cut_corner(int t2,int t1,Point * p2,Point * p1)949 cut_corner( int t2, int t1, Point * p2, Point * p1)
950 {
951 	switch (t2 * 10 + t1) {
952 	/*
953 	 >xxa    xx is tangent 3
954   	    ab>  aa is tangent 1
955 	         bb is something not 1
956 
957         corner is detected when (aa) is exactly 1px high,
958 	and is cut so that (ab) becomes (b)
959 	*/
960 	case 13: case 14: return p2->y + 1 == p1->y;
961 	case 23: case 24: return p2->y - 1 == p1->y;
962 	case 31: case 32: return p2->x + 1 == p1->x;
963 	case 41: case 42: return p2->x - 1 == p1->x;
964 	}
965 	return false;
966 }
967 
968 SV *
Drawable_render_spline(SV * obj,SV * points,HV * profile)969 Drawable_render_spline( SV * obj, SV * points, HV * profile)
970 {
971 	dPROFILE;
972 	NPoint *p, *pp;
973 	Point *rendered, *storage;
974 	NPoint *nrendered, *nstorage;
975 	SV *ret;
976 	Bool ok, closed, as_integer;
977 	int i, j, degree, precision, n_points, final_size, k, dim, n_add_points, temp_size,
978 		tangent, last_tangent;
979 	double *knots, *weights, t, dt, *weighted, *temp;
980 
981 	knots = weights = weighted = NULL;
982 	p = NULL;
983 	ret = NULL;
984 	ok = false;
985 
986 	/* parse input */
987 	if ( pexist( degree )) {
988 		degree = pget_i(degree);
989 		if ( degree < 2 ) {
990 			warn("degree must be at least 2");
991 			goto EXIT;
992 		}
993 	} else
994 		degree = 2;
995 
996 	if ( pexist( precision )) {
997 		precision = pget_i(precision);
998 		if ( precision < 2 || precision > 1024 ) {
999 			warn("precision must be at least 2 and max 1024");
1000 			goto EXIT;
1001 		}
1002 	} else
1003 		precision = 24;
1004 
1005 	as_integer = pexist(integer) ? pget_B( integer ) : true;
1006 
1007 	p = (NPoint*) prima_read_array( points, "Drawable::render_spline", 'd', 2, degree + 1, -1, &n_points, NULL);
1008 	if ( !p) goto EXIT;
1009 
1010 	/* closed curve will need at least one extra point and unclamped default knot set */
1011 	if ( pexist( closed )) {
1012 		SV * sv = pget_sv(closed);
1013 		if ( SvTYPE(sv) == SVt_NULL ) goto DETECT_SHAPE;
1014 		closed = SvTRUE(sv);
1015 	}
1016 	else {
1017 	DETECT_SHAPE:
1018 		closed = p[0].x == p[n_points-1].x && p[0].y == p[n_points-1].y;
1019 	}
1020 	n_add_points = closed ? degree - 1 : 0;
1021 	n_points += n_add_points;
1022 
1023 	if ( pexist( knots )) {
1024 		knots = (double*) prima_read_array( pget_sv(knots), "knots", 'd', 1,
1025 			n_points + degree + 1, n_points + degree + 1, NULL, NULL);
1026 		if (!knots) goto EXIT;
1027 	} else
1028 		knots = default_knots(n_points, degree, !closed);
1029 
1030 	if ( pexist( weights )) {
1031 		weights = (double*) prima_read_array(pget_sv(weights), "weights", 'd', 1,
1032 			n_points, n_points, NULL, NULL);
1033 		if (!weights) goto EXIT;
1034 		dim = 3;
1035 	} else {
1036 		weights = NULL; /* all ones */
1037 		dim = 2;
1038 	}
1039 
1040 	/* allocate result storage */
1041 	precision *= n_points - n_add_points;
1042 	ret = prima_array_new(( precision + 1) * (as_integer ? sizeof(Point) : sizeof(NPoint)) );
1043 
1044 	temp_size = sizeof(double) * 3 * n_points;
1045 	if ( !(weighted = malloc( 2 * temp_size ))) {
1046 		warn("not enough memory");
1047 		goto EXIT;
1048 	}
1049 
1050 	/* convert to weighted points */
1051 	for ( i = j = 0, pp = p; i < n_points; i++, pp++) {
1052 		register double w = weights ? weights[i] : 1.0;
1053 		weighted[j++] = pp->x * w;
1054 		weighted[j++] = pp->y * w;
1055 		weighted[j++] = w;
1056 		if ( i == n_points - n_add_points - 1 ) pp = p;
1057 	}
1058 	temp = weighted + 3 * n_points;
1059 
1060 	/* render */
1061 	final_size = 0;
1062 	if ( as_integer ) {
1063 		nrendered = nstorage = NULL;
1064 		rendered  = storage  = (Point*) prima_array_get_storage(ret);
1065 	} else {
1066 		rendered  = storage  = NULL;
1067 		nrendered = nstorage = (NPoint*) prima_array_get_storage(ret);
1068 	}
1069 	k = -1;
1070 	last_tangent = -1;
1071 	for ( i = 0, t = 0.0, dt = 1.0 / precision; i < precision - 1; i++, t += dt) {
1072 		memcpy( temp, weighted, temp_size);
1073 		if (!render_point(t, degree, n_points, dim, temp, knots, &k, rendered, nrendered))
1074 			goto EXIT;
1075 		if ( as_integer && i > 0 ) {
1076 			/* primitive line detection */
1077 			tangent = tangent_detect( rendered-1, rendered);
1078 			if ( tangent == 0 ) continue;
1079 			if ( final_size > 1 && tangent > 0 && tangent == last_tangent) {
1080 				tangent_apply( tangent, rendered-1);
1081 				continue;
1082 			} else if ( final_size > 1 && cut_corner(last_tangent, tangent, rendered-2, rendered-1)) {
1083 			/* primitive corner detection - convert 4-connectivity into 8- */
1084 				*(rendered-1) = *rendered;
1085 				tangent = -1;
1086 				rendered--;
1087 				final_size--;
1088 				continue;
1089 			} else
1090 				last_tangent = tangent;
1091 		}
1092 		final_size++;
1093 		if ( as_integer )
1094 			rendered++;
1095 		else
1096 			nrendered++;
1097 	}
1098 	memcpy( temp, weighted, temp_size);
1099 	if ( !render_point(1.0, degree, n_points, dim, temp, knots, &k, rendered, nrendered))
1100 		goto EXIT;
1101 	final_size++;
1102 	rendered++;
1103 	nrendered++;
1104 
1105 	/* looks good */
1106 	ok = true;
1107 	if ( closed ) {
1108 		final_size++;
1109 		if ( as_integer ) {
1110 			*rendered = storage[0];
1111 			rendered++;
1112 		} else {
1113 			*nrendered = nstorage[0];
1114 			nrendered++;
1115 		}
1116 	}
1117 	if ( final_size == 1 ) {
1118 		final_size = 2;
1119 		if ( as_integer )
1120 			storage[1] = storage[0];
1121 		else
1122 			nstorage[1] = nstorage[0];
1123 	}
1124 	prima_array_truncate( ret, final_size * (as_integer ? sizeof( Point) : sizeof(NPoint)) );
1125 
1126 EXIT:
1127 	hv_clear(profile); /* old gencls bork */
1128 	if (weighted) free(weighted);
1129 	if (p)        free(p);
1130 	if (knots)    free(knots);
1131 	if (weights)  free(weights);
1132 	if ( ok ) {
1133 		return prima_array_tie( ret,
1134 			as_integer ? sizeof(int) : sizeof(double),
1135 			as_integer ? "i" : "d"
1136 		);
1137 	} else {
1138 		if (ret)  sv_free(ret);
1139 		return newRV_noinc(( SV *) newAV());
1140 	}
1141 }
1142 
1143 SV *
Drawable_render_polyline(SV * obj,SV * points,HV * profile)1144 Drawable_render_polyline( SV * obj, SV * points, HV * profile)
1145 {
1146 	dPROFILE;
1147 	int count;
1148 	Bool free_input = false, free_buffer = false, as_integer = false;
1149 	double *input = NULL, *buffer = NULL, box[4];
1150 	SV * ret;
1151 	void * storage;
1152 
1153 	if (( input = (double*) prima_read_array( points, "render_polyline", 'd', 2, 1, -1, &count, &free_input)) == NULL)
1154 		goto FAIL;
1155 
1156 	if ( pexist(integer)) as_integer = pget_B(integer);
1157 
1158 	if ( pexist(matrix) ) {
1159 		int i;
1160 		double *src, *dst, *cmatrix;
1161 		if (( cmatrix = (double*) prima_read_array(
1162 			pget_sv(matrix),
1163 			"render_polyline.matrix", 'd', 1, 6, 6, NULL, NULL)
1164 		) == NULL)
1165 			goto FAIL;
1166 		if ( !( buffer = malloc(sizeof(double) * 2 * count))) {
1167 			free(cmatrix);
1168 			warn("Not enough memory");
1169 			goto FAIL;
1170 		}
1171 		free_buffer = true;
1172 
1173 		for ( i = 0, src = input, dst = buffer; i < count; i++) {
1174 			double x,y;
1175 			x = *(src++);
1176 			y = *(src++);
1177 			*(dst++) = cmatrix[0] * x + cmatrix[2] * y + cmatrix[4];
1178 			*(dst++) = cmatrix[1] * x + cmatrix[3] * y + cmatrix[5];
1179 		}
1180 		free(cmatrix);
1181 	} else {
1182 		buffer = input;
1183 		free_buffer = false;
1184 	}
1185 
1186 	if ( pexist(box) && pget_B(box)) {
1187 		int i;
1188 		double *src;
1189 		box[0] = box[2] = buffer[0];
1190 		box[1] = box[3] = buffer[1];
1191 		for ( i = 1, src = buffer + 2; i < count; i++) {
1192 			double x,y;
1193 			x = *(src++);
1194 			y = *(src++);
1195 			if ( box[0] > x ) box[0] = x;
1196 			if ( box[1] > y ) box[1] = y;
1197 			if ( box[2] < x ) box[2] = x;
1198 			if ( box[3] < y ) box[3] = y;
1199 		}
1200 		box[2] -= box[0] - 1;
1201 		box[3] -= box[1] - 1;
1202 		if ( as_integer ) {
1203 			box[0] = floor(box[0]);
1204 			box[1] = floor(box[1]);
1205 		}
1206 		if ( free_buffer ) free(buffer);
1207 		free_buffer = false;
1208 		buffer = box;
1209 		count  = 2;
1210 	}
1211 
1212 	ret = prima_array_new(count * 2 * (as_integer ? sizeof(int) : sizeof(double)));
1213 	storage = prima_array_get_storage(ret);
1214 	if ( as_integer ) {
1215 		int i, *dst;
1216 		double *src;
1217 		for ( i = 0, src = buffer, dst = (int*)storage; i < count; i++) {
1218 			register double x;
1219 			x = *(src++);
1220 			*(dst++) = x + ((x < 0) ? -.5 : +.5);
1221 			x = *(src++);
1222 			*(dst++) = x + ((x < 0) ? -.5 : +.5);
1223 		}
1224 	} else
1225 		memcpy(storage, buffer, count * 2 * sizeof(double));
1226 
1227 	if ( free_buffer ) free( buffer );
1228 	if ( free_input ) free(input);
1229 	hv_clear(profile); /* old gencls bork */
1230 
1231 	return prima_array_tie( ret,
1232 		as_integer ? sizeof(int) : sizeof(double),
1233 		as_integer ? "i" : "d");
1234 
1235 FAIL:
1236 	if ( free_buffer ) free( buffer );
1237 	if ( free_input ) free(input);
1238 	hv_clear(profile); /* old gencls bork */
1239 	return NULL_SV;
1240 }
1241 
1242 PRGBColor
prima_read_palette(int * palSize,SV * palette)1243 prima_read_palette( int * palSize, SV * palette)
1244 {
1245 	AV * av;
1246 	int i, count;
1247 	Byte * buf;
1248 
1249 	if ( !SvROK( palette) || ( SvTYPE( SvRV( palette)) != SVt_PVAV)) {
1250 		*palSize = 0;
1251 		return NULL;
1252 	}
1253 	av = (AV *) SvRV( palette);
1254 	count = av_len( av) + 1;
1255 	*palSize = count / 3;
1256 	count = *palSize * 3;
1257 	if ( count == 0) return NULL;
1258 
1259 	if ( !( buf = allocb( count))) return NULL;
1260 
1261 	for ( i = 0; i < count; i++)
1262 	{
1263 		SV **itemHolder = av_fetch( av, i, 0);
1264 		if ( itemHolder == NULL)
1265 			return ( PRGBColor) buf;
1266 		buf[ i] = SvIV( *itemHolder);
1267 	}
1268 
1269 	return ( PRGBColor) buf;
1270 }
1271 
1272 Bool
Drawable_sector(Handle self,double x,double y,double dX,double dY,double startAngle,double endAngle)1273 Drawable_sector( Handle self, double x, double y, double dX, double dY, double startAngle, double endAngle)
1274 {
1275 	CHECK_GP(false);
1276 	return IS_AA ?
1277 		primitive( self, 0, "snnnnnn", "sector", x, y, dX-1, dY-1, startAngle, endAngle) :
1278 		apc_gp_sector(self, x, y, dX, dY, startAngle, endAngle);
1279 }
1280 
1281 /* Properties */
1282 
1283 int
Drawable_alpha(Handle self,Bool set,int alpha)1284 Drawable_alpha( Handle self, Bool set, int alpha)
1285 {
1286 	if (!set) return apc_gp_get_alpha( self);
1287 	if ( alpha < 0 ) alpha = 0;
1288 	if ( alpha > 255 ) alpha = 255;
1289 	apc_gp_set_alpha( self, alpha);
1290 	return var->alpha = apc_gp_get_alpha(self);
1291 }
1292 
1293 Bool
Drawable_antialias(Handle self,Bool set,Bool aa)1294 Drawable_antialias( Handle self, Bool set, Bool aa)
1295 {
1296 	if (set) apc_gp_set_antialias( self, aa );
1297 	return var->antialias = apc_gp_get_antialias( self );
1298 }
1299 
1300 Color
Drawable_backColor(Handle self,Bool set,Color color)1301 Drawable_backColor( Handle self, Bool set, Color color)
1302 {
1303 	if (!set) return apc_gp_get_back_color( self);
1304 	apc_gp_set_back_color( self, color);
1305 	return color;
1306 }
1307 
1308 Color
Drawable_color(Handle self,Bool set,Color color)1309 Drawable_color( Handle self, Bool set, Color color)
1310 {
1311 	if (!set) return apc_gp_get_color( self);
1312 	apc_gp_set_color( self, color);
1313 	return color;
1314 }
1315 
1316 Rect
Drawable_clipRect(Handle self,Bool set,Rect clipRect)1317 Drawable_clipRect( Handle self, Bool set, Rect clipRect)
1318 {
1319 	if ( !set)
1320 		return apc_gp_get_clip_rect( self);
1321 	apc_gp_set_clip_rect( self, clipRect);
1322 	return clipRect;
1323 }
1324 
1325 int
Drawable_fillMode(Handle self,Bool set,int fillMode)1326 Drawable_fillMode( Handle self, Bool set, int fillMode)
1327 {
1328 	if (!set) return apc_gp_get_fill_mode( self);
1329 	apc_gp_set_fill_mode( self, fillMode);
1330 	return fillMode;
1331 }
1332 
1333 SV *
Drawable_fillPattern(Handle self,Bool set,SV * svpattern)1334 Drawable_fillPattern( Handle self, Bool set, SV * svpattern)
1335 {
1336 	int i;
1337 	if ( !set) {
1338 		AV * av;
1339 		FillPattern * fp = apc_gp_get_fill_pattern( self);
1340 		if ( !fp) return NULL_SV;
1341 		av = newAV();
1342 		for ( i = 0; i < 8; i++) av_push( av, newSViv(( int) (*fp)[i]));
1343 		return newRV_noinc(( SV *) av);
1344 	} else {
1345 		if ( SvROK( svpattern) && ( SvTYPE( SvRV( svpattern)) == SVt_PVAV)) {
1346 			FillPattern fp;
1347 			AV * av = ( AV *) SvRV( svpattern);
1348 			if ( av_len( av) != 7) {
1349 				warn("Illegal fillPattern passed to Drawable::fillPattern");
1350 				return NULL_SV;
1351 			}
1352 			for ( i = 0; i < 8; i++) {
1353 				SV ** holder = av_fetch( av, i, 0);
1354 				if ( !holder) {
1355 					warn("Array panic on Drawable::fillPattern");
1356 					return NULL_SV;
1357 				}
1358 				fp[ i] = SvIV( *holder);
1359 			}
1360 			apc_gp_set_fill_pattern( self, fp);
1361 		} else {
1362 			int id = SvIV( svpattern);
1363 			if (( id < 0) || ( id > fpMaxId)) {
1364 				warn("fillPattern index out of range passed to Drawable::fillPattern");
1365 				return NULL_SV;
1366 			}
1367 			apc_gp_set_fill_pattern( self, fillPatterns[ id]);
1368 		}
1369 	}
1370 	return NULL_SV;
1371 }
1372 
1373 Point
Drawable_fillPatternOffset(Handle self,Bool set,Point fpo)1374 Drawable_fillPatternOffset( Handle self, Bool set, Point fpo)
1375 {
1376 	if (!set) return apc_gp_get_fill_pattern_offset( self);
1377 	fpo. x %= 8;
1378 	fpo. y %= 8;
1379 	apc_gp_set_fill_pattern_offset( self, fpo);
1380 	return fpo;
1381 }
1382 
1383 Font
Drawable_get_font(Handle self)1384 Drawable_get_font( Handle self)
1385 {
1386 	return var-> font;
1387 }
1388 
1389 void
Drawable_set_font(Handle self,Font font)1390 Drawable_set_font( Handle self, Font font)
1391 {
1392 	clear_font_abc_caches( self);
1393 	apc_font_pick( self, &font, &var-> font);
1394 	apc_gp_set_font( self, &var-> font);
1395 }
1396 
1397 
1398 int
Drawable_lineEnd(Handle self,Bool set,int lineEnd)1399 Drawable_lineEnd( Handle self, Bool set, int lineEnd)
1400 {
1401 	if (!set) return apc_gp_get_line_end( self);
1402 	apc_gp_set_line_end( self, lineEnd);
1403 	return lineEnd;
1404 }
1405 
1406 int
Drawable_lineJoin(Handle self,Bool set,int lineJoin)1407 Drawable_lineJoin( Handle self, Bool set, int lineJoin)
1408 {
1409 	if (!set) return apc_gp_get_line_join( self);
1410 	apc_gp_set_line_join( self, lineJoin);
1411 	return lineJoin;
1412 }
1413 
1414 SV *
Drawable_linePattern(Handle self,Bool set,SV * pattern)1415 Drawable_linePattern( Handle self, Bool set, SV * pattern)
1416 {
1417 	if ( set) {
1418 		STRLEN len;
1419 		unsigned char *pat = ( unsigned char *) SvPV( pattern, len);
1420 		if ( len > 255) len = 255;
1421 		apc_gp_set_line_pattern( self, pat, len);
1422 	} else {
1423 		unsigned char ret[ 256];
1424 		int len = apc_gp_get_line_pattern( self, ret);
1425 		return newSVpvn((char*) ret, len);
1426 	}
1427 	return NULL_SV;
1428 }
1429 
1430 
1431 double
Drawable_lineWidth(Handle self,Bool set,double lineWidth)1432 Drawable_lineWidth( Handle self, Bool set, double lineWidth)
1433 {
1434 	if (!set) return apc_gp_get_line_width( self);
1435 	if ( lineWidth < 0.0 ) lineWidth = 0.0;
1436 	apc_gp_set_line_width( self, lineWidth);
1437 	return lineWidth;
1438 }
1439 
1440 double
Drawable_miterLimit(Handle self,Bool set,double miterLimit)1441 Drawable_miterLimit( Handle self, Bool set, double miterLimit)
1442 {
1443 	if (!set) return apc_gp_get_miter_limit( self);
1444 	apc_gp_set_miter_limit( self, miterLimit);
1445 	return miterLimit;
1446 }
1447 
1448 SV *
Drawable_palette(Handle self,Bool set,SV * palette)1449 Drawable_palette( Handle self, Bool set, SV * palette)
1450 {
1451 	int colors;
1452 	if ( var-> stage > csFrozen) return NULL_SV;
1453 	colors = var-> palSize;
1454 	if ( set) {
1455 		free( var-> palette);
1456 		var-> palette = prima_read_palette( &var-> palSize, palette);
1457 		if ( colors == 0 && var-> palSize == 0) return NULL_SV; /* do not bother apc */
1458 		apc_gp_set_palette( self);
1459 	} else {
1460 		AV * av = newAV();
1461 		int i;
1462 		Byte * pal = ( Byte*) var-> palette;
1463 		for ( i = 0; i < colors * 3; i++) av_push( av, newSViv( pal[ i]));
1464 		return newRV_noinc(( SV *) av);
1465 	}
1466 	return NULL_SV;
1467 }
1468 
1469 SV *
Drawable_pixel(Handle self,Bool set,int x,int y,SV * color)1470 Drawable_pixel( Handle self, Bool set, int x, int y, SV * color)
1471 {
1472 	CHECK_GP(0);
1473 	if (!set)
1474 		return newSViv( apc_gp_get_pixel( self, x, y));
1475 	apc_gp_set_pixel( self, x, y, SvIV( color));
1476 	return NULL_SV;
1477 }
1478 
1479 Handle
Drawable_region(Handle self,Bool set,Handle mask)1480 Drawable_region( Handle self, Bool set, Handle mask)
1481 {
1482 	if ( var-> stage > csFrozen) return NULL_HANDLE;
1483 	if ( !is_opt(optSystemDrawable))
1484 		return NULL_HANDLE;
1485 
1486 	if ( set) {
1487 		if ( mask && kind_of( mask, CRegion)) {
1488 			apc_gp_set_region( self, mask);
1489 			return NULL_HANDLE;
1490 		}
1491 
1492 		if ( mask && !kind_of( mask, CImage)) {
1493 			warn("Illegal object reference passed to Drawable::region");
1494 			return NULL_HANDLE;
1495 		}
1496 
1497 		if ( mask ) {
1498 			Handle region;
1499 			HV * profile = newHV();
1500 
1501 			pset_H( image, mask );
1502 			region = Object_create("Prima::Region", profile);
1503 			sv_free(( SV *) profile);
1504 
1505 			apc_gp_set_region(self, region);
1506 			Object_destroy(region);
1507 
1508 		} else
1509 			apc_gp_set_region(self, NULL_HANDLE);
1510 
1511 	} else if ( apc_gp_get_region( self, NULL_HANDLE)) {
1512 		HV * profile = newHV();
1513 		Handle i = Object_create( "Prima::Region", profile);
1514 		sv_free(( SV *) profile);
1515 		apc_gp_get_region( self, i);
1516 		--SvREFCNT( SvRV((( PAnyObject) i)-> mate));
1517 		return i;
1518 	}
1519 
1520 	return NULL_HANDLE;
1521 }
1522 
1523 int
Drawable_rop(Handle self,Bool set,int rop)1524 Drawable_rop( Handle self, Bool set, int rop)
1525 {
1526 	if (!set) return apc_gp_get_rop( self);
1527 	apc_gp_set_rop( self, rop);
1528 	return rop;
1529 }
1530 
1531 int
Drawable_rop2(Handle self,Bool set,int rop2)1532 Drawable_rop2( Handle self, Bool set, int rop2)
1533 {
1534 	if (!set) return apc_gp_get_rop2( self);
1535 	apc_gp_set_rop2( self, rop2);
1536 	return rop2;
1537 }
1538 
1539 Bool
Drawable_textOpaque(Handle self,Bool set,Bool opaque)1540 Drawable_textOpaque( Handle self, Bool set, Bool opaque)
1541 {
1542 	if (!set) return apc_gp_get_text_opaque( self);
1543 	apc_gp_set_text_opaque( self, opaque);
1544 	return opaque;
1545 }
1546 
1547 Bool
Drawable_textOutBaseline(Handle self,Bool set,Bool textOutBaseline)1548 Drawable_textOutBaseline( Handle self, Bool set, Bool textOutBaseline)
1549 {
1550 	if (!set) return apc_gp_get_text_out_baseline( self);
1551 	apc_gp_set_text_out_baseline( self, textOutBaseline);
1552 	return textOutBaseline;
1553 }
1554 
1555 Point
Drawable_translate(Handle self,Bool set,Point translate)1556 Drawable_translate( Handle self, Bool set, Point translate)
1557 {
1558 	if (!set) return apc_gp_get_transform( self);
1559 	apc_gp_set_transform( self, translate. x, translate. y);
1560 	return translate;
1561 }
1562 
1563 #ifdef __cplusplus
1564 }
1565 #endif
1566