1 /*******************************************************************************
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.g2d.image.impl;
14 import java.awt.AlphaComposite;
15 import java.awt.Color;
16 import java.awt.Graphics2D;
17 import java.awt.geom.Point2D;
18 import java.awt.geom.Rectangle2D;
19 import java.awt.image.BufferedImage;
20 import java.awt.image.ConvolveOp;
21 import java.awt.image.Kernel;
22 import java.util.Arrays;
23 import java.util.EnumSet;
25 import org.simantics.g2d.image.Image;
26 import org.simantics.scenegraph.Node;
27 import org.simantics.scenegraph.g2d.G2DParentNode;
30 * Reflects to a shadow of a symbol.
32 * @author Toni Kalajainen
34 public class Shadow extends ImageProxy implements Image {
36 public static final ShadowParameters SHADOW = new ShadowParameters(0.5, Color.BLACK, 2);
37 public static final class ShadowParameters {
38 public final double alpha;
39 public final Color color;
40 public final int size;
41 public ShadowParameters(double alpha, Color color, int size) {
48 public final ShadowParameters shadow;
50 ConvolveOp horiz, vert;
51 int shadowSizeX, shadowSizeY;
52 EnumSet<Feature> feats;
53 public ImageListener origListener;
61 public Shadow(Image source, ShadowParameters shadow)
65 shadowSizeX = shadow.size;
66 shadowSizeY = shadow.size;
67 horiz = createBlurOp(shadow.size, 1);
68 vert = createBlurOp(1, shadow.size);
69 if (source.getFeatures().contains(Feature.Volatile)) {
70 feats = EnumSet.of(Feature.Volatile);
72 feats = EnumSet.noneOf(Feature.class);
75 public Shadow(Image source, ShadowParameters shadow, double width, double height)
78 this.size = new Point2D.Double(width, height);
79 shadowSizeX = (int) Math.round( shadow.size * width / source.getBounds().getWidth() );
80 shadowSizeY = (int) Math.round( shadow.size * height / source.getBounds().getHeight() );
81 if (shadowSizeX<1) shadowSizeX = 1;
82 if (shadowSizeY<1) shadowSizeY = 1;
83 horiz = createBlurOp(shadowSizeX, 1);
84 vert = createBlurOp(1, shadowSizeY);
88 public Rectangle2D getBounds() {
89 Rectangle2D rect = source.getBounds();
90 return new Rectangle2D.Double(rect.getX() - shadowSizeX, rect.getY() - shadowSizeY, rect.getWidth() + shadowSizeX*2, rect.getHeight() + shadowSizeY*2);
93 private BufferedImage createImage() {
94 Rectangle2D origBounds = source.getBounds();
95 double width = size==null?origBounds.getWidth() :size.getX();
96 double height = size==null?origBounds.getHeight():size.getY();
97 BufferedImage subject = new BufferedImage(
98 (int)Math.ceil( width + shadowSizeX * 2 ),
99 (int)Math.ceil( height + shadowSizeY * 2 ),
100 BufferedImage.TYPE_INT_ARGB);
102 Graphics2D g = subject.createGraphics();
103 g.translate(shadowSizeX, shadowSizeY);
105 g.scale(size.getX()/origBounds.getWidth(), size.getY()/origBounds.getHeight());
106 g.translate(-origBounds.getMinX(), -origBounds.getMinY());
108 Rectangle2D bounds = new Rectangle2D.Double(0, 0, subject.getWidth(), subject.getHeight());
109 // GraphicsContextImpl gc = new GraphicsContextImpl(bounds, null);
120 private BufferedImage createShadowMask(BufferedImage image) {
121 BufferedImage mask = new BufferedImage(image.getWidth(),
123 BufferedImage.TYPE_INT_ARGB);
125 Graphics2D g2d = mask.createGraphics();
126 g2d.drawImage(image, 0, 0, null);
127 g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_IN,
128 (float)shadow.alpha));
129 g2d.setColor(shadow.color);
130 g2d.fillRect(0, 0, image.getWidth(), image.getHeight());
136 public BufferedImage createShadow() {
137 BufferedImage i1 = createImage();
138 BufferedImage i2 = new BufferedImage(i1.getWidth(), i1.getHeight(), BufferedImage.TYPE_INT_ARGB);
139 BufferedImage shadowMask = createShadowMask(i1);
140 horiz.filter(shadowMask, i2);
141 vert.filter(i2, shadowMask);
145 private ConvolveOp createBlurOp(int width, int height) {
146 float[] data = new float[width * height];
147 float value = 1.0f / (width * height);
148 Arrays.fill(data, value);
149 return new ConvolveOp(new Kernel(width, height, data));
154 public Node init(G2DParentNode parent) {
156 // Graphics2D g = gc.getGraphics2D();
157 // BufferedImage bi = createShadow();
159 // Rectangle2D origBounds = source.getBounds();
161 // g.translate(origBounds.getMinX(), origBounds.getMinY());
162 // g.scale(origBounds.getWidth()/size.getX(), origBounds.getHeight()/size.getY());
163 // g.translate(-shadowSizeX, -shadowSizeY);
164 // g.drawImage(bi, 0, 0, null);
165 // g.translate(shadowSizeX, shadowSizeY);
166 // g.scale(size.getX()/origBounds.getWidth(), size.getY()/origBounds.getHeight());
167 // g.translate(-origBounds.getMinX(), -origBounds.getMinY());
169 // g.translate(-shadowSizeX, -shadowSizeY);
170 // g.translate(origBounds.getMinX(), origBounds.getMinY());
171 // g.drawImage(bi, 0, 0, null);
172 // g.translate(shadowSizeX, shadowSizeY);
173 // g.translate(-origBounds.getMinX(), -origBounds.getMinY());
178 public EnumSet<Feature> getFeatures() {