[cairo] PS/PDF Gradients
Adrian Johnson
ajohnson at redneon.com
Sun Jan 28 05:57:27 PST 2007
I have been testing Miklós' PDF gradient patch from
http://lists.freedesktop.org/archives/cairo/2006-August/007648.html
Looking at the patch the only obvious deficiency I could see is lack of
support for the different cairo_extend_t options. The patch has the PDF
/Extend value set to [true true] which is equivalent CAIRO_EXTEND_PAD.
Testing linear gradients with rgb stops appears to be ok. Changing the
PDF /Extend value set to [false false] which is equivalent
CAIRO_EXTEND_NONE also works.
The results of testing linear gradients with CAIRO_EXTEND_PAD and rgba
stops are:
acroread - ok but the colors seem to be too dark.
evince - broken, The padded region is opaque when it should have
some transparency.
ghostscript - broken. all opaque colors. no transparency.
Changing the PDF /Extend value to [false false] to support
CAIRO_EXTEND_NONE also produced similar results.
I have not tested radial gradients.
I have attached a patch that adds PostScript support for linear
gradients. As this is a Level 3 feature a function to select the PS
level would be required. Following the same pattern as the svg
restrict_to function I would suggest:
typedef enum _cairo_ps_version {
CAIRO_PS_LEVEL_2,
CAIRO_PS_LEVEL_3
} cairo_ps_version_t;
cairo_public void
cairo_ps_surface_restrict_to_level (
cairo_surface_t *surface,
cairo_ps_version_t level);
-------------- next part --------------
>From 4ad13146019424be650b023b43f2e2fcafa96d35 Mon Sep 17 00:00:00 2001
From: Adrian Johnson <ajohnson at redneon.com>
Date: Sun, 28 Jan 2007 22:42:36 +1030
Subject: [PATCH] PS: Add support for linear gradients
---
src/cairo-pattern.c | 2 +-
src/cairo-ps-surface.c | 182 +++++++++++++++++++++++++++++++++++++++++++++---
2 files changed, 174 insertions(+), 10 deletions(-)
diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c
index 35c0739..6907f24 100644
--- a/src/cairo-pattern.c
+++ b/src/cairo-pattern.c
@@ -1076,7 +1076,7 @@ _gradient_is_opaque (const cairo_gradien
unsigned int i;
for (i = 0; i < gradient->n_stops; i++)
- if (! CAIRO_ALPHA_IS_OPAQUE (gradient->stops[i].color.alpha))
+ if (! CAIRO_ALPHA_IS_OPAQUE (gradient->stops[i].color.alpha / 65535.0))
return FALSE;
return TRUE;
diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c
index 45d9fe7..6d38802 100644
--- a/src/cairo-ps-surface.c
+++ b/src/cairo-ps-surface.c
@@ -1335,6 +1335,12 @@ pattern_supported (const cairo_pattern_t
if (pattern->type == CAIRO_PATTERN_TYPE_SOLID)
return TRUE;
+ if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR) {
+ if (pattern->extend == CAIRO_EXTEND_NONE ||
+ pattern->extend == CAIRO_EXTEND_PAD)
+ return TRUE;
+ }
+
if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE)
return surface_pattern_supported ((const cairo_surface_pattern_t *) pattern);
@@ -1749,11 +1755,156 @@ emit_surface_pattern (cairo_ps_surface_t
"makepattern setpattern\n");
}
+typedef struct _cairo_ps_color_stop {
+ double offset;
+ unsigned char color_char[3];
+} cairo_ps_color_stop_t;
+
static void
-emit_linear_pattern (cairo_ps_surface_t *surface,
- cairo_linear_pattern_t *pattern)
+emit_linear_colorgradient (cairo_ps_surface_t *surface,
+ cairo_ps_color_stop_t *stop1,
+ cairo_ps_color_stop_t *stop2)
{
- /* XXX: NYI */
+ _cairo_output_stream_printf (surface->stream,
+ " << /FunctionType 0\n"
+ " /Domain [ 0 1 ]\n"
+ " /Size [ 2 ]\n"
+ " /BitsPerSample 8\n"
+ " /Range [ 0 1 0 1 0 1 ]\n"
+ " /DataSource <%02x%02x%02x%02x%02x%02x>\n"
+ " >>\n",
+ stop1->color_char[0],
+ stop1->color_char[1],
+ stop1->color_char[2],
+ stop2->color_char[0],
+ stop2->color_char[1],
+ stop2->color_char[2]);
+}
+
+static void
+emit_stitched_colorgradient (cairo_ps_surface_t *surface,
+ unsigned int n_stops,
+ cairo_ps_color_stop_t stops[])
+{
+ unsigned int i;
+
+ _cairo_output_stream_printf (surface->stream,
+ " << /FunctionType 3\n"
+ " /Domain [ 0 1 ]\n"
+ " /Functions [\n");
+ for (i = 0; i < n_stops - 1; i++)
+ emit_linear_colorgradient (surface, &stops[i], &stops[i+1]);
+
+ _cairo_output_stream_printf (surface->stream, " ]\n");
+
+ _cairo_output_stream_printf (surface->stream, " /Bounds [ ");
+ for (i = 1; i < n_stops-1; i++)
+ _cairo_output_stream_printf (surface->stream, "%f ", stops[i].offset);
+ _cairo_output_stream_printf (surface->stream, "]\n");
+
+ _cairo_output_stream_printf (surface->stream, " /Encode [ ");
+ for (i = 1; i < n_stops; i++)
+ _cairo_output_stream_printf (surface->stream, "0 1 ");
+ _cairo_output_stream_printf (surface->stream, "]\n");
+
+ _cairo_output_stream_printf (surface->stream, " >>\n");
+}
+
+#define COLOR_STOP_EPSILLON 1e-6
+
+static cairo_status_t
+emit_pattern_stops (cairo_ps_surface_t *surface,
+ cairo_gradient_pattern_t *pattern)
+{
+ cairo_ps_color_stop_t *allstops, *stops;
+ unsigned int i, n_stops;
+
+ allstops = malloc ((pattern->n_stops + 2) * sizeof (cairo_ps_color_stop_t));
+ if (allstops == NULL)
+ return CAIRO_STATUS_NO_MEMORY;
+
+ stops = &allstops[1];
+ n_stops = pattern->n_stops;
+
+ for (i = 0; i < pattern->n_stops; i++) {
+ stops[i].color_char[0] = pattern->stops[i].color.red >> 8;
+ stops[i].color_char[1] = pattern->stops[i].color.green >> 8;
+ stops[i].color_char[2] = pattern->stops[i].color.blue >> 8;
+ stops[i].offset = _cairo_fixed_to_double (pattern->stops[i].x);
+ }
+
+ /* make sure first offset is 0.0 and last offset is 1.0. */
+ if (stops[0].offset > COLOR_STOP_EPSILLON) {
+ memcpy (allstops, stops, sizeof (cairo_ps_color_stop_t));
+ stops = allstops;
+ stops[0].offset = 0.0;
+ n_stops++;
+ }
+ if (stops[n_stops-1].offset < 1.0 - COLOR_STOP_EPSILLON) {
+ memcpy (&stops[n_stops],
+ &stops[n_stops - 1],
+ sizeof (cairo_ps_color_stop_t));
+ stops[n_stops].offset = 1.0;
+ n_stops++;
+ }
+
+ if (n_stops == 2) {
+ /* no need for stitched function */
+ emit_linear_colorgradient (surface, &stops[0], &stops[1]);
+ } else {
+ /* multiple stops: stitch. XXX possible optimization: regulary spaced
+ * stops do not require stitching. XXX */
+ emit_stitched_colorgradient (surface, n_stops,stops);
+ }
+
+ free (allstops);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+emit_linear_pattern (cairo_ps_surface_t *surface,
+ cairo_linear_pattern_t *pattern)
+{
+ double x0, y0, x1, y1;
+ cairo_matrix_t p2u;
+ cairo_status_t status;
+
+ p2u = pattern->base.base.matrix;
+ cairo_matrix_invert (&p2u);
+
+ x0 = _cairo_fixed_to_double (pattern->gradient.p1.x);
+ y0 = _cairo_fixed_to_double (pattern->gradient.p1.y);
+ cairo_matrix_transform_point (&p2u, &x0, &y0);
+ x1 = _cairo_fixed_to_double (pattern->gradient.p2.x);
+ y1 = _cairo_fixed_to_double (pattern->gradient.p2.y);
+ cairo_matrix_transform_point (&p2u, &x1, &y1);
+
+ _cairo_output_stream_printf (surface->stream,
+ "<< /PatternType 2\n"
+ " /Shading\n"
+ " << /ShadingType 2\n"
+ " /ColorSpace /DeviceRGB\n"
+ " /Coords [ %f %f %f %f ]\n"
+ " /Function\n",
+ x0, y0, x1, y1);
+
+ status = emit_pattern_stops (surface, &pattern->base);
+ if (status)
+ return status;
+
+ if (pattern->base.base.extend == CAIRO_EXTEND_PAD) {
+ _cairo_output_stream_printf (surface->stream,
+ " /Extend [ true true ]\n");
+ } else {
+ _cairo_output_stream_printf (surface->stream,
+ " /Extend [ false false ]\n");
+ }
+ _cairo_output_stream_printf (surface->stream,
+ " >>\n"
+ ">> matrix makepattern setpattern\n");
+
+ return CAIRO_STATUS_SUCCESS;
}
static void
@@ -1763,9 +1914,11 @@ emit_radial_pattern (cairo_ps_surface_t
/* XXX: NYI */
}
-static void
+static cairo_status_t
emit_pattern (cairo_ps_surface_t *surface, cairo_pattern_t *pattern)
{
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
/* FIXME: We should keep track of what pattern is currently set in
* the postscript file and only emit code if we're setting a
* different pattern. */
@@ -1780,13 +1933,15 @@ emit_pattern (cairo_ps_surface_t *surfac
break;
case CAIRO_PATTERN_TYPE_LINEAR:
- emit_linear_pattern (surface, (cairo_linear_pattern_t *) pattern);
+ status = emit_linear_pattern (surface, (cairo_linear_pattern_t *) pattern);
break;
case CAIRO_PATTERN_TYPE_RADIAL:
emit_radial_pattern (surface, (cairo_radial_pattern_t *) pattern);
break;
}
+
+ return status;
}
static cairo_int_status_t
@@ -1872,6 +2027,7 @@ _cairo_ps_surface_paint (void *abstrac
cairo_ps_surface_t *surface = abstract_surface;
cairo_output_stream_t *stream = surface->stream;
cairo_rectangle_int16_t extents, pattern_extents;
+ cairo_int_status_t status;
if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
return _analyze_operation (surface, op, source);
@@ -1893,7 +2049,9 @@ _cairo_ps_surface_paint (void *abstrac
_cairo_pattern_get_extents (source, &pattern_extents);
_cairo_rectangle_intersect (&extents, &pattern_extents);
- emit_pattern (surface, source);
+ status = emit_pattern (surface, source);
+ if (status)
+ return status;
_cairo_output_stream_printf (stream, "%d %d M\n",
extents.x, extents.y);
@@ -2032,7 +2190,9 @@ _cairo_ps_surface_stroke (void *abstra
}
}
- emit_pattern (surface, source);
+ status = emit_pattern (surface, source);
+ if (status)
+ return status;
_cairo_output_stream_printf (stream,
"gsave\n");
@@ -2099,7 +2259,9 @@ _cairo_ps_surface_fill (void *abstract_
_cairo_output_stream_printf (stream,
"%% _cairo_ps_surface_fill\n");
- emit_pattern (surface, source);
+ status = emit_pattern (surface, source);
+ if (status)
+ return status;
/* We're filling not stroking, so we pass CAIRO_LINE_CAP_ROUND. */
status = _cairo_ps_surface_emit_path (surface, stream, path,
@@ -2162,7 +2324,9 @@ _cairo_ps_surface_show_glyphs (void
num_glyphs_unsigned = num_glyphs;
- emit_pattern (surface, source);
+ status = emit_pattern (surface, source);
+ if (status)
+ return status;
glyph_ids = malloc (num_glyphs_unsigned*sizeof (cairo_ps_glyph_id_t));
if (glyph_ids == NULL)
return CAIRO_STATUS_NO_MEMORY;
--
1.4.3.4
More information about the cairo
mailing list