--- /dev/null
+/*******************************************************************************\r
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
+ * in Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ * VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.g2d.image.impl;\r
+\r
+import java.awt.AlphaComposite;\r
+import java.awt.Color;\r
+import java.awt.Graphics2D;\r
+import java.awt.geom.Point2D;\r
+import java.awt.geom.Rectangle2D;\r
+import java.awt.image.BufferedImage;\r
+import java.awt.image.ConvolveOp;\r
+import java.awt.image.Kernel;\r
+import java.util.Arrays;\r
+import java.util.EnumSet;\r
+\r
+import org.simantics.g2d.image.Image;\r
+import org.simantics.scenegraph.Node;\r
+import org.simantics.scenegraph.g2d.G2DParentNode;\r
+\r
+/**\r
+ * Reflects to a shadow of a symbol.\r
+ * \r
+ * @author Toni Kalajainen\r
+ */\r
+public class Shadow extends ImageProxy implements Image {\r
+\r
+ public static final ShadowParameters SHADOW = new ShadowParameters(0.5, Color.BLACK, 2);\r
+ public static final class ShadowParameters {\r
+ public final double alpha;\r
+ public final Color color;\r
+ public final int size;\r
+ public ShadowParameters(double alpha, Color color, int size) {\r
+ this.alpha = alpha;\r
+ this.color = color;\r
+ this.size = size;\r
+ }\r
+ }\r
+\r
+ public final ShadowParameters shadow;\r
+ Point2D size;\r
+ ConvolveOp horiz, vert;\r
+ int shadowSizeX, shadowSizeY;\r
+ EnumSet<Feature> feats;\r
+ public ImageListener origListener;\r
+\r
+ /**\r
+ * \r
+ * @param source\r
+ * @param shadow\r
+ * @param shadowOnly\r
+ */\r
+ public Shadow(Image source, ShadowParameters shadow)\r
+ {\r
+ super(source);\r
+ this.shadow = shadow;\r
+ shadowSizeX = shadow.size;\r
+ shadowSizeY = shadow.size;\r
+ horiz = createBlurOp(shadow.size, 1);\r
+ vert = createBlurOp(1, shadow.size);\r
+ if (source.getFeatures().contains(Feature.Volatile)) {\r
+ feats = EnumSet.of(Feature.Volatile);\r
+ } else\r
+ feats = EnumSet.noneOf(Feature.class);\r
+ }\r
+\r
+ public Shadow(Image source, ShadowParameters shadow, double width, double height)\r
+ {\r
+ this(source, shadow);\r
+ this.size = new Point2D.Double(width, height);\r
+ shadowSizeX = (int) Math.round( shadow.size * width / source.getBounds().getWidth() );\r
+ shadowSizeY = (int) Math.round( shadow.size * height / source.getBounds().getHeight() );\r
+ if (shadowSizeX<1) shadowSizeX = 1;\r
+ if (shadowSizeY<1) shadowSizeY = 1;\r
+ horiz = createBlurOp(shadowSizeX, 1);\r
+ vert = createBlurOp(1, shadowSizeY);\r
+ }\r
+\r
+ @Override\r
+ public Rectangle2D getBounds() {\r
+ Rectangle2D rect = source.getBounds();\r
+ return new Rectangle2D.Double(rect.getX() - shadowSizeX, rect.getY() - shadowSizeY, rect.getWidth() + shadowSizeX*2, rect.getHeight() + shadowSizeY*2);\r
+ }\r
+\r
+ private BufferedImage createImage() {\r
+ Rectangle2D origBounds = source.getBounds();\r
+ double width = size==null?origBounds.getWidth() :size.getX();\r
+ double height = size==null?origBounds.getHeight():size.getY();\r
+ BufferedImage subject = new BufferedImage(\r
+ (int)Math.ceil( width + shadowSizeX * 2 ),\r
+ (int)Math.ceil( height + shadowSizeY * 2 ),\r
+ BufferedImage.TYPE_INT_ARGB);\r
+\r
+ Graphics2D g = subject.createGraphics();\r
+ g.translate(shadowSizeX, shadowSizeY);\r
+ if (size!=null)\r
+ g.scale(size.getX()/origBounds.getWidth(), size.getY()/origBounds.getHeight());\r
+ g.translate(-origBounds.getMinX(), -origBounds.getMinY());\r
+\r
+ Rectangle2D bounds = new Rectangle2D.Double(0, 0, subject.getWidth(), subject.getHeight());\r
+// GraphicsContextImpl gc = new GraphicsContextImpl(bounds, null);\r
+// try {\r
+// source.paint(gc);\r
+// } finally {\r
+// gc.dispose();\r
+// }\r
+\r
+ g.dispose();\r
+ return subject;\r
+ }\r
+\r
+ private BufferedImage createShadowMask(BufferedImage image) {\r
+ BufferedImage mask = new BufferedImage(image.getWidth(),\r
+ image.getHeight(),\r
+ BufferedImage.TYPE_INT_ARGB);\r
+\r
+ Graphics2D g2d = mask.createGraphics();\r
+ g2d.drawImage(image, 0, 0, null);\r
+ g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_IN,\r
+ (float)shadow.alpha));\r
+ g2d.setColor(shadow.color);\r
+ g2d.fillRect(0, 0, image.getWidth(), image.getHeight());\r
+ g2d.dispose();\r
+\r
+ return mask;\r
+ }\r
+\r
+ public BufferedImage createShadow() {\r
+ BufferedImage i1 = createImage();\r
+ BufferedImage i2 = new BufferedImage(i1.getWidth(), i1.getHeight(), BufferedImage.TYPE_INT_ARGB);\r
+ BufferedImage shadowMask = createShadowMask(i1);\r
+ horiz.filter(shadowMask, i2);\r
+ vert.filter(i2, shadowMask);\r
+ return shadowMask;\r
+ }\r
+\r
+ private ConvolveOp createBlurOp(int width, int height) {\r
+ float[] data = new float[width * height];\r
+ float value = 1.0f / (width * height);\r
+ Arrays.fill(data, value);\r
+ return new ConvolveOp(new Kernel(width, height, data));\r
+ }\r
+\r
+\r
+ @Override\r
+ public Node init(G2DParentNode parent) {\r
+ return null;\r
+// Graphics2D g = gc.getGraphics2D();\r
+// BufferedImage bi = createShadow();\r
+//\r
+// Rectangle2D origBounds = source.getBounds();\r
+// if (size!=null) {\r
+// g.translate(origBounds.getMinX(), origBounds.getMinY());\r
+// g.scale(origBounds.getWidth()/size.getX(), origBounds.getHeight()/size.getY());\r
+// g.translate(-shadowSizeX, -shadowSizeY);\r
+// g.drawImage(bi, 0, 0, null);\r
+// g.translate(shadowSizeX, shadowSizeY);\r
+// g.scale(size.getX()/origBounds.getWidth(), size.getY()/origBounds.getHeight());\r
+// g.translate(-origBounds.getMinX(), -origBounds.getMinY());\r
+// } else {\r
+// g.translate(-shadowSizeX, -shadowSizeY);\r
+// g.translate(origBounds.getMinX(), origBounds.getMinY());\r
+// g.drawImage(bi, 0, 0, null);\r
+// g.translate(shadowSizeX, shadowSizeY);\r
+// g.translate(-origBounds.getMinX(), -origBounds.getMinY());\r
+// }\r
+ }\r
+\r
+ @Override\r
+ public EnumSet<Feature> getFeatures() {\r
+ return feats;\r
+ }\r
+\r
+\r
+}\r