1 /* cairo - a vector graphics library with display and print output
3 * Copyright © 2009 Intel Corporation
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.
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
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/
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.
28 * The Original Code is the cairo graphics library.
31 * Chris Wilson <chris@chris-wilson.co.uk>
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"
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
;
52 #define UNROLL3(x) x x x
54 /* the parent is always given by index/2 */
55 #define PQ_PARENT_INDEX(i) ((i) >> 1)
56 #define PQ_FIRST_ENTRY 1
58 /* left and right children are index * 2 and (index * 2) +1 respectively */
59 #define PQ_LEFT_CHILD_INDEX(i) ((i) << 1)
61 typedef struct _pqueue
{
64 rectangle_t
**elements
;
65 rectangle_t
*elements_embedded
[1024];
71 rectangle_t head
, tail
;
72 rectangle_t
*insert_cursor
;
78 struct cell
*prev
, *next
;
79 int x
, covered
, uncovered
;
80 } head
, tail
, *cursor
;
82 cairo_freepool_t pool
;
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
;
94 rectangle_compare_start (const rectangle_t
*a
,
99 cmp
= a
->top_y
- b
->top_y
;
103 return a
->left
- b
->left
;
107 rectangle_compare_stop (const rectangle_t
*a
,
108 const rectangle_t
*b
)
110 return a
->bottom_y
- b
->bottom_y
;
114 pqueue_init (pqueue_t
*pq
)
116 pq
->max_size
= ARRAY_LENGTH (pq
->elements_embedded
);
119 pq
->elements
= pq
->elements_embedded
;
120 pq
->elements
[PQ_FIRST_ENTRY
] = NULL
;
124 pqueue_fini (pqueue_t
*pq
)
126 if (pq
->elements
!= pq
->elements_embedded
)
131 pqueue_grow (pqueue_t
*pq
)
133 rectangle_t
**new_elements
;
136 if (pq
->elements
== pq
->elements_embedded
) {
137 new_elements
= _cairo_malloc_ab (pq
->max_size
,
138 sizeof (rectangle_t
*));
139 if (unlikely (new_elements
== NULL
))
142 memcpy (new_elements
, pq
->elements_embedded
,
143 sizeof (pq
->elements_embedded
));
145 new_elements
= _cairo_realloc_ab (pq
->elements
,
147 sizeof (rectangle_t
*));
148 if (unlikely (new_elements
== NULL
))
152 pq
->elements
= new_elements
;
157 pqueue_push (sweep_line_t
*sweep
, rectangle_t
*rectangle
)
159 rectangle_t
**elements
;
162 if (unlikely (sweep
->stop
.size
+ 1 == sweep
->stop
.max_size
)) {
163 if (unlikely (! pqueue_grow (&sweep
->stop
)))
164 longjmp (sweep
->jmpbuf
,
165 _cairo_error (CAIRO_STATUS_NO_MEMORY
));
168 elements
= sweep
->stop
.elements
;
169 for (i
= ++sweep
->stop
.size
;
170 i
!= PQ_FIRST_ENTRY
&&
171 rectangle_compare_stop (rectangle
,
172 elements
[parent
= PQ_PARENT_INDEX (i
)]) < 0;
175 elements
[i
] = elements
[parent
];
178 elements
[i
] = rectangle
;
182 pqueue_pop (pqueue_t
*pq
)
184 rectangle_t
**elements
= pq
->elements
;
188 tail
= elements
[pq
->size
--];
190 elements
[PQ_FIRST_ENTRY
] = NULL
;
194 for (i
= PQ_FIRST_ENTRY
;
195 (child
= PQ_LEFT_CHILD_INDEX (i
)) <= pq
->size
;
198 if (child
!= pq
->size
&&
199 rectangle_compare_stop (elements
[child
+1],
200 elements
[child
]) < 0)
205 if (rectangle_compare_stop (elements
[child
], tail
) >= 0)
208 elements
[i
] = elements
[child
];
213 static inline rectangle_t
*
214 peek_stop (sweep_line_t
*sweep
)
216 return sweep
->stop
.elements
[PQ_FIRST_ENTRY
];
219 CAIRO_COMBSORT_DECLARE (rectangle_sort
, rectangle_t
*, rectangle_compare_start
)
222 sweep_line_init (sweep_line_t
*sweep
)
224 sweep
->head
.left
= INT_MIN
;
225 sweep
->head
.next
= &sweep
->tail
;
226 sweep
->tail
.left
= INT_MAX
;
227 sweep
->tail
.prev
= &sweep
->head
;
228 sweep
->insert_cursor
= &sweep
->tail
;
230 _cairo_freepool_init (&sweep
->coverage
.pool
, sizeof (struct cell
));
232 sweep
->spans
= sweep
->spans_stack
;
233 sweep
->size_spans
= ARRAY_LENGTH (sweep
->spans_stack
);
235 sweep
->coverage
.head
.prev
= NULL
;
236 sweep
->coverage
.head
.x
= INT_MIN
;
237 sweep
->coverage
.tail
.next
= NULL
;
238 sweep
->coverage
.tail
.x
= INT_MAX
;
240 pqueue_init (&sweep
->stop
);
244 sweep_line_fini (sweep_line_t
*sweep
)
246 _cairo_freepool_fini (&sweep
->coverage
.pool
);
247 pqueue_fini (&sweep
->stop
);
249 if (sweep
->spans
!= sweep
->spans_stack
)
254 add_cell (sweep_line_t
*sweep
, int x
, int covered
, int uncovered
)
258 cell
= sweep
->coverage
.cursor
;
262 if (cell
->prev
->x
< x
)
283 sweep
->coverage
.count
++;
285 c
= _cairo_freepool_alloc (&sweep
->coverage
.pool
);
286 if (unlikely (c
== NULL
)) {
287 longjmp (sweep
->jmpbuf
,
288 _cairo_error (CAIRO_STATUS_NO_MEMORY
));
291 cell
->prev
->next
= c
;
292 c
->prev
= cell
->prev
;
304 cell
->covered
+= covered
;
305 cell
->uncovered
+= uncovered
;
306 sweep
->coverage
.cursor
= cell
;
310 _active_edges_to_spans (sweep_line_t
*sweep
)
312 int32_t y
= sweep
->current_y
;
313 rectangle_t
*rectangle
;
314 int coverage
, prev_coverage
;
318 sweep
->num_spans
= 0;
319 if (sweep
->head
.next
== &sweep
->tail
)
322 sweep
->coverage
.head
.next
= &sweep
->coverage
.tail
;
323 sweep
->coverage
.tail
.prev
= &sweep
->coverage
.head
;
324 sweep
->coverage
.cursor
= &sweep
->coverage
.tail
;
325 sweep
->coverage
.count
= 0;
327 /* XXX cell coverage only changes when a rectangle appears or
328 * disappears. Try only modifying coverage at such times.
330 for (rectangle
= sweep
->head
.next
;
331 rectangle
!= &sweep
->tail
;
332 rectangle
= rectangle
->next
)
337 if (y
== rectangle
->bottom_y
) {
338 height
= rectangle
->bottom
& CAIRO_FIXED_FRAC_MASK
;
342 height
= CAIRO_FIXED_ONE
;
343 if (y
== rectangle
->top_y
)
344 height
-= rectangle
->top
& CAIRO_FIXED_FRAC_MASK
;
345 height
*= rectangle
->dir
;
347 i
= _cairo_fixed_integer_part (rectangle
->left
),
348 frac
= _cairo_fixed_fractional_part (rectangle
->left
);
350 (CAIRO_FIXED_ONE
-frac
) * height
,
353 i
= _cairo_fixed_integer_part (rectangle
->right
),
354 frac
= _cairo_fixed_fractional_part (rectangle
->right
);
356 -(CAIRO_FIXED_ONE
-frac
) * height
,
360 if (2*sweep
->coverage
.count
>= sweep
->size_spans
) {
363 size
= sweep
->size_spans
;
364 while (size
<= 2*sweep
->coverage
.count
)
367 if (sweep
->spans
!= sweep
->spans_stack
)
370 sweep
->spans
= _cairo_malloc_ab (size
, sizeof (cairo_half_open_span_t
));
371 if (unlikely (sweep
->spans
== NULL
))
372 longjmp (sweep
->jmpbuf
, _cairo_error (CAIRO_STATUS_NO_MEMORY
));
374 sweep
->size_spans
= size
;
377 prev_coverage
= coverage
= 0;
379 for (cell
= sweep
->coverage
.head
.next
; cell
!= &sweep
->coverage
.tail
; cell
= cell
->next
) {
380 if (cell
->x
!= prev_x
&& coverage
!= prev_coverage
) {
381 int n
= sweep
->num_spans
++;
382 int c
= coverage
>> (CAIRO_FIXED_FRAC_BITS
* 2 - 8);
383 sweep
->spans
[n
].x
= prev_x
;
384 sweep
->spans
[n
].inverse
= 0;
385 sweep
->spans
[n
].coverage
= c
- (c
>> 8);
386 prev_coverage
= coverage
;
389 coverage
+= cell
->covered
;
390 if (coverage
!= prev_coverage
) {
391 int n
= sweep
->num_spans
++;
392 int c
= coverage
>> (CAIRO_FIXED_FRAC_BITS
* 2 - 8);
393 sweep
->spans
[n
].x
= cell
->x
;
394 sweep
->spans
[n
].inverse
= 0;
395 sweep
->spans
[n
].coverage
= c
- (c
>> 8);
396 prev_coverage
= coverage
;
398 coverage
+= cell
->uncovered
;
399 prev_x
= cell
->x
+ 1;
401 _cairo_freepool_reset (&sweep
->coverage
.pool
);
403 if (sweep
->num_spans
) {
404 if (prev_x
<= sweep
->xmax
) {
405 int n
= sweep
->num_spans
++;
406 int c
= coverage
>> (CAIRO_FIXED_FRAC_BITS
* 2 - 8);
407 sweep
->spans
[n
].x
= prev_x
;
408 sweep
->spans
[n
].inverse
= 0;
409 sweep
->spans
[n
].coverage
= c
- (c
>> 8);
412 if (coverage
&& prev_x
< sweep
->xmax
) {
413 int n
= sweep
->num_spans
++;
414 sweep
->spans
[n
].x
= sweep
->xmax
;
415 sweep
->spans
[n
].inverse
= 1;
416 sweep
->spans
[n
].coverage
= 0;
422 sweep_line_delete (sweep_line_t
*sweep
,
423 rectangle_t
*rectangle
)
425 if (sweep
->insert_cursor
== rectangle
)
426 sweep
->insert_cursor
= rectangle
->next
;
428 rectangle
->prev
->next
= rectangle
->next
;
429 rectangle
->next
->prev
= rectangle
->prev
;
431 pqueue_pop (&sweep
->stop
);
435 sweep_line_insert (sweep_line_t
*sweep
,
436 rectangle_t
*rectangle
)
440 pos
= sweep
->insert_cursor
;
441 if (pos
->left
!= rectangle
->left
) {
442 if (pos
->left
> rectangle
->left
) {
445 if (pos
->prev
->left
< rectangle
->left
)
454 if (pos
->left
>= rectangle
->left
)
461 pos
->prev
->next
= rectangle
;
462 rectangle
->prev
= pos
->prev
;
463 rectangle
->next
= pos
;
464 pos
->prev
= rectangle
;
465 sweep
->insert_cursor
= rectangle
;
467 pqueue_push (sweep
, rectangle
);
471 render_rows (sweep_line_t
*sweep_line
,
472 cairo_span_renderer_t
*renderer
,
475 cairo_status_t status
;
477 _active_edges_to_spans (sweep_line
);
479 status
= renderer
->render_rows (renderer
,
480 sweep_line
->current_y
, height
,
482 sweep_line
->num_spans
);
483 if (unlikely (status
))
484 longjmp (sweep_line
->jmpbuf
, status
);
487 static cairo_status_t
488 generate (cairo_rectangular_scan_converter_t
*self
,
489 cairo_span_renderer_t
*renderer
,
490 rectangle_t
**rectangles
)
492 sweep_line_t sweep_line
;
493 rectangle_t
*start
, *stop
;
494 cairo_status_t status
;
496 sweep_line_init (&sweep_line
);
497 sweep_line
.xmin
= _cairo_fixed_integer_part (self
->extents
.p1
.x
);
498 sweep_line
.xmax
= _cairo_fixed_integer_part (self
->extents
.p2
.x
);
499 sweep_line
.start
= rectangles
;
500 if ((status
= setjmp (sweep_line
.jmpbuf
)))
503 sweep_line
.current_y
= _cairo_fixed_integer_part (self
->extents
.p1
.y
);
504 start
= *sweep_line
.start
++;
506 if (start
->top_y
!= sweep_line
.current_y
) {
507 render_rows (&sweep_line
, renderer
,
508 start
->top_y
- sweep_line
.current_y
);
509 sweep_line
.current_y
= start
->top_y
;
513 sweep_line_insert (&sweep_line
, start
);
514 start
= *sweep_line
.start
++;
517 if (start
->top_y
!= sweep_line
.current_y
)
521 render_rows (&sweep_line
, renderer
, 1);
523 stop
= peek_stop (&sweep_line
);
524 while (stop
->bottom_y
== sweep_line
.current_y
) {
525 sweep_line_delete (&sweep_line
, stop
);
526 stop
= peek_stop (&sweep_line
);
531 sweep_line
.current_y
++;
533 while (stop
!= NULL
&& stop
->bottom_y
< start
->top_y
) {
534 if (stop
->bottom_y
!= sweep_line
.current_y
) {
535 render_rows (&sweep_line
, renderer
,
536 stop
->bottom_y
- sweep_line
.current_y
);
537 sweep_line
.current_y
= stop
->bottom_y
;
540 render_rows (&sweep_line
, renderer
, 1);
543 sweep_line_delete (&sweep_line
, stop
);
544 stop
= peek_stop (&sweep_line
);
545 } while (stop
!= NULL
&& stop
->bottom_y
== sweep_line
.current_y
);
547 sweep_line
.current_y
++;
552 render_rows (&sweep_line
, renderer
, 1);
554 stop
= peek_stop (&sweep_line
);
555 while (stop
->bottom_y
== sweep_line
.current_y
) {
556 sweep_line_delete (&sweep_line
, stop
);
557 stop
= peek_stop (&sweep_line
);
562 while (++sweep_line
.current_y
< _cairo_fixed_integer_part (self
->extents
.p2
.y
)) {
563 if (stop
->bottom_y
!= sweep_line
.current_y
) {
564 render_rows (&sweep_line
, renderer
,
565 stop
->bottom_y
- sweep_line
.current_y
);
566 sweep_line
.current_y
= stop
->bottom_y
;
569 render_rows (&sweep_line
, renderer
, 1);
572 sweep_line_delete (&sweep_line
, stop
);
573 stop
= peek_stop (&sweep_line
);
576 } while (stop
->bottom_y
== sweep_line
.current_y
);
581 sweep_line_fini (&sweep_line
);
585 static void generate_row(cairo_span_renderer_t
*renderer
,
586 const rectangle_t
*r
,
590 cairo_half_open_span_t spans
[4];
591 unsigned int num_spans
= 0;
592 int x1
= _cairo_fixed_integer_part (r
->left
);
593 int x2
= _cairo_fixed_integer_part (r
->right
);
595 if (! _cairo_fixed_is_integer (r
->left
)) {
596 spans
[num_spans
].x
= x1
;
597 spans
[num_spans
].coverage
=
598 coverage
* (256 - _cairo_fixed_fractional_part (r
->left
)) >> 8;
604 spans
[num_spans
].x
= x1
;
605 spans
[num_spans
].coverage
= coverage
- (coverage
>> 8);
609 if (! _cairo_fixed_is_integer (r
->right
)) {
610 spans
[num_spans
].x
= x2
++;
611 spans
[num_spans
].coverage
=
612 coverage
* _cairo_fixed_fractional_part (r
->right
) >> 8;
616 spans
[num_spans
].x
= x2
++;
617 spans
[num_spans
].coverage
= coverage
* (r
->right
- r
->left
) >> 8;
621 spans
[num_spans
].x
= x2
;
622 spans
[num_spans
].coverage
= 0;
625 renderer
->render_rows (renderer
, y
, h
, spans
, num_spans
);
628 static cairo_status_t
629 generate_box (cairo_rectangular_scan_converter_t
*self
,
630 cairo_span_renderer_t
*renderer
)
632 const rectangle_t
*r
= self
->chunks
.base
;
633 int y1
= _cairo_fixed_integer_part (r
->top
);
634 int y2
= _cairo_fixed_integer_part (r
->bottom
);
636 if (! _cairo_fixed_is_integer (r
->top
)) {
637 generate_row(renderer
, r
, y1
, 1,
638 256 - _cairo_fixed_fractional_part (r
->top
));
643 generate_row(renderer
, r
, y1
, y2
-y1
, 256);
645 if (! _cairo_fixed_is_integer (r
->bottom
))
646 generate_row(renderer
, r
, y2
, 1,
647 _cairo_fixed_fractional_part (r
->bottom
));
649 generate_row(renderer
, r
, y1
, 1, r
->bottom
- r
->top
);
651 return CAIRO_STATUS_SUCCESS
;
654 static cairo_status_t
655 _cairo_rectangular_scan_converter_generate (void *converter
,
656 cairo_span_renderer_t
*renderer
)
658 cairo_rectangular_scan_converter_t
*self
= converter
;
659 rectangle_t
*rectangles_stack
[CAIRO_STACK_ARRAY_LENGTH (rectangle_t
*)];
660 rectangle_t
**rectangles
;
661 struct _cairo_rectangular_scan_converter_chunk
*chunk
;
662 cairo_status_t status
;
665 if (unlikely (self
->num_rectangles
== 0)) {
666 return renderer
->render_rows (renderer
,
667 _cairo_fixed_integer_part (self
->extents
.p1
.y
),
668 _cairo_fixed_integer_part (self
->extents
.p2
.y
- self
->extents
.p1
.y
),
672 if (self
->num_rectangles
== 1)
673 return generate_box (self
, renderer
);
675 rectangles
= rectangles_stack
;
676 if (unlikely (self
->num_rectangles
>= ARRAY_LENGTH (rectangles_stack
))) {
677 rectangles
= _cairo_malloc_ab (self
->num_rectangles
+ 1,
678 sizeof (rectangle_t
*));
679 if (unlikely (rectangles
== NULL
))
680 return _cairo_error (CAIRO_STATUS_NO_MEMORY
);
684 for (chunk
= &self
->chunks
; chunk
!= NULL
; chunk
= chunk
->next
) {
685 rectangle_t
*rectangle
;
687 rectangle
= chunk
->base
;
688 for (i
= 0; i
< chunk
->count
; i
++)
689 rectangles
[j
++] = &rectangle
[i
];
691 rectangle_sort (rectangles
, j
);
692 rectangles
[j
] = NULL
;
694 status
= generate (self
, renderer
, rectangles
);
696 if (rectangles
!= rectangles_stack
)
703 _allocate_rectangle (cairo_rectangular_scan_converter_t
*self
)
705 rectangle_t
*rectangle
;
706 struct _cairo_rectangular_scan_converter_chunk
*chunk
;
709 if (chunk
->count
== chunk
->size
) {
712 size
= chunk
->size
* 2;
713 chunk
->next
= _cairo_malloc_ab_plus_c (size
,
714 sizeof (rectangle_t
),
715 sizeof (struct _cairo_rectangular_scan_converter_chunk
));
717 if (unlikely (chunk
->next
== NULL
))
724 chunk
->base
= chunk
+ 1;
728 rectangle
= chunk
->base
;
729 return rectangle
+ chunk
->count
++;
733 _cairo_rectangular_scan_converter_add_box (cairo_rectangular_scan_converter_t
*self
,
734 const cairo_box_t
*box
,
737 rectangle_t
*rectangle
;
739 rectangle
= _allocate_rectangle (self
);
740 if (unlikely (rectangle
== NULL
))
741 return _cairo_error (CAIRO_STATUS_NO_MEMORY
);
743 rectangle
->dir
= dir
;
744 rectangle
->left
= MAX (box
->p1
.x
, self
->extents
.p1
.x
);
745 rectangle
->right
= MIN (box
->p2
.x
, self
->extents
.p2
.x
);
746 if (unlikely (rectangle
->right
<= rectangle
->left
)) {
748 return CAIRO_STATUS_SUCCESS
;
751 rectangle
->top
= MAX (box
->p1
.y
, self
->extents
.p1
.y
);
752 rectangle
->top_y
= _cairo_fixed_integer_floor (rectangle
->top
);
753 rectangle
->bottom
= MIN (box
->p2
.y
, self
->extents
.p2
.y
);
754 rectangle
->bottom_y
= _cairo_fixed_integer_floor (rectangle
->bottom
);
755 if (likely (rectangle
->bottom
> rectangle
->top
))
756 self
->num_rectangles
++;
760 return CAIRO_STATUS_SUCCESS
;
764 _cairo_rectangular_scan_converter_destroy (void *converter
)
766 cairo_rectangular_scan_converter_t
*self
= converter
;
767 struct _cairo_rectangular_scan_converter_chunk
*chunk
, *next
;
769 for (chunk
= self
->chunks
.next
; chunk
!= NULL
; chunk
= next
) {
776 _cairo_rectangular_scan_converter_init (cairo_rectangular_scan_converter_t
*self
,
777 const cairo_rectangle_int_t
*extents
)
779 self
->base
.destroy
= _cairo_rectangular_scan_converter_destroy
;
780 self
->base
.generate
= _cairo_rectangular_scan_converter_generate
;
782 _cairo_box_from_rectangle (&self
->extents
, extents
);
784 self
->chunks
.base
= self
->buf
;
785 self
->chunks
.next
= NULL
;
786 self
->chunks
.count
= 0;
787 self
->chunks
.size
= sizeof (self
->buf
) / sizeof (rectangle_t
);
788 self
->tail
= &self
->chunks
;
790 self
->num_rectangles
= 0;