Skip to content

Commit 529049b

Browse files
micklenessjayathirthrao
authored andcommitted
8356137: GifImageDecode can produce opaque image when disposal method changes
Reviewed-by: jdv, prr
1 parent eddfc64 commit 529049b

4 files changed

Lines changed: 618 additions & 1 deletion

File tree

src/java.desktop/share/classes/sun/awt/image/GifImageDecoder.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1995, 2018, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1995, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -31,6 +31,7 @@
3131
*/
3232
package sun.awt.image;
3333

34+
import java.util.Arrays;
3435
import java.util.Hashtable;
3536
import java.io.InputStream;
3637
import java.io.IOException;
@@ -569,6 +570,9 @@ private boolean readImage(boolean first, int disposal_method, int delay)
569570
0, 0);
570571
}
571572
}
573+
if (model.getTransparentPixel() > 0) {
574+
Arrays.fill(saved_image, (byte) model.getTransparentPixel());
575+
}
572576
}
573577

574578
int hints = (interlace ? interlaceflags : normalflags);
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
/*
2+
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
import javax.imageio.IIOImage;
25+
import javax.imageio.ImageIO;
26+
import javax.imageio.ImageTypeSpecifier;
27+
import javax.imageio.ImageWriter;
28+
import javax.imageio.ImageWriteParam;
29+
import javax.imageio.metadata.IIOMetadata;
30+
import javax.imageio.metadata.IIOMetadataNode;
31+
import javax.imageio.stream.ImageOutputStream;
32+
import java.awt.AlphaComposite;
33+
import java.awt.Color;
34+
import java.awt.Graphics2D;
35+
import java.awt.image.BufferedImage;
36+
import java.awt.image.IndexColorModel;
37+
import java.io.File;
38+
import java.io.IOException;
39+
import java.net.URL;
40+
41+
/**
42+
* This constructs sample gif files used to test different combinations
43+
* of gif frame disposal methods and transparent pixel indices.
44+
*/
45+
public class GifBuilder {
46+
47+
/**
48+
* Different disposal methods for gif frames. These names exactly
49+
* correspond to the String identifier ImageIO uses.
50+
*/
51+
public enum Disposal {none, doNotDispose, restoreToBackgroundColor,
52+
restoreToPrevious};
53+
54+
55+
/**
56+
* @param disposal the frame disposal method
57+
* @param isFirstTableIndexTransparent if true then the transparent pixel
58+
* is set to 0. If false then the
59+
* transparent pixel is set to the
60+
* last index.
61+
*/
62+
public record FrameDescription(Disposal disposal, boolean
63+
isFirstTableIndexTransparent) {}
64+
65+
/**
66+
* This creates a sample gif image based on a series of FrameDescriptions,
67+
* and the calls {@link GifComparison#run(URL)}
68+
*/
69+
public static void test(FrameDescription... frameDescriptions)
70+
throws Throwable {
71+
File file = createTestFile(frameDescriptions);
72+
try {
73+
GifComparison.run(file.toURI().toURL());
74+
} finally {
75+
file.delete();
76+
}
77+
}
78+
79+
private static File createTestFile(FrameDescription... frameDescriptions)
80+
throws IOException {
81+
Color[] colors = new Color[] {
82+
Color.red,
83+
Color.yellow,
84+
Color.green,
85+
Color.cyan
86+
};
87+
File file = File.createTempFile("GifBuilder", ".gif");
88+
ImageOutputStream ios = ImageIO.createImageOutputStream(file);
89+
ImageWriter gifWriter = ImageIO.getImageWritersByFormatName("GIF").
90+
next();
91+
gifWriter.setOutput(ios);
92+
ImageWriteParam wparam = gifWriter.getDefaultWriteParam();
93+
IIOMetadata streamMetadata = gifWriter.
94+
getDefaultStreamMetadata(wparam);
95+
gifWriter.prepareWriteSequence(streamMetadata);
96+
97+
IndexColorModel icm = createIndexColorModel(colors, colors.length - 1);
98+
99+
ImageTypeSpecifier s = ImageTypeSpecifier.createFromBufferedImageType(
100+
BufferedImage.TYPE_BYTE_INDEXED);
101+
IIOMetadata metadata = gifWriter.getDefaultImageMetadata(s, wparam);
102+
String metaFormatName = metadata.getNativeMetadataFormatName();
103+
104+
for (FrameDescription frameDescription : frameDescriptions) {
105+
106+
// prepare the image:
107+
int width = 100 + 50 * (icm.getMapSize() - 2);
108+
BufferedImage bi = new BufferedImage(width, 100,
109+
BufferedImage.TYPE_BYTE_INDEXED, icm);
110+
Graphics2D g = bi.createGraphics();
111+
g.setComposite(AlphaComposite.Clear);
112+
g.fillRect(0, 0, bi.getWidth(), bi.getHeight());
113+
g.setComposite(AlphaComposite.SrcOver);
114+
int x = 0;
115+
for (int a = 0; a < icm.getMapSize() - 1; a++) {
116+
if (a != icm.getTransparentPixel()) {
117+
Color color = new Color(icm.getRGB(a));
118+
g.setColor(color);
119+
g.fillOval(x, 0, 100, 100);
120+
x += 50;
121+
}
122+
}
123+
g.dispose();
124+
125+
// wrap attributes for gifWriter:
126+
int transparentPixel = frameDescription.isFirstTableIndexTransparent
127+
? 0 : icm.getMapSize() - 1;
128+
IIOMetadata frameMetadata = gifWriter.getDefaultImageMetadata(
129+
ImageTypeSpecifier.createFromRenderedImage(bi), wparam);
130+
IIOMetadataNode root = new IIOMetadataNode(metaFormatName);
131+
IIOMetadataNode gce = new IIOMetadataNode(
132+
"GraphicControlExtension");
133+
gce.setAttribute("disposalMethod",
134+
frameDescription.disposal.name());
135+
gce.setAttribute("userInputFlag", "FALSE");
136+
gce.setAttribute("transparentColorFlag", "TRUE");
137+
gce.setAttribute("delayTime", "0");
138+
gce.setAttribute("transparentColorIndex",
139+
Integer.toString(transparentPixel));
140+
root.appendChild(gce);
141+
frameMetadata.mergeTree(metaFormatName, root);
142+
IIOImage img = new IIOImage(bi, null, frameMetadata);
143+
gifWriter.writeToSequence(img, wparam);
144+
}
145+
gifWriter.endWriteSequence();
146+
ios.flush();
147+
ios.close();
148+
149+
return file;
150+
}
151+
152+
private static IndexColorModel createIndexColorModel(Color[] colors,
153+
int transparentIndex) {
154+
byte[] r = new byte[colors.length];
155+
byte[] g = new byte[colors.length];
156+
byte[] b = new byte[colors.length];
157+
for (int a = 0; a < colors.length; a++) {
158+
r[a] = (byte) colors[a].getRed();
159+
g[a] = (byte) colors[a].getGreen();
160+
b[a] = (byte) colors[a].getBlue();
161+
}
162+
int bits = (int)(Math.log(colors.length) / Math.log(2) + .5);
163+
return new IndexColorModel(bits, colors.length, r, g, b,
164+
transparentIndex);
165+
}
166+
}

0 commit comments

Comments
 (0)