United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
JDK-4994576 : REGRESSION: affinetransform between 2 managed images causes pallete corruption

Details
Type:
Bug
Submit Date:
2004-02-13
Status:
Resolved
Updated Date:
2004-04-27
Project Name:
JDK
Resolved Date:
2004-04-27
Component:
client-libs
OS:
windows_xp
Sub-Component:
2d
CPU:
x86
Priority:
P3
Resolution:
Fixed
Affected Versions:
5.0
Fixed Versions:
5.0 (b49)

Related Reports

Sub Tasks

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



Hardware and Software, Engineered to Work Together