/* * ShootOFF - Software for Laser Dry Fire Training * Copyright (C) 2016 phrack * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.shootoff.targets.animation; import java.awt.Color; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import javax.imageio.ImageIO; import javax.imageio.ImageReader; import javax.imageio.metadata.IIOMetadata; import javax.imageio.metadata.IIOMetadataNode; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import javafx.scene.image.ImageView; import javafx.util.Duration; public class GifAnimation extends SpriteAnimation { private static ImageFrame[] frames; public GifAnimation(ImageView imageView, InputStream gifStream) throws IOException { super(imageView, readGif(gifStream)); int delay = frames[0].getDelay(); if (delay < 1) delay = SpriteAnimation.DEFAULT_DELAY; setCycleDuration(Duration.millis(delay)); } public GifAnimation(ImageView imageView, File gifFile) throws FileNotFoundException, IOException { super(imageView, readGif(new FileInputStream(gifFile))); int delay = frames[0].getDelay(); if (delay < 1) delay = SpriteAnimation.DEFAULT_DELAY; setCycleDuration(Duration.millis(delay)); } // This method is from http://stackoverflow.com/a/17269591 private static ImageFrame[] readGif(InputStream stream) throws IOException { final ArrayList<ImageFrame> frames = new ArrayList<>(2); int width = -1; int height = -1; final ImageReader reader = ImageIO.getImageReadersByFormatName("gif").next(); reader.setInput(ImageIO.createImageInputStream(stream)); final IIOMetadata metadata = reader.getStreamMetadata(); if (metadata != null) { final IIOMetadataNode globalRoot = (IIOMetadataNode) metadata.getAsTree(metadata.getNativeMetadataFormatName()); final NodeList globalScreenDescriptor = globalRoot.getElementsByTagName("LogicalScreenDescriptor"); if (globalScreenDescriptor.getLength() > 0) { final IIOMetadataNode screenDescriptor = (IIOMetadataNode) globalScreenDescriptor.item(0); if (screenDescriptor != null) { width = Integer.parseInt(screenDescriptor.getAttribute("logicalScreenWidth")); height = Integer.parseInt(screenDescriptor.getAttribute("logicalScreenHeight")); } } } BufferedImage master = null; Graphics2D masterGraphics = null; for (int frameIndex = 0;; frameIndex++) { BufferedImage image; try { image = reader.read(frameIndex); } catch (final IndexOutOfBoundsException io) { break; } if (width == -1 || height == -1) { width = image.getWidth(); height = image.getHeight(); } final IIOMetadataNode root = (IIOMetadataNode) reader.getImageMetadata(frameIndex) .getAsTree("javax_imageio_gif_image_1.0"); final IIOMetadataNode gce = (IIOMetadataNode) root.getElementsByTagName("GraphicControlExtension").item(0); final int delay = Integer.parseInt(gce.getAttribute("delayTime")) * 10; final String disposal = gce.getAttribute("disposalMethod"); int x = 0; int y = 0; if (master == null) { master = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); masterGraphics = master.createGraphics(); masterGraphics.setBackground(new Color(0, 0, 0, 0)); } else { final NodeList children = root.getChildNodes(); for (int nodeIndex = 0; nodeIndex < children.getLength(); nodeIndex++) { final Node nodeItem = children.item(nodeIndex); if (nodeItem.getNodeName().equals("ImageDescriptor")) { final NamedNodeMap map = nodeItem.getAttributes(); x = Integer.parseInt(map.getNamedItem("imageLeftPosition").getNodeValue()); y = Integer.parseInt(map.getNamedItem("imageTopPosition").getNodeValue()); } } } masterGraphics.drawImage(image, x, y, null); final BufferedImage copy = new BufferedImage(master.getColorModel(), master.copyData(null), master.isAlphaPremultiplied(), null); frames.add(new ImageFrame(copy, delay, disposal)); if (disposal.equals("restoreToPrevious")) { BufferedImage from = null; for (int i = frameIndex - 1; i >= 0; i--) { if (!frames.get(i).getDisposal().equals("restoreToPrevious") || frameIndex == 0) { from = frames.get(i).getBufferedImage(); break; } } master = new BufferedImage(from.getColorModel(), from.copyData(null), from.isAlphaPremultiplied(), null); masterGraphics = master.createGraphics(); masterGraphics.setBackground(new Color(0, 0, 0, 0)); } else if (disposal.equals("restoreToBackgroundColor")) { masterGraphics.clearRect(x, y, image.getWidth(), image.getHeight()); } } reader.dispose(); GifAnimation.frames = frames.toArray(new ImageFrame[frames.size()]); return GifAnimation.frames; } }