]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/utils/MipMapVRamBufferedImage.java
Render elements using custom color filters
[simantics/platform.git] / bundles / org.simantics.scenegraph / src / org / simantics / scenegraph / utils / 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.scenegraph.utils;
13
14 import java.awt.Color;
15 import java.awt.Graphics2D;
16 import java.awt.GraphicsConfiguration;
17 import java.awt.Point;
18 import java.awt.RenderingHints;
19 import java.awt.geom.AffineTransform;
20 import java.awt.geom.Rectangle2D;
21 import java.awt.image.VolatileImage;
22 import java.util.Objects;
23 import java.util.concurrent.atomic.AtomicInteger;
24
25 import org.simantics.scenegraph.g2d.G2DRenderingHints;
26 import org.simantics.scenegraph.g2d.color.ColorFilter;
27 import org.simantics.scenegraph.g2d.color.Graphics2DWithColorFilter;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
30
31 import com.kitfox.svg.SVGDiagram;
32
33 /**
34  * Video-ram cache suitable for rasterized PaintableSymbols scalable vector graphics.
35  * <p>
36  * This implementation rasterizes the same symbol from different mip map levels.
37  * 
38  * @see VRamImage
39  * @author Toni Kalajainen
40  */
41 public class MipMapVRamBufferedImage extends MipMapBufferedImage {
42
43     private static final Logger LOGGER = LoggerFactory.getLogger(MipMapVRamBufferedImage.class);
44     /**
45      * @param original
46      * @param imageBounds
47      * @param referenceSize a reference size for the rasterized images or
48      *        <code>null</code> to not specify one in which case a default
49      *        resolution is used
50      * @param gc
51      */
52     public MipMapVRamBufferedImage(SVGDiagram original, Rectangle2D imageBounds, Point referenceSize)
53     {
54         super(original, imageBounds, referenceSize);
55     }
56
57     @Override
58     protected IRaster createRaster(double resolution) {
59         return new VolatileRaster(resolution);
60     }
61
62     @Override
63     protected double getRasterRenderingThresholdResolution() {
64         return maxResolution*1.5;
65     }
66
67     @Override
68     protected void setupSourceRender(Graphics2D g2d) {
69 //        g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED);
70 //        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
71 //        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
72 //        g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
73 //        g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
74 //        g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);
75
76         g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED);
77         g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
78         g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
79         g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
80         g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
81         g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);
82     }
83
84     class VolatileRaster extends Raster {
85         VolatileImageProvider imageProvider;
86         int widMargin, heiMargin;
87         int wid, hei;
88         AtomicInteger validateResult = new AtomicInteger(VolatileImage.IMAGE_OK);
89
90         public VolatileRaster(double resolution) {
91             super(resolution);
92             double wid = imageBounds.getWidth();
93             double hei = imageBounds.getHeight();
94             this.wid = (int) (wid * resolution);
95             this.hei = (int) (hei * resolution);
96             widMargin = (int) (wid * resolution * (MARGIN_PERCENT/100)) +1;
97             heiMargin = (int) (hei * resolution * (MARGIN_PERCENT/100)) +1;
98             imageProvider = VolatileImageCache.getInstance().create(this.wid+widMargin*2, this.hei+heiMargin*2);
99             //System.out.println("VolatileRaster(" + resolution + "): " + this.wid + " x " + this.hei);
100         }
101
102         /**
103          * @param image
104          * @return
105          */
106         private VolatileImage sourceRender(VolatileImage image, ColorFilter colorFilter) {
107             Graphics2D target = image.createGraphics();
108             target.setBackground(new Color(255,255,255,0));
109             target.clearRect(0, 0, image.getWidth(), image.getHeight());
110
111             target.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
112             target.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
113             target.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
114             target.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
115
116             target.translate(widMargin, heiMargin);
117             target.scale(resolution, resolution);
118             target.translate(-imageBounds.getMinX(), -imageBounds.getMinY());
119             try {
120                 if (colorFilter != null) {
121                     source.render(new Graphics2DWithColorFilter(target, colorFilter));
122                 } else {
123                     source.render(target);
124                 }
125             } catch (Exception e) {
126                 e.printStackTrace();
127                 target.dispose();
128                 image = null;
129                 return null;
130             }
131             target.dispose();
132             //JAI.create("filestore", image.getSnapshot(), "d:/test" + (koss++) + ".png", "png");
133             return image;
134         }
135
136         synchronized VolatileImage restore(GraphicsConfiguration gc2, ColorFilter colorFilter) {
137             //System.out.println("restoring provider " + imageProvider);
138             VolatileImage image = imageProvider.get(gc2, this.validateResult);
139
140             // If a new image was created, we always need to render from source.
141             int validateResult = this.validateResult.get();
142             //System.out.println("got VolatileImage " + image + " (validation result=" + validated + ")");
143
144             // Couldn't get image? This is not supposed to happen but happened anyway.
145             if (image == null) {
146                 LOGGER.error("BUG: VolatileImageProvider.get returned null!");
147                 return null;
148             }
149
150 //            if (validateResult != VolatileImage.IMAGE_OK) {
151 //                System.out.println("NEED SOURCE RENDER FOR VOLATILE IMAGE PROVIDER " + imageProvider + " - " + image);
152 //            }
153
154             boolean contentsLost = validateResult != VolatileImage.IMAGE_OK || image.contentsLost();
155             boolean changed = !Objects.equals(colorFilter, previousColorFilter);
156             previousColorFilter = colorFilter;
157             return contentsLost || changed ? sourceRender(image, colorFilter) : image;
158             //return contentsLost ? sourceRender(image) : sourceRender(image);
159         }
160
161         public void paint(Graphics2D g) {
162             ColorFilter colorFilter = (ColorFilter) g.getRenderingHint(G2DRenderingHints.KEY_COLOR_FILTER);
163             VolatileImage image = restore(g.getDeviceConfiguration(), colorFilter);
164             if (image==null)
165             {
166                 g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
167                 g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
168                 g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
169                 g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
170
171                 try {
172                     source.render(new Graphics2DWithColorFilter(g, colorFilter));
173                 } catch (Exception e) {
174                     // TODO Auto-generated catch block
175                     // NOTE: Catching Exception instead of SVGException due to an
176                     // NPE when encountering invalid color SVG definitions (e.g.
177                     // rgb(256,-1,0))
178                     e.printStackTrace();
179                 }
180                 return;
181             }
182             AffineTransform af = g.getTransform();
183             Object rh = g.getRenderingHint(RenderingHints.KEY_INTERPOLATION);
184             try {
185                 /// Bicubic interpolation is very slow with opengl pipeline
186                 if (rh == RenderingHints.VALUE_INTERPOLATION_BICUBIC)
187                     g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
188                 g.translate(imageBounds.getMinX(), imageBounds.getMinY());
189                 g.scale(1/resolution, 1/resolution);
190                 g.translate(-widMargin, -heiMargin);
191                 g.drawImage(image, 0, 0, null);
192             } finally {
193                 g.setTransform(af);
194                 g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, rh);
195             }
196         }
197
198         public void release() {
199             imageProvider.dispose();
200         }
201     }
202
203     //static long koss = 0;
204
205     /*
206     @Override
207     public int hashCode() {
208         return gc.hashCode() ^original.hashCode();
209     }
210
211     @Override
212     public boolean equals(Object obj) {
213         if (!(obj instanceof VRamBufferedImage)) return false;
214         VRamBufferedImage o = (VRamBufferedImage) obj;
215         return o.gc == gc && o.original.equals(original);
216     }
217      */
218
219 }