SvgFilter.cs 5.75 KB
Newer Older
davescriven's avatar
davescriven committed
1
using System;
2
using System.Linq;
davescriven's avatar
davescriven committed
3
4
5
6
7
8
9
10
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;

namespace Svg.FilterEffects
{
11
12
13
14
15
    /// <summary>
    /// A filter effect consists of a series of graphics operations that are applied to a given source graphic to produce a modified graphical result.
    /// </summary>
    [SvgElement("filter")]
    public sealed class SvgFilter : SvgElement
davescriven's avatar
davescriven committed
16
    {
17
18
        private Bitmap sourceGraphic;
        private Bitmap sourceAlpha;
davescriven's avatar
davescriven committed
19

20
21
22
        /// <summary>
        /// Gets or sets the width of the resulting filter graphic.
        /// </summary>
davescriven's avatar
davescriven committed
23
24
25
        [SvgAttribute("width")]
        public SvgUnit Width
        {
26
27
            get { return this.Attributes.GetAttribute<SvgUnit>("width"); }
            set { this.Attributes["width"] = value; }
davescriven's avatar
davescriven committed
28
29
        }

30
31
32
        /// <summary>
        /// Gets or sets the height of the resulting filter graphic.
        /// </summary>
davescriven's avatar
davescriven committed
33
34
35
        [SvgAttribute("height")]
        public SvgUnit Height
        {
36
37
            get { return this.Attributes.GetAttribute<SvgUnit>("height"); }
            set { this.Attributes["height"] = value; }
davescriven's avatar
davescriven committed
38
39
        }

40
        internal Dictionary<string, Func<SvgVisualElement, SvgRenderer, Bitmap>> Buffer { get; private set; }
davescriven's avatar
davescriven committed
41

42
43
44
        /// <summary>
        /// Initializes a new instance of the <see cref="SvgFilter"/> class.
        /// </summary>
davescriven's avatar
davescriven committed
45
46
        public SvgFilter()
        {
47
            this.Buffer = new Dictionary<string, Func<SvgVisualElement, SvgRenderer, Bitmap>>();
davescriven's avatar
davescriven committed
48
49
        }

50
51
52
53
        /// <summary>
        /// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="SvgRenderer"/> object.
        /// </summary>
        /// <param name="renderer">The <see cref="SvgRenderer"/> object to render to.</param>
54
        protected override void Render(SvgRenderer renderer)
davescriven's avatar
davescriven committed
55
56
57
58
        {
            // Do nothing
        }

59
60
61
62
63
64
        /// <summary>
        /// Creates a new object that is a copy of the current instance.
        /// </summary>
        /// <returns>
        /// A new object that is a copy of this instance.
        /// </returns>
davescriven's avatar
davescriven committed
65
66
67
68
69
        public override object Clone()
        {
            return (SvgFilter)this.MemberwiseClone();
        }

70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
        public void ApplyFilter(SvgVisualElement element, SvgRenderer renderer)
        {
            this.Buffer.Clear();
            this.PopulateDefaults(element, renderer);

            IEnumerable<SvgFilterPrimitive> primitives = this.Children.OfType<SvgFilterPrimitive>();

            if (primitives.Count() > 0)
            {
                foreach (var primitive in primitives)
                {
                    this.Buffer.Add(primitive.Result, (e, r) => primitive.Process());
                }

                // Render the final filtered image
                renderer.DrawImageUnscaled(this.Buffer.Last().Value(element, renderer), new Point(0, 0));
            }
        }

        private void PopulateDefaults(SvgVisualElement element, SvgRenderer renderer)
        {
            this.ResetDefaults();

            this.Buffer.Add(SvgFilterPrimitive.SourceGraphic, this.CreateSourceGraphic);
            this.Buffer.Add(SvgFilterPrimitive.SourceAlpha, this.CreateSourceAlpha);
        }

        #region Defaults

        private void ResetDefaults()
davescriven's avatar
davescriven committed
100
        {
101
102
103
104
105
            if (this.sourceGraphic != null)
            {
                this.sourceGraphic.Dispose();
                this.sourceGraphic = null;
            }
davescriven's avatar
davescriven committed
106

107
            if (this.sourceAlpha != null)
davescriven's avatar
davescriven committed
108
            {
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
                this.sourceAlpha.Dispose();
                this.sourceAlpha = null;
            }
        }

        private Bitmap CreateSourceGraphic(SvgVisualElement element, SvgRenderer renderer)
        {
            if (this.sourceGraphic == null)
            {
                RectangleF bounds = element.Path.GetBounds();
                this.sourceGraphic = new Bitmap((int)bounds.Width, (int)bounds.Height);

                using (var graphics = Graphics.FromImage(this.sourceGraphic))
                {
                    graphics.Clip = renderer.Clip;
                    graphics.Transform = renderer.Transform;

                    element.RenderElement(SvgRenderer.FromGraphics(graphics));

                    graphics.Save();
                }
davescriven's avatar
davescriven committed
130
131
            }

132
            return this.sourceGraphic;
davescriven's avatar
davescriven committed
133
134
        }

135
        private Bitmap CreateSourceAlpha(SvgVisualElement element, SvgRenderer renderer)
davescriven's avatar
davescriven committed
136
        {
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
            if (this.sourceAlpha == null)
            {
                Bitmap source = this.Buffer[SvgFilterPrimitive.SourceGraphic](element, renderer);

                float[][] colorMatrixElements = {
                   new float[] {0, 0, 0, 0, 0},        // red
                   new float[] {0, 0, 0, 0, 0},        // green
                   new float[] {0, 0, 0, 0, 0},        // blue
                   new float[] {0, 0, 0, 1, 1},        // alpha
                   new float[] {0, 0, 0, 0, 0} };    // translations

                var matrix = new ColorMatrix(colorMatrixElements);

                ImageAttributes attributes = new ImageAttributes();
                attributes.SetColorMatrix(matrix);

                this.sourceAlpha = new Bitmap(source.Width, source.Height);

                using (var graphics = Graphics.FromImage(this.sourceAlpha))
                {

                    graphics.DrawImage(source, new Rectangle(0, 0, source.Width, source.Height), 0, 0,
                          source.Width, source.Height, GraphicsUnit.Pixel, attributes);
                    graphics.Save();
                }
            }
davescriven's avatar
davescriven committed
163

164
            return this.sourceAlpha;
davescriven's avatar
davescriven committed
165
        }
166
        #endregion
davescriven's avatar
davescriven committed
167
168
    }
}