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