1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 using System.Runtime.InteropServices;
6 using System.Diagnostics;
7 using System.ComponentModel;
8 using System.Drawing.Drawing2D;
9 using System.Drawing.Imaging;
10 
11 namespace System.Drawing
12 {
13     public sealed class TextureBrush : Brush
14     {
15         // When creating a texture brush from a metafile image, the dstRect
16         // is used to specify the size that the metafile image should be
17         // rendered at in the device units of the destination graphics.
18         // It is NOT used to crop the metafile image, so only the width
19         // and height values matter for metafiles.
20 
TextureBrush(Image bitmap)21         public TextureBrush(Image bitmap) : this(bitmap, WrapMode.Tile)
22         {
23         }
24 
TextureBrush(Image image, WrapMode wrapMode)25         public TextureBrush(Image image, WrapMode wrapMode)
26         {
27             if (image == null)
28             {
29                 throw new ArgumentNullException(nameof(image));
30             }
31 
32             if (wrapMode < WrapMode.Tile || wrapMode > WrapMode.Clamp)
33             {
34                 throw new InvalidEnumArgumentException(nameof(wrapMode), unchecked((int)wrapMode), typeof(WrapMode));
35             }
36 
37             IntPtr brush = IntPtr.Zero;
38             int status = SafeNativeMethods.Gdip.GdipCreateTexture(new HandleRef(image, image.nativeImage),
39                                                    (int)wrapMode,
40                                                    out brush);
41             SafeNativeMethods.Gdip.CheckStatus(status);
42 
43             SetNativeBrushInternal(brush);
44         }
45 
TextureBrush(Image image, WrapMode wrapMode, RectangleF dstRect)46         public TextureBrush(Image image, WrapMode wrapMode, RectangleF dstRect)
47         {
48             if (image == null)
49             {
50                 throw new ArgumentNullException(nameof(image));
51             }
52 
53             if (wrapMode < WrapMode.Tile || wrapMode > WrapMode.Clamp)
54             {
55                 throw new InvalidEnumArgumentException(nameof(wrapMode), unchecked((int)wrapMode), typeof(WrapMode));
56             }
57 
58             IntPtr brush = IntPtr.Zero;
59             int status = SafeNativeMethods.Gdip.GdipCreateTexture2(new HandleRef(image, image.nativeImage),
60                                                     unchecked((int)wrapMode),
61                                                     dstRect.X,
62                                                     dstRect.Y,
63                                                     dstRect.Width,
64                                                     dstRect.Height,
65                                                     out brush);
66             SafeNativeMethods.Gdip.CheckStatus(status);
67 
68             SetNativeBrushInternal(brush);
69         }
70 
TextureBrush(Image image, WrapMode wrapMode, Rectangle dstRect)71         public TextureBrush(Image image, WrapMode wrapMode, Rectangle dstRect)
72         {
73             if (image == null)
74             {
75                 throw new ArgumentNullException(nameof(image));
76             }
77 
78             if (wrapMode < WrapMode.Tile || wrapMode > WrapMode.Clamp)
79             {
80                 throw new InvalidEnumArgumentException(nameof(wrapMode), unchecked((int)wrapMode), typeof(WrapMode));
81             }
82 
83             IntPtr brush = IntPtr.Zero;
84             int status = SafeNativeMethods.Gdip.GdipCreateTexture2I(new HandleRef(image, image.nativeImage),
85                                                      unchecked((int)wrapMode),
86                                                      dstRect.X,
87                                                      dstRect.Y,
88                                                      dstRect.Width,
89                                                      dstRect.Height,
90                                                      out brush);
91             SafeNativeMethods.Gdip.CheckStatus(status);
92 
93             SetNativeBrushInternal(brush);
94         }
95 
TextureBrush(Image image, RectangleF dstRect)96         public TextureBrush(Image image, RectangleF dstRect) : this(image, dstRect, null) { }
97 
TextureBrush(Image image, RectangleF dstRect, ImageAttributes imageAttr)98         public TextureBrush(Image image, RectangleF dstRect, ImageAttributes imageAttr)
99         {
100             if (image == null)
101             {
102                 throw new ArgumentNullException(nameof(image));
103             }
104 
105             IntPtr brush = IntPtr.Zero;
106             int status = SafeNativeMethods.Gdip.GdipCreateTextureIA(new HandleRef(image, image.nativeImage),
107                                                      new HandleRef(imageAttr, (imageAttr == null) ?
108                                                        IntPtr.Zero : imageAttr.nativeImageAttributes),
109                                                      dstRect.X,
110                                                      dstRect.Y,
111                                                      dstRect.Width,
112                                                      dstRect.Height,
113                                                      out brush);
114             SafeNativeMethods.Gdip.CheckStatus(status);
115 
116             SetNativeBrushInternal(brush);
117         }
118 
TextureBrush(Image image, Rectangle dstRect)119         public TextureBrush(Image image, Rectangle dstRect) : this(image, dstRect, null) { }
120 
TextureBrush(Image image, Rectangle dstRect, ImageAttributes imageAttr)121         public TextureBrush(Image image, Rectangle dstRect, ImageAttributes imageAttr)
122         {
123             if (image == null)
124             {
125                 throw new ArgumentNullException(nameof(image));
126             }
127 
128             IntPtr brush = IntPtr.Zero;
129             int status = SafeNativeMethods.Gdip.GdipCreateTextureIAI(new HandleRef(image, image.nativeImage),
130                                                      new HandleRef(imageAttr, (imageAttr == null) ?
131                                                        IntPtr.Zero : imageAttr.nativeImageAttributes),
132                                                      dstRect.X,
133                                                      dstRect.Y,
134                                                      dstRect.Width,
135                                                      dstRect.Height,
136                                                      out brush);
137             SafeNativeMethods.Gdip.CheckStatus(status);
138 
139             SetNativeBrushInternal(brush);
140         }
141 
TextureBrush(IntPtr nativeBrush)142         internal TextureBrush(IntPtr nativeBrush)
143         {
144             Debug.Assert(nativeBrush != IntPtr.Zero, "Initializing native brush with null.");
145             SetNativeBrushInternal(nativeBrush);
146         }
147 
Clone()148         public override object Clone()
149         {
150             IntPtr cloneBrush = IntPtr.Zero;
151             int status = SafeNativeMethods.Gdip.GdipCloneBrush(new HandleRef(this, NativeBrush), out cloneBrush);
152             SafeNativeMethods.Gdip.CheckStatus(status);
153 
154             return new TextureBrush(cloneBrush);
155         }
156 
157         public Matrix Transform
158         {
159             get
160             {
161                 var matrix = new Matrix();
162                 int status = SafeNativeMethods.Gdip.GdipGetTextureTransform(new HandleRef(this, NativeBrush), new HandleRef(matrix, matrix.nativeMatrix));
163                 SafeNativeMethods.Gdip.CheckStatus(status);
164 
165                 return matrix;
166             }
167             set
168             {
169                 if (value == null)
170                 {
171                     throw new ArgumentNullException(nameof(value));
172                 }
173 
174                 int status = SafeNativeMethods.Gdip.GdipSetTextureTransform(new HandleRef(this, NativeBrush), new HandleRef(value, value.nativeMatrix));
175                 SafeNativeMethods.Gdip.CheckStatus(status);
176             }
177         }
178 
179         public WrapMode WrapMode
180         {
181             get
182             {
183                 int mode = 0;
184                 int status = SafeNativeMethods.Gdip.GdipGetTextureWrapMode(new HandleRef(this, NativeBrush), out mode);
185                 SafeNativeMethods.Gdip.CheckStatus(status);
186 
187                 return (WrapMode)mode;
188             }
189             set
190             {
191                 if (value < WrapMode.Tile || value > WrapMode.Clamp)
192                 {
193                     throw new InvalidEnumArgumentException(nameof(value), unchecked((int)value), typeof(WrapMode));
194                 }
195 
196                 int status = SafeNativeMethods.Gdip.GdipSetTextureWrapMode(new HandleRef(this, NativeBrush), unchecked((int)value));
197                 SafeNativeMethods.Gdip.CheckStatus(status);
198             }
199         }
200 
201         public Image Image
202         {
203             get
204             {
205                 IntPtr image;
206                 int status = SafeNativeMethods.Gdip.GdipGetTextureImage(new HandleRef(this, NativeBrush), out image);
207                 SafeNativeMethods.Gdip.CheckStatus(status);
208 
209                 return Image.CreateImageObject(image);
210             }
211         }
212 
ResetTransform()213         public void ResetTransform()
214         {
215             int status = SafeNativeMethods.Gdip.GdipResetTextureTransform(new HandleRef(this, NativeBrush));
216             SafeNativeMethods.Gdip.CheckStatus(status);
217         }
218 
219         public void MultiplyTransform(Matrix matrix) => MultiplyTransform(matrix, MatrixOrder.Prepend);
220 
MultiplyTransform(Matrix matrix, MatrixOrder order)221         public void MultiplyTransform(Matrix matrix, MatrixOrder order)
222         {
223             if (matrix == null)
224             {
225                 throw new ArgumentNullException(nameof(matrix));
226             }
227 
228             // Multiplying the transform by a disposed matrix is a nop in GDI+, but throws
229             // with the libgdiplus backend. Simulate a nop for compatability with GDI+.
230             if (matrix.nativeMatrix == IntPtr.Zero)
231             {
232                 return;
233             }
234 
235             int status = SafeNativeMethods.Gdip.GdipMultiplyTextureTransform(new HandleRef(this, NativeBrush),
236                                                               new HandleRef(matrix, matrix.nativeMatrix),
237                                                               order);
238             SafeNativeMethods.Gdip.CheckStatus(status);
239         }
240 
TranslateTransform(float dx, float dy)241         public void TranslateTransform(float dx, float dy) => TranslateTransform(dx, dy, MatrixOrder.Prepend);
242 
TranslateTransform(float dx, float dy, MatrixOrder order)243         public void TranslateTransform(float dx, float dy, MatrixOrder order)
244         {
245             int status = SafeNativeMethods.Gdip.GdipTranslateTextureTransform(new HandleRef(this, NativeBrush),
246                                                                dx,
247                                                                dy,
248                                                                order);
249             SafeNativeMethods.Gdip.CheckStatus(status);
250         }
251 
ScaleTransform(float sx, float sy)252         public void ScaleTransform(float sx, float sy) => ScaleTransform(sx, sy, MatrixOrder.Prepend);
253 
ScaleTransform(float sx, float sy, MatrixOrder order)254         public void ScaleTransform(float sx, float sy, MatrixOrder order)
255         {
256             int status = SafeNativeMethods.Gdip.GdipScaleTextureTransform(new HandleRef(this, NativeBrush),
257                                                            sx,
258                                                            sy,
259                                                            order);
260             SafeNativeMethods.Gdip.CheckStatus(status);
261         }
262 
RotateTransform(float angle)263         public void RotateTransform(float angle) => RotateTransform(angle, MatrixOrder.Prepend);
264 
RotateTransform(float angle, MatrixOrder order)265         public void RotateTransform(float angle, MatrixOrder order)
266         {
267             int status = SafeNativeMethods.Gdip.GdipRotateTextureTransform(new HandleRef(this, NativeBrush),
268                                                             angle,
269                                                             order);
270             SafeNativeMethods.Gdip.CheckStatus(status);
271         }
272     }
273 }
274