Bug 60317 - Text is scaled in a stair-step pattern
: Text is scaled in a stair-step pattern
Status: RESOLVED FIXED
: WebKit
Text
: 528+ (Nightly build)
: Unspecified Unspecified
: P2 Normal
Assigned To:
:
:
:
: 60318
  Show dependency treegraph
 
Reported: 2011-05-05 17:04 PST by
Modified: 2012-10-16 17:49 PST (History)


Attachments
Test Case (4.82 KB, text/html)
2011-05-05 17:04 PST, Levi Weintraub
no flags Details
demo of SVG text scaling smoothly using transforms (383 bytes, image/svg+xml)
2011-05-17 19:22 PST, Eric Seidel
no flags Details
Font-size doesn't solve this problem either (2.95 KB, text/html)
2011-05-18 10:34 PST, Levi Weintraub
no flags Details
Patch (46.18 KB, patch)
2011-05-31 16:35 PST, Levi Weintraub
no flags Review Patch | Details | Formatted Diff | Diff
Patch (93.57 KB, patch)
2011-06-01 09:45 PST, Levi Weintraub
no flags Review Patch | Details | Formatted Diff | Diff
Patch (60.57 KB, patch)
2011-06-01 11:10 PST, Levi Weintraub
no flags Review Patch | Details | Formatted Diff | Diff


Note

You need to log in before you can comment on or make changes to this bug.


Description From 2011-05-05 17:04:16 PST
Applying css zoom or svg scale to text will snap by font size and not scale evenly. See attached test case.

http://crbug.com/80578
------- Comment #1 From 2011-05-05 17:04:43 PST -------
Created an attachment (id=92504) [details]
Test Case
------- Comment #2 From 2011-05-06 11:15:20 PST -------
Currently the code explicitly avoids scaling glyphs, which prevents us from "smoothly" scaling up text. Hyatt knows this code best and understands the rationale behind this decision, I'd love some background and recommendations on how we can maintain our text rendering quality and attain this increased granularity!
------- Comment #3 From 2011-05-17 16:44:24 PST -------
I'm having some trouble here. Even feeding floats into the CG font system doesn't make font measuring scale smoothly.

Asking the font system for a font size of 8 works fine, but a font size of 9-12 (pixels) all appear identical. Changing the WebKit font system to request fonts by float doesn't make a difference.

Finally, because I can't tell the *actual* font size being returned, I can't adjust the graphics context to compensate by the proper amount and scale the glyphs.

This was all done on OSX. Anyone out there who knows CGFonts well that can help? I know entering glyph-scaling land isn't ideal, but certainly this behavior in SVG particularly is very bad.
------- Comment #4 From 2011-05-17 16:59:56 PST -------
SVG scales glyphs by applying a transform to them. :)
------- Comment #5 From 2011-05-17 18:46:47 PST -------
(In reply to comment #4)
> SVG scales glyphs by applying a transform to them. :)

