1////////////////////////////////////////////////////////////////////////////////
2//
3//  ADOBE SYSTEMS INCORPORATED
4//  Copyright 2005-2007 Adobe Systems Incorporated
5//  All Rights Reserved.
6//
7//  NOTICE: Adobe permits you to use, modify, and distribute this file
8//  in accordance with the terms of the license agreement accompanying it.
9//
10////////////////////////////////////////////////////////////////////////////////
11
12package mx.graphics
13{
14
15import flash.events.Event;
16import flash.events.EventDispatcher;
17import flash.geom.Matrix;
18
19import mx.core.mx_internal;
20import mx.events.PropertyChangeEvent;
21import mx.geom.CompoundTransform;
22
23use namespace mx_internal;
24
25[DefaultProperty("entries")]
26
27/**
28 *  The GradientBase class is the base class for
29 *  LinearGradient, LinearGradientStroke, and RadialGradient.
30 *
31 *  @langversion 3.0
32 *  @playerversion Flash 9
33 *  @playerversion AIR 1.1
34 *  @productversion Flex 3
35 */
36public class GradientBase extends EventDispatcher
37{
38    //--------------------------------------------------------------------------
39    //
40    //  Constructor
41    //
42    //--------------------------------------------------------------------------
43
44    /**
45     *  Constructor.
46     *
47     *  @langversion 3.0
48     *  @playerversion Flash 9
49     *  @playerversion AIR 1.1
50     *  @productversion Flex 3
51     */
52    public function GradientBase()
53    {
54        super();
55    }
56
57    //--------------------------------------------------------------------------
58    //
59    //  Variables
60    //
61    //--------------------------------------------------------------------------
62
63    /**
64     *  @private
65     */
66    mx_internal var colors:Array /* of uint */ = [];
67
68    /**
69     *  @private
70     */
71    mx_internal var ratios:Array /* of Number */ = [];
72
73    /**
74     *  @private
75     */
76    mx_internal var alphas:Array /* of Number */ = [];
77
78    //--------------------------------------------------------------------------
79    //
80    //  Class Properties
81    //
82    //--------------------------------------------------------------------------
83
84
85    /**
86     *  Value of the width and height of the untransformed gradient
87     *
88     *  @langversion 3.0
89     *  @playerversion Flash 10
90     *  @playerversion AIR 1.5
91     *  @productversion Flex 4
92     */
93    public static const GRADIENT_DIMENSION:Number = 1638.4;
94
95    //--------------------------------------------------------------------------
96    //
97    //  Properties
98    //
99    //--------------------------------------------------------------------------
100
101    //----------------------------------
102    //  angle
103    //----------------------------------
104
105    /**
106     *  @private
107     *  Storage for the angle property.
108     */
109    mx_internal var _angle:Number;
110
111    [Inspectable(category="General")]
112    [Deprecated(replacement="rotation")]
113    /**
114     *  By default, the LinearGradientStroke defines a transition
115     *  from left to right across the control.
116     *  Use the <code>angle</code> property to control the transition direction.
117     *  For example, a value of 180.0 causes the transition
118     *  to occur from right to left, rather than from left to right.
119     *
120     *  @default 0.0
121     *
122     *  @langversion 3.0
123     *  @playerversion Flash 9
124     *  @playerversion AIR 1.1
125     *  @productversion Flex 3
126     */
127    public function get angle():Number
128    {
129        return _angle / Math.PI * 180;
130    }
131
132    /**
133     *  @private
134     */
135    public function set angle(value:Number):void
136    {
137        var oldValue:Number = _angle;
138        _angle = value / 180 * Math.PI;
139
140        dispatchGradientChangedEvent("angle", oldValue, _angle);
141    }
142
143    //----------------------------------
144    //  compoundTransform
145    //----------------------------------
146
147    /**
148     *  Holds the matrix and the convenience transform properties (<code>x</code>, <code>y</code>, and <code>rotation</code>).
149     *  The compoundTransform is only created when the <code>matrix</code> property is set.
150     *
151     *  @langversion 3.0
152     *  @playerversion Flash 9
153     *  @playerversion AIR 1.1
154     *  @productversion Flex 3
155     */
156    protected var compoundTransform:CompoundTransform;
157
158    //----------------------------------
159    //  entries
160    //----------------------------------
161
162    /**
163     *  @private
164     *  Storage for the entries property.
165     */
166    private var _entries:Array = [];
167
168    [Bindable("propertyChange")]
169    [Inspectable(category="General", arrayType="mx.graphics.GradientEntry")]
170
171    /**
172     *  An Array of GradientEntry objects
173     *  defining the fill patterns for the gradient fill.
174     *
175     *  @default []
176     *
177     *  @langversion 3.0
178     *  @playerversion Flash 9
179     *  @playerversion AIR 1.1
180     *  @productversion Flex 3
181     */
182    public function get entries():Array
183    {
184        return _entries;
185    }
186
187    /**
188     *  @private
189     */
190    public function set entries(value:Array):void
191    {
192        var oldValue:Array = _entries;
193        _entries = value;
194
195        processEntries();
196
197        dispatchGradientChangedEvent("entries", oldValue, value);
198    }
199
200    //----------------------------------
201    //  interpolationMethod
202    //----------------------------------
203
204    /**
205     *  @private
206     *  Storage for the interpolationMethod property.
207     */
208    private var _interpolationMethod:String = "rgb";
209
210    [Inspectable(category="General", enumeration="rgb,linearRGB", defaultValue="rgb")]
211
212    /**
213     *  A value from the InterpolationMethod class
214     *  that specifies which interpolation method to use.
215     *
216     *  <p>Valid values are <code>InterpolationMethod.LINEAR_RGB</code>
217     *  and <code>InterpolationMethod.RGB</code>.</p>
218     *
219     *  @default InterpolationMethod.RGB
220     *
221     *  @langversion 3.0
222     *  @playerversion Flash 9
223     *  @playerversion AIR 1.1
224     *  @productversion Flex 3
225     */
226    public function get interpolationMethod():String
227    {
228        return _interpolationMethod;
229    }
230
231    /**
232     *  @private
233     */
234    public function set interpolationMethod(value:String):void
235    {
236        var oldValue:String = _interpolationMethod;
237        if (value != oldValue)
238        {
239            _interpolationMethod = value;
240
241            dispatchGradientChangedEvent("interpolationMethod", oldValue, value);
242        }
243    }
244
245    //----------------------------------
246    //  matrix
247    //----------------------------------
248
249    /**
250     *  @private
251     *  Storage for the matrix property.
252     */
253    private var _matrix:Matrix;
254
255    [Inspectable(category="General")]
256
257    /**
258     *  An array of values used for matrix transformation.
259     *
260     *  <p>The gradient <code>scaleX</code> and <code>scaleY</code> properties represent pixels while the Matrix scale properties represent multipliers.
261     *  Thus they are not compatible.
262     *  Another difference is the most of the transform properties (<code>x</code>, <code>y</code>, <code>scaleX</code>, and <code>scaleY</code>)
263     *  support NaN values while the matrix does not. A NaN value means that the gradient will choose an appropriate value.</p>
264     *
265     *  <p>The <code>scaleX</code> and <code>scaleY</code> properties can not be represented by the matrix.
266     *  Once the matrix is set, <code>scaleX</code> and <code>scaleY</code> can no longer be set.
267     *  Also, <code>x</code> and <code>y</code> can not be set to NaN.
268     *  The matrix can be set back to null which also resets all of the convenience transform properties back to their default values.</p>
269     *
270     *  <p>If the matrix is set, then the gradient draw logic will scale the gradient to fit the bounds of the graphic element.
271     *  It will then position the gradient in the upper left corner of the graphic element.
272     *  Finally, it will apply the matrix transformations.</p>
273
274     *  <p>By default, the LinearGradientStroke defines a transition
275     *  from left to right across the control.
276     *  Use the <code>rotation</code> property to control the transition direction.
277     *  For example, a value of 180.0 causes the transition
278     *  to occur from right to left, rather than from left to right.</p>
279     *
280     *  @default null
281     *
282     *  @langversion 3.0
283     *  @playerversion Flash 9
284     *  @playerversion AIR 1.1
285     *  @productversion Flex 3
286     */
287    public function get matrix():Matrix
288    {
289        return compoundTransform ? compoundTransform.matrix : null;
290    }
291
292    /**
293     *  @private
294     */
295    public function set matrix(value:Matrix):void
296    {
297        var oldValue:Matrix = matrix;
298
299        var oldX:Number = x;
300        var oldY:Number = y;
301        var oldRotation:Number = rotation;
302
303        if (value == null)
304        {
305            compoundTransform = null;
306            x = NaN;
307            y = NaN;
308            rotation = 0;
309        }
310        else
311        {
312            // Create the transform if none exists.
313            if (compoundTransform == null)
314                compoundTransform = new CompoundTransform();
315            compoundTransform.matrix = value; // CompoundTransform will create a clone
316
317            dispatchGradientChangedEvent("x", oldX, compoundTransform.x);
318            dispatchGradientChangedEvent("y", oldY, compoundTransform.y);
319            dispatchGradientChangedEvent("rotation", oldRotation, compoundTransform.rotationZ);
320        }
321    }
322
323    //----------------------------------
324    //  rotation
325    //----------------------------------
326
327    /**
328     *  @private
329     *  Storage for the rotation property.
330     */
331    private var _rotation:Number = 0.0;
332
333    [Bindable("propertyChange")]
334    [Inspectable(category="General")]
335
336    /**
337     *  By default, the LinearGradientStroke defines a transition
338     *  from left to right across the control.
339     *  Use the <code>rotation</code> property to control the transition direction.
340     *  For example, a value of 180.0 causes the transition
341     *  to occur from right to left, rather than from left to right.
342     *
343     *  @default 0.0
344     *
345     *  @langversion 3.0
346     *  @playerversion Flash 9
347     *  @playerversion AIR 1.1
348     *  @productversion Flex 3
349     */
350    public function get rotation():Number
351    {
352        return compoundTransform ? compoundTransform.rotationZ : _rotation;
353    }
354
355    /**
356     *  @private
357     */
358    public function set rotation(value:Number):void
359    {
360        if (value != rotation)
361        {
362            var oldValue:Number = rotation;
363
364            if (compoundTransform)
365                compoundTransform.rotationZ = value;
366            else
367                _rotation = value;
368            dispatchGradientChangedEvent("rotation", oldValue, value);
369        }
370    }
371
372    //----------------------------------
373    //  spreadMethod
374    //----------------------------------
375
376    /**
377     *  @private
378     *  Storage for the spreadMethod property.
379     */
380    private var _spreadMethod:String = "pad";
381
382    [Bindable("propertyChange")]
383    [Inspectable(category="General", enumeration="pad,reflect,repeat", defaultValue="pad")]
384
385    /**
386     *  A value from the SpreadMethod class
387     *  that specifies which spread method to use.
388     *
389     *  <p>Valid values are <code>SpreadMethod.PAD</code>,
390     *  <code>SpreadMethod.REFLECT</code>,
391     *  and <code>SpreadMethod.REPEAT</code>.</p>
392     *
393     *  @default SpreadMethod.PAD
394     *
395     *  @langversion 3.0
396     *  @playerversion Flash 9
397     *  @playerversion AIR 1.1
398     *  @productversion Flex 3
399     */
400    public function get spreadMethod():String
401    {
402        return _spreadMethod;
403    }
404
405    /**
406     *  @private
407     */
408    public function set spreadMethod(value:String):void
409    {
410        var oldValue:String = _spreadMethod;
411        if (value != oldValue)
412        {
413            _spreadMethod = value;
414            dispatchGradientChangedEvent("spreadMethod", oldValue, value);
415        }
416    }
417
418    //----------------------------------
419    //  x
420    //----------------------------------
421
422    private var _x:Number;
423
424    [Bindable("propertyChange")]
425    [Inspectable(category="General")]
426
427    /**
428     *  The distance by which to translate each point along the x axis.
429     *
430     *  @langversion 3.0
431     *  @playerversion Flash 9
432     *  @playerversion AIR 1.1
433     *  @productversion Flex 3
434     */
435    public function get x():Number
436    {
437        return compoundTransform ? compoundTransform.x : _x;
438    }
439
440    /**
441     *  @private
442     */
443    public function set x(value:Number):void
444    {
445        var oldValue:Number = x;
446        if (value != oldValue)
447        {
448            if (compoundTransform)
449            {
450                // If we have a compoundTransform, only non-NaN values are allowed
451                if (!isNaN(value))
452                    compoundTransform.x = value;
453            }
454            else
455            {
456                _x = value;
457            }
458            dispatchGradientChangedEvent("x", oldValue, value);
459        }
460    }
461
462    //----------------------------------
463    //  y
464    //----------------------------------
465
466    private var _y:Number;
467
468    [Bindable("propertyChange")]
469    [Inspectable(category="General")]
470
471     /**
472     *  The distance by which to translate each point along the y axis.
473     *
474     *  @langversion 3.0
475     *  @playerversion Flash 9
476     *  @playerversion AIR 1.1
477     *  @productversion Flex 3
478     */
479    public function get y():Number
480    {
481        return compoundTransform ? compoundTransform.y : _y;
482    }
483
484    /**
485     *  @private
486     */
487    public function set y(value:Number):void
488    {
489        var oldValue:Number = y;
490        if (value != oldValue)
491        {
492            if (compoundTransform)
493            {
494                // If we have a compoundTransform, only non-NaN values are allowed
495                if (!isNaN(value))
496                    compoundTransform.y = value;
497            }
498            else
499            {
500                _y = value;
501            }
502
503            dispatchGradientChangedEvent("y", oldValue, value);
504        }
505    }
506
507    mx_internal function get rotationInRadians():Number
508    {
509        return rotation / 180 * Math.PI;
510    }
511
512    //--------------------------------------------------------------------------
513    //
514    //  Methods
515    //
516    //--------------------------------------------------------------------------
517
518    /**
519     *  @private
520     *  Extract the gradient information in the public <code>entries</code>
521     *  Array into the internal <code>colors</code>, <code>ratios</code>,
522     *  and <code>alphas</code> arrays.
523     */
524    private function processEntries():void
525    {
526        colors = [];
527        ratios = [];
528        alphas = [];
529
530        if (!_entries || _entries.length == 0)
531            return;
532
533        var ratioConvert:Number = 255;
534
535        var i:int;
536
537        var n:int = _entries.length;
538        for (i = 0; i < n; i++)
539        {
540            var e:GradientEntry = _entries[i];
541            e.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE,
542                               entry_propertyChangeHandler, false, 0, true);
543            colors.push(e.color);
544            alphas.push(e.alpha);
545            ratios.push(e.ratio * ratioConvert);
546        }
547
548        if (isNaN(ratios[0]))
549            ratios[0] = 0;
550
551        if (isNaN(ratios[n - 1]))
552            ratios[n - 1] = 255;
553
554        i = 1;
555
556        while (true)
557        {
558            while (i < n && !isNaN(ratios[i]))
559            {
560                i++;
561            }
562
563            if (i == n)
564                break;
565
566            var start:int = i - 1;
567
568            while (i < n && isNaN(ratios[i]))
569            {
570                i++;
571            }
572
573            var br:Number = ratios[start];
574            var tr:Number = ratios[i];
575
576            for (var j:int = 1; j < i - start; j++)
577            {
578                ratios[j] = br + j * (tr - br) / (i - start);
579            }
580        }
581    }
582
583    /**
584     *  Dispatch a gradientChanged event.
585     *
586     *  @langversion 3.0
587     *  @playerversion Flash 9
588     *  @playerversion AIR 1.1
589     *  @productversion Flex 3
590     */
591    mx_internal function dispatchGradientChangedEvent(prop:String,
592                                                      oldValue:*, value:*):void
593    {
594        dispatchEvent(PropertyChangeEvent.createUpdateEvent(this, prop,
595                                                            oldValue, value));
596    }
597
598    //--------------------------------------------------------------------------
599    //
600    //  Event handlers
601    //
602    //--------------------------------------------------------------------------
603
604    /**
605     *  @private
606     */
607    private function entry_propertyChangeHandler(event:Event):void
608    {
609        processEntries();
610
611        dispatchGradientChangedEvent("entries", entries, entries);
612    }
613}
614
615}
616