Skip to content

8356137: GifImageDecode can produce opaque image when disposal method changes #25044

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 41 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
5dbf0a6
Merge pull request #1 from openjdk/master
mickleness May 22, 2022
333c87c
Merge pull request #2 from openjdk/master
mickleness May 24, 2022
57346a4
Merge pull request #3 from openjdk/master
mickleness May 29, 2022
69481fc
Merge pull request #4 from openjdk/master
mickleness Mar 11, 2023
81085d4
Merge pull request #5 from openjdk/master
mickleness Oct 13, 2024
93ef96a
Merge branch 'openjdk:master' into master
mickleness Dec 18, 2024
026c3db
Merge branch 'openjdk:master' into master
mickleness Mar 3, 2025
cc97808
Merge branch 'openjdk:master' into master
mickleness Mar 21, 2025
86b7944
Merge branch 'master' of https://github.com/mickleness/jdk
mickleness Apr 4, 2025
b9f767b
Merge branch 'master' of https://github.com/mickleness/jdk
mickleness Apr 20, 2025
15cef1c
Merge branch 'master' of https://github.com/mickleness/jdk
mickleness Apr 27, 2025
5dbf7a1
8356137: Fixing non-zero transparent pixel with disposal method 1
mickleness May 5, 2025
15d3e63
8356137: removing (c) 2002
mickleness May 6, 2025
d15cc02
8356137: removing layered pass/fail pattern
mickleness May 6, 2025
1d0b047
8356137: adding empty line at bottom of file
mickleness May 6, 2025
a0b963b
8356137: fixing typo
mickleness May 6, 2025
925d558
8356137: removing sentence fragment
mickleness May 6, 2025
ceb8e6a
8356137: cleaning up error message
mickleness May 7, 2025
c707223
8356120: updating (c) year
mickleness May 7, 2025
cadefb0
GifComparison: adding common helper class for gif bugs
mickleness May 14, 2025
0092256
8356137: change package to access GifComparison
mickleness May 14, 2025
61b9d77
GifComparison: code cleanup
mickleness May 14, 2025
c206839
GifComparison: fixing error message
mickleness May 14, 2025
cfa679a
8356137: readding transparency check at (20, 20)
mickleness May 14, 2025
bf967a1
Merge branch 'master' of https://github.com/mickleness/jdk
mickleness May 15, 2025
80944a2
Merge branch 'master' of https://github.com/mickleness/jdk
mickleness May 26, 2025
3f15c82
Merge branch 'master' into JDK-8356137
mickleness May 26, 2025
6caefc1
8356320: trivial whitespace and comment changes
mickleness May 26, 2025
1980454
8356137: Adding copyright to GifComparison
mickleness May 26, 2025
64a7b21
8356137: flood fill saved_image at initialization
mickleness May 27, 2025
62c2bf0
8356137: remove the redundant transparency check
mickleness May 27, 2025
2212cab
8356137: only inspect last frame of gif
mickleness May 27, 2025
e0546b1
8356137: trivial javadoc update
mickleness May 27, 2025
060d4dc
8356137: Adding GifBuilder to dynamically create test file
mickleness Jun 1, 2025
804c1e5
8356137: Use new GifBuilder for 8356137 test case
mickleness Jun 1, 2025
981a6f5
8356137: remove test file now that we use GifBuilder
mickleness Jun 1, 2025
392a9e5
8356137: adding copyright
mickleness Jun 1, 2025
9dddc5f
Merge branch 'master' into JDK-8356137
mickleness Jul 9, 2025
e143c8c
8354646: rewrapping line breaks so text is < 80 chars
mickleness Jul 9, 2025
ab24369
8356137: rewrapping line breaks so text is < 80 chars
mickleness Jul 9, 2025
1dbbe45
Merge remote-tracking branch 'origin/JDK-8356137' into JDK-8356137
mickleness Jul 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 1995, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1995, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -31,6 +31,7 @@
*/
package sun.awt.image;

import java.util.Arrays;
import java.util.Hashtable;
import java.io.InputStream;
import java.io.IOException;
Expand Down Expand Up @@ -569,6 +570,9 @@ private boolean readImage(boolean first, int disposal_method, int delay)
0, 0);
}
}
if (model.getTransparentPixel() > 0) {
Arrays.fill(saved_image, (byte) model.getTransparentPixel());
}
}

