001: /*
002: *
003: *
004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026: package com.sun.perseus.model;
027:
028: import com.sun.perseus.util.SVGConstants;
029:
030: import com.sun.perseus.j2d.LinearGradientPaintDef;
031:
032: import org.w3c.dom.DOMException;
033:
034: import org.w3c.dom.svg.SVGRGBColor;
035: import org.w3c.dom.svg.SVGMatrix;
036:
037: import com.sun.perseus.j2d.RGB;
038: import com.sun.perseus.j2d.PaintDef;
039: import com.sun.perseus.j2d.PaintTarget;
040: import com.sun.perseus.j2d.PaintServer;
041: import com.sun.perseus.j2d.Transform;
042:
043: /**
044: * <code>GradientElement</code> abstract class is a helper base
045: * class for <code>LinearGradient</code> and <code>RadialGradient</code>.
046: * <br />
047: *
048: * @version $Id: GradientElement.java,v 1.10 2006/06/29 10:47:31 ln156897 Exp $
049: */
050: public abstract class GradientElement extends PaintElement {
051: /**
052: * Computed PaintDef, in userSpaceOnUse.
053: */
054: PaintDef computedPaint;
055: /**
056: * The last computed GradientColorMap fractions
057: */
058: float[] lastColorMapFractions;
059:
060: /**
061: * The last computed GradientColorMap colors
062: */
063: int[] lastColorMapRGBA;
064:
065: /**
066: * Addintional paint transform, like the gradientTransform on gradients.
067: */
068: Transform transform = new Transform(null);
069:
070: /**
071: * Constructor.
072: *
073: * @param ownerDocument this element's owner <code>DocumentNode</code>
074: */
075: public GradientElement(final DocumentNode ownerDocument) {
076: super (ownerDocument);
077:
078: isObjectBBox = true;
079: }
080:
081: /**
082: * @param newTransform this node's new transform. Note that the
083: * input value is used by reference.
084: */
085: public void setTransform(final Transform newTransform) {
086: if (equal(newTransform, transform)) {
087: return;
088: }
089: modifyingNode();
090: this .transform = newTransform;
091: onPaintChange();
092: modifiedNode();
093: }
094:
095: /**
096: * @return this node's transform
097: */
098: public Transform getTransform() {
099: return transform;
100: }
101:
102: /**
103: * Sets the isObjectBBox state.
104: *
105: * @param newIsObjectBBox the new value for the isObjectBBox
106: * property.
107: */
108: public void setIsObjectBBox(final boolean newIsObjectBBox) {
109: if (newIsObjectBBox == isObjectBBox) {
110: return;
111: }
112:
113: isObjectBBox = newIsObjectBBox;
114: onPaintChange();
115: }
116:
117: /**
118: * Supported traits: transform.
119: *
120: * @param traitName the name of the trait which the element may support.
121: * @return true if this element supports the given trait in one of the
122: * trait accessor methods.
123: */
124: boolean supportsTrait(final String traitName) {
125: if (SVGConstants.SVG_GRADIENT_TRANSFORM_ATTRIBUTE == traitName
126: || SVGConstants.SVG_GRADIENT_UNITS_ATTRIBUTE == traitName) {
127: return true;
128: } else {
129: return super .supportsTrait(traitName);
130: }
131: }
132:
133: /**
134: * @param name the requested trait name.
135: * @return the requested trait value.
136: *
137: * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested
138: * trait is not supported on this element or null.
139: * @throws DOMException with error code TYPE_MISMATCH_ERR if requested
140: * trait's computed value cannot be converted to a String (SVG Tiny only).
141: */
142: public String getTraitImpl(final String name) throws DOMException {
143: if (SVGConstants.SVG_GRADIENT_TRANSFORM_ATTRIBUTE == name) {
144: return toStringTrait(transform);
145: }
146: if (SVGConstants.SVG_GRADIENT_UNITS_ATTRIBUTE == name) {
147: if (isObjectBBox) {
148: return SVGConstants.SVG_OBJECT_BOUND_BOX_VALUE;
149: } else {
150: return SVGConstants.SVG_USER_SPACE_ON_USE_VALUE;
151: }
152: } else {
153: return super .getTraitImpl(name);
154: }
155: }
156:
157: /**
158: * @param name the trait's name.
159: * @param value the new trait value, as a string.
160: *
161: * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested
162: * trait is not supported on this element or null.
163: * @throws DOMException with error code TYPE_MISMATCH_ERR if the requested
164: * trait's value cannot be specified as a String
165: * @throws DOMException with error code INVALID_ACCESS_ERR if the input
166: * value is an invalid value for the given trait or null.
167: * @throws DOMException with error code NO_MODIFICATION_ALLOWED_ERR: if
168: * attempt is made to change readonly trait.
169: */
170: public void setTraitImpl(final String name, final String value)
171: throws DOMException {
172: if (SVGConstants.SVG_GRADIENT_TRANSFORM_ATTRIBUTE == name) {
173: setTransform(parseTransformTrait(name, value));
174: } else if (SVGConstants.SVG_GRADIENT_UNITS_ATTRIBUTE
175: .equals(name)) {
176: if (SVGConstants.SVG_OBJECT_BOUND_BOX_VALUE.equals(value)) {
177: setIsObjectBBox(true);
178: } else if (SVGConstants.SVG_USER_SPACE_ON_USE_VALUE
179: .equals(value)) {
180: setIsObjectBBox(false);
181: } else {
182: throw illegalTraitValue(name, value);
183: }
184: } else {
185: super .setTraitImpl(name, value);
186: }
187: }
188:
189: /**
190: * @param name matrix trait name.
191: * @return the trait value corresponding to name as SVGMatrix.
192: *
193: * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested
194: * trait is not supported on this element or null.
195: * @throws DOMException with error code TYPE_MISMATCH_ERR if requested
196: * trait's computed value cannot be converted to {@link
197: * org.w3c.dom.svg.SVGMatrix SVGMatrix}
198: */
199: SVGMatrix getMatrixTraitImpl(final String name) throws DOMException {
200: if (SVGConstants.SVG_GRADIENT_TRANSFORM_ATTRIBUTE.equals(name)) {
201: return toSVGMatrixTrait(transform);
202: } else {
203: return super .getMatrixTraitImpl(name);
204: }
205: }
206:
207: /**
208: * @param name name of trait to set
209: * @param matrix Transform value of trait
210: *
211: * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested
212: * trait is not supported on this element or null.
213: * @throws DOMException with error code TYPE_MISMATCH_ERR if the requested
214: * trait's value cannot be specified as an {@link org.w3c.dom.svg.SVGMatrix
215: * SVGMatrix}
216: * @throws DOMException with error code INVALID_ACCESS_ERR if the input
217: * value is an invalid value for the given trait or null.
218: * @throws DOMException with error code NO_MODIFICATION_ALLOWED_ERR: if
219: * attempt is made to change readonly trait.
220: */
221: void setMatrixTraitImpl(final String name, final Transform matrix)
222: throws DOMException {
223: if (SVGConstants.SVG_GRADIENT_TRANSFORM_ATTRIBUTE.equals(name)) {
224: setTransform(matrix);
225: } else {
226: super .setMatrixTraitImpl(name, matrix);
227: }
228: }
229:
230: /**
231: * @param name the name of the trait to convert.
232: * @param value the float trait value to convert.
233: */
234: String toStringTrait(final String name, final float[][] value) {
235: if (SVGConstants.SVG_GRADIENT_TRANSFORM_ATTRIBUTE == name) {
236: Transform transform = new Transform(value[0][0],
237: value[1][0], value[2][0], value[3][0], value[4][0],
238: value[5][0]);
239: return toStringTrait(transform);
240: } else {
241: return super .toStringTrait(name, value);
242: }
243: }
244:
245: /**
246: * GradientElement handles the gradientTransform as a TransformTraitAnim and
247: * the gradientUnits as a StringTraitAnim.
248: *
249: * @param traitName the trait name.
250: */
251: TraitAnim createTraitAnimImpl(final String traitName) {
252: if (SVGConstants.SVG_GRADIENT_TRANSFORM_ATTRIBUTE == traitName) {
253: return new TransformTraitAnim(this , traitName);
254: } else if (SVGConstants.SVG_GRADIENT_UNITS_ATTRIBUTE == traitName) {
255: return new StringTraitAnim(this , NULL_NS, traitName);
256: } else {
257: return super .createTraitAnimImpl(traitName);
258: }
259: }
260:
261: /**
262: * Set the trait value as float array.
263: *
264: * @param name the trait's name.
265: * @param value the trait's value.
266: *
267: * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested
268: * trait is not supported on this element.
269: * @throws DOMException with error code TYPE_MISMATCH_ERR if the requested
270: * trait's value cannot be specified as a float
271: * @throws DOMException with error code INVALID_ACCESS_ERR if the input
272: * value is an invalid value for the given trait.
273: */
274: void setFloatArrayTrait(final String name, final float[][] value)
275: throws DOMException {
276: // We use .equals for the transform attribute as the string may not
277: // have been interned. We use == for the motion pseudo attribute because
278: // it is only used internally and from the SVGConstants strings.
279: if (SVGConstants.SVG_GRADIENT_TRANSFORM_ATTRIBUTE.equals(name)) {
280: if (transform == null) {
281: modifyingNode();
282: transform = new Transform(value[0][0], value[1][0],
283: value[2][0], value[3][0], value[4][0],
284: value[5][0]);
285: } else {
286: if (!transform.equals(value)) {
287: modifyingNode();
288: transform.setTransform(value[0][0], value[1][0],
289: value[2][0], value[3][0], value[4][0],
290: value[5][0]);
291: } else {
292: return;
293: }
294: }
295: modifiedNode();
296: } else {
297: super .setFloatArrayTrait(name, value);
298: }
299: }
300:
301: /**
302: * Validates the input trait value.
303: *
304: * @param namespaceURI the trait's namespace URI.
305: * @param traitName the name of the trait to be validated.
306: * @param value the value to be validated
307: * @param reqNamespaceURI the namespace of the element requesting
308: * validation.
309: * @param reqLocalName the local name of the element requesting validation.
310: * @param reqTraitNamespace the namespace of the trait which has the values
311: * value on the requesting element.
312: * @param reqTraitName the name of the trait which has the values value on
313: * the requesting element.
314: * @throws DOMException with error code INVALID_ACCESS_ERR if the input
315: * value is incompatible with the given trait.
316: */
317: String validateTraitNS(final String namespaceURI,
318: final String traitName, final String value,
319: final String reqNamespaceURI, final String reqLocalName,
320: final String reqTraitNamespace, final String reqTraitName)
321: throws DOMException {
322: if (namespaceURI != null) {
323: return super .validateTraitNS(namespaceURI, traitName,
324: value, reqNamespaceURI, reqLocalName,
325: reqTraitNamespace, reqTraitName);
326: }
327:
328: if (SVGConstants.SVG_GRADIENT_UNITS_ATTRIBUTE.equals(traitName)) {
329: if (!SVGConstants.SVG_USER_SPACE_ON_USE_VALUE.equals(value)
330: && !SVGConstants.SVG_OBJECT_BOUND_BOX_VALUE
331: .equals(value)) {
332: throw illegalTraitValue(traitName, value);
333: }
334: return value;
335: }
336:
337: return super .validateTraitNS(namespaceURI, traitName, value,
338: reqNamespaceURI, reqLocalName, reqTraitNamespace,
339: reqTraitName);
340: }
341:
342: /**
343: * Validates the input trait value.
344: *
345: * @param traitName the name of the trait to be validated.
346: * @param value the value to be validated
347: * @param reqNamespaceURI the namespace of the element requesting
348: * validation.
349: * @param reqLocalName the local name of the element requesting validation.
350: * @param reqTraitNamespace the namespace of the trait which has the values
351: * value on the requesting element.
352: * @param reqTraitName the name of the trait which has the values value on
353: * the requesting element.
354: * @throws DOMException with error code INVALID_ACCESS_ERR if the input
355: * value is incompatible with the given trait.
356: */
357: public float[][] validateFloatArrayTrait(final String traitName,
358: final String value, final String reqNamespaceURI,
359: final String reqLocalName, final String reqTraitNamespace,
360: final String reqTraitName) throws DOMException {
361: if (SVGConstants.SVG_GRADIENT_TRANSFORM_ATTRIBUTE == traitName) {
362: Transform txf = parseTransformTrait(traitName, value);
363: return new float[][] { { txf.getComponent(0) },
364: { txf.getComponent(1) }, { txf.getComponent(2) },
365: { txf.getComponent(3) }, { txf.getComponent(4) },
366: { txf.getComponent(5) } };
367: } else {
368: return super .validateFloatArrayTrait(traitName, value,
369: reqNamespaceURI, reqLocalName, reqTraitNamespace,
370: reqTraitName);
371: }
372: }
373:
374: /**
375: * Builds a GradientColorMap from the children <stop>
376: * elements.
377: */
378: final void buildGradientColorMap() {
379: // First, compute the number of stop children.
380: ElementNode c = (ElementNode) getFirstElementChild();
381: int n = 0;
382: Stop[] stop = new Stop[5];
383:
384: while (c != null) {
385: if (c.getLocalName() == SVGConstants.SVG_STOP_TAG
386: && c.getNamespaceURI() == SVGConstants.SVG_NAMESPACE_URI) {
387: stop[n] = (Stop) c;
388: n++;
389: if (n > stop.length - 1) {
390: Stop[] tmpStop = new Stop[stop.length + 5];
391: System.arraycopy(stop, 0, tmpStop, 0, stop.length);
392: stop = tmpStop;
393: }
394: }
395:
396: c = (ElementNode) c.getNextElementSibling();
397: }
398:
399: if (n == 0) {
400: // To obtain the same result as a 'none' fill, we just use two
401: // stops with fully transparent fill.
402: lastColorMapFractions = new float[] { 0, 1 };
403: lastColorMapRGBA = new int[] { 0x00000000, 0x00000000 };
404: return;
405: }
406: if (n == 1) {
407: // We duplicate the single gradient to provide the effect of
408: // a solid color.
409: RGB color = stop[0].getStopColor();
410: int a = (int) (stop[0].getStopOpacity() * 255);
411:
412: lastColorMapFractions = new float[] { 0, 1 };
413: lastColorMapRGBA = new int[] {
414: a << 24 | color.getRed() << 16
415: | color.getGreen() << 8 | color.getBlue(),
416: a << 24 | color.getRed() << 16
417: | color.getGreen() << 8 | color.getBlue() };
418: return;
419: }
420:
421: float[] fractions = new float[n];
422: int[] rgba = new int[n];
423: RGB col = null;
424:
425: for (int i = 0; i < n; i++) {
426: fractions[i] = stop[i].getOffset();
427: if (i > 0 && fractions[i] <= fractions[i - 1]) {
428: fractions[i] = fractions[i - 1];
429: }
430:
431: col = stop[i].getStopColor();
432:
433: rgba[i] = ((int) (stop[i].getStopOpacity() * 255) << 24)
434: | col.getRed() << 16 | col.getGreen() << 8
435: | col.getBlue();
436: }
437:
438: // Check that the first stop is zero. If not, we need to dupplicate the
439: // first stop and give it fraction zero.
440: if (fractions[0] != 0) {
441: float[] tmpFractions = new float[fractions.length + 1];
442: int[] tmpRgba = new int[rgba.length + 1];
443: tmpFractions[0] = 0;
444: tmpRgba[0] = rgba[0];
445: System.arraycopy(fractions, 0, tmpFractions, 1,
446: fractions.length);
447: System.arraycopy(rgba, 0, tmpRgba, 1, rgba.length);
448: fractions = tmpFractions;
449: rgba = tmpRgba;
450: }
451:
452: // Check that the last stop is 1. If not we duplicate the last stop.
453: if (fractions[fractions.length - 1] != 1) {
454: float[] tmpFractions = new float[fractions.length + 1];
455: int[] tmpRgba = new int[rgba.length + 1];
456: tmpFractions[tmpFractions.length - 1] = 1;
457: tmpRgba[tmpRgba.length - 1] = rgba[rgba.length - 1];
458: System.arraycopy(fractions, 0, tmpFractions, 0,
459: fractions.length);
460: System.arraycopy(rgba, 0, tmpRgba, 0, rgba.length);
461: fractions = tmpFractions;
462: rgba = tmpRgba;
463: }
464:
465: lastColorMapFractions = fractions;
466: lastColorMapRGBA = rgba;
467: }
468:
469: /**
470: * Should be called when the paint should recompute itself and
471: * notify its references.
472: */
473: protected void onPaintChange() {
474: computedPaint = null;
475: lastColorMapFractions = null;
476: lastColorMapRGBA = null;
477:
478: if (loaded) {
479: notifyPaintChange();
480: }
481: }
482: }
|