summaryrefslogtreecommitdiff
path: root/src/cairo-ps-surface.c
diff options
context:
space:
mode:
authorAdrian Johnson <ajohnson@redneon.com>2011-09-06 23:21:35 +0930
committerAdrian Johnson <ajohnson@redneon.com>2011-09-06 23:21:35 +0930
commita7ed3dd9143601c4e70696cece6d2b78f2c6053a (patch)
treefb09b57e0b106c084a5bb759d572198a194e8470 /src/cairo-ps-surface.c
parentc34d6adfccfe9dc7f76a0ab970914dd8f6e626b0 (diff)
ps: support stencil masks
When the mask is an A1 image (or can be reduced to A1) use the imagemask operator.
Diffstat (limited to 'src/cairo-ps-surface.c')
-rw-r--r--src/cairo-ps-surface.c295
1 files changed, 206 insertions, 89 deletions
diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c
index 5444b335..9c1d299d 100644
--- a/src/cairo-ps-surface.c
+++ b/src/cairo-ps-surface.c
@@ -1773,10 +1773,31 @@ pattern_supported (cairo_ps_surface_t *surface, const cairo_pattern_t *pattern)
}
}
+static cairo_bool_t
+mask_supported (cairo_ps_surface_t *surface, const cairo_pattern_t *mask)
+{
+ if (surface->ps_level == CAIRO_PS_LEVEL_2)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (mask->type == CAIRO_PATTERN_TYPE_SURFACE) {
+ cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) mask;
+ if (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_IMAGE) {
+ /* check if mask if opaque or bilevel alpha */
+ if (_cairo_ps_surface_analyze_surface_pattern_transparency (surface, surface_pattern) == CAIRO_STATUS_SUCCESS) {
+ surface->ps_level_used = CAIRO_PS_LEVEL_3;
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
static cairo_int_status_t
_cairo_ps_surface_analyze_operation (cairo_ps_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *pattern,
+ const cairo_pattern_t *mask,
const cairo_rectangle_int_t *extents)
{
double min_alpha;
@@ -1806,8 +1827,16 @@ _cairo_ps_surface_analyze_operation (cairo_ps_surface_t *surface,
}
}
- if (op == CAIRO_OPERATOR_SOURCE)
- return CAIRO_STATUS_SUCCESS;
+ if (op == CAIRO_OPERATOR_SOURCE) {
+ if (mask)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ else
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ /* Mask is only supported when the mask is an image with opaque or bilevel alpha. */
+ if (mask && !mask_supported (surface, mask))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
/* CAIRO_OPERATOR_OVER is only supported for opaque patterns. If
* the pattern contains transparency, we return
@@ -1839,9 +1868,10 @@ static cairo_bool_t
_cairo_ps_surface_operation_supported (cairo_ps_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *pattern,
+ const cairo_pattern_t *mask,
const cairo_rectangle_int_t *extents)
{
- return _cairo_ps_surface_analyze_operation (surface, op, pattern, extents) != CAIRO_INT_STATUS_UNSUPPORTED;
+ return _cairo_ps_surface_analyze_operation (surface, op, pattern, mask, extents) != CAIRO_INT_STATUS_UNSUPPORTED;
}
/* The "standard" implementation limit for PostScript string sizes is
@@ -2123,16 +2153,18 @@ static cairo_status_t
_cairo_ps_surface_emit_image (cairo_ps_surface_t *surface,
cairo_image_surface_t *image,
cairo_operator_t op,
- cairo_filter_t filter)
+ cairo_filter_t filter,
+ cairo_bool_t stencil_mask)
{
cairo_status_t status;
unsigned char *data;
unsigned long data_size;
cairo_image_surface_t *ps_image = image;
- int x, y, i;
+ int x, y, i, a;
cairo_image_transparency_t transparency;
cairo_bool_t use_mask;
- uint32_t *pixel;
+ uint32_t *pixel32;
+ uint8_t *pixel8;
int bit;
cairo_image_color_t color;
const char *interpolate;
@@ -2156,57 +2188,62 @@ _cairo_ps_surface_emit_image (cairo_ps_surface_t *surface,
break;
}
- transparency = _cairo_image_analyze_transparency (image);
+ if (stencil_mask) {
+ use_mask = FALSE;
+ color = CAIRO_IMAGE_IS_MONOCHROME;
+ } else {
+ transparency = _cairo_image_analyze_transparency (image);
- /* PostScript can not represent the alpha channel, so we blend the
- current image over a white (or black for CONTENT_COLOR
- surfaces) RGB surface to eliminate it. */
+ /* PostScript can not represent the alpha channel, so we blend the
+ current image over a white (or black for CONTENT_COLOR
+ surfaces) RGB surface to eliminate it. */
- if (op == CAIRO_OPERATOR_SOURCE ||
- transparency == CAIRO_IMAGE_HAS_ALPHA ||
- (transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA &&
- surface->ps_level == CAIRO_PS_LEVEL_2))
- {
- status = _cairo_ps_surface_flatten_image_transparency (surface,
- image,
- &ps_image);
- if (unlikely (status))
- return status;
+ if (op == CAIRO_OPERATOR_SOURCE ||
+ transparency == CAIRO_IMAGE_HAS_ALPHA ||
+ (transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA &&
+ surface->ps_level == CAIRO_PS_LEVEL_2))
+ {
+ status = _cairo_ps_surface_flatten_image_transparency (surface,
+ image,
+ &ps_image);
+ if (unlikely (status))
+ return status;
- use_mask = FALSE;
- } else if (transparency == CAIRO_IMAGE_IS_OPAQUE) {
- use_mask = FALSE;
- } else { /* transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA */
- use_mask = TRUE;
- }
+ use_mask = FALSE;
+ } else if (transparency == CAIRO_IMAGE_IS_OPAQUE) {
+ use_mask = FALSE;
+ } else { /* transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA */
+ use_mask = TRUE;
+ }
- color = _cairo_image_analyze_color (ps_image);
+ color = _cairo_image_analyze_color (ps_image);
+ }
/* Type 2 (mask and image interleaved) has the mask and image
* samples interleaved by row. The mask row is first, one bit per
* pixel with (bit 7 first). The row is padded to byte
* boundaries. The image data is 3 bytes per pixel RGB format. */
switch (color) {
- case CAIRO_IMAGE_IS_COLOR:
- case CAIRO_IMAGE_UNKNOWN_COLOR:
- if (use_mask)
- data_size = ps_image->height * ((ps_image->width + 7)/8 + 3*ps_image->width);
- else
- data_size = ps_image->height * ps_image->width * 3;
- break;
+ case CAIRO_IMAGE_IS_COLOR:
+ case CAIRO_IMAGE_UNKNOWN_COLOR:
+ if (use_mask)
+ data_size = ps_image->height * ((ps_image->width + 7)/8 + 3*ps_image->width);
+ else
+ data_size = ps_image->height * ps_image->width * 3;
+ break;
- case CAIRO_IMAGE_IS_GRAYSCALE:
- if (use_mask)
- data_size = ps_image->height * ((ps_image->width + 7)/8 + ps_image->width);
- else
- data_size = ps_image->height * ps_image->width;
- break;
- case CAIRO_IMAGE_IS_MONOCHROME:
- if (use_mask)
- data_size = ps_image->height * ((ps_image->width + 7)/8) * 2;
- else
- data_size = ps_image->height * ((ps_image->width + 7)/8);
- break;
+ case CAIRO_IMAGE_IS_GRAYSCALE:
+ if (use_mask)
+ data_size = ps_image->height * ((ps_image->width + 7)/8 + ps_image->width);
+ else
+ data_size = ps_image->height * ps_image->width;
+ break;
+ case CAIRO_IMAGE_IS_MONOCHROME:
+ if (use_mask)
+ data_size = ps_image->height * ((ps_image->width + 7)/8) * 2;
+ else
+ data_size = ps_image->height * ((ps_image->width + 7)/8);
+ break;
}
data = malloc (data_size);
if (unlikely (data == NULL)) {
@@ -2216,45 +2253,70 @@ _cairo_ps_surface_emit_image (cairo_ps_surface_t *surface,
i = 0;
for (y = 0; y < ps_image->height; y++) {
- if (use_mask) {
+ if (stencil_mask || use_mask) {
/* mask row */
- pixel = (uint32_t *) (ps_image->data + y * ps_image->stride);
- bit = 7;
- for (x = 0; x < ps_image->width; x++, pixel++) {
- if (bit == 7)
- data[i] = 0;
- if (*pixel & 0xff000000)
- data[i] |= (1 << bit);
- bit--;
- if (bit < 0) {
- bit = 7;
- i++;
+ if (ps_image->format == CAIRO_FORMAT_A1) {
+ pixel8 = (uint8_t *) (ps_image->data + y * ps_image->stride);
+
+ for (x = 0; x < (ps_image->width + 7) / 8; x++, pixel8++) {
+ a = *pixel8;
+ a = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (a);
+ data[i++] = a;
+ }
+ } else {
+ pixel8 = (uint8_t *) (ps_image->data + y * ps_image->stride);
+ pixel32 = (uint32_t *) (ps_image->data + y * ps_image->stride);
+ bit = 7;
+ for (x = 0; x < ps_image->width; x++) {
+ if (ps_image->format == CAIRO_FORMAT_ARGB32) {
+ a = (*pixel32 & 0xff000000) >> 24;
+ pixel32++;
+ } else {
+ a = *pixel8;
+ pixel8++;
+ }
+
+ if (transparency == CAIRO_IMAGE_HAS_ALPHA) {
+ data[i++] = a;
+ } else { /* transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA or CAIRO_IMAGE_IS_OPAQUE */
+ if (bit == 7)
+ data[i] = 0;
+ if (a != 0)
+ data[i] |= (1 << bit);
+ bit--;
+ if (bit < 0) {
+ bit = 7;
+ i++;
+ }
+ }
}
+ if (bit != 7)
+ i++;
}
- if (bit != 7)
- i++;
}
+ if (stencil_mask)
+ continue;
/* image row*/
- pixel = (uint32_t *) (ps_image->data + y * ps_image->stride);
+ pixel32 = (uint32_t *) (ps_image->data + y * ps_image->stride);
bit = 7;
- for (x = 0; x < ps_image->width; x++, pixel++) {
+ for (x = 0; x < ps_image->width; x++, pixel32++) {
int r, g, b;
if (ps_image->format == CAIRO_FORMAT_ARGB32) {
/* At this point ARGB32 images are either opaque or
* bilevel alpha so we don't need to unpremultiply. */
- if (((*pixel & 0xff000000) >> 24) == 0) {
+ if (((*pixel32 & 0xff000000) >> 24) == 0) {
r = g = b = 0;
} else {
- r = (*pixel & 0x00ff0000) >> 16;
- g = (*pixel & 0x0000ff00) >> 8;
- b = (*pixel & 0x000000ff) >> 0;
+ r = (*pixel32 & 0x00ff0000) >> 16;
+ g = (*pixel32 & 0x0000ff00) >> 8;
+ b = (*pixel32 & 0x000000ff) >> 0;
}
} else if (ps_image->format == CAIRO_FORMAT_RGB24) {
- r = (*pixel & 0x00ff0000) >> 16;
- g = (*pixel & 0x0000ff00) >> 8;
- b = (*pixel & 0x000000ff) >> 0;
+ r = (*pixel32 & 0x00ff0000) >> 16;
+ g = (*pixel32 & 0x0000ff00) >> 8;
+ b = (*pixel32 & 0x000000ff) >> 0;
} else {
r = g = b = 0;
}
@@ -2374,8 +2436,12 @@ _cairo_ps_surface_emit_image (cairo_ps_surface_t *surface,
interpolate,
ps_image->height);
} else {
+ if (!stencil_mask) {
+ _cairo_output_stream_printf (surface->stream,
+ "%s setcolorspace\n",
+ color == CAIRO_IMAGE_IS_COLOR ? "/DeviceRGB" : "/DeviceGray");
+ }
_cairo_output_stream_printf (surface->stream,
- "%s setcolorspace\n"
"8 dict dup begin\n"
" /ImageType 1 def\n"
" /Width %d def\n"
@@ -2383,7 +2449,6 @@ _cairo_ps_surface_emit_image (cairo_ps_surface_t *surface,
" /Interpolate %s def\n"
" /BitsPerComponent %d def\n"
" /Decode [ %s ] def\n",
- color == CAIRO_IMAGE_IS_COLOR ? "/DeviceRGB" : "/DeviceGray",
ps_image->width,
ps_image->height,
interpolate,
@@ -2408,9 +2473,10 @@ _cairo_ps_surface_emit_image (cairo_ps_surface_t *surface,
" /Interpolate %s def\n"
" /ImageMatrix [ 1 0 0 -1 0 %d ] def\n"
"end\n"
- "image\n",
+ "%s\n",
interpolate,
- ps_image->height);
+ ps_image->height,
+ stencil_mask ? "imagemask" : "image");
}
if (!surface->use_string_datasource) {
@@ -2848,7 +2914,8 @@ _cairo_ps_surface_emit_surface (cairo_ps_surface_t *surface,
cairo_surface_pattern_t *pattern,
cairo_operator_t op,
int width,
- int height)
+ int height,
+ cairo_bool_t stencil_mask)
{
cairo_int_status_t status;
@@ -2870,7 +2937,7 @@ _cairo_ps_surface_emit_surface (cairo_ps_surface_t *surface,
}
status = _cairo_ps_surface_emit_image (surface, surface->image,
- op, pattern->base.filter);
+ op, pattern->base.filter, stencil_mask);
}
return status;
@@ -2926,7 +2993,8 @@ static cairo_status_t
_cairo_ps_surface_paint_surface (cairo_ps_surface_t *surface,
cairo_surface_pattern_t *pattern,
cairo_rectangle_int_t *extents,
- cairo_operator_t op)
+ cairo_operator_t op,
+ cairo_bool_t stencil_mask)
{
cairo_status_t status;
int width, height;
@@ -2996,7 +3064,7 @@ _cairo_ps_surface_paint_surface (cairo_ps_surface_t *surface,
ps_p2d.x0, ps_p2d.y0);
}
- status = _cairo_ps_surface_emit_surface (surface, pattern, op, width, height);
+ status = _cairo_ps_surface_emit_surface (surface, pattern, op, width, height, stencil_mask);
_cairo_ps_surface_release_surface (surface, pattern);
return status;
@@ -3088,7 +3156,7 @@ _cairo_ps_surface_emit_surface_pattern (cairo_ps_surface_t *surface,
xstep, ystep);
}
status = _cairo_ps_surface_emit_surface (surface, pattern, op,
- pattern_width, pattern_height);
+ pattern_width, pattern_height, FALSE);
if (unlikely (status))
return status;
@@ -3715,9 +3783,9 @@ _cairo_ps_surface_paint (void *abstract_surface,
return status;
if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
- return _cairo_ps_surface_analyze_operation (surface, op, source, &extents.bounded);
+ return _cairo_ps_surface_analyze_operation (surface, op, source, NULL, &extents.bounded);
- assert (_cairo_ps_surface_operation_supported (surface, op, source, &extents.bounded));
+ assert (_cairo_ps_surface_operation_supported (surface, op, source, NULL, &extents.bounded));
#if DEBUG_PS
_cairo_output_stream_printf (stream,
@@ -3739,7 +3807,7 @@ _cairo_ps_surface_paint (void *abstract_surface,
_cairo_output_stream_printf (stream, "q\n");
status = _cairo_ps_surface_paint_surface (surface,
(cairo_surface_pattern_t *) source,
- &extents.bounded, op);
+ &extents.bounded, op, FALSE);
if (unlikely (status))
return status;
@@ -3757,6 +3825,55 @@ _cairo_ps_surface_paint (void *abstract_surface,
}
static cairo_int_status_t
+_cairo_ps_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_ps_surface_t *surface = abstract_surface;
+ cairo_output_stream_t *stream = surface->stream;
+ cairo_composite_rectangles_t extents;
+ cairo_rectangle_int_t unbounded;
+ cairo_status_t status;
+
+ _cairo_ps_surface_get_extents (surface, &unbounded);
+ status = _cairo_composite_rectangles_init_for_mask (&extents, &unbounded,
+ op, source, mask, clip);
+ if (unlikely (status))
+ return status;
+
+ if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
+ return _cairo_ps_surface_analyze_operation (surface, op, source, mask, &extents.bounded);
+
+ assert (_cairo_ps_surface_operation_supported (surface, op, source, mask, &extents.bounded));
+
+#if DEBUG_PS
+ _cairo_output_stream_printf (stream,
+ "%% _cairo_ps_surface_mask\n");
+#endif
+
+ status = _cairo_ps_surface_set_clip (surface, &extents);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_ps_surface_emit_pattern (surface, source, &extents.bounded, op);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (stream, "q\n");
+ status = _cairo_ps_surface_paint_surface (surface,
+ (cairo_surface_pattern_t *) mask,
+ &extents.bounded, op, TRUE);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (stream, "Q\n");
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
_cairo_ps_surface_stroke (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
@@ -3796,9 +3913,9 @@ _cairo_ps_surface_stroke (void *abstract_surface,
if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
- return _cairo_ps_surface_analyze_operation (surface, op, source, &extents.bounded);
+ return _cairo_ps_surface_analyze_operation (surface, op, source, NULL, &extents.bounded);
- assert (_cairo_ps_surface_operation_supported (surface, op, source, &extents.bounded));
+ assert (_cairo_ps_surface_operation_supported (surface, op, source, NULL, &extents.bounded));
#if DEBUG_PS
_cairo_output_stream_printf (surface->stream,
@@ -3854,9 +3971,9 @@ _cairo_ps_surface_fill (void *abstract_surface,
}
if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
- return _cairo_ps_surface_analyze_operation (surface, op, source, &extents.bounded);
+ return _cairo_ps_surface_analyze_operation (surface, op, source, NULL, &extents.bounded);
- assert (_cairo_ps_surface_operation_supported (surface, op, source, &extents.bounded));
+ assert (_cairo_ps_surface_operation_supported (surface, op, source, NULL, &extents.bounded));
#if DEBUG_PS
_cairo_output_stream_printf (surface->stream,
@@ -3885,7 +4002,7 @@ _cairo_ps_surface_fill (void *abstract_surface,
status = _cairo_ps_surface_paint_surface (surface,
(cairo_surface_pattern_t *) source,
- &extents.bounded, op);
+ &extents.bounded, op, FALSE);
if (unlikely (status))
return status;
@@ -3945,9 +4062,9 @@ _cairo_ps_surface_show_text_glyphs (void *abstract_surface,
}
if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
- return _cairo_ps_surface_analyze_operation (surface, op, source, &extents.bounded);
+ return _cairo_ps_surface_analyze_operation (surface, op, source, NULL, &extents.bounded);
- assert (_cairo_ps_surface_operation_supported (surface, op, source, &extents.bounded));
+ assert (_cairo_ps_surface_operation_supported (surface, op, source, NULL, &extents.bounded));
#if DEBUG_PS
_cairo_output_stream_printf (surface->stream,
@@ -4119,7 +4236,7 @@ static const cairo_surface_backend_t cairo_ps_surface_backend = {
/* Here are the drawing functions */
_cairo_ps_surface_paint, /* paint */
- NULL, /* mask */
+ _cairo_ps_surface_mask,
_cairo_ps_surface_stroke,
_cairo_ps_surface_fill,
NULL, /* show_glyphs */