/* * JAME 6.2.1 * http://jame.sourceforge.net * * Copyright 2001, 2016 Andrea Medeghini * * This file is part of JAME. * * JAME is an application for creating fractals and other graphics artifacts. * * JAME 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. * * JAME 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 JAME. If not, see <http://www.gnu.org/licenses/>. * */ package net.sf.jame.contextfree.renderer; import java.awt.Graphics2D; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.util.Map; import java.util.concurrent.ThreadFactory; import net.sf.jame.contextfree.ContextFreeRuntime; import net.sf.jame.contextfree.cfdg.CFDGRuntimeElement; import net.sf.jame.core.util.DefaultThreadFactory; import net.sf.jame.core.util.DoubleVector4D; import net.sf.jame.core.util.IntegerVector2D; import net.sf.jame.core.util.IntegerVector4D; import net.sf.jame.core.util.RenderWorker; import net.sf.jame.core.util.Surface; import net.sf.jame.core.util.Tile; import net.sf.jame.twister.renderer.TwisterRenderer; import net.sf.jame.twister.util.View; /** * @author Andrea Medeghini */ public abstract class AbstractContextFreeRenderer implements ContextFreeRenderer { private Graphics2D newBuffer; private Graphics2D oldBuffer; private BufferedImage newImage; private BufferedImage oldImage; private IntegerVector2D bufferSize; private Tile newTile; private Tile oldTile; protected int imageDim; private int tileDim; protected double rotationValue; protected int renderMode = ContextFreeRenderer.MODE_CALCULATE; protected int newImageMode = 0; protected int oldImageMode = 0; private boolean dynamic = false; private boolean dynamicZoom = false; private final AffineTransform transform = new AffineTransform(); protected ContextFreeRuntime runtime; protected View newView = new View(new IntegerVector4D(0, 0, 0, 0), new DoubleVector4D(0, 0, 1, 0), new DoubleVector4D(0, 0, 0, 0)); protected View oldView = new View(new IntegerVector4D(0, 0, 0, 0), new DoubleVector4D(0, 0, 1, 0), new DoubleVector4D(0, 0, 0, 0)); protected int percent = 100; protected CFDGRuntimeElement cfdgRuntime; protected int status = TwisterRenderer.STATUS_TERMINATED; private final ContextFreeWorker renderWorker; protected final ThreadFactory factory; private boolean viewChanged; private boolean invalidated; private final Object lock = new Object(); /** * */ public AbstractContextFreeRenderer(final int threadPriority) { factory = new DefaultThreadFactory("ContextFreeRendererWorker", true, threadPriority); renderWorker = new ContextFreeWorker(factory); } /** * @see java.lang.Object#finalize() */ @Override public void finalize() throws Throwable { dispose(); super.finalize(); } public void dispose() { stop(); free(); } /** * */ protected void free() { if (newBuffer != null) { newBuffer.dispose(); } if (newImage != null) { newImage.flush(); } newImage = null; newBuffer = null; if (oldBuffer != null) { oldBuffer.dispose(); } if (oldImage != null) { oldImage.flush(); } oldImage = null; oldBuffer = null; } /** * */ protected void init() { imageDim = (int) Math.sqrt(((oldTile.getImageSize().getX() + oldTile.getTileBorder().getX() * 2) * (oldTile.getImageSize().getX() + oldTile.getTileBorder().getX() * 2)) + ((oldTile.getImageSize().getY() + oldTile.getTileBorder().getY() * 2) * (oldTile.getImageSize().getY() + oldTile.getTileBorder().getY() * 2))); tileDim = (int) Math.sqrt(((oldTile.getTileSize().getX() + oldTile.getTileBorder().getX() * 2) * (oldTile.getTileSize().getX() + oldTile.getTileBorder().getX() * 2)) + ((oldTile.getTileSize().getY() + oldTile.getTileBorder().getY() * 2) * (oldTile.getTileSize().getY() + oldTile.getTileBorder().getY() * 2))); bufferSize = new IntegerVector2D(tileDim, tileDim); newImage = new BufferedImage(bufferSize.getX(), bufferSize.getY(), Surface.DEFAULT_TYPE); oldImage = newImage;//new BufferedImage(bufferSize.getX(), bufferSize.getY(), Surface.DEFAULT_TYPE); newBuffer = newImage.createGraphics(); oldBuffer = newBuffer;//oldImage.createGraphics(); } /** * */ protected final void swapImages() { synchronized (lock) { final BufferedImage tmpImage = oldImage; oldImage = newImage; newImage = tmpImage; final Graphics2D tmpBuffer = oldBuffer; oldBuffer = newBuffer; newBuffer = tmpBuffer; } } /** * @see net.sf.jame.mandelbrot.renderer.MandelbrotFractalRenderer#setRenderingHints(java.util.Map) */ public void setRenderingHints(final Map<Object, Object> hints) { } /** * @see net.sf.jame.mandelbrot.renderer.MandelbrotFractalRenderer#getRenderingStatus() */ public int getRenderingStatus() { return status; } /** * @see net.sf.jame.mandelbrot.renderer.MandelbrotFractalRenderer#setMode(int) */ public void setMode(final int renderMode) { this.renderMode |= renderMode; } /** * @see net.sf.jame.mandelbrot.renderer.MandelbrotFractalRenderer#getMode() */ public int getMode() { return renderMode; } /** * @see net.sf.jame.mandelbrot.renderer.MandelbrotFractalRenderer#setTile(net.sf.jame.core.util.Tile) */ public void setTile(final Tile tile) { synchronized (this) { invalidated = false; newTile = tile; } } /** * @see net.sf.jame.mandelbrot.renderer.MandelbrotFractalRenderer#getTile() */ public Tile getTile() { return oldTile; } /** * @return */ public int getBufferWidth() { return bufferSize.getX(); } /** * @return */ public int getBufferHeight() { return bufferSize.getY(); } /** * @return */ protected boolean isTileSupported() { return true; } public boolean isDynamic() { final boolean value = dynamic; dynamic = false; return value; } public boolean isViewChanged() { final boolean value = viewChanged; viewChanged = false; return value; } /** * @see net.sf.jame.mandelbrot.renderer.MandelbrotFractalRenderer#setView(net.sf.jame.twister.util.View) */ public void setView(final View view) { synchronized (this) { newView = view; } } private void updateView(final View view) { rotationValue = view.getRotation().getZ(); dynamic = view.getStatus().getZ() == 1; dynamicZoom = dynamic; if (view.getStatus().getZ() == 2) { setMode(ContextFreeRenderer.MODE_CALCULATE); } } /** * */ protected void updateTransform() { final int offsetX = (getBufferWidth() - oldTile.getTileSize().getX() - oldTile.getTileBorder().getX() * 2) / 2; final int offsetY = (getBufferHeight() - oldTile.getTileSize().getY() - oldTile.getTileBorder().getY() * 2) / 2; transform.setToTranslation(-offsetX, -offsetY); final int centerX = getBufferWidth() / 2; final int centerY = getBufferHeight() / 2; transform.rotate(rotationValue, centerX, centerY); } /** * @see net.sf.jame.mandelbrot.renderer.MandelbrotFractalRenderer#startRenderer() */ public void startRenderer() { renderWorker.executeTask(); } /** * @see net.sf.jame.mandelbrot.renderer.MandelbrotFractalRenderer#abortRenderer() */ public void abortRenderer() { renderWorker.abortTasks(); } /** * @see net.sf.jame.mandelbrot.renderer.MandelbrotFractalRenderer#joinRenderer() */ public void joinRenderer() throws InterruptedException { renderWorker.waitTasks(); } /** * */ public void start() { renderWorker.start(); } /** * */ public void stop() { renderWorker.stop(); } public void startTasks() { renderWorker.executeTask(); } public void clearTasks() { renderWorker.clearTasks(); } public void abortTasks() { renderWorker.abortTasks(); } /** * @see net.sf.jame.mandelbrot.renderer.MandelbrotFractalRenderer#asyncStop() */ public final void asyncStop() { clearTasks(); abortTasks(); } /** * @see net.sf.jame.mandelbrot.renderer.MandelbrotFractalRenderer#asyncStart() */ public final void asyncStart() { startTasks(); } public ContextFreeRuntime getRuntime() { return runtime; } public void setRuntime(ContextFreeRuntime runtime) { this.runtime = runtime; } /** * @see net.sf.jame.mandelbrot.renderer.MandelbrotFractalRenderer#drawImage(java.awt.Graphics2D) */ public void drawImage(final Graphics2D g) { synchronized (lock) { final AffineTransform t = g.getTransform(); if (oldTile != null) { // g.setClip(oldTile.getTileBorder().getX(), oldTile.getTileBorder().getY(), oldTile.getTileSize().getX(), oldTile.getTileSize().getY()); // g.setClip(0, 0, oldTile.getTileSize().getX() + oldTile.getTileBorder().getX() + 2, oldTile.getTileSize().getY() + oldTile.getTileBorder().getY() + 2); g.setTransform(transform); g.drawImage(newImage, 0, 0, null); g.setTransform(t); g.setClip(null); // g.dispose(); } } } /** * @see net.sf.jame.mandelbrot.renderer.MandelbrotFractalRenderer#drawImage(java.awt.Graphics2D, int, int) */ public void drawImage(final Graphics2D g, final int x, final int y) { synchronized (lock) { final AffineTransform t = g.getTransform(); if (oldTile != null) { // g.setClip(oldTile.getTileBorder().getX(), oldTile.getTileBorder().getY(), oldTile.getTileSize().getX(), oldTile.getTileSize().getY()); // g.setClip(0, 0, oldTile.getTileSize().getX() + oldTile.getTileBorder().getX() + 2, oldTile.getTileSize().getY() + oldTile.getTileBorder().getY() + 2); g.setTransform(transform); g.drawImage(newImage, x, y, null); g.setTransform(t); g.setClip(null); // g.dispose(); } } } /** * @see net.sf.jame.mandelbrot.renderer.MandelbrotFractalRenderer#drawImage(java.awt.Graphics2D, int, int, int, int) */ public void drawImage(final Graphics2D g, final int x, final int y, final int w, final int h) { synchronized (lock) { final AffineTransform t = g.getTransform(); if (oldTile != null) { g.setClip(x, y, w, h); g.setTransform(transform); final double sx = w / (double) getTile().getTileSize().getX(); final double sy = h / (double) getTile().getTileSize().getY(); final int dw = (int) Math.rint(bufferSize.getX() * sx); final int dh = (int) Math.rint(bufferSize.getY() * sy); g.drawImage(newImage, x, y, dw, dh, null); g.setTransform(t); g.setClip(null); // g.dispose(); } } } /** * @return */ protected Graphics2D getGraphics() { return oldBuffer; } /** * @param scale * @return */ protected void copyOldBuffer() { // oldBuffer.setComposite(AlphaComposite.Src); // oldBuffer.drawImage(newImage, 0, 0, null); } /** * @see net.sf.jame.mandelbrot.renderer.MandelbrotFractalRenderer#isInterrupted() */ public boolean isInterrupted() { return Thread.currentThread().isInterrupted(); } private void render() { synchronized (this) { cfdgRuntime = runtime.getCFDG(); if (oldView != newView) { viewChanged = true; updateView(newView); } oldView = newView; if (newTile != oldTile) { setMode(ContextFreeRenderer.MODE_CALCULATE); oldTile = newTile; free(); init(); } } percent = 0; status = TwisterRenderer.STATUS_RENDERING; doRender(dynamicZoom); if (percent == 100) { status = TwisterRenderer.STATUS_TERMINATED; } else { status = TwisterRenderer.STATUS_ABORTED; } dynamicZoom = false; } protected abstract void doRender(boolean dynamicZoom); private class ContextFreeWorker extends RenderWorker { /** * @param factory */ public ContextFreeWorker(ThreadFactory factory) { super(factory); } /** * @see net.sf.jame.core.util.RenderWorker#execute() */ @Override protected void execute() { try { if (!invalidated) { render(); } } catch (final Throwable e) { invalidated = true; e.printStackTrace(); } } } }