From 38c184b494fd5ca4461f7fbcba323b13cbcca5b9 Mon Sep 17 00:00:00 2001 From: Raman Varabets Date: Sun, 2 Jun 2024 04:13:46 +0800 Subject: [PATCH] Add support for color glyphs to SVG backend Signed-off-by: Raman Varabets --- src/cairo-surface.c | 13 +++ src/cairo-svg-surface-private.h | 7 ++ src/cairo-svg-surface.c | 161 ++++++++++++++++++++++++++++++-- 3 files changed, 173 insertions(+), 8 deletions(-) diff --git a/src/cairo-surface.c b/src/cairo-surface.c index 86c1837b9..213cb3d29 100644 --- a/src/cairo-surface.c +++ b/src/cairo-surface.c @@ -2863,6 +2863,19 @@ composite_color_glyphs (cairo_surface_t *surface, continue; } + cairo_bool_t supports_color_glyph = FALSE; + + if (surface->backend->supports_color_glyph) { + _cairo_scaled_font_thaw_cache (scaled_font); + supports_color_glyph = _cairo_surface_supports_color_glyph (surface, scaled_font, glyphs[glyph_pos].index); + _cairo_scaled_font_freeze_cache (scaled_font); + } + + if (supports_color_glyph) { + glyphs[remaining_glyphs++] = glyphs[glyph_pos]; + continue; + } + status = composite_one_color_glyph (surface, op, source, clip, &glyphs[glyph_pos], scaled_glyph, x_scale, y_scale); diff --git a/src/cairo-svg-surface-private.h b/src/cairo-svg-surface-private.h index e2a194081..72820f152 100644 --- a/src/cairo-svg-surface-private.h +++ b/src/cairo-svg-surface-private.h @@ -45,6 +45,13 @@ #include "cairo-surface-private.h" +typedef struct _cairo_svg_color_glyph { + cairo_hash_entry_t base; + cairo_scaled_font_t *scaled_font; + unsigned long glyph_index; + cairo_bool_t supported; +} cairo_svg_color_glyph_t; + struct _cairo_svg_surface_start { cairo_surface_t base; diff --git a/src/cairo-svg-surface.c b/src/cairo-svg-surface.c index 249f46f53..bf37d5cb5 100644 --- a/src/cairo-svg-surface.c +++ b/src/cairo-svg-surface.c @@ -117,6 +117,25 @@ _cairo_svg_source_surface_equal (const void *key_a, const void *key_b) return a->id == b->id; } +static cairo_bool_t +_cairo_svg_color_glyph_equal (const void *key_a, const void *key_b) +{ + const cairo_svg_color_glyph_t *a = key_a; + const cairo_svg_color_glyph_t *b = key_b; + + if (a->scaled_font != b->scaled_font) + return FALSE; + + return (a->glyph_index == b->glyph_index); +} + +static void +_cairo_svg_color_glyph_init_key (cairo_svg_color_glyph_t *key) +{ + key->base.hash = _cairo_hash_uintptr (_CAIRO_HASH_INIT_VALUE, (uintptr_t)key->scaled_font); + key->base.hash = _cairo_hash_uintptr (key->base.hash, key->glyph_index); +} + static void _cairo_svg_source_surface_pluck (void *entry, void *closure) { @@ -526,6 +545,8 @@ typedef struct _cairo_svg_surface { cairo_svg_document_t *document; + cairo_hash_table_t *color_glyphs; + cairo_svg_stream_t xml_node; cairo_array_t page_set; @@ -1080,6 +1101,12 @@ _cairo_svg_surface_create_for_document (cairo_svg_document_t *document, surface->document = _cairo_svg_document_reference (document); + surface->color_glyphs = _cairo_hash_table_create (_cairo_svg_color_glyph_equal); + if (unlikely (surface->color_glyphs == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP; // FIXME + } + surface->xml_node = _cairo_svg_stream_create (); _cairo_array_init (&surface->page_set, sizeof (cairo_svg_page_t)); @@ -1111,6 +1138,7 @@ _cairo_svg_surface_create_for_document (cairo_svg_document_t *document, /* ignore status as we are on the error path */ CLEANUP: + (void) _cairo_hash_table_destroy (surface->color_glyphs); (void) _cairo_svg_stream_destroy (&surface->xml_node); (void) _cairo_svg_document_destroy (document); @@ -1346,6 +1374,56 @@ _cairo_svg_document_emit_outline_glyph_data (cairo_svg_document_t *document, return status; } +static cairo_int_status_t +_cairo_svg_document_emit_color_glyph_data (cairo_svg_document_t *document, + cairo_scaled_font_t *scaled_font, + unsigned long glyph_index) +{ + cairo_status_t status; + + cairo_scaled_glyph_t *scaled_glyph; + status = _cairo_scaled_glyph_lookup (scaled_font, + glyph_index, + CAIRO_SCALED_GLYPH_INFO_METRICS | CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE, + NULL, /* foreground color */ + &scaled_glyph); + if (unlikely (status)) { + return status; + } + + cairo_surface_t *paginated_surface = _cairo_svg_surface_create_for_document (document, + CAIRO_CONTENT_COLOR_ALPHA, + 0, + 0, + FALSE); + cairo_svg_surface_t *svg_surface = (cairo_svg_surface_t *) _cairo_paginated_surface_get_target (paginated_surface); + status = paginated_surface->status; + if (unlikely (status)) { + goto cleanup; + } + + cairo_surface_set_fallback_resolution (paginated_surface, + document->owner->x_fallback_resolution, + document->owner->y_fallback_resolution); + + cairo_pattern_t *pattern = cairo_pattern_create_for_surface (scaled_glyph->recording_surface); + _cairo_svg_surface_emit_composite_pattern (//&temporary_stream, + &document->xml_node_glyphs, + svg_surface, + (cairo_surface_pattern_t *) pattern, + invalid_pattern_id, + NULL); + cairo_pattern_destroy (pattern); + +cleanup: + if (status == CAIRO_STATUS_SUCCESS) { + status = cairo_surface_status (paginated_surface); + } + cairo_surface_destroy (paginated_surface); + + return status; +} + static cairo_int_status_t _cairo_svg_document_emit_bitmap_glyph_data (cairo_svg_document_t *document, cairo_scaled_font_t *scaled_font, @@ -1483,7 +1561,7 @@ _cairo_svg_document_emit_bitmap_glyph_data (cairo_svg_document_t *document, goto cleanup; } - cleanup: +cleanup: if (status == CAIRO_STATUS_SUCCESS) { status = cairo_surface_status (paginated_surface); } @@ -1510,13 +1588,19 @@ _cairo_svg_document_emit_glyph (cairo_svg_document_t *document, font_id, subset_glyph_index); - status = _cairo_svg_document_emit_outline_glyph_data (document, - scaled_font, - scaled_font_glyph_index); - if (status == CAIRO_INT_STATUS_UNSUPPORTED) - status = _cairo_svg_document_emit_bitmap_glyph_data (document, - scaled_font, - scaled_font_glyph_index); + status = _cairo_svg_document_emit_color_glyph_data (document, + scaled_font, + scaled_font_glyph_index); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + status = _cairo_svg_document_emit_outline_glyph_data (document, + scaled_font, + scaled_font_glyph_index); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + status = _cairo_svg_document_emit_bitmap_glyph_data (document, + scaled_font, + scaled_font_glyph_index); + } + } if (unlikely (status)) return status; @@ -4126,6 +4210,63 @@ _cairo_svg_surface_get_supported_mime_types (void *abstract_surface) return _cairo_svg_supported_mime_types; } +static cairo_bool_t +_cairo_svg_surface_supports_color_glyph (void *abstract_surface, + cairo_scaled_font_t *scaled_font, + unsigned long glyph_index) +{ + cairo_svg_surface_t *surface = abstract_surface; + cairo_svg_color_glyph_t glyph_key; + cairo_svg_color_glyph_t *glyph_entry; + cairo_scaled_glyph_t *scaled_glyph; + cairo_status_t status; + + glyph_key.scaled_font = scaled_font; + glyph_key.glyph_index = glyph_index; + + _cairo_svg_color_glyph_init_key (&glyph_key); + glyph_entry = _cairo_hash_table_lookup (surface->color_glyphs, &glyph_key.base); + if (glyph_entry) + return glyph_entry->supported; + + glyph_entry = _cairo_malloc (sizeof (cairo_svg_color_glyph_t)); + if (glyph_entry == NULL) { + status = _cairo_surface_set_error (&surface->base, + _cairo_error (CAIRO_STATUS_NO_MEMORY)); + return FALSE; + } + + glyph_entry->scaled_font = cairo_scaled_font_reference (scaled_font); + glyph_entry->glyph_index = glyph_index; + _cairo_svg_color_glyph_init_key (glyph_entry); + + glyph_entry->supported = FALSE; + _cairo_scaled_font_freeze_cache (scaled_font); + status = _cairo_scaled_glyph_lookup (scaled_font, + glyph_index, + CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE, + NULL, /* foreground color */ + &scaled_glyph); + if (unlikely (status)) + goto done; + + glyph_entry->supported = !(scaled_glyph->recording_uses_foreground_color || + scaled_glyph->recording_uses_foreground_marker); + +done: + _cairo_scaled_font_thaw_cache (scaled_font); + + status = _cairo_hash_table_insert (surface->color_glyphs, + &glyph_entry->base); + if (unlikely(status)) { + status = _cairo_surface_set_error (&surface->base, + _cairo_error (CAIRO_STATUS_NO_MEMORY)); + return FALSE; + } + + return glyph_entry->supported; +} + static const cairo_surface_backend_t cairo_svg_surface_backend = { CAIRO_SURFACE_TYPE_SVG, _cairo_svg_surface_finish, @@ -4160,6 +4301,10 @@ static const cairo_surface_backend_t cairo_svg_surface_backend = { NULL, /* has_show_text_glyphs */ NULL, /* show_text_glyphs */ _cairo_svg_surface_get_supported_mime_types, + NULL, /* tag */ + _cairo_svg_surface_supports_color_glyph, + NULL, /* analyze_recording_surface */ + NULL, /* command_id */ }; static cairo_status_t