/* * Copyright 2017 Laszlo Balazs-Csiki * * This file is part of Pixelitor. Pixelitor is free software: you * can redistribute it and/or modify it under the terms of the GNU * General Public License, version 3 as published by the Free * Software Foundation. * * Pixelitor 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 Pixelitor. If not, see <http://www.gnu.org/licenses/>. */ package pixelitor.filters.animation; import pixelitor.ChangeReason; import pixelitor.Composition; import pixelitor.filters.Filter; import pixelitor.filters.FilterWithParametrizedGUI; import pixelitor.filters.gui.ParamSetState; import pixelitor.gui.PixelitorWindow; import pixelitor.layers.Drawable; import pixelitor.utils.Messages; import javax.swing.*; import java.awt.image.BufferedImage; import java.io.IOException; class RenderFramesTask extends SwingWorker<Void, Void> { private final TweenAnimation animation; private final Drawable dr; public RenderFramesTask(TweenAnimation tweenAnimation, Drawable dr) { this.animation = tweenAnimation; this.dr = dr; } @SuppressWarnings("ProhibitedExceptionDeclared") @Override protected Void doInBackground() throws Exception { try { renderFrames(); } catch (Exception e) { SwingUtilities.invokeLater(() -> Messages.showException(e)); } return null; } private void renderFrames() { int numFrames = animation.getNumFrames(); FilterWithParametrizedGUI filter = animation.getFilter(); AnimationWriter animationWriter = animation.createAnimationWriter(); boolean canceled = false; PixelitorWindow busyCursorParent = PixelitorWindow.getInstance(); dr.tweenCalculatingStarted(); int numTotalFrames = numFrames; boolean pingPong = animation.isPingPong() && numFrames > 2; if (pingPong) { numTotalFrames = 2 * numFrames - 2; } for (int frameNr = 0; frameNr < numTotalFrames; frameNr++) { if (isCancelled()) { canceled = true; break; } int percentProgress = (int) ((100.0 * frameNr) / numTotalFrames); setProgress(percentProgress); double time; if (frameNr < numFrames) { // ping: normal animation forwards time = ((double) frameNr) / numFrames; } else { // pong: animating backwards // TODO we are calculating the same frames again // they could be cached somewhere // perhaps in an array of soft references with the calculated frames // or on the disk int effectiveFrame = 2 * (numFrames - 1) - frameNr; time = ((double) effectiveFrame) / numFrames; } BufferedImage image = renderFrame(filter, time, busyCursorParent); try { animationWriter.addFrame(image); } catch (IOException e) { canceled = true; Messages.showException(e); break; } } setProgress(100); dr.tweenCalculatingEnded(); if (canceled) { animationWriter.cancel(); } else { animationWriter.finish(); } } private BufferedImage renderFrame(FilterWithParametrizedGUI filter, double time, PixelitorWindow busyCursorParent) { long runCountBefore = Filter.runCount; ParamSetState intermediateState = animation.tween(time); filter.getParamSet().setState(intermediateState); filter.executeWithBusyCursor(dr, ChangeReason.TWEEN_PREVIEW, busyCursorParent); long runCountAfter = Filter.runCount; assert runCountAfter == runCountBefore + 1; Composition comp = dr.getComp(); comp.repaint(); return comp.getCompositeImage(); } @Override protected void done() { } }