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

Dmitry Batrak dmitry.batrak at jetbrains.com
Mon Dec 26 11:42:48 UTC 2016

Thanks for your response!
Sorry about not mentioning it explicitly in the original email, but it was
about functionality gap - we can definitely achieve required behaviour using
already existing API. It was about performance and simplicity of the code
to achieve that.

> You don't really need a hidpi device, you just need a transform for that
> to become a problem.
Graphics transform doesn't represent a problem for our case, as we deliver
application (where user only chooses font name and size), not a text
to our users, and transform is under our control (we don't use any). On the
other hand, we cannot control what screen type our application is being
used on.

Complex text, surely, needs to be handled differently, and we do handle it
differently (using Font.layoutGlyphVector). But as our use case is a code
editor, most of the actual text is simple, and a separate code path for it,
for optimization purposes, seemed like a good idea. This changed somewhat
programming fonts with ligatures became popular, so maybe, in future, always
working with glyph vectors will be a simpler option.

As for the code sample with getStringBounds, you're right, it should use
getWidth() instead of 'x'. The code was not real - it's just an
We've not settled down on a solution, supporting hidpi screens yet. We can
probably live with the existing API, I just wanted to highlight that making
quite simple things with it now requires more complex code. Optimising
getStringBounds can be useful, but introducing something like 'charWidth2D'
looked conceptually simpler for me (I understand though, that it might be
late for Java 9). And 'charWidth', as well as other integer-returning
methods just become much less useful in Java 9, taking into account
of hidpi monitors.

Best regards,
Dmitry Batrak

On Fri, Dec 23, 2016 at 9:52 PM, Phil Race <philip.race at oracle.com> wrote:

> 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 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/20161226/83d36493/attachment.html>

More information about the 2d-dev mailing list