mirror of
https://github.com/openjdk/jdk.git
synced 2026-04-20 03:40:26 +00:00
6334602: Animated GIFs created from opaque PNG image frames appear transparent when loaded with Toolkit APIs
Reviewed-by: serb, prr
This commit is contained in:
parent
cbde1f4c5f
commit
7395b4d356
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1995, 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1995, 2016, 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
|
||||
@ -31,7 +31,6 @@
|
||||
*/
|
||||
package sun.awt.image;
|
||||
|
||||
import java.util.Vector;
|
||||
import java.util.Hashtable;
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
@ -620,7 +619,6 @@ public class GifImageDecoder extends ImageDecoder {
|
||||
|
||||
class GifFrame {
|
||||
private static final boolean verbose = false;
|
||||
private static IndexColorModel trans_model;
|
||||
|
||||
static final int DISPOSAL_NONE = 0x00;
|
||||
static final int DISPOSAL_SAVE = 0x01;
|
||||
@ -718,12 +716,6 @@ class GifFrame {
|
||||
case DISPOSAL_BGCOLOR:
|
||||
byte tpix;
|
||||
if (model.getTransparentPixel() < 0) {
|
||||
model = trans_model;
|
||||
if (model == null) {
|
||||
model = new IndexColorModel(8, 1,
|
||||
new byte[4], 0, true);
|
||||
trans_model = model;
|
||||
}
|
||||
tpix = 0;
|
||||
} else {
|
||||
tpix = (byte) model.getTransparentPixel();
|
||||
|
||||
422
jdk/test/java/awt/image/OpaquePNGToGIFTest.java
Normal file
422
jdk/test/java/awt/image/OpaquePNGToGIFTest.java
Normal file
@ -0,0 +1,422 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6334602
|
||||
* @summary Test verifies that when we create GIF image from Opaque PNG images
|
||||
* it should not be transparent.
|
||||
* @modules java.desktop/com.sun.imageio.plugins.gif
|
||||
* @run main/manual OpaquePNGToGIFTest
|
||||
*/
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import com.sun.imageio.plugins.gif.GIFImageMetadata;
|
||||
import java.awt.Canvas;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Frame;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.GridBagConstraints;
|
||||
import java.awt.GridBagLayout;
|
||||
import java.awt.Image;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javax.imageio.IIOImage;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.ImageWriteParam;
|
||||
import javax.imageio.ImageWriter;
|
||||
import javax.imageio.metadata.IIOMetadata;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import javax.imageio.stream.ImageOutputStream;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
public final class OpaquePNGToGIFTest extends Frame {
|
||||
|
||||
Image img = null;
|
||||
private Dimension prefImgSize = new Dimension(100, 100);
|
||||
private Color canvasColor = new Color(0, 255, 0);
|
||||
File outputFile = null;
|
||||
ImageCanvas imageCanvas = new ImageCanvas();
|
||||
FileOutputStream fos;
|
||||
private static GridBagLayout layout;
|
||||
private static JPanel mainControlPanel;
|
||||
private static JPanel resultButtonPanel;
|
||||
private static JPanel canvasPanel;
|
||||
private static JLabel instructionText;
|
||||
private static JButton startTestButton;
|
||||
private static JButton passButton;
|
||||
private static JButton failButton;
|
||||
private static JDialog dialog;
|
||||
private static Frame instructionFrame;
|
||||
private static Frame imageFrame;
|
||||
Toolkit tk;
|
||||
ImageWriter writer = null;
|
||||
boolean testPassed, testGeneratedInterrupt, startButtonClicked;
|
||||
private static Thread mainThread;
|
||||
|
||||
public OpaquePNGToGIFTest() throws Exception {
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
try {
|
||||
startTest();
|
||||
} catch (Exception ex) {
|
||||
Logger.getLogger(OpaquePNGToGIFTest.class.getName()).
|
||||
log(Level.SEVERE, null, ex);
|
||||
}
|
||||
});
|
||||
mainThread = Thread.currentThread();
|
||||
try {
|
||||
Thread.sleep(60000);
|
||||
} catch (InterruptedException e) {
|
||||
if (!testPassed && testGeneratedInterrupt) {
|
||||
throw new RuntimeException("Test failed or didnt run"
|
||||
+ " properly");
|
||||
}
|
||||
}
|
||||
if (!testGeneratedInterrupt) {
|
||||
if (img != null) {
|
||||
img.flush();
|
||||
}
|
||||
instructionFrame.dispose();
|
||||
if (startButtonClicked) {
|
||||
imageFrame.dispose();
|
||||
}
|
||||
fos.close();
|
||||
Files.delete(outputFile.toPath());
|
||||
throw new RuntimeException("user has not executed the test");
|
||||
}
|
||||
}
|
||||
|
||||
public void startTest() throws Exception {
|
||||
instructionFrame = new Frame();
|
||||
dialog = new JDialog(instructionFrame);
|
||||
dialog.setTitle("Instruction Dialog");
|
||||
layout = new GridBagLayout();
|
||||
mainControlPanel = new JPanel(layout);
|
||||
resultButtonPanel = new JPanel(layout);
|
||||
canvasPanel = new JPanel();
|
||||
GridBagConstraints gbc = new GridBagConstraints();
|
||||
String instructions
|
||||
= "<html> INSTRUCTIONS:<br><br>"
|
||||
+ "After clicking on Start Test button you will see Red<br> "
|
||||
+ " circle drawn with light blue background, if the circle<br>"
|
||||
+ " color changes from Red to Green then press button Fail,<br>"
|
||||
+ " if it stays Red then press button Pass.<br><br></html>";
|
||||
instructionText = new JLabel();
|
||||
instructionText.setText(instructions);
|
||||
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 0;
|
||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||
mainControlPanel.add(instructionText, gbc);
|
||||
startTestButton = new JButton("Start Test");
|
||||
startTestButton.setActionCommand("Start Test");
|
||||
startTestButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
try {
|
||||
startButtonClicked = true;
|
||||
imageFrame = new Frame();
|
||||
|
||||
Iterator it = ImageIO.getImageWritersByFormatName("GIF");
|
||||
while (it.hasNext()) {
|
||||
writer = (ImageWriter) it.next();
|
||||
break;
|
||||
}
|
||||
// read input opaque PNG image.
|
||||
String fileName = "opaque_input.png";
|
||||
String sep = System.getProperty("file.separator");
|
||||
String dir = System.getProperty("test.src", ".");
|
||||
System.out.println(dir + " " + sep);
|
||||
String filePath = dir + sep + fileName;
|
||||
File inputFile = new File(filePath);
|
||||
ImageInputStream iis = ImageIO.
|
||||
createImageInputStream(inputFile);
|
||||
ImageReader reader = null;
|
||||
Iterator readerIter = ImageIO.getImageReaders(iis);
|
||||
while (readerIter.hasNext()) {
|
||||
reader = (ImageReader) readerIter.next();
|
||||
break;
|
||||
}
|
||||
|
||||
reader.setInput(iis);
|
||||
IIOMetadata imgData = reader.getImageMetadata(0);
|
||||
BufferedImage bi = reader.read(0);
|
||||
|
||||
//create temporary GIF imageas output
|
||||
File directory = new File(dir + sep);
|
||||
outputFile = File.createTempFile("output",
|
||||
".gif", directory);
|
||||
createAnimatedImage(bi, imgData,
|
||||
writer, outputFile);
|
||||
writer.dispose();
|
||||
iis.close();
|
||||
if (outputFile == null) {
|
||||
throw new RuntimeException("Unable to create output GIF"
|
||||
+ " file");
|
||||
}
|
||||
// extract GIF image using Toolkit and show it on a Panel.
|
||||
tk = Toolkit.getDefaultToolkit();
|
||||
img = tk.getImage(dir + sep + outputFile.getName());
|
||||
directory.delete();
|
||||
imageCanvas.setBackground(canvasColor);
|
||||
imageCanvas.setImage(img);
|
||||
imageCanvas.setPreferredSize(prefImgSize);
|
||||
canvasPanel.doLayout();
|
||||
|
||||
canvasPanel.add(imageCanvas);
|
||||
imageFrame.add("Center", canvasPanel);
|
||||
imageFrame.setSize(200, 200);
|
||||
imageFrame.setVisible(true);
|
||||
imageFrame.addWindowListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowClosing(WindowEvent e) {
|
||||
try {
|
||||
img.flush();
|
||||
instructionFrame.dispose();
|
||||
imageFrame.dispose();
|
||||
fail();
|
||||
} finally {
|
||||
try {
|
||||
fos.close();
|
||||
Files.delete(outputFile.toPath());
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(OpaquePNGToGIFTest.class.
|
||||
getName()).log(Level.SEVERE, null,
|
||||
ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(OpaquePNGToGIFTest.class.getName()).
|
||||
log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
});
|
||||
passButton = new JButton("Pass");
|
||||
passButton.setActionCommand("Pass");
|
||||
passButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
try {
|
||||
if (img != null) {
|
||||
img.flush();
|
||||
}
|
||||
instructionFrame.dispose();
|
||||
if (!startButtonClicked) {
|
||||
fail();
|
||||
} else {
|
||||
imageFrame.dispose();
|
||||
pass();
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
fos.close();
|
||||
Files.delete(outputFile.toPath());
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(OpaquePNGToGIFTest.class.
|
||||
getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
failButton = new JButton("Fail");
|
||||
failButton.setActionCommand("Fail");
|
||||
failButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
try {
|
||||
if (img != null) {
|
||||
img.flush();
|
||||
}
|
||||
instructionFrame.dispose();
|
||||
if (!startButtonClicked) {
|
||||
fail();
|
||||
} else {
|
||||
imageFrame.dispose();
|
||||
fail();
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
fos.close();
|
||||
Files.delete(outputFile.toPath());
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(OpaquePNGToGIFTest.class.
|
||||
getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
gbc.gridx = 1;
|
||||
gbc.gridy = 0;
|
||||
resultButtonPanel.add(startTestButton, gbc);
|
||||
gbc.gridx = 2;
|
||||
gbc.gridy = 0;
|
||||
resultButtonPanel.add(passButton, gbc);
|
||||
gbc.gridx = 3;
|
||||
gbc.gridy = 0;
|
||||
resultButtonPanel.add(failButton, gbc);
|
||||
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 1;
|
||||
mainControlPanel.add(resultButtonPanel, gbc);
|
||||
|
||||
dialog.add(mainControlPanel);
|
||||
dialog.setSize(400, 200);
|
||||
dialog.setLocationRelativeTo(null);
|
||||
dialog.setVisible(true);
|
||||
|
||||
dialog.addWindowListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowClosing(WindowEvent e) {
|
||||
try {
|
||||
if (img != null) {
|
||||
img.flush();
|
||||
}
|
||||
instructionFrame.dispose();
|
||||
if (!startButtonClicked) {
|
||||
fail();
|
||||
} else {
|
||||
imageFrame.dispose();
|
||||
fail();
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
fos.close();
|
||||
Files.delete(outputFile.toPath());
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(OpaquePNGToGIFTest.class.
|
||||
getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public synchronized void pass() {
|
||||
testPassed = true;
|
||||
testGeneratedInterrupt = true;
|
||||
mainThread.interrupt();
|
||||
}
|
||||
|
||||
public synchronized void fail() {
|
||||
testPassed = false;
|
||||
testGeneratedInterrupt = true;
|
||||
mainThread.interrupt();
|
||||
}
|
||||
|
||||
public void createAnimatedImage(BufferedImage bi, IIOMetadata metadata,
|
||||
ImageWriter writer, File outputFile) {
|
||||
try {
|
||||
|
||||
fos = new FileOutputStream(outputFile);
|
||||
ImageOutputStream ios = ImageIO.createImageOutputStream(fos);
|
||||
System.out.println(ios);
|
||||
writer.setOutput(ios);
|
||||
|
||||
ImageWriteParam param = writer.getDefaultWriteParam();
|
||||
IIOMetadata streamData = writer.getDefaultStreamMetadata(param);
|
||||
|
||||
writer.prepareWriteSequence(streamData);
|
||||
ImageTypeSpecifier specify = new ImageTypeSpecifier(bi);
|
||||
IIOMetadata imgData = writer.convertImageMetadata(
|
||||
(IIOMetadata) metadata, specify, param);
|
||||
GIFImageMetadata gifData = setAnimationProperties(imgData);
|
||||
IIOImage iim = new IIOImage(bi, null, gifData);
|
||||
param.setProgressiveMode(param.MODE_DISABLED);
|
||||
writer.writeToSequence(iim, param);
|
||||
writer.endWriteSequence();
|
||||
ios.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public GIFImageMetadata setAnimationProperties(IIOMetadata data) {
|
||||
ArrayList appIDs = new ArrayList();
|
||||
appIDs.add(new String("NETSCAPE").getBytes());
|
||||
ArrayList authCodes = new ArrayList();
|
||||
authCodes.add(new String("2.0").getBytes());
|
||||
ArrayList appData = new ArrayList();
|
||||
byte[] authData = {1, 0, 0};
|
||||
appData.add(authData);
|
||||
|
||||
GIFImageMetadata gifData = (GIFImageMetadata) data;
|
||||
gifData.delayTime = 200;
|
||||
// If we set disposalMethod to 2 then only the issue is reproduced.
|
||||
gifData.disposalMethod = 2;
|
||||
gifData.userInputFlag = false;
|
||||
|
||||
gifData.applicationIDs = appIDs;
|
||||
gifData.authenticationCodes = authCodes;
|
||||
gifData.applicationData = appData;
|
||||
|
||||
return gifData;
|
||||
}
|
||||
|
||||
public static void main(String args[]) throws Exception {
|
||||
OpaquePNGToGIFTest test = new OpaquePNGToGIFTest();
|
||||
}
|
||||
|
||||
class ImageCanvas extends Canvas {
|
||||
|
||||
Image im = null;
|
||||
|
||||
public void setImage(Image img) {
|
||||
im = img;
|
||||
}
|
||||
|
||||
public void clearImage() {
|
||||
im = null;
|
||||
repaint();
|
||||
}
|
||||
|
||||
public void paint(Graphics g) {
|
||||
Graphics2D g2d = (Graphics2D) g;
|
||||
|
||||
if (im != null) {
|
||||
g2d.drawImage(im, 1, 1, getWidth(), getHeight(), this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BIN
jdk/test/java/awt/image/opaque_input.png
Normal file
BIN
jdk/test/java/awt/image/opaque_input.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 775 B |
Loading…
x
Reference in New Issue
Block a user