diff --git a/diylc/diylc-core/src/org/diylc/common/DrawOption.java b/diylc/diylc-core/src/org/diylc/common/DrawOption.java index 7d47e0a4..c41fff17 100644 --- a/diylc/diylc-core/src/org/diylc/common/DrawOption.java +++ b/diylc/diylc-core/src/org/diylc/common/DrawOption.java @@ -60,5 +60,9 @@ public enum DrawOption { /** * Include extra space around the layout. */ - EXTRA_SPACE; + EXTRA_SPACE, + /** + * Whether components should be cached or not. + */ + ENABLE_CACHING; } diff --git a/diylc/diylc-core/src/org/diylc/presenter/DrawingCache.java b/diylc/diylc-core/src/org/diylc/presenter/DrawingCache.java index eff871f6..f8b242d4 100644 --- a/diylc/diylc-core/src/org/diylc/presenter/DrawingCache.java +++ b/diylc/diylc-core/src/org/diylc/presenter/DrawingCache.java @@ -27,20 +27,21 @@ import org.apache.log4j.Logger; import org.diylc.common.ComponentType; import org.diylc.core.ComponentState; import org.diylc.core.IDIYComponent; -import org.diylc.core.IDrawingObserver; import org.diylc.core.Project; /** - * A layer that injects itself between the provided {@link G2DWrapper} instance and the underlying {@link Graphics2D} instance. - * For all components that are enabled for caching, it stores a {@link BufferedImage} representing the component. - * The state of the component is recorded and compared next time it needs to be rendered to make sure that the cache is up to date. + * A layer that injects itself between the provided {@link G2DWrapper} instance and the underlying + * {@link Graphics2D} instance. For all components that are enabled for caching, it stores a + * {@link BufferedImage} representing the component. The state of the component is recorded and + * compared next time it needs to be rendered to make sure that the cache is up to date. * * @author bancika */ public class DrawingCache { - private Map, CacheValue> imageCache = new HashMap, CacheValue>(); - + private Map, CacheValue> imageCache = + new HashMap, CacheValue>(); + private static final Logger LOG = Logger.getLogger(DrawingCache.class); public static DrawingCache Instance = new DrawingCache(); @@ -52,7 +53,7 @@ public class DrawingCache { .extractComponentTypeFrom((Class>) component.getClass()); if (type.getEnableCache()) { - // if we need to apply caching + // if we need to apply caching Point firstPoint = component.getControlPoint(0); Point lastPoint = component.getControlPoint(component.getControlPointCount() - 1); @@ -60,58 +61,91 @@ public class DrawingCache { CacheValue value = null; if (imageCache.containsKey(component)) value = imageCache.get(component); - + // only honor the cache if the component hasn't changed in the meantime - if (value == null || !value.getComponent().equalsTo(component) || value.getState() != componentState) { + if (value == null || !value.getComponent().equalsTo(component) + || value.getState() != componentState || value.getZoom() != zoom) { LOG.trace("Rendering " + component.getName() + " for cache."); int width = (int) (Math.abs(firstPoint.x - lastPoint.x) * zoom) + 2; int height = (int) (Math.abs(firstPoint.y - lastPoint.y) * zoom) + 2; - BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); Graphics2D cg2d = image.createGraphics(); - // copy over rendering settings + // copy over rendering settings cg2d.setRenderingHints(g2d.getRenderingHints()); - + cg2d.setClip(0, 0, width, height); + // apply zoom + if (Math.abs(1.0 - zoom) > 1e-4) { + cg2d.scale(zoom, zoom); + } // translate to make the top-left corner of the component match (0, 0) cg2d.translate(-firstPoint.x, -firstPoint.y); - G2DWrapper wrapper = new G2DWrapper(cg2d, zoom); + // wrap the graphics so we can track + G2DWrapper wrapper = new G2DWrapper(cg2d, zoom); + wrapper.startedDrawingComponent(); if (!trackArea) { wrapper.stopTracking(); } - + // now draw the component to the buffer image component.draw(wrapper, componentState, outlineMode, project, wrapper); - + wrapper.finishedDrawingComponent(); - - cg2d.dispose(); + + cg2d.dispose(); + +// File outputfile = new File("d:\\image_" + component.getName() + ".png"); +// try { +// ImageIO.write(image, "png", outputfile); +// } catch (IOException e) { +// // TODO Auto-generated catch block +// e.printStackTrace(); +// } + + value = new CacheValue(component, image, wrapper, componentState, zoom); - value = new CacheValue(component, image, wrapper, componentState); imageCache.put(component, value); } - - // draw cached image - g2d.getCanvasGraphics().drawImage(value.getImage(), firstPoint.x, firstPoint.y, null); - + + // temporarily scale the graphic to compensate for the zoom, since we are rendering an image with the zoom appled + if (Math.abs(1.0 - zoom) > 1e-4) { + g2d.getCanvasGraphics().scale(1 / zoom, 1 / zoom); + } + try { + // draw cached image + g2d.getCanvasGraphics().drawImage(value.getImage(), (int) (firstPoint.x * zoom), + (int) (firstPoint.y * zoom), null); + } finally { + if (Math.abs(1.0 - zoom) > 1e-4) { + g2d.getCanvasGraphics().scale(zoom, zoom); + } + } + // copy over tracking area from the cache g2d.merge(value.getWrapper()); } else { // no caching, just draw as usual component.draw(g2d, componentState, outlineMode, project, g2d); } - } - + } + + public void clear() { + imageCache.clear(); + } + private class CacheValue { IDIYComponent component; BufferedImage image; - G2DWrapper wrapper; + G2DWrapper wrapper; ComponentState state; - - public CacheValue(IDIYComponent component, BufferedImage image, G2DWrapper wrapper, ComponentState state) { + double zoom; + + public CacheValue(IDIYComponent component, BufferedImage image, G2DWrapper wrapper, + ComponentState state, double zoom) { super(); // take a copy of the component, so we can check if it changed in the meantime try { @@ -121,8 +155,9 @@ public class DrawingCache { this.image = image; this.wrapper = wrapper; this.state = state; + this.zoom = zoom; } - + public IDIYComponent getComponent() { return component; } @@ -130,18 +165,22 @@ public class DrawingCache { public BufferedImage getImage() { return image; } - + public G2DWrapper getWrapper() { return wrapper; } - + public ComponentState getState() { return state; } + public double getZoom() { + return zoom; + } + @Override - public String toString() { - return component.getName() + ":" + state; + public String toString() { + return component.getName() + ":" + state + ":" + zoom; } } } diff --git a/diylc/diylc-core/src/org/diylc/presenter/DrawingManager.java b/diylc/diylc-core/src/org/diylc/presenter/DrawingManager.java index 352cb719..636814f6 100644 --- a/diylc/diylc-core/src/org/diylc/presenter/DrawingManager.java +++ b/diylc/diylc-core/src/org/diylc/presenter/DrawingManager.java @@ -39,6 +39,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.TimeUnit; import org.apache.log4j.Logger; import org.diylc.appframework.miscutils.ConfigurationManager; @@ -138,6 +139,7 @@ public class DrawingManager { IComponentFiler filter, Rectangle selectionRect, Collection> selectedComponents, Set> lockedComponents, Set> groupedComponents, List controlPointSlot, List> componentSlot, boolean dragInProgress, Double externalZoom) { + long start = System.nanoTime(); failedComponents.clear(); if (project == null) { return failedComponents; @@ -276,8 +278,12 @@ public class DrawingManager { } // Draw the component through the g2dWrapper. try { -// component.draw(g2dWrapper, state, drawOptions.contains(DrawOption.OUTLINE_MODE), project, g2dWrapper); - DrawingCache.Instance.draw(component, g2dWrapper, state, drawOptions.contains(DrawOption.OUTLINE_MODE), project, zoom, trackArea); + + if (drawOptions.contains(DrawOption.ENABLE_CACHING)) // go through the DrawingCache + DrawingCache.Instance.draw(component, g2dWrapper, state, drawOptions.contains(DrawOption.OUTLINE_MODE), project, zoom, trackArea); + else // go stragiht to the wrapper + component.draw(g2dWrapper, state, drawOptions.contains(DrawOption.OUTLINE_MODE), project, g2dWrapper); + if (g2dWrapper.isTrackingContinuityArea()) { LOG.info("Component " + component.getName() + " of type " + component.getClass().getName() + " did not stop tracking continuity area."); } @@ -427,6 +433,11 @@ public class DrawingManager { g2d.setColor(theme.getOutlineColor()); g2d.fill(extraSpaceArea); } + + long end = System.nanoTime(); + + long ms = TimeUnit.MILLISECONDS.convert(end - start, TimeUnit.NANOSECONDS); + LOG.trace(String.format("Render time = %d ms", ms)); return failedComponents; } diff --git a/diylc/diylc-core/src/org/diylc/presenter/Presenter.java b/diylc/diylc-core/src/org/diylc/presenter/Presenter.java index 11af1888..b6bcac97 100644 --- a/diylc/diylc-core/src/org/diylc/presenter/Presenter.java +++ b/diylc/diylc-core/src/org/diylc/presenter/Presenter.java @@ -284,6 +284,7 @@ public class Presenter implements IPlugInPort { instantiationManager.fillWithDefaultProperties(project, null); loadProject(project, true, null); projectFileManager.startNewFile(); + DrawingCache.Instance.clear(); } catch (Exception e) { LOG.error("Could not create new file", e); view.showMessage("Could not create a new file. Check the log for details.", "Error", IView.ERROR_MESSAGE); @@ -309,6 +310,7 @@ public class Presenter implements IPlugInPort { view.showMessage(builder.toString(), "Warning", IView.WARNING_MESSAGE); } addToRecentFiles(fileName); + DrawingCache.Instance.clear(); } catch (Exception ex) { LOG.error("Could not load file", ex); String errorMessage = "Could not open file " + fileName + ". Check the log for details."; diff --git a/diylc/diylc-swing/src/org/diylc/log4j.properties b/diylc/diylc-swing/src/org/diylc/log4j.properties index d41504c8..da60ad24 100644 --- a/diylc/diylc-swing/src/org/diylc/log4j.properties +++ b/diylc/diylc-swing/src/org/diylc/log4j.properties @@ -1,4 +1,4 @@ -log4j.rootLogger = DEBUG, fileout, stdout +log4j.rootLogger = TRACE, fileout, stdout log4j.appender.fileout = org.diylc.appframework.miscutils.NewLogForEachRunFileAppender log4j.appender.fileout.layout.ConversionPattern = %d{ABSOLUTE} %5p [%t] %c{1} - %m%n log4j.appender.fileout.layout = org.apache.log4j.PatternLayout diff --git a/diylc/diylc-swing/src/org/diylc/swing/plugins/canvas/CanvasPanel.java b/diylc/diylc-swing/src/org/diylc/swing/plugins/canvas/CanvasPanel.java index 8305d1bc..d3a62e3b 100644 --- a/diylc/diylc-swing/src/org/diylc/swing/plugins/canvas/CanvasPanel.java +++ b/diylc/diylc-swing/src/org/diylc/swing/plugins/canvas/CanvasPanel.java @@ -265,7 +265,8 @@ public class CanvasPanel extends JComponent implements Autoscroll { } if (ConfigurationManager.getInstance().readBoolean(IPlugInPort.EXTRA_SPACE_KEY, true)) { drawOptions.add(DrawOption.EXTRA_SPACE); - } + } + drawOptions.add(DrawOption.ENABLE_CACHING); plugInPort.draw(g2d, drawOptions, null, null);