[OpenJDK 2D-Dev] Missing floating-point-precision method in font measuring API

Phil Race philip.race at oracle.com
Fri Dec 23 18:52:03 UTC 2016



On 12/20/2016 02:11 AM, Dmitry Batrak wrote:
> Missing floating-point-precision method in font measuring API
>
> Hello,
>
> It looks like there's currently a gap in text measuring API with 
> respect to
> methods working with floating point coordinates. It's not something 
> new, but
> with HiDPI support added in Java 9, it seems to become more significant.
>
> Consider the following simplified use case of custom text component which
> supports highlighting. Suppose we initially draw a couple of characters
> (in paintComponent method) like this
>
>   char[] text = new char[] {'a', 'b'};
>   int x = ..., y = ...;
>   g.drawChars(text, 0, 2, x, y);
>
> But then we need draw the second character in a different color, and 
> we'd like
> the glyph positions to be the same. We assume the text is simple (e.g. 
> Latin,
> without ligatures, etc). The simplest way to do it is
>
>   g.drawChars(text, 0, 1, x, y);
>   g.setColor(...);
>   int advance = g.getFontMetrics().charWidth(text[0]);
>   g.drawChars(text, 1, 1, x + advance, y);

Even though Swing has been doing this, we've frowned on this for a long time
because there are many rendering attributes that might affect this and it
pre-supposes an unscaled graphics. You don't really need a hidpi device,
you just need a transform for that logic to become a problem.
And that also supposes that you *know* the user didn't specify any 
non-obvious
attributes on the font. There is even a "hasLayoutAttributes()" method that
should be called before you even attempt such logic.
And you still need to know its not complex text.

>
> This will not work though, if advance is not integer. We wouldn't be 
> affected
> by this issue, if we didn't use fractional metrics, but in HiDPI case
> the advance can fractional even if fractional metrics are not used -
> glyph is positioned at integer coordinates in device space, so e.g. with
> 200% scale, in user space it can contain half-pixels. Current solution is
>
>  float advance = (float)g.getFontMetrics().getStringBounds(text, 0, 1, 
> g).x;

Is that actual code, or just trying to give an idea of what you are doing.
"x" is (a) only available if you know you can cast to Rectangle, and I 
don't think you can
and (b) x would give you the logical position of the char at index 0 .. 
which should
always be 0.0 .. not the advance from there to the char at index 1.

I also don't understand why you aren't using the APIs on Font directly
if efficiency is a concern but as you note here ...

>  g.drawString(new String(text, 1, 1), x + advance, (float)y);
>
> Alternatively, we can use font.createGlyphVector() instead of 
> getStringBounds
> (this is done internally anyway), but in any case an instance of 
> GlyphVector
> will be created in the process, which seems unnecessary

... we are creating a GlyphVector which is a lot more heavyweight than a 
Rectangle
so you won't see a lot of savings.

> - all we need is advance
> which is already available, we just cannot get it directly without it 
> being
> rounded.

You can definitely get it using getStringBounds() as you noted.
You mean you cannot get it without the extra overhead ?


> Creation of new String instance, copying underlying text, also doesn't
> seem to be necessary, but there's no other method to draw part of 
> existing text
> using floating point coordinates.
>
> Do you think adding corresponding new floating-point-based method to 
> FontMetrics
> (and maybe to Graphics2D) makes sense? Maybe it's planned already as 
> part of
> HiDPI-related activity? Should I raise an RFE via bugs.java.com 
> <http://bugs.java.com> for this?


Java 2D in JDK 1.2 added the floating point APIs to Font
I am not aware of anything you might need to do at an API level that 
isn't there.
The question seems to be more about a more lightweight implementation.

What I think you are asking for is basically
public float java.awt.Font.getStringAdvance(String s, FontRenderContext frc)
and/or
public float java.awt.Font.getStringAdvance(char[] chs, int begin, int 
limit,  FontRenderContext frc)

Although if we accept that the Rectangle is just a minor problem, then
perhaps updating the implementation of getStringBound to avoid the 
GlyphVector
in the same cases that we'd be able to do with a proposed getStringAdvance.

That would get you 95% of the available performance gain with no need to 
change code.
And it would avoid needing to create new API after JDK 9 feature freeze 
which
was reached this week.

I think that is something that would be expected to be done if we added 
a new API anyway.


-phil.

>
> Thanks,
> Dmitry Batrak
>
>

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.openjdk.java.net/pipermail/2d-dev/attachments/20161223/c9bec812/attachment.html>


More information about the 2d-dev mailing list