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
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.g2d.image.impl;
\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
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
32 * Video-ram cache suitable for rasterized PaintableSymbols scalable vector graphics.
\r
34 * This implementation rasterizes the same symbol from different mip map levels.
\r
36 * @see VRamBufferedImage
\r
37 * @author Toni Kalajainen
\r
39 public class MipMapBufferedImage extends ImageProxy implements Image {
\r
41 /** Extra margin to the bounds reported by batik*/
\r
42 public static final double MARGIN_PERCENT = 3;
\r
44 public static final double MAX_DIMENSION = 800;
\r
45 public static final double MIN_DIMENSION = 4;
\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
54 public MipMapBufferedImage(Image original)
\r
57 this.source = original;
\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
66 Raster r = new Raster(resolution);
\r
67 rasters.put(resolution, r);
\r
68 resolutions.add(resolution);
\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
79 boolean vola = source.getFeatures().contains(Feature.Volatile);
\r
81 source.addImageListener(new ImageListener() {
\r
83 public void onContentChangedNotification(Image image) {
\r
86 this.caps = EnumSet.of(Feature.Volatile);
\r
88 this.caps = EnumSet.noneOf(Feature.class);
\r
93 private double maxResolution()
\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
101 private double minResolution()
\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
109 private double requiredResolution(AffineTransform at)
\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
121 private double findClosestResolution(double resolution)
\r
123 int index = Arrays.binarySearch(resolutions, resolution);
\r
124 if (index>=0) return resolutions[index];
\r
125 index = -(index+1);
\r
127 if (index>=resolutions.length) index = resolutions.length-1;
\r
128 if (index<0) index = 0;
\r
129 return resolutions[index];
\r
133 public Node init(G2DParentNode parent) {
\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
140 // source.paint(gc);
\r
144 // double requiredResolution = requiredResolution(g.getTransform());
\r
145 // if (requiredResolution > maxResolution)
\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
159 // double closestResolution = findClosestResolution(requiredResolution);
\r
161 // Raster raster = rasters.get(closestResolution);
\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
168 // raster.paint( gc );
\r
170 // g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, origInterpolationHint);
\r
174 public Image getOriginalPaintableSymbol()
\r
179 synchronized void releaseRaster() {
\r
180 for (Raster r : rasters.values())
\r
186 protected void notifyChanged() {
\r
188 super.notifyChanged();
\r
191 class Raster implements Comparable<Raster> {
\r
192 java.awt.image.BufferedImage image;
\r
194 // int widMargin, heiMargin;
\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
208 synchronized java.awt.image.BufferedImage getOrCreate()
\r
210 if (image!=null) return image;
\r
211 Rectangle2D bounds = source.getBounds();
\r
212 image = new java.awt.image.BufferedImage(
\r
215 java.awt.image.BufferedImage.TYPE_INT_ARGB);
\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
226 // new GraphicsContextImpl(new Rectangle2D.Double(0,0, image.getWidth(), image.getHeight()), null)
\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
238 // source.paint(gc);
\r
241 // Graphics2D g = gc.getGraphics2D();
\r
242 // AffineTransform af = g.getTransform();
\r
243 // Object rh = g.getRenderingHint(RenderingHints.KEY_INTERPOLATION);
\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
254 // g.setTransform(af);
\r
255 // g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, rh);
\r
260 public int compareTo(Raster other) {
\r
261 if (other.resolution<resolution)
\r
263 if (other.resolution>resolution)
\r
270 public EnumSet<Feature> getFeatures() {
\r
274 public Image getSource() {
\r