/* Given a set of vertices, compute the convex hull using the Graham scan algorithm. */ cairo_status_t _cairo_hull_compute (cairo_pen_vertex_t *vertices, int *num_vertices) { cairo_hull_t hull_stack[CAIRO_STACK_ARRAY_LENGTH (cairo_hull_t)]; cairo_hull_t *hull; int num_hull = *num_vertices; if (CAIRO_INJECT_FAULT ()) return _cairo_error (CAIRO_STATUS_NO_MEMORY); if (num_hull > ARRAY_LENGTH (hull_stack)) { hull = _cairo_malloc_ab (num_hull, sizeof (cairo_hull_t)); if (unlikely (hull == XNULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } else { hull = hull_stack; } _cairo_hull_init (hull, vertices, num_hull); qsort (hull + 1, num_hull - 1, sizeof (cairo_hull_t), _cairo_hull_vertex_compare); _cairo_hull_eliminate_concave (hull, num_hull); _cairo_hull_to_pen (hull, vertices, num_vertices); if (hull != hull_stack) xmemory_free (hull); return CAIRO_STATUS_SUCCESS; }
/** * _cairo_traps_extract_region: * @traps: a #cairo_traps_t * @region: a #cairo_region_t * * Determines if a set of trapezoids are exactly representable as a * cairo region. If so, the passed-in region is initialized to * the area representing the given traps. It should be finalized * with cairo_region_fini(). If not, %CAIRO_INT_STATUS_UNSUPPORTED * is returned. * * Return value: %CAIRO_STATUS_SUCCESS, %CAIRO_INT_STATUS_UNSUPPORTED * or %CAIRO_STATUS_NO_MEMORY **/ cairo_int_status_t _cairo_traps_extract_region (cairo_traps_t *traps, cairo_antialias_t antialias, cairo_region_t **region) { cairo_rectangle_int_t stack_rects[CAIRO_STACK_ARRAY_LENGTH (cairo_rectangle_int_t)]; cairo_rectangle_int_t *rects = stack_rects; cairo_int_status_t status; int i, rect_count; /* we only treat this a hint... */ if (antialias != CAIRO_ANTIALIAS_NONE && ! traps->maybe_region) return CAIRO_INT_STATUS_UNSUPPORTED; if (! _traps_are_pixel_aligned (traps, antialias)) { traps->maybe_region = FALSE; return CAIRO_INT_STATUS_UNSUPPORTED; } if (traps->num_traps > ARRAY_LENGTH (stack_rects)) { rects = _cairo_malloc_ab (traps->num_traps, sizeof (cairo_rectangle_int_t)); if (unlikely (rects == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } rect_count = 0; for (i = 0; i < traps->num_traps; i++) { int x1, y1, x2, y2; if (antialias == CAIRO_ANTIALIAS_NONE) { x1 = _cairo_fixed_integer_round_down (traps->traps[i].left.p1.x); y1 = _cairo_fixed_integer_round_down (traps->traps[i].top); x2 = _cairo_fixed_integer_round_down (traps->traps[i].right.p1.x); y2 = _cairo_fixed_integer_round_down (traps->traps[i].bottom); } else { x1 = _cairo_fixed_integer_part (traps->traps[i].left.p1.x); y1 = _cairo_fixed_integer_part (traps->traps[i].top); x2 = _cairo_fixed_integer_part (traps->traps[i].right.p1.x); y2 = _cairo_fixed_integer_part (traps->traps[i].bottom); } if (x2 > x1 && y2 > y1) { rects[rect_count].x = x1; rects[rect_count].y = y1; rects[rect_count].width = x2 - x1; rects[rect_count].height = y2 - y1; rect_count++; } } *region = cairo_region_create_rectangles (rects, rect_count); status = (*region)->status; if (rects != stack_rects) free (rects); return status; }
cairo_int_status_t _cairo_region_init_boxes (cairo_region_t *region, cairo_box_int_t *boxes, int count) { pixman_box32_t stack_pboxes[CAIRO_STACK_ARRAY_LENGTH (pixman_box32_t)]; pixman_box32_t *pboxes = stack_pboxes; cairo_int_status_t status = CAIRO_STATUS_SUCCESS; int i; if (count > ARRAY_LENGTH (stack_pboxes)) { pboxes = _cairo_malloc_ab (count, sizeof (pixman_box32_t)); if (unlikely (pboxes == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } for (i = 0; i < count; i++) { pboxes[i].x1 = boxes[i].p1.x; pboxes[i].y1 = boxes[i].p1.y; pboxes[i].x2 = boxes[i].p2.x; pboxes[i].y2 = boxes[i].p2.y; } if (! pixman_region32_init_rects (®ion->rgn, pboxes, count)) status = _cairo_error (CAIRO_STATUS_NO_MEMORY); if (pboxes != stack_pboxes) free (pboxes); return status; }
/** * _cairo_traps_extract_region: * @traps: a #cairo_traps_t * @region: a #cairo_region_t * * Determines if a set of trapezoids are exactly representable as a * cairo region. If so, the passed-in region is initialized to * the area representing the given traps. It should be finalized * with _cairo_region_fini(). If not, %CAIRO_INT_STATUS_UNSUPPORTED * is returned. * * Return value: %CAIRO_STATUS_SUCCESS, %CAIRO_INT_STATUS_UNSUPPORTED * or %CAIRO_STATUS_NO_MEMORY **/ cairo_int_status_t _cairo_traps_extract_region (cairo_traps_t *traps, cairo_region_t *region) { cairo_box_int_t stack_boxes[CAIRO_STACK_ARRAY_LENGTH (cairo_box_int_t)]; cairo_box_int_t *boxes = stack_boxes; int i, box_count; cairo_int_status_t status; for (i = 0; i < traps->num_traps; i++) if (!(traps->traps[i].left.p1.x == traps->traps[i].left.p2.x && traps->traps[i].right.p1.x == traps->traps[i].right.p2.x && _cairo_fixed_is_integer(traps->traps[i].top) && _cairo_fixed_is_integer(traps->traps[i].bottom) && _cairo_fixed_is_integer(traps->traps[i].left.p1.x) && _cairo_fixed_is_integer(traps->traps[i].right.p1.x))) { return CAIRO_INT_STATUS_UNSUPPORTED; } if (traps->num_traps > ARRAY_LENGTH(stack_boxes)) { boxes = _cairo_malloc_ab (traps->num_traps, sizeof(cairo_box_int_t)); if (boxes == NULL) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } box_count = 0; for (i = 0; i < traps->num_traps; i++) { int x1 = _cairo_fixed_integer_part(traps->traps[i].left.p1.x); int y1 = _cairo_fixed_integer_part(traps->traps[i].top); int x2 = _cairo_fixed_integer_part(traps->traps[i].right.p1.x); int y2 = _cairo_fixed_integer_part(traps->traps[i].bottom); /* XXX: Sometimes we get degenerate trapezoids from the tesellator; * skip these. */ if (x1 == x2 || y1 == y2) continue; boxes[box_count].p1.x = x1; boxes[box_count].p1.y = y1; boxes[box_count].p2.x = x2; boxes[box_count].p2.y = y2; box_count++; } status = _cairo_region_init_boxes (region, boxes, box_count); if (boxes != stack_boxes) free (boxes); if (status) _cairo_region_fini (region); return status; }
/** * cairo_region_create_rectangles: * @rects: an array of @count rectangles * @count: number of rectangles * * Allocates a new region object containing the union of all given @rects. * * Return value: A newly allocated #cairo_region_t. Free with * cairo_region_destroy(). This function always returns a * valid pointer; if memory cannot be allocated, then a special * error object is returned where all operations on the object do nothing. * You can check for this with cairo_region_status(). * * Since: 1.10 **/ cairo_region_t * cairo_region_create_rectangles (const cairo_rectangle_int_t *rects, int count) { pixman_box32_t stack_pboxes[CAIRO_STACK_ARRAY_LENGTH (pixman_box32_t)]; pixman_box32_t *pboxes = stack_pboxes; cairo_region_t *region; int i; region = _cairo_malloc (sizeof (cairo_region_t)); if (unlikely (region == NULL)) return _cairo_region_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); CAIRO_REFERENCE_COUNT_INIT (®ion->ref_count, 1); region->status = CAIRO_STATUS_SUCCESS; if (count == 1) { pixman_region32_init_rect (®ion->rgn, rects->x, rects->y, rects->width, rects->height); return region; } if (count > ARRAY_LENGTH (stack_pboxes)) { pboxes = _cairo_malloc_ab (count, sizeof (pixman_box32_t)); if (unlikely (pboxes == NULL)) { free (region); return _cairo_region_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } } for (i = 0; i < count; i++) { pboxes[i].x1 = rects[i].x; pboxes[i].y1 = rects[i].y; pboxes[i].x2 = rects[i].x + rects[i].width; pboxes[i].y2 = rects[i].y + rects[i].height; } i = pixman_region32_init_rects (®ion->rgn, pboxes, count); if (pboxes != stack_pboxes) free (pboxes); if (unlikely (i == 0)) { free (region); return _cairo_region_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } return region; }
static cairo_int_status_t _cairo_image_surface_fill_rectangles (void *abstract_surface, cairo_operator_t op, const cairo_color_t *color, cairo_rectangle_int_t *rects, int num_rects) { cairo_image_surface_t *surface = abstract_surface; pixman_color_t pixman_color; pixman_rectangle16_t stack_rects[CAIRO_STACK_ARRAY_LENGTH (pixman_rectangle16_t)]; pixman_rectangle16_t *pixman_rects = stack_rects; int i; cairo_int_status_t status = CAIRO_STATUS_SUCCESS; pixman_color.red = color->red_short; pixman_color.green = color->green_short; pixman_color.blue = color->blue_short; pixman_color.alpha = color->alpha_short; if (num_rects > ARRAY_LENGTH (stack_rects)) { pixman_rects = _cairo_malloc_ab (num_rects, sizeof (pixman_rectangle16_t)); if (pixman_rects == NULL) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } for (i = 0; i < num_rects; i++) { pixman_rects[i].x = rects[i].x; pixman_rects[i].y = rects[i].y; pixman_rects[i].width = rects[i].width; pixman_rects[i].height = rects[i].height; } /* XXX: pixman_fill_rectangles() should be implemented */ if (! pixman_image_fill_rectangles (_pixman_operator (op), surface->pixman_image, &pixman_color, num_rects, pixman_rects)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); } //+EAWebKitChange //11/10/2011 if (pixman_rects != stack_rects) cairo_free (pixman_rects); //-EAWebKitChange return status; }
cairo_status_t _cairo_surface_wrapper_show_text_glyphs (cairo_surface_wrapper_t *wrapper, cairo_operator_t op, const cairo_pattern_t *source, const char *utf8, int utf8_len, const cairo_glyph_t *glyphs, int num_glyphs, const cairo_text_cluster_t *clusters, int num_clusters, cairo_text_cluster_flags_t cluster_flags, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip) { cairo_status_t status; cairo_clip_t *dev_clip; cairo_glyph_t stack_glyphs [CAIRO_STACK_ARRAY_LENGTH(cairo_glyph_t)]; cairo_glyph_t *dev_glyphs = stack_glyphs; cairo_scaled_font_t *dev_scaled_font = scaled_font; cairo_pattern_union_t source_copy; cairo_font_options_t options; if (unlikely (wrapper->target->status)) return wrapper->target->status; dev_clip = _cairo_surface_wrapper_get_clip (wrapper, clip); if (_cairo_clip_is_all_clipped (dev_clip)) return CAIRO_INT_STATUS_NOTHING_TO_DO; cairo_surface_get_font_options (wrapper->target, &options); cairo_font_options_merge (&options, &scaled_font->options); if (wrapper->needs_transform) { cairo_matrix_t m; int i; _cairo_surface_wrapper_get_transform (wrapper, &m); if (! _cairo_matrix_is_translation (&wrapper->transform)) { cairo_matrix_t ctm; /* XXX No device-transform? A bug in the tangle of layers? */ _cairo_matrix_multiply (&ctm, &wrapper->transform, &scaled_font->ctm); dev_scaled_font = cairo_scaled_font_create (scaled_font->font_face, &scaled_font->font_matrix, &ctm, &options); } if (num_glyphs > ARRAY_LENGTH (stack_glyphs)) { dev_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t)); if (unlikely (dev_glyphs == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto FINISH; } } for (i = 0; i < num_glyphs; i++) { dev_glyphs[i] = glyphs[i]; cairo_matrix_transform_point (&m, &dev_glyphs[i].x, &dev_glyphs[i].y); } status = cairo_matrix_invert (&m); assert (status == CAIRO_STATUS_SUCCESS); _copy_transformed_pattern (&source_copy.base, source, &m); source = &source_copy.base; } else { if (! cairo_font_options_equal (&options, &scaled_font->options)) { dev_scaled_font = cairo_scaled_font_create (scaled_font->font_face, &scaled_font->font_matrix, &scaled_font->ctm, &options); } /* show_text_glyphs is special because _cairo_surface_show_text_glyphs is allowed * to modify the glyph array that's passed in. We must always * copy the array before handing it to the backend. */ if (num_glyphs > ARRAY_LENGTH (stack_glyphs)) { dev_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t)); if (unlikely (dev_glyphs == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto FINISH; } } memcpy (dev_glyphs, glyphs, sizeof (cairo_glyph_t) * num_glyphs); } status = _cairo_surface_show_text_glyphs (wrapper->target, op, source, utf8, utf8_len, dev_glyphs, num_glyphs, clusters, num_clusters, cluster_flags, dev_scaled_font, dev_clip); FINISH: _cairo_clip_destroy (dev_clip); if (dev_glyphs != stack_glyphs) free (dev_glyphs); if (dev_scaled_font != scaled_font) cairo_scaled_font_destroy (dev_scaled_font); return status; }
cairo_status_t _cairo_bentley_ottmann_tessellate_boxes (const cairo_boxes_t *in, cairo_fill_rule_t fill_rule, cairo_boxes_t *out) { rectangle_t stack_rectangles[CAIRO_STACK_ARRAY_LENGTH (rectangle_t)]; rectangle_t *stack_rectangles_ptrs[ARRAY_LENGTH (stack_rectangles) + 3]; rectangle_t *rectangles, **rectangles_ptrs; rectangle_t *stack_rectangles_chain[CAIRO_STACK_ARRAY_LENGTH (rectangle_t *) ]; rectangle_t **rectangles_chain = NULL; const struct _cairo_boxes_chunk *chunk; cairo_status_t status; int i, j, y_min, y_max; if (unlikely (in->num_boxes == 0)) { _cairo_boxes_clear (out); return CAIRO_STATUS_SUCCESS; } if (in->num_boxes == 1) { if (in == out) { cairo_box_t *box = &in->chunks.base[0]; if (box->p1.x > box->p2.x) { cairo_fixed_t tmp = box->p1.x; box->p1.x = box->p2.x; box->p2.x = tmp; } } else { cairo_box_t box = in->chunks.base[0]; if (box.p1.x > box.p2.x) { cairo_fixed_t tmp = box.p1.x; box.p1.x = box.p2.x; box.p2.x = tmp; } _cairo_boxes_clear (out); status = _cairo_boxes_add (out, CAIRO_ANTIALIAS_DEFAULT, &box); assert (status == CAIRO_STATUS_SUCCESS); } return CAIRO_STATUS_SUCCESS; } y_min = INT_MAX; y_max = INT_MIN; for (chunk = &in->chunks; chunk != NULL; chunk = chunk->next) { const cairo_box_t *box = chunk->base; for (i = 0; i < chunk->count; i++) { if (box[i].p1.y < y_min) y_min = box[i].p1.y; if (box[i].p1.y > y_max) y_max = box[i].p1.y; } } y_min = _cairo_fixed_integer_floor (y_min); y_max = _cairo_fixed_integer_floor (y_max) + 1; y_max -= y_min; if (y_max < in->num_boxes) { rectangles_chain = stack_rectangles_chain; if (y_max > ARRAY_LENGTH (stack_rectangles_chain)) { rectangles_chain = _cairo_malloc_ab (y_max, sizeof (rectangle_t *)); if (unlikely (rectangles_chain == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } memset (rectangles_chain, 0, y_max * sizeof (rectangle_t*)); } rectangles = stack_rectangles; rectangles_ptrs = stack_rectangles_ptrs; if (in->num_boxes > ARRAY_LENGTH (stack_rectangles)) { rectangles = _cairo_malloc_ab_plus_c (in->num_boxes, sizeof (rectangle_t) + sizeof (rectangle_t *), 3*sizeof (rectangle_t *)); if (unlikely (rectangles == NULL)) { if (rectangles_chain != stack_rectangles_chain) free (rectangles_chain); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } rectangles_ptrs = (rectangle_t **) (rectangles + in->num_boxes); } j = 0; for (chunk = &in->chunks; chunk != NULL; chunk = chunk->next) { const cairo_box_t *box = chunk->base; for (i = 0; i < chunk->count; i++) { int h; if (box[i].p1.x < box[i].p2.x) { rectangles[j].left.x = box[i].p1.x; rectangles[j].left.dir = 1; rectangles[j].right.x = box[i].p2.x; rectangles[j].right.dir = -1; } else { rectangles[j].right.x = box[i].p1.x; rectangles[j].right.dir = 1; rectangles[j].left.x = box[i].p2.x; rectangles[j].left.dir = -1; } rectangles[j].left.right = NULL; rectangles[j].right.right = NULL; rectangles[j].top = box[i].p1.y; rectangles[j].bottom = box[i].p2.y; if (rectangles_chain) { h = _cairo_fixed_integer_floor (box[i].p1.y) - y_min; rectangles[j].left.next = (edge_t *)rectangles_chain[h]; rectangles_chain[h] = &rectangles[j]; } else { rectangles_ptrs[j+2] = &rectangles[j]; } j++; } } if (rectangles_chain) { j = 2; for (y_min = 0; y_min < y_max; y_min++) { rectangle_t *r; int start = j; for (r = rectangles_chain[y_min]; r; r = (rectangle_t *)r->left.next) rectangles_ptrs[j++] = r; if (j > start + 1) _rectangle_sort (rectangles_ptrs + start, j - start); } if (rectangles_chain != stack_rectangles_chain) free (rectangles_chain); j -= 2; } else { _rectangle_sort (rectangles_ptrs + 2, j); } _cairo_boxes_clear (out); status = _cairo_bentley_ottmann_tessellate_rectangular (rectangles_ptrs+2, j, fill_rule, FALSE, out); if (rectangles != stack_rectangles) free (rectangles); return status; }
cairo_status_t _cairo_bentley_ottmann_tessellate_rectangular_traps (cairo_traps_t *traps, cairo_fill_rule_t fill_rule) { rectangle_t stack_rectangles[CAIRO_STACK_ARRAY_LENGTH (rectangle_t)]; rectangle_t *stack_rectangles_ptrs[ARRAY_LENGTH (stack_rectangles) + 3]; rectangle_t *rectangles, **rectangles_ptrs; cairo_status_t status; int i; assert (traps->is_rectangular); if (unlikely (traps->num_traps <= 1)) { if (traps->num_traps == 1) { cairo_trapezoid_t *trap = traps->traps; if (trap->left.p1.x > trap->right.p1.x) { cairo_line_t tmp = trap->left; trap->left = trap->right; trap->right = tmp; } } return CAIRO_STATUS_SUCCESS; } dump_traps (traps, "bo-rects-traps-in.txt"); rectangles = stack_rectangles; rectangles_ptrs = stack_rectangles_ptrs; if (traps->num_traps > ARRAY_LENGTH (stack_rectangles)) { rectangles = _cairo_malloc_ab_plus_c (traps->num_traps, sizeof (rectangle_t) + sizeof (rectangle_t *), 3*sizeof (rectangle_t *)); if (unlikely (rectangles == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); rectangles_ptrs = (rectangle_t **) (rectangles + traps->num_traps); } for (i = 0; i < traps->num_traps; i++) { if (traps->traps[i].left.p1.x < traps->traps[i].right.p1.x) { rectangles[i].left.x = traps->traps[i].left.p1.x; rectangles[i].left.dir = 1; rectangles[i].right.x = traps->traps[i].right.p1.x; rectangles[i].right.dir = -1; } else { rectangles[i].right.x = traps->traps[i].left.p1.x; rectangles[i].right.dir = 1; rectangles[i].left.x = traps->traps[i].right.p1.x; rectangles[i].left.dir = -1; } rectangles[i].left.right = NULL; rectangles[i].right.right = NULL; rectangles[i].top = traps->traps[i].top; rectangles[i].bottom = traps->traps[i].bottom; rectangles_ptrs[i+2] = &rectangles[i]; } /* XXX incremental sort */ _rectangle_sort (rectangles_ptrs+2, i); _cairo_traps_clear (traps); status = _cairo_bentley_ottmann_tessellate_rectangular (rectangles_ptrs+2, i, fill_rule, TRUE, traps); traps->is_rectilinear = TRUE; traps->is_rectangular = TRUE; if (rectangles != stack_rectangles) free (rectangles); dump_traps (traps, "bo-rects-traps-out.txt"); return status; }
/* This special-case filler supports only a path that describes a * device-axis aligned rectangle. It exists to avoid the overhead of * the general tessellator when drawing very common rectangles. * * If the path described anything but a device-axis aligned rectangle, * this function will abort. */ cairo_region_t * _cairo_path_fixed_fill_rectilinear_to_region (const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, const cairo_rectangle_int_t *extents) { cairo_rectangle_int_t rectangle_stack[CAIRO_STACK_ARRAY_LENGTH (cairo_rectangle_int_t)]; cairo_box_t box; cairo_region_t *region = NULL; assert (path->maybe_fill_region); assert (! path->is_empty_fill); if (_cairo_path_fixed_is_box (path, &box)) { rectangle_stack[0].x = _cairo_fixed_integer_part (box.p1.x); rectangle_stack[0].y = _cairo_fixed_integer_part (box.p1.y); rectangle_stack[0].width = _cairo_fixed_integer_part (box.p2.x) - rectangle_stack[0].x; rectangle_stack[0].height = _cairo_fixed_integer_part (box.p2.y) - rectangle_stack[0].y; if (! _cairo_rectangle_intersect (&rectangle_stack[0], extents)) region = cairo_region_create (); else region = cairo_region_create_rectangle (&rectangle_stack[0]); } else if (fill_rule == CAIRO_FILL_RULE_WINDING) { cairo_rectangle_int_t *rects = rectangle_stack; cairo_path_fixed_iter_t iter; int last_cw = -1; int size = ARRAY_LENGTH (rectangle_stack); int count = 0; /* Support a series of rectangles as can be expected to describe a * GdkRegion clip region during exposes. */ _cairo_path_fixed_iter_init (&iter, path); while (_cairo_path_fixed_iter_is_fill_box (&iter, &box)) { int cw = 0; if (box.p1.x > box.p2.x) { cairo_fixed_t t; t = box.p1.x; box.p1.x = box.p2.x; box.p2.x = t; cw = ! cw; } if (box.p1.y > box.p2.y) { cairo_fixed_t t; t = box.p1.y; box.p1.y = box.p2.y; box.p2.y = t; cw = ! cw; } if (last_cw < 0) last_cw = cw; else if (last_cw != cw) goto TESSELLATE; if (count == size) { cairo_rectangle_int_t *new_rects; size *= 4; if (rects == rectangle_stack) { new_rects = _cairo_malloc_ab (size, sizeof (cairo_rectangle_int_t)); if (unlikely (new_rects == NULL)) { /* XXX _cairo_region_nil */ break; } memcpy (new_rects, rects, sizeof (rectangle_stack)); } else { new_rects = _cairo_realloc_ab (rects, size, sizeof (cairo_rectangle_int_t)); if (unlikely (new_rects == NULL)) { /* XXX _cairo_region_nil */ break; } } rects = new_rects; } rects[count].x = _cairo_fixed_integer_part (box.p1.x); rects[count].y = _cairo_fixed_integer_part (box.p1.y); rects[count].width = _cairo_fixed_integer_part (box.p2.x) - rects[count].x; rects[count].height = _cairo_fixed_integer_part (box.p2.y) - rects[count].y; if (_cairo_rectangle_intersect (&rects[count], extents)) count++; } if (_cairo_path_fixed_iter_at_end (&iter)) region = cairo_region_create_rectangles (rects, count); TESSELLATE: if (rects != rectangle_stack) free (rects); } if (region == NULL) { /* Hmm, complex polygon */ region = _cairo_path_fixed_fill_rectilinear_tessellate_to_region (path, fill_rule, extents); } return region; }
cairo_status_t _cairo_bentley_ottmann_tessellate_rectilinear_traps (cairo_traps_t *traps, cairo_fill_rule_t fill_rule) { cairo_bo_event_t stack_events[CAIRO_STACK_ARRAY_LENGTH (cairo_bo_event_t)]; cairo_bo_event_t *events; cairo_bo_event_t *stack_event_ptrs[ARRAY_LENGTH (stack_events) + 1]; cairo_bo_event_t **event_ptrs; cairo_bo_edge_t stack_edges[ARRAY_LENGTH (stack_events)]; cairo_bo_edge_t *edges; cairo_status_t status; int i, j, k; if (unlikely (traps->num_traps == 0)) return CAIRO_STATUS_SUCCESS; assert (traps->is_rectilinear); i = 4 * traps->num_traps; events = stack_events; event_ptrs = stack_event_ptrs; edges = stack_edges; if (i > ARRAY_LENGTH (stack_events)) { events = _cairo_malloc_ab_plus_c (i, sizeof (cairo_bo_event_t) + sizeof (cairo_bo_edge_t) + sizeof (cairo_bo_event_t *), sizeof (cairo_bo_event_t *)); if (unlikely (events == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); event_ptrs = (cairo_bo_event_t **) (events + i); edges = (cairo_bo_edge_t *) (event_ptrs + i + 1); } for (i = j = k = 0; i < traps->num_traps; i++) { edges[k].edge.top = traps->traps[i].top; edges[k].edge.bottom = traps->traps[i].bottom; edges[k].edge.line = traps->traps[i].left; edges[k].edge.dir = 1; edges[k].deferred_trap.right = NULL; edges[k].prev = NULL; edges[k].next = NULL; event_ptrs[j] = &events[j]; events[j].type = CAIRO_BO_EVENT_TYPE_START; events[j].point.y = traps->traps[i].top; events[j].point.x = traps->traps[i].left.p1.x; events[j].edge = &edges[k]; j++; event_ptrs[j] = &events[j]; events[j].type = CAIRO_BO_EVENT_TYPE_STOP; events[j].point.y = traps->traps[i].bottom; events[j].point.x = traps->traps[i].left.p1.x; events[j].edge = &edges[k]; j++; k++; edges[k].edge.top = traps->traps[i].top; edges[k].edge.bottom = traps->traps[i].bottom; edges[k].edge.line = traps->traps[i].right; edges[k].edge.dir = -1; edges[k].deferred_trap.right = NULL; edges[k].prev = NULL; edges[k].next = NULL; event_ptrs[j] = &events[j]; events[j].type = CAIRO_BO_EVENT_TYPE_START; events[j].point.y = traps->traps[i].top; events[j].point.x = traps->traps[i].right.p1.x; events[j].edge = &edges[k]; j++; event_ptrs[j] = &events[j]; events[j].type = CAIRO_BO_EVENT_TYPE_STOP; events[j].point.y = traps->traps[i].bottom; events[j].point.x = traps->traps[i].right.p1.x; events[j].edge = &edges[k]; j++; k++; } _cairo_traps_clear (traps); status = _cairo_bentley_ottmann_tessellate_rectilinear (event_ptrs, j, fill_rule, TRUE, traps); traps->is_rectilinear = TRUE; if (events != stack_events) free (events); return status; }
cairo_status_t _cairo_bentley_ottmann_tessellate_rectilinear_polygon_to_boxes (const cairo_polygon_t *polygon, cairo_fill_rule_t fill_rule, cairo_boxes_t *boxes) { cairo_status_t status; cairo_bo_event_t stack_events[CAIRO_STACK_ARRAY_LENGTH (cairo_bo_event_t)]; cairo_bo_event_t *events; cairo_bo_event_t *stack_event_ptrs[ARRAY_LENGTH (stack_events) + 1]; cairo_bo_event_t **event_ptrs; cairo_bo_edge_t stack_edges[ARRAY_LENGTH (stack_events)]; cairo_bo_edge_t *edges; int num_events; int i, j; if (unlikely (polygon->num_edges == 0)) return CAIRO_STATUS_SUCCESS; num_events = 2 * polygon->num_edges; events = stack_events; event_ptrs = stack_event_ptrs; edges = stack_edges; if (num_events > ARRAY_LENGTH (stack_events)) { events = _cairo_malloc_ab_plus_c (num_events, sizeof (cairo_bo_event_t) + sizeof (cairo_bo_edge_t) + sizeof (cairo_bo_event_t *), sizeof (cairo_bo_event_t *)); if (unlikely (events == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); event_ptrs = (cairo_bo_event_t **) (events + num_events); edges = (cairo_bo_edge_t *) (event_ptrs + num_events + 1); } for (i = j = 0; i < polygon->num_edges; i++) { edges[i].edge = polygon->edges[i]; edges[i].deferred_trap.right = NULL; edges[i].prev = NULL; edges[i].next = NULL; event_ptrs[j] = &events[j]; events[j].type = CAIRO_BO_EVENT_TYPE_START; events[j].point.y = polygon->edges[i].top; events[j].point.x = polygon->edges[i].line.p1.x; events[j].edge = &edges[i]; j++; event_ptrs[j] = &events[j]; events[j].type = CAIRO_BO_EVENT_TYPE_STOP; events[j].point.y = polygon->edges[i].bottom; events[j].point.x = polygon->edges[i].line.p1.x; events[j].edge = &edges[i]; j++; } status = _cairo_bentley_ottmann_tessellate_rectilinear (event_ptrs, j, fill_rule, FALSE, boxes); if (events != stack_events) free (events); return status; }
cairo_status_t _cairo_polygon_intersect (cairo_polygon_t *a, int winding_a, cairo_polygon_t *b, int winding_b) { cairo_status_t status; cairo_bo_start_event_t stack_events[CAIRO_STACK_ARRAY_LENGTH (cairo_bo_start_event_t)]; cairo_bo_start_event_t *events; cairo_bo_event_t *stack_event_ptrs[ARRAY_LENGTH (stack_events) + 1]; cairo_bo_event_t **event_ptrs; int num_events; int i, j; /* XXX lazy */ if (winding_a != CAIRO_FILL_RULE_WINDING) { status = _cairo_polygon_reduce (a, winding_a); if (unlikely (status)) return status; } if (winding_b != CAIRO_FILL_RULE_WINDING) { status = _cairo_polygon_reduce (b, winding_b); if (unlikely (status)) return status; } if (unlikely (0 == a->num_edges)) return CAIRO_STATUS_SUCCESS; if (unlikely (0 == b->num_edges)) { a->num_edges = 0; return CAIRO_STATUS_SUCCESS; } events = stack_events; event_ptrs = stack_event_ptrs; num_events = a->num_edges + b->num_edges; if (num_events > ARRAY_LENGTH (stack_events)) { events = _cairo_malloc_ab_plus_c (num_events, sizeof (cairo_bo_start_event_t) + sizeof (cairo_bo_event_t *), sizeof (cairo_bo_event_t *)); if (unlikely (events == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); event_ptrs = (cairo_bo_event_t **) (events + num_events); } j = 0; for (i = 0; i < a->num_edges; i++) { event_ptrs[j] = (cairo_bo_event_t *) &events[j]; events[j].type = CAIRO_BO_EVENT_TYPE_START; events[j].point.y.ordinate = a->edges[i].top; events[j].point.y.approx = EXACT; events[j].point.x.ordinate = _line_compute_intersection_x_for_y (&a->edges[i].line, events[j].point.y.ordinate); events[j].point.x.approx = EXACT; events[j].edge.a_or_b = 0; events[j].edge.edge = a->edges[i]; events[j].edge.deferred.other = NULL; events[j].edge.prev = NULL; events[j].edge.next = NULL; j++; } for (i = 0; i < b->num_edges; i++) { event_ptrs[j] = (cairo_bo_event_t *) &events[j]; events[j].type = CAIRO_BO_EVENT_TYPE_START; events[j].point.y.ordinate = b->edges[i].top; events[j].point.y.approx = EXACT; events[j].point.x.ordinate = _line_compute_intersection_x_for_y (&b->edges[i].line, events[j].point.y.ordinate); events[j].point.x.approx = EXACT; events[j].edge.a_or_b = 1; events[j].edge.edge = b->edges[i]; events[j].edge.deferred.other = NULL; events[j].edge.prev = NULL; events[j].edge.next = NULL; j++; } assert (j == num_events); #if 0 { FILE *file = fopen ("clip_a.txt", "w"); _cairo_debug_print_polygon (file, a); fclose (file); } { FILE *file = fopen ("clip_b.txt", "w"); _cairo_debug_print_polygon (file, b); fclose (file); } #endif a->num_edges = 0; status = intersection_sweep (event_ptrs, num_events, a); if (events != stack_events) free (events); #if 0 { FILE *file = fopen ("clip_result.txt", "w"); _cairo_debug_print_polygon (file, a); fclose (file); } #endif return status; }
cairo_status_t _cairo_polygon_reduce (cairo_polygon_t *polygon, cairo_fill_rule_t fill_rule) { cairo_status_t status; cairo_bo_start_event_t stack_events[CAIRO_STACK_ARRAY_LENGTH (cairo_bo_start_event_t)]; cairo_bo_start_event_t *events; cairo_bo_event_t *stack_event_ptrs[ARRAY_LENGTH (stack_events) + 1]; cairo_bo_event_t **event_ptrs; int num_limits; int num_events; int i; num_events = polygon->num_edges; if (unlikely (0 == num_events)) return CAIRO_STATUS_SUCCESS; if (DEBUG_POLYGON) { FILE *file = fopen ("reduce_in.txt", "w"); _cairo_debug_print_polygon (file, polygon); fclose (file); } events = stack_events; event_ptrs = stack_event_ptrs; if (num_events > ARRAY_LENGTH (stack_events)) { events = _cairo_malloc_ab_plus_c (num_events, sizeof (cairo_bo_start_event_t) + sizeof (cairo_bo_event_t *), sizeof (cairo_bo_event_t *)); if (unlikely (events == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); event_ptrs = (cairo_bo_event_t **) (events + num_events); } for (i = 0; i < num_events; i++) { event_ptrs[i] = (cairo_bo_event_t *) &events[i]; events[i].type = CAIRO_BO_EVENT_TYPE_START; events[i].point.y = polygon->edges[i].top; events[i].point.x = _line_compute_intersection_x_for_y (&polygon->edges[i].line, events[i].point.y); events[i].edge.edge = polygon->edges[i]; events[i].edge.deferred.right = NULL; events[i].edge.prev = NULL; events[i].edge.next = NULL; } num_limits = polygon->num_limits; polygon->num_limits = 0; polygon->num_edges = 0; status = _cairo_bentley_ottmann_tessellate_bo_edges (event_ptrs, num_events, fill_rule, polygon); polygon->num_limits = num_limits; if (events != stack_events) free (events); if (DEBUG_POLYGON) { FILE *file = fopen ("reduce_out.txt", "w"); _cairo_debug_print_polygon (file, polygon); fclose (file); } return status; }
static cairo_int_status_t _cairo_image_surface_composite_trapezoids (cairo_operator_t op, cairo_pattern_t *pattern, void *abstract_dst, cairo_antialias_t antialias, int src_x, int src_y, int dst_x, int dst_y, unsigned int width, unsigned int height, cairo_trapezoid_t *traps, int num_traps) { cairo_surface_attributes_t attributes; cairo_image_surface_t *dst = abstract_dst; cairo_image_surface_t *src; cairo_int_status_t status; pixman_image_t *mask; pixman_format_code_t format; uint32_t *mask_data; pixman_trapezoid_t stack_traps[CAIRO_STACK_ARRAY_LENGTH (pixman_trapezoid_t)]; pixman_trapezoid_t *pixman_traps = stack_traps; int mask_stride; int i; if (height == 0 || width == 0) return CAIRO_STATUS_SUCCESS; /* Convert traps to pixman traps */ if (num_traps > ARRAY_LENGTH (stack_traps)) { pixman_traps = _cairo_malloc_ab (num_traps, sizeof (pixman_trapezoid_t)); if (pixman_traps == NULL) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } for (i = 0; i < num_traps; i++) { pixman_traps[i].top = _cairo_fixed_to_16_16 (traps[i].top); pixman_traps[i].bottom = _cairo_fixed_to_16_16 (traps[i].bottom); pixman_traps[i].left.p1.x = _cairo_fixed_to_16_16 (traps[i].left.p1.x); pixman_traps[i].left.p1.y = _cairo_fixed_to_16_16 (traps[i].left.p1.y); pixman_traps[i].left.p2.x = _cairo_fixed_to_16_16 (traps[i].left.p2.x); pixman_traps[i].left.p2.y = _cairo_fixed_to_16_16 (traps[i].left.p2.y); pixman_traps[i].right.p1.x = _cairo_fixed_to_16_16 (traps[i].right.p1.x); pixman_traps[i].right.p1.y = _cairo_fixed_to_16_16 (traps[i].right.p1.y); pixman_traps[i].right.p2.x = _cairo_fixed_to_16_16 (traps[i].right.p2.x); pixman_traps[i].right.p2.y = _cairo_fixed_to_16_16 (traps[i].right.p2.y); } /* Special case adding trapezoids onto a mask surface; we want to avoid * creating an intermediate temporary mask unnecessarily. * * We make the assumption here that the portion of the trapezoids * contained within the surface is bounded by [dst_x,dst_y,width,height]; * the Cairo core code passes bounds based on the trapezoid extents. * * Currently the check surface->has_clip is needed for correct * functioning, since pixman_add_trapezoids() doesn't obey the * surface clip, which is a libpixman bug , but there's no harm in * falling through to the general case when the surface is clipped * since libpixman would have to generate an intermediate mask anyways. */ if (op == CAIRO_OPERATOR_ADD && _cairo_pattern_is_opaque_solid (pattern) && dst->base.content == CAIRO_CONTENT_ALPHA && ! dst->has_clip && antialias != CAIRO_ANTIALIAS_NONE) { pixman_add_trapezoids (dst->pixman_image, 0, 0, num_traps, pixman_traps); status = CAIRO_STATUS_SUCCESS; goto finish; } status = _cairo_pattern_acquire_surface (pattern, &dst->base, src_x, src_y, width, height, (cairo_surface_t **) &src, &attributes); if (status) goto finish; status = _cairo_image_surface_set_attributes (src, &attributes); if (status) goto CLEANUP_SOURCE; switch (antialias) { case CAIRO_ANTIALIAS_NONE: format = PIXMAN_a1; mask_stride = ((width + 31) / 8) & ~0x03; break; case CAIRO_ANTIALIAS_GRAY: case CAIRO_ANTIALIAS_SUBPIXEL: case CAIRO_ANTIALIAS_DEFAULT: default: format = PIXMAN_a8; mask_stride = (width + 3) & ~3; break; } /* The image must be initially transparent */ mask_data = calloc (mask_stride, height); if (mask_data == NULL) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP_SOURCE; } mask = pixman_image_create_bits (format, width, height, mask_data, mask_stride); if (mask == NULL) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP_IMAGE_DATA; } pixman_add_trapezoids (mask, - dst_x, - dst_y, num_traps, pixman_traps); pixman_image_composite (_pixman_operator (op), src->pixman_image, mask, dst->pixman_image, src_x + attributes.x_offset, src_y + attributes.y_offset, 0, 0, dst_x, dst_y, width, height); if (! _cairo_operator_bounded_by_mask (op)) status = _cairo_surface_composite_shape_fixup_unbounded (&dst->base, &attributes, src->width, src->height, width, height, src_x, src_y, 0, 0, dst_x, dst_y, width, height); pixman_image_unref (mask); CLEANUP_IMAGE_DATA: free (mask_data); CLEANUP_SOURCE: _cairo_pattern_release_surface (pattern, &src->base, &attributes); finish: if (pixman_traps != stack_traps) free (pixman_traps); return status; }
static cairo_int_status_t _cairo_clip_path_to_region_geometric (cairo_clip_path_t *clip_path) { cairo_traps_t traps; cairo_box_t stack_boxes[CAIRO_STACK_ARRAY_LENGTH (cairo_box_t)]; cairo_box_t *boxes = stack_boxes; cairo_status_t status; int n; /* If we have nothing to intersect with this path, then it cannot * magically be reduced into a region. */ if (clip_path->prev == NULL) goto UNSUPPORTED; /* Start simple... Intersect some boxes with an arbitrary path. */ if (! clip_path->path.is_rectilinear) goto UNSUPPORTED; if (clip_path->prev->prev != NULL) goto UNSUPPORTED; _cairo_traps_init (&traps); _cairo_box_from_rectangle (&boxes[0], &clip_path->extents); _cairo_traps_limit (&traps, boxes, 1); status = _cairo_path_fixed_fill_rectilinear_to_traps (&clip_path->path, clip_path->fill_rule, &traps); if (unlikely (_cairo_status_is_error (status))) return status; if (status == CAIRO_INT_STATUS_UNSUPPORTED) goto UNSUPPORTED; if (traps.num_traps > ARRAY_LENGTH (stack_boxes)) { boxes = _cairo_malloc_ab (traps.num_traps, sizeof (cairo_box_t)); if (unlikely (boxes == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } for (n = 0; n < traps.num_traps; n++) { boxes[n].p1.x = traps.traps[n].left.p1.x; boxes[n].p1.y = traps.traps[n].top; boxes[n].p2.x = traps.traps[n].right.p1.x; boxes[n].p2.y = traps.traps[n].bottom; } _cairo_traps_clear (&traps); _cairo_traps_limit (&traps, boxes, n); status = _cairo_path_fixed_fill_to_traps (&clip_path->prev->path, clip_path->prev->fill_rule, clip_path->prev->tolerance, &traps); if (boxes != stack_boxes) free (boxes); if (unlikely (status)) return status; status = _cairo_traps_extract_region (&traps, &clip_path->region); _cairo_traps_fini (&traps); if (status == CAIRO_INT_STATUS_UNSUPPORTED) goto UNSUPPORTED; if (unlikely (status)) return status; clip_path->flags |= CAIRO_CLIP_PATH_HAS_REGION; return CAIRO_STATUS_SUCCESS; UNSUPPORTED: clip_path->flags |= CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED; return CAIRO_INT_STATUS_UNSUPPORTED; }