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