SvgPatternServer.cs 11 KB
Newer Older
davescriven's avatar
davescriven committed
1
2
3
4
5
6
7
8
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing.Drawing2D;
using System.Drawing;
using System.ComponentModel;

using Svg.Transforms;
Eric Domke's avatar
Eric Domke committed
9
using System.Linq;
davescriven's avatar
davescriven committed
10
11
12

namespace Svg
{
13
14
15
16
    /// <summary>
    /// A pattern is used to fill or stroke an object using a pre-defined graphic object which can be replicated ("tiled") at fixed intervals in x and y to cover the areas to be painted.
    /// </summary>
    [SvgElement("pattern")]
17
    public sealed class SvgPatternServer : SvgPaintServer, ISvgViewPort, ISvgSupportsCoordinateUnits
davescriven's avatar
davescriven committed
18
19
20
21
22
    {
        private SvgUnit _width;
        private SvgUnit _height;
        private SvgUnit _x;
        private SvgUnit _y;
Eric Domke's avatar
Eric Domke committed
23
        private SvgPaintServer _inheritGradient;
davescriven's avatar
davescriven committed
24
        private SvgViewBox _viewBox;
Eric Domke's avatar
Eric Domke committed
25
26
        private SvgCoordinateUnits _patternUnits = SvgCoordinateUnits.Inherit;
        private SvgCoordinateUnits _patternContentUnits = SvgCoordinateUnits.Inherit;
davescriven's avatar
davescriven committed
27

28
29
30
31
32
33
        [SvgAttribute("overflow")]
        public SvgOverflow Overflow
        {
            get { return this.Attributes.GetAttribute<SvgOverflow>("overflow"); }
            set { this.Attributes["overflow"] = value; }
        }
34
35


36
37
38
39
        /// <summary>
        /// Specifies a supplemental transformation which is applied on top of any 
        /// transformations necessary to create a new pattern coordinate system.
        /// </summary>
davescriven's avatar
davescriven committed
40
41
42
43
44
45
        [SvgAttribute("viewBox")]
        public SvgViewBox ViewBox
        {
            get { return this._viewBox; }
            set { this._viewBox = value; }
        }
Tebjan Halm's avatar
Tebjan Halm committed
46
47
48
49
50
51
52
        
        /// <summary>
        /// Gets or sets the aspect of the viewport.
        /// </summary>
        /// <value></value>
        [SvgAttribute("preserveAspectRatio")]
        public SvgAspectRatio AspectRatio 
53
54
55
56
        {
            get;
            set;
        }
davescriven's avatar
davescriven committed
57

58
59
60
        /// <summary>
        /// Gets or sets the width of the pattern.
        /// </summary>
davescriven's avatar
davescriven committed
61
62
63
64
65
66
67
        [SvgAttribute("width")]
        public SvgUnit Width
        {
            get { return this._width; }
            set { this._width = value; }
        }

68
69
70
71
72
73
74
75
76
77
78
79
80
        /// <summary>
        /// Gets or sets the width of the pattern.
        /// </summary>
        [SvgAttribute("patternUnits")]
        public SvgCoordinateUnits PatternUnits
        {
            get { return this._patternUnits; }
            set { this._patternUnits = value; }
        }

        /// <summary>
        /// Gets or sets the width of the pattern.
        /// </summary>
Eric Domke's avatar
Eric Domke committed
81
        [SvgAttribute("patternContentUnits")]
82
83
84
85
86
87
        public SvgCoordinateUnits PatternContentUnits
        {
            get { return this._patternContentUnits; }
            set { this._patternContentUnits = value; }
        }

88
89
90
        /// <summary>
        /// Gets or sets the height of the pattern.
        /// </summary>
davescriven's avatar
davescriven committed
91
92
93
94
95
96
97
        [SvgAttribute("height")]
        public SvgUnit Height
        {
            get { return this._height; }
            set { this._height = value; }
        }

98
99
100
        /// <summary>
        /// Gets or sets the X-axis location of the pattern.
        /// </summary>
davescriven's avatar
davescriven committed
101
102
103
104
105
106
107
        [SvgAttribute("x")]
        public SvgUnit X
        {
            get { return this._x; }
            set { this._x = value; }
        }

108
109
110
        /// <summary>
        /// Gets or sets the Y-axis location of the pattern.
        /// </summary>
davescriven's avatar
davescriven committed
111
112
113
114
115
116
117
        [SvgAttribute("y")]
        public SvgUnit Y
        {
            get { return this._y; }
            set { this._y = value; }
        }

Eric Domke's avatar
Eric Domke committed
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
        /// <summary>
        /// Gets or sets another gradient fill from which to inherit the stops from.
        /// </summary>
        [SvgAttribute("href", SvgAttributeAttribute.XLinkNamespace)]
        public SvgPaintServer InheritGradient
        {
            get { return this._inheritGradient; }
            set
            {
                this._inheritGradient = value;
            }
        }

        [SvgAttribute("patternTransform")]
        public SvgTransformCollection PatternTransform
        {
134
135
            get { return (this.Attributes.GetAttribute<SvgTransformCollection>("patternTransform")); }
            set { this.Attributes["patternTransform"] = value; }
Eric Domke's avatar
Eric Domke committed
136
137
        }

138
        private Matrix EffectivePatternTransform
Eric Domke's avatar
Eric Domke committed
139
140
141
142
143
144
145
146
147
148
149
150
151
        {
            get
            {
                var transform = new Matrix();

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

152
153
154
        /// <summary>
        /// Initializes a new instance of the <see cref="SvgPatternServer"/> class.
        /// </summary>
davescriven's avatar
davescriven committed
155
156
        public SvgPatternServer()
        {
Eric Domke's avatar
Eric Domke committed
157
158
159
160
161
162
163
164
165
166
167
            this._x = SvgUnit.None;
            this._y = SvgUnit.None;
            this._width = SvgUnit.None;
            this._height = SvgUnit.None;
        }

        private SvgUnit NormalizeUnit(SvgUnit orig)
        {
            return (orig.Type == SvgUnitType.Percentage && this.PatternUnits == SvgCoordinateUnits.ObjectBoundingBox ?
                    new SvgUnit(SvgUnitType.User, orig.Value / 100) :
                    orig);
davescriven's avatar
davescriven committed
168
169
        }

170
171
172
        /// <summary>
        /// Gets a <see cref="Brush"/> representing the current paint server.
        /// </summary>
173
        /// <param name="renderingElement">The owner <see cref="SvgVisualElement"/>.</param>
174
        /// <param name="opacity">The opacity of the brush.</param>
Eric Domke's avatar
Eric Domke committed
175
        public override Brush GetBrush(SvgVisualElement renderingElement, ISvgRenderer renderer, float opacity, bool forStroke = false)
davescriven's avatar
davescriven committed
176
        {
Eric Domke's avatar
Eric Domke committed
177
178
179
180
181
182
183
184
185
186
187
188
189
            var chain = new List<SvgPatternServer>();
            var curr = this;
            while (curr != null)
            {
                chain.Add(curr);
                curr = SvgDeferredPaintServer.TryGet<SvgPatternServer>(curr._inheritGradient, renderingElement);
            }

            var childElem = chain.Where((p) => p.Children != null && p.Children.Count > 0).FirstOrDefault();
            if (childElem == null) return null;
            var widthElem = chain.Where((p) => p.Width != null && p.Width != SvgUnit.None).FirstOrDefault();
            var heightElem = chain.Where((p) => p.Height != null && p.Height != SvgUnit.None).FirstOrDefault();
            if (widthElem == null && heightElem == null) return null;
davescriven's avatar
davescriven committed
190

Eric Domke's avatar
Eric Domke committed
191
192
193
194
195
196
197
198
199
200
201
            var viewBoxElem = chain.Where((p) => p.ViewBox != null && p.ViewBox != SvgViewBox.Empty).FirstOrDefault();
            var viewBox = viewBoxElem == null ? SvgViewBox.Empty : viewBoxElem.ViewBox;
            var xElem = chain.Where((p) => p.X != null && p.X != SvgUnit.None).FirstOrDefault();
            var yElem = chain.Where((p) => p.Y != null && p.Y != SvgUnit.None).FirstOrDefault();
            var xUnit = xElem == null ? SvgUnit.Empty : xElem.X;
            var yUnit = yElem == null ? SvgUnit.Empty : yElem.Y;

            var patternUnitElem = chain.Where((p) => p.PatternUnits != SvgCoordinateUnits.Inherit).FirstOrDefault();
            var patternUnits = (patternUnitElem == null ? SvgCoordinateUnits.ObjectBoundingBox : patternUnitElem.PatternUnits);
            var patternContentUnitElem = chain.Where((p) => p.PatternContentUnits != SvgCoordinateUnits.Inherit).FirstOrDefault();
            var patternContentUnits = (patternContentUnitElem == null ? SvgCoordinateUnits.UserSpaceOnUse : patternContentUnitElem.PatternContentUnits);
davescriven's avatar
davescriven committed
202

203
            try
davescriven's avatar
davescriven committed
204
            {
Eric Domke's avatar
Eric Domke committed
205
                if (patternUnits == SvgCoordinateUnits.ObjectBoundingBox) renderer.SetBoundable(renderingElement);
davescriven's avatar
davescriven committed
206

Eric Domke's avatar
Eric Domke committed
207
                using (var patternMatrix = new Matrix())
davescriven's avatar
davescriven committed
208
                {
Eric Domke's avatar
Eric Domke committed
209
210
211
                    var bounds = renderer.GetBoundable().Bounds;
                    var xScale = (patternUnits == SvgCoordinateUnits.ObjectBoundingBox ? bounds.Width : 1);
                    var yScale = (patternUnits == SvgCoordinateUnits.ObjectBoundingBox ? bounds.Height : 1);
212

Eric Domke's avatar
Eric Domke committed
213
214
                    float x = xScale * NormalizeUnit(xUnit).ToDeviceValue(renderer, UnitRenderingType.Horizontal, this);
                    float y = yScale * NormalizeUnit(yUnit).ToDeviceValue(renderer, UnitRenderingType.Vertical, this);
davescriven's avatar
davescriven committed
215

Eric Domke's avatar
Eric Domke committed
216
217
                    float width = xScale * NormalizeUnit(widthElem.Width).ToDeviceValue(renderer, UnitRenderingType.Horizontal, this);
                    float height = yScale * NormalizeUnit(heightElem.Height).ToDeviceValue(renderer, UnitRenderingType.Vertical, this);
davescriven's avatar
davescriven committed
218

Eric Domke's avatar
Eric Domke committed
219
220
221
222
223
                    // Apply a scale if needed
                    patternMatrix.Scale((patternContentUnits == SvgCoordinateUnits.ObjectBoundingBox ? bounds.Width : 1) * 
                                        (viewBox.Width > 0 ? width / viewBox.Width : 1),
                                        (patternContentUnits == SvgCoordinateUnits.ObjectBoundingBox ? bounds.Height : 1) * 
                                        (viewBox.Height > 0 ? height / viewBox.Height : 1), MatrixOrder.Prepend);
Eric Domke's avatar
Eric Domke committed
224
                    
Eric Domke's avatar
Eric Domke committed
225
226
                    Bitmap image = new Bitmap((int)width, (int)height);
                    using (var iRenderer = SvgRenderer.FromImage(image))
227
                    {
Eric Domke's avatar
Eric Domke committed
228
229
230
231
232
233
                        iRenderer.SetBoundable((_patternContentUnits == SvgCoordinateUnits.ObjectBoundingBox) ? new GenericBoundable(0, 0, width, height) : renderer.GetBoundable());
                        iRenderer.Transform = patternMatrix;
                        iRenderer.SmoothingMode = SmoothingMode.AntiAlias;
                        iRenderer.SetClip(new Region(new RectangleF(0, 0,
                            viewBox.Width > 0 ? viewBox.Width : width,
                            viewBox.Height > 0 ? viewBox.Height : height)));
davescriven's avatar
davescriven committed
234

Eric Domke's avatar
Eric Domke committed
235
236
237
238
239
                        foreach (SvgElement child in childElem.Children)
                        {
                            child.RenderElement(iRenderer);
                        }
                    }
davescriven's avatar
davescriven committed
240

Eric Domke's avatar
Eric Domke committed
241
242
243
244
245
246
                    TextureBrush textureBrush = new TextureBrush(image);
                    var brushTransform = EffectivePatternTransform.Clone();
                    brushTransform.Translate(x, y, MatrixOrder.Append);
                    textureBrush.Transform = brushTransform;
                    return textureBrush;
                }
247
248
249
250
251
            }
            finally
            {
                if (this.PatternUnits == SvgCoordinateUnits.ObjectBoundingBox) renderer.PopBoundable();
            }
davescriven's avatar
davescriven committed
252
        }
253

254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
        public override SvgElement DeepCopy()
        {
            return DeepCopy<SvgPatternServer>();
        }


        public override SvgElement DeepCopy<T>()
        {
            var newObj = base.DeepCopy<T>() as SvgPatternServer;
            newObj.Overflow = this.Overflow;
            newObj.ViewBox = this.ViewBox;
            newObj.AspectRatio = this.AspectRatio;
            newObj.X = this.X;
            newObj.Y = this.Y;
            newObj.Width = this.Width;
            newObj.Height = this.Height;
            return newObj;

        }
273
274
275
276
277

        public SvgCoordinateUnits GetUnits()
        {
            return _patternUnits;
        }
davescriven's avatar
davescriven committed
278
279
    }
}