/* JWildfire - an image and animation processor written in Java Copyright (C) 1995-2017 Andreas Maschke This is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This software 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this software; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jwildfire.create.tina.swing; import java.awt.Color; import java.awt.Rectangle; import java.util.List; import javax.swing.JPanel; import javax.swing.JToggleButton; import org.jwildfire.base.Prefs; import org.jwildfire.base.Tools; import org.jwildfire.create.tina.base.Flame; import org.jwildfire.create.tina.base.Layer; import org.jwildfire.create.tina.base.XYZProjectedPoint; import org.jwildfire.create.tina.base.raster.AbstractRaster; import org.jwildfire.create.tina.render.AbstractRenderThread; import org.jwildfire.create.tina.render.DrawFocusPointFlameRenderer; import org.jwildfire.create.tina.render.FlameBGColorHandler; import org.jwildfire.create.tina.render.FlameRenderer; import org.jwildfire.create.tina.render.IterationObserver; import org.jwildfire.create.tina.render.ProgressUpdater; import org.jwildfire.create.tina.render.RenderInfo; import org.jwildfire.create.tina.render.RenderMode; import org.jwildfire.create.tina.render.RenderThreads; import org.jwildfire.create.tina.render.RenderedFlame; import org.jwildfire.create.tina.swing.flamepanel.FlamePanel; import org.jwildfire.create.tina.swing.flamepanel.FlamePanelConfig; import org.jwildfire.create.tina.variation.RessourceManager; import org.jwildfire.image.SimpleImage; import org.jwildfire.swing.ErrorHandler; import org.jwildfire.swing.ImagePanel; import org.jwildfire.transform.ComposeTransformer; import org.jwildfire.transform.RectangleTransformer; import org.jwildfire.transform.TextTransformer; import org.jwildfire.transform.TextTransformer.FontStyle; import org.jwildfire.transform.TextTransformer.HAlignment; import org.jwildfire.transform.TextTransformer.Mode; import org.jwildfire.transform.TextTransformer.VAlignment; public class FlamePreviewHelper implements IterationObserver { private final Prefs prefs; private final JPanel centerPanel; private final JToggleButton toggleTransparencyButton; private final JToggleButton layerAppendBtn; private final JToggleButton layerPreviewBtn; private final ProgressUpdater mainProgressUpdater; private final FlameHolder flameHolder; private final LayerHolder layerHolder; private final ErrorHandler errorHandler; private final DetachedPreviewProvider detachedPreviewProvider; private final FlamePanelProvider flamePanelProvider; private final FlameMessageHelper messageHelper; private final RandomBatchHolder randomBatchHolder; private InteractiveRendererDisplayUpdater displayUpdater = new EmptyInteractiveRendererDisplayUpdater(); private FlameRenderer renderer; public FlamePreviewHelper(ErrorHandler pErrorHandler, JPanel pCenterPanel, JToggleButton pToggleTransparencyButton, JToggleButton pLayerAppendBtn, JToggleButton pLayerPreviewBtn, ProgressUpdater pMainProgressUpdater, FlameHolder pFlameHolder, LayerHolder pLayerHolder, DetachedPreviewProvider pDetachedPreviewProvider, FlamePanelProvider pFlamePanelProvider, FlameMessageHelper pMessageHelper, RandomBatchHolder pRandomBatchHolder) { prefs = Prefs.getPrefs(); errorHandler = pErrorHandler; centerPanel = pCenterPanel; toggleTransparencyButton = pToggleTransparencyButton; layerAppendBtn = pLayerAppendBtn; layerPreviewBtn = pLayerPreviewBtn; mainProgressUpdater = pMainProgressUpdater; flameHolder = pFlameHolder; layerHolder = pLayerHolder; detachedPreviewProvider = pDetachedPreviewProvider; flamePanelProvider = pFlamePanelProvider; messageHelper = pMessageHelper; randomBatchHolder = pRandomBatchHolder; } public void fastRefreshFlameImage(boolean pQuickRender, boolean pMouseDown, int pDownScale) { FlamePanel imgPanel = flamePanelProvider.getFlamePanel(); SimpleImage img = fastRenderFlameImage(pQuickRender, pMouseDown, pDownScale); if (img != null) { imgPanel.setImage(img); } imgPanel.repaint(); } private boolean isProgressivePreviewEnabled(FlamePanelConfig cfg) { return !isTransparencyEnabled() && !isDrawFocusPointEnabled(cfg) && cfg.isProgressivePreview(); } public void refreshFlameImage(boolean pQuickRender, boolean pMouseDown, int pDownScale, boolean pReRender, boolean pAllowUseCache) { cancelBackgroundRender(); if (pQuickRender && detachedPreviewProvider != null && detachedPreviewProvider.getDetachedPreviewController() != null && pDownScale == 1) { detachedPreviewProvider.getDetachedPreviewController().setFlame(flameHolder.getFlame()); } FlamePanel imgPanel = flamePanelProvider.getFlamePanel(); FlamePanelConfig cfg = flamePanelProvider.getFlamePanelConfig(); if (pReRender) { if (!pQuickRender || !isProgressivePreviewEnabled(cfg)) { SimpleImage img = renderFlameImage(pQuickRender, pMouseDown, pDownScale, pAllowUseCache); if (img != null) { imgPanel.setImage(img); } } } imgPanel.setBounds(imgPanel.getParentImageBounds()); if (!cfg.isNoControls()) { centerPanel.getParent().validate(); centerPanel.repaint(); } else { imgPanel.repaint(); } if (pReRender && isProgressivePreviewEnabled(cfg) && pQuickRender) { if (pAllowUseCache) { SimpleImage img = renderFlameImage(pQuickRender, pMouseDown, pDownScale, pAllowUseCache); if (img != null) { imgPanel.setImage(img); } } else { startBackgroundRender(imgPanel); } } if (pQuickRender && !cfg.isNoControls() && randomBatchHolder != null) { refreshThumbnail(); } } private void refreshThumbnail() { Flame flame = flameHolder.getFlame(); List<FlameThumbnail> randomBatch = randomBatchHolder.getRandomBatch(); for (int i = 0; i < randomBatch.size(); i++) { Flame bFlame = randomBatch.get(i).getFlame(); if (bFlame == flame) { randomBatch.get(i).setPreview(null); ImagePanel pnl = randomBatch.get(i).getImgPanel(); if (pnl != null) { pnl.replaceImage(randomBatch.get(i).getPreview(prefs.getTinaRenderPreviewQuality() / 2)); pnl.repaint(); } break; } } } private AbstractRaster prevRaster; public SimpleImage renderFlameImage(boolean pQuickRender, boolean pMouseDown, int pDownScale, boolean pAllowUseCache) { if (!pAllowUseCache) { prevRaster = null; } FlamePanel imgPanel = flamePanelProvider.getFlamePanel(); FlamePanelConfig cfg = flamePanelProvider.getFlamePanelConfig(); Rectangle panelBounds = imgPanel.getParentImageBounds(); Rectangle bounds; if (pDownScale != 1) { bounds = new Rectangle(panelBounds.width / pDownScale, panelBounds.height / pDownScale); } else { bounds = panelBounds; } int renderScale = pQuickRender && pMouseDown ? 2 : 1; int width = bounds.width / renderScale; int height = bounds.height / renderScale; if (width >= 16 && height >= 16) { RenderInfo info = new RenderInfo(width, height, RenderMode.PREVIEW); Flame flame = flameHolder.getFlame(); if (flame != null) { double oldSpatialFilterRadius = flame.getSpatialFilterRadius(); double oldSampleDensity = flame.getSampleDensity(); int oldSpatialOversampling = flame.getSpatialOversampling(); boolean oldPostNoiseFilter = flame.isPostNoiseFilter(); try { double wScl = (double) info.getImageWidth() / (double) flame.getWidth(); double hScl = (double) info.getImageHeight() / (double) flame.getHeight(); flame.setPixelsPerUnit((wScl + hScl) * 0.5 * flame.getPixelsPerUnit()); flame.setWidth(info.getImageWidth()); flame.setHeight(info.getImageHeight()); try { FlameRenderer renderer; if (pQuickRender) { flame.setSampleDensity(prefs.getTinaRenderRealtimeQuality()); flame.applyFastOversamplingSettings(); flame.setSpatialOversampling(oldSpatialOversampling); } else { flame.setSampleDensity(prefs.getTinaRenderPreviewQuality()); } if (isDrawFocusPointEnabled(cfg)) { renderer = new DrawFocusPointFlameRenderer(flame, prefs, isTransparencyEnabled()); } else { renderer = new FlameRenderer(flame, prefs, isTransparencyEnabled(), false); } if (pQuickRender) { renderer.setProgressUpdater(null); } else { renderer.setProgressUpdater(mainProgressUpdater); } long t0 = System.currentTimeMillis(); renderer.setRenderScale(renderScale); RenderedFlame res; if (prevRaster != null && pAllowUseCache) { info.setStoreRaster(false); info.setRestoredRaster(prevRaster); flame.setSampleDensity(prevRaster.getSampleDensity()); res = renderer.renderFlame(info); } else { info.setStoreRaster(true); res = renderer.renderFlame(info); prevRaster = res.getRaster(); } SimpleImage img = res.getImage(); long t1 = System.currentTimeMillis(); img.getBufferedImg().setAccelerationPriority(1.0f); if (layerAppendBtn != null && layerAppendBtn.isSelected() && !pMouseDown) { showLayerAppendModeIndicator(img); } if (layerPreviewBtn != null && layerPreviewBtn.isSelected() && layerHolder != null) { SimpleImage layerImg = createLayerPreview(img, renderScale, width, height); showLayerPreview(img, layerImg, renderScale, width, height); } if (pDownScale != 1) { SimpleImage background = new SimpleImage(panelBounds.width, panelBounds.height); ComposeTransformer cT = new ComposeTransformer(); cT.setHAlign(ComposeTransformer.HAlignment.CENTRE); cT.setVAlign(ComposeTransformer.VAlignment.CENTRE); cT.setForegroundImage(img); cT.transformImage(background); img = background; } if (!cfg.isNoControls() && messageHelper != null) { messageHelper.showStatusMessage(flame, "render time: " + Tools.doubleToString((t1 - t0) * 0.001) + "s"); } return img; } catch (Throwable ex) { errorHandler.handleError(ex); } } finally { flame.setSpatialFilterRadius(oldSpatialFilterRadius); flame.setSampleDensity(oldSampleDensity); flame.setSpatialOversampling(oldSpatialOversampling); flame.setPostNoiseFilter(oldPostNoiseFilter); } } } return null; } private SimpleImage createLayerPreview(SimpleImage img, int renderScale, int width, int height) { FlamePanelConfig cfg = flamePanelProvider.getFlamePanelConfig(); Flame flame = flameHolder.getFlame(); Layer onlyVisibleLayer = null; for (Layer layer : flameHolder.getFlame().getLayers()) { if (layer.isVisible()) { if (onlyVisibleLayer == null) { onlyVisibleLayer = layer; } else { onlyVisibleLayer = null; break; } } } boolean drawSubLayer = flame.getLayers().size() > 1 && layerHolder.getLayer() != null && layerHolder.getLayer() != onlyVisibleLayer && !cfg.isNoControls(); if (drawSubLayer) { Flame singleLayerFlame = new Flame(); singleLayerFlame.assign(flame); singleLayerFlame.getLayers().clear(); singleLayerFlame.getLayers().add(layerHolder.getLayer().makeCopy()); singleLayerFlame.getFirstLayer().setVisible(true); singleLayerFlame.getFirstLayer().setWeight(1.0); RenderInfo lInfo = new RenderInfo(width / 4 * renderScale, height / 4 * renderScale, RenderMode.PREVIEW); double lWScl = (double) lInfo.getImageWidth() / (double) singleLayerFlame.getWidth(); double lHScl = (double) lInfo.getImageHeight() / (double) singleLayerFlame.getHeight(); singleLayerFlame.setPixelsPerUnit((lWScl + lHScl) * 0.5 * singleLayerFlame.getPixelsPerUnit() * 0.5); singleLayerFlame.setWidth(lInfo.getImageWidth()); singleLayerFlame.setHeight(lInfo.getImageHeight()); singleLayerFlame.setSampleDensity(prefs.getTinaRenderRealtimeQuality() * 2.0 / 3.0); FlameRenderer lRenderer = new FlameRenderer(singleLayerFlame, prefs, false, false); RenderedFlame lRes = lRenderer.renderFlame(lInfo); SimpleImage layerImg = lRes.getImage(); boolean drawLayerNumber = true; if (drawLayerNumber) { RectangleTransformer rT = new RectangleTransformer(); int textWidth = 28; int textHeight = 22; int margin = 2; rT.setColor(new java.awt.Color(0, 0, 0)); rT.setLeft(layerImg.getImageWidth() - textWidth - 2 * margin); rT.setTop(layerImg.getImageHeight() - textHeight - 2 * margin); rT.setThickness(textHeight / 2 + 1); rT.setWidth(textWidth); rT.setHeight(textHeight); rT.transformImage(layerImg); TextTransformer txt = new TextTransformer(); txt.setText1(" " + (flame.getLayers().indexOf(layerHolder.getLayer()) + 1) + " "); txt.setAntialiasing(true); txt.setColor(new java.awt.Color(200, 200, 200)); txt.setMode(Mode.NORMAL); txt.setFontStyle(FontStyle.BOLD); txt.setFontName("Arial"); txt.setFontSize(16); txt.setHAlign(HAlignment.NONE); txt.setPosX(layerImg.getImageWidth() - textWidth - margin); txt.setPosY(layerImg.getImageHeight() - textHeight - margin); txt.setVAlign(VAlignment.NONE); txt.transformImage(layerImg); } RectangleTransformer rT = new RectangleTransformer(); rT.setColor(new java.awt.Color(200, 200, 200)); rT.setLeft(0); rT.setTop(0); rT.setThickness(3); rT.setWidth(lInfo.getImageWidth()); rT.setHeight(lInfo.getImageHeight()); rT.transformImage(layerImg); return layerImg; } return null; } private void showLayerPreview(SimpleImage img, SimpleImage layerImg, int renderScale, int width, int height) { if (layerImg != null) { ComposeTransformer cT = new ComposeTransformer(); cT.setHAlign(ComposeTransformer.HAlignment.LEFT); cT.setVAlign(ComposeTransformer.VAlignment.TOP); cT.setTop(10); cT.setLeft(10); cT.setForegroundImage(layerImg); cT.transformImage(img); } } private void showLayerAppendModeIndicator(SimpleImage img) { TextTransformer txt = new TextTransformer(); txt.setText1("layer-append-mode active"); txt.setAntialiasing(true); txt.setColor(Color.RED); txt.setMode(Mode.NORMAL); txt.setFontStyle(FontStyle.BOLD); txt.setFontName("Arial"); txt.setFontSize(16); txt.setHAlign(HAlignment.RIGHT); txt.setVAlign(VAlignment.BOTTOM); txt.transformImage(img); } private boolean isDrawFocusPointEnabled(FlamePanelConfig cfg) { return !cfg.isNoControls() && cfg.getMouseDragOperation() == MouseDragOperation.FOCUS; } private boolean isTransparencyEnabled() { return toggleTransparencyButton != null && toggleTransparencyButton.isSelected(); } public SimpleImage fastRenderFlameImage(boolean pQuickRender, boolean pMouseDown, int pDownScale) { FlamePanel imgPanel = flamePanelProvider.getFlamePanel(); FlamePanelConfig cfg = flamePanelProvider.getFlamePanelConfig(); Rectangle panelBounds = imgPanel.getParentImageBounds(); Rectangle bounds; if (pDownScale != 1) { bounds = new Rectangle(panelBounds.width / pDownScale, panelBounds.height / pDownScale); } else { bounds = panelBounds; } int renderScale = pQuickRender && pMouseDown ? 2 : 1; int width = bounds.width / renderScale; int height = bounds.height / renderScale; if (width >= 16 && height >= 16) { RenderInfo info = new RenderInfo(width, height, RenderMode.PREVIEW); Flame flame = flameHolder.getFlame(); if (flame != null) { double oldSpatialFilterRadius = flame.getSpatialFilterRadius(); double oldSampleDensity = flame.getSampleDensity(); int oldSpatialOversampling = flame.getSpatialOversampling(); boolean oldPostNoiseFilter = flame.isPostNoiseFilter(); try { double wScl = (double) info.getImageWidth() / (double) flame.getWidth(); double hScl = (double) info.getImageHeight() / (double) flame.getHeight(); flame.setPixelsPerUnit((wScl + hScl) * 0.5 * flame.getPixelsPerUnit()); flame.setWidth(info.getImageWidth()); flame.setHeight(info.getImageHeight()); try { FlameRenderer renderer; if (isDrawFocusPointEnabled(cfg)) { renderer = new DrawFocusPointFlameRenderer(flame, prefs, isTransparencyEnabled()); } else { renderer = new FlameRenderer(flame, prefs, isTransparencyEnabled(), false); } renderer.setProgressUpdater(null); flame.setSampleDensity(prefs.getTinaRenderRealtimeQuality()); flame.applyFastOversamplingSettings(); renderer.setRenderScale(renderScale); RenderedFlame res = renderer.renderFlame(info); SimpleImage img = res.getImage(); img.getBufferedImg().setAccelerationPriority(1.0f); return img; } catch (Throwable ex) { errorHandler.handleError(ex); } } finally { flame.setSampleDensity(oldSampleDensity); flame.setSpatialFilterRadius(oldSpatialFilterRadius); flame.setSpatialOversampling(oldSpatialOversampling); flame.setPostNoiseFilter(oldPostNoiseFilter); } } } return null; } public void setImage(SimpleImage pImage) { FlamePanel imgPanel = flamePanelProvider.getFlamePanel(); imgPanel.setImage(pImage); } public void forceRepaint() { try { flamePanelProvider.getFlamePanel().paint(flamePanelProvider.getFlamePanel().getGraphics()); } catch (Exception ex) { } } public Rectangle getPanelBounds() { return flamePanelProvider.getFlamePanel().getBounds(); } private UpdateDisplayThread updateDisplayThread; private Thread updateDisplayExecuteThread; private RenderThreads threads; public void cancelBackgroundRender() { if (threads == null) return; if (updateDisplayThread != null) { updateDisplayThread.cancel(); } try { for (Thread thread : threads.getExecutingThreads()) { try { thread.setPriority(Thread.NORM_PRIORITY); } catch (Exception ex) { ex.printStackTrace(); } } if (updateDisplayExecuteThread != null) { updateDisplayExecuteThread.setPriority(Thread.MAX_PRIORITY); } } catch (Exception ex) { ex.printStackTrace(); } while (true) { boolean done = true; for (AbstractRenderThread thread : threads.getRenderThreads()) { if (!thread.isFinished()) { done = false; thread.cancel(); try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } } if (done) { break; } } threads = null; if (updateDisplayThread != null) { updateDisplayThread.cancel(); while (!updateDisplayThread.isFinished()) { try { updateDisplayThread.cancel(); Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } updateDisplayThread = null; } } private void startBackgroundRender(FlamePanel pImgPanel) { prevRaster = null; if (flameHolder == null) { return; } Flame flame = flameHolder.getFlame().makeCopy(); flame.applyFastOversamplingSettings(); Rectangle panelBounds = pImgPanel.getParentImageBounds(); RenderInfo info = new RenderInfo(panelBounds.width, panelBounds.height, RenderMode.PREVIEW); double wScl = (double) info.getImageWidth() / (double) flame.getWidth(); double hScl = (double) info.getImageHeight() / (double) flame.getHeight(); flame.setPixelsPerUnit((wScl + hScl) * 0.5 * flame.getPixelsPerUnit()); flame.setWidth(info.getImageWidth()); flame.setHeight(info.getImageHeight()); flame.setSampleDensity(10.0); info.setRenderHDR(false); info.setRenderZBuffer(false); renderer = new FlameRenderer(flame, prefs, flame.isBGTransparency(), false); renderer.registerIterationObserver(this); SimpleImage image = new SimpleImage(pImgPanel.getImage().getImageWidth(), pImgPanel.getImage().getImageHeight()); initImage(image, flame); displayUpdater = new BufferedInteractiveRendererDisplayUpdater(pImgPanel, image, true); displayUpdater.initRender(prefs.getTinaRenderThreads()); threads = renderer.startRenderFlame(info); for (Thread thread : threads.getExecutingThreads()) { thread.setPriority(Thread.MIN_PRIORITY); } updateDisplayThread = new UpdateDisplayThread(flame, image); updateDisplayExecuteThread = new Thread(updateDisplayThread); updateDisplayExecuteThread.setPriority(Thread.MIN_PRIORITY); updateDisplayExecuteThread.start(); } private void initImage(SimpleImage pImage, Flame flame) { if (flame.getBGImageFilename() != null && !flame.getBGImageFilename().isEmpty()) { try { SimpleImage bgImg = (SimpleImage) RessourceManager.getImage(flame.getBGImageFilename()); pImage.fillBackground(bgImg); } catch (Exception ex) { ex.printStackTrace(); } } else { new FlameBGColorHandler(flame).fillBackground(pImage); } } private final static int INITIAL_IMAGE_UPDATE_INTERVAL = 5; private final static int IMAGE_UPDATE_INC_INTERVAL = 10; private final static int MAX_UPDATE_INC_INTERVAL = 100; private class UpdateDisplayThread implements Runnable, InteractiveRendererImagePostProcessor { private int nextImageUpdate; private int lastImageUpdateInterval; private boolean cancelSignalled; private boolean replaceImageFlag; private boolean finished; private SimpleImage image; private int maxPreviewTimeInMilliseconds; private double maxPreviewQuality; private SimpleImage layerImg; public UpdateDisplayThread(Flame flame, SimpleImage pImage) { Prefs prefs = Prefs.getPrefs(); maxPreviewTimeInMilliseconds = Tools.FTOI(prefs.getTinaEditorProgressivePreviewMaxRenderTime() * 1000.0); double renderQualityScale = flame.getLayers().size() * (flame.getSolidRenderSettings().isSolidRenderingEnabled() ? 1.5 : 1.0); maxPreviewQuality = Tools.FTOI(prefs.getTinaEditorProgressivePreviewMaxRenderQuality() * renderQualityScale); nextImageUpdate = INITIAL_IMAGE_UPDATE_INTERVAL; lastImageUpdateInterval = INITIAL_IMAGE_UPDATE_INTERVAL; image = pImage; } private double getCurrQuality() { try { if (threads != null) { List<AbstractRenderThread> renderThreads = threads.getRenderThreads(); if (renderThreads != null && renderThreads.size() > 0) { return renderThreads.get(0).getTonemapper().calcDensity(displayUpdater.getSampleCount()); } } } catch (Exception ex) { ex.printStackTrace(); } return 0.0; } @Override public void run() { long t0 = System.currentTimeMillis(); finished = cancelSignalled = false; try { replaceImageFlag = false; while (!cancelSignalled) { double currQuality = getCurrQuality(); if (currQuality > maxPreviewQuality || System.currentTimeMillis() - t0 > maxPreviewTimeInMilliseconds) { cancelSignalled = true; for (AbstractRenderThread thread : threads.getRenderThreads()) { if (!thread.isFinished()) { thread.cancel(); } } break; } try { if (--nextImageUpdate <= 0) { lastImageUpdateInterval += IMAGE_UPDATE_INC_INTERVAL; if (lastImageUpdateInterval > MAX_UPDATE_INC_INTERVAL) { lastImageUpdateInterval = MAX_UPDATE_INC_INTERVAL; } if (!replaceImageFlag) { FlamePanel imgPanel = flamePanelProvider.getFlamePanel(); imgPanel.replaceImage(image); replaceImageFlag = true; } currQuality = threads.getRenderThreads().get(0).getTonemapper().calcDensity(displayUpdater.getSampleCount()); if (currQuality > 0.25) { if (currQuality < 0.5) currQuality *= 10; else if (currQuality < 1.0) currQuality *= 5; for (AbstractRenderThread thread : threads.getRenderThreads()) { thread.getTonemapper().setDensity(currQuality); } displayUpdater.updateImage(this); } nextImageUpdate = lastImageUpdateInterval; } else { Thread.sleep(1); } } catch (Throwable e) { e.printStackTrace(); } } } finally { finished = true; } } public void cancel() { cancelSignalled = true; } public boolean isFinished() { return finished; } @Override public void postProcessImage(SimpleImage pImage) { if (layerAppendBtn != null && layerAppendBtn.isSelected()) { showLayerAppendModeIndicator(image); } if (layerPreviewBtn != null && layerPreviewBtn.isSelected() && layerHolder != null) { Rectangle panelBounds = flamePanelProvider.getFlamePanel().getParentImageBounds(); if (layerImg == null) { layerImg = createLayerPreview(image, 1, panelBounds.width, panelBounds.height); } showLayerPreview(image, layerImg, 1, panelBounds.width, panelBounds.height); } } } @Override public void notifyIterationFinished(AbstractRenderThread pEventSource, int pPlotX, int pPlotY, XYZProjectedPoint pProjectedPoint, double pX, double pY, double pZ, double pColorRed, double pColorGreen, double pColorBlue) { displayUpdater.iterationFinished(pEventSource, pPlotX, pPlotY); } public void stopPreviewRendering() { cancelBackgroundRender(); } }