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