summaryrefslogtreecommitdiff
path: root/src/cairo-user-font.c
diff options
context:
space:
mode:
authorAdrian Johnson <ajohnson@redneon.com>2023-01-22 13:27:53 +1030
committerAdrian Johnson <ajohnson@redneon.com>2023-01-26 19:49:06 +1030
commite93d175aac6805692d660f1f6444e6b21492e2a7 (patch)
treebc6216ef593280b7b8c3b93af6027e727b8a9555 /src/cairo-user-font.c
parent0151a67e33f7c96645ae581b31861780b531a7e1 (diff)
downloadcairo-e93d175aac6805692d660f1f6444e6b21492e2a7.tar.gz
Add new cairo_user_scaled_font_get_foreground_source() function
The previous approach using foreground colors in user fonts does not work for gradients since the foreground color is not available at the time of recording. Add a new function cairo_user_scaled_font_get_foreground_source() that can be called by the color render function to retrieve the foreground pattern. Calling this function signals to cairo that the foreground color is used. In this case cairo will call the render function whenever the foreground color has changed.
Diffstat (limited to 'src/cairo-user-font.c')
-rw-r--r--src/cairo-user-font.c206
1 files changed, 185 insertions, 21 deletions
diff --git a/src/cairo-user-font.c b/src/cairo-user-font.c
index 80cd4c303..913395fd9 100644
--- a/src/cairo-user-font.c
+++ b/src/cairo-user-font.c
@@ -95,13 +95,19 @@ typedef struct _cairo_user_scaled_font {
double snap_x_scale;
double snap_y_scale;
+ cairo_pattern_t *foreground_marker;
+ cairo_pattern_t *foreground_pattern;
+ cairo_bool_t foreground_marker_used;
+ cairo_bool_t foreground_colors_used;
+
} cairo_user_scaled_font_t;
/* #cairo_user_scaled_font_t */
static cairo_surface_t *
-_cairo_user_scaled_font_create_recording_surface (const cairo_user_scaled_font_t *scaled_font,
- cairo_bool_t color)
+_cairo_user_scaled_font_create_recording_surface (cairo_user_scaled_font_t *scaled_font,
+ cairo_bool_t color,
+ const cairo_color_t *foreground_color)
{
cairo_content_t content;
@@ -113,10 +119,20 @@ _cairo_user_scaled_font_create_recording_surface (const cairo_user_scaled_font_t
CAIRO_CONTENT_ALPHA;
}
+ if (scaled_font->foreground_pattern)
+ cairo_pattern_destroy (scaled_font->foreground_pattern);
+
+ scaled_font->foreground_marker_used = FALSE;
+ scaled_font->foreground_colors_used = FALSE;
+ if (foreground_color) {
+ scaled_font->foreground_pattern = _cairo_pattern_create_solid (foreground_color);
+ } else {
+ scaled_font->foreground_pattern = cairo_pattern_create_rgb (0, 0, 0);
+ }
+
return cairo_recording_surface_create (content, NULL);
}
-
static cairo_t *
_cairo_user_scaled_font_create_recording_context (const cairo_user_scaled_font_t *scaled_font,
cairo_surface_t *recording_surface,
@@ -143,7 +159,8 @@ _cairo_user_scaled_font_create_recording_context (const cairo_user_scaled_font_t
static cairo_int_status_t
_cairo_user_scaled_glyph_init_record_glyph (cairo_user_scaled_font_t *scaled_font,
- cairo_scaled_glyph_t *scaled_glyph)
+ cairo_scaled_glyph_t *scaled_glyph,
+ const cairo_color_t *foreground_color)
{
cairo_user_font_face_t *face =
(cairo_user_font_face_t *) scaled_font->base.font_face;
@@ -151,29 +168,25 @@ _cairo_user_scaled_glyph_init_record_glyph (cairo_user_scaled_font_t *scaled_fon
cairo_surface_t *recording_surface = NULL;
cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
cairo_t *cr;
+ cairo_bool_t foreground_used = FALSE;
if (!face->scaled_font_methods.render_color_glyph && !face->scaled_font_methods.render_glyph)
return CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED;
/* special case for 0 rank matrix (as in _cairo_scaled_font_init): empty surface */
if (_cairo_matrix_is_scale_0 (&scaled_font->base.scale)) {
- recording_surface = _cairo_user_scaled_font_create_recording_surface (scaled_font, FALSE);
+ recording_surface = _cairo_user_scaled_font_create_recording_surface (scaled_font, FALSE, foreground_color);
_cairo_scaled_glyph_set_recording_surface (scaled_glyph,
&scaled_font->base,
- recording_surface);
+ recording_surface,
+ NULL);
} else {
status = CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED;
if (face->scaled_font_methods.render_color_glyph) {
- cairo_pattern_t *pattern;
-
- recording_surface = _cairo_user_scaled_font_create_recording_surface (scaled_font, TRUE);
+ recording_surface = _cairo_user_scaled_font_create_recording_surface (scaled_font, TRUE, foreground_color);
cr = _cairo_user_scaled_font_create_recording_context (scaled_font, recording_surface, TRUE);
- pattern = cairo_pattern_create_rgb (0, 0, 0);
- pattern->is_userfont_foreground = TRUE;
- cairo_set_source (cr, pattern);
- cairo_pattern_destroy (pattern);
status = face->scaled_font_methods.render_color_glyph ((cairo_scaled_font_t *)scaled_font,
_cairo_scaled_glyph_index(scaled_glyph),
cr, &extents);
@@ -182,14 +195,16 @@ _cairo_user_scaled_glyph_init_record_glyph (cairo_user_scaled_font_t *scaled_fon
scaled_glyph->color_glyph = TRUE;
scaled_glyph->color_glyph_set = TRUE;
}
+
cairo_destroy (cr);
+ foreground_used = scaled_font->foreground_marker_used || scaled_font->foreground_colors_used;
}
if (status == (cairo_int_status_t)CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED &&
face->scaled_font_methods.render_glyph) {
if (recording_surface)
cairo_surface_destroy (recording_surface);
- recording_surface = _cairo_user_scaled_font_create_recording_surface (scaled_font, FALSE);
+ recording_surface = _cairo_user_scaled_font_create_recording_surface (scaled_font, FALSE, foreground_color);
recording_surface->device_transform.x0 = .25 * _cairo_scaled_glyph_xphase (scaled_glyph);
recording_surface->device_transform.y0 = .25 * _cairo_scaled_glyph_yphase (scaled_glyph);
@@ -205,6 +220,7 @@ _cairo_user_scaled_glyph_init_record_glyph (cairo_user_scaled_font_t *scaled_fon
}
cairo_destroy (cr);
+ foreground_used = FALSE;
}
if (status != CAIRO_INT_STATUS_SUCCESS) {
@@ -215,7 +231,8 @@ _cairo_user_scaled_glyph_init_record_glyph (cairo_user_scaled_font_t *scaled_fon
_cairo_scaled_glyph_set_recording_surface (scaled_glyph,
&scaled_font->base,
- recording_surface);
+ recording_surface,
+ foreground_used ? foreground_color : NULL);
}
/* set metrics */
@@ -265,8 +282,8 @@ _cairo_user_scaled_glyph_init_surface (cairo_user_scaled_font_t *scaled_font,
cairo_surface_t *surface;
cairo_format_t format;
int width, height;
- cairo_bool_t foreground_used;
cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
+ cairo_bool_t foreground_used;
/* TODO
* extend the glyph cache to support argb glyphs.
@@ -313,20 +330,23 @@ _cairo_user_scaled_glyph_init_surface (cairo_user_scaled_font_t *scaled_font,
surface,
foreground_color,
&foreground_used);
+
} else {
status = _cairo_recording_surface_replay (scaled_glyph->recording_surface, surface);
+ foreground_used = FALSE;
}
-
if (unlikely (status)) {
cairo_surface_destroy(surface);
return status;
}
+ foreground_used = foreground_used || scaled_glyph->recording_uses_foreground_color;
+
if (info == CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE) {
_cairo_scaled_glyph_set_color_surface (scaled_glyph,
&scaled_font->base,
(cairo_image_surface_t *)surface,
- foreground_used);
+ foreground_used ? foreground_color : NULL);
surface = NULL;
} else {
_cairo_scaled_glyph_set_surface (scaled_glyph,
@@ -341,6 +361,18 @@ _cairo_user_scaled_glyph_init_surface (cairo_user_scaled_font_t *scaled_font,
return status;
}
+static void
+_cairo_user_scaled_glyph_fini (void *abstract_font)
+{
+ cairo_user_scaled_font_t *scaled_font = abstract_font;
+
+ if (scaled_font->foreground_pattern)
+ cairo_pattern_destroy (scaled_font->foreground_pattern);
+
+ if (scaled_font->foreground_marker)
+ cairo_pattern_destroy (scaled_font->foreground_marker);
+}
+
static cairo_int_status_t
_cairo_user_scaled_glyph_init (void *abstract_font,
cairo_scaled_glyph_t *scaled_glyph,
@@ -349,9 +381,21 @@ _cairo_user_scaled_glyph_init (void *abstract_font,
{
cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
cairo_user_scaled_font_t *scaled_font = abstract_font;
+ cairo_bool_t need_recording = FALSE;
if (!scaled_glyph->recording_surface) {
- status = _cairo_user_scaled_glyph_init_record_glyph (scaled_font, scaled_glyph);
+ need_recording = TRUE;
+ } else {
+ if ((info & (CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE|CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE)) &&
+ scaled_glyph->recording_uses_foreground_color &&
+ !_cairo_color_equal (foreground_color, &scaled_glyph->foreground_color))
+ {
+ need_recording = TRUE;
+ }
+ }
+
+ if (need_recording) {
+ status = _cairo_user_scaled_glyph_init_record_glyph (scaled_font, scaled_glyph, foreground_color);
if (status)
return status;
}
@@ -511,7 +555,7 @@ _cairo_user_font_face_create_for_toy (cairo_toy_font_face_t *toy_face,
static const cairo_scaled_font_backend_t _cairo_user_scaled_font_backend = {
CAIRO_FONT_TYPE_USER,
- NULL, /* scaled_font_fini */
+ _cairo_user_scaled_glyph_fini,
_cairo_user_scaled_glyph_init,
_cairo_user_text_to_glyphs,
_cairo_user_ucs4_to_index,
@@ -553,6 +597,10 @@ _cairo_user_font_face_scaled_font_create (void *abstract_
return status;
}
+ user_scaled_font->foreground_pattern = NULL;
+ user_scaled_font->foreground_marker = cairo_pattern_create_rgb (0, 0, 0);
+ user_scaled_font->foreground_marker->is_userfont_foreground = TRUE;
+
/* XXX metrics hinting? */
/* compute a normalized version of font scale matrix to compute
@@ -601,7 +649,7 @@ _cairo_user_font_face_scaled_font_create (void *abstract_
cairo_surface_t *recording_surface;
cairo_t *cr;
- recording_surface = _cairo_user_scaled_font_create_recording_surface (user_scaled_font, FALSE);
+ recording_surface = _cairo_user_scaled_font_create_recording_surface (user_scaled_font, FALSE, NULL);
cr = _cairo_user_scaled_font_create_recording_context (user_scaled_font, recording_surface, FALSE);
cairo_surface_destroy (recording_surface);
@@ -1056,3 +1104,119 @@ cairo_user_font_face_get_unicode_to_glyph_func (cairo_font_face_t *font_face)
user_font_face = (cairo_user_font_face_t *) font_face;
return user_font_face->scaled_font_methods.unicode_to_glyph;
}
+
+/**
+ * cairo_user_scaled_font_get_foreground_marker:
+ * @scaled_font: A user scaled font
+ *
+ * Gets the foreground pattern of the glyph currently being
+ * rendered. A #cairo_user_scaled_font_render_glyph_func_t function
+ * that has been set with
+ * cairo_user_font_face_set_render_color_glyph_func() may call this
+ * function to retrieve the current foreground pattern for the glyph
+ * being rendered. The function should not be called outside of a
+ * cairo_user_font_face_set_render_color_glyph_func() callback.
+ *
+ * The foreground marker pattern contains an internal marker to
+ * indicate that it is to be substituted with the current source when
+ * rendered to a surface. Querying the foreground marker will reveal a
+ * solid black color, however this is not representative of the color
+ * that will actually be used. Similarly, setting a solid black color
+ * will render black, not the foreground pattern when the glyph is
+ * painted to a surface. Using the foreground marker as the source
+ * instead of cairo_user_scaled_font_get_foreground_source() in a
+ * color render callback has the following benefits:
+ *
+ * 1. Cairo only needs to call the render callback once as it can
+ * cache the recording. Cairo will substitute the actual foreground
+ * color when rendering the recording.
+ *
+ * 2. On backends that have the concept of a foreground color in fonts such as
+ * PDF, PostScript, and SVG, cairo can generate more optimal
+ * output. The glyph can be included in an embedded font.
+ *
+ * The one drawback of the using foreground marker is the render
+ * callback can not access the color components of the pattern as the
+ * actual foreground pattern is not available at the time the render
+ * callback is invoked. If the render callback needs to query the
+ * foreground pattern, use
+ * cairo_user_scaled_font_get_foreground_source().
+ *
+ * If the render callback simply wants to call cairo_set_source() with
+ * the foreground pattern,
+ * cairo_user_scaled_font_get_foreground_marker() is the preferred
+ * function to use as it results in better performance than
+ * cairo_user_scaled_font_get_foreground_source().
+ *
+ * Return value: the current foreground source marker pattern. This
+ * object is owned by cairo. This object must not be modified or used
+ * outside of a color render callback. To keep a reference to it,
+ * you must call cairo_pattern_reference().
+ *
+ * Since: 1.18
+ **/
+cairo_pattern_t *
+cairo_user_scaled_font_get_foreground_marker (cairo_scaled_font_t *scaled_font)
+{
+ cairo_user_scaled_font_t *user_scaled_font;
+
+ if (scaled_font->backend != &_cairo_user_scaled_font_backend)
+ return _cairo_pattern_create_in_error (CAIRO_STATUS_FONT_TYPE_MISMATCH);
+
+ user_scaled_font = (cairo_user_scaled_font_t *)scaled_font;
+ return user_scaled_font->foreground_marker;
+}
+
+/**
+ * cairo_user_scaled_font_get_foreground_source:
+ * @scaled_font: A user scaled font
+ *
+ * Gets the foreground pattern of the glyph currently being
+ * rendered. A #cairo_user_scaled_font_render_glyph_func_t function
+ * that has been set with
+ * cairo_user_font_face_set_render_color_glyph_func() may call this
+ * function to retrieve the current foreground pattern for the glyph
+ * being rendered. The function should not be called outside of a
+ * cairo_user_font_face_set_render_color_glyph_func() callback.
+ *
+ * This function returns the current source at the time the glyph is
+ * rendered. Compared with
+ * cairo_user_scaled_font_get_foreground_marker(), this function
+ * returns the actual source pattern that will be used to render the
+ * glyph. The render callback is free to query the pattern and
+ * extract color components or other pattern data. For example if the
+ * render callback wants to create a gradient stop based on colors in
+ * the foreground source pattern, it will need to use this function in
+ * order to be able to query the colors in the foreground pattern.
+ *
+ * While this function does not have the restrictions on using the
+ * pattern that cairo_user_scaled_font_get_foreground_marker() has, it
+ * does incur a performance penalty. If a render callback calls this
+ * function:
+ *
+ * 1. Cairo will call the render callback whenever the current pattern
+ * of the context in which the glyph is rendered changes.
+ *
+ * 2. On backends that support font embedding (PDF, PostScript, and
+ * SVG), cairo can not embed this glyph in a font. Instead the glyph
+ * will be emitted as an image or sequence of drawing operations each
+ * time it is used.
+ *
+ * Return value: the current foreground source pattern. This object is
+ * owned by cairo. To keep a reference to it, you must call
+ * cairo_pattern_reference().
+ *
+ * Since: 1.18
+ **/
+cairo_pattern_t *
+cairo_user_scaled_font_get_foreground_source (cairo_scaled_font_t *scaled_font)
+{
+ cairo_user_scaled_font_t *user_scaled_font;
+
+ if (scaled_font->backend != &_cairo_user_scaled_font_backend)
+ return _cairo_pattern_create_in_error (CAIRO_STATUS_FONT_TYPE_MISMATCH);
+
+ user_scaled_font = (cairo_user_scaled_font_t *)scaled_font;
+ user_scaled_font->foreground_colors_used = TRUE;
+ return user_scaled_font->foreground_pattern;
+}