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.Point;
022: import java.awt.geom.Rectangle2D;
023: import java.awt.image.Kernel;
024: import java.util.Map;
025: import java.util.StringTokenizer;
026:
027: import org.apache.batik.ext.awt.image.PadMode;
028: import org.apache.batik.ext.awt.image.renderable.ConvolveMatrixRable;
029: import org.apache.batik.ext.awt.image.renderable.ConvolveMatrixRable8Bit;
030: import org.apache.batik.ext.awt.image.renderable.Filter;
031: import org.apache.batik.ext.awt.image.renderable.PadRable;
032: import org.apache.batik.ext.awt.image.renderable.PadRable8Bit;
033: import org.apache.batik.gvt.GraphicsNode;
034: import org.w3c.dom.Element;
035:
036: /**
037: * Bridge class for the <feConvolveMatrix> element.
038: *
039: * @author <a href="mailto:tkormann@apache.org">Thierry Kormann</a>
040: * @version $Id: SVGFeConvolveMatrixElementBridge.java 501922 2007-01-31 17:47:47Z dvholten $
041: */
042: public class SVGFeConvolveMatrixElementBridge extends
043: AbstractSVGFilterPrimitiveElementBridge {
044:
045: /**
046: * Constructs a new bridge for the <feConvolveMatrix> element.
047: */
048: public SVGFeConvolveMatrixElementBridge() {
049: }
050:
051: /**
052: * Returns 'feConvolveMatrix'.
053: */
054: public String getLocalName() {
055: return SVG_FE_CONVOLVE_MATRIX_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: // 'order' attribute - default is [3, 3]
082: int[] orderXY = convertOrder(filterElement, ctx);
083:
084: // 'kernelMatrix' attribute - required
085: float[] kernelMatrix = convertKernelMatrix(filterElement,
086: orderXY, ctx);
087:
088: // 'divisor' attribute - default is kernel matrix sum or 1 if sum is 0
089: float divisor = convertDivisor(filterElement, kernelMatrix, ctx);
090:
091: // 'bias' attribute - default is 0
092: float bias = convertNumber(filterElement, SVG_BIAS_ATTRIBUTE,
093: 0, ctx);
094:
095: // 'targetX' and 'targetY' attribute
096: int[] targetXY = convertTarget(filterElement, orderXY, ctx);
097:
098: // 'edgeMode' attribute - default is 'duplicate'
099: PadMode padMode = convertEdgeMode(filterElement, ctx);
100:
101: // 'kernelUnitLength' attribute
102: double[] kernelUnitLength = convertKernelUnitLength(
103: filterElement, ctx);
104:
105: // 'preserveAlpha' attribute - default is 'false'
106: boolean preserveAlpha = convertPreserveAlpha(filterElement, ctx);
107:
108: // 'in' attribute
109: Filter in = getIn(filterElement, filteredElement, filteredNode,
110: inputFilter, filterMap, ctx);
111: if (in == null) {
112: return null; // disable the filter
113: }
114:
115: // Default region is the size of in (if in is SourceGraphic or
116: // SourceAlpha it will already include a pad/crop to the
117: // proper filter region size).
118: Rectangle2D defaultRegion = in.getBounds2D();
119: Rectangle2D primitiveRegion = SVGUtilities
120: .convertFilterPrimitiveRegion(filterElement,
121: filteredElement, filteredNode, defaultRegion,
122: filterRegion, ctx);
123:
124: PadRable pad = new PadRable8Bit(in, primitiveRegion,
125: PadMode.ZERO_PAD);
126:
127: // build the convolve filter
128: ConvolveMatrixRable convolve = new ConvolveMatrixRable8Bit(pad);
129: for (int i = 0; i < kernelMatrix.length; i++) {
130: kernelMatrix[i] /= divisor;
131: }
132: convolve.setKernel(new Kernel(orderXY[0], orderXY[1],
133: kernelMatrix));
134: convolve.setTarget(new Point(targetXY[0], targetXY[1]));
135: convolve.setBias(bias);
136: convolve.setEdgeMode(padMode);
137: convolve.setKernelUnitLength(kernelUnitLength);
138: convolve.setPreserveAlpha(preserveAlpha);
139:
140: // handle the 'color-interpolation-filters' property
141: handleColorInterpolationFilters(convolve, filterElement);
142:
143: PadRable filter = new PadRable8Bit(convolve, primitiveRegion,
144: PadMode.ZERO_PAD);
145:
146: // update the filter Map
147: updateFilterMap(filterElement, filter, filterMap);
148:
149: return filter;
150: }
151:
152: /**
153: * Convert the 'order' attribute of the specified feConvolveMatrix
154: * filter primitive element.
155: *
156: * @param filterElement the feConvolveMatrix filter primitive element
157: * @param ctx the BridgeContext to use for error information
158: */
159: protected static int[] convertOrder(Element filterElement,
160: BridgeContext ctx) {
161: String s = filterElement.getAttributeNS(null,
162: SVG_ORDER_ATTRIBUTE);
163: if (s.length() == 0) {
164: return new int[] { 3, 3 };
165: }
166: int[] orderXY = new int[2];
167: StringTokenizer tokens = new StringTokenizer(s, " ,");
168: try {
169: orderXY[0] = SVGUtilities.convertSVGInteger(tokens
170: .nextToken());
171: if (tokens.hasMoreTokens()) {
172: orderXY[1] = SVGUtilities.convertSVGInteger(tokens
173: .nextToken());
174: } else {
175: orderXY[1] = orderXY[0];
176: }
177: } catch (NumberFormatException nfEx) {
178: throw new BridgeException(ctx, filterElement, nfEx,
179: ERR_ATTRIBUTE_VALUE_MALFORMED, new Object[] {
180: SVG_ORDER_ATTRIBUTE, s, nfEx });
181: }
182: if (tokens.hasMoreTokens() || orderXY[0] <= 0
183: || orderXY[1] <= 0) {
184: throw new BridgeException(ctx, filterElement,
185: ERR_ATTRIBUTE_VALUE_MALFORMED, new Object[] {
186: SVG_ORDER_ATTRIBUTE, s });
187: }
188: return orderXY;
189: }
190:
191: /**
192: * Convert the 'kernelMatrix' attribute of the specified feConvolveMatrix
193: * filter primitive element.
194: *
195: * @param filterElement the feConvolveMatrix filter primitive element
196: * @param orderXY the value of the 'order' attribute
197: * @param ctx the BridgeContext to use for error information
198: */
199: protected static float[] convertKernelMatrix(Element filterElement,
200: int[] orderXY, BridgeContext ctx) {
201:
202: String s = filterElement.getAttributeNS(null,
203: SVG_KERNEL_MATRIX_ATTRIBUTE);
204: if (s.length() == 0) {
205: throw new BridgeException(ctx, filterElement,
206: ERR_ATTRIBUTE_MISSING,
207: new Object[] { SVG_KERNEL_MATRIX_ATTRIBUTE });
208: }
209: int size = orderXY[0] * orderXY[1];
210: float[] kernelMatrix = new float[size];
211: StringTokenizer tokens = new StringTokenizer(s, " ,");
212: int i = 0;
213: try {
214: while (tokens.hasMoreTokens() && i < size) {
215: kernelMatrix[i++] = SVGUtilities
216: .convertSVGNumber(tokens.nextToken());
217: }
218: } catch (NumberFormatException nfEx) {
219: throw new BridgeException(ctx, filterElement, nfEx,
220: ERR_ATTRIBUTE_VALUE_MALFORMED, new Object[] {
221: SVG_KERNEL_MATRIX_ATTRIBUTE, s, nfEx });
222: }
223: if (i != size) {
224: throw new BridgeException(ctx, filterElement,
225: ERR_ATTRIBUTE_VALUE_MALFORMED, new Object[] {
226: SVG_KERNEL_MATRIX_ATTRIBUTE, s });
227: }
228: return kernelMatrix;
229: }
230:
231: /**
232: * Convert the 'divisor' attribute of the specified feConvolveMatrix
233: * filter primitive element.
234: *
235: * @param filterElement the feConvolveMatrix filter primitive element
236: * @param kernelMatrix the value of the 'kernelMatrix' attribute
237: * @param ctx the BridgeContext to use for error information
238: */
239: protected static float convertDivisor(Element filterElement,
240: float[] kernelMatrix, BridgeContext ctx) {
241:
242: String s = filterElement.getAttributeNS(null,
243: SVG_DIVISOR_ATTRIBUTE);
244: if (s.length() == 0) {
245: // default is sum of kernel values (if sum is zero then 1.0)
246: float sum = 0;
247: for (int i = 0; i < kernelMatrix.length; ++i) {
248: sum += kernelMatrix[i];
249: }
250: return (sum == 0) ? 1.0f : sum;
251: } else {
252: try {
253: return SVGUtilities.convertSVGNumber(s);
254: } catch (NumberFormatException nfEx) {
255: throw new BridgeException(ctx, filterElement, nfEx,
256: ERR_ATTRIBUTE_VALUE_MALFORMED, new Object[] {
257: SVG_DIVISOR_ATTRIBUTE, s, nfEx });
258: }
259: }
260: }
261:
262: /**
263: * Convert the 'targetX' and 'targetY' attributes of the specified
264: * feConvolveMatrix filter primitive element.
265: *
266: * @param filterElement the feConvolveMatrix filter primitive element
267: * @param orderXY the value of the 'order' attribute
268: * @param ctx the BridgeContext to use for error information
269: */
270: protected static int[] convertTarget(Element filterElement,
271: int[] orderXY, BridgeContext ctx) {
272:
273: int[] targetXY = new int[2];
274: // 'targetX' attribute - default is floor(orderX / 2)
275: String s = filterElement.getAttributeNS(null,
276: SVG_TARGET_X_ATTRIBUTE);
277: if (s.length() == 0) {
278: targetXY[0] = orderXY[0] / 2;
279: } else {
280: try {
281: int v = SVGUtilities.convertSVGInteger(s);
282: if (v < 0 || v >= orderXY[0]) {
283: throw new BridgeException(ctx, filterElement,
284: ERR_ATTRIBUTE_VALUE_MALFORMED,
285: new Object[] { SVG_TARGET_X_ATTRIBUTE, s });
286: }
287: targetXY[0] = v;
288: } catch (NumberFormatException nfEx) {
289: throw new BridgeException(ctx, filterElement, nfEx,
290: ERR_ATTRIBUTE_VALUE_MALFORMED, new Object[] {
291: SVG_TARGET_X_ATTRIBUTE, s, nfEx });
292: }
293: }
294: // 'targetY' attribute - default is floor(orderY / 2)
295: s = filterElement.getAttributeNS(null, SVG_TARGET_Y_ATTRIBUTE);
296: if (s.length() == 0) {
297: targetXY[1] = orderXY[1] / 2;
298: } else {
299: try {
300: int v = SVGUtilities.convertSVGInteger(s);
301: if (v < 0 || v >= orderXY[1]) {
302: throw new BridgeException(ctx, filterElement,
303: ERR_ATTRIBUTE_VALUE_MALFORMED,
304: new Object[] { SVG_TARGET_Y_ATTRIBUTE, s });
305: }
306: targetXY[1] = v;
307: } catch (NumberFormatException nfEx) {
308: throw new BridgeException(ctx, filterElement, nfEx,
309: ERR_ATTRIBUTE_VALUE_MALFORMED, new Object[] {
310: SVG_TARGET_Y_ATTRIBUTE, s, nfEx });
311: }
312: }
313: return targetXY;
314: }
315:
316: /**
317: * Convert the 'kernelUnitLength' attribute of the specified
318: * feConvolveMatrix filter primitive element.
319: *
320: * @param filterElement the feConvolveMatrix filter primitive element
321: * @param ctx the BridgeContext to use for error information
322: */
323: protected static double[] convertKernelUnitLength(
324: Element filterElement, BridgeContext ctx) {
325: String s = filterElement.getAttributeNS(null,
326: SVG_KERNEL_UNIT_LENGTH_ATTRIBUTE);
327: if (s.length() == 0) {
328: return null;
329: }
330: double[] units = new double[2];
331: StringTokenizer tokens = new StringTokenizer(s, " ,");
332: try {
333: units[0] = SVGUtilities
334: .convertSVGNumber(tokens.nextToken());
335: if (tokens.hasMoreTokens()) {
336: units[1] = SVGUtilities.convertSVGNumber(tokens
337: .nextToken());
338: } else {
339: units[1] = units[0];
340: }
341: } catch (NumberFormatException nfEx) {
342: throw new BridgeException(ctx, filterElement, nfEx,
343: ERR_ATTRIBUTE_VALUE_MALFORMED, new Object[] {
344: SVG_KERNEL_UNIT_LENGTH_ATTRIBUTE, s });
345:
346: }
347: if (tokens.hasMoreTokens() || units[0] <= 0 || units[1] <= 0) {
348: throw new BridgeException(ctx, filterElement,
349: ERR_ATTRIBUTE_VALUE_MALFORMED, new Object[] {
350: SVG_KERNEL_UNIT_LENGTH_ATTRIBUTE, s });
351: }
352: return units;
353: }
354:
355: /**
356: * Convert the 'edgeMode' attribute of the specified feConvolveMatrix
357: * filter primitive element.
358: *
359: * @param filterElement the feConvolveMatrix filter primitive element
360: * @param ctx the BridgeContext to use for error information
361: */
362: protected static PadMode convertEdgeMode(Element filterElement,
363: BridgeContext ctx) {
364: String s = filterElement.getAttributeNS(null,
365: SVG_EDGE_MODE_ATTRIBUTE);
366: if (s.length() == 0) {
367: return PadMode.REPLICATE;
368: }
369: if (SVG_DUPLICATE_VALUE.equals(s)) {
370: return PadMode.REPLICATE;
371: }
372: if (SVG_WRAP_VALUE.equals(s)) {
373: return PadMode.WRAP;
374: }
375: if (SVG_NONE_VALUE.equals(s)) {
376: return PadMode.ZERO_PAD;
377: }
378: throw new BridgeException(ctx, filterElement,
379: ERR_ATTRIBUTE_VALUE_MALFORMED, new Object[] {
380: SVG_EDGE_MODE_ATTRIBUTE, s });
381: }
382:
383: /**
384: * Convert the 'preserveAlpha' attribute of the specified feConvolveMatrix
385: * filter primitive element.
386: *
387: * @param filterElement the feConvolveMatrix filter primitive element
388: * @param ctx the BridgeContext to use for error information
389: */
390: protected static boolean convertPreserveAlpha(
391: Element filterElement, BridgeContext ctx) {
392: String s = filterElement.getAttributeNS(null,
393: SVG_PRESERVE_ALPHA_ATTRIBUTE);
394: if (s.length() == 0) {
395: return false;
396: }
397: if (SVG_TRUE_VALUE.equals(s)) {
398: return true;
399: }
400: if (SVG_FALSE_VALUE.equals(s)) {
401: return false;
402: }
403: throw new BridgeException(ctx, filterElement,
404: ERR_ATTRIBUTE_VALUE_MALFORMED, new Object[] {
405: SVG_PRESERVE_ALPHA_ATTRIBUTE, s });
406: }
407: }
|