Add support for color glyphs to SVG backend

Signed-off-by: Raman Varabets <gitlab-20240601-da2ef10c@cyborgize.sg>
This commit is contained in:
Raman Varabets 2024-06-02 04:13:46 +08:00
parent 19c15cedbf
commit 38c184b494
3 changed files with 173 additions and 8 deletions

View file

@ -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);

View file

@ -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;

View file

@ -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