REGRESSION: affinetransform between 2 managed images causes pallete corruption
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 :
customer 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)
The bug is covered in this Thread on javagaming.org also :-
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
compile and run the code provided
EXPECTED VERSUS ACTUAL BEHAVIOR :
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)
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
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  objectImages = new BufferedImage[NUM_ROTATIONS];
public TestCase() throws Exception
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();
for(int i =0;i < NUM_ROTATIONS;i++)
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
Thread me = new Thread(this);
boolean running = true;
public void processWindowEvent(WindowEvent we)
running = false;
public void run()
int angle = 80;
BufferStrategy bs = getBufferStrategy();
Graphics g = bs.getDrawGraphics();
public static void main(String  args)
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)
First seen in 2D's nightly build on 2003-09-15. Caused by the fix
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).
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
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
Andrew's fix for 4886732 addressed almost all of the cases except
one, when conversion 4 was required for IntegerComponentRaster-based
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
Note that the 'non-default' method are not even used in the current
code: they're called from store/expandRasterArray, which are never
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
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
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.
PLEASE NOTE: JDK6 is formerly known as Project Mustang