[OpenJDK 2D-Dev] HiDPI support issues on Windows
Anton Tarasov
anton.tarasov at jetbrains.com
Mon Oct 17 10:03:35 UTC 2016
Thanks a lot, Jim! This sheds light on the subject and really sounds
promising.
Anton.
On 10/6/2016 9:31 PM, Jim Graham wrote:
> Ah, I see.
>
> There are a lot of mistaken assumptions in the rendering there. It's
> not just line thickness, it is assumptions about stroke control and
> positioning of strokes.
>
> The biggest issue seems to be that developers (including our own
> internal Swing developers who heard this lecture from me decades ago,
> but ignored it because it was inconvenient and not yet relevant at the
> time) who use the integer-based draw commands assume that their lines
> are centered on the pixel that they are naming. In other words "if I
> draw a line along x=5 then I am telling the system to fill all the
> pixels with an X coordinate of 5", but that is not what that drawing
> request is asking for. The coordinates are at pixel edges and it is
> an artifact of our fill/stroke rules and stroke control hinting that
> has made this work out at 1:1 scaling. As soon as you scale you can
> see the issues. We used to only scale for printing, but now we are
> starting to scale for the screen.
>
> g.drawRect(0,0,w-1,h-1) is a completely disfunctional way to outline a
> component with any settings other than 1:1 coordinates and
> STROKE_CONTROL on. I've been mentioning this for years (going on
> decades now), but Swing was addicted to that boilerplate for drawing a
> border. Thankfully, FX and its CSS-focused skinning has gone with a
> different mechanism (primarily most FX components are outlined using
> successive fills - optimized in implementation to avoid overdrawing -
> rather than using strokes based on mistaken assumptions about where
> the line is drawn).
>
> In particular, the line in that example g.drawRect(0,0,w-1,h-1)
> technically occurs at the following coordinates:
>
> outer part of outline goes from 0.0-0.5,0.0-0.5 to w-1+0.5,h-1+0.5
> (i.e. -0.5,-0.5 to w-0.5,h-0.5)
> inner part of outline goes from 0.0+0.5,0.0+0.5 to w-1-0.5,h-1-0.5
> (i.e. +0.5,+0.5 to w-1.5,h-1.5)
>
> At a high enough scale you can see the stroke starting to separate
> from the fill on the right and bottom edges where the closest it gets
> to the edge is 0.5 scaled coordiantes. That rounds to 0 for 1:1 and
> with the biasing from STROKE_CONTROL, but at higher resolutions it
> becomes a non-zero number of pixels.
>
> Even at 1:1 scale if you follow our filling rules explicitly (which
> would require setting STROKE_CONTROL to PURE) then that would fill the
> rows at y=-1 and y=h-2 and the columns at x=-1 and x=w-2, which is not
> what you want at all. Setting STROKE_CONTROL on allows us to bias the
> location of the stroke a little and it ends up filling the correct
> pixels as a side effect (though STROKE_CONTROL is meant to achieve
> consistency in strokes, it also ends up shifting the line by enough
> that the fill rules choose different pixels in this case). The
> STROKE_CONTROL=on version of the path is assumed to be
> (0.25,0.25,w-0.75,h-0.75) because we snap all coordinates in a
> stroke-controlled non-AA path to the nearest location that is
> 0.25,0.25 within a pixel. This snapping to a consistent sub-pixel
> location biasing at 0.25 was chosen because line widths grow more
> evenly at that offset, but it offsets the outline enough so that the
> outline considered for filling becomes:
>
> outer part of outline goes from 0.25-0.5,0.25-0.5 to
> 0.25+w-1+0.5,0.25+h-1+0.5
> (i.e. -0.25,-0.25 to w-0.25,h-0.25)
> inner part of outline goes from 0.25+0.5,0.25+0.5 to
> 0.25+w-1-0.5,0.25+h-1-0.5
> (i.e. +0.75,+0.75 to w-1.25,h-1.25)
>
> which renders the rows columns at 0 and wh-1 at a 1:1 scale using our
> fill rules. Note that if you scale high enough you can still see
> separation between fill and outline, but the gap is only 0.25 scaled
> coordinates so it would take a scale of at least 4x to see it.
>
> The technically accurate way to render the first/last
> pixel/1-unit-coordinate boundary of a component would be to
> drawRect(0.5,0.5,w-1,h-1) (with no stroke control set) which would
> place the rectangle at the following coordinates:
>
> outer part of outline goes from 0.5-0.5,0.5-0.5 to
> 0.5+w-1+0.5,0.5+h-1+0.5
> (i.e. 0,0, to w,h)
> inner part of outline goes form 0.5+0.5,0.5+0.5 to
> 0.5+w-1-0.5,0.5+h-1-0.5
> (i.e. 1,1 to w-1,h-1)
>
> which completely encloses the first/last row/column of pixels on a 1:1
> coordinate system and accurately covers the first/last N pixels in any
> arbitrary N-scaled coordinate system. The rounding for scales like
> 1.5 still might not work out the way you wanted, but at least the
> exact geometry is consistent with respect to the placement of pixels.
> With AA you will get a consistent border all around if w,h are snapped
> to a pixel size, but with non-AA then rounding error might lead to an
> extra pixel on one pair of sides. I haven't done the analysis to see
> how the above technique would be affected by STROKE_CONTROL because
> really what you are looking for is to render the a consistent edge
> around the component and so successive fills as is done with most of
> our CSS skinning files in FX is a better solution overall. There are
> just too many considerations in filling to make it worthwhile for
> simple rectangular regions...
>
> ...jim
>
> On 10/4/16 1:46 PM, Anton Tarasov wrote:
>> On 10/4/2016 11:30 PM, Jim Graham wrote:
>>> I wasn't familiar with the test code for this particular case. Is
>>> it in a bug report somewhere?
>> Jim, I'm re-attaching it (it was in my first e-mail).
>>
>> Thanks,
>> Anton.
>>
>>>
>>> ...jim
>>>
>>> On 10/4/16 1:01 PM, Anton Tarasov wrote:
>>>> Also, the problem with primitives rendering
>>>> (http://cr.openjdk.java.net/%7Eant/hidpi_pics/Scaling-150-percent.png)
>>>> is
>>>> still there. But it seems to relate to line-thikness
>>>> (border-thickness) rounding inaccuracy. What we can do with
>>>> that?...
>>
More information about the 2d-dev
mailing list