Oops, I forgot the attachment.
I'm sorry.

----- "Denis Lila" <dl...@redhat.com> wrote:

> Hello Jim.
> 
> Thank you for your reply. It seems my code did not fully take into
> account your second point after all.
> The dx's of the transformed dashes are di*newx/<x,y> (where
> di is the untransformed dash length, newx is the transformed x 
> coordinate, and <x,y> is the untransformed line length). Obviously,
> newx/<x,y> is constant for all dash segments, so it can be computed
> outside of the loop, but I was computing t=di/<x,y> inside the loop, 
> and then t*newx also inside the loop.
> 
> I have fixed this and I included an improved version of the patch.
> 
> However, I do not understand the second part of your e-mail
> ("One more optimization ..."). I am not sure what you mean by
> "major axis", how one would loop along it, and what the "inner loop"
> is. There are no nested loops in this method.
> 
> Also, the computation of the dxi and dyi of the transformed dash
> segment
> dash[i] involves just 1 multiplication and 1 bit shift (along with an
> overhead of 2 divisions and 2 bit shifts).
> The computation of the actual endpoint of the dashes (done in the
> while(true)
> loop) most of the time involves just 2 additions.
> I am not sure how this can be made any simpler.
> 
> Thank you,
> Denis.
> 
> ----- "Jim Graham" <james.gra...@oracle.com> wrote:
> 
> > Hi Denis,
> > 
> > Here are my thoughts on it:
> > 
> > - Lines are affinely transformed into lines.  The slope may be
> > different 
> > before and after the transform, but both have a single slope.
> > 
> > - The ratio of a line length to its transformed line length is a
> scale
> > 
> > factor that depends solely on the angle of the line.  Thus, for 
> > determining dashing you can simply compute this scale factor once
> for
> > a 
> > given line and then that single scale factor can be applied to every
> 
> > dash segment.
> > 
> > It appears that your setup code takes these factors into account,
> > though 
> > I haven't done a grueling line by line analysis as to whether you
> got
> > 
> > the math right.
> > 
> > One more optimization is that once you know the angle of the line
> then
> > 
> > you have a factor for how the length of a segment of that line
> relates
> > 
> > to its dx and dy.  Note that for horizontal and vertical lines one
> of
> > 
> > those factors may be Infinity, but every line will have a non-zero
> and
> > 
> > non-infinity factor for one of those two dimensions.
> > 
> > This means that you can calculate the dashing by simply looping
> along
> > 
> > the major axis of the line and comparing either the dx, or the dy to
> 
> > scaled "lengths" that represent the lengths of the transformed
> dashes
> > 
> > projected onto the major axis.
> > 
> > Finally, the other dx,dy can be computed from the dx,dy of the major
> 
> > axis with another scale.  I am pretty sure that this dx=>dy or
> dy=>dx
> > 
> > scale factor might be zero, but it would never be infinite if you
> are
> > 
> > calculating along the major axis of the transformed line, but I
> didn't
> > 
> > write out a proof for it.
> > 
> > Taking both of these concepts into account - can that make the inner
> 
> > loop even simpler?
> > 
> >                     ...jim
> > 
> > Denis Lila wrote:
> > > Hello.
> > > 
> > > I think I have a fix for this bug:
> > http://icedtea.classpath.org/bugzilla/show_bug.cgi?id=504
> > > 
> > > The problem is caused by the "symmetric" variable in
> > pisces/Dasher.java.
> > > symmetric is set to (m00 == m11 && m10 == -m01), and never
> changed.
> > > 
> > > It is only used in one place (in lineTo) to simplify the
> computation
> > of
> > > the length of the line before an affine transformation A was
> applied
> > to it.
> > > 
> > > This is why it causes a problem:
> > > If A = [[a00, a01], [a10, a11]] and (x,y) is a point obtained by
> > applying
> > > A to some other point (x',y'), then what we want is the length of
> > the vector
> > > (x',y'), which is ||Ainv*(x,y)||. Ainv = (1/det(A)) * [[a11,
> > -a01],[-a10, a00]],
> > > so, after some calculations, ||Ainv*(x,y)|| ends up being equal
> to
> > > sqrt(x^2*(a11^2 + a10^2) + y^2*(a00^2 + a01^2) - x*y*(a11*a01 +
> > a00*a10)) * 1/|det(A)|.
> > > If symmetric==true, this simplifies to:
> > > sqrt((a11^2 + a01^2) * (x^2 + y^2)) * 1/|det(A)|, and
> > > |det(A)| = a11^2 + a01^2, so, the final answer is:
> > > sqrt((x^2 + y^2)) / sqrt(det(A)). Therefore the problem in
> > Dasher.java
> > > is that it divides by det(A), not sqrt(det(A)).
> > > 
> > > My fix for this was to remove the "symmetric" special case.
> Another
> > possible fix
> > > would have been to introduce an instance "sqrtldet" and set it to
> > sqrt(det(A)),
> > > and divide by that instead of det(A). This didn't seem worth it,
> > because the only
> > > benefit we gain by having the "symmetric" variable is to save 3
> > multiplications
> > > and 1 division per iteration of the while(true) loop, at the
> expense
> > of making the 
> > > code more complex, harder to read, introducing more opportunity
> for
> > bugs, and adding
> > > hundreds of operations of overhead (since PiscesMath.sqrt would
> have
> > to be called to
> > > initialize sqrtldet).
> > > 
> > > To make up for this slight performance loss I have moved the code
> > that computes
> > > the transformed dash vectors outside of the while loop, since
> they
> > are constant
> > > and they only need to be computed once for any one line.
> > > Moreover, computing the constant dash vectors inside the loop
> > causes
> > > them to not really be constant (since they're computed by
> dividing
> > numbers that
> > > aren't constant). This can cause irregularities in dashes (see
> > comment 14 in
> > > http://icedtea.classpath.org/bugzilla/show_bug.cgi?id=197).
> > > 
> > > I would very much appreciate any comments/suggestions.
> > > 
> > > Thank you,
> > > Denis Lila.
> > >
exporting patch:
# HG changeset patch
# User Denis Lila <dl...@redhat.com>
# Date 1277131744 14400
# Node ID 61f354c51110bf62b1e9b4da1cc0089000e10897
# Parent  83c7768292d783709a1cdb7ddd3d68df65e23148
Patch that fixes the drawing of uniformly scaled dashed lines, and improves the performace of dashing.

diff --git a/src/share/classes/sun/java2d/pisces/Dasher.java b/src/share/classes/sun/java2d/pisces/Dasher.java
--- a/src/share/classes/sun/java2d/pisces/Dasher.java
+++ b/src/share/classes/sun/java2d/pisces/Dasher.java
@@ -56,7 +56,6 @@
 
     Transform4 transform;
 
-    boolean symmetric;
     long ldet;
 
     boolean firstDashOn;
@@ -143,7 +142,6 @@
         this.m10 = transform.m10;
         this.m11 = transform.m11;
         this.ldet = ((long)m00*m11 - (long)m01*m10) >> 16;
-        this.symmetric = (m00 == m11 && m10 == -m01);
     }
 
     public void moveTo(int x0, int y0) {
@@ -181,51 +179,77 @@
     }
 
     public void lineTo(int x1, int y1) {
+        int lx = x1 - x0;
+        int ly = y1 - y0;
+
+        // Compute segment length in the untransformed
+        // coordinate system
+        // IMPL NOTE - use fixed point
+
+        int l;
+        long la = ((long)ly*m00 - (long)lx*m10)/ldet;
+        long lb = ((long)ly*m01 - (long)lx*m11)/ldet;
+        l = (int)PiscesMath.hypot(la, lb);
+
+        // Compute the number of transformed dash lengths that will
+        // be needed in the while(true) loop. We carry out the first
+        // iteration outside the loop so we can take into account phase.
+        int lenAcc = dash[idx] - phase;
+        int dashesToCompute = (lenAcc > l) ? 0 : 1; 
+        for (int i = 1; i < dash.length; i++) {
+            lenAcc += dash[(i + idx) % dash.length];
+            if (lenAcc > l) break;
+            dashesToCompute += 1;
+        }
+
+        long t;
+        int[] dxsplit = new int[dash.length];
+        int[] dysplit = new int[dash.length];
+
+        // In this comment, let (x,y), di, dxi, dyi, t be, respectively, the
+        // untransformed line, dash[i], the x and y component of dash i
+        // for line (x,y), and the angle of line (x,y). Let (x', y'), di', 
+        // dxi', dyi', t' be the transformed counterparts of the above.
+        // We are trying to find the dxi' and the dyi'.
+        // dxi' = cos(t')*di' = (x' / <x',y'>) * di' and we know that the
+        // ratio of untransformed and transformed lengths depends only on
+        // the angle of the line, so di'/di = <x',y'>/<x,y> which means:
+        // di' = (di * <x',y'>)/<x,y>
+        // So: dxi' = ((di * <x',y'>)/<x,y>) * (x' / <x',y'>) which simplifies
+        // to  dxi' =  (di * x') / <x, y>. dyi' is similar.
+        // x' / <x, y> is constant, so we compute it outside of the loop.
+        //
+        // Note: in the code: dxsplit[i] = dxi'; dysplit[i] = dyi'; dash[i] = di
+        // lx = x', ly = y', and <x,y> = l.
+        long cx = ((long) lx << 16) / l;
+        long cy = ((long) ly << 16) / l;
+        for (int i = 0; i < dashesToCompute; i++) {
+            int j = (i + idx) % dash.length;
+            dxsplit[j] = (int) ((dash[j] * cx) >> 16);
+            dysplit[j] = (int) ((dash[j] * cy) >> 16);
+        }
+
         while (true) {
-            int d = dash[idx] - phase;
-            int lx = x1 - x0;
-            int ly = y1 - y0;
-
-            // Compute segment length in the untransformed
-            // coordinate system
-            // IMPL NOTE - use fixed point
-
-            int l;
-            if (symmetric) {
-                l = (int)((PiscesMath.hypot(lx, ly)*65536L)/ldet);
-            } else{
-                long la = ((long)ly*m00 - (long)lx*m10)/ldet;
-                long lb = ((long)ly*m01 - (long)lx*m11)/ldet;
-                l = (int)PiscesMath.hypot(la, lb);
-            }
-
-            if (l < d) {
+            if (l < dash[idx] - phase) {
                 goTo(x1, y1);
                 // Advance phase within current dash segment
                 phase += l;
                 return;
             }
 
-            long t;
             int xsplit, ysplit;
-//             // For zero length dashses, SE appears to move 1/8 unit
-//             // in device space
-//             if (d == 0) {
-//                 double dlx = lx/65536.0;
-//                 double dly = ly/65536.0;
-//                 len = PiscesMath.hypot(dlx, dly);
-//                 double dt = 1.0/(8*len);
-//                 double dxsplit = (x0/65536.0) + dt*dlx;
-//                 double dysplit = (y0/65536.0) + dt*dly;
-//                 xsplit = (int)(dxsplit*65536.0);
-//                 ysplit = (int)(dysplit*65536.0);
-//             } else {
-                t = ((long)d << 16)/l;
-                xsplit = x0 + (int)(t*(x1 - x0) >> 16);
-                ysplit = y0 + (int)(t*(y1 - y0) >> 16);
-//             }
+            if (phase == 0) {
+                xsplit = x0 + dxsplit[idx];
+                ysplit = y0 + dysplit[idx];
+            } else {
+                long p = (((long)dash[idx] - phase)<<16) / dash[idx];
+                xsplit = x0 + (int)((p * dxsplit[idx]) >> 16);
+                ysplit = y0 + (int)((p * dysplit[idx]) >> 16);
+            }
+
             goTo(xsplit, ysplit);
 
+            l -= (dash[idx] - phase);
             // Advance to next dash segment
             idx = (idx + 1) % dash.length;
             dashOn = !dashOn;

Reply via email to