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

BufferedImage "fillPolygon" method incorrectly renders using an offset

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Unresolved
    • Icon: P4 P4
    • None
    • 5.0
    • client-libs
    • 2d
    • Cause Known
    • x86
    • windows_xp

      FULL PRODUCT VERSION :
      C:\Documents and Settings\jodove>java -version
      java version "1.5.0_04"
      Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_04-b05)
      Java HotSpot(TM) Client VM (build 1.5.0_04-b05, mixed mode, sharing)


      ADDITIONAL OS VERSION INFORMATION :
      cpu type: intel pentium
      cpu speed: 1.9 GHz
      memory: 512MB
      operating system: WindowsXP, HomeEdition, version 2002 (service pack 2)


      A DESCRIPTION OF THE PROBLEM :
      Hi Sun,

      This is an FYI regarding a "bug" that I appear to have found in SDK 1.5.0. I am an scjp / scjd with a BS degree in computer science and math - so I'm a pretty good developer, but this problem has got me "scratching my head" (so to speak ;-) Hopefully you guys will know how to fix this for any upcoming releases. Until a fix or patch is made, however, I cannot use SDK 1.5.0, at all. Instead, I will simply keep using 1.4.x, which works under this scenario.

      NOTE:
      you can consistently reproduce the errant behavior that I describe below, using the Java code that I have copied below, in the "steps to reproduce" section of this bug report.

      NOTE:
      I also posted this problem to the folks on the JavaRanch website (www.javaranch.com), who confirmed that this appears to be a bug.

      In short the problem, can loosely be defined as follows.

      [1]
      over the past year I have *successfully* implemented a "graphical geometry editor" using J2SE SDK 1.4.x. Everything worked fine under SDK 1.4.x, and life was good.

      [2]
      however, I downloaded Sun's new J2SE SDK 1.5.0. I compiled my geometry editor successfully (without errors) against this new SDK 1.5.0, and then began using it. However, the following "fillPolygon" method of class Graphics is no longer working as it did before!

      class: java.awt.Graphics
      method: public void fillPolygon(Polygon p)

      In short, any polygon that I render using this method is erroneously "offset" from where it **should** be. My graphical editor was quite large, so I abstracted the problem into a very small Java SWING application, the entire source code of which I copied into this bug report in the "Steps to Reproduce" section. Please take a look at the documentation within this source code file, which is **very** good - you will be able to follow it easily, as the code is trivial. Overall, the problem is related to the usage of BufferedImage objects in conjuction with the "fillPolygon" method.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      NOTE: the below source code is a very small SWING application that can be used to consistently reproduce this problem. The code works fine under SDK 1.4.x, however, under SDK 1.5.0 the problem occurs. Again this source code is documented *very well* - you will have no problem in understand what it does. If you have questions, please feel free to contact me. My contact information is as follows:
      Regards,
      John Dove
      ###@###.###
      http://home.nycap.rr.com/jdove


      CUSTOMER SUBMITTED WORKAROUND :
      none that I have found
      SOURCE CODE is as follows:
      -------------------------------------

      package possible.fillPolygon.bug;

      ////////////////////////////////////////////////////////////////////////////////
      ////////////////////////////////////////////////////////////////////////////////
      // declare imports
      ////////////////////////////////////////////////////////////////////////////////
      ////////////////////////////////////////////////////////////////////////////////

      // j2se sdk imports (applicable to both 1.4.x and 1.5.x sdk versions)
      import javax.swing.*;
      import java.awt.*;
      import java.awt.image.*;

      ////////////////////////////////////////////////////////////////////////////////
      ////////////////////////////////////////////////////////////////////////////////
      // top-level public class
      ////////////////////////////////////////////////////////////////////////////////
      ////////////////////////////////////////////////////////////////////////////////
      /**
        * this small SWING application program demonstrates a possible bug between
        * the 1.4.x and 1.5.0 version of the J2SE SDK api. Specifically, this program
        * functions as follows:
        *
        * 1. it is a SWING application.
        * 2. it creates a JComponent subclass and overrides its "paint" method in
        * order to "hardcode" the drawing of a polygon.
        * 3. the polygon image is drawn twice, such that the first drawing is done
        * via a "fillPolygon" method call, and the second drawing is done via a
        * "drawPolygon" method call. The former draws the polygon and fills its body
        * with a color; the latter simply draws the outline of the polygon.
        * 4. the "drawPolygon" method functions correctly under both SDK 1.4.x and 1.5.x.
        * 5. the "fillPolygon" method functions correctly under SDK 1.4.x, but NOT
        * under SDK 1.5.x. Specifically, in terms of SDK 1.5.x, the fillPolygon method
        * renders the filled polygon at an offset from where it should be.
        *
        *
        * To see this discrepancy in behavior, simply compile this small source code
        * file under SDK 1.4.x xor SDK 1.5.x and run it.
        *
        * IMPORTANT: this behavior only occurs when the paint method draws the polygon
        * into a BufferedImage object's graphics object, such that afterward, the
        * BufferedImage object is then drawn into the Graphics object of the paint
        * method itself. If a bufferImage object is NOT used, thereby drawing the
        * polygon directly into the GRaphics object of the paint method, then the behavior
        * works correctly under both 1.4.x and 1.5.0. In general, it appears that this
        * problem is related to the combined functioning of the BufferedImage class
        * and its "fillPolygon" method.
        *
        * @author John Dove scjp/scjd July 2005
        */
      public class App extends JFrame
      {
         //////////////////////////////////////////////////////////////////////////////
         //////////////////////////////////////////////////////////////////////////////
         // inner class
         //////////////////////////////////////////////////////////////////////////////
         //////////////////////////////////////////////////////////////////////////////

         /**
          * inner JComponent subclass, which has hardcoded logic for drawing the
          * polygon.
          */
         class ImageCanvas extends JComponent
         {
           public void paint(Graphics g)
           {
             // create a bufferedImage object, into which we will draw our polygon
             // images (i.e. the outline of the polygon via the "drawPolygon" method,
             // and the filled polygon via the "fillPolygon" method, the latter of
             // which seems to have the problem)
             BufferedImage bi = new BufferedImage
             (
               this.getWidth(),
               this.getHeight(),
               BufferedImage.TYPE_INT_RGB
             );
             Graphics big = bi.getGraphics();

             // offset value that is added to the coordinates of the polygon,
             // which moves it "out of visible rendering range" in Java user space,
             // which has an upper left device coordinate of (0,0) and extends to
             // the extents of this JComponent subclass when "realized" on the screen.
             // (Just look at the remainder of this code, it is very trivial - you will
             // understand.)
             int offsetX = 1073741793;
             int offsetY = 1073741793;

             // define the vertex offsets of the polygon in terms of pixels. In this
             // case our polygon will look like a small triangle
             int [] polygonVertexOffsetsX = new int[] {0, 30, 0};
             int [] polygonVertexOffsetsY = new int[] {0, 0, 30};

             // now, add the above "offset" x/y values to the vertex arrays
             System.out.println("-----------------------------------------------------");
             for(int i=0; i<3; i++)
             {
               polygonVertexOffsetsX[i] += offsetX;
               polygonVertexOffsetsY[i] += offsetY;
               System.out.println("polygon vertex ["+i+"]: ("+polygonVertexOffsetsX[i]+","+polygonVertexOffsetsY[i]+")");
             }
             System.out.println("-----------------------------------------------------");

             // take the graphics object of the BUFFERED IMAGE, and set its "translation"
             // value such that it "translates" any rendered polygon's back into
             // our Java user space "view." Technically, the values of this translate
             // setting will be "added" to any pixels that are rendered, thereby
             // affecting the final and resultant pixel location. In other words, we
             // will render our polygon and bring it into view.
             int translateX = -1073741664;
             int translateY = -1073741664;
             big.translate(translateX,translateY);

             // define a polygon object using our vertices
             java.awt.Polygon polygon = new java.awt.Polygon
             (
               polygonVertexOffsetsX,
               polygonVertexOffsetsY,
               polygonVertexOffsetsX.length
             );

             // draw a filled version of this polygon
             big.setColor(Color.yellow);
             big.fillPolygon(polygon);

             // draw the polygon outline
             big.setColor(Color.blue);
             big.drawPolygon(polygon);

             // NOTE: these two polygon renderings should be drawn at the same location,
             // however under SDK 1.5.x, the "fillPolygon" method draws the filled
             // polygon at an offset from where it should be.

             // draw the BUFFERED IMAGE into this method's Graphics object
             g.drawImage(bi,0,0,this);

             // NOTE: if a BUFFERED IMAGE is NOT used, and the polygons are drawn
             // directly into the Graphics object of this method itself, then
             // both SDK 1.4.x and 1.5.0 work correctly. Thus, this appears to be
             // a problem with the rendering of poylgons via the "fillPolygon" method
             // specifically within BufferedImage objects.
           }
         }

         //////////////////////////////////////////////////////////////////////////////
         //////////////////////////////////////////////////////////////////////////////
         // methods
         //////////////////////////////////////////////////////////////////////////////
         //////////////////////////////////////////////////////////////////////////////

         public App()
         {
           ImageCanvas ic = new ImageCanvas();
           getContentPane().add(ic);
           this.setSize(640,480);
           this.setVisible(true);
         }

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






      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      the "fillPolygon" method, as called on the Graphics object of the BufferImage object, *should* render polygons in their *correct* positions.
      ACTUAL -
      under SDK 1.5.0, the "fillPolygon" method, as called on the Graphics object of the BufferImage object, renders the polygon at an incorrect location - specifically, it incorrectly offsets it from where it should be. The shape of the polygon itself however looks to be correct.

      ERROR MESSAGES/STACK TRACES THAT OCCUR :
      no error messages are reported

      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      // NOTE: the below source code was copied into the "Steps to reproduce" section of this bug report as well (fyi)

      package possible.fillPolygon.bug;

      ////////////////////////////////////////////////////////////////////////////////
      ////////////////////////////////////////////////////////////////////////////////
      // declare imports
      ////////////////////////////////////////////////////////////////////////////////
      ////////////////////////////////////////////////////////////////////////////////

      // j2se sdk imports (applicable to both 1.4.x and 1.5.x sdk versions)
      import javax.swing.*;
      import java.awt.*;
      import java.awt.image.*;

      ////////////////////////////////////////////////////////////////////////////////
      ////////////////////////////////////////////////////////////////////////////////
      // top-level public class
      ////////////////////////////////////////////////////////////////////////////////
      ////////////////////////////////////////////////////////////////////////////////
      /**
        * this small SWING application program demonstrates a possible bug between
        * the 1.4.x and 1.5.0 version of the J2SE SDK api. Specifically, this program
        * functions as follows:
        *
        * 1. it is a SWING application.
        * 2. it creates a JComponent subclass and overrides its "paint" method in
        * order to "hardcode" the drawing of a polygon.
        * 3. the polygon image is drawn twice, such that the first drawing is done
        * via a "fillPolygon" method call, and the second drawing is done via a
        * "drawPolygon" method call. The former draws the polygon and fills its body
        * with a color; the latter simply draws the outline of the polygon.
        * 4. the "drawPolygon" method functions correctly under both SDK 1.4.x and 1.5.x.
        * 5. the "fillPolygon" method functions correctly under SDK 1.4.x, but NOT
        * under SDK 1.5.x. Specifically, in terms of SDK 1.5.x, the fillPolygon method
        * renders the filled polygon at an offset from where it should be.
        *
        *
        * To see this discrepancy in behavior, simply compile this small source code
        * file under SDK 1.4.x xor SDK 1.5.x and run it.
        *
        * IMPORTANT: this behavior only occurs when the paint method draws the polygon
        * into a BufferedImage object's graphics object, such that afterward, the
        * BufferedImage object is then drawn into the Graphics object of the paint
        * method itself. If a bufferImage object is NOT used, thereby drawing the
        * polygon directly into the GRaphics object of the paint method, then the behavior
        * works correctly under both 1.4.x and 1.5.0. In general, it appears that this
        * problem is related to the combined functioning of the BufferedImage class
        * and its "fillPolygon" method.
        *
        * @author John Dove scjp/scjd July 2005
        */
      public class App extends JFrame
      {
         //////////////////////////////////////////////////////////////////////////////
         //////////////////////////////////////////////////////////////////////////////
         // inner class
         //////////////////////////////////////////////////////////////////////////////
         //////////////////////////////////////////////////////////////////////////////

         /**
          * inner JComponent subclass, which has hardcoded logic for drawing the
          * polygon.
          */
         class ImageCanvas extends JComponent
         {
           public void paint(Graphics g)
           {
             // create a bufferedImage object, into which we will draw our polygon
             // images (i.e. the outline of the polygon via the "drawPolygon" method,
             // and the filled polygon via the "fillPolygon" method, the latter of
             // which seems to have the problem)
             BufferedImage bi = new BufferedImage
             (
               this.getWidth(),
               this.getHeight(),
               BufferedImage.TYPE_INT_RGB
             );
             Graphics big = bi.getGraphics();

             // offset value that is added to the coordinates of the polygon,
             // which moves it "out of visible rendering range" in Java user space,
             // which has an upper left device coordinate of (0,0) and extends to
             // the extents of this JComponent subclass when "realized" on the screen.
             // (Just look at the remainder of this code, it is very trivial - you will
             // understand.)
             int offsetX = 1073741793;
             int offsetY = 1073741793;

             // define the vertex offsets of the polygon in terms of pixels. In this
             // case our polygon will look like a small triangle
             int [] polygonVertexOffsetsX = new int[] {0, 30, 0};
             int [] polygonVertexOffsetsY = new int[] {0, 0, 30};

             // now, add the above "offset" x/y values to the vertex arrays
             System.out.println("-----------------------------------------------------");
             for(int i=0; i<3; i++)
             {
               polygonVertexOffsetsX[i] += offsetX;
               polygonVertexOffsetsY[i] += offsetY;
               System.out.println("polygon vertex ["+i+"]: ("+polygonVertexOffsetsX[i]+","+polygonVertexOffsetsY[i]+")");
             }
             System.out.println("-----------------------------------------------------");

             // take the graphics object of the BUFFERED IMAGE, and set its "translation"
             // value such that it "translates" any rendered polygon's back into
             // our Java user space "view." Technically, the values of this translate
             // setting will be "added" to any pixels that are rendered, thereby
             // affecting the final and resultant pixel location. In other words, we
             // will render our polygon and bring it into view.
             int translateX = -1073741664;
             int translateY = -1073741664;
             big.translate(translateX,translateY);

             // define a polygon object using our vertices
             java.awt.Polygon polygon = new java.awt.Polygon
             (
               polygonVertexOffsetsX,
               polygonVertexOffsetsY,
               polygonVertexOffsetsX.length
             );

             // draw a filled version of this polygon
             big.setColor(Color.yellow);
             big.fillPolygon(polygon);

             // draw the polygon outline
             big.setColor(Color.blue);
             big.drawPolygon(polygon);

             // NOTE: these two polygon renderings should be drawn at the same location,
             // however under SDK 1.5.x, the "fillPolygon" method draws the filled
             // polygon at an offset from where it should be.

             // draw the BUFFERED IMAGE into this method's Graphics object
             g.drawImage(bi,0,0,this);

             // NOTE: if a BUFFERED IMAGE is NOT used, and the polygons are drawn
             // directly into the Graphics object of this method itself, then
             // both SDK 1.4.x and 1.5.0 work correctly. Thus, this appears to be
             // a problem with the rendering of poylgons via the "fillPolygon" method
             // specifically within BufferedImage objects.
           }
         }

         //////////////////////////////////////////////////////////////////////////////
         //////////////////////////////////////////////////////////////////////////////
         // methods
         //////////////////////////////////////////////////////////////////////////////
         //////////////////////////////////////////////////////////////////////////////

         public App()
         {
           ImageCanvas ic = new ImageCanvas();
           getContentPane().add(ic);
           this.setSize(640,480);
           this.setVisible(true);
         }

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

            Unassigned Unassigned
            prr Philip Race
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

              Created:
              Updated:
              Imported:
              Indexed: