1 /****************************************************************************
2  *
3  * Copyright (c) 2003 Sasha Vasko <sasha at aftercode.net>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.   See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  *
19  ****************************************************************************/
20 
21 #define LOCAL_DEBUG
22 #include "../configure.h"
23 #include "asapp.h"
24 #include "shape.h"
25 
create_shape()26 ASVector *create_shape ()
27 {
28 	ASVector *shape = create_asvector (sizeof (XRectangle));
29 
30 	LOCAL_DEBUG_CALLER_OUT ("creating shape %p", shape);
31 	return shape;
32 }
33 
destroy_shape(ASVector ** pshape)34 void destroy_shape (ASVector ** pshape)
35 {
36 	if (pshape) {
37 		LOCAL_DEBUG_CALLER_OUT ("destroying shape %p", *pshape);
38 		destroy_asvector (pshape);
39 	}
40 }
41 
42 
43 /* using int type here to avoid problems with signed/unsigned comparisonand for simplier and faster code */
44 static Bool
assimilate_rectangle(int new_x,int new_y,int new_width,int new_height,ASVector * shape)45 assimilate_rectangle (int new_x, int new_y, int new_width, int new_height,
46 											ASVector * shape)
47 {
48 	XRectangle *shape_rects = PVECTOR_HEAD (XRectangle, shape);
49 	int shape_rect_count = PVECTOR_USED (shape);
50 	int i;
51 	Bool changed = False;
52 	int selected = -1;
53 	int selected_area = 0;
54 
55 	if (new_width == 0 || new_height == 0)
56 		return False;
57 
58 	LOCAL_DEBUG_OUT (" assimilating %dx%d%+d%+d", new_width, new_height,
59 									 new_x, new_y);
60 
61 	/* pass 1 : find rectangles that are entirely inside us :  */
62 	for (i = 0; i < shape_rect_count; ++i) {
63 		if (shape_rects[i].x >= new_x &&
64 				shape_rects[i].y >= new_y &&
65 				shape_rects[i].x + shape_rects[i].width <= new_x + new_width &&
66 				shape_rects[i].y + shape_rects[i].height <= new_y + new_height) {
67 			LOCAL_DEBUG_OUT (" \tassimilated by %dx%d%+d%+d",
68 											 shape_rects[i].width, shape_rects[i].height,
69 											 shape_rects[i].x, shape_rects[i].y);
70 			shape_rects[i].x = new_x;
71 			shape_rects[i].y = new_y;
72 			shape_rects[i].width = new_width;
73 			shape_rects[i].height = new_height;
74 			return True;
75 		}
76 		/* horizontally overlapping */
77 		if (new_y == shape_rects[i].y && new_height == shape_rects[i].height) {
78 			if (new_x + new_width >= shape_rects[i].x
79 					&& new_x <= shape_rects[i].x + shape_rects[i].width) {
80 				int w1 = new_x + new_width;
81 				int w2 = shape_rects[i].x + shape_rects[i].width;
82 
83 				LOCAL_DEBUG_OUT (" \thorizontally overlapping %dx%d%+d%+d",
84 												 shape_rects[i].width, shape_rects[i].height,
85 												 shape_rects[i].x, shape_rects[i].y);
86 				shape_rects[i].x = min (new_x, (int)shape_rects[i].x);
87 				shape_rects[i].width = max (w1, w2) - shape_rects[i].x;
88 				return True;
89 			}
90 		}
91 		/* vertically overlapping */
92 		if (new_x == shape_rects[i].x && new_width == shape_rects[i].width) {
93 			if (new_y + new_height >= shape_rects[i].y
94 					&& new_y <= shape_rects[i].y + shape_rects[i].height) {
95 				int h1 = new_y + new_height;
96 				int h2 = shape_rects[i].y + shape_rects[i].height;
97 
98 				LOCAL_DEBUG_OUT (" \tvertically overlapping %dx%d%+d%+d",
99 												 shape_rects[i].width, shape_rects[i].height,
100 												 shape_rects[i].x, shape_rects[i].y);
101 				shape_rects[i].y = min (new_y, (int)shape_rects[i].y);
102 				shape_rects[i].height = max (h1, h2) - shape_rects[i].y;
103 				return True;
104 			}
105 		}
106 	}
107 	/* pass 2 : find largest intersecting rectangle :  */
108 	for (i = 0; i < shape_rect_count; ++i) {
109 		int intersect_width =
110 				(shape_rects[i].x + (int)shape_rects[i].width - new_x);
111 		int intersect_height =
112 				(shape_rects[i].y + (int)shape_rects[i].height - new_y);
113 
114 		if (shape_rects[i].x > new_x) {
115 			if (shape_rects[i].x >= new_x + new_width)
116 				intersect_width = 0;
117 			else
118 				intersect_width -= shape_rects[i].x - new_x;
119 		}
120 		if (shape_rects[i].y > new_y) {
121 			if (shape_rects[i].y >= new_y + new_height)
122 				intersect_height = 0;
123 			else
124 				intersect_height -= shape_rects[i].y - new_y;
125 		}
126 		if (intersect_width > 0 && intersect_height > 0) {
127 			int new_area = intersect_width * intersect_height;
128 
129 			if (new_area > selected_area) {
130 				selected = i;
131 				selected_area = new_area;
132 			}
133 		}
134 	}
135 	if (selected >= 0) {
136 		int x1 = shape_rects[selected].x;
137 		int y1 = shape_rects[selected].y;
138 		int x2 = x1 + shape_rects[selected].width;
139 		int y2 = y1 + shape_rects[selected].height;
140 		int top = (y1 > new_y) ? y1 - new_y : 0;
141 		int bottom = (new_y + new_height > y2) ? new_y + new_height - y2 : 0;
142 		int left = (x1 > new_x) ? x1 - new_x : 0;
143 		int right = (new_x + new_width > x2) ? new_x + new_width - x2 : 0;
144 
145 		LOCAL_DEBUG_OUT (" \tintersected %dx%d%+d%+d",
146 										 shape_rects[selected].width,
147 										 shape_rects[selected].height, shape_rects[selected].x,
148 										 shape_rects[selected].y);
149 
150 		if (top > 0) {							/* there could be entire row of new segments above selected rectangle */
151 			if (new_x < x1)						/* left top segment */
152 				if (assimilate_rectangle (new_x, new_y, x1 - new_x, top, shape))
153 					changed = True;
154 			if (new_x + new_width > x2)	/* right top segment */
155 				if (assimilate_rectangle
156 						(x2, new_y, new_x + new_width - x2, top, shape))
157 					changed = True;
158 			/* center top segment */
159 			if (assimilate_rectangle
160 					(new_x + left, new_y, new_width - (left + right), top, shape))
161 				changed = True;
162 		}
163 
164 		if (bottom > 0) {						/* there could be entire row of new segments above selected rectangle */
165 			if (new_x < x1)						/* left bottom segment */
166 				if (assimilate_rectangle (new_x, y2, x1 - new_x, bottom, shape))
167 					changed = True;
168 			if (new_x + new_width > x2)	/* right bottom segment */
169 				if (assimilate_rectangle
170 						(x2, y2, new_x + new_width - x2, bottom, shape))
171 					changed = True;
172 			/* center bottom segment */
173 			if (assimilate_rectangle
174 					(new_x + left, y2, new_width - (left + right), bottom, shape))
175 				changed = True;
176 		}
177 
178 		if (left > 0) {							/* there could be left center segment */
179 			if (assimilate_rectangle
180 					(new_x, new_y + top, left, new_height - (top + bottom), shape))
181 				changed = True;
182 		}
183 		if (right > 0) {						/* there could be right center segment */
184 			if (assimilate_rectangle
185 					(new_x + new_width - right, new_y + top, right,
186 					 new_height - (top + bottom), shape))
187 				changed = True;
188 		}
189 	} else {											/* simply appending rectangle to the list : */
190 		XRectangle new_rect;
191 
192 		new_rect.x = new_x;
193 		new_rect.y = new_y;
194 		new_rect.width = new_width;
195 		new_rect.height = new_height;
196 		LOCAL_DEBUG_OUT (" \tappending %dx%d%+d%+d as new", new_width,
197 										 new_height, new_x, new_y);
198 		vector_insert_elem (shape, &new_rect, 1, NULL, False);
199 		changed = True;
200 	}
201 	return changed;
202 }
203 
204 /* using int type here to avoid problems with signed/unsigned comparisonand for simplier and faster code */
205 static Bool
subtract_rectangle(int new_x,int new_y,int new_width,int new_height,ASVector * shape)206 subtract_rectangle (int new_x, int new_y, int new_width, int new_height,
207 										ASVector * shape)
208 {
209 	XRectangle *shape_rects = PVECTOR_HEAD (XRectangle, shape);
210 	int shape_rect_count = PVECTOR_USED (shape);
211 	int i;
212 	Bool changed = False;
213 	XRectangle segments[4];
214 	int seg_count;
215 
216 	if (new_width == 0 || new_height == 0)
217 		return False;
218 
219 	LOCAL_DEBUG_OUT (" subtracting %dx%d%+d%+d", new_width, new_height,
220 									 new_x, new_y);
221 
222 	/* pass 1 : find rectangles that are entirely inside us and delete them all :  */
223 	i = shape_rect_count;
224 	while (--i >= 0) {
225 		if (shape_rects[i].x >= new_x &&
226 				shape_rects[i].y >= new_y &&
227 				shape_rects[i].x + shape_rects[i].width <= new_x + new_width &&
228 				shape_rects[i].y + shape_rects[i].height <= new_y + new_height) {
229 			LOCAL_DEBUG_OUT (" \ttrashing %dx%d%+d%+d", shape_rects[i].width,
230 											 shape_rects[i].height, shape_rects[i].x,
231 											 shape_rects[i].y);
232 			vector_remove_index (shape, i);
233 			changed = True;
234 		}
235 	}
236 	/* pass 2 : adjust all the intersected rectangles :  */
237 	for (i = 0; i < shape_rect_count; ++i) {
238 		int left = shape_rects[i].x;
239 		int right = shape_rects[i].x + shape_rects[i].width;
240 		int top = shape_rects[i].y;
241 		int bottom = shape_rects[i].y + shape_rects[i].height;
242 		int s_left = new_x;
243 		int s_right = new_x + new_width;
244 		int s_top = new_y;
245 		int s_bottom = new_y + new_height;
246 
247 
248 		if (left >= s_right || top >= s_bottom || right <= s_left
249 				|| bottom <= s_top)
250 			continue;
251 		seg_count = 0;
252 		if (top < s_top) {					/* there will be top portion */
253 			segments[seg_count].x = left;
254 			segments[seg_count].y = top;
255 			segments[seg_count].width = right - left;
256 			segments[seg_count].height = s_top - top;
257 			++seg_count;
258 		}
259 		if (left < s_left) {				/* there will be left segment */
260 			segments[seg_count].x = left;
261 			segments[seg_count].y = max (top, s_top);
262 			segments[seg_count].width = s_left - left;
263 			segments[seg_count].height =
264 					min (s_bottom, bottom) - segments[seg_count].y;
265 			++seg_count;
266 		}
267 		if (right > s_right) {			/* there will be right segment */
268 			segments[seg_count].x = s_right;
269 			segments[seg_count].y = max (top, s_top);
270 			segments[seg_count].width = right - s_right;
271 			segments[seg_count].height =
272 					min (s_bottom, bottom) - segments[seg_count].y;
273 			++seg_count;
274 		}
275 		if (bottom > s_bottom) {		/* there will be right segment */
276 			segments[seg_count].x = left;
277 			segments[seg_count].y = s_bottom;
278 			segments[seg_count].width = right - left;
279 			segments[seg_count].height = bottom - s_bottom;
280 			++seg_count;
281 		}
282 		if (seg_count > 0) {
283 			--seg_count;
284 			shape_rects[i] = segments[seg_count];
285 			if (seg_count > 0) {
286 				append_vector (shape, &segments[0], seg_count);
287 				shape_rects = PVECTOR_HEAD (XRectangle, shape);
288 				shape_rect_count = PVECTOR_USED (shape);
289 			}
290 		}
291 	}
292 
293 	return changed;
294 }
295 
296 
297 Bool
add_shape_rectangles(ASVector * shape,XRectangle * rects,unsigned int count,int x_origin,int y_origin,unsigned int clip_width,unsigned int clip_height)298 add_shape_rectangles (ASVector * shape, XRectangle * rects,
299 											unsigned int count, int x_origin, int y_origin,
300 											unsigned int clip_width, unsigned int clip_height)
301 {
302 	int i;
303 	int new_x, new_y, new_width, new_height;
304 	Bool changed = False;
305 
306 	if (shape == NULL || rects == NULL || count == 0)
307 		return False;
308 
309 #if defined(LOCAL_DEBUG) && !defined(NO_DEBUG_OUTPUT)
310 	print_shape (shape);
311 #endif
312 	LOCAL_DEBUG_OUT ("adding %d rectangles at %+d%+d clipped by %dx%d",
313 									 count, x_origin, y_origin, clip_width, clip_height);
314 
315 	for (i = 0; i < count; ++i) {
316 		new_x = rects[i].x + x_origin;
317 		if (new_x >= (int)clip_width)
318 			continue;
319 		new_y = rects[i].y + y_origin;
320 		if (new_y >= (int)clip_height)
321 			continue;
322 		new_width =
323 				(new_x + (int)rects[i].width >
324 				 (int)clip_width) ? (int)clip_width - new_x : (int)rects[i].width;
325 		new_height =
326 				(new_y + (int)rects[i].height >
327 				 (int)clip_height) ? (int)clip_height -
328 				new_y : (int)rects[i].height;
329 
330 		if (assimilate_rectangle (new_x, new_y, new_width, new_height, shape))
331 			changed = True;
332 	}
333 	return changed;
334 }
335 
336 
print_shape(ASVector * shape)337 Bool print_shape (ASVector * shape)
338 {
339 	if (shape) {
340 		XRectangle *shape_rects = PVECTOR_HEAD (XRectangle, shape);
341 		int shape_rect_count = PVECTOR_USED (shape);
342 		int i;
343 
344 		show_progress ("Printing shape %p of %d rectangles : ", shape,
345 									 shape_rect_count);
346 		for (i = 0; i < shape_rect_count; ++i)
347 			show_progress ("\trects[%d] = %dx%d%+d%+d;", i, shape_rects[i].width,
348 										 shape_rects[i].height, shape_rects[i].x,
349 										 shape_rects[i].y);
350 		return True;
351 
352 	}
353 	return False;
354 }
355 
print_rectangles_list(ASVector * list)356 void print_rectangles_list (ASVector * list)
357 {
358 #if !defined(NO_DEBUG_OUTPUT)
359 	XRectangle *rects = PVECTOR_HEAD (XRectangle, list);
360 	int i = PVECTOR_USED (list);
361 
362 	fprintf (stderr, "\tRectangles.count = %d;\n", i);
363 	while (--i >= 0)
364 		fprintf (stderr, "\tRectangles[%d] \t= %4.4dx%4.4d%+4.4d%+4.4d;\n", i,
365 						 rects[i].width, rects[i].height, rects[i].x, rects[i].y);
366 #endif
367 }
368 
369 
add_shape_mask(struct ASVector * shape,struct ASImage * mask_im)370 Bool add_shape_mask (struct ASVector *shape, struct ASImage *mask_im)
371 {
372 
373 	return False;
374 }
375 
376 Bool
subtract_shape_rectangle(ASVector * shape,XRectangle * rects,unsigned int count,int x_origin,int y_origin,unsigned int clip_width,unsigned int clip_height)377 subtract_shape_rectangle (ASVector * shape, XRectangle * rects,
378 													unsigned int count, int x_origin, int y_origin,
379 													unsigned int clip_width,
380 													unsigned int clip_height)
381 {
382 	int i;
383 	int new_x, new_y, new_width, new_height;
384 	Bool changed = False;
385 
386 	if (shape == NULL || rects == NULL || count == 0)
387 		return False;
388 
389 	LOCAL_DEBUG_OUT ("subtract %d rectangles at %+d%+d clipped by %dx%d",
390 									 count, x_origin, y_origin, clip_width, clip_height);
391 	for (i = 0; i < count; ++i) {
392 		new_x = rects[i].x + x_origin;
393 		if (new_x >= (int)clip_width)
394 			continue;
395 		new_y = rects[i].y + y_origin;
396 		if (new_y >= (int)clip_height)
397 			continue;
398 		new_width =
399 				(new_x + (int)rects[i].width >
400 				 (int)clip_width) ? (int)clip_width - new_x : (int)rects[i].width;
401 		new_height =
402 				(new_y + (int)rects[i].height >
403 				 (int)clip_height) ? (int)clip_height -
404 				new_y : (int)rects[i].height;
405 
406 		if (subtract_rectangle (new_x, new_y, new_width, new_height, shape))
407 			changed = True;
408 	}
409 	return changed;
410 }
411 
query_shape_from_window(struct ASVector * shape,Window w)412 Bool query_shape_from_window (struct ASVector * shape, Window w)
413 {
414 
415 	return False;
416 }
417 
apply_shape_to_window(struct ASVector * shape,Window w)418 Bool apply_shape_to_window (struct ASVector * shape, Window w)
419 {
420 
421 	return False;
422 }
423 
424 
425 /*************************************************************************/
426 /* This version differs from above, in that its tryes to compile a list
427  * of as many rectangles as possible : */
428 void
subtract_rectangle_from_list(ASVector * list,int left,int top,int right,int bottom)429 subtract_rectangle_from_list (ASVector * list, int left, int top,
430 															int right, int bottom)
431 {
432 	int i = PVECTOR_USED (list);
433 	XRectangle *rects = PVECTOR_HEAD (XRectangle, list);
434 	XRectangle tmp;
435 
436 	/* must trace in reverse order ! */
437 	LOCAL_DEBUG_CALLER_OUT ("rect - (%d,%d)-(%d,%d)", left, top, right,
438 													bottom);
439 
440 	while (--i >= 0) {
441 		int r_left = rects[i].x, r_right = rects[i].x + (int)rects[i].width;
442 		int r_top = rects[i].y, r_bottom = rects[i].y + (int)rects[i].height;
443 		Bool disected = False;
444 
445 		LOCAL_DEBUG_OUT ("rect[%d] - (%d,%d)-(%d,%d)", i, r_left, r_top,
446 										 r_right, r_bottom);
447 
448 		/* first we check if entire rectangle is overlapped, in which case - we simply delete it ! */
449 		if (top <= r_top && bottom >= r_bottom && left <= r_left
450 				&& right >= r_right) {
451 			vector_remove_index (list, i);
452 			rects = PVECTOR_HEAD (XRectangle, list);	/* memory may have gotten reallocated */
453 			continue;
454 		}
455 
456 		/* otherwise we can build at most 4 rectangles from each substraction : */
457 		if (top < r_bottom && bottom > r_top) {	/* we may need to create 2 vertical rectangles ( left and right ) : */
458 			/* left rectangle : */
459 			tmp.y = r_top;
460 			tmp.height = r_bottom - r_top;
461 			if (left > r_left && left < r_right) {
462 				rects[i].x = r_left;
463 				rects[i].width = left - r_left;
464 				LOCAL_DEBUG_OUT ("adjusted rect[%d] - (%d,%d)-(%d,%d)", i, r_left,
465 												 r_top, left, r_bottom);
466 				/* y and height remain unchanged ! */
467 				disected = True;
468 			}
469 			/* right rectangle : */
470 			if (right > r_left && right < r_right) {
471 				tmp.x = right;
472 				tmp.width = r_right - right;
473 				if (disected) {
474 					append_vector (list, &tmp, 1);
475 					rects = PVECTOR_HEAD (XRectangle, list);	/* memory may have gotten reallocated */
476 					LOCAL_DEBUG_OUT ("added rect - (%d,%d)-(%d,%d)", tmp.x, tmp.y,
477 													 tmp.x + tmp.width, tmp.y + tmp.height);
478 				} else {
479 					rects[i] = tmp;
480 					disected = True;
481 					LOCAL_DEBUG_OUT ("adjusted rect[%d] - (%d,%d)-(%d,%d)", i, tmp.x,
482 													 tmp.y, tmp.x + tmp.width, tmp.y + tmp.height);
483 				}
484 			}
485 		}
486 		if (left < r_right && right > r_left) {	/* we may need to create 2 horizontal rectangles ( top and bottom ) : */
487 			/* top rectangle : */
488 			tmp.x = r_left;
489 			tmp.width = r_right - r_left;
490 			if (top > r_top && top < r_bottom) {
491 				tmp.y = r_top;
492 				tmp.height = top - r_top;
493 				if (disected) {
494 					append_vector (list, &tmp, 1);
495 					rects = PVECTOR_HEAD (XRectangle, list);	/* memory may have gotten reallocated */
496 					LOCAL_DEBUG_OUT ("added rect - (%d,%d)-(%d,%d)", tmp.x, tmp.y,
497 													 tmp.x + tmp.width, tmp.y + tmp.height);
498 				} else {
499 					rects[i] = tmp;
500 					disected = True;
501 					LOCAL_DEBUG_OUT ("adjusted rect[%d] - (%d,%d)-(%d,%d)", i, tmp.x,
502 													 tmp.y, tmp.x + tmp.width, tmp.y + tmp.height);
503 				}
504 			}
505 			/* bottom rectangle */
506 			if (bottom > r_top && bottom < r_bottom) {
507 				tmp.y = bottom;
508 				tmp.height = r_bottom - bottom;
509 				if (disected) {
510 					append_vector (list, &tmp, 1);
511 					rects = PVECTOR_HEAD (XRectangle, list);	/* memory may have gotten reallocated */
512 					LOCAL_DEBUG_OUT ("added rect - (%d,%d)-(%d,%d)", tmp.x, tmp.y,
513 													 tmp.x + tmp.width, tmp.y + tmp.height);
514 				} else {
515 					rects[i] = tmp;
516 					disected = True;
517 					LOCAL_DEBUG_OUT ("adjusted rect[%d] - (%d,%d)-(%d,%d)", i, tmp.x,
518 													 tmp.y, tmp.x + tmp.width, tmp.y + tmp.height);
519 				}
520 			}
521 		}
522 	}
523 }
524