From e1a93cfa4212746802fbe165b2a2a451fa0fc8ec Mon Sep 17 00:00:00 2001 From: hneemann Date: Sun, 21 May 2017 10:54:46 +0200 Subject: [PATCH] improved gif exporter --- .../neemann/digital/draw/gif/GifExporter.java | 110 ++++++++++++++---- .../java/de/neemann/digital/gui/Main.java | 54 +++------ src/main/resources/lang/lang_de.xml | 6 +- src/main/resources/lang/lang_en.xml | 6 +- 4 files changed, 116 insertions(+), 60 deletions(-) diff --git a/src/main/java/de/neemann/digital/draw/gif/GifExporter.java b/src/main/java/de/neemann/digital/draw/gif/GifExporter.java index 802220d55..b98284f3a 100644 --- a/src/main/java/de/neemann/digital/draw/gif/GifExporter.java +++ b/src/main/java/de/neemann/digital/draw/gif/GifExporter.java @@ -1,18 +1,29 @@ package de.neemann.digital.draw.gif; import de.neemann.digital.core.Model; +import de.neemann.digital.core.ModelEvent; +import de.neemann.digital.core.ModelStateObserver; import de.neemann.digital.core.NodeException; -import de.neemann.digital.core.ObservableValue; -import de.neemann.digital.core.wiring.Clock; import de.neemann.digital.draw.elements.Circuit; import de.neemann.digital.draw.graphics.GraphicMinMax; import de.neemann.digital.draw.graphics.GraphicsImage; import de.neemann.digital.draw.graphics.linemerger.GraphicLineCollector; import de.neemann.digital.draw.graphics.linemerger.GraphicSkipLines; +import de.neemann.digital.gui.ModelModifier; +import de.neemann.digital.lang.Lang; +import de.neemann.gui.ErrorMessage; +import de.neemann.gui.Screen; +import de.neemann.gui.ToolTipAction; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.imageio.stream.FileImageOutputStream; -import javax.imageio.stream.ImageOutputStream; +import javax.swing.*; +import javax.swing.border.EmptyBorder; import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; @@ -21,52 +32,109 @@ import java.io.IOException; * Exporter which creates an animated GIF file. * Created by hneemann on 17.05.17. */ -public class GifExporter { - private final Model model; +public class GifExporter extends JDialog implements ModelStateObserver, ModelModifier { + private static final Logger LOGGER = LoggerFactory.getLogger(GifExporter.class); private final Circuit circuit; - private final int frames; private final int delayMs; private final GraphicMinMax minMax; + private final JLabel frameLabel; + private int frames; + private FileImageOutputStream output; + private GifSequenceWriter writer; + private boolean closed = false; /** * Creates a new instance * - * @param model the mode to use + * @param parent the parent frame * @param circuit the circuit to export - * @param frames then number of frames to write to the file * @param delayMs the delay between frames im milliseconds */ - public GifExporter(Model model, Circuit circuit, int frames, int delayMs) { - this.model = model; + public GifExporter(JFrame parent, Circuit circuit, int delayMs) { + super(parent, "GIF-Export", false); + setDefaultCloseOperation(DISPOSE_ON_CLOSE); + frameLabel = new JLabel(Lang.get("msg_framesWritten_N", frames)); + frameLabel.setFont(Screen.getInstance().getFont(1.5f)); + frameLabel.setBorder(new EmptyBorder(5, 5, 5, 5)); + getContentPane().add(frameLabel); + + addWindowListener(new WindowAdapter() { + @Override + public void windowClosed(WindowEvent windowEvent) { + close(); + } + }); + + getContentPane().add(new ToolTipAction(Lang.get("btn_gifComplete")) { + @Override + public void actionPerformed(ActionEvent actionEvent) { + close(); + } + }.setToolTip(Lang.get("btn_gifComplete_tt")).createJButton(), BorderLayout.SOUTH); + this.circuit = circuit; - this.frames = frames; this.delayMs = delayMs; minMax = new GraphicMinMax(); circuit.drawTo(minMax); + + pack(); + setLocation(parent.getLocation()); } /** * Exports the file * * @param file the file to write + * @return this for chained calls * @throws IOException IOException * @throws NodeException NodeException */ - public void export(File file) throws IOException, NodeException { - try (ImageOutputStream output = new FileImageOutputStream(file)) { - try (GifSequenceWriter writer = new GifSequenceWriter(output, BufferedImage.TYPE_INT_ARGB, delayMs, true)) { - for (int i = 0; i < frames; i++) { - writer.writeToSequence(createBufferedImage()); + public GifExporter export(File file) throws IOException, NodeException { + output = new FileImageOutputStream(file); + writer = new GifSequenceWriter(output, BufferedImage.TYPE_INT_ARGB, delayMs, true); + LOGGER.debug("open GIF file"); + return this; + } - Clock clock = model.getClocks().get(0); - ObservableValue o = clock.getClockOutput(); - o.setBool(!o.getBool()); - model.doStep(); - } + private void close() { + if (!closed) { + try { + writer.close(); + output.close(); + LOGGER.debug("closed GIF file"); + closed = true; + } catch (IOException e) { + SwingUtilities.invokeLater(new ErrorMessage(Lang.get("msg_errorWritingGif")).addCause(e)); } } + dispose(); + } + + @Override + public void preInit(Model model) throws NodeException { + SwingUtilities.invokeLater(() -> setVisible(true)); + model.addObserver(this); + } + + @Override + public void handleEvent(ModelEvent event) { + if (event.equals(ModelEvent.STEP)) { + writeImage(); + } } + private void writeImage() { + if (!closed) { + try { + writer.writeToSequence(createBufferedImage()); + } catch (IOException e) { + SwingUtilities.invokeLater(new ErrorMessage(Lang.get("msg_errorWritingGif")).addCause(e)); + } + frames++; + frameLabel.setText(Lang.get("msg_framesWritten_N", frames)); + LOGGER.debug("frame written to GIF file"); + } + } private BufferedImage createBufferedImage() throws IOException { GraphicsImage gri = GraphicsImage.create(null, minMax.getMin(), minMax.getMax(), "gif", 1); diff --git a/src/main/java/de/neemann/digital/gui/Main.java b/src/main/java/de/neemann/digital/gui/Main.java index cfc53fbb3..724e03e20 100644 --- a/src/main/java/de/neemann/digital/gui/Main.java +++ b/src/main/java/de/neemann/digital/gui/Main.java @@ -1151,44 +1151,28 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS @Override public void actionPerformed(ActionEvent e) { + JFileChooser fc = new MyFileChooser(); + if (filename != null) + fc.setSelectedFile(SaveAsHelper.checkSuffix(filename, "gif")); - if (model == null) - new ErrorMessage(Lang.get("msg_modelNeedsToBeStarted")).show(Main.this); - else { + if (lastExportDirectory != null) + fc.setCurrentDirectory(lastExportDirectory); - String numStr = JOptionPane.showInputDialog(Lang.get("msg_numberOfFrames")); - if (numStr != null) { - - int f = 0; - try { - f = Integer.parseInt(numStr); - } catch (NumberFormatException e1) { - new ErrorMessage().addCause(e1).show(Main.this); + fc.addChoosableFileFilter(new FileNameExtensionFilter(name, "gif")); + new SaveAsHelper(Main.this, fc, "gif").checkOverwrite( + file -> { + lastExportDirectory = file.getParentFile(); + try { + GifExporter ge = new GifExporter(Main.this, circuitComponent.getCircuit(), 500).export(file); + setDebug(false); + windowPosManager.closeAll(); + runModelState.enter(false, ge); + circuitComponent.hasChanged(); + } catch (NodeException e1) { + new ErrorMessage().addCause(e1).show(Main.this); + } } - final int frames = f; - if (frames > 0) { - - JFileChooser fc = new MyFileChooser(); - if (filename != null) - fc.setSelectedFile(SaveAsHelper.checkSuffix(filename, "gif")); - - if (lastExportDirectory != null) - fc.setCurrentDirectory(lastExportDirectory); - - fc.addChoosableFileFilter(new FileNameExtensionFilter(name, "gif")); - new SaveAsHelper(Main.this, fc, "gif").checkOverwrite( - file -> { - lastExportDirectory = file.getParentFile(); - try { - new GifExporter(model, circuitComponent.getCircuit(), frames, 500).export(file); - } catch (NodeException e1) { - new ErrorMessage().addCause(e1).show(Main.this); - } - } - ); - } - } - } + ); } } diff --git a/src/main/resources/lang/lang_de.xml b/src/main/resources/lang/lang_de.xml index f773f3a39..13f1e7daf 100644 --- a/src/main/resources/lang/lang_de.xml +++ b/src/main/resources/lang/lang_de.xml @@ -858,8 +858,10 @@ Die Icons stammen aus dem Tango Desktop Project. Pin {0} Nummerierungshilfe Wählen Sie Pin {0}: - Die Schaltung muss gestartet sein. - Anzahl der Einzelbilder: + Geschriebene Bilder: {0} + Fehler beim Schreiben der GIF Datei! + Fertig + Die GIF-Datei wird abgeschlossen. Ok diff --git a/src/main/resources/lang/lang_en.xml b/src/main/resources/lang/lang_en.xml index 9c79bdc80..a74d6480c 100644 --- a/src/main/resources/lang/lang_en.xml +++ b/src/main/resources/lang/lang_en.xml @@ -847,8 +847,10 @@ The icons are taken from the Tango Desktop Project. Error during speed test! Pin {0} Numbering Wizard - The circuit needs to be started. - Number of frames: + Written frames: {0} + Error writing to GIF file! + Ready + The GIF file is finalized and closed. Ok