title: Java 8 graphics glitch when stroking sub-pixel coordinates on Linux
body: <p>It seems that stroking on sub-pixel coordinates became broken in Java 8.</p>
  
  <p>I have three sets of cases, shown on screenshots (columns represent cases, rows represent different stroke widths) :</p>
  
  <p>Java 7u51 (400% scale)<br>
  <img src="https://i.stack.imgur.com/6egcI.png" alt="Screenshot on Java 7u51 (400% scale)"><br>
  Java 8u60 (400% scale)<br>
  <img src="https://i.stack.imgur.com/pNJlB.png" alt="Screenshot on Java 8u60 (400% scale)"></p>
  
  <ol>
  <li>Fill and stroke on the same coordinates. Works as intended, stroked area is larger than the filling area.</li>
  <li>Stroking is shrunk (by stroke width) and centered (by half of the width) to be inside bounds of the filling region. This part is broken in Java 8 for 1px stroke, where painting occurs on a sub-pixel coordinate (first row); 3px stroke doesn't have this problem (third row). It seems that 0.5 is rounded up for the 1px stroke.</li>
  <li>Filling rectangle is shrunk an centered the same way of case 2. I need this on graphics, which support sub-pixel drawing, to make non-overlapping fill when cells are overlapping. Here you can see that fill operation rounds down 0.5 to 0, so it's only stroking problem.</li>
  </ol>
  
  <p>The code is below:</p>
  
  <pre><code>import static java.awt.BasicStroke.*;
  
  import java.awt.*;
  import java.awt.geom.*;
  
  import javax.swing.*;
  
  public class TestCase
  {
      public static void main(String[] args)
      {
          JFrame frame = new JFrame("Test case");
          frame.setSize(115, 115);
          frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
  
          frame.getContentPane().add(new TestPanel());
  
          frame.setVisible(true);
      }
  
      private static class TestPanel extends JPanel
      {
          TestPanel()
          {
              setOpaque(true);
          }
  
          @Override
          protected void paintComponent(Graphics g)
          {
              Graphics2D g2 = (Graphics2D) g;
              g2.setColor(Color.white);
              g2.fill(getBounds());
  
              Rectangle2D rect = new Rectangle2D.Double();
              Color background = new Color(0, 255, 255);
              Color border = new Color(255, 0, 0, 128);
              Stroke STROKE_1PX = new BasicStroke(1, CAP_SQUARE, JOIN_MITER);
              Stroke STROKE_2PX = new BasicStroke(2, CAP_SQUARE, JOIN_MITER);
              Stroke STROKE_3PX = new BasicStroke(3, CAP_SQUARE, JOIN_MITER);
              g2.translate(10, 10);
  
              /**
               * Filling and stroking by original coordinates
               */
              rect.setRect(0, 0, 25, 25);
              g2.setColor(background);
              g2.fill(rect);
              g2.setColor(border);
              g2.setStroke(STROKE_1PX);
              g2.draw(rect);
              g2.translate(0, 35);
              g2.setColor(background);
              g2.fill(rect);
              g2.setColor(border);
              g2.setStroke(STROKE_2PX);
              g2.draw(rect);
              g2.translate(0, 35);
              g2.setColor(background);
              g2.fill(rect);
              g2.setColor(border);
              g2.setStroke(STROKE_3PX);
              g2.draw(rect);
  
              /**
               * Stroking is shrunk to be inside the filling rect
               */
              g2.translate(35, -70);
              rect.setRect(0, 0, 25, 25);
              g2.setColor(background);
              g2.fill(rect);
              rect.setRect(0.5, 0.5, 24, 24);
              g2.setColor(border);
              g2.setStroke(STROKE_1PX);
              g2.draw(rect);
              g2.translate(0, 35);
              rect.setRect(0, 0, 25, 25);
              g2.setColor(background);
              g2.fill(rect);
              rect.setRect(1, 1, 23, 23);
              g2.setColor(border);
              g2.setStroke(STROKE_2PX);
              g2.draw(rect);
              g2.translate(0, 35);
              rect.setRect(0, 0, 25, 25);
              g2.setColor(background);
              g2.fill(rect);
              rect.setRect(1.5, 1.5, 22, 22);
              g2.setColor(border);
              g2.setStroke(STROKE_3PX);
              g2.draw(rect);
  
              /**
               * Filling rect is additionally shrunk and centered
               */
              g2.translate(35, -70);
              rect.setRect(0.5, 0.5, 24, 24);
              g2.setColor(background);
              g2.fill(rect);
              g2.setColor(border);
              g2.setStroke(STROKE_1PX);
              g2.draw(rect);
              g2.translate(0, 35);
              rect.setRect(1, 1, 23, 23);
              g2.setColor(background);
              g2.fill(rect);
              g2.setColor(border);
              g2.setStroke(STROKE_2PX);
              g2.draw(rect);
              g2.translate(0, 35);
              rect.setRect(1.5, 1.5, 22, 22);
              g2.setColor(background);
              g2.fill(rect);
              g2.setColor(border);
              g2.setStroke(STROKE_3PX);
              g2.draw(rect);
          }
      }
  }
  </code></pre>
  
  <p>As I tested, Java 7 doesn't have this problem (tried on 7u51), Windows (8u77) and Mac (8u60) too. Tried on Ubuntu (8u60 and 8u77) and Linux Mint (8u60) on different machines and the bug was here.</p>
  
  <p><strong>Did anyone faced such issue? Is there any general workaround?</strong></p>
  
  <p>I cannot simply handle 1px case in places where stroking is used. It's because there is a lot of places and I'm working with different <code>Graphics2D</code> implementations and it seems, that from what I've used the issue reproduces only on <code>SunGraphics2D</code>. This means I need to use <code>instanceOf</code> in these places to not break common logic.</p>