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.AffineTransform;
022: import java.awt.geom.Rectangle2D;
023: import java.util.Map;
024:
025: import org.apache.batik.dom.util.XLinkSupport;
026: import org.apache.batik.ext.awt.image.PadMode;
027: import org.apache.batik.ext.awt.image.renderable.AffineRable8Bit;
028: import org.apache.batik.ext.awt.image.renderable.Filter;
029: import org.apache.batik.ext.awt.image.renderable.PadRable8Bit;
030: import org.apache.batik.ext.awt.image.spi.ImageTagRegistry;
031: import org.apache.batik.gvt.GraphicsNode;
032: import org.apache.batik.util.ParsedURL;
033: import org.w3c.dom.Document;
034: import org.w3c.dom.Element;
035:
036: /**
037: * Bridge class for the <feImage> element.
038: *
039: * @author <a href="mailto:tkormann@apache.org">Thierry Kormann</a>
040: * @version $Id: SVGFeImageElementBridge.java 501922 2007-01-31 17:47:47Z dvholten $
041: */
042: public class SVGFeImageElementBridge extends
043: AbstractSVGFilterPrimitiveElementBridge {
044:
045: /**
046: * Constructs a new bridge for the <feImage> element.
047: */
048: public SVGFeImageElementBridge() {
049: }
050:
051: /**
052: * Returns 'feImage'.
053: */
054: public String getLocalName() {
055: return SVG_FE_IMAGE_TAG;
056: }
057:
058: /**
059: * Creates a <tt>Filter</tt> primitive according to the specified
060: * parameters.
061: *
062: * @param ctx the bridge context to use
063: * @param filterElement the element that defines a filter
064: * @param filteredElement the element that references the filter
065: * @param filteredNode the graphics node to filter
066: *
067: * @param inputFilter the <tt>Filter</tt> that represents the current
068: * filter input if the filter chain.
069: * @param filterRegion the filter area defined for the filter chain
070: * the new node will be part of.
071: * @param filterMap a map where the mediator can map a name to the
072: * <tt>Filter</tt> it creates. Other <tt>FilterBridge</tt>s
073: * can then access a filter node from the filterMap if they
074: * know its name.
075: */
076: public Filter createFilter(BridgeContext ctx,
077: Element filterElement, Element filteredElement,
078: GraphicsNode filteredNode, Filter inputFilter,
079: Rectangle2D filterRegion, Map filterMap) {
080:
081: // 'xlink:href' attribute
082: String uriStr = XLinkSupport.getXLinkHref(filterElement);
083: if (uriStr.length() == 0) {
084: throw new BridgeException(ctx, filterElement,
085: ERR_ATTRIBUTE_MISSING,
086: new Object[] { "xlink:href" });
087: }
088:
089: //
090: // According the the SVG specification, feImage behaves like
091: // <image> if it references an SVG document or a raster image
092: // and it behaves like a <use> if it references a document
093: // fragment.
094: //
095: // To provide this behavior, depending on whether the uri
096: // contains a fragment identifier, we create either an
097: // <image> or a <use> element and request the corresponding
098: // bridges to build the corresponding GraphicsNode for us.
099: //
100: // Then, we take care of the possible transformation needed
101: // from objectBoundingBox space to user space.
102: //
103:
104: Document document = filterElement.getOwnerDocument();
105: boolean isUse = uriStr.indexOf('#') != -1;
106: Element contentElement = null;
107: if (isUse) {
108: contentElement = document.createElementNS(
109: SVG_NAMESPACE_URI, SVG_USE_TAG);
110: } else {
111: contentElement = document.createElementNS(
112: SVG_NAMESPACE_URI, SVG_IMAGE_TAG);
113: }
114:
115: contentElement.setAttributeNS(XLINK_NAMESPACE_URI,
116: XLINK_HREF_QNAME, uriStr);
117:
118: Element proxyElement = document.createElementNS(
119: SVG_NAMESPACE_URI, SVG_G_TAG);
120: proxyElement.appendChild(contentElement);
121:
122: // feImage's default region is that of the filter chain.
123: Rectangle2D defaultRegion = filterRegion;
124: Element filterDefElement = (Element) (filterElement
125: .getParentNode());
126:
127: Rectangle2D primitiveRegion = SVGUtilities
128: .getBaseFilterPrimitiveRegion(filterElement,
129: filteredElement, filteredNode, defaultRegion,
130: ctx);
131:
132: // System.err.println(">>>>>>>> primitiveRegion : " + primitiveRegion);
133:
134: contentElement.setAttributeNS(null, SVG_X_ATTRIBUTE, String
135: .valueOf(primitiveRegion.getX()));
136: contentElement.setAttributeNS(null, SVG_Y_ATTRIBUTE, String
137: .valueOf(primitiveRegion.getY()));
138: contentElement.setAttributeNS(null, SVG_WIDTH_ATTRIBUTE, String
139: .valueOf(primitiveRegion.getWidth()));
140: contentElement.setAttributeNS(null, SVG_HEIGHT_ATTRIBUTE,
141: String.valueOf(primitiveRegion.getHeight()));
142:
143: GraphicsNode node = ctx.getGVTBuilder()
144: .build(ctx, proxyElement);
145: Filter filter = node.getGraphicsNodeRable(true);
146:
147: // 'primitiveUnits' attribute - default is userSpaceOnUse
148: short coordSystemType;
149: String s = SVGUtilities.getChainableAttributeNS(
150: filterDefElement, null, SVG_PRIMITIVE_UNITS_ATTRIBUTE,
151: ctx);
152: if (s.length() == 0) {
153: coordSystemType = SVGUtilities.USER_SPACE_ON_USE;
154: } else {
155: coordSystemType = SVGUtilities.parseCoordinateSystem(
156: filterDefElement, SVG_PRIMITIVE_UNITS_ATTRIBUTE, s,
157: ctx);
158: }
159:
160: // Compute the transform from object bounding box to user
161: // space if needed.
162: AffineTransform at = new AffineTransform();
163: if (coordSystemType == SVGUtilities.OBJECT_BOUNDING_BOX) {
164: at = SVGUtilities.toObjectBBox(at, filteredNode);
165: }
166: filter = new AffineRable8Bit(filter, at);
167:
168: // handle the 'color-interpolation-filters' property
169: handleColorInterpolationFilters(filter, filterElement);
170:
171: // get filter primitive chain region
172: Rectangle2D primitiveRegionUserSpace = SVGUtilities
173: .convertFilterPrimitiveRegion(filterElement,
174: filteredElement, filteredNode, defaultRegion,
175: filterRegion, ctx);
176: filter = new PadRable8Bit(filter, primitiveRegionUserSpace,
177: PadMode.ZERO_PAD);
178:
179: // update the filter Map
180: updateFilterMap(filterElement, filter, filterMap);
181:
182: return filter;
183: }
184:
185: /**
186: * Returns a Filter that represents a svg document or element as an image.
187: *
188: * @param ctx the bridge context
189: * @param primitiveRegion the primitive region
190: * @param refElement the referenced element
191: * @param toBBoxNeeded true if there is a need to transform to ObjectBoundingBox
192: * space
193: * @param filterElement parent filter element
194: * @param filteredNode node to which the filter applies
195: */
196: protected static Filter createSVGFeImage(BridgeContext ctx,
197: Rectangle2D primitiveRegion, Element refElement,
198: boolean toBBoxNeeded, Element filterElement,
199: GraphicsNode filteredNode) {
200:
201: //
202: // <!> FIX ME
203: // Unresolved issue on the feImage behavior when referencing an
204: // image (PNG, JPEG or SVG image).
205: // VH & TK, 03/08/2002
206: // Furthermore, for feImage referencing doc fragment, should act
207: // like a <use>, i.e., CSS cascading and the whole zing bang.
208: //
209: GraphicsNode node = ctx.getGVTBuilder().build(ctx, refElement);
210: Filter filter = node.getGraphicsNodeRable(true);
211:
212: AffineTransform at = new AffineTransform();
213:
214: if (toBBoxNeeded) {
215: // 'primitiveUnits' attribute - default is userSpaceOnUse
216: short coordSystemType;
217: Element filterDefElement = (Element) (filterElement
218: .getParentNode());
219: String s = SVGUtilities.getChainableAttributeNS(
220: filterDefElement, null,
221: SVG_PRIMITIVE_UNITS_ATTRIBUTE, ctx);
222: if (s.length() == 0) {
223: coordSystemType = SVGUtilities.USER_SPACE_ON_USE;
224: } else {
225: coordSystemType = SVGUtilities.parseCoordinateSystem(
226: filterDefElement,
227: SVG_PRIMITIVE_UNITS_ATTRIBUTE, s, ctx);
228: }
229:
230: if (coordSystemType == SVGUtilities.OBJECT_BOUNDING_BOX) {
231: at = SVGUtilities.toObjectBBox(at, filteredNode);
232: }
233:
234: Rectangle2D bounds = filteredNode.getGeometryBounds();
235: at.preConcatenate(AffineTransform.getTranslateInstance(
236: primitiveRegion.getX() - bounds.getX(),
237: primitiveRegion.getY() - bounds.getY()));
238:
239: } else {
240:
241: // Need to translate the image to the x, y coordinate to
242: // have the same behavior as the <use> element
243: at
244: .translate(primitiveRegion.getX(), primitiveRegion
245: .getY());
246: }
247:
248: return new AffineRable8Bit(filter, at);
249: }
250:
251: /**
252: * Returns a Filter that represents an raster image (JPG or PNG).
253: *
254: * @param ctx the bridge context
255: * @param primitiveRegion the primitive region
256: * @param purl the url of the image
257: */
258: protected static Filter createRasterFeImage(BridgeContext ctx,
259: Rectangle2D primitiveRegion, ParsedURL purl) {
260:
261: // Need to fit the raster image to the filter region so that
262: // we have the same behavior as raster images in the <image> element.
263: Filter filter = ImageTagRegistry.getRegistry().readURL(purl);
264:
265: Rectangle2D bounds = filter.getBounds2D();
266: AffineTransform scale = new AffineTransform();
267: scale.translate(primitiveRegion.getX(), primitiveRegion.getY());
268: scale.scale(primitiveRegion.getWidth()
269: / (bounds.getWidth() - 1), primitiveRegion.getHeight()
270: / (bounds.getHeight() - 1));
271: scale.translate(-bounds.getX(), -bounds.getY());
272:
273: return new AffineRable8Bit(filter, scale);
274: }
275: }
|