Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-7129078

Lines rendered differently using graphics customer from paint() & getGraphics()

    Details

    • Subcomponent:
      2d
    • CPU:
      x86
    • OS:
      windows_xp

      Description

      FULL PRODUCT VERSION :
      java version "1.7.0_02"
      Java(TM) SE Runtime Environment (build 1.7.0_02-b13)
      Java HotSpot(TM) Client VM (build 22.0-b10, mixed mode, sharing)

      ADDITIONAL OS VERSION INFORMATION :
      Microsoft Windows XP [Version 5.1.2600]


      A DESCRIPTION OF THE PROBLEM :
      Shape outline drawing algorithm (and the digital differential analyzer line drawing algorithm for individual line segments) seem to render the same shape differently depending on whether the graphics customer used is the argument of a paint() method [g1] or returned by getGraphics() [g2]. If the line endpoints coordinates are integral values, the starting and ending pixels are rendered identically on g1 and g2 and a difference is shown only in the line DDA algorithm (line segments in oblique lines) -- see left shape in example. If any of the line endpoints coordinates has a fractional value, coordinates with fractions >= 0.5 are rounded to the next pixel (e.g. 30.6 -> 31) on g1 but not g2 (where the fraction is ignored) -- see right shape in example. The result of this unexpected behavior is that the same lines/shapes drawn in XOR mode do not always perfectly overlap (and auto erase as they should) if drawn twice using g1 and g2 respectively. Also, if the same line/shape is drawn twice using different colors (in paint mode), the second drawing does not overwrite all pixels from the first color. The attached source code should illustrate the problem more clearly.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Create a shape (a simple oblique line for example) and draw it in paint() with color1 then (on mouse click for example) draw it again using a graphics customer returned by getGraphics() using color2. Check source code for a working example.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      The shape/line pixels should perfectly overlap (the same pixels should be rendered in both drawing operations paint/getGraphics).
      ACTUAL -
      Pixels rendered using graphics customer from getGraphics are not the same as those affected by paint() drawing.

      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      /*
       The following code can be run as an applet or a desktop application.

       Click the left mouse button to overwrite the left shape using a
       graphics customer returned by getGraphics. The coordinates of
       the left shape are integral values.

       Click the right mouse button to overwrite the right shape using a
       graphics customer returned by getGraphics. The coordinates of
       the right shape have fractions.

       Written by Patrick David Aoun ( xxxxx@xxxxx ) for Oracle engineers
       to illustrate potential bug.

      */

      import java.awt.Color;
      import java.awt.Container;
      import java.awt.Graphics;
      import java.awt.Graphics2D;
      import java.awt.Shape;
      import java.awt.event.InputEvent;
      import java.awt.event.MouseAdapter;
      import java.awt.event.MouseEvent;
      import java.awt.geom.Path2D;

      import javax.swing.JApplet;
      import javax.swing.JFrame;
      import javax.swing.JPanel;

      public class JavaDDABug extends JApplet {

          private static final long serialVersionUID = 1L;

          Container container = null;

          JXBPanel panel;

          Shape shapeLeft, shapeRight;

          public static void main(String[] args) {

              JFrame frame = new JFrame("Java DDA Bug demo");
              frame.setSize(300, 300);
              frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
              frame.setLocationRelativeTo(null);
              frame.setVisible(true);

              JavaDDABug app = new JavaDDABug();
              app.container = frame;
              app.init();
          }

          public void init() {

              if (container == null) {
                  container = this;
                  setSize(300, 300);
              }

              panel = new JXBPanel();
              panel.setLocation(0, 0);
              panel.setSize(300, 300);

              container.add(panel);
              JXBMouseListener daml = new JXBMouseListener();
              panel.addMouseListener(daml);

              Path2D path = new Path2D.Double();
              path.moveTo(10, 10);
              path.lineTo(30, 250);
              path.closePath();

              shapeLeft = path.createTransformedShape(null);

              path.reset();

              path.moveTo(110, 10);
              
              // Also change 130.6 to 130.4 and notice the change
              // in value rounding between paint() and getGraphics().
              path.lineTo(130.6, 250);
              
              path.closePath();

              shapeRight = path.createTransformedShape(null);

          }

          class JXBMouseListener extends MouseAdapter {

              public void mousePressed(MouseEvent e) {

                  Graphics2D g2d = (Graphics2D) panel.getGraphics();

                  int modifiers = e.getModifiers();

                  g2d.setColor(Color.red);

                  if ((modifiers & InputEvent.BUTTON1_MASK) != 0) {

                      g2d.draw(shapeLeft);

                  } else if ((modifiers & InputEvent.BUTTON3_MASK) != 0) {

                      g2d.draw(shapeRight);

                  }

              }

          }

          class JXBPanel extends JPanel {

              private static final long serialVersionUID = 1L;

              @Override
              public void paint(Graphics g) {

                  Graphics2D g2d = (Graphics2D) g;

                  g2d.setColor(Color.black);
                  g2d.fillRect(0, 0, getWidth(), getHeight());
                  g2d.setColor(Color.yellow);
                  g2d.draw(shapeLeft);
                  g2d.draw(shapeRight);

              }

          }

      }

      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      Override the draw method and reimplement the DDA correctly? Or may be avoid using getGraphics() to draw...

        Attachments

          Issue Links

            Activity

              People

              • Assignee:
                Unassigned
                Reporter:
                webbuggrp Webbug Group
              • Votes:
                0 Vote for this issue
                Watchers:
                4 Start watching this issue

                Dates

                • Created:
                  Updated:
                  Imported:
                  Indexed: