[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