I believe that is false :(

If it were true, I think we'd only see this issue in CSS but no SVG. You can see in SVGInlineTextBox::paintTextWithShadows it rescales the graphics context by the inverse of the scalingFactor, then depends on the Font to draw at the proper scale. Unfortunately, there's no feedback between the font the font system returns and the one requested. Hence, requesting a 13 and 14pt font will return the same thing, much like requesting a 6pt font scaled to 1.1 and 1.2x size.
------- Comment #6 From 2011-05-17 18:52:31 PST -------
I don't mean with zoom.  I mean with transforms.

<svg>
<text id="text" transform="scale(4)" style="font: Times 24px">HELLO</text>
</svg>

I suspect if we added an <animateTransform target="text" from="scale(1)" to="scale(4)" duration="4s" /> in there, it would be smooth.
------- Comment #7 From 2011-05-17 18:59:04 PST -------
(In reply to comment #6)
> I don't mean with zoom.  I mean with transforms.
> 
> <svg>
> <text id="text" transform="scale(4)" style="font: Times 24px">HELLO</text>
> </svg>
> 
> I suspect if we added an <animateTransform target="text" from="scale(1)" to="scale(4)" duration="4s" /> in there, it would be smooth.

But you can see in the attached test case that just using scale values doesn't scale smoothly. In fact, as I've seen, it explicitly relies on the font system to do this for it, and the font system doesn't work as a developer would expect.
------- Comment #8 From 2011-05-17 19:22:52 PST -------
Created an attachment (id=93858) [details]
demo of SVG text scaling smoothly using transforms

Note that due to bugs in WebCore, SVG animation only works when SVG is parsed as XML (and gets an SVGDocument as root), animation does not work when SVG is used in HTML5.
------- Comment #9 From 2011-05-18 08:28:56 PST -------
Some background:

WebCore/platform/graphics/mac/FontCacheMac.mm:

FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontDescription, const AtomicString& family)
{
    NSFontTraitMask traits = fontDescription.italic() ? NSFontItalicTrait : 0;
    NSInteger weight = toAppKitFontWeight(fontDescription.weight());
    float size = fontDescription.computedPixelSize();

    NSFont *nsFont = [WebFontCache fontWithFamily:family traits:traits weight:weight size:size];
..

The font is always requested with the "computedPixelSize" font size, not the real floating-point font size.
The computedPixelSize is an integer value, not a float as might be suggested by reading the code.

I ran across this problem a while ago as well.
Levi, does that help?
------- Comment #10 From 2011-05-18 10:00:57 PST -------
(In reply to comment #8)
> Created an attachment (id=93858) [details] [details]
> demo of SVG text scaling smoothly using transforms
> 
> Note that due to bugs in WebCore, SVG animation only works when SVG is parsed as XML (and gets an SVGDocument as root), animation does not work when SVG is used in HTML5.

It definitely worked in october last year. Are you sure about that Eric? I made a presentation by using the HTML5rocks slides and embedded SVG elements with SMIL animations. It worked!
------- Comment #11 From 2011-05-18 10:34:35 PST -------
Created an attachment (id=93932) [details]
Font-size doesn't solve this problem either

(In reply to comment #9)
> I ran across this problem a while ago as well.
> Levi, does that help?

Thanks for taking a look!

I actually tried piping floating point sizes down to CGFont (which takes a float for its size) and found that it makes no difference. Requesting fonts of different pixel sizes doesn't even always result in different-sized fonts, which I believe illustrates this point quite well. I wish there were better feedback from the font system as to the *actual* size of the returned font, and how it differs from the specified size.
------- Comment #12 From 2011-05-18 15:00:14 PST -------
I managed to get the attached test case working properly by scaling the specified Font and not requesting the closest matching one. I ran into a snag when running the regression tests with this change because various SVG tests pick extremely small fonts and scale them up by 50x or more. The font engine throws out fonts that are too small to render, and we end up not displaying anything...

I feel like I'm back to square one. Ideally we could pick the closest font and scale it by the delta between what was provided and what was requested, but I still don't know how to figure out what the *actual* size CGFont we were given is! Any ideas?
------- Comment #13 From 2011-05-19 00:47:43 PST -------
(In reply to comment #12)
> I managed to get the attached test case working properly by scaling the specified Font and not requesting the closest matching one. I ran into a snag when running the regression tests with this change because various SVG tests pick extremely small fonts and scale them up by 50x or more. The font engine throws out fonts that are too small to render, and we end up not displaying anything...
> 
> I feel like I'm back to square one. Ideally we could pick the closest font and scale it by the delta between what was provided and what was requested, but I still don't know how to figure out what the *actual* size CGFont we were given is! Any ideas?

Hmm, I'm not sure I follow. For SVG we recently changed the way we request fonts. Before we always used the actual specified font size:
<svg viewBox="0 0 5 5">
<text font-size="1" y="1">A</text>
</svg>

So we requested a font with size 1, and then just rendered it on the context, praying for the graphics engine to handle it correctly. What we do now, is calculate the scaling factor, to get the real font size that will appear on the screen (eg. here sth. like 40, if the width/height of the browser window is large enough). Then we scale the current context by the inverse scaling factor, render the text, and scale back.
This way we get antialiasing and hinting working correctly in arbitary transformed documents.

Okay, I think I should be more concrete:
Say we have a document with width=1000, height=1000. Then the <svg> from the example above is scaled to fit in that document. The text appears at y=200, and has a font size of 200.

When painting, the RenderSVGRoot object scales the content by 200, then the font is rendered using font size 200, to do that we scale the context by 1/200, draw the font with size=200, and scale again.
Obviously only text needs this trick, any Path can be arbitary transformed, w/o loosing precision....

I hope that explains what SVG is doing ..... though the bug you've found is still valid, but has nothing to do with that part of the SVG text handling.
------- Comment #14 From 2011-05-19 11:28:52 PST -------
(In reply to comment #13)
> I hope that explains what SVG is doing ..... though the bug you've found is still valid, but has nothing to do with that part of the SVG text handling.

Thanks again for the great description. What SVG is doing now makes perfect sense, and ideally we'd just tweak the current algorithm. Where we're currently scaling the graphics context by 1 / scalingFactor (SVGInlineTextBox::paintTextWithShadows and elsewhere), I think we should also be scaling it by the difference between the requested size and the actual. There's just no way to figure out what that delta is from the current font system... at least that I can find.
------- Comment #15 From 2011-05-19 11:32:30 PST -------
(In reply to comment #14)
> (In reply to comment #13)
> > I hope that explains what SVG is doing ..... though the bug you've found is still valid, but has nothing to do with that part of the SVG text handling.
> 
> Thanks again for the great description. What SVG is doing now makes perfect sense, and ideally we'd just tweak the current algorithm. Where we're currently scaling the graphics context by 1 / scalingFactor (SVGInlineTextBox::paintTextWithShadows and elsewhere), I think we should also be scaling it by the difference between the requested size and the actual. There's just no way to figure out what that delta is from the current font system... at least that I can find.

I'm sure there is CoreGraphics/CoreText SPI which Apple could add to WKSystemInterface if this ends up being the way to go.

Can your solution be implemented for other graphics libraries first?
------- Comment #16 From 2011-05-31 16:35:46 PST -------
Created an attachment (id=95508) [details]
Patch
------- Comment #17 From 2011-05-31 23:33:00 PST -------
(From update of attachment 95508 [details])
View in context: https://bugs.webkit.org/attachment.cgi?id=95508&action=review

Good catch Levi, can you tweak the ChangeLog? Then I'll r+.

> LayoutTests/ChangeLog:8
> +        Scaling the specified font in SVG when font-rendering: geometricPrecision is specified.

I'm not gettin' this sentence? :-) Don't you want to say:
"Stop scaling the specified font to the actual on-screen value, when font-rendering: geometricPrecision is specified", something like that.
------- Comment #18 From 2011-05-31 23:41:59 PST -------
(From update of attachment 95508 [details])
Might be useful to have your test case compare geometricPrecision cases vs. not.
------- Comment #19 From 2011-05-31 23:44:30 PST -------
http://www.w3.org/1999/07/06/WD-SVG-19990706/render.html
geometric-precision
Indicates that the user agent should emphasize geometric precision over legibility and rendering speed. This option will usually cause the user agent to suspend the use of hinting so that glyph outlines are drawn with comparable geometric precision to the rendering of path data.
------- Comment #20 From 2011-06-01 09:45:12 PST -------
Created an attachment (id=95614) [details]
Patch
------- Comment #21 From 2011-06-01 09:54:41 PST -------
(From update of attachment 95614 [details])
Ok, so now you'll want to avoid scrollbars to avoid needing paltform results.  You'll also want to use ahem. :)
------- Comment #22 From 2011-06-01 10:41:17 PST -------
(In reply to comment #21)
> (From update of attachment 95614 [details] [details])
> Ok, so now you'll want to avoid scrollbars to avoid needing paltform results.  You'll also want to use ahem. :)

D'oh! I got that part right the first time at least! Thanks, I'll fix it :)
------- Comment #23 From 2011-06-01 11:09:37 PST -------
(In reply to comment #21)
> (From update of attachment 95614 [details] [details])
> Ok, so now you'll want to avoid scrollbars to avoid needing paltform results.  You'll also want to use ahem. :)

Scrollbars are easy, but Ahem makes this test much harder to follow...
------- Comment #24 From 2011-06-01 11:10:20 PST -------
Created an attachment (id=95629) [details]
Patch
------- Comment #25 From 2011-06-01 12:28:49 PST -------
(From update of attachment 95629 [details])
View in context: https://bugs.webkit.org/attachment.cgi?id=95629&action=review

I'm not sure why ahem makes this any harder?  I suspect due to platform differences you'll need multiple image results, which is sad.  There may be a set of safe fonts to use, I just know that ahem is one such safe font.

> LayoutTests/svg/text/scaling-font-with-geometric-precision.html:42
> +<text class="geometric" transform="scale(1.2)" dy="15" fill="navy">iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii</text>

I would have probably generated these with javascript.  BUt it's OK as is too.
------- Comment #26 From 2011-06-01 14:00:59 PST -------
(From update of attachment 95629 [details])
Clearing flags on attachment: 95629

Committed r87846: <http://trac.webkit.org/changeset/87846>