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