Closed Bug 831277 Opened 12 years ago Closed 12 years ago

round glyph positions appropriately in the graphite shaper

Categories

(Core :: Graphics: Text, defect)

defect
Not set
normal

Tracking

()

RESOLVED FIXED
mozilla21

People

(Reporter: jfkthame, Assigned: jfkthame)

References

Details

Attachments

(4 files)

The graphite shaper doesn't handle the rounding of glyph positions (returned in floating-point by graphite) to appunits or device pixels properly, which results in minor positioning discrepancies between graphite and opentype/harfbuzz shaping even when results might be expected to match. (I think this is the reason for bug 700022 comment 7.) We should fix the rounding to give more consistent results.
Blocks: 700023
Can you post or link to a simple testcase?  Doesn't need to be a reftest, just something to evaluate the changes being made.  And this only affects WinXP by default, or possibly someone on Win7 if they've messed about with the ClearType params?
(In reply to John Daggett (:jtd) from comment #3)
> Can you post or link to a simple testcase?  Doesn't need to be a reftest,
> just something to evaluate the changes being made.

You can see the effect on OS X if you load
  http://people.mozilla.org/~jkew/charis-test.html
and then (using a separate window), toggle graphite rendering on and off. Note how the glyph positions shift slightly.

With the patch here, the positions stay unchanged when switching between opentype and graphite rendering.

Similarly, on Linux this eliminates the opentype/graphite discrepancy. The adjustments are subtle, but make a definite improvement; see attached screenshots.

>  And this only affects
> WinXP by default, or possibly someone on Win7 if they've messed about with
> the ClearType params?

On Windows, there's a different issue: opentype and graphite metrics/positions differ more significantly than on OS X or Linux, and this patch doesn't eliminate that discrepancy. That's because it turns out gfxGraphiteShaper hasn't actually hooked up the option to use hinted glyph advances from the font, so it's using unhinted widths, while harfbuzz uses hinted widths which on windows can be substantially different. I'll file this as a separate bug.
Comment on attachment 702800 [details] [diff] [review]
b. properly round glyph positions to appunits or pixels (as appropriate for the platform) in the graphite shaper

> -        uint32_t appAdvance = adv * dev2appUnits;
> +        uint32_t appAdvance = roundX ? NSToIntRound(adv) * dev2appUnits :
> +                                       NSToIntRound(adv * dev2appUnits);
> 
> -                d->mYOffset = -yLocs[j] * dev2appUnits;
> +                d->mYOffset = roundY ? NSToIntRound(-yLocs[j]) * dev2appUnits :
> +                              -yLocs[j] * dev2appUnits;
> 
> -                    d->mXOffset = dev2appUnits *
> -                        (rtl ? (xLocs[j] - clusterLoc) :
> -                               (xLocs[j] - clusterLoc - adv));
> +                    float dx = rtl ? (xLocs[j] - clusterLoc) :
> +                                     (xLocs[j] - clusterLoc - adv);
> +                    d->mXOffset = roundX ? NSToIntRound(dx) * dev2appUnits :
> +                                           dx * dev2appUnits;

Hmmm, why are you introducing the rounding in the *non-rounding* case
(i.e. NSToIntRound(adv * dev2appUnits)) and only for the base
character xAdvance and not for the yAdvance?  That seems like an odd
change.
Comment on attachment 702799 [details] [diff] [review]
a. make GetRoundOffsetsToPixels a method on gfxContext, instead of private to the harfbuzz shaper

> +#ifdef XP_WIN
> +#include "gfxWindowsPlatform.h"
> +#endif

Minor nit but this should probably be conditionalized on
CAIRO_HAS_DWRITE_FONT instead of XP_WIN.

r+ with that fixed.
Attachment #702799 - Flags: review?(jdaggett) → review+
(In reply to John Daggett (:jtd) from comment #7)
> Comment on attachment 702800 [details] [diff] [review]
> b. properly round glyph positions to appunits or pixels (as appropriate for
> the platform) in the graphite shaper
> 
> > -        uint32_t appAdvance = adv * dev2appUnits;
> > +        uint32_t appAdvance = roundX ? NSToIntRound(adv) * dev2appUnits :
> > +                                       NSToIntRound(adv * dev2appUnits);
> > 
> > -                d->mYOffset = -yLocs[j] * dev2appUnits;
> > +                d->mYOffset = roundY ? NSToIntRound(-yLocs[j]) * dev2appUnits :
> > +                              -yLocs[j] * dev2appUnits;
> > 
> > -                    d->mXOffset = dev2appUnits *
> > -                        (rtl ? (xLocs[j] - clusterLoc) :
> > -                               (xLocs[j] - clusterLoc - adv));
> > +                    float dx = rtl ? (xLocs[j] - clusterLoc) :
> > +                                     (xLocs[j] - clusterLoc - adv);
> > +                    d->mXOffset = roundX ? NSToIntRound(dx) * dev2appUnits :
> > +                                           dx * dev2appUnits;
> 
> Hmmm, why are you introducing the rounding in the *non-rounding* case
> (i.e. NSToIntRound(adv * dev2appUnits)) 

Even when we're not rounding positions to whole pixels, we're converting the floating-point advance from graphite to an integer number of appUnits. It's better to round than truncate for that conversion. This fixes the (slight) discrepancy in positioning that you'll see if you toggle graphite on/off on OS X with the Charis sample mentioned above.

>and only for the base
> character xAdvance and not for the yAdvance?  That seems like an odd
> change.

There's no yAdvance, I assume you mean the offsets. The x- and y-offsets are stored as floating-point fields in the DetailedGlyph record, whereas the (x-)advance is an integer. So we can retain floating-point precision for the offsets (if any), but the advance is always quantized to appUnits, even if we aren't doing pixel-snapping.
Attachment #702800 - Flags: review?(jdaggett) → review+
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: