diff --git a/diylc/diylc-core/src/org/diylc/common/ComponentType.java b/diylc/diylc-core/src/org/diylc/common/ComponentType.java
index ae50ace5..87b5bda9 100644
--- a/diylc/diylc-core/src/org/diylc/common/ComponentType.java
+++ b/diylc/diylc-core/src/org/diylc/common/ComponentType.java
@@ -48,11 +48,12 @@ public class ComponentType {
private IComponentTransformer transformer;
private KeywordPolicy keywordPolicy;
private String keywordTag;
+ private boolean enableCache;
public ComponentType(String name, String description, CreationMethod creationMethod, String category,
String namePrefix, String author, Icon icon, Class extends IDIYComponent>> instanceClass, double zOrder,
boolean flexibleZOrder, BomPolicy bomPolicy, boolean autoEdit, IComponentTransformer transformer,
- KeywordPolicy keywordPolicy, String keywordTag) {
+ KeywordPolicy keywordPolicy, String keywordTag, boolean enableCache) {
super();
this.name = name;
this.description = description;
@@ -69,6 +70,7 @@ public class ComponentType {
this.transformer = transformer;
this.keywordPolicy = keywordPolicy;
this.keywordTag = keywordTag;
+ this.enableCache = enableCache;
}
public String getName() {
@@ -130,6 +132,10 @@ public class ComponentType {
public String getKeywordTag() {
return keywordTag;
}
+
+ public boolean getEnableCache() {
+ return enableCache;
+ }
@Override
public String toString() {
diff --git a/diylc/diylc-core/src/org/diylc/core/annotations/ComponentDescriptor.java b/diylc/diylc-core/src/org/diylc/core/annotations/ComponentDescriptor.java
index 367811f0..5a52e663 100644
--- a/diylc/diylc-core/src/org/diylc/core/annotations/ComponentDescriptor.java
+++ b/diylc/diylc-core/src/org/diylc/core/annotations/ComponentDescriptor.java
@@ -110,5 +110,12 @@ public @interface ComponentDescriptor {
*
* @return
*/
- String keywordTag() default "";
+ String keywordTag() default "";
+
+ /**
+ * Only to be used for components that span in its eternity in the rectangle defined by the two outside control points.
+ *
+ * @return true if the component drawing should be cached
+ */
+ boolean enableCache() default false;
}
diff --git a/diylc/diylc-core/src/org/diylc/presenter/ComponentProcessor.java b/diylc/diylc-core/src/org/diylc/presenter/ComponentProcessor.java
index b5ef3e3f..4a269309 100644
--- a/diylc/diylc-core/src/org/diylc/presenter/ComponentProcessor.java
+++ b/diylc/diylc-core/src/org/diylc/presenter/ComponentProcessor.java
@@ -93,7 +93,8 @@ public class ComponentProcessor {
double zOrder;
boolean flexibleZOrder;
BomPolicy bomPolicy;
- boolean autoEdit;
+ boolean autoEdit;
+ boolean cacheDrawing;
IComponentTransformer transformer;
KeywordPolicy keywordPolicy;
String keywordTag;
@@ -111,7 +112,8 @@ public class ComponentProcessor {
autoEdit = annotation.autoEdit();
transformer = getComponentTransformer(annotation.transformer());
keywordPolicy = annotation.keywordPolicy();
- keywordTag = annotation.keywordTag();
+ keywordTag = annotation.keywordTag();
+ cacheDrawing = annotation.enableCache();
} else { // default
return null;
}
@@ -133,7 +135,7 @@ public class ComponentProcessor {
}
ComponentType componentType =
new ComponentType(name, description, creationMethod, category, namePrefix, author, icon, clazz, zOrder,
- flexibleZOrder, bomPolicy, autoEdit, transformer, keywordPolicy, keywordTag);
+ flexibleZOrder, bomPolicy, autoEdit, transformer, keywordPolicy, keywordTag, cacheDrawing);
componentTypeMap.put(clazz.getName(), componentType);
return componentType;
}
diff --git a/diylc/diylc-core/src/org/diylc/presenter/Connection.java b/diylc/diylc-core/src/org/diylc/presenter/Connection.java
index 73d41b10..81f66edc 100644
--- a/diylc/diylc-core/src/org/diylc/presenter/Connection.java
+++ b/diylc/diylc-core/src/org/diylc/presenter/Connection.java
@@ -1,7 +1,29 @@
+/*
+ *
+ * DIY Layout Creator (DIYLC). Copyright (c) 2009-2018 held jointly by the individual authors.
+ *
+ * This file is part of DIYLC.
+ *
+ * DIYLC 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.
+ *
+ * DIYLC 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 DIYLC. If not, see
+ * .
+ */
package org.diylc.presenter;
import java.awt.geom.Point2D;
+/**
+ * Represents a continuity connection between two points.
+ *
+ * @author bancika
+ */
public class Connection {
private Point2D p1;
diff --git a/diylc/diylc-core/src/org/diylc/presenter/DrawingCache.java b/diylc/diylc-core/src/org/diylc/presenter/DrawingCache.java
new file mode 100644
index 00000000..eff871f6
--- /dev/null
+++ b/diylc/diylc-core/src/org/diylc/presenter/DrawingCache.java
@@ -0,0 +1,147 @@
+/*
+ *
+ * DIY Layout Creator (DIYLC). Copyright (c) 2009-2018 held jointly by the individual authors.
+ *
+ * This file is part of DIYLC.
+ *
+ * DIYLC 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.
+ *
+ * DIYLC 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 DIYLC. If not, see
+ * .
+ */
+package org.diylc.presenter;
+
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.image.BufferedImage;
+import java.util.HashMap;
+import java.util.Map;
+
+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.
+ *
+ * @author bancika
+ */
+public class DrawingCache {
+
+ private Map, CacheValue> imageCache = new HashMap, CacheValue>();
+
+ private static final Logger LOG = Logger.getLogger(DrawingCache.class);
+
+ public static DrawingCache Instance = new DrawingCache();
+
+ @SuppressWarnings("unchecked")
+ public void draw(IDIYComponent> component, G2DWrapper g2d, ComponentState componentState,
+ boolean outlineMode, Project project, double zoom, boolean trackArea) {
+ ComponentType type = ComponentProcessor.getInstance()
+ .extractComponentTypeFrom((Class extends IDIYComponent>>) component.getClass());
+
+ if (type.getEnableCache()) {
+ // if we need to apply caching
+ Point firstPoint = component.getControlPoint(0);
+ Point lastPoint = component.getControlPoint(component.getControlPointCount() - 1);
+
+ // cache hit!
+ 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) {
+ 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);
+ Graphics2D cg2d = image.createGraphics();
+
+ // copy over rendering settings
+ cg2d.setRenderingHints(g2d.getRenderingHints());
+
+ cg2d.setClip(0, 0, width, height);
+
+ // 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);
+
+ 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();
+
+ value = new CacheValue(component, image, wrapper, componentState);
+ imageCache.put(component, value);
+ }
+
+ // draw cached image
+ g2d.getCanvasGraphics().drawImage(value.getImage(), firstPoint.x, firstPoint.y, null);
+
+ // 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);
+ }
+ }
+
+ private class CacheValue {
+ IDIYComponent> component;
+ BufferedImage image;
+ G2DWrapper wrapper;
+ ComponentState state;
+
+ public CacheValue(IDIYComponent> component, BufferedImage image, G2DWrapper wrapper, ComponentState state) {
+ super();
+ // take a copy of the component, so we can check if it changed in the meantime
+ try {
+ this.component = component.clone();
+ } catch (CloneNotSupportedException e) {
+ }
+ this.image = image;
+ this.wrapper = wrapper;
+ this.state = state;
+ }
+
+ public IDIYComponent> getComponent() {
+ return component;
+ }
+
+ public BufferedImage getImage() {
+ return image;
+ }
+
+ public G2DWrapper getWrapper() {
+ return wrapper;
+ }
+
+ public ComponentState getState() {
+ return state;
+ }
+
+ @Override
+ public String toString() {
+ return component.getName() + ":" + state;
+ }
+ }
+}
diff --git a/diylc/diylc-core/src/org/diylc/presenter/DrawingManager.java b/diylc/diylc-core/src/org/diylc/presenter/DrawingManager.java
index 97d19125..352cb719 100644
--- a/diylc/diylc-core/src/org/diylc/presenter/DrawingManager.java
+++ b/diylc/diylc-core/src/org/diylc/presenter/DrawingManager.java
@@ -83,6 +83,7 @@ public class DrawingManager {
private Theme theme = (Theme) ConfigurationManager.getInstance().readObject(IPlugInPort.THEME_KEY,
Constants.DEFAULT_THEME);
+ // Maps keyed by object reference.
// Keeps Area object of each drawn component.
private Map, ComponentArea> componentAreaMap;
// Maps components to the last state they are drawn in. Also, used to
@@ -263,7 +264,7 @@ public class DrawingManager {
// Do not track the area if component is not invalidated and was
// drawn in the same state.
boolean trackArea = lastDrawnStateMap.get(component) != state;
-
+
synchronized (g2d) {
g2dWrapper.startedDrawingComponent();
if (!trackArea) {
@@ -275,11 +276,15 @@ public class DrawingManager {
}
// Draw the component through the g2dWrapper.
try {
- component.draw(g2dWrapper, state, drawOptions.contains(DrawOption.OUTLINE_MODE), project, g2dWrapper);
+// 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 (g2dWrapper.isTrackingContinuityArea()) {
- LOG.info("Component " + component.getName() + " of type " + component.getClass().getName() + " did not stop tracking continuity area.");
- g2dWrapper.stopTrackingContinuityArea();
+ LOG.info("Component " + component.getName() + " of type " + component.getClass().getName() + " did not stop tracking continuity area.");
}
+
+ // just in case, stop all tracking
+ g2dWrapper.stopTrackingContinuityArea();
+ g2dWrapper.stopTracking();
} catch (Exception e) {
LOG.error("Error drawing " + component.getName(), e);
failedComponents.add(component);
diff --git a/diylc/diylc-core/src/org/diylc/presenter/G2DWrapper.java b/diylc/diylc-core/src/org/diylc/presenter/G2DWrapper.java
index 42a969ae..16f9d9ec 100644
--- a/diylc/diylc-core/src/org/diylc/presenter/G2DWrapper.java
+++ b/diylc/diylc-core/src/org/diylc/presenter/G2DWrapper.java
@@ -120,6 +120,10 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver {
continuityMarker = null;
currentTx = new AffineTransform();
}
+
+ public Graphics2D getCanvasGraphics() {
+ return canvasGraphics;
+ }
/**
* Clears out the current area and caches canvas settings.
@@ -157,6 +161,12 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver {
// Map sorted = sortByComparator(trackingStacks, false);
return new ComponentArea(currentArea, continuityPositiveAreas.values(), continuityNegativeAreas.values());
}
+
+ public void merge(G2DWrapper wrapper) {
+ currentArea = wrapper.currentArea;
+ continuityPositiveAreas = wrapper.continuityPositiveAreas;
+ continuityNegativeAreas = wrapper.continuityNegativeAreas;
+ }
@Override
public void startTracking() {
@@ -721,6 +731,7 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver {
canvasGraphics.setXORMode(c1);
}
+ @SuppressWarnings("unused")
private static Map sortByComparator(Map unsortMap,
final boolean order) {
List> list =
diff --git a/diylc/diylc-core/src/org/diylc/presenter/InstantiationManager.java b/diylc/diylc-core/src/org/diylc/presenter/InstantiationManager.java
index 3a72207e..a7b91fb6 100644
--- a/diylc/diylc-core/src/org/diylc/presenter/InstantiationManager.java
+++ b/diylc/diylc-core/src/org/diylc/presenter/InstantiationManager.java
@@ -65,10 +65,10 @@ public class InstantiationManager {
public static final ComponentType clipboardType = new ComponentType("Clipboard contents",
"Components from the clipboard", CreationMethod.SINGLE_CLICK, "Multi", "", "", null, null, 0, false, null,
- false, null, KeywordPolicy.NEVER_SHOW, null);
+ false, null, KeywordPolicy.NEVER_SHOW, null, false);
public static final ComponentType blockType = new ComponentType("Building block",
"Components from the building block", CreationMethod.SINGLE_CLICK, "Multi", "", "", null, null, 0, false, null,
- false, null, KeywordPolicy.NEVER_SHOW, null);
+ false, null, KeywordPolicy.NEVER_SHOW, null, false);
public InstantiationManager() {}
diff --git a/diylc/diylc-library/lib/diylc-core.jar b/diylc/diylc-library/lib/diylc-core.jar
index 5e292c76..8be14863 100644
Binary files a/diylc/diylc-library/lib/diylc-core.jar and b/diylc/diylc-library/lib/diylc-core.jar differ
diff --git a/diylc/diylc-library/src/org/diylc/components/boards/Breadboard.java b/diylc/diylc-library/src/org/diylc/components/boards/Breadboard.java
index f8d2d4a0..ff7c4cb7 100644
--- a/diylc/diylc-library/src/org/diylc/components/boards/Breadboard.java
+++ b/diylc/diylc-library/src/org/diylc/components/boards/Breadboard.java
@@ -49,7 +49,7 @@ import org.diylc.core.measures.SizeUnit;
@ComponentDescriptor(name = "Breadboard", category = "Boards", author = "Branislav Stojkovic",
description = "Prototyping solderless breadboard", instanceNamePrefix = "BB",
zOrder = IDIYComponent.BOARD, bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, autoEdit = false,
- keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME)
+ keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME, enableCache = true)
public class Breadboard extends AbstractComponent {
private static final long serialVersionUID = 1L;
diff --git a/diylc/diylc-library/src/org/diylc/components/boards/EyeletBoard.java b/diylc/diylc-library/src/org/diylc/components/boards/EyeletBoard.java
index 66dff972..61723414 100644
--- a/diylc/diylc-library/src/org/diylc/components/boards/EyeletBoard.java
+++ b/diylc/diylc-library/src/org/diylc/components/boards/EyeletBoard.java
@@ -43,7 +43,7 @@ import org.diylc.utils.Constants;
@ComponentDescriptor(name = "Eyelet Board", category = "Boards", author = "Branislav Stojkovic",
zOrder = IDIYComponent.BOARD, instanceNamePrefix = "Board", description = "Perforated board with eyelets",
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, autoEdit = false, keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME,
- transformer = SimpleComponentTransformer.class)
+ transformer = SimpleComponentTransformer.class, enableCache = true)
public class EyeletBoard extends AbstractBoard {
private static final long serialVersionUID = 1L;
diff --git a/diylc/diylc-library/src/org/diylc/components/boards/PerfBoard.java b/diylc/diylc-library/src/org/diylc/components/boards/PerfBoard.java
index 905b0c3a..85e9d34b 100644
--- a/diylc/diylc-library/src/org/diylc/components/boards/PerfBoard.java
+++ b/diylc/diylc-library/src/org/diylc/components/boards/PerfBoard.java
@@ -44,7 +44,7 @@ import org.diylc.utils.Constants;
@ComponentDescriptor(name = "Perf Board w/ Pads", category = "Boards", author = "Branislav Stojkovic",
zOrder = IDIYComponent.BOARD, instanceNamePrefix = "Board", description = "Perforated board with solder pads",
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, autoEdit = false, keywordPolicy = KeywordPolicy.SHOW_TAG,
- keywordTag = "Perf Board", transformer = SimpleComponentTransformer.class)
+ keywordTag = "Perf Board", transformer = SimpleComponentTransformer.class, enableCache = true)
public class PerfBoard extends AbstractBoard {
private static final long serialVersionUID = 1L;
diff --git a/diylc/diylc-library/src/org/diylc/components/boards/ProtoBoard.java b/diylc/diylc-library/src/org/diylc/components/boards/ProtoBoard.java
index 0b5988f5..c3c640dc 100644
--- a/diylc/diylc-library/src/org/diylc/components/boards/ProtoBoard.java
+++ b/diylc/diylc-library/src/org/diylc/components/boards/ProtoBoard.java
@@ -47,7 +47,7 @@ import org.diylc.core.measures.SizeUnit;
@ComponentDescriptor(name = "P-0+o (Proto) Board", category = "Boards", author = "Branislav Stojkovic",
description = "A prototyping board for modular synths, central bus with V+ GND and V-", instanceNamePrefix = "BB",
zOrder = IDIYComponent.BOARD, bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, autoEdit = false,
- keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME)
+ keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME, enableCache = true)
public class ProtoBoard extends AbstractComponent {
private static final long serialVersionUID = 1L;
diff --git a/diylc/diylc-library/src/org/diylc/components/boards/TriPadBoard.java b/diylc/diylc-library/src/org/diylc/components/boards/TriPadBoard.java
index a9c95edd..eca992cc 100644
--- a/diylc/diylc-library/src/org/diylc/components/boards/TriPadBoard.java
+++ b/diylc/diylc-library/src/org/diylc/components/boards/TriPadBoard.java
@@ -45,7 +45,7 @@ import org.diylc.utils.Constants;
@ComponentDescriptor(name = "TriPad Board", category = "Boards", author = "Hauke Juhls", zOrder = IDIYComponent.BOARD,
instanceNamePrefix = "Board",
description = "Perforated FR4 board with copper strips connecting 3 holes in a row (aka TriPad Board)",
- keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME, transformer = SimpleComponentTransformer.class)
+ keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME, transformer = SimpleComponentTransformer.class, enableCache = true)
public class TriPadBoard extends AbstractBoard {
private static final long serialVersionUID = 1L;
diff --git a/diylc/diylc-library/src/org/diylc/components/boards/VeroBoard.java b/diylc/diylc-library/src/org/diylc/components/boards/VeroBoard.java
index 32298eb4..a87019d7 100644
--- a/diylc/diylc-library/src/org/diylc/components/boards/VeroBoard.java
+++ b/diylc/diylc-library/src/org/diylc/components/boards/VeroBoard.java
@@ -47,7 +47,7 @@ import org.diylc.utils.Constants;
zOrder = IDIYComponent.BOARD, instanceNamePrefix = "Board",
description = "Perforated FR4 board with copper strips connecting all holes in a row",
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, autoEdit = false, keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME,
- transformer = SimpleComponentTransformer.class)
+ transformer = SimpleComponentTransformer.class, enableCache = true)
public class VeroBoard extends AbstractBoard {
private static final long serialVersionUID = 1L;
diff --git a/diylc/diylc-swing/lib/diylc-core.jar b/diylc/diylc-swing/lib/diylc-core.jar
index 5e292c76..8be14863 100644
Binary files a/diylc/diylc-swing/lib/diylc-core.jar and b/diylc/diylc-swing/lib/diylc-core.jar differ
diff --git a/diylc/diylc-swing/library/main.jar b/diylc/diylc-swing/library/main.jar
index 6ea5d242..2fa6c9f8 100644
Binary files a/diylc/diylc-swing/library/main.jar and b/diylc/diylc-swing/library/main.jar differ