001: /*
002:
003: Licensed to the Apache Software Foundation (ASF) under one or more
004: contributor license agreements. See the NOTICE file distributed with
005: this work for additional information regarding copyright ownership.
006: The ASF licenses this file to You under the Apache License, Version 2.0
007: (the "License"); you may not use this file except in compliance with
008: the License. You may obtain a copy of the License at
009:
010: http://www.apache.org/licenses/LICENSE-2.0
011:
012: Unless required by applicable law or agreed to in writing, software
013: distributed under the License is distributed on an "AS IS" BASIS,
014: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: See the License for the specific language governing permissions and
016: limitations under the License.
017:
018: */
019: package org.apache.batik.bridge;
020:
021: import java.awt.geom.Rectangle2D;
022: import java.net.MalformedURLException;
023: import java.net.URL;
024: import java.util.HashMap;
025: import java.util.Iterator;
026: import java.util.LinkedList;
027: import java.util.List;
028: import java.util.Map;
029:
030: import org.apache.batik.dom.svg.SVGOMDocument;
031: import org.apache.batik.dom.util.XLinkSupport;
032: import org.apache.batik.ext.awt.image.PadMode;
033: import org.apache.batik.ext.awt.image.renderable.Filter;
034: import org.apache.batik.ext.awt.image.renderable.FilterChainRable;
035: import org.apache.batik.ext.awt.image.renderable.FilterChainRable8Bit;
036: import org.apache.batik.ext.awt.image.renderable.PadRable8Bit;
037: import org.apache.batik.gvt.GraphicsNode;
038: import org.w3c.dom.Element;
039: import org.w3c.dom.Node;
040:
041: /**
042: * Bridge class for the <filter> element.
043: *
044: * @author <a href="mailto:tkormann@apache.org">Thierry Kormann</a>
045: * @version $Id: SVGFilterElementBridge.java 501922 2007-01-31 17:47:47Z dvholten $
046: */
047: public class SVGFilterElementBridge extends AnimatableGenericSVGBridge
048: implements FilterBridge, ErrorConstants {
049:
050: /**
051: * Constructs a new bridge for the <filter> element.
052: */
053: public SVGFilterElementBridge() {
054: }
055:
056: /**
057: * Returns 'filter'.
058: */
059: public String getLocalName() {
060: return SVG_FILTER_TAG;
061: }
062:
063: /**
064: * Creates a <tt>Filter</tt> according to the specified parameters.
065: *
066: * @param ctx the bridge context to use
067: * @param filterElement the element that defines the filter
068: * @param filteredElement the element that references the filter element
069: * @param filteredNode the graphics node to filter
070: */
071: public Filter createFilter(BridgeContext ctx,
072: Element filterElement, Element filteredElement,
073: GraphicsNode filteredNode) {
074:
075: // get filter chain region
076: Rectangle2D filterRegion = SVGUtilities
077: .convertFilterChainRegion(filterElement,
078: filteredElement, filteredNode, ctx);
079:
080: // make the initial source as a RenderableImage
081: Filter sourceGraphic = filteredNode.getGraphicsNodeRable(true);
082: // Pad out to filterRegion
083: sourceGraphic = new PadRable8Bit(sourceGraphic, filterRegion,
084: PadMode.ZERO_PAD);
085:
086: // build a FilterChainRable8Bit
087: FilterChainRable filterChain = new FilterChainRable8Bit(
088: sourceGraphic, filterRegion);
089:
090: // 'filterRes' attribute - default is implementation specific
091: float[] filterRes = SVGUtilities.convertFilterRes(
092: filterElement, ctx);
093: filterChain.setFilterResolutionX((int) filterRes[0]);
094: filterChain.setFilterResolutionY((int) filterRes[1]);
095:
096: // Create a map for filter nodes to advertise themselves as
097: // named source
098: Map filterNodeMap = new HashMap(11);
099: filterNodeMap.put(SVG_SOURCE_GRAPHIC_VALUE, sourceGraphic);
100:
101: Filter in = buildFilterPrimitives(filterElement, filterRegion,
102: filteredElement, filteredNode, sourceGraphic,
103: filterNodeMap, ctx);
104: if ((in == null) || (in == sourceGraphic)) {
105: return null; // no filter primitives found, disable the filter.
106: } else {
107: filterChain.setSource(in);
108: return filterChain;
109: }
110: }
111:
112: /**
113: * Builds the filter primitives of filter chain of the specified
114: * filter element and returns the last filter primitive
115: * created. Filter primitives can be children of the filter or
116: * defined on one of its 'ancestor' (linked with the xlink:href
117: * attribute).
118: *
119: * @param filterElement the filter element
120: * @param filterRegion the filter chain region
121: * @param filteredElement the filtered element
122: * @param filteredNode the filtered node
123: * @param in the input Filter
124: * @param filterNodeMap the map used by named filter primitives
125: * @param ctx the bridge context
126: * @return the last filter primitive created
127: */
128: protected static Filter buildFilterPrimitives(
129: Element filterElement, Rectangle2D filterRegion,
130: Element filteredElement, GraphicsNode filteredNode,
131: Filter in, Map filterNodeMap, BridgeContext ctx) {
132:
133: List refs = new LinkedList();
134: for (;;) {
135: Filter newIn = buildLocalFilterPrimitives(filterElement,
136: filterRegion, filteredElement, filteredNode, in,
137: filterNodeMap, ctx);
138: if (newIn != in) {
139: return newIn; // filter primitives found, exit
140: }
141: String uri = XLinkSupport.getXLinkHref(filterElement);
142: if (uri.length() == 0) {
143: return in; // no xlink:href found, exit
144: }
145: // check if there is circular dependencies
146: SVGOMDocument doc = (SVGOMDocument) filterElement
147: .getOwnerDocument();
148: URL url;
149: try {
150: url = new URL(doc.getURLObject(), uri);
151: } catch (MalformedURLException ex) {
152: throw new BridgeException(ctx, filterElement, ex,
153: ERR_URI_MALFORMED, new Object[] { uri });
154:
155: }
156: if (contains(refs, url)) {
157: throw new BridgeException(ctx, filterElement,
158: ERR_XLINK_HREF_CIRCULAR_DEPENDENCIES,
159: new Object[] { uri });
160: }
161: refs.add(url);
162: filterElement = ctx
163: .getReferencedElement(filterElement, uri);
164: }
165: }
166:
167: /**
168: * Builds the filter primitives of filter chain of the specified
169: * filter element and returns the last filter primitive
170: * created or 'in' if no filter primitive has been specified.
171: *
172: * @param filterElement the filter element
173: * @param filterRegion the filter chain region
174: * @param filteredElement the filtered element
175: * @param filteredNode the filtered node
176: * @param in the input Filter
177: * @param filterNodeMap the map used by named filter primitives
178: * @param ctx the bridge context
179: * @return the last filter primitive created or 'in'
180: */
181: protected static Filter buildLocalFilterPrimitives(
182: Element filterElement, Rectangle2D filterRegion,
183: Element filteredElement, GraphicsNode filteredNode,
184: Filter in, Map filterNodeMap, BridgeContext ctx) {
185:
186: for (Node n = filterElement.getFirstChild(); n != null; n = n
187: .getNextSibling()) {
188:
189: if (n.getNodeType() != Node.ELEMENT_NODE) {
190: continue; // skip node that is not an Element
191: }
192: Element e = (Element) n;
193: Bridge bridge = ctx.getBridge(e);
194: if (bridge == null
195: || !(bridge instanceof FilterPrimitiveBridge)) {
196: continue;
197: }
198: FilterPrimitiveBridge filterBridge = (FilterPrimitiveBridge) bridge;
199: Filter filterNode = filterBridge.createFilter(ctx, e,
200: filteredElement, filteredNode, in, filterRegion,
201: filterNodeMap);
202: if (filterNode == null) {
203: return null; // disable the filter if a primitive is null
204: } else {
205: in = filterNode;
206: }
207: }
208: return in;
209: }
210:
211: /**
212: * Returns true if the specified list of URLs contains the specified url.
213: *
214: * @param urls the list of URLs
215: * @param key the url to search for
216: */
217: private static boolean contains(List urls, URL key) {
218: Iterator iter = urls.iterator();
219: while (iter.hasNext()) {
220: URL url = (URL) iter.next();
221: if (url.sameFile(key) && url.getRef().equals(key.getRef())) {
222: return true;
223: }
224: }
225: return false;
226: }
227: }
|