[OpenJDK 2D-Dev] RFR 8144938: Handle properly coordinate overflow in Marlin Renderer
Laurent Bourgès
bourges.laurent at gmail.com
Fri Mar 11 22:14:47 UTC 2016
Jim,
Here is a webrev illustrating my 'simple' approach:
http://cr.openjdk.java.net/~lbourges/marlin/marlin-8144938.1/
Changes:
- MarlinRenderingEngine.strokeTo(): removed outat ie always call
src.getPathIterator(at)
that ensures Marlin is working in device-space coordinates; so pathTo() can
filter out NaN / Infinity points
- TransformingPathConsumer2D: removed transformConsumer() and all its
related inner classes (simplification)
I will perform a MapBench benchmark focused on non-uniform transformation
to check the possible performance 'regression'.
Please give me your point of view on the NaN/Infinity overflow handling
that I think it is now correct as all path coordinates are in
the device-space.
I think you proposed an optimized approach to refine my solution and get
rid of all inverseDelta/delta transformations at all, does it ?
I am not sure to have understood all the maths here (after my first 2/3
reads) but it seems quite complex and I am a bit reluctant as computing
curve offsets or curved lengths seems to me very very tricky and
error-prone !
I think your advices are very interesting but I need more concrete
directions to try making changes myself in the Stroker / Dasher code.
What is the use case for non-uniform transformations ? how important is it
? In other words, is it worth such effort ?
Do you have an alternative solution (simple) ?
>>
>
> Yes, if we can take a non-uniform transform and express the pen size as a
> tilted ellipse, then we can supply the tilted ellipse parameters to the
> stroker and it could perform device-space widening of non-uniform pens.
> Basically, we have a method right now that takes the dx,dy of a path and
> turns it into a unit vector, does a simplistic 90 degree transform, and
> then scales it by the line width. It looks something like (adapted from
> Stroker.computeOffset()):
>
> mdx = dy / len(dx,dy) * halfwidth;
> mdy = -dx / len(dx,dy) * halfwidth;
>
> which is shorthand for:
>
> (1) normalize dx,dy
> (2) rotate dx,dy by 90 degrees
> (3) multiply by half the transformed line width
>
> What we'd need to do for the general case is:
>
> (1) inverse delta transform to user space
> (2) normalize dx,dy to unit vector
> (3) rotate by 90 degrees
> (4) scale by (half) user space line width
> (5) transform back to device space
>
I agree.
> (1) and (3) through (5) are all 2x2 transforms and they could be able to
> be flattened into a single transform, but we have that pesky normalize step
> at (2) that gets in the way. If not for that, we could replace the
> contents of computeOffset with just a 2x2 matrix multiply, but I punted
> when I looked at how to introduce the normalization step. I'm pretty sure
> that (1) and (2) could be swapped somehow because all we really have is an
> elliptical pen and we should be able to take a normalized vector in device
> space and compute a transform that rotates it to the proper orientation
> that comes from "inverseTx, rotate90, Tx" and applies the proper scale, but
> how to compute that? When done we'd just have a 2x2 matrix to multiply by,
> but since the construction of that 2x2 matrix wasn't immediately obvious
> from concatenating component matrices I punted to other problems.
>
A bit lost, but globally I understand. Probably some degenated cases may
apperar (matrix inversion ?)
> Another thing to consider is that dash length will vary based on angle so
> we'd need a way to compute "user length of device dx,dy" quickly. It's
> essentially computing "len(inverseTransform(dx,dy))", which is:
>
> udx = inverseMxx * dx + inverseMxy * dy;
> udy = inverseMyx * dx + inverseMyy * dy;
> return sqrt(udx * udx + udy * udy);
>
> which is (renaming inverseM* to I*), and please check my math:
>
> sqrt((Ixx * dx + Ixy * dy)^2 + (Iyx * dx + Iyy * dy)^2);
> sqrt(Ixx*Ixx*dx*dx + 2Ixx*Ixy*dx*dy + Ixy*Ixy*dy*dy +
> Iyx*Iyx*dx*dx + 2Iyx*Iyy*dx*dy + Iyy*Iyy*dy*dy);
> sqrt( (Ixx*Ixx + Iyx*Iyx)*dx*dx +
> 2(Ixx*Ixy + Iyx*Iyy)*dx*dy +
> (Ixy*Ixy + Iyy*Iyy)*dy*dy);
> sqrt(K1 * dx * dx + K2 * dx * dy + K3 * dy * dy);
>
Will check later (as it is late here) but it looks correct.
>
> For a uniform scale K1 == K3 == squared inverse scale and K2 == 0 and
> K1,K3 can then be factored out of the sqrt into a constant and applied to
> the dash lengths instead.
>
> which is only a little more complicated than a regular length
> computation. Unfortunately, since the angle changes over the course of a
> curve, it might prove a little tricky for the iterator that computes the
> length of the dashes along the curve, but it could be as simple as calling
> a different length method as it iterates.
>
It seems too simple to be true. I have doubts I can handle such changes
myself.
> Besides, we should also check early if the transformation matrix
>> contains NaN or Infinity values avoiding the path iteration. I advocate
>> it is an extreme case.
>>
>
> That could be a short-cut optimization, but it depends on how much it
> costs compared to just letting the path degeneracy elimination turn it all
> into a NOP segment by segment...
>
I agree this shortcut is only worth if it happens in reality...
To conclude tonight, I will run few benchmarks and am looking forward your
comments.
Cheers,
Laurent
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.openjdk.java.net/pipermail/2d-dev/attachments/20160311/e44014c2/attachment.html>
More information about the 2d-dev
mailing list