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