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.ComponentModel;
6 using System.Drawing.Imaging;
7 using System.Drawing.Internal;
8 using System.IO;
9 using System.Runtime.InteropServices;
10 using System.Security.Permissions;
11 
12 namespace System.Drawing
13 {
14     public sealed partial class Bitmap : Image
15     {
16         private static Color s_defaultTransparentColor = Color.LightGray;
17 
Bitmap()18         private Bitmap() { }
19 
20         internal Bitmap(IntPtr ptr) => SetNativeImage(ptr);
21 
Bitmap(string filename)22         public Bitmap(string filename) : this (filename, useIcm: false) { }
23 
Bitmap(string filename, bool useIcm)24         public Bitmap(string filename, bool useIcm)
25         {
26             // GDI+ will read this file multiple times. Get the fully qualified path
27             // so if the app's default directory changes we won't get an error.
28             filename = Path.GetFullPath(filename);
29 
30             IntPtr bitmap = IntPtr.Zero;
31             int status;
32 
33             if (useIcm)
34             {
35                 status = SafeNativeMethods.Gdip.GdipCreateBitmapFromFileICM(filename, out bitmap);
36             }
37             else
38             {
39                 status = SafeNativeMethods.Gdip.GdipCreateBitmapFromFile(filename, out bitmap);
40             }
41             SafeNativeMethods.Gdip.CheckStatus(status);
42 
43             ValidateBitmap(bitmap);
44 
45             SetNativeImage(bitmap);
46             EnsureSave(this, filename, null);
47         }
48 
Bitmap(Stream stream)49         public Bitmap(Stream stream) : this(stream, false) { }
50 
Bitmap(int width, int height)51         public Bitmap(int width, int height) : this(width, height, PixelFormat.Format32bppArgb)
52         {
53         }
54 
Bitmap(int width, int height, Graphics g)55         public Bitmap(int width, int height, Graphics g)
56         {
57             if (g == null)
58             {
59                 throw new ArgumentNullException(nameof(g));
60             }
61 
62             IntPtr bitmap = IntPtr.Zero;
63             int status = SafeNativeMethods.Gdip.GdipCreateBitmapFromGraphics(width, height, new HandleRef(g, g.NativeGraphics), out bitmap);
64             SafeNativeMethods.Gdip.CheckStatus(status);
65 
66             SetNativeImage(bitmap);
67         }
68 
Bitmap(int width, int height, int stride, PixelFormat format, IntPtr scan0)69         public Bitmap(int width, int height, int stride, PixelFormat format, IntPtr scan0)
70         {
71             IntPtr bitmap = IntPtr.Zero;
72             int status = SafeNativeMethods.Gdip.GdipCreateBitmapFromScan0(width, height, stride, unchecked((int)format), new HandleRef(null, scan0), out bitmap);
73             SafeNativeMethods.Gdip.CheckStatus(status);
74 
75             SetNativeImage(bitmap);
76         }
77 
Bitmap(int width, int height, PixelFormat format)78         public Bitmap(int width, int height, PixelFormat format)
79         {
80             IntPtr bitmap = IntPtr.Zero;
81             int status = SafeNativeMethods.Gdip.GdipCreateBitmapFromScan0(width, height, 0, unchecked((int)format), NativeMethods.NullHandleRef, out bitmap);
82             SafeNativeMethods.Gdip.CheckStatus(status);
83 
84             SetNativeImage(bitmap);
85         }
86 
Bitmap(Image original)87         public Bitmap(Image original) : this(original, original.Width, original.Height)
88         {
89         }
90 
Bitmap(Image original, Size newSize)91         public Bitmap(Image original, Size newSize) : this(original, newSize.Width, newSize.Height)
92         {
93         }
94 
Bitmap(Image original, int width, int height)95         public Bitmap(Image original, int width, int height) : this(width, height, PixelFormat.Format32bppArgb)
96         {
97             using (Graphics g = Graphics.FromImage(this))
98             {
99                 g.Clear(Color.Transparent);
100                 g.DrawImage(original, 0, 0, width, height);
101             }
102         }
103 
FromHicon(IntPtr hicon)104         public static Bitmap FromHicon(IntPtr hicon)
105         {
106             IntPtr bitmap = IntPtr.Zero;
107             int status = SafeNativeMethods.Gdip.GdipCreateBitmapFromHICON(new HandleRef(null, hicon), out bitmap);
108             SafeNativeMethods.Gdip.CheckStatus(status);
109 
110             return new Bitmap(bitmap);
111         }
112 
FromResource(IntPtr hinstance, string bitmapName)113         public static Bitmap FromResource(IntPtr hinstance, string bitmapName)
114         {
115             IntPtr bitmap;
116             IntPtr name = Marshal.StringToHGlobalUni(bitmapName);
117             try
118             {
119                 int status = SafeNativeMethods.Gdip.GdipCreateBitmapFromResource(new HandleRef(null, hinstance),
120                                                                   new HandleRef(null, name),
121                                                                   out bitmap);
122                 SafeNativeMethods.Gdip.CheckStatus(status);
123             }
124             finally
125             {
126                 Marshal.FreeHGlobal(name);
127             }
128 
129             return new Bitmap(bitmap);
130         }
131 
132         [EditorBrowsable(EditorBrowsableState.Advanced)]
GetHbitmap()133         public IntPtr GetHbitmap() => GetHbitmap(Color.LightGray);
134 
135         [EditorBrowsable(EditorBrowsableState.Advanced)]
GetHbitmap(Color background)136         public IntPtr GetHbitmap(Color background)
137         {
138             IntPtr hBitmap = IntPtr.Zero;
139             int status = SafeNativeMethods.Gdip.GdipCreateHBITMAPFromBitmap(new HandleRef(this, nativeImage), out hBitmap,
140                                                              ColorTranslator.ToWin32(background));
141             if (status == 2 /* invalid parameter*/ && (Width >= short.MaxValue || Height >= short.MaxValue))
142             {
143                 throw new ArgumentException(SR.Format(SR.GdiplusInvalidSize));
144             }
145 
146             SafeNativeMethods.Gdip.CheckStatus(status);
147 
148             return hBitmap;
149         }
150 
151         [EditorBrowsable(EditorBrowsableState.Advanced)]
GetHicon()152         public IntPtr GetHicon()
153         {
154             IntPtr hIcon = IntPtr.Zero;
155             int status = SafeNativeMethods.Gdip.GdipCreateHICONFromBitmap(new HandleRef(this, nativeImage), out hIcon);
156             SafeNativeMethods.Gdip.CheckStatus(status);
157 
158             return hIcon;
159         }
160 
Clone(RectangleF rect, PixelFormat format)161         public Bitmap Clone(RectangleF rect, PixelFormat format)
162         {
163             if (rect.Width == 0 || rect.Height == 0)
164             {
165                 throw new ArgumentException(SR.Format(SR.GdiplusInvalidRectangle, rect.ToString()));
166             }
167 
168             IntPtr dstHandle = IntPtr.Zero;
169 
170             int status = SafeNativeMethods.Gdip.GdipCloneBitmapArea(
171                                                     rect.X,
172                                                     rect.Y,
173                                                     rect.Width,
174                                                     rect.Height,
175                                                     unchecked((int)format),
176                                                     new HandleRef(this, nativeImage),
177                                                     out dstHandle);
178 
179             if (status != SafeNativeMethods.Gdip.Ok || dstHandle == IntPtr.Zero)
180                 throw SafeNativeMethods.Gdip.StatusException(status);
181 
182             return new Bitmap(dstHandle);
183         }
184 
MakeTransparent()185         public void MakeTransparent()
186         {
187             Color transparent = s_defaultTransparentColor;
188             if (Height > 0 && Width > 0)
189             {
190                 transparent = GetPixel(0, Size.Height - 1);
191             }
192             if (transparent.A < 255)
193             {
194                 // It's already transparent, and if we proceeded, we will do something
195                 // unintended like making black transparent
196                 return;
197             }
198 
199             MakeTransparent(transparent);
200         }
201 
MakeTransparent(Color transparentColor)202         public void MakeTransparent(Color transparentColor)
203         {
204             if (RawFormat.Guid == ImageFormat.Icon.Guid)
205             {
206                 throw new InvalidOperationException(SR.Format(SR.CantMakeIconTransparent));
207             }
208 
209             Size size = Size;
210 
211             // The new bitmap must be in 32bppARGB  format, because that's the only
212             // thing that supports alpha.  (And that's what the image is initialized to -- transparent)
213             using (var result = new Bitmap(size.Width, size.Height, PixelFormat.Format32bppArgb))
214             using (Graphics graphics = Graphics.FromImage(result))
215             {
216                 graphics.Clear(Color.Transparent);
217                 Rectangle rectangle = new Rectangle(0, 0, size.Width, size.Height);
218 
219                 using (var attributes = new ImageAttributes())
220                 {
221                     attributes.SetColorKey(transparentColor, transparentColor);
222                     graphics.DrawImage(this, rectangle,
223                                         0, 0, size.Width, size.Height,
224                                         GraphicsUnit.Pixel, attributes, null, IntPtr.Zero);
225                 }
226 
227                 // Swap nativeImage pointers to make it look like we modified the image in place
228                 IntPtr temp = nativeImage;
229                 nativeImage = result.nativeImage;
230                 result.nativeImage = temp;
231             }
232         }
233 
LockBits(Rectangle rect, ImageLockMode flags, PixelFormat format)234         public BitmapData LockBits(Rectangle rect, ImageLockMode flags, PixelFormat format)
235         {
236             return LockBits(rect, flags, format, new BitmapData());
237         }
238 
LockBits(Rectangle rect, ImageLockMode flags, PixelFormat format, BitmapData bitmapData)239         public BitmapData LockBits(Rectangle rect, ImageLockMode flags, PixelFormat format, BitmapData bitmapData)
240         {
241             var gprect = new GPRECT(rect);
242             int status = SafeNativeMethods.Gdip.GdipBitmapLockBits(new HandleRef(this, nativeImage), ref gprect,
243                                                     flags, format, bitmapData);
244             // libgdiplus has the wrong error code mapping for this state.
245             if (status == 7)
246             {
247                 status = 8;
248             }
249             SafeNativeMethods.Gdip.CheckStatus(status);
250 
251             return bitmapData;
252         }
253 
UnlockBits(BitmapData bitmapdata)254         public void UnlockBits(BitmapData bitmapdata)
255         {
256             int status = SafeNativeMethods.Gdip.GdipBitmapUnlockBits(new HandleRef(this, nativeImage), bitmapdata);
257             SafeNativeMethods.Gdip.CheckStatus(status);
258         }
259 
GetPixel(int x, int y)260         public Color GetPixel(int x, int y)
261         {
262             if (x < 0 || x >= Width)
263             {
264                 throw new ArgumentOutOfRangeException(nameof(x), SR.Format(SR.ValidRangeX));
265             }
266 
267             if (y < 0 || y >= Height)
268             {
269                 throw new ArgumentOutOfRangeException(nameof(y), SR.Format(SR.ValidRangeY));
270             }
271 
272             int color = 0;
273             int status = SafeNativeMethods.Gdip.GdipBitmapGetPixel(new HandleRef(this, nativeImage), x, y, out color);
274             SafeNativeMethods.Gdip.CheckStatus(status);
275 
276             return Color.FromArgb(color);
277         }
278 
SetPixel(int x, int y, Color color)279         public void SetPixel(int x, int y, Color color)
280         {
281             if ((PixelFormat & PixelFormat.Indexed) != 0)
282             {
283                 throw new InvalidOperationException(SR.Format(SR.GdiplusCannotSetPixelFromIndexedPixelFormat));
284             }
285 
286             if (x < 0 || x >= Width)
287             {
288                 throw new ArgumentOutOfRangeException(nameof(x), SR.Format(SR.ValidRangeX));
289             }
290 
291             if (y < 0 || y >= Height)
292             {
293                 throw new ArgumentOutOfRangeException(nameof(y), SR.Format(SR.ValidRangeY));
294             }
295 
296             int status = SafeNativeMethods.Gdip.GdipBitmapSetPixel(new HandleRef(this, nativeImage), x, y, color.ToArgb());
297             SafeNativeMethods.Gdip.CheckStatus(status);
298         }
299 
SetResolution(float xDpi, float yDpi)300         public void SetResolution(float xDpi, float yDpi)
301         {
302             int status = SafeNativeMethods.Gdip.GdipBitmapSetResolution(new HandleRef(this, nativeImage), xDpi, yDpi);
303             SafeNativeMethods.Gdip.CheckStatus(status);
304         }
Clone(Rectangle rect, PixelFormat format)305         public Bitmap Clone(Rectangle rect, PixelFormat format)
306         {
307             if (rect.Width == 0 || rect.Height == 0)
308             {
309                 throw new ArgumentException(SR.Format(SR.GdiplusInvalidRectangle, rect.ToString()));
310             }
311 
312             IntPtr dstHandle = IntPtr.Zero;
313             int status = SafeNativeMethods.Gdip.GdipCloneBitmapAreaI(
314                                                      rect.X,
315                                                      rect.Y,
316                                                      rect.Width,
317                                                      rect.Height,
318                                                      unchecked((int)format),
319                                                      new HandleRef(this, nativeImage),
320                                                      out dstHandle);
321 
322             if (status != SafeNativeMethods.Gdip.Ok || dstHandle == IntPtr.Zero)
323                 throw SafeNativeMethods.Gdip.StatusException(status);
324 
325             return new Bitmap(dstHandle);
326         }
327     }
328 }
329