]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.g2d/src/org/simantics/g2d/image/impl/MipMapVRamBufferedImage.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.g2d / src / org / simantics / g2d / image / impl / MipMapVRamBufferedImage.java
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
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.g2d.image.impl;\r
13 \r
14 import java.awt.Color;\r
15 import java.awt.Graphics2D;\r
16 import java.awt.GraphicsConfiguration;\r
17 import java.awt.Shape;\r
18 import java.awt.Transparency;\r
19 import java.awt.geom.AffineTransform;\r
20 import java.awt.geom.Rectangle2D;\r
21 import java.awt.image.VolatileImage;\r
22 import java.util.ArrayList;\r
23 import java.util.Arrays;\r
24 import java.util.EnumSet;\r
25 import java.util.HashMap;\r
26 import java.util.List;\r
27 import java.util.Map;\r
28 \r
29 import org.simantics.g2d.image.Image;\r
30 import org.simantics.scenegraph.Node;\r
31 import org.simantics.scenegraph.g2d.G2DParentNode;\r
32 import org.simantics.scenegraph.utils.QualityHints;\r
33 \r
34 /**\r
35  * Video-ram cache suitable for rasterized PaintableSymbols scalable vector graphics.\r
36  * <p>\r
37  * This implementation rasterizes the same symbol from different mip map levels.\r
38  * \r
39  * @see VRamImage\r
40  * @author Toni Kalajainen\r
41  */\r
42 public class MipMapVRamBufferedImage extends ImageProxy implements Image {\r
43 \r
44     /** Extra margin to the bounds reported by batik*/\r
45     public static final double MARGIN_PERCENT = 3;\r
46 \r
47     public static final double MAX_DIMENSION = 800;\r
48     public static final double MIN_DIMENSION = 4;\r
49 \r
50     final GraphicsConfiguration gc;\r
51     Shape outline;\r
52 \r
53     double [] resolutions;\r
54     Map<Double, Raster> rasters = new HashMap<Double, Raster>();\r
55     double minResolution, maxResolution;\r
56     EnumSet<Feature> caps;\r
57 \r
58     public MipMapVRamBufferedImage(Image original, GraphicsConfiguration gc)\r
59     {\r
60         super(original);\r
61         if(gc==null)\r
62             throw new IllegalArgumentException("Argument is null.");\r
63         this.source = original;\r
64         this.gc = gc;\r
65 \r
66         // Init rasters - they are built on-demand\r
67         double maxResolution = maxResolution();\r
68         double minResolution = minResolution();\r
69         double resolution = maxResolution;\r
70         List<Double> resolutions = new ArrayList<Double>();\r
71         while (resolution>minResolution)\r
72         {\r
73             Raster r = new Raster(resolution);\r
74             rasters.put(resolution, r);\r
75             resolutions.add(resolution);\r
76             resolution /= 2;\r
77         }\r
78 \r
79         // arraylist -> array\r
80         this.resolutions = new double[resolutions.size()];\r
81         for (int i=0; i<resolutions.size(); i++)\r
82             this.resolutions[i] = resolutions.get(resolutions.size()-1-i);\r
83         this.minResolution = this.resolutions[0];\r
84         this.maxResolution = this.resolutions[this.resolutions.length-1];\r
85 \r
86         if (original.getFeatures().contains(Feature.Volatile))\r
87             caps = EnumSet.noneOf(Feature.class);\r
88         else\r
89             caps = EnumSet.of(Feature.Vector);\r
90     }\r
91 \r
92     private double maxResolution()\r
93     {\r
94         Rectangle2D bounds = source.getBounds();\r
95         double wid = bounds.getWidth();\r
96         double hei = bounds.getHeight();\r
97         return MAX_DIMENSION/Math.sqrt(wid*hei);\r
98     }\r
99 \r
100     private double minResolution()\r
101     {\r
102         Rectangle2D bounds = source.getBounds();\r
103         double wid = bounds.getWidth();\r
104         double hei = bounds.getHeight();\r
105         return MIN_DIMENSION/Math.sqrt(wid*hei);\r
106     }\r
107 \r
108     private double requiredResolution(AffineTransform at)\r
109     {\r
110         double m00 = at.getScaleX();\r
111         double m11 = at.getScaleY();\r
112         double m10 = at.getShearY();\r
113         double m01 = at.getShearX();\r
114         // Project unit vector to canvas\r
115         double sx = Math.sqrt( m00*m00+m10*m10 );\r
116         double sy = Math.sqrt( m01*m01+m11*m11 );\r
117         return Math.sqrt(sx*sx+sy*sy);\r
118     }\r
119 \r
120     private double findClosestResolution(double resolution)\r
121     {\r
122         int index = Arrays.binarySearch(resolutions, resolution);\r
123         if (index>=0) return resolutions[index];\r
124         index  = -(index+1);\r
125 \r
126         if (index>=resolutions.length) index = resolutions.length-1;\r
127         if (index<0) index = 0;\r
128         return resolutions[index];\r
129     }\r
130 \r
131     @Override\r
132     public Node init(G2DParentNode parent) {\r
133         return null;\r
134 //              Graphics2D g = gc.getGraphics2D();\r
135 //              // Quality rendering requested, do not render from cache\r
136 //              if (g.getRenderingHint(RenderingHints.KEY_RENDERING) == RenderingHints.VALUE_RENDER_QUALITY)\r
137 //              {\r
138 //                      source.paint(gc);\r
139 //                      return;\r
140 //              }\r
141 //\r
142 //              double requiredResolution = requiredResolution(g.getTransform());\r
143 //              if (requiredResolution > maxResolution)\r
144 //              {\r
145 //                      g = gc.createClone();\r
146 //                      g.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED);\r
147 //                      g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);\r
148 //                      g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);\r
149 //                      g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);\r
150 //                      g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);\r
151 //                      g.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);\r
152 //                      gc = new GraphicsContextImpl(g, gc.getBounds(), gc.getNode());\r
153 //                      source.paint(gc);\r
154 //                      return;\r
155 //              }\r
156 //\r
157 //              double closestResolution = findClosestResolution(requiredResolution);\r
158 //\r
159 //              Raster raster = rasters.get(closestResolution);\r
160 //\r
161 //              Object origInterpolationHint = g.getRenderingHint(RenderingHints.KEY_INTERPOLATION);\r
162 //              if (origInterpolationHint==null)\r
163 //                      origInterpolationHint = RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR;\r
164 //              g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);\r
165 //              try {\r
166 //                      raster.paint( gc );\r
167 //              } finally {\r
168 //                      g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, origInterpolationHint);\r
169 //              }\r
170     }\r
171 \r
172     public Image getOriginalPaintableSymbol()\r
173     {\r
174         return source;\r
175     }\r
176 \r
177     synchronized void releaseRaster() {\r
178         for (Raster r : rasters.values())\r
179             r.image = null;\r
180 \r
181     }\r
182 \r
183     @Override\r
184     protected void notifyChanged() {\r
185         releaseRaster();\r
186         super.notifyChanged();\r
187     }\r
188 \r
189     class Raster implements Comparable<Raster> {\r
190         VolatileImage image;\r
191         double resolution;\r
192         int widMargin, heiMargin;\r
193         int wid, hei;\r
194 \r
195         Raster(double resolution) {\r
196             Rectangle2D bounds = source.getBounds();\r
197             this.resolution = resolution;\r
198             double wid = bounds.getWidth();\r
199             double hei = bounds.getHeight();\r
200             this.wid = (int) (wid * resolution);\r
201             this.hei = (int) (hei * resolution);\r
202             widMargin = (int) (wid * resolution * (MARGIN_PERCENT/100)) +1;\r
203             heiMargin = (int) (hei * resolution * (MARGIN_PERCENT/100)) +1;\r
204         }\r
205 \r
206         synchronized VolatileImage restore()\r
207         {\r
208             Rectangle2D bounds = source.getBounds();\r
209             if (image==null) {\r
210                 image = gc.createCompatibleVolatileImage(\r
211                         wid+widMargin*2,\r
212                         hei+heiMargin*2,\r
213                         Transparency.TRANSLUCENT);\r
214             }\r
215             int validateResult = image.validate(gc);\r
216             if (validateResult == VolatileImage.IMAGE_INCOMPATIBLE)\r
217                 return null;\r
218 \r
219             if (validateResult == VolatileImage.IMAGE_OK)\r
220                 return image;\r
221 \r
222             if (validateResult == VolatileImage.IMAGE_RESTORED /*raster.contentsLost()*/) {\r
223                 Graphics2D target = image.createGraphics();\r
224                 target.setBackground(new Color(255,255,255,0));\r
225                 target.clearRect(0, 0, image.getWidth(), image.getHeight());\r
226                 QualityHints.HIGH_QUALITY_HINTS.setQuality(target);\r
227                 target.translate(widMargin, heiMargin);\r
228                 target.scale(resolution, resolution);\r
229                 target.translate(-bounds.getMinX(), -bounds.getMinY());\r
230                 source.init(null);\r
231 //                                              new GraphicsContextImpl(new Rectangle2D.Double(0,0, image.getWidth(), image.getHeight()), null)\r
232 //                                              );\r
233                 target.dispose();\r
234                 return image;\r
235             }\r
236             return null;\r
237         }\r
238 \r
239 //        public void paint(GraphicsContext gc) {\r
240 //                      Rectangle2D bounds = source.getBounds();\r
241 //                      VolatileImage image = restore();\r
242 //                      if (image==null)\r
243 //                      {\r
244 //                              QualityHints.LOW_QUALITY_HINTS.setQuality(gc.getGraphics2D());\r
245 //                              source.paint(gc);\r
246 //                              return;\r
247 //                      }\r
248 //                      Graphics2D g = gc.getGraphics2D();\r
249 //                      AffineTransform af = g.getTransform();\r
250 //                      Object rh = g.getRenderingHint(RenderingHints.KEY_INTERPOLATION);\r
251 //                      try {\r
252 //                              /// Bicubic interpolation is very slow with opengl pipeline\r
253 //                              if (rh == RenderingHints.VALUE_INTERPOLATION_BICUBIC)\r
254 //                              g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,\r
255 //                                              RenderingHints.VALUE_INTERPOLATION_BILINEAR);\r
256 //                              g.translate(bounds.getMinX(), bounds.getMinY());\r
257 //                              g.scale(1/resolution, 1/resolution);\r
258 //                              g.translate(-widMargin, -heiMargin);\r
259 //                              g.drawImage(image, 0, 0, null);\r
260 //                      } finally {\r
261 //                              g.setTransform(af);\r
262 //                              g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, rh);\r
263 //                      }\r
264 //        }\r
265 \r
266         @Override\r
267         public int compareTo(Raster other) {\r
268             if (other.resolution<resolution)\r
269                 return -1;\r
270             if (other.resolution>resolution)\r
271                 return 1;\r
272             return 0;\r
273         }\r
274     }\r
275     /*\r
276         @Override\r
277         public int hashCode() {\r
278                 return gc.hashCode() ^original.hashCode();\r
279         }\r
280 \r
281         @Override\r
282         public boolean equals(Object obj) {\r
283                 if (!(obj instanceof VRamBufferedImage)) return false;\r
284                 VRamBufferedImage o = (VRamBufferedImage) obj;\r
285                 return o.gc == gc && o.original.equals(original);\r
286         }\r
287      */\r
288     @Override\r
289     public EnumSet<Feature> getFeatures() {\r
290         return caps;\r
291     }\r
292 \r
293     public Image getSource() {\r
294         return source;\r
295     }\r
296 \r
297     public GraphicsConfiguration getGraphicsConfiguration() {\r
298         return gc;\r
299     }\r
300 \r
301 }\r
302 \r