Hi,

A bug was filed in the IcedTea bugzilla which pointed out a few issues in java2d [1].

I am attaching example code and it's output to demonstrate one particular issue. The attached program draws 5 horizontal lines with different dash patterns. Using a BasicStroke with CAP_ROUND (or CAP_SQUARE) and a very small value for length of the dash, no dashes are drawn at the start of the line.

This seems to be the code responsible (Dasher.java line 221):
//                 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);

'd' is the length of the dash, and 'l' is the distance between the current location and the end location. Notice that the value of l varies: as the current coordinate gets closer to the end of the line, l becomes smaller. So if the value of d is very small, ((long)d << 16)/l changes from 0 to a positive number. Hence dashes of length 0 are drawn in the beginning and dashes of a non-zero length are drawn at the end of the line.

The attached patch uses the total length of the line to calculate t, so it wont change for the same value of d. However, this patch causes other issues. The dash output now depends on the total length of the line. For example, changing the size of the window in the test case changes the value of the total length of the line. If total length becomes very large, t becomes 0, and the dashes on the line disappear. If the window is made small, the dashes appear on the line again.

Thanks,
Omair

[1] http://icedtea.classpath.org/bugzilla/show_bug.cgi?id=197
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;

import javax.swing.JFrame;

public class BasicStrokeDashTest extends JFrame {

    private static final long serialVersionUID = -1280835182820012533L;

    static final int WIDTH = 900;
    static final int HEIGHT = 400;
    static final int GAP = 40;

    public BasicStrokeDashTest() {
        super();

        setSize(WIDTH, HEIGHT);
        setVisible(true);
        setDefaultCloseOperation(EXIT_ON_CLOSE);

    }

    @Override
    public void paint(Graphics g) {
        Graphics2D g2 = (Graphics2D) g;

        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);

        clearBackground(g2);

        drawLines(g2);
    }

    private void clearBackground(Graphics2D g) {
        g.setColor(Color.WHITE);
        g.fillRect(0, 0, getWidth(), getHeight());
        g.setColor(Color.BLACK);

    }

    private void drawLines(Graphics2D g) {
        int lines = 5;
        int spacing = getHeight() / (lines + 1);

        drawHorizontalLineWithDashPattern(g, spacing * 1, new float[] { 0.1f,
                2.0f });

        drawHorizontalLineWithDashPattern(g, spacing * 2, new float[] { 0.05f,
                2.0f });

        drawHorizontalLineWithDashPattern(g, spacing * 3, new float[] { 0.01f,
                2.0f });

        drawHorizontalLineWithDashPattern(g, spacing * 4, new float[] { 0.005f,
                2.0f });

        drawHorizontalLineWithDashPattern(g, spacing * 5, new float[] { 0.001f,
                2.0f });

    }

    private void drawHorizontalLineWithDashPattern(Graphics2D g, int y,
            float[] dashPattern) {
        float strokeWidth = 1.0f;

        // draw an indicator to show that a line will be drawn here
        BasicStroke indicatorStroke = new BasicStroke(strokeWidth,
                BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL);
        g.setStroke(indicatorStroke);
        g.drawLine(GAP / 3, y, (GAP * 2) / 3, y);

        // now draw the actual line
        BasicStroke stroke = new BasicStroke(strokeWidth,
                BasicStroke.CAP_ROUND, BasicStroke.JOIN_BEVEL, strokeWidth,
                dashPattern, 0.0f);
        g.setStroke(stroke);
        g.setColor(getForeground());
        g.drawLine(GAP, y, getWidth() - GAP, y);

    }

    public static void main(String[] args) {
        new BasicStrokeDashTest();
    }

}

<<inline: dash-stroke-bug.png>>

--- Dasher.java.orig	2009-04-17 16:43:43.000000000 -0400
+++ Dasher.java	2009-04-30 16:31:17.000000000 -0400
@@ -181,6 +181,21 @@
     }
 
     public void lineTo(int x1, int y1) {
+        System.out.println("LineTo: x:" + x1/65536 + " y:" + y1/65536);
+        int totalLengthX = x1 - x0;
+        int totalLengthY = y1 - y0;
+        int totalLength;
+        if (symmetric) {
+            totalLength = (int)((PiscesMath.hypot(totalLengthX, totalLengthY)*65536L)/ldet);
+        } else{
+            long lengthA = ((long)totalLengthY*m00 - (long)totalLengthX*m10)/ldet;
+            long lengthB = ((long)totalLengthY*m01 - (long)totalLengthX*m11)/ldet;
+            totalLength = (int)PiscesMath.hypot(lengthA, lengthB);
+        }
+
+        int startX = x0;
+        int startY = y0;
+
         while (true) {
             int d = dash[idx] - phase;
             int lx = x1 - x0;
@@ -220,9 +235,12 @@
 //                 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);
+                t = ((long)d << 16)/totalLength;
+                int deltaX = (int)(t*(x1 - startX) >> 16);
+                xsplit = x0 + deltaX;
+                int deltaY = (int)(t*(y1 - startY) >> 16);
+                ysplit = y0 + deltaY;
+                // System.out.println("t:" + t + " deltaX:" + deltaX + " deltaY:" + deltaY);
 //             }
             goTo(xsplit, ysplit);
 

Reply via email to