[OpenJDK 2D-Dev] RFR 8144938: Handle properly coordinate overflow in Marlin Renderer
james.graham at oracle.com
Fri Mar 11 21:02:01 UTC 2016
On 3/11/16 9:06 AM, Laurent Bourgès wrote:
> By the way I started refactoring MRE.strokeTo () to get rid of outat ie
> I propose to remove the optimisation for non-uniform at as I prefer
> filtering transformed coordinates once even if it requires invDelta /
> Delta transform stages in the pipeline.
> I hope it will not impact too much the performance but it simplifies
> notably the logic and the code.
> 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
(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.
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);
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.
> 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...
More information about the 2d-dev