/work/workdir/UnpackedTarball/cairo/src/cairo-rectangular-scan-converter.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* cairo - a vector graphics library with display and print output |
2 | | * |
3 | | * Copyright © 2009 Intel Corporation |
4 | | * |
5 | | * This library is free software; you can redistribute it and/or |
6 | | * modify it either under the terms of the GNU Lesser General Public |
7 | | * License version 2.1 as published by the Free Software Foundation |
8 | | * (the "LGPL") or, at your option, under the terms of the Mozilla |
9 | | * Public License Version 1.1 (the "MPL"). If you do not alter this |
10 | | * notice, a recipient may use your version of this file under either |
11 | | * the MPL or the LGPL. |
12 | | * |
13 | | * You should have received a copy of the LGPL along with this library |
14 | | * in the file COPYING-LGPL-2.1; if not, write to the Free Software |
15 | | * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA |
16 | | * You should have received a copy of the MPL along with this library |
17 | | * in the file COPYING-MPL-1.1 |
18 | | * |
19 | | * The contents of this file are subject to the Mozilla Public License |
20 | | * Version 1.1 (the "License"); you may not use this file except in |
21 | | * compliance with the License. You may obtain a copy of the License at |
22 | | * http://www.mozilla.org/MPL/ |
23 | | * |
24 | | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
25 | | * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
26 | | * the specific language governing rights and limitations. |
27 | | * |
28 | | * The Original Code is the cairo graphics library. |
29 | | * |
30 | | * Contributor(s): |
31 | | * Chris Wilson <chris@chris-wilson.co.uk> |
32 | | */ |
33 | | |
34 | | #include "cairoint.h" |
35 | | |
36 | | #include "cairo-combsort-inline.h" |
37 | | #include "cairo-error-private.h" |
38 | | #include "cairo-freelist-private.h" |
39 | | #include "cairo-list-private.h" |
40 | | #include "cairo-spans-private.h" |
41 | | |
42 | | #include <setjmp.h> |
43 | | |
44 | | typedef struct _rectangle { |
45 | | struct _rectangle *next, *prev; |
46 | | cairo_fixed_t left, right; |
47 | | cairo_fixed_t top, bottom; |
48 | | int32_t top_y, bottom_y; |
49 | | int dir; |
50 | | } rectangle_t; |
51 | | |
52 | 3.35k | #define UNROLL3(x) x x x |
53 | | |
54 | | /* the parent is always given by index/2 */ |
55 | 322 | #define PQ_PARENT_INDEX(i) ((i) >> 1) |
56 | 2.14k | #define PQ_FIRST_ENTRY 1 |
57 | | |
58 | | /* left and right children are index * 2 and (index * 2) +1 respectively */ |
59 | 426 | #define PQ_LEFT_CHILD_INDEX(i) ((i) << 1) |
60 | | |
61 | | typedef struct _pqueue { |
62 | | int size, max_size; |
63 | | |
64 | | rectangle_t **elements; |
65 | | rectangle_t *elements_embedded[1024]; |
66 | | } pqueue_t; |
67 | | |
68 | | typedef struct { |
69 | | rectangle_t **start; |
70 | | pqueue_t stop; |
71 | | rectangle_t head, tail; |
72 | | rectangle_t *insert_cursor; |
73 | | int32_t current_y; |
74 | | int32_t xmin, xmax; |
75 | | |
76 | | struct coverage { |
77 | | struct cell { |
78 | | struct cell *prev, *next; |
79 | | int x, covered, uncovered; |
80 | | } head, tail, *cursor; |
81 | | unsigned int count; |
82 | | cairo_freepool_t pool; |
83 | | } coverage; |
84 | | |
85 | | cairo_half_open_span_t spans_stack[CAIRO_STACK_ARRAY_LENGTH (cairo_half_open_span_t)]; |
86 | | cairo_half_open_span_t *spans; |
87 | | unsigned int num_spans; |
88 | | unsigned int size_spans; |
89 | | |
90 | | jmp_buf jmpbuf; |
91 | | } sweep_line_t; |
92 | | |
93 | | static inline int |
94 | | rectangle_compare_start (const rectangle_t *a, |
95 | | const rectangle_t *b) |
96 | 639 | { |
97 | 639 | int cmp; |
98 | | |
99 | 639 | cmp = a->top_y - b->top_y; |
100 | 639 | if (cmp) |
101 | 535 | return cmp; |
102 | | |
103 | 104 | return a->left - b->left; |
104 | 639 | } |
105 | | |
106 | | static inline int |
107 | | rectangle_compare_stop (const rectangle_t *a, |
108 | | const rectangle_t *b) |
109 | 530 | { |
110 | 530 | return a->bottom_y - b->bottom_y; |
111 | 530 | } |
112 | | |
113 | | static inline void |
114 | | pqueue_init (pqueue_t *pq) |
115 | 109 | { |
116 | 109 | pq->max_size = ARRAY_LENGTH (pq->elements_embedded); |
117 | 109 | pq->size = 0; |
118 | | |
119 | 109 | pq->elements = pq->elements_embedded; |
120 | 109 | pq->elements[PQ_FIRST_ENTRY] = NULL; |
121 | 109 | } |
122 | | |
123 | | static inline void |
124 | | pqueue_fini (pqueue_t *pq) |
125 | 109 | { |
126 | 109 | if (pq->elements != pq->elements_embedded) |
127 | 0 | free (pq->elements); |
128 | 109 | } |
129 | | |
130 | | static cairo_bool_t |
131 | | pqueue_grow (pqueue_t *pq) |
132 | 0 | { |
133 | 0 | rectangle_t **new_elements; |
134 | 0 | pq->max_size *= 2; |
135 | |
|
136 | 0 | if (pq->elements == pq->elements_embedded) { |
137 | 0 | new_elements = _cairo_malloc_ab (pq->max_size, |
138 | 0 | sizeof (rectangle_t *)); |
139 | 0 | if (unlikely (new_elements == NULL)) |
140 | 0 | return FALSE; |
141 | | |
142 | 0 | memcpy (new_elements, pq->elements_embedded, |
143 | 0 | sizeof (pq->elements_embedded)); |
144 | 0 | } else { |
145 | 0 | new_elements = _cairo_realloc_ab (pq->elements, |
146 | 0 | pq->max_size, |
147 | 0 | sizeof (rectangle_t *)); |
148 | 0 | if (unlikely (new_elements == NULL)) |
149 | 0 | return FALSE; |
150 | 0 | } |
151 | | |
152 | 0 | pq->elements = new_elements; |
153 | 0 | return TRUE; |
154 | 0 | } |
155 | | |
156 | | static inline void |
157 | | pqueue_push (sweep_line_t *sweep, rectangle_t *rectangle) |
158 | 431 | { |
159 | 431 | rectangle_t **elements; |
160 | 431 | int i, parent; |
161 | | |
162 | 431 | if (unlikely (sweep->stop.size + 1 == sweep->stop.max_size)) { |
163 | 0 | if (unlikely (! pqueue_grow (&sweep->stop))) |
164 | 0 | longjmp (sweep->jmpbuf, |
165 | 0 | _cairo_error (CAIRO_STATUS_NO_MEMORY)); |
166 | 0 | } |
167 | | |
168 | 431 | elements = sweep->stop.elements; |
169 | 431 | for (i = ++sweep->stop.size; |
170 | 431 | i != PQ_FIRST_ENTRY && |
171 | 431 | rectangle_compare_stop (rectangle, |
172 | 322 | elements[parent = PQ_PARENT_INDEX (i)]) < 0; |
173 | 431 | i = parent) |
174 | 0 | { |
175 | 0 | elements[i] = elements[parent]; |
176 | 0 | } |
177 | | |
178 | 431 | elements[i] = rectangle; |
179 | 431 | } |
180 | | |
181 | | static inline void |
182 | | pqueue_pop (pqueue_t *pq) |
183 | 425 | { |
184 | 425 | rectangle_t **elements = pq->elements; |
185 | 425 | rectangle_t *tail; |
186 | 425 | int child, i; |
187 | | |
188 | 425 | tail = elements[pq->size--]; |
189 | 425 | if (pq->size == 0) { |
190 | 103 | elements[PQ_FIRST_ENTRY] = NULL; |
191 | 103 | return; |
192 | 103 | } |
193 | | |
194 | 322 | for (i = PQ_FIRST_ENTRY; |
195 | 426 | (child = PQ_LEFT_CHILD_INDEX (i)) <= pq->size; |
196 | 322 | i = child) |
197 | 208 | { |
198 | 208 | if (child != pq->size && |
199 | 208 | rectangle_compare_stop (elements[child+1], |
200 | 0 | elements[child]) < 0) |
201 | 0 | { |
202 | 0 | child++; |
203 | 0 | } |
204 | | |
205 | 208 | if (rectangle_compare_stop (elements[child], tail) >= 0) |
206 | 104 | break; |
207 | | |
208 | 104 | elements[i] = elements[child]; |
209 | 104 | } |
210 | 322 | elements[i] = tail; |
211 | 322 | } |
212 | | |
213 | | static inline rectangle_t * |
214 | | peek_stop (sweep_line_t *sweep) |
215 | 752 | { |
216 | 752 | return sweep->stop.elements[PQ_FIRST_ENTRY]; |
217 | 752 | } |
218 | | |
219 | | CAIRO_COMBSORT_DECLARE (rectangle_sort, rectangle_t *, rectangle_compare_start) |
220 | | |
221 | | static void |
222 | | sweep_line_init (sweep_line_t *sweep) |
223 | 109 | { |
224 | 109 | sweep->head.left = INT_MIN; |
225 | 109 | sweep->head.next = &sweep->tail; |
226 | 109 | sweep->tail.left = INT_MAX; |
227 | 109 | sweep->tail.prev = &sweep->head; |
228 | 109 | sweep->insert_cursor = &sweep->tail; |
229 | | |
230 | 109 | _cairo_freepool_init (&sweep->coverage.pool, sizeof (struct cell)); |
231 | | |
232 | 109 | sweep->spans = sweep->spans_stack; |
233 | 109 | sweep->size_spans = ARRAY_LENGTH (sweep->spans_stack); |
234 | | |
235 | 109 | sweep->coverage.head.prev = NULL; |
236 | 109 | sweep->coverage.head.x = INT_MIN; |
237 | 109 | sweep->coverage.tail.next = NULL; |
238 | 109 | sweep->coverage.tail.x = INT_MAX; |
239 | | |
240 | 109 | pqueue_init (&sweep->stop); |
241 | 109 | } |
242 | | |
243 | | static void |
244 | | sweep_line_fini (sweep_line_t *sweep) |
245 | 109 | { |
246 | 109 | _cairo_freepool_fini (&sweep->coverage.pool); |
247 | 109 | pqueue_fini (&sweep->stop); |
248 | | |
249 | 109 | if (sweep->spans != sweep->spans_stack) |
250 | 0 | free (sweep->spans); |
251 | 109 | } |
252 | | |
253 | | static inline void |
254 | | add_cell (sweep_line_t *sweep, int x, int covered, int uncovered) |
255 | 1.75k | { |
256 | 1.75k | struct cell *cell; |
257 | | |
258 | 1.75k | cell = sweep->coverage.cursor; |
259 | 1.75k | if (cell->x > x) { |
260 | 768 | do { |
261 | 768 | UNROLL3({ |
262 | 306 | if (cell->prev->x < x) |
263 | 306 | break; |
264 | 306 | cell = cell->prev; |
265 | 306 | }) |
266 | 306 | } while (TRUE); |
267 | 988 | } else { |
268 | 988 | if (cell->x == x) |
269 | 98 | goto found; |
270 | | |
271 | 890 | do { |
272 | 890 | UNROLL3({ |
273 | 104 | cell = cell->next; |
274 | 104 | if (cell->x >= x) |
275 | 104 | break; |
276 | 104 | }) |
277 | 104 | } while (TRUE); |
278 | 890 | } |
279 | | |
280 | 1.65k | if (x != cell->x) { |
281 | 1.14k | struct cell *c; |
282 | | |
283 | 1.14k | sweep->coverage.count++; |
284 | | |
285 | 1.14k | c = _cairo_freepool_alloc (&sweep->coverage.pool); |
286 | 1.14k | if (unlikely (c == NULL)) { |
287 | 0 | longjmp (sweep->jmpbuf, |
288 | 0 | _cairo_error (CAIRO_STATUS_NO_MEMORY)); |
289 | 0 | } |
290 | | |
291 | 1.14k | cell->prev->next = c; |
292 | 1.14k | c->prev = cell->prev; |
293 | 1.14k | c->next = cell; |
294 | 1.14k | cell->prev = c; |
295 | | |
296 | 1.14k | c->x = x; |
297 | 1.14k | c->covered = 0; |
298 | 1.14k | c->uncovered = 0; |
299 | | |
300 | 1.14k | cell = c; |
301 | 1.14k | } |
302 | | |
303 | 1.75k | found: |
304 | 1.75k | cell->covered += covered; |
305 | 1.75k | cell->uncovered += uncovered; |
306 | 1.75k | sweep->coverage.cursor = cell; |
307 | 1.75k | } |
308 | | |
309 | | static inline void |
310 | | _active_edges_to_spans (sweep_line_t *sweep) |
311 | 446 | { |
312 | 446 | int32_t y = sweep->current_y; |
313 | 446 | rectangle_t *rectangle; |
314 | 446 | int coverage, prev_coverage; |
315 | 446 | int prev_x; |
316 | 446 | struct cell *cell; |
317 | | |
318 | 446 | sweep->num_spans = 0; |
319 | 446 | if (sweep->head.next == &sweep->tail) |
320 | 0 | return; |
321 | | |
322 | 446 | sweep->coverage.head.next = &sweep->coverage.tail; |
323 | 446 | sweep->coverage.tail.prev = &sweep->coverage.head; |
324 | 446 | sweep->coverage.cursor = &sweep->coverage.tail; |
325 | 446 | sweep->coverage.count = 0; |
326 | | |
327 | | /* XXX cell coverage only changes when a rectangle appears or |
328 | | * disappears. Try only modifying coverage at such times. |
329 | | */ |
330 | 446 | for (rectangle = sweep->head.next; |
331 | 1.32k | rectangle != &sweep->tail; |
332 | 878 | rectangle = rectangle->next) |
333 | 878 | { |
334 | 878 | int height; |
335 | 878 | int frac, i; |
336 | | |
337 | 878 | if (y == rectangle->bottom_y) { |
338 | 425 | height = rectangle->bottom & CAIRO_FIXED_FRAC_MASK; |
339 | 425 | if (height == 0) |
340 | 0 | continue; |
341 | 425 | } else |
342 | 453 | height = CAIRO_FIXED_ONE; |
343 | 878 | if (y == rectangle->top_y) |
344 | 431 | height -= rectangle->top & CAIRO_FIXED_FRAC_MASK; |
345 | 878 | height *= rectangle->dir; |
346 | | |
347 | 878 | i = _cairo_fixed_integer_part (rectangle->left), |
348 | 878 | frac = _cairo_fixed_fractional_part (rectangle->left); |
349 | 878 | add_cell (sweep, i, |
350 | 878 | (CAIRO_FIXED_ONE-frac) * height, |
351 | 878 | frac * height); |
352 | | |
353 | 878 | i = _cairo_fixed_integer_part (rectangle->right), |
354 | 878 | frac = _cairo_fixed_fractional_part (rectangle->right); |
355 | 878 | add_cell (sweep, i, |
356 | 878 | -(CAIRO_FIXED_ONE-frac) * height, |
357 | 878 | -frac * height); |
358 | 878 | } |
359 | | |
360 | 446 | if (2*sweep->coverage.count >= sweep->size_spans) { |
361 | 0 | unsigned size; |
362 | |
|
363 | 0 | size = sweep->size_spans; |
364 | 0 | while (size <= 2*sweep->coverage.count) |
365 | 0 | size <<= 1; |
366 | |
|
367 | 0 | if (sweep->spans != sweep->spans_stack) |
368 | 0 | free (sweep->spans); |
369 | |
|
370 | 0 | sweep->spans = _cairo_malloc_ab (size, sizeof (cairo_half_open_span_t)); |
371 | 0 | if (unlikely (sweep->spans == NULL)) |
372 | 0 | longjmp (sweep->jmpbuf, _cairo_error (CAIRO_STATUS_NO_MEMORY)); |
373 | | |
374 | 0 | sweep->size_spans = size; |
375 | 0 | } |
376 | | |
377 | 446 | prev_coverage = coverage = 0; |
378 | 446 | prev_x = INT_MIN; |
379 | 1.59k | for (cell = sweep->coverage.head.next; cell != &sweep->coverage.tail; cell = cell->next) { |
380 | 1.14k | if (cell->x != prev_x && coverage != prev_coverage) { |
381 | 240 | int n = sweep->num_spans++; |
382 | 240 | int c = coverage >> (CAIRO_FIXED_FRAC_BITS * 2 - 8); |
383 | 240 | sweep->spans[n].x = prev_x; |
384 | 240 | sweep->spans[n].inverse = 0; |
385 | 240 | sweep->spans[n].coverage = c - (c >> 8); |
386 | 240 | prev_coverage = coverage; |
387 | 240 | } |
388 | | |
389 | 1.14k | coverage += cell->covered; |
390 | 1.14k | if (coverage != prev_coverage) { |
391 | 1.14k | int n = sweep->num_spans++; |
392 | 1.14k | int c = coverage >> (CAIRO_FIXED_FRAC_BITS * 2 - 8); |
393 | 1.14k | sweep->spans[n].x = cell->x; |
394 | 1.14k | sweep->spans[n].inverse = 0; |
395 | 1.14k | sweep->spans[n].coverage = c - (c >> 8); |
396 | 1.14k | prev_coverage = coverage; |
397 | 1.14k | } |
398 | 1.14k | coverage += cell->uncovered; |
399 | 1.14k | prev_x = cell->x + 1; |
400 | 1.14k | } |
401 | 446 | _cairo_freepool_reset (&sweep->coverage.pool); |
402 | | |
403 | 446 | if (sweep->num_spans) { |
404 | 446 | if (prev_x <= sweep->xmax) { |
405 | 422 | int n = sweep->num_spans++; |
406 | 422 | int c = coverage >> (CAIRO_FIXED_FRAC_BITS * 2 - 8); |
407 | 422 | sweep->spans[n].x = prev_x; |
408 | 422 | sweep->spans[n].inverse = 0; |
409 | 422 | sweep->spans[n].coverage = c - (c >> 8); |
410 | 422 | } |
411 | | |
412 | 446 | if (coverage && prev_x < sweep->xmax) { |
413 | 0 | int n = sweep->num_spans++; |
414 | 0 | sweep->spans[n].x = sweep->xmax; |
415 | 0 | sweep->spans[n].inverse = 1; |
416 | 0 | sweep->spans[n].coverage = 0; |
417 | 0 | } |
418 | 446 | } |
419 | 446 | } |
420 | | |
421 | | static inline void |
422 | | sweep_line_delete (sweep_line_t *sweep, |
423 | | rectangle_t *rectangle) |
424 | 425 | { |
425 | 425 | if (sweep->insert_cursor == rectangle) |
426 | 103 | sweep->insert_cursor = rectangle->next; |
427 | | |
428 | 425 | rectangle->prev->next = rectangle->next; |
429 | 425 | rectangle->next->prev = rectangle->prev; |
430 | | |
431 | 425 | pqueue_pop (&sweep->stop); |
432 | 425 | } |
433 | | |
434 | | static inline void |
435 | | sweep_line_insert (sweep_line_t *sweep, |
436 | | rectangle_t *rectangle) |
437 | 431 | { |
438 | 431 | rectangle_t *pos; |
439 | | |
440 | 431 | pos = sweep->insert_cursor; |
441 | 431 | if (pos->left != rectangle->left) { |
442 | 327 | if (pos->left > rectangle->left) { |
443 | 218 | do { |
444 | 218 | UNROLL3({ |
445 | 104 | if (pos->prev->left < rectangle->left) |
446 | 104 | break; |
447 | 104 | pos = pos->prev; |
448 | 104 | }) |
449 | 104 | } while (TRUE); |
450 | 218 | } else { |
451 | 109 | do { |
452 | 109 | UNROLL3({ |
453 | 104 | pos = pos->next; |
454 | 104 | if (pos->left >= rectangle->left) |
455 | 104 | break; |
456 | 104 | }); |
457 | 0 | } while (TRUE); |
458 | 109 | } |
459 | 327 | } |
460 | | |
461 | 0 | pos->prev->next = rectangle; |
462 | 431 | rectangle->prev = pos->prev; |
463 | 431 | rectangle->next = pos; |
464 | 431 | pos->prev = rectangle; |
465 | 431 | sweep->insert_cursor = rectangle; |
466 | | |
467 | 431 | pqueue_push (sweep, rectangle); |
468 | 431 | } |
469 | | |
470 | | static void |
471 | | render_rows (sweep_line_t *sweep_line, |
472 | | cairo_span_renderer_t *renderer, |
473 | | int height) |
474 | 446 | { |
475 | 446 | cairo_status_t status; |
476 | | |
477 | 446 | _active_edges_to_spans (sweep_line); |
478 | | |
479 | 446 | status = renderer->render_rows (renderer, |
480 | 446 | sweep_line->current_y, height, |
481 | 446 | sweep_line->spans, |
482 | 446 | sweep_line->num_spans); |
483 | 446 | if (unlikely (status)) |
484 | 0 | longjmp (sweep_line->jmpbuf, status); |
485 | 446 | } |
486 | | |
487 | | static cairo_status_t |
488 | | generate (cairo_rectangular_scan_converter_t *self, |
489 | | cairo_span_renderer_t *renderer, |
490 | | rectangle_t **rectangles) |
491 | 109 | { |
492 | 109 | sweep_line_t sweep_line; |
493 | 109 | rectangle_t *start, *stop; |
494 | 109 | cairo_status_t status; |
495 | | |
496 | 109 | sweep_line_init (&sweep_line); |
497 | 109 | sweep_line.xmin = _cairo_fixed_integer_part (self->extents.p1.x); |
498 | 109 | sweep_line.xmax = _cairo_fixed_integer_part (self->extents.p2.x); |
499 | 109 | sweep_line.start = rectangles; |
500 | 109 | if ((status = setjmp (sweep_line.jmpbuf))) |
501 | 0 | goto out; |
502 | | |
503 | 109 | sweep_line.current_y = _cairo_fixed_integer_part (self->extents.p1.y); |
504 | 109 | start = *sweep_line.start++; |
505 | 327 | do { |
506 | 327 | if (start->top_y != sweep_line.current_y) { |
507 | 11 | render_rows (&sweep_line, renderer, |
508 | 11 | start->top_y - sweep_line.current_y); |
509 | 11 | sweep_line.current_y = start->top_y; |
510 | 11 | } |
511 | | |
512 | 431 | do { |
513 | 431 | sweep_line_insert (&sweep_line, start); |
514 | 431 | start = *sweep_line.start++; |
515 | 431 | if (start == NULL) |
516 | 109 | goto end; |
517 | 322 | if (start->top_y != sweep_line.current_y) |
518 | 218 | break; |
519 | 322 | } while (TRUE); |
520 | | |
521 | 218 | render_rows (&sweep_line, renderer, 1); |
522 | | |
523 | 218 | stop = peek_stop (&sweep_line); |
524 | 327 | while (stop->bottom_y == sweep_line.current_y) { |
525 | 109 | sweep_line_delete (&sweep_line, stop); |
526 | 109 | stop = peek_stop (&sweep_line); |
527 | 109 | if (stop == NULL) |
528 | 0 | break; |
529 | 109 | } |
530 | | |
531 | 218 | sweep_line.current_y++; |
532 | | |
533 | 218 | while (stop != NULL && stop->bottom_y < start->top_y) { |
534 | 0 | if (stop->bottom_y != sweep_line.current_y) { |
535 | 0 | render_rows (&sweep_line, renderer, |
536 | 0 | stop->bottom_y - sweep_line.current_y); |
537 | 0 | sweep_line.current_y = stop->bottom_y; |
538 | 0 | } |
539 | |
|
540 | 0 | render_rows (&sweep_line, renderer, 1); |
541 | |
|
542 | 0 | do { |
543 | 0 | sweep_line_delete (&sweep_line, stop); |
544 | 0 | stop = peek_stop (&sweep_line); |
545 | 0 | } while (stop != NULL && stop->bottom_y == sweep_line.current_y); |
546 | |
|
547 | 0 | sweep_line.current_y++; |
548 | 0 | } |
549 | 218 | } while (TRUE); |
550 | | |
551 | 109 | end: |
552 | 109 | render_rows (&sweep_line, renderer, 1); |
553 | | |
554 | 109 | stop = peek_stop (&sweep_line); |
555 | 322 | while (stop->bottom_y == sweep_line.current_y) { |
556 | 213 | sweep_line_delete (&sweep_line, stop); |
557 | 213 | stop = peek_stop (&sweep_line); |
558 | 213 | if (stop == NULL) |
559 | 0 | goto out; |
560 | 213 | } |
561 | | |
562 | 109 | while (++sweep_line.current_y < _cairo_fixed_integer_part (self->extents.p2.y)) { |
563 | 103 | if (stop->bottom_y != sweep_line.current_y) { |
564 | 5 | render_rows (&sweep_line, renderer, |
565 | 5 | stop->bottom_y - sweep_line.current_y); |
566 | 5 | sweep_line.current_y = stop->bottom_y; |
567 | 5 | } |
568 | | |
569 | 103 | render_rows (&sweep_line, renderer, 1); |
570 | | |
571 | 103 | do { |
572 | 103 | sweep_line_delete (&sweep_line, stop); |
573 | 103 | stop = peek_stop (&sweep_line); |
574 | 103 | if (stop == NULL) |
575 | 103 | goto out; |
576 | 103 | } while (stop->bottom_y == sweep_line.current_y); |
577 | | |
578 | 103 | } |
579 | | |
580 | 109 | out: |
581 | 109 | sweep_line_fini (&sweep_line); |
582 | | |
583 | 109 | return status; |
584 | 109 | } |
585 | | static void generate_row(cairo_span_renderer_t *renderer, |
586 | | const rectangle_t *r, |
587 | | int y, int h, |
588 | | uint16_t coverage) |
589 | 1.17k | { |
590 | 1.17k | cairo_half_open_span_t spans[4]; |
591 | 1.17k | unsigned int num_spans = 0; |
592 | 1.17k | int x1 = _cairo_fixed_integer_part (r->left); |
593 | 1.17k | int x2 = _cairo_fixed_integer_part (r->right); |
594 | 1.17k | if (x2 > x1) { |
595 | 1.17k | if (! _cairo_fixed_is_integer (r->left)) { |
596 | 1.17k | spans[num_spans].x = x1; |
597 | 1.17k | spans[num_spans].coverage = |
598 | 1.17k | coverage * (256 - _cairo_fixed_fractional_part (r->left)) >> 8; |
599 | 1.17k | num_spans++; |
600 | 1.17k | x1++; |
601 | 1.17k | } |
602 | | |
603 | 1.17k | if (x2 > x1) { |
604 | 1.09k | spans[num_spans].x = x1; |
605 | 1.09k | spans[num_spans].coverage = coverage - (coverage >> 8); |
606 | 1.09k | num_spans++; |
607 | 1.09k | } |
608 | | |
609 | 1.17k | if (! _cairo_fixed_is_integer (r->right)) { |
610 | 159 | spans[num_spans].x = x2++; |
611 | 159 | spans[num_spans].coverage = |
612 | 159 | coverage * _cairo_fixed_fractional_part (r->right) >> 8; |
613 | 159 | num_spans++; |
614 | 159 | } |
615 | 1.17k | } else { |
616 | 0 | spans[num_spans].x = x2++; |
617 | 0 | spans[num_spans].coverage = coverage * (r->right - r->left) >> 8; |
618 | 0 | num_spans++; |
619 | 0 | } |
620 | | |
621 | 1.17k | spans[num_spans].x = x2; |
622 | 1.17k | spans[num_spans].coverage = 0; |
623 | 1.17k | num_spans++; |
624 | | |
625 | 1.17k | renderer->render_rows (renderer, y, h, spans, num_spans); |
626 | 1.17k | } |
627 | | |
628 | | static cairo_status_t |
629 | | generate_box (cairo_rectangular_scan_converter_t *self, |
630 | | cairo_span_renderer_t *renderer) |
631 | 438 | { |
632 | 438 | const rectangle_t *r = self->chunks.base; |
633 | 438 | int y1 = _cairo_fixed_integer_part (r->top); |
634 | 438 | int y2 = _cairo_fixed_integer_part (r->bottom); |
635 | 438 | if (y2 > y1) { |
636 | 438 | if (! _cairo_fixed_is_integer (r->top)) { |
637 | 438 | generate_row(renderer, r, y1, 1, |
638 | 438 | 256 - _cairo_fixed_fractional_part (r->top)); |
639 | 438 | y1++; |
640 | 438 | } |
641 | | |
642 | 438 | if (y2 > y1) |
643 | 428 | generate_row(renderer, r, y1, y2-y1, 256); |
644 | | |
645 | 438 | if (! _cairo_fixed_is_integer (r->bottom)) |
646 | 311 | generate_row(renderer, r, y2, 1, |
647 | 311 | _cairo_fixed_fractional_part (r->bottom)); |
648 | 438 | } else |
649 | 0 | generate_row(renderer, r, y1, 1, r->bottom - r->top); |
650 | | |
651 | 438 | return CAIRO_STATUS_SUCCESS; |
652 | 438 | } |
653 | | |
654 | | static cairo_status_t |
655 | | _cairo_rectangular_scan_converter_generate (void *converter, |
656 | | cairo_span_renderer_t *renderer) |
657 | 547 | { |
658 | 547 | cairo_rectangular_scan_converter_t *self = converter; |
659 | 547 | rectangle_t *rectangles_stack[CAIRO_STACK_ARRAY_LENGTH (rectangle_t *)]; |
660 | 547 | rectangle_t **rectangles; |
661 | 547 | struct _cairo_rectangular_scan_converter_chunk *chunk; |
662 | 547 | cairo_status_t status; |
663 | 547 | int i, j; |
664 | | |
665 | 547 | if (unlikely (self->num_rectangles == 0)) { |
666 | 0 | return renderer->render_rows (renderer, |
667 | 0 | _cairo_fixed_integer_part (self->extents.p1.y), |
668 | 0 | _cairo_fixed_integer_part (self->extents.p2.y - self->extents.p1.y), |
669 | 0 | NULL, 0); |
670 | 0 | } |
671 | | |
672 | 547 | if (self->num_rectangles == 1) |
673 | 438 | return generate_box (self, renderer); |
674 | | |
675 | 109 | rectangles = rectangles_stack; |
676 | 109 | if (unlikely (self->num_rectangles >= ARRAY_LENGTH (rectangles_stack))) { |
677 | 0 | rectangles = _cairo_malloc_ab (self->num_rectangles + 1, |
678 | 0 | sizeof (rectangle_t *)); |
679 | 0 | if (unlikely (rectangles == NULL)) |
680 | 0 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
681 | 0 | } |
682 | | |
683 | 109 | j = 0; |
684 | 218 | for (chunk = &self->chunks; chunk != NULL; chunk = chunk->next) { |
685 | 109 | rectangle_t *rectangle; |
686 | | |
687 | 109 | rectangle = chunk->base; |
688 | 540 | for (i = 0; i < chunk->count; i++) |
689 | 431 | rectangles[j++] = &rectangle[i]; |
690 | 109 | } |
691 | 109 | rectangle_sort (rectangles, j); |
692 | 109 | rectangles[j] = NULL; |
693 | | |
694 | 109 | status = generate (self, renderer, rectangles); |
695 | | |
696 | 109 | if (rectangles != rectangles_stack) |
697 | 0 | free (rectangles); |
698 | | |
699 | 109 | return status; |
700 | 109 | } |
701 | | |
702 | | static rectangle_t * |
703 | | _allocate_rectangle (cairo_rectangular_scan_converter_t *self) |
704 | 869 | { |
705 | 869 | rectangle_t *rectangle; |
706 | 869 | struct _cairo_rectangular_scan_converter_chunk *chunk; |
707 | | |
708 | 869 | chunk = self->tail; |
709 | 869 | if (chunk->count == chunk->size) { |
710 | 0 | int size; |
711 | |
|
712 | 0 | size = chunk->size * 2; |
713 | 0 | chunk->next = _cairo_malloc_ab_plus_c (size, |
714 | 0 | sizeof (rectangle_t), |
715 | 0 | sizeof (struct _cairo_rectangular_scan_converter_chunk)); |
716 | |
|
717 | 0 | if (unlikely (chunk->next == NULL)) |
718 | 0 | return NULL; |
719 | | |
720 | 0 | chunk = chunk->next; |
721 | 0 | chunk->next = NULL; |
722 | 0 | chunk->count = 0; |
723 | 0 | chunk->size = size; |
724 | 0 | chunk->base = chunk + 1; |
725 | 0 | self->tail = chunk; |
726 | 0 | } |
727 | | |
728 | 869 | rectangle = chunk->base; |
729 | 869 | return rectangle + chunk->count++; |
730 | 869 | } |
731 | | |
732 | | cairo_status_t |
733 | | _cairo_rectangular_scan_converter_add_box (cairo_rectangular_scan_converter_t *self, |
734 | | const cairo_box_t *box, |
735 | | int dir) |
736 | 869 | { |
737 | 869 | rectangle_t *rectangle; |
738 | | |
739 | 869 | rectangle = _allocate_rectangle (self); |
740 | 869 | if (unlikely (rectangle == NULL)) |
741 | 0 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
742 | | |
743 | 869 | rectangle->dir = dir; |
744 | 869 | rectangle->left = MAX (box->p1.x, self->extents.p1.x); |
745 | 869 | rectangle->right = MIN (box->p2.x, self->extents.p2.x); |
746 | 869 | if (unlikely (rectangle->right <= rectangle->left)) { |
747 | 0 | self->tail->count--; |
748 | 0 | return CAIRO_STATUS_SUCCESS; |
749 | 0 | } |
750 | | |
751 | 869 | rectangle->top = MAX (box->p1.y, self->extents.p1.y); |
752 | 869 | rectangle->top_y = _cairo_fixed_integer_floor (rectangle->top); |
753 | 869 | rectangle->bottom = MIN (box->p2.y, self->extents.p2.y); |
754 | 869 | rectangle->bottom_y = _cairo_fixed_integer_floor (rectangle->bottom); |
755 | 869 | if (likely (rectangle->bottom > rectangle->top)) |
756 | 869 | self->num_rectangles++; |
757 | 0 | else |
758 | 0 | self->tail->count--; |
759 | | |
760 | 869 | return CAIRO_STATUS_SUCCESS; |
761 | 869 | } |
762 | | |
763 | | static void |
764 | | _cairo_rectangular_scan_converter_destroy (void *converter) |
765 | 547 | { |
766 | 547 | cairo_rectangular_scan_converter_t *self = converter; |
767 | 547 | struct _cairo_rectangular_scan_converter_chunk *chunk, *next; |
768 | | |
769 | 547 | for (chunk = self->chunks.next; chunk != NULL; chunk = next) { |
770 | 0 | next = chunk->next; |
771 | 0 | free (chunk); |
772 | 0 | } |
773 | 547 | } |
774 | | |
775 | | void |
776 | | _cairo_rectangular_scan_converter_init (cairo_rectangular_scan_converter_t *self, |
777 | | const cairo_rectangle_int_t *extents) |
778 | 547 | { |
779 | 547 | self->base.destroy = _cairo_rectangular_scan_converter_destroy; |
780 | 547 | self->base.generate = _cairo_rectangular_scan_converter_generate; |
781 | | |
782 | 547 | _cairo_box_from_rectangle (&self->extents, extents); |
783 | | |
784 | 547 | self->chunks.base = self->buf; |
785 | 547 | self->chunks.next = NULL; |
786 | 547 | self->chunks.count = 0; |
787 | 547 | self->chunks.size = sizeof (self->buf) / sizeof (rectangle_t); |
788 | 547 | self->tail = &self->chunks; |
789 | | |
790 | 547 | self->num_rectangles = 0; |
791 | 547 | } |