/*******************************************************************************
* 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.g2d.image.impl;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.Shape;
import java.awt.Transparency;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.awt.image.VolatileImage;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.simantics.g2d.image.Image;
import org.simantics.scenegraph.Node;
import org.simantics.scenegraph.g2d.G2DParentNode;
import org.simantics.scenegraph.utils.QualityHints;
/**
* 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 MipMapVRamBufferedImage extends ImageProxy implements Image {
/** Extra margin to the bounds reported by batik*/
public static final double MARGIN_PERCENT = 3;
public static final double MAX_DIMENSION = 800;
public static final double MIN_DIMENSION = 4;
final GraphicsConfiguration gc;
Shape outline;
double [] resolutions;
Map rasters = new HashMap();
double minResolution, maxResolution;
EnumSet caps;
public MipMapVRamBufferedImage(Image original, GraphicsConfiguration gc)
{
super(original);
if(gc==null)
throw new IllegalArgumentException("Argument is null.");
this.source = original;
this.gc = gc;
// Init rasters - they are built on-demand
double maxResolution = maxResolution();
double minResolution = minResolution();
double resolution = maxResolution;
List resolutions = new ArrayList();
while (resolution>minResolution)
{
Raster r = new Raster(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 Node init(G2DParentNode parent) {
return null;
// Graphics2D g = gc.getGraphics2D();
// // Quality rendering requested, do not render from cache
// if (g.getRenderingHint(RenderingHints.KEY_RENDERING) == RenderingHints.VALUE_RENDER_QUALITY)
// {
// source.paint(gc);
// return;
// }
//
// double requiredResolution = requiredResolution(g.getTransform());
// if (requiredResolution > maxResolution)
// {
// g = gc.createClone();
// g.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED);
// g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
// g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
// g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
// g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
// g.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);
// gc = new GraphicsContextImpl(g, gc.getBounds(), gc.getNode());
// source.paint(gc);
// return;
// }
//
// double closestResolution = findClosestResolution(requiredResolution);
//
// Raster raster = rasters.get(closestResolution);
//
// 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);
// try {
// raster.paint( gc );
// } finally {
// g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, origInterpolationHint);
// }
}
public Image getOriginalPaintableSymbol()
{
return source;
}
synchronized void releaseRaster() {
for (Raster r : rasters.values())
r.image = null;
}
@Override
protected void notifyChanged() {
releaseRaster();
super.notifyChanged();
}
class Raster implements Comparable {
VolatileImage image;
double resolution;
int widMargin, heiMargin;
int wid, hei;
Raster(double resolution) {
Rectangle2D bounds = source.getBounds();
this.resolution = resolution;
double wid = bounds.getWidth();
double hei = bounds.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 VolatileImage restore()
{
Rectangle2D bounds = source.getBounds();
if (image==null) {
image = gc.createCompatibleVolatileImage(
wid+widMargin*2,
hei+heiMargin*2,
Transparency.TRANSLUCENT);
}
int validateResult = image.validate(gc);
if (validateResult == VolatileImage.IMAGE_INCOMPATIBLE)
return null;
if (validateResult == VolatileImage.IMAGE_OK)
return image;
if (validateResult == VolatileImage.IMAGE_RESTORED /*raster.contentsLost()*/) {
Graphics2D target = image.createGraphics();
target.setBackground(new Color(255,255,255,0));
target.clearRect(0, 0, image.getWidth(), image.getHeight());
QualityHints.HIGH_QUALITY_HINTS.setQuality(target);
target.translate(widMargin, heiMargin);
target.scale(resolution, resolution);
target.translate(-bounds.getMinX(), -bounds.getMinY());
source.init(null);
// new GraphicsContextImpl(new Rectangle2D.Double(0,0, image.getWidth(), image.getHeight()), null)
// );
target.dispose();
return image;
}
return null;
}
// public void paint(GraphicsContext gc) {
// Rectangle2D bounds = source.getBounds();
// VolatileImage image = restore();
// if (image==null)
// {
// QualityHints.LOW_QUALITY_HINTS.setQuality(gc.getGraphics2D());
// source.paint(gc);
// return;
// }
// Graphics2D g = gc.getGraphics2D();
// 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(bounds.getMinX(), bounds.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);
// }
// }
@Override
public int compareTo(Raster other) {
if (other.resolutionresolution)
return 1;
return 0;
}
}
/*
@Override
public int hashCode() {
return gc.hashCode() ^original.hashCode();
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof VRamBufferedImage)) return false;
VRamBufferedImage o = (VRamBufferedImage) obj;
return o.gc == gc && o.original.equals(original);
}
*/
@Override
public EnumSet getFeatures() {
return caps;
}
public Image getSource() {
return source;
}
public GraphicsConfiguration getGraphicsConfiguration() {
return gc;
}
}