Add support for PDF meta-surface patterns

This commit is contained in:
Adrian Johnson 2007-09-05 22:26:16 +09:30
parent b85032584b
commit d24f019101
6 changed files with 316 additions and 91 deletions

View file

@ -284,16 +284,7 @@ static cairo_boilerplate_target_t targets[] =
_cairo_boilerplate_pdf_surface_write_to_png,
_cairo_boilerplate_pdf_cleanup,
NULL, TRUE },
/* XXX: We expect type image here only due to a limitation in
* the current PDF/meta-surface code. A PDF surface is
* "naturally" COLOR_ALPHA, so the COLOR-only variant goes
* through create_similar in _cairo_boilerplate_pdf_create_surface which results
* in the similar surface being used as a source. We do not yet
* have source support for PDF/meta-surfaces, so the
* create_similar path for all paginated surfaces currently
* returns an image surface.*/
{ "pdf", CAIRO_SURFACE_TYPE_IMAGE, CAIRO_CONTENT_COLOR, 0,
{ "pdf", CAIRO_INTERNAL_SURFACE_TYPE_META, CAIRO_CONTENT_COLOR, 0,
_cairo_boilerplate_pdf_create_surface,
_cairo_boilerplate_pdf_surface_write_to_png,
_cairo_boilerplate_pdf_cleanup,

View file

@ -39,6 +39,7 @@
#include "cairo-analysis-surface-private.h"
#include "cairo-paginated-private.h"
#include "cairo-region-private.h"
#include "cairo-meta-surface-private.h"
typedef struct {
cairo_surface_t base;
@ -56,6 +57,33 @@ typedef struct {
} cairo_analysis_surface_t;
static cairo_int_status_t
_cairo_analysis_surface_analyze_meta_surface_pattern (cairo_analysis_surface_t *surface,
cairo_pattern_t *pattern)
{
cairo_surface_pattern_t *surface_pattern;
cairo_surface_t *meta_surface;
cairo_surface_t *analysis;
cairo_status_t status;
assert (pattern->type == CAIRO_PATTERN_TYPE_SURFACE);
surface_pattern = (cairo_surface_pattern_t *) pattern;
assert (_cairo_surface_is_meta (surface_pattern->surface));
meta_surface = surface_pattern->surface;
analysis = _cairo_analysis_surface_create (surface->target,
surface->width, surface->height);
if (analysis == NULL)
return CAIRO_STATUS_NO_MEMORY;
status = _cairo_meta_surface_replay_analyze_meta_pattern (meta_surface, analysis);
if (status == CAIRO_STATUS_SUCCESS)
status = analysis->status;
cairo_surface_destroy (analysis);
return status;
}
static cairo_int_status_t
_cairo_analysis_surface_add_operation (cairo_analysis_surface_t *surface,
cairo_rectangle_int_t *rect,
@ -186,6 +214,10 @@ _cairo_analysis_surface_paint (void *abstract_surface,
backend_status = (*surface->target->backend->paint) (surface->target, op,
source);
if (backend_status == CAIRO_INT_STATUS_ANALYZE_META_SURFACE_PATTERN)
backend_status = _cairo_analysis_surface_analyze_meta_surface_pattern (surface,
source);
status = _cairo_surface_get_extents (&surface->base, &extents);
if (status)
return status;
@ -222,6 +254,28 @@ _cairo_analysis_surface_mask (void *abstract_surface,
backend_status = (*surface->target->backend->mask) (surface->target, op,
source, mask);
if (backend_status == CAIRO_INT_STATUS_ANALYZE_META_SURFACE_PATTERN) {
if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) source;
if (_cairo_surface_is_meta (surface_pattern->surface))
backend_status = _cairo_analysis_surface_analyze_meta_surface_pattern (surface,
source);
if (backend_status != CAIRO_STATUS_SUCCESS &&
backend_status != CAIRO_INT_STATUS_IMAGE_FALLBACK)
return backend_status;
}
if (mask->type == CAIRO_PATTERN_TYPE_SURFACE) {
cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) mask;
if (_cairo_surface_is_meta (surface_pattern->surface))
backend_status = _cairo_analysis_surface_analyze_meta_surface_pattern (surface,
mask);
if (backend_status != CAIRO_STATUS_SUCCESS &&
backend_status != CAIRO_INT_STATUS_IMAGE_FALLBACK)
return backend_status;
}
}
status = _cairo_surface_get_extents (&surface->base, &extents);
if (status)
return status;
@ -273,6 +327,10 @@ _cairo_analysis_surface_stroke (void *abstract_surface,
ctm, ctm_inverse,
tolerance, antialias);
if (backend_status == CAIRO_INT_STATUS_ANALYZE_META_SURFACE_PATTERN)
backend_status = _cairo_analysis_surface_analyze_meta_surface_pattern (surface,
source);
status = _cairo_surface_get_extents (&surface->base, &extents);
if (status)
return status;
@ -342,6 +400,10 @@ _cairo_analysis_surface_fill (void *abstract_surface,
source, path, fill_rule,
tolerance, antialias);
if (backend_status == CAIRO_INT_STATUS_ANALYZE_META_SURFACE_PATTERN)
backend_status = _cairo_analysis_surface_analyze_meta_surface_pattern (surface,
source);
status = _cairo_surface_get_extents (&surface->base, &extents);
if (status)
return status;
@ -409,6 +471,10 @@ _cairo_analysis_surface_show_glyphs (void *abstract_surface,
glyphs, num_glyphs,
scaled_font);
if (backend_status == CAIRO_INT_STATUS_ANALYZE_META_SURFACE_PATTERN)
backend_status = _cairo_analysis_surface_analyze_meta_surface_pattern (surface,
source);
status = _cairo_surface_get_extents (&surface->base, &extents);
if (status)
return status;

View file

@ -163,6 +163,10 @@ cairo_private cairo_status_t
_cairo_meta_surface_replay (cairo_surface_t *surface,
cairo_surface_t *target);
cairo_private cairo_status_t
_cairo_meta_surface_replay_analyze_meta_pattern (cairo_surface_t *surface,
cairo_surface_t *target);
cairo_private cairo_status_t
_cairo_meta_surface_replay_and_create_regions (cairo_surface_t *surface,
cairo_surface_t *target);

View file

@ -59,6 +59,12 @@
#include "cairo-meta-surface-private.h"
#include "cairo-clip-private.h"
typedef enum {
CAIRO_META_REPLAY,
CAIRO_META_CREATE_REGIONS,
CAIRO_META_ANALYZE_META_PATTERN
} cairo_meta_replay_type_t;
static const cairo_surface_backend_t cairo_meta_surface_backend;
/* Currently all meta surfaces do have a size which should be passed
@ -650,7 +656,7 @@ _cairo_command_get_path (cairo_command_t *command)
static cairo_status_t
_cairo_meta_surface_replay_internal (cairo_surface_t *surface,
cairo_surface_t *target,
cairo_bool_t create_regions,
cairo_meta_replay_type_t type,
cairo_meta_region_type_t region)
{
cairo_meta_surface_t *meta;
@ -675,7 +681,7 @@ _cairo_meta_surface_replay_internal (cairo_surface_t *surface,
for (i = meta->replay_start_idx; i < num_elements; i++) {
command = elements[i];
if (!create_regions && region != CAIRO_META_REGION_ALL) {
if (type == CAIRO_META_REPLAY && region != CAIRO_META_REGION_ALL) {
if (command->header.region != region)
continue;
}
@ -834,7 +840,7 @@ _cairo_meta_surface_replay_internal (cairo_surface_t *surface,
if (dev_path == &path_copy)
_cairo_path_fixed_fini (&path_copy);
if (create_regions) {
if (type == CAIRO_META_CREATE_REGIONS) {
if (status == CAIRO_STATUS_SUCCESS) {
command->header.region = CAIRO_META_REGION_NATIVE;
} else if (status == CAIRO_INT_STATUS_IMAGE_FALLBACK) {
@ -842,6 +848,12 @@ _cairo_meta_surface_replay_internal (cairo_surface_t *surface,
status = CAIRO_STATUS_SUCCESS;
}
}
if (type == CAIRO_META_ANALYZE_META_PATTERN) {
if (status == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY)
status = CAIRO_STATUS_SUCCESS;
}
if (status)
break;
}
@ -855,7 +867,20 @@ cairo_status_t
_cairo_meta_surface_replay (cairo_surface_t *surface,
cairo_surface_t *target)
{
return _cairo_meta_surface_replay_internal (surface, target, FALSE, CAIRO_META_REGION_ALL);
return _cairo_meta_surface_replay_internal (surface,
target,
CAIRO_META_REPLAY,
CAIRO_META_REGION_ALL);
}
cairo_status_t
_cairo_meta_surface_replay_analyze_meta_pattern (cairo_surface_t *surface,
cairo_surface_t *target)
{
return _cairo_meta_surface_replay_internal (surface,
target,
CAIRO_META_ANALYZE_META_PATTERN,
CAIRO_META_REGION_ALL);
}
/* Replay meta to surface. When the return status of each operation is
@ -868,7 +893,10 @@ cairo_status_t
_cairo_meta_surface_replay_and_create_regions (cairo_surface_t *surface,
cairo_surface_t *target)
{
return _cairo_meta_surface_replay_internal (surface, target, TRUE, CAIRO_META_REGION_ALL);
return _cairo_meta_surface_replay_internal (surface,
target,
CAIRO_META_CREATE_REGIONS,
CAIRO_META_REGION_ALL);
}
cairo_status_t
@ -876,5 +904,8 @@ _cairo_meta_surface_replay_region (cairo_surface_t *surface,
cairo_surface_t *target,
cairo_meta_region_type_t region)
{
return _cairo_meta_surface_replay_internal (surface, target, FALSE, region);
return _cairo_meta_surface_replay_internal (surface,
target,
CAIRO_META_REPLAY,
region);
}

View file

@ -45,6 +45,7 @@
#include "cairo-paginated-private.h"
#include "cairo-path-fixed-private.h"
#include "cairo-output-stream-private.h"
#include "cairo-meta-surface-private.h"
#include <time.h>
#include <zlib.h>
@ -235,6 +236,9 @@ _cairo_pdf_surface_open_stream (cairo_pdf_surface_t *surface,
static cairo_status_t
_cairo_pdf_surface_close_stream (cairo_pdf_surface_t *surface);
static cairo_status_t
_cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface);
static void
_cairo_pdf_surface_write_pages (cairo_pdf_surface_t *surface);
@ -1036,6 +1040,15 @@ _cairo_pdf_group_element_array_finish (cairo_array_t *array)
}
}
static cairo_surface_t *
_cairo_pdf_surface_create_similar (void *abstract_surface,
cairo_content_t content,
int width,
int height)
{
return _cairo_meta_surface_create (content, width, height);
}
static cairo_status_t
_cairo_pdf_surface_finish (void *abstract_surface)
{
@ -1308,14 +1321,120 @@ _cairo_pdf_surface_emit_image (cairo_pdf_surface_t *surface,
_cairo_output_stream_printf (surface->output, "\r\n");
status = _cairo_pdf_surface_close_stream (surface);
CLEANUP_COMPRESSED:
CLEANUP_COMPRESSED:
free (compressed);
CLEANUP_RGB:
CLEANUP_RGB:
free (rgb);
CLEANUP:
CLEANUP:
return status;
}
static cairo_status_t
_cairo_pdf_surface_emit_image_surface (cairo_pdf_surface_t *surface,
cairo_surface_pattern_t *pattern,
cairo_pdf_resource_t *resource,
int *width,
int *height)
{
cairo_surface_t *pat_surface;
cairo_surface_attributes_t pat_attr;
cairo_image_surface_t *image;
void *image_extra;
cairo_status_t status = CAIRO_STATUS_SUCCESS;
status = _cairo_pattern_acquire_surface ((cairo_pattern_t *)pattern,
(cairo_surface_t *)surface,
0, 0, -1, -1,
&pat_surface, &pat_attr);
if (status)
return status;
status = _cairo_surface_acquire_source_image (pat_surface, &image, &image_extra);
if (status)
goto BAIL2;
status = _cairo_pdf_surface_emit_image (surface, image, resource);
if (status)
goto BAIL;
*width = image->width;
*height = image->height;
BAIL:
_cairo_surface_release_source_image (pat_surface, image, image_extra);
BAIL2:
_cairo_pattern_release_surface ((cairo_pattern_t *)pattern, pat_surface, &pat_attr);
return status;
}
static cairo_status_t
_cairo_pdf_surface_emit_meta_surface (cairo_pdf_surface_t *surface,
cairo_surface_t *meta_surface,
cairo_pdf_resource_t *resource)
{
cairo_array_t group;
cairo_array_t *old_group;
double old_width, old_height;
cairo_matrix_t old_cairo_to_pdf;
cairo_rectangle_int16_t meta_extents;
cairo_status_t status;
int alpha = 0;
status = _cairo_surface_get_extents (meta_surface, &meta_extents);
if (status)
return status;
_cairo_pdf_surface_resume_content_stream (surface);
_cairo_pdf_surface_stop_content_stream (surface);
_cairo_array_init (&group, sizeof (cairo_pdf_group_element_t));
old_group = surface->current_group;
old_width = surface->width;
old_height = surface->height;
old_cairo_to_pdf = surface->cairo_to_pdf;
surface->current_group = &group;
surface->width = meta_extents.width;
surface->height = meta_extents.height;
cairo_matrix_init (&surface->cairo_to_pdf, 1, 0, 0, -1, 0, surface->height);
_cairo_pdf_surface_start_content_stream (surface);
if (cairo_surface_get_content (meta_surface) == CAIRO_CONTENT_COLOR) {
status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha);
if (status)
return status;
_cairo_output_stream_printf (surface->output,
"q /a%d gs 0 0 0 rg 0 0 %f %f re f Q\r\n",
alpha,
surface->width,
surface->height);
}
status = _cairo_meta_surface_replay (meta_surface, &surface->base);
if (status)
return status;
_cairo_pdf_surface_stop_content_stream (surface);
_cairo_pdf_surface_open_group (surface);
_cairo_pdf_surface_write_group_list (surface, &group);
_cairo_pdf_surface_close_group (surface, resource);
surface->current_group = old_group;
surface->width = old_width;
surface->height = old_height;
surface->cairo_to_pdf = old_cairo_to_pdf;
_cairo_pdf_surface_start_content_stream (surface);
_cairo_pdf_group_element_array_finish (&group);
_cairo_array_fini (&group);
_cairo_pdf_surface_pause_content_stream (surface);
return 0;
}
static void
_cairo_pdf_surface_emit_solid_pattern (cairo_pdf_surface_t *surface,
cairo_solid_pattern_t *pattern)
@ -1331,81 +1450,84 @@ _cairo_pdf_surface_emit_solid_pattern (cairo_pdf_surface_t *surface,
static cairo_status_t
_cairo_pdf_surface_emit_surface_pattern (cairo_pdf_surface_t *surface,
cairo_surface_pattern_t *pattern)
cairo_surface_pattern_t *pattern)
{
cairo_pdf_resource_t stream;
cairo_surface_t *pat_surface;
cairo_surface_attributes_t pat_attr;
cairo_image_surface_t *image;
void *image_extra;
cairo_status_t status = CAIRO_STATUS_SUCCESS;
cairo_pdf_resource_t image_resource = {0}; /* squelch bogus compiler warning */
cairo_pdf_resource_t pattern_resource = {0}; /* squelch bogus compiler warning */
cairo_matrix_t cairo_p2d, pdf_p2d;
cairo_extend_t extend = cairo_pattern_get_extend (&pattern->base);
double xstep, ystep;
cairo_rectangle_int_t surface_extents;
/* XXX: Should do something clever here for PDF source surfaces ? */
cairo_rectangle_int16_t surface_extents;
int pattern_width = 0; /* squelch bogus compiler warning */
int pattern_height = 0; /* squelch bogus compiler warning */
_cairo_pdf_surface_pause_content_stream (surface);
status = _cairo_pattern_acquire_surface ((cairo_pattern_t *)pattern,
(cairo_surface_t *)surface,
0, 0, -1, -1,
&pat_surface, &pat_attr);
if (_cairo_surface_is_meta (pattern->surface)) {
cairo_surface_t *meta_surface = pattern->surface;
cairo_rectangle_int16_t pattern_extents;
status = _cairo_pdf_surface_emit_meta_surface (surface,
meta_surface,
&pattern_resource);
status = _cairo_surface_get_extents (meta_surface, &pattern_extents);
if (status)
return status;
pattern_width = pattern_extents.width;
pattern_height = pattern_extents.height;
} else {
status = _cairo_pdf_surface_emit_image_surface (surface,
pattern,
&pattern_resource,
&pattern_width,
&pattern_height);
}
if (status)
return status;
status = _cairo_surface_acquire_source_image (pat_surface, &image, &image_extra);
if (status)
goto BAIL2;
status = _cairo_pdf_surface_emit_image (surface, image, &image_resource);
if (status)
goto BAIL;
status = _cairo_surface_get_extents (&surface->base, &surface_extents);
if (status)
goto BAIL;
return status;
switch (extend) {
/* We implement EXTEND_PAD like EXTEND_NONE for now */
/* We implement EXTEND_PAD like EXTEND_NONE for now */
case CAIRO_EXTEND_PAD:
case CAIRO_EXTEND_NONE:
{
/* In PS/PDF, (as far as I can tell), all patterns are
* repeating. So we support cairo's EXTEND_NONE semantics
* by setting the repeat step size to a size large enough
* to guarantee that no more than a single occurrence will
* be visible.
*
* First, map the surface extents into pattern space (since
* xstep and ystep are in pattern space). Then use an upper
* bound on the length of the diagonal of the pattern image
* and the surface as repeat size. This guarantees to never
* repeat visibly.
*/
double x1 = 0.0, y1 = 0.0;
double x2 = surface->width, y2 = surface->height;
_cairo_matrix_transform_bounding_box (&pattern->base.matrix,
&x1, &y1, &x2, &y2,
NULL);
{
/* In PS/PDF, (as far as I can tell), all patterns are
* repeating. So we support cairo's EXTEND_NONE semantics
* by setting the repeat step size to a size large enough
* to guarantee that no more than a single occurrence will
* be visible.
*
* First, map the surface extents into pattern space (since
* xstep and ystep are in pattern space). Then use an upper
* bound on the length of the diagonal of the pattern image
* and the surface as repeat size. This guarantees to never
* repeat visibly.
*/
double x1 = 0.0, y1 = 0.0;
double x2 = surface->width, y2 = surface->height;
_cairo_matrix_transform_bounding_box (&pattern->base.matrix,
&x1, &y1, &x2, &y2,
NULL);
/* Rather than computing precise bounds of the union, just
* add the surface extents unconditionally. We only
* required an answer that's large enough, we don't really
* care if it's not as tight as possible.*/
xstep = ystep = ceil ((x2 - x1) + (y2 - y1) +
image->width + image->height);
}
break;
/* Rather than computing precise bounds of the union, just
* add the surface extents unconditionally. We only
* required an answer that's large enough, we don't really
* care if it's not as tight as possible.*/
xstep = ystep = ceil ((x2 - x1) + (y2 - y1) +
pattern_width + pattern_height);
}
break;
case CAIRO_EXTEND_REPEAT:
case CAIRO_EXTEND_REFLECT:
xstep = image->width;
ystep = image->height;
xstep = pattern_width;
ystep = pattern_height;
break;
/* All the rest (if any) should have been analyzed away, so this
* case should be unreachable. */
/* All the rest (if any) should have been analyzed away, so this
* case should be unreachable. */
default:
ASSERT_NOT_REACHED;
xstep = 0;
@ -1448,11 +1570,11 @@ _cairo_pdf_surface_emit_surface_pattern (cairo_pdf_surface_t *surface,
cairo_matrix_translate (&pdf_p2d, 0.0, surface_extents.height);
cairo_matrix_scale (&pdf_p2d, 1.0, -1.0);
cairo_matrix_multiply (&pdf_p2d, &cairo_p2d, &pdf_p2d);
cairo_matrix_translate (&pdf_p2d, 0.0, image->height);
cairo_matrix_translate (&pdf_p2d, 0.0, pattern_height);
cairo_matrix_scale (&pdf_p2d, 1.0, -1.0);
stream = _cairo_pdf_surface_open_stream (surface,
FALSE,
FALSE,
" /BBox [0 0 %d %d]\r\n"
" /XStep %f\r\n"
" /YStep %f\r\n"
@ -1460,23 +1582,29 @@ _cairo_pdf_surface_emit_surface_pattern (cairo_pdf_surface_t *surface,
" /TilingType 1\r\n"
" /PaintType 1\r\n"
" /Matrix [ %f %f %f %f %f %f ]\r\n"
" /Resources << /XObject << /res%d %d 0 R >> >>\r\n",
image->width, image->height,
" /Resources << /XObject << /x%d %d 0 R >> >>\r\n",
pattern_width, pattern_height,
xstep, ystep,
pdf_p2d.xx, pdf_p2d.yx,
pdf_p2d.xy, pdf_p2d.yy,
pdf_p2d.x0, pdf_p2d.y0,
image_resource.id,
image_resource.id);
pattern_resource.id,
pattern_resource.id);
_cairo_output_stream_printf (surface->output,
"q %d 0 0 %d 0 0 cm /res%d Do Q\r\n",
image->width, image->height,
image_resource.id);
if (_cairo_surface_is_meta (pattern->surface)) {
_cairo_output_stream_printf (surface->output,
"/x%d Do\r\n",
pattern_resource.id);
} else {
_cairo_output_stream_printf (surface->output,
"q %d 0 0 %d 0 0 cm /x%d Do Q\r\n",
pattern_width, pattern_height,
pattern_resource.id);
}
status = _cairo_pdf_surface_close_stream (surface);
if (status)
goto BAIL;
return status;
_cairo_pdf_surface_resume_content_stream (surface);
@ -1485,11 +1613,6 @@ _cairo_pdf_surface_emit_surface_pattern (cairo_pdf_surface_t *surface,
surface->emitted_pattern.pattern = stream;
surface->emitted_pattern.alpha = 1.0;
BAIL:
_cairo_surface_release_source_image (pat_surface, image, image_extra);
BAIL2:
_cairo_pattern_release_surface ((cairo_pattern_t *)pattern, pat_surface, &pat_attr);
return status;
}
@ -3436,6 +3559,9 @@ _surface_pattern_supported (cairo_surface_pattern_t *pattern)
{
cairo_extend_t extend;
if (_cairo_surface_is_meta (pattern->surface))
return TRUE;
if (pattern->surface->backend->acquire_source_image == NULL)
return FALSE;
@ -3524,8 +3650,14 @@ _cairo_pdf_surface_analyze_operation (cairo_pdf_surface_t *surface,
if (! _pattern_supported (pattern))
return CAIRO_INT_STATUS_UNSUPPORTED;
if (op == CAIRO_OPERATOR_OVER)
if (op == CAIRO_OPERATOR_OVER) {
if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern;
if ( _cairo_surface_is_meta (surface_pattern->surface))
return CAIRO_INT_STATUS_ANALYZE_META_SURFACE_PATTERN;
}
return CAIRO_STATUS_SUCCESS;
}
/* The SOURCE operator is only supported for the fallback images. */
if (op == CAIRO_OPERATOR_SOURCE &&
@ -4089,7 +4221,7 @@ _cairo_pdf_surface_set_paginated_mode (void *abstract_surface,
static const cairo_surface_backend_t cairo_pdf_surface_backend = {
CAIRO_SURFACE_TYPE_PDF,
NULL, /* create_similar */
_cairo_pdf_surface_create_similar,
_cairo_pdf_surface_finish,
NULL, /* acquire_source_image */
NULL, /* release_source_image */

View file

@ -136,7 +136,8 @@ typedef enum _cairo_int_status {
CAIRO_INT_STATUS_NOTHING_TO_DO,
CAIRO_INT_STATUS_CACHE_EMPTY,
CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY,
CAIRO_INT_STATUS_IMAGE_FALLBACK
CAIRO_INT_STATUS_IMAGE_FALLBACK,
CAIRO_INT_STATUS_ANALYZE_META_SURFACE_PATTERN,
} cairo_int_status_t;
typedef enum _cairo_internal_surface_type {