/*
* Copyright (C) 2010 Brockmann Consult GmbH (info@brockmann-consult.de)
*
* 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 org.esa.snap.timeseries.export.animations;
import com.bc.ceres.core.ProgressMonitor;
import com.bc.ceres.swing.progress.ProgressMonitorSwingWorker;
import org.esa.snap.core.datamodel.Band;
import org.esa.snap.core.datamodel.RasterDataNode;
import org.esa.snap.rcp.SnapApp;
import org.esa.snap.util.io.SnapFileFilter;
import org.w3c.dom.Node;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.metadata.IIOInvalidTreeException;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataNode;
import javax.imageio.stream.ImageOutputStream;
import java.awt.Component;
import java.awt.image.RenderedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import static org.esa.snap.timeseries.export.util.TimeSeriesExportHelper.*;
public class AnimatedGifExport extends ProgressMonitorSwingWorker<Void, Void> {
private final File outputFile;
private static final String EXPORT_DIR_PREFERENCES_KEY = "user.export.dir";
private RenderedImage[] frames;
private int level;
public AnimatedGifExport(Component parentComponent, String title) {
super(parentComponent, title);
FileWithLevel fileWithLevel = fetchOutputFile();
this.outputFile = fileWithLevel.file;
this.level = fileWithLevel.level;
}
@Override
protected Void doInBackground(ProgressMonitor pm) throws Exception {
exportAnimation("50", outputFile, pm);
return null;
}
public void createFrames(List<Band> bandsForVariable) {
List<RenderedImage> images = new ArrayList<>();
for (Band band : bandsForVariable) {
images.add(band.getGeophysicalImage().getImage(level));
}
frames = images.toArray(new RenderedImage[images.size()]);
}
private void exportAnimation(String delayTime, File file, ProgressMonitor pm) {
ImageWriter imageWriter = ImageIO.getImageWritersByFormatName("gif").next();
try {
ImageOutputStream outputStream = ImageIO.createImageOutputStream(file);
imageWriter.setOutput(outputStream);
imageWriter.prepareWriteSequence(null);
pm.beginTask("Exporting time series as animated gif", frames.length);
for (int i = 0; i < frames.length; i++) {
RenderedImage currentImage = frames[i];
ImageWriteParam writeParameters = imageWriter.getDefaultWriteParam();
IIOMetadata metadata = imageWriter.getDefaultImageMetadata(new ImageTypeSpecifier(currentImage),
writeParameters);
configure(metadata, delayTime, i);
IIOImage image = new IIOImage(currentImage, null, metadata);
imageWriter.writeToSequence(image, null);
pm.worked(1);
}
imageWriter.endWriteSequence();
outputStream.close();
pm.done();
} catch (IOException e) {
SnapApp.getDefault().handleError("Unable to create animated gif", e);
}
}
private static void configure(IIOMetadata meta, String delayTime, int imageIndex) {
String metaFormat = meta.getNativeMetadataFormatName();
if (!"javax_imageio_gif_image_1.0".equals(metaFormat)) {
throw new IllegalArgumentException(
"Unfamiliar gif metadata format: " + metaFormat);
}
Node root = meta.getAsTree(metaFormat);
//find the GraphicControlExtension node
Node child = root.getFirstChild();
while (child != null) {
if ("GraphicControlExtension".equals(child.getNodeName())) {
IIOMetadataNode gce = (IIOMetadataNode) child;
gce.setAttribute("userInputFlag", "FALSE");
gce.setAttribute("delayTime", delayTime);
break;
}
child = child.getNextSibling();
}
//only the first node needs the ApplicationExtensions node
if (imageIndex == 0) {
IIOMetadataNode parentNode = new IIOMetadataNode("ApplicationExtensions");
IIOMetadataNode childNode = new IIOMetadataNode("ApplicationExtension");
childNode.setAttribute("applicationID", "NETSCAPE");
childNode.setAttribute("authenticationCode", "2.0");
byte[] userObject = new byte[]{
//last two bytes is an unsigned short (little endian) that
//indicates the number of times to loop.
//0 means loop forever.
0x1, 0x0, 0x0
};
childNode.setUserObject(userObject);
parentNode.appendChild(childNode);
root.appendChild(parentNode);
}
try {
meta.setFromTree(metaFormat, root);
} catch (IIOInvalidTreeException e) {
SnapApp.getDefault().handleError(e.getMessage(), e);
}
}
private FileWithLevel fetchOutputFile() {
final RasterDataNode currentRaster = SnapApp.getDefault().getSelectedProductSceneView().getRaster();
SnapFileFilter gifFilter = new SnapFileFilter("gif", "gif", "Animated GIF");
return getOutputFileWithLevelOption(currentRaster,
"Export time series as animated GIF",
"time_series_",
EXPORT_DIR_PREFERENCES_KEY,
gifFilter,
null);
}
}