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.RenderingHints;
022: import java.awt.Shape;
023: import java.awt.geom.AffineTransform;
024: import java.awt.geom.Area;
025: import java.awt.geom.GeneralPath;
026:
027: import org.apache.batik.dom.svg.SVGOMUseElement;
028: import org.apache.batik.ext.awt.image.renderable.ClipRable;
029: import org.apache.batik.ext.awt.image.renderable.ClipRable8Bit;
030: import org.apache.batik.ext.awt.image.renderable.Filter;
031: import org.apache.batik.gvt.GraphicsNode;
032: import org.apache.batik.gvt.ShapeNode;
033: import org.w3c.dom.Element;
034: import org.w3c.dom.Node;
035:
036: /**
037: * Bridge class for the <clipPath> element.
038: *
039: * @author <a href="mailto:tkormann@apache.org">Thierry Kormann</a>
040: * @version $Id: SVGClipPathElementBridge.java 475477 2006-11-15 22:44:28Z cam $
041: */
042: public class SVGClipPathElementBridge extends
043: AnimatableGenericSVGBridge implements ClipBridge {
044:
045: /**
046: * Constructs a new bridge for the <clipPath> element.
047: */
048: public SVGClipPathElementBridge() {
049: }
050:
051: /**
052: * Returns 'clipPath'.
053: */
054: public String getLocalName() {
055: return SVG_CLIP_PATH_TAG;
056: }
057:
058: /**
059: * Creates a <tt>Clip</tt> according to the specified parameters.
060: *
061: * @param ctx the bridge context to use
062: * @param clipElement the element that defines the clip
063: * @param clipedElement the element that references the clip element
064: * @param clipedNode the graphics node to clip
065: */
066: public ClipRable createClip(BridgeContext ctx, Element clipElement,
067: Element clipedElement, GraphicsNode clipedNode) {
068:
069: String s;
070:
071: // 'transform' attribute
072: AffineTransform Tx;
073: s = clipElement.getAttributeNS(null, SVG_TRANSFORM_ATTRIBUTE);
074: if (s.length() != 0) {
075: Tx = SVGUtilities.convertTransform(clipElement,
076: SVG_TRANSFORM_ATTRIBUTE, s, ctx);
077: } else {
078: Tx = new AffineTransform();
079: }
080:
081: // 'clipPathUnits' attribute - default is userSpaceOnUse
082: short coordSystemType;
083: s = clipElement.getAttributeNS(null,
084: SVG_CLIP_PATH_UNITS_ATTRIBUTE);
085: if (s.length() == 0) {
086: coordSystemType = SVGUtilities.USER_SPACE_ON_USE;
087: } else {
088: coordSystemType = SVGUtilities.parseCoordinateSystem(
089: clipElement, SVG_CLIP_PATH_UNITS_ATTRIBUTE, s, ctx);
090: }
091: // additional transform to move to objectBoundingBox coordinate system
092: if (coordSystemType == SVGUtilities.OBJECT_BOUNDING_BOX) {
093: Tx = SVGUtilities.toObjectBBox(Tx, clipedNode);
094: }
095:
096: // Build the GVT tree that represents the clip path
097: //
098: // The silhouettes of the child elements are logically OR'd
099: // together to create a single silhouette which is then used to
100: // restrict the region onto which paint can be applied.
101: //
102: // The 'clipPath' element or any of its children can specify
103: // property 'clip-path'.
104: //
105: Area clipPath = new Area();
106: GVTBuilder builder = ctx.getGVTBuilder();
107: boolean hasChildren = false;
108: for (Node node = clipElement.getFirstChild(); node != null; node = node
109: .getNextSibling()) {
110:
111: // check if the node is a valid Element
112: if (node.getNodeType() != Node.ELEMENT_NODE) {
113: continue;
114: }
115:
116: Element child = (Element) node;
117: GraphicsNode clipNode = builder.build(ctx, child);
118: // check if a GVT node has been created
119: if (clipNode == null) {
120: continue;
121: }
122: hasChildren = true;
123:
124: // if this is a 'use' element, get the actual shape used
125: if (child instanceof SVGOMUseElement) {
126: Node shadowChild = ((SVGOMUseElement) child)
127: .getCSSFirstChild();
128:
129: if (shadowChild != null
130: && shadowChild.getNodeType() == Node.ELEMENT_NODE) {
131: child = (Element) shadowChild;
132: }
133: }
134:
135: // compute the outline of the current clipPath's child
136: int wr = CSSUtilities.convertClipRule(child);
137: GeneralPath path = new GeneralPath(clipNode.getOutline());
138: path.setWindingRule(wr);
139:
140: AffineTransform at = clipNode.getTransform();
141: if (at == null)
142: at = Tx;
143: else
144: at.preConcatenate(Tx);
145:
146: Shape outline = at.createTransformedShape(path);
147:
148: // apply the 'clip-path' of the current clipPath's child
149: ShapeNode outlineNode = new ShapeNode();
150: outlineNode.setShape(outline);
151: ClipRable clip = CSSUtilities.convertClipPath(child,
152: outlineNode, ctx);
153: if (clip != null) {
154: Area area = new Area(outline);
155: area.subtract(new Area(clip.getClipPath()));
156: outline = area;
157: }
158: clipPath.add(new Area(outline));
159: }
160: if (!hasChildren) {
161: return null; // empty clipPath
162: }
163:
164: // construct the shape node that represents the clipPath
165: ShapeNode clipPathNode = new ShapeNode();
166: clipPathNode.setShape(clipPath);
167:
168: // apply the 'clip-path' of the clipPath element (already in user space)
169: ClipRable clipElementClipPath = CSSUtilities.convertClipPath(
170: clipElement, clipPathNode, ctx);
171: if (clipElementClipPath != null) {
172: clipPath.subtract(new Area(clipElementClipPath
173: .getClipPath()));
174: }
175:
176: Filter filter = clipedNode.getFilter();
177: if (filter == null) {
178: // Make the initial source as a RenderableImage
179: filter = clipedNode.getGraphicsNodeRable(true);
180: }
181:
182: boolean useAA = false;
183: RenderingHints hints;
184: hints = CSSUtilities.convertShapeRendering(clipElement, null);
185: if (hints != null) {
186: Object o = hints.get(RenderingHints.KEY_ANTIALIASING);
187: useAA = (o == RenderingHints.VALUE_ANTIALIAS_ON);
188: }
189:
190: return new ClipRable8Bit(filter, clipPath, useAA);
191: }
192: }
|