diff --git a/jdk/src/java.desktop/share/classes/sun/awt/image/GifImageDecoder.java b/jdk/src/java.desktop/share/classes/sun/awt/image/GifImageDecoder.java index 12f6cfa64b5..fa4612f1848 100644 --- a/jdk/src/java.desktop/share/classes/sun/awt/image/GifImageDecoder.java +++ b/jdk/src/java.desktop/share/classes/sun/awt/image/GifImageDecoder.java @@ -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(); diff --git a/jdk/test/java/awt/image/OpaquePNGToGIFTest.java b/jdk/test/java/awt/image/OpaquePNGToGIFTest.java new file mode 100644 index 00000000000..ce22df97e36 --- /dev/null +++ b/jdk/test/java/awt/image/OpaquePNGToGIFTest.java @@ -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 + = " INSTRUCTIONS:

" + + "After clicking on Start Test button you will see Red
" + + " circle drawn with light blue background, if the circle
" + + " color changes from Red to Green then press button Fail,
" + + " if it stays Red then press button Pass.

"; + 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); + } + } + } +} + diff --git a/jdk/test/java/awt/image/opaque_input.png b/jdk/test/java/awt/image/opaque_input.png new file mode 100644 index 00000000000..54eba5724f9 Binary files /dev/null and b/jdk/test/java/awt/image/opaque_input.png differ