/work/workdir/UnpackedTarball/cairo/src/cairo-bentley-ottmann-rectangular.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright © 2004 Carl Worth |
3 | | * Copyright © 2006 Red Hat, Inc. |
4 | | * Copyright © 2009 Chris Wilson |
5 | | * |
6 | | * This library is free software; you can redistribute it and/or |
7 | | * modify it either under the terms of the GNU Lesser General Public |
8 | | * License version 2.1 as published by the Free Software Foundation |
9 | | * (the "LGPL") or, at your option, under the terms of the Mozilla |
10 | | * Public License Version 1.1 (the "MPL"). If you do not alter this |
11 | | * notice, a recipient may use your version of this file under either |
12 | | * the MPL or the LGPL. |
13 | | * |
14 | | * You should have received a copy of the LGPL along with this library |
15 | | * in the file COPYING-LGPL-2.1; if not, write to the Free Software |
16 | | * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA |
17 | | * You should have received a copy of the MPL along with this library |
18 | | * in the file COPYING-MPL-1.1 |
19 | | * |
20 | | * The contents of this file are subject to the Mozilla Public License |
21 | | * Version 1.1 (the "License"); you may not use this file except in |
22 | | * compliance with the License. You may obtain a copy of the License at |
23 | | * http://www.mozilla.org/MPL/ |
24 | | * |
25 | | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
26 | | * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
27 | | * the specific language governing rights and limitations. |
28 | | * |
29 | | * The Original Code is the cairo graphics library. |
30 | | * |
31 | | * The Initial Developer of the Original Code is Carl Worth |
32 | | * |
33 | | * Contributor(s): |
34 | | * Carl D. Worth <cworth@cworth.org> |
35 | | * Chris Wilson <chris@chris-wilson.co.uk> |
36 | | */ |
37 | | |
38 | | /* Provide definitions for standalone compilation */ |
39 | | #include "cairoint.h" |
40 | | |
41 | | #include "cairo-boxes-private.h" |
42 | | #include "cairo-error-private.h" |
43 | | #include "cairo-combsort-inline.h" |
44 | | #include "cairo-list-private.h" |
45 | | #include "cairo-traps-private.h" |
46 | | |
47 | | #include <setjmp.h> |
48 | | |
49 | | typedef struct _rectangle rectangle_t; |
50 | | typedef struct _edge edge_t; |
51 | | |
52 | | struct _edge { |
53 | | edge_t *next, *prev; |
54 | | edge_t *right; |
55 | | cairo_fixed_t x, top; |
56 | | int dir; |
57 | | }; |
58 | | |
59 | | struct _rectangle { |
60 | | edge_t left, right; |
61 | | int32_t top, bottom; |
62 | | }; |
63 | | |
64 | | #define UNROLL3(x) x x x |
65 | | |
66 | | /* the parent is always given by index/2 */ |
67 | 24.1k | #define PQ_PARENT_INDEX(i) ((i) >> 1) |
68 | 177k | #define PQ_FIRST_ENTRY 1 |
69 | | |
70 | | /* left and right children are index * 2 and (index * 2) +1 respectively */ |
71 | 24.1k | #define PQ_LEFT_CHILD_INDEX(i) ((i) << 1) |
72 | | |
73 | | typedef struct _sweep_line { |
74 | | rectangle_t **rectangles; |
75 | | rectangle_t **stop; |
76 | | edge_t head, tail, *insert, *cursor; |
77 | | int32_t current_y; |
78 | | int32_t last_y; |
79 | | int stop_size; |
80 | | |
81 | | int32_t insert_x; |
82 | | cairo_fill_rule_t fill_rule; |
83 | | |
84 | | cairo_bool_t do_traps; |
85 | | void *container; |
86 | | |
87 | | jmp_buf unwind; |
88 | | } sweep_line_t; |
89 | | |
90 | | #define DEBUG_TRAPS 0 |
91 | | |
92 | | #if DEBUG_TRAPS |
93 | | static void |
94 | | dump_traps (cairo_traps_t *traps, const char *filename) |
95 | | { |
96 | | FILE *file; |
97 | | int n; |
98 | | |
99 | | if (getenv ("CAIRO_DEBUG_TRAPS") == NULL) |
100 | | return; |
101 | | |
102 | | file = fopen (filename, "a"); |
103 | | if (file != NULL) { |
104 | | for (n = 0; n < traps->num_traps; n++) { |
105 | | fprintf (file, "%d %d L:(%d, %d), (%d, %d) R:(%d, %d), (%d, %d)\n", |
106 | | traps->traps[n].top, |
107 | | traps->traps[n].bottom, |
108 | | traps->traps[n].left.p1.x, |
109 | | traps->traps[n].left.p1.y, |
110 | | traps->traps[n].left.p2.x, |
111 | | traps->traps[n].left.p2.y, |
112 | | traps->traps[n].right.p1.x, |
113 | | traps->traps[n].right.p1.y, |
114 | | traps->traps[n].right.p2.x, |
115 | | traps->traps[n].right.p2.y); |
116 | | } |
117 | | fprintf (file, "\n"); |
118 | | fclose (file); |
119 | | } |
120 | | } |
121 | | #else |
122 | | #define dump_traps(traps, filename) |
123 | | #endif |
124 | | |
125 | | static inline int |
126 | | rectangle_compare_start (const rectangle_t *a, |
127 | | const rectangle_t *b) |
128 | 138k | { |
129 | 138k | return a->top - b->top; |
130 | 138k | } |
131 | | |
132 | | static inline int |
133 | | rectangle_compare_stop (const rectangle_t *a, |
134 | | const rectangle_t *b) |
135 | 43.8k | { |
136 | 43.8k | return a->bottom - b->bottom; |
137 | 43.8k | } |
138 | | |
139 | | static inline void |
140 | | pqueue_push (sweep_line_t *sweep, rectangle_t *rectangle) |
141 | 37.4k | { |
142 | 37.4k | rectangle_t **elements; |
143 | 37.4k | int i, parent; |
144 | | |
145 | 37.4k | elements = sweep->stop; |
146 | 37.4k | for (i = ++sweep->stop_size; |
147 | 37.4k | i != PQ_FIRST_ENTRY && |
148 | 37.4k | rectangle_compare_stop (rectangle, |
149 | 24.1k | elements[parent = PQ_PARENT_INDEX (i)]) < 0; |
150 | 37.4k | i = parent) |
151 | 29 | { |
152 | 29 | elements[i] = elements[parent]; |
153 | 29 | } |
154 | | |
155 | 37.4k | elements[i] = rectangle; |
156 | 37.4k | } |
157 | | |
158 | | static inline void |
159 | | rectangle_pop_stop (sweep_line_t *sweep) |
160 | 37.4k | { |
161 | 37.4k | rectangle_t **elements = sweep->stop; |
162 | 37.4k | rectangle_t *tail; |
163 | 37.4k | int child, i; |
164 | | |
165 | 37.4k | tail = elements[sweep->stop_size--]; |
166 | 37.4k | if (sweep->stop_size == 0) { |
167 | 13.2k | elements[PQ_FIRST_ENTRY] = NULL; |
168 | 13.2k | return; |
169 | 13.2k | } |
170 | | |
171 | 24.1k | for (i = PQ_FIRST_ENTRY; |
172 | 24.1k | (child = PQ_LEFT_CHILD_INDEX (i)) <= sweep->stop_size; |
173 | 24.1k | i = child) |
174 | 10.8k | { |
175 | 10.8k | if (child != sweep->stop_size && |
176 | 10.8k | rectangle_compare_stop (elements[child+1], |
177 | 8.89k | elements[child]) < 0) |
178 | 0 | { |
179 | 0 | child++; |
180 | 0 | } |
181 | | |
182 | 10.8k | if (rectangle_compare_stop (elements[child], tail) >= 0) |
183 | 10.8k | break; |
184 | | |
185 | 0 | elements[i] = elements[child]; |
186 | 0 | } |
187 | 24.1k | elements[i] = tail; |
188 | 24.1k | } |
189 | | |
190 | | static inline rectangle_t * |
191 | | rectangle_pop_start (sweep_line_t *sweep_line) |
192 | 50.7k | { |
193 | 50.7k | return *sweep_line->rectangles++; |
194 | 50.7k | } |
195 | | |
196 | | static inline rectangle_t * |
197 | | rectangle_peek_stop (sweep_line_t *sweep_line) |
198 | 65.3k | { |
199 | 65.3k | return sweep_line->stop[PQ_FIRST_ENTRY]; |
200 | 65.3k | } |
201 | | |
202 | | CAIRO_COMBSORT_DECLARE (_rectangle_sort, |
203 | | rectangle_t *, |
204 | | rectangle_compare_start) |
205 | | |
206 | | static void |
207 | | sweep_line_init (sweep_line_t *sweep_line, |
208 | | rectangle_t **rectangles, |
209 | | int num_rectangles, |
210 | | cairo_fill_rule_t fill_rule, |
211 | | cairo_bool_t do_traps, |
212 | | void *container) |
213 | 13.2k | { |
214 | 13.2k | rectangles[-2] = NULL; |
215 | 13.2k | rectangles[-1] = NULL; |
216 | 13.2k | rectangles[num_rectangles] = NULL; |
217 | 13.2k | sweep_line->rectangles = rectangles; |
218 | 13.2k | sweep_line->stop = rectangles - 2; |
219 | 13.2k | sweep_line->stop_size = 0; |
220 | | |
221 | 13.2k | sweep_line->insert = NULL; |
222 | 13.2k | sweep_line->insert_x = INT_MAX; |
223 | 13.2k | sweep_line->cursor = &sweep_line->tail; |
224 | | |
225 | 13.2k | sweep_line->head.dir = 0; |
226 | 13.2k | sweep_line->head.x = INT32_MIN; |
227 | 13.2k | sweep_line->head.right = NULL; |
228 | 13.2k | sweep_line->head.prev = NULL; |
229 | 13.2k | sweep_line->head.next = &sweep_line->tail; |
230 | 13.2k | sweep_line->tail.prev = &sweep_line->head; |
231 | 13.2k | sweep_line->tail.next = NULL; |
232 | 13.2k | sweep_line->tail.right = NULL; |
233 | 13.2k | sweep_line->tail.x = INT32_MAX; |
234 | 13.2k | sweep_line->tail.dir = 0; |
235 | | |
236 | 13.2k | sweep_line->current_y = INT32_MIN; |
237 | 13.2k | sweep_line->last_y = INT32_MIN; |
238 | | |
239 | 13.2k | sweep_line->fill_rule = fill_rule; |
240 | 13.2k | sweep_line->container = container; |
241 | 13.2k | sweep_line->do_traps = do_traps; |
242 | 13.2k | } |
243 | | |
244 | | static void |
245 | | edge_end_box (sweep_line_t *sweep_line, edge_t *left, int32_t bot) |
246 | 14.5k | { |
247 | 14.5k | cairo_status_t status = CAIRO_STATUS_SUCCESS; |
248 | | |
249 | | /* Only emit (trivial) non-degenerate trapezoids with positive height. */ |
250 | 14.5k | if (likely (left->top < bot)) { |
251 | 14.5k | if (sweep_line->do_traps) { |
252 | 0 | cairo_line_t _left = { |
253 | 0 | { left->x, left->top }, |
254 | 0 | { left->x, bot }, |
255 | 0 | }, _right = { |
256 | 0 | { left->right->x, left->top }, |
257 | 0 | { left->right->x, bot }, |
258 | 0 | }; |
259 | 0 | _cairo_traps_add_trap (sweep_line->container, left->top, bot, &_left, &_right); |
260 | 0 | status = _cairo_traps_status ((cairo_traps_t *) sweep_line->container); |
261 | 14.5k | } else { |
262 | 14.5k | cairo_box_t box; |
263 | | |
264 | 14.5k | box.p1.x = left->x; |
265 | 14.5k | box.p1.y = left->top; |
266 | 14.5k | box.p2.x = left->right->x; |
267 | 14.5k | box.p2.y = bot; |
268 | | |
269 | 14.5k | status = _cairo_boxes_add (sweep_line->container, |
270 | 14.5k | CAIRO_ANTIALIAS_DEFAULT, |
271 | 14.5k | &box); |
272 | 14.5k | } |
273 | 14.5k | } |
274 | 14.5k | if (unlikely (status)) |
275 | 0 | longjmp (sweep_line->unwind, status); |
276 | | |
277 | 14.5k | left->right = NULL; |
278 | 14.5k | } |
279 | | |
280 | | /* Start a new trapezoid at the given top y coordinate, whose edges |
281 | | * are `edge' and `edge->next'. If `edge' already has a trapezoid, |
282 | | * then either add it to the traps in `traps', if the trapezoid's |
283 | | * right edge differs from `edge->next', or do nothing if the new |
284 | | * trapezoid would be a continuation of the existing one. */ |
285 | | static inline void |
286 | | edge_start_or_continue_box (sweep_line_t *sweep_line, |
287 | | edge_t *left, |
288 | | edge_t *right, |
289 | | int top) |
290 | 14.6k | { |
291 | 14.6k | if (left->right == right) |
292 | 0 | return; |
293 | | |
294 | 14.6k | if (left->right != NULL) { |
295 | 58 | if (left->right->x == right->x) { |
296 | | /* continuation on right, so just swap edges */ |
297 | 58 | left->right = right; |
298 | 58 | return; |
299 | 58 | } |
300 | | |
301 | 0 | edge_end_box (sweep_line, left, top); |
302 | 0 | } |
303 | | |
304 | 14.5k | if (left->x != right->x) { |
305 | 14.5k | left->top = top; |
306 | 14.5k | left->right = right; |
307 | 14.5k | } |
308 | 14.5k | } |
309 | | /* |
310 | | * Merge two sorted edge lists. |
311 | | * Input: |
312 | | * - head_a: The head of the first list. |
313 | | * - head_b: The head of the second list; head_b cannot be NULL. |
314 | | * Output: |
315 | | * Returns the head of the merged list. |
316 | | * |
317 | | * Implementation notes: |
318 | | * To make it fast (in particular, to reduce to an insertion sort whenever |
319 | | * one of the two input lists only has a single element) we iterate through |
320 | | * a list until its head becomes greater than the head of the other list, |
321 | | * then we switch their roles. As soon as one of the two lists is empty, we |
322 | | * just attach the other one to the current list and exit. |
323 | | * Writes to memory are only needed to "switch" lists (as it also requires |
324 | | * attaching to the output list the list which we will be iterating next) and |
325 | | * to attach the last non-empty list. |
326 | | */ |
327 | | static edge_t * |
328 | | merge_sorted_edges (edge_t *head_a, edge_t *head_b) |
329 | 37.4k | { |
330 | 37.4k | edge_t *head, *prev; |
331 | 37.4k | int32_t x; |
332 | | |
333 | 37.4k | prev = head_a->prev; |
334 | 37.4k | if (head_a->x <= head_b->x) { |
335 | 22.8k | head = head_a; |
336 | 22.8k | } else { |
337 | 14.5k | head_b->prev = prev; |
338 | 14.5k | head = head_b; |
339 | 14.5k | goto start_with_b; |
340 | 14.5k | } |
341 | | |
342 | 22.9k | do { |
343 | 22.9k | x = head_b->x; |
344 | 74.3k | while (head_a != NULL && head_a->x <= x) { |
345 | 51.4k | prev = head_a; |
346 | 51.4k | head_a = head_a->next; |
347 | 51.4k | } |
348 | | |
349 | 22.9k | head_b->prev = prev; |
350 | 22.9k | prev->next = head_b; |
351 | 22.9k | if (head_a == NULL) |
352 | 0 | return head; |
353 | | |
354 | 37.4k | start_with_b: |
355 | 37.4k | x = head_a->x; |
356 | 207k | while (head_b != NULL && head_b->x <= x) { |
357 | 169k | prev = head_b; |
358 | 169k | head_b = head_b->next; |
359 | 169k | } |
360 | | |
361 | 37.4k | head_a->prev = prev; |
362 | 37.4k | prev->next = head_a; |
363 | 37.4k | if (head_b == NULL) |
364 | 37.4k | return head; |
365 | 37.4k | } while (1); |
366 | 22.8k | } |
367 | | |
368 | | /* |
369 | | * Sort (part of) a list. |
370 | | * Input: |
371 | | * - list: The list to be sorted; list cannot be NULL. |
372 | | * - limit: Recursion limit. |
373 | | * Output: |
374 | | * - head_out: The head of the sorted list containing the first 2^(level+1) elements of the |
375 | | * input list; if the input list has fewer elements, head_out be a sorted list |
376 | | * containing all the elements of the input list. |
377 | | * Returns the head of the list of unprocessed elements (NULL if the sorted list contains |
378 | | * all the elements of the input list). |
379 | | * |
380 | | * Implementation notes: |
381 | | * Special case single element list, unroll/inline the sorting of the first two elements. |
382 | | * Some tail recursion is used since we iterate on the bottom-up solution of the problem |
383 | | * (we start with a small sorted list and keep merging other lists of the same size to it). |
384 | | */ |
385 | | static edge_t * |
386 | | sort_edges (edge_t *list, |
387 | | unsigned int level, |
388 | | edge_t **head_out) |
389 | 37.4k | { |
390 | 37.4k | edge_t *head_other, *remaining; |
391 | 37.4k | unsigned int i; |
392 | | |
393 | 37.4k | head_other = list->next; |
394 | | |
395 | 37.4k | if (head_other == NULL) { |
396 | 0 | *head_out = list; |
397 | 0 | return NULL; |
398 | 0 | } |
399 | | |
400 | 37.4k | remaining = head_other->next; |
401 | 37.4k | if (list->x <= head_other->x) { |
402 | 37.4k | *head_out = list; |
403 | 37.4k | head_other->next = NULL; |
404 | 37.4k | } else { |
405 | 0 | *head_out = head_other; |
406 | 0 | head_other->prev = list->prev; |
407 | 0 | head_other->next = list; |
408 | 0 | list->prev = head_other; |
409 | 0 | list->next = NULL; |
410 | 0 | } |
411 | | |
412 | 60.2k | for (i = 0; i < level && remaining; i++) { |
413 | 22.8k | remaining = sort_edges (remaining, i, &head_other); |
414 | 22.8k | *head_out = merge_sorted_edges (*head_out, head_other); |
415 | 22.8k | } |
416 | | |
417 | 37.4k | return remaining; |
418 | 37.4k | } |
419 | | |
420 | | static edge_t * |
421 | | merge_unsorted_edges (edge_t *head, edge_t *unsorted) |
422 | 14.5k | { |
423 | 14.5k | sort_edges (unsorted, UINT_MAX, &unsorted); |
424 | 14.5k | return merge_sorted_edges (head, unsorted); |
425 | 14.5k | } |
426 | | |
427 | | static void |
428 | | active_edges_insert (sweep_line_t *sweep) |
429 | 14.5k | { |
430 | 14.5k | edge_t *prev; |
431 | 14.5k | int x; |
432 | | |
433 | 14.5k | x = sweep->insert_x; |
434 | 14.5k | prev = sweep->cursor; |
435 | 14.5k | if (prev->x > x) { |
436 | 13.2k | do { |
437 | 13.2k | prev = prev->prev; |
438 | 13.2k | } while (prev->x > x); |
439 | 13.2k | } else { |
440 | 1.29k | while (prev->next->x < x) |
441 | 0 | prev = prev->next; |
442 | 1.29k | } |
443 | | |
444 | 14.5k | prev->next = merge_unsorted_edges (prev->next, sweep->insert); |
445 | 14.5k | sweep->cursor = sweep->insert; |
446 | 14.5k | sweep->insert = NULL; |
447 | 14.5k | sweep->insert_x = INT_MAX; |
448 | 14.5k | } |
449 | | |
450 | | static inline void |
451 | | active_edges_to_traps (sweep_line_t *sweep) |
452 | 14.6k | { |
453 | 14.6k | int top = sweep->current_y; |
454 | 14.6k | edge_t *pos; |
455 | | |
456 | 14.6k | if (sweep->last_y == sweep->current_y) |
457 | 0 | return; |
458 | | |
459 | 14.6k | if (sweep->insert) |
460 | 14.5k | active_edges_insert (sweep); |
461 | | |
462 | 14.6k | pos = sweep->head.next; |
463 | 14.6k | if (pos == &sweep->tail) |
464 | 0 | return; |
465 | | |
466 | 14.6k | if (sweep->fill_rule == CAIRO_FILL_RULE_WINDING) { |
467 | 12.0k | do { |
468 | 12.0k | edge_t *left, *right; |
469 | 12.0k | int winding; |
470 | | |
471 | 12.0k | left = pos; |
472 | 12.0k | winding = left->dir; |
473 | | |
474 | 12.0k | right = left->next; |
475 | | |
476 | | /* Check if there is a co-linear edge with an existing trap */ |
477 | 30.4k | while (right->x == left->x) { |
478 | 18.3k | if (right->right != NULL) { |
479 | 0 | assert (left->right == NULL); |
480 | | /* continuation on left */ |
481 | 0 | left->top = right->top; |
482 | 0 | left->right = right->right; |
483 | 0 | right->right = NULL; |
484 | 0 | } |
485 | 18.3k | winding += right->dir; |
486 | 18.3k | right = right->next; |
487 | 18.3k | } |
488 | | |
489 | 12.0k | if (winding == 0) { |
490 | 0 | if (left->right != NULL) |
491 | 0 | edge_end_box (sweep, left, top); |
492 | 0 | pos = right; |
493 | 0 | continue; |
494 | 0 | } |
495 | | |
496 | 30.5k | do { |
497 | | /* End all subsumed traps */ |
498 | 30.5k | if (unlikely (right->right != NULL)) |
499 | 0 | edge_end_box (sweep, right, top); |
500 | | |
501 | | /* Greedily search for the closing edge, so that we generate |
502 | | * the * maximal span width with the minimal number of |
503 | | * boxes. |
504 | | */ |
505 | 30.5k | winding += right->dir; |
506 | 30.5k | if (winding == 0 && right->x != right->next->x) |
507 | 12.0k | break; |
508 | | |
509 | 18.5k | right = right->next; |
510 | 18.5k | } while (TRUE); |
511 | | |
512 | 0 | edge_start_or_continue_box (sweep, left, right, top); |
513 | | |
514 | 12.0k | pos = right->next; |
515 | 12.0k | } while (pos != &sweep->tail); |
516 | 12.0k | } else { |
517 | 2.58k | do { |
518 | 2.58k | edge_t *right = pos->next; |
519 | 2.58k | int count = 0; |
520 | | |
521 | 11.5k | do { |
522 | | /* End all subsumed traps */ |
523 | 11.5k | if (unlikely (right->right != NULL)) |
524 | 0 | edge_end_box (sweep, right, top); |
525 | | |
526 | | /* skip co-linear edges */ |
527 | 11.5k | if (++count & 1 && right->x != right->next->x) |
528 | 2.58k | break; |
529 | | |
530 | 8.94k | right = right->next; |
531 | 8.94k | } while (TRUE); |
532 | | |
533 | 0 | edge_start_or_continue_box (sweep, pos, right, top); |
534 | | |
535 | 2.58k | pos = right->next; |
536 | 2.58k | } while (pos != &sweep->tail); |
537 | 2.56k | } |
538 | | |
539 | 14.6k | sweep->last_y = sweep->current_y; |
540 | 14.6k | } |
541 | | |
542 | | static inline void |
543 | | sweep_line_delete_edge (sweep_line_t *sweep, edge_t *edge) |
544 | 74.8k | { |
545 | 74.8k | if (edge->right != NULL) { |
546 | 24.1k | edge_t *next = edge->next; |
547 | 24.1k | if (next->x == edge->x) { |
548 | 9.57k | next->top = edge->top; |
549 | 9.57k | next->right = edge->right; |
550 | 9.57k | } else |
551 | 14.5k | edge_end_box (sweep, edge, sweep->current_y); |
552 | 24.1k | } |
553 | | |
554 | 74.8k | if (sweep->cursor == edge) |
555 | 14.5k | sweep->cursor = edge->prev; |
556 | | |
557 | 74.8k | edge->prev->next = edge->next; |
558 | 74.8k | edge->next->prev = edge->prev; |
559 | 74.8k | } |
560 | | |
561 | | static inline cairo_bool_t |
562 | | sweep_line_delete (sweep_line_t *sweep, rectangle_t *rectangle) |
563 | 37.4k | { |
564 | 37.4k | cairo_bool_t update; |
565 | | |
566 | 37.4k | update = TRUE; |
567 | 37.4k | if (sweep->fill_rule == CAIRO_FILL_RULE_WINDING && |
568 | 37.4k | rectangle->left.prev->dir == rectangle->left.dir) |
569 | 11.9k | { |
570 | 11.9k | update = rectangle->left.next != &rectangle->right; |
571 | 11.9k | } |
572 | | |
573 | 37.4k | sweep_line_delete_edge (sweep, &rectangle->left); |
574 | 37.4k | sweep_line_delete_edge (sweep, &rectangle->right); |
575 | | |
576 | 37.4k | rectangle_pop_stop (sweep); |
577 | 37.4k | return update; |
578 | 37.4k | } |
579 | | |
580 | | static inline void |
581 | | sweep_line_insert (sweep_line_t *sweep, rectangle_t *rectangle) |
582 | 37.4k | { |
583 | 37.4k | if (sweep->insert) |
584 | 22.8k | sweep->insert->prev = &rectangle->right; |
585 | 37.4k | rectangle->right.next = sweep->insert; |
586 | 37.4k | rectangle->right.prev = &rectangle->left; |
587 | 37.4k | rectangle->left.next = &rectangle->right; |
588 | 37.4k | rectangle->left.prev = NULL; |
589 | 37.4k | sweep->insert = &rectangle->left; |
590 | 37.4k | if (rectangle->left.x < sweep->insert_x) |
591 | 14.5k | sweep->insert_x = rectangle->left.x; |
592 | | |
593 | 37.4k | pqueue_push (sweep, rectangle); |
594 | 37.4k | } |
595 | | |
596 | | static cairo_status_t |
597 | | _cairo_bentley_ottmann_tessellate_rectangular (rectangle_t **rectangles, |
598 | | int num_rectangles, |
599 | | cairo_fill_rule_t fill_rule, |
600 | | cairo_bool_t do_traps, |
601 | | void *container) |
602 | 13.2k | { |
603 | 13.2k | sweep_line_t sweep_line; |
604 | 13.2k | rectangle_t *rectangle; |
605 | 13.2k | cairo_status_t status; |
606 | 13.2k | cairo_bool_t update; |
607 | | |
608 | 13.2k | sweep_line_init (&sweep_line, |
609 | 13.2k | rectangles, num_rectangles, |
610 | 13.2k | fill_rule, |
611 | 13.2k | do_traps, container); |
612 | 13.2k | if ((status = setjmp (sweep_line.unwind))) |
613 | 0 | return status; |
614 | | |
615 | 13.2k | update = FALSE; |
616 | | |
617 | 13.2k | rectangle = rectangle_pop_start (&sweep_line); |
618 | 14.5k | do { |
619 | 14.5k | if (rectangle->top != sweep_line.current_y) { |
620 | 14.5k | rectangle_t *stop; |
621 | | |
622 | 14.5k | stop = rectangle_peek_stop (&sweep_line); |
623 | 14.6k | while (stop != NULL && stop->bottom < rectangle->top) { |
624 | 29 | if (stop->bottom != sweep_line.current_y) { |
625 | 29 | if (update) { |
626 | 29 | active_edges_to_traps (&sweep_line); |
627 | 29 | update = FALSE; |
628 | 29 | } |
629 | | |
630 | 29 | sweep_line.current_y = stop->bottom; |
631 | 29 | } |
632 | | |
633 | 29 | update |= sweep_line_delete (&sweep_line, stop); |
634 | 29 | stop = rectangle_peek_stop (&sweep_line); |
635 | 29 | } |
636 | | |
637 | 14.5k | if (update) { |
638 | 1.29k | active_edges_to_traps (&sweep_line); |
639 | 1.29k | update = FALSE; |
640 | 1.29k | } |
641 | | |
642 | 14.5k | sweep_line.current_y = rectangle->top; |
643 | 14.5k | } |
644 | | |
645 | 37.4k | do { |
646 | 37.4k | sweep_line_insert (&sweep_line, rectangle); |
647 | 37.4k | } while ((rectangle = rectangle_pop_start (&sweep_line)) != NULL && |
648 | 37.4k | sweep_line.current_y == rectangle->top); |
649 | 14.5k | update = TRUE; |
650 | 14.5k | } while (rectangle); |
651 | | |
652 | 50.6k | while ((rectangle = rectangle_peek_stop (&sweep_line)) != NULL) { |
653 | 37.4k | if (rectangle->bottom != sweep_line.current_y) { |
654 | 13.2k | if (update) { |
655 | 13.2k | active_edges_to_traps (&sweep_line); |
656 | 13.2k | update = FALSE; |
657 | 13.2k | } |
658 | 13.2k | sweep_line.current_y = rectangle->bottom; |
659 | 13.2k | } |
660 | | |
661 | 37.4k | update |= sweep_line_delete (&sweep_line, rectangle); |
662 | 37.4k | } |
663 | | |
664 | 13.2k | return CAIRO_STATUS_SUCCESS; |
665 | 13.2k | } |
666 | | |
667 | | cairo_status_t |
668 | | _cairo_bentley_ottmann_tessellate_rectangular_traps (cairo_traps_t *traps, |
669 | | cairo_fill_rule_t fill_rule) |
670 | 0 | { |
671 | 0 | rectangle_t stack_rectangles[CAIRO_STACK_ARRAY_LENGTH (rectangle_t)]; |
672 | 0 | rectangle_t *stack_rectangles_ptrs[ARRAY_LENGTH (stack_rectangles) + 3]; |
673 | 0 | rectangle_t *rectangles, **rectangles_ptrs; |
674 | 0 | cairo_status_t status; |
675 | 0 | int i; |
676 | |
|
677 | 0 | assert (traps->is_rectangular); |
678 | | |
679 | 0 | if (unlikely (traps->num_traps <= 1)) { |
680 | 0 | if (traps->num_traps == 1) { |
681 | 0 | cairo_trapezoid_t *trap = traps->traps; |
682 | 0 | if (trap->left.p1.x > trap->right.p1.x) { |
683 | 0 | cairo_line_t tmp = trap->left; |
684 | 0 | trap->left = trap->right; |
685 | 0 | trap->right = tmp; |
686 | 0 | } |
687 | 0 | } |
688 | 0 | return CAIRO_STATUS_SUCCESS; |
689 | 0 | } |
690 | | |
691 | 0 | dump_traps (traps, "bo-rects-traps-in.txt"); |
692 | |
|
693 | 0 | rectangles = stack_rectangles; |
694 | 0 | rectangles_ptrs = stack_rectangles_ptrs; |
695 | 0 | if (traps->num_traps > ARRAY_LENGTH (stack_rectangles)) { |
696 | 0 | rectangles = _cairo_malloc_ab_plus_c (traps->num_traps, |
697 | 0 | sizeof (rectangle_t) + |
698 | 0 | sizeof (rectangle_t *), |
699 | 0 | 3*sizeof (rectangle_t *)); |
700 | 0 | if (unlikely (rectangles == NULL)) |
701 | 0 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
702 | | |
703 | 0 | rectangles_ptrs = (rectangle_t **) (rectangles + traps->num_traps); |
704 | 0 | } |
705 | | |
706 | 0 | for (i = 0; i < traps->num_traps; i++) { |
707 | 0 | if (traps->traps[i].left.p1.x < traps->traps[i].right.p1.x) { |
708 | 0 | rectangles[i].left.x = traps->traps[i].left.p1.x; |
709 | 0 | rectangles[i].left.dir = 1; |
710 | |
|
711 | 0 | rectangles[i].right.x = traps->traps[i].right.p1.x; |
712 | 0 | rectangles[i].right.dir = -1; |
713 | 0 | } else { |
714 | 0 | rectangles[i].right.x = traps->traps[i].left.p1.x; |
715 | 0 | rectangles[i].right.dir = 1; |
716 | |
|
717 | 0 | rectangles[i].left.x = traps->traps[i].right.p1.x; |
718 | 0 | rectangles[i].left.dir = -1; |
719 | 0 | } |
720 | |
|
721 | 0 | rectangles[i].left.right = NULL; |
722 | 0 | rectangles[i].right.right = NULL; |
723 | |
|
724 | 0 | rectangles[i].top = traps->traps[i].top; |
725 | 0 | rectangles[i].bottom = traps->traps[i].bottom; |
726 | |
|
727 | 0 | rectangles_ptrs[i+2] = &rectangles[i]; |
728 | 0 | } |
729 | | /* XXX incremental sort */ |
730 | 0 | _rectangle_sort (rectangles_ptrs+2, i); |
731 | |
|
732 | 0 | _cairo_traps_clear (traps); |
733 | 0 | status = _cairo_bentley_ottmann_tessellate_rectangular (rectangles_ptrs+2, i, |
734 | 0 | fill_rule, |
735 | 0 | TRUE, traps); |
736 | 0 | traps->is_rectilinear = TRUE; |
737 | 0 | traps->is_rectangular = TRUE; |
738 | |
|
739 | 0 | if (rectangles != stack_rectangles) |
740 | 0 | free (rectangles); |
741 | |
|
742 | 0 | dump_traps (traps, "bo-rects-traps-out.txt"); |
743 | |
|
744 | 0 | return status; |
745 | 0 | } |
746 | | |
747 | | cairo_status_t |
748 | | _cairo_bentley_ottmann_tessellate_boxes (const cairo_boxes_t *in, |
749 | | cairo_fill_rule_t fill_rule, |
750 | | cairo_boxes_t *out) |
751 | 21.4k | { |
752 | 21.4k | rectangle_t stack_rectangles[CAIRO_STACK_ARRAY_LENGTH (rectangle_t)]; |
753 | 21.4k | rectangle_t *stack_rectangles_ptrs[ARRAY_LENGTH (stack_rectangles) + 3]; |
754 | 21.4k | rectangle_t *rectangles, **rectangles_ptrs; |
755 | 21.4k | rectangle_t *stack_rectangles_chain[CAIRO_STACK_ARRAY_LENGTH (rectangle_t *) ]; |
756 | 21.4k | rectangle_t **rectangles_chain = NULL; |
757 | 21.4k | const struct _cairo_boxes_chunk *chunk; |
758 | 21.4k | cairo_status_t status; |
759 | 21.4k | int i, j, y_min, y_max; |
760 | | |
761 | 21.4k | if (unlikely (in->num_boxes == 0)) { |
762 | 7.20k | _cairo_boxes_clear (out); |
763 | 7.20k | return CAIRO_STATUS_SUCCESS; |
764 | 7.20k | } |
765 | | |
766 | 14.2k | if (in->num_boxes == 1) { |
767 | 993 | if (in == out) { |
768 | 993 | cairo_box_t *box = &in->chunks.base[0]; |
769 | | |
770 | 993 | if (box->p1.x > box->p2.x) { |
771 | 0 | cairo_fixed_t tmp = box->p1.x; |
772 | 0 | box->p1.x = box->p2.x; |
773 | 0 | box->p2.x = tmp; |
774 | 0 | } |
775 | 993 | } else { |
776 | 0 | cairo_box_t box = in->chunks.base[0]; |
777 | |
|
778 | 0 | if (box.p1.x > box.p2.x) { |
779 | 0 | cairo_fixed_t tmp = box.p1.x; |
780 | 0 | box.p1.x = box.p2.x; |
781 | 0 | box.p2.x = tmp; |
782 | 0 | } |
783 | |
|
784 | 0 | _cairo_boxes_clear (out); |
785 | 0 | status = _cairo_boxes_add (out, CAIRO_ANTIALIAS_DEFAULT, &box); |
786 | 0 | assert (status == CAIRO_STATUS_SUCCESS); |
787 | 0 | } |
788 | 993 | return CAIRO_STATUS_SUCCESS; |
789 | 993 | } |
790 | | |
791 | 13.2k | y_min = INT_MAX; y_max = INT_MIN; |
792 | 26.6k | for (chunk = &in->chunks; chunk != NULL; chunk = chunk->next) { |
793 | 13.3k | const cairo_box_t *box = chunk->base; |
794 | 50.7k | for (i = 0; i < chunk->count; i++) { |
795 | 37.4k | if (box[i].p1.y < y_min) |
796 | 13.2k | y_min = box[i].p1.y; |
797 | 37.4k | if (box[i].p1.y > y_max) |
798 | 14.5k | y_max = box[i].p1.y; |
799 | 37.4k | } |
800 | 13.3k | } |
801 | 13.2k | y_min = _cairo_fixed_integer_floor (y_min); |
802 | 13.2k | y_max = _cairo_fixed_integer_floor (y_max) + 1; |
803 | 13.2k | y_max -= y_min; |
804 | | |
805 | 13.2k | if (y_max < in->num_boxes) { |
806 | 12.0k | rectangles_chain = stack_rectangles_chain; |
807 | 12.0k | if (y_max > ARRAY_LENGTH (stack_rectangles_chain)) { |
808 | 0 | rectangles_chain = _cairo_malloc_ab (y_max, sizeof (rectangle_t *)); |
809 | 0 | if (unlikely (rectangles_chain == NULL)) |
810 | 0 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
811 | 0 | } |
812 | 12.0k | memset (rectangles_chain, 0, y_max * sizeof (rectangle_t*)); |
813 | 12.0k | } |
814 | | |
815 | 13.2k | rectangles = stack_rectangles; |
816 | 13.2k | rectangles_ptrs = stack_rectangles_ptrs; |
817 | 13.2k | if (in->num_boxes > ARRAY_LENGTH (stack_rectangles)) { |
818 | 19 | rectangles = _cairo_malloc_ab_plus_c (in->num_boxes, |
819 | 19 | sizeof (rectangle_t) + |
820 | 19 | sizeof (rectangle_t *), |
821 | 19 | 3*sizeof (rectangle_t *)); |
822 | 19 | if (unlikely (rectangles == NULL)) { |
823 | 0 | if (rectangles_chain != stack_rectangles_chain) |
824 | 0 | free (rectangles_chain); |
825 | 0 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
826 | 0 | } |
827 | | |
828 | 19 | rectangles_ptrs = (rectangle_t **) (rectangles + in->num_boxes); |
829 | 19 | } |
830 | | |
831 | 13.2k | j = 0; |
832 | 26.6k | for (chunk = &in->chunks; chunk != NULL; chunk = chunk->next) { |
833 | 13.3k | const cairo_box_t *box = chunk->base; |
834 | 50.7k | for (i = 0; i < chunk->count; i++) { |
835 | 37.4k | int h; |
836 | | |
837 | 37.4k | if (box[i].p1.x < box[i].p2.x) { |
838 | 35.2k | rectangles[j].left.x = box[i].p1.x; |
839 | 35.2k | rectangles[j].left.dir = 1; |
840 | | |
841 | 35.2k | rectangles[j].right.x = box[i].p2.x; |
842 | 35.2k | rectangles[j].right.dir = -1; |
843 | 35.2k | } else { |
844 | 2.19k | rectangles[j].right.x = box[i].p1.x; |
845 | 2.19k | rectangles[j].right.dir = 1; |
846 | | |
847 | 2.19k | rectangles[j].left.x = box[i].p2.x; |
848 | 2.19k | rectangles[j].left.dir = -1; |
849 | 2.19k | } |
850 | | |
851 | 37.4k | rectangles[j].left.right = NULL; |
852 | 37.4k | rectangles[j].right.right = NULL; |
853 | | |
854 | 37.4k | rectangles[j].top = box[i].p1.y; |
855 | 37.4k | rectangles[j].bottom = box[i].p2.y; |
856 | | |
857 | 37.4k | if (rectangles_chain) { |
858 | 34.8k | h = _cairo_fixed_integer_floor (box[i].p1.y) - y_min; |
859 | 34.8k | rectangles[j].left.next = (edge_t *)rectangles_chain[h]; |
860 | 34.8k | rectangles_chain[h] = &rectangles[j]; |
861 | 34.8k | } else { |
862 | 2.53k | rectangles_ptrs[j+2] = &rectangles[j]; |
863 | 2.53k | } |
864 | 37.4k | j++; |
865 | 37.4k | } |
866 | 13.3k | } |
867 | | |
868 | 13.2k | if (rectangles_chain) { |
869 | 12.0k | j = 2; |
870 | 24.1k | for (y_min = 0; y_min < y_max; y_min++) { |
871 | 12.0k | rectangle_t *r; |
872 | 12.0k | int start = j; |
873 | 46.9k | for (r = rectangles_chain[y_min]; r; r = (rectangle_t *)r->left.next) |
874 | 34.8k | rectangles_ptrs[j++] = r; |
875 | 12.0k | if (j > start + 1) |
876 | 12.0k | _rectangle_sort (rectangles_ptrs + start, j - start); |
877 | 12.0k | } |
878 | | |
879 | 12.0k | if (rectangles_chain != stack_rectangles_chain) |
880 | 0 | free (rectangles_chain); |
881 | | |
882 | 12.0k | j -= 2; |
883 | 12.0k | } else { |
884 | 1.26k | _rectangle_sort (rectangles_ptrs + 2, j); |
885 | 1.26k | } |
886 | | |
887 | 13.2k | _cairo_boxes_clear (out); |
888 | 13.2k | status = _cairo_bentley_ottmann_tessellate_rectangular (rectangles_ptrs+2, j, |
889 | 13.2k | fill_rule, |
890 | 13.2k | FALSE, out); |
891 | 13.2k | if (rectangles != stack_rectangles) |
892 | 19 | free (rectangles); |
893 | | |
894 | 13.2k | return status; |
895 | 13.2k | } |