]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.g2d/src/org/simantics/g2d/image/impl/Shadow.java
Merge "ShapeNode with separate stroke and fill paints"
[simantics/platform.git] / bundles / org.simantics.g2d / src / org / simantics / g2d / image / impl / Shadow.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management
3  * in Industry THTH ry.
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
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.g2d.image.impl;
13
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;
24
25 import org.simantics.g2d.image.Image;
26 import org.simantics.scenegraph.Node;
27 import org.simantics.scenegraph.g2d.G2DParentNode;
28
29 /**
30  * Reflects to a shadow of a symbol.
31  * 
32  * @author Toni Kalajainen
33  */
34 public class Shadow extends ImageProxy implements Image {
35
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) {
42             this.alpha = alpha;
43             this.color = color;
44             this.size = size;
45         }
46     }
47
48     public final ShadowParameters shadow;
49     Point2D size;
50     ConvolveOp horiz, vert;
51     int shadowSizeX, shadowSizeY;
52     EnumSet<Feature> feats;
53     public ImageListener origListener;
54
55     /**
56      * 
57      * @param source
58      * @param shadow
59      * @param shadowOnly
60      */
61     public Shadow(Image source, ShadowParameters shadow)
62     {
63         super(source);
64         this.shadow = 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);
71         } else
72             feats = EnumSet.noneOf(Feature.class);
73     }
74
75     public Shadow(Image source, ShadowParameters shadow, double width, double height)
76     {
77         this(source, shadow);
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);
85     }
86
87     @Override
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);
91     }
92
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);
101
102         Graphics2D g = subject.createGraphics();
103         g.translate(shadowSizeX, shadowSizeY);
104         if (size!=null)
105             g.scale(size.getX()/origBounds.getWidth(), size.getY()/origBounds.getHeight());
106         g.translate(-origBounds.getMinX(), -origBounds.getMinY());
107
108         Rectangle2D bounds = new Rectangle2D.Double(0, 0, subject.getWidth(), subject.getHeight());
109 //        GraphicsContextImpl gc = new GraphicsContextImpl(bounds, null);
110 //        try {
111 //              source.paint(gc);
112 //        } finally {
113 //              gc.dispose();
114 //        }
115
116         g.dispose();
117         return subject;
118     }
119
120     private BufferedImage createShadowMask(BufferedImage image) {
121         BufferedImage mask = new BufferedImage(image.getWidth(),
122                 image.getHeight(),
123                 BufferedImage.TYPE_INT_ARGB);
124
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());
131         g2d.dispose();
132
133         return mask;
134     }
135
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);
142         return shadowMask;
143     }
144
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));
150     }
151
152
153     @Override
154     public Node init(G2DParentNode parent) {
155         return null;
156 //              Graphics2D g = gc.getGraphics2D();
157 //              BufferedImage bi = createShadow();
158 //
159 //      Rectangle2D origBounds = source.getBounds();
160 //      if (size!=null) {
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());
168 //              } else {
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());
174 //              }
175     }
176
177     @Override
178     public EnumSet<Feature> getFeatures() {
179         return feats;
180     }
181
182
183 }