[cairo] Problem with text rendering on Win32+ClearType, and
possible fix
Owen Taylor
otaylor at redhat.com
Wed Oct 4 08:35:38 PDT 2006
On Wed, 2006-10-04 at 03:28 +0300, Tor Lillqvist wrote:
> I have for a long time been noticing a problem you can see in the
> screenshots http://www.saunalahti.fi/tlillqvi/ctbug.png and
> http://www.saunalahti.fi/tlillqvi/ctbug2.png (note how the "1" digits
> that occur as the final character of a string have been partially cut
> off). This problem happens only when ClearType is active, and it
> started happening between cairo 1.0.2 and 1.0.4 (yes, that long
> ago...). Today I finally did some searching for the root cause.
>
> At first, the problem seemed stem from the fact that starting with
> 1.0.4, cairo_win32_surface_create() sets the surface format to
> CAIRO_FORMAT_ARGB32 if GetDeviceCaps(hdc,BITSPIXEL) returns 32. (In
> 1.0.2, the format was always set to CAIRO_FORMAT_RGB24.) If the
> surface format is ARGB32, _cairo_win32_scaled_font_show_glyphs() uses
> its software fallback branch, and I thought, maybe that code is buggy.
>
> If I changed cairo_win32_surface_create() to use RGB24 also when
> BITSPIXEL is 32, the problem went away, as
> _cairo_win32_scaled_font_show_glyphs() then uses the "direct"
> branch. But this seemed like using a awfully large hammer to fix the
> problem.
But it may be the right hammer none-the-less. If the second branch
of _cairo_win32_scaled_font_show_glyphs is always getting called,
well, that's basically a disaster:
- The performance is much, much worse
- The quality is much, much worse (it's not very obvious for
black-text on white-background, but if you try white-text
on a black-background, the result is horrid)
While the documentation of BITSPIXEL "Number of adjacent color
bits for each pixel" makes it sound like like the X concept of
"depth" - number of *used* bits per pixel, if you are seeing a value of
32 in normal cases, then it's pretty clearly the X concept of
"bits per pixel" - the number of *total* bits per pixel.
I'm not really sure how to extract the diff and commit message
for the 1.0.2 => 1.0.4 change.
The only reason I could see for the change is if someone had a
ARGB surface stored in a win32 DIB/DDB and wanted to use it as
a *source* surface. But that can only properly be handled with
a separate constructor. AFAIK Win32 does not know how to use a ARGB
surface as a drawing *destination* so marking all Win32 surfaces
created from a HDC as ARGB32 means one of two things:
1) We are never using the GDI-accelerated fast paths
2) We are using the GDI-accelerated fast paths when we shouldn't,
and the result wouldn't be correct if the surface was
truly ARGB32
[ Not sure what is up with the || dst->format == CAIRO_FORMAT_ARGB32
in _cairo_win32_surface_composite's ... does AlphaBlend really
handle that ARGB destinations correctly? Or was that just hacked
in when someone noticed that cairo was never using AlphaBlend? ]
It's just a bad idea all around to treat all xRGB surfaces as ARGB
surfaces.
> I then tried if I could fix the problem by some clever hacking on the
> code in the software fallback branch in
> _cairo_win32_scaled_font_show_glyphs(). What worked was to use width+1
> and height+1 instead of width and height there in all places. But that
> didn't really seem proper either. Anyway, clearly the problem had
> something to do with the metrics of glyph strings to be rendered.
>
> I then looked for a way to make cairo use slightly wider and higher
> metrics for a string to be rendered. It turns out that adding one to
> the GLYPHMETRICS::gmBlackBoxX and GLYPHMETRICS::gmBlackBoxY that are
> filled in by the GetGlyphOutlineW() calls in
> _cairo_win32_scaled_font_init_glyph_metrics() had the desired effect.
>
> Could this be the proper fix? Is there a fencepost error in the
> interpretation of the GLYPHMETRICS::gmBlackBoxX and gmBlackBoxY
> values?
This isn't *the* proper fix, but it could possibly be *a* proper
fix. I'm skeptical, however... the docs for GLPYHMETRICS are
pretty unambiguous. I would rather suspect a rounding
problem elsewhere that is causing a pixel to be dropped.
> I.e., my potential patch is:
>
> --- 1.2.4/src/cairo-win32-font.c Fri Aug 18 17:20:16 2006
> +++ 1.2.4-tml/src/cairo-win32-font.c Wed Oct 4 02:42:27 2006
> @@ -773,15 +773,15 @@ _cairo_win32_scaled_font_init_glyph_metr
> if (scaled_font->swap_axes) {
> extents.x_bearing = - metrics.gmptGlyphOrigin.y / scaled_font->y_scale;
> extents.y_bearing = metrics.gmptGlyphOrigin.x / scaled_font->x_scale;
> - extents.width = metrics.gmBlackBoxY / scaled_font->y_scale;
> - extents.height = metrics.gmBlackBoxX / scaled_font->x_scale;
> + extents.width = (metrics.gmBlackBoxY+1) / scaled_font->y_scale;
> + extents.height = (metrics.gmBlackBoxX+1) / scaled_font->x_scale;
> extents.x_advance = metrics.gmCellIncY / scaled_font->x_scale;
> extents.y_advance = metrics.gmCellIncX / scaled_font->y_scale;
> } else {
> extents.x_bearing = metrics.gmptGlyphOrigin.x / scaled_font->x_scale;
> extents.y_bearing = - metrics.gmptGlyphOrigin.y / scaled_font->y_scale;
> - extents.width = metrics.gmBlackBoxX / scaled_font->x_scale;
> - extents.height = metrics.gmBlackBoxY / scaled_font->y_scale;
> + extents.width = (metrics.gmBlackBoxX+1) / scaled_font->x_scale;
> + extents.height = (metrics.gmBlackBoxY+1) / scaled_font->y_scale;
> extents.x_advance = metrics.gmCellIncX / scaled_font->x_scale;
> extents.y_advance = metrics.gmCellIncY / scaled_font->y_scale;
> }
> @@ -811,8 +811,8 @@ _cairo_win32_scaled_font_init_glyph_metr
>
> extents.x_bearing = (double)metrics.gmptGlyphOrigin.x / scaled_font->em_square;
> extents.y_bearing = - (double)metrics.gmptGlyphOrigin.y / scaled_font->em_square;
> - extents.width = (double)metrics.gmBlackBoxX / scaled_font->em_square;
> - extents.height = (double)metrics.gmBlackBoxY / scaled_font->em_square;
> + extents.width = (double)(metrics.gmBlackBoxX+1) / scaled_font->em_square;
> + extents.height = (double)(metrics.gmBlackBoxY+1) / scaled_font->em_square;
> extents.x_advance = (double)metrics.gmCellIncX / scaled_font->em_square;
> extents.y_advance = (double)metrics.gmCellIncY / scaled_font->em_square;
> }
More information about the cairo
mailing list