int hints = (interlace ? interlaceflags : normalflags);
Expand Down
166 changes: 166 additions & 0 deletions test/jdk/sun/awt/image/gif/GifBuilder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
/*
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageWriter;
import javax.imageio.ImageWriteParam;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataNode;
import javax.imageio.stream.ImageOutputStream;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.image.IndexColorModel;
import java.io.File;
import java.io.IOException;
import java.net.URL;

/**
* This constructs sample gif files used to test different combinations
* of gif frame disposal methods and transparent pixel indices.
*/
public class GifBuilder {

/**
* Different disposal methods for gif frames. These names exactly
* correspond to the String identifier ImageIO uses.
*/
public enum Disposal {none, doNotDispose, restoreToBackgroundColor,
restoreToPrevious};


/**
* @param disposal the frame disposal method
* @param isFirstTableIndexTransparent if true then the transparent pixel
* is set to 0. If false then the
* transparent pixel is set to the
* last index.
*/
public record FrameDescription(Disposal disposal, boolean
isFirstTableIndexTransparent) {}

/**
* This creates a sample gif image based on a series of FrameDescriptions,
* and the calls {@link GifComparison#run(URL)}
*/
public static void test(FrameDescription... frameDescriptions)
throws Throwable {
File file = createTestFile(frameDescriptions);
try {
GifComparison.run(file.toURI().toURL());
} finally {
file.delete();
}
}

private static File createTestFile(FrameDescription... frameDescriptions)
throws IOException {
Color[] colors = new Color[] {
Color.red,
Color.yellow,
Color.green,
Color.cyan
};
File file = File.createTempFile("GifBuilder", ".gif");
ImageOutputStream ios = ImageIO.createImageOutputStream(file);
ImageWriter gifWriter = ImageIO.getImageWritersByFormatName("GIF").
next();
gifWriter.setOutput(ios);
ImageWriteParam wparam = gifWriter.getDefaultWriteParam();
IIOMetadata streamMetadata = gifWriter.
getDefaultStreamMetadata(wparam);
gifWriter.prepareWriteSequence(streamMetadata);

IndexColorModel icm = createIndexColorModel(colors, colors.length - 1);

ImageTypeSpecifier s = ImageTypeSpecifier.createFromBufferedImageType(
BufferedImage.TYPE_BYTE_INDEXED);
IIOMetadata metadata = gifWriter.getDefaultImageMetadata(s, wparam);
String metaFormatName = metadata.getNativeMetadataFormatName();

for (FrameDescription frameDescription : frameDescriptions) {

// prepare the image:
int width = 100 + 50 * (icm.getMapSize() - 2);
BufferedImage bi = new BufferedImage(width, 100,
BufferedImage.TYPE_BYTE_INDEXED, icm);
Graphics2D g = bi.createGraphics();
g.setComposite(AlphaComposite.Clear);
g.fillRect(0, 0, bi.getWidth(), bi.getHeight());
g.setComposite(AlphaComposite.SrcOver);
int x = 0;
for (int a = 0; a < icm.getMapSize() - 1; a++) {
if (a != icm.getTransparentPixel()) {
Color color = new Color(icm.getRGB(a));
g.setColor(color);
g.fillOval(x, 0, 100, 100);
x += 50;
}
}
g.dispose();

// wrap attributes for gifWriter:
int transparentPixel = frameDescription.isFirstTableIndexTransparent
? 0 : icm.getMapSize() - 1;
IIOMetadata frameMetadata = gifWriter.getDefaultImageMetadata(
ImageTypeSpecifier.createFromRenderedImage(bi), wparam);
IIOMetadataNode root = new IIOMetadataNode(metaFormatName);
IIOMetadataNode gce = new IIOMetadataNode(
"GraphicControlExtension");
gce.setAttribute("disposalMethod",
frameDescription.disposal.name());
gce.setAttribute("userInputFlag", "FALSE");
gce.setAttribute("transparentColorFlag", "TRUE");
gce.setAttribute("delayTime", "0");
gce.setAttribute("transparentColorIndex",
Integer.toString(transparentPixel));
root.appendChild(gce);
frameMetadata.mergeTree(metaFormatName, root);
IIOImage img = new IIOImage(bi, null, frameMetadata);
gifWriter.writeToSequence(img, wparam);
}
gifWriter.endWriteSequence();
ios.flush();
ios.close();

return file;
}

private static IndexColorModel createIndexColorModel(Color[] colors,
int transparentIndex) {
byte[] r = new byte[colors.length];
byte[] g = new byte[colors.length];
byte[] b = new byte[colors.length];
for (int a = 0; a < colors.length; a++) {
r[a] = (byte) colors[a].getRed();
g[a] = (byte) colors[a].getGreen();
b[a] = (byte) colors[a].getBlue();
}
int bits = (int)(Math.log(colors.length) / Math.log(2) + .5);
return new IndexColorModel(bits, colors.length, r, g, b,
transparentIndex);
}
}
Loading