| /* | |
| * 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); | |
| } | |
| } | |
| } | |
| } | |