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

Clipped lines corrupted when using shaped based drawing API

XMLWordPrintable

    • 2d
    • generic
    • generic

      ADDITIONAL SYSTEM INFORMATION :
      The problems occurs at least on Linux and Windows based systems in combination with Java 18, Java 17, Java 8. And its seems to be independent from using different rendering pipeline.

      A DESCRIPTION OF THE PROBLEM :
      Drawing lines with the shape based 2D API results into problems when parts of these line are contained in a clipping region. The resulting lines in the clipping region either contain holes (pixels not being set at all) or using a different pixel representations in case of a slanted line.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      - start the provided test program "ClippingProblem"
      - observe that a JFrame with two lines on the left hand side and a more complex path on the right hand side is being shown
      - the upper line of the left hand side is draw using "drawLine" API and the lower one is using "draw" API with a Line object
      - when moving the cursor inside of that frame a small rectangle with a white outline is being shown at the current mouse position
      - this rectangle can be used to interactively trigger a clipped redraw within the boundaries of that rectangle for the JPanel showing the lines, path, and the clipping rectangle
      - move the clipping rectangle attached to the mouse in a way that its upper part partially intersects with the lower line (line drawn with shape based "draw" method) on the left hand side
      - perform this interaction multiple times, with intersection, without intersection, ...

      BAD: during some of these interactions the line is not completely redrawn. i.e. the line contains holes, which means that some pixels are not set correctly. In some other cases the line is not correctly restored, i.e. some other pixels are set than before.

      - perform the same interaction with the upper line on the left hand side (drawn with drawLine) and observe that the line stays as it is, i.e. no holes and no pixel changes

      - perform the same interaction with the lines (not curves) visualized within the path on the right hand side

      BAD: the same pixel artifacts as for the simple line can be provoked

      - Please note, instead of using the clipping rectangle inside of the test application also an external window can be used to provoke via exposes partial (clipped) redraws of the test application resulting into the same problems.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      The pixels of a clipped line should not differ within its clipping region from an unclipped line using the same line coordinates. Please note, that this is seems to be already fulfilled for the older 2D API (drawLine, etc.), but not for the shaped based API (draw(Shape shape)).
      ACTUAL -
      The pixels of a clipped line within its clipping region may contain either holes (pixels not being set at all) or pixels are being set at different positions compared to its unclipped counterpart.

      ---------- BEGIN SOURCE ----------
      package clipping;

      import java.awt.BasicStroke;
      import java.awt.EventQueue;
      import java.awt.Graphics;
      import java.awt.Graphics2D;
      import java.awt.Rectangle;
      import java.awt.event.MouseEvent;
      import java.awt.event.MouseMotionListener;
      import java.awt.geom.GeneralPath;
      import javax.swing.JFrame;
      import javax.swing.JPanel;

      class Surface extends JPanel implements MouseMotionListener
      {
          // current position of interactive cliping rectangle
          private int x = -1, y = -1;
          // size of clipping rectangle
          private static final int RECTANGLE_SIZE = 50;
          
          public Surface ()
          {
              // install mouse motion listener
              addMouseMotionListener(this);
          }
          
          /**
           * Performs the drawing with the passed graphics context.
           * @param g the graphics context to be used.
           */
          private void doDrawing (Graphics g)
          {
              Graphics2D g2d = (Graphics2D) g;
              
              // setup stroke
              g2d.setStroke(new BasicStroke(1.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
              // draw interactive rectangle representing clipping region
              if (x != -1 && y != -1)
              {
                  g2d.drawRect(x - RECTANGLE_SIZE / 2, y - RECTANGLE_SIZE / 2,
                               RECTANGLE_SIZE, RECTANGLE_SIZE);
              }
              // draw a simple line using the "old" API
              g2d.drawLine(30, 40, 200, 20);
              // draw a simple line using the shape based API
              g2d.draw(new java.awt.geom.Line2D.Double(30.0d, 120.0d, 200.0d, 100.0d));
              // draw a closed path consisting of a line and a curve
              GeneralPath shape = new GeneralPath();
              shape.moveTo(250, 10);
              shape.lineTo(400, 400);
              shape.curveTo(500, 5, 600, 600, 700, 7);
              shape.closePath();
              g2d.draw(shape);
          }

          @Override
          public void paintComponent (Graphics g)
          {
              super.paintComponent(g);
              doDrawing(g);
          }

          @Override
          public void mouseDragged (MouseEvent e)
          {
          }

          @Override
          public void mouseMoved (MouseEvent e)
          {
              Rectangle rect = new Rectangle(-1, -1);
              
              // build rectangle of previous position
              if (x != -1 && y != -1)
              {
                  rect.add(new Rectangle(x - RECTANGLE_SIZE / 2, y - RECTANGLE_SIZE / 2,
                                         RECTANGLE_SIZE, RECTANGLE_SIZE));
              }
              // add rectangle of new position
              if (e != null)
              {
                  x = e.getX();
                  y = e.getY();
                  rect.add(new Rectangle(x - RECTANGLE_SIZE / 2, y - RECTANGLE_SIZE / 2,
                                         RECTANGLE_SIZE, RECTANGLE_SIZE));
              }
              // grow rectangle by 1 pixel
              rect.grow(1, 1);
              // redraw dirty region
              repaint(rect);
          }
      }

      public class ClippingProblem extends JFrame
      {
          public ClippingProblem ()
          {
              initUI();
          }

          private void initUI ()
          {

              add(new Surface());

              setTitle("Clipping Problems");
              setSize(750, 500);
              setLocationRelativeTo(null);
              setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
          }

          public static void main (String[] args)
          {

              EventQueue.invokeLater(new Runnable()
              {
                  @Override
                  public void run ()
                  {
                      ClippingProblem ex = new ClippingProblem();
                      ex.setVisible(true);
                  }
              });
          }
      }
      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      Avoid using the shape based 2D drawing API.

      FREQUENCY : always


            prr Philip Race
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

              Created:
              Updated: