[svg] Use finer-grained fallbacks for SVG 1.2
The use of fine-grained fallbacks requires the native support of the SOURCE operator applied to an image on the target surface. SVG 1.2 introduces the "comp-op:src" mode fulfilling this criteria - so we can enable fine-grained fallbacks for 1.2+. Update test/fine-grained-fallbacks to exercise this pathway in SVG 1.2 - as SVG natively supported all the current operations within that test. This reveals yet another librsvg bug in handling SVG 1.2.
|
|
@ -65,7 +65,7 @@ struct _cairo_paginated_surface_backend {
|
|||
* before the mode is changed to RENDER.
|
||||
*/
|
||||
cairo_warn cairo_int_status_t
|
||||
(*set_bounding_box) (void *surface,
|
||||
(*set_bounding_box) (void *surface,
|
||||
cairo_box_t *bbox);
|
||||
|
||||
/* Optional. Indicates whether the page requires fallback images.
|
||||
|
|
@ -73,8 +73,11 @@ struct _cairo_paginated_surface_backend {
|
|||
* mode is changed to RENDER.
|
||||
*/
|
||||
cairo_warn cairo_int_status_t
|
||||
(*set_fallback_images_required)(void *surface,
|
||||
cairo_bool_t fallbacks_required);
|
||||
(*set_fallback_images_required) (void *surface,
|
||||
cairo_bool_t fallbacks_required);
|
||||
|
||||
cairo_bool_t
|
||||
(*supports_fine_grained_fallbacks) (void *surface);
|
||||
};
|
||||
|
||||
/* A #cairo_paginated_surface_t provides a very convenient wrapper that
|
||||
|
|
|
|||
|
|
@ -329,36 +329,23 @@ _paint_page (cairo_paginated_surface_t *surface)
|
|||
|
||||
/* Finer grained fallbacks are currently only supported for some
|
||||
* surface types */
|
||||
switch (surface->target->type) {
|
||||
case CAIRO_SURFACE_TYPE_PDF:
|
||||
case CAIRO_SURFACE_TYPE_PS:
|
||||
case CAIRO_SURFACE_TYPE_WIN32_PRINTING:
|
||||
has_supported = _cairo_analysis_surface_has_supported (analysis);
|
||||
has_page_fallback = FALSE;
|
||||
has_finegrained_fallback = _cairo_analysis_surface_has_unsupported (analysis);
|
||||
break;
|
||||
|
||||
case CAIRO_SURFACE_TYPE_IMAGE:
|
||||
case CAIRO_SURFACE_TYPE_XLIB:
|
||||
case CAIRO_SURFACE_TYPE_XCB:
|
||||
case CAIRO_SURFACE_TYPE_GLITZ:
|
||||
case CAIRO_SURFACE_TYPE_QUARTZ:
|
||||
case CAIRO_SURFACE_TYPE_QUARTZ_IMAGE:
|
||||
case CAIRO_SURFACE_TYPE_WIN32:
|
||||
case CAIRO_SURFACE_TYPE_BEOS:
|
||||
case CAIRO_SURFACE_TYPE_DIRECTFB:
|
||||
case CAIRO_SURFACE_TYPE_SVG:
|
||||
case CAIRO_SURFACE_TYPE_OS2:
|
||||
default:
|
||||
if (_cairo_analysis_surface_has_unsupported (analysis)) {
|
||||
has_supported = FALSE;
|
||||
has_page_fallback = TRUE;
|
||||
} else {
|
||||
has_supported = TRUE;
|
||||
has_page_fallback = FALSE;
|
||||
}
|
||||
has_finegrained_fallback = FALSE;
|
||||
break;
|
||||
if (surface->backend->supports_fine_grained_fallbacks != NULL &&
|
||||
surface->backend->supports_fine_grained_fallbacks (surface->target))
|
||||
{
|
||||
has_supported = _cairo_analysis_surface_has_supported (analysis);
|
||||
has_page_fallback = FALSE;
|
||||
has_finegrained_fallback = _cairo_analysis_surface_has_unsupported (analysis);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_cairo_analysis_surface_has_unsupported (analysis)) {
|
||||
has_supported = FALSE;
|
||||
has_page_fallback = TRUE;
|
||||
} else {
|
||||
has_supported = TRUE;
|
||||
has_page_fallback = FALSE;
|
||||
}
|
||||
has_finegrained_fallback = FALSE;
|
||||
}
|
||||
|
||||
if (has_supported) {
|
||||
|
|
|
|||
|
|
@ -1277,8 +1277,8 @@ _cairo_pdf_surface_start_page (void *abstract_surface)
|
|||
}
|
||||
|
||||
static cairo_int_status_t
|
||||
_cairo_pdf_surface_has_fallback_images (void *abstract_surface,
|
||||
cairo_bool_t has_fallbacks)
|
||||
_cairo_pdf_surface_has_fallback_images (void *abstract_surface,
|
||||
cairo_bool_t has_fallbacks)
|
||||
{
|
||||
cairo_status_t status;
|
||||
cairo_pdf_surface_t *surface = abstract_surface;
|
||||
|
|
@ -1291,6 +1291,12 @@ _cairo_pdf_surface_has_fallback_images (void *abstract_surface,
|
|||
return CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static cairo_bool_t
|
||||
_cairo_pdf_surface_supports_fine_grained_fallbacks (void *abstract_surface)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Emit alpha channel from the image into the given data, providing
|
||||
* an id that can be used to reference the resulting SMask object.
|
||||
*
|
||||
|
|
@ -4981,9 +4987,11 @@ static const cairo_surface_backend_t cairo_pdf_surface_backend = {
|
|||
_cairo_pdf_surface_show_text_glyphs,
|
||||
};
|
||||
|
||||
static const cairo_paginated_surface_backend_t cairo_pdf_surface_paginated_backend = {
|
||||
static const cairo_paginated_surface_backend_t
|
||||
cairo_pdf_surface_paginated_backend = {
|
||||
_cairo_pdf_surface_start_page,
|
||||
_cairo_pdf_surface_set_paginated_mode,
|
||||
NULL, /* set_bounding_box */
|
||||
_cairo_pdf_surface_has_fallback_images,
|
||||
_cairo_pdf_surface_supports_fine_grained_fallbacks,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3284,6 +3284,12 @@ _cairo_ps_surface_set_bounding_box (void *abstract_surface,
|
|||
return _cairo_output_stream_get_status (surface->stream);
|
||||
}
|
||||
|
||||
static cairo_bool_t
|
||||
_cairo_ps_surface_supports_fine_grained_fallbacks (void *abstract_surface)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static const cairo_surface_backend_t cairo_ps_surface_backend = {
|
||||
CAIRO_SURFACE_TYPE_PS,
|
||||
_cairo_ps_surface_create_similar,
|
||||
|
|
@ -3322,4 +3328,6 @@ static const cairo_paginated_surface_backend_t cairo_ps_surface_paginated_backen
|
|||
_cairo_ps_surface_start_page,
|
||||
_cairo_ps_surface_set_paginated_mode,
|
||||
_cairo_ps_surface_set_bounding_box,
|
||||
NULL, /* _cairo_ps_surface_has_fallback_images, */
|
||||
_cairo_ps_surface_supports_fine_grained_fallbacks,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2022,11 +2022,8 @@ _cairo_svg_surface_paint (void *abstract_surface,
|
|||
* above always return FALSE. In order to make it work, we need a way
|
||||
* to know if there's an active clipping path.
|
||||
* Optimization of CLEAR works because of a test in paginated surface,
|
||||
* and an optimiszation in meta surface. */
|
||||
if (surface->clip_level == 0 &&
|
||||
(op == CAIRO_OPERATOR_CLEAR ||
|
||||
op == CAIRO_OPERATOR_SOURCE))
|
||||
{
|
||||
* and an optimization in meta surface. */
|
||||
if (surface->clip_level == 0 && op == CAIRO_OPERATOR_CLEAR) {
|
||||
status = _cairo_output_stream_destroy (surface->xml_node);
|
||||
if (status) {
|
||||
surface->xml_node = NULL;
|
||||
|
|
@ -2054,7 +2051,8 @@ _cairo_svg_surface_paint (void *abstract_surface,
|
|||
}
|
||||
}
|
||||
|
||||
return _cairo_svg_surface_emit_paint (surface->xml_node, surface, op, source, 0, NULL);
|
||||
return _cairo_svg_surface_emit_paint (surface->xml_node,
|
||||
surface, op, source, 0, NULL);
|
||||
}
|
||||
|
||||
static cairo_int_status_t
|
||||
|
|
@ -2551,15 +2549,33 @@ _cairo_svg_document_finish (cairo_svg_document_t *document)
|
|||
}
|
||||
|
||||
static void
|
||||
_cairo_svg_surface_set_paginated_mode (void *abstract_surface,
|
||||
cairo_paginated_mode_t paginated_mode)
|
||||
_cairo_svg_surface_set_paginated_mode (void *abstract_surface,
|
||||
cairo_paginated_mode_t paginated_mode)
|
||||
{
|
||||
cairo_svg_surface_t *surface = abstract_surface;
|
||||
|
||||
surface->paginated_mode = paginated_mode;
|
||||
}
|
||||
|
||||
static cairo_bool_t
|
||||
_cairo_svg_surface_supports_fine_grained_fallbacks (void *abstract_surface)
|
||||
{
|
||||
cairo_svg_surface_t *surface = abstract_surface;
|
||||
cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
|
||||
|
||||
if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2) {
|
||||
status = _cairo_svg_surface_analyze_operator (surface,
|
||||
CAIRO_OPERATOR_SOURCE);
|
||||
}
|
||||
|
||||
return status == CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static const cairo_paginated_surface_backend_t cairo_svg_surface_paginated_backend = {
|
||||
NULL /*_cairo_svg_surface_start_page*/,
|
||||
_cairo_svg_surface_set_paginated_mode
|
||||
_cairo_svg_surface_set_paginated_mode,
|
||||
NULL, /* _cairo_svg_surface_set_bounding_box */
|
||||
NULL, /* _cairo_svg_surface_set_fallback_images_required */
|
||||
_cairo_svg_surface_supports_fine_grained_fallbacks,
|
||||
|
||||
};
|
||||
|
|
|
|||
|
|
@ -478,9 +478,9 @@ REFERENCE_IMAGES = \
|
|||
filter-nearest-offset-svg12-ref.png \
|
||||
finer-grained-fallbacks-ref.png \
|
||||
finer-grained-fallbacks-rgb24-ref.png \
|
||||
finer-grained-fallbacks-ps2-argb32-ref.png \
|
||||
finer-grained-fallbacks-ps3-argb32-ref.png \
|
||||
finer-grained-fallbacks-ps2-ref.png \
|
||||
finer-grained-fallbacks-ps2-rgb24-ref.png \
|
||||
finer-grained-fallbacks-ps3-ref.png \
|
||||
finer-grained-fallbacks-ps3-rgb24-ref.png \
|
||||
font-matrix-translation-ps2-argb32-ref.png \
|
||||
font-matrix-translation-ps3-argb32-ref.png \
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 1 KiB |
BIN
test/finer-grained-fallbacks-ps2-ref.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 819 B After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1 KiB |
BIN
test/finer-grained-fallbacks-ps3-ref.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 819 B After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 796 B After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 590 B After Width: | Height: | Size: 1.1 KiB |
|
|
@ -31,7 +31,7 @@ static cairo_test_draw_function_t draw;
|
|||
#define CIRCLE_SIZE 10
|
||||
#define PAD 2
|
||||
#define WIDTH (CIRCLE_SIZE*6.5 + PAD)
|
||||
#define HEIGHT (CIRCLE_SIZE*3.5 + PAD)
|
||||
#define HEIGHT (CIRCLE_SIZE*7.0 + PAD)
|
||||
|
||||
static const cairo_test_t test = {
|
||||
"finer-grained-fallbacks",
|
||||
|
|
@ -50,6 +50,19 @@ draw_circle (cairo_t *cr, double x, double y)
|
|||
cairo_restore (cr);
|
||||
}
|
||||
|
||||
static void
|
||||
draw_image_circle (cairo_t *cr, cairo_surface_t *source, double x, double y)
|
||||
{
|
||||
cairo_save (cr);
|
||||
|
||||
cairo_set_source_surface (cr, source, x, y);
|
||||
cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REFLECT);
|
||||
cairo_rectangle (cr, x, y, CIRCLE_SIZE, CIRCLE_SIZE);
|
||||
cairo_fill (cr);
|
||||
|
||||
cairo_restore (cr);
|
||||
}
|
||||
|
||||
static void
|
||||
draw_circles (cairo_t *cr)
|
||||
{
|
||||
|
|
@ -61,6 +74,17 @@ draw_circles (cairo_t *cr)
|
|||
draw_circle (cr, CIRCLE_SIZE*6, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
draw_image_circles (cairo_t *cr, cairo_surface_t *source)
|
||||
{
|
||||
draw_image_circle (cr, source, 0, -CIRCLE_SIZE*0.1);
|
||||
draw_image_circle (cr, source, CIRCLE_SIZE*0.4, CIRCLE_SIZE*0.25);
|
||||
|
||||
draw_image_circle (cr, source, CIRCLE_SIZE*2, 0);
|
||||
draw_image_circle (cr, source, CIRCLE_SIZE*4, 0);
|
||||
draw_image_circle (cr, source, CIRCLE_SIZE*6, 0);
|
||||
}
|
||||
|
||||
/* For each of circle and fallback_circle we draw:
|
||||
* - two overlapping
|
||||
* - one isolated
|
||||
|
|
@ -74,12 +98,40 @@ draw_circles (cairo_t *cr)
|
|||
*
|
||||
* Fallback circles are drawn in red. CAIRO_OPERATOR_ADD is used to
|
||||
* ensure they will be emitted as a fallback image in PS/PDF.
|
||||
*
|
||||
* In order to trigger a fallback for SVG, we need to use a surface with
|
||||
* REFLECT.
|
||||
*/
|
||||
static cairo_surface_t *
|
||||
surface_create (cairo_t *target)
|
||||
{
|
||||
cairo_surface_t *surface;
|
||||
cairo_t *cr;
|
||||
|
||||
surface = cairo_surface_create_similar (cairo_get_target (target),
|
||||
CAIRO_CONTENT_COLOR_ALPHA,
|
||||
CIRCLE_SIZE, CIRCLE_SIZE);
|
||||
cr = cairo_create (surface);
|
||||
cairo_surface_destroy (surface);
|
||||
|
||||
cairo_set_source_rgb (cr, 1.0, 0.0, 0.0);
|
||||
draw_circle (cr, CIRCLE_SIZE/2, CIRCLE_SIZE/2);
|
||||
|
||||
surface = cairo_surface_reference (cairo_get_target (cr));
|
||||
cairo_destroy (cr);
|
||||
|
||||
return surface;
|
||||
}
|
||||
|
||||
static cairo_test_status_t
|
||||
draw (cairo_t *cr, int width, int height)
|
||||
{
|
||||
cairo_surface_t *surface;
|
||||
|
||||
cairo_translate (cr, PAD, PAD);
|
||||
|
||||
cairo_save (cr);
|
||||
|
||||
/* Draw overlapping circle and fallback circle */
|
||||
cairo_set_source_rgb (cr, 0.0, 1.0, 0.0);
|
||||
cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
|
||||
|
|
@ -101,6 +153,31 @@ draw (cairo_t *cr, int width, int height)
|
|||
cairo_translate (cr, 0, CIRCLE_SIZE*2);
|
||||
draw_circles (cr);
|
||||
|
||||
cairo_restore (cr);
|
||||
cairo_translate (cr, 0, CIRCLE_SIZE * 3.5);
|
||||
|
||||
/* Draw using fallback surface */
|
||||
surface = surface_create (cr);
|
||||
|
||||
cairo_set_source_rgb (cr, 0.0, 1.0, 0.0);
|
||||
cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
|
||||
draw_circle (cr, CIRCLE_SIZE*0.5, CIRCLE_SIZE*1.5);
|
||||
|
||||
cairo_set_operator (cr, CAIRO_OPERATOR_ADD);
|
||||
draw_image_circle (cr, surface, CIRCLE_SIZE/4, CIRCLE_SIZE + CIRCLE_SIZE/4);
|
||||
|
||||
/* Draw circles */
|
||||
cairo_set_source_rgb (cr, 0.0, 1.0, 0.0);
|
||||
cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
|
||||
cairo_translate (cr, CIRCLE_SIZE*2.5, CIRCLE_SIZE*0.6);
|
||||
draw_circles (cr);
|
||||
|
||||
cairo_set_operator (cr, CAIRO_OPERATOR_ADD);
|
||||
cairo_translate (cr, -CIRCLE_SIZE/2, CIRCLE_SIZE*1.5);
|
||||
draw_image_circles (cr, surface);
|
||||
|
||||
cairo_surface_destroy (surface);
|
||||
|
||||
return CAIRO_TEST_SUCCESS;
|
||||
}
|
||||
|
||||
|
|
|
|||