X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;ds=sidebyside;f=bundles%2Forg.simantics.scenegraph%2Fsrc%2Forg%2Fsimantics%2Fscenegraph%2Futils%2FMipMapBufferedImage.java;fp=bundles%2Forg.simantics.scenegraph%2Fsrc%2Forg%2Fsimantics%2Fscenegraph%2Futils%2FMipMapBufferedImage.java;h=10a8decb39bb7b63ca86d776251ce78f7917fd17;hb=969bd23cab98a79ca9101af33334000879fb60c5;hp=0000000000000000000000000000000000000000;hpb=866dba5cd5a3929bbeae85991796acb212338a08;p=simantics%2Fplatform.git diff --git a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/utils/MipMapBufferedImage.java b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/utils/MipMapBufferedImage.java new file mode 100644 index 000000000..10a8decb3 --- /dev/null +++ b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/utils/MipMapBufferedImage.java @@ -0,0 +1,349 @@ +/******************************************************************************* + * Copyright (c) 2007, 2010 Association for Decentralized Information Management + * in Industry THTH ry. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * VTT Technical Research Centre of Finland - initial API and implementation + *******************************************************************************/ +package org.simantics.scenegraph.utils; + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Point; +import java.awt.RenderingHints; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.kitfox.svg.SVGDiagram; + +/** + * Video-ram cache suitable for rasterized PaintableSymbols scalable vector graphics. + *

+ * This implementation rasterizes the same symbol from different mip map levels. + * + * @see VRamImage + * @author Toni Kalajainen + */ +public class MipMapBufferedImage extends BufferedImage { + + /** Extra margin to the bounds reported by batik */ + public static final double MARGIN_PERCENT = 3; + + // Was 800 in VRam.. ? + public static final double MAX_DIMENSION = 600; + public static final double MIN_DIMENSION = 4; + + //Shape outline; + + Map rasters = new HashMap(); + double[] resolutions; + double minResolution, maxResolution; + + /** + * @param original + * @param imageBounds + * @param referenceSize + * + * FIXME: shouldn't be SVG dependent + */ + public MipMapBufferedImage(SVGDiagram original, Rectangle2D imageBounds, Point referenceSize) { + super(original, imageBounds, referenceSize); + initializeRasters(); + } + + private void initializeRasters() { + List resolutions = new ArrayList(); + + if (referenceSize != null && !imageBounds.isEmpty()) { + // Init rasters - they are built on-demand + double maxResolution = maxResolution(); + double minResolution = minResolution(); + double fitResolution = fitResolution(referenceSize); + double resolution = fitResolution; + while (true) { + double next = resolution * 2; + if (next > maxResolution) + break; + resolution = next; + } + while (resolution > minResolution) { + IRaster r = createRaster(resolution); + rasters.put(resolution, r); + resolutions.add(resolution); + resolution /= 2; + } + } else { + // Init rasters - they are built on-demand + double maxResolution = maxResolution(); + double minResolution = minResolution(); + double resolution = maxResolution; + while (resolution > minResolution) { + IRaster r = createRaster(resolution); + rasters.put(resolution, r); + resolutions.add(resolution); + resolution /= 2; + } + } + + // arraylist -> array + this.resolutions = new double[resolutions.size()]; + for (int i=0; i=0) return resolutions[index]; + index = -(index+1); + + if (index>=resolutions.length) index = resolutions.length-1; + if (index<0) index = 0; + return resolutions[index]; + } + + @Override + public void paint(Graphics2D g) { + // Quality rendering requested, do not render from cache + //QualityHints.HIGH_QUALITY_HINTS.setQuality(g); + if (g.getRenderingHint(RenderingHints.KEY_RENDERING) == RenderingHints.VALUE_RENDER_QUALITY) + { + try { + source.render(g); + } catch (Exception e) { + // NOTE: Catching Exception instead of SVGException due to an + // NPE when encountering invalid color SVG definitions (e.g. + // rgb(256,-1,0)) + e.printStackTrace(); + } + return; + } + + double requiredResolution = requiredResolution(g.getTransform()); + // This scale makes the mipmapped painting use a mipmap that is smaller + // than the requested image pixel size in cases where the required + // resolution only slightly exceeds the size of an available mipmap. + requiredResolution *= 0.95; + //System.out.println("required resolution: " + requiredResolution); + + if (requiredResolution > getRasterRenderingThresholdResolution()) { + Graphics2D g2d = (Graphics2D) g.create(); + setupSourceRender(g2d); + try { + source.render(g2d); + } catch (Exception e) { + // NOTE: Catching Exception instead of SVGException due to an + // NPE when encountering invalid color SVG definitions (e.g. + // rgb(256,-1,0)) + e.printStackTrace(); + } finally { + g2d.dispose(); + } + } else { + Object origInterpolationHint = g.getRenderingHint(RenderingHints.KEY_INTERPOLATION); + if (origInterpolationHint==null) + origInterpolationHint = RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR; + + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); + + double closestResolution = findClosestResolution(requiredResolution); + //System.out.println(" resolutions: " + Arrays.toString(resolutions)); + //System.out.println(" closest resolution: " + closestResolution); + IRaster raster = rasters.get(closestResolution); + try { + raster.paint( g ); + } finally { + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, origInterpolationHint); + } + } + } + + protected void setupSourceRender(Graphics2D g2d) { + g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED); + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); + g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); + g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); + g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED); + g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE); + } + + protected double getRasterRenderingThresholdResolution() { + return maxResolution; + } + + @Override + public synchronized void releaseRaster() { + for (IRaster r : rasters.values()) + r.release(); + } + + static interface IRaster extends Comparable { + double getResolution(); + void paint(Graphics2D g); + void release(); + } + + static abstract class Raster implements IRaster { + protected final double resolution; + + public Raster(double resolution) { + this.resolution = resolution; + } + + public double getResolution() { + return resolution; + } + + public int compareTo(IRaster o) { + double r = getResolution(); + double or = o.getResolution(); + if (or < r) + return -1; + if (or > r) + return 1; + return 0; + } + } + + class BufferedRaster extends Raster { + java.awt.image.BufferedImage image; + //int widMargin, heiMargin; + int wid, hei; + + BufferedRaster(double resolution) { + super(resolution); + double wid = imageBounds.getWidth(); + double hei = imageBounds.getHeight(); + this.wid = (int) (wid * resolution); + this.hei = (int) (hei * resolution); +// widMargin = (int) (wid * resolution * (MARGIN_PERCENT/100)) +1; +// heiMargin = (int) (hei * resolution * (MARGIN_PERCENT/100)) +1; + } + + synchronized java.awt.image.BufferedImage getOrCreate() + { + if (image!=null) return image; + image = new java.awt.image.BufferedImage( + (wid+0*2+1), + (hei+0*2+1), + java.awt.image.BufferedImage.TYPE_INT_ARGB); + + Graphics2D target = image.createGraphics(); + target.setBackground(new Color(255,255,255,0)); + target.clearRect(0, 0, image.getWidth(), image.getHeight()); + + target.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + target.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + target.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + target.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); + +// target.translate(widMargin, heiMargin); + target.scale(resolution, resolution); + target.translate(-imageBounds.getMinX(), -imageBounds.getMinY()); + try { + source.render(target); + } catch (Exception e) { + // TODO Auto-generated catch block + // NOTE: Catching Exception instead of SVGException due to an + // NPE when encountering invalid color SVG definitions (e.g. + // rgb(256,-1,0)) + e.printStackTrace(); + } +// source.paint( +// new GraphicsContextImpl(target, new Rectangle2D.Double(0,0, image.getWidth(), image.getHeight()), null) +// ); + target.dispose(); + + return image; + } + + public void paint(Graphics2D g) { + java.awt.image.BufferedImage image = getOrCreate(); + if (image==null) + { + try { + source.render(g); + } catch (Exception e) { + // TODO Auto-generated catch block + // NOTE: Catching Exception instead of SVGException due to an + // NPE when encountering invalid color SVG definitions (e.g. + // rgb(256,-1,0)) + e.printStackTrace(); + } + return; + } + AffineTransform af = g.getTransform(); + Object rh = g.getRenderingHint(RenderingHints.KEY_INTERPOLATION); + try { + /// Bicubic interpolation is very slow with opengl pipeline + if (rh == RenderingHints.VALUE_INTERPOLATION_BICUBIC) + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, + RenderingHints.VALUE_INTERPOLATION_BILINEAR); + g.translate(imageBounds.getMinX(), imageBounds.getMinY()); + g.scale(1/resolution, 1/resolution); +// g.translate(-widMargin, -heiMargin); + g.drawImage(image, 0, 0, null); + } finally { + g.setTransform(af); + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, rh); + } + } + + public void release() { + image = null; + } + } + +}