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