X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;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=d76b9ec05a1063c437dd1b2c26a1edd9ebb221db;hb=0ae2b770234dfc3cbb18bd38f324125cf0faca07;hp=10a8decb39bb7b63ca86d776251ce78f7917fd17;hpb=24e2b34260f219f0d1644ca7a138894980e25b14;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 index 10a8decb3..d76b9ec05 100644 --- a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/utils/MipMapBufferedImage.java +++ b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/utils/MipMapBufferedImage.java @@ -1,349 +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; - } - } - -} +/******************************************************************************* + * 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; + } + } + +}