/******************************************************************************* * 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.AlphaComposite; import java.awt.Color; import java.awt.Graphics2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.awt.image.ConvolveOp; import java.awt.image.Kernel; import java.util.Arrays; import java.util.EnumSet; import org.simantics.g2d.image.Image; import org.simantics.scenegraph.Node; import org.simantics.scenegraph.g2d.G2DParentNode; /** * Reflects to a shadow of a symbol. * * @author Toni Kalajainen */ public class Shadow extends ImageProxy implements Image { public static final ShadowParameters SHADOW = new ShadowParameters(0.5, Color.BLACK, 2); public static final class ShadowParameters { public final double alpha; public final Color color; public final int size; public ShadowParameters(double alpha, Color color, int size) { this.alpha = alpha; this.color = color; this.size = size; } } public final ShadowParameters shadow; Point2D size; ConvolveOp horiz, vert; int shadowSizeX, shadowSizeY; EnumSet feats; public ImageListener origListener; /** * * @param source * @param shadow * @param shadowOnly */ public Shadow(Image source, ShadowParameters shadow) { super(source); this.shadow = shadow; shadowSizeX = shadow.size; shadowSizeY = shadow.size; horiz = createBlurOp(shadow.size, 1); vert = createBlurOp(1, shadow.size); if (source.getFeatures().contains(Feature.Volatile)) { feats = EnumSet.of(Feature.Volatile); } else feats = EnumSet.noneOf(Feature.class); } public Shadow(Image source, ShadowParameters shadow, double width, double height) { this(source, shadow); this.size = new Point2D.Double(width, height); shadowSizeX = (int) Math.round( shadow.size * width / source.getBounds().getWidth() ); shadowSizeY = (int) Math.round( shadow.size * height / source.getBounds().getHeight() ); if (shadowSizeX<1) shadowSizeX = 1; if (shadowSizeY<1) shadowSizeY = 1; horiz = createBlurOp(shadowSizeX, 1); vert = createBlurOp(1, shadowSizeY); } @Override public Rectangle2D getBounds() { Rectangle2D rect = source.getBounds(); return new Rectangle2D.Double(rect.getX() - shadowSizeX, rect.getY() - shadowSizeY, rect.getWidth() + shadowSizeX*2, rect.getHeight() + shadowSizeY*2); } private BufferedImage createImage() { Rectangle2D origBounds = source.getBounds(); double width = size==null?origBounds.getWidth() :size.getX(); double height = size==null?origBounds.getHeight():size.getY(); BufferedImage subject = new BufferedImage( (int)Math.ceil( width + shadowSizeX * 2 ), (int)Math.ceil( height + shadowSizeY * 2 ), BufferedImage.TYPE_INT_ARGB); Graphics2D g = subject.createGraphics(); g.translate(shadowSizeX, shadowSizeY); if (size!=null) g.scale(size.getX()/origBounds.getWidth(), size.getY()/origBounds.getHeight()); g.translate(-origBounds.getMinX(), -origBounds.getMinY()); Rectangle2D bounds = new Rectangle2D.Double(0, 0, subject.getWidth(), subject.getHeight()); // GraphicsContextImpl gc = new GraphicsContextImpl(bounds, null); // try { // source.paint(gc); // } finally { // gc.dispose(); // } g.dispose(); return subject; } private BufferedImage createShadowMask(BufferedImage image) { BufferedImage mask = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB); Graphics2D g2d = mask.createGraphics(); g2d.drawImage(image, 0, 0, null); g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_IN, (float)shadow.alpha)); g2d.setColor(shadow.color); g2d.fillRect(0, 0, image.getWidth(), image.getHeight()); g2d.dispose(); return mask; } public BufferedImage createShadow() { BufferedImage i1 = createImage(); BufferedImage i2 = new BufferedImage(i1.getWidth(), i1.getHeight(), BufferedImage.TYPE_INT_ARGB); BufferedImage shadowMask = createShadowMask(i1); horiz.filter(shadowMask, i2); vert.filter(i2, shadowMask); return shadowMask; } private ConvolveOp createBlurOp(int width, int height) { float[] data = new float[width * height]; float value = 1.0f / (width * height); Arrays.fill(data, value); return new ConvolveOp(new Kernel(width, height, data)); } @Override public Node init(G2DParentNode parent) { return null; // Graphics2D g = gc.getGraphics2D(); // BufferedImage bi = createShadow(); // // Rectangle2D origBounds = source.getBounds(); // if (size!=null) { // g.translate(origBounds.getMinX(), origBounds.getMinY()); // g.scale(origBounds.getWidth()/size.getX(), origBounds.getHeight()/size.getY()); // g.translate(-shadowSizeX, -shadowSizeY); // g.drawImage(bi, 0, 0, null); // g.translate(shadowSizeX, shadowSizeY); // g.scale(size.getX()/origBounds.getWidth(), size.getY()/origBounds.getHeight()); // g.translate(-origBounds.getMinX(), -origBounds.getMinY()); // } else { // g.translate(-shadowSizeX, -shadowSizeY); // g.translate(origBounds.getMinX(), origBounds.getMinY()); // g.drawImage(bi, 0, 0, null); // g.translate(shadowSizeX, shadowSizeY); // g.translate(-origBounds.getMinX(), -origBounds.getMinY()); // } } @Override public EnumSet getFeatures() { return feats; } }