SvgGradientServer.cs 7.3 KB
Newer Older
davescriven's avatar
davescriven committed
1
2
3
4
5
6
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;

namespace Svg
{
7
8
9
    /// <summary>
    /// Provides the base class for all paint servers that wish to render a gradient.
    /// </summary>
davescriven's avatar
davescriven committed
10
11
    public abstract class SvgGradientServer : SvgPaintServer
    {
12
        private SvgCoordinateUnits _gradientUnits;
davescriven's avatar
davescriven committed
13
        private SvgGradientSpreadMethod _spreadMethod = SvgGradientSpreadMethod.Pad;
14
        private SvgGradientServer _inheritGradient;
davescriven's avatar
davescriven committed
15
16
        private List<SvgGradientStop> _stops;

17
18
19
        /// <summary>
        /// Initializes a new instance of the <see cref="SvgGradientServer"/> class.
        /// </summary>
davescriven's avatar
davescriven committed
20
21
        internal SvgGradientServer()
        {
22
            this.GradientUnits = SvgCoordinateUnits.ObjectBoundingBox;
davescriven's avatar
davescriven committed
23
24
25
            this._stops = new List<SvgGradientStop>();
        }

26
27
28
29
30
31
        /// <summary>
        /// Called by the underlying <see cref="SvgElement"/> when an element has been added to the
        /// <see cref="Children"/> collection.
        /// </summary>
        /// <param name="child">The <see cref="SvgElement"/> that has been added.</param>
        /// <param name="index">An <see cref="int"/> representing the index where the element was added to the collection.</param>
32
        protected override void AddElement(SvgElement child, int index)
davescriven's avatar
davescriven committed
33
34
        {
            if (child is SvgGradientStop)
35
            {
davescriven's avatar
davescriven committed
36
                this.Stops.Add((SvgGradientStop)child);
37
38
            }

39
            base.AddElement(child, index);
davescriven's avatar
davescriven committed
40
41
        }

42
43
44
45
46
        /// <summary>
        /// Called by the underlying <see cref="SvgElement"/> when an element has been removed from the
        /// <see cref="Children"/> collection.
        /// </summary>
        /// <param name="child">The <see cref="SvgElement"/> that has been removed.</param>
47
        protected override void RemoveElement(SvgElement child)
davescriven's avatar
davescriven committed
48
49
        {
            if (child is SvgGradientStop)
50
51
52
53
            {
                this.Stops.Remove((SvgGradientStop)child);
            }

54
            base.RemoveElement(child);
davescriven's avatar
davescriven committed
55
56
        }

57
58
59
        /// <summary>
        /// Gets the ramp of colors to use on a gradient.
        /// </summary>
davescriven's avatar
davescriven committed
60
61
62
63
64
        public List<SvgGradientStop> Stops
        {
            get { return this._stops; }
        }

65
66
67
        /// <summary>
        /// Specifies what happens if the gradient starts or ends inside the bounds of the target rectangle.
        /// </summary>
davescriven's avatar
davescriven committed
68
69
70
71
72
73
74
        [SvgAttribute("spreadMethod")]
        public SvgGradientSpreadMethod SpreadMethod
        {
            get { return this._spreadMethod; }
            set { this._spreadMethod = value; }
        }

75
76
77
        /// <summary>
        /// Gets or sets the coordinate system of the gradient.
        /// </summary>
davescriven's avatar
davescriven committed
78
        [SvgAttribute("gradientUnits")]
79
        public SvgCoordinateUnits GradientUnits
davescriven's avatar
davescriven committed
80
81
82
83
84
        {
            get { return this._gradientUnits; }
            set { this._gradientUnits = value; }
        }

85
86
87
        /// <summary>
        /// Gets or sets another gradient fill from which to inherit the stops from.
        /// </summary>
88
        [SvgAttribute("href")]
89
        public SvgGradientServer InheritGradient
davescriven's avatar
davescriven committed
90
91
        {
            get { return this._inheritGradient; }
92
93
94
            set 
            { 
                this._inheritGradient = value;
95
                this.InheritStops();
96
            }
davescriven's avatar
davescriven committed
97
98
        }

99
        /// <summary>
100
        /// Gets a <see cref="ColorBlend"/> representing the <see cref="SvgGradientServer"/>'s gradient stops.
101
102
103
        /// </summary>
        /// <param name="owner">The parent <see cref="SvgVisualElement"/>.</param>
        /// <param name="opacity">The opacity of the colour blend.</param>
104
        protected ColorBlend GetColourBlend(SvgVisualElement owner, float opacity, bool radial)
davescriven's avatar
davescriven committed
105
106
107
108
109
110
111
        {
            int colourBlends = this.Stops.Count;
            bool insertStart = false;
            bool insertEnd = false;

            //gradient.Transform = renderingElement.Transforms.Matrix;

112
            //stops should be processed in reverse order if it's a radial gradient
113

davescriven's avatar
davescriven committed
114
115
116
117
118
119
120
            // May need to increase the number of colour blends because the range *must* be from 0.0 to 1.0.
            // E.g. 0.5 - 0.8 isn't valid therefore the rest need to be calculated.

            // If the first stop doesn't start at zero
            if (this.Stops[0].Offset.Value > 0)
            {
                colourBlends++;
121
122
123
124
125
126
127
128
129
                
                if (radial)
                {
                    insertEnd = true;
                }
                else
                {
                    insertStart = true;
                }
davescriven's avatar
davescriven committed
130
131
132
133
134
135
136
            }

            // If the last stop doesn't end at 1 a stop
            float lastValue = this.Stops[this.Stops.Count - 1].Offset.Value;
            if (lastValue < 100 || lastValue < 1)
            {
                colourBlends++;
137
138
139
140
141
142
143
144
                if (radial)
                {
                    insertStart = true;
                }
                else
                {
                    insertEnd = true;
                }
davescriven's avatar
davescriven committed
145
146
            }

147
            ColorBlend blend = new ColorBlend(colourBlends);
davescriven's avatar
davescriven committed
148
149
150
151
152
153
154
155
156

            // Set positions and colour values
            int actualStops = 0;
            float mergedOpacity = 0.0f;
            float position = 0.0f;
            Color colour = Color.Black;

            for (int i = 0; i < colourBlends; i++)
            {
157
                var currentStop = this.Stops[radial ? this.Stops.Count - 1 - actualStops : actualStops];
158
159

                mergedOpacity = opacity * currentStop.Opacity;
160
161
162
163
                position =
                    radial
                    ? 1 - (currentStop.Offset.ToDeviceValue(owner) / owner.Bounds.Width)
                    : (currentStop.Offset.ToDeviceValue(owner) / owner.Bounds.Width);
164
165
166
                colour = Color.FromArgb((int)(mergedOpacity * 255), currentStop.Colour);

                actualStops++;
davescriven's avatar
davescriven committed
167
168
169
170
171

                // Insert this colour before itself at position 0
                if (insertStart && i == 0)
                {
                    blend.Positions[i] = 0.0f;
172
173
174
                    blend.Colors[i] = colour;

                    i++;
davescriven's avatar
davescriven committed
175
176
177
178
179
180
181
182
                }

                blend.Positions[i] = position;
                blend.Colors[i] = colour;

                // Insert this colour after itself at position 0
                if (insertEnd && i == colourBlends - 2)
                {
183
184
185
186
                    i++;

                    blend.Positions[i] = 1.0f;
                    blend.Colors[i] = colour;
davescriven's avatar
davescriven committed
187
188
189
190
191
192
                }
            }

            return blend;
        }

193
194
195
196
        /// <summary>
        // If this gradient contains no stops then it will search any inherited gradients for stops.
        /// </summary>
        protected virtual void InheritStops()
davescriven's avatar
davescriven committed
197
        {
198
            if (this.Stops.Count == 0 && this.InheritGradient != null)
199
            {
200
                _stops.AddRange(this.InheritGradient.Stops);
201
            }
davescriven's avatar
davescriven committed
202
        }
203

204

205
206
207
		public override SvgElement DeepCopy<T>()
		{
			var newObj = base.DeepCopy<T>() as SvgGradientServer;
208
209
			
            newObj.SpreadMethod = this.SpreadMethod;
210
211
212
			newObj.GradientUnits = this.GradientUnits;
			newObj.InheritGradient = this.InheritGradient;

213
			return newObj;
214
		}
davescriven's avatar
davescriven committed
215
216
    }
}