/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* * Copyright (c) 2011 Intel Corporation * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "cairoint.h" #include "cairo-spans-private.h" #include "cairo-error-private.h" #include #include #include struct quorem { int32_t quo; int32_t rem; }; struct edge { struct edge *next, *prev; int32_t height_left; int32_t dir; int32_t vertical; int32_t dy; struct quorem x; struct quorem dxdy; }; /* A collection of sorted and vertically clipped edges of the polygon. * Edges are moved from the polygon to an active list while scan * converting. */ struct polygon { /* The vertical clip extents. */ int32_t ymin, ymax; int num_edges; struct edge *edges; /* Array of edges all starting in the same bucket. An edge is put * into bucket EDGE_BUCKET_INDEX(edge->ytop, polygon->ymin) when * it is added to the polygon. */ struct edge **y_buckets; struct edge *y_buckets_embedded[64]; struct edge edges_embedded[32]; }; struct mono_scan_converter { struct polygon polygon[1]; /* Leftmost edge on the current scan line. */ struct edge head, tail; int is_vertical; cairo_half_open_span_t *spans; cairo_half_open_span_t spans_embedded[64]; int num_spans; /* Clip box. */ int32_t xmin, xmax; int32_t ymin, ymax; }; #define I(x) _cairo_fixed_integer_round_down(x) /* Compute the floored division a/b. Assumes / and % perform symmetric * division. */ inline static struct quorem floored_divrem(int a, int b) { struct quorem qr; qr.quo = a/b; qr.rem = a%b; if ((a^b)<0 && qr.rem) { qr.quo -= 1; qr.rem += b; } return qr; } /* Compute the floored division (x*a)/b. Assumes / and % perform symmetric * division. */ static struct quorem floored_muldivrem(int x, int a, int b) { struct quorem qr; long long xa = (long long)x*a; qr.quo = xa/b; qr.rem = xa%b; if ((xa>=0) != (b>=0) && qr.rem) { qr.quo -= 1; qr.rem += b; } return qr; } static cairo_status_t polygon_init (struct polygon *polygon, int ymin, int ymax) { unsigned h = ymax - ymin + 1; polygon->y_buckets = polygon->y_buckets_embedded; if (h > ARRAY_LENGTH (polygon->y_buckets_embedded)) { polygon->y_buckets = _cairo_malloc_ab (h, sizeof (struct edge *)); if (unlikely (NULL == polygon->y_buckets)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } memset (polygon->y_buckets, 0, h * sizeof (struct edge *)); polygon->y_buckets[h-1] = (void *)-1; polygon->ymin = ymin; polygon->ymax = ymax; return CAIRO_STATUS_SUCCESS; } static void polygon_fini (struct polygon *polygon) { if (polygon->y_buckets != polygon->y_buckets_embedded) free (polygon->y_buckets); if (polygon->edges != polygon->edges_embedded) free (polygon->edges); } static void _polygon_insert_edge_into_its_y_bucket(struct polygon *polygon, struct edge *e, int y) { struct edge **ptail = &polygon->y_buckets[y - polygon->ymin]; if (*ptail) (*ptail)->prev = e; e->next = *ptail; e->prev = NULL; *ptail = e; } inline static void polygon_add_edge (struct polygon *polygon, const cairo_edge_t *edge) { struct edge *e; cairo_fixed_t dx; cairo_fixed_t dy; int y, ytop, ybot; int ymin = polygon->ymin; int ymax = polygon->ymax; y = I(edge->top); ytop = MAX(y, ymin); y = I(edge->bottom); ybot = MIN(y, ymax); if (ybot <= ytop) return; e = polygon->edges + polygon->num_edges++; e->height_left = ybot - ytop; e->dir = edge->dir; dx = edge->line.p2.x - edge->line.p1.x; dy = edge->line.p2.y - edge->line.p1.y; if (dx == 0) { e->vertical = TRUE; e->x.quo = edge->line.p1.x; e->x.rem = 0; e->dxdy.quo = 0; e->dxdy.rem = 0; e->dy = 0; } else { e->vertical = FALSE; e->dxdy = floored_muldivrem (dx, CAIRO_FIXED_ONE, dy); e->dy = dy; e->x = floored_muldivrem (ytop * CAIRO_FIXED_ONE + CAIRO_FIXED_FRAC_MASK/2 - edge->line.p1.y, dx, dy); e->x.quo += edge->line.p1.x; } e->x.rem -= dy; _polygon_insert_edge_into_its_y_bucket (polygon, e, ytop); } static struct edge * merge_sorted_edges (struct edge *head_a, struct edge *head_b) { struct edge *head, **next, *prev; int32_t x; prev = head_a->prev; next = &head; if (head_a->x.quo <= head_b->x.quo) { head = head_a; } else { head = head_b; head_b->prev = prev; goto start_with_b; } do { x = head_b->x.quo; while (head_a != NULL && head_a->x.quo <= x) { prev = head_a; next = &head_a->next; head_a = head_a->next; } head_b->prev = prev; *next = head_b; if (head_a == NULL) return head; start_with_b: x = head_a->x.quo; while (head_b != NULL && head_b->x.quo <= x) { prev = head_b; next = &head_b->next; head_b = head_b->next; } head_a->prev = prev; *next = head_a; if (head_b == NULL) return head; } while (1); } static struct edge * sort_edges (struct edge *list, unsigned int level, struct edge **head_out) { struct edge *head_other, *remaining; unsigned int i; head_other = list->next; if (head_other == NULL) { *head_out = list; return NULL; } remaining = head_other->next; if (list->x.quo <= head_other->x.quo) { *head_out = list; head_other->next = NULL; } else { *head_out = head_other; head_other->prev = list->prev; head_other->next = list; list->prev = head_other; list->next = NULL; } for (i = 0; i < level && remaining; i++) { remaining = sort_edges (remaining, i, &head_other); *head_out = merge_sorted_edges (*head_out, head_other); } return remaining; } static struct edge * merge_unsorted_edges (struct edge *head, struct edge *unsorted) { sort_edges (unsorted, UINT_MAX, &unsorted); return merge_sorted_edges (head, unsorted); } inline static void active_list_merge_edges (struct mono_scan_converter *c, struct edge *edges) { struct edge *e; for (e = edges; c->is_vertical && e; e = e->next) c->is_vertical = e->vertical; c->head.next = merge_unsorted_edges (c->head.next, edges); } inline static void add_span (struct mono_scan_converter *c, int x1, int x2) { int n; if (x1 < c->xmin) x1 = c->xmin; if (x2 > c->xmax) x2 = c->xmax; if (x2 <= x1) return; n = c->num_spans++; c->spans[n].x = x1; c->spans[n].coverage = 255; n = c->num_spans++; c->spans[n].x = x2; c->spans[n].coverage = 0; } inline static void row (struct mono_scan_converter *c, unsigned int mask) { struct edge *edge = c->head.next; int xstart = INT_MIN, prev_x = INT_MIN; int winding = 0; c->num_spans = 0; while (&c->tail != edge) { struct edge *next = edge->next; int xend = I(edge->x.quo); if (--edge->height_left) { if (!edge->vertical) { edge->x.quo += edge->dxdy.quo; edge->x.rem += edge->dxdy.rem; if (edge->x.rem >= 0) { ++edge->x.quo; edge->x.rem -= edge->dy; } } if (edge->x.quo < prev_x) { struct edge *pos = edge->prev; pos->next = next; next->prev = pos; do { pos = pos->prev; } while (edge->x.quo < pos->x.quo); pos->next->prev = edge; edge->next = pos->next; edge->prev = pos; pos->next = edge; } else prev_x = edge->x.quo; } else { edge->prev->next = next; next->prev = edge->prev; } winding += edge->dir; if ((winding & mask) == 0) { if (I(next->x.quo) > xend + 1) { add_span (c, xstart, xend); xstart = INT_MIN; } } else if (xstart == INT_MIN) xstart = xend; edge = next; } } inline static void dec (struct edge *e, int h) { e->height_left -= h; if (e->height_left == 0) { e->prev->next = e->next; e->next->prev = e->prev; } } static cairo_status_t _mono_scan_converter_init(struct mono_scan_converter *c, int xmin, int ymin, int xmax, int ymax) { cairo_status_t status; int max_num_spans; status = polygon_init (c->polygon, ymin, ymax); if (unlikely (status)) return status; max_num_spans = xmax - xmin + 1; if (max_num_spans > ARRAY_LENGTH(c->spans_embedded)) { c->spans = _cairo_malloc_ab (max_num_spans, sizeof (cairo_half_open_span_t)); if (unlikely (c->spans == NULL)) { polygon_fini (c->polygon); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } } else c->spans = c->spans_embedded; c->xmin = xmin; c->xmax = xmax; c->ymin = ymin; c->ymax = ymax; c->head.vertical = 1; c->head.height_left = INT_MAX; c->head.x.quo = _cairo_fixed_from_int (_cairo_fixed_integer_part (INT_MIN)); c->head.prev = NULL; c->head.next = &c->tail; c->tail.prev = &c->head; c->tail.next = NULL; c->tail.x.quo = _cairo_fixed_from_int (_cairo_fixed_integer_part (INT_MAX)); c->tail.height_left = INT_MAX; c->tail.vertical = 1; c->is_vertical = 1; return CAIRO_STATUS_SUCCESS; } static void _mono_scan_converter_fini(struct mono_scan_converter *self) { if (self->spans != self->spans_embedded) free (self->spans); polygon_fini(self->polygon); } static cairo_status_t mono_scan_converter_allocate_edges(struct mono_scan_converter *c, int num_edges) { c->polygon->num_edges = 0; c->polygon->edges = c->polygon->edges_embedded; if (num_edges > ARRAY_LENGTH (c->polygon->edges_embedded)) { c->polygon->edges = _cairo_malloc_ab (num_edges, sizeof (struct edge)); if (unlikely (c->polygon->edges == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } return CAIRO_STATUS_SUCCESS; } static void mono_scan_converter_add_edge (struct mono_scan_converter *c, const cairo_edge_t *edge) { polygon_add_edge (c->polygon, edge); } static void step_edges (struct mono_scan_converter *c, int count) { struct edge *edge; for (edge = c->head.next; edge != &c->tail; edge = edge->next) { edge->height_left -= count; if (! edge->height_left) { edge->prev->next = edge->next; edge->next->prev = edge->prev; } } } static cairo_status_t mono_scan_converter_render(struct mono_scan_converter *c, unsigned int winding_mask, cairo_span_renderer_t *renderer) { struct polygon *polygon = c->polygon; int i, j, h = c->ymax - c->ymin; cairo_status_t status; for (i = 0; i < h; i = j) { j = i + 1; if (polygon->y_buckets[i]) active_list_merge_edges (c, polygon->y_buckets[i]); if (c->is_vertical) { int min_height; struct edge *e; e = c->head.next; min_height = e->height_left; while (e != &c->tail) { if (e->height_left < min_height) min_height = e->height_left; e = e->next; } while (--min_height >= 1 && polygon->y_buckets[j] == NULL) j++; if (j != i + 1) step_edges (c, j - (i + 1)); } row (c, winding_mask); if (c->num_spans) { status = renderer->render_rows (renderer, c->ymin+i, j-i, c->spans, c->num_spans); if (unlikely (status)) return status; } /* XXX recompute after dropping edges? */ if (c->head.next == &c->tail) c->is_vertical = 1; } return CAIRO_STATUS_SUCCESS; } struct _cairo_mono_scan_converter { cairo_scan_converter_t base; struct mono_scan_converter converter[1]; cairo_fill_rule_t fill_rule; }; typedef struct _cairo_mono_scan_converter cairo_mono_scan_converter_t; static void _cairo_mono_scan_converter_destroy (void *converter) { cairo_mono_scan_converter_t *self = converter; _mono_scan_converter_fini (self->converter); free(self); } cairo_status_t _cairo_mono_scan_converter_add_polygon (void *converter, const cairo_polygon_t *polygon) { cairo_mono_scan_converter_t *self = converter; cairo_status_t status; int i; #if 0 FILE *file = fopen ("polygon.txt", "w"); _cairo_debug_print_polygon (file, polygon); fclose (file); #endif status = mono_scan_converter_allocate_edges (self->converter, polygon->num_edges); if (unlikely (status)) return status; for (i = 0; i < polygon->num_edges; i++) mono_scan_converter_add_edge (self->converter, &polygon->edges[i]); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_mono_scan_converter_generate (void *converter, cairo_span_renderer_t *renderer) { cairo_mono_scan_converter_t *self = converter; return mono_scan_converter_render (self->converter, self->fill_rule == CAIRO_FILL_RULE_WINDING ? ~0 : 1, renderer); } cairo_scan_converter_t * _cairo_mono_scan_converter_create (int xmin, int ymin, int xmax, int ymax, cairo_fill_rule_t fill_rule) { cairo_mono_scan_converter_t *self; cairo_status_t status; self = malloc (sizeof(struct _cairo_mono_scan_converter)); if (unlikely (self == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto bail_nomem; } self->base.destroy = _cairo_mono_scan_converter_destroy; self->base.generate = _cairo_mono_scan_converter_generate; status = _mono_scan_converter_init (self->converter, xmin, ymin, xmax, ymax); if (unlikely (status)) goto bail; self->fill_rule = fill_rule; return &self->base; bail: self->base.destroy(&self->base); bail_nomem: return _cairo_scan_converter_create_in_error (status); }