JDK-4994576 : REGRESSION: affinetransform between 2 managed images causes pallete corruption
  • Type: Bug
  • Component: client-libs
  • Sub-Component: 2d
  • Affected Version: 5.0
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2004-02-13
  • Updated: 2004-04-27
  • Resolved: 2004-04-27
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
5.0 b49Fixed
Description

Name: gm110360			Date: 02/13/2004


FULL PRODUCT VERSION :
java version "1.5.0-beta"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0-beta-b32c)
Java HotSpot(TM) Client VM (build 1.5.0-beta-b32c, mixed mode)

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

EXTRA RELEVANT SYSTEM CONFIGURATION :
Athlon 1.33, GeForce2 GTS 32mb, directX9

A DESCRIPTION OF THE PROBLEM :
When performing a rotate(), when the source and destination images are managed Images, the pallete appears corrupt in the destination image.

This Executable Jar demonstrates the problem. (sourcecode and image included in jar)
http://www.pkl.net/~rsc/4KShooter/TestCase.jar

The bug is covered in this Thread on javagaming.org also :-

http://www.javagaming.org/cgi-bin/JGNetForums/YaBB.cgi?board=2D;action=display;num=1076627809

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
compile and run the code provided

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
With Java1.4.2_03 the rotation frames are displayed fine.
With Java1.5beta1 the palette is corrupted in all frames but 1 (when the rotation angle is 0)

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.imageio.ImageIO;
public class TestCase extends Frame implements Runnable
{
   
   static final int IMAGE_WIDTH = 32, IMAGE_HEIGHT = 32;
   static final int SHIP_WIDTH = 21, SHIP_HEIGHT = 25;

   static final int NUM_ROTATIONS = 90;
   static final float ANGLE_INC = (float)((Math.PI*2d) / NUM_ROTATIONS);
   
   static final String STRING_SHIPS_FILENAME = "image.gif";
   
   static final int SCREEN_WIDTH = 400,SCREEN_HEIGHT = 300;
   
   BufferedImage loadedImage;
   
   BufferedImage [] objectImages = new BufferedImage[NUM_ROTATIONS];
   
   public TestCase() throws Exception
   {
      super("ManagedImage/AffineTransform bug");
      
      setSize(SCREEN_WIDTH,SCREEN_HEIGHT);
      show();
      GraphicsConfiguration gc = getGraphicsConfiguration();
      
      //load the image
      loadedImage = ImageIO.read(getClass().getResource(STRING_SHIPS_FILENAME));

      //copy it into the centre of the 1st element of the array
      BufferedImage src = gc.createCompatibleImage(IMAGE_WIDTH,IMAGE_HEIGHT, Transparency.BITMASK);
      {
         Graphics2D g = src.createGraphics();
         g.drawImage(loadedImage,(IMAGE_WIDTH-SHIP_WIDTH)/2,(IMAGE_HEIGHT-SHIP_HEIGHT)/2,null);
      }
         
      for(int i =0;i < NUM_ROTATIONS;i++)
      {
         //create rotations
         objectImages[i] = gc.createCompatibleImage(IMAGE_WIDTH,IMAGE_HEIGHT,Transparency.BITMASK);
         Graphics2D g = objectImages[i].createGraphics();
         g.rotate(i*ANGLE_INC,IMAGE_WIDTH/2,IMAGE_HEIGHT/2); //combined, saved 1byte
         g.drawImage(src,0,0,null);
      }
         
      createBufferStrategy(2);
      enableEvents(AWTEvent.WINDOW_EVENT_MASK);
      
      Thread me = new Thread(this);
      me.start();
   }
   
   boolean running = true;
   
   public void processWindowEvent(WindowEvent we)
   {
      if(we.getID()==WindowEvent.WINDOW_CLOSING)
      {
         running = false;
      }
   }
   
