1 #include "unix/guts.h"
2 #include "Icon.h"
3 
4 #define pREGION GET_REGION(self)->region
5 #define pAPERTURE GET_REGION(self)->aperture
6 
7 Region
prima_region_create(Handle mask)8 prima_region_create( Handle mask)
9 {
10 	unsigned long w, h, x, y, size = 256, count = 0;
11 	Region	  rgn = None;
12 	Byte	   * idata;
13 	XRectangle * current, * rdata;
14 	Bool	  set = 0;
15 
16 	if ( !mask)
17 		return None;
18 
19 	w = PImage( mask)-> w;
20 	h = PImage( mask)-> h;
21 	/*
22 		XUnionRegion is actually SLOWER than the image scan -
23 		- uncomment if this is wrong
24 	if ( X( mask)-> cached_region) {
25 		rgn = XCreateRegion();
26 		XUnionRegion( rgn, X( mask)-> cached_region, rgn);
27 		return rgn;
28 	}
29 	*/
30 
31 	idata  = PImage( mask)-> data + PImage( mask)-> dataSize - PImage( mask)-> lineSize;
32 
33 	rdata = ( XRectangle*) malloc( size * sizeof( XRectangle));
34 	if ( !rdata) {
35 		warn("Not enough memory");
36 		return None;
37 	}
38 
39 	count = 0;
40 	current = rdata;
41 	current--;
42 
43 	for ( y = 0; y < h; y++) {
44 		for ( x = 0; x < w; x++) {
45 			if ( idata[ x >> 3] == 0) {
46 				x += 7;
47 				continue;
48 			}
49 			if ( idata[ x >> 3] & ( 1 << ( 7 - ( x & 7)))) {
50 				if ( set && current-> y == y && current-> x + current-> width == x)
51 					current-> width++;
52 				else {
53 					set = 1;
54 					if ( count >= size) {
55 						void * xrdata = realloc( rdata, ( size *= 3) * sizeof( XRectangle));
56 						if ( !xrdata) {
57 							warn("Not enough memory");
58 							free( rdata);
59 							return None;
60 						}
61 						rdata = xrdata;
62 						current = rdata;
63 						current += count - 1;
64 					}
65 					count++;
66 					current++;
67 					current-> x   = x;
68 					current-> y   = y;
69 					current-> width  = 1;
70 					current-> height = 1;
71 				}
72 			}
73 		}
74 		idata -= PImage( mask)-> lineSize;
75 	}
76 
77 	if ( set) {
78 		rgn = XCreateRegion();
79 		for ( x = 0, current = rdata; x < count; x++, current++)
80 			XUnionRectWithRegion( current, rgn, rgn);
81 
82 		/*
83 		X( mask)-> cached_region = XCreateRegion();
84 		XUnionRegion( X( mask)-> cached_region, rgn, X( mask)-> cached_region);
85 		*/
86 	}
87 	free( rdata);
88 
89 	return rgn;
90 }
91 
92 static Bool
rgn_empty(Handle self)93 rgn_empty(Handle self)
94 {
95 	XRectangle xr;
96 	pREGION = XCreateRegion();
97 	xr. x = 0;
98 	xr. y = 0;
99 	xr. width  = 1;
100 	xr. height = 1;
101 	XUnionRectWithRegion( &xr, pREGION, pREGION);
102 	XXorRegion( pREGION, pREGION, pREGION);
103 	pAPERTURE = 0;
104 	return true;
105 }
106 
107 static Bool
rgn_rect(Handle self,int count,Box * r)108 rgn_rect(Handle self, int count, Box * r)
109 {
110 	int i, aperture;
111 	Box * rr;
112 	pREGION = XCreateRegion();
113 
114 	aperture = r->y + r->height;
115 	for ( i = 0, rr = r; i < count; i++, rr++) {
116 		if ( aperture < rr->y + rr->height)
117 			aperture = rr->y + rr->height;
118 	}
119 
120 	for ( i = 0; i < count; i++, r++) {
121 		XRectangle xr;
122 		xr. x = r-> x;
123 		xr. y = aperture - r->y - r->height;
124 		xr. width  = r-> width;
125 		xr. height = r-> height;
126 		XUnionRectWithRegion( &xr, pREGION, pREGION);
127 	}
128 	pAPERTURE = aperture;
129 	return true;
130 }
131 
132 static Bool
rgn_polygon(Handle self,PolygonRegionRec * r)133 rgn_polygon(Handle self, PolygonRegionRec * r)
134 {
135 	int i, max, open, xp_points;
136 	XPoint * xp;
137 
138 	open =
139 		r->points[r->n_points-1].x != r->points[0].x ||
140 		r->points[r->n_points-1].y != r->points[0].y;
141 	xp_points = r->n_points + (open ? 1 : 0);
142 
143 	if ( !( xp = malloc( sizeof(XPoint) * xp_points ))) {
144 		warn("Not enough memory");
145 		return false;
146 	}
147 
148 	for ( i = 0, max = 0; i < r->n_points; i++) {
149 		if ( max < r->points[i].y)
150 			max = r->points[i].y;
151 	}
152 	max++;
153 	for ( i = 0; i < r->n_points; i++) {
154 		xp[i].x = r->points[i].x;
155 		xp[i].y = max - r->points[i].y - 1;
156 	}
157 	if ( open ) {
158 		xp[i].x = r->points[0].x;
159 		xp[i].y = max - r->points[0].y - 1;
160 	}
161 
162 	pAPERTURE = max;
163 	pREGION = XPolygonRegion( xp, r->n_points,
164 		((r-> fill_mode & fmWinding) == fmAlternate) ? EvenOddRule : WindingRule);
165 
166 	if (( r->fill_mode & fmOverlay) == 0) goto NO_OVERLAY;
167 
168 	/* superimpose polyline points using Bresenham
169 	because x11 regions are as broken as filled shapes */
170 	for ( i = 0; i < xp_points-1; i++) {
171 		int curr_maj, curr_min, to_maj, delta_maj, delta_min;
172 		int delta_y, delta_x;
173 		int dir = 0, d, d_inc1, d_inc2;
174 		int inc_maj, inc_min;
175 		int x, y, acc_x = 0, acc_y = INT_MIN, ox;
176 		XPoint a = xp[i], b = xp[i+1];
177 		delta_y = b.y - a.y;
178 		delta_x = b.x - a.x;
179 		if (abs(delta_y) > abs(delta_x)) dir = 1;
180 
181 		if (dir) {
182 			curr_maj = a.y;
183 			curr_min = a.x;
184 			to_maj = b.y;
185 			delta_maj = delta_y;
186 			delta_min = delta_x;
187 		} else {
188 			curr_maj = a.x;
189 			curr_min = a.y;
190 			to_maj = b.x;
191 			delta_maj = delta_x;
192 			delta_min = delta_y;
193 		}
194 
195 		if (delta_maj != 0)
196 			inc_maj = (abs(delta_maj)==delta_maj ? 1 : -1);
197 		else
198 			inc_maj = 0;
199 
200 		if (delta_min != 0)
201 			inc_min = (abs(delta_min)==delta_min ? 1 : -1);
202 		else
203 			inc_min = 0;
204 
205 		delta_maj = abs(delta_maj);
206 		delta_min = abs(delta_min);
207 
208 		d      = (delta_min << 1) - delta_maj;
209 		d_inc1 = (delta_min << 1);
210 		d_inc2 = ((delta_min - delta_maj) << 1);
211 
212 		x = INT_MIN;
213 		while(1) {
214 			ox = x;
215 			if (dir) {
216 				x = curr_min;
217 				y = curr_maj;
218 			} else {
219 				x = curr_maj;
220 				y = curr_min;
221 			}
222 			if ( acc_y != y ) {
223 				if ( acc_y > INT_MIN) {
224 					XRectangle xr;
225 					xr. y = acc_y;
226 					xr. height = 1;
227 					if (ox < acc_x) {
228 						xr.x = ox;
229 						xr.width = acc_x - ox + 1;
230 					} else {
231 						xr.x = acc_x;
232 						xr.width = ox - acc_x + 1;
233 					}
234 					XUnionRectWithRegion( &xr, pREGION, pREGION);
235 				}
236 				acc_x = x;
237 				acc_y = y;
238 			}
239 
240 			if (curr_maj == to_maj) break;
241 			curr_maj += inc_maj;
242 			if (d < 0) {
243 				d += d_inc1;
244 			} else {
245 				d += d_inc2;
246 				curr_min += inc_min;
247 			}
248 		}
249 		if ( acc_y > INT_MIN) {
250 			XRectangle xr;
251 			xr. y = acc_y;
252 			xr. height = 1;
253 			if (x < acc_x) {
254 				xr.x = x;
255 				xr.width = acc_x - x + 1;
256 			} else {
257 				xr.x = acc_x;
258 				xr.width = x - acc_x + 1;
259 			}
260 			XUnionRectWithRegion( &xr, pREGION, pREGION);
261 		}
262 	}
263 
264 NO_OVERLAY:
265 	free( xp );
266 	return true;
267 }
268 
269 static Bool
rgn_image(Handle self,Handle image)270 rgn_image(Handle self, Handle image)
271 {
272 	pREGION = prima_region_create(image);
273 
274 	if ( !pREGION )
275 		pREGION = XCreateRegion();
276 	else
277 		pAPERTURE = PImage(image)->h;
278 	return true;
279 }
280 
281 Bool
apc_region_create(Handle self,PRegionRec rec)282 apc_region_create( Handle self, PRegionRec rec)
283 {
284 	switch( rec-> type ) {
285 	case rgnEmpty:
286 		return rgn_empty(self);
287 	case rgnRectangle:
288 		return rgn_rect(self, rec->data.box.n_boxes, rec->data.box.boxes);
289 	case rgnPolygon:
290 		return rgn_polygon(self, &rec->data.polygon);
291 	case rgnImage:
292 		return rgn_image(self, rec->data.image);
293 	default:
294 		return false;
295 	}
296 }
297 
298 Bool
apc_region_destroy(Handle self)299 apc_region_destroy( Handle self)
300 {
301 	if ( pREGION ) {
302 		XDestroyRegion(pREGION);
303 		pREGION = NULL;
304 	}
305 	return true;
306 }
307 
308 ApiHandle
apc_region_get_handle(Handle self)309 apc_region_get_handle( Handle self)
310 {
311 	return (ApiHandle) pREGION;
312 }
313 
314 Bool
apc_region_offset(Handle self,int dx,int dy)315 apc_region_offset( Handle self, int dx, int dy)
316 {
317 	XOffsetRegion(pREGION, dx, -dy);
318 	return true;
319 }
320 
321 Bool
apc_region_combine(Handle self,Handle other_region,int rgnop)322 apc_region_combine( Handle self, Handle other_region, int rgnop)
323 {
324 	PRegionSysData r2;
325 	int d;
326 	Bool ok = true;
327 
328 	r2 = GET_REGION(other_region);
329 
330 	if ( rgnop == rgnopCopy ) {
331 		if ( pREGION ) XDestroyRegion( pREGION );
332 		pREGION = XCreateRegion();
333 		XUnionRegion( pREGION, r2->region, pREGION);
334 		pAPERTURE = r2-> aperture;
335 		return true;
336 	}
337 
338 	d = pAPERTURE - r2-> aperture;
339 	if ( d > 0 )
340 		XOffsetRegion( r2-> region, 0, d);
341 	else
342 		XOffsetRegion( pREGION, 0, -d);
343 
344 	switch (rgnop) {
345 	case rgnopIntersect:
346 		XIntersectRegion( pREGION, r2->region, pREGION);
347 		break;
348 	case rgnopUnion:
349 		XUnionRegion( pREGION, r2->region, pREGION);
350 		break;
351 	case rgnopXor:
352 		XXorRegion( pREGION, r2->region, pREGION);
353 		break;
354 	case rgnopDiff:
355 		XSubtractRegion( pREGION, r2->region, pREGION);
356 		break;
357 	default:
358 		ok = false;
359 	}
360 	if ( d > 0 )
361 		XOffsetRegion( r2-> region, 0, -d);
362 	else
363 		pAPERTURE = r2-> aperture;
364 
365 	return ok;
366 }
367 
368 Bool
apc_region_point_inside(Handle self,Point p)369 apc_region_point_inside( Handle self, Point p)
370 {
371 	return XPointInRegion( pREGION, p.x, pAPERTURE - p.y - 1);
372 }
373 
374 int
apc_region_rect_inside(Handle self,Rect r)375 apc_region_rect_inside( Handle self, Rect r)
376 {
377 	int res = XRectInRegion(
378 		pREGION,
379 		r. left, pAPERTURE - r. bottom - 1,
380 		r. right - r.left + 1, r.top - r.bottom + 1
381 	);
382 	switch (res) {
383 	case RectangleIn:   return rgnInside;
384 	case RectanglePart: return rgnPartially;
385 	default:	    return rgnOutside;
386 	}
387 }
388 
389 Bool
apc_region_equals(Handle self,Handle other_region)390 apc_region_equals( Handle self, Handle other_region)
391 {
392 	return XEqualRegion( pREGION, GET_REGION(other_region)->region);
393 }
394 
395 Bool
apc_region_is_empty(Handle self)396 apc_region_is_empty( Handle self)
397 {
398 	return XEmptyRegion( pREGION );
399 }
400 
401 Box
apc_region_get_box(Handle self)402 apc_region_get_box( Handle self)
403 {
404 	Box box;
405 	XRectangle xr;
406 	XClipBox( pREGION, &xr);
407 	box. x	    = xr. x;
408 	box. y	    = pAPERTURE - xr. height - xr.y;
409 	box. width  = xr. width;
410 	box. height = xr. height;
411 	return box;
412 }
413 
414 #define REVERT(a)	(XX-> size. y - (a) - 1)
415 #define SORT(a,b)	{ int swp; if ((a) > (b)) { swp=(a); (a)=(b); (b)=swp; }}
416 
417 /* returns rect in X coordinates BUT without menuHeight deviation */
418 void
prima_gp_get_clip_rect(Handle self,XRectangle * cr,Bool for_internal_paints)419 prima_gp_get_clip_rect( Handle self, XRectangle *cr, Bool for_internal_paints)
420 {
421 	DEFXX;
422 	XRectangle r;
423 
424 	cr-> x = 0;
425 	cr-> y = 0;
426 	cr-> width = XX-> size.x;
427 	cr-> height = XX-> size.y;
428 	if ( XF_IN_PAINT(XX) && XX-> paint_region) {
429 		XClipBox( XX-> paint_region, &r);
430 		prima_rect_intersect( cr, &r);
431 	}
432 	if ( XX-> clip_rect. x != 0
433 		|| XX-> clip_rect. y != 0
434 		|| XX-> clip_rect. width != XX-> size.x
435 		|| XX-> clip_rect. height != XX-> size.y) {
436 		prima_rect_intersect( cr, &XX-> clip_rect);
437 	}
438 
439 	if ( for_internal_paints) {
440 		cr-> x += XX-> btransform. x;
441 		cr-> y -= XX-> btransform. y;
442 	}
443 }
444 
445 Rect
apc_gp_get_clip_rect(Handle self)446 apc_gp_get_clip_rect( Handle self)
447 {
448 	DEFXX;
449 	XRectangle cr;
450 	Rect r;
451 
452 	prima_gp_get_clip_rect( self, &cr, 0);
453 	r. left = cr. x;
454 	r. top = XX-> size. y - cr. y - 1;
455 	r. bottom = r. top - cr. height + 1;
456 	r. right = cr. x + cr. width - 1;
457 	return r;
458 }
459 
460 
461 Bool
apc_gp_set_clip_rect(Handle self,Rect clipRect)462 apc_gp_set_clip_rect( Handle self, Rect clipRect)
463 {
464 	DEFXX;
465 	Region region;
466 	XRectangle r;
467 
468 	if ( !XF_IN_PAINT(XX))
469 		return false;
470 
471 	SORT( clipRect. left, clipRect. right);
472 	SORT( clipRect. bottom, clipRect. top);
473 	r. x = clipRect. left;
474 	r. y = REVERT( clipRect. top);
475 	r. width = clipRect. right - clipRect. left+1;
476 	r. height = clipRect. top - clipRect. bottom+1;
477 	XX-> clip_rect = r;
478 	XX-> clip_mask_extent. x = r. width;
479 	XX-> clip_mask_extent. y = r. height;
480 	region = XCreateRegion();
481 	XUnionRectWithRegion( &r, region, region);
482 	if ( XX-> paint_region)
483 		XIntersectRegion( region, XX-> paint_region, region);
484 	if ( XX-> btransform. x != 0 || XX-> btransform. y != 0) {
485 		XOffsetRegion( region, XX-> btransform. x, -XX-> btransform. y);
486 	}
487 	XSetRegion( DISP, XX-> gc, region);
488 	if ( XX-> flags. kill_current_region)
489 		XDestroyRegion( XX-> current_region);
490 	XX-> flags. kill_current_region = 1;
491 	XX-> current_region = region;
492 	XX-> flags. xft_clip = 0;
493 #ifdef USE_XFT
494 	if ( XX-> xft_drawable) prima_xft_update_region( self);
495 #endif
496 	CLIP_ARGB_PICTURE(XX->argb_picture, region);
497 	return true;
498 }
499 
500 Bool
apc_gp_set_region(Handle self,Handle rgn)501 apc_gp_set_region( Handle self, Handle rgn)
502 {
503 	DEFXX;
504 	Region region;
505 	PRegionSysData r;
506 
507 	if ( PObject( self)-> options. optInDrawInfo) return false;
508 	if ( !XF_IN_PAINT(XX)) return false;
509 
510 	if (rgn == NULL_HANDLE) {
511 		Rect r;
512 		r. left   = 0;
513 		r. bottom = 0;
514 		r. right  = XX-> size. x - 1;
515 		r. top	  = XX-> size. y - 1;
516 		return apc_gp_set_clip_rect( self, r);
517 	}
518 
519 	r = GET_REGION(rgn);
520 
521 	XClipBox( r-> region, &XX-> clip_rect);
522 	XX-> clip_rect. y += XX-> size. y - r-> aperture;
523 	XX-> clip_mask_extent. x = XX-> clip_rect. width;
524 	XX-> clip_mask_extent. y = XX-> clip_rect. height;
525 	if ( XX-> clip_rect. width == 0 || XX-> clip_rect. height == 0) {
526 		Rect r;
527 		r. left   = -1;
528 		r. bottom = -1;
529 		r. right  = -1;
530 		r. top	  = -1;
531 		return apc_gp_set_clip_rect( self, r);
532 	}
533 
534 	region = XCreateRegion();
535 	XUnionRegion( region, r-> region, region);
536 	/* offset region if drawable is buffered */
537 	XOffsetRegion( region, XX-> btransform. x, XX-> size.y - r-> aperture - XX-> btransform. y);
538 	/* otherwise ( and only otherwise ), and if there's a
539 		X11 clipping, intersect the region with it. X11 clipping
540 		must not mix with the buffer clipping */
541 	if (( !XX-> udrawable || XX-> udrawable == XX-> gdrawable) &&
542 		XX-> paint_region)
543 		XIntersectRegion( region, XX-> paint_region, region);
544 	XSetRegion( DISP, XX-> gc, region);
545 	if ( XX-> flags. kill_current_region)
546 		XDestroyRegion( XX-> current_region);
547 	XX-> flags. kill_current_region = 1;
548 	XX-> current_region = region;
549 	XX-> flags. xft_clip = 0;
550 #ifdef USE_XFT
551 	if ( XX-> xft_drawable) prima_xft_update_region( self);
552 #endif
553 	CLIP_ARGB_PICTURE(XX->argb_picture, region);
554 	return true;
555 }
556 
557 Bool
apc_gp_get_region(Handle self,Handle rgn)558 apc_gp_get_region( Handle self, Handle rgn)
559 {
560 	DEFXX;
561 	GC gc;
562 	XGCValues gcv;
563 	int w, h;
564 	Pixmap pixmap;
565 	XImage * i;
566 	Image pi;
567 	Region rgn2;
568 
569 	if ( !XF_IN_PAINT(XX)) return false;
570 
571 	if ( !rgn)
572 		return XX-> clip_mask_extent. x != 0 && XX-> clip_mask_extent. y != 0;
573 
574 	if ( XX-> clip_mask_extent. x == 0 || XX-> clip_mask_extent. y == 0)
575 		return false;
576 
577 	w = XX-> clip_mask_extent. x;
578 	h = XX-> clip_mask_extent. y;
579 
580 	pixmap = XCreatePixmap( DISP, guts.root, w, h,
581 		XF_LAYERED(XX) ? guts. argb_depth :
582 		( XT_IS_BITMAP(XX) ? 1 : guts. depth )
583 	);
584 	XCHECKPOINT;
585 
586 	gcv. graphics_exposures = false;
587 	gcv. fill_style = FillSolid;
588 	gcv. foreground = 0;
589 	gcv. clip_y_origin = -XX-> clip_rect. y + XX-> btransform. y;
590 	gcv. clip_x_origin = -XX-> clip_rect. x - XX-> btransform. x;
591 	XCHECKPOINT;
592 	gc = XCreateGC( DISP, pixmap, GCGraphicsExposures|GCFillStyle|GCForeground|GCClipXOrigin|GCClipYOrigin, &gcv);
593 	XFillRectangle( DISP, pixmap, gc, 0, 0, w, h);
594 	XSetForeground( DISP, gc, 1 );
595 	XCopyGC( DISP, XX->gc, GCClipMask, gc);
596 	XFillRectangle( DISP, pixmap, gc, 0, 0, w, h);
597 	XFreeGC( DISP, gc);
598 	XCHECKPOINT;
599 
600 	i = XGetImage( DISP, pixmap, 0, 0, w, h, 1, XYPixmap);
601 	XFreePixmap( DISP, pixmap);
602 	if ( !i ) {
603 		warn("Cannot query image");
604 		return false;
605 	}
606 
607 	img_fill_dummy( &pi, w, h, imBW | imGrayScale, NULL, NULL);
608 	if ( !( pi.data = malloc(pi.lineSize * h))) {
609 		XDestroyImage( i);
610 		warn("Not enough memory");
611 		return false;
612 	}
613 
614 	prima_copy_xybitmap( pi.data, (Byte*)i-> data, w, h, pi.lineSize, i-> bytes_per_line);
615 	XDestroyImage( i);
616 
617 	rgn2 = prima_region_create((Handle) &pi);
618 	free(pi.data);
619 
620 	if ( GET_REGION(rgn)-> region )
621 		XDestroyRegion(  GET_REGION(rgn)-> region );
622 	if ( rgn2 ) {
623 		XOffsetRegion( rgn2, XX-> clip_rect.x, 0);
624 		GET_REGION(rgn)-> aperture = XX-> size.y - XX-> clip_rect.y;
625 		GET_REGION(rgn)-> region = rgn2;
626 	} else {
627 		GET_REGION(rgn)-> aperture = 0;
628 		GET_REGION(rgn)-> region = XCreateRegion();
629 	}
630 	return true;
631 }
632 
633 PRegionRec
apc_region_copy_rects(Handle self)634 apc_region_copy_rects( Handle self)
635 {
636 	int i, aperture;
637 	PRegionRec ret;
638 	Box *dst;
639 	BoxRec *src;
640 	REGION *region;
641 
642 	region = (REGION*) pREGION;
643 	ret = malloc(sizeof(RegionRec) + sizeof(Box) * region->numRects);
644 	if ( ret == NULL ) {
645 		warn("Not enough memory\n");
646 		return NULL;
647 	}
648 
649 	ret-> type = rgnRectangle;
650 	ret-> data. box. n_boxes = region-> numRects;
651 	src = region->rects;
652 	dst = ret-> data. box. boxes = (Box*) (((Byte*)ret) + sizeof(RegionRec));
653 	aperture = pAPERTURE;
654 	for ( i = 0; i < ret->data. box. n_boxes; i++, src++, dst++) {
655 		dst-> x = src-> x1;
656 		dst-> y = aperture - src-> y2;
657 		dst-> width  = src-> x2 - src->x1;
658 		dst-> height = src-> y2 - src->y1;
659 /*		printf("%d: %d %d %d %d => %d %d %d %d\n", aperture, src->x1, src->y1, src->x2, src->y2, dst->x, dst->y, dst-> width, dst->height); */
660 	}
661 
662 	return ret;
663 }
664 
665