# HG changeset patch # User Robert O'Callahan # Date 1294019288 -46800 # Node ID 8857392e37aea7475ed6d8ee4b45023e1233bcec # Parent c53f60831c43cca397dfed8adf8d350aeec7d3ca Bug 363861. Part 2: Introduce cairo_surface_get/set_subpixel_antialiasing. r=jrmuizel,sr=vlad,a=blocking diff --git a/gfx/cairo/cairo/src/cairo-quartz-surface.c b/gfx/cairo/cairo/src/cairo-quartz-surface.c --- a/gfx/cairo/cairo/src/cairo-quartz-surface.c +++ b/gfx/cairo/cairo/src/cairo-quartz-surface.c @@ -2473,16 +2473,17 @@ _cairo_quartz_surface_show_glyphs (void cairo_int_status_t rv = CAIRO_STATUS_SUCCESS; cairo_quartz_drawing_state_t state; float xprev, yprev; int i; CGFontRef cgfref = NULL; cairo_bool_t isClipping = FALSE; cairo_bool_t didForceFontSmoothing = FALSE; + cairo_antialias_t effective_antialiasing; if (IS_EMPTY(surface)) return CAIRO_STATUS_SUCCESS; if (num_glyphs <= 0) return CAIRO_STATUS_SUCCESS; if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_QUARTZ) @@ -2514,16 +2515,22 @@ _cairo_quartz_surface_show_glyphs (void goto BAIL; } /* this doesn't addref */ cgfref = _cairo_quartz_scaled_font_get_cg_font_ref (scaled_font); CGContextSetFont (state.context, cgfref); CGContextSetFontSize (state.context, 1.0); + effective_antialiasing = scaled_font->options.antialias; + if (effective_antialiasing == CAIRO_ANTIALIAS_SUBPIXEL && + !surface->base.permit_subpixel_antialiasing) { + effective_antialiasing = CAIRO_ANTIALIAS_GRAY; + } + switch (scaled_font->options.antialias) { case CAIRO_ANTIALIAS_SUBPIXEL: CGContextSetShouldAntialias (state.context, TRUE); CGContextSetShouldSmoothFonts (state.context, TRUE); if (CGContextSetAllowsFontSmoothingPtr && !CGContextGetAllowsFontSmoothingPtr (state.context)) { didForceFontSmoothing = TRUE; diff --git a/gfx/cairo/cairo/src/cairo-surface-private.h b/gfx/cairo/cairo/src/cairo-surface-private.h --- a/gfx/cairo/cairo/src/cairo-surface-private.h +++ b/gfx/cairo/cairo/src/cairo-surface-private.h @@ -58,16 +58,17 @@ struct _cairo_surface { cairo_reference_count_t ref_count; cairo_status_t status; unsigned int unique_id; unsigned finished : 1; unsigned is_clear : 1; unsigned has_font_options : 1; + unsigned permit_subpixel_antialiasing : 1; cairo_user_data_array_t user_data; cairo_user_data_array_t mime_data; cairo_matrix_t device_transform; cairo_matrix_t device_transform_inverse; /* The actual resolution of the device, in dots per inch. */ diff --git a/gfx/cairo/cairo/src/cairo-surface.c b/gfx/cairo/cairo/src/cairo-surface.c --- a/gfx/cairo/cairo/src/cairo-surface.c +++ b/gfx/cairo/cairo/src/cairo-surface.c @@ -49,17 +49,18 @@ const cairo_surface_t name = { \ NULL, /* backend */ \ CAIRO_SURFACE_TYPE_IMAGE, /* type */ \ CAIRO_CONTENT_COLOR, /* content */ \ CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ \ status, /* status */ \ 0, /* unique id */ \ FALSE, /* finished */ \ TRUE, /* is_clear */ \ - FALSE, /* has_font_options */ \ + FALSE, /* has_font_options */ \ + FALSE, /* permit_subpixel_antialiasing */ \ { 0, 0, 0, NULL, }, /* user_data */ \ { 0, 0, 0, NULL, }, /* mime_data */ \ { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, /* device_transform */ \ { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, /* device_transform_inverse */ \ 0.0, /* x_resolution */ \ 0.0, /* y_resolution */ \ 0.0, /* x_fallback_resolution */ \ 0.0, /* y_fallback_resolution */ \ @@ -342,46 +343,48 @@ _cairo_surface_init (cairo_surface_t * surface->content = content; surface->type = backend->type; CAIRO_REFERENCE_COUNT_INIT (&surface->ref_count, 1); surface->status = CAIRO_STATUS_SUCCESS; surface->unique_id = _cairo_surface_allocate_unique_id (); surface->finished = FALSE; surface->is_clear = FALSE; + surface->has_font_options = FALSE; + surface->permit_subpixel_antialiasing = TRUE; _cairo_user_data_array_init (&surface->user_data); _cairo_user_data_array_init (&surface->mime_data); cairo_matrix_init_identity (&surface->device_transform); cairo_matrix_init_identity (&surface->device_transform_inverse); surface->x_resolution = CAIRO_SURFACE_RESOLUTION_DEFAULT; surface->y_resolution = CAIRO_SURFACE_RESOLUTION_DEFAULT; surface->x_fallback_resolution = CAIRO_SURFACE_FALLBACK_RESOLUTION_DEFAULT; surface->y_fallback_resolution = CAIRO_SURFACE_FALLBACK_RESOLUTION_DEFAULT; _cairo_array_init (&surface->snapshots, sizeof (cairo_surface_t *)); surface->snapshot_of = NULL; - - surface->has_font_options = FALSE; } static void _cairo_surface_copy_similar_properties (cairo_surface_t *surface, cairo_surface_t *other) { if (other->has_font_options || other->backend != surface->backend) { cairo_font_options_t options; cairo_surface_get_font_options (other, &options); _cairo_surface_set_font_options (surface, &options); } + surface->permit_subpixel_antialiasing = other->permit_subpixel_antialiasing; + cairo_surface_set_fallback_resolution (surface, other->x_fallback_resolution, other->y_fallback_resolution); } cairo_surface_t * _cairo_surface_create_similar_scratch (cairo_surface_t *other, cairo_content_t content, @@ -2482,16 +2485,67 @@ cairo_surface_has_show_text_glyphs (cair if (surface->backend->has_show_text_glyphs) return surface->backend->has_show_text_glyphs (surface); else return surface->backend->show_text_glyphs != NULL; } slim_hidden_def (cairo_surface_has_show_text_glyphs); +/** + * cairo_surface_set_subpixel_antialiasing: + * @surface: a #cairo_surface_t + * + * Sets whether the surface permits subpixel antialiasing. By default, + * surfaces permit subpixel antialiasing. + * + * Enabling subpixel antialiasing for CONTENT_COLOR_ALPHA surfaces generally + * requires that the pixels in the areas under a subpixel antialiasing + * operation already be opaque. + * + * Since: 1.12 + **/ +void +cairo_surface_set_subpixel_antialiasing (cairo_surface_t *surface, + cairo_subpixel_antialiasing_t enabled) +{ + if (surface->status) + return; + + if (surface->finished) { + _cairo_surface_set_error (surface, CAIRO_STATUS_SURFACE_FINISHED); + return; + } + + surface->permit_subpixel_antialiasing = + enabled == CAIRO_SUBPIXEL_ANTIALIASING_ENABLED; +} +slim_hidden_def (cairo_surface_set_subpixel_antialiasing); + +/** + * cairo_surface_get_subpixel_antialiasing: + * @surface: a #cairo_surface_t + * + * Gets whether the surface supports subpixel antialiasing. By default, + * CAIRO_CONTENT_COLOR surfaces support subpixel antialiasing but other + * surfaces do not. + * + * Since: 1.12 + **/ +cairo_subpixel_antialiasing_t +cairo_surface_get_subpixel_antialiasing (cairo_surface_t *surface) +{ + if (surface->status) + return CAIRO_SUBPIXEL_ANTIALIASING_DISABLED; + + return surface->permit_subpixel_antialiasing ? + CAIRO_SUBPIXEL_ANTIALIASING_ENABLED : CAIRO_SUBPIXEL_ANTIALIASING_DISABLED; +} +slim_hidden_def (cairo_surface_get_subpixel_antialiasing); + /* Note: the backends may modify the contents of the glyph array as long as * they do not return %CAIRO_INT_STATUS_UNSUPPORTED. This makes it possible to * avoid copying the array again and again, and edit it in-place. * Backends are in fact free to use the array as a generic buffer as they * see fit. * * For show_glyphs backend method, and NOT for show_text_glyphs method, * when they do return UNSUPPORTED, they may adjust remaining_glyphs to notify diff --git a/gfx/cairo/cairo/src/cairo-win32-font.c b/gfx/cairo/cairo/src/cairo-win32-font.c --- a/gfx/cairo/cairo/src/cairo-win32-font.c +++ b/gfx/cairo/cairo/src/cairo-win32-font.c @@ -1380,16 +1380,17 @@ _cairo_win32_scaled_font_show_glyphs (vo cairo_win32_surface_t *surface = (cairo_win32_surface_t *)generic_surface; cairo_status_t status; if (width == 0 || height == 0) return CAIRO_STATUS_SUCCESS; if (_cairo_surface_is_win32 (generic_surface) && surface->format == CAIRO_FORMAT_RGB24 && + (generic_surface->permit_subpixel_antialiasing || scaled_font->quality != CLEARTYPE_QUALITY) && op == CAIRO_OPERATOR_OVER && _cairo_pattern_is_opaque_solid (pattern)) { cairo_solid_pattern_t *solid_pattern = (cairo_solid_pattern_t *)pattern; /* When compositing OVER on a GDI-understood surface, with a * solid opaque color, we can just call ExtTextOut directly. */ @@ -1411,16 +1412,18 @@ _cairo_win32_scaled_font_show_glyphs (vo * surface by drawing the the glyphs onto a DIB, black-on-white then * inverting. GDI outputs gamma-corrected images so inverted black-on-white * is very different from white-on-black. We favor the more common * case where the final output is dark-on-light. */ cairo_win32_surface_t *tmp_surface; cairo_surface_t *mask_surface; cairo_surface_pattern_t mask; + cairo_bool_t use_subpixel_antialiasing = + scaled_font->quality == CLEARTYPE_QUALITY && generic_surface->permit_subpixel_antialiasing; RECT r; tmp_surface = (cairo_win32_surface_t *)cairo_win32_surface_create_with_dib (CAIRO_FORMAT_ARGB32, width, height); if (tmp_surface->base.status) return tmp_surface->base.status; r.left = 0; r.top = 0; @@ -1432,17 +1435,17 @@ _cairo_win32_scaled_font_show_glyphs (vo scaled_font, RGB (0, 0, 0), dest_x, dest_y, glyphs, num_glyphs); if (status) { cairo_surface_destroy (&tmp_surface->base); return status; } - if (scaled_font->quality == CLEARTYPE_QUALITY) { + if (use_subpixel_antialiasing) { /* For ClearType, we need a 4-channel mask. If we are compositing on * a surface with alpha, we need to compute the alpha channel of * the mask (we just copy the green channel). But for a destination * surface without alpha the alpha channel of the mask is ignored */ if (surface->format != CAIRO_FORMAT_RGB24) _compute_argb32_mask_alpha (tmp_surface); @@ -1460,17 +1463,17 @@ _cairo_win32_scaled_font_show_glyphs (vo /* For op == OVER, no-cleartype, a possible optimization here is to * draw onto an intermediate ARGB32 surface and alpha-blend that with the * destination */ _cairo_pattern_init_for_surface (&mask, mask_surface); cairo_surface_destroy (mask_surface); - if (scaled_font->quality == CLEARTYPE_QUALITY) + if (use_subpixel_antialiasing) mask.base.has_component_alpha = TRUE; status = _cairo_surface_composite (op, pattern, &mask.base, &surface->base, source_x, source_y, 0, 0, dest_x, dest_y, diff --git a/gfx/cairo/cairo/src/cairo-xlib-surface.c b/gfx/cairo/cairo/src/cairo-xlib-surface.c --- a/gfx/cairo/cairo/src/cairo-xlib-surface.c +++ b/gfx/cairo/cairo/src/cairo-xlib-surface.c @@ -3570,16 +3570,17 @@ typedef struct _cairo_xlib_font_glyphset GlyphSet glyphset; cairo_format_t format; XRenderPictFormat *xrender_format; cairo_xlib_font_glyphset_free_glyphs_t *pending_free_glyphs; } cairo_xlib_font_glyphset_info_t; typedef struct _cairo_xlib_surface_font_private { cairo_scaled_font_t *scaled_font; + cairo_scaled_font_t *grayscale_font; cairo_xlib_hook_t close_display_hook; cairo_xlib_display_t *display; cairo_xlib_font_glyphset_info_t glyphset_info[NUM_GLYPHSETS]; } cairo_xlib_surface_font_private_t; /* callback from CloseDisplay */ static void _cairo_xlib_surface_remove_scaled_font (cairo_xlib_display_t *display, @@ -3599,16 +3600,20 @@ _cairo_xlib_surface_remove_scaled_font ( _cairo_scaled_font_reset_cache (scaled_font); CAIRO_MUTEX_UNLOCK (scaled_font->mutex); if (font_private != NULL) { Display *dpy; int i; + if (font_private->grayscale_font) { + cairo_scaled_font_destroy (font_private->grayscale_font); + } + dpy = _cairo_xlib_display_get_dpy (display); for (i = 0; i < NUM_GLYPHSETS; i++) { cairo_xlib_font_glyphset_info_t *glyphset_info; glyphset_info = &font_private->glyphset_info[i]; if (glyphset_info->glyphset) XRenderFreeGlyphSet (dpy, glyphset_info->glyphset); @@ -3629,16 +3634,17 @@ _cairo_xlib_surface_font_init (Display cairo_status_t status; int i; font_private = malloc (sizeof (cairo_xlib_surface_font_private_t)); if (unlikely (font_private == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); font_private->scaled_font = scaled_font; + font_private->grayscale_font = NULL; status = _cairo_xlib_display_get (dpy, &font_private->display); if (unlikely (status)) { free (font_private); return status; } /* initialize and hook into the CloseDisplay callback */ font_private->close_display_hook.func = @@ -3671,16 +3677,20 @@ _cairo_xlib_surface_scaled_font_fini (ca { cairo_xlib_surface_font_private_t *font_private; font_private = scaled_font->surface_private; if (font_private != NULL) { cairo_xlib_display_t *display; int i; + if (font_private->grayscale_font) { + cairo_scaled_font_destroy (font_private->grayscale_font); + } + display = font_private->display; _cairo_xlib_remove_close_display_hook (display, &font_private->close_display_hook); for (i = 0; i < NUM_GLYPHSETS; i++) { cairo_xlib_font_glyphset_info_t *glyphset_info; glyphset_info = &font_private->glyphset_info[i]; @@ -4417,16 +4427,62 @@ _cairo_xlib_surface_owns_font (cairo_xli (font_private != NULL && font_private->display != dst->display)) { return FALSE; } return TRUE; } +/* Gets a grayscale version of scaled_font. The grayscale version is cached + * in our surface_private data. + */ +static cairo_scaled_font_t * +_cairo_xlib_get_grayscale_font (cairo_xlib_surface_t *dst, + cairo_scaled_font_t *scaled_font) +{ + cairo_xlib_surface_font_private_t *font_private = scaled_font->surface_private; + cairo_bool_t needs_font; + + if (font_private == NULL) { + cairo_status_t status = _cairo_xlib_surface_font_init (dst->dpy, scaled_font); + if (unlikely (status)) + return _cairo_scaled_font_create_in_error (status); + font_private = scaled_font->surface_private; + } + + CAIRO_MUTEX_LOCK (scaled_font->mutex); + needs_font = !font_private->grayscale_font; + CAIRO_MUTEX_UNLOCK (scaled_font->mutex); + + if (needs_font) { + cairo_font_options_t options; + cairo_scaled_font_t *new_font; + + options = scaled_font->options; + options.antialias = CAIRO_ANTIALIAS_GRAY; + new_font = cairo_scaled_font_create (scaled_font->font_face, + &scaled_font->font_matrix, + &scaled_font->ctm, &options); + + CAIRO_MUTEX_LOCK (scaled_font->mutex); + if (!font_private->grayscale_font) { + font_private->grayscale_font = new_font; + new_font = NULL; + } + CAIRO_MUTEX_UNLOCK (scaled_font->mutex); + + if (new_font) { + cairo_scaled_font_destroy (new_font); + } + } + + return font_private->grayscale_font; +} + static cairo_int_status_t _cairo_xlib_surface_show_glyphs (void *abstract_dst, cairo_operator_t op, const cairo_pattern_t *src_pattern, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, cairo_clip_t *clip, @@ -4475,16 +4531,21 @@ _cairo_xlib_surface_show_glyphs (void operation = _categorize_composite_operation (dst, op, src_pattern, TRUE); if (operation == DO_UNSUPPORTED) return UNSUPPORTED ("unsupported op"); if (! _cairo_xlib_surface_owns_font (dst, scaled_font)) return UNSUPPORTED ("unowned font"); + if (!dst->base.permit_subpixel_antialiasing && + scaled_font->options.antialias == CAIRO_ANTIALIAS_SUBPIXEL) { + scaled_font = _cairo_xlib_get_grayscale_font (dst, scaled_font); + } + X_DEBUG ((dst->dpy, "show_glyphs (dst=%x)", (unsigned int) dst->drawable)); if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1) { cairo_rectangle_int_t glyph_extents; const cairo_rectangle_int_t *clip_extents; diff --git a/gfx/cairo/cairo/src/cairo.h b/gfx/cairo/cairo/src/cairo.h --- a/gfx/cairo/cairo/src/cairo.h +++ b/gfx/cairo/cairo/src/cairo.h @@ -2101,16 +2101,35 @@ cairo_public void cairo_surface_copy_page (cairo_surface_t *surface); cairo_public void cairo_surface_show_page (cairo_surface_t *surface); cairo_public cairo_bool_t cairo_surface_has_show_text_glyphs (cairo_surface_t *surface); +/** + * _cairo_subpixel_antialiasing_t: + * @CAIRO_SUBPIXEL_ANTIALIASING_ENABLED: subpixel antialiasing is enabled + * for this surface. + * @CAIRO_SUBPIXEL_ANTIALIASING_DISABLED: subpixel antialiasing is disabled + * for this surface. + */ +typedef enum _cairo_subpixel_antialiasing_t { + CAIRO_SUBPIXEL_ANTIALIASING_ENABLED, + CAIRO_SUBPIXEL_ANTIALIASING_DISABLED +} cairo_subpixel_antialiasing_t; + +cairo_public void +cairo_surface_set_subpixel_antialiasing (cairo_surface_t *surface, + cairo_subpixel_antialiasing_t enabled); + +cairo_public cairo_subpixel_antialiasing_t +cairo_surface_get_subpixel_antialiasing (cairo_surface_t *surface); + /* Image-surface functions */ /** * cairo_format_t: * @CAIRO_FORMAT_ARGB32: each pixel is a 32-bit quantity, with * alpha in the upper 8 bits, then red, then green, then blue. * The 32-bit quantities are stored native-endian. Pre-multiplied * alpha is used. (That is, 50% transparent red is 0x80800000, diff --git a/gfx/cairo/cairo/src/cairoint.h b/gfx/cairo/cairo/src/cairoint.h --- a/gfx/cairo/cairo/src/cairoint.h +++ b/gfx/cairo/cairo/src/cairoint.h @@ -2750,16 +2750,18 @@ slim_hidden_proto (cairo_surface_destroy slim_hidden_proto (cairo_surface_finish); slim_hidden_proto (cairo_surface_flush); slim_hidden_proto (cairo_surface_get_content); slim_hidden_proto (cairo_surface_get_device_offset); slim_hidden_proto (cairo_surface_get_font_options); slim_hidden_proto (cairo_surface_get_mime_data); slim_hidden_proto (cairo_surface_get_type); slim_hidden_proto (cairo_surface_has_show_text_glyphs); +slim_hidden_proto (cairo_surface_set_subpixel_antialiasing); +slim_hidden_proto (cairo_surface_get_subpixel_antialiasing); slim_hidden_proto (cairo_surface_mark_dirty_rectangle); slim_hidden_proto_no_warn (cairo_surface_reference); slim_hidden_proto (cairo_surface_set_device_offset); slim_hidden_proto (cairo_surface_set_fallback_resolution); slim_hidden_proto (cairo_surface_set_mime_data); slim_hidden_proto (cairo_surface_show_page); slim_hidden_proto (cairo_surface_status); slim_hidden_proto (cairo_text_cluster_allocate);