   public void run()
   {
      int angle = 80;
      while(running)
      {
         BufferStrategy bs = getBufferStrategy();
         Graphics g = bs.getDrawGraphics();
         g.setColor(Color.black);
         g.fillRect(0,0,SCREEN_WIDTH,SCREEN_HEIGHT);
         g.drawImage(objectImages[angle],40,40,null);
         g.setColor(Color.white);
         g.drawString(String.valueOf(angle*4),50,90);
         bs.show();
         
         
         angle=(angle+1)%NUM_ROTATIONS;
         try
         {
            Thread.sleep(50);
         }
         catch(Exception e)
         {
         }
      }
      dispose();
   }
   
   
   public static void main(String [] args)
   {
      try
      {
         new TestCase();
      }
      catch(Exception e)
      {
         System.out.println("Something went wrong");
      }
   }
}
---------- END SOURCE ----------

Release Regression From : 1.4.2
The above release value was the last known release where this 
bug was known to work. Since then there has been a regression.

(Incident Review ID: 238672) 
======================================================================

Comments
CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: tiger-beta2 FIXED IN: tiger-beta2 INTEGRATED IN: tiger-b49 tiger-beta2
2004-06-14

EVALUATION First seen in 2D's nightly build on 2003-09-15. Caused by the fix for 4886732: AffineTransformOp does not work properly for some of the rendering hints (changes in awt_ImagingLib.c, http://javaweb.sfbay/jcg/1.5.0-tiger/2D/4886732). ###@###.### 2004-02-19 This is kind of obscure bug, so let me start with a brief introduction of how we interface with medialib code (at least, as I understand it). Suppose we filter a SRC image to a DST with transform. The longest chain of conversions may be: SRC -1-> SRC_ARGB DST -2-> DST_ARGB SRC_ARGB -3-> DST_ARGB DST_ARGB -4-> DST where 1,2,3 are conversions happening in awt_ImagingLib.c (namely, expand/set*default methods), and 3 is the transformation itself (for example, mlib_ImageAffine_s32_1ch_nn). Note that whether 1,2,4 happen or not depends on the format of the source and destination images. So, prior to the fix for 4886732, 1,2 and 4 were broken because they used to deal with the data on byte-by-byte basis, thus screwing up the byte order. Interestingly, it worked if the number of conversions was even: the bytes will be switched twice: once converting the source image, and once - converting the results to the destination. But for some combinations of image formats only source, or only destination conversion is needed, so those cases were not working. Andrew's fix for 4886732 addressed almost all of the cases except one, when conversion 4 was required for IntegerComponentRaster-based images. One of the cases would be transforming a 25-bit image to almost any destination. For example 25-bit to BGR 25-bit -1-> ARGB // good, covered by previous fix ARGB -2-> ARGB ARGB -3-> BGR // bytes swapped during conversion in setPackedICRdefault So, my fix plugs this hole by fixing the setPackedICRdefault method. (awt_ImagingLib.c line 3179). While at it, I've removed a lot of duplicated code, by making set/expandPacked* call corresponding set/expandPacked*default method, with 'supportsAlpha' set to FALSE, as that was the only difference that I could find between the default and non-default methods. (Correction: it was pointed out that they were indeed different in their initial state, but when fixed, they'd appear to be the exactly the same) Note that the 'non-default' method are not even used in the current code: they're called from store/expandRasterArray, which are never called. After fixing this bug I extended Andrew's testcase to render (both using drawImage and AffineTransformOp.filter) from any to almost any of the default image formats (plus a couple of custom ones), and found that we still have problems in cases with ushort data. For example, rendering transformed USHORT_4444_ARGB to almost any format results in garbage, on any platform, on 1.5 and 1.4.2. But this happens only if AffineTransformOp is used directly instead of just calling drawImage with transform set. The reason for this is a bug in one of the storeImageArray method, which never converts the image back to the destination format from expanded ARGB, but just mem-copies it instead (see awt_ImagingLib.c:2404). The drawImage escapes this bug by always executing AffineTransformOp.filter(src, 'new ARGB image') - see DrawImage.renderImageXform(), thus avoiding conversion 3, and then using our sw loops to convert ARBG to dest. As I see it, there are two ways to fix this: - fix medialib conversion - fix AffineTransformOp to behave the same way the code in renderImageXform does (that is, avoid the buggy code) The ideal solution would be to do both, but I really didn't want to spend time working on this medialib code since it's likely that it'll become obsolete with Jim's new transformation code. In any case, we should try to make our drawImage and AffineTransformOp.filter stuff consistent, probably by introducing some helper code in the latter. ###@###.### 2004-03-11
2004-03-11