[cairo-commit] 22 commits - boilerplate/cairo-boilerplate.c boilerplate/cairo-boilerplate-canvas.c boilerplate/Makefile.sources boilerplate/Makefile.win32.features build/configure.ac.features build/Makefile.win32.features build/Makefile.win32.features-h configure.ac perf/micro src/cairo-backend-private.h src/cairo-bentley-ottmann.c src/cairo-bentley-ottmann-rectangular.c src/cairo-bentley-ottmann-rectilinear.c src/cairo-botor-scan-converter.c src/cairo-canvas-context.c src/cairo-canvas-device.c src/cairo-canvas.h src/cairo-canvas-private.h src/cairo-canvas-surface.c src/cairo-clip-private.h src/cairo-clip-tor-scan-converter.c src/cairo-contour.c src/cairo-contour-private.h src/cairo-debug.c src/cairo-default-context.c src/cairo-default-context-private.h src/cairo-error-private.h src/cairo-fast-image-surface.c src/cairo-ft-font.c src/cairo-gstate.c src/cairo.h src/cairo-image-surface.c src/cairo-image-surface-private.h src/cairoint.h src/cairo-mime-surface.c src/cairo-paginated-surf ace.c src/cairo-path-bounds.c src/cairo-path-fill.c src/cairo-path-fixed.c src/cairo-path-fixed-private.h src/cairo-path-in-fill.c src/cairo-path-stroke-boxes.c src/cairo-path-stroke.c src/cairo-path-stroke-polygon.c src/cairo-path-stroke-traps.c src/cairo-pattern.c src/cairo-pattern-private.h src/cairo-pdf-surface.c src/cairo-png.c src/cairo-polygon.c src/cairo-ps-surface.c src/cairo-recording-surface.c src/cairo-rectangular-scan-converter.c src/cairo-scaled-font.c src/cairo-skia.h src/cairo-slope.c src/cairo-slope-private.h src/cairo-spans-private.h src/cairo-spline.c src/cairo-stroke-dash.c src/cairo-stroke-dash-private.h src/cairo-surface.c src/cairo-surface-fallback.c src/cairo-surface-snapshot.c src/cairo-surface-subsurface.c src/cairo-svg-surface.c src/cairo-tor33-scan-converter.c src/cairo-tor-scan-converter.c src/cairo-traps.c src/cairo-traps-private.h src/cairo-type3-glyph-surface.c src/cairo-types-private.h src/cairo-xcb-private.h src/cairo-xcb-surface.c src/cairo -xcb-surface-core.c src/cairo-xcb-surface-render.c src/cairo-xlib-surface.c src/Makefile.sources src/Makefile.win32.features src/skia util/.gitignore util/Makefile.am util/show-contour.c
Chris Wilson
ickle at kemper.freedesktop.org
Wed Aug 10 02:35:47 PDT 2011
boilerplate/Makefile.sources | 1
boilerplate/Makefile.win32.features | 22
boilerplate/cairo-boilerplate-canvas.c | 172 +
boilerplate/cairo-boilerplate.c | 92
build/Makefile.win32.features | 1
build/Makefile.win32.features-h | 4
build/configure.ac.features | 2
configure.ac | 4
perf/micro/many-strokes.c | 2
src/Makefile.sources | 26
src/Makefile.win32.features | 26
src/cairo-backend-private.h | 2
src/cairo-bentley-ottmann-rectangular.c | 1
src/cairo-bentley-ottmann-rectilinear.c | 1
src/cairo-bentley-ottmann.c | 1
src/cairo-botor-scan-converter.c | 3
src/cairo-canvas-context.c | 1296 +++++++++++
src/cairo-canvas-device.c | 245 ++
src/cairo-canvas-private.h | 102
src/cairo-canvas-surface.c | 187 +
src/cairo-canvas.h | 69
src/cairo-clip-private.h | 1
src/cairo-clip-tor-scan-converter.c | 1842 ++++++++++++++++
src/cairo-contour-private.h | 158 +
src/cairo-contour.c | 455 ++++
src/cairo-debug.c | 6
src/cairo-default-context-private.h | 7
src/cairo-default-context.c | 37
src/cairo-error-private.h | 8
src/cairo-fast-image-surface.c | 3626 ++++++++++++++++++++++++++++++++
src/cairo-ft-font.c | 1
src/cairo-gstate.c | 1
src/cairo-image-surface-private.h | 84
src/cairo-image-surface.c | 1244 ++++------
src/cairo-mime-surface.c | 348 +++
src/cairo-paginated-surface.c | 1
src/cairo-path-bounds.c | 2
src/cairo-path-fill.c | 1
src/cairo-path-fixed-private.h | 3
src/cairo-path-fixed.c | 16
src/cairo-path-in-fill.c | 2
src/cairo-path-stroke-boxes.c | 776 ++++++
src/cairo-path-stroke-polygon.c | 1351 +++++++++++
src/cairo-path-stroke-traps.c | 1732 +++++++++++++++
src/cairo-path-stroke.c | 865 -------
src/cairo-pattern-private.h | 5
src/cairo-pattern.c | 29
src/cairo-pdf-surface.c | 1
src/cairo-png.c | 1
src/cairo-polygon.c | 48
src/cairo-ps-surface.c | 1
src/cairo-recording-surface.c | 2
src/cairo-rectangular-scan-converter.c | 49
src/cairo-scaled-font.c | 1
src/cairo-skia.h | 18
src/cairo-slope-private.h | 10
src/cairo-slope.c | 27
src/cairo-spans-private.h | 37
src/cairo-spline.c | 25
src/cairo-stroke-dash-private.h | 65
src/cairo-stroke-dash.c | 93
src/cairo-surface-fallback.c | 4
src/cairo-surface-snapshot.c | 1
src/cairo-surface-subsurface.c | 1
src/cairo-surface.c | 1
src/cairo-svg-surface.c | 1
src/cairo-tor-scan-converter.c | 75
src/cairo-tor33-scan-converter.c | 2035 +++++++++++++++++
src/cairo-traps-private.h | 122 +
src/cairo-traps.c | 250 ++
src/cairo-type3-glyph-surface.c | 1
src/cairo-types-private.h | 17
src/cairo-xcb-private.h | 2
src/cairo-xcb-surface-core.c | 1
src/cairo-xcb-surface-render.c | 187 +
src/cairo-xcb-surface.c | 143 -
src/cairo-xlib-surface.c | 1
src/cairo.h | 105
src/cairoint.h | 110
src/skia/cairo-skia-context.cpp | 1711 +++++++++++++++
src/skia/cairo-skia-private.h | 110
src/skia/cairo-skia-surface.cpp | 505 ++++
util/.gitignore | 1
util/Makefile.am | 7
util/show-contour.c | 660 +++++
85 files changed, 19333 insertions(+), 1956 deletions(-)
New commits:
commit ae530627977342a3ce49e6c4b26d75bf2f59a581
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date: Tue Aug 9 22:21:33 2011 +0100
tor: update is-vertical along with min-height
Similar to the minimum height property, is-vertical can only change
after an insertion or deletion event. So we only need to update the
flags after one of those events, so simply update it along side
min-height.
Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>
diff --git a/src/cairo-tor-scan-converter.c b/src/cairo-tor-scan-converter.c
index 3f709ee..e49aca4 100644
--- a/src/cairo-tor-scan-converter.c
+++ b/src/cairo-tor-scan-converter.c
@@ -483,6 +483,7 @@ struct active_list {
* scan conversion by a full pixel row if an edge ends somewhere
* within it. */
grid_scaled_y_t min_height;
+ int is_vertical;
};
struct glitter_scan_converter {
@@ -1110,6 +1111,7 @@ active_list_reset (struct active_list *active)
active->tail.height_left = INT_MAX;
active->tail.vertical = 1;
active->min_height = 0;
+ active->is_vertical = 1;
}
static void
@@ -1251,14 +1253,17 @@ active_list_can_step_full_row (struct active_list *active)
* list if we have been dropping edges. */
if (active->min_height <= 0) {
int min_height = INT_MAX;
+ int is_vertical = 1;
e = active->head.next;
while (NULL != e) {
if (e->height_left < min_height)
min_height = e->height_left;
+ is_vertical &= e->vertical;
e = e->next;
}
+ active->is_vertical = is_vertical;
active->min_height = min_height;
}
@@ -1301,6 +1306,7 @@ polygon_fill_buckets (struct active_list *active,
struct edge **buckets)
{
grid_scaled_y_t min_height = active->min_height;
+ int is_vertical = active->is_vertical;
while (edge) {
struct edge *next = edge->next;
@@ -1312,9 +1318,11 @@ polygon_fill_buckets (struct active_list *active,
buckets[suby] = edge;
if (edge->height_left < min_height)
min_height = edge->height_left;
+ is_vertical &= edge->vertical;
edge = next;
}
+ active->is_vertical = is_vertical;
active->min_height = min_height;
}
@@ -1642,19 +1650,6 @@ glitter_scan_converter_add_edge (glitter_scan_converter_t *converter,
polygon_add_edge (converter->polygon, &e);
}
-static cairo_bool_t
-active_list_is_vertical (struct active_list *active)
-{
- struct edge *e;
-
- for (e = active->head.next; e != &active->tail; e = e->next) {
- if (! e->vertical)
- return FALSE;
- }
-
- return TRUE;
-}
-
static void
step_edges (struct active_list *active, int count)
{
@@ -1702,6 +1697,7 @@ glitter_scan_converter_render(
if (! polygon->y_buckets[i]) {
if (active->head.next == &active->tail) {
active->min_height = INT_MAX;
+ active->is_vertical = 1;
for (; j < h && ! polygon->y_buckets[j]; j++)
;
continue;
@@ -1717,7 +1713,7 @@ glitter_scan_converter_render(
else
apply_evenodd_fill_rule_and_step_edges (active, coverages);
- if (active_list_is_vertical (active)) {
+ if (active->is_vertical) {
while (j < h &&
polygon->y_buckets[j] == NULL &&
active->min_height >= 2*GRID_Y)
commit 49db2eb10449a5277c1def96f7efbf9738527bb2
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date: Mon Aug 1 19:31:43 2011 +0100
xcb: stroke-to-traps
The xserver will render to an intermediate mask and should be correct
with respect to self-intersections...
Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>
diff --git a/src/Makefile.sources b/src/Makefile.sources
index 2c505fc..16b27c0 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -167,6 +167,7 @@ cairo_sources = \
cairo-path-stroke.c \
cairo-path-stroke-boxes.c \
cairo-path-stroke-polygon.c \
+ cairo-path-stroke-traps.c \
cairo-pattern.c \
cairo-pen.c \
cairo-polygon.c \
@@ -196,6 +197,7 @@ cairo_sources = \
cairo-clip-tor-scan-converter.c \
cairo-toy-font-face.c \
cairo-traps.c \
+ cairo-traps-private.h \
cairo-unicode.c \
cairo-user-font.c \
cairo-version.c \
diff --git a/src/cairo-bentley-ottmann-rectangular.c b/src/cairo-bentley-ottmann-rectangular.c
index 7baec13..33c3a48 100644
--- a/src/cairo-bentley-ottmann-rectangular.c
+++ b/src/cairo-bentley-ottmann-rectangular.c
@@ -42,6 +42,7 @@
#include "cairo-error-private.h"
#include "cairo-combsort-private.h"
#include "cairo-list-private.h"
+#include "cairo-traps-private.h"
#include <setjmp.h>
diff --git a/src/cairo-bentley-ottmann-rectilinear.c b/src/cairo-bentley-ottmann-rectilinear.c
index a3eb490..28aee32 100644
--- a/src/cairo-bentley-ottmann-rectilinear.c
+++ b/src/cairo-bentley-ottmann-rectilinear.c
@@ -41,6 +41,7 @@
#include "cairo-boxes-private.h"
#include "cairo-combsort-private.h"
#include "cairo-error-private.h"
+#include "cairo-traps-private.h"
typedef struct _cairo_bo_edge cairo_bo_edge_t;
typedef struct _cairo_bo_trap cairo_bo_trap_t;
diff --git a/src/cairo-bentley-ottmann.c b/src/cairo-bentley-ottmann.c
index d7b017a..47fcc90 100644
--- a/src/cairo-bentley-ottmann.c
+++ b/src/cairo-bentley-ottmann.c
@@ -41,6 +41,7 @@
#include "cairo-error-private.h"
#include "cairo-freelist-private.h"
#include "cairo-combsort-private.h"
+#include "cairo-traps-private.h"
#define DEBUG_PRINT_STATE 0
#define DEBUG_EVENTS 0
diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c
index 1d59fc5..9036d3a 100644
--- a/src/cairo-gstate.c
+++ b/src/cairo-gstate.c
@@ -41,6 +41,7 @@
#include "cairo-error-private.h"
#include "cairo-gstate-private.h"
#include "cairo-pattern-private.h"
+#include "cairo-traps-private.h"
#if _XOPEN_SOURCE >= 600 || defined (_ISOC99_SOURCE)
#define ISFINITE(x) isfinite (x)
diff --git a/src/cairo-image-surface.c b/src/cairo-image-surface.c
index 9ba0d7b..c9061f2 100644
--- a/src/cairo-image-surface.c
+++ b/src/cairo-image-surface.c
@@ -52,6 +52,7 @@
#include "cairo-scaled-font-private.h"
#include "cairo-surface-snapshot-private.h"
#include "cairo-surface-subsurface-private.h"
+#include "cairo-traps-private.h"
/* Limit on the width / height of an image surface in pixels. This is
* mainly determined by coordinates of things sent to pixman at the
diff --git a/src/cairo-path-bounds.c b/src/cairo-path-bounds.c
index 1fc9a06..f336098 100644
--- a/src/cairo-path-bounds.c
+++ b/src/cairo-path-bounds.c
@@ -39,7 +39,7 @@
#include "cairo-box-private.h"
#include "cairo-error-private.h"
#include "cairo-path-fixed-private.h"
-
+#include "cairo-traps-private.h"
typedef struct _cairo_path_bounder {
cairo_point_t current_point;
diff --git a/src/cairo-path-fill.c b/src/cairo-path-fill.c
index b15b1a4..e065a01 100644
--- a/src/cairo-path-fill.c
+++ b/src/cairo-path-fill.c
@@ -40,6 +40,7 @@
#include "cairo-error-private.h"
#include "cairo-path-fixed-private.h"
#include "cairo-region-private.h"
+#include "cairo-traps-private.h"
typedef struct cairo_filler {
cairo_polygon_t *polygon;
diff --git a/src/cairo-path-fixed.c b/src/cairo-path-fixed.c
index 8d661f4..0a901a4 100644
--- a/src/cairo-path-fixed.c
+++ b/src/cairo-path-fixed.c
@@ -42,6 +42,7 @@
#include "cairo-error-private.h"
#include "cairo-path-fixed-private.h"
#include "cairo-slope-private.h"
+#include "cairo-traps-private.h"
static cairo_status_t
_cairo_path_fixed_add (cairo_path_fixed_t *path,
diff --git a/src/cairo-path-stroke-traps.c b/src/cairo-path-stroke-traps.c
new file mode 100644
index 0000000..87f92b0
--- /dev/null
+++ b/src/cairo-path-stroke-traps.c
@@ -0,0 +1,1732 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth at cworth.org>
+ */
+
+#include "cairoint.h"
+#include "cairo-box-private.h"
+#include "cairo-error-private.h"
+#include "cairo-path-fixed-private.h"
+#include "cairo-slope-private.h"
+#include "cairo-traps-private.h"
+
+typedef struct _cairo_stroker_dash {
+ cairo_bool_t dashed;
+ unsigned int dash_index;
+ cairo_bool_t dash_on;
+ cairo_bool_t dash_starts_on;
+ double dash_remain;
+
+ double dash_offset;
+ const double *dashes;
+ unsigned int num_dashes;
+} cairo_stroker_dash_t;
+
+typedef struct cairo_stroker {
+ cairo_stroke_style_t style;
+
+ const cairo_matrix_t *ctm;
+ const cairo_matrix_t *ctm_inverse;
+ double tolerance;
+ double ctm_determinant;
+ cairo_bool_t ctm_det_positive;
+
+ cairo_traps_t *traps;
+
+ cairo_pen_t pen;
+
+ cairo_point_t current_point;
+ cairo_point_t first_point;
+
+ cairo_bool_t has_initial_sub_path;
+
+ cairo_bool_t has_current_face;
+ cairo_stroke_face_t current_face;
+
+ cairo_bool_t has_first_face;
+ cairo_stroke_face_t first_face;
+
+ cairo_stroker_dash_t dash;
+
+ cairo_bool_t has_bounds;
+ cairo_box_t bounds;
+} cairo_stroker_t;
+
+static void
+_cairo_stroker_dash_start (cairo_stroker_dash_t *dash)
+{
+ double offset;
+ cairo_bool_t on = TRUE;
+ unsigned int i = 0;
+
+ if (! dash->dashed)
+ return;
+
+ offset = dash->dash_offset;
+
+ /* We stop searching for a starting point as soon as the
+ offset reaches zero. Otherwise when an initial dash
+ segment shrinks to zero it will be skipped over. */
+ while (offset > 0.0 && offset >= dash->dashes[i]) {
+ offset -= dash->dashes[i];
+ on = !on;
+ if (++i == dash->num_dashes)
+ i = 0;
+ }
+
+ dash->dash_index = i;
+ dash->dash_on = dash->dash_starts_on = on;
+ dash->dash_remain = dash->dashes[i] - offset;
+}
+
+static void
+_cairo_stroker_dash_step (cairo_stroker_dash_t *dash, double step)
+{
+ dash->dash_remain -= step;
+ if (dash->dash_remain <= 0.) {
+ if (++dash->dash_index == dash->num_dashes)
+ dash->dash_index = 0;
+
+ dash->dash_on = ! dash->dash_on;
+ dash->dash_remain = dash->dashes[dash->dash_index];
+ }
+}
+
+static void
+_cairo_stroker_dash_init (cairo_stroker_dash_t *dash,
+ const cairo_stroke_style_t *style)
+{
+ dash->dashed = style->dash != NULL;
+ if (! dash->dashed)
+ return;
+
+ dash->dashes = style->dash;
+ dash->num_dashes = style->num_dashes;
+ dash->dash_offset = style->dash_offset;
+
+ _cairo_stroker_dash_start (dash);
+}
+
+static cairo_status_t
+_cairo_stroker_init (cairo_stroker_t *stroker,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_traps_t *traps)
+{
+ cairo_status_t status;
+
+ stroker->style = *stroke_style;
+ stroker->ctm = ctm;
+ stroker->ctm_inverse = ctm_inverse;
+ stroker->tolerance = tolerance;
+ stroker->traps = traps;
+
+ _cairo_stroker_dash_init (&stroker->dash, stroke_style);
+
+ stroker->ctm_determinant = _cairo_matrix_compute_determinant (stroker->ctm);
+ stroker->ctm_det_positive = stroker->ctm_determinant >= 0.0;
+
+ status = _cairo_pen_init (&stroker->pen,
+ stroke_style->line_width / 2.0,
+ tolerance, ctm);
+ if (status)
+ return status;
+
+ stroker->has_current_face = FALSE;
+ stroker->has_first_face = FALSE;
+ stroker->has_initial_sub_path = FALSE;
+
+ stroker->has_bounds = traps->num_limits;
+ if (stroker->has_bounds) {
+ /* Extend the bounds in each direction to account for the maximum area
+ * we might generate trapezoids, to capture line segments that are outside
+ * of the bounds but which might generate rendering that's within bounds.
+ */
+ double dx, dy;
+ cairo_fixed_t fdx, fdy;
+
+ _cairo_stroke_style_max_distance_from_path (&stroker->style,
+ stroker->ctm,
+ &dx, &dy);
+
+ fdx = _cairo_fixed_from_double (dx);
+ fdy = _cairo_fixed_from_double (dy);
+
+ stroker->bounds.p1.x -= fdx;
+ stroker->bounds.p2.x += fdx;
+
+ stroker->bounds.p1.y -= fdy;
+ stroker->bounds.p2.y += fdy;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_stroker_fini (cairo_stroker_t *stroker)
+{
+ _cairo_pen_fini (&stroker->pen);
+}
+
+static void
+_translate_point (cairo_point_t *point, cairo_point_t *offset)
+{
+ point->x += offset->x;
+ point->y += offset->y;
+}
+
+static int
+_cairo_stroker_face_clockwise (cairo_stroke_face_t *in, cairo_stroke_face_t *out)
+{
+ cairo_slope_t in_slope, out_slope;
+
+ _cairo_slope_init (&in_slope, &in->point, &in->cw);
+ _cairo_slope_init (&out_slope, &out->point, &out->cw);
+
+ return _cairo_slope_compare (&in_slope, &out_slope) < 0;
+}
+
+/**
+ * _cairo_slope_compare_sgn
+ *
+ * Return -1, 0 or 1 depending on the relative slopes of
+ * two lines.
+ */
+static int
+_cairo_slope_compare_sgn (double dx1, double dy1, double dx2, double dy2)
+{
+ double c = (dx1 * dy2 - dx2 * dy1);
+
+ if (c > 0) return 1;
+ if (c < 0) return -1;
+ return 0;
+}
+
+static cairo_status_t
+_cairo_stroker_join (cairo_stroker_t *stroker, cairo_stroke_face_t *in, cairo_stroke_face_t *out)
+{
+ int clockwise = _cairo_stroker_face_clockwise (out, in);
+ cairo_point_t *inpt, *outpt;
+ cairo_status_t status;
+
+ if (in->cw.x == out->cw.x
+ && in->cw.y == out->cw.y
+ && in->ccw.x == out->ccw.x
+ && in->ccw.y == out->ccw.y)
+ {
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (clockwise) {
+ inpt = &in->ccw;
+ outpt = &out->ccw;
+ } else {
+ inpt = &in->cw;
+ outpt = &out->cw;
+ }
+
+ switch (stroker->style.line_join) {
+ case CAIRO_LINE_JOIN_ROUND: {
+ int i;
+ int start, step, stop;
+ cairo_point_t tri[3];
+ cairo_pen_t *pen = &stroker->pen;
+
+ tri[0] = in->point;
+ if (clockwise) {
+ start = _cairo_pen_find_active_ccw_vertex_index (pen, &in->dev_vector);
+ stop = _cairo_pen_find_active_ccw_vertex_index (pen, &out->dev_vector);
+ step = -1;
+ } else {
+ start = _cairo_pen_find_active_cw_vertex_index (pen, &in->dev_vector);
+ stop = _cairo_pen_find_active_cw_vertex_index (pen, &out->dev_vector);
+ step = +1;
+ }
+
+ i = start;
+ tri[1] = *inpt;
+ while (i != stop) {
+ tri[2] = in->point;
+ _translate_point (&tri[2], &pen->vertices[i].point);
+ status = _cairo_traps_tessellate_triangle (stroker->traps, tri);
+ if (status)
+ return status;
+ tri[1] = tri[2];
+ i += step;
+ if (i < 0)
+ i = pen->num_vertices - 1;
+ if (i >= pen->num_vertices)
+ i = 0;
+ }
+
+ tri[2] = *outpt;
+
+ return _cairo_traps_tessellate_triangle (stroker->traps, tri);
+ }
+ case CAIRO_LINE_JOIN_MITER:
+ default: {
+ /* dot product of incoming slope vector with outgoing slope vector */
+ double in_dot_out = ((-in->usr_vector.x * out->usr_vector.x)+
+ (-in->usr_vector.y * out->usr_vector.y));
+ double ml = stroker->style.miter_limit;
+
+ /* Check the miter limit -- lines meeting at an acute angle
+ * can generate long miters, the limit converts them to bevel
+ *
+ * Consider the miter join formed when two line segments
+ * meet at an angle psi:
+ *
+ * /.\
+ * /. .\
+ * /./ \.\
+ * /./psi\.\
+ *
+ * We can zoom in on the right half of that to see:
+ *
+ * |\
+ * | \ psi/2
+ * | \
+ * | \
+ * | \
+ * | \
+ * miter \
+ * length \
+ * | \
+ * | .\
+ * | . \
+ * |. line \
+ * \ width \
+ * \ \
+ *
+ *
+ * The right triangle in that figure, (the line-width side is
+ * shown faintly with three '.' characters), gives us the
+ * following expression relating miter length, angle and line
+ * width:
+ *
+ * 1 /sin (psi/2) = miter_length / line_width
+ *
+ * The right-hand side of this relationship is the same ratio
+ * in which the miter limit (ml) is expressed. We want to know
+ * when the miter length is within the miter limit. That is
+ * when the following condition holds:
+ *
+ * 1/sin(psi/2) <= ml
+ * 1 <= ml sin(psi/2)
+ * 1 <= ml² sin²(psi/2)
+ * 2 <= ml² 2 sin²(psi/2)
+ * 2·sin²(psi/2) = 1-cos(psi)
+ * 2 <= ml² (1-cos(psi))
+ *
+ * in · out = |in| |out| cos (psi)
+ *
+ * in and out are both unit vectors, so:
+ *
+ * in · out = cos (psi)
+ *
+ * 2 <= ml² (1 - in · out)
+ *
+ */
+ if (2 <= ml * ml * (1 - in_dot_out))
+ {
+ double x1, y1, x2, y2;
+ double mx, my;
+ double dx1, dx2, dy1, dy2;
+ cairo_point_t outer;
+ cairo_point_t quad[4];
+ double ix, iy;
+ double fdx1, fdy1, fdx2, fdy2;
+ double mdx, mdy;
+
+ /*
+ * we've got the points already transformed to device
+ * space, but need to do some computation with them and
+ * also need to transform the slope from user space to
+ * device space
+ */
+ /* outer point of incoming line face */
+ x1 = _cairo_fixed_to_double (inpt->x);
+ y1 = _cairo_fixed_to_double (inpt->y);
+ dx1 = in->usr_vector.x;
+ dy1 = in->usr_vector.y;
+ cairo_matrix_transform_distance (stroker->ctm, &dx1, &dy1);
+
+ /* outer point of outgoing line face */
+ x2 = _cairo_fixed_to_double (outpt->x);
+ y2 = _cairo_fixed_to_double (outpt->y);
+ dx2 = out->usr_vector.x;
+ dy2 = out->usr_vector.y;
+ cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2);
+
+ /*
+ * Compute the location of the outer corner of the miter.
+ * That's pretty easy -- just the intersection of the two
+ * outer edges. We've got slopes and points on each
+ * of those edges. Compute my directly, then compute
+ * mx by using the edge with the larger dy; that avoids
+ * dividing by values close to zero.
+ */
+ my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) /
+ (dx1 * dy2 - dx2 * dy1));
+ if (fabs (dy1) >= fabs (dy2))
+ mx = (my - y1) * dx1 / dy1 + x1;
+ else
+ mx = (my - y2) * dx2 / dy2 + x2;
+
+ /*
+ * When the two outer edges are nearly parallel, slight
+ * perturbations in the position of the outer points of the lines
+ * caused by representing them in fixed point form can cause the
+ * intersection point of the miter to move a large amount. If
+ * that moves the miter intersection from between the two faces,
+ * then draw a bevel instead.
+ */
+
+ ix = _cairo_fixed_to_double (in->point.x);
+ iy = _cairo_fixed_to_double (in->point.y);
+
+ /* slope of one face */
+ fdx1 = x1 - ix; fdy1 = y1 - iy;
+
+ /* slope of the other face */
+ fdx2 = x2 - ix; fdy2 = y2 - iy;
+
+ /* slope from the intersection to the miter point */
+ mdx = mx - ix; mdy = my - iy;
+
+ /*
+ * Make sure the miter point line lies between the two
+ * faces by comparing the slopes
+ */
+ if (_cairo_slope_compare_sgn (fdx1, fdy1, mdx, mdy) !=
+ _cairo_slope_compare_sgn (fdx2, fdy2, mdx, mdy))
+ {
+ /*
+ * Draw the quadrilateral
+ */
+ outer.x = _cairo_fixed_from_double (mx);
+ outer.y = _cairo_fixed_from_double (my);
+
+ quad[0] = in->point;
+ quad[1] = *inpt;
+ quad[2] = outer;
+ quad[3] = *outpt;
+
+ return _cairo_traps_tessellate_convex_quad (stroker->traps, quad);
+ }
+ }
+ /* fall through ... */
+ }
+ case CAIRO_LINE_JOIN_BEVEL: {
+ cairo_point_t tri[3];
+ tri[0] = in->point;
+ tri[1] = *inpt;
+ tri[2] = *outpt;
+
+ return _cairo_traps_tessellate_triangle (stroker->traps, tri);
+ }
+ }
+}
+
+static cairo_status_t
+_cairo_stroker_add_cap (cairo_stroker_t *stroker, cairo_stroke_face_t *f)
+{
+ cairo_status_t status;
+
+ if (stroker->style.line_cap == CAIRO_LINE_CAP_BUTT)
+ return CAIRO_STATUS_SUCCESS;
+
+ switch (stroker->style.line_cap) {
+ case CAIRO_LINE_CAP_ROUND: {
+ int i;
+ int start, stop;
+ cairo_slope_t slope;
+ cairo_point_t tri[3];
+ cairo_pen_t *pen = &stroker->pen;
+
+ slope = f->dev_vector;
+ start = _cairo_pen_find_active_cw_vertex_index (pen, &slope);
+ slope.dx = -slope.dx;
+ slope.dy = -slope.dy;
+ stop = _cairo_pen_find_active_cw_vertex_index (pen, &slope);
+
+ tri[0] = f->point;
+ tri[1] = f->cw;
+ for (i=start; i != stop; i = (i+1) % pen->num_vertices) {
+ tri[2] = f->point;
+ _translate_point (&tri[2], &pen->vertices[i].point);
+ status = _cairo_traps_tessellate_triangle (stroker->traps, tri);
+ if (status)
+ return status;
+ tri[1] = tri[2];
+ }
+ tri[2] = f->ccw;
+
+ return _cairo_traps_tessellate_triangle (stroker->traps, tri);
+ }
+ case CAIRO_LINE_CAP_SQUARE: {
+ double dx, dy;
+ cairo_slope_t fvector;
+ cairo_point_t q[4];
+
+ dx = f->usr_vector.x;
+ dy = f->usr_vector.y;
+ dx *= stroker->style.line_width / 2.0;
+ dy *= stroker->style.line_width / 2.0;
+ cairo_matrix_transform_distance (stroker->ctm, &dx, &dy);
+ fvector.dx = _cairo_fixed_from_double (dx);
+ fvector.dy = _cairo_fixed_from_double (dy);
+ q[0] = f->cw;
+ q[1].x = f->cw.x + fvector.dx;
+ q[1].y = f->cw.y + fvector.dy;
+ q[2].x = f->ccw.x + fvector.dx;
+ q[2].y = f->ccw.y + fvector.dy;
+ q[3] = f->ccw;
+
+ return _cairo_traps_tessellate_convex_quad (stroker->traps, q);
+ }
+ case CAIRO_LINE_CAP_BUTT:
+ default:
+ return CAIRO_STATUS_SUCCESS;
+ }
+}
+
+static cairo_status_t
+_cairo_stroker_add_leading_cap (cairo_stroker_t *stroker,
+ cairo_stroke_face_t *face)
+{
+ cairo_stroke_face_t reversed;
+ cairo_point_t t;
+
+ reversed = *face;
+
+ /* The initial cap needs an outward facing vector. Reverse everything */
+ reversed.usr_vector.x = -reversed.usr_vector.x;
+ reversed.usr_vector.y = -reversed.usr_vector.y;
+ reversed.dev_vector.dx = -reversed.dev_vector.dx;
+ reversed.dev_vector.dy = -reversed.dev_vector.dy;
+ t = reversed.cw;
+ reversed.cw = reversed.ccw;
+ reversed.ccw = t;
+
+ return _cairo_stroker_add_cap (stroker, &reversed);
+}
+
+static cairo_status_t
+_cairo_stroker_add_trailing_cap (cairo_stroker_t *stroker,
+ cairo_stroke_face_t *face)
+{
+ return _cairo_stroker_add_cap (stroker, face);
+}
+
+static inline cairo_bool_t
+_compute_normalized_device_slope (double *dx, double *dy,
+ const cairo_matrix_t *ctm_inverse,
+ double *mag_out)
+{
+ double dx0 = *dx, dy0 = *dy;
+ double mag;
+
+ cairo_matrix_transform_distance (ctm_inverse, &dx0, &dy0);
+
+ if (dx0 == 0.0 && dy0 == 0.0) {
+ if (mag_out)
+ *mag_out = 0.0;
+ return FALSE;
+ }
+
+ if (dx0 == 0.0) {
+ *dx = 0.0;
+ if (dy0 > 0.0) {
+ mag = dy0;
+ *dy = 1.0;
+ } else {
+ mag = -dy0;
+ *dy = -1.0;
+ }
+ } else if (dy0 == 0.0) {
+ *dy = 0.0;
+ if (dx0 > 0.0) {
+ mag = dx0;
+ *dx = 1.0;
+ } else {
+ mag = -dx0;
+ *dx = -1.0;
+ }
+ } else {
+ mag = sqrt (dx0 * dx0 + dy0 * dy0);
+ *dx = dx0 / mag;
+ *dy = dy0 / mag;
+ }
+
+ if (mag_out)
+ *mag_out = mag;
+
+ return TRUE;
+}
+
+static void
+_compute_face (const cairo_point_t *point,
+ cairo_slope_t *dev_slope,
+ double slope_dx, double slope_dy,
+ cairo_stroker_t *stroker, cairo_stroke_face_t *face);
+
+static cairo_status_t
+_cairo_stroker_add_caps (cairo_stroker_t *stroker)
+{
+ cairo_status_t status;
+ /* check for a degenerative sub_path */
+ if (stroker->has_initial_sub_path
+ && !stroker->has_first_face
+ && !stroker->has_current_face
+ && stroker->style.line_cap == CAIRO_LINE_CAP_ROUND)
+ {
+ /* pick an arbitrary slope to use */
+ double dx = 1.0, dy = 0.0;
+ cairo_slope_t slope = { CAIRO_FIXED_ONE, 0 };
+ cairo_stroke_face_t face;
+
+ _compute_normalized_device_slope (&dx, &dy, stroker->ctm_inverse, NULL);
+
+ /* arbitrarily choose first_point
+ * first_point and current_point should be the same */
+ _compute_face (&stroker->first_point, &slope, dx, dy, stroker, &face);
+
+ status = _cairo_stroker_add_leading_cap (stroker, &face);
+ if (status)
+ return status;
+ status = _cairo_stroker_add_trailing_cap (stroker, &face);
+ if (status)
+ return status;
+ }
+
+ if (stroker->has_first_face) {
+ status = _cairo_stroker_add_leading_cap (stroker, &stroker->first_face);
+ if (status)
+ return status;
+ }
+
+ if (stroker->has_current_face) {
+ status = _cairo_stroker_add_trailing_cap (stroker, &stroker->current_face);
+ if (status)
+ return status;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_compute_face (const cairo_point_t *point,
+ cairo_slope_t *dev_slope,
+ double slope_dx, double slope_dy,
+ cairo_stroker_t *stroker, cairo_stroke_face_t *face)
+{
+ double face_dx, face_dy;
+ cairo_point_t offset_ccw, offset_cw;
+
+ /*
+ * rotate to get a line_width/2 vector along the face, note that
+ * the vector must be rotated the right direction in device space,
+ * but by 90° in user space. So, the rotation depends on
+ * whether the ctm reflects or not, and that can be determined
+ * by looking at the determinant of the matrix.
+ */
+ if (stroker->ctm_det_positive)
+ {
+ face_dx = - slope_dy * (stroker->style.line_width / 2.0);
+ face_dy = slope_dx * (stroker->style.line_width / 2.0);
+ }
+ else
+ {
+ face_dx = slope_dy * (stroker->style.line_width / 2.0);
+ face_dy = - slope_dx * (stroker->style.line_width / 2.0);
+ }
+
+ /* back to device space */
+ cairo_matrix_transform_distance (stroker->ctm, &face_dx, &face_dy);
+
+ offset_ccw.x = _cairo_fixed_from_double (face_dx);
+ offset_ccw.y = _cairo_fixed_from_double (face_dy);
+ offset_cw.x = -offset_ccw.x;
+ offset_cw.y = -offset_ccw.y;
+
+ face->ccw = *point;
+ _translate_point (&face->ccw, &offset_ccw);
+
+ face->point = *point;
+
+ face->cw = *point;
+ _translate_point (&face->cw, &offset_cw);
+
+ face->usr_vector.x = slope_dx;
+ face->usr_vector.y = slope_dy;
+
+ face->dev_vector = *dev_slope;
+}
+
+static cairo_status_t
+_cairo_stroker_add_sub_edge (cairo_stroker_t *stroker,
+ const cairo_point_t *p1,
+ const cairo_point_t *p2,
+ cairo_slope_t *dev_slope, double slope_dx, double slope_dy,
+ cairo_stroke_face_t *start, cairo_stroke_face_t *end)
+{
+ cairo_point_t rectangle[4];
+
+ _compute_face (p1, dev_slope, slope_dx, slope_dy, stroker, start);
+
+ /* XXX: This could be optimized slightly by not calling
+ _compute_face again but rather translating the relevant
+ fields from start. */
+ _compute_face (p2, dev_slope, slope_dx, slope_dy, stroker, end);
+
+ if (p1->x == p2->x && p1->y == p2->y)
+ return CAIRO_STATUS_SUCCESS;
+
+ rectangle[0] = start->cw;
+ rectangle[1] = start->ccw;
+ rectangle[2] = end->ccw;
+ rectangle[3] = end->cw;
+
+ return _cairo_traps_tessellate_convex_quad (stroker->traps, rectangle);
+}
+
+static cairo_status_t
+_cairo_stroker_move_to (void *closure,
+ const cairo_point_t *point)
+{
+ cairo_status_t status;
+ cairo_stroker_t *stroker = closure;
+
+ /* Cap the start and end of the previous sub path as needed */
+ status = _cairo_stroker_add_caps (stroker);
+ if (status)
+ return status;
+
+ stroker->first_point = *point;
+ stroker->current_point = *point;
+
+ stroker->has_first_face = FALSE;
+ stroker->has_current_face = FALSE;
+ stroker->has_initial_sub_path = FALSE;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_stroker_move_to_dashed (void *closure,
+ const cairo_point_t *point)
+{
+ /* reset the dash pattern for new sub paths */
+ cairo_stroker_t *stroker = closure;
+ _cairo_stroker_dash_start (&stroker->dash);
+
+ return _cairo_stroker_move_to (closure, point);
+}
+
+static cairo_status_t
+_cairo_stroker_line_to (void *closure,
+ const cairo_point_t *point)
+{
+ cairo_status_t status;
+ cairo_stroker_t *stroker = closure;
+ cairo_stroke_face_t start, end;
+ const cairo_point_t *p1 = &stroker->current_point;
+ const cairo_point_t *p2 = point;
+ cairo_slope_t dev_slope;
+ double slope_dx, slope_dy;
+
+ stroker->has_initial_sub_path = TRUE;
+
+ if (p1->x == p2->x && p1->y == p2->y)
+ return CAIRO_STATUS_SUCCESS;
+
+ _cairo_slope_init (&dev_slope, p1, p2);
+ slope_dx = _cairo_fixed_to_double (p2->x - p1->x);
+ slope_dy = _cairo_fixed_to_double (p2->y - p1->y);
+ _compute_normalized_device_slope (&slope_dx, &slope_dy, stroker->ctm_inverse, NULL);
+
+ status = _cairo_stroker_add_sub_edge (stroker, p1, p2, &dev_slope, slope_dx, slope_dy, &start, &end);
+ if (status)
+ return status;
+
+ if (stroker->has_current_face) {
+ /* Join with final face from previous segment */
+ status = _cairo_stroker_join (stroker, &stroker->current_face, &start);
+ if (status)
+ return status;
+ } else if (!stroker->has_first_face) {
+ /* Save sub path's first face in case needed for closing join */
+ stroker->first_face = start;
+ stroker->has_first_face = TRUE;
+ }
+ stroker->current_face = end;
+ stroker->has_current_face = TRUE;
+
+ stroker->current_point = *point;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/*
+ * Dashed lines. Cap each dash end, join around turns when on
+ */
+static cairo_status_t
+_cairo_stroker_line_to_dashed (void *closure,
+ const cairo_point_t *point)
+{
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+ cairo_stroker_t *stroker = closure;
+ double mag, remain, step_length = 0;
+ double slope_dx, slope_dy;
+ double dx2, dy2;
+ cairo_stroke_face_t sub_start, sub_end;
+ const cairo_point_t *p1 = &stroker->current_point;
+ const cairo_point_t *p2 = point;
+ cairo_slope_t dev_slope;
+ cairo_bool_t fully_in_bounds = TRUE;
+ cairo_line_t segment;
+
+ stroker->has_initial_sub_path = stroker->dash.dash_starts_on;
+
+ if (p1->x == p2->x && p1->y == p2->y)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (stroker->has_bounds &&
+ (!_cairo_box_contains_point (&stroker->bounds, p1) ||
+ !_cairo_box_contains_point (&stroker->bounds, p2)))
+ {
+ fully_in_bounds = FALSE;
+ }
+
+ _cairo_slope_init (&dev_slope, p1, p2);
+
+ slope_dx = _cairo_fixed_to_double (p2->x - p1->x);
+ slope_dy = _cairo_fixed_to_double (p2->y - p1->y);
+
+ if (!_compute_normalized_device_slope (&slope_dx, &slope_dy, stroker->ctm_inverse, &mag))
+ return CAIRO_STATUS_SUCCESS;
+
+ remain = mag;
+ segment.p1 = *p1;
+ while (remain) {
+ step_length = MIN (stroker->dash.dash_remain, remain);
+ remain -= step_length;
+ dx2 = slope_dx * (mag - remain);
+ dy2 = slope_dy * (mag - remain);
+ cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2);
+ segment.p2.x = _cairo_fixed_from_double (dx2) + p1->x;
+ segment.p2.y = _cairo_fixed_from_double (dy2) + p1->y;
+
+ if (fully_in_bounds ||
+ _cairo_box_intersects_line_segment (&stroker->bounds, &segment))
+ {
+ if (stroker->dash.dash_on) {
+ status = _cairo_stroker_add_sub_edge (stroker, &segment.p1, &segment.p2, &dev_slope, slope_dx, slope_dy, &sub_start, &sub_end);
+ if (status)
+ return status;
+
+ if (stroker->has_current_face) {
+ /* Join with final face from previous segment */
+ status = _cairo_stroker_join (stroker, &stroker->current_face, &sub_start);
+ stroker->has_current_face = FALSE;
+ if (status)
+ return status;
+ } else if (!stroker->has_first_face && stroker->dash.dash_starts_on) {
+ /* Save sub path's first face in case needed for closing join */
+ stroker->first_face = sub_start;
+ stroker->has_first_face = TRUE;
+ } else {
+ /* Cap dash start if not connecting to a previous segment */
+ status = _cairo_stroker_add_leading_cap (stroker, &sub_start);
+ if (status)
+ return status;
+ }
+
+ if (remain) {
+ /* Cap dash end if not at end of segment */
+ status = _cairo_stroker_add_trailing_cap (stroker, &sub_end);
+ if (status)
+ return status;
+ } else {
+ stroker->current_face = sub_end;
+ stroker->has_current_face = TRUE;
+ }
+ } else {
+ if (stroker->has_current_face) {
+ /* Cap final face from previous segment */
+ status = _cairo_stroker_add_trailing_cap (stroker, &stroker->current_face);
+ if (status)
+ return status;
+ stroker->has_current_face = FALSE;
+ }
+ }
+ } else {
+ if (stroker->has_current_face) {
+ /* Cap final face from previous segment */
+ status = _cairo_stroker_add_trailing_cap (stroker, &stroker->current_face);
+ if (status)
+ return status;
+ stroker->has_current_face = FALSE;
+ }
+ }
+
+ _cairo_stroker_dash_step (&stroker->dash, step_length);
+ segment.p1 = segment.p2;
+ }
+
+ if (stroker->dash.dash_on && !stroker->has_current_face) {
+ /* This segment ends on a transition to dash_on, compute a new face
+ * and add cap for the begining of the next dash_on step.
+ *
+ * Note: this will create a degenerate cap if this is not the last line
+ * in the path. Whether this behaviour is desirable or not is debatable.
+ * On one side these degnerate caps can not be reproduced with regular path stroking.
+ * On the other side Acroread 7 also produces the degenerate caps. */
+ _compute_face (point, &dev_slope, slope_dx, slope_dy, stroker, &stroker->current_face);
+ stroker->has_current_face = TRUE;
+ status = _cairo_stroker_add_leading_cap (stroker, &stroker->current_face);
+ if (status)
+ return status;
+ }
+
+ stroker->current_point = *point;
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_stroker_curve_to (void *closure,
+ const cairo_point_t *b,
+ const cairo_point_t *c,
+ const cairo_point_t *d)
+{
+ cairo_stroker_t *stroker = closure;
+ cairo_spline_t spline;
+ cairo_line_join_t line_join_save;
+ cairo_stroke_face_t face;
+ double slope_dx, slope_dy;
+ cairo_path_fixed_line_to_func_t *line_to;
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+ line_to = stroker->dash.dashed ?
+ _cairo_stroker_line_to_dashed :
+ _cairo_stroker_line_to;
+
+ if (! _cairo_spline_init (&spline,
+ line_to, stroker,
+ &stroker->current_point, b, c, d))
+ {
+ return line_to (closure, d);
+ }
+
+ /* If the line width is so small that the pen is reduced to a
+ single point, then we have nothing to do. */
+ if (stroker->pen.num_vertices <= 1)
+ return CAIRO_STATUS_SUCCESS;
+
+ /* Compute the initial face */
+ if (! stroker->dash.dashed || stroker->dash.dash_on) {
+ slope_dx = _cairo_fixed_to_double (spline.initial_slope.dx);
+ slope_dy = _cairo_fixed_to_double (spline.initial_slope.dy);
+ if (_compute_normalized_device_slope (&slope_dx, &slope_dy,
+ stroker->ctm_inverse, NULL))
+ {
+ _compute_face (&stroker->current_point,
+ &spline.initial_slope,
+ slope_dx, slope_dy,
+ stroker, &face);
+ }
+ if (stroker->has_current_face) {
+ status = _cairo_stroker_join (stroker,
+ &stroker->current_face, &face);
+ if (unlikely (status))
+ return status;
+ } else if (! stroker->has_first_face) {
+ stroker->first_face = face;
+ stroker->has_first_face = TRUE;
+ }
+
+ stroker->current_face = face;
+ stroker->has_current_face = TRUE;
+ }
+
+ /* Temporarily modify the stroker to use round joins to guarantee
+ * smooth stroked curves. */
+ line_join_save = stroker->style.line_join;
+ stroker->style.line_join = CAIRO_LINE_JOIN_ROUND;
+
+ status = _cairo_spline_decompose (&spline, stroker->tolerance);
+ if (unlikely (status))
+ return status;
+
+ /* And join the final face */
+ if (! stroker->dash.dashed || stroker->dash.dash_on) {
+ slope_dx = _cairo_fixed_to_double (spline.final_slope.dx);
+ slope_dy = _cairo_fixed_to_double (spline.final_slope.dy);
+ if (_compute_normalized_device_slope (&slope_dx, &slope_dy,
+ stroker->ctm_inverse, NULL))
+ {
+ _compute_face (&stroker->current_point,
+ &spline.final_slope,
+ slope_dx, slope_dy,
+ stroker, &face);
+ }
+
+ status = _cairo_stroker_join (stroker, &stroker->current_face, &face);
+ if (unlikely (status))
+ return status;
+
+ stroker->current_face = face;
+ }
+
+ stroker->style.line_join = line_join_save;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_stroker_close_path (void *closure)
+{
+ cairo_status_t status;
+ cairo_stroker_t *stroker = closure;
+
+ if (stroker->dash.dashed)
+ status = _cairo_stroker_line_to_dashed (stroker, &stroker->first_point);
+ else
+ status = _cairo_stroker_line_to (stroker, &stroker->first_point);
+ if (status)
+ return status;
+
+ if (stroker->has_first_face && stroker->has_current_face) {
+ /* Join first and final faces of sub path */
+ status = _cairo_stroker_join (stroker, &stroker->current_face, &stroker->first_face);
+ if (status)
+ return status;
+ } else {
+ /* Cap the start and end of the sub path as needed */
+ status = _cairo_stroker_add_caps (stroker);
+ if (status)
+ return status;
+ }
+
+ stroker->has_initial_sub_path = FALSE;
+ stroker->has_first_face = FALSE;
+ stroker->has_current_face = FALSE;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_int_status_t
+_cairo_path_fixed_stroke_to_traps (const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_traps_t *traps)
+{
+ cairo_int_status_t status;
+ cairo_stroker_t stroker;
+
+ status = _cairo_stroker_init (&stroker, stroke_style,
+ ctm, ctm_inverse, tolerance,
+ traps);
+ if (unlikely (status))
+ return status;
+
+ if (stroker.style.dash)
+ status = _cairo_path_fixed_interpret (path,
+ _cairo_stroker_move_to_dashed,
+ _cairo_stroker_line_to_dashed,
+ _cairo_stroker_curve_to,
+ _cairo_stroker_close_path,
+ &stroker);
+ else
+ status = _cairo_path_fixed_interpret (path,
+ _cairo_stroker_move_to,
+ _cairo_stroker_line_to,
+ _cairo_stroker_curve_to,
+ _cairo_stroker_close_path,
+ &stroker);
+ /* Cap the start and end of the final sub path as needed */
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS))
+ status = _cairo_stroker_add_caps (&stroker);
+
+ _cairo_stroker_fini (&stroker);
+
+ return status;
+}
+
+typedef struct _segment_t {
+ cairo_point_t p1, p2;
+ cairo_bool_t is_horizontal;
+ cairo_bool_t has_join;
+} segment_t;
+
+typedef struct _cairo_rectilinear_stroker {
+ const cairo_stroke_style_t *stroke_style;
+ const cairo_matrix_t *ctm;
+ cairo_antialias_t antialias;
+
+ cairo_fixed_t half_line_width;
+ cairo_traps_t *traps;
+ cairo_point_t current_point;
+ cairo_point_t first_point;
+ cairo_bool_t open_sub_path;
+
+ cairo_stroker_dash_t dash;
+
+ cairo_bool_t has_bounds;
+ cairo_box_t bounds;
+
+ int num_segments;
+ int segments_size;
+ segment_t *segments;
+ segment_t segments_embedded[8]; /* common case is a single rectangle */
+} cairo_rectilinear_stroker_t;
+
+static void
+_cairo_rectilinear_stroker_limit (cairo_rectilinear_stroker_t *stroker,
+ const cairo_box_t *boxes,
+ int num_boxes)
+{
+ stroker->has_bounds = TRUE;
+ _cairo_boxes_get_extents (boxes, num_boxes, &stroker->bounds);
+
+ stroker->bounds.p1.x -= stroker->half_line_width;
+ stroker->bounds.p2.x += stroker->half_line_width;
+
+ stroker->bounds.p1.y -= stroker->half_line_width;
+ stroker->bounds.p2.y += stroker->half_line_width;
+}
+
+static cairo_bool_t
+_cairo_rectilinear_stroker_init (cairo_rectilinear_stroker_t *stroker,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ cairo_antialias_t antialias,
+ cairo_traps_t *traps)
+{
+ /* This special-case rectilinear stroker only supports
+ * miter-joined lines (not curves) and a translation-only matrix
+ * (though it could probably be extended to support a matrix with
+ * uniform, integer scaling).
+ *
+ * It also only supports horizontal and vertical line_to
+ * elements. But we don't catch that here, but instead return
+ * UNSUPPORTED from _cairo_rectilinear_stroker_line_to if any
+ * non-rectilinear line_to is encountered.
+ */
+ if (stroke_style->line_join != CAIRO_LINE_JOIN_MITER)
+ return FALSE;
+
+ /* If the miter limit turns right angles into bevels, then we
+ * can't use this optimization. Remember, the ratio is
+ * 1/sin(ɸ/2). So the cutoff is 1/sin(Ï/4.0) or â·2,
+ * which we round for safety. */
+ if (stroke_style->miter_limit < M_SQRT2)
+ return FALSE;
+
+ if (! (stroke_style->line_cap == CAIRO_LINE_CAP_BUTT ||
+ stroke_style->line_cap == CAIRO_LINE_CAP_SQUARE))
+ {
+ return FALSE;
+ }
+
+ if (! _cairo_matrix_has_unity_scale (ctm))
+ return FALSE;
+
+ stroker->stroke_style = stroke_style;
+ stroker->ctm = ctm;
+ stroker->antialias = antialias;
+
+ stroker->half_line_width =
+ _cairo_fixed_from_double (stroke_style->line_width / 2.0);
+ stroker->open_sub_path = FALSE;
+ stroker->segments = stroker->segments_embedded;
+ stroker->segments_size = ARRAY_LENGTH (stroker->segments_embedded);
+ stroker->num_segments = 0;
+
+ _cairo_stroker_dash_init (&stroker->dash, stroke_style);
+
+ stroker->has_bounds = FALSE;
+
+ stroker->traps = traps;
+
+ return TRUE;
+}
+
+static void
+_cairo_rectilinear_stroker_fini (cairo_rectilinear_stroker_t *stroker)
+{
+ if (stroker->segments != stroker->segments_embedded)
+ free (stroker->segments);
+}
+
+static cairo_status_t
+_cairo_rectilinear_stroker_add_segment (cairo_rectilinear_stroker_t *stroker,
+ const cairo_point_t *p1,
+ const cairo_point_t *p2,
+ cairo_bool_t is_horizontal,
+ cairo_bool_t has_join)
+{
+ if (CAIRO_INJECT_FAULT ())
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ if (stroker->num_segments == stroker->segments_size) {
+ int new_size = stroker->segments_size * 2;
+ segment_t *new_segments;
+
+ if (stroker->segments == stroker->segments_embedded) {
+ new_segments = _cairo_malloc_ab (new_size, sizeof (segment_t));
+ if (unlikely (new_segments == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ memcpy (new_segments, stroker->segments,
+ stroker->num_segments * sizeof (segment_t));
+ } else {
+ new_segments = _cairo_realloc_ab (stroker->segments,
+ new_size, sizeof (segment_t));
+ if (unlikely (new_segments == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ stroker->segments_size = new_size;
+ stroker->segments = new_segments;
+ }
+
+ stroker->segments[stroker->num_segments].p1 = *p1;
+ stroker->segments[stroker->num_segments].p2 = *p2;
+ stroker->segments[stroker->num_segments].has_join = has_join;
+ stroker->segments[stroker->num_segments].is_horizontal = is_horizontal;
+ stroker->num_segments++;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_rectilinear_stroker_emit_segments (cairo_rectilinear_stroker_t *stroker)
+{
+ cairo_status_t status;
+ cairo_line_cap_t line_cap = stroker->stroke_style->line_cap;
+ cairo_fixed_t half_line_width = stroker->half_line_width;
+ int i;
+
+ for (i = 0; i < stroker->num_segments; i++) {
+ cairo_point_t *a, *b;
+ cairo_bool_t lengthen_initial, shorten_final, lengthen_final;
+
+ a = &stroker->segments[i].p1;
+ b = &stroker->segments[i].p2;
+
+ /* For each segment we generate a single rectangular
+ * trapezoid. This rectangle is based on a perpendicular
+ * extension (by half the line width) of the segment endpoints
+ * after some adjustments of the endpoints to account for caps
+ * and joins.
+ */
+
+ /* We adjust the initial point of the segment to extend the
+ * rectangle to include the previous cap or join, (this
+ * adjustment applies to all segments except for the first
+ * segment of open, butt-capped paths).
+ */
+ lengthen_initial = TRUE;
+ if (i == 0 && stroker->open_sub_path && line_cap == CAIRO_LINE_CAP_BUTT)
+ lengthen_initial = FALSE;
+
+ /* The adjustment of the final point is trickier. For all but
+ * the last segment we shorten the segment at the final
+ * endpoint to not overlap with the subsequent join. For the
+ * last segment we do the same shortening if the path is
+ * closed. If the path is open and butt-capped we do no
+ * adjustment, while if it's open and square-capped we do a
+ * lengthening adjustment instead to include the cap.
+ */
+ shorten_final = TRUE;
+ lengthen_final = FALSE;
+ if (i == stroker->num_segments - 1 && stroker->open_sub_path) {
+ shorten_final = FALSE;
+ if (line_cap == CAIRO_LINE_CAP_SQUARE)
+ lengthen_final = TRUE;
+ }
+
+ /* Perform the adjustments of the endpoints. */
+ if (a->y == b->y) {
+ if (a->x < b->x) {
+ if (lengthen_initial)
+ a->x -= half_line_width;
+ if (shorten_final)
+ b->x -= half_line_width;
+ else if (lengthen_final)
+ b->x += half_line_width;
+ } else {
+ if (lengthen_initial)
+ a->x += half_line_width;
+ if (shorten_final)
+ b->x += half_line_width;
+ else if (lengthen_final)
+ b->x -= half_line_width;
+ }
+
+ if (a->x > b->x) {
+ cairo_point_t *t;
+
+ t = a;
+ a = b;
+ b = t;
+ }
+ } else {
+ if (a->y < b->y) {
+ if (lengthen_initial)
+ a->y -= half_line_width;
+ if (shorten_final)
+ b->y -= half_line_width;
+ else if (lengthen_final)
+ b->y += half_line_width;
+ } else {
+ if (lengthen_initial)
+ a->y += half_line_width;
+ if (shorten_final)
+ b->y += half_line_width;
+ else if (lengthen_final)
+ b->y -= half_line_width;
+ }
+
+ if (a->y > b->y) {
+ cairo_point_t *t;
+
+ t = a;
+ a = b;
+ b = t;
+ }
+ }
+
+ /* Form the rectangle by expanding by half the line width in
+ * either perpendicular direction. */
+ if (a->y == b->y) {
+ a->y -= half_line_width;
+ b->y += half_line_width;
+ } else {
+ a->x -= half_line_width;
+ b->x += half_line_width;
+ }
+
+ if (stroker->antialias == CAIRO_ANTIALIAS_NONE) {
+ a->x = _cairo_fixed_round_down (a->x);
+ a->y = _cairo_fixed_round_down (a->y);
+ b->x = _cairo_fixed_round_down (b->x);
+ b->y = _cairo_fixed_round_down (b->y);
+ }
+ status = _cairo_traps_tessellate_rectangle (stroker->traps, a, b);
+ if (unlikely (status))
+ return status;
+ }
+
+ stroker->num_segments = 0;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_rectilinear_stroker_emit_segments_dashed (cairo_rectilinear_stroker_t *stroker)
+{
+ cairo_status_t status;
+ cairo_line_cap_t line_cap = stroker->stroke_style->line_cap;
+ cairo_fixed_t half_line_width = stroker->half_line_width;
+ int i;
+
+ for (i = 0; i < stroker->num_segments; i++) {
+ cairo_point_t *a, *b;
+ cairo_bool_t is_horizontal;
+
+ a = &stroker->segments[i].p1;
+ b = &stroker->segments[i].p2;
+
+ is_horizontal = stroker->segments[i].is_horizontal;
+
+ /* Handle the joins for a potentially degenerate segment. */
+ if (line_cap == CAIRO_LINE_CAP_BUTT &&
+ stroker->segments[i].has_join &&
+ (i != stroker->num_segments - 1 ||
+ (! stroker->open_sub_path && stroker->dash.dash_starts_on)))
+ {
+ cairo_point_t p1 = stroker->segments[i].p1;
+ cairo_point_t p2 = stroker->segments[i].p2;
+ cairo_slope_t out_slope;
+ int j = (i + 1) % stroker->num_segments;
+
+ _cairo_slope_init (&out_slope,
+ &stroker->segments[j].p1,
+ &stroker->segments[j].p2);
+
+ if (is_horizontal) {
+ if (p1.x <= p2.x) {
+ p1.x = p2.x;
+ p2.x += half_line_width;
+ } else {
+ p1.x = p2.x - half_line_width;
+ }
+ if (out_slope.dy >= 0)
+ p1.y -= half_line_width;
+ if (out_slope.dy <= 0)
+ p2.y += half_line_width;
+ } else {
+ if (p1.y <= p2.y) {
+ p1.y = p2.y;
+ p2.y += half_line_width;
+ } else {
+ p1.y = p2.y - half_line_width;
+ }
+ if (out_slope.dx >= 0)
+ p1.x -= half_line_width;
+ if (out_slope.dx <= 0)
+ p2.x += half_line_width;
+ }
+
+ if (stroker->antialias == CAIRO_ANTIALIAS_NONE) {
+ p1.x = _cairo_fixed_round_down (p1.x);
+ p1.y = _cairo_fixed_round_down (p1.y);
+ p2.x = _cairo_fixed_round_down (p2.x);
+ p2.y = _cairo_fixed_round_down (p2.y);
+ }
+ status = _cairo_traps_tessellate_rectangle (stroker->traps, &p1, &p2);
+ if (unlikely (status))
+ return status;
+ }
+
+ /* Perform the adjustments of the endpoints. */
+ if (is_horizontal) {
+ if (line_cap == CAIRO_LINE_CAP_SQUARE) {
+ if (a->x <= b->x) {
+ a->x -= half_line_width;
+ b->x += half_line_width;
+ } else {
+ a->x += half_line_width;
+ b->x -= half_line_width;
+ }
+ }
+
+ if (a->x > b->x) {
+ cairo_point_t *t;
+
+ t = a;
+ a = b;
+ b = t;
+ }
+
+ a->y -= half_line_width;
+ b->y += half_line_width;
+ } else {
+ if (line_cap == CAIRO_LINE_CAP_SQUARE) {
+ if (a->y <= b->y) {
+ a->y -= half_line_width;
+ b->y += half_line_width;
+ } else {
+ a->y += half_line_width;
+ b->y -= half_line_width;
+ }
+ }
+
+ if (a->y > b->y) {
+ cairo_point_t *t;
+
+ t = a;
+ a = b;
+ b = t;
+ }
+
+ a->x -= half_line_width;
+ b->x += half_line_width;
+ }
+
+ if (a->x == b->x && a->y == b->y)
+ continue;
+
+ if (stroker->antialias == CAIRO_ANTIALIAS_NONE) {
+ a->x = _cairo_fixed_round_down (a->x);
+ a->y = _cairo_fixed_round_down (a->y);
+ b->x = _cairo_fixed_round_down (b->x);
+ b->y = _cairo_fixed_round_down (b->y);
+ }
+ status = _cairo_traps_tessellate_rectangle (stroker->traps, a, b);
+ if (unlikely (status))
+ return status;
+ }
+
+ stroker->num_segments = 0;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_rectilinear_stroker_move_to (void *closure,
+ const cairo_point_t *point)
+{
+ cairo_rectilinear_stroker_t *stroker = closure;
+ cairo_status_t status;
+
+ if (stroker->dash.dashed)
+ status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker);
+ else
+ status = _cairo_rectilinear_stroker_emit_segments (stroker);
+ if (unlikely (status))
+ return status;
+
+ /* reset the dash pattern for new sub paths */
+ _cairo_stroker_dash_start (&stroker->dash);
+
+ stroker->current_point = *point;
+ stroker->first_point = *point;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_rectilinear_stroker_line_to (void *closure,
+ const cairo_point_t *b)
+{
+ cairo_rectilinear_stroker_t *stroker = closure;
+ cairo_point_t *a = &stroker->current_point;
+ cairo_status_t status;
+
+ /* We only support horizontal or vertical elements. */
+ assert (a->x == b->x || a->y == b->y);
+
+ /* We don't draw anything for degenerate paths. */
+ if (a->x == b->x && a->y == b->y)
+ return CAIRO_STATUS_SUCCESS;
+
+ status = _cairo_rectilinear_stroker_add_segment (stroker, a, b,
+ a->y == b->y,
+ TRUE);
+
+ stroker->current_point = *b;
+ stroker->open_sub_path = TRUE;
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_rectilinear_stroker_line_to_dashed (void *closure,
+ const cairo_point_t *point)
+{
+ cairo_rectilinear_stroker_t *stroker = closure;
+ const cairo_point_t *a = &stroker->current_point;
+ const cairo_point_t *b = point;
+ cairo_bool_t fully_in_bounds;
+ double sign, remain;
+ cairo_fixed_t mag;
+ cairo_status_t status;
+ cairo_line_t segment;
+ cairo_bool_t dash_on = FALSE;
+ cairo_bool_t is_horizontal;
+
+ /* We don't draw anything for degenerate paths. */
+ if (a->x == b->x && a->y == b->y)
+ return CAIRO_STATUS_SUCCESS;
+
+ /* We only support horizontal or vertical elements. */
+ assert (a->x == b->x || a->y == b->y);
+
+ fully_in_bounds = TRUE;
+ if (stroker->has_bounds &&
+ (! _cairo_box_contains_point (&stroker->bounds, a) ||
+ ! _cairo_box_contains_point (&stroker->bounds, b)))
+ {
+ fully_in_bounds = FALSE;
+ }
+
+ is_horizontal = a->y == b->y;
+ if (is_horizontal)
+ mag = b->x - a->x;
+ else
+ mag = b->y - a->y;
+ if (mag < 0) {
+ remain = _cairo_fixed_to_double (-mag);
+ sign = 1.;
+ } else {
+ remain = _cairo_fixed_to_double (mag);
+ sign = -1.;
+ }
+
+ segment.p2 = segment.p1 = *a;
+ while (remain > 0.) {
+ double step_length;
+
+ step_length = MIN (stroker->dash.dash_remain, remain);
+ remain -= step_length;
+
+ mag = _cairo_fixed_from_double (sign*remain);
+ if (is_horizontal)
+ segment.p2.x = b->x + mag;
+ else
+ segment.p2.y = b->y + mag;
+
+ if (stroker->dash.dash_on &&
+ (fully_in_bounds ||
+ _cairo_box_intersects_line_segment (&stroker->bounds, &segment)))
+ {
+ status = _cairo_rectilinear_stroker_add_segment (stroker,
+ &segment.p1,
+ &segment.p2,
+ is_horizontal,
+ remain <= 0.);
+ if (unlikely (status))
+ return status;
+
+ dash_on = TRUE;
+ }
+ else
+ {
+ dash_on = FALSE;
+ }
+
+ _cairo_stroker_dash_step (&stroker->dash, step_length);
+ segment.p1 = segment.p2;
+ }
+
+ if (stroker->dash.dash_on && ! dash_on &&
+ (fully_in_bounds ||
+ _cairo_box_intersects_line_segment (&stroker->bounds, &segment)))
+ {
+
+ /* This segment ends on a transition to dash_on, compute a new face
+ * and add cap for the beginning of the next dash_on step.
+ */
+
+ status = _cairo_rectilinear_stroker_add_segment (stroker,
+ &segment.p1,
+ &segment.p1,
+ is_horizontal,
+ TRUE);
+ if (unlikely (status))
+ return status;
+ }
+
+ stroker->current_point = *point;
+ stroker->open_sub_path = TRUE;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_rectilinear_stroker_close_path (void *closure)
+{
+ cairo_rectilinear_stroker_t *stroker = closure;
+ cairo_status_t status;
+
+ /* We don't draw anything for degenerate paths. */
+ if (! stroker->open_sub_path)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (stroker->dash.dashed) {
+ status = _cairo_rectilinear_stroker_line_to_dashed (stroker,
+ &stroker->first_point);
+ } else {
+ status = _cairo_rectilinear_stroker_line_to (stroker,
+ &stroker->first_point);
+ }
+ if (unlikely (status))
+ return status;
+
+ stroker->open_sub_path = FALSE;
+
+ if (stroker->dash.dashed)
+ status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker);
+ else
+ status = _cairo_rectilinear_stroker_emit_segments (stroker);
+ if (unlikely (status))
+ return status;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_int_status_t
+_cairo_path_fixed_stroke_rectilinear_to_traps (const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ cairo_antialias_t antialias,
+ cairo_traps_t *traps)
+{
+ cairo_rectilinear_stroker_t rectilinear_stroker;
+ cairo_int_status_t status;
+
+ assert (_cairo_path_fixed_stroke_is_rectilinear (path));
+
+ if (! _cairo_rectilinear_stroker_init (&rectilinear_stroker,
+ stroke_style, ctm, antialias,
+ traps))
+ {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ if (traps->num_limits) {
+ _cairo_rectilinear_stroker_limit (&rectilinear_stroker,
+ traps->limits,
+ traps->num_limits);
+ }
+
+ status = _cairo_path_fixed_interpret (path,
+ _cairo_rectilinear_stroker_move_to,
+ rectilinear_stroker.dash.dashed ?
+ _cairo_rectilinear_stroker_line_to_dashed :
+ _cairo_rectilinear_stroker_line_to,
+ NULL,
+ _cairo_rectilinear_stroker_close_path,
+ &rectilinear_stroker);
+ if (unlikely (status))
+ goto BAIL;
+
+ if (rectilinear_stroker.dash.dashed)
+ status = _cairo_rectilinear_stroker_emit_segments_dashed (&rectilinear_stroker);
+ else
+ status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker);
+
+ traps->maybe_region = antialias == CAIRO_ANTIALIAS_NONE || path->fill_maybe_region;
+ traps->is_rectilinear = 1;
+ traps->is_rectangular = 1;
+ /* As we incrementally tessellate, we do not eliminate self-intersections */
+ traps->has_intersections = traps->num_traps > 1;
+BAIL:
+ _cairo_rectilinear_stroker_fini (&rectilinear_stroker);
+
+ if (unlikely (status))
+ _cairo_traps_clear (traps);
+
+ return status;
+}
diff --git a/src/cairo-path-stroke.c b/src/cairo-path-stroke.c
index 56eb97d..10d4963 100644
--- a/src/cairo-path-stroke.c
+++ b/src/cairo-path-stroke.c
@@ -1315,722 +1315,3 @@ BAIL:
return status;
}
-
-cairo_int_status_t
-_cairo_path_fixed_stroke_to_traps (const cairo_path_fixed_t *path,
- const cairo_stroke_style_t *stroke_style,
- const cairo_matrix_t *ctm,
- const cairo_matrix_t *ctm_inverse,
- double tolerance,
- cairo_traps_t *traps)
-{
- cairo_int_status_t status;
- cairo_polygon_t polygon;
-
- /* Before we do anything else, we attempt the rectilinear
- * stroker. It's careful to generate trapezoids that align to
- * device-pixel boundaries when possible. Many backends can render
- * those much faster than non-aligned trapezoids, (by using clip
- * regions, etc.) */
- if (_cairo_path_fixed_stroke_is_rectilinear (path)) {
- status = _cairo_path_fixed_stroke_rectilinear_to_traps (path,
- stroke_style,
- ctm,
- CAIRO_ANTIALIAS_DEFAULT,
- traps);
- if (status != CAIRO_INT_STATUS_UNSUPPORTED)
- return status;
- }
-
- _cairo_polygon_init (&polygon, traps->limits, traps->num_limits);
-
- status = _cairo_path_fixed_stroke_to_polygon (path,
- stroke_style,
- ctm,
- ctm_inverse,
- tolerance,
- &polygon);
- if (unlikely (status))
- goto BAIL;
-
- status = _cairo_polygon_status (&polygon);
- if (unlikely (status))
- goto BAIL;
-
- status = _cairo_bentley_ottmann_tessellate_polygon (traps, &polygon,
- CAIRO_FILL_RULE_WINDING);
-
-BAIL:
- _cairo_polygon_fini (&polygon);
-
- return status;
-}
-
-typedef struct _segment_t {
- cairo_point_t p1, p2;
- cairo_bool_t is_horizontal;
- cairo_bool_t has_join;
-} segment_t;
-
-typedef struct _cairo_rectilinear_stroker {
- const cairo_stroke_style_t *stroke_style;
- const cairo_matrix_t *ctm;
- cairo_antialias_t antialias;
-
- cairo_fixed_t half_line_width;
- cairo_bool_t do_traps;
- void *container;
- cairo_point_t current_point;
- cairo_point_t first_point;
- cairo_bool_t open_sub_path;
-
- cairo_stroker_dash_t dash;
-
- cairo_bool_t has_bounds;
- cairo_box_t bounds;
-
- int num_segments;
- int segments_size;
- segment_t *segments;
- segment_t segments_embedded[8]; /* common case is a single rectangle */
-} cairo_rectilinear_stroker_t;
-
-static void
-_cairo_rectilinear_stroker_limit (cairo_rectilinear_stroker_t *stroker,
- const cairo_box_t *boxes,
- int num_boxes)
-{
- stroker->has_bounds = TRUE;
- _cairo_boxes_get_extents (boxes, num_boxes, &stroker->bounds);
-
- stroker->bounds.p1.x -= stroker->half_line_width;
- stroker->bounds.p2.x += stroker->half_line_width;
-
- stroker->bounds.p1.y -= stroker->half_line_width;
- stroker->bounds.p2.y += stroker->half_line_width;
-}
-
-static cairo_bool_t
-_cairo_rectilinear_stroker_init (cairo_rectilinear_stroker_t *stroker,
- const cairo_stroke_style_t *stroke_style,
- const cairo_matrix_t *ctm,
- cairo_antialias_t antialias,
- cairo_bool_t do_traps,
- void *container)
-{
- /* This special-case rectilinear stroker only supports
- * miter-joined lines (not curves) and a translation-only matrix
- * (though it could probably be extended to support a matrix with
- * uniform, integer scaling).
- *
- * It also only supports horizontal and vertical line_to
- * elements. But we don't catch that here, but instead return
- * UNSUPPORTED from _cairo_rectilinear_stroker_line_to if any
- * non-rectilinear line_to is encountered.
- */
- if (stroke_style->line_join != CAIRO_LINE_JOIN_MITER)
- return FALSE;
-
- /* If the miter limit turns right angles into bevels, then we
- * can't use this optimization. Remember, the ratio is
- * 1/sin(ɸ/2). So the cutoff is 1/sin(Ï/4.0) or â·2,
- * which we round for safety. */
- if (stroke_style->miter_limit < M_SQRT2)
- return FALSE;
-
- if (! (stroke_style->line_cap == CAIRO_LINE_CAP_BUTT ||
- stroke_style->line_cap == CAIRO_LINE_CAP_SQUARE))
- {
- return FALSE;
- }
-
- if (! _cairo_matrix_has_unity_scale (ctm))
- return FALSE;
-
- stroker->stroke_style = stroke_style;
- stroker->ctm = ctm;
- stroker->antialias = antialias;
-
- stroker->half_line_width =
- _cairo_fixed_from_double (stroke_style->line_width / 2.0);
- stroker->open_sub_path = FALSE;
- stroker->segments = stroker->segments_embedded;
- stroker->segments_size = ARRAY_LENGTH (stroker->segments_embedded);
- stroker->num_segments = 0;
-
- _cairo_stroker_dash_init (&stroker->dash, stroke_style);
-
- stroker->has_bounds = FALSE;
-
- stroker->do_traps = do_traps;
- stroker->container = container;
-
- return TRUE;
-}
-
-static void
-_cairo_rectilinear_stroker_fini (cairo_rectilinear_stroker_t *stroker)
-{
- if (stroker->segments != stroker->segments_embedded)
- free (stroker->segments);
-}
-
-static cairo_status_t
-_cairo_rectilinear_stroker_add_segment (cairo_rectilinear_stroker_t *stroker,
- const cairo_point_t *p1,
- const cairo_point_t *p2,
- cairo_bool_t is_horizontal,
- cairo_bool_t has_join)
-{
- if (CAIRO_INJECT_FAULT ())
- return _cairo_error (CAIRO_STATUS_NO_MEMORY);
-
- if (stroker->num_segments == stroker->segments_size) {
- int new_size = stroker->segments_size * 2;
- segment_t *new_segments;
-
- if (stroker->segments == stroker->segments_embedded) {
- new_segments = _cairo_malloc_ab (new_size, sizeof (segment_t));
- if (unlikely (new_segments == NULL))
- return _cairo_error (CAIRO_STATUS_NO_MEMORY);
-
- memcpy (new_segments, stroker->segments,
- stroker->num_segments * sizeof (segment_t));
- } else {
- new_segments = _cairo_realloc_ab (stroker->segments,
- new_size, sizeof (segment_t));
- if (unlikely (new_segments == NULL))
- return _cairo_error (CAIRO_STATUS_NO_MEMORY);
- }
-
- stroker->segments_size = new_size;
- stroker->segments = new_segments;
- }
-
- stroker->segments[stroker->num_segments].p1 = *p1;
- stroker->segments[stroker->num_segments].p2 = *p2;
- stroker->segments[stroker->num_segments].has_join = has_join;
- stroker->segments[stroker->num_segments].is_horizontal = is_horizontal;
- stroker->num_segments++;
-
- return CAIRO_STATUS_SUCCESS;
-}
-
-static cairo_status_t
-_cairo_rectilinear_stroker_emit_segments (cairo_rectilinear_stroker_t *stroker)
-{
- cairo_status_t status;
- cairo_line_cap_t line_cap = stroker->stroke_style->line_cap;
- cairo_fixed_t half_line_width = stroker->half_line_width;
- int i;
-
- for (i = 0; i < stroker->num_segments; i++) {
- cairo_point_t *a, *b;
- cairo_bool_t lengthen_initial, shorten_final, lengthen_final;
-
- a = &stroker->segments[i].p1;
- b = &stroker->segments[i].p2;
-
- /* For each segment we generate a single rectangular
- * trapezoid. This rectangle is based on a perpendicular
- * extension (by half the line width) of the segment endpoints
- * after some adjustments of the endpoints to account for caps
- * and joins.
- */
-
- /* We adjust the initial point of the segment to extend the
- * rectangle to include the previous cap or join, (this
- * adjustment applies to all segments except for the first
- * segment of open, butt-capped paths).
- */
- lengthen_initial = TRUE;
- if (i == 0 && stroker->open_sub_path && line_cap == CAIRO_LINE_CAP_BUTT)
- lengthen_initial = FALSE;
-
- /* The adjustment of the final point is trickier. For all but
- * the last segment we shorten the segment at the final
- * endpoint to not overlap with the subsequent join. For the
- * last segment we do the same shortening if the path is
- * closed. If the path is open and butt-capped we do no
- * adjustment, while if it's open and square-capped we do a
- * lengthening adjustment instead to include the cap.
- */
- shorten_final = TRUE;
- lengthen_final = FALSE;
- if (i == stroker->num_segments - 1 && stroker->open_sub_path) {
- shorten_final = FALSE;
- if (line_cap == CAIRO_LINE_CAP_SQUARE)
- lengthen_final = TRUE;
- }
-
- /* Perform the adjustments of the endpoints. */
- if (a->y == b->y) {
- if (a->x < b->x) {
- if (lengthen_initial)
- a->x -= half_line_width;
- if (shorten_final)
- b->x -= half_line_width;
- else if (lengthen_final)
- b->x += half_line_width;
- } else {
- if (lengthen_initial)
- a->x += half_line_width;
- if (shorten_final)
- b->x += half_line_width;
- else if (lengthen_final)
- b->x -= half_line_width;
- }
-
- if (a->x > b->x) {
- cairo_point_t *t;
-
- t = a;
- a = b;
- b = t;
- }
- } else {
- if (a->y < b->y) {
- if (lengthen_initial)
- a->y -= half_line_width;
- if (shorten_final)
- b->y -= half_line_width;
- else if (lengthen_final)
- b->y += half_line_width;
- } else {
- if (lengthen_initial)
- a->y += half_line_width;
- if (shorten_final)
- b->y += half_line_width;
- else if (lengthen_final)
- b->y -= half_line_width;
- }
-
- if (a->y > b->y) {
- cairo_point_t *t;
-
- t = a;
- a = b;
- b = t;
- }
- }
-
- /* Form the rectangle by expanding by half the line width in
- * either perpendicular direction. */
- if (a->y == b->y) {
- a->y -= half_line_width;
- b->y += half_line_width;
- } else {
- a->x -= half_line_width;
- b->x += half_line_width;
- }
-
- if (stroker->do_traps) {
- if (stroker->antialias == CAIRO_ANTIALIAS_NONE) {
- a->x = _cairo_fixed_round_down (a->x);
- a->y = _cairo_fixed_round_down (a->y);
- b->x = _cairo_fixed_round_down (b->x);
- b->y = _cairo_fixed_round_down (b->y);
- }
- status = _cairo_traps_tessellate_rectangle (stroker->container, a, b);
- } else {
- cairo_box_t box;
-
- box.p1 = *a;
- box.p2 = *b;
-
- status = _cairo_boxes_add (stroker->container, stroker->antialias, &box);
- }
- if (unlikely (status))
- return status;
- }
-
- stroker->num_segments = 0;
-
- return CAIRO_STATUS_SUCCESS;
-}
-
-static cairo_status_t
-_cairo_rectilinear_stroker_emit_segments_dashed (cairo_rectilinear_stroker_t *stroker)
-{
- cairo_status_t status;
- cairo_line_cap_t line_cap = stroker->stroke_style->line_cap;
- cairo_fixed_t half_line_width = stroker->half_line_width;
- int i;
-
- for (i = 0; i < stroker->num_segments; i++) {
- cairo_point_t *a, *b;
- cairo_bool_t is_horizontal;
-
- a = &stroker->segments[i].p1;
- b = &stroker->segments[i].p2;
-
- is_horizontal = stroker->segments[i].is_horizontal;
-
- /* Handle the joins for a potentially degenerate segment. */
- if (line_cap == CAIRO_LINE_CAP_BUTT &&
- stroker->segments[i].has_join &&
- (i != stroker->num_segments - 1 ||
- (! stroker->open_sub_path && stroker->dash.dash_starts_on)))
- {
- cairo_point_t p1 = stroker->segments[i].p1;
- cairo_point_t p2 = stroker->segments[i].p2;
- cairo_slope_t out_slope;
- int j = (i + 1) % stroker->num_segments;
-
- _cairo_slope_init (&out_slope,
- &stroker->segments[j].p1,
- &stroker->segments[j].p2);
-
- if (is_horizontal) {
- if (p1.x <= p2.x) {
- p1.x = p2.x;
- p2.x += half_line_width;
- } else {
- p1.x = p2.x - half_line_width;
- }
- if (out_slope.dy >= 0)
- p1.y -= half_line_width;
- if (out_slope.dy <= 0)
- p2.y += half_line_width;
- } else {
- if (p1.y <= p2.y) {
- p1.y = p2.y;
- p2.y += half_line_width;
- } else {
- p1.y = p2.y - half_line_width;
- }
- if (out_slope.dx >= 0)
- p1.x -= half_line_width;
- if (out_slope.dx <= 0)
- p2.x += half_line_width;
- }
-
- if (stroker->do_traps) {
- if (stroker->antialias == CAIRO_ANTIALIAS_NONE) {
- p1.x = _cairo_fixed_round_down (p1.x);
- p1.y = _cairo_fixed_round_down (p1.y);
- p2.x = _cairo_fixed_round_down (p2.x);
- p2.y = _cairo_fixed_round_down (p2.y);
- }
- status = _cairo_traps_tessellate_rectangle (stroker->container, &p1, &p2);
- } else {
- cairo_box_t box;
-
- box.p1 = p1;
- box.p2 = p2;
-
- status = _cairo_boxes_add (stroker->container, stroker->antialias, &box);
- }
- if (unlikely (status))
- return status;
- }
-
- /* Perform the adjustments of the endpoints. */
- if (is_horizontal) {
- if (line_cap == CAIRO_LINE_CAP_SQUARE) {
- if (a->x <= b->x) {
- a->x -= half_line_width;
- b->x += half_line_width;
- } else {
- a->x += half_line_width;
- b->x -= half_line_width;
- }
- }
-
- if (a->x > b->x) {
- cairo_point_t *t;
-
- t = a;
- a = b;
- b = t;
- }
-
- a->y -= half_line_width;
- b->y += half_line_width;
- } else {
- if (line_cap == CAIRO_LINE_CAP_SQUARE) {
- if (a->y <= b->y) {
- a->y -= half_line_width;
- b->y += half_line_width;
- } else {
- a->y += half_line_width;
- b->y -= half_line_width;
- }
- }
-
- if (a->y > b->y) {
- cairo_point_t *t;
-
- t = a;
- a = b;
- b = t;
- }
-
- a->x -= half_line_width;
- b->x += half_line_width;
- }
-
- if (a->x == b->x && a->y == b->y)
- continue;
-
- if (stroker->do_traps) {
- if (stroker->antialias == CAIRO_ANTIALIAS_NONE) {
- a->x = _cairo_fixed_round_down (a->x);
- a->y = _cairo_fixed_round_down (a->y);
- b->x = _cairo_fixed_round_down (b->x);
- b->y = _cairo_fixed_round_down (b->y);
- }
- status = _cairo_traps_tessellate_rectangle (stroker->container, a, b);
- } else {
- cairo_box_t box;
-
- box.p1 = *a;
- box.p2 = *b;
-
- status = _cairo_boxes_add (stroker->container, stroker->antialias, &box);
- }
- if (unlikely (status))
- return status;
- }
-
- stroker->num_segments = 0;
-
- return CAIRO_STATUS_SUCCESS;
-}
-
-static cairo_status_t
-_cairo_rectilinear_stroker_move_to (void *closure,
- const cairo_point_t *point)
-{
- cairo_rectilinear_stroker_t *stroker = closure;
- cairo_status_t status;
-
- if (stroker->dash.dashed)
- status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker);
- else
- status = _cairo_rectilinear_stroker_emit_segments (stroker);
- if (unlikely (status))
- return status;
-
- /* reset the dash pattern for new sub paths */
- _cairo_stroker_dash_start (&stroker->dash);
-
- stroker->current_point = *point;
- stroker->first_point = *point;
-
- return CAIRO_STATUS_SUCCESS;
-}
-
-static cairo_status_t
-_cairo_rectilinear_stroker_line_to (void *closure,
- const cairo_point_t *b)
-{
- cairo_rectilinear_stroker_t *stroker = closure;
- cairo_point_t *a = &stroker->current_point;
- cairo_status_t status;
-
- /* We only support horizontal or vertical elements. */
- assert (a->x == b->x || a->y == b->y);
-
- /* We don't draw anything for degenerate paths. */
- if (a->x == b->x && a->y == b->y)
- return CAIRO_STATUS_SUCCESS;
-
- status = _cairo_rectilinear_stroker_add_segment (stroker, a, b,
- a->y == b->y,
- TRUE);
-
- stroker->current_point = *b;
- stroker->open_sub_path = TRUE;
-
- return status;
-}
-
-static cairo_status_t
-_cairo_rectilinear_stroker_line_to_dashed (void *closure,
- const cairo_point_t *point)
-{
- cairo_rectilinear_stroker_t *stroker = closure;
- const cairo_point_t *a = &stroker->current_point;
- const cairo_point_t *b = point;
- cairo_bool_t fully_in_bounds;
- double sign, remain;
- cairo_fixed_t mag;
- cairo_status_t status;
- cairo_line_t segment;
- cairo_bool_t dash_on = FALSE;
- cairo_bool_t is_horizontal;
-
- /* We don't draw anything for degenerate paths. */
- if (a->x == b->x && a->y == b->y)
- return CAIRO_STATUS_SUCCESS;
-
- /* We only support horizontal or vertical elements. */
- assert (a->x == b->x || a->y == b->y);
-
- fully_in_bounds = TRUE;
- if (stroker->has_bounds &&
- (! _cairo_box_contains_point (&stroker->bounds, a) ||
- ! _cairo_box_contains_point (&stroker->bounds, b)))
- {
- fully_in_bounds = FALSE;
- }
-
- is_horizontal = a->y == b->y;
- if (is_horizontal)
- mag = b->x - a->x;
- else
- mag = b->y - a->y;
- if (mag < 0) {
- remain = _cairo_fixed_to_double (-mag);
- sign = 1.;
- } else {
- remain = _cairo_fixed_to_double (mag);
- sign = -1.;
- }
-
- segment.p2 = segment.p1 = *a;
- while (remain > 0.) {
- double step_length;
-
- step_length = MIN (stroker->dash.dash_remain, remain);
- remain -= step_length;
-
- mag = _cairo_fixed_from_double (sign*remain);
- if (is_horizontal)
- segment.p2.x = b->x + mag;
- else
- segment.p2.y = b->y + mag;
-
- if (stroker->dash.dash_on &&
- (fully_in_bounds ||
- _cairo_box_intersects_line_segment (&stroker->bounds, &segment)))
- {
- status = _cairo_rectilinear_stroker_add_segment (stroker,
- &segment.p1,
- &segment.p2,
- is_horizontal,
- remain <= 0.);
- if (unlikely (status))
- return status;
-
- dash_on = TRUE;
- }
- else
- {
- dash_on = FALSE;
- }
-
- _cairo_stroker_dash_step (&stroker->dash, step_length);
- segment.p1 = segment.p2;
- }
-
- if (stroker->dash.dash_on && ! dash_on &&
- (fully_in_bounds ||
- _cairo_box_intersects_line_segment (&stroker->bounds, &segment)))
- {
-
- /* This segment ends on a transition to dash_on, compute a new face
- * and add cap for the beginning of the next dash_on step.
- */
-
- status = _cairo_rectilinear_stroker_add_segment (stroker,
- &segment.p1,
- &segment.p1,
- is_horizontal,
- TRUE);
- if (unlikely (status))
- return status;
- }
-
- stroker->current_point = *point;
- stroker->open_sub_path = TRUE;
-
- return CAIRO_STATUS_SUCCESS;
-}
-
-static cairo_status_t
-_cairo_rectilinear_stroker_close_path (void *closure)
-{
- cairo_rectilinear_stroker_t *stroker = closure;
- cairo_status_t status;
-
- /* We don't draw anything for degenerate paths. */
- if (! stroker->open_sub_path)
- return CAIRO_STATUS_SUCCESS;
-
- if (stroker->dash.dashed) {
- status = _cairo_rectilinear_stroker_line_to_dashed (stroker,
- &stroker->first_point);
- } else {
- status = _cairo_rectilinear_stroker_line_to (stroker,
- &stroker->first_point);
- }
- if (unlikely (status))
- return status;
-
- stroker->open_sub_path = FALSE;
-
- if (stroker->dash.dashed)
- status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker);
- else
- status = _cairo_rectilinear_stroker_emit_segments (stroker);
- if (unlikely (status))
- return status;
-
- return CAIRO_STATUS_SUCCESS;
-}
-
-cairo_int_status_t
-_cairo_path_fixed_stroke_rectilinear_to_traps (const cairo_path_fixed_t *path,
- const cairo_stroke_style_t *stroke_style,
- const cairo_matrix_t *ctm,
- cairo_antialias_t antialias,
- cairo_traps_t *traps)
-{
- cairo_rectilinear_stroker_t rectilinear_stroker;
- cairo_int_status_t status;
-
- assert (_cairo_path_fixed_stroke_is_rectilinear (path));
-
- if (! _cairo_rectilinear_stroker_init (&rectilinear_stroker,
- stroke_style, ctm, antialias,
- TRUE, traps))
- {
- return CAIRO_INT_STATUS_UNSUPPORTED;
- }
-
- if (traps->num_limits) {
- _cairo_rectilinear_stroker_limit (&rectilinear_stroker,
- traps->limits,
- traps->num_limits);
- }
-
- status = _cairo_path_fixed_interpret (path,
- _cairo_rectilinear_stroker_move_to,
- rectilinear_stroker.dash.dashed ?
- _cairo_rectilinear_stroker_line_to_dashed :
- _cairo_rectilinear_stroker_line_to,
- NULL,
- _cairo_rectilinear_stroker_close_path,
- &rectilinear_stroker);
- if (unlikely (status))
- goto BAIL;
-
- if (rectilinear_stroker.dash.dashed)
- status = _cairo_rectilinear_stroker_emit_segments_dashed (&rectilinear_stroker);
- else
- status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker);
-
- traps->is_rectilinear = 1;
- traps->is_rectangular = 1;
- /* As we incrementally tessellate, we do not eliminate self-intersections */
- traps->has_intersections = traps->num_traps > 1;
-BAIL:
- _cairo_rectilinear_stroker_fini (&rectilinear_stroker);
-
- if (unlikely (status))
- _cairo_traps_clear (traps);
-
- return status;
-}
diff --git a/src/cairo-recording-surface.c b/src/cairo-recording-surface.c
index be803ec..254768c 100644
--- a/src/cairo-recording-surface.c
+++ b/src/cairo-recording-surface.c
@@ -86,6 +86,7 @@
#include "cairo-image-surface-private.h"
#include "cairo-recording-surface-private.h"
#include "cairo-surface-wrapper-private.h"
+#include "cairo-traps-private.h"
typedef enum {
CAIRO_RECORDING_REPLAY,
diff --git a/src/cairo-surface-fallback.c b/src/cairo-surface-fallback.c
index b65b2bf..824a64d 100644
--- a/src/cairo-surface-fallback.c
+++ b/src/cairo-surface-fallback.c
@@ -49,6 +49,7 @@
#include "cairo-region-private.h"
#include "cairo-spans-private.h"
#include "cairo-surface-fallback-private.h"
+#include "cairo-traps-private.h"
typedef struct {
cairo_surface_t *dst;
@@ -1053,6 +1054,7 @@ _cairo_surface_fallback_stroke (cairo_surface_t *surface,
_cairo_polygon_init_with_clip (&polygon, extents.clip);
_cairo_traps_init_with_clip (&traps, extents.clip);
+#if 0
if (_cairo_path_fixed_stroke_is_rectilinear (path)) {
status = _cairo_path_fixed_stroke_rectilinear_to_traps (path,
stroke_style,
@@ -1065,6 +1067,7 @@ _cairo_surface_fallback_stroke (cairo_surface_t *surface,
if (_cairo_status_is_error (status))
goto CLEANUP;
}
+#endif
status = _cairo_path_fixed_stroke_to_polygon (path,
stroke_style,
diff --git a/src/cairo-traps-private.h b/src/cairo-traps-private.h
new file mode 100644
index 0000000..1d52121
--- /dev/null
+++ b/src/cairo-traps-private.h
@@ -0,0 +1,122 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth at cworth.org>
+ */
+
+#ifndef CAIRO_TRAPS_H
+#define CAIRO_TRAPS_H
+
+#include "cairo-types-private.h"
+#include "cairo-compiler-private.h"
+
+
+/* cairo-traps.c */
+cairo_private void
+_cairo_traps_init (cairo_traps_t *traps);
+
+cairo_private void
+_cairo_traps_init_with_clip (cairo_traps_t *traps,
+ const cairo_clip_t *clip);
+
+cairo_private void
+_cairo_traps_init_with_clip (cairo_traps_t *traps,
+ const cairo_clip_t *clip);
+
+cairo_private void
+_cairo_traps_limit (cairo_traps_t *traps,
+ const cairo_box_t *boxes,
+ int num_boxes);
+
+cairo_private cairo_status_t
+_cairo_traps_init_boxes (cairo_traps_t *traps,
+ const cairo_boxes_t *boxes);
+
+cairo_private void
+_cairo_traps_clear (cairo_traps_t *traps);
+
+cairo_private void
+_cairo_traps_fini (cairo_traps_t *traps);
+
+#define _cairo_traps_status(T) (T)->status
+
+cairo_private void
+_cairo_traps_translate (cairo_traps_t *traps, int x, int y);
+
+cairo_private cairo_status_t
+_cairo_traps_tessellate_rectangle (cairo_traps_t *traps,
+ const cairo_point_t *top_left,
+ const cairo_point_t *bottom_right);
+
+cairo_private cairo_status_t
+_cairo_traps_tessellate_convex_quad (cairo_traps_t *traps,
+ const cairo_point_t q[4]);
+
+cairo_private cairo_status_t
+_cairo_traps_tessellate_triangle (cairo_traps_t *traps,
+ const cairo_point_t t[3]);
+
+cairo_private void
+_cairo_traps_add_trap (cairo_traps_t *traps,
+ cairo_fixed_t top, cairo_fixed_t bottom,
+ cairo_line_t *left, cairo_line_t *right);
+
+
+cairo_private cairo_int_status_t
+_cairo_path_fixed_stroke_to_traps (const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_traps_t *traps);
+
+cairo_private cairo_int_status_t
+_cairo_path_fixed_stroke_rectilinear_to_traps (const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ cairo_antialias_t antialias,
+ cairo_traps_t *traps);
+
+cairo_private cairo_int_status_t
+_cairo_path_fixed_fill_rectilinear_to_traps (const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ cairo_antialias_t antialias,
+ cairo_traps_t *traps);
+
+cairo_private cairo_bool_t
+_cairo_traps_to_boxes (cairo_traps_t *traps,
+ cairo_antialias_t antialias,
+ cairo_boxes_t *boxes);
+
+#endif /* CAIRO_TRAPS_H */
diff --git a/src/cairo-traps.c b/src/cairo-traps.c
index 42e2eb5..2b9afd3 100644
--- a/src/cairo-traps.c
+++ b/src/cairo-traps.c
@@ -43,6 +43,7 @@
#include "cairo-error-private.h"
#include "cairo-region-private.h"
#include "cairo-slope-private.h"
+#include "cairo-traps-private.h"
/* private functions */
@@ -67,15 +68,6 @@ _cairo_traps_init (cairo_traps_t *traps)
}
void
-_cairo_traps_limit (cairo_traps_t *traps,
- const cairo_box_t *limits,
- int num_limits)
-{
- traps->limits = limits;
- traps->num_limits = num_limits;
-}
-
-void
_cairo_traps_init_with_clip (cairo_traps_t *traps,
const cairo_clip_t *clip)
{
@@ -85,6 +77,15 @@ _cairo_traps_init_with_clip (cairo_traps_t *traps,
}
void
+_cairo_traps_limit (cairo_traps_t *traps,
+ const cairo_box_t *limits,
+ int num_limits)
+{
+ traps->limits = limits;
+ traps->num_limits = num_limits;
+}
+
+void
_cairo_traps_clear (cairo_traps_t *traps)
{
traps->status = CAIRO_STATUS_SUCCESS;
@@ -305,6 +306,177 @@ _cairo_traps_tessellate_rectangle (cairo_traps_t *traps,
return traps->status;
}
+static int
+_compare_point_fixed_by_y (const void *av, const void *bv)
+{
+ const cairo_point_t *a = av, *b = bv;
+
+ int ret = a->y - b->y;
+ if (ret == 0)
+ ret = a->x - b->x;
+ return ret;
+}
+
+cairo_status_t
+_cairo_traps_tessellate_convex_quad (cairo_traps_t *traps,
+ const cairo_point_t q[4])
+{
+ int a, b, c, d;
+ int i;
+ cairo_slope_t ab, ad;
+ cairo_bool_t b_left_of_d;
+ cairo_line_t left;
+ cairo_line_t right;
+
+ /* Choose a as a point with minimal y */
+ a = 0;
+ for (i = 1; i < 4; i++)
+ if (_compare_point_fixed_by_y (&q[i], &q[a]) < 0)
+ a = i;
+
+ /* b and d are adjacent to a, while c is opposite */
+ b = (a + 1) % 4;
+ c = (a + 2) % 4;
+ d = (a + 3) % 4;
+
+ /* Choose between b and d so that b.y is less than d.y */
+ if (_compare_point_fixed_by_y (&q[d], &q[b]) < 0) {
+ b = (a + 3) % 4;
+ d = (a + 1) % 4;
+ }
+
+ /* Without freedom left to choose anything else, we have four
+ * cases to tessellate.
+ *
+ * First, we have to determine the Y-axis sort of the four
+ * vertices, (either abcd or abdc). After that we need to detemine
+ * which edges will be "left" and which will be "right" in the
+ * resulting trapezoids. This can be determined by computing a
+ * slope comparison of ab and ad to determine if b is left of d or
+ * not.
+ *
+ * Note that "left of" here is in the sense of which edges should
+ * be the left vs. right edges of the trapezoid. In particular, b
+ * left of d does *not* mean that b.x is less than d.x.
+ *
+ * This should hopefully be made clear in the lame ASCII art
+ * below. Since the same slope comparison is used in all cases, we
+ * compute it before testing for the Y-value sort. */
+
+ /* Note: If a == b then the ab slope doesn't give us any
+ * information. In that case, we can replace it with the ac (or
+ * equivalenly the bc) slope which gives us exactly the same
+ * information we need. At worst the names of the identifiers ab
+ * and b_left_of_d are inaccurate in this case, (would be ac, and
+ * c_left_of_d). */
+ if (q[a].x == q[b].x && q[a].y == q[b].y)
+ _cairo_slope_init (&ab, &q[a], &q[c]);
+ else
+ _cairo_slope_init (&ab, &q[a], &q[b]);
+
+ _cairo_slope_init (&ad, &q[a], &q[d]);
+
+ b_left_of_d = (_cairo_slope_compare (&ab, &ad) > 0);
+
+ if (q[c].y <= q[d].y) {
+ if (b_left_of_d) {
+ /* Y-sort is abcd and b is left of d, (slope(ab) > slope (ad))
+ *
+ * top bot left right
+ * _a a a
+ * / / /| |\ a.y b.y ab ad
+ * b / b | b \
+ * / / | | \ \ b.y c.y bc ad
+ * c / c | c \
+ * | / \| \ \ c.y d.y cd ad
+ * d d d
+ */
+ left.p1 = q[a]; left.p2 = q[b];
+ right.p1 = q[a]; right.p2 = q[d];
+ _cairo_traps_add_trap (traps, q[a].y, q[b].y, &left, &right);
+ left.p1 = q[b]; left.p2 = q[c];
+ _cairo_traps_add_trap (traps, q[b].y, q[c].y, &left, &right);
+ left.p1 = q[c]; left.p2 = q[d];
+ _cairo_traps_add_trap (traps, q[c].y, q[d].y, &left, &right);
+ } else {
+ /* Y-sort is abcd and b is right of d, (slope(ab) <= slope (ad))
+ *
+ * a a a_
+ * /| |\ \ \ a.y b.y ad ab
+ * / b | b \ b
+ * / / | | \ \ b.y c.y ad bc
+ * / c | c \ c
+ * / / |/ \ | c.y d.y ad cd
+ * d d d
+ */
+ left.p1 = q[a]; left.p2 = q[d];
+ right.p1 = q[a]; right.p2 = q[b];
+ _cairo_traps_add_trap (traps, q[a].y, q[b].y, &left, &right);
+ right.p1 = q[b]; right.p2 = q[c];
+ _cairo_traps_add_trap (traps, q[b].y, q[c].y, &left, &right);
+ right.p1 = q[c]; right.p2 = q[d];
+ _cairo_traps_add_trap (traps, q[c].y, q[d].y, &left, &right);
+ }
+ } else {
+ if (b_left_of_d) {
+ /* Y-sort is abdc and b is left of d, (slope (ab) > slope (ad))
+ *
+ * a a a
+ * // / \ |\ a.y b.y ab ad
+ * /b/ b \ b \
+ * / / \ \ \ \ b.y d.y bc ad
+ * /d/ \ d \ d
+ * // \ / \| d.y c.y bc dc
+ * c c c
+ */
+ left.p1 = q[a]; left.p2 = q[b];
+ right.p1 = q[a]; right.p2 = q[d];
+ _cairo_traps_add_trap (traps, q[a].y, q[b].y, &left, &right);
+ left.p1 = q[b]; left.p2 = q[c];
+ _cairo_traps_add_trap (traps, q[b].y, q[d].y, &left, &right);
+ right.p1 = q[d]; right.p2 = q[c];
+ _cairo_traps_add_trap (traps, q[d].y, q[c].y, &left, &right);
+ } else {
+ /* Y-sort is abdc and b is right of d, (slope (ab) <= slope (ad))
+ *
+ * a a a
+ * /| / \ \\ a.y b.y ad ab
+ * / b / b \b\
+ * / / / / \ \ b.y d.y ad bc
+ * d / d / \d\
+ * |/ \ / \\ d.y c.y dc bc
+ * c c c
+ */
+ left.p1 = q[a]; left.p2 = q[d];
+ right.p1 = q[a]; right.p2 = q[b];
+ _cairo_traps_add_trap (traps, q[a].y, q[b].y, &left, &right);
+ right.p1 = q[b]; right.p2 = q[c];
+ _cairo_traps_add_trap (traps, q[b].y, q[d].y, &left, &right);
+ left.p1 = q[d]; left.p2 = q[c];
+ _cairo_traps_add_trap (traps, q[d].y, q[c].y, &left, &right);
+ }
+ }
+
+ return traps->status;
+}
+
+/* A triangle is simply a degenerate case of a convex
+ * quadrilateral. We would not benefit from having any distinct
+ * implementation of triangle vs. quadrilateral tessellation here. */
+cairo_status_t
+_cairo_traps_tessellate_triangle (cairo_traps_t *traps,
+ const cairo_point_t t[3])
+{
+ cairo_point_t quad[4];
+
+ quad[0] = t[0];
+ quad[1] = t[0];
+ quad[2] = t[1];
+ quad[3] = t[2];
+
+ return _cairo_traps_tessellate_convex_quad (traps, quad);
+}
+
void
_cairo_traps_translate (cairo_traps_t *traps, int x, int y)
{
@@ -608,6 +780,66 @@ _cairo_traps_extract_region (cairo_traps_t *traps,
return status;
}
+cairo_bool_t
+_cairo_traps_to_boxes (cairo_traps_t *traps,
+ cairo_antialias_t antialias,
+ cairo_boxes_t *boxes)
+{
+ int i;
+
+ 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)
+ return FALSE;
+ }
+
+ _cairo_boxes_init (boxes);
+
+ boxes->num_boxes = traps->num_traps;
+ boxes->chunks.base = (cairo_box_t *) traps->traps;
+ boxes->chunks.count = traps->num_traps;
+ boxes->chunks.size = traps->num_traps;
+
+ if (antialias != CAIRO_ANTIALIAS_NONE) {
+ for (i = 0; i < traps->num_traps; i++) {
+ /* Note the traps and boxes alias so we need to take the local copies first. */
+ cairo_fixed_t x1 = traps->traps[i].left.p1.x;
+ cairo_fixed_t x2 = traps->traps[i].right.p1.x;
+ cairo_fixed_t y1 = traps->traps[i].top;
+ cairo_fixed_t y2 = traps->traps[i].bottom;
+
+ boxes->chunks.base[i].p1.x = x1;
+ boxes->chunks.base[i].p1.y = y1;
+ boxes->chunks.base[i].p2.x = x2;
+ boxes->chunks.base[i].p2.y = y2;
+
+ if (boxes->is_pixel_aligned) {
+ boxes->is_pixel_aligned =
+ _cairo_fixed_is_integer (x1) && _cairo_fixed_is_integer (y1) &&
+ _cairo_fixed_is_integer (x2) && _cairo_fixed_is_integer (y2);
+ }
+ }
+ } else {
+ boxes->is_pixel_aligned = TRUE;
+
+ for (i = 0; i < traps->num_traps; i++) {
+ /* Note the traps and boxes alias so we need to take the local copies first. */
+ cairo_fixed_t x1 = traps->traps[i].left.p1.x;
+ cairo_fixed_t x2 = traps->traps[i].right.p1.x;
+ cairo_fixed_t y1 = traps->traps[i].top;
+ cairo_fixed_t y2 = traps->traps[i].bottom;
+
+ /* round down here to match Pixman's behavior when using traps. */
+ boxes->chunks.base[i].p1.x = _cairo_fixed_round_down (x1);
+ boxes->chunks.base[i].p1.y = _cairo_fixed_round_down (y1);
+ boxes->chunks.base[i].p2.x = _cairo_fixed_round_down (x2);
+ boxes->chunks.base[i].p2.y = _cairo_fixed_round_down (y2);
+ }
+ }
+
+ return TRUE;
+}
+
/* moves trap points such that they become the actual corners of the trapezoid */
static void
_sanitize_trap (cairo_trapezoid_t *t)
diff --git a/src/cairo-xcb-surface-render.c b/src/cairo-xcb-surface-render.c
index b42795e..e603341 100644
--- a/src/cairo-xcb-surface-render.c
+++ b/src/cairo-xcb-surface-render.c
@@ -39,6 +39,7 @@
#include "cairo-surface-offset-private.h"
#include "cairo-surface-snapshot-private.h"
#include "cairo-surface-subsurface-private.h"
+#include "cairo-traps-private.h"
#include "cairo-xcb-private.h"
#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */
@@ -2891,21 +2892,12 @@ _composite_polygon (cairo_xcb_surface_t *dst,
_cairo_traps_init (&traps.traps);
- status = _cairo_bentley_ottmann_tessellate_polygon (&traps.traps, polygon, fill_rule);
+ status = _cairo_bentley_ottmann_tessellate_polygon (&traps.traps,
+ polygon,
+ fill_rule);
if (unlikely (status))
goto CLEANUP_TRAPS;
- if (traps.traps.has_intersections) {
- if (traps.traps.is_rectangular)
- status = _cairo_bentley_ottmann_tessellate_rectangular_traps (&traps.traps, CAIRO_FILL_RULE_WINDING);
- else if (traps.traps.is_rectilinear)
- status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (&traps.traps, CAIRO_FILL_RULE_WINDING);
- else
- status = _cairo_bentley_ottmann_tessellate_traps (&traps.traps, CAIRO_FILL_RULE_WINDING);
- if (unlikely (status))
- goto CLEANUP_TRAPS;
- }
-
/* Use a fast path if the trapezoids consist of a simple region,
* but we can only do this if we do not have a clip surface, or can
* substitute the mask with the clip.
@@ -3246,19 +3238,6 @@ _composite_mask_clip (void *closure,
if (unlikely (status))
return status;
- if (info.traps.has_intersections) {
- if (info.traps.is_rectangular)
- status = _cairo_bentley_ottmann_tessellate_rectangular_traps (&info.traps, CAIRO_FILL_RULE_WINDING);
- else if (info.traps.is_rectilinear)
- status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (&info.traps, CAIRO_FILL_RULE_WINDING);
- else
- status = _cairo_bentley_ottmann_tessellate_traps (&info.traps, CAIRO_FILL_RULE_WINDING);
- if (unlikely (status)) {
- _cairo_traps_fini (&info.traps);
- return status;
- }
- }
-
dst->deferred_clear = FALSE; /* assert(trap extents == extents); */
status = _composite_traps (&info,
@@ -3484,33 +3463,97 @@ _cairo_xcb_surface_render_mask (cairo_xcb_surface_t *surface,
}
static cairo_int_status_t
-_cairo_xcb_surface_render_stroke_as_polygon (cairo_xcb_surface_t *dst,
- cairo_operator_t op,
- const cairo_pattern_t *source,
- const cairo_path_fixed_t *path,
- const cairo_stroke_style_t *stroke_style,
- const cairo_matrix_t *ctm,
- const cairo_matrix_t *ctm_inverse,
- double tolerance,
- cairo_antialias_t antialias,
- cairo_composite_rectangles_t *extents)
+_composite_traps_as_boxes (cairo_xcb_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ composite_traps_info_t *info,
+ cairo_composite_rectangles_t *extents)
{
- cairo_polygon_t polygon;
- cairo_status_t status;
+ cairo_boxes_t boxes;
- _cairo_polygon_init_with_clip (&polygon, extents->clip);
- status = _cairo_path_fixed_stroke_to_polygon (path,
- stroke_style,
- ctm, ctm_inverse,
- tolerance,
- &polygon);
- if (likely (status == CAIRO_STATUS_SUCCESS)) {
- status = _composite_polygon (dst, op, source,
- &polygon, antialias,
- CAIRO_FILL_RULE_WINDING,
- extents);
+ if (! _cairo_traps_to_boxes (&info->traps, info->antialias, &boxes))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ return _clip_and_composite_boxes (dst, op, src, &boxes, extents);
+}
+
+static cairo_int_status_t
+_clip_and_composite_traps (cairo_xcb_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ composite_traps_info_t *info,
+ cairo_composite_rectangles_t *extents)
+{
+ cairo_int_status_t status;
+
+ status = trim_extents_to_traps (extents, &info->traps);
+ if (unlikely (status != CAIRO_INT_STATUS_SUCCESS))
+ return status;
+
+ status = _composite_traps_as_boxes (dst, op, src, info, extents);
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ unsigned int flags = 0;
+
+ /* For unbounded operations, the X11 server will estimate the
+ * affected rectangle and apply the operation to that. However,
+ * there are cases where this is an overestimate (e.g. the
+ * clip-fill-{eo,nz}-unbounded test).
+ *
+ * The clip will trim that overestimate to our expectations.
+ */
+ if (! extents->is_bounded)
+ flags |= FORCE_CLIP_REGION;
+
+ status = _clip_and_composite (dst, op, src,
+ _composite_traps, NULL,
+ info, extents,
+ need_unbounded_clip (extents) | flags);
}
- _cairo_polygon_fini (&polygon);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_xcb_surface_render_stroke_as_traps (cairo_xcb_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_composite_rectangles_t *extents)
+{
+ composite_traps_info_t info;
+ cairo_int_status_t status;
+
+ info.antialias = antialias;
+ _cairo_traps_init_with_clip (&info.traps, extents->clip);
+
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ if (_cairo_path_fixed_stroke_is_rectilinear (path)) {
+ status = _cairo_path_fixed_stroke_rectilinear_to_traps (path,
+ stroke_style,
+ ctm,
+ antialias,
+ &info.traps);
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS))
+ status = _clip_and_composite_traps (dst, op, source,
+ &info, extents);
+ }
+
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ status = _cairo_path_fixed_stroke_to_traps (path,
+ stroke_style,
+ ctm, ctm_inverse,
+ tolerance,
+ &info.traps);
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS))
+ status = _clip_and_composite_traps (dst, op, source,
+ &info, extents);
+ }
+ _cairo_traps_fini (&info.traps);
return status;
}
@@ -3612,30 +3655,31 @@ _cairo_xcb_surface_render_stroke (cairo_xcb_surface_t *surface,
return status;
status = CAIRO_INT_STATUS_UNSUPPORTED;
- if (_cairo_path_fixed_stroke_is_rectilinear (path)) {
- cairo_boxes_t boxes;
-
- _cairo_boxes_init_with_clip (&boxes, extents.clip);
- status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path,
- style,
- ctm,
- antialias,
- &boxes);
- if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
- status = _clip_and_composite_boxes (surface, op, source,
- &boxes, &extents);
- }
- _cairo_boxes_fini (&boxes);
- }
+ if (surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS) {
+ status = _cairo_xcb_surface_render_stroke_as_traps (surface, op, source,
+ path, style,
+ ctm, ctm_inverse,
+ tolerance, antialias,
+ &extents);
+ }
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
- if (surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS) {
- status = _cairo_xcb_surface_render_stroke_as_polygon (surface, op, source,
- path, style,
- ctm, ctm_inverse,
- tolerance, antialias,
- &extents);
- } else if (surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE) {
+ if (_cairo_path_fixed_stroke_is_rectilinear (path)) {
+ cairo_boxes_t boxes;
+
+ _cairo_boxes_init_with_clip (&boxes, extents.clip);
+ status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path,
+ style,
+ ctm,
+ antialias,
+ &boxes);
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+ status = _clip_and_composite_boxes (surface, op, source,
+ &boxes, &extents);
+ }
+ _cairo_boxes_fini (&boxes);
+ }
+ if (surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE) {
status = _cairo_xcb_surface_render_stroke_via_mask (surface, op, source,
path, style,
ctm, ctm_inverse,
diff --git a/src/cairo-xcb-surface.c b/src/cairo-xcb-surface.c
index abf8fe3..c966794 100644
--- a/src/cairo-xcb-surface.c
+++ b/src/cairo-xcb-surface.c
@@ -628,15 +628,14 @@ _cairo_xcb_surface_flush (void *abstract_surface)
if (status == CAIRO_STATUS_SUCCESS && ! surface->base.finished) {
status = cairo_surface_status (surface->fallback);
- if (status == CAIRO_STATUS_SUCCESS) {
- status = _put_image (surface, (cairo_image_surface_t *)surface->fallback);
- }
+ if (status == CAIRO_STATUS_SUCCESS)
+ status = _put_image (surface,
+ (cairo_image_surface_t *) surface->fallback);
- if (status == CAIRO_STATUS_SUCCESS) {
+ if (status == CAIRO_STATUS_SUCCESS)
_cairo_surface_attach_snapshot (&surface->base,
surface->fallback,
cairo_surface_finish);
- }
}
cairo_surface_destroy (surface->fallback);
@@ -696,15 +695,19 @@ _cairo_xcb_surface_unmap (void *abstract_surface,
static cairo_surface_t *
_cairo_xcb_surface_fallback (cairo_xcb_surface_t *surface)
{
- cairo_surface_t *image;
+ if (surface->fallback == NULL) {
+ cairo_surface_t *image;
- image = _get_image (surface, TRUE, 0, 0, surface->width, surface->height);
+ image = _get_image (surface, TRUE,
+ 0, 0, surface->width, surface->height);
- /* If there was a deferred clear, _get_image applied it */
- if (image->status == CAIRO_STATUS_SUCCESS)
- surface->deferred_clear = FALSE;
+ /* If there was a deferred clear, _get_image applied it */
+ if (image->status == CAIRO_STATUS_SUCCESS)
+ surface->deferred_clear = FALSE;
- return image;
+ surface->fallback = image;
+ }
+ return surface->fallback;
}
static cairo_int_status_t
@@ -714,9 +717,10 @@ _cairo_xcb_surface_paint (void *abstract_surface,
const cairo_clip_t *clip)
{
cairo_xcb_surface_t *surface = abstract_surface;
- cairo_int_status_t status;
if (surface->fallback == NULL) {
+ cairo_int_status_t status;
+
status = _cairo_xcb_surface_cairo_paint (surface, op, source, clip);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
@@ -724,11 +728,10 @@ _cairo_xcb_surface_paint (void *abstract_surface,
status = _cairo_xcb_surface_render_paint (surface, op, source, clip);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
-
- surface->fallback = _cairo_xcb_surface_fallback (surface);
}
- return _cairo_surface_paint (surface->fallback, op, source, clip);
+ return _cairo_surface_paint (_cairo_xcb_surface_fallback (surface),
+ op, source, clip);
}
static cairo_int_status_t
@@ -739,9 +742,10 @@ _cairo_xcb_surface_mask (void *abstract_surface,
const cairo_clip_t *clip)
{
cairo_xcb_surface_t *surface = abstract_surface;
- cairo_int_status_t status;
if (surface->fallback == NULL) {
+ cairo_int_status_t status;
+
status = _cairo_xcb_surface_cairo_mask (surface,
op, source, mask, clip);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
@@ -751,11 +755,9 @@ _cairo_xcb_surface_mask (void *abstract_surface,
op, source, mask, clip);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
-
- surface->fallback = _cairo_xcb_surface_fallback (surface);
}
- return _cairo_surface_mask (surface->fallback,
+ return _cairo_surface_mask (_cairo_xcb_surface_fallback (surface),
op, source, mask,
clip);
}
@@ -773,9 +775,10 @@ _cairo_xcb_surface_stroke (void *abstract_surface,
const cairo_clip_t *clip)
{
cairo_xcb_surface_t *surface = abstract_surface;
- cairo_int_status_t status;
if (surface->fallback == NULL) {
+ cairo_int_status_t status;
+
status = _cairo_xcb_surface_cairo_stroke (surface, op, source,
path, style,
ctm, ctm_inverse,
@@ -793,11 +796,9 @@ _cairo_xcb_surface_stroke (void *abstract_surface,
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
-
- surface->fallback = _cairo_xcb_surface_fallback (surface);
}
- return _cairo_surface_stroke (surface->fallback,
+ return _cairo_surface_stroke (_cairo_xcb_surface_fallback (surface),
op, source,
path, style,
ctm, ctm_inverse,
@@ -816,9 +817,10 @@ _cairo_xcb_surface_fill (void *abstract_surface,
const cairo_clip_t *clip)
{
cairo_xcb_surface_t *surface = abstract_surface;
- cairo_int_status_t status;
if (surface->fallback == NULL) {
+ cairo_int_status_t status;
+
status = _cairo_xcb_surface_cairo_fill (surface, op, source,
path, fill_rule,
tolerance, antialias,
@@ -832,11 +834,9 @@ _cairo_xcb_surface_fill (void *abstract_surface,
clip);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
-
- surface->fallback = _cairo_xcb_surface_fallback (surface);
}
- return _cairo_surface_fill (surface->fallback,
+ return _cairo_surface_fill (_cairo_xcb_surface_fallback (surface),
op, source,
path, fill_rule,
tolerance, antialias,
@@ -854,11 +854,12 @@ _cairo_xcb_surface_glyphs (void *abstract_surface,
int *num_remaining)
{
cairo_xcb_surface_t *surface = abstract_surface;
- cairo_int_status_t status;
*num_remaining = 0;
if (surface->fallback == NULL) {
+ cairo_int_status_t status;
+
status = _cairo_xcb_surface_cairo_glyphs (surface,
op, source,
scaled_font, glyphs, num_glyphs,
@@ -872,11 +873,9 @@ _cairo_xcb_surface_glyphs (void *abstract_surface,
clip);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
-
- surface->fallback = _cairo_xcb_surface_fallback (surface);
}
- return _cairo_surface_show_text_glyphs (surface->fallback,
+ return _cairo_surface_show_text_glyphs (_cairo_xcb_surface_fallback (surface),
op, source,
NULL, 0,
glyphs, num_glyphs,
diff --git a/src/cairoint.h b/src/cairoint.h
index 1976a7a..9fef9c5 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -1376,12 +1376,6 @@ _cairo_path_fixed_fill_rectilinear_to_polygon (const cairo_path_fixed_t *path,
cairo_antialias_t antialias,
cairo_polygon_t *polygon);
-cairo_private cairo_int_status_t
-_cairo_path_fixed_fill_rectilinear_to_traps (const cairo_path_fixed_t *path,
- cairo_fill_rule_t fill_rule,
- cairo_antialias_t antialias,
- cairo_traps_t *traps);
-
cairo_private cairo_status_t
_cairo_path_fixed_fill_rectilinear_to_boxes (const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
@@ -1417,13 +1411,6 @@ _cairo_path_fixed_stroke_dashed_to_polygon (const cairo_path_fixed_t *path,
cairo_polygon_t *polygon);
cairo_private cairo_int_status_t
-_cairo_path_fixed_stroke_rectilinear_to_traps (const cairo_path_fixed_t *path,
- const cairo_stroke_style_t *stroke_style,
- const cairo_matrix_t *ctm,
- cairo_antialias_t antialias,
- cairo_traps_t *traps);
-
-cairo_private cairo_int_status_t
_cairo_path_fixed_stroke_rectilinear_to_boxes (const cairo_path_fixed_t *path,
const cairo_stroke_style_t *stroke_style,
const cairo_matrix_t *ctm,
@@ -2206,44 +2193,6 @@ _cairo_matrix_to_pixman_matrix_offset (const cairo_matrix_t *matrix,
int *out_x_offset,
int *out_y_offset);
-/* cairo-traps.c */
-cairo_private void
-_cairo_traps_init (cairo_traps_t *traps);
-
-cairo_private void
-_cairo_traps_init_with_clip (cairo_traps_t *traps,
- const cairo_clip_t *clip);
-
-cairo_private void
-_cairo_traps_limit (cairo_traps_t *traps,
- const cairo_box_t *boxes,
- int num_boxes);
-
-cairo_private cairo_status_t
-_cairo_traps_init_boxes (cairo_traps_t *traps,
- const cairo_boxes_t *boxes);
-
-cairo_private void
-_cairo_traps_clear (cairo_traps_t *traps);
-
-cairo_private void
-_cairo_traps_fini (cairo_traps_t *traps);
-
-#define _cairo_traps_status(T) (T)->status
-
-cairo_private void
-_cairo_traps_translate (cairo_traps_t *traps, int x, int y);
-
-cairo_private cairo_status_t
-_cairo_traps_tessellate_rectangle (cairo_traps_t *traps,
- const cairo_point_t *top_left,
- const cairo_point_t *bottom_right);
-
-cairo_private void
-_cairo_traps_add_trap (cairo_traps_t *traps,
- cairo_fixed_t top, cairo_fixed_t bottom,
- cairo_line_t *left, cairo_line_t *right);
-
cairo_private cairo_status_t
_cairo_bentley_ottmann_tessellate_rectilinear_polygon (cairo_traps_t *traps,
const cairo_polygon_t *polygon,
commit b546c0a98d53854433cedabec619ad63ba598b79
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date: Tue Aug 9 18:53:05 2011 +0100
types
diff --git a/src/cairo-contour-private.h b/src/cairo-contour-private.h
index ef9ad9d..0898db8 100644
--- a/src/cairo-contour-private.h
+++ b/src/cairo-contour-private.h
@@ -49,15 +49,23 @@ CAIRO_BEGIN_DECLS
* contains no holes, but maybe either concave or convex.
*/
+struct _cairo_contour_chain {
+ cairo_point_t *points;
+ int num_points, size_points;
+ struct _cairo_contour_chain *next;
+};
+
+struct _cairo_contour_iter {
+ cairo_point_t *point;
+ cairo_contour_chain_t *chain;
+};
+
struct _cairo_contour {
+ cairo_list_t next;
int direction;
- struct _cairo_contour_chain {
- cairo_point_t *points;
- int num_points, size_points;
- struct _cairo_contour_chain *next;
- } chain, *tail;
+ cairo_contour_chain_t chain, *tail;
+
cairo_point_t embedded_points[64];
- cairo_list_t next;
};
/* Initial definition of a shape is a set of contours (some representing holes) */
diff --git a/src/cairo-contour.c b/src/cairo-contour.c
index aa3ad6d..603a49a 100644
--- a/src/cairo-contour.c
+++ b/src/cairo-contour.c
@@ -60,14 +60,14 @@ cairo_int_status_t
__cairo_contour_add_point (cairo_contour_t *contour,
const cairo_point_t *point)
{
- struct _cairo_contour_chain *tail = contour->tail;
- struct _cairo_contour_chain *next;
+ cairo_contour_chain_t *tail = contour->tail;
+ cairo_contour_chain_t *next;
assert (tail->next == NULL);
next = _cairo_malloc_ab_plus_c (tail->size_points*2,
sizeof (cairo_point_t),
- sizeof (struct _cairo_contour_chain));
+ sizeof (cairo_contour_chain_t));
if (unlikely (next == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
@@ -85,7 +85,7 @@ __cairo_contour_add_point (cairo_contour_t *contour,
static void
first_inc (cairo_contour_t *contour,
cairo_point_t **p,
- struct _cairo_contour_chain **chain)
+ cairo_contour_chain_t **chain)
{
if (*p == (*chain)->points + (*chain)->num_points) {
assert ((*chain)->next);
@@ -98,10 +98,10 @@ first_inc (cairo_contour_t *contour,
static void
last_dec (cairo_contour_t *contour,
cairo_point_t **p,
- struct _cairo_contour_chain **chain)
+ cairo_contour_chain_t **chain)
{
if (*p == (*chain)->points) {
- struct _cairo_contour_chain *prev;
+ cairo_contour_chain_t *prev;
assert (*chain != &contour->chain);
for (prev = &contour->chain; prev->next != *chain; prev = prev->next)
;
@@ -114,7 +114,7 @@ last_dec (cairo_contour_t *contour,
void
_cairo_contour_reverse (cairo_contour_t *contour)
{
- struct _cairo_contour_chain *first_chain, *last_chain;
+ cairo_contour_chain_t *first_chain, *last_chain;
cairo_point_t *first, *last;
contour->direction = -contour->direction;
@@ -144,7 +144,7 @@ cairo_int_status_t
_cairo_contour_add (cairo_contour_t *dst,
const cairo_contour_t *src)
{
- const struct _cairo_contour_chain *chain;
+ const cairo_contour_chain_t *chain;
cairo_int_status_t status;
int i;
@@ -159,13 +159,8 @@ _cairo_contour_add (cairo_contour_t *dst,
return CAIRO_INT_STATUS_SUCCESS;
}
-struct _cairo_contour_iter {
- struct _cairo_contour_chain *chain;
- cairo_point_t *point;
-};
-
static inline cairo_bool_t
-iter_next (struct _cairo_contour_iter *iter)
+iter_next (cairo_contour_iter_t *iter)
{
if (iter->point == &iter->chain->points[iter->chain->size_points-1]) {
iter->chain = iter->chain->next;
@@ -181,30 +176,30 @@ iter_next (struct _cairo_contour_iter *iter)
}
static cairo_bool_t
-iter_equal (const struct _cairo_contour_iter *i1,
- const struct _cairo_contour_iter *i2)
+iter_equal (const cairo_contour_iter_t *i1,
+ const cairo_contour_iter_t *i2)
{
return i1->chain == i2->chain && i1->point == i2->point;
}
static void
-iter_init (struct _cairo_contour_iter *iter, cairo_contour_t *contour)
+iter_init (cairo_contour_iter_t *iter, cairo_contour_t *contour)
{
iter->chain = &contour->chain;
iter->point = &contour->chain.points[0];
}
static void
-iter_init_last (struct _cairo_contour_iter *iter, cairo_contour_t *contour)
+iter_init_last (cairo_contour_iter_t *iter, cairo_contour_t *contour)
{
iter->chain = contour->tail;
iter->point = &contour->tail->points[contour->tail->num_points-1];
}
-static const struct _cairo_contour_chain *prev_const_chain(const cairo_contour_t *contour,
- const struct _cairo_contour_chain *chain)
+static const cairo_contour_chain_t *prev_const_chain(const cairo_contour_t *contour,
+ const cairo_contour_chain_t *chain)
{
- const struct _cairo_contour_chain *prev;
+ const cairo_contour_chain_t *prev;
if (chain == &contour->chain)
return NULL;
@@ -219,7 +214,7 @@ cairo_int_status_t
_cairo_contour_add_reversed (cairo_contour_t *dst,
const cairo_contour_t *src)
{
- const struct _cairo_contour_chain *last;
+ const cairo_contour_chain_t *last;
cairo_int_status_t status;
int i;
@@ -251,10 +246,10 @@ _cairo_point_distance2 (const cairo_point_t *p1, const cairo_point_t *p2)
static cairo_bool_t
_cairo_contour_simplify_chain (cairo_contour_t *contour, const double tolerance,
- const struct _cairo_contour_iter *first,
- const struct _cairo_contour_iter *last)
+ const cairo_contour_iter_t *first,
+ const cairo_contour_iter_t *last)
{
- struct _cairo_contour_iter iter, furthest;
+ cairo_contour_iter_t iter, furthest;
uint64_t max_error;
int x0, y0;
int nx, ny;
@@ -311,9 +306,9 @@ _cairo_contour_simplify_chain (cairo_contour_t *contour, const double tolerance,
void
_cairo_contour_simplify (cairo_contour_t *contour, double tolerance)
{
- struct _cairo_contour_chain *chain;
+ cairo_contour_chain_t *chain;
cairo_point_t *last = NULL;
- struct _cairo_contour_iter iter, furthest;
+ cairo_contour_iter_t iter, furthest;
cairo_bool_t simplified;
uint64_t max = 0;
int i;
@@ -386,7 +381,7 @@ _cairo_contour_simplify (cairo_contour_t *contour, double tolerance)
}
if (iter.chain) {
- struct _cairo_contour_chain *next;
+ cairo_contour_chain_t *next;
for (chain = iter.chain->next; chain; chain = next) {
next = chain->next;
@@ -408,7 +403,7 @@ _cairo_contour_reset (cairo_contour_t *contour)
void
_cairo_contour_fini (cairo_contour_t *contour)
{
- struct _cairo_contour_chain *chain, *next;
+ cairo_contour_chain_t *chain, *next;
for (chain = contour->chain.next; chain; chain = next) {
next = chain->next;
@@ -419,7 +414,7 @@ _cairo_contour_fini (cairo_contour_t *contour)
void
_cairo_debug_print_contour (FILE *file, cairo_contour_t *contour)
{
- struct _cairo_contour_chain *chain;
+ cairo_contour_chain_t *chain;
int num_points, size_points;
int i;
@@ -447,7 +442,7 @@ _cairo_debug_print_contour (FILE *file, cairo_contour_t *contour)
void
__cairo_contour_remove_last_chain (cairo_contour_t *contour)
{
- struct _cairo_contour_chain *chain;
+ cairo_contour_chain_t *chain;
if (contour->tail == &contour->chain)
return;
diff --git a/src/cairo-types-private.h b/src/cairo-types-private.h
index a1d4863..1fa25fd 100644
--- a/src/cairo-types-private.h
+++ b/src/cairo-types-private.h
@@ -64,6 +64,8 @@ typedef struct _cairo_clip_path cairo_clip_path_t;
typedef struct _cairo_color cairo_color_t;
typedef struct _cairo_color_stop cairo_color_stop_t;
typedef struct _cairo_contour cairo_contour_t;
+typedef struct _cairo_contour_chain cairo_contour_chain_t;
+typedef struct _cairo_contour_iter cairo_contour_iter_t;
typedef struct _cairo_device_backend cairo_device_backend_t;
typedef struct _cairo_font_face_backend cairo_font_face_backend_t;
typedef struct _cairo_gstate cairo_gstate_t;
commit d58c0083762302e866598040134b86f3c3e0aeb5
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date: Tue Aug 9 10:37:15 2011 +0100
meh
diff --git a/perf/micro/many-strokes.c b/perf/micro/many-strokes.c
index f5b3a8c..7a31f0e 100644
--- a/perf/micro/many-strokes.c
+++ b/perf/micro/many-strokes.c
@@ -175,6 +175,8 @@ many_strokes (cairo_perf_t *perf, cairo_t *cr, int width, int height)
if (! cairo_perf_can_run (perf, "many-strokes", NULL))
return;
+ cairo_set_source_rgb (cr, 1., 1., 1.);
+
cairo_perf_run (perf, "many-strokes-halign", do_many_strokes_ha, NULL);
cairo_perf_run (perf, "many-strokes-valign", do_many_strokes_va, NULL);
cairo_perf_run (perf, "many-strokes-horizontal", do_many_strokes_h, NULL);
diff --git a/src/cairo-path-fixed.c b/src/cairo-path-fixed.c
index 5f754df..8d661f4 100644
--- a/src/cairo-path-fixed.c
+++ b/src/cairo-path-fixed.c
@@ -230,7 +230,7 @@ int
_cairo_path_fixed_num_ops (const cairo_path_fixed_t *path)
{
const cairo_path_buf_t *buf;
- int num_ops;
+ int num_ops = 0;
cairo_path_foreach_buf_start (buf, path) {
num_ops += buf->num_ops;
diff --git a/src/cairo-path-stroke-polygon.c b/src/cairo-path-stroke-polygon.c
index 6f7ba07..f73102d 100644
--- a/src/cairo-path-stroke-polygon.c
+++ b/src/cairo-path-stroke-polygon.c
@@ -358,6 +358,8 @@ inner_close (struct stroker *stroker,
if (1 || inner->contour.chain.num_points == 0) {
contour_add_point (stroker, inner, &in->point);
contour_add_point (stroker, inner, inpt);
+ *_cairo_contour_first_point (&inner->contour) =
+ *_cairo_contour_last_point (&inner->contour);
return;
}
@@ -428,8 +430,11 @@ outer_close (struct stroker *stroker,
outer = &stroker->ccw;
}
- if (close_enough (inpt, outpt, stroker->contour_tolerance))
+ if (close_enough (inpt, outpt, stroker->contour_tolerance)) {
+ *_cairo_contour_first_point (&outer->contour) =
+ *_cairo_contour_last_point (&outer->contour);
return;
+ }
switch (stroker->style.line_join) {
case CAIRO_LINE_JOIN_ROUND:
@@ -1122,14 +1127,23 @@ spline_to (void *closure,
if (dump)
_cairo_contour_add_point (&stroker->path, point);
#endif
-
- compute_face (point, tangent, stroker, &face);
-
- if (1) {
+ if (tangent->dx == 0 && tangent->dy == 0) {
const cairo_point_t *inpt, *outpt;
struct stroke_contour *outer;
+ cairo_point_t t;
int clockwise;
+ face = stroker->current_face;
+
+ face.usr_vector.x = -face.usr_vector.x;
+ face.usr_vector.y = -face.usr_vector.y;
+ face.dev_vector.dx = -face.dev_vector.dx;
+ face.dev_vector.dy = -face.dev_vector.dy;
+
+ t = face.cw;
+ face.cw = face.ccw;
+ face.ccw = t;
+
clockwise = join_is_clockwise (&stroker->current_face, &face);
if (clockwise) {
inpt = &stroker->current_face.cw;
@@ -1146,11 +1160,13 @@ spline_to (void *closure,
&face.dev_vector,
&stroker->current_face.point, inpt, outpt,
clockwise, outer);
+ } else {
+ compute_face (point, tangent, stroker, &face);
+ contour_add_point (stroker, &stroker->cw, &face.cw);
+ contour_add_point (stroker, &stroker->ccw, &face.ccw);
}
stroker->current_face = face;
- contour_add_point (stroker, &stroker->cw, &face.cw);
- contour_add_point (stroker, &stroker->ccw, &face.ccw);
return CAIRO_STATUS_SUCCESS;
}
commit ea0d7015eda875139887f540642978b8b499fff3
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date: Mon Aug 8 23:09:11 2011 +0100
disable
diff --git a/src/cairo-path-stroke-polygon.c b/src/cairo-path-stroke-polygon.c
index 5603bc7..6f7ba07 100644
--- a/src/cairo-path-stroke-polygon.c
+++ b/src/cairo-path-stroke-polygon.c
@@ -47,7 +47,7 @@
#include "cairo-path-fixed-private.h"
#include "cairo-slope-private.h"
-#define DEBUG 1
+#define DEBUG 0
static int dump;
static int dump_once;
@@ -299,6 +299,7 @@ inner_join (struct stroker *stroker,
negate = 0;
}
+#if 0
half_line_width = CAIRO_FIXED_ONE*CAIRO_FIXED_ONE/2 * stroker->style.line_width * out->length + .5;
/* On the inside, the previous end-point is always
@@ -326,6 +327,10 @@ prev:
compute_inner_joint (&last, d_last, p, d_p, half_line_width);
contour_add_point (stroker, inner, &last);
+#else
+ contour_add_point (stroker, inner, &in->point);
+ contour_add_point (stroker, inner, outpt);
+#endif
}
static void
@@ -351,7 +356,8 @@ inner_close (struct stroker *stroker,
}
if (1 || inner->contour.chain.num_points == 0) {
- contour_add_point (stroker, inner, outpt);
+ contour_add_point (stroker, inner, &in->point);
+ contour_add_point (stroker, inner, inpt);
return;
}
@@ -1221,9 +1227,9 @@ close_path (void *closure)
_cairo_debug_print_contour (file, &stroker->cw.contour);
_cairo_debug_print_contour (file, &stroker->ccw.contour);
fclose (file);
- }
- _cairo_contour_reset (&stroker->path);
+ _cairo_contour_reset (&stroker->path);
+ }
#endif
_cairo_contour_reset (&stroker->cw.contour);
_cairo_contour_reset (&stroker->ccw.contour);
@@ -1259,10 +1265,14 @@ _cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t *path,
polygon);
}
+#if 0
if (dump_once == 0 && _cairo_path_fixed_num_ops (path) > 1000) {
dump = 1;
dump_once = 1;
}
+#else
+ dump = 1;
+#endif
stroker.style = *style;
stroker.ctm = ctm;
commit bf6bb2ac512a2396ce4c7b6abf17513ce0bf7575
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date: Mon Aug 8 22:36:57 2011 +0100
spans-fix
diff --git a/src/cairo-tor-scan-converter.c b/src/cairo-tor-scan-converter.c
index 9cf9f2f..3f709ee 100644
--- a/src/cairo-tor-scan-converter.c
+++ b/src/cairo-tor-scan-converter.c
@@ -1699,7 +1699,7 @@ glitter_scan_converter_render(
/* Determine if we can ignore this row or use the full pixel
* stepper. */
- if (GRID_Y == EDGE_Y_BUCKET_HEIGHT && ! polygon->y_buckets[i]) {
+ if (! polygon->y_buckets[i]) {
if (active->head.next == &active->tail) {
active->min_height = INT_MAX;
for (; j < h && ! polygon->y_buckets[j]; j++)
commit 16f3a40412b16ea11498d364c5133443e4507767
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date: Sun Aug 7 17:24:37 2011 +0100
Add a HTML5 Canvas backend
Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>
diff --git a/boilerplate/Makefile.sources b/boilerplate/Makefile.sources
index 63ef34e..22caf8e 100644
--- a/boilerplate/Makefile.sources
+++ b/boilerplate/Makefile.sources
@@ -21,6 +21,7 @@ cairo_boilerplate_private = \
cairo_boilerplate_beos_cxx_sources = cairo-boilerplate-beos.cpp
cairo_boilerplate_directfb_sources = cairo-boilerplate-directfb.c
+cairo_boilerplate_canvas_sources = cairo-boilerplate-canvas.c
cairo_boilerplate_drm_sources = cairo-boilerplate-drm.c
cairo_boilerplate_glx_sources = cairo-boilerplate-glx.c
cairo_boilerplate_wgl_sources = cairo-boilerplate-wgl.c
diff --git a/boilerplate/Makefile.win32.features b/boilerplate/Makefile.win32.features
index 3542699..fc0b7c2 100644
--- a/boilerplate/Makefile.win32.features
+++ b/boilerplate/Makefile.win32.features
@@ -418,6 +418,18 @@ enabled_cairo_boilerplate_private += $(cairo_boilerplate_recording_private)
enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_recording_cxx_sources)
enabled_cairo_boilerplate_sources += $(cairo_boilerplate_recording_sources)
+unsupported_cairo_boilerplate_headers += $(cairo_boilerplate_canvas_headers)
+all_cairo_boilerplate_headers += $(cairo_boilerplate_canvas_headers)
+all_cairo_boilerplate_private += $(cairo_boilerplate_canvas_private)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_canvas_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_canvas_sources)
+ifeq ($(CAIRO_HAS_CANVAS_SURFACE),1)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_canvas_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_canvas_private)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_canvas_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_canvas_sources)
+endif
+
unsupported_cairo_boilerplate_headers += $(cairo_boilerplate_tee_headers)
all_cairo_boilerplate_headers += $(cairo_boilerplate_tee_headers)
all_cairo_boilerplate_private += $(cairo_boilerplate_tee_private)
diff --git a/boilerplate/cairo-boilerplate-canvas.c b/boilerplate/cairo-boilerplate-canvas.c
new file mode 100644
index 0000000..3d12087
--- /dev/null
+++ b/boilerplate/cairo-boilerplate-canvas.c
@@ -0,0 +1,172 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/*
+ * Copyright © 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.
+ *
+ * Author: Chris Wilson <chris at chris-wilson.co.uk>
+ *
+ */
+
+#include "cairo-boilerplate-private.h"
+
+#include <cairo-canvas.h>
+
+#if HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+
+static const cairo_user_data_key_t canvas_closure_key;
+
+typedef struct _canvas_target_closure
+{
+ cairo_device_t *device;
+ char *filename;
+ int width;
+ int height;
+} canvas_target_closure_t;
+
+#define ARRAY_LENGTH(__array) ((int) (sizeof (__array) / sizeof (__array[0])))
+
+static cairo_surface_t *
+_cairo_boilerplate_canvas_create_surface (const char *name,
+ cairo_content_t content,
+ double width,
+ double height,
+ double max_width,
+ double max_height,
+ cairo_boilerplate_mode_t mode,
+ int id,
+ void **closure)
+{
+ canvas_target_closure_t *ptc;
+ cairo_surface_t *surface;
+ cairo_status_t status;
+
+ *closure = ptc = xmalloc (sizeof (canvas_target_closure_t));
+
+ ptc->width = ceil (width);
+ ptc->height = ceil (height);
+
+ xasprintf (&ptc->filename, "%s.out.html", name);
+ xunlink (ptc->filename);
+
+ ptc->device = cairo_canvas_create (ptc->filename);
+ cairo_canvas_emit_basic_page (ptc->device);
+
+ surface = cairo_canvas_surface_create (ptc->device, NULL, width, height);
+ if (cairo_surface_status (surface))
+ goto CLEANUP_FILENAME;
+
+
+ status = cairo_surface_set_user_data (surface, &canvas_closure_key, ptc, NULL);
+ if (status == CAIRO_STATUS_SUCCESS)
+ return surface;
+
+ cairo_surface_destroy (surface);
+ surface = cairo_boilerplate_surface_create_in_error (status);
+
+ CLEANUP_FILENAME:
+ cairo_device_destroy (ptc->device);
+ free (ptc->filename);
+ free (ptc);
+ return surface;
+}
+
+static cairo_status_t
+_cairo_boilerplate_canvas_finish_surface (cairo_surface_t *surface)
+{
+ canvas_target_closure_t *ptc = cairo_surface_get_user_data (surface,
+ &canvas_closure_key);
+ cairo_status_t status;
+
+ cairo_surface_finish (surface);
+ status = cairo_surface_status (surface);
+ if (status)
+ return status;
+
+ cairo_device_destroy (ptc->device);
+ ptc->device = NULL;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_boilerplate_canvas_surface_write_to_png (cairo_surface_t *surface,
+ const char *filename)
+{
+ return CAIRO_STATUS_WRITE_ERROR;
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_canvas_convert_to_image (cairo_surface_t *surface,
+ int page)
+{
+ canvas_target_closure_t *ptc =
+ cairo_surface_get_user_data (surface, &canvas_closure_key);
+
+ return cairo_boilerplate_convert_to_image (ptc->filename, page);
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_canvas_get_image_surface (cairo_surface_t *surface,
+ int page,
+ int width,
+ int height)
+{
+ cairo_surface_t *image;
+
+ image = _cairo_boilerplate_canvas_convert_to_image (surface, page);
+ cairo_surface_set_device_offset (image,
+ cairo_image_surface_get_width (image) - width,
+ cairo_image_surface_get_height (image) - height);
+ surface = _cairo_boilerplate_get_image_surface (image, 0, width, height);
+ cairo_surface_destroy (image);
+
+ return surface;
+}
+
+static void
+_cairo_boilerplate_canvas_cleanup (void *closure)
+{
+ canvas_target_closure_t *ptc = closure;
+ cairo_device_destroy (ptc->device);
+ free (ptc->filename);
+ free (ptc);
+}
+
+
+static const cairo_boilerplate_target_t targets[] = {
+ {
+ "canvas", "canvas", ".canvas", NULL,
+ CAIRO_SURFACE_TYPE_CANVAS,
+ CAIRO_CONTENT_COLOR_ALPHA, 0,
+ "cairo_canvas_surface_create",
+ _cairo_boilerplate_canvas_create_surface,
+ cairo_surface_create_similar,
+ NULL,
+ _cairo_boilerplate_canvas_finish_surface,
+ _cairo_boilerplate_canvas_get_image_surface,
+ _cairo_boilerplate_canvas_surface_write_to_png,
+ _cairo_boilerplate_canvas_cleanup,
+ NULL, NULL, FALSE, TRUE, TRUE
+ },
+};
+CAIRO_BOILERPLATE (canvas, targets)
diff --git a/build/Makefile.win32.features b/build/Makefile.win32.features
index 5d75ba3..100e41c 100644
--- a/build/Makefile.win32.features
+++ b/build/Makefile.win32.features
@@ -31,6 +31,7 @@ CAIRO_HAS_PS_SURFACE=1
CAIRO_HAS_PDF_SURFACE=1
CAIRO_HAS_SVG_SURFACE=1
CAIRO_HAS_TEST_SURFACES=0
+CAIRO_HAS_CANVAS_SURFACE=0
CAIRO_HAS_TEE_SURFACE=0
CAIRO_HAS_XML_SURFACE=0
CAIRO_HAS_PTHREAD=0
diff --git a/build/Makefile.win32.features-h b/build/Makefile.win32.features-h
index 79de87d..edd5ca0 100644
--- a/build/Makefile.win32.features-h
+++ b/build/Makefile.win32.features-h
@@ -101,6 +101,9 @@ endif
@echo "#define CAIRO_HAS_IMAGE_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
@echo "#define CAIRO_HAS_MIME_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
@echo "#define CAIRO_HAS_RECORDING_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
+ifeq ($(CAIRO_HAS_CANVAS_SURFACE),1)
+ @echo "#define CAIRO_HAS_CANVAS_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
+endif
ifeq ($(CAIRO_HAS_TEE_SURFACE),1)
@echo "#define CAIRO_HAS_TEE_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
endif
diff --git a/build/configure.ac.features b/build/configure.ac.features
index 9c5680a..a6ca856 100644
--- a/build/configure.ac.features
+++ b/build/configure.ac.features
@@ -366,6 +366,7 @@ AC_DEFUN([CAIRO_REPORT],
echo " Image: yes (always builtin)"
echo " Recording: yes (always builtin)"
echo " Mime: yes (always builtin)"
+ echo " HTML5 Canvas: $use_canvas"
echo " Tee: $use_tee"
echo " XML: $use_xml"
echo " Skia: $use_skia"
diff --git a/configure.ac b/configure.ac
index 9fe6690..5df61a2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -614,6 +614,7 @@ dnl ===========================================================================
CAIRO_ENABLE_SURFACE_BACKEND(mime, mime, always)
CAIRO_ENABLE_SURFACE_BACKEND(recording, recording, always)
+CAIRO_ENABLE_SURFACE_BACKEND(canvas, HTML5 Canvas, no)
CAIRO_ENABLE_SURFACE_BACKEND(tee, tee, no)
CAIRO_ENABLE_SURFACE_BACKEND(xml, xml, no, [
use_xml=$have_libz
diff --git a/src/Makefile.sources b/src/Makefile.sources
index 8d6480e..2c505fc 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -399,5 +399,13 @@ cairo_tee_sources = cairo-tee-surface.c
cairo_xml_headers = cairo-xml.h
cairo_xml_sources = cairo-xml-surface.c
+cairo_canvas_headers = cairo-canvas.h
+cairo_canvas_private = cairo-canvas-private.h
+cairo_canvas_sources = \
+ cairo-canvas-context.c \
+ cairo-canvas-device.c \
+ cairo-canvas-surface.c \
+ $(NULL)
+
cairo_vg_headers = cairo-vg.h
cairo_vg_sources = cairo-vg-surface.c
diff --git a/src/Makefile.win32.features b/src/Makefile.win32.features
index 6e72fdb..a103dcf 100644
--- a/src/Makefile.win32.features
+++ b/src/Makefile.win32.features
@@ -540,6 +540,22 @@ enabled_cairo_private += $(cairo_recording_private)
enabled_cairo_cxx_sources += $(cairo_recording_cxx_sources)
enabled_cairo_sources += $(cairo_recording_sources)
+unsupported_cairo_headers += $(cairo_canvas_headers)
+all_cairo_headers += $(cairo_canvas_headers)
+all_cairo_private += $(cairo_canvas_private)
+all_cairo_cxx_sources += $(cairo_canvas_cxx_sources)
+all_cairo_sources += $(cairo_canvas_sources)
+ifeq ($(CAIRO_HAS_CANVAS_SURFACE),1)
+enabled_cairo_headers += $(cairo_canvas_headers)
+enabled_cairo_private += $(cairo_canvas_private)
+enabled_cairo_cxx_sources += $(cairo_canvas_cxx_sources)
+enabled_cairo_sources += $(cairo_canvas_sources)
+endif
+all_cairo_pkgconf += cairo-canvas.pc
+ifeq ($(CAIRO_HAS_CANVAS_SURFACE),1)
+enabled_cairo_pkgconf += cairo-canvas.pc
+endif
+
unsupported_cairo_headers += $(cairo_tee_headers)
all_cairo_headers += $(cairo_tee_headers)
all_cairo_private += $(cairo_tee_private)
diff --git a/src/cairo-backend-private.h b/src/cairo-backend-private.h
index 1dd5ea0..050f0d3 100644
--- a/src/cairo-backend-private.h
+++ b/src/cairo-backend-private.h
@@ -40,6 +40,7 @@
typedef enum _cairo_backend_type {
CAIRO_TYPE_DEFAULT,
+ CAIRO_TYPE_CANVAS,
CAIRO_TYPE_SKIA,
} cairo_backend_type_t;
diff --git a/src/cairo-canvas-context.c b/src/cairo-canvas-context.c
new file mode 100644
index 0000000..246a4f4
--- /dev/null
+++ b/src/cairo-canvas-context.c
@@ -0,0 +1,1296 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth at cworth.org>
+ * Chris Wilson <chris at chris-wilson.co.uk>
+ */
+
+/* Missing from Cairo:
+ * - separate fill/stroke styles
+ * - shadow
+ */
+
+#include "cairoint.h"
+
+#include "cairo-canvas-private.h"
+
+#include "cairo-private.h"
+#include "cairo-arc-private.h"
+#include "cairo-backend-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-freed-pool-private.h"
+#include "cairo-path-private.h"
+#include "cairo-pattern-private.h"
+
+/* Ideally all we map all of cairo's API directly onto the canvas.
+ * However, some operations (such as mask) cannot be supported and so
+ * we need to replay onto a fallback image and upload that instead.
+ * There we do everything twice...
+ */
+
+#define CAIRO_TOLERANCE_MINIMUM _cairo_fixed_to_double(1)
+
+#if !defined(INFINITY)
+#define INFINITY HUGE_VAL
+#endif
+
+static freed_pool_t context_pool;
+
+void
+_cairo_canvas_context_reset_static_data (void)
+{
+ _freed_pool_reset (&context_pool);
+}
+
+static void
+_cairo_canvas_context_destroy (void *abstract_cr)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+
+ _cairo_default_context_fini (&cr->base);
+
+ /* mark the context as invalid to protect against misuse */
+ cr->base.base.status = CAIRO_STATUS_NULL_POINTER;
+ _freed_pool_put (&context_pool, cr);
+}
+
+static cairo_surface_t *
+_cairo_canvas_context_get_original_target (void *abstract_cr)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+
+ return cr->original_target;
+}
+
+static cairo_surface_t *
+_cairo_canvas_context_get_current_target (void *abstract_cr)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+
+ return cr->target;
+}
+
+static cairo_status_t
+_cairo_canvas_context_save (void *abstract_cr)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ cairo_status_t status;
+
+ status = cr->default_backend->save (&cr->base);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_canvas_emit (cr->device, "cr%d.save()\n", cr->var);
+}
+
+static cairo_status_t
+_cairo_canvas_context_restore (void *abstract_cr)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ cairo_status_t status;
+
+ status = cr->default_backend->restore (&cr->base);
+ if (unlikely (status))
+ return status;
+
+ cr->clear = cr->default_backend->get_operator == CAIRO_OPERATOR_CLEAR;
+
+ return _cairo_canvas_emit (cr->device, "cr%d.restore()\n", cr->var);
+}
+
+static cairo_status_t
+_cairo_canvas_context_push_group (void *abstract_cr, cairo_content_t content)
+{
+}
+
+static cairo_pattern_t *
+_cairo_canvas_context_pop_group (void *abstract_cr)
+{
+}
+
+static cairo_status_t
+_cairo_canvas_context_set_source (void *abstract_cr,
+ cairo_pattern_t *source)
+{
+}
+
+static cairo_status_t
+_cairo_canvas_context_set_source_rgba (void *abstract_cr, double red, double green, double blue, double alpha)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ cairo_status_t status;
+
+ status = cr->default_backend->set_source_rgba (&cr->base,
+ red, green, blue, alpha);
+ if (unlikely (status))
+ return status;
+
+ red = _cairo_restrict_value (red, 0.0, 1.0);
+ green = _cairo_restrict_value (green, 0.0, 1.0);
+ blue = _cairo_restrict_value (blue, 0.0, 1.0);
+ alpha = _cairo_restrict_value (alpha, 0.0, 1.0);
+
+ if (CAIRO_ALPHA_IS_OPAQUE (alpha)) {
+ return _cairo_canvas_emit (cr->device,
+ "cr%d.fillStyle = cr%d.strokeStyle = '#%02x%02x%02x'\n",
+ cr->var, cr->var,
+ (int)(255*red),
+ (int)(255*green),
+ (int)(255*blue));
+ } else {
+ return _cairo_canvas_emit (cr->device,
+ "cr%d.fillStyle = cr%d.strokeStyle = 'rgba(%d, %d, %d, %d)'\n",
+ cr->var, cr->var,
+ (int)(255*red),
+ (int)(255*green),
+ (int)(255*blue),
+ (int)(255*alpha));
+ }
+}
+
+static cairo_status_t
+_cairo_canvas_context_set_source_surface (void *abstract_cr,
+ cairo_surface_t *surface,
+ double x,
+ double y)
+{
+}
+
+static cairo_pattern_t *
+_cairo_canvas_context_get_source (void *abstract_cr)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ return cr->default_backend->get_source (&cr->base);
+}
+
+static cairo_status_t
+_cairo_canvas_context_set_tolerance (void *abstract_cr,
+ double tolerance)
+{
+ /* XXX ignored */
+ cairo_canvas_context_t *cr = abstract_cr;
+ return cr->default_backend->set_tolerance (&cr->base, tolerance);
+}
+
+static cairo_status_t
+_cairo_canvas_context_set_operator (void *abstract_cr, cairo_operator_t op)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ cairo_status_t status;
+ static const char *map[] = {
+ [CAIRO_OPERATOR_ATOP] = "source-atop",
+ [CAIRO_OPERATOR_IN] = "source-in",
+ [CAIRO_OPERATOR_OUT] = "source-out",
+ [CAIRO_OPERATOR_OVER] = "source-over",
+ [CAIRO_OPERATOR_DEST_ATOP] = "destination-atop",
+ [CAIRO_OPERATOR_DEST_IN] = "destination-in",
+ [CAIRO_OPERATOR_DEST_OUT] = "destination-out",
+ [CAIRO_OPERATOR_DEST_OVER] = "destination-over",
+ [CAIRO_OPERATOR_LIGHTEN] = "lighter",
+ [CAIRO_OPERATOR_SOURCE] = "copy",
+ [CAIRO_OPERATOR_XOR] = "xor",
+ };
+
+ status = cr->default_backend->set_operator (&cr->base, op);
+ if (unlikely (status))
+ return status;
+
+ if (op == CAIRO_OPERATOR_CLEAR) {
+ cr->clear = TRUE;
+ return CAIRO_STATUS_SUCCESS;
+ }
+ cr->clear = FALSE;
+
+ /* XXX */
+
+ return _cairo_canvas_emit (cr->device,
+ "cr%d.globalCompositeOperation = '%s'\n",
+ cr->var, map[op]);
+}
+
+static cairo_status_t
+_cairo_canvas_context_set_opacity (void *abstract_cr, double opacity)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ cairo_status_t status;
+
+ status = cr->default_backend->set_opacity (&cr->base, opacity);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_canvas_emit (cr->device,
+ "cr%d.globalAlpha = %f\n",
+ cr->var,
+ opacity);
+}
+
+static cairo_status_t
+_cairo_canvas_context_set_antialias (void *abstract_cr,
+ cairo_antialias_t antialias)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ cairo_status_t status;
+
+ status = cr->default_backend->set_antialias (&cr->base, antialias);
+ if (unlikely (status))
+ return status;
+
+ /* XXX ignored */
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_canvas_context_set_fill_rule (void *abstract_cr,
+ cairo_fill_rule_t fill_rule)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ cairo_status_t status;
+
+ status = cr->default_backend->set_fill_rule (&cr->base, fill_rule);
+ if (unlikely (status))
+ return status;
+
+ /* XXX even-odd fallback! */
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_canvas_context_set_line_width (void *abstract_cr,
+ double line_width)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ cairo_status_t status;
+
+ status = cr->default_backend->set_line_width (&cr->base, line_width);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_canvas_emit (cr->device,
+ "cr%d.lineWidth = %f\n",
+ cr->var,
+ line_width);
+}
+
+static cairo_status_t
+_cairo_canvas_context_set_line_cap (void *abstract_cr,
+ cairo_line_cap_t line_cap)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ cairo_status_t status;
+ static const char *map[] = {
+ [CAIRO_LINE_CAP_BUTT] = "butt",
+ [CAIRO_LINE_CAP_ROUND] = "round",
+ [CAIRO_LINE_CAP_SQUARE] = "square"
+ };
+
+ status = cr->default_backend->set_line_cap (&cr->base, line_cap);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_canvas_emit (cr->device,
+ "cr%d.lineCap = '%s'\n",
+ cr->var,
+ map[line_cap]);
+}
+
+static cairo_status_t
+_cairo_canvas_context_set_line_join (void *abstract_cr,
+ cairo_line_join_t line_join)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ cairo_status_t status;
+ static const char *map[] = {
+ [CAIRO_LINE_JOIN_MITER] = "miter",
+ [CAIRO_LINE_JOIN_BEVEL] = "bevel",
+ [CAIRO_LINE_JOIN_ROUND] = "round"
+ };
+
+ status = cr->default_backend->set_line_join (&cr->base, line_join);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_canvas_emit (cr->device,
+ "cr%d.lineJoin = '%s'\n",
+ cr->var,
+ map[line_join]);
+}
+
+static cairo_status_t
+_cairo_canvas_context_set_dash (void *abstract_cr,
+ const double *dashes,
+ int num_dashes,
+ double offset)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ cairo_status_t status;
+
+ status = cr->default_backend->set_dash (&cr->base, dashes, num_dashes, offset);
+ if (unlikely (status))
+ return status;
+
+ /* XXX stroke-to-path fallback */
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_canvas_context_set_miter_limit (void *abstract_cr,
+ double limit)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ cairo_status_t status;
+
+ status = cr->default_backend->set_miter_limit (&cr->base, limit);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_canvas_emit (cr->device,
+ "cr%d.miterLimit = %f\n",
+ cr->var,
+ limit);
+}
+
+static cairo_antialias_t
+_cairo_canvas_context_get_antialias (void *abstract_cr)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ return cr->default_backend->get_antialias (&cr->base);
+}
+
+static void
+_cairo_canvas_context_get_dash (void *abstract_cr,
+ double *dashes,
+ int *num_dashes,
+ double *offset)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ return cr->default_backend->get_dash (&cr->base,
+ dashes, num_dashes, offset);
+}
+
+static cairo_fill_rule_t
+_cairo_canvas_context_get_fill_rule (void *abstract_cr)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ return cr->default_backend->get_fill_rule (&cr->base);
+}
+
+static double
+_cairo_canvas_context_get_line_width (void *abstract_cr)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ return cr->default_backend->get_line_width (&cr->base);
+}
+
+static cairo_line_cap_t
+_cairo_canvas_context_get_line_cap (void *abstract_cr)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ return cr->default_backend->get_line_cap (&cr->base);
+}
+
+static cairo_line_join_t
+_cairo_canvas_context_get_line_join (void *abstract_cr)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ return cr->default_backend->get_line_join (&cr->base);
+}
+
+static double
+_cairo_canvas_context_get_miter_limit (void *abstract_cr)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ return cr->default_backend->get_miter_limit (&cr->base);
+}
+
+static cairo_operator_t
+_cairo_canvas_context_get_operator (void *abstract_cr)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ return cr->default_backend->get_operator (&cr->base);
+}
+
+static double
+_cairo_canvas_context_get_opacity (void *abstract_cr)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ return cr->default_backend->get_opacity (&cr->base);
+}
+
+static double
+_cairo_canvas_context_get_tolerance (void *abstract_cr)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ return cr->default_backend->get_tolerance (&cr->base);
+}
+
+/* Current transformation matrix */
+
+static cairo_status_t
+_cairo_canvas_context_translate (void *abstract_cr,
+ double tx,
+ double ty)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ cairo_status_t status;
+
+ status = cr->default_backend->translate (&cr->base, tx, ty);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_canvas_emit (cr->device,
+ "cr%d.translate(%f, %f)\n",
+ cr->var,
+ tx, ty);
+}
+
+static cairo_status_t
+_cairo_canvas_context_scale (void *abstract_cr,
+ double sx,
+ double sy)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ cairo_status_t status;
+
+ status = cr->default_backend->scale (&cr->base, sx, sy);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_canvas_emit (cr->device,
+ "cr%d.scale(%f, %f)\n",
+ cr->var,
+ sx, sy);
+}
+
+static cairo_status_t
+_cairo_canvas_context_rotate (void *abstract_cr,
+ double theta)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ cairo_status_t status;
+
+ status = cr->default_backend->rotate (&cr->base, theta);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_canvas_emit (cr->device,
+ "cr%d.rotate(%f)\n",
+ cr->var,
+ theta);
+}
+
+static cairo_status_t
+_cairo_canvas_context_transform (void *abstract_cr,
+ const cairo_matrix_t *matrix)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ cairo_status_t status;
+
+ status = cr->default_backend->transform (&cr->base, matrix);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_canvas_emit (cr->device,
+ "cr%d.transform(%f, %f, %f, %f, %f, %f)\n",
+ cr->var,
+ matrix->xx, matrix->xy,
+ matrix->yx, matrix->yy,
+ matrix->x0, matrix->y0);
+}
+
+static cairo_status_t
+_cairo_canvas_context_set_matrix (void *abstract_cr,
+ const cairo_matrix_t *matrix)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ cairo_status_t status;
+
+ status = cr->default_backend->set_matrix (&cr->base, matrix);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_canvas_emit (cr->device,
+ "cr%d.setTransform(%f, %f, %f, %f, %f, %f)\n",
+ cr->var,
+ matrix->xx, matrix->xy,
+ matrix->yx, matrix->yy,
+ matrix->x0, matrix->y0);
+}
+
+static cairo_status_t
+_cairo_canvas_context_set_identity_matrix (void *abstract_cr)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ cairo_status_t status;
+
+ status = cr->default_backend->set_identity_matrix (&cr->base);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_canvas_emit (cr->device,
+ "cr%d.setTransform(1, 0, 0, 1, 0, 0)\n",
+ cr->var);
+}
+
+static void
+_cairo_canvas_context_get_matrix (void *abstract_cr,
+ cairo_matrix_t *matrix)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ cr->default_backend->get_matrix (&cr->base, matrix);
+}
+
+static void
+_cairo_canvas_context_user_to_device (void *abstract_cr,
+ double *x,
+ double *y)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ cr->default_backend->user_to_device (&cr->base, x, y);
+}
+
+static void
+_cairo_canvas_context_user_to_device_distance (void *abstract_cr, double *dx, double *dy)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ cr->default_backend->user_to_device_distance (&cr->base, dx, dy);
+}
+
+static void
+_cairo_canvas_context_device_to_user (void *abstract_cr,
+ double *x,
+ double *y)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ cr->default_backend->device_to_user (&cr->base, x, y);
+}
+
+static void
+_cairo_canvas_context_device_to_user_distance (void *abstract_cr,
+ double *dx,
+ double *dy)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ cr->default_backend->device_to_user_distance (&cr->base, dx, dy);
+}
+
+/* Path constructor */
+
+static cairo_status_t
+_cairo_canvas_context_new_path (void *abstract_cr)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ cairo_status_t status;
+
+ status = cr->default_backend->new_path (&cr->base);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_canvas_emit (cr->device, "cr%d.beginPath()\n", cr->var);
+}
+
+static cairo_status_t
+_cairo_canvas_context_new_sub_path (void *abstract_cr)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ return cr->default_backend->new_sub_path (&cr->base);
+}
+
+static cairo_status_t
+_cairo_canvas_context_move_to (void *abstract_cr, double x, double y)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ cairo_status_t status;
+
+ status = cr->default_backend->move_to (&cr->base, x, y);
+ if (unlikely (status))
+ return status;
+
+ cr->x = x;
+ cr->y = y;
+
+ return _cairo_canvas_emit (cr->device,
+ "cr%d.moveTo(%f, %f)\n",
+ cr->var,
+ x, y);
+}
+
+static cairo_status_t
+_cairo_canvas_context_line_to (void *abstract_cr, double x, double y)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ cairo_status_t status;
+
+ status = cr->default_backend->line_to (&cr->base, x, y);
+ if (unlikely (status))
+ return status;
+
+ cr->x = x;
+ cr->y = y;
+
+ return _cairo_canvas_emit (cr->device,
+ "cr%d.lineTo(%f, %f)\n",
+ cr->var,
+ x, y);
+}
+
+static cairo_status_t
+_cairo_canvas_context_curve_to (void *abstract_cr,
+ double x1, double y1,
+ double x2, double y2,
+ double x3, double y3)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ cairo_status_t status;
+
+ status = cr->default_backend->curve_to (&cr->base, x1, y1, x2, y2, x3, y3);
+ if (unlikely (status))
+ return status;
+
+ cr->x = x3;
+ cr->y = y3;
+
+ return _cairo_canvas_emit (cr->device,
+ "cr%d.bezierCurveTo(%f, %f, %f, %f, %f, %f)\n",
+ cr->var,
+ x1, y1, x2, y2, x3, y3);
+}
+
+static cairo_status_t
+_cairo_canvas_context_arc (void *abstract_cr,
+ double xc, double yc, double radius,
+ double angle1, double angle2,
+ cairo_bool_t forward)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ cairo_status_t status;
+
+ status = cr->default_backend->arc (&cr->base, xc, yc, radius, angle1, angle2, forward);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_canvas_emit (cr->device,
+ "cr%d.arc(%f, %f, %f, %f, %f%s)\n",
+ cr->var,
+ xc, yc, radius, angle1, angle2,
+ forward ? "" : ", false");
+}
+
+static cairo_status_t
+_cairo_canvas_context_rel_move_to (void *abstract_cr, double dx, double dy)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ return _cairo_canvas_context_move_to (cr, cr->x + dx, cr->y + dy);
+}
+
+static cairo_status_t
+_cairo_canvas_context_rel_line_to (void *abstract_cr, double dx, double dy)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ return _cairo_canvas_context_line_to (cr, cr->x + dx, cr->y + dy);
+}
+
+static cairo_status_t
+_cairo_canvas_context_rel_curve_to (void *abstract_cr,
+ double dx1, double dy1,
+ double dx2, double dy2,
+ double dx3, double dy3)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ return _cairo_canvas_context_curve_to (cr,
+ cr->x + dx1, cr->y + dy1,
+ cr->x + dx2, cr->y + dy2,
+ cr->x + dx3, cr->y + dy3);
+}
+
+static cairo_status_t
+_cairo_canvas_context_close_path (void *abstract_cr)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ cairo_status_t status;
+
+ status = cr->default_backend->close_path (&cr->base);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_canvas_emit (cr->device, "cr%d.closePath()\n", cr->var);
+}
+
+static cairo_status_t
+_cairo_canvas_context_rectangle (void *abstract_cr,
+ double x, double y,
+ double width, double height)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ cairo_status_t status;
+
+ status = cr->default_backend->rectangle (&cr->base, x, y, width,height);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_canvas_emit (cr->device,
+ "cr%d.rect(%f, %f, %f, %f)\n",
+ cr->var,
+ x, y,width, height);
+}
+
+static void
+_cairo_canvas_context_path_extents (void *abstract_cr,
+ double *x1, double *y1,
+ double *x2, double *y2)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ return cr->default_backend->path_extents (&cr->base, x1, y1, x2, y2);
+}
+
+static cairo_bool_t
+_cairo_canvas_context_has_current_point (void *abstract_cr)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ return cr->default_backend->has_current_point (&cr->base);
+}
+
+static cairo_bool_t
+_cairo_canvas_context_get_current_point (void *abstract_cr,
+ double *x,
+ double *y)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ return cr->default_backend->get_current_point (&cr->base, x, y);
+}
+
+static cairo_path_t *
+_cairo_canvas_context_copy_path (void *abstract_cr)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ return cr->default_backend->copy_path (&cr->base);
+}
+
+static cairo_path_t *
+_cairo_canvas_context_copy_path_flat (void *abstract_cr)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ return cr->default_backend->copy_path_flat (&cr->base);
+}
+
+static cairo_status_t
+_cairo_canvas_context_append_path (void *abstract_cr,
+ const cairo_path_t *path)
+{
+}
+
+static cairo_status_t
+_cairo_canvas_context_paint (void *abstract_cr)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ cairo_canvas_surface_t *target = (cairo_canvas_surface_t *)cr->target;
+ cairo_status_t status;
+
+ status = cr->default_backend->paint (&cr->base);
+ if (unlikely (status))
+ return status;
+
+ target->base.is_clear = FALSE;
+ if (cr->clear) {
+ return _cairo_canvas_emit (cr->device,
+ "cr%d.clearRect(0, 0, %d, %d)\n",
+ cr->var,
+ target->width, target->height);
+ } else {
+ return _cairo_canvas_emit (cr->device,
+ "cr%d.fillRect(0, 0, %d, %d)\n",
+ cr->var,
+ target->width, target->height);
+ }
+}
+
+static cairo_status_t
+_cairo_canvas_context_paint_with_alpha (void *abstract_cr,
+ double alpha)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ cairo_canvas_surface_t *target = (cairo_canvas_surface_t *)cr->target;
+ cairo_status_t status;
+ double opacity;
+
+ status = cr->default_backend->paint (&cr->base);
+ if (unlikely (status))
+ return status;
+
+ if (cr->clear) {
+ return _cairo_canvas_emit (cr->device,
+ "cr%d.clearRect(0, 0, %d, %d)\n",
+ cr->var,
+ target->width, target->height);
+ }
+
+ opacity = cr->default_backend->get_opacity (&cr->base);
+
+ _cairo_canvas_emit (cr->device, "cr%d.globalAlpha = %f\n",
+ cr->var,
+ alpha * opacity);
+
+ if (cr->default_backend->get_operator (&cr->base) == CAIRO_OPERATOR_CLEAR) {
+ _cairo_canvas_emit (cr->device,
+ "cr%d.clearRect(0, 0, %d, %d)\n",
+ cr->var,
+ target->width, target->height);
+ } else {
+ _cairo_canvas_emit (cr->device,
+ "cr%d.fillRect(0, 0, %d, %d)\n",
+ cr->var,
+ target->width, target->height);
+ }
+
+ target->base.is_clear = FALSE;
+ return _cairo_canvas_emit (cr->device, "cr.globalAlpha = %f", opacity);
+}
+
+static cairo_status_t
+_cairo_canvas_context_mask (void *abstract_cr,
+ cairo_pattern_t *mask)
+{
+}
+
+static cairo_status_t
+_cairo_canvas_context_stroke_preserve (void *abstract_cr)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ cairo_canvas_surface_t *target = (cairo_canvas_surface_t *)cr->target;
+ cairo_status_t status;
+
+ status = cr->default_backend->stroke_preserve (&cr->base);
+ if (unlikely (status))
+ return status;
+
+ if (cr->clear) {
+ _cairo_canvas_emit (cr->device,
+ "cr%d.globalCompositeOperation = 'dest-out'\n"
+ "cr%d.strokeStyle = 'white'\n",
+ cr->var, cr->var);
+ }
+
+ target->base.is_clear = FALSE;
+ return _cairo_canvas_emit (cr->device, "cr%d.stroke()\n", cr->var);
+}
+
+static cairo_status_t
+_cairo_canvas_context_stroke (void *abstract_cr)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+
+ _cairo_canvas_context_stroke_preserve (cr);
+ return _cairo_canvas_context_new_path (cr);
+}
+
+static cairo_status_t
+_cairo_canvas_context_in_stroke (void *abstract_cr,
+ double x, double y,
+ cairo_bool_t *inside)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ return cr->default_backend->in_stroke (&cr->base, x, y, inside);
+}
+
+static cairo_status_t
+_cairo_canvas_context_stroke_extents (void *abstract_cr,
+ double *x1, double *y1,
+ double *x2, double *y2)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ return cr->default_backend->stroke_extents (&cr->base, x1, y1, x2, y2);
+}
+
+static cairo_status_t
+_cairo_canvas_context_fill_preserve (void *abstract_cr)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ cairo_canvas_surface_t *target = (cairo_canvas_surface_t *)cr->target;
+ cairo_status_t status;
+
+ status = cr->default_backend->fill_preserve (&cr->base);
+ if (unlikely (status))
+ return status;
+
+ if (cr->clear) {
+ _cairo_canvas_emit (cr->device,
+ "cr%d.globalCompositeOperation = 'dest-out'\n"
+ "cr%d.strokeStyle = 'white'\n",
+ cr->var, cr->var);
+ }
+
+ target->base.is_clear = FALSE;
+ return _cairo_canvas_emit (cr->device, "cr%d.fill()\n", cr->var);
+}
+
+static cairo_status_t
+_cairo_canvas_context_fill (void *abstract_cr)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+
+ _cairo_canvas_context_fill_preserve (cr);
+ return _cairo_canvas_context_new_path (cr);
+}
+
+static cairo_status_t
+_cairo_canvas_context_in_fill (void *abstract_cr,
+ double x, double y,
+ cairo_bool_t *inside)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ return cr->default_backend->in_fill (&cr->base, x, y, inside);
+}
+
+static cairo_status_t
+_cairo_canvas_context_fill_extents (void *abstract_cr,
+ double *x1, double *y1,
+ double *x2, double *y2)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ return cr->default_backend->fill_extents (&cr->base, x1, y1, x2, y2);
+}
+
+static cairo_status_t
+_cairo_canvas_context_clip_preserve (void *abstract_cr)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ cairo_status_t status;
+
+ status = cr->default_backend->clip_preserve (&cr->base);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_canvas_emit (cr->device, "cr%d.clip()\n", cr->var);
+}
+
+static cairo_status_t
+_cairo_canvas_context_clip (void *abstract_cr)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+
+ _cairo_canvas_context_clip_preserve (cr);
+ return _cairo_canvas_context_new_path (cr);
+}
+
+static cairo_status_t
+_cairo_canvas_context_in_clip (void *abstract_cr,
+ double x, double y,
+ cairo_bool_t *inside)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ return cr->default_backend->in_clip (&cr->base, x, y, inside);
+}
+
+static cairo_status_t
+_cairo_canvas_context_reset_clip (void *abstract_cr)
+{
+ /* XXX */
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_canvas_context_clip_extents (void *abstract_cr,
+ double *x1, double *y1,
+ double *x2, double *y2)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ return cr->default_backend->clip_extents (&cr->base, x1, y1, x2, y2);
+}
+
+static cairo_rectangle_list_t *
+_cairo_canvas_context_copy_clip_rectangle_list (void *abstract_cr)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ return cr->default_backend->clip_copy_rectangle_list (&cr->base);
+}
+
+static cairo_status_t
+_cairo_canvas_context_copy_page (void *abstract_cr)
+{
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_canvas_context_show_page (void *abstract_cr)
+{
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_canvas_context_set_font_face (void *abstract_cr,
+ cairo_font_face_t *font_face)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ return cr->default_backend->set_font_face (&cr->base, font_face);
+}
+
+static cairo_font_face_t *
+_cairo_canvas_context_get_font_face (void *abstract_cr)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ return cr->default_backend->get_font_face (&cr->base);
+}
+
+static cairo_status_t
+_cairo_canvas_context_font_extents (void *abstract_cr,
+ cairo_font_extents_t *extents)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ return cr->default_backend->font_extents (&cr->base, extents);
+}
+
+static cairo_status_t
+_cairo_canvas_context_set_font_size (void *abstract_cr,
+ double size)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ return cr->default_backend->set_font_size (&cr->base, size);
+}
+
+static cairo_status_t
+_cairo_canvas_context_set_font_matrix (void *abstract_cr,
+ const cairo_matrix_t *matrix)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ return cr->default_backend->set_font_matrix (&cr->base, matrix);
+}
+
+static void
+_cairo_canvas_context_get_font_matrix (void *abstract_cr,
+ cairo_matrix_t *matrix)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ cr->default_backend->get_font_matrix (&cr->base, matrix);
+}
+
+static cairo_status_t
+_cairo_canvas_context_set_font_options (void *abstract_cr,
+ const cairo_font_options_t *options)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ return cr->default_backend->set_font_options (&cr->base, options);
+}
+
+static void
+_cairo_canvas_context_get_font_options (void *abstract_cr,
+ cairo_font_options_t *options)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ cr->default_backend->get_font_options (&cr->base, options);
+}
+
+static cairo_status_t
+_cairo_canvas_context_set_scaled_font (void *abstract_cr,
+ cairo_scaled_font_t *scaled_font)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ return cr->default_backend->set_scaled_font (&cr->base, scaled_font);
+}
+
+static cairo_scaled_font_t *
+_cairo_canvas_context_get_scaled_font (void *abstract_cr)
+{
+ cairo_canvas_context_t *cr = abstract_cr;
+ return cr->default_backend->get_scaled_font (&cr->base);
+}
+
+static cairo_status_t
+_cairo_canvas_context_glyphs (void *abstract_cr,
+ const cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_glyph_text_info_t *info)
+{
+}
+
+static cairo_status_t
+_cairo_canvas_context_glyph_path (void *abstract_cr,
+ const cairo_glyph_t *glyphs,
+ int num_glyphs)
+{
+}
+
+static cairo_status_t
+_cairo_canvas_context_glyph_extents (void *abstract_cr,
+ const cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_text_extents_t *extents)
+{
+}
+
+static const cairo_backend_t _cairo_canvas_context_backend = {
+ CAIRO_TYPE_CANVAS,
+ _cairo_canvas_context_destroy,
+
+ _cairo_canvas_context_get_original_target,
+ _cairo_canvas_context_get_current_target,
+
+ _cairo_canvas_context_save,
+ _cairo_canvas_context_restore,
+
+ _cairo_canvas_context_push_group,
+ _cairo_canvas_context_pop_group,
+
+ _cairo_canvas_context_set_source_rgba,
+ _cairo_canvas_context_set_source_surface,
+ _cairo_canvas_context_set_source,
+ _cairo_canvas_context_get_source,
+
+ _cairo_canvas_context_set_antialias,
+ _cairo_canvas_context_set_dash,
+ _cairo_canvas_context_set_fill_rule,
+ _cairo_canvas_context_set_line_cap,
+ _cairo_canvas_context_set_line_join,
+ _cairo_canvas_context_set_line_width,
+ _cairo_canvas_context_set_miter_limit,
+ _cairo_canvas_context_set_opacity,
+ _cairo_canvas_context_set_operator,
+ _cairo_canvas_context_set_tolerance,
+ _cairo_canvas_context_get_antialias,
+ _cairo_canvas_context_get_dash,
+ _cairo_canvas_context_get_fill_rule,
+ _cairo_canvas_context_get_line_cap,
+ _cairo_canvas_context_get_line_join,
+ _cairo_canvas_context_get_line_width,
+ _cairo_canvas_context_get_miter_limit,
+ _cairo_canvas_context_get_opacity,
+ _cairo_canvas_context_get_operator,
+ _cairo_canvas_context_get_tolerance,
+
+ _cairo_canvas_context_translate,
+ _cairo_canvas_context_scale,
+ _cairo_canvas_context_rotate,
+ _cairo_canvas_context_transform,
+ _cairo_canvas_context_set_matrix,
+ _cairo_canvas_context_set_identity_matrix,
+ _cairo_canvas_context_get_matrix,
+ _cairo_canvas_context_user_to_device,
+ _cairo_canvas_context_user_to_device_distance,
+ _cairo_canvas_context_device_to_user,
+ _cairo_canvas_context_device_to_user_distance,
+
+ _cairo_canvas_context_new_path,
+ _cairo_canvas_context_new_sub_path,
+ _cairo_canvas_context_move_to,
+ _cairo_canvas_context_rel_move_to,
+ _cairo_canvas_context_line_to,
+ _cairo_canvas_context_rel_line_to,
+ _cairo_canvas_context_curve_to,
+ _cairo_canvas_context_rel_curve_to,
+ NULL, /* arc-to */
+ NULL, /* rel-arc-to */
+ _cairo_canvas_context_close_path,
+ _cairo_canvas_context_arc,
+ _cairo_canvas_context_rectangle,
+ _cairo_canvas_context_path_extents,
+ _cairo_canvas_context_has_current_point,
+ _cairo_canvas_context_get_current_point,
+ _cairo_canvas_context_copy_path,
+ _cairo_canvas_context_copy_path_flat,
+ _cairo_canvas_context_append_path,
+
+ NULL, /* stroke-to-path */
+
+ _cairo_canvas_context_clip,
+ _cairo_canvas_context_clip_preserve,
+ _cairo_canvas_context_in_clip,
+ _cairo_canvas_context_clip_extents,
+ _cairo_canvas_context_reset_clip,
+ _cairo_canvas_context_copy_clip_rectangle_list,
+
+ _cairo_canvas_context_paint,
+ _cairo_canvas_context_paint_with_alpha,
+ _cairo_canvas_context_mask,
+
+ _cairo_canvas_context_stroke,
+ _cairo_canvas_context_stroke_preserve,
+ _cairo_canvas_context_in_stroke,
+ _cairo_canvas_context_stroke_extents,
+
+ _cairo_canvas_context_fill,
+ _cairo_canvas_context_fill_preserve,
+ _cairo_canvas_context_in_fill,
+ _cairo_canvas_context_fill_extents,
+
+ _cairo_canvas_context_set_font_face,
+ _cairo_canvas_context_get_font_face,
+ _cairo_canvas_context_set_font_size,
+ _cairo_canvas_context_set_font_matrix,
+ _cairo_canvas_context_get_font_matrix,
+ _cairo_canvas_context_set_font_options,
+ _cairo_canvas_context_get_font_options,
+ _cairo_canvas_context_set_scaled_font,
+ _cairo_canvas_context_get_scaled_font,
+ _cairo_canvas_context_font_extents,
+
+ _cairo_canvas_context_glyphs,
+ _cairo_canvas_context_glyph_path,
+ _cairo_canvas_context_glyph_extents,
+
+ _cairo_canvas_context_copy_page,
+ _cairo_canvas_context_show_page,
+};
+
+
+cairo_t *
+_cairo_canvas_context_create (void *_target)
+{
+ cairo_canvas_surface_t *target = _target;
+ cairo_canvas_context_t *cr;
+ cairo_status_t status;
+
+ cr = _freed_pool_get (&context_pool);
+ if (unlikely (cr == NULL)) {
+ cr = malloc (sizeof (cairo_canvas_context_t));
+ if (unlikely (cr == NULL))
+ return _cairo_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+
+ status = _cairo_default_context_init (&cr->base, target->recording);
+ if (unlikely (status)) {
+ _freed_pool_put (&context_pool, cr);
+ return _cairo_create_in_error (status);
+ }
+
+ cr->default_backend = cr->base.base.backend;
+ cr->base.base.backend = &_cairo_canvas_context_backend;
+
+ cr->target = &target->base;
+ cr->device = target->base.device;
+
+ cr->var = _cairo_canvas_create_context (cr->device, target);
+
+ return &cr->base.base;
+}
diff --git a/src/cairo-canvas-device.c b/src/cairo-canvas-device.c
new file mode 100644
index 0000000..6c6b281
--- /dev/null
+++ b/src/cairo-canvas-device.c
@@ -0,0 +1,245 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Chris Wilson <chris at chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-canvas.h"
+#include "cairo-canvas-private.h"
+
+#include "cairo-output-stream-private.h"
+
+#include <stdarg.h>
+
+#define _cairo_output_stream_puts(S, STR) \
+ _cairo_output_stream_write ((S), (STR), strlen (STR))
+
+static const char *basic_html_page_header =
+"<!DOCTYPE html>\n"
+"<html>\n"
+" <head>\n"
+" <title>Basic Canvas</title>\n"
+" </head>\n"
+" <body>\n";
+static const char *basic_html_page_footer =
+" </body>\n"
+"</html>\n";
+
+int
+_cairo_canvas_create_context (cairo_device_t *device,
+ cairo_canvas_surface_t *surface)
+{
+ cairo_canvas_device_t *canvas = (cairo_canvas_device_t *)device;
+ int id = canvas->context_id++;
+
+ if (canvas->emit_page && canvas->current == NULL ) {
+ _cairo_canvas_emit (device, "<script>\n");
+ canvas->indent += 2;
+ canvas->current = &surface->base;
+ }
+
+ _cairo_canvas_emit (device,
+ "var cr%d = document.getElementById('%s').getContext('2d')\n",
+ id, surface->id);
+
+ return id;
+}
+
+static void CAIRO_PRINTF_FORMAT ( 2, 0)
+__cairo_canvas_emit (cairo_canvas_device_t *canvas,
+ const char *format,
+ va_list va)
+{
+ char buf[80];
+
+ if (canvas->indent) {
+ memset(buf, ' ', canvas->indent);
+ _cairo_output_stream_write (canvas->stream, buf, canvas->indent);
+ }
+
+ _cairo_output_stream_vprintf (canvas->stream, format, va);
+}
+
+cairo_status_t
+_cairo_canvas_emit (cairo_device_t *device,
+ const char *format,
+ ...)
+{
+ cairo_canvas_device_t *canvas = (cairo_canvas_device_t *)device;
+ va_list ap;
+
+ va_start (ap, format);
+ __cairo_canvas_emit (canvas, format, ap);
+ va_end (ap);
+
+ return _cairo_output_stream_get_status (canvas->stream);
+}
+
+void
+_cairo_canvas_basic_page_emit (cairo_device_t *device,
+ const char *format,
+ ...)
+{
+ cairo_canvas_device_t *canvas = (cairo_canvas_device_t *)device;
+ va_list ap;
+
+ if (! canvas->emit_page)
+ return;
+
+ va_start (ap, format);
+ __cairo_canvas_emit (canvas, format, ap);
+ va_end (ap);
+}
+
+static cairo_status_t
+_cairo_canvas_device_flush (void *abstract_device)
+{
+ cairo_canvas_device_t *canvas = abstract_device;
+ cairo_status_t status;
+
+ status = _cairo_output_stream_flush (canvas->stream);
+
+ return status;
+}
+
+static void
+_cairo_canvas_device_destroy (void *abstract_device)
+{
+ cairo_canvas_device_t *canvas = abstract_device;
+
+ if (canvas->emit_page) {
+ if (canvas->current ) {
+ _cairo_canvas_emit (&canvas->base, "</script>\n");
+ canvas->indent -= 2;
+ }
+ _cairo_output_stream_puts (canvas->stream,
+ basic_html_page_footer);
+ }
+
+ _cairo_output_stream_destroy (canvas->stream);
+
+ free (canvas->images);
+ free (canvas);
+}
+
+static const cairo_device_backend_t _cairo_canvas_device_backend = {
+ CAIRO_DEVICE_TYPE_CANVAS,
+
+ NULL, NULL, /* lock, unlock */
+
+ _cairo_canvas_device_flush,
+ NULL, /* finish */
+ _cairo_canvas_device_destroy
+};
+
+static cairo_device_t *
+_cairo_canvas_create_internal (cairo_output_stream_t *stream)
+{
+ cairo_canvas_device_t *canvas;
+
+ canvas = malloc (sizeof (cairo_canvas_device_t));
+ if (unlikely (canvas == NULL))
+ return _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+ memset (canvas, 0, sizeof (cairo_canvas_device_t));
+
+ _cairo_device_init (&canvas->base, &_cairo_canvas_device_backend);
+ canvas->stream = stream;
+
+ return &canvas->base;
+}
+
+cairo_device_t *
+cairo_canvas_create (const char *filename)
+{
+ cairo_output_stream_t *stream;
+ cairo_status_t status;
+
+ stream = _cairo_output_stream_create_for_filename (filename);
+ if ((status = _cairo_output_stream_get_status (stream)))
+ return _cairo_device_create_in_error (status);
+
+ return _cairo_canvas_create_internal (stream);
+}
+
+cairo_device_t *
+cairo_canvas_create_for_stream (cairo_write_func_t write_func,
+ void *closure)
+{
+ cairo_output_stream_t *stream;
+ cairo_status_t status;
+
+ stream = _cairo_output_stream_create (write_func, NULL, closure);
+ if ((status = _cairo_output_stream_get_status (stream)))
+ return _cairo_device_create_in_error (status);
+
+ return _cairo_canvas_create_internal (stream);
+}
+
+void
+cairo_canvas_emit_basic_page (cairo_device_t *device)
+{
+ cairo_canvas_device_t *canvas;
+
+ if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_CANVAS))
+ return;
+
+ if (unlikely (device->status))
+ return;
+
+ canvas = (cairo_canvas_device_t *)device;
+ canvas->emit_page = TRUE;
+ _cairo_output_stream_puts (canvas->stream, basic_html_page_header);
+ canvas->indent = 4;
+}
+
+void
+cairo_canvas_set_image_directory (cairo_device_t *device,
+ const char *directory)
+{
+ cairo_canvas_device_t *canvas;
+
+ if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_CANVAS))
+ return;
+
+ if (unlikely (device->status))
+ return;
+
+ canvas = (cairo_canvas_device_t *)device;
+ free (canvas->images);
+ if (directory)
+ canvas->images = strdup (directory);
+ else
+ canvas->images = NULL;
+}
diff --git a/src/cairo-canvas-private.h b/src/cairo-canvas-private.h
new file mode 100644
index 0000000..5f79c44
--- /dev/null
+++ b/src/cairo-canvas-private.h
@@ -0,0 +1,102 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Chris Wilson <chris at chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_CANVAS_PRIVATE_H
+#define CAIRO_CANVAS_PRIVATE_H
+
+#include "cairo-default-context-private.h"
+#include "cairo-device-private.h"
+#include "cairo-surface-private.h"
+
+typedef struct _cairo_canvas_context cairo_canvas_context_t;
+typedef struct _cairo_canvas_device cairo_canvas_device_t;
+typedef struct _cairo_canvas_surface cairo_canvas_surface_t;
+
+struct _cairo_canvas_context {
+ cairo_default_context_t base;
+ const cairo_backend_t *default_backend;
+
+ cairo_device_t *device;
+ int var;
+
+ cairo_surface_t *target;
+ cairo_surface_t *original_target;
+ cairo_surface_t *parent_target;
+ cairo_bool_t clear;
+
+ double x, y;
+};
+
+struct _cairo_canvas_device {
+ cairo_device_t base;
+
+ cairo_output_stream_t *stream;
+ int indent;
+ int canvas_id;
+ int context_id;
+
+ cairo_surface_t *current;
+
+ cairo_bool_t emit_page;
+ char *images;
+};
+
+struct _cairo_canvas_surface {
+ cairo_surface_t base;
+
+ char *id;
+ int width, height;
+
+ cairo_surface_t *recording;
+};
+
+cairo_private cairo_t *
+_cairo_canvas_context_create (void *target);
+
+cairo_private void
+_cairo_canvas_basic_page_emit (cairo_device_t *device,
+ const char *format,
+ ...) CAIRO_PRINTF_FORMAT ( 2, 3);
+
+cairo_private int
+_cairo_canvas_create_context (cairo_device_t *device,
+ cairo_canvas_surface_t *surface);
+
+cairo_private_no_warn cairo_status_t
+_cairo_canvas_emit (cairo_device_t *device,
+ const char *format,
+ ...) CAIRO_PRINTF_FORMAT ( 2, 3);
+
+#endif /* CAIRO_CANVAS_PRIVATE_H */
diff --git a/src/cairo-canvas-surface.c b/src/cairo-canvas-surface.c
new file mode 100644
index 0000000..c36d405
--- /dev/null
+++ b/src/cairo-canvas-surface.c
@@ -0,0 +1,187 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Chris Wilson <chris at chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-canvas.h"
+#include "cairo-canvas-private.h"
+
+#include "cairo-recording-surface-private.h"
+
+static cairo_status_t
+_cairo_canvas_surface_finish (void *abstract_surface)
+{
+ cairo_canvas_surface_t *surface = abstract_surface;
+
+ free (surface->id);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_surface_t *
+_cairo_canvas_surface_create_similar (void *abstract_other,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ cairo_canvas_surface_t *other = abstract_other;
+
+ return cairo_canvas_surface_create (other->base.device, NULL,
+ width, height);
+}
+
+static cairo_bool_t
+_cairo_canvas_surface_get_extents (void *abstract_surface,
+ cairo_rectangle_int_t *rectangle)
+{
+ cairo_canvas_surface_t *surface = abstract_surface;
+
+ rectangle->x = 0;
+ rectangle->y = 0;
+ rectangle->width = surface->width;
+ rectangle->height = surface->height;
+
+ return TRUE;
+}
+
+static cairo_surface_t *
+_cairo_canvas_surface_snapshot (void *abstract_surface)
+{
+ cairo_canvas_surface_t *surface = abstract_surface;
+
+ return _cairo_surface_snapshot (surface->recording);
+}
+
+static const cairo_surface_backend_t
+_cairo_canvas_surface_backend = {
+ CAIRO_SURFACE_TYPE_CANVAS,
+ _cairo_canvas_surface_finish,
+ _cairo_canvas_context_create,
+
+ _cairo_canvas_surface_create_similar,
+ NULL, /* create_similar_image */
+ NULL, /* map_to_image */
+ NULL, /* unmap_image */
+
+ NULL, NULL, /* source image */
+ NULL, NULL, /* dst image */
+ NULL, /* clone_similar */
+ NULL, /* composite */
+ NULL, /* fill_rectangles */
+ NULL, /* composite_trapezoids */
+ NULL, /* create_span_renderer */
+ NULL, /* check_span_renderer */
+ NULL, NULL, /* copy/show page */
+ _cairo_canvas_surface_get_extents,
+ NULL, /* old_show_glyphs */
+ NULL, /* get_font_options */
+ NULL, /* flush */
+ NULL, /* mark_dirty_rectangle */
+ NULL, /* font fini */
+ NULL, /* scaled_glyph_fini */
+
+ /* The 5 high level operations */
+ /* XXX allow for replay? */
+ NULL, /* paint */
+ NULL, /* mask */
+ NULL, /* stroke */
+ NULL, /* fill */
+ NULL, /* glyphs */
+
+ _cairo_canvas_surface_snapshot,
+
+ NULL, /* is_similar */
+ NULL, /* fill_stroke */
+ NULL, /* create_solid_pattern_surface */
+ NULL, /* can_repaint_solid_pattern_surface */
+
+ /* The alternate high-level text operation */
+ NULL, NULL, /* has, show_text_glyphs */
+};
+
+cairo_surface_t *
+cairo_canvas_surface_create (cairo_device_t *device,
+ const char *id,
+ int width, int height)
+{
+ cairo_canvas_surface_t *surface;
+ cairo_canvas_device_t *canvas;
+
+ if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_CANVAS))
+ return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
+
+ if (unlikely (device->status))
+ return _cairo_surface_create_in_error (device->status);
+
+ canvas = (cairo_canvas_device_t *)device;
+
+
+ surface = malloc (sizeof (cairo_canvas_surface_t));
+ if (unlikely (surface == NULL))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ _cairo_surface_init (&surface->base,
+ &_cairo_canvas_surface_backend, device,
+ CAIRO_CONTENT_COLOR_ALPHA);
+
+ surface->width = width;
+ surface->height = height;
+ surface->base.is_clear = TRUE;
+
+ surface->recording =
+ cairo_recording_surface_create (surface->base.content, NULL);
+
+ if (id) {
+ surface->id = strdup (id);
+ } else {
+ char buf[80], len;
+ len = sprintf (buf, "cairo-canvas-%d", ++canvas->canvas_id);
+ surface->id = malloc (len+1);
+ if (surface->id)
+ memcpy (surface->id, buf, len+1);
+ }
+ if (unlikely (surface->id == NULL)) {
+ free (surface);
+ return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ _cairo_canvas_basic_page_emit (device,
+ "<canvas id=\"%s\" width=\"%dpx\" height=\"%dpx\">\n",
+ surface->id,
+ surface->width,
+ surface->height);
+ _cairo_canvas_basic_page_emit (device, "</canvas>\n");
+
+ return &surface->base;
+}
diff --git a/src/cairo-canvas.h b/src/cairo-canvas.h
new file mode 100644
index 0000000..b9ef824
--- /dev/null
+++ b/src/cairo-canvas.h
@@ -0,0 +1,69 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson
+ *
+ * Contributor(s):
+ * Chris Wilson <chris at chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_CANVAS_H
+#define CAIRO_CANVAS_H
+
+#include "cairo.h"
+
+#if CAIRO_HAS_CANVAS_SURFACE
+
+CAIRO_BEGIN_DECLS
+
+cairo_public cairo_device_t *
+cairo_canvas_create (const char *filename);
+
+cairo_public cairo_device_t *
+cairo_canvas_create_for_stream (cairo_write_func_t write_func,
+ void *closure);
+
+cairo_public void
+cairo_canvas_emit_basic_page (cairo_device_t *canvas);
+
+cairo_public void
+cairo_canvas_set_image_directory (cairo_device_t *canvas,
+ const char *directory);
+
+cairo_public cairo_surface_t *
+cairo_canvas_surface_create (cairo_device_t *canvas,
+ const char *id, int width, int height);
+
+CAIRO_END_DECLS
+
+#else /*CAIRO_HAS_CANVAS_SURFACE*/
+# error Cairo was not compiled with support for the HTML5 Canvas backend
+#endif /*CAIRO_HAS_CANVAS_SURFACE*/
+
+#endif /*CAIRO_CANVAS_H*/
diff --git a/src/cairo-debug.c b/src/cairo-debug.c
index 6afdb3f..162335c 100644
--- a/src/cairo-debug.c
+++ b/src/cairo-debug.c
@@ -89,6 +89,10 @@ cairo_debug_reset_static_data (void)
_cairo_drm_device_reset_static_data ();
#endif
+#if CAIRO_HAS_CANVAS_SURFACE
+ _cairo_canvas_context_reset_static_data ();
+#endif
+
_cairo_default_context_reset_static_data ();
CAIRO_MUTEX_FINALIZE ();
diff --git a/src/cairo-default-context-private.h b/src/cairo-default-context-private.h
index 32fd12d..4933cdd 100644
--- a/src/cairo-default-context-private.h
+++ b/src/cairo-default-context-private.h
@@ -55,4 +55,11 @@ struct _cairo_default_context {
cairo_private cairo_t *
_cairo_default_context_create (void *target);
+cairo_private cairo_status_t
+_cairo_default_context_init (cairo_default_context_t *cr,
+ void *target);
+
+cairo_private void
+_cairo_default_context_fini (cairo_default_context_t *cr);
+
#endif /* CAIRO_DEFAULT_CONTEXT_PRIVATE_H */
diff --git a/src/cairo-default-context.c b/src/cairo-default-context.c
index 4124632..79cddc6 100644
--- a/src/cairo-default-context.c
+++ b/src/cairo-default-context.c
@@ -63,11 +63,9 @@ _cairo_default_context_reset_static_data (void)
_freed_pool_reset (&context_pool);
}
-static void
-_cairo_default_context_destroy (void *abstract_cr)
+void
+_cairo_default_context_fini (cairo_default_context_t *cr)
{
- cairo_default_context_t *cr = abstract_cr;
-
while (cr->gstate != &cr->gstate_tail[0]) {
if (_cairo_gstate_restore (&cr->gstate, &cr->gstate_freelist))
break;
@@ -84,6 +82,14 @@ _cairo_default_context_destroy (void *abstract_cr)
_cairo_path_fixed_fini (cr->path);
_cairo_fini (&cr->base);
+}
+
+static void
+_cairo_default_context_destroy (void *abstract_cr)
+{
+ cairo_default_context_t *cr = abstract_cr;
+
+ _cairo_default_context_fini (cr);
/* mark the context as invalid to protect against misuse */
cr->base.status = CAIRO_STATUS_NULL_POINTER;
@@ -1381,6 +1387,20 @@ static const cairo_backend_t _cairo_default_context_backend = {
_cairo_default_context_show_page,
};
+cairo_status_t
+_cairo_default_context_init (cairo_default_context_t *cr,
+ void *target)
+{
+ _cairo_init (&cr->base, &_cairo_default_context_backend);
+ _cairo_path_fixed_init (cr->path);
+
+ cr->gstate = &cr->gstate_tail[0];
+ cr->gstate_freelist = &cr->gstate_tail[1];
+ cr->gstate_tail[1].next = NULL;
+
+ return _cairo_gstate_init (cr->gstate, target);
+}
+
cairo_t *
_cairo_default_context_create (void *target)
{
@@ -1394,14 +1414,7 @@ _cairo_default_context_create (void *target)
return _cairo_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
}
- _cairo_init (&cr->base, &_cairo_default_context_backend);
- _cairo_path_fixed_init (cr->path);
-
- cr->gstate = &cr->gstate_tail[0];
- cr->gstate_freelist = &cr->gstate_tail[1];
- cr->gstate_tail[1].next = NULL;
-
- status = _cairo_gstate_init (cr->gstate, target);
+ status = _cairo_default_context_init (cr, target);
if (unlikely (status)) {
_freed_pool_put (&context_pool, cr);
return _cairo_create_in_error (status);
diff --git a/src/cairo.h b/src/cairo.h
index 24b9f0d..e2114b4 100644
--- a/src/cairo.h
+++ b/src/cairo.h
@@ -2017,7 +2017,7 @@ cairo_device_reference (cairo_device_t *device);
* @CAIRO_DEVICE_TYPE_XCB: The device is of type xcb
* @CAIRO_DEVICE_TYPE_XLIB: The device is of type xlib
* @CAIRO_DEVICE_TYPE_XML: The device is of type XML
- * @CAIRO_DEVICE_TYPE_INVALID: The device is invalid
+ * @CAIRO_DEVICE_TYPE_CANVAS: The device is of type HTML5 Canvas (since 1.12)
*
* #cairo_device_type_t is used to describe the type of a given
* device. The devices types are also known as "backends" within cairo.
@@ -2045,6 +2045,7 @@ typedef enum _cairo_device_type {
CAIRO_DEVICE_TYPE_XCB,
CAIRO_DEVICE_TYPE_XLIB,
CAIRO_DEVICE_TYPE_XML,
+ CAIRO_DEVICE_TYPE_CANVAS,
CAIRO_DEVICE_TYPE_INVALID = -1
} cairo_device_type_t;
@@ -2158,6 +2159,7 @@ cairo_surface_status (cairo_surface_t *surface);
* @CAIRO_SURFACE_TYPE_SKIA: The surface is of type Skia, since 1.10
* @CAIRO_SURFACE_TYPE_SUBSURFACE: The surface is a subsurface created with
* cairo_surface_create_for_rectangle(), since 1.10
+ * @CAIRO_SURFACE_TYPE_CANVAS: The surface is of type HTML5 Canvas, since 1.12
* @CAIRO_SURFACE_TYPE_FASTIMAGE: The surface is of type fast-image, since 1.12
*
* #cairo_surface_type_t is used to describe the type of a given
@@ -2209,6 +2211,7 @@ typedef enum _cairo_surface_type {
CAIRO_SURFACE_TYPE_XML,
CAIRO_SURFACE_TYPE_SKIA,
CAIRO_SURFACE_TYPE_SUBSURFACE,
+ CAIRO_SURFACE_TYPE_CANVAS,
CAIRO_SURFACE_TYPE_FASTIMAGE
} cairo_surface_type_t;
diff --git a/src/cairoint.h b/src/cairoint.h
index 0d3790b..1976a7a 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -430,6 +430,9 @@ cairo_private void
_cairo_default_context_reset_static_data (void);
cairo_private void
+_cairo_canvas_context_reset_static_data (void);
+
+cairo_private void
_cairo_toy_font_face_reset_static_data (void);
cairo_private void
commit 06bba6a2a90cee3694645743053117df04091108
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date: Sun Aug 7 11:54:00 2011 +0100
moah
diff --git a/src/cairo-path-fixed-private.h b/src/cairo-path-fixed-private.h
index e24b1c7..aa3a6b9 100644
--- a/src/cairo-path-fixed-private.h
+++ b/src/cairo-path-fixed-private.h
@@ -182,4 +182,7 @@ _cairo_path_fixed_fill_maybe_region (const cairo_path_fixed_t *path)
path->current_point.y == path->last_move_point.y;
}
+cairo_private int
+_cairo_path_fixed_num_ops (const cairo_path_fixed_t *path);
+
#endif /* CAIRO_PATH_FIXED_PRIVATE_H */
diff --git a/src/cairo-path-fixed.c b/src/cairo-path-fixed.c
index 8c1e3ab..5f754df 100644
--- a/src/cairo-path-fixed.c
+++ b/src/cairo-path-fixed.c
@@ -226,6 +226,19 @@ _cairo_path_fixed_size (const cairo_path_fixed_t *path)
num_points * sizeof (buf->points[0]);
}
+int
+_cairo_path_fixed_num_ops (const cairo_path_fixed_t *path)
+{
+ const cairo_path_buf_t *buf;
+ int num_ops;
+
+ cairo_path_foreach_buf_start (buf, path) {
+ num_ops += buf->num_ops;
+ } cairo_path_foreach_buf_end (buf, path);
+
+ return num_ops;
+}
+
cairo_bool_t
_cairo_path_fixed_equal (const cairo_path_fixed_t *a,
const cairo_path_fixed_t *b)
diff --git a/src/cairo-path-stroke-polygon.c b/src/cairo-path-stroke-polygon.c
index 94c2c05..5603bc7 100644
--- a/src/cairo-path-stroke-polygon.c
+++ b/src/cairo-path-stroke-polygon.c
@@ -49,6 +49,9 @@
#define DEBUG 1
+static int dump;
+static int dump_once;
+
struct stroker {
cairo_stroke_style_t style;
@@ -81,7 +84,7 @@ struct stroker {
cairo_stroke_face_t first_face;
};
-static inline void
+static inline double
normalize_slope (double *dx, double *dy);
static void
@@ -227,36 +230,48 @@ join_is_clockwise (const cairo_stroke_face_t *in,
return _cairo_slope_compare (&in->dev_vector, &out->dev_vector) < 0;
}
-static double
+static cairo_int64_t
distance_from_face (const cairo_stroke_face_t *face,
- const cairo_point_t *p)
+ const cairo_point_t *p,
+ cairo_bool_t negate)
{
- return (p->x - face->point.x) * face->dev_slope.y - (p->y - face->point.y) * face->dev_slope.x;
+ int32_t dx = (p->x - face->point.x);
+ int32_t dy = (p->y - face->point.y);
+ cairo_int64_t d;
+
+ d = _cairo_int64_sub (_cairo_int32x32_64_mul (dx, face->dev_vector.dy),
+ _cairo_int32x32_64_mul (dy, face->dev_vector.dx));
+ if (negate)
+ d = _cairo_int64_negate (d);
+ return d;
}
-static double
+static cairo_int64_t
distance_along_face (const cairo_stroke_face_t *face,
const cairo_point_t *p)
{
- return (p->x - face->point.x) * face->dev_slope.x + (p->y - face->point.y) * face->dev_slope.y;
+ int32_t dx = (p->x - face->point.x);
+ int32_t dy = (p->y - face->point.y);
+ return _cairo_int64_add (_cairo_int32x32_64_mul (dx, face->dev_vector.dx),
+ _cairo_int32x32_64_mul (dy, face->dev_vector.dy));
}
-
-static double
-distance_from_edge (struct stroker *stroker,
- const cairo_point_t *p,
- const cairo_point_t *e1,
- const cairo_point_t *e2)
+static void
+compute_inner_joint (cairo_point_t *p1, cairo_int64_t d_p1,
+ const cairo_point_t *p2, cairo_int64_t d_p2,
+ cairo_int64_t half_line_width)
{
- cairo_stroke_face_t edge;
- cairo_slope_t slope;
+ int32_t dx = p2->x - p1->x;
+ int32_t dy = p2->y - p1->y;
- _cairo_slope_init (&slope, e1, e2);
- if (slope.dx == 0 && slope.dy == 0)
- return 0;
+ half_line_width = _cairo_int64_sub (half_line_width, d_p1);
+ d_p2 = _cairo_int64_sub (d_p2, d_p1);
- compute_face (e1, &slope, stroker, &edge);
- return fabs (distance_from_face (&edge, p));
+ p1->x += _cairo_int_96by64_32x64_divrem (_cairo_int64x32_128_mul (half_line_width, dx),
+ d_p2).quo;
+
+ p1->y += _cairo_int_96by64_32x64_divrem (_cairo_int64x32_128_mul (half_line_width, dy),
+ d_p2).quo;
}
static void
@@ -268,32 +283,29 @@ inner_join (struct stroker *stroker,
cairo_point_t last;
const cairo_point_t *p, *outpt;
struct stroke_contour *inner;
- double d_p, d_last = -1;
- double dot;
- double line_width;
- double sign;
+ cairo_int64_t d_p, d_last;
+ cairo_int64_t half_line_width;
+ cairo_bool_t negate;
/* XXX line segments shorter than line-width */
if (clockwise) {
inner = &stroker->ccw;
outpt = &out->ccw;
- sign = -1;
+ negate = 1;
} else {
inner = &stroker->cw;
outpt = &out->cw;
- sign = 1;
+ negate = 0;
}
- line_width = stroker->style.line_width/2;
- line_width *= CAIRO_FIXED_ONE;
+ half_line_width = CAIRO_FIXED_ONE*CAIRO_FIXED_ONE/2 * stroker->style.line_width * out->length + .5;
/* On the inside, the previous end-point is always
* closer to the new face by definition.
*/
last = *_cairo_contour_last_point (&inner->contour);
- d_last = sign * distance_from_face (out, &last);
- assert (d_last <= line_width);
+ d_last = distance_from_face (out, &last, negate);
_cairo_contour_remove_last_point (&inner->contour);
prev:
@@ -302,8 +314,9 @@ prev:
return;
}
p = _cairo_contour_last_point (&inner->contour);
- if ((d_p = sign * distance_from_face (out, p)) <= line_width &&
- distance_along_face (out, p) >= 0)
+ d_p = distance_from_face (out, p, negate);
+ if (_cairo_int64_lt (d_p, half_line_width) &&
+ !_cairo_int64_negative (distance_along_face (out, p)))
{
last = *p;
d_last = d_p;
@@ -311,11 +324,7 @@ prev:
goto prev;
}
- if (d_p != d_last) { /* parallel edge */
- dot = (line_width - d_last) / (d_p - d_last);
- last.x += dot * (p->x - last.x);
- last.y += dot * (p->y - last.y);
- }
+ compute_inner_joint (&last, d_last, p, d_p, half_line_width);
contour_add_point (stroker, inner, &last);
}
@@ -328,10 +337,6 @@ inner_close (struct stroker *stroker,
const cairo_point_t *p, *outpt, *inpt;
struct stroke_contour *inner;
struct _cairo_contour_chain *chain;
- double d_p, d_last = -1;
- double line_width;
- double sign;
- int i;
/* XXX line segments shorter than line-width */
@@ -339,19 +344,18 @@ inner_close (struct stroker *stroker,
inner = &stroker->ccw;
outpt = &in->ccw;
inpt = &out->ccw;
- sign = -1;
} else {
inner = &stroker->cw;
outpt = &in->cw;
inpt = &out->cw;
- sign = 1;
}
- if (inner->contour.chain.num_points == 0) {
+ if (1 || inner->contour.chain.num_points == 0) {
contour_add_point (stroker, inner, outpt);
return;
}
+#if 0
line_width = stroker->style.line_width/2;
line_width *= CAIRO_FIXED_ONE;
@@ -390,6 +394,7 @@ out:
*pp = last;
}
}
+#endif
}
static void
@@ -571,6 +576,8 @@ outer_close (struct stroker *stroker,
p.y = _cairo_fixed_from_double (my);
*_cairo_contour_last_point (&outer->contour) = p;
+ *_cairo_contour_first_point (&outer->contour) = p;
+ return;
}
}
break;
@@ -606,9 +613,6 @@ outer_join (struct stroker *stroker,
outer = &stroker->ccw;
}
- if (close_enough (inpt, outpt, stroker->contour_tolerance))
- return;
-
switch (stroker->style.line_join) {
case CAIRO_LINE_JOIN_ROUND:
/* construct a fan around the common midpoint */
@@ -751,7 +755,7 @@ outer_join (struct stroker *stroker,
* Make sure the miter point line lies between the two
* faces by comparing the slopes
*/
- if (slope_compare_sgn (fdx1, fdy1, mdx, mdy) !=
+ if (1 || slope_compare_sgn (fdx1, fdy1, mdx, mdy) !=
slope_compare_sgn (fdx2, fdy2, mdx, mdy))
{
cairo_point_t p;
@@ -852,7 +856,7 @@ add_trailing_cap (struct stroker *stroker,
add_cap (stroker, face, c);
}
-static inline void
+static inline double
normalize_slope (double *dx, double *dy)
{
double dx0 = *dx, dy0 = *dy;
@@ -883,6 +887,8 @@ normalize_slope (double *dx, double *dy)
*dx = dx0 / mag;
*dy = dy0 / mag;
}
+
+ return mag;
}
static void
@@ -897,7 +903,7 @@ compute_face (const cairo_point_t *point,
slope_dx = _cairo_fixed_to_double (dev_slope->dx);
slope_dy = _cairo_fixed_to_double (dev_slope->dy);
- normalize_slope (&slope_dx, &slope_dy);
+ face->length = normalize_slope (&slope_dx, &slope_dy);
face->dev_slope.x = slope_dx;
face->dev_slope.y = slope_dy;
@@ -973,28 +979,39 @@ add_caps (struct stroker *stroker)
_cairo_polygon_add_contour (stroker->polygon, &stroker->ccw.contour);
_cairo_contour_reset (&stroker->ccw.contour);
} else {
+ if (stroker->has_current_face)
+ add_trailing_cap (stroker, &stroker->current_face, &stroker->ccw);
+
#if DEBUG
- {
+ if (dump) {
FILE *file = fopen ("contours.txt", "a");
_cairo_debug_print_contour (file, &stroker->path);
_cairo_debug_print_contour (file, &stroker->cw.contour);
_cairo_debug_print_contour (file, &stroker->ccw.contour);
fclose (file);
+ _cairo_contour_reset (&stroker->path);
}
- _cairo_contour_reset (&stroker->path);
#endif
- if (stroker->has_current_face)
- add_trailing_cap (stroker, &stroker->current_face, &stroker->ccw);
-
_cairo_polygon_add_contour (stroker->polygon, &stroker->ccw.contour);
_cairo_contour_reset (&stroker->ccw.contour);
if (stroker->has_first_face) {
+ _cairo_contour_add_point (&stroker->ccw.contour,
+ &stroker->first_face.cw);
add_leading_cap (stroker, &stroker->first_face, &stroker->ccw);
+#if DEBUG
+ if (dump) {
+ FILE *file = fopen ("contours.txt", "a");
+ _cairo_debug_print_contour (file, &stroker->ccw.contour);
+ fclose (file);
+ }
+#endif
+
+ _cairo_polygon_add_contour (stroker->polygon,
+ &stroker->ccw.contour);
+ _cairo_contour_reset (&stroker->ccw.contour);
}
- _cairo_polygon_add_contour (stroker->polygon, &stroker->ccw.contour);
- _cairo_contour_reset (&stroker->ccw.contour);
_cairo_polygon_add_contour (stroker->polygon, &stroker->cw.contour);
_cairo_contour_reset (&stroker->cw.contour);
@@ -1020,7 +1037,8 @@ move_to (void *closure,
stroker->first_point = *point;
#if DEBUG
- _cairo_contour_add_point (&stroker->path, point);
+ if (dump)
+ _cairo_contour_add_point (&stroker->path, point);
#endif
stroker->current_face.point = *point;
@@ -1043,7 +1061,8 @@ line_to (void *closure,
return CAIRO_STATUS_SUCCESS;
#if DEBUG
- _cairo_contour_add_point (&stroker->path, point);
+ if (dump)
+ _cairo_contour_add_point (&stroker->path, point);
#endif
_cairo_slope_init (&dev_slope, p1, point);
@@ -1094,7 +1113,8 @@ spline_to (void *closure,
cairo_stroke_face_t face;
#if DEBUG
- _cairo_contour_add_point (&stroker->path, point);
+ if (dump)
+ _cairo_contour_add_point (&stroker->path, point);
#endif
compute_face (point, tangent, stroker, &face);
@@ -1195,7 +1215,7 @@ close_path (void *closure)
_cairo_polygon_add_contour (stroker->polygon, &stroker->ccw.contour);
#if DEBUG
- {
+ if (dump) {
FILE *file = fopen ("contours.txt", "a");
_cairo_debug_print_contour (file, &stroker->path);
_cairo_debug_print_contour (file, &stroker->cw.contour);
@@ -1239,6 +1259,11 @@ _cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t *path,
polygon);
}
+ if (dump_once == 0 && _cairo_path_fixed_num_ops (path) > 1000) {
+ dump = 1;
+ dump_once = 1;
+ }
+
stroker.style = *style;
stroker.ctm = ctm;
stroker.ctm_inverse = ctm_inverse;
@@ -1263,7 +1288,8 @@ _cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t *path,
stroker.has_initial_sub_path = FALSE;
#if DEBUG
- _cairo_contour_init (&stroker.path, 0);
+ if (dump)
+ _cairo_contour_init (&stroker.path, 0);
#endif
_cairo_contour_init (&stroker.cw.contour, 1);
_cairo_contour_init (&stroker.ccw.contour, -1);
@@ -1286,5 +1312,14 @@ _cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t *path,
_cairo_contour_fini (&stroker.ccw.contour);
_cairo_pen_fini (&stroker.pen);
+#if DEBUG
+ if (dump) {
+ FILE *file = fopen ("polygons.txt", "a");
+ _cairo_debug_print_polygon (file, polygon);
+ fclose (file);
+ dump = 0;
+ }
+#endif
+
return status;
}
diff --git a/src/cairoint.h b/src/cairoint.h
index c6243be..0d3790b 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -1032,6 +1032,7 @@ typedef struct _cairo_stroke_face {
cairo_slope_t dev_vector;
cairo_point_double_t dev_slope;
cairo_point_double_t usr_vector;
+ double length;
} cairo_stroke_face_t;
/* cairo.c */
commit 877c958932451f4f8d1b3a6ffb3f2a2dfbd6f8d9
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date: Sat Aug 6 10:26:03 2011 +0100
progress?
diff --git a/src/cairo-path-fixed.c b/src/cairo-path-fixed.c
index 7af45f8..8c1e3ab 100644
--- a/src/cairo-path-fixed.c
+++ b/src/cairo-path-fixed.c
@@ -1128,7 +1128,7 @@ _cpf_curve_to (void *closure,
cairo_point_t *p0 = &cpf->current_point;
if (! _cairo_spline_init (&spline,
- cpf->line_to,
+ (cairo_spline_add_point_func_t)cpf->line_to,
cpf->closure,
p0, p1, p2, p3))
{
diff --git a/src/cairo-path-in-fill.c b/src/cairo-path-in-fill.c
index 3738874..1787fb1 100644
--- a/src/cairo-path-in-fill.c
+++ b/src/cairo-path-in-fill.c
@@ -217,7 +217,7 @@ _cairo_in_fill_curve_to (void *closure,
/* XXX Investigate direct inspection of the inflections? */
if (! _cairo_spline_init (&spline,
- _cairo_in_fill_line_to,
+ (cairo_spline_add_point_func_t)_cairo_in_fill_line_to,
in_fill,
&in_fill->current_point, b, c, d))
{
diff --git a/src/cairo-path-stroke-polygon.c b/src/cairo-path-stroke-polygon.c
index fed3dba..94c2c05 100644
--- a/src/cairo-path-stroke-polygon.c
+++ b/src/cairo-path-stroke-polygon.c
@@ -59,18 +59,17 @@ struct stroker {
struct stroke_contour {
/* Note that these are not strictly contours as they may intersect */
cairo_contour_t contour;
- cairo_uint64_t tolerance;
} cw, ccw;
cairo_polygon_t *polygon;
const cairo_matrix_t *ctm;
const cairo_matrix_t *ctm_inverse;
double tolerance;
+ cairo_uint64_t contour_tolerance;
cairo_bool_t ctm_det_positive;
cairo_pen_t pen;
- cairo_point_t current_point;
cairo_point_t first_point;
cairo_bool_t has_initial_sub_path;
@@ -85,6 +84,12 @@ struct stroker {
static inline void
normalize_slope (double *dx, double *dy);
+static void
+compute_face (const cairo_point_t *point,
+ const cairo_slope_t *dev_slope,
+ struct stroker *stroker,
+ cairo_stroke_face_t *face);
+
static cairo_uint64_t
_cairo_point_distance2 (const cairo_point_t *p1,
const cairo_point_t *p2)
@@ -95,18 +100,20 @@ _cairo_point_distance2 (const cairo_point_t *p1,
}
static cairo_bool_t
-close_enough (const struct stroke_contour *c,
- const cairo_point_t *p1,
- const cairo_point_t *p2)
+close_enough (const cairo_point_t *p1,
+ const cairo_point_t *p2,
+ cairo_uint64_t tolerance)
{
- return _cairo_int64_lt (_cairo_point_distance2 (p1, p2), c->tolerance);
+ return _cairo_int64_lt (_cairo_point_distance2 (p1, p2), tolerance);
}
static void
-contour_add_point (struct stroke_contour *c,
+contour_add_point (struct stroker *stroker,
+ struct stroke_contour *c,
const cairo_point_t *point)
{
- if (! close_enough (c,point, _cairo_contour_last_point (&c->contour)))
+ if (! close_enough (point, _cairo_contour_last_point (&c->contour),
+ stroker->contour_tolerance))
_cairo_contour_add_point (&c->contour, point);
}
@@ -209,7 +216,7 @@ add_fan (struct stroker *stroker,
{
cairo_point_t p = *midpt;
translate_point (&p, &stroker->pen.vertices[i].point);
- contour_add_point (c, &p);
+ contour_add_point (stroker, c, &p);
}
}
@@ -227,10 +234,36 @@ distance_from_face (const cairo_stroke_face_t *face,
return (p->x - face->point.x) * face->dev_slope.y - (p->y - face->point.y) * face->dev_slope.x;
}
+static double
+distance_along_face (const cairo_stroke_face_t *face,
+ const cairo_point_t *p)
+{
+ return (p->x - face->point.x) * face->dev_slope.x + (p->y - face->point.y) * face->dev_slope.y;
+}
+
+
+static double
+distance_from_edge (struct stroker *stroker,
+ const cairo_point_t *p,
+ const cairo_point_t *e1,
+ const cairo_point_t *e2)
+{
+ cairo_stroke_face_t edge;
+ cairo_slope_t slope;
+
+ _cairo_slope_init (&slope, e1, e2);
+ if (slope.dx == 0 && slope.dy == 0)
+ return 0;
+
+ compute_face (e1, &slope, stroker, &edge);
+ return fabs (distance_from_face (&edge, p));
+}
+
static void
inner_join (struct stroker *stroker,
const cairo_stroke_face_t *in,
- const cairo_stroke_face_t *out)
+ const cairo_stroke_face_t *out,
+ int clockwise)
{
cairo_point_t last;
const cairo_point_t *p, *outpt;
@@ -242,40 +275,48 @@ inner_join (struct stroker *stroker,
/* XXX line segments shorter than line-width */
- if (join_is_clockwise (in, out)) {
+ if (clockwise) {
inner = &stroker->ccw;
outpt = &out->ccw;
sign = -1;
} else {
- outpt = &out->cw;
inner = &stroker->cw;
+ outpt = &out->cw;
sign = 1;
}
line_width = stroker->style.line_width/2;
line_width *= CAIRO_FIXED_ONE;
+
+ /* On the inside, the previous end-point is always
+ * closer to the new face by definition.
+ */
+ last = *_cairo_contour_last_point (&inner->contour);
+ d_last = sign * distance_from_face (out, &last);
+ assert (d_last <= line_width);
+ _cairo_contour_remove_last_point (&inner->contour);
+
prev:
if (inner->contour.chain.num_points == 0) {
- contour_add_point (inner, outpt);
+ contour_add_point (stroker, inner, outpt);
return;
}
p = _cairo_contour_last_point (&inner->contour);
- if ((d_p = sign * distance_from_face (out, p)) <= line_width) {
+ if ((d_p = sign * distance_from_face (out, p)) <= line_width &&
+ distance_along_face (out, p) >= 0)
+ {
last = *p;
d_last = d_p;
_cairo_contour_remove_last_point (&inner->contour);
goto prev;
}
- if (d_last < 0) {
- //contour_add_point (inner, outpt);
- return;
+ if (d_p != d_last) { /* parallel edge */
+ dot = (line_width - d_last) / (d_p - d_last);
+ last.x += dot * (p->x - last.x);
+ last.y += dot * (p->y - last.y);
}
-
- dot = (line_width - d_last) / (d_p - d_last);
- last.x += dot * (p->x - last.x);
- last.y += dot * (p->y - last.y);
- contour_add_point (inner, &last);
+ contour_add_point (stroker, inner, &last);
}
static void
@@ -284,11 +325,10 @@ inner_close (struct stroker *stroker,
cairo_stroke_face_t *out)
{
cairo_point_t last;
- const cairo_point_t *p, *outpt;
+ const cairo_point_t *p, *outpt, *inpt;
struct stroke_contour *inner;
struct _cairo_contour_chain *chain;
double d_p, d_last = -1;
- double dot;
double line_width;
double sign;
int i;
@@ -298,39 +338,48 @@ inner_close (struct stroker *stroker,
if (join_is_clockwise (in, out)) {
inner = &stroker->ccw;
outpt = &in->ccw;
+ inpt = &out->ccw;
sign = -1;
} else {
- outpt = &in->cw;
inner = &stroker->cw;
+ outpt = &in->cw;
+ inpt = &out->cw;
sign = 1;
}
if (inner->contour.chain.num_points == 0) {
- contour_add_point (inner, outpt);
+ contour_add_point (stroker, inner, outpt);
return;
}
line_width = stroker->style.line_width/2;
line_width *= CAIRO_FIXED_ONE;
+ d_last = sign * distance_from_face (out, outpt);
+ last = *outpt;
+
for (chain = &inner->contour.chain; chain; chain = chain->next) {
for (i = 0; i < chain->num_points; i++) {
p = &chain->points[i];
- if ((d_p = sign * distance_from_face (in, p)) >= line_width)
+ if ((d_p = sign * distance_from_face (in, p)) >= line_width &&
+ distance_from_edge (stroker, inpt, &last, p) < line_width)
{
- printf ("point [%d] =%f, last_dp=%f\n", i,d_p, d_last);
goto out;
}
- last = *p;
- d_last = d_p;
+ if (p->x != last.x || p->y != last.y) {
+ last = *p;
+ d_last = d_p;
+ }
}
}
out:
- dot = (line_width - d_last) / (d_p - d_last);
- last.x += dot * (p->x - last.x);
- last.y += dot * (p->y - last.y);
+ if (d_p != d_last) {
+ double dot = (line_width - d_last) / (d_p - d_last);
+ last.x += dot * (p->x - last.x);
+ last.y += dot * (p->y - last.y);
+ }
*_cairo_contour_last_point (&inner->contour) = last;
for (chain = &inner->contour.chain; chain; chain = chain->next) {
@@ -368,7 +417,7 @@ outer_close (struct stroker *stroker,
outer = &stroker->ccw;
}
- if (close_enough (outer, inpt, outpt))
+ if (close_enough (inpt, outpt, stroker->contour_tolerance))
return;
switch (stroker->style.line_join) {
@@ -530,24 +579,23 @@ outer_close (struct stroker *stroker,
case CAIRO_LINE_JOIN_BEVEL:
break;
}
- contour_add_point (outer, outpt);
+ contour_add_point (stroker, outer, outpt);
}
static void
-join (struct stroker *stroker,
- const cairo_stroke_face_t *in,
- const cairo_stroke_face_t *out)
+outer_join (struct stroker *stroker,
+ const cairo_stroke_face_t *in,
+ const cairo_stroke_face_t *out,
+ int clockwise)
{
const cairo_point_t *inpt, *outpt;
struct stroke_contour *outer;
- int clockwise;
if (in->cw.x == out->cw.x && in->cw.y == out->cw.y &&
in->ccw.x == out->ccw.x && out->ccw.y == out->ccw.y)
{
return;
}
- clockwise = join_is_clockwise (in, out);
if (clockwise) {
inpt = &in->cw;
outpt = &out->cw;
@@ -558,7 +606,7 @@ join (struct stroker *stroker,
outer = &stroker->ccw;
}
- if (close_enough (outer, inpt, outpt))
+ if (close_enough (inpt, outpt, stroker->contour_tolerance))
return;
switch (stroker->style.line_join) {
@@ -721,7 +769,7 @@ join (struct stroker *stroker,
case CAIRO_LINE_JOIN_BEVEL:
break;
}
- contour_add_point (outer, outpt);
+ contour_add_point (stroker,outer, outpt);
}
static void
@@ -762,15 +810,15 @@ add_cap (struct stroker *stroker,
quad[2].y = f->cw.y + fvector.dy;
quad[3] = f->cw;
- contour_add_point (c, &quad[1]);
- contour_add_point (c, &quad[2]);
+ contour_add_point (stroker, c, &quad[1]);
+ contour_add_point (stroker, c, &quad[2]);
}
case CAIRO_LINE_CAP_BUTT:
default:
break;
}
- contour_add_point (c, &f->cw);
+ contour_add_point (stroker, c, &f->cw);
}
static void
@@ -788,6 +836,7 @@ add_leading_cap (struct stroker *stroker,
reversed.usr_vector.y = -reversed.usr_vector.y;
reversed.dev_vector.dx = -reversed.dev_vector.dx;
reversed.dev_vector.dy = -reversed.dev_vector.dy;
+
t = reversed.cw;
reversed.cw = reversed.ccw;
reversed.ccw = t;
@@ -837,8 +886,10 @@ normalize_slope (double *dx, double *dy)
}
static void
-compute_face (const cairo_point_t *point, cairo_slope_t *dev_slope,
- struct stroker *stroker, cairo_stroke_face_t *face)
+compute_face (const cairo_point_t *point,
+ const cairo_slope_t *dev_slope,
+ struct stroker *stroker,
+ cairo_stroke_face_t *face)
{
double face_dx, face_dy;
cairo_point_t offset_ccw, offset_cw;
@@ -913,8 +964,7 @@ add_caps (struct stroker *stroker)
cairo_slope_t slope = { CAIRO_FIXED_ONE, 0 };
cairo_stroke_face_t face;
- /* arbitrarily choose first_point
- * first_point and current_point should be the same */
+ /* arbitrarily choose first_point */
compute_face (&stroker->first_point, &slope, stroker, &face);
add_leading_cap (stroker, &face, &stroker->ccw);
@@ -937,8 +987,6 @@ add_caps (struct stroker *stroker)
if (stroker->has_current_face)
add_trailing_cap (stroker, &stroker->current_face, &stroker->ccw);
- //_cairo_contour_simplify (&stroker->ccw.contour, stroker->tolerance);
-
_cairo_polygon_add_contour (stroker->polygon, &stroker->ccw.contour);
_cairo_contour_reset (&stroker->ccw.contour);
@@ -948,13 +996,15 @@ add_caps (struct stroker *stroker)
_cairo_polygon_add_contour (stroker->polygon, &stroker->ccw.contour);
_cairo_contour_reset (&stroker->ccw.contour);
- //_cairo_contour_simplify (&stroker->cw.contour, stroker->tolerance);
_cairo_polygon_add_contour (stroker->polygon, &stroker->cw.contour);
_cairo_contour_reset (&stroker->cw.contour);
}
}
static cairo_status_t
+close_path (void *closure);
+
+static cairo_status_t
move_to (void *closure,
const cairo_point_t *point)
{
@@ -963,16 +1013,17 @@ move_to (void *closure,
/* Cap the start and end of the previous sub path as needed */
add_caps (stroker);
+ stroker->has_first_face = FALSE;
+ stroker->has_current_face = FALSE;
+ stroker->has_initial_sub_path = FALSE;
+
stroker->first_point = *point;
- stroker->current_point = *point;
#if DEBUG
_cairo_contour_add_point (&stroker->path, point);
#endif
- stroker->has_first_face = FALSE;
- stroker->has_current_face = FALSE;
- stroker->has_initial_sub_path = FALSE;
+ stroker->current_face.point = *point;
return CAIRO_STATUS_SUCCESS;
}
@@ -983,7 +1034,7 @@ line_to (void *closure,
{
struct stroker *stroker = closure;
cairo_stroke_face_t start;
- cairo_point_t *p1 = &stroker->current_point;
+ cairo_point_t *p1 = &stroker->current_face.point;
cairo_slope_t dev_slope;
stroker->has_initial_sub_path = TRUE;
@@ -999,9 +1050,16 @@ line_to (void *closure,
compute_face (p1, &dev_slope, stroker, &start);
if (stroker->has_current_face) {
+ int clockwise = join_is_clockwise (&stroker->current_face, &start);
/* Join with final face from previous segment */
- join (stroker, &stroker->current_face, &start);
- inner_join (stroker, &stroker->current_face, &start);
+ if (! close_enough (&stroker->current_face.ccw, &start.ccw,
+ stroker->contour_tolerance) ||
+ ! close_enough (&stroker->current_face.cw, &start.cw,
+ stroker->contour_tolerance))
+ {
+ outer_join (stroker, &stroker->current_face, &start, clockwise);
+ inner_join (stroker, &stroker->current_face, &start, clockwise);
+ }
} else {
if (! stroker->has_first_face) {
/* Save sub path's first face in case needed for closing join */
@@ -1010,8 +1068,8 @@ line_to (void *closure,
}
stroker->has_current_face = TRUE;
- contour_add_point (&stroker->cw, &start.cw);
- contour_add_point (&stroker->ccw, &start.ccw);
+ contour_add_point (stroker, &stroker->cw, &start.cw);
+ contour_add_point (stroker, &stroker->ccw, &start.ccw);
}
stroker->current_face = start;
@@ -1021,36 +1079,52 @@ line_to (void *closure,
stroker->current_face.cw.x += dev_slope.dx;
stroker->current_face.cw.y += dev_slope.dy;
- contour_add_point (&stroker->cw, &stroker->current_face.cw);
- contour_add_point (&stroker->ccw, &stroker->current_face.ccw);
-
- stroker->current_point = *point;
+ contour_add_point (stroker, &stroker->cw, &stroker->current_face.cw);
+ contour_add_point (stroker, &stroker->ccw, &stroker->current_face.ccw);
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
spline_to (void *closure,
- const cairo_point_t *point)
+ const cairo_point_t *point,
+ const cairo_slope_t *tangent)
{
struct stroker *stroker = closure;
- cairo_slope_t dev_slope;
-
- if (stroker->current_point.x == point->x &&
- stroker->current_point.y == point->y)
- return CAIRO_STATUS_SUCCESS;
+ cairo_stroke_face_t face;
#if DEBUG
_cairo_contour_add_point (&stroker->path, point);
#endif
- _cairo_slope_init (&dev_slope, &stroker->current_point, point);
- compute_face (point, &dev_slope, stroker, &stroker->current_face);
+ compute_face (point, tangent, stroker, &face);
- contour_add_point (&stroker->cw, &stroker->current_face.cw);
- contour_add_point (&stroker->ccw, &stroker->current_face.ccw);
+ if (1) {
+ const cairo_point_t *inpt, *outpt;
+ struct stroke_contour *outer;
+ int clockwise;
- stroker->current_point = *point;
+ clockwise = join_is_clockwise (&stroker->current_face, &face);
+ if (clockwise) {
+ inpt = &stroker->current_face.cw;
+ outpt = &face.cw;
+ outer = &stroker->cw;
+ } else {
+ inpt = &stroker->current_face.ccw;
+ outpt = &face.ccw;
+ outer = &stroker->ccw;
+ }
+
+ add_fan (stroker,
+ &stroker->current_face.dev_vector,
+ &face.dev_vector,
+ &stroker->current_face.point, inpt, outpt,
+ clockwise, outer);
+ }
+
+ stroker->current_face = face;
+ contour_add_point (stroker, &stroker->cw, &face.cw);
+ contour_add_point (stroker, &stroker->ccw, &face.ccw);
return CAIRO_STATUS_SUCCESS;
}
@@ -1064,49 +1138,35 @@ curve_to (void *closure,
struct stroker *stroker = closure;
cairo_spline_t spline;
cairo_stroke_face_t face;
- cairo_status_t status = CAIRO_STATUS_SUCCESS;
if (! _cairo_spline_init (&spline, spline_to, stroker,
- &stroker->current_point, b, c, d))
+ &stroker->current_face.point, b, c, d))
{
return line_to (closure, d);
}
- /* Compute the initial face */
- compute_face (&stroker->current_point,
- &spline.initial_slope,
+ compute_face (&stroker->current_face.point, &spline.initial_slope,
stroker, &face);
if (stroker->has_current_face) {
- join (stroker, &stroker->current_face, &face);
- inner_join (stroker, &stroker->current_face, &face);
+ int clockwise = join_is_clockwise (&stroker->current_face, &face);
+ /* Join with final face from previous segment */
+ outer_join (stroker, &stroker->current_face, &face, clockwise);
+ inner_join (stroker, &stroker->current_face, &face, clockwise);
} else {
- contour_add_point (&stroker->cw, &face.cw);
- contour_add_point (&stroker->ccw, &face.ccw);
-
if (! stroker->has_first_face) {
/* Save sub path's first face in case needed for closing join */
stroker->first_face = face;
stroker->has_first_face = TRUE;
}
stroker->has_current_face = TRUE;
- }
- stroker->current_face = face;
-
- /* Temporarily modify the stroker to use round joins to guarantee
- * smooth stroked curves. */
- status = _cairo_spline_decompose (&spline, stroker->tolerance);
- assert (status == CAIRO_STATUS_SUCCESS);
-
- /* Tweak the final point to lie on the out-face */
- compute_face (d, &spline.final_slope, stroker, &face);
-
- *_cairo_contour_last_point (&stroker->cw.contour) = face.cw;
- *_cairo_contour_last_point (&stroker->ccw.contour) = face.ccw;
+ contour_add_point (stroker, &stroker->cw, &face.cw);
+ contour_add_point (stroker, &stroker->ccw, &face.ccw);
+ }
stroker->current_face = face;
- return CAIRO_STATUS_SUCCESS;
+ return _cairo_spline_decompose (&spline, stroker->tolerance);
}
static cairo_status_t
@@ -1209,8 +1269,7 @@ _cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t *path,
_cairo_contour_init (&stroker.ccw.contour, -1);
tolerance *= 1 << CAIRO_FIXED_FRAC_BITS;
tolerance *= tolerance;
- stroker.cw.tolerance = tolerance;
- stroker.ccw.tolerance = tolerance;
+ stroker.contour_tolerance = tolerance;
stroker.polygon = polygon;
status = _cairo_path_fixed_interpret (path,
diff --git a/src/cairo-path-stroke.c b/src/cairo-path-stroke.c
index fcbc8e1..56eb97d 100644
--- a/src/cairo-path-stroke.c
+++ b/src/cairo-path-stroke.c
@@ -1118,7 +1118,7 @@ _cairo_stroker_curve_to (void *closure,
_cairo_stroker_line_to;
if (! _cairo_spline_init (&spline,
- line_to, stroker,
+ (cairo_spline_add_point_func_t)line_to, stroker,
&stroker->current_point, b, c, d))
{
return line_to (closure, d);
diff --git a/src/cairo-slope.c b/src/cairo-slope.c
index 827037f..73b79eb 100644
--- a/src/cairo-slope.c
+++ b/src/cairo-slope.c
@@ -59,10 +59,14 @@
int
_cairo_slope_compare (const cairo_slope_t *a, const cairo_slope_t *b)
{
- cairo_int64_t ady_bdx = _cairo_int32x32_64_mul (a->dy, b->dx);
- cairo_int64_t bdy_adx = _cairo_int32x32_64_mul (b->dy, a->dx);
+ cairo_int64_t ady_bdx, bdy_adx;
int cmp;
+ if (a->dx == b->dx && a->dy == b->dy)
+ return 0;
+
+ ady_bdx = _cairo_int32x32_64_mul (a->dy, b->dx);
+ bdy_adx = _cairo_int32x32_64_mul (b->dy, a->dx);
cmp = _cairo_int64_cmp (ady_bdx, bdy_adx);
if (cmp)
return cmp;
@@ -71,11 +75,9 @@ _cairo_slope_compare (const cairo_slope_t *a, const cairo_slope_t *b)
* zero vectors all compare equal, and more positive than any
* non-zero vector.
*/
- if (a->dx == 0 && a->dy == 0 && b->dx == 0 && b->dy ==0)
- return 0;
if (a->dx == 0 && a->dy == 0)
- return 1;
- if (b->dx == 0 && b->dy ==0)
+ return +1;
+ if (b->dx == 0 && b->dy == 0)
return -1;
/* Finally, we're looking at two vectors that are either equal or
@@ -87,13 +89,8 @@ _cairo_slope_compare (const cairo_slope_t *a, const cairo_slope_t *b)
* of b by an infinitesimally small amount, (that is, 'a' will
* always be considered less than 'b').
*/
- if ((a->dx ^ b->dx) < 0 || (a->dy ^ b->dy) < 0) {
- if (a->dx > 0 || (a->dx == 0 && a->dy > 0))
- return +1;
- else
- return -1;
- }
-
- /* Finally, for identical slopes, we obviously return 0. */
- return 0;
+ if (a->dx > 0 || (a->dx == 0 && a->dy > 0))
+ return +1;
+ else
+ return -1;
}
diff --git a/src/cairo-spline.c b/src/cairo-spline.c
index 016ecee..bf0619a 100644
--- a/src/cairo-spline.c
+++ b/src/cairo-spline.c
@@ -73,16 +73,21 @@ _cairo_spline_init (cairo_spline_t *spline,
}
static cairo_status_t
-_cairo_spline_add_point (cairo_spline_t *spline, cairo_point_t *point)
+_cairo_spline_add_point (cairo_spline_t *spline,
+ const cairo_point_t *point,
+ const cairo_point_t *knot)
{
cairo_point_t *prev;
+ cairo_slope_t slope;
prev = &spline->last_point;
if (prev->x == point->x && prev->y == point->y)
return CAIRO_STATUS_SUCCESS;
+ _cairo_slope_init (&slope, point, knot);
+
spline->last_point = *point;
- return spline->add_point_func (spline->closure, point);
+ return spline->add_point_func (spline->closure, point, &slope);
}
static void
@@ -184,13 +189,15 @@ _cairo_spline_error_squared (const cairo_spline_knots_t *knots)
}
static cairo_status_t
-_cairo_spline_decompose_into (cairo_spline_knots_t *s1, double tolerance_squared, cairo_spline_t *result)
+_cairo_spline_decompose_into (cairo_spline_knots_t *s1,
+ double tolerance_squared,
+ cairo_spline_t *result)
{
cairo_spline_knots_t s2;
cairo_status_t status;
if (_cairo_spline_error_squared (s1) < tolerance_squared)
- return _cairo_spline_add_point (result, &s1->a);
+ return _cairo_spline_add_point (result, &s1->a, &s1->b);
_de_casteljau (s1, &s2);
@@ -206,6 +213,7 @@ _cairo_spline_decompose (cairo_spline_t *spline, double tolerance)
{
cairo_spline_knots_t s1;
cairo_status_t status;
+ cairo_slope_t slope;
s1 = spline->knots;
spline->last_point = s1.a;
@@ -213,7 +221,8 @@ _cairo_spline_decompose (cairo_spline_t *spline, double tolerance)
if (unlikely (status))
return status;
- return _cairo_spline_add_point (spline, &spline->knots.d);
+ _cairo_slope_init (&slope, &spline->knots.c, &spline->knots.d);
+ return spline->add_point_func (spline->closure, &spline->knots.d, &slope);
}
/* Note: this function is only good for computing bounds in device space. */
@@ -325,7 +334,7 @@ _cairo_spline_bound (cairo_spline_add_point_func_t add_point_func,
c = -y0 + y1;
FIND_EXTREMES (a, b, c);
- status = add_point_func (closure, p0);
+ status = add_point_func (closure, p0, NULL);
if (unlikely (status))
return status;
@@ -359,10 +368,10 @@ _cairo_spline_bound (cairo_spline_add_point_func_t add_point_func,
p.x = _cairo_fixed_from_double (x);
p.y = _cairo_fixed_from_double (y);
- status = add_point_func (closure, &p);
+ status = add_point_func (closure, &p, NULL);
if (unlikely (status))
return status;
}
- return add_point_func (closure, p3);
+ return add_point_func (closure, p3, NULL);
}
diff --git a/src/cairo-types-private.h b/src/cairo-types-private.h
index f7a1f26..a1d4863 100644
--- a/src/cairo-types-private.h
+++ b/src/cairo-types-private.h
@@ -307,7 +307,8 @@ typedef struct _cairo_polygon {
typedef cairo_warn cairo_status_t
(*cairo_spline_add_point_func_t) (void *closure,
- const cairo_point_t *point);
+ const cairo_point_t *point,
+ const cairo_slope_t *tangent);
typedef struct _cairo_spline_knots {
cairo_point_t a, b, c, d;
diff --git a/util/show-contour.c b/util/show-contour.c
index 1e549cb..b9adfbc 100644
--- a/util/show-contour.c
+++ b/util/show-contour.c
@@ -228,6 +228,14 @@ trap_view_draw (TrapView *self, cairo_t *cr)
cairo_set_source_rgb (cr, 1.0, 0.0, 0.0);
break;
}
+ {
+ const point_t *p = &contour->points[0];
+ cairo_arc (cr, p->x, p->y, 4/zoom, 0, 2 * M_PI);
+ cairo_save (cr);
+ cairo_identity_matrix (cr);
+ cairo_stroke (cr);
+ cairo_restore (cr);
+ }
for (n = 0; n < contour->num_points; n++) {
const point_t *p = &contour->points[n];
cairo_arc (cr, p->x, p->y, 2/zoom, 0, 2 * M_PI);
commit 75175dea5f1c1e13559d03bc99a8572e5674d8a2
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date: Fri Aug 5 09:36:00 2011 +0100
stroke: Experiment with vertex reduction
diff --git a/src/Makefile.sources b/src/Makefile.sources
index a1ff678..8d6480e 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -63,6 +63,7 @@ cairo_private = \
cairo-combsort-private.h \
cairo-compiler-private.h \
cairo-composite-rectangles-private.h \
+ cairo-contour-private.h \
cairo-default-context-private.h \
cairo-device-private.h \
cairo-error-private.h \
@@ -130,6 +131,7 @@ cairo_sources = \
cairo-clip-region.c \
cairo-clip-surface.c \
cairo-color.c \
+ cairo-contour.c \
cairo-composite-rectangles.c \
cairo-debug.c \
cairo-default-context.c \
diff --git a/src/cairo-contour-private.h b/src/cairo-contour-private.h
new file mode 100644
index 0000000..ef9ad9d
--- /dev/null
+++ b/src/cairo-contour-private.h
@@ -0,0 +1,150 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Intel Corporation
+ *
+ * Contributor(s):
+ * Chris Wilson <chris at chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_CONTOUR_PRIVATE_H
+#define CAIRO_CONTOUR_PRIVATE_H
+
+#include "cairo-types-private.h"
+#include "cairo-compiler-private.h"
+#include "cairo-list-private.h"
+
+#include <stdio.h>
+
+CAIRO_BEGIN_DECLS
+
+/* A contour is simply a closed chain of points that divide the infinite plane
+ * into inside and outside. Each contour is a simple polygon, that is it
+ * contains no holes, but maybe either concave or convex.
+ */
+
+struct _cairo_contour {
+ int direction;
+ struct _cairo_contour_chain {
+ cairo_point_t *points;
+ int num_points, size_points;
+ struct _cairo_contour_chain *next;
+ } chain, *tail;
+ cairo_point_t embedded_points[64];
+ cairo_list_t next;
+};
+
+/* Initial definition of a shape is a set of contours (some representing holes) */
+struct _cairo_shape {
+ cairo_list_t contours;
+};
+
+typedef struct _cairo_shape cairo_shape_t;
+
+#if 0
+cairo_private cairo_status_t
+_cairo_shape_init_from_polygon (cairo_shape_t *shape,
+ const cairo_polygon_t *polygon);
+
+cairo_private cairo_status_t
+_cairo_shape_reduce (cairo_shape_t *shape, double tolerance);
+#endif
+
+cairo_private void
+_cairo_contour_init (cairo_contour_t *contour,
+ int direction);
+
+cairo_private cairo_int_status_t
+__cairo_contour_add_point (cairo_contour_t *contour,
+ const cairo_point_t *point);
+
+static inline cairo_int_status_t
+_cairo_contour_add_point (cairo_contour_t *contour,
+ const cairo_point_t *point)
+{
+ struct _cairo_contour_chain *tail = contour->tail;
+
+ if (unlikely (tail->num_points == tail->size_points))
+ return __cairo_contour_add_point (contour, point);
+
+ tail->points[tail->num_points++] = *point;
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static inline cairo_point_t *
+_cairo_contour_first_point (cairo_contour_t *c)
+{
+ return &c->chain.points[0];
+}
+
+static inline cairo_point_t *
+_cairo_contour_last_point (cairo_contour_t *c)
+{
+ return &c->tail->points[c->tail->num_points-1];
+}
+
+cairo_private void
+_cairo_contour_simplify (cairo_contour_t *contour, double tolerance);
+
+cairo_private void
+_cairo_contour_reverse (cairo_contour_t *contour);
+
+cairo_private cairo_int_status_t
+_cairo_contour_add (cairo_contour_t *dst,
+ const cairo_contour_t *src);
+
+cairo_private cairo_int_status_t
+_cairo_contour_add_reversed (cairo_contour_t *dst,
+ const cairo_contour_t *src);
+
+cairo_private void
+__cairo_contour_remove_last_chain (cairo_contour_t *contour);
+
+static inline void
+_cairo_contour_remove_last_point (cairo_contour_t *contour)
+{
+ if (contour->chain.num_points == 0)
+ return;
+
+ if (--contour->tail->num_points == 0)
+ __cairo_contour_remove_last_chain (contour);
+}
+
+cairo_private void
+_cairo_contour_reset (cairo_contour_t *contour);
+
+cairo_private void
+_cairo_contour_fini (cairo_contour_t *contour);
+
+cairo_private void
+_cairo_debug_print_contour (FILE *file, cairo_contour_t *contour);
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_CONTOUR_PRIVATE_H */
diff --git a/src/cairo-contour.c b/src/cairo-contour.c
new file mode 100644
index 0000000..aa3ad6d
--- /dev/null
+++ b/src/cairo-contour.c
@@ -0,0 +1,460 @@
+/*
+ * Copyright © 2004 Carl Worth
+ * Copyright © 2006 Red Hat, Inc.
+ * Copyright © 2008 Chris Wilson
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Carl Worth
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth at cworth.org>
+ * Chris Wilson <chris at chris-wilson.co.uk>
+ */
+
+/* Provide definitions for standalone compilation */
+#include "cairoint.h"
+
+#include "cairo-error-private.h"
+#include "cairo-freelist-private.h"
+#include "cairo-combsort-private.h"
+#include "cairo-contour-private.h"
+
+void
+_cairo_contour_init (cairo_contour_t *contour,
+ int direction)
+{
+ contour->direction = direction;
+ contour->chain.points = contour->embedded_points;
+ contour->chain.next = NULL;
+ contour->chain.num_points = 0;
+ contour->chain.size_points = ARRAY_LENGTH (contour->embedded_points);
+ contour->tail = &contour->chain;
+}
+
+cairo_int_status_t
+__cairo_contour_add_point (cairo_contour_t *contour,
+ const cairo_point_t *point)
+{
+ struct _cairo_contour_chain *tail = contour->tail;
+ struct _cairo_contour_chain *next;
+
+ assert (tail->next == NULL);
+
+ next = _cairo_malloc_ab_plus_c (tail->size_points*2,
+ sizeof (cairo_point_t),
+ sizeof (struct _cairo_contour_chain));
+ if (unlikely (next == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ next->size_points = tail->size_points*2;
+ next->num_points = 1;
+ next->points = (cairo_point_t *)(next+1);
+ next->next = NULL;
+ tail->next = next;
+ contour->tail = next;
+
+ next->points[0] = *point;
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static void
+first_inc (cairo_contour_t *contour,
+ cairo_point_t **p,
+ struct _cairo_contour_chain **chain)
+{
+ if (*p == (*chain)->points + (*chain)->num_points) {
+ assert ((*chain)->next);
+ *chain = (*chain)->next;
+ *p = &(*chain)->points[0];
+ } else
+ ++*p;
+}
+
+static void
+last_dec (cairo_contour_t *contour,
+ cairo_point_t **p,
+ struct _cairo_contour_chain **chain)
+{
+ if (*p == (*chain)->points) {
+ struct _cairo_contour_chain *prev;
+ assert (*chain != &contour->chain);
+ for (prev = &contour->chain; prev->next != *chain; prev = prev->next)
+ ;
+ *chain = prev;
+ *p = &(*chain)->points[(*chain)->num_points-1];
+ } else
+ --*p;
+}
+
+void
+_cairo_contour_reverse (cairo_contour_t *contour)
+{
+ struct _cairo_contour_chain *first_chain, *last_chain;
+ cairo_point_t *first, *last;
+
+ contour->direction = -contour->direction;
+
+ if (contour->chain.num_points <= 1)
+ return;
+
+ first_chain = &contour->chain;
+ last_chain = contour->tail;
+
+ first = &first_chain->points[0];
+ last = &last_chain->points[last_chain->num_points-1];
+
+ while (first != last) {
+ cairo_point_t p;
+
+ p = *first;
+ *first = *last;
+ *last = p;
+
+ first_inc (contour, &first, &first_chain);
+ last_dec (contour, &last, &last_chain);
+ }
+}
+
+cairo_int_status_t
+_cairo_contour_add (cairo_contour_t *dst,
+ const cairo_contour_t *src)
+{
+ const struct _cairo_contour_chain *chain;
+ cairo_int_status_t status;
+ int i;
+
+ for (chain = &src->chain; chain; chain = chain->next) {
+ for (i = 0; i < chain->num_points; i++) {
+ status = _cairo_contour_add_point (dst, &chain->points[i]);
+ if (unlikely (status))
+ return status;
+ }
+ }
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+struct _cairo_contour_iter {
+ struct _cairo_contour_chain *chain;
+ cairo_point_t *point;
+};
+
+static inline cairo_bool_t
+iter_next (struct _cairo_contour_iter *iter)
+{
+ if (iter->point == &iter->chain->points[iter->chain->size_points-1]) {
+ iter->chain = iter->chain->next;
+ if (iter->chain == NULL)
+ return FALSE;
+
+ iter->point = &iter->chain->points[0];
+ return TRUE;
+ } else {
+ iter->point++;
+ return TRUE;
+ }
+}
+
+static cairo_bool_t
+iter_equal (const struct _cairo_contour_iter *i1,
+ const struct _cairo_contour_iter *i2)
+{
+ return i1->chain == i2->chain && i1->point == i2->point;
+}
+
+static void
+iter_init (struct _cairo_contour_iter *iter, cairo_contour_t *contour)
+{
+ iter->chain = &contour->chain;
+ iter->point = &contour->chain.points[0];
+}
+
+static void
+iter_init_last (struct _cairo_contour_iter *iter, cairo_contour_t *contour)
+{
+ iter->chain = contour->tail;
+ iter->point = &contour->tail->points[contour->tail->num_points-1];
+}
+
+static const struct _cairo_contour_chain *prev_const_chain(const cairo_contour_t *contour,
+ const struct _cairo_contour_chain *chain)
+{
+ const struct _cairo_contour_chain *prev;
+
+ if (chain == &contour->chain)
+ return NULL;
+
+ for (prev = &contour->chain; prev->next != chain; prev = prev->next)
+ ;
+
+ return prev;
+}
+
+cairo_int_status_t
+_cairo_contour_add_reversed (cairo_contour_t *dst,
+ const cairo_contour_t *src)
+{
+ const struct _cairo_contour_chain *last;
+ cairo_int_status_t status;
+ int i;
+
+ if (src->chain.num_points == 0)
+ return CAIRO_INT_STATUS_SUCCESS;
+
+ for (last = src->tail; last; last = prev_const_chain (src, last)) {
+ for (i = last->num_points-1; i >= 0; i--) {
+ status = _cairo_contour_add_point (dst, &last->points[i]);
+ if (unlikely (status))
+ return status;
+ }
+ }
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+
+static uint64_t
+_cairo_point_distance2 (const cairo_point_t *p1, const cairo_point_t *p2)
+{
+ int dx = p1->x - p2->x;
+ int dy = p1->y - p2->y;
+ return dx * dx + dy * dy;
+}
+
+#define DELETED(p) ((p)->x == INT_MIN && (p)->y == INT_MAX)
+#define MARK_DELETED(p) ((p)->x = INT_MIN, (p)->y = INT_MAX)
+
+static cairo_bool_t
+_cairo_contour_simplify_chain (cairo_contour_t *contour, const double tolerance,
+ const struct _cairo_contour_iter *first,
+ const struct _cairo_contour_iter *last)
+{
+ struct _cairo_contour_iter iter, furthest;
+ uint64_t max_error;
+ int x0, y0;
+ int nx, ny;
+ int count;
+
+ iter = *first;
+ iter_next (&iter);
+ if (iter_equal (&iter, last))
+ return FALSE;
+
+ x0 = first->point->x;
+ y0 = first->point->y;
+ nx = last->point->y - y0;
+ ny = x0 - last->point->x;
+
+ count = 0;
+ max_error = 0;
+ do {
+ cairo_point_t *p = iter.point;
+ if (! DELETED(p)) {
+ uint64_t d = (uint64_t)nx * (x0 - p->x) + (uint64_t)ny * (y0 - p->y);
+ if (d * d > max_error) {
+ max_error = d * d;
+ furthest = iter;
+ }
+ count++;
+ }
+ iter_next (&iter);
+ } while (! iter_equal (&iter, last));
+ if (count == 0)
+ return FALSE;
+
+ if (max_error > tolerance * ((uint64_t)nx * nx + (uint64_t)ny * ny)) {
+ cairo_bool_t simplified;
+
+ simplified = FALSE;
+ simplified |= _cairo_contour_simplify_chain (contour, tolerance,
+ first, &furthest);
+ simplified |= _cairo_contour_simplify_chain (contour, tolerance,
+ &furthest, last);
+ return simplified;
+ } else {
+ iter = *first;
+ iter_next (&iter);
+ do {
+ MARK_DELETED (iter.point);
+ iter_next (&iter);
+ } while (! iter_equal (&iter, last));
+
+ return TRUE;
+ }
+}
+
+void
+_cairo_contour_simplify (cairo_contour_t *contour, double tolerance)
+{
+ struct _cairo_contour_chain *chain;
+ cairo_point_t *last = NULL;
+ struct _cairo_contour_iter iter, furthest;
+ cairo_bool_t simplified;
+ uint64_t max = 0;
+ int i;
+
+ if (contour->chain.num_points <= 2)
+ return;
+
+ tolerance = tolerance * (1 << CAIRO_FIXED_FRAC_BITS);
+ tolerance *= tolerance;
+
+ /* stage 1: vertex reduction */
+ for (chain = &contour->chain; chain; chain = chain->next) {
+ for (i = 0; i < chain->num_points; i++) {
+ if (last == NULL ||
+ _cairo_point_distance2 (last,
+ &chain->points[i]) > tolerance) {
+ last = &chain->points[i];
+ } else {
+ MARK_DELETED (&chain->points[i]);
+ }
+ }
+ }
+
+ /* stage2: polygon simplification using Douglas-Peucker */
+ simplified = FALSE;
+ do {
+ last = &contour->chain.points[0];
+ iter_init (&furthest, contour);
+ max = 0;
+ for (chain = &contour->chain; chain; chain = chain->next) {
+ for (i = 0; i < chain->num_points; i++) {
+ uint64_t d;
+
+ if (DELETED (&chain->points[i]))
+ continue;
+
+ d = _cairo_point_distance2 (last, &chain->points[i]);
+ if (d > max) {
+ furthest.chain = chain;
+ furthest.point = &chain->points[i];
+ max = d;
+ }
+ }
+ }
+ assert (max);
+
+ simplified = FALSE;
+ iter_init (&iter, contour);
+ simplified |= _cairo_contour_simplify_chain (contour, tolerance,
+ &iter, &furthest);
+
+ iter_init_last (&iter, contour);
+ if (! iter_equal (&furthest, &iter))
+ simplified |= _cairo_contour_simplify_chain (contour, tolerance,
+ &furthest, &iter);
+ } while (simplified);
+
+ iter_init (&iter, contour);
+ for (chain = &contour->chain; chain; chain = chain->next) {
+ int num_points = chain->num_points;
+ chain->num_points = 0;
+ for (i = 0; i < num_points; i++) {
+ if (! DELETED(&chain->points[i])) {
+ if (iter.point != &chain->points[i])
+ *iter.point = chain->points[i];
+ iter.chain->num_points++;
+ iter_next (&iter);
+ }
+ }
+ }
+
+ if (iter.chain) {
+ struct _cairo_contour_chain *next;
+
+ for (chain = iter.chain->next; chain; chain = next) {
+ next = chain->next;
+ free (chain);
+ }
+
+ iter.chain->next = NULL;
+ contour->tail = iter.chain;
+ }
+}
+
+void
+_cairo_contour_reset (cairo_contour_t *contour)
+{
+ _cairo_contour_fini (contour);
+ _cairo_contour_init (contour, contour->direction);
+}
+
+void
+_cairo_contour_fini (cairo_contour_t *contour)
+{
+ struct _cairo_contour_chain *chain, *next;
+
+ for (chain = contour->chain.next; chain; chain = next) {
+ next = chain->next;
+ free (chain);
+ }
+}
+
+void
+_cairo_debug_print_contour (FILE *file, cairo_contour_t *contour)
+{
+ struct _cairo_contour_chain *chain;
+ int num_points, size_points;
+ int i;
+
+ num_points = 0;
+ size_points = 0;
+ for (chain = &contour->chain; chain; chain = chain->next) {
+ num_points += chain->num_points;
+ size_points += chain->size_points;
+ }
+
+ fprintf (file, "contour: direction=%d, num_points=%d / %d\n",
+ contour->direction, num_points, size_points);
+
+ num_points = 0;
+ for (chain = &contour->chain; chain; chain = chain->next) {
+ for (i = 0; i < chain->num_points; i++) {
+ fprintf (file, " [%d] = (%f, %f)\n",
+ num_points++,
+ _cairo_fixed_to_double (chain->points[i].x),
+ _cairo_fixed_to_double (chain->points[i].y));
+ }
+ }
+}
+
+void
+__cairo_contour_remove_last_chain (cairo_contour_t *contour)
+{
+ struct _cairo_contour_chain *chain;
+
+ if (contour->tail == &contour->chain)
+ return;
+
+ for (chain = &contour->chain; chain->next != contour->tail; chain = chain->next)
+ ;
+ free (contour->tail);
+ contour->tail = chain;
+ chain->next = NULL;
+}
diff --git a/src/cairo-path-stroke-polygon.c b/src/cairo-path-stroke-polygon.c
index d6cd82f..fed3dba 100644
--- a/src/cairo-path-stroke-polygon.c
+++ b/src/cairo-path-stroke-polygon.c
@@ -42,26 +42,33 @@
#include "cairo-box-private.h"
#include "cairo-boxes-private.h"
+#include "cairo-contour-private.h"
#include "cairo-error-private.h"
#include "cairo-path-fixed-private.h"
#include "cairo-slope-private.h"
+#define DEBUG 1
+
struct stroker {
cairo_stroke_style_t style;
- cairo_polygon_t *polygon;
+#if DEBUG
+ cairo_contour_t path;
+#endif
- struct contour {
- cairo_point_t first, current;
- } inside, outside;
+ struct stroke_contour {
+ /* Note that these are not strictly contours as they may intersect */
+ cairo_contour_t contour;
+ cairo_uint64_t tolerance;
+ } cw, ccw;
+ cairo_polygon_t *polygon;
const cairo_matrix_t *ctm;
const cairo_matrix_t *ctm_inverse;
double tolerance;
- double ctm_determinant;
cairo_bool_t ctm_det_positive;
- cairo_pen_t pen;
+ cairo_pen_t pen;
cairo_point_t current_point;
cairo_point_t first_point;
@@ -75,33 +82,43 @@ struct stroker {
cairo_stroke_face_t first_face;
};
-static void
-translate_point (cairo_point_t *point, const cairo_point_t *offset)
+static inline void
+normalize_slope (double *dx, double *dy);
+
+static cairo_uint64_t
+_cairo_point_distance2 (const cairo_point_t *p1,
+ const cairo_point_t *p2)
{
- point->x += offset->x;
- point->y += offset->y;
+ int32_t dx = p1->x - p2->x;
+ int32_t dy = p1->y - p2->y;
+ return _cairo_int32x32_64_mul (dx, dx) + _cairo_int32x32_64_mul (dy, dy);
}
-static int
-join_is_clockwise (const cairo_stroke_face_t *in,
- const cairo_stroke_face_t *out)
+static cairo_bool_t
+close_enough (const struct stroke_contour *c,
+ const cairo_point_t *p1,
+ const cairo_point_t *p2)
{
- cairo_slope_t in_slope, out_slope;
+ return _cairo_int64_lt (_cairo_point_distance2 (p1, p2), c->tolerance);
+}
- _cairo_slope_init (&in_slope, &in->point, &in->cw);
- _cairo_slope_init (&out_slope, &out->point, &out->cw);
+static void
+contour_add_point (struct stroke_contour *c,
+ const cairo_point_t *point)
+{
+ if (! close_enough (c,point, _cairo_contour_last_point (&c->contour)))
+ _cairo_contour_add_point (&c->contour, point);
+}
- return _cairo_slope_compare (&in_slope, &out_slope) < 0;
+static void
+translate_point (cairo_point_t *point, const cairo_point_t *offset)
+{
+ point->x += offset->x;
+ point->y += offset->y;
}
-/**
- * _cairo_slope_compare_sgn
- *
- * Return -1, 0 or 1 depending on the relative slopes of
- * two lines.
- */
static int
-_cairo_slope_compare_sgn (double dx1, double dy1, double dx2, double dy2)
+slope_compare_sgn (double dx1, double dy1, double dx2, double dy2)
{
double c = (dx1 * dy2 - dx2 * dy1);
@@ -125,43 +142,19 @@ _range_step (int i, int step, int max)
* Construct a fan around the midpoint using the vertices from pen between
* inpt and outpt.
*/
-static cairo_status_t
-_tessellate_fan (struct stroker *stroker,
- const cairo_slope_t *in_vector,
- const cairo_slope_t *out_vector,
- const cairo_point_t *midpt,
- const cairo_point_t *inpt,
- const cairo_point_t *outpt,
- cairo_bool_t clockwise)
+static void
+add_fan (struct stroker *stroker,
+ const cairo_slope_t *in_vector,
+ const cairo_slope_t *out_vector,
+ const cairo_point_t *midpt,
+ const cairo_point_t *inpt,
+ const cairo_point_t *outpt,
+ cairo_bool_t clockwise,
+ struct stroke_contour *c)
{
- cairo_point_t stack_points[64], *points = stack_points;
int start, stop, step, i, npoints;
- cairo_status_t status;
if (clockwise) {
- step = -1;
-
- start = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen,
- in_vector);
- if (_cairo_slope_compare (&stroker->pen.vertices[start].slope_ccw,
- in_vector) < 0)
- start = _range_step (start, -1, stroker->pen.num_vertices);
-
- stop = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen,
- out_vector);
- if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_cw,
- out_vector) > 0)
- {
- stop = _range_step (stop, 1, stroker->pen.num_vertices);
- if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_ccw,
- in_vector) < 0)
- {
- goto BEVEL;
- }
- }
-
- npoints = start - stop;
- } else {
step = 1;
start = _cairo_pen_find_active_cw_vertex_index (&stroker->pen,
@@ -178,119 +171,405 @@ _tessellate_fan (struct stroker *stroker,
stop = _range_step (stop, -1, stroker->pen.num_vertices);
if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_cw,
in_vector) < 0)
- {
- goto BEVEL;
- }
+ return;
}
npoints = stop - start;
+ } else {
+ step = -1;
+
+ start = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen,
+ in_vector);
+ if (_cairo_slope_compare (&stroker->pen.vertices[start].slope_ccw,
+ in_vector) < 0)
+ start = _range_step (start, -1, stroker->pen.num_vertices);
+
+ stop = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen,
+ out_vector);
+ if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_cw,
+ out_vector) > 0)
+ {
+ stop = _range_step (stop, 1, stroker->pen.num_vertices);
+ if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_ccw,
+ in_vector) < 0)
+ return;
+ }
+
+ npoints = start - stop;
}
stop = _range_step (stop, step, stroker->pen.num_vertices);
-
if (npoints < 0)
npoints += stroker->pen.num_vertices;
- npoints += 3;
-
if (npoints <= 1)
- goto BEVEL;
-
- if (npoints > ARRAY_LENGTH (stack_points)) {
- points = _cairo_malloc_ab (npoints, sizeof (cairo_point_t));
- if (unlikely (points == NULL))
- return _cairo_error (CAIRO_STATUS_NO_MEMORY);
- }
-
+ return;
- /* Construct the fan. */
- npoints = 0;
- points[npoints++] = *inpt;
for (i = start;
i != stop;
i = _range_step (i, step, stroker->pen.num_vertices))
{
- points[npoints] = *midpt;
- translate_point (&points[npoints], &stroker->pen.vertices[i].point);
- npoints++;
+ cairo_point_t p = *midpt;
+ translate_point (&p, &stroker->pen.vertices[i].point);
+ contour_add_point (c, &p);
}
- points[npoints++] = *outpt;
+}
- for (i = 0; i < npoints - 1; i++) {
- if (clockwise) {
- status = _cairo_polygon_add_external_edge (stroker->polygon,
- &points[i], &points[i+1]);
- } else {
- status = _cairo_polygon_add_external_edge (stroker->polygon,
- &points[i+1], &points[i]);
- }
- if (unlikely (status))
- break;
+static int
+join_is_clockwise (const cairo_stroke_face_t *in,
+ const cairo_stroke_face_t *out)
+{
+ return _cairo_slope_compare (&in->dev_vector, &out->dev_vector) < 0;
+}
+
+static double
+distance_from_face (const cairo_stroke_face_t *face,
+ const cairo_point_t *p)
+{
+ return (p->x - face->point.x) * face->dev_slope.y - (p->y - face->point.y) * face->dev_slope.x;
+}
+
+static void
+inner_join (struct stroker *stroker,
+ const cairo_stroke_face_t *in,
+ const cairo_stroke_face_t *out)
+{
+ cairo_point_t last;
+ const cairo_point_t *p, *outpt;
+ struct stroke_contour *inner;
+ double d_p, d_last = -1;
+ double dot;
+ double line_width;
+ double sign;
+
+ /* XXX line segments shorter than line-width */
+
+ if (join_is_clockwise (in, out)) {
+ inner = &stroker->ccw;
+ outpt = &out->ccw;
+ sign = -1;
+ } else {
+ outpt = &out->cw;
+ inner = &stroker->cw;
+ sign = 1;
}
- if (points != stack_points)
- free (points);
+ line_width = stroker->style.line_width/2;
+ line_width *= CAIRO_FIXED_ONE;
+prev:
+ if (inner->contour.chain.num_points == 0) {
+ contour_add_point (inner, outpt);
+ return;
+ }
+ p = _cairo_contour_last_point (&inner->contour);
+ if ((d_p = sign * distance_from_face (out, p)) <= line_width) {
+ last = *p;
+ d_last = d_p;
+ _cairo_contour_remove_last_point (&inner->contour);
+ goto prev;
+ }
- return status;
+ if (d_last < 0) {
+ //contour_add_point (inner, outpt);
+ return;
+ }
-BEVEL:
- /* Ensure a leak free connection... */
- if (clockwise)
- return _cairo_polygon_add_external_edge (stroker->polygon, inpt, outpt);
- else
- return _cairo_polygon_add_external_edge (stroker->polygon, outpt, inpt);
+ dot = (line_width - d_last) / (d_p - d_last);
+ last.x += dot * (p->x - last.x);
+ last.y += dot * (p->y - last.y);
+ contour_add_point (inner, &last);
}
-static cairo_status_t
-join (struct stroker *stroker,
- const cairo_stroke_face_t *in,
- const cairo_stroke_face_t *out)
+static void
+inner_close (struct stroker *stroker,
+ const cairo_stroke_face_t *in,
+ cairo_stroke_face_t *out)
{
- int clockwise = join_is_clockwise (out, in);
- const cairo_point_t *inpt, *outpt;
- cairo_point_t points[4];
- cairo_status_t status;
+ cairo_point_t last;
+ const cairo_point_t *p, *outpt;
+ struct stroke_contour *inner;
+ struct _cairo_contour_chain *chain;
+ double d_p, d_last = -1;
+ double dot;
+ double line_width;
+ double sign;
+ int i;
+
+ /* XXX line segments shorter than line-width */
+
+ if (join_is_clockwise (in, out)) {
+ inner = &stroker->ccw;
+ outpt = &in->ccw;
+ sign = -1;
+ } else {
+ outpt = &in->cw;
+ inner = &stroker->cw;
+ sign = 1;
+ }
- if (in->cw.x == out->cw.x && in->cw.y == out->cw.y &&
- in->ccw.x == out->ccw.x && in->ccw.y == out->ccw.y)
- {
- return CAIRO_STATUS_SUCCESS;
+ if (inner->contour.chain.num_points == 0) {
+ contour_add_point (inner, outpt);
+ return;
}
- if (clockwise) {
- status = _cairo_polygon_add_external_edge (stroker->polygon,
- &out->cw, &in->point);
- if (unlikely (status))
- return status;
+ line_width = stroker->style.line_width/2;
+ line_width *= CAIRO_FIXED_ONE;
+
+ for (chain = &inner->contour.chain; chain; chain = chain->next) {
+ for (i = 0; i < chain->num_points; i++) {
+ p = &chain->points[i];
+ if ((d_p = sign * distance_from_face (in, p)) >= line_width)
+ {
+ printf ("point [%d] =%f, last_dp=%f\n", i,d_p, d_last);
+ goto out;
+ }
+
+ last = *p;
+ d_last = d_p;
+ }
+ }
+out:
+
+ dot = (line_width - d_last) / (d_p - d_last);
+ last.x += dot * (p->x - last.x);
+ last.y += dot * (p->y - last.y);
+ *_cairo_contour_last_point (&inner->contour) = last;
+
+ for (chain = &inner->contour.chain; chain; chain = chain->next) {
+ for (i = 0; i < chain->num_points; i++) {
+ cairo_point_t *pp = &chain->points[i];
+ if (pp == p)
+ return;
+ *pp = last;
+ }
+ }
+}
- status = _cairo_polygon_add_external_edge (stroker->polygon,
- &in->point, &in->cw);
- if (unlikely (status))
- return status;
+static void
+outer_close (struct stroker *stroker,
+ const cairo_stroke_face_t *in,
+ const cairo_stroke_face_t *out)
+{
+ const cairo_point_t *inpt, *outpt;
+ struct stroke_contour *outer;
+ int clockwise;
+ if (in->cw.x == out->cw.x && in->cw.y == out->cw.y &&
+ in->ccw.x == out->ccw.x && out->ccw.y == out->ccw.y)
+ {
+ return;
+ }
+ clockwise = join_is_clockwise (in, out);
+ if (clockwise) {
+ inpt = &in->cw;
+ outpt = &out->cw;
+ outer = &stroker->cw;
+ } else {
inpt = &in->ccw;
outpt = &out->ccw;
- } else {
- status = _cairo_polygon_add_external_edge (stroker->polygon,
- &in->ccw, &in->point);
- if (unlikely (status))
- return status;
+ outer = &stroker->ccw;
+ }
+
+ if (close_enough (outer, inpt, outpt))
+ return;
+
+ switch (stroker->style.line_join) {
+ case CAIRO_LINE_JOIN_ROUND:
+ /* construct a fan around the common midpoint */
+ add_fan (stroker,
+ &in->dev_vector,
+ &out->dev_vector,
+ &in->point, inpt, outpt,
+ clockwise, outer);
+ break;
- status = _cairo_polygon_add_external_edge (stroker->polygon,
- &in->point, &out->ccw);
- if (unlikely (status))
- return status;
+ case CAIRO_LINE_JOIN_MITER:
+ default: {
+ /* dot product of incoming slope vector with outgoing slope vector */
+ double in_dot_out = -in->usr_vector.x * out->usr_vector.x +
+ -in->usr_vector.y * out->usr_vector.y;
+ double ml = stroker->style.miter_limit;
+ /* Check the miter limit -- lines meeting at an acute angle
+ * can generate long miters, the limit converts them to bevel
+ *
+ * Consider the miter join formed when two line segments
+ * meet at an angle psi:
+ *
+ * /.\
+ * /. .\
+ * /./ \.\
+ * /./psi\.\
+ *
+ * We can zoom in on the right half of that to see:
+ *
+ * |\
+ * | \ psi/2
+ * | \
+ * | \
+ * | \
+ * | \
+ * miter \
+ * length \
+ * | \
+ * | .\
+ * | . \
+ * |. line \
+ * \ width \
+ * \ \
+ *
+ *
+ * The right triangle in that figure, (the line-width side is
+ * shown faintly with three '.' characters), gives us the
+ * following expression relating miter length, angle and line
+ * width:
+ *
+ * 1 /sin (psi/2) = miter_length / line_width
+ *
+ * The right-hand side of this relationship is the same ratio
+ * in which the miter limit (ml) is expressed. We want to know
+ * when the miter length is within the miter limit. That is
+ * when the following condition holds:
+ *
+ * 1/sin(psi/2) <= ml
+ * 1 <= ml sin(psi/2)
+ * 1 <= ml² sin²(psi/2)
+ * 2 <= ml² 2 sin²(psi/2)
+ * 2·sin²(psi/2) = 1-cos(psi)
+ * 2 <= ml² (1-cos(psi))
+ *
+ * in · out = |in| |out| cos (psi)
+ *
+ * in and out are both unit vectors, so:
+ *
+ * in · out = cos (psi)
+ *
+ * 2 <= ml² (1 - in · out)
+ *
+ */
+ if (2 <= ml * ml * (1 - in_dot_out)) {
+ double x1, y1, x2, y2;
+ double mx, my;
+ double dx1, dx2, dy1, dy2;
+ double ix, iy;
+ double fdx1, fdy1, fdx2, fdy2;
+ double mdx, mdy;
+
+ /*
+ * we've got the points already transformed to device
+ * space, but need to do some computation with them and
+ * also need to transform the slope from user space to
+ * device space
+ */
+ /* outer point of incoming line face */
+ x1 = _cairo_fixed_to_double (inpt->x);
+ y1 = _cairo_fixed_to_double (inpt->y);
+ dx1 = in->usr_vector.x;
+ dy1 = in->usr_vector.y;
+ cairo_matrix_transform_distance (stroker->ctm, &dx1, &dy1);
+
+ /* outer point of outgoing line face */
+ x2 = _cairo_fixed_to_double (outpt->x);
+ y2 = _cairo_fixed_to_double (outpt->y);
+ dx2 = out->usr_vector.x;
+ dy2 = out->usr_vector.y;
+ cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2);
+
+ /*
+ * Compute the location of the outer corner of the miter.
+ * That's pretty easy -- just the intersection of the two
+ * outer edges. We've got slopes and points on each
+ * of those edges. Compute my directly, then compute
+ * mx by using the edge with the larger dy; that avoids
+ * dividing by values close to zero.
+ */
+ my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) /
+ (dx1 * dy2 - dx2 * dy1));
+ if (fabs (dy1) >= fabs (dy2))
+ mx = (my - y1) * dx1 / dy1 + x1;
+ else
+ mx = (my - y2) * dx2 / dy2 + x2;
+
+ /*
+ * When the two outer edges are nearly parallel, slight
+ * perturbations in the position of the outer points of the lines
+ * caused by representing them in fixed point form can cause the
+ * intersection point of the miter to move a large amount. If
+ * that moves the miter intersection from between the two faces,
+ * then draw a bevel instead.
+ */
+
+ ix = _cairo_fixed_to_double (in->point.x);
+ iy = _cairo_fixed_to_double (in->point.y);
+
+ /* slope of one face */
+ fdx1 = x1 - ix; fdy1 = y1 - iy;
+
+ /* slope of the other face */
+ fdx2 = x2 - ix; fdy2 = y2 - iy;
+
+ /* slope from the intersection to the miter point */
+ mdx = mx - ix; mdy = my - iy;
+
+ /*
+ * Make sure the miter point line lies between the two
+ * faces by comparing the slopes
+ */
+ if (slope_compare_sgn (fdx1, fdy1, mdx, mdy) !=
+ slope_compare_sgn (fdx2, fdy2, mdx, mdy))
+ {
+ cairo_point_t p;
+
+ p.x = _cairo_fixed_from_double (mx);
+ p.y = _cairo_fixed_from_double (my);
+
+ *_cairo_contour_last_point (&outer->contour) = p;
+ }
+ }
+ break;
+ }
+
+ case CAIRO_LINE_JOIN_BEVEL:
+ break;
+ }
+ contour_add_point (outer, outpt);
+}
+
+static void
+join (struct stroker *stroker,
+ const cairo_stroke_face_t *in,
+ const cairo_stroke_face_t *out)
+{
+ const cairo_point_t *inpt, *outpt;
+ struct stroke_contour *outer;
+ int clockwise;
+
+ if (in->cw.x == out->cw.x && in->cw.y == out->cw.y &&
+ in->ccw.x == out->ccw.x && out->ccw.y == out->ccw.y)
+ {
+ return;
+ }
+ clockwise = join_is_clockwise (in, out);
+ if (clockwise) {
inpt = &in->cw;
outpt = &out->cw;
+ outer = &stroker->cw;
+ } else {
+ inpt = &in->ccw;
+ outpt = &out->ccw;
+ outer = &stroker->ccw;
}
+ if (close_enough (outer, inpt, outpt))
+ return;
+
switch (stroker->style.line_join) {
case CAIRO_LINE_JOIN_ROUND:
/* construct a fan around the common midpoint */
- return _tessellate_fan (stroker,
- &in->dev_vector,
- &out->dev_vector,
- &in->point, inpt, outpt,
- clockwise);
+ add_fan (stroker,
+ &in->dev_vector,
+ &out->dev_vector,
+ &in->point, inpt, outpt,
+ clockwise, outer);
+ break;
case CAIRO_LINE_JOIN_MITER:
default: {
@@ -424,55 +703,31 @@ join (struct stroker *stroker,
* Make sure the miter point line lies between the two
* faces by comparing the slopes
*/
- if (_cairo_slope_compare_sgn (fdx1, fdy1, mdx, mdy) !=
- _cairo_slope_compare_sgn (fdx2, fdy2, mdx, mdy))
+ if (slope_compare_sgn (fdx1, fdy1, mdx, mdy) !=
+ slope_compare_sgn (fdx2, fdy2, mdx, mdy))
{
- points[0].x = _cairo_fixed_from_double (mx);
- points[0].y = _cairo_fixed_from_double (my);
-
- if (clockwise) {
- status = _cairo_polygon_add_external_edge (stroker->polygon,
- inpt, &points[0]);
- if (unlikely (status))
- return status;
-
- status = _cairo_polygon_add_external_edge (stroker->polygon,
- &points[0], outpt);
- if (unlikely (status))
- return status;
- } else {
- status = _cairo_polygon_add_external_edge (stroker->polygon,
- outpt, &points[0]);
- if (unlikely (status))
- return status;
-
- status = _cairo_polygon_add_external_edge (stroker->polygon,
- &points[0], inpt);
- if (unlikely (status))
- return status;
- }
-
- return CAIRO_STATUS_SUCCESS;
+ cairo_point_t p;
+
+ p.x = _cairo_fixed_from_double (mx);
+ p.y = _cairo_fixed_from_double (my);
+
+ *_cairo_contour_last_point (&outer->contour) = p;
+ return;
}
}
+ break;
}
- /* fall through ... */
-
case CAIRO_LINE_JOIN_BEVEL:
- if (clockwise) {
- return _cairo_polygon_add_external_edge (stroker->polygon,
- inpt, outpt);
- } else {
- return _cairo_polygon_add_external_edge (stroker->polygon,
- outpt, inpt);
- }
+ break;
}
+ contour_add_point (outer, outpt);
}
-static cairo_status_t
-_cairo_stroker_add_cap (struct stroker *stroker,
- const cairo_stroke_face_t *f)
+static void
+add_cap (struct stroker *stroker,
+ const cairo_stroke_face_t *f,
+ struct stroke_contour *c)
{
switch (stroker->style.line_cap) {
case CAIRO_LINE_CAP_ROUND: {
@@ -481,12 +736,10 @@ _cairo_stroker_add_cap (struct stroker *stroker,
slope.dx = -f->dev_vector.dx;
slope.dy = -f->dev_vector.dy;
- return _tessellate_fan (stroker,
- &f->dev_vector,
- &slope,
- &f->point, &f->cw, &f->ccw,
- FALSE);
-
+ add_fan (stroker, &f->dev_vector, &slope,
+ &f->point, &f->ccw, &f->cw,
+ FALSE, c);
+ break;
}
case CAIRO_LINE_CAP_SQUARE: {
@@ -509,38 +762,21 @@ _cairo_stroker_add_cap (struct stroker *stroker,
quad[2].y = f->cw.y + fvector.dy;
quad[3] = f->cw;
- {
- cairo_status_t status;
-
- status = _cairo_polygon_add_external_edge (stroker->polygon,
- &quad[0], &quad[1]);
- if (unlikely (status))
- return status;
-
- status = _cairo_polygon_add_external_edge (stroker->polygon,
- &quad[1], &quad[2]);
- if (unlikely (status))
- return status;
-
- status = _cairo_polygon_add_external_edge (stroker->polygon,
- &quad[2], &quad[3]);
- if (unlikely (status))
- return status;
-
- return CAIRO_STATUS_SUCCESS;
- }
+ contour_add_point (c, &quad[1]);
+ contour_add_point (c, &quad[2]);
}
case CAIRO_LINE_CAP_BUTT:
default:
- return _cairo_polygon_add_external_edge (stroker->polygon,
- &f->ccw, &f->cw);
+ break;
}
+ contour_add_point (c, &f->cw);
}
-static cairo_status_t
-_cairo_stroker_add_leading_cap (struct stroker *stroker,
- const cairo_stroke_face_t *face)
+static void
+add_leading_cap (struct stroker *stroker,
+ const cairo_stroke_face_t *face,
+ struct stroke_contour *c)
{
cairo_stroke_face_t reversed;
cairo_point_t t;
@@ -556,25 +792,23 @@ _cairo_stroker_add_leading_cap (struct stroker *stroker,
reversed.cw = reversed.ccw;
reversed.ccw = t;
- return _cairo_stroker_add_cap (stroker, &reversed);
+ add_cap (stroker, &reversed, c);
}
-static cairo_status_t
-_cairo_stroker_add_trailing_cap (struct stroker *stroker,
- const cairo_stroke_face_t *face)
+static void
+add_trailing_cap (struct stroker *stroker,
+ const cairo_stroke_face_t *face,
+ struct stroke_contour *c)
{
- return _cairo_stroker_add_cap (stroker, face);
+ add_cap (stroker, face, c);
}
static inline void
-normalized_device_slope (double *dx, double *dy,
- const cairo_matrix_t *ctm_inverse)
+normalize_slope (double *dx, double *dy)
{
double dx0 = *dx, dy0 = *dy;
double mag;
- cairo_matrix_transform_distance (ctm_inverse, &dx0, &dy0);
-
assert (dx0 != 0.0 || dy0 != 0.0);
if (dx0 == 0.0) {
@@ -604,11 +838,17 @@ normalized_device_slope (double *dx, double *dy,
static void
compute_face (const cairo_point_t *point, cairo_slope_t *dev_slope,
- double slope_dx, double slope_dy,
struct stroker *stroker, cairo_stroke_face_t *face)
{
double face_dx, face_dy;
cairo_point_t offset_ccw, offset_cw;
+ double slope_dx, slope_dy;
+
+ slope_dx = _cairo_fixed_to_double (dev_slope->dx);
+ slope_dy = _cairo_fixed_to_double (dev_slope->dy);
+ normalize_slope (&slope_dx, &slope_dy);
+ face->dev_slope.x = slope_dx;
+ face->dev_slope.y = slope_dy;
/*
* rotate to get a line_width/2 vector along the face, note that
@@ -617,19 +857,29 @@ compute_face (const cairo_point_t *point, cairo_slope_t *dev_slope,
* whether the ctm reflects or not, and that can be determined
* by looking at the determinant of the matrix.
*/
- if (stroker->ctm_det_positive)
- {
+ if (! _cairo_matrix_is_identity (stroker->ctm_inverse)) {
+ /* Normalize the matrix! */
+ cairo_matrix_transform_distance (stroker->ctm_inverse,
+ &slope_dx, &slope_dy);
+ normalize_slope (&slope_dx, &slope_dy);
+
+ if (stroker->ctm_det_positive)
+ {
+ face_dx = - slope_dy * (stroker->style.line_width / 2.0);
+ face_dy = slope_dx * (stroker->style.line_width / 2.0);
+ }
+ else
+ {
+ face_dx = slope_dy * (stroker->style.line_width / 2.0);
+ face_dy = - slope_dx * (stroker->style.line_width / 2.0);
+ }
+
+ /* back to device space */
+ cairo_matrix_transform_distance (stroker->ctm, &face_dx, &face_dy);
+ } else {
face_dx = - slope_dy * (stroker->style.line_width / 2.0);
face_dy = slope_dx * (stroker->style.line_width / 2.0);
}
- else
- {
- face_dx = slope_dy * (stroker->style.line_width / 2.0);
- face_dy = - slope_dx * (stroker->style.line_width / 2.0);
- }
-
- /* back to device space */
- cairo_matrix_transform_distance (stroker->ctm, &face_dx, &face_dy);
offset_ccw.x = _cairo_fixed_from_double (face_dx);
offset_ccw.y = _cairo_fixed_from_double (face_dy);
@@ -650,88 +900,58 @@ compute_face (const cairo_point_t *point, cairo_slope_t *dev_slope,
face->dev_vector = *dev_slope;
}
-static cairo_status_t
-_cairo_stroker_add_caps (struct stroker *stroker)
+static void
+add_caps (struct stroker *stroker)
{
- cairo_status_t status;
-
/* check for a degenerative sub_path */
- if (stroker->has_initial_sub_path
- && ! stroker->has_first_face
- && ! stroker->has_current_face
- && stroker->style.line_cap == CAIRO_LINE_CAP_ROUND)
+ if (stroker->has_initial_sub_path &&
+ ! stroker->has_first_face &&
+ ! stroker->has_current_face &&
+ stroker->style.line_cap == CAIRO_LINE_CAP_ROUND)
{
/* pick an arbitrary slope to use */
- double dx = 1.0, dy = 0.0;
cairo_slope_t slope = { CAIRO_FIXED_ONE, 0 };
cairo_stroke_face_t face;
- normalized_device_slope (&dx, &dy, stroker->ctm_inverse);
-
/* arbitrarily choose first_point
* first_point and current_point should be the same */
- compute_face (&stroker->first_point, &slope, dx, dy, stroker, &face);
-
- status = _cairo_stroker_add_leading_cap (stroker, &face);
- if (unlikely (status))
- return status;
+ compute_face (&stroker->first_point, &slope, stroker, &face);
- status = _cairo_stroker_add_trailing_cap (stroker, &face);
- if (unlikely (status))
- return status;
- }
-
- if (stroker->has_first_face) {
- status = _cairo_stroker_add_leading_cap (stroker,
- &stroker->first_face);
- if (unlikely (status))
- return status;
- }
+ add_leading_cap (stroker, &face, &stroker->ccw);
+ add_trailing_cap (stroker, &face, &stroker->ccw);
- if (stroker->has_current_face) {
- status = _cairo_stroker_add_trailing_cap (stroker,
- &stroker->current_face);
- if (unlikely (status))
- return status;
- }
-
- return CAIRO_STATUS_SUCCESS;
-}
-
-static cairo_status_t
-add_segment (struct stroker *stroker,
- const cairo_point_t *p1,
- const cairo_point_t *p2,
- cairo_slope_t *dev_slope,
- double slope_dx, double slope_dy,
- cairo_stroke_face_t *start,
- cairo_stroke_face_t *end)
-{
- cairo_status_t status;
-
- if (p1->y == p2->y)
- return CAIRO_STATUS_SUCCESS;
+ _cairo_polygon_add_contour (stroker->polygon, &stroker->ccw.contour);
+ _cairo_contour_reset (&stroker->ccw.contour);
+ } else {
+#if DEBUG
+ {
+ FILE *file = fopen ("contours.txt", "a");
+ _cairo_debug_print_contour (file, &stroker->path);
+ _cairo_debug_print_contour (file, &stroker->cw.contour);
+ _cairo_debug_print_contour (file, &stroker->ccw.contour);
+ fclose (file);
+ }
+ _cairo_contour_reset (&stroker->path);
+#endif
- compute_face (p1, dev_slope, slope_dx, slope_dy, stroker, start);
- *end = *start;
+ if (stroker->has_current_face)
+ add_trailing_cap (stroker, &stroker->current_face, &stroker->ccw);
- end->point = *p2;
- end->ccw.x += p2->x - p1->x;
- end->ccw.y += p2->y - p1->y;
- end->cw.x += p2->x - p1->x;
- end->cw.y += p2->y - p1->y;
+ //_cairo_contour_simplify (&stroker->ccw.contour, stroker->tolerance);
- status = _cairo_polygon_add_external_edge (stroker->polygon,
- &end->cw, &start->cw);
- if (unlikely (status))
- return status;
+ _cairo_polygon_add_contour (stroker->polygon, &stroker->ccw.contour);
+ _cairo_contour_reset (&stroker->ccw.contour);
- status = _cairo_polygon_add_external_edge (stroker->polygon,
- &start->ccw, &end->ccw);
- if (unlikely (status))
- return status;
+ if (stroker->has_first_face) {
+ add_leading_cap (stroker, &stroker->first_face, &stroker->ccw);
+ }
+ _cairo_polygon_add_contour (stroker->polygon, &stroker->ccw.contour);
+ _cairo_contour_reset (&stroker->ccw.contour);
- return CAIRO_STATUS_SUCCESS;
+ //_cairo_contour_simplify (&stroker->cw.contour, stroker->tolerance);
+ _cairo_polygon_add_contour (stroker->polygon, &stroker->cw.contour);
+ _cairo_contour_reset (&stroker->cw.contour);
+ }
}
static cairo_status_t
@@ -739,16 +959,17 @@ move_to (void *closure,
const cairo_point_t *point)
{
struct stroker *stroker = closure;
- cairo_status_t status;
/* Cap the start and end of the previous sub path as needed */
- status = _cairo_stroker_add_caps (stroker);
- if (unlikely (status))
- return status;
+ add_caps (stroker);
stroker->first_point = *point;
stroker->current_point = *point;
+#if DEBUG
+ _cairo_contour_add_point (&stroker->path, point);
+#endif
+
stroker->has_first_face = FALSE;
stroker->has_current_face = FALSE;
stroker->has_initial_sub_path = FALSE;
@@ -761,40 +982,73 @@ line_to (void *closure,
const cairo_point_t *point)
{
struct stroker *stroker = closure;
- cairo_stroke_face_t start, end;
+ cairo_stroke_face_t start;
cairo_point_t *p1 = &stroker->current_point;
cairo_slope_t dev_slope;
- double slope_dx, slope_dy;
- cairo_status_t status;
stroker->has_initial_sub_path = TRUE;
if (p1->x == point->x && p1->y == point->y)
return CAIRO_STATUS_SUCCESS;
- _cairo_slope_init (&dev_slope, p1, point);
- slope_dx = _cairo_fixed_to_double (point->x - p1->x);
- slope_dy = _cairo_fixed_to_double (point->y - p1->y);
- normalized_device_slope (&slope_dx, &slope_dy, stroker->ctm_inverse);
+#if DEBUG
+ _cairo_contour_add_point (&stroker->path, point);
+#endif
- status = add_segment (stroker, p1, point, &dev_slope,
- slope_dx, slope_dy,
- &start, &end);
- if (unlikely (status))
- return status;
+ _cairo_slope_init (&dev_slope, p1, point);
+ compute_face (p1, &dev_slope, stroker, &start);
if (stroker->has_current_face) {
/* Join with final face from previous segment */
- status = join (stroker, &stroker->current_face, &start);
- if (unlikely (status))
- return status;
- } else if (! stroker->has_first_face) {
- /* Save sub path's first face in case needed for closing join */
- stroker->first_face = start;
- stroker->has_first_face = TRUE;
+ join (stroker, &stroker->current_face, &start);
+ inner_join (stroker, &stroker->current_face, &start);
+ } else {
+ if (! stroker->has_first_face) {
+ /* Save sub path's first face in case needed for closing join */
+ stroker->first_face = start;
+ stroker->has_first_face = TRUE;
+ }
+ stroker->has_current_face = TRUE;
+
+ contour_add_point (&stroker->cw, &start.cw);
+ contour_add_point (&stroker->ccw, &start.ccw);
}
- stroker->current_face = end;
- stroker->has_current_face = TRUE;
+
+ stroker->current_face = start;
+ stroker->current_face.point = *point;
+ stroker->current_face.ccw.x += dev_slope.dx;
+ stroker->current_face.ccw.y += dev_slope.dy;
+ stroker->current_face.cw.x += dev_slope.dx;
+ stroker->current_face.cw.y += dev_slope.dy;
+
+ contour_add_point (&stroker->cw, &stroker->current_face.cw);
+ contour_add_point (&stroker->ccw, &stroker->current_face.ccw);
+
+ stroker->current_point = *point;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+spline_to (void *closure,
+ const cairo_point_t *point)
+{
+ struct stroker *stroker = closure;
+ cairo_slope_t dev_slope;
+
+ if (stroker->current_point.x == point->x &&
+ stroker->current_point.y == point->y)
+ return CAIRO_STATUS_SUCCESS;
+
+#if DEBUG
+ _cairo_contour_add_point (&stroker->path, point);
+#endif
+
+ _cairo_slope_init (&dev_slope, &stroker->current_point, point);
+ compute_face (point, &dev_slope, stroker, &stroker->current_face);
+
+ contour_add_point (&stroker->cw, &stroker->current_face.cw);
+ contour_add_point (&stroker->ccw, &stroker->current_face.ccw);
stroker->current_point = *point;
@@ -809,64 +1063,49 @@ curve_to (void *closure,
{
struct stroker *stroker = closure;
cairo_spline_t spline;
- cairo_line_join_t line_join_save;
cairo_stroke_face_t face;
- double slope_dx, slope_dy;
cairo_status_t status = CAIRO_STATUS_SUCCESS;
- if (! _cairo_spline_init (&spline, line_to, stroker,
+ if (! _cairo_spline_init (&spline, spline_to, stroker,
&stroker->current_point, b, c, d))
{
return line_to (closure, d);
}
/* Compute the initial face */
- slope_dx = _cairo_fixed_to_double (spline.initial_slope.dx);
- slope_dy = _cairo_fixed_to_double (spline.initial_slope.dy);
- normalized_device_slope (&slope_dx, &slope_dy, stroker->ctm_inverse);
compute_face (&stroker->current_point,
&spline.initial_slope,
- slope_dx, slope_dy,
stroker, &face);
if (stroker->has_current_face) {
- status = join (stroker, &stroker->current_face, &face);
- if (unlikely (status))
- return status;
- } else if (! stroker->has_first_face) {
- stroker->first_face = face;
- stroker->has_first_face = TRUE;
- }
+ join (stroker, &stroker->current_face, &face);
+ inner_join (stroker, &stroker->current_face, &face);
+ } else {
+ contour_add_point (&stroker->cw, &face.cw);
+ contour_add_point (&stroker->ccw, &face.ccw);
+ if (! stroker->has_first_face) {
+ /* Save sub path's first face in case needed for closing join */
+ stroker->first_face = face;
+ stroker->has_first_face = TRUE;
+ }
+ stroker->has_current_face = TRUE;
+ }
stroker->current_face = face;
- stroker->has_current_face = TRUE;
/* Temporarily modify the stroker to use round joins to guarantee
* smooth stroked curves. */
- line_join_save = stroker->style.line_join;
- stroker->style.line_join = CAIRO_LINE_JOIN_ROUND;
-
status = _cairo_spline_decompose (&spline, stroker->tolerance);
- if (unlikely (status))
- return status;
+ assert (status == CAIRO_STATUS_SUCCESS);
- /* And join the final face */
- slope_dx = _cairo_fixed_to_double (spline.final_slope.dx);
- slope_dy = _cairo_fixed_to_double (spline.final_slope.dy);
- normalized_device_slope (&slope_dx, &slope_dy, stroker->ctm_inverse);
- compute_face (&stroker->current_point,
- &spline.final_slope,
- slope_dx, slope_dy,
- stroker, &face);
+ /* Tweak the final point to lie on the out-face */
+ compute_face (d, &spline.final_slope, stroker, &face);
- status = join (stroker, &stroker->current_face, &face);
- if (unlikely (status))
- return status;
+ *_cairo_contour_last_point (&stroker->cw.contour) = face.cw;
+ *_cairo_contour_last_point (&stroker->ccw.contour) = face.ccw;
stroker->current_face = face;
- stroker->style.line_join = line_join_save;
-
return CAIRO_STATUS_SUCCESS;
}
@@ -882,14 +1121,35 @@ close_path (void *closure)
if (stroker->has_first_face && stroker->has_current_face) {
/* Join first and final faces of sub path */
- status = join (stroker, &stroker->current_face, &stroker->first_face);
- if (unlikely (status))
- return status;
+ outer_close (stroker, &stroker->current_face, &stroker->first_face);
+ inner_close (stroker, &stroker->current_face, &stroker->first_face);
+#if 0
+ *_cairo_contour_first_point (&stroker->ccw.contour) =
+ *_cairo_contour_last_point (&stroker->ccw.contour);
+
+ *_cairo_contour_first_point (&stroker->cw.contour) =
+ *_cairo_contour_last_point (&stroker->cw.contour);
+#endif
+
+ _cairo_polygon_add_contour (stroker->polygon, &stroker->cw.contour);
+ _cairo_polygon_add_contour (stroker->polygon, &stroker->ccw.contour);
+
+#if DEBUG
+ {
+ FILE *file = fopen ("contours.txt", "a");
+ _cairo_debug_print_contour (file, &stroker->path);
+ _cairo_debug_print_contour (file, &stroker->cw.contour);
+ _cairo_debug_print_contour (file, &stroker->ccw.contour);
+ fclose (file);
+ }
+
+ _cairo_contour_reset (&stroker->path);
+#endif
+ _cairo_contour_reset (&stroker->cw.contour);
+ _cairo_contour_reset (&stroker->ccw.contour);
} else {
/* Cap the start and end of the sub path as needed */
- status = _cairo_stroker_add_caps (stroker);
- if (unlikely (status))
- return status;
+ add_caps (stroker);
}
stroker->has_initial_sub_path = FALSE;
@@ -924,8 +1184,8 @@ _cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t *path,
stroker.ctm_inverse = ctm_inverse;
stroker.tolerance = tolerance;
- stroker.ctm_determinant = _cairo_matrix_compute_determinant (ctm);
- stroker.ctm_det_positive = stroker.ctm_determinant >= 0.0;
+ stroker.ctm_det_positive =
+ _cairo_matrix_compute_determinant (ctm) >= 0.0;
status = _cairo_pen_init (&stroker.pen,
style->line_width / 2.0,
@@ -942,6 +1202,15 @@ _cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t *path,
stroker.has_first_face = FALSE;
stroker.has_initial_sub_path = FALSE;
+#if DEBUG
+ _cairo_contour_init (&stroker.path, 0);
+#endif
+ _cairo_contour_init (&stroker.cw.contour, 1);
+ _cairo_contour_init (&stroker.ccw.contour, -1);
+ tolerance *= 1 << CAIRO_FIXED_FRAC_BITS;
+ tolerance *= tolerance;
+ stroker.cw.tolerance = tolerance;
+ stroker.ccw.tolerance = tolerance;
stroker.polygon = polygon;
status = _cairo_path_fixed_interpret (path,
@@ -952,8 +1221,10 @@ _cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t *path,
&stroker);
/* Cap the start and end of the final sub path as needed */
if (likely (status == CAIRO_STATUS_SUCCESS))
- status = _cairo_stroker_add_caps (&stroker);
+ add_caps (&stroker);
+ _cairo_contour_fini (&stroker.cw.contour);
+ _cairo_contour_fini (&stroker.ccw.contour);
_cairo_pen_fini (&stroker.pen);
return status;
diff --git a/src/cairo-polygon.c b/src/cairo-polygon.c
index 74ec9fa..6436712 100644
--- a/src/cairo-polygon.c
+++ b/src/cairo-polygon.c
@@ -38,12 +38,14 @@
#include "cairoint.h"
#include "cairo-boxes-private.h"
+#include "cairo-contour-private.h"
#include "cairo-error-private.h"
static void
_cairo_polygon_add_edge (cairo_polygon_t *polygon,
const cairo_point_t *p1,
- const cairo_point_t *p2);
+ const cairo_point_t *p2,
+ int dir);
void
_cairo_polygon_init (cairo_polygon_t *polygon,
@@ -131,12 +133,12 @@ _cairo_polygon_init_boxes (cairo_polygon_t *polygon,
p1 = chunk->base[i].p1;
p2.x = p1.x;
p2.y = chunk->base[i].p2.y;
- _cairo_polygon_add_edge (polygon, &p1, &p2);
+ _cairo_polygon_add_edge (polygon, &p1, &p2, 1);
p1 = chunk->base[i].p2;
p2.x = p1.x;
p2.y = chunk->base[i].p1.y;
- _cairo_polygon_add_edge (polygon, &p1, &p2);
+ _cairo_polygon_add_edge (polygon, &p1, &p2, 1);
}
}
@@ -178,12 +180,12 @@ _cairo_polygon_init_box_array (cairo_polygon_t *polygon,
p1 = boxes[i].p1;
p2.x = p1.x;
p2.y = boxes[i].p2.y;
- _cairo_polygon_add_edge (polygon, &p1, &p2);
+ _cairo_polygon_add_edge (polygon, &p1, &p2, 1);
p1 = boxes[i].p2;
p2.x = p1.x;
p2.y = boxes[i].p1.y;
- _cairo_polygon_add_edge (polygon, &p1, &p2);
+ _cairo_polygon_add_edge (polygon, &p1, &p2, 1);
}
return polygon->status;
@@ -406,20 +408,17 @@ _add_clipped_edge (cairo_polygon_t *polygon,
static void
_cairo_polygon_add_edge (cairo_polygon_t *polygon,
const cairo_point_t *p1,
- const cairo_point_t *p2)
+ const cairo_point_t *p2,
+ int dir)
{
- int dir;
-
/* drop horizontal edges */
if (p1->y == p2->y)
return;
- if (p1->y < p2->y) {
- dir = 1;
- } else {
+ if (p1->y > p2->y) {
const cairo_point_t *t;
t = p1, p1 = p2, p2 = t;
- dir = -1;
+ dir = -dir;
}
if (polygon->num_limits) {
@@ -439,7 +438,7 @@ _cairo_polygon_add_external_edge (void *polygon,
const cairo_point_t *p1,
const cairo_point_t *p2)
{
- _cairo_polygon_add_edge (polygon, p1, p2);
+ _cairo_polygon_add_edge (polygon, p1, p2, 1);
return _cairo_polygon_status (polygon);
}
@@ -469,3 +468,26 @@ _cairo_polygon_add_line (cairo_polygon_t *polygon,
return polygon->status;
}
+
+cairo_status_t
+_cairo_polygon_add_contour (cairo_polygon_t *polygon,
+ const cairo_contour_t *contour)
+{
+ const struct _cairo_contour_chain *chain;
+ const cairo_point_t *prev = NULL;
+ int i;
+
+ if (contour->chain.num_points <= 1)
+ return CAIRO_INT_STATUS_SUCCESS;
+
+ prev = &contour->chain.points[0];
+ for (chain = &contour->chain; chain; chain = chain->next) {
+ for (i = 0; i < chain->num_points; i++) {
+ _cairo_polygon_add_edge (polygon, prev, &chain->points[i],
+ contour->direction);
+ prev = &chain->points[i];
+ }
+ }
+
+ return polygon->status;
+}
diff --git a/src/cairo-slope-private.h b/src/cairo-slope-private.h
index 6a58c9f..72dc0dd 100644
--- a/src/cairo-slope-private.h
+++ b/src/cairo-slope-private.h
@@ -57,11 +57,17 @@ _cairo_slope_equal (const cairo_slope_t *a, const cairo_slope_t *b)
_cairo_int32x32_64_mul (b->dy, a->dx));
}
+static inline cairo_int64_t
+_cairo_slope_dot_product (const cairo_slope_t *a, const cairo_slope_t *b)
+{
+ return _cairo_int64_add (_cairo_int32x32_64_mul (a->dx, b->dx),
+ _cairo_int32x32_64_mul (a->dy, b->dy));
+}
+
static inline cairo_bool_t
_cairo_slope_backwards (const cairo_slope_t *a, const cairo_slope_t *b)
{
- return _cairo_int64_negative (_cairo_int64_add (_cairo_int32x32_64_mul (a->dx, b->dx),
- _cairo_int32x32_64_mul (a->dy, b->dy)));
+ return _cairo_int64_negative (_cairo_slope_dot_product (a, b));
}
cairo_private int
diff --git a/src/cairo-types-private.h b/src/cairo-types-private.h
index 4933cf2..f7a1f26 100644
--- a/src/cairo-types-private.h
+++ b/src/cairo-types-private.h
@@ -63,6 +63,7 @@ typedef struct _cairo_clip cairo_clip_t;
typedef struct _cairo_clip_path cairo_clip_path_t;
typedef struct _cairo_color cairo_color_t;
typedef struct _cairo_color_stop cairo_color_stop_t;
+typedef struct _cairo_contour cairo_contour_t;
typedef struct _cairo_device_backend cairo_device_backend_t;
typedef struct _cairo_font_face_backend cairo_font_face_backend_t;
typedef struct _cairo_gstate cairo_gstate_t;
diff --git a/src/cairoint.h b/src/cairoint.h
index f745f41..c6243be 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -1030,6 +1030,7 @@ typedef struct _cairo_stroke_face {
cairo_point_t point;
cairo_point_t cw;
cairo_slope_t dev_vector;
+ cairo_point_double_t dev_slope;
cairo_point_double_t usr_vector;
} cairo_stroke_face_t;
@@ -2097,6 +2098,10 @@ _cairo_polygon_add_external_edge (void *polygon,
const cairo_point_t *p2);
cairo_private cairo_status_t
+_cairo_polygon_add_contour (cairo_polygon_t *polygon,
+ const cairo_contour_t *contour);
+
+cairo_private cairo_status_t
_cairo_polygon_reduce (cairo_polygon_t *polygon,
cairo_fill_rule_t fill_rule);
diff --git a/util/.gitignore b/util/.gitignore
index ff7ffbd..c3ac04f 100644
--- a/util/.gitignore
+++ b/util/.gitignore
@@ -2,6 +2,7 @@
.libs
Makefile
Makefile.in
+show-contour
show-edges
show-events
show-traps
diff --git a/util/Makefile.am b/util/Makefile.am
index f3aa079..6c6c849 100644
--- a/util/Makefile.am
+++ b/util/Makefile.am
@@ -32,7 +32,7 @@ AM_CPPFLAGS = -I$(top_srcdir)/src \
-I$(top_srcdir)/util/cairo-script \
$(CAIRO_CFLAGS)
-EXTRA_PROGRAMS += show-traps show-edges show-polygon show-events
+EXTRA_PROGRAMS += show-contour show-traps show-edges show-polygon show-events
if CAIRO_HAS_INTERPRETER
EXTRA_PROGRAMS += trace-to-xml xml-to-trace
endif
@@ -56,6 +56,11 @@ show_edges_CFLAGS = $(gtk_CFLAGS)
#show_edges_LDADD = $(top_builddir)/src/libcairo.la $(gtk_LIBS)
show_edges_LDADD = $(gtk_LIBS)
+show_contour_SOURCES = show-contour.c
+show_contour_CFLAGS = $(gtk_CFLAGS)
+#show_contour_LDADD = $(top_builddir)/src/libcairo.la $(gtk_LIBS)
+show_contour_LDADD = $(gtk_LIBS)
+
show_events_SOURCES = show-events.c
show_events_CFLAGS = $(gtk_CFLAGS)
#show_events_LDADD = $(top_builddir)/src/libcairo.la $(gtk_LIBS)
diff --git a/util/show-contour.c b/util/show-contour.c
new file mode 100644
index 0000000..1e549cb
--- /dev/null
+++ b/util/show-contour.c
@@ -0,0 +1,652 @@
+#define _GNU_SOURCE
+#include <gtk/gtk.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <gdk/gdkkeysyms.h>
+#include <math.h>
+
+typedef struct _point {
+ gdouble x, y;
+} point_t;
+typedef struct _box {
+ point_t p1, p2;
+} box_t;
+
+typedef struct _contour {
+ struct _contour *next, *prev;
+ int direction;
+ int num_points;
+ int size;
+ point_t points[0];
+} contour_t;
+
+typedef struct _TrapView {
+ GtkWidget widget;
+
+ cairo_surface_t *pixmap;
+ int pixmap_width, pixmap_height;
+
+ box_t extents;
+ contour_t *contours;
+
+ double px, py;
+
+ gint mag_x, mag_y;
+ gint mag_size;
+ gdouble mag_zoom;
+ gboolean in_mag_drag;
+ gint mag_drag_x, mag_drag_y;
+} TrapView;
+
+typedef struct _TrapViewClass {
+ GtkWidgetClass parent_class;
+} TrapViewClass;
+
+G_DEFINE_TYPE (TrapView, trap_view, GTK_TYPE_WIDGET)
+
+static cairo_surface_t *
+pixmap_create (TrapView *self, cairo_surface_t *target)
+{
+ cairo_surface_t *surface =
+ cairo_surface_create_similar (target, CAIRO_CONTENT_COLOR,
+ self->widget.allocation.width,
+ self->widget.allocation.height);
+ cairo_t *cr = cairo_create (surface);
+ contour_t *contour;
+ gdouble sf_x, sf_y, sf;
+ gdouble mid, dim;
+ gdouble x0, y0;
+ int n;
+ box_t extents;
+
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ cairo_paint (cr);
+
+ if (self->contours == NULL) {
+ cairo_destroy(cr);
+ return surface;
+ }
+
+ extents = self->extents;
+
+ mid = (extents.p2.x + extents.p1.x) / 2.;
+ dim = (extents.p2.x - extents.p1.x) / 2. * 1.25;
+ sf_x = self->widget.allocation.width / dim / 2;
+
+ mid = (extents.p2.y + extents.p1.y) / 2.;
+ dim = (extents.p2.y - extents.p1.y) / 2. * 1.25;
+ sf_y = self->widget.allocation.height / dim / 2;
+
+ sf = MIN (sf_x, sf_y);
+
+ mid = (extents.p2.x + extents.p1.x) / 2.;
+ dim = sf_x / sf * (extents.p2.x - extents.p1.x) / 2. * 1.25;
+ x0 = mid - dim;
+ mid = (extents.p2.y + extents.p1.y) / 2.;
+ dim = sf_y / sf * (extents.p2.y - extents.p1.y) / 2. * 1.25;
+ y0 = mid - dim;
+
+ for (contour = self->contours; contour; contour = contour->next) {
+ if (contour->num_points == 0)
+ continue;
+
+ cairo_save (cr); {
+ cairo_scale (cr, sf, sf);
+ cairo_translate (cr, -x0, -y0);
+ switch (contour->direction) {
+ case -1:
+ cairo_set_source_rgb (cr, 0.0, 0.0, 1.0);
+ break;
+ case 0:
+ cairo_set_source_rgb (cr, 0.0, 1.0, 0.0);
+ break;
+ case 1:
+ cairo_set_source_rgb (cr, 1.0, 0.0, 0.0);
+ break;
+ }
+ {
+ const point_t *p = &contour->points[0];
+ cairo_arc (cr, p->x, p->y, 4/sf, 0, 2 * M_PI);
+ cairo_save (cr);
+ cairo_identity_matrix (cr);
+ cairo_stroke (cr);
+ cairo_restore (cr);
+ }
+ for (n = 0; n < contour->num_points; n++) {
+ const point_t *p = &contour->points[n];
+ cairo_arc (cr, p->x, p->y, 2/sf, 0, 2 * M_PI);
+ cairo_fill (cr);
+ }
+ for (n = 0; n < contour->num_points; n++) {
+ const point_t *p = &contour->points[n];
+ cairo_line_to (cr, p->x, p->y);
+ }
+ } cairo_restore (cr);
+
+ switch (contour->direction) {
+ case -1:
+ cairo_set_source_rgb (cr, 0.3, 0.3, 0.9);
+ break;
+ case 0:
+ cairo_set_source_rgb (cr, 0.3, 0.9, 0.3);
+ break;
+ case 1:
+ cairo_set_source_rgb (cr, 0.9, 0.3, 0.3);
+ break;
+ }
+ cairo_set_line_width (cr, 1.);
+ cairo_stroke (cr);
+ }
+
+ cairo_destroy (cr);
+ return surface;
+}
+
+static void
+trap_view_draw (TrapView *self, cairo_t *cr)
+{
+ contour_t *contour;
+ gdouble sf_x, sf_y, sf;
+ gdouble mid, dim;
+ gdouble x0, y0;
+ int n;
+ box_t extents;
+
+ extents = self->extents;
+
+ mid = (extents.p2.x + extents.p1.x) / 2.;
+ dim = (extents.p2.x - extents.p1.x) / 2. * 1.25;
+ sf_x = self->widget.allocation.width / dim / 2;
+
+ mid = (extents.p2.y + extents.p1.y) / 2.;
+ dim = (extents.p2.y - extents.p1.y) / 2. * 1.25;
+ sf_y = self->widget.allocation.height / dim / 2;
+
+ sf = MIN (sf_x, sf_y);
+
+ mid = (extents.p2.x + extents.p1.x) / 2.;
+ dim = sf_x / sf * (extents.p2.x - extents.p1.x) / 2. * 1.25;
+ x0 = mid - dim;
+ mid = (extents.p2.y + extents.p1.y) / 2.;
+ dim = sf_y / sf * (extents.p2.y - extents.p1.y) / 2. * 1.25;
+ y0 = mid - dim;
+
+ if (self->pixmap_width != self->widget.allocation.width ||
+ self->pixmap_height != self->widget.allocation.height)
+ {
+ cairo_surface_destroy (self->pixmap);
+ self->pixmap = pixmap_create (self, cairo_get_target (cr));
+ self->pixmap_width = self->widget.allocation.width;
+ self->pixmap_height = self->widget.allocation.height;
+ }
+
+ cairo_set_source_surface (cr, self->pixmap, 0, 0);
+ cairo_paint (cr);
+
+ if (self->contours == NULL)
+ return;
+
+ /* draw a zoom view of the area around the mouse */
+ if (1) {
+ double zoom = self->mag_zoom;
+ int size = self->mag_size;
+ int mag_x = self->mag_x;
+ int mag_y = self->mag_y;
+
+ if (1) {
+ mag_x = self->px + size/4;
+ mag_y = self->py - size/2;
+ }
+
+ cairo_save (cr); {
+ /* bottom right */
+ cairo_rectangle (cr, mag_x, mag_y, size, size);
+ cairo_stroke_preserve (cr);
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ cairo_fill_preserve (cr);
+ cairo_clip (cr);
+
+ /* compute roi in extents */
+ cairo_translate (cr, mag_x + size/2, mag_y + size/2);
+
+ cairo_save (cr); {
+ for (contour = self->contours; contour; contour = contour->next) {
+ if (contour->num_points == 0)
+ continue;
+
+ cairo_save (cr); {
+ cairo_scale (cr, zoom, zoom);
+ cairo_translate (cr, -(self->px / sf + x0), -(self->py /sf + y0));
+ switch (contour->direction) {
+ case -1:
+ cairo_set_source_rgb (cr, 0.0, 0.0, 1.0);
+ break;
+ case 0:
+ cairo_set_source_rgb (cr, 0.0, 1.0, 0.0);
+ break;
+ case 1:
+ cairo_set_source_rgb (cr, 1.0, 0.0, 0.0);
+ break;
+ }
+ for (n = 0; n < contour->num_points; n++) {
+ const point_t *p = &contour->points[n];
+ cairo_arc (cr, p->x, p->y, 2/zoom, 0, 2 * M_PI);
+ cairo_fill (cr);
+ }
+ for (n = 0; n < contour->num_points; n++) {
+ const point_t *p = &contour->points[n];
+ cairo_line_to (cr, p->x, p->y);
+ }
+ } cairo_restore (cr);
+
+ switch (contour->direction) {
+ case -1:
+ cairo_set_source_rgb (cr, 0.3, 0.3, 0.9);
+ break;
+ case 0:
+ cairo_set_source_rgb (cr, 0.3, 0.9, 0.3);
+ break;
+ case 1:
+ cairo_set_source_rgb (cr, 0.9, 0.3, 0.3);
+ break;
+ }
+ cairo_stroke (cr);
+ }
+ } cairo_restore (cr);
+
+ /* grid */
+ cairo_save (cr); {
+ int i;
+
+ cairo_translate (cr,
+ -zoom*fmod (self->px/sf + x0, 1.),
+ -zoom*fmod (self->py/sf + y0, 1.));
+ zoom /= 2;
+ for (i = -size/2/zoom; i <= size/2/zoom + 1; i+=2) {
+ cairo_move_to (cr, zoom*i, -size/2);
+ cairo_line_to (cr, zoom*i, size/2 + zoom);
+ cairo_move_to (cr, -size/2, zoom*i);
+ cairo_line_to (cr, size/2 + zoom, zoom*i);
+ }
+ zoom *= 2;
+ cairo_set_source_rgba (cr, .7, .7, .7, .5);
+ cairo_set_line_width (cr, 1.);
+ cairo_stroke (cr);
+
+ for (i = -size/2/zoom - 1; i <= size/2/zoom + 1; i++) {
+ cairo_move_to (cr, zoom*i, -size/2);
+ cairo_line_to (cr, zoom*i, size/2 + zoom);
+ cairo_move_to (cr, -size/2, zoom*i);
+ cairo_line_to (cr, size/2 + zoom, zoom*i);
+ }
+ cairo_set_source_rgba (cr, .1, .1, .1, .5);
+ cairo_set_line_width (cr, 2.);
+ cairo_stroke (cr);
+ } cairo_restore (cr);
+
+ } cairo_restore (cr);
+ }
+}
+
+
+static gdouble
+edge_length (const point_t *p1, const point_t *p2)
+{
+ return hypot (p2->x - p1->x, p2->y - p1->y);
+}
+
+static gdouble
+contour_compute_total_length (const contour_t *contour)
+{
+ int n;
+ gdouble len = 0.;
+ for (n = 1; n < contour->num_points; n++)
+ len += edge_length (&contour->points[n-1], &contour->points[n]);
+ return len;
+}
+
+static void
+trap_view_draw_labels (TrapView *self, cairo_t *cr)
+{
+ contour_t *contour;
+ int y = 12;
+
+ for (contour = self->contours; contour; contour = contour->next) {
+ double total_length = contour_compute_total_length (contour) / 256.;
+ PangoLayout *layout;
+ gint width, height;
+ GString *string;
+ gchar *str;
+
+ if (contour->num_points == 0)
+ continue;
+
+ string = g_string_new (NULL);
+ g_string_append_printf (string,
+ "Number of points:\t%d\n"
+ "Total length of contour: \t%.2f",
+ contour->num_points,
+ total_length);
+
+ str = g_string_free (string, FALSE);
+ layout = gtk_widget_create_pango_layout (&self->widget, str);
+ g_free (str);
+
+ pango_layout_get_pixel_size (layout, &width, &height);
+
+ switch (contour->direction) {
+ case -1:
+ cairo_set_source_rgb (cr, 0.9, 0.3, 0.3);
+ break;
+ case 0:
+ cairo_set_source_rgb (cr, 0.3, 0.9, 0.3);
+ break;
+ case 1:
+ cairo_set_source_rgb (cr, 0.3, 0.3, 0.9);
+ break;
+ }
+
+ cairo_move_to (cr, 10, y);
+ pango_cairo_show_layout (cr, layout);
+ g_object_unref (layout);
+
+ y += height + 4;
+ }
+}
+
+static gboolean
+trap_view_expose (GtkWidget *w, GdkEventExpose *ev)
+{
+ TrapView *self = (TrapView *) w;
+ cairo_t *cr;
+
+ cr = gdk_cairo_create (w->window);
+ gdk_cairo_region (cr, ev->region);
+ cairo_clip (cr);
+
+ trap_view_draw (self, cr);
+ trap_view_draw_labels (self, cr);
+
+ cairo_destroy (cr);
+ return FALSE;
+}
+
+static gboolean
+trap_view_key_press (GtkWidget *w, GdkEventKey *ev)
+{
+ switch (ev->keyval) {
+ case GDK_Escape:
+ case GDK_Q:
+ gtk_main_quit ();
+ break;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+trap_view_button_press (GtkWidget *w, GdkEventButton *ev)
+{
+ TrapView *self = (TrapView *) w;
+
+ if (ev->x < self->mag_x ||
+ ev->y < self->mag_y ||
+ ev->x > self->mag_x + self->mag_size ||
+ ev->y > self->mag_y + self->mag_size)
+ {
+ }
+ else
+ {
+ self->in_mag_drag = TRUE;
+ self->mag_drag_x = ev->x;
+ self->mag_drag_y = ev->y;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+trap_view_button_release (GtkWidget *w, GdkEventButton *ev)
+{
+ TrapView *self = (TrapView *) w;
+
+ self->in_mag_drag = FALSE;
+
+ return FALSE;
+}
+
+static void
+trap_view_update_mouse (TrapView *self, GdkEventMotion *ev)
+{
+ self->px = ev->x;
+ self->py = ev->y;
+
+ gtk_widget_queue_draw (&self->widget);
+}
+
+static void
+trap_view_update_magnifier (TrapView *self, gint *xy)
+{
+ self->mag_x = xy[0];
+ self->mag_y = xy[1];
+
+ gtk_widget_queue_draw (&self->widget);
+}
+
+static gboolean
+trap_view_motion (GtkWidget *w, GdkEventMotion *ev)
+{
+ TrapView *self = (TrapView *) w;
+
+ if (self->in_mag_drag) {
+ int xy[2];
+
+ xy[0] = self->mag_x + ev->x - self->mag_drag_x;
+ xy[1] = self->mag_y + ev->y - self->mag_drag_y;
+
+ trap_view_update_magnifier (self, xy);
+
+ self->mag_drag_x = ev->x;
+ self->mag_drag_y = ev->y;
+ } else if (ev->x < self->mag_x ||
+ ev->y < self->mag_y ||
+ ev->x > self->mag_x + self->mag_size ||
+ ev->y > self->mag_y + self->mag_size)
+ {
+ trap_view_update_mouse (self, ev);
+ }
+
+ return FALSE;
+}
+
+static void
+trap_view_realize (GtkWidget *widget)
+{
+ GdkWindowAttr attributes;
+
+ GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+
+ attributes.window_type = GDK_WINDOW_CHILD;
+ attributes.x = widget->allocation.x;
+ attributes.y = widget->allocation.y;
+ attributes.width = widget->allocation.width;
+ attributes.height = widget->allocation.height;
+ attributes.wclass = GDK_INPUT_OUTPUT;
+ attributes.visual = gtk_widget_get_visual (widget);
+ attributes.colormap = gtk_widget_get_colormap (widget);
+ attributes.event_mask = gtk_widget_get_events (widget) |
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_KEY_PRESS_MASK |
+ GDK_KEY_RELEASE_MASK |
+ GDK_POINTER_MOTION_MASK |
+ GDK_BUTTON_MOTION_MASK |
+ GDK_EXPOSURE_MASK;
+
+ widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
+ &attributes,
+ GDK_WA_X | GDK_WA_Y |
+ GDK_WA_VISUAL | GDK_WA_COLORMAP);
+ gdk_window_set_user_data (widget->window, widget);
+
+ widget->style = gtk_style_attach (widget->style, widget->window);
+ gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
+}
+
+static void
+trap_view_size_allocate (GtkWidget *w, GdkRectangle *r)
+{
+ TrapView *self = (TrapView *) w;
+
+ GTK_WIDGET_CLASS (trap_view_parent_class)->size_allocate (w, r);
+
+ self->mag_x = w->allocation.width - self->mag_size - 10;
+ self->mag_y = w->allocation.height - self->mag_size - 10;
+}
+
+static void
+trap_view_finalize (GObject *obj)
+{
+ G_OBJECT_CLASS (trap_view_parent_class)->finalize (obj);
+}
+
+static void
+trap_view_class_init (TrapViewClass *klass)
+{
+ GObjectClass *object_class = (GObjectClass *) klass;
+ GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
+
+ object_class->finalize = trap_view_finalize;
+
+ widget_class->realize = trap_view_realize;
+ widget_class->size_allocate = trap_view_size_allocate;
+ widget_class->expose_event = trap_view_expose;
+ widget_class->key_press_event = trap_view_key_press;
+ widget_class->button_press_event = trap_view_button_press;
+ widget_class->button_release_event = trap_view_button_release;
+ widget_class->motion_notify_event = trap_view_motion;
+}
+
+static void
+trap_view_init (TrapView *self)
+{
+ self->mag_zoom = 64;
+ self->mag_size = 200;
+
+ self->extents.p1.x = G_MAXDOUBLE;
+ self->extents.p1.y = G_MAXDOUBLE;
+ self->extents.p2.x = -G_MAXDOUBLE;
+ self->extents.p2.y = -G_MAXDOUBLE;
+
+ GTK_WIDGET_SET_FLAGS (self, GTK_CAN_FOCUS);
+}
+
+static contour_t *
+_contour_add_point (TrapView *tv, contour_t *contour, point_t *p)
+{
+ if (contour == NULL)
+ return NULL;
+
+ if (p->y < tv->extents.p1.y)
+ tv->extents.p1.y = p->y;
+ if (p->y > tv->extents.p2.y)
+ tv->extents.p2.y = p->y;
+
+ if (p->x < tv->extents.p1.x)
+ tv->extents.p1.x = p->x;
+ if (p->x > tv->extents.p2.x)
+ tv->extents.p2.x = p->x;
+
+ if (contour->num_points == contour->size) {
+ int newsize = 2 * contour->size;
+ void *newcontour;
+
+ newcontour = g_realloc (contour,
+ sizeof (contour_t) + newsize * sizeof (point_t));
+ if (newcontour == NULL)
+ return contour;
+
+ contour = newcontour;
+ contour->size = newsize;
+
+ if (contour->next != NULL)
+ contour->next->prev = newcontour;
+ if (contour->prev != NULL)
+ contour->prev->next = newcontour;
+ else
+ tv->contours = newcontour;
+ }
+
+ contour->points[contour->num_points++] = *p;
+
+ return contour;
+}
+
+static contour_t *
+contour_new (TrapView *tv, int direction)
+{
+ contour_t *t;
+
+ t = g_malloc (sizeof (contour_t) + 128 * sizeof (point_t));
+ t->direction = direction;
+ t->prev = NULL;
+ t->next = tv->contours;
+ if (tv->contours)
+ tv->contours->prev = t;
+ tv->contours = t;
+
+ t->size = 128;
+ t->num_points = 0;
+
+ return t;
+}
+
+int
+main (int argc, char **argv)
+{
+ TrapView *tv;
+ contour_t *contour = NULL;
+ GtkWidget *window;
+ FILE *file;
+ char *line = NULL;
+ size_t len = 0;
+
+ gtk_init (&argc, &argv);
+
+ tv = g_object_new (trap_view_get_type (), NULL);
+
+ file = fopen (argv[1], "r");
+ if (file != NULL) {
+ while (getline (&line, &len, file) != -1) {
+ point_t p;
+ int direction;
+
+ if (sscanf (line, "contour: direction=%d", &direction)) {
+ if (contour)
+ g_print ("read %d contour\n", contour->num_points);
+
+ contour = contour_new (tv, direction);
+ } else if (sscanf (line, " [%*d] = (%lf, %lf)", &p.x, &p.y) == 2) {
+ contour = _contour_add_point (tv, contour, &p);
+ }
+ }
+
+ if (contour)
+ g_print ("read %d contour\n", contour->num_points);
+
+ g_print ("extents=(%lg, %lg), (%lg, %lg)\n",
+ tv->extents.p1.x, tv->extents.p1.y,
+ tv->extents.p2.x, tv->extents.p2.y);
+ fclose (file);
+ }
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ g_signal_connect (window, "delete-event",
+ G_CALLBACK (gtk_main_quit), NULL);
+ gtk_widget_set_size_request (window, 800, 800);
+ gtk_container_add (GTK_CONTAINER (window), &tv->widget);
+ gtk_widget_show_all (window);
+
+ gtk_main ();
+ return 0;
+}
commit 7b34be56c09271e9df4b12adf370e14aef7f57c9
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date: Thu Aug 4 17:31:05 2011 +0100
step1 of a fast stroke-to-polygon
diff --git a/src/Makefile.sources b/src/Makefile.sources
index 28c9d13..a1ff678 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -164,6 +164,7 @@ cairo_sources = \
cairo-path-in-fill.c \
cairo-path-stroke.c \
cairo-path-stroke-boxes.c \
+ cairo-path-stroke-polygon.c \
cairo-pattern.c \
cairo-pen.c \
cairo-polygon.c \
diff --git a/src/cairo-path-stroke-polygon.c b/src/cairo-path-stroke-polygon.c
new file mode 100644
index 0000000..d6cd82f
--- /dev/null
+++ b/src/cairo-path-stroke-polygon.c
@@ -0,0 +1,960 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth at cworth.org>
+ * Chris Wilson <chris at chris-wilson.co.uk>
+ */
+
+#define _BSD_SOURCE /* for hypot() */
+#include "cairoint.h"
+
+#include "cairo-box-private.h"
+#include "cairo-boxes-private.h"
+#include "cairo-error-private.h"
+#include "cairo-path-fixed-private.h"
+#include "cairo-slope-private.h"
+
+struct stroker {
+ cairo_stroke_style_t style;
+
+ cairo_polygon_t *polygon;
+
+ struct contour {
+ cairo_point_t first, current;
+ } inside, outside;
+
+ const cairo_matrix_t *ctm;
+ const cairo_matrix_t *ctm_inverse;
+ double tolerance;
+ double ctm_determinant;
+ cairo_bool_t ctm_det_positive;
+
+ cairo_pen_t pen;
+
+ cairo_point_t current_point;
+ cairo_point_t first_point;
+
+ cairo_bool_t has_initial_sub_path;
+
+ cairo_bool_t has_current_face;
+ cairo_stroke_face_t current_face;
+
+ cairo_bool_t has_first_face;
+ cairo_stroke_face_t first_face;
+};
+
+static void
+translate_point (cairo_point_t *point, const cairo_point_t *offset)
+{
+ point->x += offset->x;
+ point->y += offset->y;
+}
+
+static int
+join_is_clockwise (const cairo_stroke_face_t *in,
+ const cairo_stroke_face_t *out)
+{
+ cairo_slope_t in_slope, out_slope;
+
+ _cairo_slope_init (&in_slope, &in->point, &in->cw);
+ _cairo_slope_init (&out_slope, &out->point, &out->cw);
+
+ return _cairo_slope_compare (&in_slope, &out_slope) < 0;
+}
+
+/**
+ * _cairo_slope_compare_sgn
+ *
+ * Return -1, 0 or 1 depending on the relative slopes of
+ * two lines.
+ */
+static int
+_cairo_slope_compare_sgn (double dx1, double dy1, double dx2, double dy2)
+{
+ double c = (dx1 * dy2 - dx2 * dy1);
+
+ if (c > 0) return 1;
+ if (c < 0) return -1;
+ return 0;
+}
+
+static inline int
+_range_step (int i, int step, int max)
+{
+ i += step;
+ if (i < 0)
+ i = max - 1;
+ if (i >= max)
+ i = 0;
+ return i;
+}
+
+/*
+ * Construct a fan around the midpoint using the vertices from pen between
+ * inpt and outpt.
+ */
+static cairo_status_t
+_tessellate_fan (struct stroker *stroker,
+ const cairo_slope_t *in_vector,
+ const cairo_slope_t *out_vector,
+ const cairo_point_t *midpt,
+ const cairo_point_t *inpt,
+ const cairo_point_t *outpt,
+ cairo_bool_t clockwise)
+{
+ cairo_point_t stack_points[64], *points = stack_points;
+ int start, stop, step, i, npoints;
+ cairo_status_t status;
+
+ if (clockwise) {
+ step = -1;
+
+ start = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen,
+ in_vector);
+ if (_cairo_slope_compare (&stroker->pen.vertices[start].slope_ccw,
+ in_vector) < 0)
+ start = _range_step (start, -1, stroker->pen.num_vertices);
+
+ stop = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen,
+ out_vector);
+ if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_cw,
+ out_vector) > 0)
+ {
+ stop = _range_step (stop, 1, stroker->pen.num_vertices);
+ if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_ccw,
+ in_vector) < 0)
+ {
+ goto BEVEL;
+ }
+ }
+
+ npoints = start - stop;
+ } else {
+ step = 1;
+
+ start = _cairo_pen_find_active_cw_vertex_index (&stroker->pen,
+ in_vector);
+ if (_cairo_slope_compare (&stroker->pen.vertices[start].slope_cw,
+ in_vector) < 0)
+ start = _range_step (start, 1, stroker->pen.num_vertices);
+
+ stop = _cairo_pen_find_active_cw_vertex_index (&stroker->pen,
+ out_vector);
+ if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_ccw,
+ out_vector) > 0)
+ {
+ stop = _range_step (stop, -1, stroker->pen.num_vertices);
+ if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_cw,
+ in_vector) < 0)
+ {
+ goto BEVEL;
+ }
+ }
+
+ npoints = stop - start;
+ }
+ stop = _range_step (stop, step, stroker->pen.num_vertices);
+
+ if (npoints < 0)
+ npoints += stroker->pen.num_vertices;
+ npoints += 3;
+
+ if (npoints <= 1)
+ goto BEVEL;
+
+ if (npoints > ARRAY_LENGTH (stack_points)) {
+ points = _cairo_malloc_ab (npoints, sizeof (cairo_point_t));
+ if (unlikely (points == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+
+ /* Construct the fan. */
+ npoints = 0;
+ points[npoints++] = *inpt;
+ for (i = start;
+ i != stop;
+ i = _range_step (i, step, stroker->pen.num_vertices))
+ {
+ points[npoints] = *midpt;
+ translate_point (&points[npoints], &stroker->pen.vertices[i].point);
+ npoints++;
+ }
+ points[npoints++] = *outpt;
+
+ for (i = 0; i < npoints - 1; i++) {
+ if (clockwise) {
+ status = _cairo_polygon_add_external_edge (stroker->polygon,
+ &points[i], &points[i+1]);
+ } else {
+ status = _cairo_polygon_add_external_edge (stroker->polygon,
+ &points[i+1], &points[i]);
+ }
+ if (unlikely (status))
+ break;
+ }
+
+ if (points != stack_points)
+ free (points);
+
+ return status;
+
+BEVEL:
+ /* Ensure a leak free connection... */
+ if (clockwise)
+ return _cairo_polygon_add_external_edge (stroker->polygon, inpt, outpt);
+ else
+ return _cairo_polygon_add_external_edge (stroker->polygon, outpt, inpt);
+}
+
+static cairo_status_t
+join (struct stroker *stroker,
+ const cairo_stroke_face_t *in,
+ const cairo_stroke_face_t *out)
+{
+ int clockwise = join_is_clockwise (out, in);
+ const cairo_point_t *inpt, *outpt;
+ cairo_point_t points[4];
+ cairo_status_t status;
+
+ if (in->cw.x == out->cw.x && in->cw.y == out->cw.y &&
+ in->ccw.x == out->ccw.x && in->ccw.y == out->ccw.y)
+ {
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (clockwise) {
+ status = _cairo_polygon_add_external_edge (stroker->polygon,
+ &out->cw, &in->point);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_polygon_add_external_edge (stroker->polygon,
+ &in->point, &in->cw);
+ if (unlikely (status))
+ return status;
+
+ inpt = &in->ccw;
+ outpt = &out->ccw;
+ } else {
+ status = _cairo_polygon_add_external_edge (stroker->polygon,
+ &in->ccw, &in->point);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_polygon_add_external_edge (stroker->polygon,
+ &in->point, &out->ccw);
+ if (unlikely (status))
+ return status;
+
+ inpt = &in->cw;
+ outpt = &out->cw;
+ }
+
+ switch (stroker->style.line_join) {
+ case CAIRO_LINE_JOIN_ROUND:
+ /* construct a fan around the common midpoint */
+ return _tessellate_fan (stroker,
+ &in->dev_vector,
+ &out->dev_vector,
+ &in->point, inpt, outpt,
+ clockwise);
+
+ case CAIRO_LINE_JOIN_MITER:
+ default: {
+ /* dot product of incoming slope vector with outgoing slope vector */
+ double in_dot_out = -in->usr_vector.x * out->usr_vector.x +
+ -in->usr_vector.y * out->usr_vector.y;
+ double ml = stroker->style.miter_limit;
+
+ /* Check the miter limit -- lines meeting at an acute angle
+ * can generate long miters, the limit converts them to bevel
+ *
+ * Consider the miter join formed when two line segments
+ * meet at an angle psi:
+ *
+ * /.\
+ * /. .\
+ * /./ \.\
+ * /./psi\.\
+ *
+ * We can zoom in on the right half of that to see:
+ *
+ * |\
+ * | \ psi/2
+ * | \
+ * | \
+ * | \
+ * | \
+ * miter \
+ * length \
+ * | \
+ * | .\
+ * | . \
+ * |. line \
+ * \ width \
+ * \ \
+ *
+ *
+ * The right triangle in that figure, (the line-width side is
+ * shown faintly with three '.' characters), gives us the
+ * following expression relating miter length, angle and line
+ * width:
+ *
+ * 1 /sin (psi/2) = miter_length / line_width
+ *
+ * The right-hand side of this relationship is the same ratio
+ * in which the miter limit (ml) is expressed. We want to know
+ * when the miter length is within the miter limit. That is
+ * when the following condition holds:
+ *
+ * 1/sin(psi/2) <= ml
+ * 1 <= ml sin(psi/2)
+ * 1 <= ml² sin²(psi/2)
+ * 2 <= ml² 2 sin²(psi/2)
+ * 2·sin²(psi/2) = 1-cos(psi)
+ * 2 <= ml² (1-cos(psi))
+ *
+ * in · out = |in| |out| cos (psi)
+ *
+ * in and out are both unit vectors, so:
+ *
+ * in · out = cos (psi)
+ *
+ * 2 <= ml² (1 - in · out)
+ *
+ */
+ if (2 <= ml * ml * (1 - in_dot_out)) {
+ double x1, y1, x2, y2;
+ double mx, my;
+ double dx1, dx2, dy1, dy2;
+ double ix, iy;
+ double fdx1, fdy1, fdx2, fdy2;
+ double mdx, mdy;
+
+ /*
+ * we've got the points already transformed to device
+ * space, but need to do some computation with them and
+ * also need to transform the slope from user space to
+ * device space
+ */
+ /* outer point of incoming line face */
+ x1 = _cairo_fixed_to_double (inpt->x);
+ y1 = _cairo_fixed_to_double (inpt->y);
+ dx1 = in->usr_vector.x;
+ dy1 = in->usr_vector.y;
+ cairo_matrix_transform_distance (stroker->ctm, &dx1, &dy1);
+
+ /* outer point of outgoing line face */
+ x2 = _cairo_fixed_to_double (outpt->x);
+ y2 = _cairo_fixed_to_double (outpt->y);
+ dx2 = out->usr_vector.x;
+ dy2 = out->usr_vector.y;
+ cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2);
+
+ /*
+ * Compute the location of the outer corner of the miter.
+ * That's pretty easy -- just the intersection of the two
+ * outer edges. We've got slopes and points on each
+ * of those edges. Compute my directly, then compute
+ * mx by using the edge with the larger dy; that avoids
+ * dividing by values close to zero.
+ */
+ my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) /
+ (dx1 * dy2 - dx2 * dy1));
+ if (fabs (dy1) >= fabs (dy2))
+ mx = (my - y1) * dx1 / dy1 + x1;
+ else
+ mx = (my - y2) * dx2 / dy2 + x2;
+
+ /*
+ * When the two outer edges are nearly parallel, slight
+ * perturbations in the position of the outer points of the lines
+ * caused by representing them in fixed point form can cause the
+ * intersection point of the miter to move a large amount. If
+ * that moves the miter intersection from between the two faces,
+ * then draw a bevel instead.
+ */
+
+ ix = _cairo_fixed_to_double (in->point.x);
+ iy = _cairo_fixed_to_double (in->point.y);
+
+ /* slope of one face */
+ fdx1 = x1 - ix; fdy1 = y1 - iy;
+
+ /* slope of the other face */
+ fdx2 = x2 - ix; fdy2 = y2 - iy;
+
+ /* slope from the intersection to the miter point */
+ mdx = mx - ix; mdy = my - iy;
+
+ /*
+ * Make sure the miter point line lies between the two
+ * faces by comparing the slopes
+ */
+ if (_cairo_slope_compare_sgn (fdx1, fdy1, mdx, mdy) !=
+ _cairo_slope_compare_sgn (fdx2, fdy2, mdx, mdy))
+ {
+ points[0].x = _cairo_fixed_from_double (mx);
+ points[0].y = _cairo_fixed_from_double (my);
+
+ if (clockwise) {
+ status = _cairo_polygon_add_external_edge (stroker->polygon,
+ inpt, &points[0]);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_polygon_add_external_edge (stroker->polygon,
+ &points[0], outpt);
+ if (unlikely (status))
+ return status;
+ } else {
+ status = _cairo_polygon_add_external_edge (stroker->polygon,
+ outpt, &points[0]);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_polygon_add_external_edge (stroker->polygon,
+ &points[0], inpt);
+ if (unlikely (status))
+ return status;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+ }
+ }
+
+ /* fall through ... */
+
+ case CAIRO_LINE_JOIN_BEVEL:
+ if (clockwise) {
+ return _cairo_polygon_add_external_edge (stroker->polygon,
+ inpt, outpt);
+ } else {
+ return _cairo_polygon_add_external_edge (stroker->polygon,
+ outpt, inpt);
+ }
+ }
+}
+
+static cairo_status_t
+_cairo_stroker_add_cap (struct stroker *stroker,
+ const cairo_stroke_face_t *f)
+{
+ switch (stroker->style.line_cap) {
+ case CAIRO_LINE_CAP_ROUND: {
+ cairo_slope_t slope;
+
+ slope.dx = -f->dev_vector.dx;
+ slope.dy = -f->dev_vector.dy;
+
+ return _tessellate_fan (stroker,
+ &f->dev_vector,
+ &slope,
+ &f->point, &f->cw, &f->ccw,
+ FALSE);
+
+ }
+
+ case CAIRO_LINE_CAP_SQUARE: {
+ double dx, dy;
+ cairo_slope_t fvector;
+ cairo_point_t quad[4];
+
+ dx = f->usr_vector.x;
+ dy = f->usr_vector.y;
+ dx *= stroker->style.line_width / 2.0;
+ dy *= stroker->style.line_width / 2.0;
+ cairo_matrix_transform_distance (stroker->ctm, &dx, &dy);
+ fvector.dx = _cairo_fixed_from_double (dx);
+ fvector.dy = _cairo_fixed_from_double (dy);
+
+ quad[0] = f->ccw;
+ quad[1].x = f->ccw.x + fvector.dx;
+ quad[1].y = f->ccw.y + fvector.dy;
+ quad[2].x = f->cw.x + fvector.dx;
+ quad[2].y = f->cw.y + fvector.dy;
+ quad[3] = f->cw;
+
+ {
+ cairo_status_t status;
+
+ status = _cairo_polygon_add_external_edge (stroker->polygon,
+ &quad[0], &quad[1]);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_polygon_add_external_edge (stroker->polygon,
+ &quad[1], &quad[2]);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_polygon_add_external_edge (stroker->polygon,
+ &quad[2], &quad[3]);
+ if (unlikely (status))
+ return status;
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+ }
+
+ case CAIRO_LINE_CAP_BUTT:
+ default:
+ return _cairo_polygon_add_external_edge (stroker->polygon,
+ &f->ccw, &f->cw);
+ }
+}
+
+static cairo_status_t
+_cairo_stroker_add_leading_cap (struct stroker *stroker,
+ const cairo_stroke_face_t *face)
+{
+ cairo_stroke_face_t reversed;
+ cairo_point_t t;
+
+ reversed = *face;
+
+ /* The initial cap needs an outward facing vector. Reverse everything */
+ reversed.usr_vector.x = -reversed.usr_vector.x;
+ reversed.usr_vector.y = -reversed.usr_vector.y;
+ reversed.dev_vector.dx = -reversed.dev_vector.dx;
+ reversed.dev_vector.dy = -reversed.dev_vector.dy;
+ t = reversed.cw;
+ reversed.cw = reversed.ccw;
+ reversed.ccw = t;
+
+ return _cairo_stroker_add_cap (stroker, &reversed);
+}
+
+static cairo_status_t
+_cairo_stroker_add_trailing_cap (struct stroker *stroker,
+ const cairo_stroke_face_t *face)
+{
+ return _cairo_stroker_add_cap (stroker, face);
+}
+
+static inline void
+normalized_device_slope (double *dx, double *dy,
+ const cairo_matrix_t *ctm_inverse)
+{
+ double dx0 = *dx, dy0 = *dy;
+ double mag;
+
+ cairo_matrix_transform_distance (ctm_inverse, &dx0, &dy0);
+
+ assert (dx0 != 0.0 || dy0 != 0.0);
+
+ if (dx0 == 0.0) {
+ *dx = 0.0;
+ if (dy0 > 0.0) {
+ mag = dy0;
+ *dy = 1.0;
+ } else {
+ mag = -dy0;
+ *dy = -1.0;
+ }
+ } else if (dy0 == 0.0) {
+ *dy = 0.0;
+ if (dx0 > 0.0) {
+ mag = dx0;
+ *dx = 1.0;
+ } else {
+ mag = -dx0;
+ *dx = -1.0;
+ }
+ } else {
+ mag = hypot (dx0, dy0);
+ *dx = dx0 / mag;
+ *dy = dy0 / mag;
+ }
+}
+
+static void
+compute_face (const cairo_point_t *point, cairo_slope_t *dev_slope,
+ double slope_dx, double slope_dy,
+ struct stroker *stroker, cairo_stroke_face_t *face)
+{
+ double face_dx, face_dy;
+ cairo_point_t offset_ccw, offset_cw;
+
+ /*
+ * rotate to get a line_width/2 vector along the face, note that
+ * the vector must be rotated the right direction in device space,
+ * but by 90° in user space. So, the rotation depends on
+ * whether the ctm reflects or not, and that can be determined
+ * by looking at the determinant of the matrix.
+ */
+ if (stroker->ctm_det_positive)
+ {
+ face_dx = - slope_dy * (stroker->style.line_width / 2.0);
+ face_dy = slope_dx * (stroker->style.line_width / 2.0);
+ }
+ else
+ {
+ face_dx = slope_dy * (stroker->style.line_width / 2.0);
+ face_dy = - slope_dx * (stroker->style.line_width / 2.0);
+ }
+
+ /* back to device space */
+ cairo_matrix_transform_distance (stroker->ctm, &face_dx, &face_dy);
+
+ offset_ccw.x = _cairo_fixed_from_double (face_dx);
+ offset_ccw.y = _cairo_fixed_from_double (face_dy);
+ offset_cw.x = -offset_ccw.x;
+ offset_cw.y = -offset_ccw.y;
+
+ face->ccw = *point;
+ translate_point (&face->ccw, &offset_ccw);
+
+ face->point = *point;
+
+ face->cw = *point;
+ translate_point (&face->cw, &offset_cw);
+
+ face->usr_vector.x = slope_dx;
+ face->usr_vector.y = slope_dy;
+
+ face->dev_vector = *dev_slope;
+}
+
+static cairo_status_t
+_cairo_stroker_add_caps (struct stroker *stroker)
+{
+ cairo_status_t status;
+
+ /* check for a degenerative sub_path */
+ if (stroker->has_initial_sub_path
+ && ! stroker->has_first_face
+ && ! stroker->has_current_face
+ && stroker->style.line_cap == CAIRO_LINE_CAP_ROUND)
+ {
+ /* pick an arbitrary slope to use */
+ double dx = 1.0, dy = 0.0;
+ cairo_slope_t slope = { CAIRO_FIXED_ONE, 0 };
+ cairo_stroke_face_t face;
+
+ normalized_device_slope (&dx, &dy, stroker->ctm_inverse);
+
+ /* arbitrarily choose first_point
+ * first_point and current_point should be the same */
+ compute_face (&stroker->first_point, &slope, dx, dy, stroker, &face);
+
+ status = _cairo_stroker_add_leading_cap (stroker, &face);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_stroker_add_trailing_cap (stroker, &face);
+ if (unlikely (status))
+ return status;
+ }
+
+ if (stroker->has_first_face) {
+ status = _cairo_stroker_add_leading_cap (stroker,
+ &stroker->first_face);
+ if (unlikely (status))
+ return status;
+ }
+
+ if (stroker->has_current_face) {
+ status = _cairo_stroker_add_trailing_cap (stroker,
+ &stroker->current_face);
+ if (unlikely (status))
+ return status;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+add_segment (struct stroker *stroker,
+ const cairo_point_t *p1,
+ const cairo_point_t *p2,
+ cairo_slope_t *dev_slope,
+ double slope_dx, double slope_dy,
+ cairo_stroke_face_t *start,
+ cairo_stroke_face_t *end)
+{
+ cairo_status_t status;
+
+ if (p1->y == p2->y)
+ return CAIRO_STATUS_SUCCESS;
+
+ compute_face (p1, dev_slope, slope_dx, slope_dy, stroker, start);
+ *end = *start;
+
+ end->point = *p2;
+ end->ccw.x += p2->x - p1->x;
+ end->ccw.y += p2->y - p1->y;
+ end->cw.x += p2->x - p1->x;
+ end->cw.y += p2->y - p1->y;
+
+ status = _cairo_polygon_add_external_edge (stroker->polygon,
+ &end->cw, &start->cw);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_polygon_add_external_edge (stroker->polygon,
+ &start->ccw, &end->ccw);
+ if (unlikely (status))
+ return status;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+move_to (void *closure,
+ const cairo_point_t *point)
+{
+ struct stroker *stroker = closure;
+ cairo_status_t status;
+
+ /* Cap the start and end of the previous sub path as needed */
+ status = _cairo_stroker_add_caps (stroker);
+ if (unlikely (status))
+ return status;
+
+ stroker->first_point = *point;
+ stroker->current_point = *point;
+
+ stroker->has_first_face = FALSE;
+ stroker->has_current_face = FALSE;
+ stroker->has_initial_sub_path = FALSE;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+line_to (void *closure,
+ const cairo_point_t *point)
+{
+ struct stroker *stroker = closure;
+ cairo_stroke_face_t start, end;
+ cairo_point_t *p1 = &stroker->current_point;
+ cairo_slope_t dev_slope;
+ double slope_dx, slope_dy;
+ cairo_status_t status;
+
+ stroker->has_initial_sub_path = TRUE;
+
+ if (p1->x == point->x && p1->y == point->y)
+ return CAIRO_STATUS_SUCCESS;
+
+ _cairo_slope_init (&dev_slope, p1, point);
+ slope_dx = _cairo_fixed_to_double (point->x - p1->x);
+ slope_dy = _cairo_fixed_to_double (point->y - p1->y);
+ normalized_device_slope (&slope_dx, &slope_dy, stroker->ctm_inverse);
+
+ status = add_segment (stroker, p1, point, &dev_slope,
+ slope_dx, slope_dy,
+ &start, &end);
+ if (unlikely (status))
+ return status;
+
+ if (stroker->has_current_face) {
+ /* Join with final face from previous segment */
+ status = join (stroker, &stroker->current_face, &start);
+ if (unlikely (status))
+ return status;
+ } else if (! stroker->has_first_face) {
+ /* Save sub path's first face in case needed for closing join */
+ stroker->first_face = start;
+ stroker->has_first_face = TRUE;
+ }
+ stroker->current_face = end;
+ stroker->has_current_face = TRUE;
+
+ stroker->current_point = *point;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+curve_to (void *closure,
+ const cairo_point_t *b,
+ const cairo_point_t *c,
+ const cairo_point_t *d)
+{
+ struct stroker *stroker = closure;
+ cairo_spline_t spline;
+ cairo_line_join_t line_join_save;
+ cairo_stroke_face_t face;
+ double slope_dx, slope_dy;
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+ if (! _cairo_spline_init (&spline, line_to, stroker,
+ &stroker->current_point, b, c, d))
+ {
+ return line_to (closure, d);
+ }
+
+ /* Compute the initial face */
+ slope_dx = _cairo_fixed_to_double (spline.initial_slope.dx);
+ slope_dy = _cairo_fixed_to_double (spline.initial_slope.dy);
+ normalized_device_slope (&slope_dx, &slope_dy, stroker->ctm_inverse);
+ compute_face (&stroker->current_point,
+ &spline.initial_slope,
+ slope_dx, slope_dy,
+ stroker, &face);
+
+ if (stroker->has_current_face) {
+ status = join (stroker, &stroker->current_face, &face);
+ if (unlikely (status))
+ return status;
+ } else if (! stroker->has_first_face) {
+ stroker->first_face = face;
+ stroker->has_first_face = TRUE;
+ }
+
+ stroker->current_face = face;
+ stroker->has_current_face = TRUE;
+
+ /* Temporarily modify the stroker to use round joins to guarantee
+ * smooth stroked curves. */
+ line_join_save = stroker->style.line_join;
+ stroker->style.line_join = CAIRO_LINE_JOIN_ROUND;
+
+ status = _cairo_spline_decompose (&spline, stroker->tolerance);
+ if (unlikely (status))
+ return status;
+
+ /* And join the final face */
+ slope_dx = _cairo_fixed_to_double (spline.final_slope.dx);
+ slope_dy = _cairo_fixed_to_double (spline.final_slope.dy);
+ normalized_device_slope (&slope_dx, &slope_dy, stroker->ctm_inverse);
+ compute_face (&stroker->current_point,
+ &spline.final_slope,
+ slope_dx, slope_dy,
+ stroker, &face);
+
+ status = join (stroker, &stroker->current_face, &face);
+ if (unlikely (status))
+ return status;
+
+ stroker->current_face = face;
+
+ stroker->style.line_join = line_join_save;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+close_path (void *closure)
+{
+ struct stroker *stroker = closure;
+ cairo_status_t status;
+
+ status = line_to (stroker, &stroker->first_point);
+ if (unlikely (status))
+ return status;
+
+ if (stroker->has_first_face && stroker->has_current_face) {
+ /* Join first and final faces of sub path */
+ status = join (stroker, &stroker->current_face, &stroker->first_face);
+ if (unlikely (status))
+ return status;
+ } else {
+ /* Cap the start and end of the sub path as needed */
+ status = _cairo_stroker_add_caps (stroker);
+ if (unlikely (status))
+ return status;
+ }
+
+ stroker->has_initial_sub_path = FALSE;
+ stroker->has_first_face = FALSE;
+ stroker->has_current_face = FALSE;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_polygon_t *polygon)
+{
+ struct stroker stroker;
+ cairo_status_t status;
+
+ if (style->num_dashes) {
+ return _cairo_path_fixed_stroke_dashed_to_polygon (path,
+ style,
+ ctm,
+ ctm_inverse,
+ tolerance,
+ polygon);
+ }
+
+ stroker.style = *style;
+ stroker.ctm = ctm;
+ stroker.ctm_inverse = ctm_inverse;
+ stroker.tolerance = tolerance;
+
+ stroker.ctm_determinant = _cairo_matrix_compute_determinant (ctm);
+ stroker.ctm_det_positive = stroker.ctm_determinant >= 0.0;
+
+ status = _cairo_pen_init (&stroker.pen,
+ style->line_width / 2.0,
+ tolerance, ctm);
+ if (unlikely (status))
+ return status;
+
+ /* If the line width is so small that the pen is reduced to a
+ single point, then we have nothing to do. */
+ if (stroker.pen.num_vertices <= 1)
+ return CAIRO_STATUS_SUCCESS;
+
+ stroker.has_current_face = FALSE;
+ stroker.has_first_face = FALSE;
+ stroker.has_initial_sub_path = FALSE;
+
+ stroker.polygon = polygon;
+
+ status = _cairo_path_fixed_interpret (path,
+ move_to,
+ line_to,
+ curve_to,
+ close_path,
+ &stroker);
+ /* Cap the start and end of the final sub path as needed */
+ if (likely (status == CAIRO_STATUS_SUCCESS))
+ status = _cairo_stroker_add_caps (&stroker);
+
+ _cairo_pen_fini (&stroker.pen);
+
+ return status;
+}
diff --git a/src/cairo-path-stroke.c b/src/cairo-path-stroke.c
index a182e65..fcbc8e1 100644
--- a/src/cairo-path-stroke.c
+++ b/src/cairo-path-stroke.c
@@ -1274,12 +1274,12 @@ BAIL:
}
cairo_status_t
-_cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t *path,
- const cairo_stroke_style_t *stroke_style,
- const cairo_matrix_t *ctm,
- const cairo_matrix_t *ctm_inverse,
- double tolerance,
- cairo_polygon_t *polygon)
+_cairo_path_fixed_stroke_dashed_to_polygon (const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_polygon_t *polygon)
{
cairo_stroker_t stroker;
cairo_status_t status;
diff --git a/src/cairoint.h b/src/cairoint.h
index 50f99c9..f745f41 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -1403,6 +1403,14 @@ _cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t *path,
double tolerance,
cairo_polygon_t *polygon);
+cairo_private cairo_status_t
+_cairo_path_fixed_stroke_dashed_to_polygon (const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_polygon_t *polygon);
+
cairo_private cairo_int_status_t
_cairo_path_fixed_stroke_rectilinear_to_traps (const cairo_path_fixed_t *path,
const cairo_stroke_style_t *stroke_style,
commit b8c353f6d39a49cec3ce043fe057f3112ccc9a53
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date: Thu Aug 4 11:59:08 2011 +0100
rectangular-bug-fix
diff --git a/src/cairo-clip-private.h b/src/cairo-clip-private.h
index 5302471..9aa8f49 100644
--- a/src/cairo-clip-private.h
+++ b/src/cairo-clip-private.h
@@ -40,6 +40,7 @@
#include "cairo-types-private.h"
#include "cairo-boxes-private.h"
+#include "cairo-error-private.h"
#include "cairo-compiler-private.h"
#include "cairo-path-fixed-private.h"
#include "cairo-reference-count-private.h"
diff --git a/src/cairo-image-surface.c b/src/cairo-image-surface.c
index 4302868..9ba0d7b 100644
--- a/src/cairo-image-surface.c
+++ b/src/cairo-image-surface.c
@@ -817,7 +817,7 @@ _cairo_image_surface_release_source_image (void *abstract_surf
static pixman_op_t
_pixman_operator (cairo_operator_t op)
{
- switch (op) {
+ switch ((int) op) {
case CAIRO_OPERATOR_CLEAR:
return PIXMAN_OP_CLEAR;
@@ -2752,11 +2752,12 @@ _cairo_image_clipped_spans (void *abstract_renderer,
assert (num_spans);
do {
- if (! spans[0].is_clipped)
+ if (! spans[0].is_clipped) {
pixman_image_compositor_blt (r->compositor,
spans[0].x, y,
spans[1].x - spans[0].x, height,
r->opacity * spans[0].coverage);
+ }
spans++;
} while (--num_spans > 1);
@@ -2809,6 +2810,7 @@ _cairo_image_span_renderer_init (cairo_image_span_renderer_t *r,
else
r->base.render_rows = _cairo_image_bounded_spans;
r->base.finish = NULL;
+ r->extents = composite->bounded;
} else {
if (needs_clip)
r->base.render_rows = _cairo_image_clipped_spans;
@@ -2816,9 +2818,9 @@ _cairo_image_span_renderer_init (cairo_image_span_renderer_t *r,
r->base.render_rows = _cairo_image_unbounded_spans;
r->base.finish = _cairo_image_finish_unbounded_spans;
r->extents = composite->unbounded;
- r->extents.height += r->extents.y;
-
}
+ r->extents.height += r->extents.y;
+
r->compositor =
pixman_image_create_compositor (_pixman_operator (op),
r->src, NULL, dst->pixman_image,
diff --git a/src/cairo-rectangular-scan-converter.c b/src/cairo-rectangular-scan-converter.c
index 1922364..68040a2 100644
--- a/src/cairo-rectangular-scan-converter.c
+++ b/src/cairo-rectangular-scan-converter.c
@@ -493,13 +493,13 @@ generate (cairo_rectangular_scan_converter_t *self,
cairo_status_t status;
sweep_line_init (&sweep_line);
- sweep_line.xmin = self->xmin;
- sweep_line.xmax = self->xmax;
+ sweep_line.xmin = _cairo_fixed_integer_part (self->extents.p1.x);
+ sweep_line.xmax = _cairo_fixed_integer_part (self->extents.p2.x);
sweep_line.start = rectangles;
if ((status = setjmp (sweep_line.jmpbuf)))
goto BAIL;
- sweep_line.current_y = self->ymin;
+ sweep_line.current_y = _cairo_fixed_integer_part (self->extents.p1.y);
start = *sweep_line.start++;
do {
if (start->top_y != sweep_line.current_y) {
@@ -582,7 +582,7 @@ generate (cairo_rectangular_scan_converter_t *self,
out:
status = renderer->render_rows (renderer,
sweep_line.current_y,
- self->ymax - sweep_line.current_y,
+ _cairo_fixed_integer_part (self->extents.p2.y) - sweep_line.current_y,
NULL, 0);
BAIL:
@@ -615,18 +615,18 @@ static void generate_row(cairo_span_renderer_t *renderer,
}
if (! _cairo_fixed_is_integer (r->right)) {
- spans[num_spans].x = x2;
+ spans[num_spans].x = x2++;
spans[num_spans].coverage =
coverage * _cairo_fixed_fractional_part (r->right) >> 8;
num_spans++;
}
} else {
- spans[num_spans].x = x1;
+ spans[num_spans].x = x2++;
spans[num_spans].coverage = coverage * (r->right - r->left) >> 8;
num_spans++;
}
- spans[num_spans].x = x2 + 1;
+ spans[num_spans].x = x2;
spans[num_spans].coverage = 0;
num_spans++;
@@ -672,7 +672,8 @@ _cairo_rectangular_scan_converter_generate (void *converter,
if (unlikely (self->num_rectangles == 0)) {
return renderer->render_rows (renderer,
- self->ymin, self->ymax - self->ymin,
+ _cairo_fixed_integer_part (self->extents.p1.y),
+ _cairo_fixed_integer_part (self->extents.p2.y - self->extents.p1.y),
NULL, 0);
}
@@ -747,17 +748,22 @@ _cairo_rectangular_scan_converter_add_box (cairo_rectangular_scan_converter_t *s
if (unlikely (rectangle == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
- rectangle->left = box->p1.x;
- rectangle->right = box->p2.x;
rectangle->dir = dir;
+ rectangle->left = MAX (box->p1.x, self->extents.p1.x);
+ rectangle->right = MIN (box->p2.x, self->extents.p2.x);
+ if (unlikely (rectangle->right <= rectangle->left)) {
+ self->tail->count--;
+ return CAIRO_STATUS_SUCCESS;
+ }
- rectangle->top = box->p1.y;
- rectangle->top_y = _cairo_fixed_integer_floor (box->p1.y);
- rectangle->bottom = box->p2.y;
- rectangle->bottom_y = _cairo_fixed_integer_floor (box->p2.y);
- assert (rectangle->bottom_y >= rectangle->top_y);
-
- self->num_rectangles++;
+ rectangle->top = MAX (box->p1.y, self->extents.p1.y);
+ rectangle->top_y = _cairo_fixed_integer_floor (rectangle->top);
+ rectangle->bottom = MIN (box->p2.y, self->extents.p2.y);
+ rectangle->bottom_y = _cairo_fixed_integer_floor (rectangle->bottom);
+ if (likely (rectangle->bottom_y > rectangle->top_y))
+ self->num_rectangles++;
+ else
+ self->tail->count--;
return CAIRO_STATUS_SUCCESS;
}
@@ -783,10 +789,7 @@ _cairo_rectangular_scan_converter_init (cairo_rectangular_scan_converter_t *self
self->base.add_polygon = NULL;
self->base.generate = _cairo_rectangular_scan_converter_generate;
- self->xmin = extents->x;
- self->xmax = extents->x + extents->width;
- self->ymin = extents->y;
- self->ymax = extents->y + extents->height;
+ _cairo_box_from_rectangle (&self->extents, extents);
self->chunks.base = self->buf;
self->chunks.next = NULL;
diff --git a/src/cairo-spans-private.h b/src/cairo-spans-private.h
index 3acfd48..52ed1d5 100644
--- a/src/cairo-spans-private.h
+++ b/src/cairo-spans-private.h
@@ -140,8 +140,7 @@ _cairo_tor33_scan_converter_reset (void *converter);
typedef struct _cairo_rectangular_scan_converter {
cairo_scan_converter_t base;
- int xmin, xmax;
- int ymin, ymax;
+ cairo_box_t extents;
struct _cairo_rectangular_scan_converter_chunk {
struct _cairo_rectangular_scan_converter_chunk *next;
commit fdb879b8f5f167fcf8b161ca934774cc166f2665
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date: Thu Aug 4 00:19:42 2011 +0100
image: move surface definition to new header for subclassing
Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>
diff --git a/src/Makefile.sources b/src/Makefile.sources
index fb67e5b..28c9d13 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -75,6 +75,7 @@ cairo_private = \
cairo-gstate-private.h \
cairo-hash-private.h \
cairo-image-info-private.h \
+ cairo-image-surface-private.h \
cairo-list-private.h \
cairo-malloc-private.h \
cairo-mutex-impl-private.h \
diff --git a/src/cairo-debug.c b/src/cairo-debug.c
index ca94e2b..6afdb3f 100644
--- a/src/cairo-debug.c
+++ b/src/cairo-debug.c
@@ -34,6 +34,7 @@
*/
#include "cairoint.h"
+#include "cairo-image-surface-private.h"
/**
* cairo_debug_reset_static_data:
diff --git a/src/cairo-fast-image-surface.c b/src/cairo-fast-image-surface.c
index ab27c74..de1af94 100644
--- a/src/cairo-fast-image-surface.c
+++ b/src/cairo-fast-image-surface.c
@@ -45,6 +45,7 @@
#include "cairo-default-context-private.h"
#include "cairo-error-private.h"
#include "cairo-gstate-private.h"
+#include "cairo-image-surface-private.h"
#include "cairo-paginated-private.h"
#include "cairo-pattern-private.h"
#include "cairo-recording-surface-private.h"
diff --git a/src/cairo-ft-font.c b/src/cairo-ft-font.c
index 7f5f6d0..8ef37aa 100644
--- a/src/cairo-ft-font.c
+++ b/src/cairo-ft-font.c
@@ -42,6 +42,7 @@
#include "cairoint.h"
#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
#include "cairo-ft-private.h"
#include "cairo-pattern-private.h"
diff --git a/src/cairo-image-surface-private.h b/src/cairo-image-surface-private.h
new file mode 100644
index 0000000..97745af
--- /dev/null
+++ b/src/cairo-image-surface-private.h
@@ -0,0 +1,84 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth at cworth.org>
+ */
+
+#ifndef CAIRO_IMAGE_SURFACE_PRIVATE_H
+#define CAIRO_IMAGE_SURFACE_PRIVATE_H
+
+#include "cairo-surface-private.h"
+
+CAIRO_BEGIN_DECLS
+
+struct _cairo_image_surface {
+ cairo_surface_t base;
+
+ pixman_format_code_t pixman_format;
+ cairo_format_t format;
+ unsigned char *data;
+
+ int width;
+ int height;
+ int stride;
+ int depth;
+
+ pixman_image_t *pixman_image;
+
+ unsigned owns_data : 1;
+ unsigned transparency : 2;
+ unsigned color : 2;
+};
+
+extern const cairo_private cairo_surface_backend_t _cairo_image_surface_backend;
+
+cairo_private void
+_cairo_image_surface_init (cairo_image_surface_t *surface,
+ pixman_image_t *pixman_image,
+ pixman_format_code_t pixman_format);
+
+cairo_private_no_warn cairo_bool_t
+_cairo_image_surface_get_extents (void *abstract_surface,
+ cairo_rectangle_int_t *rectangle);
+
+cairo_private void
+_cairo_image_surface_get_font_options (void *abstract_surface,
+ cairo_font_options_t *options);
+
+cairo_private cairo_status_t
+_cairo_image_surface_finish (void *abstract_surface);
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_IMAGE_SURFACE_PRIVATE_H */
diff --git a/src/cairo-image-surface.c b/src/cairo-image-surface.c
index 2989953..4302868 100644
--- a/src/cairo-image-surface.c
+++ b/src/cairo-image-surface.c
@@ -44,6 +44,7 @@
#include "cairo-composite-rectangles-private.h"
#include "cairo-default-context-private.h"
#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
#include "cairo-paginated-private.h"
#include "cairo-pattern-private.h"
#include "cairo-recording-surface-private.h"
@@ -144,13 +145,33 @@ _cairo_content_from_pixman_format (pixman_format_code_t pixman_format)
return content;
}
+void
+_cairo_image_surface_init (cairo_image_surface_t *surface,
+ pixman_image_t *pixman_image,
+ pixman_format_code_t pixman_format)
+{
+ surface->pixman_image = pixman_image;
+
+ surface->pixman_format = pixman_format;
+ surface->format = _cairo_format_from_pixman_format (pixman_format);
+ surface->data = (uint8_t *) pixman_image_get_data (pixman_image);
+ surface->owns_data = FALSE;
+ surface->transparency = CAIRO_IMAGE_UNKNOWN;
+ surface->color = CAIRO_IMAGE_UNKNOWN_COLOR;
+
+ surface->width = pixman_image_get_width (pixman_image);
+ surface->height = pixman_image_get_height (pixman_image);
+ surface->stride = pixman_image_get_stride (pixman_image);
+ surface->depth = pixman_image_get_depth (pixman_image);
+
+ surface->base.is_clear = surface->width == 0 || surface->height == 0;
+}
+
cairo_surface_t *
_cairo_image_surface_create_for_pixman_image (pixman_image_t *pixman_image,
pixman_format_code_t pixman_format)
{
cairo_image_surface_t *surface;
- int width = pixman_image_get_width (pixman_image);
- int height = pixman_image_get_height (pixman_image);
surface = malloc (sizeof (cairo_image_surface_t));
if (unlikely (surface == NULL))
@@ -161,21 +182,7 @@ _cairo_image_surface_create_for_pixman_image (pixman_image_t *pixman_image,
NULL, /* device */
_cairo_content_from_pixman_format (pixman_format));
- surface->pixman_image = pixman_image;
-
- surface->pixman_format = pixman_format;
- surface->format = _cairo_format_from_pixman_format (pixman_format);
- surface->data = (uint8_t *) pixman_image_get_data (pixman_image);
- surface->owns_data = FALSE;
- surface->transparency = CAIRO_IMAGE_UNKNOWN;
- surface->color = CAIRO_IMAGE_UNKNOWN_COLOR;
-
- surface->width = width;
- surface->height = height;
- surface->stride = pixman_image_get_stride (pixman_image);
- surface->depth = pixman_image_get_depth (pixman_image);
-
- surface->base.is_clear = width == 0 || height == 0;
+ _cairo_image_surface_init (surface, pixman_image, pixman_format);
return &surface->base;
}
@@ -760,7 +767,7 @@ _cairo_image_surface_unmap_image (void *abstract_surface,
return CAIRO_INT_STATUS_SUCCESS;
}
-static cairo_status_t
+cairo_status_t
_cairo_image_surface_finish (void *abstract_surface)
{
cairo_image_surface_t *surface = abstract_surface;
@@ -3396,7 +3403,7 @@ _clip_and_composite_trapezoids (cairo_image_surface_t *dst,
}
/* high level image interface */
-static cairo_bool_t
+cairo_bool_t
_cairo_image_surface_get_extents (void *abstract_surface,
cairo_rectangle_int_t *rectangle)
{
@@ -4155,7 +4162,7 @@ _cairo_image_surface_glyphs (void *abstract_surface,
return status;
}
-static void
+void
_cairo_image_surface_get_font_options (void *abstract_surface,
cairo_font_options_t *options)
{
diff --git a/src/cairo-mime-surface.c b/src/cairo-mime-surface.c
index d1ec974..8fc3714 100644
--- a/src/cairo-mime-surface.c
+++ b/src/cairo-mime-surface.c
@@ -45,6 +45,7 @@
#include "cairoint.h"
#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
typedef struct _cairo_mime_surface {
cairo_surface_t base;
diff --git a/src/cairo-paginated-surface.c b/src/cairo-paginated-surface.c
index 4d93642..61c2815 100644
--- a/src/cairo-paginated-surface.c
+++ b/src/cairo-paginated-surface.c
@@ -49,6 +49,7 @@
#include "cairo-recording-surface-private.h"
#include "cairo-analysis-surface-private.h"
#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
static const cairo_surface_backend_t cairo_paginated_surface_backend;
diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c
index 8c33e4c..581cc2b 100644
--- a/src/cairo-pattern.c
+++ b/src/cairo-pattern.c
@@ -33,6 +33,7 @@
#include "cairo-freed-pool-private.h"
#include "cairo-path-private.h"
#include "cairo-pattern-private.h"
+#include "cairo-image-surface-private.h"
#include <float.h>
diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c
index 01ebcf1..0af638f 100644
--- a/src/cairo-pdf-surface.c
+++ b/src/cairo-pdf-surface.c
@@ -49,6 +49,7 @@
#include "cairo-composite-rectangles-private.h"
#include "cairo-default-context-private.h"
#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
#include "cairo-image-info-private.h"
#include "cairo-recording-surface-private.h"
#include "cairo-output-stream-private.h"
diff --git a/src/cairo-png.c b/src/cairo-png.c
index 818b7b0..59bbe68 100644
--- a/src/cairo-png.c
+++ b/src/cairo-png.c
@@ -39,6 +39,7 @@
#include "cairoint.h"
#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
#include "cairo-output-stream-private.h"
#include <stdio.h>
diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c
index 96f5bcf..267fd59 100644
--- a/src/cairo-ps-surface.c
+++ b/src/cairo-ps-surface.c
@@ -62,6 +62,7 @@
#include "cairo-composite-rectangles-private.h"
#include "cairo-default-context-private.h"
#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
#include "cairo-scaled-font-subsets-private.h"
#include "cairo-paginated-private.h"
#include "cairo-recording-surface-private.h"
diff --git a/src/cairo-recording-surface.c b/src/cairo-recording-surface.c
index 478695f..be803ec 100644
--- a/src/cairo-recording-surface.c
+++ b/src/cairo-recording-surface.c
@@ -83,6 +83,7 @@
#include "cairo-composite-rectangles-private.h"
#include "cairo-default-context-private.h"
#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
#include "cairo-recording-surface-private.h"
#include "cairo-surface-wrapper-private.h"
diff --git a/src/cairo-scaled-font.c b/src/cairo-scaled-font.c
index cb59bce..5b77546 100644
--- a/src/cairo-scaled-font.c
+++ b/src/cairo-scaled-font.c
@@ -40,6 +40,7 @@
#include "cairoint.h"
#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
#include "cairo-pattern-private.h"
#include "cairo-scaled-font-private.h"
diff --git a/src/cairo-surface-fallback.c b/src/cairo-surface-fallback.c
index acbf799..b65b2bf 100644
--- a/src/cairo-surface-fallback.c
+++ b/src/cairo-surface-fallback.c
@@ -44,6 +44,7 @@
#include "cairo-clip-private.h"
#include "cairo-composite-rectangles-private.h"
#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
#include "cairo-pattern-private.h"
#include "cairo-region-private.h"
#include "cairo-spans-private.h"
diff --git a/src/cairo-surface-snapshot.c b/src/cairo-surface-snapshot.c
index c88d015..56d108b 100644
--- a/src/cairo-surface-snapshot.c
+++ b/src/cairo-surface-snapshot.c
@@ -40,6 +40,7 @@
#include "cairoint.h"
#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
#include "cairo-surface-snapshot-private.h"
static cairo_status_t
diff --git a/src/cairo-surface-subsurface.c b/src/cairo-surface-subsurface.c
index 29d9ef0..248c20c 100644
--- a/src/cairo-surface-subsurface.c
+++ b/src/cairo-surface-subsurface.c
@@ -36,6 +36,7 @@
#include "cairoint.h"
#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
#include "cairo-recording-surface-private.h"
#include "cairo-surface-offset-private.h"
#include "cairo-surface-subsurface-private.h"
diff --git a/src/cairo-surface.c b/src/cairo-surface.c
index 71f8aea..db127fb 100644
--- a/src/cairo-surface.c
+++ b/src/cairo-surface.c
@@ -42,6 +42,7 @@
#include "cairo-clip-private.h"
#include "cairo-device-private.h"
#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
#include "cairo-recording-surface-private.h"
#include "cairo-region-private.h"
#include "cairo-tee-surface-private.h"
diff --git a/src/cairo-svg-surface.c b/src/cairo-svg-surface.c
index 66d0fc2..88acad2 100644
--- a/src/cairo-svg-surface.c
+++ b/src/cairo-svg-surface.c
@@ -46,6 +46,7 @@
#include "cairo-default-context-private.h"
#include "cairo-error-private.h"
#include "cairo-image-info-private.h"
+#include "cairo-image-surface-private.h"
#include "cairo-recording-surface-private.h"
#include "cairo-output-stream-private.h"
#include "cairo-path-fixed-private.h"
diff --git a/src/cairo-type3-glyph-surface.c b/src/cairo-type3-glyph-surface.c
index 93c3a6a..0755415 100644
--- a/src/cairo-type3-glyph-surface.c
+++ b/src/cairo-type3-glyph-surface.c
@@ -44,6 +44,7 @@
#include "cairo-analysis-surface-private.h"
#include "cairo-default-context-private.h"
#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
#include "cairo-surface-clipper-private.h"
static const cairo_surface_backend_t cairo_type3_glyph_surface_backend;
diff --git a/src/cairo-xcb-private.h b/src/cairo-xcb-private.h
index 150aab9..d9557b4 100644
--- a/src/cairo-xcb-private.h
+++ b/src/cairo-xcb-private.h
@@ -83,7 +83,7 @@ struct _cairo_xcb_shm_info {
struct _cairo_xcb_surface {
cairo_surface_t base;
- cairo_image_surface_t *fallback;
+ cairo_surface_t *fallback;
cairo_xcb_connection_t *connection;
cairo_xcb_screen_t *screen;
diff --git a/src/cairo-xcb-surface-core.c b/src/cairo-xcb-surface-core.c
index c3c0953..0770dcd 100644
--- a/src/cairo-xcb-surface-core.c
+++ b/src/cairo-xcb-surface-core.c
@@ -33,6 +33,7 @@
#include "cairo-boxes-private.h"
#include "cairo-xcb-private.h"
+#include "cairo-image-surface-private.h"
/* XXX dithering */
diff --git a/src/cairo-xcb-surface-render.c b/src/cairo-xcb-surface-render.c
index e08ca68..b42795e 100644
--- a/src/cairo-xcb-surface-render.c
+++ b/src/cairo-xcb-surface-render.c
@@ -34,6 +34,7 @@
#include "cairo-boxes-private.h"
#include "cairo-clip-private.h"
#include "cairo-composite-rectangles-private.h"
+#include "cairo-image-surface-private.h"
#include "cairo-region-private.h"
#include "cairo-surface-offset-private.h"
#include "cairo-surface-snapshot-private.h"
diff --git a/src/cairo-xcb-surface.c b/src/cairo-xcb-surface.c
index da35e75..abf8fe3 100644
--- a/src/cairo-xcb-surface.c
+++ b/src/cairo-xcb-surface.c
@@ -44,6 +44,7 @@
#include "cairo-xcb-private.h"
#include "cairo-default-context-private.h"
+#include "cairo-image-surface-private.h"
#define XLIB_COORD_MAX 32767
@@ -222,8 +223,8 @@ _cairo_xcb_surface_finish (void *abstract_surface)
cairo_status_t status;
if (surface->fallback != NULL) {
- cairo_surface_finish (&surface->fallback->base);
- cairo_surface_destroy (&surface->fallback->base);
+ cairo_surface_finish (surface->fallback);
+ cairo_surface_destroy (surface->fallback);
}
cairo_list_del (&surface->link);
@@ -297,7 +298,7 @@ _cairo_xcb_surface_create_shm_image (cairo_xcb_connection_t *connection,
}
#endif
-static cairo_image_surface_t *
+static cairo_surface_t *
_get_shm_image (cairo_xcb_surface_t *surface,
int x, int y,
int width, int height)
@@ -329,19 +330,19 @@ _get_shm_image (cairo_xcb_surface_t *surface,
}
done:
- return (cairo_image_surface_t *) image;
+ return image;
#else
- return NULL;;
+ return NULL;
#endif
}
-static cairo_image_surface_t *
+static cairo_surface_t *
_get_image (cairo_xcb_surface_t *surface,
cairo_bool_t use_shm,
int x, int y,
int width, int height)
{
- cairo_image_surface_t *image;
+ cairo_surface_t *image;
cairo_xcb_connection_t *connection;
xcb_get_image_reply_t *reply;
cairo_int_status_t status;
@@ -353,7 +354,7 @@ _get_image (cairo_xcb_surface_t *surface,
assert (y + height <= surface->height);
if (surface->deferred_clear) {
- image = (cairo_image_surface_t *)
+ image =
_cairo_image_surface_create_with_pixman_format (NULL,
surface->pixman_format,
width, height,
@@ -362,14 +363,13 @@ _get_image (cairo_xcb_surface_t *surface,
cairo_solid_pattern_t solid;
_cairo_pattern_init_solid (&solid, &surface->deferred_clear_color);
- status = _cairo_surface_paint (&image->base,
+ status = _cairo_surface_paint (image,
CAIRO_OPERATOR_SOURCE,
&solid.base,
NULL);
if (unlikely (status)) {
- cairo_surface_destroy (&image->base);
- image = (cairo_image_surface_t *)
- _cairo_surface_create_in_error (status);
+ cairo_surface_destroy (image);
+ image = _cairo_surface_create_in_error (status);
}
}
return image;
@@ -379,7 +379,7 @@ _get_image (cairo_xcb_surface_t *surface,
status = _cairo_xcb_connection_acquire (connection);
if (unlikely (status))
- return (cairo_image_surface_t *) _cairo_surface_create_in_error (status);
+ return _cairo_surface_create_in_error (status);
if (use_shm) {
image = _get_shm_image (surface, x, y, width, height);
@@ -450,22 +450,20 @@ _get_image (cairo_xcb_surface_t *surface,
/* XXX format conversion */
assert (reply->depth == surface->depth);
- image = (cairo_image_surface_t *)
- _cairo_image_surface_create_with_pixman_format
+ image = _cairo_image_surface_create_with_pixman_format
(xcb_get_image_data (reply),
surface->pixman_format,
width, height,
CAIRO_STRIDE_FOR_WIDTH_BPP (width,
PIXMAN_FORMAT_BPP (surface->pixman_format)));
- status = image->base.status;
+ status = image->status;
if (unlikely (status)) {
free (reply);
goto FAIL;
}
- assert (xcb_get_image_data_length (reply) == image->height * image->stride);
-
- pixman_image_set_destroy_function (image->pixman_image, _destroy_image, reply);
+ /* XXX */
+ pixman_image_set_destroy_function (((cairo_image_surface_t *)image)->pixman_image, _destroy_image, reply);
_cairo_xcb_connection_release (connection);
@@ -473,7 +471,7 @@ _get_image (cairo_xcb_surface_t *surface,
FAIL:
_cairo_xcb_connection_release (connection);
- return (cairo_image_surface_t *) _cairo_surface_create_in_error (status);
+ return _cairo_surface_create_in_error (status);
}
static cairo_status_t
@@ -482,29 +480,28 @@ _cairo_xcb_surface_acquire_source_image (void *abstract_surface,
void **image_extra)
{
cairo_xcb_surface_t *surface = abstract_surface;
- cairo_image_surface_t *image;
+ cairo_surface_t *image;
if (surface->fallback != NULL) {
- image = (cairo_image_surface_t *) cairo_surface_reference (&surface->fallback->base);
+ image = cairo_surface_reference (surface->fallback);
goto DONE;
}
- image = (cairo_image_surface_t *)
- _cairo_surface_has_snapshot (&surface->base,
- &_cairo_image_surface_backend);
+ image = _cairo_surface_has_snapshot (&surface->base,
+ &_cairo_image_surface_backend);
if (image != NULL) {
- image = (cairo_image_surface_t *) cairo_surface_reference (&image->base);
+ image = cairo_surface_reference (image);
goto DONE;
}
image = _get_image (surface, FALSE, 0, 0, surface->width, surface->height);
- if (unlikely (image->base.status))
- return image->base.status;
+ if (unlikely (image->status))
+ return image->status;
- _cairo_surface_attach_snapshot (&surface->base, &image->base, NULL);
+ _cairo_surface_attach_snapshot (&surface->base, image, NULL);
DONE:
- *image_out = image;
+ *image_out = (cairo_image_surface_t *) image;
*image_extra = NULL;
return CAIRO_STATUS_SUCCESS;
}
@@ -629,20 +626,20 @@ _cairo_xcb_surface_flush (void *abstract_surface)
status = surface->base.status;
if (status == CAIRO_STATUS_SUCCESS && ! surface->base.finished) {
- status = cairo_surface_status (&surface->fallback->base);
+ status = cairo_surface_status (surface->fallback);
if (status == CAIRO_STATUS_SUCCESS) {
- status = _put_image (surface, surface->fallback);
+ status = _put_image (surface, (cairo_image_surface_t *)surface->fallback);
}
if (status == CAIRO_STATUS_SUCCESS) {
_cairo_surface_attach_snapshot (&surface->base,
- &surface->fallback->base,
+ surface->fallback,
cairo_surface_finish);
}
}
- cairo_surface_destroy (&surface->fallback->base);
+ cairo_surface_destroy (surface->fallback);
surface->fallback = NULL;
return status;
@@ -653,16 +650,16 @@ _cairo_xcb_surface_map_to_image (void *abstract_surface,
const cairo_rectangle_int_t *extents)
{
cairo_xcb_surface_t *surface = abstract_surface;
- cairo_image_surface_t *image;
+ cairo_surface_t *image;
if (surface->fallback)
- return surface->fallback->base.backend->map_to_image (surface->fallback, extents);
+ return surface->fallback->backend->map_to_image (surface->fallback, extents);
image = _get_image (surface, TRUE,
extents->x, extents->y,
extents->width, extents->height);
- if (unlikely (image->base.status))
- return &image->base;
+ if (unlikely (image->status))
+ return image;
/* Do we have a deferred clear and this image surface does NOT cover the
* whole xcb surface? Have to apply the clear in that case, else
@@ -675,14 +672,14 @@ _cairo_xcb_surface_map_to_image (void *abstract_surface,
extents->height == surface->height)) {
cairo_status_t status = _cairo_xcb_surface_clear (surface);
if (unlikely (status)) {
- cairo_surface_destroy(&image->base);
+ cairo_surface_destroy(image);
return _cairo_surface_create_in_error (status);
}
}
surface->deferred_clear = FALSE;
- cairo_surface_set_device_offset (&image->base, -extents->x, -extents->y);
- return &image->base;
+ cairo_surface_set_device_offset (image, -extents->x, -extents->y);
+ return image;
}
static cairo_int_status_t
@@ -692,18 +689,19 @@ _cairo_xcb_surface_unmap (void *abstract_surface,
cairo_xcb_surface_t *surface = abstract_surface;
if (surface->fallback)
- return surface->fallback->base.backend->unmap_image (surface->fallback, image);
+ return surface->fallback->backend->unmap_image (surface->fallback, image);
return _put_image (abstract_surface, image);
}
-static cairo_image_surface_t *
+static cairo_surface_t *
_cairo_xcb_surface_fallback (cairo_xcb_surface_t *surface)
{
- cairo_image_surface_t *image;
+ cairo_surface_t *image;
+
image = _get_image (surface, TRUE, 0, 0, surface->width, surface->height);
/* If there was a deferred clear, _get_image applied it */
- if (image->base.status == CAIRO_STATUS_SUCCESS)
+ if (image->status == CAIRO_STATUS_SUCCESS)
surface->deferred_clear = FALSE;
return image;
@@ -730,7 +728,7 @@ _cairo_xcb_surface_paint (void *abstract_surface,
surface->fallback = _cairo_xcb_surface_fallback (surface);
}
- return _cairo_surface_paint (&surface->fallback->base, op, source, clip);
+ return _cairo_surface_paint (surface->fallback, op, source, clip);
}
static cairo_int_status_t
@@ -757,7 +755,7 @@ _cairo_xcb_surface_mask (void *abstract_surface,
surface->fallback = _cairo_xcb_surface_fallback (surface);
}
- return _cairo_surface_mask (&surface->fallback->base,
+ return _cairo_surface_mask (surface->fallback,
op, source, mask,
clip);
}
@@ -799,7 +797,7 @@ _cairo_xcb_surface_stroke (void *abstract_surface,
surface->fallback = _cairo_xcb_surface_fallback (surface);
}
- return _cairo_surface_stroke (&surface->fallback->base,
+ return _cairo_surface_stroke (surface->fallback,
op, source,
path, style,
ctm, ctm_inverse,
@@ -838,7 +836,7 @@ _cairo_xcb_surface_fill (void *abstract_surface,
surface->fallback = _cairo_xcb_surface_fallback (surface);
}
- return _cairo_surface_fill (&surface->fallback->base,
+ return _cairo_surface_fill (surface->fallback,
op, source,
path, fill_rule,
tolerance, antialias,
@@ -878,7 +876,7 @@ _cairo_xcb_surface_glyphs (void *abstract_surface,
surface->fallback = _cairo_xcb_surface_fallback (surface);
}
- return _cairo_surface_show_text_glyphs (&surface->fallback->base,
+ return _cairo_surface_show_text_glyphs (surface->fallback,
op, source,
NULL, 0,
glyphs, num_glyphs,
diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c
index 57b2604..35e3362 100644
--- a/src/cairo-xlib-surface.c
+++ b/src/cairo-xlib-surface.c
@@ -54,6 +54,7 @@
#include "cairo-clip-private.h"
#include "cairo-default-context-private.h"
#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
#include "cairo-pattern-private.h"
#include "cairo-region-private.h"
#include "cairo-scaled-font-private.h"
diff --git a/src/cairoint.h b/src/cairoint.h
index 43ec8c7..50f99c9 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -255,6 +255,7 @@ _cairo_isdigit (int c)
#include "cairo-cache-private.h"
#include "cairo-reference-count-private.h"
#include "cairo-spans-private.h"
+#include "cairo-surface-private.h"
cairo_private void
_cairo_box_from_doubles (cairo_box_t *box,
@@ -796,7 +797,7 @@ struct _cairo_surface_backend {
* FALSE the surface is considered to be
* boundless and infinite bounds are used for it.
*/
- cairo_warn cairo_bool_t
+ cairo_bool_t
(*get_extents) (void *surface,
cairo_rectangle_int_t *extents);
@@ -947,29 +948,6 @@ struct _cairo_surface_backend {
void **image_extra);
};
-#include "cairo-surface-private.h"
-
-struct _cairo_image_surface {
- cairo_surface_t base;
-
- pixman_format_code_t pixman_format;
- cairo_format_t format;
- unsigned char *data;
-
- int width;
- int height;
- int stride;
- int depth;
-
- pixman_image_t *pixman_image;
-
- unsigned owns_data : 1;
- unsigned transparency : 2;
- unsigned color : 2;
-};
-
-extern const cairo_private cairo_surface_backend_t _cairo_image_surface_backend;
-
#define CAIRO_EXTEND_SURFACE_DEFAULT CAIRO_EXTEND_NONE
#define CAIRO_EXTEND_GRADIENT_DEFAULT CAIRO_EXTEND_PAD
#define CAIRO_FILTER_DEFAULT CAIRO_FILTER_GOOD
@@ -1859,7 +1837,7 @@ cairo_private cairo_bool_t
_cairo_surface_is_similar (cairo_surface_t *surface_a,
cairo_surface_t *surface_b);
-cairo_private cairo_bool_t
+cairo_private_no_warn cairo_bool_t
_cairo_surface_get_extents (cairo_surface_t *surface,
cairo_rectangle_int_t *extents);
diff --git a/src/skia/cairo-skia-private.h b/src/skia/cairo-skia-private.h
index 3af3eee..cbd8c88 100644
--- a/src/skia/cairo-skia-private.h
+++ b/src/skia/cairo-skia-private.h
@@ -37,6 +37,7 @@
#define CAIRO_SKIA_CONTEXT_PRIVATE_H
#include "cairo-private.h"
+#include "cairo-image-surface-private.h"
#include <SkBitmap.h>
#include <SkCanvas.h>
diff --git a/src/skia/cairo-skia-surface.cpp b/src/skia/cairo-skia-surface.cpp
index 4c10625..ba41b86 100644
--- a/src/skia/cairo-skia-surface.cpp
+++ b/src/skia/cairo-skia-surface.cpp
@@ -34,6 +34,29 @@
* Vladimir Vukicevic <vladimir at mozilla.com>
*/
+
+/***
+
+Todo:
+
+*** Skia:
+
+- mask()
+
+*** Sk:
+
+High:
+- antialiased clipping?
+
+Medium:
+- implement clip path reset (to avoid restore/save)
+- implement complex radial patterns (2 centers and 2 radii)
+
+Low:
+- implement EXTEND_NONE
+
+***/
+
#include "cairoint.h"
#include "cairo-skia.h"
@@ -82,10 +105,9 @@ _cairo_skia_surface_finish (void *asurface)
{
cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
- cairo_surface_finish (&surface->image.base);
delete surface->bitmap;
- return CAIRO_STATUS_SUCCESS;
+ return _cairo_image_surface_finish (&surface->image);
}
static cairo_surface_t *
@@ -140,27 +162,6 @@ _cairo_skia_surface_release_source_image (void *asurface,
surface->bitmap->unlockPixels ();
}
-static cairo_bool_t
-_cairo_skia_surface_get_extents (void *asurface,
- cairo_rectangle_int_t *extents)
-{
- cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
- extents->x = extents->y = 0;
- extents->width = surface->image.width;
- extents->height = surface->image.height;
- return TRUE;
-}
-
-static void
-_cairo_skia_surface_get_font_options (void *abstract_surface,
- cairo_font_options_t *options)
-{
- _cairo_font_options_init_default (options);
-
- cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON);
- _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_ON);
-}
-
static cairo_rectangle_t *
to_rectangle (cairo_rectangle_t *rf,
cairo_rectangle_int_t *ri)
@@ -379,9 +380,9 @@ cairo_skia_surface_backend = {
NULL, /* copy_page */
NULL, /* show_page */
- _cairo_skia_surface_get_extents,
+ _cairo_image_surface_get_extents,
NULL, /* old_show_glyphs */
- _cairo_skia_surface_get_font_options,
+ _cairo_image_surface_get_font_options,
NULL, /* flush */
NULL, /* mark_dirty_rectangle */
NULL, /* scaled_font_fini */
@@ -426,6 +427,7 @@ sk_config_to_pixman_format_code (SkBitmap::Config config,
return (pixman_format_code_t) -1;
}
}
+
static cairo_skia_surface_t *
_cairo_skia_surface_create_internal (SkBitmap::Config config,
bool opaque,
@@ -501,25 +503,3 @@ cairo_skia_surface_create_for_data (unsigned char *data,
return &_cairo_skia_surface_create_internal (config, opaque, data, width, height, stride)->image.base;
}
-
-/***
-
-Todo:
-
-*** Skia:
-
-- mask()
-
-*** Sk:
-
-High:
-- antialiased clipping?
-
-Medium:
-- implement clip path reset (to avoid restore/save)
-- implement complex radial patterns (2 centers and 2 radii)
-
-Low:
-- implement EXTEND_NONE
-
-***/
commit fa259aa1a1b8b16dd9d23110e52576210af8404c
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date: Fri Apr 23 23:40:06 2010 +0100
skia
diff --git a/src/cairo-backend-private.h b/src/cairo-backend-private.h
index e09b436..1dd5ea0 100644
--- a/src/cairo-backend-private.h
+++ b/src/cairo-backend-private.h
@@ -40,6 +40,7 @@
typedef enum _cairo_backend_type {
CAIRO_TYPE_DEFAULT,
+ CAIRO_TYPE_SKIA,
} cairo_backend_type_t;
struct _cairo_backend {
diff --git a/src/cairo-error-private.h b/src/cairo-error-private.h
index 1c82ae6..a548a35 100644
--- a/src/cairo-error-private.h
+++ b/src/cairo-error-private.h
@@ -46,6 +46,10 @@
CAIRO_BEGIN_DECLS
+/* Sure wish C had a real enum type so that this would be distinct
+ * from #cairo_status_t. Oh well, without that, I'll use this bogus 100
+ * offset. We want to keep it fit in int8_t as the compiler may choose
+ * that for #cairo_status_t */
enum _cairo_int_status {
CAIRO_INT_STATUS_SUCCESS = 0,
@@ -97,6 +101,8 @@ enum _cairo_int_status {
CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN,
};
+typedef enum _cairo_int_status cairo_int_status_t;
+
#define _cairo_status_is_error(status) \
(status != CAIRO_STATUS_SUCCESS && status < CAIRO_STATUS_LAST_STATUS)
@@ -107,7 +113,7 @@ static inline cairo_status_t
_cairo_public_status (cairo_int_status_t status)
{
assert (status <= CAIRO_INT_STATUS_LAST_STATUS);
- return status;
+ return (cairo_status_t) status;
}
cairo_private cairo_status_t
diff --git a/src/cairo-types-private.h b/src/cairo-types-private.h
index b1b0db3..4933cf2 100644
--- a/src/cairo-types-private.h
+++ b/src/cairo-types-private.h
@@ -44,6 +44,8 @@
#include "cairo-list-private.h"
#include "cairo-reference-count-private.h"
+CAIRO_BEGIN_DECLS
+
/**
* SECTION:cairo-types
* @Title: Types
@@ -225,12 +227,6 @@ typedef enum _cairo_paginated_mode {
CAIRO_PAGINATED_MODE_FALLBACK /* paint fallback images */
} cairo_paginated_mode_t;
-/* Sure wish C had a real enum type so that this would be distinct
- * from #cairo_status_t. Oh well, without that, I'll use this bogus 100
- * offset. We want to keep it fit in int8_t as the compiler may choose
- * that for #cairo_status_t */
-typedef enum _cairo_int_status cairo_int_status_t;
-
typedef enum _cairo_internal_surface_type {
CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT = 0x1000,
CAIRO_INTERNAL_SURFACE_TYPE_PAGINATED,
@@ -419,4 +415,7 @@ typedef struct _cairo_scaled_glyph {
void *surface_private; /* for the surface backend */
} cairo_scaled_glyph_t;
+
+CAIRO_END_DECLS
+
#endif /* CAIRO_TYPES_PRIVATE_H */
diff --git a/src/cairoint.h b/src/cairoint.h
index 4354e3f..43ec8c7 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -71,6 +71,7 @@
#include <pixman.h>
#include "cairo-compiler-private.h"
+#include "cairo-error-private.h"
#if CAIRO_HAS_PS_SURFACE || CAIRO_HAS_SCRIPT_SURFACE || CAIRO_HAS_XML_SURFACE
#define CAIRO_HAS_DEFLATE_STREAM 1
diff --git a/src/skia/cairo-skia-context.cpp b/src/skia/cairo-skia-context.cpp
index 58fc7dd..de17845 100644
--- a/src/skia/cairo-skia-context.cpp
+++ b/src/skia/cairo-skia-context.cpp
@@ -45,8 +45,10 @@
#include "cairo-arc-private.h"
#include "cairo-backend-private.h"
#include "cairo-default-context-private.h"
+#include "cairo-freed-pool-private.h"
#include "cairo-gstate-private.h"
#include "cairo-path-private.h"
+#include "cairo-pattern-private.h"
#include "cairo-skia-private.h"
#include <SkShader.h>
@@ -68,53 +70,7 @@
#endif
-#if HAS_ATOMIC_OPS
-/* We keep a small stash of contexts to reduce malloc pressure */
-#define CAIRO_STASH_SIZE 4
-static struct {
- cairo_skia_context_t pool[CAIRO_STASH_SIZE];
- cairo_atomic_int_t occupied;
-} _context_stash;
-
-static cairo_skia_context_t *
-_context_get (void)
-{
- cairo_atomic_int_t avail, old, n;
-
- do {
- old = _cairo_atomic_int_get (&_context_stash.occupied);
- avail = ffs (~old) - 1;
- if (avail >= CAIRO_STASH_SIZE)
- return NULL;
-
- n = old | (1 << avail);
- } while (_cairo_atomic_int_cmpxchg (&_context_stash.occupied, old, n) != old);
-
- return &_context_stash.pool[avail];
-}
-
-static void
-_context_put (cairo_skia_context_t *cr)
-{
- cairo_atomic_int_t old, n, avail;
-
- if (cr < &_context_stash.pool[0] ||
- cr >= &_context_stash.pool[CAIRO_STASH_SIZE])
- {
- delete (cr);
- return;
- }
-
- avail = ~(1 << (cr - &_context_stash.pool[0]));
- do {
- old = _cairo_atomic_int_get (&_context_stash.occupied);
- n = old & avail;
- } while (_cairo_atomic_int_cmpxchg (&_context_stash.occupied, old, n) != old);
-}
-#else
-#define _context_get() NULL
-#define _context_put(cr) delete (cr)
-#endif
+static freed_pool_t context_pool;
static void
_cairo_skia_context_destroy (void *abstract_cr)
@@ -140,7 +96,7 @@ _cairo_skia_context_destroy (void *abstract_cr)
_cairo_fini (&cr->base);
- _context_put (cr);
+ _freed_pool_put (&context_pool, cr);
}
static cairo_surface_t *
@@ -343,6 +299,16 @@ color_to_sk (const cairo_color_t& c)
(U8CPU) (c.blue * 255));
}
+static inline SkColor
+color_stop_to_sk (const cairo_color_stop_t& c)
+{
+ /* Need unpremultiplied 1-byte values */
+ return SkColorSetARGB ((U8CPU) (c.alpha * 255),
+ (U8CPU) (c.red * 255),
+ (U8CPU) (c.green * 255),
+ (U8CPU) (c.blue * 255));
+}
+
static SkShader*
source_to_sk_shader (cairo_skia_context_t *cr,
const cairo_pattern_t *pattern)
@@ -401,17 +367,17 @@ source_to_sk_shader (cairo_skia_context_t *cr,
for (unsigned int i = 0; i < gradient->n_stops; i++) {
pos[i] = CAIRO_FIXED_TO_SK_SCALAR (gradient->stops[i].offset);
- colors[i] = color_to_sk (gradient->stops[i].color);
+ colors[i] = color_stop_to_sk (gradient->stops[i].color);
}
if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR) {
cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient;
SkPoint points[2];
- points[0].set (CAIRO_FIXED_TO_SK_SCALAR (linear->p1.x),
- CAIRO_FIXED_TO_SK_SCALAR (linear->p1.y));
- points[1].set (CAIRO_FIXED_TO_SK_SCALAR (linear->p2.x),
- CAIRO_FIXED_TO_SK_SCALAR (linear->p2.y));
+ points[0].set (SkFloatToScalar (linear->pd1.x),
+ SkFloatToScalar (linear->pd1.y));
+ points[1].set (SkFloatToScalar (linear->pd2.x),
+ SkFloatToScalar (linear->pd2.y));
shader = SkGradientShader::CreateLinear (points, colors, pos, gradient->n_stops,
extend_to_sk (pattern->extend));
} else {
@@ -1264,12 +1230,26 @@ _cairo_skia_context_paint (void *abstract_cr)
}
static cairo_status_t
+_cairo_skia_context_paint_with_alpha (void *abstract_cr,
+ double alpha)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ if (CAIRO_ALPHA_IS_OPAQUE (alpha))
+ return _cairo_skia_context_paint (cr);
+
+ /*XXX */
+ return _cairo_skia_context_paint (cr);
+}
+
+static cairo_status_t
_cairo_skia_context_mask (void *abstract_cr,
- cairo_pattern_t *mask)
+ cairo_pattern_t *mask)
{
//cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
- ASSERT_NOT_REACHED;
+ /* XXX */
+ //ASSERT_NOT_REACHED;
return CAIRO_STATUS_SUCCESS;
}
@@ -1544,7 +1524,8 @@ _cairo_skia_context_glyphs (void *abstract_cr,
{
//cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
- ASSERT_NOT_REACHED;
+ /* XXX */
+ //ASSERT_NOT_REACHED;
return CAIRO_STATUS_SUCCESS;
}
@@ -1572,6 +1553,7 @@ _cairo_skia_context_glyph_extents (void *abstract_cr,
}
static const cairo_backend_t _cairo_skia_context_backend = {
+ CAIRO_TYPE_SKIA,
_cairo_skia_context_destroy,
_cairo_skia_context_get_original_target,
@@ -1651,6 +1633,7 @@ static const cairo_backend_t _cairo_skia_context_backend = {
_cairo_skia_context_copy_clip_rectangle_list,
_cairo_skia_context_paint,
+ _cairo_skia_context_paint_with_alpha,
_cairo_skia_context_mask,
_cairo_skia_context_stroke,
@@ -1688,7 +1671,7 @@ _cairo_skia_context_create (void *target)
cairo_skia_surface_t *surface = (cairo_skia_surface_t *) target;
cairo_skia_context_t *cr;
- cr = _context_get ();
+ cr = (cairo_skia_context_t *) _freed_pool_get (&context_pool);
if (unlikely (cr == NULL)) {
cr = new cairo_skia_context_t;
if (unlikely (cr == NULL))
diff --git a/src/skia/cairo-skia-private.h b/src/skia/cairo-skia-private.h
index 7e85613..3af3eee 100644
--- a/src/skia/cairo-skia-private.h
+++ b/src/skia/cairo-skia-private.h
@@ -37,7 +37,6 @@
#define CAIRO_SKIA_CONTEXT_PRIVATE_H
#include "cairo-private.h"
-#include "cairo-image-surface-private.h"
#include <SkBitmap.h>
#include <SkCanvas.h>
@@ -95,6 +94,7 @@ format_to_sk_config (cairo_format_t format,
case CAIRO_FORMAT_A1:
config = SkBitmap::kA1_Config;
break;
+ case CAIRO_FORMAT_RGB30:
case CAIRO_FORMAT_INVALID:
default:
return false;
diff --git a/src/skia/cairo-skia-surface.cpp b/src/skia/cairo-skia-surface.cpp
index 95ec84d..4c10625 100644
--- a/src/skia/cairo-skia-surface.cpp
+++ b/src/skia/cairo-skia-surface.cpp
@@ -39,8 +39,8 @@
#include "cairo-skia.h"
#include "cairo-skia-private.h"
+#include "cairo-composite-rectangles-private.h"
#include "cairo-error-private.h"
-#include "cairo-foreign-context-private.h"
static cairo_skia_surface_t *
_cairo_skia_surface_create_internal (SkBitmap::Config config,
@@ -82,9 +82,38 @@ _cairo_skia_surface_finish (void *asurface)
{
cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
+ cairo_surface_finish (&surface->image.base);
delete surface->bitmap;
- return _cairo_image_surface_finish (&surface->image);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_surface_t *
+_cairo_skia_surface_map_to_image (void *asurface,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
+
+ surface->bitmap->lockPixels ();
+
+ if (extents->width < surface->image.width ||
+ extents->height < surface->image.height)
+ {
+ return _cairo_surface_create_for_rectangle_int (&surface->image.base,
+ extents);
+ }
+
+ return cairo_surface_reference (&surface->image.base);
+}
+
+static cairo_int_status_t
+_cairo_skia_surface_unmap_image (void *asurface,
+ cairo_image_surface_t *image)
+{
+ cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
+
+ surface->bitmap->unlockPixels ();
+ return CAIRO_INT_STATUS_SUCCESS;
}
static cairo_status_t
@@ -111,13 +140,231 @@ _cairo_skia_surface_release_source_image (void *asurface,
surface->bitmap->unlockPixels ();
}
+static cairo_bool_t
+_cairo_skia_surface_get_extents (void *asurface,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
+ extents->x = extents->y = 0;
+ extents->width = surface->image.width;
+ extents->height = surface->image.height;
+ return TRUE;
+}
+
+static void
+_cairo_skia_surface_get_font_options (void *abstract_surface,
+ cairo_font_options_t *options)
+{
+ _cairo_font_options_init_default (options);
+
+ cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON);
+ _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_ON);
+}
+
+static cairo_rectangle_t *
+to_rectangle (cairo_rectangle_t *rf,
+ cairo_rectangle_int_t *ri)
+{
+ rf->x = ri->x;
+ rf->y = ri->y;
+ rf->width = ri->width;
+ rf->height = ri->height;
+ return rf;
+}
+
+static cairo_int_status_t
+_cairo_foreign_surface_paint (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip)
+{
+ cairo_surface_t *surface = (cairo_surface_t *) abstract_surface;
+ cairo_surface_t *image;
+ cairo_rectangle_int_t extents;
+ cairo_rectangle_t rect;
+ cairo_composite_rectangles_t composite;
+ cairo_int_status_t status;
+
+ _cairo_surface_get_extents (surface, &extents);
+ status = _cairo_composite_rectangles_init_for_paint (&composite, &extents,
+ op, source,
+ clip);
+ if (unlikely (status))
+ return status;
+
+ image = cairo_surface_map_to_image (surface,
+ to_rectangle(&rect, &composite.unbounded));
+ status = (cairo_int_status_t)
+ _cairo_surface_paint (image, op, source, clip);
+ cairo_surface_unmap_image (surface, image);
+
+ _cairo_composite_rectangles_fini (&composite);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_foreign_surface_mask (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip)
+{
+ cairo_surface_t *surface =(cairo_surface_t *) abstract_surface;
+ cairo_surface_t *image;
+ cairo_rectangle_int_t extents;
+ cairo_rectangle_t rect;
+ cairo_composite_rectangles_t composite;
+ cairo_int_status_t status;
+
+ _cairo_surface_get_extents (surface, &extents);
+ status = _cairo_composite_rectangles_init_for_mask (&composite, &extents,
+ op, source, mask,
+ clip);
+ if (unlikely (status))
+ return status;
+
+ image = cairo_surface_map_to_image (surface,
+ to_rectangle(&rect, &composite.unbounded));
+ status = (cairo_int_status_t)
+ _cairo_surface_mask (image, op, source, mask, clip);
+ cairo_surface_unmap_image (surface, image);
+
+ _cairo_composite_rectangles_fini (&composite);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_foreign_surface_stroke (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t*style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_surface_t *surface =(cairo_surface_t *) abstract_surface;
+ cairo_surface_t *image;
+ cairo_composite_rectangles_t composite;
+ cairo_rectangle_int_t extents;
+ cairo_rectangle_t rect;
+ cairo_int_status_t status;
+
+ _cairo_surface_get_extents (surface, &extents);
+ status = _cairo_composite_rectangles_init_for_stroke (&composite, &extents,
+ op, source,
+ path, style, ctm,
+ clip);
+ if (unlikely (status))
+ return status;
+
+ image = cairo_surface_map_to_image (surface,
+ to_rectangle(&rect, &composite.unbounded));
+ status = (cairo_int_status_t)
+ _cairo_surface_stroke (image, op, source, path, style, ctm, ctm_inverse, tolerance, antialias, clip);
+ cairo_surface_unmap_image (surface, image);
+
+ _cairo_composite_rectangles_fini (&composite);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_foreign_surface_fill (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_surface_t *surface =(cairo_surface_t *) abstract_surface;
+ cairo_surface_t *image;
+ cairo_composite_rectangles_t composite;
+ cairo_rectangle_int_t extents;
+ cairo_rectangle_t rect;
+ cairo_int_status_t status;
+
+ _cairo_surface_get_extents (surface, &extents);
+ status = _cairo_composite_rectangles_init_for_fill (&composite, &extents,
+ op, source, path,
+ clip);
+ if (unlikely (status))
+ return status;
+
+ image = cairo_surface_map_to_image (surface,
+ to_rectangle(&rect, &composite.unbounded));
+ status = (cairo_int_status_t)
+ _cairo_surface_fill (image, op, source, path, fill_rule, tolerance, antialias, clip);
+ cairo_surface_unmap_image (surface, image);
+
+ _cairo_composite_rectangles_fini (&composite);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_foreign_surface_glyphs (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip,
+ int *num_remaining)
+{
+ cairo_surface_t *surface =(cairo_surface_t *) abstract_surface;
+ cairo_surface_t *image;
+ cairo_composite_rectangles_t composite;
+ cairo_rectangle_int_t extents;
+ cairo_rectangle_t rect;
+ cairo_int_status_t status;
+ cairo_bool_t overlap;
+
+ _cairo_surface_get_extents (surface, &extents);
+ status = _cairo_composite_rectangles_init_for_glyphs (&composite, &extents,
+ op, source,
+ scaled_font,
+ glyphs, num_glyphs,
+ clip,
+ &overlap);
+ if (unlikely (status))
+ return status;
+
+ image = cairo_surface_map_to_image (surface,
+ to_rectangle(&rect, &composite.unbounded));
+ status = (cairo_int_status_t)
+ _cairo_surface_show_text_glyphs (image,
+ op, source,
+ NULL, 0,
+ glyphs, num_glyphs,
+ NULL, 0, (cairo_text_cluster_flags_t)0,
+ scaled_font,
+ clip);
+ cairo_surface_unmap_image (surface, image);
+ _cairo_composite_rectangles_fini (&composite);
+
+ *num_remaining = 0;
+ return status;
+}
+
static const struct _cairo_surface_backend
cairo_skia_surface_backend = {
CAIRO_SURFACE_TYPE_SKIA,
+ _cairo_skia_surface_finish,
+
_cairo_skia_context_create,
_cairo_skia_surface_create_similar,
- _cairo_skia_surface_finish,
+ NULL, //_cairo_skia_surface_create_similar_image,
+ _cairo_skia_surface_map_to_image,
+ _cairo_skia_surface_unmap_image,
+
_cairo_skia_surface_acquire_source_image,
_cairo_skia_surface_release_source_image,
@@ -132,20 +379,20 @@ cairo_skia_surface_backend = {
NULL, /* copy_page */
NULL, /* show_page */
- _cairo_image_surface_get_extents,
+ _cairo_skia_surface_get_extents,
NULL, /* old_show_glyphs */
- _cairo_image_surface_get_font_options,
+ _cairo_skia_surface_get_font_options,
NULL, /* flush */
NULL, /* mark_dirty_rectangle */
NULL, /* scaled_font_fini */
NULL, /* scaled_glyph_fini */
- /* XXX native surface functions */
- _cairo_foreign_context_paint,
- _cairo_foreign_context_mask,
- _cairo_foreign_context_stroke,
- _cairo_foreign_context_fill,
- _cairo_foreign_context_glyphs
+ /* XXX native surface functions? */
+ _cairo_foreign_surface_paint,
+ _cairo_foreign_surface_mask,
+ _cairo_foreign_surface_stroke,
+ _cairo_foreign_surface_fill,
+ _cairo_foreign_surface_glyphs
};
/*
commit e39363367bdaa14acb73e268effe777ac793404f
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date: Sat Apr 24 00:50:33 2010 +0100
skia: Update to use cairo_backend_t interface
diff --git a/configure.ac b/configure.ac
index 793263f..9fe6690 100644
--- a/configure.ac
+++ b/configure.ac
@@ -199,7 +199,7 @@ CAIRO_ENABLE_SURFACE_BACKEND(skia, Skia, no, [
[AS_HELP_STRING([--with-skia=/path/to/skia],
[directory to find compiled skia sources])],
[skia_DIR="$withval"],
- [skia_DIR="`pwd`/../mesa"])
+ [skia_DIR="`pwd`/../skia"])
skia_NONPKGCONFIG_CFLAGS="-I$skia_DIR/include/config -I$skia_DIR/include/core -I$skia_DIR/include/effects"
skia_NONPKGCONFIG_LIBS="$skia_DIR/out/libskia.a"
AC_SUBST(skia_DIR)
diff --git a/src/Makefile.sources b/src/Makefile.sources
index 059f6ae..fb67e5b 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -312,7 +312,10 @@ cairo_win32_sources = cairo-win32-surface.c cairo-win32-printing-surface.c
cairo_win32_font_sources = cairo-win32-font.c
cairo_skia_headers = cairo-skia.h
-cairo_skia_cxx_sources = cairo-skia-surface.cpp
+cairo_skia_cxx_sources = \
+ skia/cairo-skia-context.cpp \
+ skia/cairo-skia-surface.cpp \
+ $(NULL)
cairo_os2_headers = cairo-os2.h
cairo_os2_private = cairo-os2-private.h
diff --git a/src/cairo-skia.h b/src/cairo-skia.h
index f628235..99b9286 100644
--- a/src/cairo-skia.h
+++ b/src/cairo-skia.h
@@ -55,24 +55,6 @@ cairo_skia_surface_create_for_data (unsigned char *data,
int height,
int stride);
-cairo_public unsigned char *
-cairo_skia_surface_get_data (cairo_surface_t *surface);
-
-cairo_public cairo_format_t
-cairo_skia_surface_get_format (cairo_surface_t *surface);
-
-cairo_public int
-cairo_skia_surface_get_width (cairo_surface_t *surface);
-
-cairo_public int
-cairo_skia_surface_get_height (cairo_surface_t *surface);
-
-cairo_public int
-cairo_skia_surface_get_stride (cairo_surface_t *surface);
-
-cairo_public cairo_surface_t *
-cairo_skia_surface_get_image (cairo_surface_t *surface);
-
CAIRO_END_DECLS
#else
diff --git a/src/skia/cairo-skia-context.cpp b/src/skia/cairo-skia-context.cpp
new file mode 100644
index 0000000..58fc7dd
--- /dev/null
+++ b/src/skia/cairo-skia-context.cpp
@@ -0,0 +1,1728 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2010 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth at cworth.org>
+ * Chris Wilson <chris at chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-private.h"
+#include "cairo-error-private.h"
+#include "cairo-arc-private.h"
+#include "cairo-backend-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-gstate-private.h"
+#include "cairo-path-private.h"
+#include "cairo-skia-private.h"
+
+#include <SkShader.h>
+#include <SkColorShader.h>
+#include <SkGradientShader.h>
+#include <SkDashPathEffect.h>
+
+#if !defined(INFINITY)
+#define INFINITY HUGE_VAL
+#endif
+
+#if (CAIRO_FIXED_BITS == 32) && (CAIRO_FIXED_FRAC_BITS == 16) && defined(SK_SCALAR_IS_FIXED)
+# define CAIRO_FIXED_TO_SK_SCALAR(x) (x)
+#elif defined(SK_SCALAR_IS_FIXED)
+/* This can be done better, but this will do for now */
+# define CAIRO_FIXED_TO_SK_SCALAR(x) SkFloatToScalar(_cairo_fixed_to_double(x))
+#else
+# define CAIRO_FIXED_TO_SK_SCALAR(x) SkFloatToScalar(_cairo_fixed_to_double(x))
+#endif
+
+
+#if HAS_ATOMIC_OPS
+/* We keep a small stash of contexts to reduce malloc pressure */
+#define CAIRO_STASH_SIZE 4
+static struct {
+ cairo_skia_context_t pool[CAIRO_STASH_SIZE];
+ cairo_atomic_int_t occupied;
+} _context_stash;
+
+static cairo_skia_context_t *
+_context_get (void)
+{
+ cairo_atomic_int_t avail, old, n;
+
+ do {
+ old = _cairo_atomic_int_get (&_context_stash.occupied);
+ avail = ffs (~old) - 1;
+ if (avail >= CAIRO_STASH_SIZE)
+ return NULL;
+
+ n = old | (1 << avail);
+ } while (_cairo_atomic_int_cmpxchg (&_context_stash.occupied, old, n) != old);
+
+ return &_context_stash.pool[avail];
+}
+
+static void
+_context_put (cairo_skia_context_t *cr)
+{
+ cairo_atomic_int_t old, n, avail;
+
+ if (cr < &_context_stash.pool[0] ||
+ cr >= &_context_stash.pool[CAIRO_STASH_SIZE])
+ {
+ delete (cr);
+ return;
+ }
+
+ avail = ~(1 << (cr - &_context_stash.pool[0]));
+ do {
+ old = _cairo_atomic_int_get (&_context_stash.occupied);
+ n = old & avail;
+ } while (_cairo_atomic_int_cmpxchg (&_context_stash.occupied, old, n) != old);
+}
+#else
+#define _context_get() NULL
+#define _context_put(cr) delete (cr)
+#endif
+
+static void
+_cairo_skia_context_destroy (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ cr->path->reset ();
+ cr->paint->reset ();
+
+ delete cr->canvas;
+
+ cairo_surface_destroy (&cr->target->image.base);
+ cairo_surface_destroy (&cr->original->image.base);
+
+ if (cr->source != NULL) {
+ if (cr->source_image != NULL) {
+ _cairo_surface_release_source_image (cr->source, cr->source_image, cr->source_extra);
+ cr->source_image = NULL;
+ }
+ cairo_surface_destroy (cr->source);
+ cr->source = NULL;
+ }
+
+ _cairo_fini (&cr->base);
+
+ _context_put (cr);
+}
+
+static cairo_surface_t *
+_cairo_skia_context_get_original_target (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ return &cr->original->image.base;
+}
+
+static cairo_surface_t *
+_cairo_skia_context_get_current_target (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ return &cr->target->image.base;
+}
+
+static cairo_status_t
+_cairo_skia_context_save (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ cr->canvas->save ();
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_restore (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ cr->canvas->restore ();
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_push_group (void *abstract_cr, cairo_content_t content)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+ cairo_surface_t *group_surface;
+ cairo_status_t status;
+ int width, height;
+
+ //clip = _cairo_gstate_get_clip (cr->gstate);
+ width = cr->target->image.width;
+ height = cr->target->image.height;
+ group_surface = cr->target->image.base.backend->create_similar (&cr->target->image.base,
+ content, width, height);
+
+#if 0
+ /* Set device offsets on the new surface so that logically it appears at
+ * the same location on the parent surface -- when we pop_group this,
+ * the source pattern will get fixed up for the appropriate target surface
+ * device offsets, so we want to set our own surface offsets from /that/,
+ * and not from the device origin. */
+ cairo_surface_set_device_offset (group_surface,
+ parent_surface->device_transform.x0 - extents.x,
+ parent_surface->device_transform.y0 - extents.y);
+
+ /* If we have a current path, we need to adjust it to compensate for
+ * the device offset just applied. */
+ _cairo_path_fixed_transform (cr->path,
+ &group_surface->device_transform);
+#endif
+
+ status = _cairo_skia_context_save (cr);
+ if (unlikely (status)) {
+ cairo_surface_destroy (group_surface);
+ return status;
+ }
+
+ cairo_surface_destroy (&cr->target->image.base);
+ cr->target = (cairo_skia_surface_t *) group_surface;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_pattern_t *
+_cairo_skia_context_pop_group (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+ cairo_surface_t *group_surface;
+ cairo_pattern_t *group_pattern;
+ cairo_status_t status;
+
+ group_surface = cairo_surface_reference (&cr->target->image.base);
+
+ status = _cairo_skia_context_restore (cr);
+ if (unlikely (status)) {
+ group_pattern = _cairo_pattern_create_in_error (status);
+ goto done;
+ }
+
+ group_pattern = cairo_pattern_create_for_surface (group_surface);
+ status = group_pattern->status;
+ if (unlikely (status))
+ goto done;
+
+#if 0
+ _cairo_gstate_get_matrix (cr->gstate, &group_matrix);
+ /* Transform by group_matrix centered around device_transform so that when
+ * we call _cairo_gstate_copy_transformed_pattern the result is a pattern
+ * with a matrix equivalent to the device_transform of group_surface. */
+ if (_cairo_surface_has_device_transform (group_surface)) {
+ cairo_pattern_set_matrix (group_pattern, &group_surface->device_transform);
+ _cairo_pattern_transform (group_pattern, &group_matrix);
+ _cairo_pattern_transform (group_pattern, &group_surface->device_transform_inverse);
+ } else {
+ cairo_pattern_set_matrix (group_pattern, &group_matrix);
+ }
+
+ /* If we have a current path, we need to adjust it to compensate for
+ * the device offset just removed. */
+ _cairo_path_fixed_transform (cr->path,
+ &group_surface->device_transform_inverse);
+#endif
+
+done:
+ cairo_surface_destroy (group_surface);
+
+ return group_pattern;
+}
+
+static inline cairo_surface_t *
+surface_from_pattern (const cairo_pattern_t *pattern)
+{
+ return (reinterpret_cast <const cairo_surface_pattern_t *> (pattern))->surface;
+}
+
+static inline bool
+surface_to_sk_bitmap (cairo_surface_t *surface, SkBitmap& bitmap)
+{
+ cairo_image_surface_t *img = (cairo_image_surface_t *) surface;
+ SkBitmap::Config config;
+ bool opaque;
+
+ if (unlikely (! format_to_sk_config (img->format, config, opaque)))
+ return false;
+
+ bitmap.reset ();
+ bitmap.setConfig (config, img->width, img->height, img->stride);
+ bitmap.setIsOpaque (opaque);
+ bitmap.setPixels (img->data);
+
+ return true;
+}
+
+static inline SkMatrix
+matrix_to_sk (const cairo_matrix_t& mat)
+{
+ SkMatrix skm;
+
+ skm.reset ();
+ skm.set (SkMatrix::kMScaleX, SkFloatToScalar (mat.xx));
+ skm.set (SkMatrix::kMSkewX, SkFloatToScalar (mat.xy));
+ skm.set (SkMatrix::kMTransX, SkFloatToScalar (mat.x0));
+ skm.set (SkMatrix::kMSkewY, SkFloatToScalar (mat.yx));
+ skm.set (SkMatrix::kMScaleY, SkFloatToScalar (mat.yy));
+ skm.set (SkMatrix::kMTransY, SkFloatToScalar (mat.y0));
+
+ /*
+ skm[6] = SkFloatToScalar (0.0);
+ skm[7] = SkFloatToScalar (0.0);
+ skm[8] = SkFloatToScalar (1.0); -- this isn't right, it wants a magic value in there that it'll set itself. It wants Sk_Fract1 (2.30), not Sk_Scalar1
+ */
+
+ return skm;
+}
+
+static inline SkMatrix
+matrix_inverse_to_sk (const cairo_matrix_t& mat)
+{
+ cairo_matrix_t inv = mat;
+ cairo_status_t status = cairo_matrix_invert (&inv);
+ assert (status == CAIRO_STATUS_SUCCESS);
+ return matrix_to_sk (inv);
+}
+
+static SkShader::TileMode
+extend_to_sk (cairo_extend_t extend)
+{
+ static const SkShader::TileMode modeMap[] = {
+ SkShader::kClamp_TileMode, // NONE behaves like PAD, because noone wants NONE
+ SkShader::kRepeat_TileMode,
+ SkShader::kMirror_TileMode,
+ SkShader::kClamp_TileMode
+ };
+
+ return modeMap[extend];
+}
+
+static inline SkColor
+color_to_sk (const cairo_color_t& c)
+{
+ /* Need unpremultiplied 1-byte values */
+ return SkColorSetARGB ((U8CPU) (c.alpha * 255),
+ (U8CPU) (c.red * 255),
+ (U8CPU) (c.green * 255),
+ (U8CPU) (c.blue * 255));
+}
+
+static SkShader*
+source_to_sk_shader (cairo_skia_context_t *cr,
+ const cairo_pattern_t *pattern)
+{
+ SkShader *shader = NULL;
+
+ if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) {
+ cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern;
+ return new SkColorShader (color_to_sk (solid->color));
+ } else if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
+ cairo_surface_t *surface = surface_from_pattern (pattern);
+
+ cr->source = cairo_surface_reference (surface);
+
+ if (surface->type == CAIRO_SURFACE_TYPE_SKIA) {
+ cairo_skia_surface_t *esurf = (cairo_skia_surface_t *) surface;
+
+ shader = SkShader::CreateBitmapShader (*esurf->bitmap,
+ extend_to_sk (pattern->extend),
+ extend_to_sk (pattern->extend));
+ } else {
+ SkBitmap bitmap;
+
+ if (! _cairo_surface_is_image (surface)) {
+ cairo_status_t status;
+
+ status = _cairo_surface_acquire_source_image (surface,
+ &cr->source_image,
+ &cr->source_extra);
+ if (status)
+ return NULL;
+
+ surface = &cr->source_image->base;
+ }
+
+ if (unlikely (! surface_to_sk_bitmap (surface, bitmap)))
+ return NULL;
+
+ shader = SkShader::CreateBitmapShader (bitmap,
+ extend_to_sk (pattern->extend),
+ extend_to_sk (pattern->extend));
+ }
+ } else if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR
+ /* || pattern->type == CAIRO_PATTERN_TYPE_RADIAL */)
+ {
+ cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t *) pattern;
+ SkColor colors_stack[10];
+ SkScalar pos_stack[10];
+ SkColor *colors = colors_stack;
+ SkScalar *pos = pos_stack;
+
+ if (gradient->n_stops > 10) {
+ colors = new SkColor[gradient->n_stops];
+ pos = new SkScalar[gradient->n_stops];
+ }
+
+ for (unsigned int i = 0; i < gradient->n_stops; i++) {
+ pos[i] = CAIRO_FIXED_TO_SK_SCALAR (gradient->stops[i].offset);
+ colors[i] = color_to_sk (gradient->stops[i].color);
+ }
+
+ if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR) {
+ cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient;
+ SkPoint points[2];
+
+ points[0].set (CAIRO_FIXED_TO_SK_SCALAR (linear->p1.x),
+ CAIRO_FIXED_TO_SK_SCALAR (linear->p1.y));
+ points[1].set (CAIRO_FIXED_TO_SK_SCALAR (linear->p2.x),
+ CAIRO_FIXED_TO_SK_SCALAR (linear->p2.y));
+ shader = SkGradientShader::CreateLinear (points, colors, pos, gradient->n_stops,
+ extend_to_sk (pattern->extend));
+ } else {
+ // XXX todo -- implement real radial shaders in Skia
+ }
+
+ if (gradient->n_stops > 10) {
+ delete [] colors;
+ delete [] pos;
+ }
+ }
+
+ if (shader && ! _cairo_matrix_is_identity (&pattern->matrix))
+ shader->setLocalMatrix (matrix_inverse_to_sk (pattern->matrix));
+
+ return shader;
+}
+
+static inline bool
+pattern_filter_to_sk (const cairo_pattern_t *pattern)
+{
+ switch (pattern->filter) {
+ case CAIRO_FILTER_GOOD:
+ case CAIRO_FILTER_BEST:
+ case CAIRO_FILTER_BILINEAR:
+ case CAIRO_FILTER_GAUSSIAN:
+ return true;
+ default:
+ case CAIRO_FILTER_FAST:
+ case CAIRO_FILTER_NEAREST:
+ return false;
+ }
+}
+
+static inline bool
+pattern_to_sk_color (const cairo_pattern_t *pattern, SkColor& color)
+{
+ if (pattern->type != CAIRO_PATTERN_TYPE_SOLID)
+ return false;
+
+ color = color_to_sk (((cairo_solid_pattern_t *) pattern)->color);
+ return true;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_source (void *abstract_cr,
+ cairo_pattern_t *source)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+ SkColor color;
+
+ if (cr->source != NULL) {
+ if (cr->source_image != NULL) {
+ _cairo_surface_release_source_image (cr->source, cr->source_image, cr->source_extra);
+ cr->source_image = NULL;
+ }
+ cairo_surface_destroy (cr->source);
+ cr->source = NULL;
+ }
+
+ if (pattern_to_sk_color (source, color)) {
+ cr->paint->setColor (color);
+ } else {
+ SkShader *shader = source_to_sk_shader (cr, source);
+ if (shader == NULL) {
+ ASSERT_NOT_REACHED;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ cr->paint->setShader (shader);
+ shader->unref ();
+
+ cr->paint->setFilterBitmap (pattern_filter_to_sk (source));
+ }
+
+ /* XXX change notification */
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_source_rgba (void *abstract_cr, double red, double green, double blue, double alpha)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ /* Need unpremultiplied 1-byte values */
+ cr->paint->setARGB ((U8CPU) (alpha * 255),
+ (U8CPU) (red * 255),
+ (U8CPU) (green * 255),
+ (U8CPU) (blue * 255));
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_source_surface (void *abstract_cr,
+ cairo_surface_t *surface,
+ double x,
+ double y)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+ cairo_pattern_t *pattern;
+ cairo_matrix_t matrix;
+ cairo_status_t status;
+
+ if (surface->type == CAIRO_SURFACE_TYPE_SKIA) {
+ cairo_skia_surface_t *esurf = (cairo_skia_surface_t *) surface;
+ SkShader *shader;
+
+ shader = SkShader::CreateBitmapShader (*esurf->bitmap,
+ SkShader::kClamp_TileMode, /* XXX */
+ SkShader::kClamp_TileMode);
+
+ cr->paint->setShader (shader);
+ shader->unref ();
+
+ cr->paint->setFilterBitmap (true);
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ pattern = cairo_pattern_create_for_surface (surface);
+ if (unlikely (pattern->status))
+ return pattern->status;
+
+ cairo_matrix_init_translate (&matrix, -x, -y);
+ cairo_pattern_set_matrix (pattern, &matrix);
+
+ status = _cairo_skia_context_set_source (cr, pattern);
+ cairo_pattern_destroy (pattern);
+
+ return status;
+}
+
+static cairo_pattern_t *
+_cairo_skia_context_get_source (void *abstract_cr)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ ASSERT_NOT_REACHED;
+ return NULL;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_tolerance (void *abstract_cr,
+ double tolerance)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ /* XXX ignored */
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static inline SkXfermode::Mode
+operator_to_sk (cairo_operator_t op)
+{
+ static const SkXfermode::Mode modeMap[] = {
+ SkXfermode::kClear_Mode,
+
+ SkXfermode::kSrc_Mode,
+ SkXfermode::kSrcOver_Mode,
+ SkXfermode::kSrcIn_Mode,
+ SkXfermode::kSrcOut_Mode,
+ SkXfermode::kSrcATop_Mode,
+
+ SkXfermode::kDst_Mode,
+ SkXfermode::kDstOver_Mode,
+ SkXfermode::kDstIn_Mode,
+ SkXfermode::kDstOut_Mode,
+ SkXfermode::kDstATop_Mode,
+
+ SkXfermode::kXor_Mode,
+ SkXfermode::kPlus_Mode, // XXX Add?
+ SkXfermode::kPlus_Mode, // XXX SATURATE
+
+ SkXfermode::kPlus_Mode,
+ SkXfermode::kMultiply_Mode,
+ SkXfermode::kScreen_Mode,
+ SkXfermode::kOverlay_Mode,
+ SkXfermode::kDarken_Mode,
+ SkXfermode::kLighten_Mode,
+ SkXfermode::kColorDodge_Mode,
+ SkXfermode::kColorBurn_Mode,
+ SkXfermode::kHardLight_Mode,
+ SkXfermode::kSoftLight_Mode,
+ SkXfermode::kDifference_Mode,
+ SkXfermode::kExclusion_Mode,
+
+ SkXfermode::kSrcOver_Mode, // XXX: CAIRO_OPERATOR_HSL_HUE
+ SkXfermode::kSrcOver_Mode, // XXX: CAIRO_OPERATOR_HSL_SATURATION,
+ SkXfermode::kSrcOver_Mode, // XXX: CAIRO_OPERATOR_HSL_COLOR,
+ SkXfermode::kSrcOver_Mode, // XXX: CAIRO_OPERATOR_HSL_LUMINOSITY
+ };
+
+ return modeMap[op];
+}
+
+static cairo_status_t
+_cairo_skia_context_set_operator (void *abstract_cr, cairo_operator_t op)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ cr->paint->setXfermodeMode (operator_to_sk (op));
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_opacity (void *abstract_cr, double opacity)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ /* XXX */
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_antialias (void *abstract_cr, cairo_antialias_t antialias)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ cr->paint->setAntiAlias (antialias != CAIRO_ANTIALIAS_NONE);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_fill_rule (void *abstract_cr,
+ cairo_fill_rule_t fill_rule)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ cr->path->setFillType (fill_rule == CAIRO_FILL_RULE_WINDING ? SkPath::kWinding_FillType : SkPath::kEvenOdd_FillType);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_line_width (void *abstract_cr,
+ double line_width)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ cr->paint->setStrokeWidth (SkFloatToScalar (line_width));
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_line_cap (void *abstract_cr,
+ cairo_line_cap_t line_cap)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+ static const SkPaint::Cap map[] = {
+ SkPaint::kButt_Cap,
+ SkPaint::kRound_Cap,
+ SkPaint::kSquare_Cap
+ };
+ cr->paint->setStrokeCap (map[line_cap]);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_line_join (void *abstract_cr,
+ cairo_line_join_t line_join)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+ static const SkPaint::Join map[] = {
+ SkPaint::kMiter_Join,
+ SkPaint::kRound_Join,
+ SkPaint::kBevel_Join
+ };
+ cr->paint->setStrokeJoin (map[line_join]);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_dash (void *abstract_cr,
+ const double *dashes,
+ int num_dashes,
+ double offset)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+ SkScalar intervals_static[20];
+ SkScalar *intervals = intervals_static;
+
+ int loop = 0;
+ if ((num_dashes & 1) != 0) {
+ loop = 1;
+ num_dashes <<= 1;
+ }
+
+ if (num_dashes > 20)
+ intervals = new SkScalar[num_dashes];
+
+ int i = 0;
+ do {
+ for (int j = 0; i < num_dashes; j++)
+ intervals[i++] = SkFloatToScalar (dashes[j]);
+ } while (loop--);
+
+ SkDashPathEffect *dash = new SkDashPathEffect (intervals, num_dashes, SkFloatToScalar (offset));
+
+ cr->paint->setPathEffect (dash);
+ dash->unref ();
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_miter_limit (void *abstract_cr, double limit)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ cr->paint->setStrokeMiter (SkFloatToScalar (limit));
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_antialias_t
+_cairo_skia_context_get_antialias (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ return cr->paint->isAntiAlias () ? CAIRO_ANTIALIAS_DEFAULT : CAIRO_ANTIALIAS_NONE;
+}
+
+static void
+_cairo_skia_context_get_dash (void *abstract_cr,
+ double *dashes,
+ int *num_dashes,
+ double *offset)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ *num_dashes = 0;
+ /* XXX */
+}
+
+static cairo_fill_rule_t
+_cairo_skia_context_get_fill_rule (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+ SkPath::FillType ft;
+
+ ft = cr->path->getFillType ();
+ if (ft == SkPath::kWinding_FillType)
+ return CAIRO_FILL_RULE_WINDING;
+ if (ft == SkPath::kEvenOdd_FillType)
+ return CAIRO_FILL_RULE_EVEN_ODD;;
+
+ ASSERT_NOT_REACHED;
+ return CAIRO_FILL_RULE_WINDING;
+}
+
+static double
+_cairo_skia_context_get_line_width (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ return /* ScalarToFloat */ cr->paint->getStrokeWidth ();
+}
+
+static cairo_line_cap_t
+_cairo_skia_context_get_line_cap (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+ static const cairo_line_cap_t map[] = {
+ CAIRO_LINE_CAP_BUTT,
+ CAIRO_LINE_CAP_ROUND,
+ CAIRO_LINE_CAP_SQUARE
+ };
+ return map[cr->paint->getStrokeCap ()];
+}
+
+static cairo_line_join_t
+_cairo_skia_context_get_line_join (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+ static const cairo_line_join_t map[] = {
+ CAIRO_LINE_JOIN_MITER,
+ CAIRO_LINE_JOIN_ROUND,
+ CAIRO_LINE_JOIN_BEVEL
+ };
+ return map[cr->paint->getStrokeJoin ()];
+}
+
+static double
+_cairo_skia_context_get_miter_limit (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ return /* SkScalarToFloat */ cr->paint->getStrokeMiter ();
+}
+
+static cairo_operator_t
+_cairo_skia_context_get_operator (void *abstract_cr)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ ASSERT_NOT_REACHED;
+ //cr->paint->getXfermode ();
+ return CAIRO_OPERATOR_OVER;
+}
+
+static double
+_cairo_skia_context_get_opacity (void *abstract_cr)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ ASSERT_NOT_REACHED;
+ return 1.;
+}
+
+static double
+_cairo_skia_context_get_tolerance (void *abstract_cr)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ /* XXX */
+ return CAIRO_GSTATE_TOLERANCE_DEFAULT;
+}
+
+
+/* Current tranformation matrix */
+
+static cairo_status_t
+_cairo_skia_context_translate (void *abstract_cr,
+ double tx,
+ double ty)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ cairo_matrix_translate (&cr->matrix, tx, ty);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_scale (void *abstract_cr,
+ double sx,
+ double sy)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ cairo_matrix_scale (&cr->matrix, sx, sy);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_rotate (void *abstract_cr,
+ double theta)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ cairo_matrix_rotate (&cr->matrix, theta);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_transform (void *abstract_cr,
+ const cairo_matrix_t *matrix)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ cairo_matrix_multiply (&cr->matrix, &cr->matrix, matrix);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_matrix (void *abstract_cr,
+ const cairo_matrix_t *matrix)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ cr->matrix = *matrix;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_identity_matrix (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ cairo_matrix_init_identity (&cr->matrix);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_skia_context_get_matrix (void *abstract_cr,
+ cairo_matrix_t *matrix)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ *matrix = cr->matrix;
+}
+
+static void
+_cairo_skia_context_user_to_device (void *abstract_cr,
+ double *x,
+ double *y)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ cairo_matrix_transform_point (&cr->matrix, x, y);
+}
+
+static void
+_cairo_skia_context_user_to_device_distance (void *abstract_cr,
+ double *dx,
+ double *dy)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ cairo_matrix_transform_distance (&cr->matrix, dx, dy);
+}
+
+static void
+_cairo_skia_context_device_to_user (void *abstract_cr,
+ double *x,
+ double *y)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+ cairo_matrix_t inverse;
+ cairo_status_t status;
+
+ inverse = cr->matrix;
+ status = cairo_matrix_invert (&inverse);
+ assert (CAIRO_STATUS_SUCCESS == status);
+
+ cairo_matrix_transform_point (&inverse, x, y);
+}
+
+static void
+_cairo_skia_context_device_to_user_distance (void *abstract_cr,
+ double *dx,
+ double *dy)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+ cairo_matrix_t inverse;
+ cairo_status_t status;
+
+ inverse = cr->matrix;
+ status = cairo_matrix_invert (&inverse);
+ assert (CAIRO_STATUS_SUCCESS == status);
+
+ cairo_matrix_transform_distance (&inverse, dx, dy);
+}
+
+/* Path constructor */
+
+static cairo_status_t
+_cairo_skia_context_new_path (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ cr->path->reset ();
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_new_sub_path (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ cr->path->rMoveTo (0, 0); /* XXX */
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+user_to_device_point (cairo_skia_context_t *cr, double *x, double *y)
+{
+ cairo_matrix_transform_point (&cr->matrix, x, y);
+ cairo_matrix_transform_point (&cr->target->image.base.device_transform, x, y);
+}
+
+static void
+user_to_device_distance (cairo_skia_context_t *cr, double *dx, double *dy)
+{
+ cairo_matrix_transform_distance (&cr->matrix, dx, dy);
+ cairo_matrix_transform_distance (&cr->target->image.base.device_transform, dx, dy);
+}
+
+static cairo_status_t
+_cairo_skia_context_move_to (void *abstract_cr, double x, double y)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ user_to_device_point (cr, &x, &y);
+ cr->path->moveTo (SkFloatToScalar (x), SkFloatToScalar (y));
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_line_to (void *abstract_cr, double x, double y)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ user_to_device_point (cr, &x, &y);
+ cr->path->lineTo (SkFloatToScalar (x), SkFloatToScalar (y));
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_curve_to (void *abstract_cr,
+ double x1, double y1,
+ double x2, double y2,
+ double x3, double y3)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ user_to_device_point (cr, &x1, &y1);
+ user_to_device_point (cr, &x2, &y2);
+ user_to_device_point (cr, &x3, &y3);
+ cr->path->cubicTo (SkFloatToScalar (x1), SkFloatToScalar (y1),
+ SkFloatToScalar (x2), SkFloatToScalar (y2),
+ SkFloatToScalar (x3), SkFloatToScalar (y3));
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_arc_to (void *abstract_cr,
+ double x1, double y1,
+ double x2, double y2,
+ double radius)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+#if 0
+ user_to_device_point (cr, &x1, &y1);
+ user_to_device_point (cr, &x2, &y2);
+ user_to_device_distance (cr, &radius, &radius);
+#endif
+
+ cr->path->arcTo (SkFloatToScalar (x1), SkFloatToScalar (y1),
+ SkFloatToScalar (x2), SkFloatToScalar (y2),
+ SkFloatToScalar (radius));
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_rel_move_to (void *abstract_cr, double dx, double dy)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ user_to_device_distance (cr, &dx, &dy);
+ cr->path->rMoveTo (SkFloatToScalar (dx), SkFloatToScalar (dy));
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_rel_line_to (void *abstract_cr, double dx, double dy)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ user_to_device_distance (cr, &dx, &dy);
+ cr->path->rLineTo (SkFloatToScalar (dx), SkFloatToScalar (dy));
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_rel_curve_to (void *abstract_cr,
+ double dx1, double dy1,
+ double dx2, double dy2,
+ double dx3, double dy3)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ user_to_device_distance (cr, &dx1, &dy1);
+ user_to_device_distance (cr, &dx2, &dy2);
+ user_to_device_distance (cr, &dx3, &dy3);
+ cr->path->rCubicTo (SkFloatToScalar (dx1), SkFloatToScalar (dy1),
+ SkFloatToScalar (dx2), SkFloatToScalar (dy2),
+ SkFloatToScalar (dx3), SkFloatToScalar (dy3));
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_rel_arc_to (void *abstract_cr,
+ double dx1, double dy1,
+ double dx2, double dy2,
+ double radius)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+#if 0
+ user_to_device_point (cr, &x1, &y1);
+ user_to_device_point (cr, &x2, &y2);
+ user_to_device_distance (cr, &radius, &radius);
+#endif
+
+ cr->path->arcTo (SkFloatToScalar (dx1), SkFloatToScalar (dy1),
+ SkFloatToScalar (dx2), SkFloatToScalar (dy2),
+ SkFloatToScalar (radius));
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_close_path (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ cr->path->close ();
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_rectangle (void *abstract_cr,
+ double x, double y,
+ double width, double height)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+ double x1, y1, x2, y2;
+
+ /* XXX assume no rotation! */
+ x1 = x, y1 = y;
+ user_to_device_point (cr, &x1, &y1);
+
+ x2 = x + width, y2 = y + height;
+ user_to_device_point (cr, &x2, &y2);
+
+ cr->path->addRect (SkFloatToScalar (x1), SkFloatToScalar (y1),
+ SkFloatToScalar (x2), SkFloatToScalar (y2));
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_arc (void *abstract_cr,
+ double xc, double yc, double radius,
+ double angle1, double angle2,
+ cairo_bool_t forward)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+ cairo_status_t status;
+
+ /* XXX cr->path->arc() */
+
+ while (angle2 < angle1)
+ angle2 += 2 * M_PI;
+
+ status = _cairo_skia_context_line_to (cr,
+ xc + radius * cos (angle1),
+ yc + radius * sin (angle1));
+ if (unlikely (status))
+ return status;
+
+ if (forward)
+ _cairo_arc_path (&cr->base, xc, yc, radius, angle1, angle2);
+ else
+ _cairo_arc_path_negative (&cr->base, xc, yc, radius, angle1, angle2);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_skia_context_path_extents (void *abstract_cr,
+ double *x1,
+ double *y1,
+ double *x2,
+ double *y2)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+ SkRect rect;
+
+ rect = cr->path->getBounds ();
+
+ ASSERT_NOT_REACHED;
+ /* XXX transform SkScalar rect to user */
+}
+
+static cairo_bool_t
+_cairo_skia_context_has_current_point (void *abstract_cr)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ return TRUE;
+}
+
+static cairo_bool_t
+_cairo_skia_context_get_current_point (void *abstract_cr,
+ double *x,
+ double *y)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+ SkPoint pt;
+
+ cr->path->getLastPt (&pt);
+ //*x = SkScalarToFloat (pt.x);
+ //*y = SkScalarToFloat (pt.y);
+ //_cairo_gstate_backend_to_user (cr->gstate, x, y);
+
+ return TRUE;
+}
+
+static cairo_path_t *
+_cairo_skia_context_copy_path (void *abstract_cr)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ /* XXX iterate */
+ ASSERT_NOT_REACHED;
+ return NULL;
+}
+
+static cairo_path_t *
+_cairo_skia_context_copy_path_flat (void *abstract_cr)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ /* XXX iterate and decompose */
+ ASSERT_NOT_REACHED;
+ return NULL;
+}
+
+static cairo_status_t
+_cairo_skia_context_append_path (void *abstract_cr,
+ const cairo_path_t *path)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ // return _cairo_path_append_to_context (path, cr);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_stroke_to_path (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ cr->paint->getFillPath (*cr->path, cr->path);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+
+static cairo_status_t
+_cairo_skia_context_paint (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ if (cr->source != NULL) {
+ SkBitmap bitmap;
+ SkMatrix bitmapMatrix;
+
+ if (cr->source->type == CAIRO_SURFACE_TYPE_SKIA) {
+ cairo_skia_surface_t *esurf = (cairo_skia_surface_t *) cr->source->type;
+
+ bitmap = *esurf->bitmap;
+ } else {
+ surface_to_sk_bitmap (&cr->source_image->base, bitmap);
+ }
+
+ // XXX pattern->matrix, pattern->filter, pattern->extend
+ cr->canvas->drawBitmapMatrix (bitmap, bitmapMatrix, cr->paint);
+ } else {
+ cr->canvas->drawPaint (*cr->paint);
+ }
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_mask (void *abstract_cr,
+ cairo_pattern_t *mask)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ ASSERT_NOT_REACHED;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_stroke_preserve (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ /* XXX pen transformation? */
+ assert (_cairo_matrix_is_identity (&cr->matrix));
+ cr->canvas->drawPath (*cr->path, *cr->paint);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_stroke (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+ cairo_status_t status;
+
+ status = _cairo_skia_context_stroke_preserve (cr);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_skia_context_new_path (cr);
+}
+
+static cairo_status_t
+_cairo_skia_context_in_stroke (void *abstract_cr,
+ double x, double y,
+ cairo_bool_t *inside)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ ASSERT_NOT_REACHED;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_stroke_extents (void *abstract_cr,
+ double *x1, double *y1, double *x2, double *y2)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ ASSERT_NOT_REACHED;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_fill_preserve (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ cr->canvas->drawPath (*cr->path, *cr->paint);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_fill (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+ cairo_status_t status;
+
+ status = _cairo_skia_context_fill_preserve (cr);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_skia_context_new_path (cr);
+}
+
+static cairo_status_t
+_cairo_skia_context_in_fill (void *abstract_cr,
+ double x, double y,
+ cairo_bool_t *inside)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ ASSERT_NOT_REACHED;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_fill_extents (void *abstract_cr,
+ double *x1, double *y1, double *x2, double *y2)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ ASSERT_NOT_REACHED;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_clip_preserve (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ cr->canvas->clipPath (*cr->path);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_clip (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+ cairo_status_t status;
+
+ status = _cairo_skia_context_clip_preserve (cr);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_skia_context_new_path (cr);
+}
+
+static cairo_status_t
+_cairo_skia_context_in_clip (void *abstract_cr,
+ double x, double y,
+ cairo_bool_t *inside)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_reset_clip (void *abstract_cr)
+{
+ cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ /* XXX TODO: teach Skia how to reset the clip path ??? */
+ cr->canvas->restore ();
+ cr->canvas->save ();
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_clip_extents (void *abstract_cr,
+ double *x1, double *y1, double *x2, double *y2)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ ASSERT_NOT_REACHED;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_rectangle_list_t *
+_cairo_skia_context_copy_clip_rectangle_list (void *abstract_cr)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ ASSERT_NOT_REACHED;
+ return NULL;
+}
+
+static cairo_status_t
+_cairo_skia_context_copy_page (void *abstract_cr)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ ASSERT_NOT_REACHED;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_show_page (void *abstract_cr)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+ ASSERT_NOT_REACHED;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_font_face (void *abstract_cr,
+ cairo_font_face_t *font_face)
+{
+ // cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ //return _cairo_gstate_set_font_face (cr->gstate, font_face);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_font_face_t *
+_cairo_skia_context_get_font_face (void *abstract_cr)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ ASSERT_NOT_REACHED;
+ return NULL;
+}
+
+static cairo_status_t
+_cairo_skia_context_font_extents (void *abstract_cr,
+ cairo_font_extents_t *extents)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ ASSERT_NOT_REACHED;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_font_size (void *abstract_cr,
+ double size)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ ASSERT_NOT_REACHED;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_font_matrix (void *abstract_cr,
+ const cairo_matrix_t *matrix)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ ASSERT_NOT_REACHED;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_skia_context_get_font_matrix (void *abstract_cr,
+ cairo_matrix_t *matrix)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ ASSERT_NOT_REACHED;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_font_options (void *abstract_cr,
+ const cairo_font_options_t *options)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ ASSERT_NOT_REACHED;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_skia_context_get_font_options (void *abstract_cr,
+ cairo_font_options_t *options)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ ASSERT_NOT_REACHED;
+}
+
+static cairo_status_t
+_cairo_skia_context_set_scaled_font (void *abstract_cr,
+ cairo_scaled_font_t *scaled_font)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ ASSERT_NOT_REACHED;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_scaled_font_t *
+_cairo_skia_context_get_scaled_font (void *abstract_cr)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ ASSERT_NOT_REACHED;
+ return NULL;
+}
+
+static cairo_status_t
+_cairo_skia_context_glyphs (void *abstract_cr,
+ const cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_glyph_text_info_t *info)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ ASSERT_NOT_REACHED;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_glyph_path (void *abstract_cr,
+ const cairo_glyph_t *glyphs,
+ int num_glyphs)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ ASSERT_NOT_REACHED;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_skia_context_glyph_extents (void *abstract_cr,
+ const cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_text_extents_t *extents)
+{
+ //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
+
+ ASSERT_NOT_REACHED;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_backend_t _cairo_skia_context_backend = {
+ _cairo_skia_context_destroy,
+
+ _cairo_skia_context_get_original_target,
+ _cairo_skia_context_get_current_target,
+
+ _cairo_skia_context_save,
+ _cairo_skia_context_restore,
+
+ _cairo_skia_context_push_group,
+ _cairo_skia_context_pop_group,
+
+ _cairo_skia_context_set_source_rgba,
+ _cairo_skia_context_set_source_surface,
+ _cairo_skia_context_set_source,
+ _cairo_skia_context_get_source,
+
+ _cairo_skia_context_set_antialias,
+ _cairo_skia_context_set_dash,
+ _cairo_skia_context_set_fill_rule,
+ _cairo_skia_context_set_line_cap,
+ _cairo_skia_context_set_line_join,
+ _cairo_skia_context_set_line_width,
+ _cairo_skia_context_set_miter_limit,
+ _cairo_skia_context_set_opacity,
+ _cairo_skia_context_set_operator,
+ _cairo_skia_context_set_tolerance,
+ _cairo_skia_context_get_antialias,
+ _cairo_skia_context_get_dash,
+ _cairo_skia_context_get_fill_rule,
+ _cairo_skia_context_get_line_cap,
+ _cairo_skia_context_get_line_join,
+ _cairo_skia_context_get_line_width,
+ _cairo_skia_context_get_miter_limit,
+ _cairo_skia_context_get_opacity,
+ _cairo_skia_context_get_operator,
+ _cairo_skia_context_get_tolerance,
+
+ _cairo_skia_context_translate,
+ _cairo_skia_context_scale,
+ _cairo_skia_context_rotate,
+ _cairo_skia_context_transform,
+ _cairo_skia_context_set_matrix,
+ _cairo_skia_context_set_identity_matrix,
+ _cairo_skia_context_get_matrix,
+ _cairo_skia_context_user_to_device,
+ _cairo_skia_context_user_to_device_distance,
+ _cairo_skia_context_device_to_user,
+ _cairo_skia_context_device_to_user_distance,
+
+ _cairo_skia_context_new_path,
+ _cairo_skia_context_new_sub_path,
+ _cairo_skia_context_move_to,
+ _cairo_skia_context_rel_move_to,
+ _cairo_skia_context_line_to,
+ _cairo_skia_context_rel_line_to,
+ _cairo_skia_context_curve_to,
+ _cairo_skia_context_rel_curve_to,
+ _cairo_skia_context_arc_to,
+ _cairo_skia_context_rel_arc_to,
+ _cairo_skia_context_close_path,
+ _cairo_skia_context_arc,
+ _cairo_skia_context_rectangle,
+ _cairo_skia_context_path_extents,
+ _cairo_skia_context_has_current_point,
+ _cairo_skia_context_get_current_point,
+ _cairo_skia_context_copy_path,
+ _cairo_skia_context_copy_path_flat,
+ _cairo_skia_context_append_path,
+
+ _cairo_skia_stroke_to_path,
+
+ _cairo_skia_context_clip,
+ _cairo_skia_context_clip_preserve,
+ _cairo_skia_context_in_clip,
+ _cairo_skia_context_clip_extents,
+ _cairo_skia_context_reset_clip,
+ _cairo_skia_context_copy_clip_rectangle_list,
+
+ _cairo_skia_context_paint,
+ _cairo_skia_context_mask,
+
+ _cairo_skia_context_stroke,
+ _cairo_skia_context_stroke_preserve,
+ _cairo_skia_context_in_stroke,
+ _cairo_skia_context_stroke_extents,
+
+ _cairo_skia_context_fill,
+ _cairo_skia_context_fill_preserve,
+ _cairo_skia_context_in_fill,
+ _cairo_skia_context_fill_extents,
+
+ _cairo_skia_context_set_font_face,
+ _cairo_skia_context_get_font_face,
+ _cairo_skia_context_set_font_size,
+ _cairo_skia_context_set_font_matrix,
+ _cairo_skia_context_get_font_matrix,
+ _cairo_skia_context_set_font_options,
+ _cairo_skia_context_get_font_options,
+ _cairo_skia_context_set_scaled_font,
+ _cairo_skia_context_get_scaled_font,
+ _cairo_skia_context_font_extents,
+
+ _cairo_skia_context_glyphs,
+ _cairo_skia_context_glyph_path,
+ _cairo_skia_context_glyph_extents,
+
+ _cairo_skia_context_copy_page,
+ _cairo_skia_context_show_page,
+};
+
+cairo_t *
+_cairo_skia_context_create (void *target)
+{
+ cairo_skia_surface_t *surface = (cairo_skia_surface_t *) target;
+ cairo_skia_context_t *cr;
+
+ cr = _context_get ();
+ if (unlikely (cr == NULL)) {
+ cr = new cairo_skia_context_t;
+ if (unlikely (cr == NULL))
+ return _cairo_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ cr->path = new SkPath;
+ cr->paint = new SkPaint;
+ }
+
+ _cairo_init (&cr->base, &_cairo_skia_context_backend);
+
+ cr->source = NULL;
+ cr->source_image = NULL;
+
+ cr->target = (cairo_skia_surface_t *) cairo_surface_reference ((cairo_surface_t *) target);
+ cr->original = (cairo_skia_surface_t *) cairo_surface_reference ((cairo_surface_t *) target);
+ cr->canvas = new SkCanvas (*surface->bitmap);
+ cr->canvas->save ();
+
+ cairo_matrix_init_identity (&cr->matrix);
+
+ return &cr->base;
+}
+
+#if 0
+void
+_cairo_skia_context_set_SkPaint (cairo_t *cr, SkPaint paint)
+{
+ *cr->paint = paint;
+}
+
+void
+_cairo_skia_context_set_SkPath (cairo_t *cr, SkPath path)
+{
+ *cr->path = path;
+}
+#endif
diff --git a/src/skia/cairo-skia-private.h b/src/skia/cairo-skia-private.h
new file mode 100644
index 0000000..7e85613
--- /dev/null
+++ b/src/skia/cairo-skia-private.h
@@ -0,0 +1,109 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth at redhat.com>
+ */
+
+#ifndef CAIRO_SKIA_CONTEXT_PRIVATE_H
+#define CAIRO_SKIA_CONTEXT_PRIVATE_H
+
+#include "cairo-private.h"
+#include "cairo-image-surface-private.h"
+
+#include <SkBitmap.h>
+#include <SkCanvas.h>
+#include <SkPaint.h>
+#include <SkPath.h>
+
+typedef struct _cairo_skia_context cairo_skia_context_t;
+typedef struct _cairo_skia_surface cairo_skia_surface_t;
+
+struct _cairo_skia_context {
+ cairo_t base;
+
+ cairo_skia_surface_t *original;
+ cairo_skia_surface_t *target;
+
+ cairo_matrix_t matrix;
+
+ SkCanvas *canvas;
+ SkPaint *paint;
+ SkPath *path;
+
+ cairo_surface_t *source;
+ cairo_image_surface_t *source_image;
+ void *source_extra;
+};
+
+struct _cairo_skia_surface {
+ cairo_image_surface_t image;
+
+ SkBitmap *bitmap;
+};
+
+static inline bool
+format_to_sk_config (cairo_format_t format,
+ SkBitmap::Config& config,
+ bool& opaque)
+{
+ opaque = false;
+
+ switch (format) {
+ case CAIRO_FORMAT_ARGB32:
+ config = SkBitmap::kARGB_8888_Config;
+ break;
+ case CAIRO_FORMAT_RGB24:
+ config = SkBitmap::kARGB_8888_Config;
+ opaque = true;
+ break;
+ case CAIRO_FORMAT_RGB16_565:
+ config = SkBitmap::kRGB_565_Config;
+ opaque = true;
+ break;
+ case CAIRO_FORMAT_A8:
+ config = SkBitmap::kA8_Config;
+ break;
+ case CAIRO_FORMAT_A1:
+ config = SkBitmap::kA1_Config;
+ break;
+ case CAIRO_FORMAT_INVALID:
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+cairo_private cairo_t *
+_cairo_skia_context_create (void *target);
+
+#endif /* CAIRO_SKIA_CONTEXT_PRIVATE_H */
diff --git a/src/skia/cairo-skia-surface.cpp b/src/skia/cairo-skia-surface.cpp
new file mode 100644
index 0000000..95ec84d
--- /dev/null
+++ b/src/skia/cairo-skia-surface.cpp
@@ -0,0 +1,278 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2007 Mozilla Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Mozilla Corporation.
+ *
+ * Contributor(s):
+ * Vladimir Vukicevic <vladimir at mozilla.com>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-skia.h"
+#include "cairo-skia-private.h"
+
+#include "cairo-error-private.h"
+#include "cairo-foreign-context-private.h"
+
+static cairo_skia_surface_t *
+_cairo_skia_surface_create_internal (SkBitmap::Config config,
+ bool opaque,
+ unsigned char *data,
+ int width,
+ int height,
+ int stride);
+
+static cairo_surface_t *
+_cairo_skia_surface_create_similar (void *asurface,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
+ SkBitmap::Config config;
+ bool opaque;
+
+ if (content == surface->image.base.content)
+ {
+ config = surface->bitmap->getConfig ();
+ opaque = surface->bitmap->isOpaque ();
+ }
+ else if (! format_to_sk_config (_cairo_format_from_content (content),
+ config, opaque))
+ {
+ return NULL;
+ }
+
+ return &_cairo_skia_surface_create_internal (config, opaque,
+ NULL,
+ width, height,
+ 0)->image.base;
+}
+
+static cairo_status_t
+_cairo_skia_surface_finish (void *asurface)
+{
+ cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
+
+ delete surface->bitmap;
+
+ return _cairo_image_surface_finish (&surface->image);
+}
+
+static cairo_status_t
+_cairo_skia_surface_acquire_source_image (void *asurface,
+ cairo_image_surface_t **image_out,
+ void **image_extra)
+{
+ cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
+
+ surface->bitmap->lockPixels ();
+
+ *image_out = &surface->image;
+ *image_extra = NULL;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_skia_surface_release_source_image (void *asurface,
+ cairo_image_surface_t *image,
+ void *image_extra)
+{
+ cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
+
+ surface->bitmap->unlockPixels ();
+}
+
+static const struct _cairo_surface_backend
+cairo_skia_surface_backend = {
+ CAIRO_SURFACE_TYPE_SKIA,
+ _cairo_skia_context_create,
+
+ _cairo_skia_surface_create_similar,
+ _cairo_skia_surface_finish,
+ _cairo_skia_surface_acquire_source_image,
+ _cairo_skia_surface_release_source_image,
+
+ NULL, NULL,
+ NULL, /* clone similar */
+ NULL, /* composite */
+ NULL, /* fill_rectangles */
+ NULL, /* composite_trapezoids */
+ NULL, /* create_span_renderer */
+ NULL, /* check_span_renderer */
+
+ NULL, /* copy_page */
+ NULL, /* show_page */
+
+ _cairo_image_surface_get_extents,
+ NULL, /* old_show_glyphs */
+ _cairo_image_surface_get_font_options,
+ NULL, /* flush */
+ NULL, /* mark_dirty_rectangle */
+ NULL, /* scaled_font_fini */
+ NULL, /* scaled_glyph_fini */
+
+ /* XXX native surface functions */
+ _cairo_foreign_context_paint,
+ _cairo_foreign_context_mask,
+ _cairo_foreign_context_stroke,
+ _cairo_foreign_context_fill,
+ _cairo_foreign_context_glyphs
+};
+
+/*
+ * Surface constructors
+ */
+
+static inline pixman_format_code_t
+sk_config_to_pixman_format_code (SkBitmap::Config config,
+ bool opaque)
+{
+ switch (config) {
+ case SkBitmap::kARGB_8888_Config:
+ return opaque ? PIXMAN_x8r8g8b8 : PIXMAN_a8r8g8b8;
+
+ case SkBitmap::kA8_Config:
+ return PIXMAN_a8;
+
+ case SkBitmap::kA1_Config:
+ return PIXMAN_a1;
+ case SkBitmap::kRGB_565_Config:
+ return PIXMAN_r5g6b5;
+ case SkBitmap::kARGB_4444_Config:
+ return PIXMAN_a4r4g4b4;
+
+ case SkBitmap::kNo_Config:
+ case SkBitmap::kIndex8_Config:
+ case SkBitmap::kRLE_Index8_Config:
+ case SkBitmap::kConfigCount:
+ default:
+ ASSERT_NOT_REACHED;
+ return (pixman_format_code_t) -1;
+ }
+}
+static cairo_skia_surface_t *
+_cairo_skia_surface_create_internal (SkBitmap::Config config,
+ bool opaque,
+ unsigned char *data,
+ int width,
+ int height,
+ int stride)
+{
+ cairo_skia_surface_t *surface;
+ pixman_image_t *pixman_image;
+ pixman_format_code_t pixman_format;
+
+ surface = (cairo_skia_surface_t *) malloc (sizeof (cairo_skia_surface_t));
+ if (unlikely (surface == NULL))
+ return (cairo_skia_surface_t *) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ pixman_format = sk_config_to_pixman_format_code (config, opaque);
+ pixman_image = pixman_image_create_bits (pixman_format,
+ width, height,
+ (uint32_t *) data, stride);
+ if (unlikely (pixman_image == NULL))
+ return (cairo_skia_surface_t *) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ _cairo_surface_init (&surface->image.base,
+ &cairo_skia_surface_backend,
+ NULL, /* device */
+ _cairo_content_from_pixman_format (pixman_format));
+
+ _cairo_image_surface_init (&surface->image, pixman_image, pixman_format);
+
+ surface->bitmap = new SkBitmap;
+ surface->bitmap->setConfig (config, width, height, surface->image.stride);
+ surface->bitmap->setIsOpaque (opaque);
+ surface->bitmap->setPixels (surface->image.data);
+
+ surface->image.base.is_clear = data == NULL;
+
+ return surface;
+}
+
+cairo_surface_t *
+cairo_skia_surface_create (cairo_format_t format,
+ int width,
+ int height)
+{
+ SkBitmap::Config config;
+ bool opaque;
+
+ if (! CAIRO_FORMAT_VALID (format) ||
+ ! format_to_sk_config (format, config, opaque))
+ {
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+ }
+
+ return &_cairo_skia_surface_create_internal (config, opaque, NULL, width, height, 0)->image.base;
+}
+
+cairo_surface_t *
+cairo_skia_surface_create_for_data (unsigned char *data,
+ cairo_format_t format,
+ int width,
+ int height,
+ int stride)
+{
+ SkBitmap::Config config;
+ bool opaque;
+
+ if (! CAIRO_FORMAT_VALID (format) ||
+ ! format_to_sk_config (format, config, opaque))
+ {
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+ }
+
+ return &_cairo_skia_surface_create_internal (config, opaque, data, width, height, stride)->image.base;
+}
+
+/***
+
+Todo:
+
+*** Skia:
+
+- mask()
+
+*** Sk:
+
+High:
+- antialiased clipping?
+
+Medium:
+- implement clip path reset (to avoid restore/save)
+- implement complex radial patterns (2 centers and 2 radii)
+
+Low:
+- implement EXTEND_NONE
+
+***/
commit ecd345d68b74cc4dfbe377b219957907186d6fe8
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date: Wed Aug 3 14:11:49 2011 +0100
fast-image: perform an incremental stroke
Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>
diff --git a/src/cairo-fast-image-surface.c b/src/cairo-fast-image-surface.c
index 630607b..ab27c74 100644
--- a/src/cairo-fast-image-surface.c
+++ b/src/cairo-fast-image-surface.c
@@ -2820,7 +2820,7 @@ _composite_polygon (cairo_fast_image_surface_t *dst,
r->x + r->width,
r->y + r->height,
fill_rule);
- status = converter->add_polygon (converter, polygon);
+ status = _cairo_tor33_scan_converter_add_polygon (converter, polygon);
if (unlikely (status))
goto CLEANUP_CONVERTER;
}
@@ -2913,6 +2913,130 @@ _clip_and_composite_polygon (cairo_fast_image_surface_t *dst,
extents, need_bounded_clip (extents));
}
+struct fast_image_surface_incremental_stroke {
+ fast_image_span_renderer_t renderer;
+ cairo_scan_converter_t *converter;
+};
+
+static cairo_status_t
+fast_image_surface_add_triangle (void *closure,
+ const cairo_point_t triangle[3])
+{
+ struct fast_image_surface_incremental_stroke *stroker = closure;
+ cairo_int_status_t status;
+
+ status = _cairo_tor33_scan_converter_reset (stroker->converter);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_tor33_scan_converter_add_contour (stroker->converter,
+ triangle, 3, TRUE);
+ if (unlikely (status))
+ return status;
+
+ return stroker->converter->generate (stroker->converter,
+ &stroker->renderer.base);
+}
+
+static cairo_status_t
+fast_image_surface_add_triangle_fan (void *closure,
+ const cairo_point_t *midpt,
+ const cairo_point_t *points,
+ int npoints)
+{
+ struct fast_image_surface_incremental_stroke *stroker = closure;
+ cairo_int_status_t status;
+
+ status = _cairo_tor33_scan_converter_reset (stroker->converter);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_tor33_scan_converter_add_edge (stroker->converter,
+ midpt, points);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_tor33_scan_converter_add_contour (stroker->converter,
+ points, npoints, FALSE);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_tor33_scan_converter_add_edge (stroker->converter,
+ &points[npoints-1], midpt);
+ if (unlikely (status))
+ return status;
+
+ return stroker->converter->generate (stroker->converter,
+ &stroker->renderer.base);
+}
+
+static cairo_status_t
+fast_image_surface_add_convex_quad (void *closure,
+ const cairo_point_t quad[4])
+{
+ struct fast_image_surface_incremental_stroke *stroker = closure;
+ cairo_int_status_t status;
+
+ status = _cairo_tor33_scan_converter_reset (stroker->converter);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_tor33_scan_converter_add_contour (stroker->converter,
+ quad, 4, TRUE);
+ if (unlikely (status))
+ return status;
+
+ return stroker->converter->generate (stroker->converter,
+ &stroker->renderer.base);
+}
+
+static cairo_int_status_t
+fast_image_surface_incremental_stroke (cairo_fast_image_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_composite_rectangles_t *composite)
+{
+ struct fast_image_surface_incremental_stroke stroker;
+ const cairo_rectangle_int_t *r = &composite->bounded;
+ cairo_int_status_t status;
+
+ if (! composite->is_bounded)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (composite->clip->path)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = fast_image_span_renderer_init (&stroker.renderer,
+ surface, op, source, 1.,
+ composite, FALSE);
+ if (unlikely (status))
+ return status;
+
+ stroker.converter = _cairo_tor33_scan_converter_create (r->x, r->y,
+ r->x + r->width,
+ r->y + r->height,
+ CAIRO_FILL_RULE_WINDING);
+
+ status = _cairo_path_fixed_stroke_to_shaper (path, style,
+ ctm, ctm_inverse,
+ tolerance,
+ fast_image_surface_add_triangle,
+ fast_image_surface_add_triangle_fan,
+ fast_image_surface_add_convex_quad,
+ &stroker);
+
+ _cairo_image_span_renderer_fini (&stroker.renderer);
+ stroker.converter->destroy (stroker.converter);
+
+ return status;
+}
+
static cairo_int_status_t
_cairo_fast_image_surface_stroke (void *abstract_surface,
cairo_operator_t op,
@@ -2959,6 +3083,14 @@ _cairo_fast_image_surface_stroke (void *abstract_surface,
}
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ status = fast_image_surface_incremental_stroke (surface, op, source,
+ path, style,
+ ctm, ctm_inverse,
+ tolerance, antialias,
+ &composite);
+ }
+
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
cairo_polygon_t polygon;
cairo_box_t extents;
diff --git a/src/cairo-path-stroke.c b/src/cairo-path-stroke.c
index a164b28..a182e65 100644
--- a/src/cairo-path-stroke.c
+++ b/src/cairo-path-stroke.c
@@ -1223,8 +1223,8 @@ _cairo_stroker_close_path (void *closure)
return CAIRO_STATUS_SUCCESS;
}
-cairo_status_t
-_cairo_path_fixed_stroke_to_shaper (cairo_path_fixed_t *path,
+cairo_int_status_t
+_cairo_path_fixed_stroke_to_shaper (const cairo_path_fixed_t *path,
const cairo_stroke_style_t *stroke_style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
diff --git a/src/cairo-spans-private.h b/src/cairo-spans-private.h
index d35da4f..3acfd48 100644
--- a/src/cairo-spans-private.h
+++ b/src/cairo-spans-private.h
@@ -123,6 +123,20 @@ _cairo_clip_tor_scan_converter_create (cairo_clip_t *clip,
cairo_polygon_t *polygon,
cairo_fill_rule_t fill_rule);
+cairo_private cairo_status_t
+_cairo_tor33_scan_converter_add_edge (void *converter,
+ const cairo_point_t *p1,
+ const cairo_point_t *p2);
+
+cairo_private cairo_status_t
+_cairo_tor33_scan_converter_add_contour (void *converter,
+ const cairo_point_t *points,
+ int npoints,
+ cairo_bool_t close);
+
+cairo_private cairo_status_t
+_cairo_tor33_scan_converter_reset (void *converter);
+
typedef struct _cairo_rectangular_scan_converter {
cairo_scan_converter_t base;
diff --git a/src/cairo-tor33-scan-converter.c b/src/cairo-tor33-scan-converter.c
index 1141f11..3bee35a 100644
--- a/src/cairo-tor33-scan-converter.c
+++ b/src/cairo-tor33-scan-converter.c
@@ -999,6 +999,11 @@ polygon_reset (struct polygon *polygon,
pool_reset(polygon->edge_pool.base);
+ if (polygon->ymin == ymin && polygon->ymax == ymax) {
+ memset (polygon->y_buckets, 0, num_buckets * sizeof (struct edge *));
+ return CAIRO_STATUS_SUCCESS;
+ }
+
if (unlikely (h > 0x7FFFFFFFU - GRID_Y))
goto bail_no_mem; /* even if you could, you wouldn't want to. */
@@ -1893,40 +1898,6 @@ _cairo_tor33_scan_converter_destroy (void *converter)
}
static cairo_status_t
-_cairo_tor33_scan_converter_add_edge (void *converter,
- const cairo_point_t *p1,
- const cairo_point_t *p2,
- int top, int bottom,
- int dir)
-{
- cairo_tor33_scan_converter_t *self = converter;
- cairo_edge_t edge;
-
- edge.line.p1 = *p1;
- edge.line.p2 = *p2;
- edge.top = top;
- edge.bottom = bottom;
- edge.dir = dir;
-
- glitter_scan_converter_add_edge (self->converter, &edge);
- return CAIRO_STATUS_SUCCESS;
-}
-
-cairo_status_t
-_cairo_tor33_scan_converter_add_polygon (void *converter,
- const cairo_polygon_t *polygon)
-{
- cairo_tor33_scan_converter_t *self = converter;
- int i;
-
- for (i = 0; i < polygon->num_edges; i++)
- glitter_scan_converter_add_edge (self->converter,
- &polygon->edges[i]);
-
- return CAIRO_STATUS_SUCCESS;
-}
-
-static cairo_status_t
_cairo_tor33_scan_converter_generate (void *converter,
cairo_span_renderer_t *renderer)
{
@@ -1960,8 +1931,6 @@ _cairo_tor33_scan_converter_create (int xmin,
}
self->base.destroy = _cairo_tor33_scan_converter_destroy;
- self->base.add_edge = _cairo_tor33_scan_converter_add_edge;
- self->base.add_polygon = _cairo_tor33_scan_converter_add_polygon;
self->base.generate = _cairo_tor33_scan_converter_generate;
pool_init (self->span_pool.base, &self->jmp,
@@ -1983,3 +1952,84 @@ _cairo_tor33_scan_converter_create (int xmin,
bail_nomem:
return _cairo_scan_converter_create_in_error (status);
}
+
+cairo_status_t
+_cairo_tor33_scan_converter_add_edge (void *converter,
+ const cairo_point_t *p1,
+ const cairo_point_t *p2)
+{
+ cairo_tor33_scan_converter_t *self = converter;
+ cairo_edge_t edge;
+ int dir;
+
+ if (p1->y == p2->y)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (p1->y < p2->y) {
+ dir = 1;
+ } else {
+ const cairo_point_t *tmp = p1;
+ p1 = p2;
+ p2 = tmp;
+ dir = -1;
+ }
+
+ edge.line.p1 = *p1;
+ edge.line.p2 = *p2;
+ edge.top = p1->y;
+ edge.bottom = p2->y;
+ edge.dir = dir;
+
+ glitter_scan_converter_add_edge (self->converter, &edge);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_tor33_scan_converter_add_contour (void *converter,
+ const cairo_point_t *points,
+ int npoints,
+ cairo_bool_t close)
+{
+ cairo_status_t status;
+ int n;
+
+ for (n = 0; n < npoints-1; n++) {
+ status = _cairo_tor33_scan_converter_add_edge (converter,
+ &points[n], &points[n+1]);
+ if (unlikely (status))
+ return status;
+ }
+
+ status = CAIRO_STATUS_SUCCESS;
+ if (close)
+ status = _cairo_tor33_scan_converter_add_edge (converter,
+ &points[npoints-1],
+ &points[0]);
+ return status;
+}
+
+cairo_status_t
+_cairo_tor33_scan_converter_add_polygon (void *converter,
+ const cairo_polygon_t *polygon)
+{
+ cairo_tor33_scan_converter_t *self = converter;
+ int i;
+
+ for (i = 0; i < polygon->num_edges; i++)
+ glitter_scan_converter_add_edge (self->converter,
+ &polygon->edges[i]);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_tor33_scan_converter_reset (void *converter)
+{
+ cairo_tor33_scan_converter_t *self = converter;
+
+ active_list_reset(self->converter->active);
+ cell_list_reset(self->converter->coverages);
+ return polygon_reset(self->converter->polygon,
+ self->converter->ymin,
+ self->converter->ymax);
+}
diff --git a/src/cairoint.h b/src/cairoint.h
index 049fc92..4354e3f 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -1454,8 +1454,8 @@ _cairo_path_fixed_stroke_to_traps (const cairo_path_fixed_t *path,
double tolerance,
cairo_traps_t *traps);
-cairo_private cairo_status_t
-_cairo_path_fixed_stroke_to_shaper (cairo_path_fixed_t *path,
+cairo_private cairo_int_status_t
+_cairo_path_fixed_stroke_to_shaper (const cairo_path_fixed_t *path,
const cairo_stroke_style_t *stroke_style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
commit 995cfdf1802074181678a708061c9322f5daabfe
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date: Wed Aug 3 13:02:46 2011 +0100
fast-image: use a low-quality tor scan converter
Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>
diff --git a/src/Makefile.sources b/src/Makefile.sources
index 2a1a1a6..059f6ae 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -188,6 +188,7 @@ cairo_sources = \
cairo-surface-wrapper.c \
cairo-system.c \
cairo-tor-scan-converter.c \
+ cairo-tor33-scan-converter.c \
cairo-clip-tor-scan-converter.c \
cairo-toy-font-face.c \
cairo-traps.c \
diff --git a/src/cairo-fast-image-surface.c b/src/cairo-fast-image-surface.c
index b50ad80..630607b 100644
--- a/src/cairo-fast-image-surface.c
+++ b/src/cairo-fast-image-surface.c
@@ -2767,13 +2767,13 @@ _composite_spans (void *closure,
if (unlikely (status))
return status;
- converter = _cairo_tor_scan_converter_create (r->x, r->y,
- r->x + r->width,
- r->y + r->height,
- info->fill_rule);
+ converter = _cairo_tor33_scan_converter_create (r->x, r->y,
+ r->x + r->width,
+ r->y + r->height,
+ info->fill_rule);
- status= _cairo_tor_scan_converter_add_polygon (converter,
- info->polygon);
+ status= _cairo_tor33_scan_converter_add_polygon (converter,
+ info->polygon);
if (unlikely (status))
goto CLEANUP_CONVERTER;
@@ -2816,10 +2816,10 @@ _composite_polygon (cairo_fast_image_surface_t *dst,
polygon,
fill_rule);
} else {
- converter = _cairo_tor_scan_converter_create (r->x, r->y,
- r->x + r->width,
- r->y + r->height,
- fill_rule);
+ converter = _cairo_tor33_scan_converter_create (r->x, r->y,
+ r->x + r->width,
+ r->y + r->height,
+ fill_rule);
status = converter->add_polygon (converter, polygon);
if (unlikely (status))
goto CLEANUP_CONVERTER;
diff --git a/src/cairo-spans-private.h b/src/cairo-spans-private.h
index 45853f0..d35da4f 100644
--- a/src/cairo-spans-private.h
+++ b/src/cairo-spans-private.h
@@ -109,6 +109,16 @@ _cairo_tor_scan_converter_add_polygon (void *converter,
const cairo_polygon_t *polygon);
cairo_private cairo_scan_converter_t *
+_cairo_tor33_scan_converter_create (int xmin,
+ int ymin,
+ int xmax,
+ int ymax,
+ cairo_fill_rule_t fill_rule);
+cairo_private cairo_status_t
+_cairo_tor33_scan_converter_add_polygon (void *converter,
+ const cairo_polygon_t *polygon);
+
+cairo_private cairo_scan_converter_t *
_cairo_clip_tor_scan_converter_create (cairo_clip_t *clip,
cairo_polygon_t *polygon,
cairo_fill_rule_t fill_rule);
diff --git a/src/cairo-tor33-scan-converter.c b/src/cairo-tor33-scan-converter.c
new file mode 100644
index 0000000..1141f11
--- /dev/null
+++ b/src/cairo-tor33-scan-converter.c
@@ -0,0 +1,1985 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* glitter-paths - polygon scan converter
+ *
+ * Copyright (c) 2008 M Joonas Pihlaja
+ * Copyright (c) 2007 David Turner
+ *
+ * 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.
+ */
+/* This is the Glitter paths scan converter incorporated into cairo.
+ * The source is from commit 734c53237a867a773640bd5b64816249fa1730f8
+ * of
+ *
+ * http://gitweb.freedesktop.org/?p=users/joonas/glitter-paths
+ */
+/* Glitter-paths is a stand alone polygon rasteriser derived from
+ * David Turner's reimplementation of Tor Anderssons's 15x17
+ * supersampling rasteriser from the Apparition graphics library. The
+ * main new feature here is cheaply choosing per-scan line between
+ * doing fully analytical coverage computation for an entire row at a
+ * time vs. using a supersampling approach.
+ *
+ * David Turner's code can be found at
+ *
+ * http://david.freetype.org/rasterizer-shootout/raster-comparison-20070813.tar.bz2
+ *
+ * In particular this file incorporates large parts of ftgrays_tor10.h
+ * from raster-comparison-20070813.tar.bz2
+ */
+/* Overview
+ *
+ * A scan converter's basic purpose to take polygon edges and convert
+ * them into an RLE compressed A8 mask. This one works in two phases:
+ * gathering edges and generating spans.
+ *
+ * 1) As the user feeds the scan converter edges they are vertically
+ * clipped and bucketted into a _polygon_ data structure. The edges
+ * are also snapped from the user's coordinates to the subpixel grid
+ * coordinates used during scan conversion.
+ *
+ * user
+ * |
+ * | edges
+ * V
+ * polygon buckets
+ *
+ * 2) Generating spans works by performing a vertical sweep of pixel
+ * rows from top to bottom and maintaining an _active_list_ of edges
+ * that intersect the row. From the active list the fill rule
+ * determines which edges are the left and right edges of the start of
+ * each span, and their contribution is then accumulated into a pixel
+ * coverage list (_cell_list_) as coverage deltas. Once the coverage
+ * deltas of all edges are known we can form spans of constant pixel
+ * coverage by summing the deltas during a traversal of the cell list.
+ * At the end of a pixel row the cell list is sent to a coverage
+ * blitter for rendering to some target surface.
+ *
+ * The pixel coverages are computed by either supersampling the row
+ * and box filtering a mono rasterisation, or by computing the exact
+ * coverages of edges in the active list. The supersampling method is
+ * used whenever some edge starts or stops within the row or there are
+ * edge intersections in the row.
+ *
+ * polygon bucket for \
+ * current pixel row |
+ * | |
+ * | activate new edges | Repeat GRID_Y times if we
+ * V \ are supersampling this row,
+ * active list / or just once if we're computing
+ * | | analytical coverage.
+ * | coverage deltas |
+ * V |
+ * pixel coverage list /
+ * |
+ * V
+ * coverage blitter
+ */
+#include "cairoint.h"
+#include "cairo-spans-private.h"
+#include "cairo-error-private.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <setjmp.h>
+
+/*-------------------------------------------------------------------------
+ * cairo specific config
+ */
+#define I static
+
+/* Prefer cairo's status type. */
+#define GLITTER_HAVE_STATUS_T 1
+#define GLITTER_STATUS_SUCCESS CAIRO_STATUS_SUCCESS
+#define GLITTER_STATUS_NO_MEMORY CAIRO_STATUS_NO_MEMORY
+typedef cairo_status_t glitter_status_t;
+
+/* The input coordinate scale and the rasterisation grid scales. */
+#define GLITTER_INPUT_BITS CAIRO_FIXED_FRAC_BITS
+#define GRID_X_BITS 3
+#define GRID_Y_BITS 3
+
+/* Set glitter up to use a cairo span renderer to do the coverage
+ * blitting. */
+struct pool;
+struct cell_list;
+
+static glitter_status_t
+blit_with_span_renderer(
+ struct cell_list *coverages,
+ cairo_span_renderer_t *span_renderer,
+ struct pool *span_pool,
+ int y,
+ int height,
+ int xmin,
+ int xmax);
+
+#define GLITTER_BLIT_COVERAGES_ARGS \
+ cairo_span_renderer_t *span_renderer, \
+ struct pool *span_pool
+
+#define GLITTER_BLIT_COVERAGES(cells, y, height,xmin, xmax) do { \
+ blit_with_span_renderer (cells, \
+ span_renderer, \
+ span_pool, \
+ y, height, \
+ xmin, xmax); \
+} while (0)
+
+/*-------------------------------------------------------------------------
+ * glitter-paths.h
+ */
+
+/* "Input scaled" numbers are fixed precision reals with multiplier
+ * 2**GLITTER_INPUT_BITS. Input coordinates are given to glitter as
+ * pixel scaled numbers. These get converted to the internal grid
+ * scaled numbers as soon as possible. Internal overflow is possible
+ * if GRID_X/Y inside glitter-paths.c is larger than
+ * 1<<GLITTER_INPUT_BITS. */
+#ifndef GLITTER_INPUT_BITS
+# define GLITTER_INPUT_BITS 8
+#endif
+#define GLITTER_INPUT_SCALE (1<<GLITTER_INPUT_BITS)
+typedef int glitter_input_scaled_t;
+
+#if !GLITTER_HAVE_STATUS_T
+typedef enum {
+ GLITTER_STATUS_SUCCESS = 0,
+ GLITTER_STATUS_NO_MEMORY
+} glitter_status_t;
+#endif
+
+#ifndef I
+# define I /*static*/
+#endif
+
+/* Opaque type for scan converting. */
+typedef struct glitter_scan_converter glitter_scan_converter_t;
+
+/* Reset a scan converter to accept polygon edges and set the clip box
+ * in pixels. Allocates O(ymax-ymin) bytes of memory. The clip box
+ * is set to integer pixel coordinates xmin <= x < xmax, ymin <= y <
+ * ymax. */
+I glitter_status_t
+glitter_scan_converter_reset(
+ glitter_scan_converter_t *converter,
+ int xmin, int ymin,
+ int xmax, int ymax);
+
+/* Add a new polygon edge from pixel (x1,y1) to (x2,y2) to the scan
+ * converter. The coordinates represent pixel positions scaled by
+ * 2**GLITTER_PIXEL_BITS. If this function fails then the scan
+ * converter should be reset or destroyed. Dir must be +1 or -1,
+ * with the latter reversing the orientation of the edge. */
+I void
+glitter_scan_converter_add_edge (glitter_scan_converter_t *converter,
+ const cairo_edge_t *edge);
+
+/* Render the polygon in the scan converter to the given A8 format
+ * image raster. Only the pixels accessible as pixels[y*stride+x] for
+ * x,y inside the clip box are written to, where xmin <= x < xmax,
+ * ymin <= y < ymax. The image is assumed to be clear on input.
+ *
+ * If nonzero_fill is true then the interior of the polygon is
+ * computed with the non-zero fill rule. Otherwise the even-odd fill
+ * rule is used.
+ *
+ * The scan converter must be reset or destroyed after this call. */
+#ifndef GLITTER_BLIT_COVERAGES_ARGS
+# define GLITTER_BLIT_COVERAGES_ARGS unsigned char *raster_pixels, long raster_stride
+#endif
+I void
+glitter_scan_converter_render(
+ glitter_scan_converter_t *converter,
+ int nonzero_fill,
+ GLITTER_BLIT_COVERAGES_ARGS);
+
+/*-------------------------------------------------------------------------
+ * glitter-paths.c: Implementation internal types
+ */
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+/* All polygon coordinates are snapped onto a subsample grid. "Grid
+ * scaled" numbers are fixed precision reals with multiplier GRID_X or
+ * GRID_Y. */
+typedef int grid_scaled_t;
+typedef int grid_scaled_x_t;
+typedef int grid_scaled_y_t;
+
+/* Default x/y scale factors.
+ * You can either define GRID_X/Y_BITS to get a power-of-two scale
+ * or define GRID_X/Y separately. */
+#if !defined(GRID_X) && !defined(GRID_X_BITS)
+# define GRID_X_BITS 8
+#endif
+#if !defined(GRID_Y) && !defined(GRID_Y_BITS)
+# define GRID_Y 15
+#endif
+
+/* Use GRID_X/Y_BITS to define GRID_X/Y if they're available. */
+#ifdef GRID_X_BITS
+# define GRID_X (1 << GRID_X_BITS)
+#endif
+#ifdef GRID_Y_BITS
+# define GRID_Y (1 << GRID_Y_BITS)
+#endif
+
+/* The GRID_X_TO_INT_FRAC macro splits a grid scaled coordinate into
+ * integer and fractional parts. The integer part is floored. */
+#if defined(GRID_X_TO_INT_FRAC)
+ /* do nothing */
+#elif defined(GRID_X_BITS)
+# define GRID_X_TO_INT_FRAC(x, i, f) \
+ _GRID_TO_INT_FRAC_shift(x, i, f, GRID_X_BITS)
+#else
+# define GRID_X_TO_INT_FRAC(x, i, f) \
+ _GRID_TO_INT_FRAC_general(x, i, f, GRID_X)
+#endif
+
+#define _GRID_TO_INT_FRAC_general(t, i, f, m) do { \
+ (i) = (t) / (m); \
+ (f) = (t) % (m); \
+ if ((f) < 0) { \
+ --(i); \
+ (f) += (m); \
+ } \
+} while (0)
+
+#define _GRID_TO_INT_FRAC_shift(t, i, f, b) do { \
+ (f) = (t) & ((1 << (b)) - 1); \
+ (i) = (t) >> (b); \
+} while (0)
+
+/* A grid area is a real in [0,1] scaled by 2*GRID_X*GRID_Y. We want
+ * to be able to represent exactly areas of subpixel trapezoids whose
+ * vertices are given in grid scaled coordinates. The scale factor
+ * comes from needing to accurately represent the area 0.5*dx*dy of a
+ * triangle with base dx and height dy in grid scaled numbers. */
+typedef int grid_area_t;
+#define GRID_XY (2*GRID_X*GRID_Y) /* Unit area on the grid. */
+
+/* GRID_AREA_TO_ALPHA(area): map [0,GRID_XY] to [0,255]. */
+#if GRID_XY == 510
+# define GRID_AREA_TO_ALPHA(c) (((c)+1) >> 1)
+#elif GRID_XY == 255
+# define GRID_AREA_TO_ALPHA(c) (c)
+#elif GRID_XY == 64
+# define GRID_AREA_TO_ALPHA(c) (((c) << 2) | -(((c) & 0x40) >> 6))
+#elif GRID_XY == 128
+# define GRID_AREA_TO_ALPHA(c) ((((c) << 1) | -((c) >> 7)) & 255)
+#elif GRID_XY == 256
+# define GRID_AREA_TO_ALPHA(c) (((c) | -((c) >> 8)) & 255)
+#elif GRID_XY == 15
+# define GRID_AREA_TO_ALPHA(c) (((c) << 4) + (c))
+#elif GRID_XY == 2*256*15
+# define GRID_AREA_TO_ALPHA(c) (((c) + ((c)<<4) + 256) >> 9)
+#else
+# define GRID_AREA_TO_ALPHA(c) (((c)*255 + GRID_XY/2) / GRID_XY)
+#endif
+
+#define UNROLL3(x) x x x
+
+struct quorem {
+ int32_t quo;
+ int32_t rem;
+};
+
+/* Header for a chunk of memory in a memory pool. */
+struct _pool_chunk {
+ /* # bytes used in this chunk. */
+ size_t size;
+
+ /* # bytes total in this chunk */
+ size_t capacity;
+
+ /* Pointer to the previous chunk or %NULL if this is the sentinel
+ * chunk in the pool header. */
+ struct _pool_chunk *prev_chunk;
+
+ /* Actual data starts here. Well aligned for pointers. */
+};
+
+/* A memory pool. This is supposed to be embedded on the stack or
+ * within some other structure. It may optionally be followed by an
+ * embedded array from which requests are fulfilled until
+ * malloc needs to be called to allocate a first real chunk. */
+struct pool {
+ /* Chunk we're allocating from. */
+ struct _pool_chunk *current;
+
+ jmp_buf *jmp;
+
+ /* Free list of previously allocated chunks. All have >= default
+ * capacity. */
+ struct _pool_chunk *first_free;
+
+ /* The default capacity of a chunk. */
+ size_t default_capacity;
+
+ /* Header for the sentinel chunk. Directly following the pool
+ * struct should be some space for embedded elements from which
+ * the sentinel chunk allocates from. */
+ struct _pool_chunk sentinel[1];
+};
+
+/* A polygon edge. */
+struct edge {
+ /* Next in y-bucket or active list. */
+ struct edge *next;
+
+ /* Current x coordinate while the edge is on the active
+ * list. Initialised to the x coordinate of the top of the
+ * edge. The quotient is in grid_scaled_x_t units and the
+ * remainder is mod dy in grid_scaled_y_t units.*/
+ struct quorem x;
+
+ /* Advance of the current x when moving down a subsample line. */
+ struct quorem dxdy;
+
+ /* Advance of the current x when moving down a full pixel
+ * row. Only initialised when the height of the edge is large
+ * enough that there's a chance the edge could be stepped by a
+ * full row's worth of subsample rows at a time. */
+ struct quorem dxdy_full;
+
+ /* The clipped y of the top of the edge. */
+ grid_scaled_y_t ytop;
+
+ /* y2-y1 after orienting the edge downwards. */
+ grid_scaled_y_t dy;
+
+ /* Number of subsample rows remaining to scan convert of this
+ * edge. */
+ grid_scaled_y_t height_left;
+
+ /* Original sign of the edge: +1 for downwards, -1 for upwards
+ * edges. */
+ int dir;
+ int vertical;
+};
+
+#define EDGE_Y_BUCKET_INDEX(y, ymin) (((y) - (ymin))/GRID_Y)
+
+/* 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. */
+ grid_scaled_y_t ymin, ymax;
+
+ /* 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 {
+ struct pool base[1];
+ struct edge embedded[32];
+ } edge_pool;
+};
+
+/* A cell records the effect on pixel coverage of polygon edges
+ * passing through a pixel. It contains two accumulators of pixel
+ * coverage.
+ *
+ * Consider the effects of a polygon edge on the coverage of a pixel
+ * it intersects and that of the following one. The coverage of the
+ * following pixel is the height of the edge multiplied by the width
+ * of the pixel, and the coverage of the pixel itself is the area of
+ * the trapezoid formed by the edge and the right side of the pixel.
+ *
+ * +-----------------------+-----------------------+
+ * | | |
+ * | | |
+ * |_______________________|_______________________|
+ * | \...................|.......................|\
+ * | \..................|.......................| |
+ * | \.................|.......................| |
+ * | \....covered.....|.......................| |
+ * | \....area.......|.......................| } covered height
+ * | \..............|.......................| |
+ * |uncovered\.............|.......................| |
+ * | area \............|.......................| |
+ * |___________\...........|.......................|/
+ * | | |
+ * | | |
+ * | | |
+ * +-----------------------+-----------------------+
+ *
+ * Since the coverage of the following pixel will always be a multiple
+ * of the width of the pixel, we can store the height of the covered
+ * area instead. The coverage of the pixel itself is the total
+ * coverage minus the area of the uncovered area to the left of the
+ * edge. As it's faster to compute the uncovered area we only store
+ * that and subtract it from the total coverage later when forming
+ * spans to blit.
+ *
+ * The heights and areas are signed, with left edges of the polygon
+ * having positive sign and right edges having negative sign. When
+ * two edges intersect they swap their left/rightness so their
+ * contribution above and below the intersection point must be
+ * computed separately. */
+struct cell {
+ struct cell *next;
+ int x;
+ grid_area_t uncovered_area;
+ grid_scaled_y_t covered_height;
+};
+
+/* A cell list represents the scan line sparsely as cells ordered by
+ * ascending x. It is geared towards scanning the cells in order
+ * using an internal cursor. */
+struct cell_list {
+ /* Sentinel nodes */
+ struct cell head, tail;
+
+ /* Cursor state for iterating through the cell list. */
+ struct cell *cursor;
+
+ /* Cells in the cell list are owned by the cell list and are
+ * allocated from this pool. */
+ struct {
+ struct pool base[1];
+ struct cell embedded[32];
+ } cell_pool;
+};
+
+struct cell_pair {
+ struct cell *cell1;
+ struct cell *cell2;
+};
+
+/* The active list contains edges in the current scan line ordered by
+ * the x-coordinate of the intercept of the edge and the scan line. */
+struct active_list {
+ /* Leftmost edge on the current scan line. */
+ struct edge *head;
+
+ /* A lower bound on the height of the active edges is used to
+ * estimate how soon some active edge ends. We can't advance the
+ * scan conversion by a full pixel row if an edge ends somewhere
+ * within it. */
+ grid_scaled_y_t min_height;
+};
+
+struct glitter_scan_converter {
+ struct polygon polygon[1];
+ struct active_list active[1];
+ struct cell_list coverages[1];
+
+ /* Clip box. */
+ grid_scaled_x_t xmin, xmax;
+ grid_scaled_y_t ymin, ymax;
+};
+
+/* 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 struct _pool_chunk *
+_pool_chunk_init(
+ struct _pool_chunk *p,
+ struct _pool_chunk *prev_chunk,
+ size_t capacity)
+{
+ p->prev_chunk = prev_chunk;
+ p->size = 0;
+ p->capacity = capacity;
+ return p;
+}
+
+static struct _pool_chunk *
+_pool_chunk_create(struct pool *pool, size_t size)
+{
+ struct _pool_chunk *p;
+
+ p = malloc(size + sizeof(struct _pool_chunk));
+ if (unlikely (NULL == p))
+ longjmp (*pool->jmp, _cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ return _pool_chunk_init(p, pool->current, size);
+}
+
+static void
+pool_init(struct pool *pool,
+ jmp_buf *jmp,
+ size_t default_capacity,
+ size_t embedded_capacity)
+{
+ pool->jmp = jmp;
+ pool->current = pool->sentinel;
+ pool->first_free = NULL;
+ pool->default_capacity = default_capacity;
+ _pool_chunk_init(pool->sentinel, NULL, embedded_capacity);
+}
+
+static void
+pool_fini(struct pool *pool)
+{
+ struct _pool_chunk *p = pool->current;
+ do {
+ while (NULL != p) {
+ struct _pool_chunk *prev = p->prev_chunk;
+ if (p != pool->sentinel)
+ free(p);
+ p = prev;
+ }
+ p = pool->first_free;
+ pool->first_free = NULL;
+ } while (NULL != p);
+}
+
+/* Satisfy an allocation by first allocating a new large enough chunk
+ * and adding it to the head of the pool's chunk list. This function
+ * is called as a fallback if pool_alloc() couldn't do a quick
+ * allocation from the current chunk in the pool. */
+static void *
+_pool_alloc_from_new_chunk(
+ struct pool *pool,
+ size_t size)
+{
+ struct _pool_chunk *chunk;
+ void *obj;
+ size_t capacity;
+
+ /* If the allocation is smaller than the default chunk size then
+ * try getting a chunk off the free list. Force alloc of a new
+ * chunk for large requests. */
+ capacity = size;
+ chunk = NULL;
+ if (size < pool->default_capacity) {
+ capacity = pool->default_capacity;
+ chunk = pool->first_free;
+ if (chunk) {
+ pool->first_free = chunk->prev_chunk;
+ _pool_chunk_init(chunk, pool->current, chunk->capacity);
+ }
+ }
+
+ if (NULL == chunk)
+ chunk = _pool_chunk_create (pool, capacity);
+ pool->current = chunk;
+
+ obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size);
+ chunk->size += size;
+ return obj;
+}
+
+/* Allocate size bytes from the pool. The first allocated address
+ * returned from a pool is aligned to sizeof(void*). Subsequent
+ * addresses will maintain alignment as long as multiples of void* are
+ * allocated. Returns the address of a new memory area or %NULL on
+ * allocation failures. The pool retains ownership of the returned
+ * memory. */
+inline static void *
+pool_alloc (struct pool *pool, size_t size)
+{
+ struct _pool_chunk *chunk = pool->current;
+
+ if (size <= chunk->capacity - chunk->size) {
+ void *obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size);
+ chunk->size += size;
+ return obj;
+ } else {
+ return _pool_alloc_from_new_chunk(pool, size);
+ }
+}
+
+/* Relinquish all pool_alloced memory back to the pool. */
+static void
+pool_reset (struct pool *pool)
+{
+ /* Transfer all used chunks to the chunk free list. */
+ struct _pool_chunk *chunk = pool->current;
+ if (chunk != pool->sentinel) {
+ while (chunk->prev_chunk != pool->sentinel) {
+ chunk = chunk->prev_chunk;
+ }
+ chunk->prev_chunk = pool->first_free;
+ pool->first_free = pool->current;
+ }
+ /* Reset the sentinel as the current chunk. */
+ pool->current = pool->sentinel;
+ pool->sentinel->size = 0;
+}
+
+/* Rewinds the cell list's cursor to the beginning. After rewinding
+ * we're good to cell_list_find() the cell any x coordinate. */
+inline static void
+cell_list_rewind (struct cell_list *cells)
+{
+ cells->cursor = &cells->head;
+}
+
+/* Rewind the cell list if its cursor has been advanced past x. */
+inline static void
+cell_list_maybe_rewind (struct cell_list *cells, int x)
+{
+ struct cell *tail = cells->cursor;
+ if (tail->x > x)
+ cell_list_rewind (cells);
+}
+
+static void
+cell_list_init(struct cell_list *cells, jmp_buf *jmp)
+{
+ pool_init(cells->cell_pool.base, jmp,
+ 256*sizeof(struct cell),
+ sizeof(cells->cell_pool.embedded));
+ cells->tail.next = NULL;
+ cells->tail.x = INT_MAX;
+ cells->head.x = INT_MIN;
+ cells->head.next = &cells->tail;
+ cell_list_rewind (cells);
+}
+
+static void
+cell_list_fini(struct cell_list *cells)
+{
+ pool_fini (cells->cell_pool.base);
+}
+
+/* Empty the cell list. This is called at the start of every pixel
+ * row. */
+inline static void
+cell_list_reset (struct cell_list *cells)
+{
+ cell_list_rewind (cells);
+ cells->head.next = &cells->tail;
+ pool_reset (cells->cell_pool.base);
+}
+
+static struct cell *
+cell_list_alloc (struct cell_list *cells,
+ struct cell *tail,
+ int x)
+{
+ struct cell *cell;
+
+ cell = pool_alloc (cells->cell_pool.base, sizeof (struct cell));
+ cell->next = tail->next;
+ tail->next = cell;
+ cell->x = x;
+ cell->uncovered_area = 0;
+ cell->covered_height = 0;
+ return cell;
+}
+
+/* Find a cell at the given x-coordinate. Returns %NULL if a new cell
+ * needed to be allocated but couldn't be. Cells must be found with
+ * non-decreasing x-coordinate until the cell list is rewound using
+ * cell_list_rewind(). Ownership of the returned cell is retained by
+ * the cell list. */
+inline static struct cell *
+cell_list_find (struct cell_list *cells, int x)
+{
+ struct cell *tail = cells->cursor;
+
+ while (1) {
+ UNROLL3({
+ if (tail->next->x > x)
+ break;
+ tail = tail->next;
+ });
+ }
+
+ if (tail->x != x)
+ tail = cell_list_alloc (cells, tail, x);
+ return cells->cursor = tail;
+
+}
+
+/* Find two cells at x1 and x2. This is exactly equivalent
+ * to
+ *
+ * pair.cell1 = cell_list_find(cells, x1);
+ * pair.cell2 = cell_list_find(cells, x2);
+ *
+ * except with less function call overhead. */
+inline static struct cell_pair
+cell_list_find_pair(struct cell_list *cells, int x1, int x2)
+{
+ struct cell_pair pair;
+
+ pair.cell1 = cells->cursor;
+ while (1) {
+ UNROLL3({
+ if (pair.cell1->next->x > x1)
+ break;
+ pair.cell1 = pair.cell1->next;
+ });
+ }
+ if (pair.cell1->x != x1) {
+ struct cell *cell = pool_alloc (cells->cell_pool.base,
+ sizeof (struct cell));
+ cell->x = x1;
+ cell->uncovered_area = 0;
+ cell->covered_height = 0;
+ cell->next = pair.cell1->next;
+ pair.cell1->next = cell;
+ pair.cell1 = cell;
+ }
+
+ pair.cell2 = pair.cell1;
+ while (1) {
+ UNROLL3({
+ if (pair.cell2->next->x > x2)
+ break;
+ pair.cell2 = pair.cell2->next;
+ });
+ }
+ if (pair.cell2->x != x2) {
+ struct cell *cell = pool_alloc (cells->cell_pool.base,
+ sizeof (struct cell));
+ cell->uncovered_area = 0;
+ cell->covered_height = 0;
+ cell->x = x2;
+ cell->next = pair.cell2->next;
+ pair.cell2->next = cell;
+ pair.cell2 = cell;
+ }
+
+ cells->cursor = pair.cell2;
+ return pair;
+}
+
+/* Add an unbounded subpixel span covering subpixels >= x to the
+ * coverage cells. */
+static void
+cell_list_add_unbounded_subspan (struct cell_list *cells,
+ grid_scaled_x_t x)
+{
+ struct cell *cell;
+ int ix, fx;
+
+ GRID_X_TO_INT_FRAC(x, ix, fx);
+
+ cell = cell_list_find (cells, ix);
+ cell->uncovered_area += 2*fx;
+ cell->covered_height++;
+}
+
+/* Add a subpixel span covering [x1, x2) to the coverage cells. */
+inline static void
+cell_list_add_subspan(
+ struct cell_list *cells,
+ grid_scaled_x_t x1,
+ grid_scaled_x_t x2)
+{
+ int ix1, fx1;
+ int ix2, fx2;
+
+ GRID_X_TO_INT_FRAC(x1, ix1, fx1);
+ GRID_X_TO_INT_FRAC(x2, ix2, fx2);
+
+ if (ix1 != ix2) {
+ struct cell_pair p;
+ p = cell_list_find_pair(cells, ix1, ix2);
+ p.cell1->uncovered_area += 2*fx1;
+ ++p.cell1->covered_height;
+ p.cell2->uncovered_area -= 2*fx2;
+ --p.cell2->covered_height;
+ } else {
+ struct cell *cell = cell_list_find(cells, ix1);
+ cell->uncovered_area += 2*(fx1-fx2);
+ }
+}
+
+/* Adds the analytical coverage of an edge crossing the current pixel
+ * row to the coverage cells and advances the edge's x position to the
+ * following row.
+ *
+ * This function is only called when we know that during this pixel row:
+ *
+ * 1) The relative order of all edges on the active list doesn't
+ * change. In particular, no edges intersect within this row to pixel
+ * precision.
+ *
+ * 2) No new edges start in this row.
+ *
+ * 3) No existing edges end mid-row.
+ *
+ * This function depends on being called with all edges from the
+ * active list in the order they appear on the list (i.e. with
+ * non-decreasing x-coordinate.) */
+static void
+cell_list_render_edge(
+ struct cell_list *cells,
+ struct edge *edge,
+ int sign)
+{
+ grid_scaled_y_t y1, y2, dy;
+ grid_scaled_x_t dx;
+ int ix1, ix2;
+ grid_scaled_x_t fx1, fx2;
+
+ struct quorem x1 = edge->x;
+ struct quorem x2 = x1;
+
+ if (! edge->vertical) {
+ x2.quo += edge->dxdy_full.quo;
+ x2.rem += edge->dxdy_full.rem;
+ if (x2.rem >= 0) {
+ ++x2.quo;
+ x2.rem -= edge->dy;
+ }
+
+ edge->x = x2;
+ }
+
+ GRID_X_TO_INT_FRAC(x1.quo, ix1, fx1);
+ GRID_X_TO_INT_FRAC(x2.quo, ix2, fx2);
+
+ /* Edge is entirely within a column? */
+ if (ix1 == ix2) {
+ /* We always know that ix1 is >= the cell list cursor in this
+ * case due to the no-intersections precondition. */
+ struct cell *cell = cell_list_find(cells, ix1);
+ cell->covered_height += sign*GRID_Y;
+ cell->uncovered_area += sign*(fx1 + fx2)*GRID_Y;
+ return;
+ }
+
+ /* Orient the edge left-to-right. */
+ dx = x2.quo - x1.quo;
+ if (dx >= 0) {
+ y1 = 0;
+ y2 = GRID_Y;
+ } else {
+ int tmp;
+ tmp = ix1; ix1 = ix2; ix2 = tmp;
+ tmp = fx1; fx1 = fx2; fx2 = tmp;
+ dx = -dx;
+ sign = -sign;
+ y1 = GRID_Y;
+ y2 = 0;
+ }
+ dy = y2 - y1;
+
+ /* Add coverage for all pixels [ix1,ix2] on this row crossed
+ * by the edge. */
+ {
+ struct cell_pair pair;
+ struct quorem y = floored_divrem((GRID_X - fx1)*dy, dx);
+
+ /* When rendering a previous edge on the active list we may
+ * advance the cell list cursor past the leftmost pixel of the
+ * current edge even though the two edges don't intersect.
+ * e.g. consider two edges going down and rightwards:
+ *
+ * --\_+---\_+-----+-----+----
+ * \_ \_ | |
+ * | \_ | \_ | |
+ * | \_| \_| |
+ * | \_ \_ |
+ * ----+-----+-\---+-\---+----
+ *
+ * The left edge touches cells past the starting cell of the
+ * right edge. Fortunately such cases are rare.
+ *
+ * The rewinding is never necessary if the current edge stays
+ * within a single column because we've checked before calling
+ * this function that the active list order won't change. */
+ cell_list_maybe_rewind(cells, ix1);
+
+ pair = cell_list_find_pair(cells, ix1, ix1+1);
+ pair.cell1->uncovered_area += sign*y.quo*(GRID_X + fx1);
+ pair.cell1->covered_height += sign*y.quo;
+ y.quo += y1;
+
+ if (ix1+1 < ix2) {
+ struct quorem dydx_full = floored_divrem(GRID_X*dy, dx);
+ struct cell *cell = pair.cell2;
+
+ ++ix1;
+ do {
+ grid_scaled_y_t y_skip = dydx_full.quo;
+ y.rem += dydx_full.rem;
+ if (y.rem >= dx) {
+ ++y_skip;
+ y.rem -= dx;
+ }
+
+ y.quo += y_skip;
+
+ y_skip *= sign;
+ cell->uncovered_area += y_skip*GRID_X;
+ cell->covered_height += y_skip;
+
+ ++ix1;
+ cell = cell_list_find(cells, ix1);
+ } while (ix1 != ix2);
+
+ pair.cell2 = cell;
+ }
+ pair.cell2->uncovered_area += sign*(y2 - y.quo)*fx2;
+ pair.cell2->covered_height += sign*(y2 - y.quo);
+ }
+}
+
+static void
+polygon_init (struct polygon *polygon, jmp_buf *jmp)
+{
+ polygon->ymin = polygon->ymax = 0;
+ polygon->y_buckets = polygon->y_buckets_embedded;
+ pool_init (polygon->edge_pool.base, jmp,
+ 8192 - sizeof (struct _pool_chunk),
+ sizeof (polygon->edge_pool.embedded));
+}
+
+static void
+polygon_fini (struct polygon *polygon)
+{
+ if (polygon->y_buckets != polygon->y_buckets_embedded)
+ free (polygon->y_buckets);
+
+ pool_fini (polygon->edge_pool.base);
+}
+
+/* Empties the polygon of all edges. The polygon is then prepared to
+ * receive new edges and clip them to the vertical range
+ * [ymin,ymax). */
+static glitter_status_t
+polygon_reset (struct polygon *polygon,
+ grid_scaled_y_t ymin,
+ grid_scaled_y_t ymax)
+{
+ unsigned h = ymax - ymin;
+ unsigned num_buckets = EDGE_Y_BUCKET_INDEX(ymax + GRID_Y-1, ymin);
+
+ pool_reset(polygon->edge_pool.base);
+
+ if (unlikely (h > 0x7FFFFFFFU - GRID_Y))
+ goto bail_no_mem; /* even if you could, you wouldn't want to. */
+
+ if (polygon->y_buckets != polygon->y_buckets_embedded)
+ free (polygon->y_buckets);
+
+ polygon->y_buckets = polygon->y_buckets_embedded;
+ if (num_buckets > ARRAY_LENGTH (polygon->y_buckets_embedded)) {
+ polygon->y_buckets = _cairo_malloc_ab (num_buckets,
+ sizeof (struct edge *));
+ if (unlikely (NULL == polygon->y_buckets))
+ goto bail_no_mem;
+ }
+ memset (polygon->y_buckets, 0, num_buckets * sizeof (struct edge *));
+
+ polygon->ymin = ymin;
+ polygon->ymax = ymax;
+ return GLITTER_STATUS_SUCCESS;
+
+ bail_no_mem:
+ polygon->ymin = 0;
+ polygon->ymax = 0;
+ return GLITTER_STATUS_NO_MEMORY;
+}
+
+static void
+_polygon_insert_edge_into_its_y_bucket(
+ struct polygon *polygon,
+ struct edge *e)
+{
+ unsigned ix = EDGE_Y_BUCKET_INDEX(e->ytop, polygon->ymin);
+ struct edge **ptail = &polygon->y_buckets[ix];
+ e->next = *ptail;
+ *ptail = e;
+}
+
+inline static void
+polygon_add_edge (struct polygon *polygon,
+ const cairo_edge_t *edge)
+{
+ struct edge *e;
+ grid_scaled_x_t dx;
+ grid_scaled_y_t dy;
+ grid_scaled_y_t ytop, ybot;
+ grid_scaled_y_t ymin = polygon->ymin;
+ grid_scaled_y_t ymax = polygon->ymax;
+
+ assert (edge->bottom > edge->top);
+
+ if (unlikely (edge->top >= ymax || edge->bottom <= ymin))
+ return;
+
+ e = pool_alloc (polygon->edge_pool.base, sizeof (struct edge));
+
+ dx = edge->line.p2.x - edge->line.p1.x;
+ dy = edge->line.p2.y - edge->line.p1.y;
+ e->dy = dy;
+ e->dir = edge->dir;
+
+ ytop = edge->top >= ymin ? edge->top : ymin;
+ ybot = edge->bottom <= ymax ? edge->bottom : ymax;
+ e->ytop = ytop;
+ e->height_left = ybot - ytop;
+
+ 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->dxdy_full.quo = 0;
+ e->dxdy_full.rem = 0;
+ } else {
+ e->vertical = FALSE;
+ e->dxdy = floored_divrem (dx, dy);
+ if (ytop == edge->line.p1.y) {
+ e->x.quo = edge->line.p1.x;
+ e->x.rem = 0;
+ } else {
+ e->x = floored_muldivrem (ytop - edge->line.p1.y, dx, dy);
+ e->x.quo += edge->line.p1.x;
+ }
+
+ if (e->height_left >= GRID_Y) {
+ e->dxdy_full = floored_muldivrem (GRID_Y, dx, dy);
+ } else {
+ e->dxdy_full.quo = 0;
+ e->dxdy_full.rem = 0;
+ }
+ }
+
+ _polygon_insert_edge_into_its_y_bucket (polygon, e);
+
+ e->x.rem -= dy; /* Bias the remainder for faster
+ * edge advancement. */
+}
+
+static void
+active_list_reset (struct active_list *active)
+{
+ active->head = NULL;
+ active->min_height = 0;
+}
+
+static void
+active_list_init(struct active_list *active)
+{
+ active_list_reset(active);
+}
+
+/*
+ * Merge two sorted edge lists.
+ * Input:
+ * - head_a: The head of the first list.
+ * - head_b: The head of the second list; head_b cannot be NULL.
+ * Output:
+ * Returns the head of the merged list.
+ *
+ * Implementation notes:
+ * To make it fast (in particular, to reduce to an insertion sort whenever
+ * one of the two input lists only has a single element) we iterate through
+ * a list until its head becomes greater than the head of the other list,
+ * then we switch their roles. As soon as one of the two lists is empty, we
+ * just attach the other one to the current list and exit.
+ * Writes to memory are only needed to "switch" lists (as it also requires
+ * attaching to the output list the list which we will be iterating next) and
+ * to attach the last non-empty list.
+ */
+static struct edge *
+merge_sorted_edges (struct edge *head_a, struct edge *head_b)
+{
+ struct edge *head, **next;
+ int32_t x;
+
+ if (head_a == NULL)
+ return head_b;
+
+ next = &head;
+ if (head_a->x.quo <= head_b->x.quo) {
+ head = head_a;
+ } else {
+ head = head_b;
+ goto start_with_b;
+ }
+
+ do {
+ x = head_b->x.quo;
+ while (head_a != NULL && head_a->x.quo <= x) {
+ next = &head_a->next;
+ head_a = head_a->next;
+ }
+
+ *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) {
+ next = &head_b->next;
+ head_b = head_b->next;
+ }
+
+ *next = head_a;
+ if (head_b == NULL)
+ return head;
+ } while (1);
+}
+
+/*
+ * Sort (part of) a list.
+ * Input:
+ * - list: The list to be sorted; list cannot be NULL.
+ * - limit: Recursion limit.
+ * Output:
+ * - head_out: The head of the sorted list containing the first 2^(level+1) elements of the
+ * input list; if the input list has fewer elements, head_out be a sorted list
+ * containing all the elements of the input list.
+ * Returns the head of the list of unprocessed elements (NULL if the sorted list contains
+ * all the elements of the input list).
+ *
+ * Implementation notes:
+ * Special case single element list, unroll/inline the sorting of the first two elements.
+ * Some tail recursion is used since we iterate on the bottom-up solution of the problem
+ * (we start with a small sorted list and keep merging other lists of the same size to it).
+ */
+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;
+
+ /* Single element list -> return */
+ if (head_other == NULL) {
+ *head_out = list;
+ return NULL;
+ }
+
+ /* Unroll the first iteration of the following loop (halves the number of calls to merge_sorted_edges):
+ * - Initialize remaining to be the list containing the elements after the second in the input list.
+ * - Initialize *head_out to be the sorted list containing the first two element.
+ */
+ remaining = head_other->next;
+ if (list->x.quo <= head_other->x.quo) {
+ *head_out = list;
+ /* list->next = head_other; */ /* The input list is already like this. */
+ head_other->next = NULL;
+ } else {
+ *head_out = head_other;
+ head_other->next = list;
+ list->next = NULL;
+ }
+
+ for (i = 0; i < level && remaining; i++) {
+ /* Extract a sorted list of the same size as *head_out
+ * (2^(i+1) elements) from the list of remaining elements. */
+ remaining = sort_edges (remaining, i, &head_other);
+ *head_out = merge_sorted_edges (*head_out, head_other);
+ }
+
+ /* *head_out now contains (at most) 2^(level+1) elements. */
+
+ return remaining;
+}
+
+/* Test if the edges on the active list can be safely advanced by a
+ * full row without intersections or any edges ending. */
+inline static int
+active_list_can_step_full_row (struct active_list *active)
+{
+ const struct edge *e;
+ int prev_x = INT_MIN;
+
+ /* Recomputes the minimum height of all edges on the active
+ * list if we have been dropping edges. */
+ if (active->min_height <= 0) {
+ int min_height = INT_MAX;
+
+ e = active->head;
+ while (NULL != e) {
+ if (e->height_left < min_height)
+ min_height = e->height_left;
+ e = e->next;
+ }
+
+ active->min_height = min_height;
+ }
+
+ if (active->min_height < GRID_Y)
+ return 0;
+
+ /* Check for intersections as no edges end during the next row. */
+ e = active->head;
+ while (NULL != e) {
+ struct quorem x = e->x;
+
+ if (! e->vertical) {
+ x.quo += e->dxdy_full.quo;
+ x.rem += e->dxdy_full.rem;
+ if (x.rem >= 0)
+ ++x.quo;
+ }
+
+ if (x.quo <= prev_x)
+ return 0;
+
+ prev_x = x.quo;
+ e = e->next;
+ }
+
+ return 1;
+}
+
+/* Merges edges on the given subpixel row from the polygon to the
+ * active_list. */
+inline static void
+active_list_merge_edges_from_polygon(struct active_list *active,
+ struct edge **ptail,
+ grid_scaled_y_t y,
+ struct polygon *polygon)
+{
+ /* Split off the edges on the current subrow and merge them into
+ * the active list. */
+ int min_height = active->min_height;
+ struct edge *subrow_edges = NULL;
+ struct edge *tail = *ptail;
+
+ do {
+ struct edge *next = tail->next;
+
+ if (y == tail->ytop) {
+ tail->next = subrow_edges;
+ subrow_edges = tail;
+
+ if (tail->height_left < min_height)
+ min_height = tail->height_left;
+
+ *ptail = next;
+ } else
+ ptail = &tail->next;
+
+ tail = next;
+ } while (tail);
+
+ if (subrow_edges) {
+ sort_edges (subrow_edges, UINT_MAX, &subrow_edges);
+ active->head = merge_sorted_edges (active->head, subrow_edges);
+ active->min_height = min_height;
+ }
+}
+
+/* Advance the edges on the active list by one subsample row by
+ * updating their x positions. Drop edges from the list that end. */
+inline static void
+active_list_substep_edges(struct active_list *active)
+{
+ struct edge **cursor = &active->head;
+ grid_scaled_x_t prev_x = INT_MIN;
+ struct edge *unsorted = NULL;
+ struct edge *edge = *cursor;
+
+ do {
+ UNROLL3({
+ struct edge *next;
+
+ if (NULL == edge)
+ break;
+
+ next = edge->next;
+ if (--edge->height_left) {
+ 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) {
+ *cursor = next;
+ edge->next = unsorted;
+ unsorted = edge;
+ } else {
+ prev_x = edge->x.quo;
+ cursor = &edge->next;
+ }
+ } else {
+ *cursor = next;
+ }
+ edge = next;
+ })
+ } while (1);
+
+ if (unsorted) {
+ sort_edges (unsorted, UINT_MAX, &unsorted);
+ active->head = merge_sorted_edges (active->head, unsorted);
+ }
+}
+
+inline static void
+apply_nonzero_fill_rule_for_subrow (struct active_list *active,
+ struct cell_list *coverages)
+{
+ struct edge *edge = active->head;
+ int winding = 0;
+ int xstart;
+ int xend;
+
+ cell_list_rewind (coverages);
+
+ while (NULL != edge) {
+ xstart = edge->x.quo;
+ winding = edge->dir;
+ while (1) {
+ edge = edge->next;
+ if (NULL == edge)
+ return cell_list_add_unbounded_subspan (coverages, xstart);
+
+ winding += edge->dir;
+ if (0 == winding) {
+ if (edge->next == NULL || edge->next->x.quo != edge->x.quo)
+ break;
+ }
+ }
+
+ xend = edge->x.quo;
+ cell_list_add_subspan (coverages, xstart, xend);
+
+ edge = edge->next;
+ }
+}
+
+static void
+apply_evenodd_fill_rule_for_subrow (struct active_list *active,
+ struct cell_list *coverages)
+{
+ struct edge *edge = active->head;
+ int xstart;
+ int xend;
+
+ cell_list_rewind (coverages);
+
+ while (NULL != edge) {
+ xstart = edge->x.quo;
+
+ while (1) {
+ edge = edge->next;
+ if (NULL == edge) {
+ cell_list_add_unbounded_subspan (coverages, xstart);
+ return;
+ }
+
+ if (edge->next == NULL || edge->next->x.quo != edge->x.quo)
+ break;
+
+ edge = edge->next;
+ }
+
+ xend = edge->x.quo;
+ cell_list_add_subspan (coverages, xstart, xend);
+
+ edge = edge->next;
+ }
+}
+
+static void
+apply_nonzero_fill_rule_and_step_edges (struct active_list *active,
+ struct cell_list *coverages)
+{
+ struct edge **cursor = &active->head;
+ struct edge *left_edge;
+
+ left_edge = *cursor;
+ while (NULL != left_edge) {
+ struct edge *right_edge;
+ int winding = left_edge->dir;
+
+ left_edge->height_left -= GRID_Y;
+ if (left_edge->height_left)
+ cursor = &left_edge->next;
+ else
+ *cursor = left_edge->next;
+
+ while (1) {
+ right_edge = *cursor;
+ if (NULL == right_edge) {
+ cell_list_render_edge (coverages, left_edge, +1);
+ return;
+ }
+
+ right_edge->height_left -= GRID_Y;
+ if (right_edge->height_left)
+ cursor = &right_edge->next;
+ else
+ *cursor = right_edge->next;
+
+ winding += right_edge->dir;
+ if (0 == winding) {
+ if (right_edge->next == NULL ||
+ right_edge->next->x.quo != right_edge->x.quo)
+ {
+ break;
+ }
+ }
+
+ if (! right_edge->vertical) {
+ right_edge->x.quo += right_edge->dxdy_full.quo;
+ right_edge->x.rem += right_edge->dxdy_full.rem;
+ if (right_edge->x.rem >= 0) {
+ ++right_edge->x.quo;
+ right_edge->x.rem -= right_edge->dy;
+ }
+ }
+ }
+
+ cell_list_render_edge (coverages, left_edge, +1);
+ cell_list_render_edge (coverages, right_edge, -1);
+
+ left_edge = *cursor;
+ }
+}
+
+static void
+apply_evenodd_fill_rule_and_step_edges (struct active_list *active,
+ struct cell_list *coverages)
+{
+ struct edge **cursor = &active->head;
+ struct edge *left_edge;
+
+ left_edge = *cursor;
+ while (NULL != left_edge) {
+ struct edge *right_edge;
+
+ left_edge->height_left -= GRID_Y;
+ if (left_edge->height_left)
+ cursor = &left_edge->next;
+ else
+ *cursor = left_edge->next;
+
+ while (1) {
+ right_edge = *cursor;
+ if (NULL == right_edge) {
+ cell_list_render_edge (coverages, left_edge, +1);
+ return;
+ }
+
+ right_edge->height_left -= GRID_Y;
+ if (right_edge->height_left)
+ cursor = &right_edge->next;
+ else
+ *cursor = right_edge->next;
+
+ if (right_edge->next == NULL ||
+ right_edge->next->x.quo != right_edge->x.quo)
+ {
+ break;
+ }
+
+ if (! right_edge->vertical) {
+ right_edge->x.quo += right_edge->dxdy_full.quo;
+ right_edge->x.rem += right_edge->dxdy_full.rem;
+ if (right_edge->x.rem >= 0) {
+ ++right_edge->x.quo;
+ right_edge->x.rem -= right_edge->dy;
+ }
+ }
+ }
+
+ cell_list_render_edge (coverages, left_edge, +1);
+ cell_list_render_edge (coverages, right_edge, -1);
+
+ left_edge = *cursor;
+ }
+}
+
+static void
+_glitter_scan_converter_init(glitter_scan_converter_t *converter, jmp_buf *jmp)
+{
+ polygon_init(converter->polygon, jmp);
+ active_list_init(converter->active);
+ cell_list_init(converter->coverages, jmp);
+ converter->xmin=0;
+ converter->ymin=0;
+ converter->xmax=0;
+ converter->ymax=0;
+}
+
+static void
+_glitter_scan_converter_fini(glitter_scan_converter_t *converter)
+{
+ polygon_fini(converter->polygon);
+ cell_list_fini(converter->coverages);
+ converter->xmin=0;
+ converter->ymin=0;
+ converter->xmax=0;
+ converter->ymax=0;
+}
+
+static grid_scaled_t
+int_to_grid_scaled(int i, int scale)
+{
+ /* Clamp to max/min representable scaled number. */
+ if (i >= 0) {
+ if (i >= INT_MAX/scale)
+ i = INT_MAX/scale;
+ }
+ else {
+ if (i <= INT_MIN/scale)
+ i = INT_MIN/scale;
+ }
+ return i*scale;
+}
+
+#define int_to_grid_scaled_x(x) int_to_grid_scaled((x), GRID_X)
+#define int_to_grid_scaled_y(x) int_to_grid_scaled((x), GRID_Y)
+
+I glitter_status_t
+glitter_scan_converter_reset(
+ glitter_scan_converter_t *converter,
+ int xmin, int ymin,
+ int xmax, int ymax)
+{
+ glitter_status_t status;
+
+ converter->xmin = 0; converter->xmax = 0;
+ converter->ymin = 0; converter->ymax = 0;
+
+ xmin = int_to_grid_scaled_x(xmin);
+ ymin = int_to_grid_scaled_y(ymin);
+ xmax = int_to_grid_scaled_x(xmax);
+ ymax = int_to_grid_scaled_y(ymax);
+
+ active_list_reset(converter->active);
+ cell_list_reset(converter->coverages);
+ status = polygon_reset(converter->polygon, ymin, ymax);
+ if (status)
+ return status;
+
+ converter->xmin = xmin;
+ converter->xmax = xmax;
+ converter->ymin = ymin;
+ converter->ymax = ymax;
+ return GLITTER_STATUS_SUCCESS;
+}
+
+/* INPUT_TO_GRID_X/Y (in_coord, out_grid_scaled, grid_scale)
+ * These macros convert an input coordinate in the client's
+ * device space to the rasterisation grid.
+ */
+/* Gah.. this bit of ugly defines INPUT_TO_GRID_X/Y so as to use
+ * shifts if possible, and something saneish if not.
+ */
+#if !defined(INPUT_TO_GRID_Y) && defined(GRID_Y_BITS) && GRID_Y_BITS <= GLITTER_INPUT_BITS
+# define INPUT_TO_GRID_Y(in, out) (out) = (in) >> (GLITTER_INPUT_BITS - GRID_Y_BITS)
+#else
+# define INPUT_TO_GRID_Y(in, out) INPUT_TO_GRID_general(in, out, GRID_Y)
+#endif
+
+#if !defined(INPUT_TO_GRID_X) && defined(GRID_X_BITS) && GRID_X_BITS <= GLITTER_INPUT_BITS
+# define INPUT_TO_GRID_X(in, out) (out) = (in) >> (GLITTER_INPUT_BITS - GRID_X_BITS)
+#else
+# define INPUT_TO_GRID_X(in, out) INPUT_TO_GRID_general(in, out, GRID_X)
+#endif
+
+#define INPUT_TO_GRID_general(in, out, grid_scale) do { \
+ long long tmp__ = (long long)(grid_scale) * (in); \
+ tmp__ >>= GLITTER_INPUT_BITS; \
+ (out) = tmp__; \
+} while (0)
+
+I void
+glitter_scan_converter_add_edge (glitter_scan_converter_t *converter,
+ const cairo_edge_t *edge)
+{
+ cairo_edge_t e;
+
+ INPUT_TO_GRID_Y (edge->top, e.top);
+ INPUT_TO_GRID_Y (edge->bottom, e.bottom);
+ if (e.top >= e.bottom)
+ return;
+
+ /* XXX: possible overflows if GRID_X/Y > 2**GLITTER_INPUT_BITS */
+ INPUT_TO_GRID_Y (edge->line.p1.y, e.line.p1.y);
+ INPUT_TO_GRID_Y (edge->line.p2.y, e.line.p2.y);
+ if (e.line.p1.y == e.line.p2.y)
+ return;
+
+ INPUT_TO_GRID_X (edge->line.p1.x, e.line.p1.x);
+ INPUT_TO_GRID_X (edge->line.p2.x, e.line.p2.x);
+
+ e.dir = edge->dir;
+
+ polygon_add_edge (converter->polygon, &e);
+}
+
+static cairo_bool_t
+active_list_is_vertical (struct active_list *active)
+{
+ struct edge *e;
+
+ for (e = active->head; e != NULL; e = e->next) {
+ if (! e->vertical)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+step_edges (struct active_list *active, int count)
+{
+ struct edge **cursor = &active->head;
+ struct edge *edge;
+
+ for (edge = *cursor; edge != NULL; edge = *cursor) {
+ edge->height_left -= GRID_Y * count;
+ if (edge->height_left)
+ cursor = &edge->next;
+ else
+ *cursor = edge->next;
+ }
+}
+
+I void
+glitter_scan_converter_render(
+ glitter_scan_converter_t *converter,
+ int nonzero_fill,
+ GLITTER_BLIT_COVERAGES_ARGS)
+{
+ int i, j;
+ int ymax_i = converter->ymax / GRID_Y;
+ int ymin_i = converter->ymin / GRID_Y;
+ int xmin_i, xmax_i;
+ int h = ymax_i - ymin_i;
+ struct polygon *polygon = converter->polygon;
+ struct cell_list *coverages = converter->coverages;
+ struct active_list *active = converter->active;
+
+ xmin_i = converter->xmin / GRID_X;
+ xmax_i = converter->xmax / GRID_X;
+ if (xmin_i >= xmax_i)
+ return;
+
+ /* Render each pixel row. */
+ for (i = 0; i < h; i = j) {
+ int do_full_step = 0;
+
+ j = i + 1;
+
+ /* Determine if we can ignore this row or use the full pixel
+ * stepper. */
+ if (! polygon->y_buckets[i]) {
+ if (! active->head) {
+ for (; j < h && ! polygon->y_buckets[j]; j++)
+ ;
+ continue;
+ }
+
+ do_full_step = active_list_can_step_full_row (active);
+ }
+
+ if (do_full_step) {
+ /* Step by a full pixel row's worth. */
+ if (nonzero_fill)
+ apply_nonzero_fill_rule_and_step_edges (active, coverages);
+ else
+ apply_evenodd_fill_rule_and_step_edges (active, coverages);
+
+ if (active_list_is_vertical (active)) {
+ while (j < h &&
+ polygon->y_buckets[j] == NULL &&
+ active->min_height >= 2*GRID_Y)
+ {
+ active->min_height -= GRID_Y;
+ j++;
+ }
+ if (j != i + 1)
+ step_edges (active, j - (i + 1));
+ }
+ } else {
+ grid_scaled_y_t suby;
+
+ /* Subsample this row. */
+ for (suby = 0; suby < GRID_Y; suby++) {
+ grid_scaled_y_t y = (i+ymin_i)*GRID_Y + suby;
+
+ if (polygon->y_buckets[i]) {
+ active_list_merge_edges_from_polygon (active,
+ &polygon->y_buckets[i], y,
+ polygon);
+ }
+
+ if (nonzero_fill)
+ apply_nonzero_fill_rule_for_subrow (active, coverages);
+ else
+ apply_evenodd_fill_rule_for_subrow (active, coverages);
+
+ active_list_substep_edges(active);
+ }
+ }
+
+ GLITTER_BLIT_COVERAGES(coverages, i+ymin_i, j-i, xmin_i, xmax_i);
+ cell_list_reset (coverages);
+
+ if (! active->head)
+ active->min_height = INT_MAX;
+ else
+ active->min_height -= GRID_Y;
+ }
+}
+
+/*-------------------------------------------------------------------------
+ * cairo specific implementation: the coverage blitter and
+ * scan converter subclass. */
+
+static glitter_status_t
+blit_with_span_renderer (struct cell_list *cells,
+ cairo_span_renderer_t *renderer,
+ struct pool *span_pool,
+ int y, int height,
+ int xmin, int xmax)
+{
+ struct cell *cell = cells->head.next;
+ int prev_x = xmin, last_x = -1;
+ int cover = 0, last_cover = 0;
+ cairo_half_open_span_t *spans;
+ unsigned num_spans;
+
+ if (cell == &cells->tail)
+ return CAIRO_STATUS_SUCCESS;
+
+ /* Skip cells to the left of the clip region. */
+ while (cell->x < xmin) {
+ cover += cell->covered_height;
+ cell = cell->next;
+ }
+ cover *= GRID_X*2;
+
+ /* Count number of cells remaining. */
+ {
+ struct cell *next = cell;
+ num_spans = 2;
+ while (next->x < xmax) {
+ next = next->next;
+ ++num_spans;
+ }
+ num_spans = 2*num_spans;
+ }
+
+ /* Allocate enough spans for the row. */
+ pool_reset (span_pool);
+ spans = pool_alloc (span_pool, sizeof(spans[0])*num_spans);
+ num_spans = 0;
+
+ /* Form the spans from the coverages and areas. */
+ for (; cell->x < xmax; cell = cell->next) {
+ int x = cell->x;
+ int area;
+
+ if (x > prev_x && cover != last_cover) {
+ spans[num_spans].x = prev_x;
+ spans[num_spans].is_clipped = 0;
+ spans[num_spans].coverage = GRID_AREA_TO_ALPHA (cover);
+ last_cover = cover;
+ last_x = prev_x;
+ ++num_spans;
+ }
+
+ cover += cell->covered_height*GRID_X*2;
+ area = cover - cell->uncovered_area;
+
+ if (area != last_cover) {
+ spans[num_spans].x = x;
+ spans[num_spans].is_clipped = 0;
+ spans[num_spans].coverage = GRID_AREA_TO_ALPHA (area);
+ last_cover = area;
+ last_x = x;
+ ++num_spans;
+ }
+
+ prev_x = x+1;
+ }
+
+ if (prev_x <= xmax && cover != last_cover) {
+ spans[num_spans].x = prev_x;
+ spans[num_spans].is_clipped = 0;
+ spans[num_spans].coverage = GRID_AREA_TO_ALPHA (cover);
+ last_cover = cover;
+ last_x = prev_x;
+ ++num_spans;
+ }
+
+ if (last_x < xmax && last_cover) {
+ spans[num_spans].x = xmax;
+ spans[num_spans].coverage = 0;
+ ++num_spans;
+ }
+
+ /* Dump them into the renderer. */
+ return renderer->render_rows (renderer, y, height, spans, num_spans);
+}
+
+struct _cairo_tor33_scan_converter {
+ cairo_scan_converter_t base;
+
+ glitter_scan_converter_t converter[1];
+ cairo_fill_rule_t fill_rule;
+
+ jmp_buf jmp;
+
+ struct {
+ struct pool base[1];
+ cairo_half_open_span_t embedded[32];
+ } span_pool;
+};
+
+typedef struct _cairo_tor33_scan_converter cairo_tor33_scan_converter_t;
+
+static void
+_cairo_tor33_scan_converter_destroy (void *converter)
+{
+ cairo_tor33_scan_converter_t *self = converter;
+ if (self == NULL) {
+ return;
+ }
+ _glitter_scan_converter_fini (self->converter);
+ pool_fini (self->span_pool.base);
+ free(self);
+}
+
+static cairo_status_t
+_cairo_tor33_scan_converter_add_edge (void *converter,
+ const cairo_point_t *p1,
+ const cairo_point_t *p2,
+ int top, int bottom,
+ int dir)
+{
+ cairo_tor33_scan_converter_t *self = converter;
+ cairo_edge_t edge;
+
+ edge.line.p1 = *p1;
+ edge.line.p2 = *p2;
+ edge.top = top;
+ edge.bottom = bottom;
+ edge.dir = dir;
+
+ glitter_scan_converter_add_edge (self->converter, &edge);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_tor33_scan_converter_add_polygon (void *converter,
+ const cairo_polygon_t *polygon)
+{
+ cairo_tor33_scan_converter_t *self = converter;
+ int i;
+
+ for (i = 0; i < polygon->num_edges; i++)
+ glitter_scan_converter_add_edge (self->converter,
+ &polygon->edges[i]);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_tor33_scan_converter_generate (void *converter,
+ cairo_span_renderer_t *renderer)
+{
+ cairo_tor33_scan_converter_t *self = converter;
+ cairo_status_t status;
+
+ if ((status = setjmp (self->jmp)))
+ return _cairo_scan_converter_set_error (self, _cairo_error (status));
+
+ glitter_scan_converter_render (self->converter,
+ self->fill_rule == CAIRO_FILL_RULE_WINDING,
+ renderer,
+ self->span_pool.base);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_scan_converter_t *
+_cairo_tor33_scan_converter_create (int xmin,
+ int ymin,
+ int xmax,
+ int ymax,
+ cairo_fill_rule_t fill_rule)
+{
+ cairo_tor33_scan_converter_t *self;
+ cairo_status_t status;
+
+ self = calloc (1, sizeof(struct _cairo_tor33_scan_converter));
+ if (unlikely (self == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto bail_nomem;
+ }
+
+ self->base.destroy = _cairo_tor33_scan_converter_destroy;
+ self->base.add_edge = _cairo_tor33_scan_converter_add_edge;
+ self->base.add_polygon = _cairo_tor33_scan_converter_add_polygon;
+ self->base.generate = _cairo_tor33_scan_converter_generate;
+
+ pool_init (self->span_pool.base, &self->jmp,
+ 250 * sizeof(self->span_pool.embedded[0]),
+ sizeof(self->span_pool.embedded));
+
+ _glitter_scan_converter_init (self->converter, &self->jmp);
+ status = glitter_scan_converter_reset (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);
+}
commit ca68f4a520ec6390014bbe2d76d4089054950cac
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date: Wed Aug 3 12:53:18 2011 +0100
fast-image: Use tolerance to adjust rectilinear strokes to the nearest pixel
Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>
diff --git a/src/Makefile.sources b/src/Makefile.sources
index 31f16a6..2a1a1a6 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -95,6 +95,7 @@ cairo_private = \
cairo-scaled-font-private.h \
cairo-slope-private.h \
cairo-spans-private.h \
+ cairo-stroke-dash-private.h \
cairo-surface-fallback-private.h \
cairo-surface-private.h \
cairo-surface-clipper-private.h \
@@ -161,6 +162,7 @@ cairo_sources = \
cairo-path-fixed.c \
cairo-path-in-fill.c \
cairo-path-stroke.c \
+ cairo-path-stroke-boxes.c \
cairo-pattern.c \
cairo-pen.c \
cairo-polygon.c \
@@ -175,6 +177,7 @@ cairo_sources = \
cairo-slope.c \
cairo-spans.c \
cairo-spline.c \
+ cairo-stroke-dash.c \
cairo-stroke-style.c \
cairo-surface.c \
cairo-surface-fallback.c \
diff --git a/src/cairo-fast-image-surface.c b/src/cairo-fast-image-surface.c
index 30d3702..b50ad80 100644
--- a/src/cairo-fast-image-surface.c
+++ b/src/cairo-fast-image-surface.c
@@ -2930,6 +2930,8 @@ _cairo_fast_image_surface_stroke (void *abstract_surface,
cairo_rectangle_int_t unbounded;
cairo_int_status_t status;
+ tolerance = FAST_IMAGE_TOLERANCE;
+
_cairo_fast_image_surface_get_extents (surface, &unbounded);
status = _cairo_composite_rectangles_init_for_stroke (&composite, &unbounded,
op, source,
@@ -2943,11 +2945,12 @@ _cairo_fast_image_surface_stroke (void *abstract_surface,
cairo_boxes_t boxes;
_cairo_boxes_init_with_clip (&boxes, composite.clip);
- status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path,
- style,
- ctm,
- antialias,
- &boxes);
+ status = _cairo_path_fixed_stroke_rectilinear_round_to_boxes (path,
+ style,
+ ctm,
+ antialias,
+ tolerance,
+ &boxes);
if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
status = _clip_and_composite_boxes (surface, op, source, 1.,
&boxes, &composite);
diff --git a/src/cairo-path-stroke-boxes.c b/src/cairo-path-stroke-boxes.c
new file mode 100644
index 0000000..d1959bc
--- /dev/null
+++ b/src/cairo-path-stroke-boxes.c
@@ -0,0 +1,776 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth at cworth.org>
+ * Chris Wilson <chris at chris-wilson.co.uk>
+ */
+
+#define _BSD_SOURCE /* for hypot() */
+#include "cairoint.h"
+
+#include "cairo-box-private.h"
+#include "cairo-boxes-private.h"
+#include "cairo-error-private.h"
+#include "cairo-path-fixed-private.h"
+#include "cairo-slope-private.h"
+#include "cairo-stroke-dash-private.h"
+
+typedef struct _segment_t {
+ cairo_point_t p1, p2;
+ cairo_bool_t is_horizontal;
+ cairo_bool_t has_join;
+} segment_t;
+
+typedef struct _cairo_rectilinear_stroker {
+ const cairo_stroke_style_t *stroke_style;
+ const cairo_matrix_t *ctm;
+ cairo_antialias_t antialias;
+ double tolerance;
+
+ cairo_fixed_t half_line_width;
+ cairo_boxes_t *boxes;
+ cairo_point_t current_point;
+ cairo_point_t first_point;
+ cairo_bool_t open_sub_path;
+
+ cairo_stroker_dash_t dash;
+
+ cairo_bool_t has_bounds;
+ cairo_box_t bounds;
+
+ int num_segments;
+ int segments_size;
+ segment_t *segments;
+ segment_t segments_embedded[8]; /* common case is a single rectangle */
+} cairo_rectilinear_stroker_t;
+
+static void
+_cairo_rectilinear_stroker_limit (cairo_rectilinear_stroker_t *stroker,
+ const cairo_box_t *boxes,
+ int num_boxes)
+{
+ stroker->has_bounds = TRUE;
+ _cairo_boxes_get_extents (boxes, num_boxes, &stroker->bounds);
+
+ stroker->bounds.p1.x -= stroker->half_line_width;
+ stroker->bounds.p2.x += stroker->half_line_width;
+
+ stroker->bounds.p1.y -= stroker->half_line_width;
+ stroker->bounds.p2.y += stroker->half_line_width;
+}
+
+static cairo_bool_t
+_cairo_rectilinear_stroker_init (cairo_rectilinear_stroker_t *stroker,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ cairo_antialias_t antialias,
+ double tolerance,
+ cairo_boxes_t *boxes)
+{
+ /* This special-case rectilinear stroker only supports
+ * miter-joined lines (not curves) and a translation-only matrix
+ * (though it could probably be extended to support a matrix with
+ * uniform, integer scaling).
+ *
+ * It also only supports horizontal and vertical line_to
+ * elements. But we don't catch that here, but instead return
+ * UNSUPPORTED from _cairo_rectilinear_stroker_line_to if any
+ * non-rectilinear line_to is encountered.
+ */
+ if (stroke_style->line_join != CAIRO_LINE_JOIN_MITER)
+ return FALSE;
+
+ /* If the miter limit turns right angles into bevels, then we
+ * can't use this optimization. Remember, the ratio is
+ * 1/sin(ɸ/2). So the cutoff is 1/sin(Ï/4.0) or â·2,
+ * which we round for safety. */
+ if (stroke_style->miter_limit < M_SQRT2)
+ return FALSE;
+
+ if (! (stroke_style->line_cap == CAIRO_LINE_CAP_BUTT ||
+ stroke_style->line_cap == CAIRO_LINE_CAP_SQUARE))
+ {
+ return FALSE;
+ }
+
+ if (! _cairo_matrix_has_unity_scale (ctm))
+ return FALSE;
+
+ stroker->stroke_style = stroke_style;
+ stroker->ctm = ctm;
+ stroker->antialias = antialias;
+ stroker->tolerance = _cairo_fixed_from_double (tolerance);
+
+ stroker->half_line_width =
+ _cairo_fixed_from_double (stroke_style->line_width / 2.0);
+ stroker->open_sub_path = FALSE;
+ stroker->segments = stroker->segments_embedded;
+ stroker->segments_size = ARRAY_LENGTH (stroker->segments_embedded);
+ stroker->num_segments = 0;
+
+ _cairo_stroker_dash_init (&stroker->dash, stroke_style);
+
+ stroker->has_bounds = FALSE;
+
+ stroker->boxes = boxes;
+
+ return TRUE;
+}
+
+static void
+_cairo_rectilinear_stroker_fini (cairo_rectilinear_stroker_t *stroker)
+{
+ if (stroker->segments != stroker->segments_embedded)
+ free (stroker->segments);
+}
+
+static cairo_fixed_t
+adjust_within_tolerance (cairo_fixed_t v, const cairo_fixed_t tolerance)
+{
+ if (! _cairo_fixed_is_integer (v)) {
+ cairo_fixed_t f = _cairo_fixed_round (v);
+ if (abs (f - v) < tolerance)
+ v = f;
+ }
+ return v;
+}
+
+static cairo_status_t
+_cairo_rectilinear_stroker_add_segment (cairo_rectilinear_stroker_t *stroker,
+ const cairo_point_t *p1,
+ const cairo_point_t *p2,
+ cairo_bool_t is_horizontal,
+ cairo_bool_t has_join)
+{
+ if (CAIRO_INJECT_FAULT ())
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ if (stroker->num_segments == stroker->segments_size) {
+ int new_size = stroker->segments_size * 2;
+ segment_t *new_segments;
+
+ if (stroker->segments == stroker->segments_embedded) {
+ new_segments = _cairo_malloc_ab (new_size, sizeof (segment_t));
+ if (unlikely (new_segments == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ memcpy (new_segments, stroker->segments,
+ stroker->num_segments * sizeof (segment_t));
+ } else {
+ new_segments = _cairo_realloc_ab (stroker->segments,
+ new_size, sizeof (segment_t));
+ if (unlikely (new_segments == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ stroker->segments_size = new_size;
+ stroker->segments = new_segments;
+ }
+
+ if (stroker->tolerance > 0) {
+ stroker->segments[stroker->num_segments].p1.x =
+ adjust_within_tolerance (p1->x, stroker->tolerance);
+ stroker->segments[stroker->num_segments].p1.y =
+ adjust_within_tolerance (p1->y, stroker->tolerance);
+
+ stroker->segments[stroker->num_segments].p2.x =
+ adjust_within_tolerance (p2->x, stroker->tolerance);
+ stroker->segments[stroker->num_segments].p2.y =
+ adjust_within_tolerance (p2->y, stroker->tolerance);
+ } else {
+ stroker->segments[stroker->num_segments].p1 = *p1;
+ stroker->segments[stroker->num_segments].p2 = *p2;
+ }
+
+ stroker->segments[stroker->num_segments].has_join = has_join;
+ stroker->segments[stroker->num_segments].is_horizontal = is_horizontal;
+ stroker->num_segments++;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_rectilinear_stroker_emit_segments (cairo_rectilinear_stroker_t *stroker)
+{
+ cairo_status_t status;
+ cairo_line_cap_t line_cap = stroker->stroke_style->line_cap;
+ cairo_fixed_t half_line_width = stroker->half_line_width;
+ int i;
+
+ for (i = 0; i < stroker->num_segments; i++) {
+ cairo_point_t *a, *b;
+ cairo_bool_t lengthen_initial, shorten_final, lengthen_final;
+ cairo_box_t box;
+
+ a = &stroker->segments[i].p1;
+ b = &stroker->segments[i].p2;
+
+ /* For each segment we generate a single rectangular
+ * trapezoid. This rectangle is based on a perpendicular
+ * extension (by half the line width) of the segment endpoints
+ * after some adjustments of the endpoints to account for caps
+ * and joins.
+ */
+
+ /* We adjust the initial point of the segment to extend the
+ * rectangle to include the previous cap or join, (this
+ * adjustment applies to all segments except for the first
+ * segment of open, butt-capped paths).
+ */
+ lengthen_initial = TRUE;
+ if (i == 0 && stroker->open_sub_path && line_cap == CAIRO_LINE_CAP_BUTT)
+ lengthen_initial = FALSE;
+
+ /* The adjustment of the final point is trickier. For all but
+ * the last segment we shorten the segment at the final
+ * endpoint to not overlap with the subsequent join. For the
+ * last segment we do the same shortening if the path is
+ * closed. If the path is open and butt-capped we do no
+ * adjustment, while if it's open and square-capped we do a
+ * lengthening adjustment instead to include the cap.
+ */
+ shorten_final = TRUE;
+ lengthen_final = FALSE;
+ if (i == stroker->num_segments - 1 && stroker->open_sub_path) {
+ shorten_final = FALSE;
+ if (line_cap == CAIRO_LINE_CAP_SQUARE)
+ lengthen_final = TRUE;
+ }
+
+ /* Perform the adjustments of the endpoints. */
+ if (a->y == b->y) {
+ if (a->x < b->x) {
+ if (lengthen_initial)
+ a->x -= half_line_width;
+ if (shorten_final)
+ b->x -= half_line_width;
+ else if (lengthen_final)
+ b->x += half_line_width;
+ } else {
+ if (lengthen_initial)
+ a->x += half_line_width;
+ if (shorten_final)
+ b->x += half_line_width;
+ else if (lengthen_final)
+ b->x -= half_line_width;
+ }
+
+ if (a->x > b->x) {
+ cairo_point_t *t;
+
+ t = a;
+ a = b;
+ b = t;
+ }
+ } else {
+ if (a->y < b->y) {
+ if (lengthen_initial)
+ a->y -= half_line_width;
+ if (shorten_final)
+ b->y -= half_line_width;
+ else if (lengthen_final)
+ b->y += half_line_width;
+ } else {
+ if (lengthen_initial)
+ a->y += half_line_width;
+ if (shorten_final)
+ b->y += half_line_width;
+ else if (lengthen_final)
+ b->y -= half_line_width;
+ }
+
+ if (a->y > b->y) {
+ cairo_point_t *t;
+
+ t = a;
+ a = b;
+ b = t;
+ }
+ }
+
+ /* Form the rectangle by expanding by half the line width in
+ * either perpendicular direction. */
+ if (a->y == b->y) {
+ a->y -= half_line_width;
+ b->y += half_line_width;
+ } else {
+ a->x -= half_line_width;
+ b->x += half_line_width;
+ }
+
+ box.p1 = *a;
+ box.p2 = *b;
+ status = _cairo_boxes_add (stroker->boxes, stroker->antialias, &box);
+ if (unlikely (status))
+ return status;
+ }
+
+ stroker->num_segments = 0;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_rectilinear_stroker_emit_segments_dashed (cairo_rectilinear_stroker_t *stroker)
+{
+ cairo_status_t status;
+ cairo_line_cap_t line_cap = stroker->stroke_style->line_cap;
+ cairo_fixed_t half_line_width = stroker->half_line_width;
+ int i;
+
+ for (i = 0; i < stroker->num_segments; i++) {
+ cairo_point_t *a, *b;
+ cairo_bool_t is_horizontal;
+ cairo_box_t box;
+
+ a = &stroker->segments[i].p1;
+ b = &stroker->segments[i].p2;
+
+ is_horizontal = stroker->segments[i].is_horizontal;
+
+ /* Handle the joins for a potentially degenerate segment. */
+ if (line_cap == CAIRO_LINE_CAP_BUTT &&
+ stroker->segments[i].has_join &&
+ (i != stroker->num_segments - 1 ||
+ (! stroker->open_sub_path && stroker->dash.dash_starts_on)))
+ {
+ cairo_point_t p1 = stroker->segments[i].p1;
+ cairo_point_t p2 = stroker->segments[i].p2;
+ cairo_slope_t out_slope;
+ int j = (i + 1) % stroker->num_segments;
+ cairo_box_t box;
+
+ _cairo_slope_init (&out_slope,
+ &stroker->segments[j].p1,
+ &stroker->segments[j].p2);
+
+ if (is_horizontal) {
+ if (p1.x <= p2.x) {
+ p1.x = p2.x;
+ p2.x += half_line_width;
+ } else {
+ p1.x = p2.x - half_line_width;
+ }
+ if (out_slope.dy >= 0)
+ p1.y -= half_line_width;
+ if (out_slope.dy <= 0)
+ p2.y += half_line_width;
+ } else {
+ if (p1.y <= p2.y) {
+ p1.y = p2.y;
+ p2.y += half_line_width;
+ } else {
+ p1.y = p2.y - half_line_width;
+ }
+ if (out_slope.dx >= 0)
+ p1.x -= half_line_width;
+ if (out_slope.dx <= 0)
+ p2.x += half_line_width;
+ }
+
+
+ box.p1 = p1;
+ box.p2 = p2;
+ status = _cairo_boxes_add (stroker->boxes, stroker->antialias, &box);
+ if (unlikely (status))
+ return status;
+ }
+
+ /* Perform the adjustments of the endpoints. */
+ if (is_horizontal) {
+ if (line_cap == CAIRO_LINE_CAP_SQUARE) {
+ if (a->x <= b->x) {
+ a->x -= half_line_width;
+ b->x += half_line_width;
+ } else {
+ a->x += half_line_width;
+ b->x -= half_line_width;
+ }
+ }
+
+ if (a->x > b->x) {
+ cairo_point_t *t;
+
+ t = a;
+ a = b;
+ b = t;
+ }
+
+ a->y -= half_line_width;
+ b->y += half_line_width;
+ } else {
+ if (line_cap == CAIRO_LINE_CAP_SQUARE) {
+ if (a->y <= b->y) {
+ a->y -= half_line_width;
+ b->y += half_line_width;
+ } else {
+ a->y += half_line_width;
+ b->y -= half_line_width;
+ }
+ }
+
+ if (a->y > b->y) {
+ cairo_point_t *t;
+
+ t = a;
+ a = b;
+ b = t;
+ }
+
+ a->x -= half_line_width;
+ b->x += half_line_width;
+ }
+
+ if (a->x == b->x && a->y == b->y)
+ continue;
+
+
+ box.p1 = *a;
+ box.p2 = *b;
+
+ status = _cairo_boxes_add (stroker->boxes, stroker->antialias, &box);
+ if (unlikely (status))
+ return status;
+ }
+
+ stroker->num_segments = 0;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_rectilinear_stroker_move_to (void *closure,
+ const cairo_point_t *point)
+{
+ cairo_rectilinear_stroker_t *stroker = closure;
+ cairo_status_t status;
+
+ if (stroker->dash.dashed)
+ status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker);
+ else
+ status = _cairo_rectilinear_stroker_emit_segments (stroker);
+ if (unlikely (status))
+ return status;
+
+ /* reset the dash pattern for new sub paths */
+ _cairo_stroker_dash_start (&stroker->dash);
+
+ stroker->current_point = *point;
+ stroker->first_point = *point;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_rectilinear_stroker_line_to (void *closure,
+ const cairo_point_t *b)
+{
+ cairo_rectilinear_stroker_t *stroker = closure;
+ cairo_point_t *a = &stroker->current_point;
+ cairo_status_t status;
+
+ /* We only support horizontal or vertical elements. */
+ assert (a->x == b->x || a->y == b->y);
+
+ /* We don't draw anything for degenerate paths. */
+ if (a->x == b->x && a->y == b->y)
+ return CAIRO_STATUS_SUCCESS;
+
+ status = _cairo_rectilinear_stroker_add_segment (stroker, a, b,
+ a->y == b->y,
+ TRUE);
+
+ stroker->current_point = *b;
+ stroker->open_sub_path = TRUE;
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_rectilinear_stroker_line_to_dashed (void *closure,
+ const cairo_point_t *point)
+{
+ cairo_rectilinear_stroker_t *stroker = closure;
+ const cairo_point_t *a = &stroker->current_point;
+ const cairo_point_t *b = point;
+ cairo_bool_t fully_in_bounds;
+ double sign, remain;
+ cairo_fixed_t mag;
+ cairo_status_t status;
+ cairo_line_t segment;
+ cairo_bool_t dash_on = FALSE;
+ cairo_bool_t is_horizontal;
+
+ /* We don't draw anything for degenerate paths. */
+ if (a->x == b->x && a->y == b->y)
+ return CAIRO_STATUS_SUCCESS;
+
+ /* We only support horizontal or vertical elements. */
+ assert (a->x == b->x || a->y == b->y);
+
+ fully_in_bounds = TRUE;
+ if (stroker->has_bounds &&
+ (! _cairo_box_contains_point (&stroker->bounds, a) ||
+ ! _cairo_box_contains_point (&stroker->bounds, b)))
+ {
+ fully_in_bounds = FALSE;
+ }
+
+ is_horizontal = a->y == b->y;
+ if (is_horizontal)
+ mag = b->x - a->x;
+ else
+ mag = b->y - a->y;
+ if (mag < 0) {
+ remain = _cairo_fixed_to_double (-mag);
+ sign = 1.;
+ } else {
+ remain = _cairo_fixed_to_double (mag);
+ sign = -1.;
+ }
+
+ segment.p2 = segment.p1 = *a;
+ while (remain > 0.) {
+ double step_length;
+
+ step_length = MIN (stroker->dash.dash_remain, remain);
+ remain -= step_length;
+
+ mag = _cairo_fixed_from_double (sign*remain);
+ if (is_horizontal)
+ segment.p2.x = b->x + mag;
+ else
+ segment.p2.y = b->y + mag;
+
+ if (stroker->dash.dash_on &&
+ (fully_in_bounds ||
+ _cairo_box_intersects_line_segment (&stroker->bounds, &segment)))
+ {
+ status = _cairo_rectilinear_stroker_add_segment (stroker,
+ &segment.p1,
+ &segment.p2,
+ is_horizontal,
+ remain <= 0.);
+ if (unlikely (status))
+ return status;
+
+ dash_on = TRUE;
+ }
+ else
+ {
+ dash_on = FALSE;
+ }
+
+ _cairo_stroker_dash_step (&stroker->dash, step_length);
+ segment.p1 = segment.p2;
+ }
+
+ if (stroker->dash.dash_on && ! dash_on &&
+ (fully_in_bounds ||
+ _cairo_box_intersects_line_segment (&stroker->bounds, &segment)))
+ {
+
+ /* This segment ends on a transition to dash_on, compute a new face
+ * and add cap for the beginning of the next dash_on step.
+ */
+
+ status = _cairo_rectilinear_stroker_add_segment (stroker,
+ &segment.p1,
+ &segment.p1,
+ is_horizontal,
+ TRUE);
+ if (unlikely (status))
+ return status;
+ }
+
+ stroker->current_point = *point;
+ stroker->open_sub_path = TRUE;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_rectilinear_stroker_close_path (void *closure)
+{
+ cairo_rectilinear_stroker_t *stroker = closure;
+ cairo_status_t status;
+
+ /* We don't draw anything for degenerate paths. */
+ if (! stroker->open_sub_path)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (stroker->dash.dashed) {
+ status = _cairo_rectilinear_stroker_line_to_dashed (stroker,
+ &stroker->first_point);
+ } else {
+ status = _cairo_rectilinear_stroker_line_to (stroker,
+ &stroker->first_point);
+ }
+ if (unlikely (status))
+ return status;
+
+ stroker->open_sub_path = FALSE;
+
+ if (stroker->dash.dashed)
+ status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker);
+ else
+ status = _cairo_rectilinear_stroker_emit_segments (stroker);
+ if (unlikely (status))
+ return status;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_int_status_t
+_cairo_path_fixed_stroke_rectilinear_to_boxes (const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ cairo_antialias_t antialias,
+ cairo_boxes_t *boxes)
+{
+ cairo_rectilinear_stroker_t rectilinear_stroker;
+ cairo_int_status_t status;
+
+ assert (_cairo_path_fixed_stroke_is_rectilinear (path));
+
+ if (! _cairo_rectilinear_stroker_init (&rectilinear_stroker,
+ stroke_style, ctm, antialias, 0,
+ boxes))
+ {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ if (boxes->num_limits) {
+ _cairo_rectilinear_stroker_limit (&rectilinear_stroker,
+ boxes->limits,
+ boxes->num_limits);
+ }
+
+ status = _cairo_path_fixed_interpret (path,
+ _cairo_rectilinear_stroker_move_to,
+ rectilinear_stroker.dash.dashed ?
+ _cairo_rectilinear_stroker_line_to_dashed :
+ _cairo_rectilinear_stroker_line_to,
+ NULL,
+ _cairo_rectilinear_stroker_close_path,
+ &rectilinear_stroker);
+ if (unlikely (status))
+ goto BAIL;
+
+ if (rectilinear_stroker.dash.dashed)
+ status = _cairo_rectilinear_stroker_emit_segments_dashed (&rectilinear_stroker);
+ else
+ status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker);
+ if (unlikely (status))
+ goto BAIL;
+
+ /* As we incrementally tessellate, we do not eliminate self-intersections */
+ status = _cairo_bentley_ottmann_tessellate_boxes (boxes,
+ CAIRO_FILL_RULE_WINDING,
+ boxes);
+ if (unlikely (status))
+ goto BAIL;
+
+ _cairo_rectilinear_stroker_fini (&rectilinear_stroker);
+
+ return CAIRO_STATUS_SUCCESS;
+
+BAIL:
+ _cairo_rectilinear_stroker_fini (&rectilinear_stroker);
+ _cairo_boxes_clear (boxes);
+ return status;
+}
+
+cairo_int_status_t
+_cairo_path_fixed_stroke_rectilinear_round_to_boxes (const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ cairo_antialias_t antialias,
+ double tolerance,
+ cairo_boxes_t *boxes)
+{
+ cairo_rectilinear_stroker_t rectilinear_stroker;
+ cairo_int_status_t status;
+
+ assert (_cairo_path_fixed_stroke_is_rectilinear (path));
+
+ if (! _cairo_rectilinear_stroker_init (&rectilinear_stroker,
+ stroke_style, ctm, antialias,
+ tolerance,
+ boxes))
+ {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ if (boxes->num_limits) {
+ _cairo_rectilinear_stroker_limit (&rectilinear_stroker,
+ boxes->limits,
+ boxes->num_limits);
+ }
+
+ status = _cairo_path_fixed_interpret (path,
+ _cairo_rectilinear_stroker_move_to,
+ rectilinear_stroker.dash.dashed ?
+ _cairo_rectilinear_stroker_line_to_dashed :
+ _cairo_rectilinear_stroker_line_to,
+ NULL,
+ _cairo_rectilinear_stroker_close_path,
+ &rectilinear_stroker);
+ if (unlikely (status))
+ goto BAIL;
+
+ if (rectilinear_stroker.dash.dashed)
+ status = _cairo_rectilinear_stroker_emit_segments_dashed (&rectilinear_stroker);
+ else
+ status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker);
+ if (unlikely (status))
+ goto BAIL;
+
+ /* As we incrementally tessellate, we do not eliminate self-intersections */
+ status = _cairo_bentley_ottmann_tessellate_boxes (boxes,
+ CAIRO_FILL_RULE_WINDING,
+ boxes);
+ if (unlikely (status))
+ goto BAIL;
+
+ _cairo_rectilinear_stroker_fini (&rectilinear_stroker);
+
+ return CAIRO_STATUS_SUCCESS;
+
+BAIL:
+ _cairo_rectilinear_stroker_fini (&rectilinear_stroker);
+ _cairo_boxes_clear (boxes);
+ return status;
+}
diff --git a/src/cairo-path-stroke.c b/src/cairo-path-stroke.c
index 31ce1ac..a164b28 100644
--- a/src/cairo-path-stroke.c
+++ b/src/cairo-path-stroke.c
@@ -44,18 +44,7 @@
#include "cairo-error-private.h"
#include "cairo-path-fixed-private.h"
#include "cairo-slope-private.h"
-
-typedef struct _cairo_stroker_dash {
- cairo_bool_t dashed;
- unsigned int dash_index;
- cairo_bool_t dash_on;
- cairo_bool_t dash_starts_on;
- double dash_remain;
-
- double dash_offset;
- const double *dashes;
- unsigned int num_dashes;
-} cairo_stroker_dash_t;
+#include "cairo-stroke-dash-private.h"
typedef struct cairo_stroker {
cairo_stroke_style_t style;
@@ -98,61 +87,6 @@ typedef struct cairo_stroker {
cairo_box_t bounds;
} cairo_stroker_t;
-static void
-_cairo_stroker_dash_start (cairo_stroker_dash_t *dash)
-{
- double offset;
- cairo_bool_t on = TRUE;
- unsigned int i = 0;
-
- if (! dash->dashed)
- return;
-
- offset = dash->dash_offset;
-
- /* We stop searching for a starting point as soon as the
- offset reaches zero. Otherwise when an initial dash
- segment shrinks to zero it will be skipped over. */
- while (offset > 0.0 && offset >= dash->dashes[i]) {
- offset -= dash->dashes[i];
- on = !on;
- if (++i == dash->num_dashes)
- i = 0;
- }
-
- dash->dash_index = i;
- dash->dash_on = dash->dash_starts_on = on;
- dash->dash_remain = dash->dashes[i] - offset;
-}
-
-static void
-_cairo_stroker_dash_step (cairo_stroker_dash_t *dash, double step)
-{
- dash->dash_remain -= step;
- if (dash->dash_remain <= 0.) {
- if (++dash->dash_index == dash->num_dashes)
- dash->dash_index = 0;
-
- dash->dash_on = ! dash->dash_on;
- dash->dash_remain = dash->dashes[dash->dash_index];
- }
-}
-
-static void
-_cairo_stroker_dash_init (cairo_stroker_dash_t *dash,
- const cairo_stroke_style_t *style)
-{
- dash->dashed = style->dash != NULL;
- if (! dash->dashed)
- return;
-
- dash->dashes = style->dash;
- dash->num_dashes = style->num_dashes;
- dash->dash_offset = style->dash_offset;
-
- _cairo_stroker_dash_start (dash);
-}
-
static cairo_status_t
_cairo_stroker_init (cairo_stroker_t *stroker,
const cairo_stroke_style_t *stroke_style,
@@ -2100,63 +2034,3 @@ BAIL:
return status;
}
-
-cairo_int_status_t
-_cairo_path_fixed_stroke_rectilinear_to_boxes (const cairo_path_fixed_t *path,
- const cairo_stroke_style_t *stroke_style,
- const cairo_matrix_t *ctm,
- cairo_antialias_t antialias,
- cairo_boxes_t *boxes)
-{
- cairo_rectilinear_stroker_t rectilinear_stroker;
- cairo_int_status_t status;
-
- assert (_cairo_path_fixed_stroke_is_rectilinear (path));
-
- if (! _cairo_rectilinear_stroker_init (&rectilinear_stroker,
- stroke_style, ctm, antialias,
- FALSE, boxes))
- {
- return CAIRO_INT_STATUS_UNSUPPORTED;
- }
-
- if (boxes->num_limits) {
- _cairo_rectilinear_stroker_limit (&rectilinear_stroker,
- boxes->limits,
- boxes->num_limits);
- }
-
- status = _cairo_path_fixed_interpret (path,
- _cairo_rectilinear_stroker_move_to,
- rectilinear_stroker.dash.dashed ?
- _cairo_rectilinear_stroker_line_to_dashed :
- _cairo_rectilinear_stroker_line_to,
- NULL,
- _cairo_rectilinear_stroker_close_path,
- &rectilinear_stroker);
- if (unlikely (status))
- goto BAIL;
-
- if (rectilinear_stroker.dash.dashed)
- status = _cairo_rectilinear_stroker_emit_segments_dashed (&rectilinear_stroker);
- else
- status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker);
- if (unlikely (status))
- goto BAIL;
-
- /* As we incrementally tessellate, we do not eliminate self-intersections */
- status = _cairo_bentley_ottmann_tessellate_boxes (boxes,
- CAIRO_FILL_RULE_WINDING,
- boxes);
- if (unlikely (status))
- goto BAIL;
-
- _cairo_rectilinear_stroker_fini (&rectilinear_stroker);
-
- return CAIRO_STATUS_SUCCESS;
-
-BAIL:
- _cairo_rectilinear_stroker_fini (&rectilinear_stroker);
- _cairo_boxes_clear (boxes);
- return status;
-}
diff --git a/src/cairo-stroke-dash-private.h b/src/cairo-stroke-dash-private.h
new file mode 100644
index 0000000..74ee862
--- /dev/null
+++ b/src/cairo-stroke-dash-private.h
@@ -0,0 +1,65 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth at redhat.com>
+ */
+
+#ifndef CAIRO_STROKE_DASH_PRIVATE_H
+#define CAIRO_STROKE_DASH_PRIVATE_H
+
+#include "cairo-types-private.h"
+#include "cairo-compiler-private.h"
+
+typedef struct _cairo_stroker_dash {
+ cairo_bool_t dashed;
+ unsigned int dash_index;
+ cairo_bool_t dash_on;
+ cairo_bool_t dash_starts_on;
+ double dash_remain;
+
+ double dash_offset;
+ const double *dashes;
+ unsigned int num_dashes;
+} cairo_stroker_dash_t;
+
+
+cairo_private void
+_cairo_stroker_dash_init (cairo_stroker_dash_t *dash,
+ const cairo_stroke_style_t *style);
+
+cairo_private void
+_cairo_stroker_dash_start (cairo_stroker_dash_t *dash);
+
+cairo_private void
+_cairo_stroker_dash_step (cairo_stroker_dash_t *dash, double step);
+
+#endif /* CAIRO_STROKE_DASH_PRIVATE_H */
diff --git a/src/cairo-stroke-dash.c b/src/cairo-stroke-dash.c
new file mode 100644
index 0000000..97dbae5
--- /dev/null
+++ b/src/cairo-stroke-dash.c
@@ -0,0 +1,93 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Carl Worth <cworth at cworth.org>
+ */
+
+#include "cairoint.h"
+#include "cairo-stroke-dash-private.h"
+
+void
+_cairo_stroker_dash_start (cairo_stroker_dash_t *dash)
+{
+ double offset;
+ cairo_bool_t on = TRUE;
+ unsigned int i = 0;
+
+ if (! dash->dashed)
+ return;
+
+ offset = dash->dash_offset;
+
+ /* We stop searching for a starting point as soon as the
+ offset reaches zero. Otherwise when an initial dash
+ segment shrinks to zero it will be skipped over. */
+ while (offset > 0.0 && offset >= dash->dashes[i]) {
+ offset -= dash->dashes[i];
+ on = !on;
+ if (++i == dash->num_dashes)
+ i = 0;
+ }
+
+ dash->dash_index = i;
+ dash->dash_on = dash->dash_starts_on = on;
+ dash->dash_remain = dash->dashes[i] - offset;
+}
+
+void
+_cairo_stroker_dash_step (cairo_stroker_dash_t *dash, double step)
+{
+ dash->dash_remain -= step;
+ if (dash->dash_remain <= 0.) {
+ if (++dash->dash_index == dash->num_dashes)
+ dash->dash_index = 0;
+
+ dash->dash_on = ! dash->dash_on;
+ dash->dash_remain = dash->dashes[dash->dash_index];
+ }
+}
+
+void
+_cairo_stroker_dash_init (cairo_stroker_dash_t *dash,
+ const cairo_stroke_style_t *style)
+{
+ dash->dashed = style->dash != NULL;
+ if (! dash->dashed)
+ return;
+
+ dash->dashes = style->dash;
+ dash->num_dashes = style->num_dashes;
+ dash->dash_offset = style->dash_offset;
+
+ _cairo_stroker_dash_start (dash);
+}
+
diff --git a/src/cairoint.h b/src/cairoint.h
index 5715ad1..049fc92 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -1439,6 +1439,14 @@ _cairo_path_fixed_stroke_rectilinear_to_boxes (const cairo_path_fixed_t *path,
cairo_boxes_t *boxes);
cairo_private cairo_int_status_t
+_cairo_path_fixed_stroke_rectilinear_round_to_boxes (const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ cairo_antialias_t antialias,
+ double tolerance,
+ cairo_boxes_t *boxes);
+
+cairo_private cairo_int_status_t
_cairo_path_fixed_stroke_to_traps (const cairo_path_fixed_t *path,
const cairo_stroke_style_t *stroke_style,
const cairo_matrix_t *ctm,
commit 206a43c7824a038a226439ac9e7ee1eea5eb9d8a
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date: Wed Aug 3 12:08:29 2011 +0100
api: Introduce cairo_fast_image_surface_t
The intention of this experimental surface/context backend is to compromise on
image quality in the quest for speed.
It is strongly recommended that you think twice before using this!
Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>
diff --git a/boilerplate/cairo-boilerplate.c b/boilerplate/cairo-boilerplate.c
index b89e6d8..9f406eb 100644
--- a/boilerplate/cairo-boilerplate.c
+++ b/boilerplate/cairo-boilerplate.c
@@ -127,10 +127,10 @@ cairo_boilerplate_format_from_content (cairo_content_t content)
static cairo_surface_t *
_cairo_boilerplate_image_create_surface (const char *name,
cairo_content_t content,
- double width,
- double height,
- double max_width,
- double max_height,
+ double width,
+ double height,
+ double max_width,
+ double max_height,
cairo_boilerplate_mode_t mode,
int id,
void **closure)
@@ -181,6 +181,60 @@ _cairo_boilerplate_image_create_similar (cairo_surface_t *other,
}
static cairo_surface_t *
+_cairo_boilerplate_fast_image_create_surface (const char *name,
+ cairo_content_t content,
+ double width,
+ double height,
+ double max_width,
+ double max_height,
+ cairo_boilerplate_mode_t mode,
+ int id,
+ void **closure)
+{
+ cairo_format_t format;
+
+ *closure = NULL;
+
+ if (content == CAIRO_CONTENT_COLOR_ALPHA) {
+ format = CAIRO_FORMAT_ARGB32;
+ } else if (content == CAIRO_CONTENT_COLOR) {
+ format = CAIRO_FORMAT_RGB24;
+ } else {
+ assert (0); /* not reached */
+ return NULL;
+ }
+
+ return cairo_fast_image_surface_create (format, ceil (width), ceil (height));
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_fast_image_create_similar (cairo_surface_t *other,
+ cairo_content_t content,
+ int width, int height)
+{
+ cairo_format_t format;
+ cairo_surface_t *surface;
+ int stride;
+ void *ptr;
+
+ switch (content) {
+ case CAIRO_CONTENT_ALPHA: format = CAIRO_FORMAT_A8; break;
+ case CAIRO_CONTENT_COLOR: format = CAIRO_FORMAT_RGB24; break;
+ default:
+ case CAIRO_CONTENT_COLOR_ALPHA: format = CAIRO_FORMAT_ARGB32; break;
+ }
+
+ stride = cairo_format_stride_for_width(format, width);
+ ptr = malloc (stride* height);
+
+ surface = cairo_fast_image_surface_create_for_data (ptr, format,
+ width, height, stride);
+ cairo_surface_set_user_data (surface, &key, ptr, free);
+
+ return surface;
+}
+
+static cairo_surface_t *
_cairo_boilerplate_image16_create_surface (const char *name,
cairo_content_t content,
double width,
@@ -228,7 +282,7 @@ static char *
_cairo_boilerplate_image_describe (void *closure)
{
char *s;
-
+
xasprintf (&s, "pixman %s", pixman_version_string ());
return s;
@@ -236,7 +290,7 @@ _cairo_boilerplate_image_describe (void *closure)
#if CAIRO_HAS_RECORDING_SURFACE
static cairo_surface_t *
-_cairo_boilerplate_recording_create_surface (const char *name,
+_cairo_boilerplate_recording_create_surface (const char *name,
cairo_content_t content,
double width,
double height,
@@ -426,6 +480,32 @@ static const cairo_boilerplate_target_t builtin_targets[] = {
_cairo_boilerplate_image_describe,
TRUE, FALSE, FALSE
},
+ {
+ "fast-image", "fast-image", NULL, NULL,
+ CAIRO_SURFACE_TYPE_FASTIMAGE, CAIRO_CONTENT_COLOR_ALPHA, 4,
+ NULL,
+ _cairo_boilerplate_fast_image_create_surface,
+ _cairo_boilerplate_fast_image_create_similar,
+ NULL, NULL,
+ _cairo_boilerplate_get_image_surface,
+ cairo_surface_write_to_png,
+ NULL, NULL,
+ _cairo_boilerplate_image_describe,
+ TRUE, FALSE, FALSE
+ },
+ {
+ "fast-image", "fast-image", NULL, NULL,
+ CAIRO_SURFACE_TYPE_FASTIMAGE, CAIRO_CONTENT_COLOR, 4,
+ NULL,
+ _cairo_boilerplate_fast_image_create_surface,
+ _cairo_boilerplate_fast_image_create_similar,
+ NULL, NULL,
+ _cairo_boilerplate_get_image_surface,
+ cairo_surface_write_to_png,
+ NULL, NULL,
+ _cairo_boilerplate_image_describe,
+ FALSE, FALSE, FALSE
+ },
#if CAIRO_HAS_RECORDING_SURFACE
{
"recording", "image", NULL, NULL,
diff --git a/src/Makefile.sources b/src/Makefile.sources
index 1dbe4c0..31f16a6 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -145,6 +145,7 @@ cairo_sources = \
cairo-hull.c \
cairo-image-info.c \
cairo-image-surface.c \
+ cairo-fast-image-surface.c \
cairo-lzw.c \
cairo-matrix.c \
cairo-mesh-pattern-rasterizer.c \
diff --git a/src/cairo-debug.c b/src/cairo-debug.c
index ada0cdb..ca94e2b 100644
--- a/src/cairo-debug.c
+++ b/src/cairo-debug.c
@@ -82,6 +82,7 @@ cairo_debug_reset_static_data (void)
_cairo_clip_reset_static_data ();
_cairo_image_reset_static_data ();
+ _cairo_fast_image_reset_static_data ();
#if CAIRO_HAS_DRM_SURFACE
_cairo_drm_device_reset_static_data ();
diff --git a/src/cairo-fast-image-surface.c b/src/cairo-fast-image-surface.c
new file mode 100644
index 0000000..30d3702
--- /dev/null
+++ b/src/cairo-fast-image-surface.c
@@ -0,0 +1,3490 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2003 University of Southern California
+ * Copyright © 2009,2010 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth at cworth.org>
+ * Chris Wilson <chris at chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-boxes-private.h"
+#include "cairo-clip-private.h"
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-gstate-private.h"
+#include "cairo-paginated-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-recording-surface-private.h"
+#include "cairo-region-private.h"
+#include "cairo-scaled-font-private.h"
+#include "cairo-surface-snapshot-private.h"
+#include "cairo-surface-subsurface-private.h"
+
+#define FAST_IMAGE_TOLERANCE 0.3
+
+
+/* Limit on the width / height of an image surface in pixels. This is
+ * mainly determined by coordinates of things sent to pixman at the
+ * moment being in 16.16 format. */
+#define MAX_IMAGE_SIZE 16383
+#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */
+
+#if CAIRO_NO_MUTEX
+#undef PIXMAN_HAS_ATOMIC_OPS
+#define PIXMAN_HAS_ATOMIC_OPS 1
+#endif
+
+/**
+ * SECTION:cairo-fast-image
+ * @Title: Fast [Low Quality] Image Surfaces
+ * @Short_Description: Rendering to memory buffers
+ * @See_Also: #cairo_surface_t
+ *
+ * Image surfaces provide the ability to render to memory buffers
+ * either allocated by cairo or by the calling code. The supported
+ * image formats are those defined in #cairo_format_t.
+ */
+
+static const cairo_surface_backend_t fast_image_surface_backend;
+
+typedef struct _cairo_fast_image_surface {
+ cairo_surface_t base;
+
+ pixman_image_t *pixman_image;
+ pixman_format_code_t pixman_format;
+ cairo_format_t format;
+
+ int width;
+ int height;
+} cairo_fast_image_surface_t;
+
+static pixman_image_t *
+_pixman_image_for_solid (const cairo_solid_pattern_t *pattern);
+
+static cairo_surface_t *
+fast_image_surface_create (pixman_format_code_t pixman_format,
+ int width,
+ int height)
+{
+ cairo_fast_image_surface_t *surface;
+ pixman_image_t *pixman_image;
+
+ pixman_image = pixman_image_create_bits (pixman_format, width, height,
+ NULL, 0);
+ if (unlikely (pixman_image == NULL))
+ return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+ surface = malloc (sizeof (cairo_fast_image_surface_t));
+ if (unlikely (surface == NULL)) {
+ pixman_image_unref (pixman_image);
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+
+ _cairo_surface_init (&surface->base,
+ &fast_image_surface_backend,
+ NULL, /* device */
+ _cairo_content_from_pixman_format (pixman_format));
+
+ surface->pixman_image = pixman_image;
+ surface->pixman_format = pixman_format;
+ surface->format = _cairo_format_from_pixman_format (pixman_format);
+ surface->base.is_clear = TRUE;
+
+ surface->width = width;
+ surface->height = height;
+
+ return &surface->base;
+}
+
+static cairo_bool_t
+is_size_valid (int width, int height)
+{
+ return 0 <= width && width <= MAX_IMAGE_SIZE &&
+ 0 <= height && height <= MAX_IMAGE_SIZE;
+}
+
+static cairo_surface_t *
+_cairo_fast_image_surface_create_similar (void *abstract_other,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ cairo_fast_image_surface_t *other = abstract_other;
+
+ if (! is_size_valid (width, height))
+ return _cairo_image_surface_create_with_content (content, width, height);
+
+ if (content == other->base.content)
+ return fast_image_surface_create (other->pixman_format, width, height);
+
+ return fast_image_surface_create (_cairo_format_to_pixman_format_code (_cairo_format_from_content (content)),
+ width, height);
+}
+
+static cairo_surface_t *
+_cairo_fast_image_surface_map_to_image (void *abstract_other,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_fast_image_surface_t *other = abstract_other;
+ cairo_surface_t *surface;
+ int stride = pixman_image_get_stride (other->pixman_image);
+ uint8_t *data;
+
+ data = (uint8_t *) pixman_image_get_data (other->pixman_image);
+ data += extents->y * stride;
+ data += extents->x * PIXMAN_FORMAT_BPP (other->pixman_format)/ 8;
+
+ surface =
+ _cairo_image_surface_create_with_pixman_format (data,
+ other->pixman_format,
+ extents->width,
+ extents->height,
+ stride);
+
+ cairo_surface_set_device_offset (surface, -extents->x, -extents->y);
+ return surface;
+}
+
+static cairo_int_status_t
+_cairo_fast_image_surface_unmap_image (void *abstract_surface,
+ cairo_image_surface_t *image)
+{
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_fast_image_surface_finish (void *abstract_surface)
+{
+ cairo_fast_image_surface_t *surface = abstract_surface;
+
+ if (surface->pixman_image) {
+ pixman_image_unref (surface->pixman_image);
+ surface->pixman_image = NULL;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_fast_image_surface_acquire_source_image (void *abstract_surface,
+ cairo_image_surface_t **image_out,
+ void **image_extra)
+{
+ cairo_fast_image_surface_t *surface = abstract_surface;
+
+ *image_out =
+ (cairo_image_surface_t *)
+ _cairo_image_surface_create_with_pixman_format ((uint8_t *) pixman_image_get_data (surface->pixman_image),
+ surface->pixman_format,
+ surface->width,
+ surface->height,
+ pixman_image_get_stride (surface->pixman_image));
+ *image_extra = NULL;
+
+ return (*image_out)->base.status;
+}
+
+static void
+_cairo_fast_image_surface_release_source_image (void *abstract_surface,
+ cairo_image_surface_t *image,
+ void *image_extra)
+{
+ cairo_surface_destroy (&image->base);
+}
+
+static pixman_op_t
+_pixman_operator (cairo_operator_t op)
+{
+ switch (op) {
+ case CAIRO_OPERATOR_CLEAR:
+ return PIXMAN_OP_CLEAR;
+
+ case CAIRO_OPERATOR_SOURCE:
+ return PIXMAN_OP_SRC;
+ case CAIRO_OPERATOR_OVER:
+ return PIXMAN_OP_OVER;
+ case CAIRO_OPERATOR_IN:
+ return PIXMAN_OP_IN;
+ case CAIRO_OPERATOR_OUT:
+ return PIXMAN_OP_OUT;
+ case CAIRO_OPERATOR_ATOP:
+ return PIXMAN_OP_ATOP;
+
+ case CAIRO_OPERATOR_DEST:
+ return PIXMAN_OP_DST;
+ case CAIRO_OPERATOR_DEST_OVER:
+ return PIXMAN_OP_OVER_REVERSE;
+ case CAIRO_OPERATOR_DEST_IN:
+ return PIXMAN_OP_IN_REVERSE;
+ case CAIRO_OPERATOR_DEST_OUT:
+ return PIXMAN_OP_OUT_REVERSE;
+ case CAIRO_OPERATOR_DEST_ATOP:
+ return PIXMAN_OP_ATOP_REVERSE;
+
+ case CAIRO_OPERATOR_XOR:
+ return PIXMAN_OP_XOR;
+ case CAIRO_OPERATOR_ADD:
+ return PIXMAN_OP_ADD;
+ case CAIRO_OPERATOR_SATURATE:
+ return PIXMAN_OP_SATURATE;
+
+ case CAIRO_OPERATOR_MULTIPLY:
+ return PIXMAN_OP_MULTIPLY;
+ case CAIRO_OPERATOR_SCREEN:
+ return PIXMAN_OP_SCREEN;
+ case CAIRO_OPERATOR_OVERLAY:
+ return PIXMAN_OP_OVERLAY;
+ case CAIRO_OPERATOR_DARKEN:
+ return PIXMAN_OP_DARKEN;
+ case CAIRO_OPERATOR_LIGHTEN:
+ return PIXMAN_OP_LIGHTEN;
+ case CAIRO_OPERATOR_COLOR_DODGE:
+ return PIXMAN_OP_COLOR_DODGE;
+ case CAIRO_OPERATOR_COLOR_BURN:
+ return PIXMAN_OP_COLOR_BURN;
+ case CAIRO_OPERATOR_HARD_LIGHT:
+ return PIXMAN_OP_HARD_LIGHT;
+ case CAIRO_OPERATOR_SOFT_LIGHT:
+ return PIXMAN_OP_SOFT_LIGHT;
+ case CAIRO_OPERATOR_DIFFERENCE:
+ return PIXMAN_OP_DIFFERENCE;
+ case CAIRO_OPERATOR_EXCLUSION:
+ return PIXMAN_OP_EXCLUSION;
+ case CAIRO_OPERATOR_HSL_HUE:
+ return PIXMAN_OP_HSL_HUE;
+ case CAIRO_OPERATOR_HSL_SATURATION:
+ return PIXMAN_OP_HSL_SATURATION;
+ case CAIRO_OPERATOR_HSL_COLOR:
+ return PIXMAN_OP_HSL_COLOR;
+ case CAIRO_OPERATOR_HSL_LUMINOSITY:
+ return PIXMAN_OP_HSL_LUMINOSITY;
+
+ default:
+ ASSERT_NOT_REACHED;
+ return PIXMAN_OP_OVER;
+ }
+}
+
+#if PIXMAN_HAS_ATOMIC_OPS
+static pixman_image_t *__pixman_transparent_image;
+static pixman_image_t *__pixman_black_image;
+static pixman_image_t *__pixman_white_image;
+
+static pixman_image_t *
+_pixman_transparent_image (void)
+{
+ pixman_image_t *image;
+
+ image = __pixman_transparent_image;
+ if (unlikely (image == NULL)) {
+ pixman_color_t color;
+
+ color.red = 0x00;
+ color.green = 0x00;
+ color.blue = 0x00;
+ color.alpha = 0x00;
+
+ image = pixman_image_create_solid_fill (&color);
+ if (unlikely (image == NULL))
+ return NULL;
+
+ if (_cairo_atomic_ptr_cmpxchg (&__pixman_transparent_image,
+ NULL, image))
+ {
+ pixman_image_ref (image);
+ }
+ } else {
+ pixman_image_ref (image);
+ }
+
+ return image;
+}
+
+static pixman_image_t *
+_pixman_black_image (void)
+{
+ pixman_image_t *image;
+
+ image = __pixman_black_image;
+ if (unlikely (image == NULL)) {
+ pixman_color_t color;
+
+ color.red = 0x00;
+ color.green = 0x00;
+ color.blue = 0x00;
+ color.alpha = 0xffff;
+
+ image = pixman_image_create_solid_fill (&color);
+ if (unlikely (image == NULL))
+ return NULL;
+
+ if (_cairo_atomic_ptr_cmpxchg (&__pixman_black_image,
+ NULL, image))
+ {
+ pixman_image_ref (image);
+ }
+ } else {
+ pixman_image_ref (image);
+ }
+
+ return image;
+}
+
+static pixman_image_t *
+_pixman_white_image (void)
+{
+ pixman_image_t *image;
+
+ image = __pixman_white_image;
+ if (unlikely (image == NULL)) {
+ pixman_color_t color;
+
+ color.red = 0xffff;
+ color.green = 0xffff;
+ color.blue = 0xffff;
+ color.alpha = 0xffff;
+
+ image = pixman_image_create_solid_fill (&color);
+ if (unlikely (image == NULL))
+ return NULL;
+
+ if (_cairo_atomic_ptr_cmpxchg (&__pixman_white_image,
+ NULL, image))
+ {
+ pixman_image_ref (image);
+ }
+ } else {
+ pixman_image_ref (image);
+ }
+
+ return image;
+}
+
+static uint32_t
+hars_petruska_f54_1_random (void)
+{
+#define rol(x,k) ((x << k) | (x >> (32-k)))
+ static uint32_t x;
+ return x = (x ^ rol (x, 5) ^ rol (x, 24)) + 0x37798849;
+#undef rol
+}
+
+static struct {
+ cairo_color_t color;
+ pixman_image_t *image;
+} cache[16];
+static int n_cached;
+
+#else /* !PIXMAN_HAS_ATOMIC_OPS */
+static pixman_image_t *
+_pixman_transparent_image (void)
+{
+ return _pixman_image_for_solid (&_cairo_pattern_clear);
+}
+
+static pixman_image_t *
+_pixman_black_image (void)
+{
+ return _pixman_image_for_solid (&_cairo_pattern_black);
+}
+
+static pixman_image_t *
+_pixman_white_image (void)
+{
+ return _pixman_image_for_solid (&_cairo_pattern_white);
+}
+#endif /* !PIXMAN_HAS_ATOMIC_OPS */
+
+void
+_cairo_fast_image_reset_static_data (void)
+{
+#if PIXMAN_HAS_ATOMIC_OPS
+ while (n_cached)
+ pixman_image_unref (cache[--n_cached].image);
+
+ if (__pixman_transparent_image) {
+ pixman_image_unref (__pixman_transparent_image);
+ __pixman_transparent_image = NULL;
+ }
+
+ if (__pixman_black_image) {
+ pixman_image_unref (__pixman_black_image);
+ __pixman_black_image = NULL;
+ }
+
+ if (__pixman_white_image) {
+ pixman_image_unref (__pixman_white_image);
+ __pixman_white_image = NULL;
+ }
+#endif
+}
+
+static pixman_image_t *
+_pixman_image_for_solid (const cairo_solid_pattern_t *pattern)
+{
+ pixman_color_t color;
+ pixman_image_t *image;
+
+#if PIXMAN_HAS_ATOMIC_OPS
+ int i;
+
+ if (pattern->color.alpha_short <= 0x00ff)
+ return _pixman_transparent_image ();
+
+ if (pattern->color.alpha_short >= 0xff00) {
+ if (pattern->color.red_short <= 0x00ff &&
+ pattern->color.green_short <= 0x00ff &&
+ pattern->color.blue_short <= 0x00ff)
+ {
+ return _pixman_black_image ();
+ }
+
+ if (pattern->color.red_short >= 0xff00 &&
+ pattern->color.green_short >= 0xff00 &&
+ pattern->color.blue_short >= 0xff00)
+ {
+ return _pixman_white_image ();
+ }
+ }
+
+ CAIRO_MUTEX_LOCK (_cairo_image_solid_cache_mutex);
+ for (i = 0; i < n_cached; i++) {
+ if (_cairo_color_equal (&cache[i].color, &pattern->color)) {
+ image = pixman_image_ref (cache[i].image);
+ goto UNLOCK;
+ }
+ }
+#endif
+
+ color.red = pattern->color.red_short;
+ color.green = pattern->color.green_short;
+ color.blue = pattern->color.blue_short;
+ color.alpha = pattern->color.alpha_short;
+
+ image = pixman_image_create_solid_fill (&color);
+#if PIXMAN_HAS_ATOMIC_OPS
+ if (image == NULL)
+ goto UNLOCK;
+
+ if (n_cached < ARRAY_LENGTH (cache)) {
+ i = n_cached++;
+ } else {
+ i = hars_petruska_f54_1_random () % ARRAY_LENGTH (cache);
+ pixman_image_unref (cache[i].image);
+ }
+ cache[i].image = pixman_image_ref (image);
+ cache[i].color = pattern->color;
+
+UNLOCK:
+ CAIRO_MUTEX_UNLOCK (_cairo_image_solid_cache_mutex);
+#endif
+ return image;
+}
+
+static pixman_image_t *
+_pixman_image_for_gradient (const cairo_gradient_pattern_t *pattern,
+ const cairo_rectangle_int_t *extents,
+ int *ix, int *iy)
+{
+ pixman_image_t *pixman_image;
+ pixman_gradient_stop_t pixman_stops_static[2];
+ pixman_gradient_stop_t *pixman_stops = pixman_stops_static;
+ pixman_transform_t pixman_transform;
+ cairo_matrix_t matrix;
+ cairo_circle_double_t extremes[2];
+ pixman_point_fixed_t p1, p2;
+ unsigned int i;
+ cairo_int_status_t status;
+
+ if (pattern->n_stops > ARRAY_LENGTH(pixman_stops_static)) {
+ pixman_stops = _cairo_malloc_ab (pattern->n_stops,
+ sizeof(pixman_gradient_stop_t));
+ if (unlikely (pixman_stops == NULL))
+ return NULL;
+ }
+
+ for (i = 0; i < pattern->n_stops; i++) {
+ pixman_stops[i].x = _cairo_fixed_16_16_from_double (pattern->stops[i].offset);
+ pixman_stops[i].color.red = pattern->stops[i].color.red_short;
+ pixman_stops[i].color.green = pattern->stops[i].color.green_short;
+ pixman_stops[i].color.blue = pattern->stops[i].color.blue_short;
+ pixman_stops[i].color.alpha = pattern->stops[i].color.alpha_short;
+ }
+
+ _cairo_gradient_pattern_fit_to_range (pattern, PIXMAN_MAX_INT >> 1, &matrix, extremes);
+
+ p1.x = _cairo_fixed_16_16_from_double (extremes[0].center.x);
+ p1.y = _cairo_fixed_16_16_from_double (extremes[0].center.y);
+ p2.x = _cairo_fixed_16_16_from_double (extremes[1].center.x);
+ p2.y = _cairo_fixed_16_16_from_double (extremes[1].center.y);
+
+ if (pattern->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
+ pixman_image = pixman_image_create_linear_gradient (&p1, &p2,
+ pixman_stops,
+ pattern->n_stops);
+ } else {
+ pixman_fixed_t r1, r2;
+
+ r1 = _cairo_fixed_16_16_from_double (extremes[0].radius);
+ r2 = _cairo_fixed_16_16_from_double (extremes[1].radius);
+
+ pixman_image = pixman_image_create_radial_gradient (&p1, &p2, r1, r2,
+ pixman_stops,
+ pattern->n_stops);
+ }
+
+ if (pixman_stops != pixman_stops_static)
+ free (pixman_stops);
+
+ if (unlikely (pixman_image == NULL))
+ return NULL;
+
+ *ix = *iy = 0;
+ status = _cairo_matrix_to_pixman_matrix_offset (&matrix, pattern->base.filter,
+ extents->x + extents->width/2.,
+ extents->y + extents->height/2.,
+ &pixman_transform, ix, iy);
+ if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) {
+ if (unlikely (status != CAIRO_INT_STATUS_SUCCESS) ||
+ ! pixman_image_set_transform (pixman_image, &pixman_transform))
+ {
+ pixman_image_unref (pixman_image);
+ return NULL;
+ }
+ }
+
+ {
+ pixman_repeat_t pixman_repeat;
+
+ switch (pattern->base.extend) {
+ default:
+ case CAIRO_EXTEND_NONE:
+ pixman_repeat = PIXMAN_REPEAT_NONE;
+ break;
+ case CAIRO_EXTEND_REPEAT:
+ pixman_repeat = PIXMAN_REPEAT_NORMAL;
+ break;
+ case CAIRO_EXTEND_REFLECT:
+ pixman_repeat = PIXMAN_REPEAT_REFLECT;
+ break;
+ case CAIRO_EXTEND_PAD:
+ pixman_repeat = PIXMAN_REPEAT_PAD;
+ break;
+ }
+
+ pixman_image_set_repeat (pixman_image, pixman_repeat);
+ }
+
+ return pixman_image;
+}
+
+struct acquire_source_cleanup {
+ cairo_surface_t *surface;
+ cairo_image_surface_t *image;
+ void *image_extra;
+};
+
+static void
+_acquire_source_cleanup (pixman_image_t *pixman_image,
+ void *closure)
+{
+ struct acquire_source_cleanup *data = closure;
+
+ _cairo_surface_release_source_image (data->surface,
+ data->image,
+ data->image_extra);
+ free (data);
+}
+
+static uint16_t
+expand_channel (uint16_t v, uint32_t bits)
+{
+ int offset = 16 - bits;
+ while (offset > 0) {
+ v |= v >> bits;
+ offset -= bits;
+ bits += bits;
+ }
+ return v;
+}
+
+static pixman_image_t *
+_pixel_to_solid (cairo_format_t format, uint8_t *data, int stride, int x, int y)
+{
+ uint32_t pixel;
+ pixman_color_t color;
+
+ switch (format) {
+ default:
+ ASSERT_NOT_REACHED;
+ return NULL;
+
+ case CAIRO_FORMAT_INVALID:
+ return NULL;
+
+ case CAIRO_FORMAT_A1:
+ pixel = *(uint8_t *) (data + y * stride + x/8);
+ return pixel & (1 << (x&7)) ? _pixman_black_image () : _pixman_transparent_image ();
+
+ case CAIRO_FORMAT_A8:
+ color.alpha = *(uint8_t *) (data + y * stride + x);
+ color.alpha |= color.alpha << 8;
+ if (color.alpha == 0)
+ return _pixman_transparent_image ();
+ if (color.alpha == 0xffff)
+ return _pixman_black_image ();
+
+ color.red = color.green = color.blue = 0;
+ return pixman_image_create_solid_fill (&color);
+
+ case CAIRO_FORMAT_RGB16_565:
+ pixel = *(uint16_t *) (data + y * stride + 2 * x);
+ if (pixel == 0)
+ return _pixman_black_image ();
+ if (pixel == 0xffff)
+ return _pixman_white_image ();
+
+ color.alpha = 0xffff;
+ color.red = expand_channel ((pixel >> 11 & 0x1f) << 11, 5);
+ color.green = expand_channel ((pixel >> 5 & 0x3f) << 10, 6);
+ color.blue = expand_channel ((pixel & 0x1f) << 11, 5);
+ return pixman_image_create_solid_fill (&color);
+
+ case CAIRO_FORMAT_RGB30:
+ pixel = *(uint32_t *) (data + y * stride + 4 * x);
+ pixel &= 0x3fffffff; /* ignore alpha bits */
+ if (pixel == 0)
+ return _pixman_black_image ();
+ if (pixel == 0x3fffffff)
+ return _pixman_white_image ();
+
+ /* convert 10bpc to 16bpc */
+ color.alpha = 0xffff;
+ color.red = expand_channel((pixel >> 20) & 0x3fff, 10);
+ color.green = expand_channel((pixel >> 10) & 0x3fff, 10);
+ color.blue = expand_channel(pixel & 0x3fff, 10);
+ return pixman_image_create_solid_fill (&color);
+
+ case CAIRO_FORMAT_ARGB32:
+ case CAIRO_FORMAT_RGB24:
+ pixel = *(uint32_t *) (data + y * stride + 4 * x);
+ color.alpha = format == CAIRO_FORMAT_ARGB32 ? (pixel >> 24) | (pixel >> 16 & 0xff00) : 0xffff;
+ if (color.alpha == 0)
+ return _pixman_transparent_image ();
+ if (pixel == 0xffffffff)
+ return _pixman_white_image ();
+ if (color.alpha == 0xffff && (pixel & 0xffffff) == 0)
+ return _pixman_black_image ();
+
+ color.red = (pixel >> 16 & 0xff) | (pixel >> 8 & 0xff00);
+ color.green = (pixel >> 8 & 0xff) | (pixel & 0xff00);
+ color.blue = (pixel & 0xff) | (pixel << 8 & 0xff00);
+ return pixman_image_create_solid_fill (&color);
+ }
+}
+
+static pixman_image_t *
+_fast_pixel_to_solid (cairo_fast_image_surface_t *image, int x, int y)
+{
+ uint8_t *data = (uint8_t *) pixman_image_get_data (image->pixman_image);
+ int stride = pixman_image_get_stride (image->pixman_image);
+ return _pixel_to_solid (image->format, data, stride, x, y);
+}
+
+static pixman_image_t *
+_image_pixel_to_solid (cairo_image_surface_t *image, int x, int y)
+{
+ return _pixel_to_solid (image->format, image->data, image->stride, x, y);
+}
+
+static pixman_image_t *
+_pixman_image_for_image (const cairo_surface_pattern_t *pattern,
+ cairo_bool_t is_mask,
+ const cairo_rectangle_int_t *extents,
+ const cairo_rectangle_int_t *sample,
+ cairo_extend_t *extend,
+ int *ix, int *iy)
+{
+ pixman_image_t *pixman_image = NULL;
+
+ if (pattern->surface->type == CAIRO_SURFACE_TYPE_IMAGE &&
+ (! is_mask || ! pattern->base.has_component_alpha ||
+ (pattern->surface->content & CAIRO_CONTENT_COLOR) == 0))
+ {
+ cairo_image_surface_t *source = (cairo_image_surface_t *) pattern->surface;
+ cairo_surface_type_t type;
+
+ if (source->base.backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT)
+ source = (cairo_image_surface_t *) ((cairo_surface_snapshot_t *) pattern->surface)->target;
+
+ type = source->base.backend->type;
+ if (type == CAIRO_SURFACE_TYPE_IMAGE) {
+ if (*extend != CAIRO_EXTEND_NONE &&
+ sample->x >= 0 &&
+ sample->y >= 0 &&
+ sample->x + sample->width <= source->width &&
+ sample->y + sample->height <= source->height)
+ {
+ *extend = CAIRO_EXTEND_NONE;
+ }
+
+ if (sample->width == 1 && sample->height == 1) {
+ if (sample->x < 0 ||
+ sample->y < 0 ||
+ sample->x >= source->width ||
+ sample->y >= source->height)
+ {
+ if (extend == CAIRO_EXTEND_NONE)
+ return _pixman_transparent_image ();
+ }
+ else
+ {
+ pixman_image = _image_pixel_to_solid (source,
+ sample->x, sample->y);
+ if (pixman_image)
+ return pixman_image;
+ }
+ }
+
+#if PIXMAN_HAS_ATOMIC_OPS
+ /* avoid allocating a 'pattern' image if we can reuse the original */
+ if (*extend == CAIRO_EXTEND_NONE &&
+ _cairo_matrix_is_pixman_translation (&pattern->base.matrix,
+ filter, ix, iy))
+ {
+ return pixman_image_ref (source->pixman_image);
+ }
+#endif
+
+ pixman_image = pixman_image_create_bits (source->pixman_format,
+ source->width,
+ source->height,
+ (uint32_t *) source->data,
+ source->stride);
+ if (unlikely (pixman_image == NULL))
+ return NULL;
+ } else if (type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
+ cairo_surface_subsurface_t *sub;
+ cairo_bool_t is_contained = FALSE;
+
+ sub = (cairo_surface_subsurface_t *) source;
+ source = (cairo_image_surface_t *) sub->target;
+
+ if (sample->x >= 0 &&
+ sample->y >= 0 &&
+ sample->x + sample->width <= sub->extents.width &&
+ sample->y + sample->height <= sub->extents.height)
+ {
+ is_contained = TRUE;
+ }
+
+ if (sample->width == 1 && sample->height == 1) {
+ if (is_contained) {
+ pixman_image = _image_pixel_to_solid (source,
+ sub->extents.x + sample->x,
+ sub->extents.y + sample->y);
+ if (pixman_image)
+ return pixman_image;
+ } else {
+ if (extend == CAIRO_EXTEND_NONE)
+ return _pixman_transparent_image ();
+ }
+ }
+
+#if PIXMAN_HAS_ATOMIC_OPS
+ *ix = sub->extents.x;
+ *iy = sub->extents.y;
+ if (is_contained &&
+ _cairo_matrix_is_pixman_translation (&pattern->base.matrix,
+ filter, ix, iy))
+ {
+ return pixman_image_ref (source->pixman_image);
+ }
+#endif
+
+ /* Avoid sub-byte offsets, force a copy in that case. */
+ if (PIXMAN_FORMAT_BPP (source->pixman_format) >= 8) {
+ void *data = source->data
+ + sub->extents.x * PIXMAN_FORMAT_BPP(source->pixman_format)/8
+ + sub->extents.y * source->stride;
+ pixman_image = pixman_image_create_bits (source->pixman_format,
+ sub->extents.width,
+ sub->extents.height,
+ data,
+ source->stride);
+ if (unlikely (pixman_image == NULL))
+ return NULL;
+ }
+ }
+ }
+
+ return pixman_image;
+}
+
+static pixman_image_t *
+_pixman_image_for_fast_image (const cairo_surface_pattern_t *pattern,
+ cairo_bool_t is_mask,
+ const cairo_rectangle_int_t *extents,
+ const cairo_rectangle_int_t *sample,
+ cairo_extend_t *extend,
+ int *ix, int *iy)
+{
+ pixman_image_t *pixman_image = NULL;
+
+ if (pattern->surface->type == CAIRO_SURFACE_TYPE_FASTIMAGE &&
+ (! is_mask || ! pattern->base.has_component_alpha ||
+ (pattern->surface->content & CAIRO_CONTENT_COLOR) == 0))
+ {
+ cairo_fast_image_surface_t *source = (cairo_fast_image_surface_t *) pattern->surface;
+ cairo_surface_type_t type;
+
+ if (source->base.backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT)
+ source = (cairo_fast_image_surface_t *) ((cairo_surface_snapshot_t *) pattern->surface)->target;
+
+ type = source->base.backend->type;
+ if (type == CAIRO_SURFACE_TYPE_FASTIMAGE) {
+ if (*extend != CAIRO_EXTEND_NONE &&
+ sample->x >= 0 &&
+ sample->y >= 0 &&
+ sample->x + sample->width <= source->width &&
+ sample->y + sample->height <= source->height)
+ {
+ *extend = CAIRO_EXTEND_NONE;
+ }
+
+ if (sample->width == 1 && sample->height == 1) {
+ if (sample->x < 0 ||
+ sample->y < 0 ||
+ sample->x >= source->width ||
+ sample->y >= source->height)
+ {
+ if (*extend == CAIRO_EXTEND_NONE)
+ return _pixman_transparent_image ();
+ }
+ else
+ {
+ pixman_image = _fast_pixel_to_solid (source,
+ sample->x, sample->y);
+ if (pixman_image)
+ return pixman_image;
+ }
+ }
+
+#if PIXMAN_HAS_ATOMIC_OPS
+ /* avoid allocating a 'pattern' image if we can reuse the original */
+ if (*extend == CAIRO_EXTEND_NONE &&
+ _cairo_matrix_is_pixman_translation (&pattern->base.matrix,
+ filter, ix, iy))
+ {
+ return pixman_image_ref (source->pixman_image);
+ }
+#endif
+
+ pixman_image = pixman_image_create_bits (source->pixman_format,
+ source->width,
+ source->height,
+ pixman_image_get_data (source->pixman_image),
+ pixman_image_get_stride (source->pixman_image));
+ if (unlikely (pixman_image == NULL))
+ return NULL;
+ } else if (type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
+ cairo_surface_subsurface_t *sub;
+ cairo_bool_t is_contained = FALSE;
+
+ sub = (cairo_surface_subsurface_t *) source;
+ source = (cairo_fast_image_surface_t *) sub->target;
+
+ if (sample->x >= 0 &&
+ sample->y >= 0 &&
+ sample->x + sample->width <= sub->extents.width &&
+ sample->y + sample->height <= sub->extents.height)
+ {
+ is_contained = TRUE;
+ }
+
+ if (sample->width == 1 && sample->height == 1) {
+ if (is_contained) {
+ pixman_image = _fast_pixel_to_solid (source,
+ sub->extents.x + sample->x,
+ sub->extents.y + sample->y);
+ if (pixman_image)
+ return pixman_image;
+ } else {
+ if (*extend == CAIRO_EXTEND_NONE)
+ return _pixman_transparent_image ();
+ }
+ }
+
+#if PIXMAN_HAS_ATOMIC_OPS
+ *ix = sub->extents.x;
+ *iy = sub->extents.y;
+ if (is_contained &&
+ _cairo_matrix_is_pixman_translation (&pattern->base.matrix,
+ filter, ix, iy))
+ {
+ return pixman_image_ref (source->pixman_image);
+ }
+#endif
+
+ /* Avoid sub-byte offsets, force a copy in that case. */
+ if (PIXMAN_FORMAT_BPP (source->pixman_format) >= 8) {
+ int stride = pixman_image_get_stride (source->pixman_image);
+ void *data = (uint8_t *) pixman_image_get_data (source->pixman_image)
+ + sub->extents.x * PIXMAN_FORMAT_BPP(source->pixman_format)/8
+ + sub->extents.y * stride;
+ pixman_image = pixman_image_create_bits (source->pixman_format,
+ sub->extents.width,
+ sub->extents.height,
+ data, stride);
+ }
+ }
+ }
+
+ return pixman_image;
+}
+
+static pixman_image_t *
+_pixman_image_for_surface (const cairo_surface_pattern_t *pattern,
+ cairo_bool_t is_mask,
+ const cairo_rectangle_int_t *extents,
+ cairo_matrix_t *dst_device_transform,
+ int *ix, int *iy)
+{
+ pixman_transform_t pixman_transform;
+ pixman_image_t *pixman_image;
+ cairo_matrix_t m;
+ cairo_rectangle_int_t sample;
+ cairo_extend_t extend;
+ cairo_filter_t filter;
+ cairo_int_status_t status;
+ cairo_bool_t undo_src_transform = FALSE;
+
+ extend = pattern->base.extend;
+ filter = _cairo_surface_pattern_sampled_area (pattern, extents, &sample);
+
+ *ix = *iy = 0;
+ pixman_image = _pixman_image_for_fast_image (pattern, is_mask,
+ extents, &sample, &extend,
+ ix, iy);
+ if (pixman_image == NULL)
+ pixman_image = _pixman_image_for_image (pattern, is_mask,
+ extents, &sample, &extend,
+ ix, iy);
+#if PIXMAN_HAS_ATOMIC_OPS
+ *ix = *iy = 0;
+#endif
+
+ if (pixman_image == NULL) {
+ struct acquire_source_cleanup *cleanup;
+ cairo_image_surface_t *image;
+ void *extra;
+ cairo_status_t status;
+
+ status = _cairo_surface_acquire_source_image_transformed (pattern->surface, dst_device_transform, &image, &extra);
+ if (unlikely (status))
+ return NULL;
+
+ if (sample.x >= 0 && sample.y >= 0 &&
+ sample.x + sample.width <= image->width &&
+ sample.y + sample.height <= image->height)
+ {
+ extend = CAIRO_EXTEND_NONE;
+ }
+
+ if (sample.width == 1 && sample.height == 1) {
+ if (sample.x < 0 ||
+ sample.y < 0 ||
+ sample.x >= image->width ||
+ sample.y >= image->height)
+ {
+ if (extend == CAIRO_EXTEND_NONE) {
+ pixman_image = _pixman_transparent_image ();
+ _cairo_surface_release_source_image (pattern->surface, image, extra);
+ return pixman_image;
+ }
+ }
+ else
+ {
+ pixman_image = _image_pixel_to_solid (image, sample.x, sample.y);
+ if (pixman_image)
+ {
+ _cairo_surface_release_source_image (pattern->surface, image, extra);
+ return pixman_image;
+ }
+ }
+ }
+
+ pixman_image = pixman_image_create_bits (image->pixman_format,
+ image->width,
+ image->height,
+ (uint32_t *) image->data,
+ image->stride);
+ if (unlikely (pixman_image == NULL)) {
+ _cairo_surface_release_source_image (pattern->surface, image, extra);
+ return NULL;
+ }
+
+ cleanup = malloc (sizeof (*cleanup));
+ if (unlikely (cleanup == NULL)) {
+ _cairo_surface_release_source_image (pattern->surface, image, extra);
+ pixman_image_unref (pixman_image);
+ return NULL;
+ }
+
+ cleanup->surface = pattern->surface;
+ cleanup->image = image;
+ cleanup->image_extra = extra;
+ pixman_image_set_destroy_function (pixman_image,
+ _acquire_source_cleanup, cleanup);
+ undo_src_transform = TRUE;
+ }
+
+ m = pattern->base.matrix;
+ if (undo_src_transform) {
+ cairo_matrix_t sm;
+
+ cairo_matrix_init_scale (&sm,
+ dst_device_transform->xx,
+ dst_device_transform->yy);
+ cairo_matrix_multiply (&m, &m, &sm);
+ }
+
+ status = _cairo_matrix_to_pixman_matrix_offset (&m, filter,
+ extents->x + extents->width/2.,
+ extents->y + extents->height/2.,
+ &pixman_transform, ix, iy);
+ if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
+ {
+ /* If the transform is an identity, we don't need to set it
+ * and we can use any filtering, so choose the fastest one. */
+ pixman_image_set_filter (pixman_image, PIXMAN_FILTER_NEAREST, NULL, 0);
+ }
+ else if (unlikely (status != CAIRO_INT_STATUS_SUCCESS ||
+ ! pixman_image_set_transform (pixman_image,
+ &pixman_transform)))
+ {
+ pixman_image_unref (pixman_image);
+ return NULL;
+ }
+ else
+ {
+ pixman_filter_t pixman_filter;
+
+ switch (filter) {
+ case CAIRO_FILTER_FAST:
+ pixman_filter = PIXMAN_FILTER_FAST;
+ break;
+ case CAIRO_FILTER_GOOD:
+ pixman_filter = PIXMAN_FILTER_GOOD;
+ break;
+ case CAIRO_FILTER_BEST:
+ pixman_filter = PIXMAN_FILTER_BEST;
+ break;
+ case CAIRO_FILTER_NEAREST:
+ pixman_filter = PIXMAN_FILTER_NEAREST;
+ break;
+ case CAIRO_FILTER_BILINEAR:
+ pixman_filter = PIXMAN_FILTER_BILINEAR;
+ break;
+ case CAIRO_FILTER_GAUSSIAN:
+ /* XXX: The GAUSSIAN value has no implementation in cairo
+ * whatsoever, so it was really a mistake to have it in the
+ * API. We could fix this by officially deprecating it, or
+ * else inventing semantics and providing an actual
+ * implementation for it. */
+ default:
+ pixman_filter = PIXMAN_FILTER_BEST;
+ }
+
+ pixman_image_set_filter (pixman_image, pixman_filter, NULL, 0);
+ }
+
+ {
+ pixman_repeat_t pixman_repeat;
+
+ switch (extend) {
+ default:
+ case CAIRO_EXTEND_NONE:
+ pixman_repeat = PIXMAN_REPEAT_NONE;
+ break;
+ case CAIRO_EXTEND_REPEAT:
+ pixman_repeat = PIXMAN_REPEAT_NORMAL;
+ break;
+ case CAIRO_EXTEND_REFLECT:
+ pixman_repeat = PIXMAN_REPEAT_REFLECT;
+ break;
+ case CAIRO_EXTEND_PAD:
+ pixman_repeat = PIXMAN_REPEAT_PAD;
+ break;
+ }
+
+ pixman_image_set_repeat (pixman_image, pixman_repeat);
+ }
+
+ if (pattern->base.has_component_alpha)
+ pixman_image_set_component_alpha (pixman_image, TRUE);
+
+ return pixman_image;
+}
+
+static pixman_image_t *
+_pixman_image_for_pattern (const cairo_pattern_t *pattern,
+ cairo_bool_t is_mask,
+ const cairo_rectangle_int_t *extents,
+ cairo_matrix_t *dst_device_transform,
+ int *tx, int *ty)
+{
+ *tx = *ty = 0;
+
+ if (pattern == NULL)
+ return _pixman_white_image ();
+
+ switch (pattern->type) {
+ default:
+ ASSERT_NOT_REACHED;
+ case CAIRO_PATTERN_TYPE_SOLID:
+ return _pixman_image_for_solid ((const cairo_solid_pattern_t *) pattern);
+
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ return _pixman_image_for_gradient ((const cairo_gradient_pattern_t *) pattern,
+ extents, tx, ty);
+
+ case CAIRO_PATTERN_TYPE_SURFACE:
+ return _pixman_image_for_surface ((const cairo_surface_pattern_t *) pattern,
+ is_mask, extents, dst_device_transform, tx, ty);
+
+ case CAIRO_PATTERN_TYPE_MESH: {
+ cairo_surface_t *image;
+ pixman_image_t *r;
+ void * data;
+ int width, height, stride;
+
+ *tx = -extents->x;
+ *ty = -extents->y;
+ width = extents->width;
+ height = extents->height;
+
+ image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
+ if (unlikely (image->status))
+ return NULL;
+
+ stride = cairo_image_surface_get_stride (image);
+ data = cairo_image_surface_get_data (image);
+
+ _cairo_mesh_pattern_rasterize ((cairo_mesh_pattern_t *) pattern,
+ data, width, height, stride, *tx, *ty);
+ r = pixman_image_ref (((cairo_image_surface_t *)image)->pixman_image);
+ cairo_surface_destroy (image);
+ return r;
+ }
+ }
+}
+
+static cairo_status_t
+fast_image_surface_fixup_unbounded (cairo_fast_image_surface_t *dst,
+ const cairo_composite_rectangles_t *rects,
+ cairo_clip_t *clip)
+{
+ cairo_surface_t *clip_surface = NULL;
+ pixman_image_t *mask = NULL;
+ pixman_box32_t boxes[4];
+ int i, mask_x = 0, mask_y = 0, n_boxes = 0;
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+ if (clip != NULL) {
+ int clip_x, clip_y;
+
+ clip_surface = _cairo_clip_get_surface (clip, &dst->base, &clip_x, &clip_y);
+ if (unlikely (clip_surface->status))
+ return clip_surface->status;
+
+ mask = ((cairo_image_surface_t *) clip_surface)->pixman_image;
+ mask_x = -clip_x;
+ mask_y = -clip_y;
+ } else {
+ if (rects->bounded.width == rects->unbounded.width &&
+ rects->bounded.height == rects->unbounded.height)
+ {
+ return CAIRO_STATUS_SUCCESS;
+ }
+ }
+
+ /* wholly unbounded? */
+ if (rects->bounded.width == 0 || rects->bounded.height == 0) {
+ int x = rects->unbounded.x;
+ int y = rects->unbounded.y;
+ int width = rects->unbounded.width;
+ int height = rects->unbounded.height;
+
+ if (mask != NULL) {
+ pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
+ mask, NULL, dst->pixman_image,
+ x + mask_x, y + mask_y,
+ 0, 0,
+ x, y,
+ width, height);
+ } else {
+ pixman_color_t color = { 0, };
+ pixman_box32_t box = { x, y, x + width, y + height };
+
+ if (! pixman_image_fill_boxes (PIXMAN_OP_CLEAR,
+ dst->pixman_image,
+ &color,
+ 1, &box))
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ cairo_surface_destroy (clip_surface);
+ return status;
+ }
+
+ /* top */
+ if (rects->bounded.y != rects->unbounded.y) {
+ boxes[n_boxes].x1 = rects->unbounded.x;
+ boxes[n_boxes].y1 = rects->unbounded.y;
+ boxes[n_boxes].x2 = rects->unbounded.x + rects->unbounded.width;
+ boxes[n_boxes].y2 = rects->bounded.y;
+ n_boxes++;
+ }
+
+ /* left */
+ if (rects->bounded.x != rects->unbounded.x) {
+ boxes[n_boxes].x1 = rects->unbounded.x;
+ boxes[n_boxes].y1 = rects->bounded.y;
+ boxes[n_boxes].x2 = rects->bounded.x;
+ boxes[n_boxes].y2 = rects->bounded.y + rects->bounded.height;
+ n_boxes++;
+ }
+
+ /* right */
+ if (rects->bounded.x + rects->bounded.width != rects->unbounded.x + rects->unbounded.width) {
+ boxes[n_boxes].x1 = rects->bounded.x + rects->bounded.width;
+ boxes[n_boxes].y1 = rects->bounded.y;
+ boxes[n_boxes].x2 = rects->unbounded.x + rects->unbounded.width;
+ boxes[n_boxes].y2 = rects->bounded.y + rects->bounded.height;
+ n_boxes++;
+ }
+
+ /* bottom */
+ if (rects->bounded.y + rects->bounded.height != rects->unbounded.y + rects->unbounded.height) {
+ boxes[n_boxes].x1 = rects->unbounded.x;
+ boxes[n_boxes].y1 = rects->bounded.y + rects->bounded.height;
+ boxes[n_boxes].x2 = rects->unbounded.x + rects->unbounded.width;
+ boxes[n_boxes].y2 = rects->unbounded.y + rects->unbounded.height;
+ n_boxes++;
+ }
+
+ if (mask != NULL) {
+ for (i = 0; i < n_boxes; i++) {
+ pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
+ mask, NULL, dst->pixman_image,
+ boxes[i].x1 + mask_x, boxes[i].y1 + mask_y,
+ 0, 0,
+ boxes[i].x1, boxes[i].y1,
+ boxes[i].x2 - boxes[i].x1, boxes[i].y2 - boxes[i].y1);
+ }
+ } else {
+ pixman_color_t color = { 0, };
+
+ if (! pixman_image_fill_boxes (PIXMAN_OP_CLEAR,
+ dst->pixman_image,
+ &color,
+ n_boxes,
+ boxes))
+ {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+ }
+
+ cairo_surface_destroy (clip_surface);
+ return status;
+}
+
+static cairo_status_t
+fast_image_surface_fixup_unbounded_boxes (cairo_fast_image_surface_t *dst,
+ const cairo_composite_rectangles_t *extents,
+ cairo_region_t *clip_region,
+ cairo_boxes_t *boxes)
+{
+ cairo_boxes_t clear;
+ cairo_box_t box;
+ cairo_status_t status;
+ struct _cairo_boxes_chunk *chunk;
+ int i;
+
+ if (boxes->num_boxes <= 1 && clip_region == NULL)
+ return fast_image_surface_fixup_unbounded (dst, extents, NULL);
+
+ _cairo_boxes_init (&clear);
+
+ box.p1.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width);
+ box.p1.y = _cairo_fixed_from_int (extents->unbounded.y);
+ box.p2.x = _cairo_fixed_from_int (extents->unbounded.x);
+ box.p2.y = _cairo_fixed_from_int (extents->unbounded.y + extents->unbounded.height);
+
+ if (clip_region == NULL) {
+ cairo_boxes_t tmp;
+
+ _cairo_boxes_init (&tmp);
+
+ status = _cairo_boxes_add (&tmp, CAIRO_ANTIALIAS_DEFAULT, &box);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ tmp.chunks.next = &boxes->chunks;
+ tmp.num_boxes += boxes->num_boxes;
+
+ status = _cairo_bentley_ottmann_tessellate_boxes (&tmp,
+ CAIRO_FILL_RULE_WINDING,
+ &clear);
+
+ tmp.chunks.next = NULL;
+ } else {
+ pixman_box32_t *pbox;
+
+ pbox = pixman_region32_rectangles (&clip_region->rgn, &i);
+ _cairo_boxes_limit (&clear, (cairo_box_t *) pbox, i);
+
+ status = _cairo_boxes_add (&clear, CAIRO_ANTIALIAS_DEFAULT, &box);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+ for (i = 0; i < chunk->count; i++) {
+ status = _cairo_boxes_add (&clear,
+ CAIRO_ANTIALIAS_DEFAULT,
+ &chunk->base[i]);
+ if (unlikely (status)) {
+ _cairo_boxes_fini (&clear);
+ return status;
+ }
+ }
+ }
+
+ status = _cairo_bentley_ottmann_tessellate_boxes (&clear,
+ CAIRO_FILL_RULE_WINDING,
+ &clear);
+ }
+
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ for (chunk = &clear.chunks; chunk != NULL; chunk = chunk->next) {
+ for (i = 0; i < chunk->count; i++) {
+ int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x);
+ int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y);
+ int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x);
+ int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y);
+
+ pixman_fill (pixman_image_get_data (dst->pixman_image),
+ pixman_image_get_stride (dst->pixman_image) / sizeof (uint32_t),
+ PIXMAN_FORMAT_BPP (dst->pixman_format),
+ x1, y1, x2 - x1, y2 - y1,
+ 0);
+ }
+ }
+ }
+
+ _cairo_boxes_fini (&clear);
+
+ return status;
+}
+
+static cairo_bool_t
+can_reduce_alpha_op (cairo_operator_t op)
+{
+ int iop = op;
+ switch (iop) {
+ case CAIRO_OPERATOR_OVER:
+ case CAIRO_OPERATOR_SOURCE:
+ case CAIRO_OPERATOR_ADD:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+static cairo_bool_t
+reduce_alpha_op (cairo_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern)
+{
+ return dst->is_clear &&
+ dst->content == CAIRO_CONTENT_ALPHA &&
+ _cairo_pattern_is_opaque_solid (pattern) &&
+ can_reduce_alpha_op (op);
+}
+
+/* low level compositor */
+typedef cairo_status_t
+(*image_draw_func_t) (void *closure,
+ cairo_fast_image_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ double opacity,
+ int dst_x,
+ int dst_y,
+ const cairo_composite_rectangles_t *extents);
+
+static pixman_image_t *
+_create_composite_mask_pattern (cairo_composite_rectangles_t *extents,
+ image_draw_func_t draw_func,
+ void *draw_closure,
+ double opacity,
+ cairo_fast_image_surface_t *dst)
+{
+ cairo_region_t *clip_region = _cairo_clip_get_region (extents->clip);
+ cairo_bool_t need_clip_surface = ! _cairo_clip_is_region (extents->clip);
+ cairo_fast_image_surface_t *mask;
+ pixman_image_t *image;
+ cairo_status_t status;
+
+ if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1)
+ clip_region = NULL;
+
+ mask = (cairo_fast_image_surface_t *)
+ fast_image_surface_create (PIXMAN_a8,
+ extents->bounded.width,
+ extents->bounded.height);
+ if (unlikely (mask->base.status))
+ return NULL;
+
+ /* Is it worth setting the clip region here? */
+ if (clip_region != NULL) {
+ pixman_bool_t ret;
+
+ pixman_region32_translate (&clip_region->rgn, -extents->bounded.x, -extents->bounded.y);
+ ret = pixman_image_set_clip_region32 (mask->pixman_image,
+ &clip_region->rgn);
+ pixman_region32_translate (&clip_region->rgn, extents->bounded.x, extents->bounded.y);
+
+ if (! ret) {
+ cairo_surface_destroy (&mask->base);
+ return NULL;
+ }
+ }
+
+ mask->base.device_transform = dst->base.device_transform;
+ status = draw_func (draw_closure, mask,
+ CAIRO_OPERATOR_ADD, NULL, opacity,
+ extents->bounded.x, extents->bounded.y,
+ extents);
+ if (unlikely (status)) {
+ cairo_surface_destroy (&mask->base);
+ return NULL;
+ }
+
+ if (need_clip_surface) {
+ status = _cairo_clip_combine_with_surface (extents->clip, &mask->base,
+ extents->bounded.x,
+ extents->bounded.y);
+ if (unlikely (status)) {
+ cairo_surface_destroy (&mask->base);
+ return NULL;
+ }
+ }
+
+ if (clip_region != NULL)
+ pixman_image_set_clip_region (mask->pixman_image, NULL);
+
+ image = mask->pixman_image;
+ pixman_image_ref (image);
+ cairo_surface_destroy (&mask->base);
+
+ return image;
+}
+
+/* Handles compositing with a clip surface when the operator allows
+ * us to combine the clip with the mask
+ */
+static cairo_status_t
+_clip_and_composite_with_mask (cairo_composite_rectangles_t *extents,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ double opacity,
+ image_draw_func_t draw_func,
+ void *draw_closure,
+ cairo_fast_image_surface_t *dst)
+{
+ pixman_image_t *mask;
+
+ mask = _create_composite_mask_pattern (extents,
+ draw_func, draw_closure,
+ opacity,
+ dst);
+ if (unlikely (mask == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ if (pattern == NULL) {
+ if (dst->pixman_format == PIXMAN_a8) {
+ pixman_image_composite32 (_pixman_operator (op),
+ mask, NULL, dst->pixman_image,
+ 0, 0, 0, 0,
+ extents->bounded.x, extents->bounded.y,
+ extents->bounded.width, extents->bounded.height);
+ } else {
+ pixman_image_t *src;
+
+ src = _pixman_white_image ();
+ if (unlikely (src == NULL)) {
+ pixman_image_unref (mask);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ pixman_image_composite32 (_pixman_operator (op),
+ src, mask, dst->pixman_image,
+ 0, 0, 0, 0,
+ extents->bounded.x, extents->bounded.y,
+ extents->bounded.width, extents->bounded.height);
+ pixman_image_unref (src);
+ }
+ } else {
+ pixman_image_t *src;
+ int src_x, src_y;
+
+ src = _pixman_image_for_pattern (pattern, FALSE, &extents->bounded,
+ &dst->base.device_transform,
+ &src_x, &src_y);
+ if (unlikely (src == NULL)) {
+ pixman_image_unref (mask);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ pixman_image_composite32 (_pixman_operator (op),
+ src, mask, dst->pixman_image,
+ extents->bounded.x + src_x, extents->bounded.y + src_y,
+ 0, 0,
+ extents->bounded.x, extents->bounded.y,
+ extents->bounded.width, extents->bounded.height);
+ pixman_image_unref (src);
+ }
+
+ pixman_image_unref (mask);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/* Handles compositing with a clip surface when we have to do the operation
+ * in two pieces and combine them together.
+ */
+static cairo_status_t
+_clip_and_composite_combine (cairo_composite_rectangles_t *extents,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ double opacity,
+ image_draw_func_t draw_func,
+ void *draw_closure,
+ cairo_fast_image_surface_t *dst)
+{
+ cairo_fast_image_surface_t *tmp;
+ cairo_surface_t *clip_surface;
+ int clip_x, clip_y;
+ cairo_status_t status;
+
+ tmp = (cairo_fast_image_surface_t *)
+ fast_image_surface_create (dst->pixman_format,
+ extents->bounded.width,
+ extents->bounded.height);
+ if (unlikely (tmp->base.status))
+ return tmp->base.status;
+
+ tmp->base.device_transform = dst->base.device_transform;
+ if (src == NULL) {
+ status = (*draw_func) (draw_closure, tmp,
+ CAIRO_OPERATOR_ADD, NULL, opacity,
+ extents->bounded.x, extents->bounded.y,
+ extents);
+ } else {
+ /* Initialize the temporary surface from the destination surface */
+ if (! dst->base.is_clear) {
+ pixman_image_composite32 (PIXMAN_OP_SRC,
+ dst->pixman_image, NULL, tmp->pixman_image,
+ extents->bounded.x,
+ extents->bounded.y,
+ 0, 0,
+ 0, 0,
+ extents->bounded.width,
+ extents->bounded.height);
+ }
+
+ status = (*draw_func) (draw_closure, tmp, op, src, opacity,
+ extents->bounded.x, extents->bounded.y,
+ extents);
+ }
+ if (unlikely (status))
+ goto CLEANUP_SURFACE;
+
+ clip_surface = _cairo_clip_get_surface (extents->clip, &dst->base,
+ &clip_x, &clip_y);
+ if (unlikely (clip_surface->status))
+ goto CLEANUP_SURFACE;
+
+ if (! dst->base.is_clear) {
+#if PIXMAN_HAS_OP_LERP
+ pixman_image_composite32 (PIXMAN_OP_LERP,
+ tmp->pixman_image,
+ ((cairo_image_surface_t *) clip_surface)->pixman_image,
+ dst->pixman_image,
+ 0, 0,
+ extents->bounded.x - clip_x,
+ extents->bounded.y - clip_y,
+ extents->bounded.x, extents->bounded.y,
+ extents->bounded.width, extents->bounded.height);
+#else
+ /* Punch the clip out of the destination */
+ pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
+ ((cairo_image_surface_t *) clip_surface)->pixman_image,
+ NULL,
+ dst->pixman_image,
+ extents->bounded.x - clip_x,
+ extents->bounded.y - clip_y,
+ 0, 0,
+ extents->bounded.x, extents->bounded.y,
+ extents->bounded.width, extents->bounded.height);
+
+ /* Now add the two results together */
+ pixman_image_composite32 (PIXMAN_OP_ADD,
+ tmp->pixman_image,
+ ((cairo_image_surface_t *) clip_surface)->pixman_image,
+ dst->pixman_image,
+ 0, 0,
+ extents->bounded.x - clip_x,
+ extents->bounded.y - clip_y,
+ extents->bounded.x, extents->bounded.y,
+ extents->bounded.width, extents->bounded.height);
+#endif
+ } else {
+ pixman_image_composite32 (PIXMAN_OP_SRC,
+ tmp->pixman_image,
+ ((cairo_image_surface_t *) clip_surface)->pixman_image,
+ dst->pixman_image,
+ 0, 0,
+ extents->bounded.x - clip_x,
+ extents->bounded.y - clip_y,
+ extents->bounded.x, extents->bounded.y,
+ extents->bounded.width, extents->bounded.height);
+ }
+
+ cairo_surface_destroy (clip_surface);
+ CLEANUP_SURFACE:
+ cairo_surface_destroy (&tmp->base);
+
+ return status;
+}
+
+/* Handles compositing for %CAIRO_OPERATOR_SOURCE, which is special; it's
+ * defined as (src IN mask IN clip) ADD (dst OUT (mask IN clip))
+ */
+static cairo_status_t
+_clip_and_composite_source (cairo_composite_rectangles_t *extents,
+ const cairo_pattern_t *pattern,
+ double opacity,
+ image_draw_func_t draw_func,
+ void *draw_closure,
+ cairo_fast_image_surface_t *dst)
+{
+ pixman_image_t *mask, *src;
+ int src_x, src_y;
+
+ if (pattern == NULL) {
+ cairo_status_t status;
+
+ status = draw_func (draw_closure, dst,
+ CAIRO_OPERATOR_SOURCE, NULL, opacity,
+ extents->bounded.x, extents->bounded.y,
+ extents);
+ if (unlikely (status))
+ return status;
+
+ if (! _cairo_clip_is_region (extents->clip))
+ status = _cairo_clip_combine_with_surface (extents->clip,
+ &dst->base, 0, 0);
+
+ return status;
+ }
+
+ /* Create a surface that is mask IN clip */
+ mask = _create_composite_mask_pattern (extents,
+ draw_func, draw_closure, opacity,
+ dst);
+ if (unlikely (mask == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ src = _pixman_image_for_pattern (pattern, FALSE, &extents->bounded,
+ &dst->base.device_transform,
+ &src_x, &src_y);
+ if (unlikely (src == NULL)) {
+ pixman_image_unref (mask);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ if (! dst->base.is_clear) {
+#if PIXMAN_HAS_OP_LERP
+ pixman_image_composite32 (PIXMAN_OP_LERP,
+ src, mask, dst->pixman_image,
+ extents->bounded.x + src_x, extents->bounded.y + src_y,
+ 0, 0,
+ extents->bounded.x, extents->bounded.y,
+ extents->bounded.width, extents->bounded.height);
+#else
+ /* Compute dest' = dest OUT (mask IN clip) */
+ pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
+ mask, NULL, dst->pixman_image,
+ 0, 0, 0, 0,
+ extents->bounded.x, extents->bounded.y,
+ extents->bounded.width, extents->bounded.height);
+
+ /* Now compute (src IN (mask IN clip)) ADD dest' */
+ pixman_image_composite32 (PIXMAN_OP_ADD,
+ src, mask, dst->pixman_image,
+ extents->bounded.x + src_x, extents->bounded.y + src_y,
+ 0, 0,
+ extents->bounded.x, extents->bounded.y,
+ extents->bounded.width, extents->bounded.height);
+#endif
+ } else {
+ pixman_image_composite32 (PIXMAN_OP_SRC,
+ src, mask, dst->pixman_image,
+ extents->bounded.x + src_x, extents->bounded.y + src_y,
+ 0, 0,
+ extents->bounded.x, extents->bounded.y,
+ extents->bounded.width, extents->bounded.height);
+ }
+
+ pixman_image_unref (src);
+ pixman_image_unref (mask);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+enum {
+ NEED_CLIP_REGION = 0x1,
+ NEED_CLIP_SURFACE = 0x2,
+ FORCE_CLIP_REGION = 0x4,
+};
+
+static cairo_bool_t
+need_bounded_clip (cairo_composite_rectangles_t *extents)
+{
+ unsigned int flags = NEED_CLIP_REGION;
+ if (! _cairo_clip_is_region (extents->clip))
+ flags |= NEED_CLIP_SURFACE;
+ return flags;
+}
+
+static cairo_bool_t
+need_unbounded_clip (cairo_composite_rectangles_t *extents)
+{
+ unsigned int flags = 0;
+ if (! extents->is_bounded) {
+ flags |= NEED_CLIP_REGION;
+ if (! _cairo_clip_is_region (extents->clip))
+ flags |= NEED_CLIP_SURFACE;
+ }
+ if (extents->clip->path != NULL)
+ flags |= NEED_CLIP_SURFACE;
+ return flags;
+}
+
+
+static cairo_status_t
+_clip_and_composite (cairo_fast_image_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ double opacity,
+ image_draw_func_t draw_func,
+ void *draw_closure,
+ cairo_composite_rectangles_t*extents,
+ unsigned int need_clip)
+{
+ cairo_region_t *clip_region = NULL;
+ cairo_status_t status;
+
+ if (need_clip & NEED_CLIP_REGION) {
+ clip_region = _cairo_clip_get_region (extents->clip);
+ if ((need_clip & FORCE_CLIP_REGION) == 0 &&
+ cairo_region_contains_rectangle (clip_region,
+ &extents->unbounded) == CAIRO_REGION_OVERLAP_IN)
+ clip_region = NULL;
+ if (clip_region != NULL)
+ pixman_image_set_clip_region32 (dst->pixman_image,
+ &clip_region->rgn);
+ }
+
+ if (reduce_alpha_op (&dst->base, op, src)) {
+ op = CAIRO_OPERATOR_ADD;
+ src = NULL;
+ }
+
+ if (op == CAIRO_OPERATOR_SOURCE) {
+ status = _clip_and_composite_source (extents, src, opacity,
+ draw_func, draw_closure, dst);
+ } else {
+ if (op == CAIRO_OPERATOR_CLEAR) {
+ src = NULL;
+ op = CAIRO_OPERATOR_DEST_OUT;
+ }
+
+ if (need_clip & NEED_CLIP_SURFACE) {
+ if (extents->is_bounded) {
+ status = _clip_and_composite_with_mask (extents, op, src, opacity,
+ draw_func, draw_closure,
+ dst);
+ } else {
+ status = _clip_and_composite_combine (extents, op, src, opacity,
+ draw_func, draw_closure,
+ dst);
+ }
+ } else {
+ status = draw_func (draw_closure, dst, op, src, opacity,
+ 0, 0, extents);
+ }
+ }
+
+ if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded) {
+ status = fast_image_surface_fixup_unbounded (dst, extents,
+ need_clip & NEED_CLIP_SURFACE ? extents->clip : NULL);
+ }
+
+ if (clip_region != NULL)
+ pixman_image_set_clip_region32 (dst->pixman_image, NULL);
+
+ return status;
+}
+
+static inline uint32_t
+color_to_uint32 (const cairo_color_t *color)
+{
+ return
+ (color->alpha_short >> 8 << 24) |
+ (color->red_short >> 8 << 16) |
+ (color->green_short & 0xff00) |
+ (color->blue_short >> 8);
+}
+
+static inline cairo_bool_t
+color_to_pixel (const cairo_color_t *color,
+ double opacity,
+ pixman_format_code_t format,
+ uint32_t *pixel)
+{
+ cairo_color_t opacity_color;
+ uint32_t c;
+
+ if (!(format == PIXMAN_a8r8g8b8 ||
+ format == PIXMAN_x8r8g8b8 ||
+ format == PIXMAN_a8b8g8r8 ||
+ format == PIXMAN_x8b8g8r8 ||
+ format == PIXMAN_b8g8r8a8 ||
+ format == PIXMAN_b8g8r8x8 ||
+ format == PIXMAN_r5g6b5 ||
+ format == PIXMAN_b5g6r5 ||
+ format == PIXMAN_a8))
+ {
+ return FALSE;
+ }
+
+ if (opacity != 1.0) {
+ _cairo_color_init_rgba (&opacity_color,
+ color->red,
+ color->green,
+ color->blue,
+ color->alpha * opacity);
+ color = &opacity_color;
+ }
+ c = color_to_uint32 (color);
+
+ if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_ABGR) {
+ c = ((c & 0xff000000) >> 0) |
+ ((c & 0x00ff0000) >> 16) |
+ ((c & 0x0000ff00) >> 0) |
+ ((c & 0x000000ff) << 16);
+ }
+
+ if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_BGRA) {
+ c = ((c & 0xff000000) >> 24) |
+ ((c & 0x00ff0000) >> 8) |
+ ((c & 0x0000ff00) << 8) |
+ ((c & 0x000000ff) << 24);
+ }
+
+ if (format == PIXMAN_a8) {
+ c = c >> 24;
+ } else if (format == PIXMAN_r5g6b5 || format == PIXMAN_b5g6r5) {
+ c = ((((c) >> 3) & 0x001f) |
+ (((c) >> 5) & 0x07e0) |
+ (((c) >> 8) & 0xf800));
+ }
+
+ *pixel = c;
+ return TRUE;
+}
+
+static inline cairo_bool_t
+pattern_to_pixel (const cairo_solid_pattern_t *solid,
+ double opacity,
+ cairo_operator_t op,
+ pixman_format_code_t format,
+ uint32_t *pixel)
+{
+ if (op == CAIRO_OPERATOR_CLEAR) {
+ *pixel = 0;
+ return TRUE;
+ }
+
+ if (solid->base.type != CAIRO_PATTERN_TYPE_SOLID)
+ return FALSE;
+
+ if (op == CAIRO_OPERATOR_OVER) {
+ if (opacity * solid->color.alpha_short >= 0xff00)
+ op = CAIRO_OPERATOR_SOURCE;
+ }
+
+ if (op != CAIRO_OPERATOR_SOURCE)
+ return FALSE;
+
+ return color_to_pixel (&solid->color, opacity, format, pixel);
+}
+
+typedef struct _cairo_fast_image_span_renderer {
+ cairo_span_renderer_t base;
+ pixman_image_compositor_t *compositor;
+ pixman_image_t *src;
+ float opacity;
+ cairo_rectangle_int_t extents;
+} fast_image_span_renderer_t;
+
+static cairo_status_t
+fast_image_bounded_opaque_spans (void *abstract_renderer,
+ int y, int height,
+ const cairo_half_open_span_t *spans,
+ unsigned num_spans)
+{
+ fast_image_span_renderer_t *r = abstract_renderer;
+
+ if (num_spans == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ do {
+ if (spans[0].coverage)
+ pixman_image_compositor_blt (r->compositor,
+ spans[0].x, y,
+ spans[1].x - spans[0].x, height,
+ spans[0].coverage);
+ spans++;
+ } while (--num_spans > 1);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+fast_image_bounded_spans (void *abstract_renderer,
+ int y, int height,
+ const cairo_half_open_span_t *spans,
+ unsigned num_spans)
+{
+ fast_image_span_renderer_t *r = abstract_renderer;
+
+ if (num_spans == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ do {
+ if (spans[0].coverage) {
+ pixman_image_compositor_blt (r->compositor,
+ spans[0].x, y,
+ spans[1].x - spans[0].x, height,
+ r->opacity * spans[0].coverage);
+ }
+ spans++;
+ } while (--num_spans > 1);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+fast_image_unbounded_spans (void *abstract_renderer,
+ int y, int height,
+ const cairo_half_open_span_t *spans,
+ unsigned num_spans)
+{
+ fast_image_span_renderer_t *r = abstract_renderer;
+
+ if (y > r->extents.y) {
+ pixman_image_compositor_blt (r->compositor,
+ r->extents.x, r->extents.y,
+ r->extents.width, y - r->extents.y,
+ 0);
+ }
+
+ if (num_spans == 0) {
+ pixman_image_compositor_blt (r->compositor,
+ r->extents.x, y,
+ r->extents.width, height,
+ 0);
+ } else {
+ if (spans[0].x != r->extents.x) {
+ pixman_image_compositor_blt (r->compositor,
+ r->extents.x, y,
+ spans[0].x - r->extents.x,
+ height,
+ 0);
+ }
+
+ do {
+ pixman_image_compositor_blt (r->compositor,
+ spans[0].x, y,
+ spans[1].x - spans[0].x, height,
+ r->opacity * spans[0].coverage);
+ spans++;
+ } while (--num_spans > 1);
+
+ if (spans[0].x != r->extents.x + r->extents.width) {
+ pixman_image_compositor_blt (r->compositor,
+ spans[0].x, y,
+ r->extents.x + r->extents.width - spans[0].x, height,
+ 0);
+ }
+ }
+
+ r->extents.y = y + height;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+fast_image_clipped_spans (void *abstract_renderer,
+ int y, int height,
+ const cairo_half_open_span_t *spans,
+ unsigned num_spans)
+{
+ fast_image_span_renderer_t *r = abstract_renderer;
+
+ assert (num_spans);
+
+ do {
+ if (! spans[0].is_clipped)
+ pixman_image_compositor_blt (r->compositor,
+ spans[0].x, y,
+ spans[1].x - spans[0].x, height,
+ r->opacity * spans[0].coverage);
+ spans++;
+ } while (--num_spans > 1);
+
+ r->extents.y = y + height;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+fast_image_finish_unbounded_spans (void *abstract_renderer)
+{
+ fast_image_span_renderer_t *r = abstract_renderer;
+
+ if (r->extents.y < r->extents.height) {
+ pixman_image_compositor_blt (r->compositor,
+ r->extents.x, r->extents.y,
+ r->extents.width,
+ r->extents.height - r->extents.y,
+ 0);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+fast_image_span_renderer_init (fast_image_span_renderer_t *r,
+ cairo_fast_image_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ float opacity,
+ const cairo_composite_rectangles_t *composite,
+ cairo_bool_t needs_clip)
+{
+ int src_x, src_y;
+
+ if (op == CAIRO_OPERATOR_CLEAR) {
+ op = CAIRO_OPERATOR_DEST_OUT;
+ pattern = NULL;
+ }
+
+ r->src = _pixman_image_for_pattern (pattern, FALSE,
+ &composite->bounded,
+ &dst->base.device_transform,
+ &src_x, &src_y);
+ if (unlikely (r->src == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ if (composite->is_bounded) {
+ if (opacity == 1.)
+ r->base.render_rows = fast_image_bounded_opaque_spans;
+ else
+ r->base.render_rows = fast_image_bounded_spans;
+ r->base.finish = NULL;
+ } else {
+ if (needs_clip)
+ r->base.render_rows = fast_image_clipped_spans;
+ else
+ r->base.render_rows = fast_image_unbounded_spans;
+ r->base.finish = fast_image_finish_unbounded_spans;
+ r->extents = composite->unbounded;
+ r->extents.height += r->extents.y;
+
+ }
+ r->compositor =
+ pixman_image_create_compositor (_pixman_operator (op),
+ r->src, NULL, dst->pixman_image,
+ composite->bounded.x + src_x,
+ composite->bounded.y + src_y,
+ 0, 0,
+ composite->bounded.x,
+ composite->bounded.y,
+ composite->bounded.width,
+ composite->bounded.height);
+ r->opacity = opacity;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_image_span_renderer_fini (fast_image_span_renderer_t *r)
+{
+ if (r->base.finish)
+ r->base.finish (r);
+
+ pixman_image_compositor_destroy (r->compositor);
+ pixman_image_unref (r->src);
+}
+
+static cairo_status_t
+_composite_unaligned_boxes (cairo_fast_image_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ double opacity,
+ const cairo_boxes_t *boxes,
+ const cairo_composite_rectangles_t *extents)
+{
+ fast_image_span_renderer_t renderer;
+ cairo_rectangular_scan_converter_t converter;
+ const struct _cairo_boxes_chunk *chunk;
+ cairo_status_t status;
+ int i;
+
+ status = fast_image_span_renderer_init (&renderer,
+ dst, op, pattern, opacity,
+ extents, FALSE);
+ if (unlikely (status))
+ return status;
+
+ assert (! extents->clip->path);
+ _cairo_rectangular_scan_converter_init (&converter, &extents->bounded);
+
+ for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+ const cairo_box_t *box = chunk->base;
+
+ for (i = 0; i < chunk->count; i++) {
+ status = _cairo_rectangular_scan_converter_add_box (&converter, &box[i], 1);
+ if (unlikely (status))
+ goto CLEANUP;
+ }
+ }
+
+ status = converter.base.generate (&converter.base, &renderer.base);
+
+CLEANUP:
+ _cairo_image_span_renderer_fini (&renderer);
+ converter.base.destroy (&converter.base);
+ return status;
+}
+
+static cairo_bool_t
+is_recording_pattern (const cairo_pattern_t *pattern)
+{
+ cairo_surface_t *surface;
+
+ if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE)
+ return FALSE;
+
+ surface = ((const cairo_surface_pattern_t *) pattern)->surface;
+ if (_cairo_surface_is_paginated (surface))
+ surface = _cairo_paginated_surface_get_recording (surface);
+ return _cairo_surface_is_recording (surface);
+}
+
+static cairo_surface_t *
+recording_pattern_get_surface (const cairo_pattern_t *pattern)
+{
+ cairo_surface_t *surface;
+
+ surface = ((const cairo_surface_pattern_t *) pattern)->surface;
+ if (_cairo_surface_is_paginated (surface))
+ surface = _cairo_paginated_surface_get_recording (surface);
+ return surface;
+}
+
+static cairo_bool_t
+op_reduces_to_source (cairo_operator_t op,
+ cairo_surface_t *dst)
+{
+ if (op == CAIRO_OPERATOR_SOURCE)
+ return TRUE;
+
+ if (dst->is_clear)
+ return op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ADD;
+
+ return FALSE;
+}
+
+static cairo_bool_t
+recording_pattern_contains_sample (const cairo_pattern_t *pattern,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_rectangle_int_t area;
+ cairo_recording_surface_t *surface;
+
+ if (! is_recording_pattern (pattern))
+ return FALSE;
+
+ if (pattern->extend == CAIRO_EXTEND_NONE)
+ return TRUE;
+
+ surface = (cairo_recording_surface_t *) recording_pattern_get_surface (pattern);
+ if (surface->unbounded)
+ return TRUE;
+
+ _cairo_surface_pattern_sampled_area ((cairo_surface_pattern_t *) pattern,
+ extents, &area);
+ if (area.x >= surface->extents.x &&
+ area.y >= surface->extents.y &&
+ area.x + area.width <= surface->extents.x + surface->extents.width &&
+ area.y + area.height <= surface->extents.y + surface->extents.height)
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static cairo_status_t
+_composite_boxes (cairo_fast_image_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ double opacity,
+ cairo_boxes_t *boxes,
+ const cairo_composite_rectangles_t *extents)
+{
+ cairo_region_t *clip_region = _cairo_clip_get_region (extents->clip);
+ cairo_bool_t need_clip_mask = extents->clip->path != NULL;
+ cairo_status_t status;
+ struct _cairo_boxes_chunk *chunk;
+ uint32_t pixel;
+ int i;
+
+ if (need_clip_mask && opacity != 1.)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (need_clip_mask &&
+ (op == CAIRO_OPERATOR_SOURCE || ! extents->is_bounded))
+ {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1)
+ clip_region = NULL;
+
+ if (! boxes->is_pixel_aligned) {
+ if (need_clip_mask)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ return _composite_unaligned_boxes (dst, op, pattern, opacity, boxes, extents);
+ }
+
+ /* Are we just copying a recording surface? */
+ if (! need_clip_mask && opacity == 1. &&
+ op_reduces_to_source (op, &dst->base) &&
+ recording_pattern_contains_sample (pattern, &extents->bounded))
+ {
+ cairo_clip_t *recording_clip;
+ cairo_bool_t was_clear = dst->base.is_clear;
+
+ /* XXX could also do tiling repeat modes... */
+
+ /* first clear the area about to be overwritten */
+ if (! dst->base.is_clear) {
+ for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+ cairo_box_t *box = chunk->base;
+
+ for (i = 0; i < chunk->count; i++) {
+ int x1 = _cairo_fixed_integer_part (box[i].p1.x);
+ int y1 = _cairo_fixed_integer_part (box[i].p1.y);
+ int x2 = _cairo_fixed_integer_part (box[i].p2.x);
+ int y2 = _cairo_fixed_integer_part (box[i].p2.y);
+
+ if (x2 == x1 || y2 == y1)
+ continue;
+
+ pixman_fill (pixman_image_get_data (dst->pixman_image),
+ pixman_image_get_stride (dst->pixman_image) / sizeof (uint32_t),
+ PIXMAN_FORMAT_BPP (dst->pixman_format),
+ x1, y1, x2 - x1, y2 - y1,
+ 0);
+ }
+ }
+
+ dst->base.is_clear = TRUE;
+ }
+
+ recording_clip = _cairo_clip_from_boxes (boxes);
+ status = _cairo_recording_surface_replay_with_clip (recording_pattern_get_surface (pattern),
+ &pattern->matrix,
+ &dst->base,
+ recording_clip);
+ _cairo_clip_destroy (recording_clip);
+ dst->base.is_clear &= was_clear;
+
+ return status;
+ }
+
+ status = CAIRO_STATUS_SUCCESS;
+ if (! need_clip_mask &&
+ pattern_to_pixel ((cairo_solid_pattern_t *) pattern, opacity, op,
+ dst->pixman_format,
+ &pixel))
+ {
+ for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+ cairo_box_t *box = chunk->base;
+
+ for (i = 0; i < chunk->count; i++) {
+ int x1 = _cairo_fixed_integer_part (box[i].p1.x);
+ int y1 = _cairo_fixed_integer_part (box[i].p1.y);
+ int x2 = _cairo_fixed_integer_part (box[i].p2.x);
+ int y2 = _cairo_fixed_integer_part (box[i].p2.y);
+
+ if (x2 == x1 || y2 == y1)
+ continue;
+
+ pixman_fill (pixman_image_get_data (dst->pixman_image),
+ pixman_image_get_stride (dst->pixman_image) / sizeof (uint32_t),
+ PIXMAN_FORMAT_BPP (dst->pixman_format),
+ x1, y1, x2 - x1, y2 - y1,
+ pixel);
+ }
+ }
+ }
+ else
+ {
+ cairo_surface_t *clip_surface = NULL;
+ pixman_image_t *src = NULL, *mask = NULL;
+ int src_x, src_y, mask_x = 0, mask_y = 0;
+ pixman_op_t pixman_op = _pixman_operator (op);
+
+ if (need_clip_mask) {
+ int clip_x, clip_y;
+
+ clip_surface = _cairo_clip_get_surface (extents->clip,
+ &dst->base,
+ &clip_x, &clip_y);
+ if (unlikely (clip_surface->status))
+ return clip_surface->status;
+
+ mask_x = -clip_x;
+ mask_y = -clip_y;
+
+ if (op == CAIRO_OPERATOR_CLEAR) {
+ pattern = NULL;
+ pixman_op = PIXMAN_OP_OUT_REVERSE;
+ }
+
+ mask = ((cairo_image_surface_t *) clip_surface)->pixman_image;
+ }
+
+ if (pattern != NULL) {
+ src = _pixman_image_for_pattern (pattern, FALSE, &extents->bounded,
+ &dst->base.device_transform,
+ &src_x, &src_y);
+ if (unlikely (src == NULL)) {
+ cairo_surface_destroy (clip_surface);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+ } else {
+ src = mask;
+ src_x = mask_x;
+ src_y = mask_y;
+ mask = NULL;
+ }
+
+ for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+ const cairo_box_t *box = chunk->base;
+
+ for (i = 0; i < chunk->count; i++) {
+ int x1 = _cairo_fixed_integer_part (box[i].p1.x);
+ int y1 = _cairo_fixed_integer_part (box[i].p1.y);
+ int x2 = _cairo_fixed_integer_part (box[i].p2.x);
+ int y2 = _cairo_fixed_integer_part (box[i].p2.y);
+
+ if (x2 == x1 || y2 == y1)
+ continue;
+
+ pixman_image_composite32 (pixman_op,
+ src, mask, dst->pixman_image,
+ x1 + src_x, y1 + src_y,
+ x1 + mask_x, y1 + mask_y,
+ x1, y1,
+ x2 - x1, y2 - y1);
+ }
+ }
+
+ cairo_surface_destroy (clip_surface);
+ if (pattern != NULL)
+ pixman_image_unref (src);
+
+ if (! extents->is_bounded) {
+ status =
+ fast_image_surface_fixup_unbounded_boxes (dst, extents,
+ clip_region, boxes);
+ }
+ }
+
+ return status;
+}
+
+static cairo_status_t
+_clip_and_composite_polygon (cairo_fast_image_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ double opacity,
+ cairo_polygon_t *polygon,
+ cairo_fill_rule_t fill_rule,
+ cairo_antialias_t antialias,
+ cairo_composite_rectangles_t *extents);
+static cairo_status_t
+_composite_boxes_fallback (void *closure,
+ cairo_fast_image_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ double opacity,
+ int dst_x,
+ int dst_y,
+ const cairo_composite_rectangles_t *extents)
+{
+ cairo_boxes_t *boxes = closure;
+ fast_image_span_renderer_t renderer;
+ cairo_rectangular_scan_converter_t converter;
+ const struct _cairo_boxes_chunk *chunk;
+ cairo_status_t status;
+ cairo_fixed_t fx = _cairo_fixed_from_int (-dst_x);
+ cairo_fixed_t fy = _cairo_fixed_from_int (-dst_y);
+ int i;
+
+ assert (! boxes->is_pixel_aligned);
+
+ status = fast_image_span_renderer_init (&renderer,
+ dst, op, pattern, opacity,
+ extents, FALSE);
+ if (unlikely (status))
+ return status;
+
+ _cairo_rectangular_scan_converter_init (&converter, &extents->bounded);
+ for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+ const cairo_box_t *box = chunk->base;
+
+ for (i = 0; i < chunk->count; i++) {
+ cairo_box_t b;
+
+ b.p1.x = box->p1.x + fx;
+ b.p1.y = box->p1.y + fy;
+ b.p2.x = box->p2.x + fx;
+ b.p2.y = box->p2.y + fy;
+
+ status = _cairo_rectangular_scan_converter_add_box (&converter,
+ &b, 1);
+ if (unlikely (status))
+ goto CLEANUP;
+ }
+ }
+
+ status = converter.base.generate (&converter.base, &renderer.base);
+
+CLEANUP:
+ _cairo_image_span_renderer_fini (&renderer);
+ converter.base.destroy (&converter.base);
+
+ return status;
+}
+
+static cairo_status_t
+_clip_and_composite_boxes (cairo_fast_image_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ double opacity,
+ cairo_boxes_t *boxes,
+ cairo_composite_rectangles_t *extents)
+{
+ cairo_int_status_t status;
+
+ if (boxes->num_boxes == 0 && extents->is_bounded)
+ return CAIRO_STATUS_SUCCESS;
+
+ /* Can we reduce drawing through a clip-mask to simply drawing the clip? */
+ if (extents->clip->path != NULL && extents->is_bounded) {
+ cairo_polygon_t polygon;
+ cairo_fill_rule_t fill_rule;
+ cairo_antialias_t antialias;
+ cairo_clip_t *clip;
+
+ clip = _cairo_clip_copy (extents->clip);
+ clip = _cairo_clip_intersect_boxes (clip, boxes);
+ status = _cairo_clip_get_polygon (clip, &polygon,
+ &fill_rule, &antialias);
+ _cairo_clip_path_destroy (clip->path);
+ clip->path = NULL;
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+ cairo_clip_t *saved_clip = extents->clip;
+ extents->clip = clip;
+ status = _clip_and_composite_polygon (dst, op, src, opacity,
+ &polygon,
+ fill_rule,
+ antialias,
+ extents);
+ if (extents->clip != clip)
+ clip = NULL;
+ extents->clip = saved_clip;
+ _cairo_polygon_fini (&polygon);
+ }
+ if (clip)
+ _cairo_clip_destroy (clip);
+
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+ }
+
+ /* Use a fast path if the boxes are pixel aligned */
+ status = _composite_boxes (dst, op, src, opacity, boxes, extents);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+
+ /* Otherwise render via a mask and composite in the usual fashion. */
+ return _clip_and_composite (dst, op, src, opacity,
+ _composite_boxes_fallback, boxes,
+ extents, need_unbounded_clip (extents));
+}
+
+/* high level image interface */
+static cairo_bool_t
+_cairo_fast_image_surface_get_extents (void *abstract_surface,
+ cairo_rectangle_int_t *rectangle)
+{
+ cairo_fast_image_surface_t *surface = abstract_surface;
+
+ rectangle->x = 0;
+ rectangle->y = 0;
+ rectangle->width = surface->width;
+ rectangle->height = surface->height;
+
+ return TRUE;
+}
+
+static cairo_int_status_t
+_cairo_fast_image_surface_paint (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_clip_t *clip)
+{
+ cairo_fast_image_surface_t *surface = abstract_surface;
+ cairo_rectangle_int_t unbounded;
+ cairo_composite_rectangles_t composite;
+ cairo_status_t status;
+ cairo_boxes_t boxes;
+
+ _cairo_fast_image_surface_get_extents (surface, &unbounded);
+ status = _cairo_composite_rectangles_init_for_paint (&composite, &unbounded,
+ op, source,
+ clip);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_clip_to_boxes (composite.clip, &boxes);
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ status = _clip_and_composite_boxes (surface, op, source, 1.,
+ &boxes, &composite);
+ _cairo_boxes_fini (&boxes);
+ }
+
+ _cairo_composite_rectangles_fini (&composite);
+
+ return status;
+}
+
+static cairo_status_t
+_composite_mask (void *closure,
+ cairo_fast_image_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *src_pattern,
+ double opacity,
+ int dst_x,
+ int dst_y,
+ const cairo_composite_rectangles_t *extents)
+{
+ const cairo_pattern_t *mask_pattern = closure;
+ pixman_image_t *src, *mask = NULL;
+ int src_x = 0, src_y = 0;
+ int mask_x = 0, mask_y = 0;
+
+ assert (opacity == 1.);
+
+ if (src_pattern != NULL) {
+ src = _pixman_image_for_pattern (src_pattern, FALSE, &extents->bounded,
+ &dst->base.device_transform,
+ &src_x, &src_y);
+ if (unlikely (src == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ mask = _pixman_image_for_pattern (mask_pattern, TRUE, &extents->bounded,
+ &dst->base.device_transform,
+ &mask_x, &mask_y);
+ if (unlikely (mask == NULL)) {
+ pixman_image_unref (src);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ if (mask_pattern->has_component_alpha)
+ pixman_image_set_component_alpha (mask, TRUE);
+ } else {
+ src = _pixman_image_for_pattern (mask_pattern, FALSE, &extents->bounded,
+ &dst->base.device_transform,
+ &src_x, &src_y);
+ if (unlikely (src == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ pixman_image_composite32 (_pixman_operator (op), src, mask, dst->pixman_image,
+ extents->bounded.x + src_x, extents->bounded.y + src_y,
+ extents->bounded.x + mask_x, extents->bounded.y + mask_y,
+ extents->bounded.x - dst_x, extents->bounded.y - dst_y,
+ extents->bounded.width, extents->bounded.height);
+
+ if (mask != NULL)
+ pixman_image_unref (mask);
+ pixman_image_unref (src);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_fast_image_surface_mask (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ const cairo_clip_t *clip)
+{
+ cairo_fast_image_surface_t *surface = abstract_surface;
+ cairo_composite_rectangles_t extents;
+ cairo_rectangle_int_t unbounded;
+ cairo_int_status_t status;
+
+ _cairo_fast_image_surface_get_extents (surface, &unbounded);
+ status = _cairo_composite_rectangles_init_for_mask (&extents, &unbounded,
+ op, source, mask, clip);
+ if (unlikely (status))
+ return status;
+
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ if (mask->type == CAIRO_PATTERN_TYPE_SOLID) {
+ cairo_boxes_t boxes;
+ double opacity = ((cairo_solid_pattern_t *)mask)->color.alpha;
+ status = _cairo_clip_to_boxes (extents.clip, &boxes);
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+ status = _clip_and_composite_boxes (surface,
+ op, source,
+ opacity,
+ &boxes, &extents);
+ _cairo_boxes_fini (&boxes);
+ }
+ }
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ status = _clip_and_composite (surface, op, source, 1.,
+ _composite_mask, (void *) mask,
+ &extents, need_bounded_clip (&extents));
+ }
+
+ _cairo_composite_rectangles_fini (&extents);
+
+ return status;
+}
+
+typedef struct {
+ cairo_polygon_t *polygon;
+ cairo_fill_rule_t fill_rule;
+ cairo_antialias_t antialias;
+} composite_spans_info_t;
+
+static cairo_status_t
+_composite_spans (void *closure,
+ cairo_fast_image_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ double opacity,
+ int dst_x,
+ int dst_y,
+ const cairo_composite_rectangles_t *extents)
+{
+ composite_spans_info_t *info = closure;
+ fast_image_span_renderer_t renderer;
+ cairo_scan_converter_t *converter;
+ const cairo_rectangle_int_t *r = &extents->bounded;
+ cairo_status_t status;
+
+ status = fast_image_span_renderer_init (&renderer,
+ dst, op, pattern, opacity,
+ extents, FALSE);
+ if (unlikely (status))
+ return status;
+
+ converter = _cairo_tor_scan_converter_create (r->x, r->y,
+ r->x + r->width,
+ r->y + r->height,
+ info->fill_rule);
+
+ status= _cairo_tor_scan_converter_add_polygon (converter,
+ info->polygon);
+ if (unlikely (status))
+ goto CLEANUP_CONVERTER;
+
+ status = converter->generate (converter, &renderer.base);
+
+ CLEANUP_CONVERTER:
+ _cairo_image_span_renderer_fini (&renderer);
+ converter->destroy (converter);
+ return status;
+}
+
+static cairo_status_t
+_composite_polygon (cairo_fast_image_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ double opacity,
+ cairo_polygon_t *polygon,
+ cairo_fill_rule_t fill_rule,
+ cairo_antialias_t antialias,
+ cairo_composite_rectangles_t *extents)
+{
+ fast_image_span_renderer_t renderer;
+ cairo_scan_converter_t *converter;
+ const cairo_rectangle_int_t *r = &extents->bounded;
+ cairo_bool_t needs_clip = !extents->is_bounded && extents->clip->path;
+ cairo_int_status_t status;
+
+ if (antialias == CAIRO_ANTIALIAS_NONE)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = fast_image_span_renderer_init (&renderer,
+ dst, op, pattern, opacity,
+ extents, needs_clip);
+ if (unlikely (status))
+ return status;
+
+ if (needs_clip) {
+ printf ("awooga!\n");
+ converter = _cairo_clip_tor_scan_converter_create (extents->clip,
+ polygon,
+ fill_rule);
+ } else {
+ converter = _cairo_tor_scan_converter_create (r->x, r->y,
+ r->x + r->width,
+ r->y + r->height,
+ fill_rule);
+ status = converter->add_polygon (converter, polygon);
+ if (unlikely (status))
+ goto CLEANUP_CONVERTER;
+ }
+
+ status = converter->generate (converter, &renderer.base);
+
+ CLEANUP_CONVERTER:
+ _cairo_image_span_renderer_fini (&renderer);
+ converter->destroy (converter);
+ return status;
+}
+
+static cairo_status_t
+_clip_and_composite_polygon (cairo_fast_image_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ double opacity,
+ cairo_polygon_t *polygon,
+ cairo_fill_rule_t fill_rule,
+ cairo_antialias_t antialias,
+ cairo_composite_rectangles_t *extents)
+{
+ cairo_int_status_t status;
+ composite_spans_info_t info;
+
+ if (_cairo_polygon_is_empty (polygon)) {
+ cairo_boxes_t boxes;
+
+ if (extents->is_bounded)
+ return CAIRO_STATUS_SUCCESS;
+
+ status = _cairo_clip_to_boxes (extents->clip, &boxes);
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+ extents->is_bounded = _cairo_operator_bounded_by_either (op);
+ extents->mask = extents->bounded = extents->unbounded;
+ status = _clip_and_composite_boxes (dst,
+ CAIRO_OPERATOR_CLEAR,
+ &_cairo_pattern_clear.base,
+ opacity,
+ &boxes, extents);
+ _cairo_boxes_fini (&boxes);
+ }
+
+ return status;
+ }
+
+ if (extents->is_bounded &&
+ (extents->clip->num_boxes > 1 || extents->clip->path)) {
+ cairo_polygon_t clipper;
+ cairo_antialias_t clip_antialias;
+ cairo_fill_rule_t clip_fill_rule;
+
+ status = _cairo_clip_get_polygon (extents->clip,
+ &clipper,
+ &clip_fill_rule,
+ &clip_antialias);
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+ cairo_clip_t *old_clip;
+
+ status = _cairo_polygon_intersect (polygon, fill_rule,
+ &clipper, fill_rule);
+ _cairo_polygon_fini (&clipper);
+ if (unlikely (status))
+ return status;
+
+ old_clip = extents->clip;
+ extents->clip = _cairo_clip_copy_region (extents->clip);
+ _cairo_clip_destroy (old_clip);
+ }
+ }
+
+ status = _cairo_composite_rectangles_intersect_mask_extents (extents,
+ &polygon->extents);
+ if (unlikely (status))
+ return status;
+
+ status = _composite_polygon (dst, op, src, opacity,
+ polygon, fill_rule, antialias,
+ extents);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+
+
+ info.polygon = polygon;
+ info.fill_rule = fill_rule;
+ info.antialias = antialias;
+
+ return _clip_and_composite (dst, op, src, opacity,
+ _composite_spans, &info,
+ extents, need_bounded_clip (extents));
+}
+
+static cairo_int_status_t
+_cairo_fast_image_surface_stroke (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_fast_image_surface_t *surface = abstract_surface;
+ cairo_composite_rectangles_t composite;
+ cairo_rectangle_int_t unbounded;
+ cairo_int_status_t status;
+
+ _cairo_fast_image_surface_get_extents (surface, &unbounded);
+ status = _cairo_composite_rectangles_init_for_stroke (&composite, &unbounded,
+ op, source,
+ path, style, ctm,
+ clip);
+ if (unlikely (status))
+ return status;
+
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ if (_cairo_path_fixed_stroke_is_rectilinear (path)) {
+ cairo_boxes_t boxes;
+
+ _cairo_boxes_init_with_clip (&boxes, composite.clip);
+ status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path,
+ style,
+ ctm,
+ antialias,
+ &boxes);
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+ status = _clip_and_composite_boxes (surface, op, source, 1.,
+ &boxes, &composite);
+ }
+ _cairo_boxes_fini (&boxes);
+ }
+
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ cairo_polygon_t polygon;
+ cairo_box_t extents;
+
+ _cairo_box_from_rectangle (&extents, &composite.clip->extents);
+ _cairo_polygon_init (&polygon, &extents, 1);
+ status = _cairo_path_fixed_stroke_to_polygon (path,
+ style,
+ ctm, ctm_inverse,
+ tolerance,
+ &polygon);
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+ status = _clip_and_composite_polygon (surface, op, source, 1.,
+ &polygon,
+ CAIRO_FILL_RULE_WINDING,
+ antialias,
+ &composite);
+ }
+ _cairo_polygon_fini (&polygon);
+ }
+
+ _cairo_composite_rectangles_fini (&composite);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_fast_image_surface_fill (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_fast_image_surface_t *surface = abstract_surface;
+ cairo_composite_rectangles_t composite;
+ cairo_rectangle_int_t unbounded;
+ cairo_status_t status;
+
+ _cairo_fast_image_surface_get_extents (surface, &unbounded);
+ status = _cairo_composite_rectangles_init_for_fill (&composite, &unbounded,
+ op, source, path,
+ clip);
+ if (unlikely (status))
+ return status;
+
+ if (_cairo_path_fixed_fill_is_rectilinear (path)) {
+ cairo_boxes_t boxes;
+
+ _cairo_boxes_init_with_clip (&boxes, composite.clip);
+ status = _cairo_path_fixed_fill_rectilinear_to_boxes (path,
+ fill_rule,
+ antialias,
+ &boxes);
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ status = _clip_and_composite_boxes (surface, op, source, 1.,
+ &boxes, &composite);
+ }
+ _cairo_boxes_fini (&boxes);
+ } else {
+ cairo_polygon_t polygon;
+ cairo_box_t extents;
+
+ assert (! _cairo_path_fixed_fill_is_empty (path));
+
+ _cairo_box_from_rectangle (&extents, &composite.clip->extents);
+ _cairo_polygon_init (&polygon, &extents, 1);
+ status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon);
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ status = _clip_and_composite_polygon (surface, op, source, 1.,
+ &polygon,
+ fill_rule, antialias,
+ &composite);
+ }
+ _cairo_polygon_fini (&polygon);
+ }
+
+ _cairo_composite_rectangles_fini (&composite);
+
+ return status;
+}
+
+typedef struct {
+ cairo_scaled_font_t *font;
+ cairo_glyph_t *glyphs;
+ int num_glyphs;
+} composite_glyphs_info_t;
+
+static cairo_status_t
+_composite_glyphs_via_mask (void *closure,
+ cairo_fast_image_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ double opacity,
+ int dst_x,
+ int dst_y,
+ const cairo_composite_rectangles_t *extents)
+{
+ composite_glyphs_info_t *info = closure;
+ cairo_scaled_font_t *font = info->font;
+ cairo_glyph_t *glyphs = info->glyphs;
+ int num_glyphs = info->num_glyphs;
+ pixman_image_t *mask = NULL;
+ pixman_image_t *src;
+ pixman_image_t *white;
+ pixman_format_code_t mask_format = 0; /* silence gcc */
+ cairo_status_t status = CAIRO_STATUS_SUCCESS; /* silence gcc */
+ int src_x, src_y;
+ int i;
+
+ assert (opacity == 1.);
+
+ src = _pixman_image_for_pattern (pattern, FALSE, &extents->bounded,
+ &dst->base.device_transform,
+ &src_x, &src_y);
+ if (unlikely (src == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ white = _pixman_white_image ();
+ if (unlikely (white == NULL)) {
+ pixman_image_unref (src);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ _cairo_scaled_font_freeze_cache (font);
+
+ for (i = 0; i < num_glyphs; i++) {
+ int x, y;
+ cairo_image_surface_t *glyph_surface;
+ cairo_scaled_glyph_t *scaled_glyph;
+
+ status = _cairo_scaled_glyph_lookup (font, glyphs[i].index,
+ CAIRO_SCALED_GLYPH_INFO_SURFACE,
+ &scaled_glyph);
+
+ if (unlikely (status))
+ goto CLEANUP;
+
+ glyph_surface = scaled_glyph->surface;
+
+ if (glyph_surface->width == 0 || glyph_surface->height == 0)
+ continue;
+
+ /* To start, create the mask using the format from the first
+ * glyph. Later we'll deal with different formats. */
+ if (mask == NULL) {
+ mask_format = glyph_surface->pixman_format;
+ mask = pixman_image_create_bits (mask_format,
+ extents->bounded.width,
+ extents->bounded.height,
+ NULL, 0);
+ if (unlikely (mask == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto CLEANUP;
+ }
+
+ if (PIXMAN_FORMAT_RGB (mask_format))
+ pixman_image_set_component_alpha (mask, TRUE);
+ }
+
+ /* If we have glyphs of different formats, we "upgrade" the mask
+ * to the wider of the formats. */
+ if (glyph_surface->pixman_format != mask_format &&
+ PIXMAN_FORMAT_BPP (mask_format) <
+ PIXMAN_FORMAT_BPP (glyph_surface->pixman_format))
+ {
+ pixman_image_t *new_mask;
+
+ mask_format = glyph_surface->pixman_format;
+ new_mask = pixman_image_create_bits (mask_format,
+ extents->bounded.width,
+ extents->bounded.height,
+ NULL, 0);
+ if (unlikely (new_mask == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto CLEANUP;
+ }
+
+ pixman_image_composite32 (PIXMAN_OP_SRC,
+ white, mask, new_mask,
+ 0, 0, 0, 0, 0, 0,
+ extents->bounded.width,
+ extents->bounded.height);
+
+ pixman_image_unref (mask);
+ mask = new_mask;
+ if (PIXMAN_FORMAT_RGB (mask_format))
+ pixman_image_set_component_alpha (mask, TRUE);
+ }
+
+ /* round glyph locations to the nearest pixel */
+ /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */
+ x = _cairo_lround (glyphs[i].x -
+ glyph_surface->base.device_transform.x0);
+ y = _cairo_lround (glyphs[i].y -
+ glyph_surface->base.device_transform.y0);
+ if (glyph_surface->pixman_format == mask_format) {
+ pixman_image_composite32 (PIXMAN_OP_ADD,
+ glyph_surface->pixman_image, NULL, mask,
+ 0, 0, 0, 0,
+ x - extents->bounded.x, y - extents->bounded.y,
+ glyph_surface->width,
+ glyph_surface->height);
+ } else {
+ pixman_image_composite32 (PIXMAN_OP_ADD,
+ white, glyph_surface->pixman_image, mask,
+ 0, 0, 0, 0,
+ x - extents->bounded.x, y - extents->bounded.y,
+ glyph_surface->width,
+ glyph_surface->height);
+ }
+ }
+
+ pixman_image_composite32 (_pixman_operator (op),
+ src, mask, dst->pixman_image,
+ extents->bounded.x + src_x, extents->bounded.y + src_y,
+ 0, 0,
+ extents->bounded.x - dst_x, extents->bounded.y - dst_y,
+ extents->bounded.width, extents->bounded.height);
+
+CLEANUP:
+ _cairo_scaled_font_thaw_cache (font);
+ if (mask != NULL)
+ pixman_image_unref (mask);
+ pixman_image_unref (src);
+ pixman_image_unref (white);
+
+ return status;
+}
+
+static cairo_status_t
+_composite_glyphs (void *closure,
+ cairo_fast_image_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ double opacity,
+ int dst_x,
+ int dst_y,
+ const cairo_composite_rectangles_t *extents)
+{
+ composite_glyphs_info_t *info = closure;
+ cairo_scaled_glyph_t *glyph_cache[64];
+ pixman_op_t pixman_op = _pixman_operator (op);
+ pixman_image_t *src = NULL;
+ int src_x = 0, src_y = 0;
+ cairo_status_t status;
+ int i;
+
+ assert (opacity == 1.);
+
+ if (pattern != NULL) {
+ src = _pixman_image_for_pattern (pattern, FALSE, &extents->bounded,
+ &dst->base.device_transform,
+ &src_x, &src_y);
+ src_x -= dst_x;
+ src_y -= dst_y;
+ } else {
+ src = _pixman_white_image ();
+ }
+ if (unlikely (src == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ memset (glyph_cache, 0, sizeof (glyph_cache));
+ status = CAIRO_STATUS_SUCCESS;
+
+ _cairo_scaled_font_freeze_cache (info->font);
+ for (i = 0; i < info->num_glyphs; i++) {
+ int x, y;
+ cairo_image_surface_t *glyph_surface;
+ cairo_scaled_glyph_t *scaled_glyph;
+ unsigned long glyph_index = info->glyphs[i].index;
+ int cache_index = glyph_index % ARRAY_LENGTH (glyph_cache);
+
+ scaled_glyph = glyph_cache[cache_index];
+ if (scaled_glyph == NULL ||
+ _cairo_scaled_glyph_index (scaled_glyph) != glyph_index)
+ {
+ status = _cairo_scaled_glyph_lookup (info->font, glyph_index,
+ CAIRO_SCALED_GLYPH_INFO_SURFACE,
+ &scaled_glyph);
+
+ if (unlikely (status))
+ break;
+
+ glyph_cache[cache_index] = scaled_glyph;
+ }
+
+ glyph_surface = scaled_glyph->surface;
+ if (glyph_surface->width && glyph_surface->height) {
+ int x1, y1, x2, y2;
+
+ /* round glyph locations to the nearest pixel */
+ /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */
+ x = _cairo_lround (info->glyphs[i].x -
+ glyph_surface->base.device_transform.x0);
+ y = _cairo_lround (info->glyphs[i].y -
+ glyph_surface->base.device_transform.y0);
+
+ x1 = x;
+ if (x1 < extents->bounded.x)
+ x1 = extents->bounded.x;
+ x2 = x + glyph_surface->width;
+ if (x2 > extents->bounded.x + extents->bounded.width)
+ x2 = extents->bounded.x + extents->bounded.width;
+
+ y1 = y;
+ if (y1 < extents->bounded.y)
+ y1 = extents->bounded.y;
+ y2 = y + glyph_surface->height;
+ if (y2 > extents->bounded.y + extents->bounded.height)
+ y2 = extents->bounded.y + extents->bounded.height;
+
+ pixman_image_composite32 (pixman_op,
+ src, glyph_surface->pixman_image, dst->pixman_image,
+ x1 + src_x, y1 + src_y,
+ x1 - x, y1 - y,
+ x1 - dst_x, y1 - dst_y,
+ x2 - x1, y2 - y1);
+ }
+ }
+ _cairo_scaled_font_thaw_cache (info->font);
+
+ pixman_image_unref (src);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_fast_image_surface_glyphs (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_clip_t *clip,
+ int *num_remaining)
+{
+ cairo_fast_image_surface_t *surface = abstract_surface;
+ cairo_composite_rectangles_t extents;
+ cairo_rectangle_int_t unbounded;
+ composite_glyphs_info_t glyph_info;
+ cairo_status_t status;
+ cairo_bool_t overlap;
+ unsigned int flags;
+
+ _cairo_fast_image_surface_get_extents (surface, &unbounded);
+ status = _cairo_composite_rectangles_init_for_glyphs (&extents, &unbounded,
+ op, source,
+ scaled_font,
+ glyphs, num_glyphs,
+ clip,
+ &overlap);
+ if (unlikely (status))
+ return status;
+
+ glyph_info.font = scaled_font;
+ glyph_info.glyphs = glyphs;
+ glyph_info.num_glyphs = num_glyphs;
+
+ flags = 0;
+ if (extents.mask.width > extents.unbounded.width ||
+ extents.mask.height > extents.unbounded.height)
+ {
+ flags |= FORCE_CLIP_REGION;
+ }
+ status = _clip_and_composite (surface, op, source, 1.,
+ extents.is_bounded == 0 ?
+ _composite_glyphs_via_mask :
+ _composite_glyphs,
+ &glyph_info,
+ &extents,
+ need_bounded_clip (&extents) | flags);
+
+ _cairo_composite_rectangles_fini (&extents);
+
+ *num_remaining = 0;
+ return status;
+}
+
+static void
+_cairo_fast_image_surface_get_font_options (void *abstract_surface,
+ cairo_font_options_t *options)
+{
+ _cairo_font_options_init_default (options);
+
+ cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON);
+ _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_ON);
+}
+
+/* Override the default context */
+
+static cairo_t *
+_cairo_fast_image_context_create (void *target)
+{
+ cairo_t *cr;
+ cairo_default_context_t *def;
+
+ cr = _cairo_default_context_create (target);
+ if (unlikely (cr->status))
+ return cr;
+
+ /* set our preferred defaults */
+ def = (cairo_default_context_t *) cr;
+ def->gstate->tolerance = FAST_IMAGE_TOLERANCE;
+
+ return cr;
+}
+
+static const cairo_surface_backend_t fast_image_surface_backend = {
+ CAIRO_SURFACE_TYPE_FASTIMAGE,
+ _cairo_fast_image_surface_finish,
+
+ _cairo_fast_image_context_create,
+
+ _cairo_fast_image_surface_create_similar,
+ NULL, /* create similar image */
+ _cairo_fast_image_surface_map_to_image,
+ _cairo_fast_image_surface_unmap_image,
+
+ _cairo_fast_image_surface_acquire_source_image,
+ _cairo_fast_image_surface_release_source_image,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+
+ NULL, /* copy_page */
+ NULL, /* show_page */
+ _cairo_fast_image_surface_get_extents,
+ NULL, /* old_show_glyphs */
+ _cairo_fast_image_surface_get_font_options,
+ NULL, /* flush */
+ NULL, /* mark dirty */
+ NULL, /* font_fini */
+ NULL, /* glyph_fini */
+
+ _cairo_fast_image_surface_paint,
+ _cairo_fast_image_surface_mask,
+ _cairo_fast_image_surface_stroke,
+ _cairo_fast_image_surface_fill,
+ _cairo_fast_image_surface_glyphs,
+ NULL, /* show_text_glyphs */
+ NULL, /* snapshot */
+ NULL, /* is_similar */
+};
+
+/**
+ * cairo_fast_image_surface_create:
+ * @format: format of pixels in the surface to create
+ * @width: width of the surface, in pixels
+ * @height: height of the surface, in pixels
+ *
+ * Creates an image surface of the specified format and
+ * dimensions. Initially the surface contents are all
+ * 0. (Specifically, within each pixel, each color or alpha channel
+ * belonging to format will be 0. The contents of bits within a pixel,
+ * but not belonging to the given format are undefined).
+ *
+ * Return value: a pointer to the newly created surface. The caller
+ * owns the surface and should call cairo_surface_destroy() when done
+ * with it.
+ *
+ * This function always returns a valid pointer, but it will return a
+ * pointer to a "nil" surface if an error such as out of memory
+ * occurs. You can use cairo_surface_status() to check for this.
+ **/
+cairo_surface_t *
+cairo_fast_image_surface_create (cairo_format_t format,
+ int width,
+ int height)
+{
+ if (! CAIRO_FORMAT_VALID (format))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+
+ if (! is_size_valid (width, height))
+ return cairo_image_surface_create (format, width, height);
+
+ return fast_image_surface_create(_cairo_format_to_pixman_format_code (format),
+ width, height);
+}
+
+cairo_surface_t *
+cairo_fast_image_surface_create_for_data (void *data,
+ cairo_format_t format,
+ int width,
+ int height,
+ int stride)
+{
+ cairo_fast_image_surface_t *surface;
+ pixman_image_t *pixman_image;
+ pixman_format_code_t pixman_format;
+
+ if (! CAIRO_FORMAT_VALID (format))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+
+ if (! is_size_valid (width, height))
+ return cairo_image_surface_create (format, width, height);
+
+ pixman_format =_cairo_format_to_pixman_format_code (format),
+ pixman_image = pixman_image_create_bits (pixman_format,
+ width, height,
+ data, stride);
+ if (unlikely (pixman_image == NULL))
+ return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+ surface = malloc (sizeof (cairo_fast_image_surface_t));
+ if (unlikely (surface == NULL)) {
+ pixman_image_unref (pixman_image);
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+
+ _cairo_surface_init (&surface->base,
+ &fast_image_surface_backend,
+ NULL, /* device */
+ _cairo_content_from_pixman_format (pixman_format));
+
+ surface->pixman_image = pixman_image;
+ surface->pixman_format = pixman_format;
+ surface->format = format;
+
+ surface->width = width;
+ surface->height = height;
+
+ surface->base.is_clear = FALSE;
+
+ return &surface->base;
+}
diff --git a/src/cairo-pattern-private.h b/src/cairo-pattern-private.h
index 647eb0b..1c22ee8 100644
--- a/src/cairo-pattern-private.h
+++ b/src/cairo-pattern-private.h
@@ -352,7 +352,10 @@ _cairo_mesh_pattern_rasterize (const cairo_mesh_pattern_t *mesh,
double x_offset,
double y_offset);
-
+cairo_private_no_warn cairo_filter_t
+_cairo_surface_pattern_sampled_area (const cairo_surface_pattern_t *pattern,
+ const cairo_rectangle_int_t *extents,
+ cairo_rectangle_int_t *sample);
CAIRO_END_DECLS
diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c
index cb6bba8..8c33e4c 100644
--- a/src/cairo-pattern.c
+++ b/src/cairo-pattern.c
@@ -5148,6 +5148,34 @@ cairo_mesh_pattern_get_control_point (cairo_pattern_t *pattern,
}
slim_hidden_def (cairo_mesh_pattern_get_control_point);
+cairo_filter_t
+_cairo_surface_pattern_sampled_area (const cairo_surface_pattern_t *pattern,
+ const cairo_rectangle_int_t *extents,
+ cairo_rectangle_int_t *sample)
+{
+ cairo_filter_t filter;
+ double x1, x2, y1, y2;
+ double pad;
+
+ x1 = extents->x;
+ y1 = extents->y;
+ x2 = extents->x + (int) extents->width;
+ y2 = extents->y + (int) extents->height;
+
+ _cairo_matrix_transform_bounding_box (&pattern->base.matrix,
+ &x1, &y1, &x2, &y2,
+ NULL);
+
+ filter = _cairo_pattern_analyze_filter (&pattern->base, &pad);
+ sample->x = floor (x1 - pad);
+ sample->y = floor (y1 - pad);
+ sample->width = ceil (x2 + pad) - sample->x;
+ sample->height = ceil (y2 + pad) - sample->y;
+
+ return filter;
+}
+
+
void
_cairo_pattern_reset_static_data (void)
{
diff --git a/src/cairo.h b/src/cairo.h
index 7987192..24b9f0d 100644
--- a/src/cairo.h
+++ b/src/cairo.h
@@ -2158,6 +2158,7 @@ cairo_surface_status (cairo_surface_t *surface);
* @CAIRO_SURFACE_TYPE_SKIA: The surface is of type Skia, since 1.10
* @CAIRO_SURFACE_TYPE_SUBSURFACE: The surface is a subsurface created with
* cairo_surface_create_for_rectangle(), since 1.10
+ * @CAIRO_SURFACE_TYPE_FASTIMAGE: The surface is of type fast-image, since 1.12
*
* #cairo_surface_type_t is used to describe the type of a given
* surface. The surface types are also known as "backends" or "surface
@@ -2207,7 +2208,8 @@ typedef enum _cairo_surface_type {
CAIRO_SURFACE_TYPE_TEE,
CAIRO_SURFACE_TYPE_XML,
CAIRO_SURFACE_TYPE_SKIA,
- CAIRO_SURFACE_TYPE_SUBSURFACE
+ CAIRO_SURFACE_TYPE_SUBSURFACE,
+ CAIRO_SURFACE_TYPE_FASTIMAGE
} cairo_surface_type_t;
cairo_public cairo_surface_type_t
@@ -2349,6 +2351,18 @@ cairo_image_surface_create_from_png_stream (cairo_read_func_t read_func,
#endif
+cairo_public cairo_surface_t *
+cairo_fast_image_surface_create (cairo_format_t format,
+ int width,
+ int height);
+
+cairo_public cairo_surface_t *
+cairo_fast_image_surface_create_for_data (void *data,
+ cairo_format_t format,
+ int width,
+ int height,
+ int stride);
+
/* Recording-surface functions */
cairo_public cairo_surface_t *
diff --git a/src/cairoint.h b/src/cairoint.h
index e47e14b..5715ad1 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -1997,6 +1997,9 @@ _pixman_format_to_masks (pixman_format_code_t pixman_format,
cairo_private void
_cairo_image_reset_static_data (void);
+cairo_private void
+_cairo_fast_image_reset_static_data (void);
+
cairo_private cairo_surface_t *
_cairo_image_surface_create_with_pixman_format (unsigned char *data,
pixman_format_code_t pixman_format,
commit f33f4e4785a9fe7b680a6941311083737603337e
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date: Tue Aug 2 23:30:00 2011 +0100
spans-image-clip
diff --git a/src/cairo-image-surface.c b/src/cairo-image-surface.c
index 508d258..2989953 100644
--- a/src/cairo-image-surface.c
+++ b/src/cairo-image-surface.c
@@ -3429,12 +3429,6 @@ _cairo_image_surface_paint (void *abstract_surface,
if (unlikely (status))
return status;
- /* If the clip cannot be reduced to a set of boxes, we will need to
- * use a clipmask. Paint is special as it is the only operation that
- * does not implicitly use a mask, so we may be able to reduce this
- * operation to a fill...
- */
-
status = _cairo_clip_to_boxes (extents.clip, &boxes);
if (likely (status == CAIRO_STATUS_SUCCESS)) {
status = _clip_and_composite_boxes (surface, op, source, 1.,
@@ -3671,9 +3665,35 @@ _clip_and_composite_polygon (cairo_image_surface_t *dst,
return status;
}
- _cairo_box_round_to_rectangle (&polygon->extents, &extents->mask);
- if (! _cairo_rectangle_intersect (&extents->bounded, &extents->mask))
- return CAIRO_STATUS_SUCCESS;
+ if (extents->is_bounded &&
+ (extents->clip->num_boxes > 1 || extents->clip->path)) {
+ cairo_polygon_t clipper;
+ cairo_antialias_t clip_antialias;
+ cairo_fill_rule_t clip_fill_rule;
+
+ status = _cairo_clip_get_polygon (extents->clip,
+ &clipper,
+ &clip_fill_rule,
+ &clip_antialias);
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+ cairo_clip_t *old_clip;
+
+ status = _cairo_polygon_intersect (polygon, fill_rule,
+ &clipper, fill_rule);
+ _cairo_polygon_fini (&clipper);
+ if (unlikely (status))
+ return status;
+
+ old_clip = extents->clip;
+ extents->clip = _cairo_clip_copy_region (extents->clip);
+ _cairo_clip_destroy (old_clip);
+ }
+ }
+
+ status = _cairo_composite_rectangles_intersect_mask_extents (extents,
+ &polygon->extents);
+ if (unlikely (status))
+ return status;
status = _composite_polygon (dst, op, src, opacity,
polygon, fill_rule, antialias,
@@ -3690,7 +3710,7 @@ _clip_and_composite_polygon (cairo_image_surface_t *dst,
status = _clip_and_composite (dst, op, src, opacity,
_composite_spans, &info,
- extents, need_unbounded_clip (extents));
+ extents, need_bounded_clip (extents));
} else {
cairo_traps_t traps;
@@ -3725,12 +3745,12 @@ _cairo_image_surface_stroke (void *abstract_surface,
const cairo_clip_t *clip)
{
cairo_image_surface_t *surface = abstract_surface;
- cairo_composite_rectangles_t extents;
+ cairo_composite_rectangles_t composite;
cairo_rectangle_int_t unbounded;
cairo_int_status_t status;
_cairo_image_surface_get_extents (surface, &unbounded);
- status = _cairo_composite_rectangles_init_for_stroke (&extents, &unbounded,
+ status = _cairo_composite_rectangles_init_for_stroke (&composite, &unbounded,
op, source,
path, style, ctm,
clip);
@@ -3741,7 +3761,7 @@ _cairo_image_surface_stroke (void *abstract_surface,
if (_cairo_path_fixed_stroke_is_rectilinear (path)) {
cairo_boxes_t boxes;
- _cairo_boxes_init_with_clip (&boxes, extents.clip);
+ _cairo_boxes_init_with_clip (&boxes, composite.clip);
status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path,
style,
ctm,
@@ -3749,15 +3769,17 @@ _cairo_image_surface_stroke (void *abstract_surface,
&boxes);
if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
status = _clip_and_composite_boxes (surface, op, source, 1.,
- &boxes, &extents);
+ &boxes, &composite);
}
_cairo_boxes_fini (&boxes);
}
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
cairo_polygon_t polygon;
+ cairo_box_t extents;
- _cairo_polygon_init_with_clip (&polygon, extents.clip);
+ _cairo_box_from_rectangle (&extents, &composite.clip->extents);
+ _cairo_polygon_init (&polygon, &extents, 1);
status = _cairo_path_fixed_stroke_to_polygon (path,
style,
ctm, ctm_inverse,
@@ -3768,12 +3790,12 @@ _cairo_image_surface_stroke (void *abstract_surface,
&polygon,
CAIRO_FILL_RULE_WINDING,
antialias,
- &extents);
+ &composite);
}
_cairo_polygon_fini (&polygon);
}
- _cairo_composite_rectangles_fini (&extents);
+ _cairo_composite_rectangles_fini (&composite);
return status;
}
@@ -3789,12 +3811,12 @@ _cairo_image_surface_fill (void *abstract_surface,
const cairo_clip_t *clip)
{
cairo_image_surface_t *surface = abstract_surface;
- cairo_composite_rectangles_t extents;
+ cairo_composite_rectangles_t composite;
cairo_rectangle_int_t unbounded;
cairo_status_t status;
_cairo_image_surface_get_extents (surface, &unbounded);
- status = _cairo_composite_rectangles_init_for_fill (&extents, &unbounded,
+ status = _cairo_composite_rectangles_init_for_fill (&composite, &unbounded,
op, source, path,
clip);
if (unlikely (status))
@@ -3803,33 +3825,35 @@ _cairo_image_surface_fill (void *abstract_surface,
if (_cairo_path_fixed_fill_is_rectilinear (path)) {
cairo_boxes_t boxes;
- _cairo_boxes_init_with_clip (&boxes, extents.clip);
+ _cairo_boxes_init_with_clip (&boxes, composite.clip);
status = _cairo_path_fixed_fill_rectilinear_to_boxes (path,
fill_rule,
antialias,
&boxes);
if (likely (status == CAIRO_STATUS_SUCCESS)) {
status = _clip_and_composite_boxes (surface, op, source, 1.,
- &boxes, &extents);
+ &boxes, &composite);
}
_cairo_boxes_fini (&boxes);
} else {
cairo_polygon_t polygon;
+ cairo_box_t extents;
assert (! _cairo_path_fixed_fill_is_empty (path));
- _cairo_polygon_init_with_clip (&polygon, extents.clip);
+ _cairo_box_from_rectangle (&extents, &composite.clip->extents);
+ _cairo_polygon_init (&polygon, &extents, 1);
status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon);
if (likely (status == CAIRO_STATUS_SUCCESS)) {
status = _clip_and_composite_polygon (surface, op, source, 1.,
&polygon,
fill_rule, antialias,
- &extents);
+ &composite);
}
_cairo_polygon_fini (&polygon);
}
- _cairo_composite_rectangles_fini (&extents);
+ _cairo_composite_rectangles_fini (&composite);
return status;
}
commit 988f5146ccd4359cc2ee3a94c15a9823b5f8eae2
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date: Sat Jul 30 17:28:21 2011 +0100
image: Interface with pixman spans.
Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>
diff --git a/src/Makefile.sources b/src/Makefile.sources
index f22f7ed..1dbe4c0 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -184,6 +184,7 @@ cairo_sources = \
cairo-surface-wrapper.c \
cairo-system.c \
cairo-tor-scan-converter.c \
+ cairo-clip-tor-scan-converter.c \
cairo-toy-font-face.c \
cairo-traps.c \
cairo-unicode.c \
diff --git a/src/cairo-botor-scan-converter.c b/src/cairo-botor-scan-converter.c
index 0778a5d..6839c5e 100644
--- a/src/cairo-botor-scan-converter.c
+++ b/src/cairo-botor-scan-converter.c
@@ -1397,6 +1397,7 @@ render_rows (cairo_botor_scan_converter_t *self,
if (x > prev_x) {
spans[num_spans].x = prev_x;
+ spans[num_spans].is_clipped = 0;
spans[num_spans].coverage = AREA_TO_ALPHA (cover);
++num_spans;
}
@@ -1413,12 +1414,14 @@ render_rows (cairo_botor_scan_converter_t *self,
if (prev_x <= self->xmax) {
spans[num_spans].x = prev_x;
+ spans[num_spans].is_clipped = 0;
spans[num_spans].coverage = AREA_TO_ALPHA (cover);
++num_spans;
}
if (cover && prev_x < self->xmax) {
spans[num_spans].x = self->xmax;
+ spans[num_spans].is_clipped = 1;
spans[num_spans].coverage = 0;
++num_spans;
}
diff --git a/src/cairo-clip-tor-scan-converter.c b/src/cairo-clip-tor-scan-converter.c
new file mode 100644
index 0000000..f777996
--- /dev/null
+++ b/src/cairo-clip-tor-scan-converter.c
@@ -0,0 +1,1842 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* glitter-paths - polygon scan converter
+ *
+ * Copyright (c) 2008 M Joonas Pihlaja
+ * Copyright (c) 2007 David Turner
+ *
+ * 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.
+ */
+/* This is the Glitter paths scan converter incorporated into cairo.
+ * The source is from commit 734c53237a867a773640bd5b64816249fa1730f8
+ * of
+ *
+ * http://gitweb.freedesktop.org/?p=users/joonas/glitter-paths
+ */
+/* Glitter-paths is a stand alone polygon rasteriser derived from
+ * David Turner's reimplementation of Tor Anderssons's 15x17
+ * supersampling rasteriser from the Apparition graphics library. The
+ * main new feature here is cheaply choosing per-scan line between
+ * doing fully analytical coverage computation for an entire row at a
+ * time vs. using a supersampling approach.
+ *
+ * David Turner's code can be found at
+ *
+ * http://david.freetype.org/rasterizer-shootout/raster-comparison-20070813.tar.bz2
+ *
+ * In particular this file incorporates large parts of ftgrays_tor10.h
+ * from raster-comparison-20070813.tar.bz2
+ */
+/* Overview
+ *
+ * A scan converter's basic purpose to take polygon edges and convert
+ * them into an RLE compressed A8 mask. This one works in two phases:
+ * gathering edges and generating spans.
+ *
+ * 1) As the user feeds the scan converter edges they are vertically
+ * clipped and bucketted into a _polygon_ data structure. The edges
+ * are also snapped from the user's coordinates to the subpixel grid
+ * coordinates used during scan conversion.
+ *
+ * user
+ * |
+ * | edges
+ * V
+ * polygon buckets
+ *
+ * 2) Generating spans works by performing a vertical sweep of pixel
+ * rows from top to bottom and maintaining an _active_list_ of edges
+ * that intersect the row. From the active list the fill rule
+ * determines which edges are the left and right edges of the start of
+ * each span, and their contribution is then accumulated into a pixel
+ * coverage list (_cell_list_) as coverage deltas. Once the coverage
+ * deltas of all edges are known we can form spans of constant pixel
+ * coverage by summing the deltas during a traversal of the cell list.
+ * At the end of a pixel row the cell list is sent to a coverage
+ * blitter for rendering to some target surface.
+ *
+ * The pixel coverages are computed by either supersampling the row
+ * and box filtering a mono rasterisation, or by computing the exact
+ * coverages of edges in the active list. The supersampling method is
+ * used whenever some edge starts or stops within the row or there are
+ * edge intersections in the row.
+ *
+ * polygon bucket for \
+ * current pixel row |
+ * | |
+ * | activate new edges | Repeat GRID_Y times if we
+ * V \ are supersampling this row,
+ * active list / or just once if we're computing
+ * | | analytical coverage.
+ * | coverage deltas |
+ * V |
+ * pixel coverage list /
+ * |
+ * V
+ * coverage blitter
+ */
+#include "cairoint.h"
+#include "cairo-spans-private.h"
+#include "cairo-error-private.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <setjmp.h>
+
+/* The input coordinate scale and the rasterisation grid scales. */
+#define GLITTER_INPUT_BITS CAIRO_FIXED_FRAC_BITS
+#define GRID_X_BITS CAIRO_FIXED_FRAC_BITS
+#define GRID_Y 15
+
+/* Set glitter up to use a cairo span renderer to do the coverage
+ * blitting. */
+struct pool;
+struct cell_list;
+
+/*-------------------------------------------------------------------------
+ * glitter-paths.h
+ */
+
+/* "Input scaled" numbers are fixed precision reals with multiplier
+ * 2**GLITTER_INPUT_BITS. Input coordinates are given to glitter as
+ * pixel scaled numbers. These get converted to the internal grid
+ * scaled numbers as soon as possible. Internal overflow is possible
+ * if GRID_X/Y inside glitter-paths.c is larger than
+ * 1<<GLITTER_INPUT_BITS. */
+#ifndef GLITTER_INPUT_BITS
+# define GLITTER_INPUT_BITS 8
+#endif
+#define GLITTER_INPUT_SCALE (1<<GLITTER_INPUT_BITS)
+typedef int glitter_input_scaled_t;
+
+/* Opaque type for scan converting. */
+typedef struct glitter_scan_converter glitter_scan_converter_t;
+
+/*-------------------------------------------------------------------------
+ * glitter-paths.c: Implementation internal types
+ */
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+/* All polygon coordinates are snapped onto a subsample grid. "Grid
+ * scaled" numbers are fixed precision reals with multiplier GRID_X or
+ * GRID_Y. */
+typedef int grid_scaled_t;
+typedef int grid_scaled_x_t;
+typedef int grid_scaled_y_t;
+
+/* Default x/y scale factors.
+ * You can either define GRID_X/Y_BITS to get a power-of-two scale
+ * or define GRID_X/Y separately. */
+#if !defined(GRID_X) && !defined(GRID_X_BITS)
+# define GRID_X_BITS 8
+#endif
+#if !defined(GRID_Y) && !defined(GRID_Y_BITS)
+# define GRID_Y 15
+#endif
+
+/* Use GRID_X/Y_BITS to define GRID_X/Y if they're available. */
+#ifdef GRID_X_BITS
+# define GRID_X (1 << GRID_X_BITS)
+#endif
+#ifdef GRID_Y_BITS
+# define GRID_Y (1 << GRID_Y_BITS)
+#endif
+
+/* The GRID_X_TO_INT_FRAC macro splits a grid scaled coordinate into
+ * integer and fractional parts. The integer part is floored. */
+#if defined(GRID_X_TO_INT_FRAC)
+ /* do nothing */
+#elif defined(GRID_X_BITS)
+# define GRID_X_TO_INT_FRAC(x, i, f) \
+ _GRID_TO_INT_FRAC_shift(x, i, f, GRID_X_BITS)
+#else
+# define GRID_X_TO_INT_FRAC(x, i, f) \
+ _GRID_TO_INT_FRAC_general(x, i, f, GRID_X)
+#endif
+
+#define _GRID_TO_INT_FRAC_general(t, i, f, m) do { \
+ (i) = (t) / (m); \
+ (f) = (t) % (m); \
+ if ((f) < 0) { \
+ --(i); \
+ (f) += (m); \
+ } \
+} while (0)
+
+#define _GRID_TO_INT_FRAC_shift(t, i, f, b) do { \
+ (f) = (t) & ((1 << (b)) - 1); \
+ (i) = (t) >> (b); \
+} while (0)
+
+/* A grid area is a real in [0,1] scaled by 2*GRID_X*GRID_Y. We want
+ * to be able to represent exactly areas of subpixel trapezoids whose
+ * vertices are given in grid scaled coordinates. The scale factor
+ * comes from needing to accurately represent the area 0.5*dx*dy of a
+ * triangle with base dx and height dy in grid scaled numbers. */
+typedef int grid_area_t;
+#define GRID_XY (2*GRID_X*GRID_Y) /* Unit area on the grid. */
+
+/* GRID_AREA_TO_ALPHA(area): map [0,GRID_XY] to [0,255]. */
+#if GRID_XY == 510
+# define GRID_AREA_TO_ALPHA(c) (((c)+1) >> 1)
+#elif GRID_XY == 255
+# define GRID_AREA_TO_ALPHA(c) (c)
+#elif GRID_XY == 64
+# define GRID_AREA_TO_ALPHA(c) (((c) << 2) | -(((c) & 0x40) >> 6))
+#elif GRID_XY == 128
+# define GRID_AREA_TO_ALPHA(c) ((((c) << 1) | -((c) >> 7)) & 255)
+#elif GRID_XY == 256
+# define GRID_AREA_TO_ALPHA(c) (((c) | -((c) >> 8)) & 255)
+#elif GRID_XY == 15
+# define GRID_AREA_TO_ALPHA(c) (((c) << 4) + (c))
+#elif GRID_XY == 2*256*15
+# define GRID_AREA_TO_ALPHA(c) (((c) + ((c)<<4) + 256) >> 9)
+#else
+# define GRID_AREA_TO_ALPHA(c) (((c)*255 + GRID_XY/2) / GRID_XY)
+#endif
+
+#define UNROLL3(x) x x x
+
+struct quorem {
+ int32_t quo;
+ int32_t rem;
+};
+
+/* Header for a chunk of memory in a memory pool. */
+struct _pool_chunk {
+ /* # bytes used in this chunk. */
+ size_t size;
+
+ /* # bytes total in this chunk */
+ size_t capacity;
+
+ /* Pointer to the previous chunk or %NULL if this is the sentinel
+ * chunk in the pool header. */
+ struct _pool_chunk *prev_chunk;
+
+ /* Actual data starts here. Well aligned for pointers. */
+};
+
+/* A memory pool. This is supposed to be embedded on the stack or
+ * within some other structure. It may optionally be followed by an
+ * embedded array from which requests are fulfilled until
+ * malloc needs to be called to allocate a first real chunk. */
+struct pool {
+ /* Chunk we're allocating from. */
+ struct _pool_chunk *current;
+
+ jmp_buf *jmp;
+
+ /* Free list of previously allocated chunks. All have >= default
+ * capacity. */
+ struct _pool_chunk *first_free;
+
+ /* The default capacity of a chunk. */
+ size_t default_capacity;
+
+ /* Header for the sentinel chunk. Directly following the pool
+ * struct should be some space for embedded elements from which
+ * the sentinel chunk allocates from. */
+ struct _pool_chunk sentinel[1];
+};
+
+/* A polygon edge. */
+struct edge {
+ /* Next in y-bucket or active list. */
+ struct edge *next;
+
+ /* Current x coordinate while the edge is on the active
+ * list. Initialised to the x coordinate of the top of the
+ * edge. The quotient is in grid_scaled_x_t units and the
+ * remainder is mod dy in grid_scaled_y_t units.*/
+ struct quorem x;
+
+ /* Advance of the current x when moving down a subsample line. */
+ struct quorem dxdy;
+
+ /* Advance of the current x when moving down a full pixel
+ * row. Only initialised when the height of the edge is large
+ * enough that there's a chance the edge could be stepped by a
+ * full row's worth of subsample rows at a time. */
+ struct quorem dxdy_full;
+
+ /* The clipped y of the top of the edge. */
+ grid_scaled_y_t ytop;
+
+ /* y2-y1 after orienting the edge downwards. */
+ grid_scaled_y_t dy;
+
+ /* Number of subsample rows remaining to scan convert of this
+ * edge. */
+ grid_scaled_y_t height_left;
+
+ /* Original sign of the edge: +1 for downwards, -1 for upwards
+ * edges. */
+ int dir;
+ int vertical;
+ int clip;
+};
+
+/* Number of subsample rows per y-bucket. Must be GRID_Y. */
+#define EDGE_Y_BUCKET_HEIGHT GRID_Y
+
+#define EDGE_Y_BUCKET_INDEX(y, ymin) (((y) - (ymin))/EDGE_Y_BUCKET_HEIGHT)
+
+/* 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. */
+ grid_scaled_y_t ymin, ymax;
+
+ /* 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 {
+ struct pool base[1];
+ struct edge embedded[32];
+ } edge_pool;
+};
+
+/* A cell records the effect on pixel coverage of polygon edges
+ * passing through a pixel. It contains two accumulators of pixel
+ * coverage.
+ *
+ * Consider the effects of a polygon edge on the coverage of a pixel
+ * it intersects and that of the following one. The coverage of the
+ * following pixel is the height of the edge multiplied by the width
+ * of the pixel, and the coverage of the pixel itself is the area of
+ * the trapezoid formed by the edge and the right side of the pixel.
+ *
+ * +-----------------------+-----------------------+
+ * | | |
+ * | | |
+ * |_______________________|_______________________|
+ * | \...................|.......................|\
+ * | \..................|.......................| |
+ * | \.................|.......................| |
+ * | \....covered.....|.......................| |
+ * | \....area.......|.......................| } covered height
+ * | \..............|.......................| |
+ * |uncovered\.............|.......................| |
+ * | area \............|.......................| |
+ * |___________\...........|.......................|/
+ * | | |
+ * | | |
+ * | | |
+ * +-----------------------+-----------------------+
+ *
+ * Since the coverage of the following pixel will always be a multiple
+ * of the width of the pixel, we can store the height of the covered
+ * area instead. The coverage of the pixel itself is the total
+ * coverage minus the area of the uncovered area to the left of the
+ * edge. As it's faster to compute the uncovered area we only store
+ * that and subtract it from the total coverage later when forming
+ * spans to blit.
+ *
+ * The heights and areas are signed, with left edges of the polygon
+ * having positive sign and right edges having negative sign. When
+ * two edges intersect they swap their left/rightness so their
+ * contribution above and below the intersection point must be
+ * computed separately. */
+struct cell {
+ struct cell *next;
+ int x;
+ grid_area_t uncovered_area;
+ grid_scaled_y_t covered_height;
+ grid_scaled_y_t clipped_height;
+};
+
+/* A cell list represents the scan line sparsely as cells ordered by
+ * ascending x. It is geared towards scanning the cells in order
+ * using an internal cursor. */
+struct cell_list {
+ /* Sentinel nodes */
+ struct cell head, tail;
+
+ /* Cursor state for iterating through the cell list. */
+ struct cell *cursor;
+
+ /* Cells in the cell list are owned by the cell list and are
+ * allocated from this pool. */
+ struct {
+ struct pool base[1];
+ struct cell embedded[32];
+ } cell_pool;
+};
+
+struct cell_pair {
+ struct cell *cell1;
+ struct cell *cell2;
+};
+
+/* The active list contains edges in the current scan line ordered by
+ * the x-coordinate of the intercept of the edge and the scan line. */
+struct active_list {
+ /* Leftmost edge on the current scan line. */
+ struct edge *head;
+
+ /* A lower bound on the height of the active edges is used to
+ * estimate how soon some active edge ends. We can't advance the
+ * scan conversion by a full pixel row if an edge ends somewhere
+ * within it. */
+ grid_scaled_y_t min_height;
+};
+
+struct glitter_scan_converter {
+ struct polygon polygon[1];
+ struct active_list active[1];
+ struct cell_list coverages[1];
+
+ /* Clip box. */
+ grid_scaled_y_t ymin, ymax;
+};
+
+/* 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 struct _pool_chunk *
+_pool_chunk_init(
+ struct _pool_chunk *p,
+ struct _pool_chunk *prev_chunk,
+ size_t capacity)
+{
+ p->prev_chunk = prev_chunk;
+ p->size = 0;
+ p->capacity = capacity;
+ return p;
+}
+
+static struct _pool_chunk *
+_pool_chunk_create(struct pool *pool, size_t size)
+{
+ struct _pool_chunk *p;
+
+ p = malloc(size + sizeof(struct _pool_chunk));
+ if (unlikely (NULL == p))
+ longjmp (*pool->jmp, _cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ return _pool_chunk_init(p, pool->current, size);
+}
+
+static void
+pool_init(struct pool *pool,
+ jmp_buf *jmp,
+ size_t default_capacity,
+ size_t embedded_capacity)
+{
+ pool->jmp = jmp;
+ pool->current = pool->sentinel;
+ pool->first_free = NULL;
+ pool->default_capacity = default_capacity;
+ _pool_chunk_init(pool->sentinel, NULL, embedded_capacity);
+}
+
+static void
+pool_fini(struct pool *pool)
+{
+ struct _pool_chunk *p = pool->current;
+ do {
+ while (NULL != p) {
+ struct _pool_chunk *prev = p->prev_chunk;
+ if (p != pool->sentinel)
+ free(p);
+ p = prev;
+ }
+ p = pool->first_free;
+ pool->first_free = NULL;
+ } while (NULL != p);
+}
+
+/* Satisfy an allocation by first allocating a new large enough chunk
+ * and adding it to the head of the pool's chunk list. This function
+ * is called as a fallback if pool_alloc() couldn't do a quick
+ * allocation from the current chunk in the pool. */
+static void *
+_pool_alloc_from_new_chunk(
+ struct pool *pool,
+ size_t size)
+{
+ struct _pool_chunk *chunk;
+ void *obj;
+ size_t capacity;
+
+ /* If the allocation is smaller than the default chunk size then
+ * try getting a chunk off the free list. Force alloc of a new
+ * chunk for large requests. */
+ capacity = size;
+ chunk = NULL;
+ if (size < pool->default_capacity) {
+ capacity = pool->default_capacity;
+ chunk = pool->first_free;
+ if (chunk) {
+ pool->first_free = chunk->prev_chunk;
+ _pool_chunk_init(chunk, pool->current, chunk->capacity);
+ }
+ }
+
+ if (NULL == chunk)
+ chunk = _pool_chunk_create (pool, capacity);
+ pool->current = chunk;
+
+ obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size);
+ chunk->size += size;
+ return obj;
+}
+
+/* Allocate size bytes from the pool. The first allocated address
+ * returned from a pool is aligned to sizeof(void*). Subsequent
+ * addresses will maintain alignment as long as multiples of void* are
+ * allocated. Returns the address of a new memory area or %NULL on
+ * allocation failures. The pool retains ownership of the returned
+ * memory. */
+inline static void *
+pool_alloc (struct pool *pool, size_t size)
+{
+ struct _pool_chunk *chunk = pool->current;
+
+ if (size <= chunk->capacity - chunk->size) {
+ void *obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size);
+ chunk->size += size;
+ return obj;
+ } else {
+ return _pool_alloc_from_new_chunk(pool, size);
+ }
+}
+
+/* Relinquish all pool_alloced memory back to the pool. */
+static void
+pool_reset (struct pool *pool)
+{
+ /* Transfer all used chunks to the chunk free list. */
+ struct _pool_chunk *chunk = pool->current;
+ if (chunk != pool->sentinel) {
+ while (chunk->prev_chunk != pool->sentinel) {
+ chunk = chunk->prev_chunk;
+ }
+ chunk->prev_chunk = pool->first_free;
+ pool->first_free = pool->current;
+ }
+ /* Reset the sentinel as the current chunk. */
+ pool->current = pool->sentinel;
+ pool->sentinel->size = 0;
+}
+
+/* Rewinds the cell list's cursor to the beginning. After rewinding
+ * we're good to cell_list_find() the cell any x coordinate. */
+inline static void
+cell_list_rewind (struct cell_list *cells)
+{
+ cells->cursor = &cells->head;
+}
+
+/* Rewind the cell list if its cursor has been advanced past x. */
+inline static void
+cell_list_maybe_rewind (struct cell_list *cells, int x)
+{
+ struct cell *tail = cells->cursor;
+ if (tail->x > x)
+ cell_list_rewind (cells);
+}
+
+static void
+cell_list_init(struct cell_list *cells, jmp_buf *jmp)
+{
+ pool_init(cells->cell_pool.base, jmp,
+ 256*sizeof(struct cell),
+ sizeof(cells->cell_pool.embedded));
+ cells->tail.next = NULL;
+ cells->tail.x = INT_MAX;
+ cells->head.x = INT_MIN;
+ cells->head.next = &cells->tail;
+ cell_list_rewind (cells);
+}
+
+static void
+cell_list_fini(struct cell_list *cells)
+{
+ pool_fini (cells->cell_pool.base);
+}
+
+/* Empty the cell list. This is called at the start of every pixel
+ * row. */
+inline static void
+cell_list_reset (struct cell_list *cells)
+{
+ cell_list_rewind (cells);
+ cells->head.next = &cells->tail;
+ pool_reset (cells->cell_pool.base);
+}
+
+static struct cell *
+cell_list_alloc (struct cell_list *cells,
+ struct cell *tail,
+ int x)
+{
+ struct cell *cell;
+
+ cell = pool_alloc (cells->cell_pool.base, sizeof (struct cell));
+ cell->next = tail->next;
+ tail->next = cell;
+ cell->x = x;
+ cell->uncovered_area = 0;
+ cell->covered_height = 0;
+ cell->clipped_height = 0;
+ return cell;
+}
+
+/* Find a cell at the given x-coordinate. Returns %NULL if a new cell
+ * needed to be allocated but couldn't be. Cells must be found with
+ * non-decreasing x-coordinate until the cell list is rewound using
+ * cell_list_rewind(). Ownership of the returned cell is retained by
+ * the cell list. */
+inline static struct cell *
+cell_list_find (struct cell_list *cells, int x)
+{
+ struct cell *tail = cells->cursor;
+
+ while (1) {
+ UNROLL3({
+ if (tail->next->x > x)
+ break;
+ tail = tail->next;
+ });
+ }
+
+ if (tail->x != x)
+ tail = cell_list_alloc (cells, tail, x);
+ return cells->cursor = tail;
+
+}
+
+/* Find two cells at x1 and x2. This is exactly equivalent
+ * to
+ *
+ * pair.cell1 = cell_list_find(cells, x1);
+ * pair.cell2 = cell_list_find(cells, x2);
+ *
+ * except with less function call overhead. */
+inline static struct cell_pair
+cell_list_find_pair(struct cell_list *cells, int x1, int x2)
+{
+ struct cell_pair pair;
+
+ pair.cell1 = cells->cursor;
+ while (1) {
+ UNROLL3({
+ if (pair.cell1->next->x > x1)
+ break;
+ pair.cell1 = pair.cell1->next;
+ });
+ }
+ if (pair.cell1->x != x1) {
+ struct cell *cell = pool_alloc (cells->cell_pool.base,
+ sizeof (struct cell));
+ cell->x = x1;
+ cell->uncovered_area = 0;
+ cell->covered_height = 0;
+ cell->clipped_height = 0;
+ cell->next = pair.cell1->next;
+ pair.cell1->next = cell;
+ pair.cell1 = cell;
+ }
+
+ pair.cell2 = pair.cell1;
+ while (1) {
+ UNROLL3({
+ if (pair.cell2->next->x > x2)
+ break;
+ pair.cell2 = pair.cell2->next;
+ });
+ }
+ if (pair.cell2->x != x2) {
+ struct cell *cell = pool_alloc (cells->cell_pool.base,
+ sizeof (struct cell));
+ cell->uncovered_area = 0;
+ cell->covered_height = 0;
+ cell->clipped_height = 0;
+ cell->x = x2;
+ cell->next = pair.cell2->next;
+ pair.cell2->next = cell;
+ pair.cell2 = cell;
+ }
+
+ cells->cursor = pair.cell2;
+ return pair;
+}
+
+/* Add a subpixel span covering [x1, x2) to the coverage cells. */
+inline static void
+cell_list_add_subspan(struct cell_list *cells,
+ grid_scaled_x_t x1,
+ grid_scaled_x_t x2)
+{
+ int ix1, fx1;
+ int ix2, fx2;
+
+ GRID_X_TO_INT_FRAC(x1, ix1, fx1);
+ GRID_X_TO_INT_FRAC(x2, ix2, fx2);
+
+ if (ix1 != ix2) {
+ struct cell_pair p;
+ p = cell_list_find_pair(cells, ix1, ix2);
+ p.cell1->uncovered_area += 2*fx1;
+ ++p.cell1->covered_height;
+ p.cell2->uncovered_area -= 2*fx2;
+ --p.cell2->covered_height;
+ } else {
+ struct cell *cell = cell_list_find(cells, ix1);
+ cell->uncovered_area += 2*(fx1-fx2);
+ }
+}
+
+/* Adds the analytical coverage of an edge crossing the current pixel
+ * row to the coverage cells and advances the edge's x position to the
+ * following row.
+ *
+ * This function is only called when we know that during this pixel row:
+ *
+ * 1) The relative order of all edges on the active list doesn't
+ * change. In particular, no edges intersect within this row to pixel
+ * precision.
+ *
+ * 2) No new edges start in this row.
+ *
+ * 3) No existing edges end mid-row.
+ *
+ * This function depends on being called with all edges from the
+ * active list in the order they appear on the list (i.e. with
+ * non-decreasing x-coordinate.) */
+static void
+cell_list_render_edge(
+ struct cell_list *cells,
+ struct edge *edge,
+ int sign)
+{
+ grid_scaled_y_t y1, y2, dy;
+ grid_scaled_x_t dx;
+ int ix1, ix2;
+ grid_scaled_x_t fx1, fx2;
+
+ struct quorem x1 = edge->x;
+ struct quorem x2 = x1;
+
+ if (! edge->vertical) {
+ x2.quo += edge->dxdy_full.quo;
+ x2.rem += edge->dxdy_full.rem;
+ if (x2.rem >= 0) {
+ ++x2.quo;
+ x2.rem -= edge->dy;
+ }
+
+ edge->x = x2;
+ }
+
+ GRID_X_TO_INT_FRAC(x1.quo, ix1, fx1);
+ GRID_X_TO_INT_FRAC(x2.quo, ix2, fx2);
+
+ /* Edge is entirely within a column? */
+ if (ix1 == ix2) {
+ /* We always know that ix1 is >= the cell list cursor in this
+ * case due to the no-intersections precondition. */
+ struct cell *cell = cell_list_find(cells, ix1);
+ cell->covered_height += sign*GRID_Y;
+ cell->uncovered_area += sign*(fx1 + fx2)*GRID_Y;
+ return;
+ }
+
+ /* Orient the edge left-to-right. */
+ dx = x2.quo - x1.quo;
+ if (dx >= 0) {
+ y1 = 0;
+ y2 = GRID_Y;
+ } else {
+ int tmp;
+ tmp = ix1; ix1 = ix2; ix2 = tmp;
+ tmp = fx1; fx1 = fx2; fx2 = tmp;
+ dx = -dx;
+ sign = -sign;
+ y1 = GRID_Y;
+ y2 = 0;
+ }
+ dy = y2 - y1;
+
+ /* Add coverage for all pixels [ix1,ix2] on this row crossed
+ * by the edge. */
+ {
+ struct cell_pair pair;
+ struct quorem y = floored_divrem((GRID_X - fx1)*dy, dx);
+
+ /* When rendering a previous edge on the active list we may
+ * advance the cell list cursor past the leftmost pixel of the
+ * current edge even though the two edges don't intersect.
+ * e.g. consider two edges going down and rightwards:
+ *
+ * --\_+---\_+-----+-----+----
+ * \_ \_ | |
+ * | \_ | \_ | |
+ * | \_| \_| |
+ * | \_ \_ |
+ * ----+-----+-\---+-\---+----
+ *
+ * The left edge touches cells past the starting cell of the
+ * right edge. Fortunately such cases are rare.
+ *
+ * The rewinding is never necessary if the current edge stays
+ * within a single column because we've checked before calling
+ * this function that the active list order won't change. */
+ cell_list_maybe_rewind(cells, ix1);
+
+ pair = cell_list_find_pair(cells, ix1, ix1+1);
+ pair.cell1->uncovered_area += sign*y.quo*(GRID_X + fx1);
+ pair.cell1->covered_height += sign*y.quo;
+ y.quo += y1;
+
+ if (ix1+1 < ix2) {
+ struct quorem dydx_full = floored_divrem(GRID_X*dy, dx);
+ struct cell *cell = pair.cell2;
+
+ ++ix1;
+ do {
+ grid_scaled_y_t y_skip = dydx_full.quo;
+ y.rem += dydx_full.rem;
+ if (y.rem >= dx) {
+ ++y_skip;
+ y.rem -= dx;
+ }
+
+ y.quo += y_skip;
+
+ y_skip *= sign;
+ cell->uncovered_area += y_skip*GRID_X;
+ cell->covered_height += y_skip;
+
+ ++ix1;
+ cell = cell_list_find(cells, ix1);
+ } while (ix1 != ix2);
+
+ pair.cell2 = cell;
+ }
+ pair.cell2->uncovered_area += sign*(y2 - y.quo)*fx2;
+ pair.cell2->covered_height += sign*(y2 - y.quo);
+ }
+}
+
+static void
+polygon_init (struct polygon *polygon, jmp_buf *jmp)
+{
+ polygon->ymin = polygon->ymax = 0;
+ polygon->y_buckets = polygon->y_buckets_embedded;
+ pool_init (polygon->edge_pool.base, jmp,
+ 8192 - sizeof (struct _pool_chunk),
+ sizeof (polygon->edge_pool.embedded));
+}
+
+static void
+polygon_fini (struct polygon *polygon)
+{
+ if (polygon->y_buckets != polygon->y_buckets_embedded)
+ free (polygon->y_buckets);
+
+ pool_fini (polygon->edge_pool.base);
+}
+
+/* Empties the polygon of all edges. The polygon is then prepared to
+ * receive new edges and clip them to the vertical range
+ * [ymin,ymax). */
+static cairo_status_t
+polygon_reset (struct polygon *polygon,
+ grid_scaled_y_t ymin,
+ grid_scaled_y_t ymax)
+{
+ unsigned h = ymax - ymin;
+ unsigned num_buckets = EDGE_Y_BUCKET_INDEX(ymax + EDGE_Y_BUCKET_HEIGHT-1,
+ ymin);
+
+ pool_reset(polygon->edge_pool.base);
+
+ if (unlikely (h > 0x7FFFFFFFU - EDGE_Y_BUCKET_HEIGHT))
+ goto bail_no_mem; /* even if you could, you wouldn't want to. */
+
+ if (polygon->y_buckets != polygon->y_buckets_embedded)
+ free (polygon->y_buckets);
+
+ polygon->y_buckets = polygon->y_buckets_embedded;
+ if (num_buckets > ARRAY_LENGTH (polygon->y_buckets_embedded)) {
+ polygon->y_buckets = _cairo_malloc_ab (num_buckets,
+ sizeof (struct edge *));
+ if (unlikely (NULL == polygon->y_buckets))
+ goto bail_no_mem;
+ }
+ memset (polygon->y_buckets, 0, num_buckets * sizeof (struct edge *));
+
+ polygon->ymin = ymin;
+ polygon->ymax = ymax;
+ return CAIRO_STATUS_SUCCESS;
+
+ bail_no_mem:
+ polygon->ymin = 0;
+ polygon->ymax = 0;
+ return CAIRO_STATUS_NO_MEMORY;
+}
+
+static void
+_polygon_insert_edge_into_its_y_bucket(
+ struct polygon *polygon,
+ struct edge *e)
+{
+ unsigned ix = EDGE_Y_BUCKET_INDEX(e->ytop, polygon->ymin);
+ struct edge **ptail = &polygon->y_buckets[ix];
+ e->next = *ptail;
+ *ptail = e;
+}
+
+inline static void
+polygon_add_edge (struct polygon *polygon,
+ const cairo_edge_t *edge,
+ int clip)
+{
+ struct edge *e;
+ grid_scaled_x_t dx;
+ grid_scaled_y_t dy;
+ grid_scaled_y_t ytop, ybot;
+ grid_scaled_y_t ymin = polygon->ymin;
+ grid_scaled_y_t ymax = polygon->ymax;
+
+ assert (edge->bottom > edge->top);
+
+ if (unlikely (edge->top >= ymax || edge->bottom <= ymin))
+ return;
+
+ e = pool_alloc (polygon->edge_pool.base, sizeof (struct edge));
+
+ dx = edge->line.p2.x - edge->line.p1.x;
+ dy = edge->line.p2.y - edge->line.p1.y;
+ e->dy = dy;
+ e->dir = edge->dir;
+ e->clip = clip;
+
+ ytop = edge->top >= ymin ? edge->top : ymin;
+ ybot = edge->bottom <= ymax ? edge->bottom : ymax;
+ e->ytop = ytop;
+ e->height_left = ybot - ytop;
+
+ 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->dxdy_full.quo = 0;
+ e->dxdy_full.rem = 0;
+ } else {
+ e->vertical = FALSE;
+ e->dxdy = floored_divrem (dx, dy);
+ if (ytop == edge->line.p1.y) {
+ e->x.quo = edge->line.p1.x;
+ e->x.rem = 0;
+ } else {
+ e->x = floored_muldivrem (ytop - edge->line.p1.y, dx, dy);
+ e->x.quo += edge->line.p1.x;
+ }
+
+ if (e->height_left >= GRID_Y) {
+ e->dxdy_full = floored_muldivrem (GRID_Y, dx, dy);
+ } else {
+ e->dxdy_full.quo = 0;
+ e->dxdy_full.rem = 0;
+ }
+ }
+
+ _polygon_insert_edge_into_its_y_bucket (polygon, e);
+
+ e->x.rem -= dy; /* Bias the remainder for faster
+ * edge advancement. */
+}
+
+static void
+active_list_reset (struct active_list *active)
+{
+ active->head = NULL;
+ active->min_height = 0;
+}
+
+static void
+active_list_init(struct active_list *active)
+{
+ active_list_reset(active);
+}
+
+/*
+ * Merge two sorted edge lists.
+ * Input:
+ * - head_a: The head of the first list.
+ * - head_b: The head of the second list; head_b cannot be NULL.
+ * Output:
+ * Returns the head of the merged list.
+ *
+ * Implementation notes:
+ * To make it fast (in particular, to reduce to an insertion sort whenever
+ * one of the two input lists only has a single element) we iterate through
+ * a list until its head becomes greater than the head of the other list,
+ * then we switch their roles. As soon as one of the two lists is empty, we
+ * just attach the other one to the current list and exit.
+ * Writes to memory are only needed to "switch" lists (as it also requires
+ * attaching to the output list the list which we will be iterating next) and
+ * to attach the last non-empty list.
+ */
+static struct edge *
+merge_sorted_edges (struct edge *head_a, struct edge *head_b)
+{
+ struct edge *head, **next;
+ int32_t x;
+
+ if (head_a == NULL)
+ return head_b;
+
+ next = &head;
+ if (head_a->x.quo <= head_b->x.quo) {
+ head = head_a;
+ } else {
+ head = head_b;
+ goto start_with_b;
+ }
+
+ do {
+ x = head_b->x.quo;
+ while (head_a != NULL && head_a->x.quo <= x) {
+ next = &head_a->next;
+ head_a = head_a->next;
+ }
+
+ *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) {
+ next = &head_b->next;
+ head_b = head_b->next;
+ }
+
+ *next = head_a;
+ if (head_b == NULL)
+ return head;
+ } while (1);
+}
+
+/*
+ * Sort (part of) a list.
+ * Input:
+ * - list: The list to be sorted; list cannot be NULL.
+ * - limit: Recursion limit.
+ * Output:
+ * - head_out: The head of the sorted list containing the first 2^(level+1) elements of the
+ * input list; if the input list has fewer elements, head_out be a sorted list
+ * containing all the elements of the input list.
+ * Returns the head of the list of unprocessed elements (NULL if the sorted list contains
+ * all the elements of the input list).
+ *
+ * Implementation notes:
+ * Special case single element list, unroll/inline the sorting of the first two elements.
+ * Some tail recursion is used since we iterate on the bottom-up solution of the problem
+ * (we start with a small sorted list and keep merging other lists of the same size to it).
+ */
+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;
+
+ /* Single element list -> return */
+ if (head_other == NULL) {
+ *head_out = list;
+ return NULL;
+ }
+
+ /* Unroll the first iteration of the following loop (halves the number of calls to merge_sorted_edges):
+ * - Initialize remaining to be the list containing the elements after the second in the input list.
+ * - Initialize *head_out to be the sorted list containing the first two element.
+ */
+ remaining = head_other->next;
+ if (list->x.quo <= head_other->x.quo) {
+ *head_out = list;
+ /* list->next = head_other; */ /* The input list is already like this. */
+ head_other->next = NULL;
+ } else {
+ *head_out = head_other;
+ head_other->next = list;
+ list->next = NULL;
+ }
+
+ for (i = 0; i < level && remaining; i++) {
+ /* Extract a sorted list of the same size as *head_out
+ * (2^(i+1) elements) from the list of remaining elements. */
+ remaining = sort_edges (remaining, i, &head_other);
+ *head_out = merge_sorted_edges (*head_out, head_other);
+ }
+
+ /* *head_out now contains (at most) 2^(level+1) elements. */
+
+ return remaining;
+}
+
+/* Test if the edges on the active list can be safely advanced by a
+ * full row without intersections or any edges ending. */
+inline static int
+active_list_can_step_full_row (struct active_list *active)
+{
+ const struct edge *e;
+ int prev_x = INT_MIN;
+
+ /* Recomputes the minimum height of all edges on the active
+ * list if we have been dropping edges. */
+ if (active->min_height <= 0) {
+ int min_height = INT_MAX;
+
+ e = active->head;
+ while (NULL != e) {
+ if (e->height_left < min_height)
+ min_height = e->height_left;
+ e = e->next;
+ }
+
+ active->min_height = min_height;
+ }
+
+ if (active->min_height < GRID_Y)
+ return 0;
+
+ /* Check for intersections as no edges end during the next row. */
+ e = active->head;
+ while (NULL != e) {
+ struct quorem x = e->x;
+
+ if (! e->vertical) {
+ x.quo += e->dxdy_full.quo;
+ x.rem += e->dxdy_full.rem;
+ if (x.rem >= 0)
+ ++x.quo;
+ }
+
+ if (x.quo <= prev_x)
+ return 0;
+
+ prev_x = x.quo;
+ e = e->next;
+ }
+
+ return 1;
+}
+
+/* Merges edges on the given subpixel row from the polygon to the
+ * active_list. */
+inline static void
+active_list_merge_edges_from_polygon(struct active_list *active,
+ struct edge **ptail,
+ grid_scaled_y_t y,
+ struct polygon *polygon)
+{
+ /* Split off the edges on the current subrow and merge them into
+ * the active list. */
+ int min_height = active->min_height;
+ struct edge *subrow_edges = NULL;
+ struct edge *tail = *ptail;
+
+ do {
+ struct edge *next = tail->next;
+
+ if (y == tail->ytop) {
+ tail->next = subrow_edges;
+ subrow_edges = tail;
+
+ if (tail->height_left < min_height)
+ min_height = tail->height_left;
+
+ *ptail = next;
+ } else
+ ptail = &tail->next;
+
+ tail = next;
+ } while (tail);
+
+ if (subrow_edges) {
+ sort_edges (subrow_edges, UINT_MAX, &subrow_edges);
+ active->head = merge_sorted_edges (active->head, subrow_edges);
+ active->min_height = min_height;
+ }
+}
+
+/* Advance the edges on the active list by one subsample row by
+ * updating their x positions. Drop edges from the list that end. */
+inline static void
+active_list_substep_edges(struct active_list *active)
+{
+ struct edge **cursor = &active->head;
+ grid_scaled_x_t prev_x = INT_MIN;
+ struct edge *unsorted = NULL;
+ struct edge *edge = *cursor;
+
+ do {
+ UNROLL3({
+ struct edge *next;
+
+ if (NULL == edge)
+ break;
+
+ next = edge->next;
+ if (--edge->height_left) {
+ 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) {
+ *cursor = next;
+ edge->next = unsorted;
+ unsorted = edge;
+ } else {
+ prev_x = edge->x.quo;
+ cursor = &edge->next;
+ }
+ } else {
+ *cursor = next;
+ }
+ edge = next;
+ })
+ } while (1);
+
+ if (unsorted) {
+ sort_edges (unsorted, UINT_MAX, &unsorted);
+ active->head = merge_sorted_edges (active->head, unsorted);
+ }
+}
+
+inline static void
+apply_nonzero_fill_rule_for_subrow (struct active_list *active,
+ struct cell_list *coverages)
+{
+ struct edge *edge = active->head;
+ int winding = 0;
+ int xstart;
+ int xend;
+
+ cell_list_rewind (coverages);
+
+ while (NULL != edge) {
+ xstart = edge->x.quo;
+ winding = edge->dir;
+ while (1) {
+ edge = edge->next;
+ if (NULL == edge) {
+ ASSERT_NOT_REACHED;
+ return;
+ }
+
+ winding += edge->dir;
+ if (0 == winding) {
+ if (edge->next == NULL || edge->next->x.quo != edge->x.quo)
+ break;
+ }
+ }
+
+ xend = edge->x.quo;
+ cell_list_add_subspan (coverages, xstart, xend);
+
+ edge = edge->next;
+ }
+}
+
+static void
+apply_evenodd_fill_rule_for_subrow (struct active_list *active,
+ struct cell_list *coverages)
+{
+ struct edge *edge = active->head;
+ int xstart;
+ int xend;
+
+ cell_list_rewind (coverages);
+
+ while (NULL != edge) {
+ xstart = edge->x.quo;
+
+ while (1) {
+ edge = edge->next;
+ if (NULL == edge) {
+ ASSERT_NOT_REACHED;
+ return;
+ }
+
+ if (edge->next == NULL || edge->next->x.quo != edge->x.quo)
+ break;
+
+ edge = edge->next;
+ }
+
+ xend = edge->x.quo;
+ cell_list_add_subspan (coverages, xstart, xend);
+
+ edge = edge->next;
+ }
+}
+
+static void
+apply_nonzero_fill_rule_and_step_edges (struct active_list *active,
+ struct cell_list *coverages)
+{
+ struct edge **cursor = &active->head;
+ struct edge *left_edge;
+
+ left_edge = *cursor;
+ while (NULL != left_edge) {
+ struct edge *right_edge;
+ int winding = left_edge->dir;
+
+ left_edge->height_left -= GRID_Y;
+ if (left_edge->height_left)
+ cursor = &left_edge->next;
+ else
+ *cursor = left_edge->next;
+
+ while (1) {
+ right_edge = *cursor;
+ if (NULL == right_edge) {
+ cell_list_render_edge (coverages, left_edge, +1);
+ return;
+ }
+
+ right_edge->height_left -= GRID_Y;
+ if (right_edge->height_left)
+ cursor = &right_edge->next;
+ else
+ *cursor = right_edge->next;
+
+ winding += right_edge->dir;
+ if (0 == winding) {
+ if (right_edge->next == NULL ||
+ right_edge->next->x.quo != right_edge->x.quo)
+ {
+ break;
+ }
+ }
+
+ if (! right_edge->vertical) {
+ right_edge->x.quo += right_edge->dxdy_full.quo;
+ right_edge->x.rem += right_edge->dxdy_full.rem;
+ if (right_edge->x.rem >= 0) {
+ ++right_edge->x.quo;
+ right_edge->x.rem -= right_edge->dy;
+ }
+ }
+ }
+
+ cell_list_render_edge (coverages, left_edge, +1);
+ cell_list_render_edge (coverages, right_edge, -1);
+
+ left_edge = *cursor;
+ }
+}
+
+static void
+apply_evenodd_fill_rule_and_step_edges (struct active_list *active,
+ struct cell_list *coverages)
+{
+ struct edge **cursor = &active->head;
+ struct edge *left_edge;
+
+ left_edge = *cursor;
+ while (NULL != left_edge) {
+ struct edge *right_edge;
+
+ left_edge->height_left -= GRID_Y;
+ if (left_edge->height_left)
+ cursor = &left_edge->next;
+ else
+ *cursor = left_edge->next;
+
+ while (1) {
+ right_edge = *cursor;
+ if (NULL == right_edge) {
+ cell_list_render_edge (coverages, left_edge, +1);
+ return;
+ }
+
+ right_edge->height_left -= GRID_Y;
+ if (right_edge->height_left)
+ cursor = &right_edge->next;
+ else
+ *cursor = right_edge->next;
+
+ if (right_edge->next == NULL ||
+ right_edge->next->x.quo != right_edge->x.quo)
+ {
+ break;
+ }
+
+ if (! right_edge->vertical) {
+ right_edge->x.quo += right_edge->dxdy_full.quo;
+ right_edge->x.rem += right_edge->dxdy_full.rem;
+ if (right_edge->x.rem >= 0) {
+ ++right_edge->x.quo;
+ right_edge->x.rem -= right_edge->dy;
+ }
+ }
+ }
+
+ cell_list_render_edge (coverages, left_edge, +1);
+ cell_list_render_edge (coverages, right_edge, -1);
+
+ left_edge = *cursor;
+ }
+}
+
+static void
+_glitter_scan_converter_init(glitter_scan_converter_t *converter, jmp_buf *jmp)
+{
+ polygon_init(converter->polygon, jmp);
+ active_list_init(converter->active);
+ cell_list_init(converter->coverages, jmp);
+ converter->ymin=0;
+ converter->ymax=0;
+}
+
+static void
+_glitter_scan_converter_fini(glitter_scan_converter_t *converter)
+{
+ polygon_fini(converter->polygon);
+ cell_list_fini(converter->coverages);
+ converter->ymin=0;
+ converter->ymax=0;
+}
+
+static grid_scaled_t
+int_to_grid_scaled(int i, int scale)
+{
+ /* Clamp to max/min representable scaled number. */
+ if (i >= 0) {
+ if (i >= INT_MAX/scale)
+ i = INT_MAX/scale;
+ }
+ else {
+ if (i <= INT_MIN/scale)
+ i = INT_MIN/scale;
+ }
+ return i*scale;
+}
+
+#define int_to_grid_scaled_x(x) int_to_grid_scaled((x), GRID_X)
+#define int_to_grid_scaled_y(x) int_to_grid_scaled((x), GRID_Y)
+
+static cairo_status_t
+glitter_scan_converter_reset(glitter_scan_converter_t *converter,
+ int ymin, int ymax)
+{
+ cairo_status_t status;
+
+ converter->ymin = 0;
+ converter->ymax = 0;
+
+ ymin = int_to_grid_scaled_y(ymin);
+ ymax = int_to_grid_scaled_y(ymax);
+
+ active_list_reset(converter->active);
+ cell_list_reset(converter->coverages);
+ status = polygon_reset(converter->polygon, ymin, ymax);
+ if (status)
+ return status;
+
+ converter->ymin = ymin;
+ converter->ymax = ymax;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/* INPUT_TO_GRID_X/Y (in_coord, out_grid_scaled, grid_scale)
+ * These macros convert an input coordinate in the client's
+ * device space to the rasterisation grid.
+ */
+/* Gah.. this bit of ugly defines INPUT_TO_GRID_X/Y so as to use
+ * shifts if possible, and something saneish if not.
+ */
+#if !defined(INPUT_TO_GRID_Y) && defined(GRID_Y_BITS) && GRID_Y_BITS <= GLITTER_INPUT_BITS
+# define INPUT_TO_GRID_Y(in, out) (out) = (in) >> (GLITTER_INPUT_BITS - GRID_Y_BITS)
+#else
+# define INPUT_TO_GRID_Y(in, out) INPUT_TO_GRID_general(in, out, GRID_Y)
+#endif
+
+#if !defined(INPUT_TO_GRID_X) && defined(GRID_X_BITS) && GRID_X_BITS <= GLITTER_INPUT_BITS
+# define INPUT_TO_GRID_X(in, out) (out) = (in) >> (GLITTER_INPUT_BITS - GRID_X_BITS)
+#else
+# define INPUT_TO_GRID_X(in, out) INPUT_TO_GRID_general(in, out, GRID_X)
+#endif
+
+#define INPUT_TO_GRID_general(in, out, grid_scale) do { \
+ long long tmp__ = (long long)(grid_scale) * (in); \
+ tmp__ >>= GLITTER_INPUT_BITS; \
+ (out) = tmp__; \
+} while (0)
+
+static void
+glitter_scan_converter_add_edge (glitter_scan_converter_t *converter,
+ const cairo_edge_t *edge,
+ int clip)
+{
+ cairo_edge_t e;
+
+ INPUT_TO_GRID_Y (edge->top, e.top);
+ INPUT_TO_GRID_Y (edge->bottom, e.bottom);
+ if (e.top >= e.bottom)
+ return;
+
+ /* XXX: possible overflows if GRID_X/Y > 2**GLITTER_INPUT_BITS */
+ INPUT_TO_GRID_Y (edge->line.p1.y, e.line.p1.y);
+ INPUT_TO_GRID_Y (edge->line.p2.y, e.line.p2.y);
+ if (e.line.p1.y == e.line.p2.y)
+ return;
+
+ INPUT_TO_GRID_X (edge->line.p1.x, e.line.p1.x);
+ INPUT_TO_GRID_X (edge->line.p2.x, e.line.p2.x);
+
+ e.dir = edge->dir;
+
+ polygon_add_edge (converter->polygon, &e, clip);
+}
+
+static cairo_bool_t
+active_list_is_vertical (struct active_list *active)
+{
+ struct edge *e;
+
+ for (e = active->head; e != NULL; e = e->next) {
+ if (! e->vertical)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+step_edges (struct active_list *active, int count)
+{
+ struct edge **cursor = &active->head;
+ struct edge *edge;
+
+ for (edge = *cursor; edge != NULL; edge = *cursor) {
+ edge->height_left -= GRID_Y * count;
+ if (edge->height_left)
+ cursor = &edge->next;
+ else
+ *cursor = edge->next;
+ }
+}
+
+static cairo_status_t
+blit_coverages (struct cell_list *cells,
+ cairo_span_renderer_t *renderer,
+ struct pool *span_pool,
+ int y, int height)
+{
+ struct cell *cell = cells->head.next;
+ int prev_x = -1;
+ int cover = 0, last_cover = 0;
+ int clip = 0;
+ cairo_half_open_span_t *spans;
+ unsigned num_spans;
+
+ assert (cell != &cells->tail);
+
+ /* Count number of cells remaining. */
+ {
+ struct cell *next = cell;
+ num_spans = 2;
+ while (next->next) {
+ next = next->next;
+ ++num_spans;
+ }
+ num_spans = 2*num_spans;
+ }
+
+ /* Allocate enough spans for the row. */
+ pool_reset (span_pool);
+ spans = pool_alloc (span_pool, sizeof(spans[0])*num_spans);
+ num_spans = 0;
+
+ /* Form the spans from the coverages and areas. */
+ for (; cell->next; cell = cell->next) {
+ int x = cell->x;
+ int area;
+
+ if (x > prev_x && cover != last_cover) {
+ spans[num_spans].x = prev_x;
+ spans[num_spans].coverage = GRID_AREA_TO_ALPHA (cover);
+ spans[num_spans].is_clipped = 0;
+ last_cover = cover;
+ ++num_spans;
+ }
+
+ cover += cell->covered_height*GRID_X*2;
+ clip += cell->covered_height*GRID_X*2;
+ area = cover - cell->uncovered_area;
+
+ if (area != last_cover) {
+ spans[num_spans].x = x;
+ spans[num_spans].is_clipped = 0;
+ spans[num_spans].coverage = GRID_AREA_TO_ALPHA (area);
+ last_cover = area;
+ ++num_spans;
+ }
+
+ prev_x = x+1;
+ }
+
+ /* Dump them into the renderer. */
+ return renderer->render_rows (renderer, y, height, spans, num_spans);
+}
+
+static void
+glitter_scan_converter_render(glitter_scan_converter_t *converter,
+ int nonzero_fill,
+ cairo_span_renderer_t *span_renderer,
+ struct pool *span_pool)
+{
+ int i, j;
+ int ymax_i = converter->ymax / GRID_Y;
+ int ymin_i = converter->ymin / GRID_Y;
+ int h = ymax_i - ymin_i;
+ struct polygon *polygon = converter->polygon;
+ struct cell_list *coverages = converter->coverages;
+ struct active_list *active = converter->active;
+
+ /* Render each pixel row. */
+ for (i = 0; i < h; i = j) {
+ int do_full_step = 0;
+
+ j = i + 1;
+
+ /* Determine if we can ignore this row or use the full pixel
+ * stepper. */
+ if (GRID_Y == EDGE_Y_BUCKET_HEIGHT && ! polygon->y_buckets[i]) {
+ if (! active->head) {
+ for (; j < h && ! polygon->y_buckets[j]; j++)
+ ;
+ continue;
+ }
+
+ do_full_step = active_list_can_step_full_row (active);
+ }
+
+ if (do_full_step) {
+ /* Step by a full pixel row's worth. */
+ if (nonzero_fill)
+ apply_nonzero_fill_rule_and_step_edges (active, coverages);
+ else
+ apply_evenodd_fill_rule_and_step_edges (active, coverages);
+
+ if (active_list_is_vertical (active)) {
+ while (j < h &&
+ polygon->y_buckets[j] == NULL &&
+ active->min_height >= 2*GRID_Y)
+ {
+ active->min_height -= GRID_Y;
+ j++;
+ }
+ if (j != i + 1)
+ step_edges (active, j - (i + 1));
+ }
+ } else {
+ grid_scaled_y_t suby;
+
+ /* Subsample this row. */
+ for (suby = 0; suby < GRID_Y; suby++) {
+ grid_scaled_y_t y = (i+ymin_i)*GRID_Y + suby;
+
+ if (polygon->y_buckets[i]) {
+ active_list_merge_edges_from_polygon (active,
+ &polygon->y_buckets[i], y,
+ polygon);
+ }
+
+ if (nonzero_fill)
+ apply_nonzero_fill_rule_for_subrow (active, coverages);
+ else
+ apply_evenodd_fill_rule_for_subrow (active, coverages);
+
+ active_list_substep_edges(active);
+ }
+ }
+
+ blit_coverages (coverages, span_renderer, span_pool, i+ymin_i, j -i);
+ cell_list_reset (coverages);
+
+ if (! active->head)
+ active->min_height = INT_MAX;
+ else
+ active->min_height -= GRID_Y;
+ }
+}
+
+struct _cairo_clip_tor_scan_converter {
+ cairo_scan_converter_t base;
+
+ glitter_scan_converter_t converter[1];
+ cairo_fill_rule_t fill_rule;
+
+ cairo_fill_rule_t clip_fill_rule;
+ cairo_antialias_t clip_antialias;
+
+ jmp_buf jmp;
+
+ struct {
+ struct pool base[1];
+ cairo_half_open_span_t embedded[32];
+ } span_pool;
+};
+
+typedef struct _cairo_clip_tor_scan_converter cairo_clip_tor_scan_converter_t;
+
+static void
+_cairo_clip_tor_scan_converter_destroy (void *converter)
+{
+ cairo_clip_tor_scan_converter_t *self = converter;
+ if (self == NULL) {
+ return;
+ }
+ _glitter_scan_converter_fini (self->converter);
+ pool_fini (self->span_pool.base);
+ free(self);
+}
+
+static cairo_status_t
+_cairo_clip_tor_scan_converter_generate (void *converter,
+ cairo_span_renderer_t *renderer)
+{
+ cairo_clip_tor_scan_converter_t *self = converter;
+ cairo_status_t status;
+
+ if ((status = setjmp (self->jmp)))
+ return _cairo_scan_converter_set_error (self, _cairo_error (status));
+
+ glitter_scan_converter_render (self->converter,
+ self->fill_rule == CAIRO_FILL_RULE_WINDING,
+ renderer,
+ self->span_pool.base);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_scan_converter_t *
+_cairo_clip_tor_scan_converter_create (cairo_clip_t *clip,
+ cairo_polygon_t *polygon,
+ cairo_fill_rule_t fill_rule)
+{
+ cairo_clip_tor_scan_converter_t *self;
+ cairo_polygon_t clipper;
+ cairo_status_t status;
+ int i;
+
+ self = calloc (1, sizeof(struct _cairo_clip_tor_scan_converter));
+ if (unlikely (self == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto bail_nomem;
+ }
+
+ self->base.destroy = _cairo_clip_tor_scan_converter_destroy;
+ self->base.generate = _cairo_clip_tor_scan_converter_generate;
+
+ pool_init (self->span_pool.base, &self->jmp,
+ 250 * sizeof(self->span_pool.embedded[0]),
+ sizeof(self->span_pool.embedded));
+
+ _glitter_scan_converter_init (self->converter, &self->jmp);
+ status = glitter_scan_converter_reset (self->converter,
+ clip->extents.y,
+ clip->extents.y + clip->extents.height);
+ if (unlikely (status))
+ goto bail;
+
+ self->fill_rule = fill_rule;
+
+ for (i = 0; i < polygon->num_edges; i++)
+ glitter_scan_converter_add_edge (self->converter,
+ &polygon->edges[i],
+ FALSE);
+
+ status = _cairo_clip_get_polygon (clip,
+ &clipper,
+ &self->clip_fill_rule,
+ &self->clip_antialias);
+ if (unlikely (status))
+ goto bail;
+
+ for (i = 0; i < clipper.num_edges; i++)
+ glitter_scan_converter_add_edge (self->converter,
+ &clipper.edges[i],
+ TRUE);
+ _cairo_polygon_fini (&clipper);
+
+ return &self->base;
+
+ bail:
+ self->base.destroy(&self->base);
+ bail_nomem:
+ return _cairo_scan_converter_create_in_error (status);
+}
+
diff --git a/src/cairo-image-surface.c b/src/cairo-image-surface.c
index c2afbab..508d258 100644
--- a/src/cairo-image-surface.c
+++ b/src/cairo-image-surface.c
@@ -1487,6 +1487,13 @@ _pixman_image_for_surface (const cairo_surface_pattern_t *pattern,
if (unlikely (status))
return NULL;
+ if (sample.x >= 0 && sample.y >= 0 &&
+ sample.x + sample.width <= image->width &&
+ sample.y + sample.height <= image->height)
+ {
+ extend = CAIRO_EXTEND_NONE;
+ }
+
if (sample.width == 1 && sample.height == 1) {
if (sample.x < 0 ||
sample.y < 0 ||
@@ -1910,32 +1917,35 @@ reduce_alpha_op (cairo_image_surface_t *dst,
/* low level compositor */
typedef cairo_status_t
(*image_draw_func_t) (void *closure,
- pixman_image_t *dst,
- pixman_format_code_t dst_format,
+ cairo_image_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *src,
+ double opacity,
int dst_x,
int dst_y,
- cairo_matrix_t *dst_device_transform,
const cairo_composite_rectangles_t *extents);
static pixman_image_t *
_create_composite_mask_pattern (cairo_composite_rectangles_t *extents,
image_draw_func_t draw_func,
void *draw_closure,
+ double opacity,
cairo_image_surface_t *dst)
{
cairo_region_t *clip_region = _cairo_clip_get_region (extents->clip);
cairo_bool_t need_clip_surface = ! _cairo_clip_is_region (extents->clip);
- pixman_image_t *mask;
+ cairo_image_surface_t *mask;
+ pixman_image_t *image;
cairo_status_t status;
if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1)
clip_region = NULL;
- mask = pixman_image_create_bits (PIXMAN_a8, extents->bounded.width, extents->bounded.height,
- NULL, 0);
- if (unlikely (mask == NULL))
+ mask = (cairo_image_surface_t *)
+ cairo_image_surface_create (CAIRO_FORMAT_A8,
+ extents->bounded.width,
+ extents->bounded.height);
+ if (unlikely (mask->base.status))
return NULL;
/* Is it worth setting the clip region here? */
@@ -1943,51 +1953,44 @@ _create_composite_mask_pattern (cairo_composite_rectangles_t *extents,
pixman_bool_t ret;
pixman_region32_translate (&clip_region->rgn, -extents->bounded.x, -extents->bounded.y);
- ret = pixman_image_set_clip_region32 (mask, &clip_region->rgn);
+ ret = pixman_image_set_clip_region32 (mask->pixman_image,
+ &clip_region->rgn);
pixman_region32_translate (&clip_region->rgn, extents->bounded.x, extents->bounded.y);
if (! ret) {
- pixman_image_unref (mask);
+ cairo_surface_destroy (&mask->base);
return NULL;
}
}
- status = draw_func (draw_closure,
- mask, PIXMAN_a8,
- CAIRO_OPERATOR_ADD, NULL,
+ mask->base.device_transform = dst->base.device_transform;
+ status = draw_func (draw_closure, mask,
+ CAIRO_OPERATOR_ADD, NULL, opacity,
extents->bounded.x, extents->bounded.y,
- &dst->base.device_transform,
extents);
if (unlikely (status)) {
- pixman_image_unref (mask);
+ cairo_surface_destroy (&mask->base);
return NULL;
}
if (need_clip_surface) {
- cairo_surface_t *tmp;
-
- tmp = _cairo_image_surface_create_for_pixman_image (mask, PIXMAN_a8);
- if (unlikely (tmp->status)) {
- pixman_image_unref (mask);
- return NULL;
- }
-
- pixman_image_ref (mask);
-
- status = _cairo_clip_combine_with_surface (extents->clip, tmp,
+ status = _cairo_clip_combine_with_surface (extents->clip, &mask->base,
extents->bounded.x,
extents->bounded.y);
- cairo_surface_destroy (tmp);
if (unlikely (status)) {
- pixman_image_unref (mask);
+ cairo_surface_destroy (&mask->base);
return NULL;
}
}
if (clip_region != NULL)
- pixman_image_set_clip_region (mask, NULL);
+ pixman_image_set_clip_region (mask->pixman_image, NULL);
+
+ image = mask->pixman_image;
+ pixman_image_ref (image);
+ cairo_surface_destroy (&mask->base);
- return mask;
+ return image;
}
/* Handles compositing with a clip surface when the operator allows
@@ -1997,13 +2000,17 @@ static cairo_status_t
_clip_and_composite_with_mask (cairo_composite_rectangles_t *extents,
cairo_operator_t op,
const cairo_pattern_t *pattern,
+ double opacity,
image_draw_func_t draw_func,
void *draw_closure,
cairo_image_surface_t *dst)
{
pixman_image_t *mask;
- mask = _create_composite_mask_pattern (extents, draw_func, draw_closure, dst);
+ mask = _create_composite_mask_pattern (extents,
+ draw_func, draw_closure,
+ opacity,
+ dst);
if (unlikely (mask == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
@@ -2060,37 +2067,39 @@ _clip_and_composite_with_mask (cairo_composite_rectangles_t *extents,
* in two pieces and combine them together.
*/
static cairo_status_t
-_clip_and_composite_combine (cairo_composite_rectangles_t *extents,
+_clip_and_composite_combine (cairo_composite_rectangles_t *extents,
cairo_operator_t op,
const cairo_pattern_t *src,
+ double opacity,
image_draw_func_t draw_func,
void *draw_closure,
- cairo_image_surface_t *dst)
+ cairo_image_surface_t *dst)
{
- pixman_image_t *tmp;
+ cairo_image_surface_t *tmp;
cairo_surface_t *clip_surface;
int clip_x, clip_y;
cairo_status_t status;
- tmp = pixman_image_create_bits (dst->pixman_format,
- extents->bounded.width,
- extents->bounded.height,
- NULL, 0);
- if (unlikely (tmp == NULL))
- return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ tmp = (cairo_image_surface_t *)
+ _cairo_image_surface_create_with_pixman_format (NULL,
+ dst->pixman_format,
+ extents->bounded.width,
+ extents->bounded.height,
+ 0);
+ if (unlikely (tmp->base.status))
+ return tmp->base.status;
+ tmp->base.device_transform = dst->base.device_transform;
if (src == NULL) {
- status = (*draw_func) (draw_closure,
- tmp, dst->pixman_format,
- CAIRO_OPERATOR_ADD, NULL,
+ status = (*draw_func) (draw_closure, tmp,
+ CAIRO_OPERATOR_ADD, NULL, opacity,
extents->bounded.x, extents->bounded.y,
- &dst->base.device_transform,
extents);
} else {
/* Initialize the temporary surface from the destination surface */
if (! dst->base.is_clear) {
pixman_image_composite32 (PIXMAN_OP_SRC,
- dst->pixman_image, NULL, tmp,
+ dst->pixman_image, NULL, tmp->pixman_image,
extents->bounded.x,
extents->bounded.y,
0, 0,
@@ -2099,11 +2108,8 @@ _clip_and_composite_combine (cairo_composite_rectangles_t *extents,
extents->bounded.height);
}
- status = (*draw_func) (draw_closure,
- tmp, dst->pixman_format,
- op, src,
+ status = (*draw_func) (draw_closure, tmp, op, src, opacity,
extents->bounded.x, extents->bounded.y,
- &dst->base.device_transform,
extents);
}
if (unlikely (status))
@@ -2117,7 +2123,7 @@ _clip_and_composite_combine (cairo_composite_rectangles_t *extents,
if (! dst->base.is_clear) {
#if PIXMAN_HAS_OP_LERP
pixman_image_composite32 (PIXMAN_OP_LERP,
- tmp,
+ tmp->pixman_image,
((cairo_image_surface_t *) clip_surface)->pixman_image,
dst->pixman_image,
0, 0,
@@ -2138,7 +2144,7 @@ _clip_and_composite_combine (cairo_composite_rectangles_t *extents,
/* Now add the two results together */
pixman_image_composite32 (PIXMAN_OP_ADD,
- tmp,
+ tmp->pixman_image,
((cairo_image_surface_t *) clip_surface)->pixman_image,
dst->pixman_image,
0, 0,
@@ -2149,7 +2155,7 @@ _clip_and_composite_combine (cairo_composite_rectangles_t *extents,
#endif
} else {
pixman_image_composite32 (PIXMAN_OP_SRC,
- tmp,
+ tmp->pixman_image,
((cairo_image_surface_t *) clip_surface)->pixman_image,
dst->pixman_image,
0, 0,
@@ -2161,7 +2167,7 @@ _clip_and_composite_combine (cairo_composite_rectangles_t *extents,
cairo_surface_destroy (clip_surface);
CLEANUP_SURFACE:
- pixman_image_unref (tmp);
+ cairo_surface_destroy (&tmp->base);
return status;
}
@@ -2172,6 +2178,7 @@ _clip_and_composite_combine (cairo_composite_rectangles_t *extents,
static cairo_status_t
_clip_and_composite_source (cairo_composite_rectangles_t *extents,
const cairo_pattern_t *pattern,
+ double opacity,
image_draw_func_t draw_func,
void *draw_closure,
cairo_image_surface_t *dst)
@@ -2182,11 +2189,9 @@ _clip_and_composite_source (cairo_composite_rectangles_t *extents,
if (pattern == NULL) {
cairo_status_t status;
- status = draw_func (draw_closure,
- dst->pixman_image, dst->pixman_format,
- CAIRO_OPERATOR_SOURCE, NULL,
+ status = draw_func (draw_closure, dst,
+ CAIRO_OPERATOR_SOURCE, NULL, opacity,
extents->bounded.x, extents->bounded.y,
- &dst->base.device_transform,
extents);
if (unlikely (status))
return status;
@@ -2199,7 +2204,9 @@ _clip_and_composite_source (cairo_composite_rectangles_t *extents,
}
/* Create a surface that is mask IN clip */
- mask = _create_composite_mask_pattern (extents, draw_func, draw_closure, dst);
+ mask = _create_composite_mask_pattern (extents,
+ draw_func, draw_closure, opacity,
+ dst);
if (unlikely (mask == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
@@ -2284,6 +2291,7 @@ static cairo_status_t
_clip_and_composite (cairo_image_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *src,
+ double opacity,
image_draw_func_t draw_func,
void *draw_closure,
cairo_composite_rectangles_t*extents,
@@ -2311,7 +2319,7 @@ _clip_and_composite (cairo_image_surface_t *dst,
}
if (op == CAIRO_OPERATOR_SOURCE) {
- status = _clip_and_composite_source (extents, src,
+ status = _clip_and_composite_source (extents, src, opacity,
draw_func, draw_closure, dst);
} else {
if (op == CAIRO_OPERATOR_CLEAR) {
@@ -2321,21 +2329,17 @@ _clip_and_composite (cairo_image_surface_t *dst,
if (need_clip & NEED_CLIP_SURFACE) {
if (extents->is_bounded) {
- status = _clip_and_composite_with_mask (extents, op, src,
+ status = _clip_and_composite_with_mask (extents, op, src, opacity,
draw_func, draw_closure,
dst);
} else {
- status = _clip_and_composite_combine (extents, op, src,
+ status = _clip_and_composite_combine (extents, op, src, opacity,
draw_func, draw_closure,
dst);
}
} else {
- status = draw_func (draw_closure,
- dst->pixman_image, dst->pixman_format,
- op, src,
- 0, 0,
- &dst->base.device_transform,
- extents);
+ status = draw_func (draw_closure, dst, op, src, opacity,
+ 0, 0, extents);
}
}
@@ -2445,13 +2449,12 @@ _pixman_image_add_traps (pixman_image_t *image,
static cairo_status_t
_composite_traps (void *closure,
- pixman_image_t *dst,
- pixman_format_code_t dst_format,
+ cairo_image_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *pattern,
+ double opacity,
int dst_x,
int dst_y,
- cairo_matrix_t *dst_device_transform,
const cairo_composite_rectangles_t *extents)
{
composite_traps_info_t *info = closure;
@@ -2468,16 +2471,17 @@ _composite_traps (void *closure,
* the Cairo core code passes bounds based on the trapezoid extents.
*/
format = info->antialias == CAIRO_ANTIALIAS_NONE ? PIXMAN_a1 : PIXMAN_a8;
- if (dst_format == format &&
+ if (dst->pixman_format == format &&
+ opacity == 1. &&
(pattern == NULL ||
(op == CAIRO_OPERATOR_ADD && _cairo_pattern_is_opaque_solid (pattern))))
{
- _pixman_image_add_traps (dst, dst_x, dst_y, info);
+ _pixman_image_add_traps (dst->pixman_image, dst_x, dst_y, info);
return CAIRO_STATUS_SUCCESS;
}
src = _pixman_image_for_pattern (pattern, FALSE, &extents->bounded,
- dst_device_transform,
+ &dst->base.device_transform,
&src_x, &src_y);
if (unlikely (src == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
@@ -2492,17 +2496,41 @@ _composite_traps (void *closure,
}
_pixman_image_add_traps (mask, extents->bounded.x, extents->bounded.y, info);
+ if (opacity != .1) {
+ pixman_color_t color;
+ pixman_image_t *alpha;
+
+ color.red = 0;
+ color.green = 0;
+ color.blue = 0;
+ color.alpha = 0xffff * opacity;
+
+ alpha = pixman_image_create_solid_fill (&color);
+ if (alpha == NULL) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto CLEANUP_MASK;
+ }
+ pixman_image_composite32 (PIXMAN_OP_IN,
+ alpha, NULL, mask,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ extents->bounded.width, extents->bounded.height);
+ pixman_image_unref (alpha);
+ }
+
pixman_image_composite32 (_pixman_operator (op),
- src, mask, dst,
+ src, mask, dst->pixman_image,
extents->bounded.x + src_x, extents->bounded.y + src_y,
0, 0,
extents->bounded.x - dst_x, extents->bounded.y - dst_y,
extents->bounded.width, extents->bounded.height);
- pixman_image_unref (mask);
status = CAIRO_STATUS_SUCCESS;
- CLEANUP_SOURCE:
+CLEANUP_MASK:
+ pixman_image_unref (mask);
+CLEANUP_SOURCE:
pixman_image_unref (src);
return status;
@@ -2520,9 +2548,11 @@ color_to_uint32 (const cairo_color_t *color)
static inline cairo_bool_t
color_to_pixel (const cairo_color_t *color,
+ double opacity,
pixman_format_code_t format,
uint32_t *pixel)
{
+ cairo_color_t opacity_color;
uint32_t c;
if (!(format == PIXMAN_a8r8g8b8 ||
@@ -2538,6 +2568,14 @@ color_to_pixel (const cairo_color_t *color,
return FALSE;
}
+ if (opacity != 1.0) {
+ _cairo_color_init_rgba (&opacity_color,
+ color->red,
+ color->green,
+ color->blue,
+ color->alpha * opacity);
+ color = &opacity_color;
+ }
c = color_to_uint32 (color);
if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_ABGR) {
@@ -2568,6 +2606,7 @@ color_to_pixel (const cairo_color_t *color,
static inline cairo_bool_t
pattern_to_pixel (const cairo_solid_pattern_t *solid,
+ double opacity,
cairo_operator_t op,
pixman_format_code_t format,
uint32_t *pixel)
@@ -2581,301 +2620,244 @@ pattern_to_pixel (const cairo_solid_pattern_t *solid,
return FALSE;
if (op == CAIRO_OPERATOR_OVER) {
- if (solid->color.alpha_short >= 0xff00)
+ if (opacity * solid->color.alpha_short >= 0xff00)
op = CAIRO_OPERATOR_SOURCE;
}
if (op != CAIRO_OPERATOR_SOURCE)
return FALSE;
- return color_to_pixel (&solid->color, format, pixel);
+ return color_to_pixel (&solid->color, opacity, format, pixel);
}
-typedef struct _fill_span {
+typedef struct _cairo_image_span_renderer {
cairo_span_renderer_t base;
-
- uint8_t *mask_data;
- pixman_image_t *src, *dst, *mask;
-} fill_span_renderer_t;
+ pixman_image_compositor_t *compositor;
+ pixman_image_t *src;
+ float opacity;
+ cairo_rectangle_int_t extents;
+} cairo_image_span_renderer_t;
static cairo_status_t
-_fill_span (void *abstract_renderer,
- int y, int height,
- const cairo_half_open_span_t *spans,
- unsigned num_spans)
+_cairo_image_bounded_opaque_spans (void *abstract_renderer,
+ int y, int height,
+ const cairo_half_open_span_t *spans,
+ unsigned num_spans)
{
- fill_span_renderer_t *renderer = abstract_renderer;
- uint8_t *row;
- unsigned i;
+ cairo_image_span_renderer_t *r = abstract_renderer;
if (num_spans == 0)
return CAIRO_STATUS_SUCCESS;
- row = renderer->mask_data - spans[0].x;
- for (i = 0; i < num_spans - 1; i++) {
- /* We implement setting the most common single pixel wide
- * span case to avoid the overhead of a memset call.
- * Open coding setting longer spans didn't show a
- * noticeable improvement over memset.
- */
- if (spans[i+1].x == spans[i].x + 1) {
- row[spans[i].x] = spans[i].coverage;
- } else {
- memset (row + spans[i].x,
- spans[i].coverage,
- spans[i+1].x - spans[i].x);
- }
- }
-
do {
- pixman_image_composite32 (PIXMAN_OP_OVER,
- renderer->src, renderer->mask, renderer->dst,
- 0, 0, 0, 0,
- spans[0].x, y++,
- spans[i].x - spans[0].x, 1);
- } while (--height);
+ if (spans[0].coverage)
+ pixman_image_compositor_blt (r->compositor,
+ spans[0].x, y,
+ spans[1].x - spans[0].x, height,
+ spans[0].coverage);
+ spans++;
+ } while (--num_spans > 1);
return CAIRO_STATUS_SUCCESS;
}
-/* avoid using region code to re-validate boxes */
static cairo_status_t
-_fill_unaligned_boxes (cairo_image_surface_t *dst,
- const cairo_pattern_t *pattern,
- uint32_t pixel,
- const cairo_boxes_t *boxes,
- const cairo_composite_rectangles_t *extents)
+_cairo_image_bounded_spans (void *abstract_renderer,
+ int y, int height,
+ const cairo_half_open_span_t *spans,
+ unsigned num_spans)
{
- uint8_t buf[CAIRO_STACK_BUFFER_SIZE];
- fill_span_renderer_t renderer;
- cairo_rectangular_scan_converter_t converter;
- const struct _cairo_boxes_chunk *chunk;
- cairo_status_t status;
- int i;
+ cairo_image_span_renderer_t *r = abstract_renderer;
- /* XXX
- * using composite for fill:
- * spiral-box-nonalign-evenodd-fill.512 2201957 2.202
- * spiral-box-nonalign-nonzero-fill.512 336726 0.337
- * spiral-box-pixalign-evenodd-fill.512 352256 0.352
- * spiral-box-pixalign-nonzero-fill.512 147056 0.147
- * using fill:
- * spiral-box-nonalign-evenodd-fill.512 3174565 3.175
- * spiral-box-nonalign-nonzero-fill.512 182710 0.183
- * spiral-box-pixalign-evenodd-fill.512 353863 0.354
- * spiral-box-pixalign-nonzero-fill.512 147402 0.147
- *
- * cairo-perf-trace seems to favour using fill.
- */
+ if (num_spans == 0)
+ return CAIRO_STATUS_SUCCESS;
- renderer.base.render_rows = _fill_span;
- renderer.dst = dst->pixman_image;
+ do {
+ if (spans[0].coverage) {
+ pixman_image_compositor_blt (r->compositor,
+ spans[0].x, y,
+ spans[1].x - spans[0].x, height,
+ r->opacity * spans[0].coverage);
+ }
+ spans++;
+ } while (--num_spans > 1);
- if ((unsigned) extents->bounded.width <= sizeof (buf)) {
- renderer.mask = pixman_image_create_bits (PIXMAN_a8,
- extents->bounded.width, 1,
- (uint32_t *) buf,
- sizeof (buf));
- } else {
- renderer.mask = pixman_image_create_bits (PIXMAN_a8,
- extents->bounded.width, 1,
- NULL, 0);
- }
- if (unlikely (renderer.mask == NULL))
- return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ return CAIRO_STATUS_SUCCESS;
+}
- renderer.mask_data = (uint8_t *) pixman_image_get_data (renderer.mask);
+static cairo_status_t
+_cairo_image_unbounded_spans (void *abstract_renderer,
+ int y, int height,
+ const cairo_half_open_span_t *spans,
+ unsigned num_spans)
+{
+ cairo_image_span_renderer_t *r = abstract_renderer;
- renderer.src = _pixman_image_for_solid ((const cairo_solid_pattern_t *) pattern);
- if (unlikely (renderer.src == NULL)) {
- status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
- goto CLEANUP_MASK;
+ if (y > r->extents.y) {
+ pixman_image_compositor_blt (r->compositor,
+ r->extents.x, r->extents.y,
+ r->extents.width, y - r->extents.y,
+ 0);
}
- _cairo_rectangular_scan_converter_init (&converter, &extents->bounded);
-
- /* first blit any aligned part of the boxes */
- for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
- const cairo_box_t *box = chunk->base;
+ if (num_spans == 0) {
+ pixman_image_compositor_blt (r->compositor,
+ r->extents.x, y,
+ r->extents.width, height,
+ 0);
+ } else {
+ if (spans[0].x != r->extents.x) {
+ pixman_image_compositor_blt (r->compositor,
+ r->extents.x, y,
+ spans[0].x - r->extents.x,
+ height,
+ 0);
+ }
- for (i = 0; i < chunk->count; i++) {
- int x1 = _cairo_fixed_integer_ceil (box[i].p1.x);
- int y1 = _cairo_fixed_integer_ceil (box[i].p1.y);
- int x2 = _cairo_fixed_integer_floor (box[i].p2.x);
- int y2 = _cairo_fixed_integer_floor (box[i].p2.y);
+ do {
+ pixman_image_compositor_blt (r->compositor,
+ spans[0].x, y,
+ spans[1].x - spans[0].x, height,
+ r->opacity * spans[0].coverage);
+ spans++;
+ } while (--num_spans > 1);
+
+ if (spans[0].x != r->extents.x + r->extents.width) {
+ pixman_image_compositor_blt (r->compositor,
+ spans[0].x, y,
+ r->extents.x + r->extents.width - spans[0].x, height,
+ 0);
+ }
+ }
- if (x2 > x1 && y2 > y1) {
- cairo_box_t b;
+ r->extents.y = y + height;
+ return CAIRO_STATUS_SUCCESS;
+}
- pixman_fill ((uint32_t *) dst->data,
- dst->stride / sizeof (uint32_t),
- PIXMAN_FORMAT_BPP (dst->pixman_format),
- x1, y1, x2 - x1, y2 - y1,
- pixel);
+static cairo_status_t
+_cairo_image_clipped_spans (void *abstract_renderer,
+ int y, int height,
+ const cairo_half_open_span_t *spans,
+ unsigned num_spans)
+{
+ cairo_image_span_renderer_t *r = abstract_renderer;
- /*
- * Corners have to be included only once if the rects
- * are passed to the rectangular scan converter
- * because it can only handle disjoint rectangles.
- */
-
- /* top (including top-left and top-right corners) */
- if (! _cairo_fixed_is_integer (box[i].p1.y)) {
- b.p1.x = box[i].p1.x;
- b.p1.y = box[i].p1.y;
- b.p2.x = box[i].p2.x;
- b.p2.y = _cairo_fixed_from_int (y1);
-
- status = _cairo_rectangular_scan_converter_add_box (&converter, &b, 1);
- if (unlikely (status))
- goto CLEANUP_CONVERTER;
- }
+ assert (num_spans);
- /* left (no corners) */
- if (! _cairo_fixed_is_integer (box[i].p1.x)) {
- b.p1.x = box[i].p1.x;
- b.p1.y = _cairo_fixed_from_int (y1);
- b.p2.x = _cairo_fixed_from_int (x1);
- b.p2.y = _cairo_fixed_from_int (y2);
+ do {
+ if (! spans[0].is_clipped)
+ pixman_image_compositor_blt (r->compositor,
+ spans[0].x, y,
+ spans[1].x - spans[0].x, height,
+ r->opacity * spans[0].coverage);
+ spans++;
+ } while (--num_spans > 1);
+
+ r->extents.y = y + height;
+ return CAIRO_STATUS_SUCCESS;
+}
- status = _cairo_rectangular_scan_converter_add_box (&converter, &b, 1);
- if (unlikely (status))
- goto CLEANUP_CONVERTER;
- }
+static cairo_status_t
+_cairo_image_finish_unbounded_spans (void *abstract_renderer)
+{
+ cairo_image_span_renderer_t *r = abstract_renderer;
- /* right (no corners) */
- if (! _cairo_fixed_is_integer (box[i].p2.x)) {
- b.p1.x = _cairo_fixed_from_int (x2);
- b.p1.y = _cairo_fixed_from_int (y1);
- b.p2.x = box[i].p2.x;
- b.p2.y = _cairo_fixed_from_int (y2);
+ if (r->extents.y < r->extents.height) {
+ pixman_image_compositor_blt (r->compositor,
+ r->extents.x, r->extents.y,
+ r->extents.width,
+ r->extents.height - r->extents.y,
+ 0);
+ }
- status = _cairo_rectangular_scan_converter_add_box (&converter, &b, 1);
- if (unlikely (status))
- goto CLEANUP_CONVERTER;
- }
+ return CAIRO_STATUS_SUCCESS;
+}
- /* bottom (including bottom-left and bottom-right corners) */
- if (! _cairo_fixed_is_integer (box[i].p2.y)) {
- b.p1.x = box[i].p1.x;
- b.p1.y = _cairo_fixed_from_int (y2);
- b.p2.x = box[i].p2.x;
- b.p2.y = box[i].p2.y;
+static cairo_status_t
+_cairo_image_span_renderer_init (cairo_image_span_renderer_t *r,
+ cairo_image_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ float opacity,
+ const cairo_composite_rectangles_t *composite,
+ cairo_bool_t needs_clip)
+{
+ int src_x, src_y;
- status = _cairo_rectangular_scan_converter_add_box (&converter, &b, 1);
- if (unlikely (status))
- goto CLEANUP_CONVERTER;
- }
- } else {
- status = _cairo_rectangular_scan_converter_add_box (&converter, &box[i], 1);
- if (unlikely (status))
- goto CLEANUP_CONVERTER;
- }
- }
+ if (op == CAIRO_OPERATOR_CLEAR) {
+ op = CAIRO_OPERATOR_DEST_OUT;
+ pattern = NULL;
}
- status = converter.base.generate (&converter.base, &renderer.base);
+ r->src = _pixman_image_for_pattern (pattern, FALSE,
+ &composite->bounded,
+ &dst->base.device_transform,
+ &src_x, &src_y);
+ if (unlikely (r->src == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
- CLEANUP_CONVERTER:
- converter.base.destroy (&converter.base);
- pixman_image_unref (renderer.src);
- CLEANUP_MASK:
- pixman_image_unref (renderer.mask);
+ if (composite->is_bounded) {
+ if (opacity == 1.)
+ r->base.render_rows = _cairo_image_bounded_opaque_spans;
+ else
+ r->base.render_rows = _cairo_image_bounded_spans;
+ r->base.finish = NULL;
+ } else {
+ if (needs_clip)
+ r->base.render_rows = _cairo_image_clipped_spans;
+ else
+ r->base.render_rows = _cairo_image_unbounded_spans;
+ r->base.finish = _cairo_image_finish_unbounded_spans;
+ r->extents = composite->unbounded;
+ r->extents.height += r->extents.y;
+
+ }
+ r->compositor =
+ pixman_image_create_compositor (_pixman_operator (op),
+ r->src, NULL, dst->pixman_image,
+ composite->bounded.x + src_x,
+ composite->bounded.y + src_y,
+ 0, 0,
+ composite->bounded.x,
+ composite->bounded.y,
+ composite->bounded.width,
+ composite->bounded.height);
+ r->opacity = opacity;
- return status;
+ return CAIRO_STATUS_SUCCESS;
}
-typedef struct _cairo_image_surface_span_renderer {
- cairo_span_renderer_t base;
-
- uint8_t *mask_data;
- uint32_t mask_stride;
-} cairo_image_surface_span_renderer_t;
-
-static cairo_status_t
-_cairo_image_surface_span (void *abstract_renderer,
- int y, int height,
- const cairo_half_open_span_t *spans,
- unsigned num_spans)
+static void
+_cairo_image_span_renderer_fini (cairo_image_span_renderer_t *r)
{
- cairo_image_surface_span_renderer_t *renderer = abstract_renderer;
- uint8_t *row;
- unsigned i;
-
- if (num_spans == 0)
- return CAIRO_STATUS_SUCCESS;
-
- /* XXX will it be quicker to repeat the sparse memset,
- * or perform a simpler memcpy?
- * The fairly dense spiral benchmarks suggests that the sparse
- * memset is a win there as well.
- */
- row = renderer->mask_data + y * renderer->mask_stride;
- do {
- for (i = 0; i < num_spans - 1; i++) {
- if (! spans[i].coverage)
- continue;
-
- /* We implement setting rendering the most common single
- * pixel wide span case to avoid the overhead of a memset
- * call. Open coding setting longer spans didn't show a
- * noticeable improvement over memset. */
- if (spans[i+1].x == spans[i].x + 1) {
- row[spans[i].x] = spans[i].coverage;
- } else {
- memset (row + spans[i].x,
- spans[i].coverage,
- spans[i+1].x - spans[i].x);
- }
- }
- row += renderer->mask_stride;
- } while (--height);
+ if (r->base.finish)
+ r->base.finish (r);
- return CAIRO_STATUS_SUCCESS;
+ pixman_image_compositor_destroy (r->compositor);
+ pixman_image_unref (r->src);
}
static cairo_status_t
_composite_unaligned_boxes (cairo_image_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *pattern,
+ double opacity,
const cairo_boxes_t *boxes,
const cairo_composite_rectangles_t *extents)
{
- uint8_t buf[CAIRO_STACK_BUFFER_SIZE];
- cairo_image_surface_span_renderer_t renderer;
+ cairo_image_span_renderer_t renderer;
cairo_rectangular_scan_converter_t converter;
- pixman_image_t *mask, *src;
- cairo_status_t status;
const struct _cairo_boxes_chunk *chunk;
- int i, src_x, src_y;
-
- /* The below code breaks for operator source. This can best be seen with
- * multiple boxes where black is drawn to dst outside of the boxes. */
- if (op == CAIRO_OPERATOR_SOURCE)
- return CAIRO_INT_STATUS_UNSUPPORTED;
-
- i = CAIRO_STRIDE_FOR_WIDTH_BPP (extents->bounded.width, 8) * extents->bounded.height;
- if ((unsigned) i <= sizeof (buf)) {
- mask = pixman_image_create_bits (PIXMAN_a8,
- extents->bounded.width,
- extents->bounded.height,
- (uint32_t *) buf,
- CAIRO_STRIDE_FOR_WIDTH_BPP (extents->bounded.width, 8));
- memset (buf, 0, i);
- } else {
- mask = pixman_image_create_bits (PIXMAN_a8,
- extents->bounded.width,
- extents->bounded.height,
- NULL, 0);
- }
- if (unlikely (mask == NULL))
- return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ cairo_status_t status;
+ int i;
- renderer.base.render_rows = _cairo_image_surface_span;
- renderer.mask_stride = pixman_image_get_stride (mask);
- renderer.mask_data = (uint8_t *) pixman_image_get_data (mask);
- renderer.mask_data -= extents->bounded.y * renderer.mask_stride + extents->bounded.x;
+ status = _cairo_image_span_renderer_init (&renderer,
+ dst, op, pattern, opacity,
+ extents, FALSE);
+ if (unlikely (status))
+ return status;
+ assert (! extents->clip->path);
_cairo_rectangular_scan_converter_init (&converter, &extents->bounded);
for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
@@ -2889,29 +2871,10 @@ _composite_unaligned_boxes (cairo_image_surface_t *dst,
}
status = converter.base.generate (&converter.base, &renderer.base);
- if (unlikely (status))
- goto CLEANUP;
-
- src = _pixman_image_for_pattern (pattern, FALSE, &extents->bounded,
- &dst->base.device_transform,
- &src_x, &src_y);
- if (unlikely (src == NULL)) {
- status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
- goto CLEANUP;
- }
-
- pixman_image_composite32 (_pixman_operator (op),
- src, mask, dst->pixman_image,
- extents->bounded.x + src_x, extents->bounded.y + src_y,
- 0, 0,
- extents->bounded.x, extents->bounded.y,
- extents->bounded.width, extents->bounded.height);
- pixman_image_unref (src);
- CLEANUP:
+CLEANUP:
+ _cairo_image_span_renderer_fini (&renderer);
converter.base.destroy (&converter.base);
- pixman_image_unref (mask);
-
return status;
}
@@ -2986,6 +2949,7 @@ static cairo_status_t
_composite_boxes (cairo_image_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *pattern,
+ double opacity,
cairo_boxes_t *boxes,
const cairo_composite_rectangles_t *extents)
{
@@ -2996,6 +2960,9 @@ _composite_boxes (cairo_image_surface_t *dst,
uint32_t pixel;
int i;
+ if (need_clip_mask && opacity != 1.)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
if (need_clip_mask &&
(op == CAIRO_OPERATOR_SOURCE || ! extents->is_bounded))
{
@@ -3009,23 +2976,16 @@ _composite_boxes (cairo_image_surface_t *dst,
if (need_clip_mask)
return CAIRO_INT_STATUS_UNSUPPORTED;
- if (pattern_to_pixel ((cairo_solid_pattern_t *) pattern, op,
- dst->pixman_format, &pixel))
- {
- return _fill_unaligned_boxes (dst, pattern, pixel, boxes, extents);
- }
- else
- {
- return _composite_unaligned_boxes (dst, op, pattern, boxes, extents);
- }
+ return _composite_unaligned_boxes (dst, op, pattern, opacity, boxes, extents);
}
/* Are we just copying a recording surface? */
- if (! need_clip_mask &&
+ if (! need_clip_mask && opacity == 1. &&
op_reduces_to_source (op, dst) &&
recording_pattern_contains_sample (pattern, &extents->bounded))
{
cairo_clip_t *recording_clip;
+ cairo_bool_t was_clear = dst->base.is_clear;
/* XXX could also do tiling repeat modes... */
@@ -3050,6 +3010,8 @@ _composite_boxes (cairo_image_surface_t *dst,
0);
}
}
+
+ dst->base.is_clear = TRUE;
}
recording_clip = _cairo_clip_from_boxes (boxes);
@@ -3058,13 +3020,15 @@ _composite_boxes (cairo_image_surface_t *dst,
&dst->base,
recording_clip);
_cairo_clip_destroy (recording_clip);
+ dst->base.is_clear &= was_clear;
return status;
}
status = CAIRO_STATUS_SUCCESS;
if (! need_clip_mask &&
- pattern_to_pixel ((cairo_solid_pattern_t *) pattern, op, dst->pixman_format,
+ pattern_to_pixel ((cairo_solid_pattern_t *) pattern, opacity, op,
+ dst->pixman_format,
&pixel))
{
for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
@@ -3167,21 +3131,75 @@ static cairo_status_t
_clip_and_composite_polygon (cairo_image_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *src,
+ double opacity,
cairo_polygon_t *polygon,
cairo_fill_rule_t fill_rule,
cairo_antialias_t antialias,
cairo_composite_rectangles_t *extents);
+static cairo_status_t
+_composite_boxes_fallback (void *closure,
+ cairo_image_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ double opacity,
+ int dst_x,
+ int dst_y,
+ const cairo_composite_rectangles_t *extents)
+{
+ cairo_boxes_t *boxes = closure;
+ cairo_image_span_renderer_t renderer;
+ cairo_rectangular_scan_converter_t converter;
+ const struct _cairo_boxes_chunk *chunk;
+ cairo_status_t status;
+ cairo_fixed_t fx = _cairo_fixed_from_int (-dst_x);
+ cairo_fixed_t fy = _cairo_fixed_from_int (-dst_y);
+ int i;
+
+ assert (! boxes->is_pixel_aligned);
+
+ status = _cairo_image_span_renderer_init (&renderer,
+ dst, op, pattern, opacity,
+ extents, FALSE);
+ if (unlikely (status))
+ return status;
+
+ _cairo_rectangular_scan_converter_init (&converter, &extents->bounded);
+ for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+ const cairo_box_t *box = chunk->base;
+
+ for (i = 0; i < chunk->count; i++) {
+ cairo_box_t b;
+
+ b.p1.x = box->p1.x + fx;
+ b.p1.y = box->p1.y + fy;
+ b.p2.x = box->p2.x + fx;
+ b.p2.y = box->p2.y + fy;
+
+ status = _cairo_rectangular_scan_converter_add_box (&converter,
+ &b, 1);
+ if (unlikely (status))
+ goto CLEANUP;
+ }
+ }
+
+ status = converter.base.generate (&converter.base, &renderer.base);
+
+CLEANUP:
+ _cairo_image_span_renderer_fini (&renderer);
+ converter.base.destroy (&converter.base);
+
+ return status;
+}
static cairo_status_t
_clip_and_composite_boxes (cairo_image_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *src,
+ double opacity,
cairo_boxes_t *boxes,
cairo_composite_rectangles_t *extents)
{
- cairo_traps_t traps;
cairo_int_status_t status;
- composite_traps_info_t info;
if (boxes->num_boxes == 0 && extents->is_bounded)
return CAIRO_STATUS_SUCCESS;
@@ -3202,7 +3220,7 @@ _clip_and_composite_boxes (cairo_image_surface_t *dst,
if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
cairo_clip_t *saved_clip = extents->clip;
extents->clip = clip;
- status = _clip_and_composite_polygon (dst, op, src,
+ status = _clip_and_composite_polygon (dst, op, src, opacity,
&polygon,
fill_rule,
antialias,
@@ -3220,24 +3238,14 @@ _clip_and_composite_boxes (cairo_image_surface_t *dst,
}
/* Use a fast path if the boxes are pixel aligned */
- status = _composite_boxes (dst, op, src, boxes, extents);
+ status = _composite_boxes (dst, op, src, opacity, boxes, extents);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
/* Otherwise render via a mask and composite in the usual fashion. */
- status = _cairo_traps_init_boxes (&traps, boxes);
- if (unlikely (status))
- return status;
-
- info.num_traps = traps.num_traps;
- info.traps = traps.traps;
- info.antialias = CAIRO_ANTIALIAS_DEFAULT;
- status = _clip_and_composite (dst, op, src,
- _composite_traps, &info,
- extents, need_unbounded_clip (extents));
-
- _cairo_traps_fini (&traps);
- return status;
+ return _clip_and_composite (dst, op, src, opacity,
+ _composite_boxes_fallback, boxes,
+ extents, need_unbounded_clip (extents));
}
static cairo_bool_t
@@ -3335,6 +3343,7 @@ static cairo_status_t
_clip_and_composite_trapezoids (cairo_image_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *src,
+ double opacity,
cairo_traps_t *traps,
cairo_antialias_t antialias,
cairo_composite_rectangles_t *extents)
@@ -3370,7 +3379,7 @@ _clip_and_composite_trapezoids (cairo_image_surface_t *dst,
cairo_boxes_t boxes;
_boxes_for_traps (&boxes, traps, antialias);
- return _clip_and_composite_boxes (dst, op, src,
+ return _clip_and_composite_boxes (dst, op, src, opacity,
&boxes, extents);
}
@@ -3381,7 +3390,7 @@ _clip_and_composite_trapezoids (cairo_image_surface_t *dst,
info.traps = traps->traps;
info.num_traps = traps->num_traps;
info.antialias = antialias;
- return _clip_and_composite (dst, op, src,
+ return _clip_and_composite (dst, op, src, opacity,
_composite_traps, &info,
extents, need_unbounded_clip (extents));
}
@@ -3428,7 +3437,7 @@ _cairo_image_surface_paint (void *abstract_surface,
status = _cairo_clip_to_boxes (extents.clip, &boxes);
if (likely (status == CAIRO_STATUS_SUCCESS)) {
- status = _clip_and_composite_boxes (surface, op, source,
+ status = _clip_and_composite_boxes (surface, op, source, 1.,
&boxes, &extents);
_cairo_boxes_fini (&boxes);
}
@@ -3440,13 +3449,12 @@ _cairo_image_surface_paint (void *abstract_surface,
static cairo_status_t
_composite_mask (void *closure,
- pixman_image_t *dst,
- pixman_format_code_t dst_format,
+ cairo_image_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *src_pattern,
+ double opacity,
int dst_x,
int dst_y,
- cairo_matrix_t *dst_device_transform,
const cairo_composite_rectangles_t *extents)
{
const cairo_pattern_t *mask_pattern = closure;
@@ -3454,15 +3462,17 @@ _composite_mask (void *closure,
int src_x = 0, src_y = 0;
int mask_x = 0, mask_y = 0;
+ assert (opacity == 1.);
+
if (src_pattern != NULL) {
src = _pixman_image_for_pattern (src_pattern, FALSE, &extents->bounded,
- dst_device_transform,
+ &dst->base.device_transform,
&src_x, &src_y);
if (unlikely (src == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
mask = _pixman_image_for_pattern (mask_pattern, TRUE, &extents->bounded,
- dst_device_transform,
+ &dst->base.device_transform,
&mask_x, &mask_y);
if (unlikely (mask == NULL)) {
pixman_image_unref (src);
@@ -3473,13 +3483,13 @@ _composite_mask (void *closure,
pixman_image_set_component_alpha (mask, TRUE);
} else {
src = _pixman_image_for_pattern (mask_pattern, FALSE, &extents->bounded,
- dst_device_transform,
+ &dst->base.device_transform,
&src_x, &src_y);
if (unlikely (src == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
- pixman_image_composite32 (_pixman_operator (op), src, mask, dst,
+ pixman_image_composite32 (_pixman_operator (op), src, mask, dst->pixman_image,
extents->bounded.x + src_x, extents->bounded.y + src_y,
extents->bounded.x + mask_x, extents->bounded.y + mask_y,
extents->bounded.x - dst_x, extents->bounded.y - dst_y,
@@ -3492,151 +3502,6 @@ _composite_mask (void *closure,
return CAIRO_STATUS_SUCCESS;
}
-static void do_unaligned_row(void (*blt)(void *closure,
- int16_t x, int16_t y,
- int16_t w, int16_t h,
- uint16_t coverage),
- void *closure,
- const cairo_box_t *b,
- int tx, int y, int h,
- uint16_t coverage)
-{
- int x1 = _cairo_fixed_integer_part (b->p1.x) - tx;
- int x2 = _cairo_fixed_integer_part (b->p2.x) - tx;
- if (x2 > x1) {
- if (! _cairo_fixed_is_integer (b->p1.x)) {
- blt(closure, x1, y, 1, h,
- coverage * (256 - _cairo_fixed_fractional_part (b->p1.x)));
- x1++;
- }
-
- if (x2 > x1)
- blt(closure, x1, y, x2-x1, h, (coverage << 8) - (coverage >> 8));
-
- if (! _cairo_fixed_is_integer (b->p2.x))
- blt(closure, x2, y, 1, h,
- coverage * _cairo_fixed_fractional_part (b->p2.x));
- } else
- blt(closure, x1, y, 1, h,
- coverage * (b->p2.x - b->p1.x));
-}
-
-static void do_unaligned_box(void (*blt)(void *closure,
- int16_t x, int16_t y,
- int16_t w, int16_t h,
- uint16_t coverage),
- void *closure,
- const cairo_box_t *b, int tx, int ty)
-{
- int y1 = _cairo_fixed_integer_part (b->p1.y) - ty;
- int y2 = _cairo_fixed_integer_part (b->p2.y) - ty;
- if (y2 > y1) {
- if (! _cairo_fixed_is_integer (b->p1.y)) {
- do_unaligned_row(blt, closure, b, tx, y1, 1,
- 256 - _cairo_fixed_fractional_part (b->p1.y));
- y1++;
- }
-
- if (y2 > y1)
- do_unaligned_row(blt, closure, b, tx, y1, y2-y1, 256);
-
- if (! _cairo_fixed_is_integer (b->p2.y))
- do_unaligned_row(blt, closure, b, tx, y2, 1,
- _cairo_fixed_fractional_part (b->p2.y));
- } else
- do_unaligned_row(blt, closure, b, tx, y1, 1,
- b->p2.y - b->p1.y);
-}
-
-struct composite_opacity_info {
- uint8_t op;
- pixman_image_t *dst;
- pixman_image_t *src;
- int src_x, src_y;
- double opacity;
-};
-
-static void composite_opacity(void *closure,
- int16_t x, int16_t y,
- int16_t w, int16_t h,
- uint16_t coverage)
-{
- struct composite_opacity_info *info = closure;
- pixman_color_t color;
- pixman_image_t *mask;
-
- color.red = 0;
- color.green = 0;
- color.blue = 0;
- color.alpha = coverage * info->opacity;
-
- mask = pixman_image_create_solid_fill (&color);
- if (mask == NULL)
- return;
-
- if (info->src) {
- pixman_image_composite32 (info->op,
- info->src,
- mask,
- info->dst,
- x + info->src_x, y + info->src_y,
- 0, 0,
- x, y,
- w, h);
- } else {
- pixman_image_composite32 (info->op,
- mask,
- NULL,
- info->dst,
- 0, 0,
- 0, 0,
- x, y,
- w, h);
- }
-
- pixman_image_unref (mask);
-}
-
-static cairo_status_t
-_composite_opacity_boxes (void *closure,
- pixman_image_t *dst,
- pixman_format_code_t dst_format,
- cairo_operator_t op,
- const cairo_pattern_t *src_pattern,
- int dst_x,
- int dst_y,
- cairo_matrix_t *dst_device_transform,
- const cairo_composite_rectangles_t *extents)
-{
- const cairo_solid_pattern_t *mask_pattern = closure;
- struct composite_opacity_info info;
- int i;
-
- info.op = _pixman_operator (op);
- info.dst = dst;
-
- if (src_pattern != NULL) {
- info.src = _pixman_image_for_pattern (src_pattern, TRUE, &extents->bounded,
- dst_device_transform,
- &info.src_x, &info.src_y);
- if (unlikely (info.src == NULL))
- return _cairo_error (CAIRO_STATUS_NO_MEMORY);
- } else
- info.src = NULL;
-
- info.opacity = mask_pattern->color.alpha;
-
- /* XXX for lots of boxes create a clip region for the fully opaque areas */
- for (i = 0; i < extents->clip->num_boxes; i++)
- do_unaligned_box(composite_opacity, &info,
- &extents->clip->boxes[i], dst_x, dst_y);
- if (info.src)
- pixman_image_unref (info.src);
-
- return CAIRO_STATUS_SUCCESS;
-}
-
-
static cairo_int_status_t
_cairo_image_surface_mask (void *abstract_surface,
cairo_operator_t op,
@@ -3647,7 +3512,7 @@ _cairo_image_surface_mask (void *abstract_surface,
cairo_image_surface_t *surface = abstract_surface;
cairo_composite_rectangles_t extents;
cairo_rectangle_int_t unbounded;
- cairo_status_t status;
+ cairo_int_status_t status;
_cairo_image_surface_get_extents (surface, &unbounded);
status = _cairo_composite_rectangles_init_for_mask (&extents, &unbounded,
@@ -3655,15 +3520,21 @@ _cairo_image_surface_mask (void *abstract_surface,
if (unlikely (status))
return status;
- if (mask->type == CAIRO_PATTERN_TYPE_SOLID &&
- extents.clip->path == NULL &&
- ! _cairo_clip_is_region (extents.clip))
- {
- status = _clip_and_composite (surface, op, source,
- _composite_opacity_boxes, (void *) mask,
- &extents, need_bounded_clip (&extents));
- } else {
- status = _clip_and_composite (surface, op, source,
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ if (mask->type == CAIRO_PATTERN_TYPE_SOLID) {
+ cairo_boxes_t boxes;
+ double opacity = ((cairo_solid_pattern_t *)mask)->color.alpha;
+ status = _cairo_clip_to_boxes (extents.clip, &boxes);
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+ status = _clip_and_composite_boxes (surface,
+ op, source,
+ opacity,
+ &boxes, &extents);
+ _cairo_boxes_fini (&boxes);
+ }
+ }
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ status = _clip_and_composite (surface, op, source, 1.,
_composite_mask, (void *) mask,
&extents, need_bounded_clip (&extents));
}
@@ -3679,124 +3550,91 @@ typedef struct {
cairo_antialias_t antialias;
} composite_spans_info_t;
-//#define USE_BOTOR_SCAN_CONVERTER
static cairo_status_t
_composite_spans (void *closure,
- pixman_image_t *dst,
- pixman_format_code_t dst_format,
+ cairo_image_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *pattern,
+ double opacity,
int dst_x,
int dst_y,
- cairo_matrix_t *dst_device_transform,
const cairo_composite_rectangles_t *extents)
{
- uint8_t mask_buf[CAIRO_STACK_BUFFER_SIZE];
composite_spans_info_t *info = closure;
- cairo_image_surface_span_renderer_t renderer;
-#if USE_BOTOR_SCAN_CONVERTER
- cairo_box_t box;
- cairo_botor_scan_converter_t converter;
-#else
+ cairo_image_span_renderer_t renderer;
cairo_scan_converter_t *converter;
-#endif
- pixman_image_t *mask;
const cairo_rectangle_int_t *r = &extents->bounded;
cairo_status_t status;
-#if USE_BOTOR_SCAN_CONVERTER
- box.p1.x = _cairo_fixed_from_int (r->x);
- box.p1.y = _cairo_fixed_from_int (r->y);
- box.p2.x = _cairo_fixed_from_int (r->x + r->width);
- box.p2.y = _cairo_fixed_from_int (r->y + r->height);
- _cairo_botor_scan_converter_init (&converter, &box, info->fill_rule);
- status = converter.base.add_polygon (&converter.base, info->polygon);
-#else
+ status = _cairo_image_span_renderer_init (&renderer,
+ dst, op, pattern, opacity,
+ extents, FALSE);
+ if (unlikely (status))
+ return status;
+
converter = _cairo_tor_scan_converter_create (r->x, r->y,
r->x + r->width,
r->y + r->height,
info->fill_rule);
- status = converter->add_polygon (converter, info->polygon);
-#endif
+
+ status= _cairo_tor_scan_converter_add_polygon (converter,
+ info->polygon);
if (unlikely (status))
goto CLEANUP_CONVERTER;
- /* TODO: support rendering to A1 surfaces (or: go add span
- * compositing to pixman.) */
-
- if (pattern == NULL &&
- dst_format == PIXMAN_a8 &&
- op == CAIRO_OPERATOR_SOURCE)
- {
- mask = dst;
- dst = NULL;
- }
- else
- {
- int stride = CAIRO_STRIDE_FOR_WIDTH_BPP (r->width, 8);
- uint8_t *data = mask_buf;
+ status = converter->generate (converter, &renderer.base);
- if (r->height * stride <= (int) sizeof (mask_buf))
- memset (data, 0, r->height * stride);
- else
- data = NULL, stride = 0;
+ CLEANUP_CONVERTER:
+ _cairo_image_span_renderer_fini (&renderer);
+ converter->destroy (converter);
+ return status;
+}
- mask = pixman_image_create_bits (PIXMAN_a8,
- r->width,
- r->height,
- (uint32_t *) data,
- stride);
- if (unlikely (mask == NULL)) {
- status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
- goto CLEANUP_CONVERTER;
- }
- }
+static cairo_status_t
+_composite_polygon (cairo_image_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ double opacity,
+ cairo_polygon_t *polygon,
+ cairo_fill_rule_t fill_rule,
+ cairo_antialias_t antialias,
+ cairo_composite_rectangles_t *extents)
+{
+ cairo_image_span_renderer_t renderer;
+ cairo_scan_converter_t *converter;
+ const cairo_rectangle_int_t *r = &extents->bounded;
+ cairo_bool_t needs_clip = !extents->is_bounded && extents->clip->path;
+ cairo_int_status_t status;
- renderer.base.render_rows = _cairo_image_surface_span;
- renderer.mask_stride = pixman_image_get_stride (mask);
- renderer.mask_data = (uint8_t *) pixman_image_get_data (mask);
- if (dst != NULL)
- renderer.mask_data -= r->y * renderer.mask_stride + r->x;
- else
- renderer.mask_data -= dst_y * renderer.mask_stride + dst_x;
+ if (antialias == CAIRO_ANTIALIAS_NONE)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
-#if USE_BOTOR_SCAN_CONVERTER
- status = converter.base.generate (&converter.base, &renderer.base);
-#else
- status = converter->generate (converter, &renderer.base);
-#endif
+ status = _cairo_image_span_renderer_init (&renderer,
+ dst, op, pattern, opacity,
+ extents, needs_clip);
if (unlikely (status))
- goto CLEANUP_RENDERER;
-
- if (dst != NULL) {
- pixman_image_t *src;
- int src_x, src_y;
-
- src = _pixman_image_for_pattern (pattern, FALSE, r,
- dst_device_transform,
- &src_x, &src_y);
- if (unlikely (src == NULL)) {
- status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
- goto CLEANUP_RENDERER;
- }
+ return status;
- pixman_image_composite32 (_pixman_operator (op), src, mask, dst,
- r->x + src_x, r->y + src_y,
- 0, 0, /* mask.x, mask.y */
- r->x - dst_x, r->y - dst_y,
- r->width, r->height);
- pixman_image_unref (src);
+ if (needs_clip) {
+ printf ("awooga!\n");
+ converter = _cairo_clip_tor_scan_converter_create (extents->clip,
+ polygon,
+ fill_rule);
+ } else {
+ converter = _cairo_tor_scan_converter_create (r->x, r->y,
+ r->x + r->width,
+ r->y + r->height,
+ fill_rule);
+ status = converter->add_polygon (converter, polygon);
+ if (unlikely (status))
+ goto CLEANUP_CONVERTER;
}
- CLEANUP_RENDERER:
- if (dst != NULL)
- pixman_image_unref (mask);
+ status = converter->generate (converter, &renderer.base);
+
CLEANUP_CONVERTER:
-#if USE_BOTOR_SCAN_CONVERTER
- converter.base.destroy (&converter.base);
-#else
+ _cairo_image_span_renderer_fini (&renderer);
converter->destroy (converter);
-#endif
return status;
}
@@ -3804,12 +3642,13 @@ static cairo_status_t
_clip_and_composite_polygon (cairo_image_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *src,
+ double opacity,
cairo_polygon_t *polygon,
cairo_fill_rule_t fill_rule,
cairo_antialias_t antialias,
cairo_composite_rectangles_t *extents)
{
- cairo_status_t status;
+ cairo_int_status_t status;
if (_cairo_polygon_is_empty (polygon)) {
cairo_boxes_t boxes;
@@ -3818,12 +3657,13 @@ _clip_and_composite_polygon (cairo_image_surface_t *dst,
return CAIRO_STATUS_SUCCESS;
status = _cairo_clip_to_boxes (extents->clip, &boxes);
- if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
extents->is_bounded = _cairo_operator_bounded_by_either (op);
extents->mask = extents->bounded = extents->unbounded;
status = _clip_and_composite_boxes (dst,
CAIRO_OPERATOR_CLEAR,
&_cairo_pattern_clear.base,
+ opacity,
&boxes, extents);
_cairo_boxes_fini (&boxes);
}
@@ -3835,6 +3675,12 @@ _clip_and_composite_polygon (cairo_image_surface_t *dst,
if (! _cairo_rectangle_intersect (&extents->bounded, &extents->mask))
return CAIRO_STATUS_SUCCESS;
+ status = _composite_polygon (dst, op, src, opacity,
+ polygon, fill_rule, antialias,
+ extents);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+
if (antialias != CAIRO_ANTIALIAS_NONE) {
composite_spans_info_t info;
@@ -3842,7 +3688,7 @@ _clip_and_composite_polygon (cairo_image_surface_t *dst,
info.fill_rule = fill_rule;
info.antialias = antialias;
- status = _clip_and_composite (dst, op, src,
+ status = _clip_and_composite (dst, op, src, opacity,
_composite_spans, &info,
extents, need_unbounded_clip (extents));
} else {
@@ -3854,8 +3700,8 @@ _clip_and_composite_polygon (cairo_image_surface_t *dst,
status = _cairo_bentley_ottmann_tessellate_polygon (&traps,
polygon,
fill_rule);
- if (likely (status == CAIRO_STATUS_SUCCESS)) {
- status = _clip_and_composite_trapezoids (dst, op, src,
+ if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
+ status = _clip_and_composite_trapezoids (dst, op, src, opacity,
&traps, antialias,
extents);
}
@@ -3902,7 +3748,7 @@ _cairo_image_surface_stroke (void *abstract_surface,
antialias,
&boxes);
if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
- status = _clip_and_composite_boxes (surface, op, source,
+ status = _clip_and_composite_boxes (surface, op, source, 1.,
&boxes, &extents);
}
_cairo_boxes_fini (&boxes);
@@ -3918,7 +3764,8 @@ _cairo_image_surface_stroke (void *abstract_surface,
tolerance,
&polygon);
if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
- status = _clip_and_composite_polygon (surface, op, source, &polygon,
+ status = _clip_and_composite_polygon (surface, op, source, 1.,
+ &polygon,
CAIRO_FILL_RULE_WINDING,
antialias,
&extents);
@@ -3962,7 +3809,7 @@ _cairo_image_surface_fill (void *abstract_surface,
antialias,
&boxes);
if (likely (status == CAIRO_STATUS_SUCCESS)) {
- status = _clip_and_composite_boxes (surface, op, source,
+ status = _clip_and_composite_boxes (surface, op, source, 1.,
&boxes, &extents);
}
_cairo_boxes_fini (&boxes);
@@ -3974,7 +3821,8 @@ _cairo_image_surface_fill (void *abstract_surface,
_cairo_polygon_init_with_clip (&polygon, extents.clip);
status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon);
if (likely (status == CAIRO_STATUS_SUCCESS)) {
- status = _clip_and_composite_polygon (surface, op, source, &polygon,
+ status = _clip_and_composite_polygon (surface, op, source, 1.,
+ &polygon,
fill_rule, antialias,
&extents);
}
@@ -3994,13 +3842,12 @@ typedef struct {
static cairo_status_t
_composite_glyphs_via_mask (void *closure,
- pixman_image_t *dst,
- pixman_format_code_t dst_format,
+ cairo_image_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *pattern,
+ double opacity,
int dst_x,
int dst_y,
- cairo_matrix_t *dst_device_transform,
const cairo_composite_rectangles_t *extents)
{
composite_glyphs_info_t *info = closure;
@@ -4015,8 +3862,10 @@ _composite_glyphs_via_mask (void *closure,
int src_x, src_y;
int i;
+ assert (opacity == 1.);
+
src = _pixman_image_for_pattern (pattern, FALSE, &extents->bounded,
- dst_device_transform,
+ &dst->base.device_transform,
&src_x, &src_y);
if (unlikely (src == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
@@ -4117,7 +3966,7 @@ _composite_glyphs_via_mask (void *closure,
}
pixman_image_composite32 (_pixman_operator (op),
- src, mask, dst,
+ src, mask, dst->pixman_image,
extents->bounded.x + src_x, extents->bounded.y + src_y,
0, 0,
extents->bounded.x - dst_x, extents->bounded.y - dst_y,
@@ -4135,13 +3984,12 @@ CLEANUP:
static cairo_status_t
_composite_glyphs (void *closure,
- pixman_image_t *dst,
- pixman_format_code_t dst_format,
+ cairo_image_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *pattern,
+ double opacity,
int dst_x,
int dst_y,
- cairo_matrix_t *dst_device_transform,
const cairo_composite_rectangles_t *extents)
{
composite_glyphs_info_t *info = closure;
@@ -4152,9 +4000,11 @@ _composite_glyphs (void *closure,
cairo_status_t status;
int i;
+ assert (opacity == 1.);
+
if (pattern != NULL) {
src = _pixman_image_for_pattern (pattern, FALSE, &extents->bounded,
- dst_device_transform,
+ &dst->base.device_transform,
&src_x, &src_y);
src_x -= dst_x;
src_y -= dst_y;
@@ -4215,7 +4065,7 @@ _composite_glyphs (void *closure,
y2 = extents->bounded.y + extents->bounded.height;
pixman_image_composite32 (pixman_op,
- src, glyph_surface->pixman_image, dst,
+ src, glyph_surface->pixman_image, dst->pixman_image,
x1 + src_x, y1 + src_y,
x1 - x, y1 - y,
x1 - dst_x, y1 - dst_y,
@@ -4267,7 +4117,7 @@ _cairo_image_surface_glyphs (void *abstract_surface,
{
flags |= FORCE_CLIP_REGION;
}
- status = _clip_and_composite (surface, op, source,
+ status = _clip_and_composite (surface, op, source, 1.,
overlap || extents.is_bounded == 0 ?
_composite_glyphs_via_mask :
_composite_glyphs,
@@ -4573,14 +4423,8 @@ _cairo_image_surface_composite_trapezoids (cairo_operator_t op,
info.traps = traps;
info.num_traps = num_traps;
info.antialias = antialias;
- status = _composite_traps (&info,
- dst->pixman_image,
- dst->pixman_format,
- op,
- &source_pattern.base,
- 0, 0,
- &dst->base.device_transform,
- &extents);
+ status = _composite_traps (&info, dst, op, &source_pattern.base, 1.,
+ 0, 0, &extents);
if (status == CAIRO_STATUS_SUCCESS && ! extents.is_bounded)
status = _cairo_image_surface_fixup_unbounded (dst, &extents, NULL);
diff --git a/src/cairo-rectangular-scan-converter.c b/src/cairo-rectangular-scan-converter.c
index 56e552b..1922364 100644
--- a/src/cairo-rectangular-scan-converter.c
+++ b/src/cairo-rectangular-scan-converter.c
@@ -380,6 +380,7 @@ _active_edges_to_spans (sweep_line_t *sweep)
if (cell->x != prev_x && coverage != prev_coverage) {
int n = sweep->num_spans++;
sweep->spans[n].x = prev_x;
+ sweep->spans[n].is_clipped = 0;
sweep->spans[n].coverage = coverage >> (CAIRO_FIXED_FRAC_BITS * 2 - 8);
sweep->spans[n].coverage -= sweep->spans[n].coverage >> 8;
prev_coverage = coverage;
@@ -389,6 +390,7 @@ _active_edges_to_spans (sweep_line_t *sweep)
if (coverage != prev_coverage) {
int n = sweep->num_spans++;
sweep->spans[n].x = cell->x;
+ sweep->spans[n].is_clipped = 0;
sweep->spans[n].coverage = coverage >> (CAIRO_FIXED_FRAC_BITS * 2 - 8);
sweep->spans[n].coverage -= sweep->spans[n].coverage >> 8;
prev_coverage = coverage;
@@ -402,12 +404,14 @@ _active_edges_to_spans (sweep_line_t *sweep)
if (prev_x <= sweep->xmax) {
int n = sweep->num_spans++;
sweep->spans[n].x = prev_x;
+ sweep->spans[n].is_clipped = 0;
sweep->spans[n].coverage = coverage;
}
if (coverage && prev_x < sweep->xmax) {
int n = sweep->num_spans++;
sweep->spans[n].x = sweep->xmax;
+ sweep->spans[n].is_clipped = 1;
sweep->spans[n].coverage = 0;
}
}
diff --git a/src/cairo-spans-private.h b/src/cairo-spans-private.h
index 00a4df8..45853f0 100644
--- a/src/cairo-spans-private.h
+++ b/src/cairo-spans-private.h
@@ -39,6 +39,8 @@ typedef struct _cairo_half_open_span {
/* The inclusive x-coordinate of the start of the span. */
int x;
+ cairo_bool_t is_clipped;
+
/* The pixel coverage for the pixels to the right. */
int coverage;
} cairo_half_open_span_t;
@@ -102,6 +104,14 @@ _cairo_tor_scan_converter_create (int xmin,
int xmax,
int ymax,
cairo_fill_rule_t fill_rule);
+cairo_private cairo_status_t
+_cairo_tor_scan_converter_add_polygon (void *converter,
+ const cairo_polygon_t *polygon);
+
+cairo_private cairo_scan_converter_t *
+_cairo_clip_tor_scan_converter_create (cairo_clip_t *clip,
+ cairo_polygon_t *polygon,
+ cairo_fill_rule_t fill_rule);
typedef struct _cairo_rectangular_scan_converter {
cairo_scan_converter_t base;
diff --git a/src/cairo-tor-scan-converter.c b/src/cairo-tor-scan-converter.c
index d9c9ce8..9cf9f2f 100644
--- a/src/cairo-tor-scan-converter.c
+++ b/src/cairo-tor-scan-converter.c
@@ -134,9 +134,6 @@ blit_with_span_renderer(
int xmin,
int xmax);
-static glitter_status_t
-blit_empty_with_span_renderer (cairo_span_renderer_t *renderer, int y, int height);
-
#define GLITTER_BLIT_COVERAGES_ARGS \
cairo_span_renderer_t *span_renderer, \
struct pool *span_pool
@@ -149,10 +146,6 @@ blit_empty_with_span_renderer (cairo_span_renderer_t *renderer, int y, int heigh
xmin, xmax); \
} while (0)
-#define GLITTER_BLIT_COVERAGES_EMPTY(y, height, xmin, xmax) do { \
- blit_empty_with_span_renderer (span_renderer, y, height); \
-} while (0)
-
/*-------------------------------------------------------------------------
* glitter-paths.h
*/
@@ -387,10 +380,7 @@ struct edge {
grid_scaled_y_t dy;
};
-/* Number of subsample rows per y-bucket. Must be GRID_Y. */
-#define EDGE_Y_BUCKET_HEIGHT GRID_Y
-
-#define EDGE_Y_BUCKET_INDEX(y, ymin) (((y) - (ymin))/EDGE_Y_BUCKET_HEIGHT)
+#define EDGE_Y_BUCKET_INDEX(y, ymin) (((y) - (ymin))/GRID_Y)
/* A collection of sorted and vertically clipped edges of the polygon.
* Edges are moved from the polygon to an active list while scan
@@ -1005,12 +995,11 @@ polygon_reset (struct polygon *polygon,
grid_scaled_y_t ymax)
{
unsigned h = ymax - ymin;
- unsigned num_buckets = EDGE_Y_BUCKET_INDEX(ymax + EDGE_Y_BUCKET_HEIGHT-1,
- ymin);
+ unsigned num_buckets = EDGE_Y_BUCKET_INDEX(ymax + GRID_Y-1, ymin);
pool_reset(polygon->edge_pool.base);
- if (unlikely (h > 0x7FFFFFFFU - EDGE_Y_BUCKET_HEIGHT))
+ if (unlikely (h > 0x7FFFFFFFU - GRID_Y))
goto bail_no_mem; /* even if you could, you wouldn't want to. */
if (polygon->y_buckets != polygon->y_buckets_embedded)
@@ -1653,18 +1642,6 @@ glitter_scan_converter_add_edge (glitter_scan_converter_t *converter,
polygon_add_edge (converter->polygon, &e);
}
-#ifndef GLITTER_BLIT_COVERAGES_BEGIN
-# define GLITTER_BLIT_COVERAGES_BEGIN
-#endif
-
-#ifndef GLITTER_BLIT_COVERAGES_END
-# define GLITTER_BLIT_COVERAGES_END
-#endif
-
-#ifndef GLITTER_BLIT_COVERAGES_EMPTY
-# define GLITTER_BLIT_COVERAGES_EMPTY(y0, y1, xmin, xmax)
-#endif
-
static cairo_bool_t
active_list_is_vertical (struct active_list *active)
{
@@ -1714,9 +1691,6 @@ glitter_scan_converter_render(
if (xmin_i >= xmax_i)
return;
- /* Let the coverage blitter initialise itself. */
- GLITTER_BLIT_COVERAGES_BEGIN;
-
/* Render each pixel row. */
for (i = 0; i < h; i = j) {
int do_full_step = 0;
@@ -1730,7 +1704,6 @@ glitter_scan_converter_render(
active->min_height = INT_MAX;
for (; j < h && ! polygon->y_buckets[j]; j++)
;
- GLITTER_BLIT_COVERAGES_EMPTY (i+ymin_i, j-i, xmin_i, xmax_i);
continue;
}
@@ -1784,9 +1757,6 @@ glitter_scan_converter_render(
active->min_height -= GRID_Y;
}
-
- /* Clean up the coverage blitter. */
- GLITTER_BLIT_COVERAGES_END;
}
/*-------------------------------------------------------------------------
@@ -1807,7 +1777,7 @@ blit_with_span_renderer (struct cell_list *cells,
unsigned num_spans;
if (cell == &cells->tail)
- return blit_empty_with_span_renderer (renderer, y, height);
+ return CAIRO_STATUS_SUCCESS;
/* Skip cells to the left of the clip region. */
while (cell->x < xmin) {
@@ -1839,6 +1809,7 @@ blit_with_span_renderer (struct cell_list *cells,
if (x > prev_x && cover != last_cover) {
spans[num_spans].x = prev_x;
+ spans[num_spans].is_clipped = 0;
spans[num_spans].coverage = GRID_AREA_TO_ALPHA (cover);
last_cover = cover;
last_x = prev_x;
@@ -1850,6 +1821,7 @@ blit_with_span_renderer (struct cell_list *cells,
if (area != last_cover) {
spans[num_spans].x = x;
+ spans[num_spans].is_clipped = 0;
spans[num_spans].coverage = GRID_AREA_TO_ALPHA (area);
last_cover = area;
last_x = x;
@@ -1861,6 +1833,7 @@ blit_with_span_renderer (struct cell_list *cells,
if (prev_x <= xmax && cover != last_cover) {
spans[num_spans].x = prev_x;
+ spans[num_spans].is_clipped = 0;
spans[num_spans].coverage = GRID_AREA_TO_ALPHA (cover);
last_cover = cover;
last_x = prev_x;
@@ -1877,12 +1850,6 @@ blit_with_span_renderer (struct cell_list *cells,
return renderer->render_rows (renderer, y, height, spans, num_spans);
}
-static glitter_status_t
-blit_empty_with_span_renderer (cairo_span_renderer_t *renderer, int y, int height)
-{
- return renderer->render_rows (renderer, y, height, NULL, 0);
-}
-
struct _cairo_tor_scan_converter {
cairo_scan_converter_t base;
@@ -1931,7 +1898,7 @@ _cairo_tor_scan_converter_add_edge (void *converter,
return CAIRO_STATUS_SUCCESS;
}
-static cairo_status_t
+cairo_status_t
_cairo_tor_scan_converter_add_polygon (void *converter,
const cairo_polygon_t *polygon)
{
commit c822674f93110fcd4a68421807c8736a788c6f32
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date: Tue Aug 2 22:31:49 2011 +0100
Introduce cairo_mime_surface_t
The mime surface is a user-callback surface designed for interfacing
cairo with an opaque data source. For instance, in a web browser, the
incoming page may be laid out and rendered to a recording surface before
all the image data has finished being downloaded. In this circumstance
we need to pass a place holder to cairo and to supply the image data
later upon demand.
diff --git a/boilerplate/Makefile.win32.features b/boilerplate/Makefile.win32.features
index 0cf7095..3542699 100644
--- a/boilerplate/Makefile.win32.features
+++ b/boilerplate/Makefile.win32.features
@@ -398,6 +398,16 @@ enabled_cairo_boilerplate_private += $(cairo_boilerplate_image_private)
enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_image_cxx_sources)
enabled_cairo_boilerplate_sources += $(cairo_boilerplate_image_sources)
+supported_cairo_boilerplate_headers += $(cairo_boilerplate_mime_headers)
+all_cairo_boilerplate_headers += $(cairo_boilerplate_mime_headers)
+all_cairo_boilerplate_private += $(cairo_boilerplate_mime_private)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_mime_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_mime_sources)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_mime_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_mime_private)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_mime_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_mime_sources)
+
supported_cairo_boilerplate_headers += $(cairo_boilerplate_recording_headers)
all_cairo_boilerplate_headers += $(cairo_boilerplate_recording_headers)
all_cairo_boilerplate_private += $(cairo_boilerplate_recording_private)
diff --git a/build/Makefile.win32.features-h b/build/Makefile.win32.features-h
index e35cc6c..79de87d 100644
--- a/build/Makefile.win32.features-h
+++ b/build/Makefile.win32.features-h
@@ -99,6 +99,7 @@ ifeq ($(CAIRO_HAS_TEST_SURFACES),1)
@echo "#define CAIRO_HAS_TEST_SURFACES 1" >> $(top_srcdir)/src/cairo-features.h
endif
@echo "#define CAIRO_HAS_IMAGE_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
+ @echo "#define CAIRO_HAS_MIME_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
@echo "#define CAIRO_HAS_RECORDING_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
ifeq ($(CAIRO_HAS_TEE_SURFACE),1)
@echo "#define CAIRO_HAS_TEE_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
diff --git a/build/configure.ac.features b/build/configure.ac.features
index 4cccd9c..9c5680a 100644
--- a/build/configure.ac.features
+++ b/build/configure.ac.features
@@ -365,6 +365,7 @@ AC_DEFUN([CAIRO_REPORT],
echo "The following surface backends:"
echo " Image: yes (always builtin)"
echo " Recording: yes (always builtin)"
+ echo " Mime: yes (always builtin)"
echo " Tee: $use_tee"
echo " XML: $use_xml"
echo " Skia: $use_skia"
diff --git a/configure.ac b/configure.ac
index 8e6bc4d..793263f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -612,6 +612,7 @@ CAIRO_ENABLE_SURFACE_BACKEND(image, image, always, [
dnl ===========================================================================
+CAIRO_ENABLE_SURFACE_BACKEND(mime, mime, always)
CAIRO_ENABLE_SURFACE_BACKEND(recording, recording, always)
CAIRO_ENABLE_SURFACE_BACKEND(tee, tee, no)
CAIRO_ENABLE_SURFACE_BACKEND(xml, xml, no, [
diff --git a/src/Makefile.sources b/src/Makefile.sources
index b5e4805..f22f7ed 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -148,6 +148,7 @@ cairo_sources = \
cairo-lzw.c \
cairo-matrix.c \
cairo-mesh-pattern-rasterizer.c \
+ cairo-mime-surface.c \
cairo-misc.c \
cairo-mutex.c \
cairo-observer.c \
diff --git a/src/Makefile.win32.features b/src/Makefile.win32.features
index 640df73..6e72fdb 100644
--- a/src/Makefile.win32.features
+++ b/src/Makefile.win32.features
@@ -520,6 +520,16 @@ enabled_cairo_private += $(cairo_image_private)
enabled_cairo_cxx_sources += $(cairo_image_cxx_sources)
enabled_cairo_sources += $(cairo_image_sources)
+supported_cairo_headers += $(cairo_mime_headers)
+all_cairo_headers += $(cairo_mime_headers)
+all_cairo_private += $(cairo_mime_private)
+all_cairo_cxx_sources += $(cairo_mime_cxx_sources)
+all_cairo_sources += $(cairo_mime_sources)
+enabled_cairo_headers += $(cairo_mime_headers)
+enabled_cairo_private += $(cairo_mime_private)
+enabled_cairo_cxx_sources += $(cairo_mime_cxx_sources)
+enabled_cairo_sources += $(cairo_mime_sources)
+
supported_cairo_headers += $(cairo_recording_headers)
all_cairo_headers += $(cairo_recording_headers)
all_cairo_private += $(cairo_recording_private)
diff --git a/src/cairo-mime-surface.c b/src/cairo-mime-surface.c
new file mode 100644
index 0000000..d1ec974
--- /dev/null
+++ b/src/cairo-mime-surface.c
@@ -0,0 +1,347 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Chris Wilson <chris at chris-wilson.co.uk>
+ */
+
+/**
+ * SECTION:cairo-mime-surface
+ * @Title: Callback Surfaces
+ * @Short_Description: Allows the user to provide a callback to supply image
+ * data upon demand
+ * @See_Also: #cairo_surface_t
+ *
+ */
+
+#include "cairoint.h"
+#include "cairo-error-private.h"
+
+typedef struct _cairo_mime_surface {
+ cairo_surface_t base;
+
+ cairo_rectangle_int_t extents;
+
+ cairo_mime_surface_acquire_t acquire;
+ cairo_mime_surface_release_t release;
+ cairo_mime_surface_snapshot_t snapshot;
+ cairo_mime_surface_destroy_t destroy;
+
+ /* an explicit pre-allocated member in preference to the general user-data */
+ void *user_data;
+} cairo_mime_surface_t;
+
+static cairo_status_t
+_cairo_mime_surface_finish (void *abstract_surface)
+{
+ cairo_mime_surface_t *surface = abstract_surface;
+
+ if (surface->destroy)
+ surface->destroy (&surface->base, surface->user_data);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_surface_t *
+_cairo_mime_surface_create_similar (void *abstract_other,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ cairo_mime_surface_t *other = abstract_other;
+ cairo_mime_surface_t *surface;
+
+ surface = (cairo_mime_surface_t *)
+ cairo_mime_surface_create (NULL, content, width, height);
+ if (likely (surface->base.status == CAIRO_STATUS_SUCCESS)) {
+ surface->acquire = other->acquire;
+ surface->release = other->release;
+ surface->snapshot = other->snapshot;
+ surface->destroy = other->destroy;
+ }
+
+ return &surface->base;
+}
+
+static cairo_bool_t
+_cairo_mime_surface_get_extents (void *abstract_surface,
+ cairo_rectangle_int_t *rectangle)
+{
+ cairo_mime_surface_t *surface = abstract_surface;
+
+ *rectangle = surface->extents;
+ return TRUE;
+}
+
+static cairo_status_t
+_cairo_mime_surface_acquire_source_image (void *abstract_surface,
+ //cairo_surface_t *target,
+ cairo_image_surface_t **image_out,
+ void **image_extra)
+{
+ cairo_mime_surface_t *mime = abstract_surface;
+ cairo_surface_t *acquired;
+
+ if (mime->acquire == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ acquired = mime->acquire (&mime->base, mime->user_data, NULL, NULL);
+
+ *image_out = (cairo_image_surface_t *) acquired;
+ *image_extra = NULL;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_mime_surface_release_source_image (void *abstract_surface,
+ cairo_image_surface_t *image,
+ void *image_extra)
+{
+ cairo_mime_surface_t *mime = abstract_surface;
+
+ if (mime->release)
+ mime->release (&mime->base, mime->user_data, &image->base);
+}
+
+static cairo_surface_t *
+_cairo_mime_surface_snapshot (void *abstract_surface)
+{
+ cairo_mime_surface_t *mime = abstract_surface;
+
+ if (mime->snapshot == NULL)
+ return NULL;
+
+ return mime->snapshot (&mime->base, mime->user_data);
+}
+
+static const cairo_surface_backend_t cairo_mime_surface_backend = {
+ CAIRO_SURFACE_TYPE_RECORDING,
+ _cairo_mime_surface_finish,
+
+ NULL, //_cairo_default_context_create,
+
+ _cairo_mime_surface_create_similar,
+ NULL, /* create similar image */
+ NULL, /* map to image */
+ NULL, /* unmap image */
+
+ _cairo_mime_surface_acquire_source_image,
+ _cairo_mime_surface_release_source_image,
+ NULL, /* acquire_dest_image */
+ NULL, /* release_dest_image */
+ NULL, /* clone_similar */
+ NULL, /* composite */
+ NULL, /* fill_rectangles */
+ NULL, /* composite_trapezoids */
+ NULL, /* create_span_renderer */
+ NULL, /* check_span_renderer */
+ NULL, /* copy_page */
+ NULL, /* show_page */
+ _cairo_mime_surface_get_extents,
+ NULL, /* old_show_glyphs */
+ NULL, /* get_font_options */
+ NULL, /* flush */
+ NULL, /* mark_dirty_rectangle */
+ NULL, /* scaled_font_fini */
+ NULL, /* scaled_glyph_fini */
+
+
+ NULL, //_cairo_mime_surface_paint,
+ NULL, //_cairo_mime_surface_mask,
+ NULL, //_cairo_mime_surface_stroke,
+ NULL, //_cairo_mime_surface_fill,
+ NULL, // glyphs
+
+ _cairo_mime_surface_snapshot,
+};
+
+cairo_surface_t *
+cairo_mime_surface_create (void *data, cairo_content_t content, int width, int height)
+{
+ cairo_mime_surface_t *surface;
+
+ if (width < 0 || height < 0)
+ return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE);
+
+ if (! CAIRO_CONTENT_VALID (content))
+ return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_CONTENT);
+
+ surface = calloc (1, sizeof (*surface));
+ if (unlikely (surface == NULL))
+ return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+ _cairo_surface_init (&surface->base,
+ &cairo_mime_surface_backend,
+ NULL, /* device */
+ content);
+
+ surface->extents.x = 0;
+ surface->extents.y = 0;
+ surface->extents.width = width;
+ surface->extents.height = height;
+
+ surface->user_data = data;
+
+ return &surface->base;
+}
+
+void
+cairo_mime_surface_set_callback_data (cairo_surface_t *surface,
+ void *data)
+{
+ cairo_mime_surface_t *mime;
+
+ if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
+ return;
+
+ if (surface->backend != &cairo_mime_surface_backend)
+ return;
+
+ mime = (cairo_mime_surface_t *)surface;
+ mime->user_data = data;
+}
+
+void *
+cairo_mime_surface_get_callback_data (cairo_surface_t *surface)
+{
+ cairo_mime_surface_t *mime;
+
+ if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
+ return NULL;
+
+ if (surface->backend != &cairo_mime_surface_backend)
+ return NULL;
+
+ mime = (cairo_mime_surface_t *)surface;
+ return mime->user_data;
+}
+
+void
+cairo_mime_surface_set_acquire (cairo_surface_t *surface,
+ cairo_mime_surface_acquire_t acquire,
+ cairo_mime_surface_release_t release)
+{
+ cairo_mime_surface_t *mime;
+
+ if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
+ return;
+
+ if (surface->backend != &cairo_mime_surface_backend)
+ return;
+
+ mime = (cairo_mime_surface_t *)surface;
+ mime->acquire = acquire;
+ mime->release = release;
+}
+
+void
+cairo_mime_surface_get_acquire (cairo_surface_t *surface,
+ cairo_mime_surface_acquire_t *acquire,
+ cairo_mime_surface_release_t *release)
+{
+ cairo_mime_surface_t *mime;
+
+ if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
+ return;
+
+ if (surface->backend != &cairo_mime_surface_backend)
+ return;
+
+ mime = (cairo_mime_surface_t *)surface;
+ if (acquire)
+ *acquire = mime->acquire;
+ if (release)
+ *release = mime->release;
+}
+
+void
+cairo_mime_surface_set_snapshot (cairo_surface_t *surface,
+ cairo_mime_surface_snapshot_t snapshot)
+{
+ cairo_mime_surface_t *mime;
+
+ if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
+ return;
+
+ if (surface->backend != &cairo_mime_surface_backend)
+ return;
+
+ mime = (cairo_mime_surface_t *)surface;
+ mime->snapshot = snapshot;
+}
+
+cairo_mime_surface_snapshot_t
+cairo_mime_surface_get_snapshot (cairo_surface_t *surface)
+{
+ cairo_mime_surface_t *mime;
+
+ if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
+ return NULL;
+
+ if (surface->backend != &cairo_mime_surface_backend)
+ return NULL;
+
+ mime = (cairo_mime_surface_t *)surface;
+ return mime->snapshot;
+}
+
+void
+cairo_mime_surface_set_destroy (cairo_surface_t *surface,
+ cairo_mime_surface_destroy_t destroy)
+{
+ cairo_mime_surface_t *mime;
+
+ if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
+ return;
+
+ if (surface->backend != &cairo_mime_surface_backend)
+ return;
+
+ mime = (cairo_mime_surface_t *)surface;
+ mime->destroy = destroy;
+}
+
+cairo_mime_surface_destroy_t
+cairo_mime_surface_get_destroy (cairo_surface_t *surface)
+{
+ cairo_mime_surface_t *mime;
+
+ if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
+ return NULL;
+
+ if (surface->backend != &cairo_mime_surface_backend)
+ return NULL;
+
+ mime = (cairo_mime_surface_t *)surface;
+ return mime->destroy;
+}
diff --git a/src/cairo.h b/src/cairo.h
index 9d788cc..7987192 100644
--- a/src/cairo.h
+++ b/src/cairo.h
@@ -433,6 +433,24 @@ typedef cairo_status_t (*cairo_read_func_t) (void *closure,
unsigned char *data,
unsigned int length);
+/**
+ * cairo_rectangle_int_t:
+ * @x: X coordinate of the left side of the rectangle
+ * @y: Y coordinate of the the top side of the rectangle
+ * @width: width of the rectangle
+ * @height: height of the rectangle
+ *
+ * A data structure for holding a rectangle with integer coordinates.
+ *
+ * Since: 1.10
+ **/
+
+typedef struct _cairo_rectangle_int {
+ int x, y;
+ int width, height;
+} cairo_rectangle_int_t;
+
+
/* Functions for manipulating state objects */
cairo_public cairo_t *
cairo_create (cairo_surface_t *target);
@@ -2344,6 +2362,55 @@ cairo_recording_surface_ink_extents (cairo_surface_t *surface,
double *width,
double *height);
+/* Mime-surface (callback) functions */
+
+typedef cairo_surface_t *(*cairo_mime_surface_acquire_t) (cairo_surface_t *mime_surface,
+ void *callback_data,
+ cairo_surface_t *target,
+ cairo_rectangle_int_t *sample_extents);
+
+typedef void (*cairo_mime_surface_release_t) (cairo_surface_t *mime_surface,
+ void *callback_data,
+ cairo_surface_t *image_surface);
+
+typedef cairo_surface_t *(*cairo_mime_surface_snapshot_t) (cairo_surface_t *mime_surface,
+ void *callback_data);
+typedef void (*cairo_mime_surface_destroy_t) (cairo_surface_t *mime_surface,
+ void *callback_data);
+
+cairo_public cairo_surface_t *
+cairo_mime_surface_create (void *data, cairo_content_t content, int width, int height);
+
+cairo_public void
+cairo_mime_surface_set_callback_data (cairo_surface_t *surface,
+ void *data);
+
+cairo_public void *
+cairo_mime_surface_get_callback_data (cairo_surface_t *surface);
+
+cairo_public void
+cairo_mime_surface_set_acquire (cairo_surface_t *surface,
+ cairo_mime_surface_acquire_t acquire,
+ cairo_mime_surface_release_t release);
+
+cairo_public void
+cairo_mime_surface_get_acquire (cairo_surface_t *surface,
+ cairo_mime_surface_acquire_t *acquire,
+ cairo_mime_surface_release_t *release);
+cairo_public void
+cairo_mime_surface_set_snapshot (cairo_surface_t *surface,
+ cairo_mime_surface_snapshot_t snapshot);
+
+cairo_public cairo_mime_surface_snapshot_t
+cairo_mime_surface_get_snapshot (cairo_surface_t *surface);
+
+cairo_public void
+cairo_mime_surface_set_destroy (cairo_surface_t *surface,
+ cairo_mime_surface_destroy_t destroy);
+
+cairo_public cairo_mime_surface_destroy_t
+cairo_mime_surface_get_destroy (cairo_surface_t *surface);
+
/* Pattern creation functions */
cairo_public cairo_pattern_t *
@@ -2671,23 +2738,6 @@ cairo_matrix_transform_point (const cairo_matrix_t *matrix,
**/
typedef struct _cairo_region cairo_region_t;
-/**
- * cairo_rectangle_int_t:
- * @x: X coordinate of the left side of the rectangle
- * @y: Y coordinate of the the top side of the rectangle
- * @width: width of the rectangle
- * @height: height of the rectangle
- *
- * A data structure for holding a rectangle with integer coordinates.
- *
- * Since: 1.10
- **/
-
-typedef struct _cairo_rectangle_int {
- int x, y;
- int width, height;
-} cairo_rectangle_int_t;
-
typedef enum _cairo_region_overlap {
CAIRO_REGION_OVERLAP_IN, /* completely inside region */
CAIRO_REGION_OVERLAP_OUT, /* completely outside region */
More information about the cairo-commit
mailing list