diff --git a/src/cairo-font-options.c b/src/cairo-font-options.c index d5c6663be..8590fc143 100644 --- a/src/cairo-font-options.c +++ b/src/cairo-font-options.c @@ -60,6 +60,7 @@ static const cairo_font_options_t _cairo_font_options_nil = { CAIRO_COLOR_MODE_DEFAULT, CAIRO_COLOR_PALETTE_DEFAULT, NULL, 0, /* custom palette */ + CAIRO_SUBPIXEL_POSITIONS_DEFAULT, }; /** @@ -82,6 +83,7 @@ _cairo_font_options_init_default (cairo_font_options_t *options) options->palette_index = CAIRO_COLOR_PALETTE_DEFAULT; options->custom_palette = NULL; options->custom_palette_size = 0; + options->subpixel_positions = CAIRO_SUBPIXEL_POSITIONS_DEFAULT; } void @@ -103,6 +105,7 @@ _cairo_font_options_init_copy (cairo_font_options_t *options, options->custom_palette = (cairo_palette_color_t *) malloc (sizeof (cairo_palette_color_t) * options->custom_palette_size); memcpy (options->custom_palette, other->custom_palette, sizeof (cairo_palette_color_t) * options->custom_palette_size); } + options->subpixel_positions = other->subpixel_positions; } cairo_bool_t @@ -117,7 +120,8 @@ _cairo_font_options_compare (const cairo_font_options_t *a, a->round_glyph_positions != b->round_glyph_positions || a->color_mode != b->color_mode || a->palette_index != b->palette_index || - a->custom_palette_size != b->custom_palette_size) + a->custom_palette_size != b->custom_palette_size || + a->subpixel_positions != b->subpixel_positions) { return FALSE; } @@ -287,6 +291,8 @@ cairo_font_options_merge (cairo_font_options_t *options, options->hint_metrics = other->hint_metrics; if (other->round_glyph_positions != CAIRO_ROUND_GLYPH_POS_DEFAULT) options->round_glyph_positions = other->round_glyph_positions; + if (other->subpixel_positions != CAIRO_SUBPIXEL_POSITIONS_DEFAULT) + options->subpixel_positions = other->subpixel_positions; if (other->variations) { if (options->variations) { @@ -349,6 +355,7 @@ cairo_font_options_equal (const cairo_font_options_t *options, options->hint_style == other->hint_style && options->hint_metrics == other->hint_metrics && options->round_glyph_positions == other->round_glyph_positions && + options->subpixel_positions == other->subpixel_positions && ((options->variations == NULL && other->variations == NULL) || (options->variations != NULL && other->variations != NULL && strcmp (options->variations, other->variations) == 0)) && @@ -393,7 +400,8 @@ cairo_font_options_hash (const cairo_font_options_t *options) (options->lcd_filter << 8) | (options->hint_style << 12) | (options->hint_metrics << 16) | - (options->color_mode << 20)) ^ hash; + (options->subpixel_positions << 20) | + (options->color_mode << 22)) ^ hash; } /** @@ -869,3 +877,43 @@ cairo_font_options_get_custom_palette_color (cairo_font_options_t *options, return CAIRO_STATUS_INVALID_INDEX; } + +/** + * cairo_font_options_set_subpixel_positions: + * @options: a #cairo_font_options_t + * @subpixel_positions: the new value + * + * Sets the subpixel positions value for the font options object. + * This controls whether glyph positions are rounded when rendering. + * + * Since: 1.20 + **/ +void +cairo_font_options_set_subpixel_positions (cairo_font_options_t *options, + cairo_subpixel_positions_t subpixel_positions) +{ + if (cairo_font_options_status (options)) + return; + + options->subpixel_positions = subpixel_positions; +} + +/** + * cairo_font_options_get_subpixel_positions: + * @options: a #cairo_font_options_t + * + * Gets the subpixel positions value for the font options object. + * See the documentation for #cairo_subpixel_positions_t for full details. + * + * Return value: the subpixel positions value for the font options object + * + * Since: 1.20 + */ +cairo_public cairo_subpixel_positions_t +cairo_font_options_get_subpixel_positions (const cairo_font_options_t *options) +{ + if (cairo_font_options_status ((cairo_font_options_t *) options)) + return CAIRO_SUBPIXEL_POSITIONS_DEFAULT; + + return options->subpixel_positions; +} diff --git a/src/cairo-ft-font.c b/src/cairo-ft-font.c index b5d08ee1b..74f957b73 100644 --- a/src/cairo-ft-font.c +++ b/src/cairo-ft-font.c @@ -2449,10 +2449,11 @@ _cairo_ft_scaled_glyph_load_glyph (cairo_ft_scaled_font_t *scaled_font, _cairo_ft_scaled_glyph_vertical_layout_bearing_fix (scaled_font, face->glyph); if (face->glyph->format == FT_GLYPH_FORMAT_OUTLINE) { + unsigned int bits = _cairo_subpixel_bits (scaled_font->base.options.subpixel_positions); FT_Pos xshift, yshift; - xshift = _cairo_scaled_glyph_xphase (scaled_glyph) << 4; - yshift = _cairo_scaled_glyph_yphase (scaled_glyph) << 4; + xshift = _cairo_scaled_glyph_xphase (scaled_glyph) << (6 - bits); + yshift = _cairo_scaled_glyph_yphase (scaled_glyph) << (6 - bits); FT_Outline_Translate (&face->glyph->outline, xshift, -yshift); } diff --git a/src/cairo-image-compositor.c b/src/cairo-image-compositor.c index 8a3b2629f..88bb90ce3 100644 --- a/src/cairo-image-compositor.c +++ b/src/cairo-image-compositor.c @@ -845,9 +845,6 @@ _cairo_image_scaled_glyph_fini (cairo_scaled_font_t *scaled_font, CAIRO_MUTEX_UNLOCK (_cairo_glyph_cache_mutex); } -#define PHASE(x) ((int)(floor (4 * (x + 0.125)) - 4 * floor (x + 0.125))) -#define POSITION(x) ((int) floor (x + 0.125)) - static cairo_int_status_t composite_glyphs (void *_dst, cairo_operator_t op, @@ -889,12 +886,14 @@ composite_glyphs (void *_dst, for (i = 0; i < info->num_glyphs; i++) { unsigned long index = info->glyphs[i].index; const void *glyph; - unsigned long xphase, yphase; - xphase = PHASE(info->glyphs[i].x); - yphase = PHASE(info->glyphs[i].y); - - index = index | (xphase << 24) | (yphase << 26); + if (info->font->options.subpixel_positions != CAIRO_SUBPIXEL_POSITIONS_OFF) { + int res = _cairo_subpixel_resolution (info->font->options.subpixel_positions); + unsigned long xphase, yphase; + xphase = _cairo_subpixel_phase (info->glyphs[i].x, res); + yphase = _cairo_subpixel_phase (info->glyphs[i].y, res); + index = index | (xphase << XSHIFT) | (yphase << YSHIFT); + } glyph = pixman_glyph_cache_lookup (glyph_cache, info->font, (void *)(uintptr_t)index); if (!glyph) { @@ -925,8 +924,15 @@ composite_glyphs (void *_dst, } } - pg->x = POSITION (info->glyphs[i].x); - pg->y = POSITION (info->glyphs[i].y); + if (info->font->options.subpixel_positions != CAIRO_SUBPIXEL_POSITIONS_OFF) { + int res = _cairo_subpixel_resolution (info->font->options.subpixel_positions); + pg->x = _cairo_subpixel_position (info->glyphs[i].x, res); + pg->y = _cairo_subpixel_position (info->glyphs[i].y, res); + } + else { + pg->x = _cairo_lround (info->glyphs[i].x); + pg->y = _cairo_lround (info->glyphs[i].y); + } pg->glyph = glyph; pg++; } @@ -954,6 +960,20 @@ composite_glyphs (void *_dst, glyph_cache, pg - pglyphs, pglyphs); } + /* Don't keep 64x64 versions of each glyph in the cache */ + if (info->font->options.subpixel_positions == CAIRO_SUBPIXEL_POSITIONS_FINE) { + int res = _cairo_subpixel_resolution (info->font->options.subpixel_positions); + for (i = 0; i < info->num_glyphs; i++) { + unsigned long index = info->glyphs[i].index; + unsigned long xphase, yphase; + xphase = _cairo_subpixel_phase (info->glyphs[i].x, res); + yphase = _cairo_subpixel_phase (info->glyphs[i].y, res); + index = index | (xphase << XSHIFT) | (yphase << YSHIFT); + + pixman_glyph_cache_remove (glyph_cache, info->font, (void *)(uintptr_t)index); + } + } + out_thaw: pixman_glyph_cache_thaw (glyph_cache); diff --git a/src/cairo-types-private.h b/src/cairo-types-private.h index 74a366c23..4914640f8 100644 --- a/src/cairo-types-private.h +++ b/src/cairo-types-private.h @@ -204,6 +204,7 @@ struct _cairo_font_options { unsigned int palette_index; cairo_palette_color_t *custom_palette; unsigned int custom_palette_size; + cairo_subpixel_positions_t subpixel_positions; }; struct _cairo_glyph_text_info { diff --git a/src/cairo-user-font.c b/src/cairo-user-font.c index 56525d5c1..01b6b378d 100644 --- a/src/cairo-user-font.c +++ b/src/cairo-user-font.c @@ -207,8 +207,11 @@ _cairo_user_scaled_glyph_init_record_glyph (cairo_user_scaled_font_t *scaled_fon if (recording_surface) cairo_surface_destroy (recording_surface); 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); + if (scaled_font->base.options.subpixel_positions != CAIRO_SUBPIXEL_POSITIONS_OFF) { + double res = _cairo_subpixel_resolution (scaled_font->base.options.subpixel_positions); + recording_surface->device_transform.x0 = _cairo_scaled_glyph_xphase (scaled_glyph) / res; + recording_surface->device_transform.y0 = _cairo_scaled_glyph_yphase (scaled_glyph) / res; + } cr = _cairo_user_scaled_font_create_recording_context (scaled_font, recording_surface, FALSE); diff --git a/src/cairo-xlib-render-compositor.c b/src/cairo-xlib-render-compositor.c index a870c00e1..4c7e509e7 100644 --- a/src/cairo-xlib-render-compositor.c +++ b/src/cairo-xlib-render-compositor.c @@ -1570,9 +1570,6 @@ check_composite_glyphs (const cairo_composite_rectangles_t *extents, * enough room for padding */ #define _cairo_sz_xGlyphElt (sz_xGlyphElt + 4) -#define PHASE(x) ((int)(floor (4 * (x + 0.125)) - 4 * floor (x + 0.125))) -#define POSITION(x) ((int) floor (x + 0.125)) - static cairo_int_status_t composite_glyphs (void *surface, cairo_operator_t op, @@ -1608,14 +1605,23 @@ composite_glyphs (void *surface, op = _render_operator (op), _cairo_xlib_surface_ensure_picture (dst); for (i = 0; i < num_glyphs; i++) { - unsigned long xphase, yphase; int this_x, this_y; int old_width; - xphase = PHASE(glyphs[i].d.x); - yphase = PHASE(glyphs[i].d.y); + if (info->font->options.subpixel_positions != CAIRO_SUBPIXEL_POSITIONS_OFF) { + int res = _cairo_subpixel_resolution (info->font->options.subpixel_positions); + unsigned long xphase, yphase; + xphase = _cairo_subpixel_phase (glyphs[i].d.x, res); + yphase = _cairo_subpixel_phase (glyphs[i].d.y, res); + glyphs[i].index |= (xphase << XSHIFT) | (yphase << YSHIFT); - glyphs[i].index |= (xphase << 24) | (yphase << 26); + this_x = _cairo_subpixel_position (glyphs[i].d.x, res); + this_y = _cairo_subpixel_position (glyphs[i].d.y, res); + } + else { + this_x = _cairo_lround (glyphs[i].d.x); + this_y = _cairo_lround (glyphs[i].d.y); + } status = _cairo_scaled_glyph_lookup (info->font, glyphs[i].index, @@ -1625,9 +1631,6 @@ composite_glyphs (void *surface, if (unlikely (status)) return status; - this_x = POSITION (glyphs[i].d.x); - this_y = POSITION (glyphs[i].d.y); - /* Send unsent glyphs to the server */ if (glyph->dev_private_key != display) { status = _cairo_xlib_surface_add_glyph (display, info->font, &glyph); diff --git a/src/cairo.h b/src/cairo.h index d53cc3d09..f9a42adb5 100644 --- a/src/cairo.h +++ b/src/cairo.h @@ -1437,6 +1437,27 @@ typedef enum _cairo_color_mode { CAIRO_COLOR_MODE_COLOR } cairo_color_mode_t; +/** + * cairo_subpixel_positions_t: + * @CAIRO_SUBPIXEL_POSITONS_DEFAULT: Use subpixel positions in + * the default manner for the font backend and target device, since 1.20 + * @CAIRO_SUBPIXEL_POSITIONS_OFF: Do not use subpixel positions, since 1.20 + * @CAIRO_SUBPIXEL_POSITIONS_ON: Use subpixel positions, since 1.20 + * @CAIRO_SUBPIXEL_POSITIONS_FINE: Use fine subpixel positions, since 1.20 + * + * Specifies whether to use subpixel positions when rendering glyphs. + * Without subpixel positions, glyph positions will be rounded to pixels. + * With subpixel positions, glyph positions rounded to a subpixel grid. + * + * Since: 1.20 + */ +typedef enum _cairo_subpixel_positions { + CAIRO_SUBPIXEL_POSITIONS_DEFAULT, + CAIRO_SUBPIXEL_POSITIONS_OFF, + CAIRO_SUBPIXEL_POSITIONS_ON, + CAIRO_SUBPIXEL_POSITIONS_FINE +} cairo_subpixel_positions_t; + /** * cairo_font_options_t: * @@ -1542,6 +1563,13 @@ cairo_font_options_get_custom_palette_color (cairo_font_options_t *options, double *red, double *green, double *blue, double *alpha); +cairo_public cairo_subpixel_positions_t +cairo_font_options_get_subpixel_positions (const cairo_font_options_t *options); + +cairo_public void +cairo_font_options_set_subpixel_positions (cairo_font_options_t *options, + cairo_subpixel_positions_t value); + /* This interface is for dealing with text as text, not caring about the font object inside the cairo_t. */ diff --git a/src/cairoint.h b/src/cairoint.h index b18a579be..e288d1302 100644 --- a/src/cairoint.h +++ b/src/cairoint.h @@ -400,10 +400,47 @@ cairo_private uintptr_t _cairo_hash_uintptr (uintptr_t hash, uintptr_t u); -/* We use bits 24-27 to store phases for subpixel positions */ -#define _cairo_scaled_glyph_index(g) ((unsigned long)((g)->hash_entry.hash & 0xffffff)) -#define _cairo_scaled_glyph_xphase(g) (int)(((g)->hash_entry.hash >> 24) & 3) -#define _cairo_scaled_glyph_yphase(g) (int)(((g)->hash_entry.hash >> 26) & 3) +/* We use the upper 8 bits to store phases for 64x64 subpixel positions */ + +static inline int _cairo_subpixel_bits (cairo_subpixel_positions_t subpixel_positions) +{ + int bits[] = { + [CAIRO_SUBPIXEL_POSITIONS_DEFAULT] = 2, + [CAIRO_SUBPIXEL_POSITIONS_OFF] = 0, + [CAIRO_SUBPIXEL_POSITIONS_ON] = 2, + [CAIRO_SUBPIXEL_POSITIONS_FINE] = 4, + }; + + return bits[subpixel_positions]; +} + +static inline int _cairo_subpixel_resolution (cairo_subpixel_positions_t subpixel_positions) +{ + return 1 << _cairo_subpixel_bits (subpixel_positions); +} + +static inline int _cairo_subpixel_phase (double pos, int res) +{ + double shifted_pos = pos + 1.0 / (2 * res); + return (int) (floor (res * shifted_pos) - res * floor (shifted_pos)); +} + +static inline double _cairo_subpixel_position (double pos, int res) +{ + return floor (pos + 1.0 / (2 * res)); +} + +#define MAX_PHASE_BITS 4 + +#define XSHIFT (32 - 2 * MAX_PHASE_BITS) +#define YSHIFT (32 - MAX_PHASE_BITS) + +#define PHASE_MASK ((1 << MAX_PHASE_BITS) - 1) +#define INDEX_MASK ((1 << XSHIFT) - 1) + +#define _cairo_scaled_glyph_index(g) ((unsigned long)((g)->hash_entry.hash & INDEX_MASK)) +#define _cairo_scaled_glyph_xphase(g) (unsigned int)(((g)->hash_entry.hash >> XSHIFT) & PHASE_MASK) +#define _cairo_scaled_glyph_yphase(g) ((unsigned int)(((g)->hash_entry.hash >> YSHIFT) & PHASE_MASK)) #define _cairo_scaled_glyph_set_index(g, i) ((g)->hash_entry.hash = (i)) #include "cairo-scaled-font-private.h" diff --git a/src/win32/cairo-dwrite-font.cpp b/src/win32/cairo-dwrite-font.cpp index 9f6d362b3..f9670effa 100644 --- a/src/win32/cairo-dwrite-font.cpp +++ b/src/win32/cairo-dwrite-font.cpp @@ -1109,8 +1109,13 @@ _cairo_dwrite_scaled_font_init_glyph_color_surface(cairo_dwrite_scaled_font_t *s FLOAT advance = 0; UINT16 index = (UINT16)_cairo_scaled_glyph_index (scaled_glyph); DWRITE_GLYPH_OFFSET offset; - double x = -x1 + .25 * _cairo_scaled_glyph_xphase (scaled_glyph); - double y = -y1 + .25 * _cairo_scaled_glyph_yphase (scaled_glyph); + double x = -x1; + double y = -y1; + if (scaled_font->base.options.subpixel_positions != CAIRO_SUBPIXEL_POSITIONS_OFF) { + double res = _cairo_subpixel_resolution (scaled_font->base.options.subpixel_positions); + x += _cairo_scaled_glyph_xphase (scaled_glyph) / res; + y += _cairo_scaled_glyph_yphase (scaled_glyph) / res; + } DWRITE_MATRIX matrix; D2D1_POINT_2F origin = {0, 0}; RefPtr run_enumerator;