package com.android.droidgraph.scene; import java.util.Collections; import java.util.List; import javax.microedition.khronos.opengles.GL10; import com.android.droidgraph.geom.BoundingBox; import com.android.droidgraph.geom.Bounds; import com.android.droidgraph.geom.Transform3D; import com.android.droidgraph.util.PrintLogUtil; /** * This class is the common base class for a type of node that interposes * a single operation or attribute on the tree and then transfers control * directly to its descendants. * An {@link GraphGroup} node could be used here, but most cases only want * to interpose in a single location in the tree and becoming an * {@code SGGroup} just for that one purpose would be both much more * heavyweight and would imply a more extensive modification of the tree * (allowing for multiple branches) just to interpose a single element. * * @author Flar */ public abstract class SGFilter extends SGParent { PrintLogUtil log = new PrintLogUtil(); /** * Flag indicating that the filter implementation does not need * access to the source as a raster image. */ public static final int NONE = (0 << 0); /** * Flag indicating that the filter implementation needs access to * the source as a raster image (in the original, local coordinate * space of the child node). */ public static final int UNTRANSFORMED = (1 << 0); /** * Flag indicating that the filter implementation needs access to * the source as a raster image (in the transformed coordinate space * of the child node). */ public static final int TRANSFORMED = (1 << 1); /** * Flag indicating that the filter implementation needs access to * the source as a raster image in both untransformed and transformed * formats. * This is equivalent to {@code (UNTRANSFORMED | TRANSFORMED)}. */ public static final int BOTH = UNTRANSFORMED | TRANSFORMED; /** * Flag indicating that the filter implementation has already cached * the rendering of the source and can render it via renderFromCache(). */ public static final int CACHED = (1 << 2); private SGNode child; private List<SGNode> singletonList; /** Creates a new instance of SGFilter */ public SGFilter() { } public final List<SGNode> getChildren() { if (child == null) { return Collections.emptyList(); } else { if (singletonList == null) { singletonList = Collections.singletonList(child); } return singletonList; } } public final SGNode getChild() { return child; } public void setChild(SGNode child) { if (child == null) { throw new IllegalArgumentException("null child"); } if (child == this.child) { return; } SGParent oldParent = child.getParent(); if (oldParent != null) { oldParent.remove(child); } this.singletonList = null; this.child = child; child.setParent(this); } @Override public void remove(SGNode node) { if (node == child) { remove(); } } public void remove() { this.child.setParent(null); this.child = null; this.singletonList = null; } public void renderFromCache(GL10 gl) { } public boolean canSkipRendering() { return false; } public boolean canSkipChildren() { return false; } /** * Returns true if the bounds of this filter node are (potentially) * larger than the bounds of its child, false otherwise. * The default implementation of this method always returns false; * subclasses should override accordingly. * * @return whether the bounds of this node expand outside the child bounds */ public boolean canExpandBounds() { return false; } public int needsSourceContent() { return NONE; } public void setupRenderGraphics(GL10 gl) { } public void renderFinalImage(GL10 g, SGSourceContent srcContent) { } @Override public Bounds getBounds(Transform3D transform) { if (child == null) { // just an empty rectangle return new BoundingBox(); } else { return child.getBounds(transform); } } /** * Calculates the accumulated bounds object representing the * global bounds relative to the root of the tree. * Since most filter nodes have the same accumulated bounds as * their child, this implementation will simply report the * accumulated bounds of its child. * Subclasses may override this behavior if they do not conform * to the above assumption. */ @Override Bounds calculateAccumBounds() { if (child == null) { return new BoundingBox(); } else { return child.getTransformedBoundsRelativeToRoot(); } } }