SvgGradientServer.cs 8.74 KB
Newer Older
James Welle's avatar
James Welle committed
1
using System;
davescriven's avatar
davescriven committed
2
3
4
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
James Welle's avatar
James Welle committed
5
using Svg.Transforms;
davescriven's avatar
davescriven committed
6
7
8

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

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

29
30
31
32
33
34
        /// <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>
35
        protected override void AddElement(SvgElement child, int index)
davescriven's avatar
davescriven committed
36
37
        {
            if (child is SvgGradientStop)
38
            {
davescriven's avatar
davescriven committed
39
                this.Stops.Add((SvgGradientStop)child);
40
41
            }

42
            base.AddElement(child, index);
davescriven's avatar
davescriven committed
43
44
        }

45
46
47
48
49
        /// <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>
50
        protected override void RemoveElement(SvgElement child)
davescriven's avatar
davescriven committed
51
52
        {
            if (child is SvgGradientStop)
53
54
55
56
            {
                this.Stops.Remove((SvgGradientStop)child);
            }

57
            base.RemoveElement(child);
davescriven's avatar
davescriven committed
58
59
        }

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

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

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

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

James Welle's avatar
James Welle committed
101
102
103
        [SvgAttribute("gradientTransform")]
        public SvgTransformCollection GradientTransform
        {
Eric Domke's avatar
Eric Domke committed
104
105
            get { return (this.Attributes.GetAttribute<SvgTransformCollection>("gradientTransform")); }
            set { this.Attributes["gradientTransform"] = value; }
James Welle's avatar
James Welle committed
106
107
        }

Eric Domke's avatar
Eric Domke committed
108
        protected Matrix EffectiveGradientTransform
James Welle's avatar
James Welle committed
109
110
111
112
113
114
115
116
117
118
119
120
121
        {
            get
            {
                var transform = new Matrix();

                if (GradientTransform != null)
                {
                    transform.Multiply(GradientTransform.GetMatrix());
                }
                return transform;
            }
        }

122
        /// <summary>
123
        /// Gets a <see cref="ColorBlend"/> representing the <see cref="SvgGradientServer"/>'s gradient stops.
124
125
126
        /// </summary>
        /// <param name="owner">The parent <see cref="SvgVisualElement"/>.</param>
        /// <param name="opacity">The opacity of the colour blend.</param>
Eric Domke's avatar
Eric Domke committed
127
        protected ColorBlend GetColorBlend(ISvgRenderer renderer, float opacity, bool radial)
davescriven's avatar
davescriven committed
128
129
130
131
132
133
134
        {
            int colourBlends = this.Stops.Count;
            bool insertStart = false;
            bool insertEnd = false;

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

135
            //stops should be processed in reverse order if it's a radial gradient
136

davescriven's avatar
davescriven committed
137
138
139
140
141
142
143
            // 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++;
144
145
146
147
148
149
150
151
152
                
                if (radial)
                {
                    insertEnd = true;
                }
                else
                {
                    insertStart = true;
                }
davescriven's avatar
davescriven committed
153
154
155
156
157
158
159
            }

            // 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++;
160
161
162
163
164
165
166
167
                if (radial)
                {
                    insertStart = true;
                }
                else
                {
                    insertEnd = true;
                }
davescriven's avatar
davescriven committed
168
169
            }

170
            ColorBlend blend = new ColorBlend(colourBlends);
davescriven's avatar
davescriven committed
171
172
173
174
175

            // Set positions and colour values
            int actualStops = 0;
            float mergedOpacity = 0.0f;
            float position = 0.0f;
176
            Color colour = System.Drawing.Color.Black;
davescriven's avatar
davescriven committed
177
178
179

            for (int i = 0; i < colourBlends; i++)
            {
180
                var currentStop = this.Stops[radial ? this.Stops.Count - 1 - actualStops : actualStops];
Tebjan Halm's avatar
Tebjan Halm committed
181
                var boundWidth = renderer.GetBoundable().Bounds.Width;
182

183
                mergedOpacity = opacity * currentStop.Opacity;
184
185
                position =
                    radial
186
187
                    ? 1 - (currentStop.Offset.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this) / boundWidth)
                    : (currentStop.Offset.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this) / boundWidth);
188
                colour = System.Drawing.Color.FromArgb((int)Math.Round(mergedOpacity * 255), currentStop.GetColor(this));
189
190

                actualStops++;
davescriven's avatar
davescriven committed
191
192
193
194
195

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

                    i++;
davescriven's avatar
davescriven committed
199
200
201
202
203
204
205
206
                }

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

                // Insert this colour after itself at position 0
                if (insertEnd && i == colourBlends - 2)
                {
207
208
209
210
                    i++;

                    blend.Positions[i] = 1.0f;
                    blend.Colors[i] = colour;
davescriven's avatar
davescriven committed
211
212
213
214
215
216
                }
            }

            return blend;
        }

217
        protected void LoadStops(SvgVisualElement parent)
davescriven's avatar
davescriven committed
218
        {
219
            var core = SvgDeferredPaintServer.TryGet<SvgGradientServer>(_inheritGradient, parent);
Eric Domke's avatar
Eric Domke committed
220
            if (this.Stops.Count == 0 && core != null)
221
            {
Eric Domke's avatar
Eric Domke committed
222
                _stops.AddRange(core.Stops);
223
            }
davescriven's avatar
davescriven committed
224
        }
225

James Welle's avatar
James Welle committed
226
227
228
229
230
231
232
233
234
235
236
237
238
239
        protected static double CalculateDistance(PointF first, PointF second)
        {
            return Math.Sqrt(Math.Pow(first.X - second.X, 2) + Math.Pow(first.Y - second.Y, 2));
        }

        protected static float CalculateLength(PointF vector)
        {
            return (float)Math.Sqrt(Math.Pow(vector.X, 2) + Math.Pow(vector.Y, 2));
        }

        public override SvgElement DeepCopy<T>()
        {
            var newObj = base.DeepCopy<T>() as SvgGradientServer;
            
240
            newObj.SpreadMethod = this.SpreadMethod;
James Welle's avatar
James Welle committed
241
242
243
            newObj.GradientUnits = this.GradientUnits;
            newObj.InheritGradient = this.InheritGradient;
            newObj.GradientTransform = this.GradientTransform;
244

James Welle's avatar
James Welle committed
245
246
            return newObj;
        }
247
248
249
250
251

        public SvgCoordinateUnits GetUnits()
        {
            return _gradientUnits;
        }
davescriven's avatar
davescriven committed
252
253
    }
}