/*****************************************************************************
* Copyright (C) The Apache Software Foundation. All rights reserved. *
* ------------------------------------------------------------------------- *
* This software is published under the terms of the Apache Software License *
* version 1.1, a copy of which has been included with this distribution in *
* the LICENSE file. *
*****************************************************************************/
package org.apache.batik.gvt.filter;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Rectangle2D;
import java.awt.image.RenderedImage;
import java.awt.image.BufferedImage;
import java.awt.image.renderable.RenderContext;
import java.awt.image.renderable.RenderableImage;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import org.apache.batik.gvt.GraphicsNode;
import org.apache.batik.gvt.CompositeGraphicsNode;
import org.apache.batik.gvt.GraphicsNodeRenderContext;
import org.apache.batik.ext.awt.image.renderable.Filter;
import org.apache.batik.gvt.filter.GraphicsNodeRable;
import org.apache.batik.gvt.filter.GraphicsNodeRableFactory;
import org.apache.batik.ext.awt.image.CompositeRule;
import org.apache.batik.ext.awt.image.PadMode;
import org.apache.batik.ext.awt.image.renderable.AbstractRable;
import org.apache.batik.ext.awt.image.renderable.CompositeRable8Bit;
import org.apache.batik.ext.awt.image.renderable.AffineRable8Bit;
/**
* This implementation of RenderableImage will render its input
* GraphicsNode into a BufferedImage upon invokation of one of its
* createRendering methods.
*
* @author <a href="mailto:vincent.hardy@eng.sun.com">Vincent Hardy</a>
* @version $Id: BackgroundRable8Bit.java,v 1.6 2001/03/26 21:27:36 deweese Exp $
*/
public class BackgroundRable8Bit
extends AbstractRable {
/**
* GraphicsNode this image can render
*/
private GraphicsNode node;
/**
* GraphicsNodeRenderContext currently associated with above GraphicsNode.
*/
private GraphicsNodeRenderContext gnrc;
/**
* Returns the <tt>GraphicsNode</tt> rendered by this image
*/
public GraphicsNode getGraphicsNode(){
return node;
}
/**
* Sets the <tt>GraphicsNode</tt> this image should render
*/
public void setGraphicsNode(GraphicsNode node){
if(node == null){
throw new IllegalArgumentException();
}
this.node = node;
}
/**
* @param node The GraphicsNode this image should represent
*/
public BackgroundRable8Bit(GraphicsNode node, GraphicsNodeRenderContext rc){
if(node == null)
throw new IllegalArgumentException();
this.gnrc = rc;
this.node = node;
}
// This is a utilitiy method that unions the bounds of
// cgn upto child (if child is null it does all children).
// It unions them with init if provided.
static Rectangle2D addBounds(CompositeGraphicsNode cgn,
GraphicsNode child,
GraphicsNodeRenderContext rc,
Rectangle2D init) {
// System.out.println("CGN: " + cgn);
// System.out.println("Child: " + child);
List children = cgn.getChildren();
Iterator i = children.iterator();
Rectangle2D r2d = null;
while (i.hasNext()) {
GraphicsNode gn = (GraphicsNode)i.next();
if (gn == child)
break;
// System.out.println("GN: " + gn);
Rectangle2D cr2d = gn.getBounds(rc);
AffineTransform at = gn.getTransform();
if (at != null)
cr2d = at.createTransformedShape(cr2d).getBounds2D();
if (r2d == null) r2d = (Rectangle2D)cr2d.clone();
r2d.add(cr2d);
}
if (r2d == null) {
if (init == null)
return CompositeGraphicsNode.VIEWPORT;
return init;
}
if (init == null)
return r2d;
init.add(r2d);
return init;
}
static Rectangle2D getViewportBounds(GraphicsNode gn,
GraphicsNode child,
GraphicsNodeRenderContext rc) {
// See if background is enabled.
Rectangle2D r2d = null;
if (gn instanceof CompositeGraphicsNode) {
CompositeGraphicsNode cgn = (CompositeGraphicsNode)gn;
r2d = cgn.getBackgroundEnable();
}
if (r2d == null)
// No background enable so check our parent's value.
r2d = getViewportBounds(gn.getParent(), gn, rc);
// No background for any ancester (error) return null
if (r2d == null)
return null;
// Background enabled is set, but it has no fixed bounds set.
if (r2d == CompositeGraphicsNode.VIEWPORT) {
// If we don't have a child then just use our bounds.
if (child == null)
return gn.getPrimitiveBounds(rc);
// gn must be composite so add all it's children's bounds
// up to child.
CompositeGraphicsNode cgn = (CompositeGraphicsNode)gn;
return addBounds(cgn, child, rc, null);
}
// We have a partial bound from parent, so map it to gn's
// coordinate system...
AffineTransform at = gn.getTransform();
if (at != null) {
try {
at = at.createInverse();
r2d = at.createTransformedShape(r2d).getBounds2D();
} catch (NoninvertibleTransformException nte) {
// Degenerate case return null;
r2d = null;
}
}
if (child != null) {
// Add our childrens bounds to it...
CompositeGraphicsNode cgn = (CompositeGraphicsNode)gn;
r2d = addBounds(cgn, child, rc, r2d);
} else
r2d.add(gn.getPrimitiveBounds(rc));
return r2d;
}
// This does the leg work for getBounds().
// It traverses the tree figuring out the bounds of the
// background image.
static Rectangle2D getBoundsRecursive(GraphicsNode gn,
GraphicsNode child) {
Rectangle2D r2d = null;
if (gn == null)
return null;
if (gn instanceof CompositeGraphicsNode) {
CompositeGraphicsNode cgn = (CompositeGraphicsNode)gn;
// See if background is enabled.
r2d = cgn.getBackgroundEnable();
}
// background has definite bounds so return them.
if (r2d != null)
return r2d;
// No background enable so check our parent's value.
r2d = getBoundsRecursive(gn.getParent(), gn);
// No background for any ancester (error) return null
if (r2d == null)
return null;
// Our parent has background but no bounds (and we must
// have been the first child so build the new bounds...
if (r2d == CompositeGraphicsNode.VIEWPORT)
return r2d;
AffineTransform at = gn.getTransform();
if (at != null) {
try {
// background has a definite bound so map it to gn's
// coordinate system...
at = at.createInverse();
r2d = at.createTransformedShape(r2d).getBounds2D();
} catch (NoninvertibleTransformException nte) {
// Degenerate case return null;
r2d = null;
}
}
return r2d;
}
/**
* Returns the bounds of this Rable in the user coordinate system.
*/
public Rectangle2D getBounds2D() {
// System.out.println("GetBounds2D called");
Rectangle2D r2d = getBoundsRecursive(node, null);
if (r2d == CompositeGraphicsNode.VIEWPORT)
r2d = getViewportBounds(node, null, getGraphicsNodeRenderContext());
return r2d;
}
/**
* Returns a filter that represents the background image
* for <tt>child</tt>.
*/
public Filter getBackground(GraphicsNode gn,
GraphicsNode child,
GraphicsNodeRenderContext rc,
Rectangle2D aoi) {
if (gn == null) {
throw new IllegalArgumentException
("BackgroundImage requested yet no parent has " +
"'enable-background:new'");
}
Rectangle2D r2d = null;
if (gn instanceof CompositeGraphicsNode) {
CompositeGraphicsNode cgn = (CompositeGraphicsNode)gn;
r2d = cgn.getBackgroundEnable();
}
Vector srcs = new Vector();
if (r2d == null) {
Rectangle2D paoi = aoi;
AffineTransform at = gn.getTransform();
if (at != null)
paoi = at.createTransformedShape(aoi).getBounds2D();
Filter f = getBackground(gn.getParent(), gn, rc, paoi);
if ((f != null) && f.getBounds2D().intersects(aoi)) {
srcs.add(f);
}
}
GraphicsNodeRableFactory gnrf;
gnrf = rc.getGraphicsNodeRableFactory();
if (child != null) {
CompositeGraphicsNode cgn = (CompositeGraphicsNode)gn;
List children = cgn.getChildren();
Iterator i = children.iterator();
while (i.hasNext()) {
GraphicsNode childGN = (GraphicsNode)i.next();
// System.out.println("Parent: " + cgn +
// "\n Child: " + child +
// "\n ChildGN: " + childGN);
if (childGN == child)
break;
Rectangle2D cbounds = childGN.getBounds(rc);
// System.out.println("Child : " + childGN);
// System.out.println("Bounds: " + cbounds);
// System.out.println(" : " + aoi);
AffineTransform at = childGN.getTransform();
if (at != null)
cbounds = at.createTransformedShape(cbounds).getBounds2D();
if (aoi.intersects(cbounds)) {
GraphicsNodeRable gnr;
gnr = gnrf.createGraphicsNodeRable(childGN, rc);
gnr.setUsePrimitivePaint(false);
srcs.add(gnr);
}
}
}
if (srcs.size() == 0)
return null;
Filter ret = null;
if (srcs.size() == 1)
ret = (Filter)srcs.get(0);
else
ret = new CompositeRable8Bit(srcs, CompositeRule.OVER, false);
if (child != null) {
// We are returning the filter to child so make
// sure to map the filter from the parents user space
// to the childs user space...
AffineTransform at = child.getTransform();
if (at != null) {
try {
at = at.createInverse();
ret = new AffineRable8Bit(ret, at);
} catch (NoninvertibleTransformException nte) {
ret = null;
}
}
}
return ret;
}
/**
* Returns true if successive renderings (that is, calls to
* createRendering() or createScaledRendering()) with the same arguments
* may produce different results. This method may be used to
* determine whether an existing rendering may be cached and
* reused. It is always safe to return true.
*/
public boolean isDynamic(){
return false;
}
/**
* Creates a RenderedImage that represented a rendering of this image
* using a given RenderContext. This is the most general way to obtain a
* rendering of a RenderableImage.
*
* <p> The created RenderedImage may have a property identified
* by the String HINTS_OBSERVED to indicate which RenderingHints
* (from the RenderContext) were used to create the image.
* In addition any RenderedImages
* that are obtained via the getSources() method on the created
* RenderedImage may have such a property.
*
* @param renderContext the RenderContext to use to produce the rendering.
* @return a RenderedImage containing the rendered data.
*/
public RenderedImage createRendering(RenderContext renderContext){
GraphicsNodeRenderContext gnrc;
gnrc = GraphicsNodeRenderContext.
getGraphicsNodeRenderContext(renderContext);
Rectangle2D r2d = getBounds2D();
// System.out.println("Rendering called");
Shape aoi = renderContext.getAreaOfInterest();
if (aoi != null) {
Rectangle2D aoiR2d = aoi.getBounds2D();
if (r2d.intersects(aoiR2d) == false)
return null;
// System.out.println("R2d: " + r2d);
// System.out.println("AOI: " + aoiR2d);
Rectangle2D.intersect(r2d, aoiR2d, r2d);
}
Filter f = getBackground(node, null, gnrc, r2d);
if ( f == null)
return null;
RenderedImage ri = f.createRendering(renderContext);
// org.ImageDisplay.showImage("BG: ", ri);
return ri;
}
protected void setGraphicsNodeRenderContext(GraphicsNodeRenderContext rc) {
this.gnrc = rc;
}
protected GraphicsNodeRenderContext getGraphicsNodeRenderContext() {
return gnrc;
}
}