[OpenJDK 2D-Dev] RFR 8144938: Handle properly coordinate overflow in Marlin Renderer

Jim Graham james.graham at oracle.com
Fri Mar 11 21:02:01 UTC 2016

Hi Laurent,

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 mailing list