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