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.Diagnostics; 6 using System.Diagnostics.CodeAnalysis; 7 using System.Numerics.Hashing; 8 9 namespace System.Drawing 10 { 11 [DebuggerDisplay("{NameAndARGBValue}")] 12 [Serializable] 13 #if !MONO 14 [System.Runtime.CompilerServices.TypeForwardedFrom("System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] 15 #endif 16 public readonly struct Color : IEquatable<Color> 17 { 18 public static readonly Color Empty = new Color(); 19 20 // ------------------------------------------------------------------- 21 // static list of "web" colors... 22 // 23 public static Color Transparent => new Color(KnownColor.Transparent); 24 25 public static Color AliceBlue => new Color(KnownColor.AliceBlue); 26 27 public static Color AntiqueWhite => new Color(KnownColor.AntiqueWhite); 28 29 public static Color Aqua => new Color(KnownColor.Aqua); 30 31 public static Color Aquamarine => new Color(KnownColor.Aquamarine); 32 33 public static Color Azure => new Color(KnownColor.Azure); 34 35 public static Color Beige => new Color(KnownColor.Beige); 36 37 public static Color Bisque => new Color(KnownColor.Bisque); 38 39 public static Color Black => new Color(KnownColor.Black); 40 41 public static Color BlanchedAlmond => new Color(KnownColor.BlanchedAlmond); 42 43 public static Color Blue => new Color(KnownColor.Blue); 44 45 public static Color BlueViolet => new Color(KnownColor.BlueViolet); 46 47 public static Color Brown => new Color(KnownColor.Brown); 48 49 public static Color BurlyWood => new Color(KnownColor.BurlyWood); 50 51 public static Color CadetBlue => new Color(KnownColor.CadetBlue); 52 53 public static Color Chartreuse => new Color(KnownColor.Chartreuse); 54 55 public static Color Chocolate => new Color(KnownColor.Chocolate); 56 57 public static Color Coral => new Color(KnownColor.Coral); 58 59 public static Color CornflowerBlue => new Color(KnownColor.CornflowerBlue); 60 61 public static Color Cornsilk => new Color(KnownColor.Cornsilk); 62 63 public static Color Crimson => new Color(KnownColor.Crimson); 64 65 public static Color Cyan => new Color(KnownColor.Cyan); 66 67 public static Color DarkBlue => new Color(KnownColor.DarkBlue); 68 69 public static Color DarkCyan => new Color(KnownColor.DarkCyan); 70 71 public static Color DarkGoldenrod => new Color(KnownColor.DarkGoldenrod); 72 73 public static Color DarkGray => new Color(KnownColor.DarkGray); 74 75 public static Color DarkGreen => new Color(KnownColor.DarkGreen); 76 77 public static Color DarkKhaki => new Color(KnownColor.DarkKhaki); 78 79 public static Color DarkMagenta => new Color(KnownColor.DarkMagenta); 80 81 public static Color DarkOliveGreen => new Color(KnownColor.DarkOliveGreen); 82 83 public static Color DarkOrange => new Color(KnownColor.DarkOrange); 84 85 public static Color DarkOrchid => new Color(KnownColor.DarkOrchid); 86 87 public static Color DarkRed => new Color(KnownColor.DarkRed); 88 89 public static Color DarkSalmon => new Color(KnownColor.DarkSalmon); 90 91 public static Color DarkSeaGreen => new Color(KnownColor.DarkSeaGreen); 92 93 public static Color DarkSlateBlue => new Color(KnownColor.DarkSlateBlue); 94 95 public static Color DarkSlateGray => new Color(KnownColor.DarkSlateGray); 96 97 public static Color DarkTurquoise => new Color(KnownColor.DarkTurquoise); 98 99 public static Color DarkViolet => new Color(KnownColor.DarkViolet); 100 101 public static Color DeepPink => new Color(KnownColor.DeepPink); 102 103 public static Color DeepSkyBlue => new Color(KnownColor.DeepSkyBlue); 104 105 public static Color DimGray => new Color(KnownColor.DimGray); 106 107 public static Color DodgerBlue => new Color(KnownColor.DodgerBlue); 108 109 public static Color Firebrick => new Color(KnownColor.Firebrick); 110 111 public static Color FloralWhite => new Color(KnownColor.FloralWhite); 112 113 public static Color ForestGreen => new Color(KnownColor.ForestGreen); 114 115 public static Color Fuchsia => new Color(KnownColor.Fuchsia); 116 117 public static Color Gainsboro => new Color(KnownColor.Gainsboro); 118 119 public static Color GhostWhite => new Color(KnownColor.GhostWhite); 120 121 public static Color Gold => new Color(KnownColor.Gold); 122 123 public static Color Goldenrod => new Color(KnownColor.Goldenrod); 124 125 public static Color Gray => new Color(KnownColor.Gray); 126 127 public static Color Green => new Color(KnownColor.Green); 128 129 public static Color GreenYellow => new Color(KnownColor.GreenYellow); 130 131 public static Color Honeydew => new Color(KnownColor.Honeydew); 132 133 public static Color HotPink => new Color(KnownColor.HotPink); 134 135 public static Color IndianRed => new Color(KnownColor.IndianRed); 136 137 public static Color Indigo => new Color(KnownColor.Indigo); 138 139 public static Color Ivory => new Color(KnownColor.Ivory); 140 141 public static Color Khaki => new Color(KnownColor.Khaki); 142 143 public static Color Lavender => new Color(KnownColor.Lavender); 144 145 public static Color LavenderBlush => new Color(KnownColor.LavenderBlush); 146 147 public static Color LawnGreen => new Color(KnownColor.LawnGreen); 148 149 public static Color LemonChiffon => new Color(KnownColor.LemonChiffon); 150 151 public static Color LightBlue => new Color(KnownColor.LightBlue); 152 153 public static Color LightCoral => new Color(KnownColor.LightCoral); 154 155 public static Color LightCyan => new Color(KnownColor.LightCyan); 156 157 public static Color LightGoldenrodYellow => new Color(KnownColor.LightGoldenrodYellow); 158 159 public static Color LightGreen => new Color(KnownColor.LightGreen); 160 161 public static Color LightGray => new Color(KnownColor.LightGray); 162 163 public static Color LightPink => new Color(KnownColor.LightPink); 164 165 public static Color LightSalmon => new Color(KnownColor.LightSalmon); 166 167 public static Color LightSeaGreen => new Color(KnownColor.LightSeaGreen); 168 169 public static Color LightSkyBlue => new Color(KnownColor.LightSkyBlue); 170 171 public static Color LightSlateGray => new Color(KnownColor.LightSlateGray); 172 173 public static Color LightSteelBlue => new Color(KnownColor.LightSteelBlue); 174 175 public static Color LightYellow => new Color(KnownColor.LightYellow); 176 177 public static Color Lime => new Color(KnownColor.Lime); 178 179 public static Color LimeGreen => new Color(KnownColor.LimeGreen); 180 181 public static Color Linen => new Color(KnownColor.Linen); 182 183 public static Color Magenta => new Color(KnownColor.Magenta); 184 185 public static Color Maroon => new Color(KnownColor.Maroon); 186 187 public static Color MediumAquamarine => new Color(KnownColor.MediumAquamarine); 188 189 public static Color MediumBlue => new Color(KnownColor.MediumBlue); 190 191 public static Color MediumOrchid => new Color(KnownColor.MediumOrchid); 192 193 public static Color MediumPurple => new Color(KnownColor.MediumPurple); 194 195 public static Color MediumSeaGreen => new Color(KnownColor.MediumSeaGreen); 196 197 public static Color MediumSlateBlue => new Color(KnownColor.MediumSlateBlue); 198 199 public static Color MediumSpringGreen => new Color(KnownColor.MediumSpringGreen); 200 201 public static Color MediumTurquoise => new Color(KnownColor.MediumTurquoise); 202 203 public static Color MediumVioletRed => new Color(KnownColor.MediumVioletRed); 204 205 public static Color MidnightBlue => new Color(KnownColor.MidnightBlue); 206 207 public static Color MintCream => new Color(KnownColor.MintCream); 208 209 public static Color MistyRose => new Color(KnownColor.MistyRose); 210 211 public static Color Moccasin => new Color(KnownColor.Moccasin); 212 213 public static Color NavajoWhite => new Color(KnownColor.NavajoWhite); 214 215 public static Color Navy => new Color(KnownColor.Navy); 216 217 public static Color OldLace => new Color(KnownColor.OldLace); 218 219 public static Color Olive => new Color(KnownColor.Olive); 220 221 public static Color OliveDrab => new Color(KnownColor.OliveDrab); 222 223 public static Color Orange => new Color(KnownColor.Orange); 224 225 public static Color OrangeRed => new Color(KnownColor.OrangeRed); 226 227 public static Color Orchid => new Color(KnownColor.Orchid); 228 229 public static Color PaleGoldenrod => new Color(KnownColor.PaleGoldenrod); 230 231 public static Color PaleGreen => new Color(KnownColor.PaleGreen); 232 233 public static Color PaleTurquoise => new Color(KnownColor.PaleTurquoise); 234 235 public static Color PaleVioletRed => new Color(KnownColor.PaleVioletRed); 236 237 public static Color PapayaWhip => new Color(KnownColor.PapayaWhip); 238 239 public static Color PeachPuff => new Color(KnownColor.PeachPuff); 240 241 public static Color Peru => new Color(KnownColor.Peru); 242 243 public static Color Pink => new Color(KnownColor.Pink); 244 245 public static Color Plum => new Color(KnownColor.Plum); 246 247 public static Color PowderBlue => new Color(KnownColor.PowderBlue); 248 249 public static Color Purple => new Color(KnownColor.Purple); 250 251 public static Color Red => new Color(KnownColor.Red); 252 253 public static Color RosyBrown => new Color(KnownColor.RosyBrown); 254 255 public static Color RoyalBlue => new Color(KnownColor.RoyalBlue); 256 257 public static Color SaddleBrown => new Color(KnownColor.SaddleBrown); 258 259 public static Color Salmon => new Color(KnownColor.Salmon); 260 261 public static Color SandyBrown => new Color(KnownColor.SandyBrown); 262 263 public static Color SeaGreen => new Color(KnownColor.SeaGreen); 264 265 public static Color SeaShell => new Color(KnownColor.SeaShell); 266 267 public static Color Sienna => new Color(KnownColor.Sienna); 268 269 public static Color Silver => new Color(KnownColor.Silver); 270 271 public static Color SkyBlue => new Color(KnownColor.SkyBlue); 272 273 public static Color SlateBlue => new Color(KnownColor.SlateBlue); 274 275 public static Color SlateGray => new Color(KnownColor.SlateGray); 276 277 public static Color Snow => new Color(KnownColor.Snow); 278 279 public static Color SpringGreen => new Color(KnownColor.SpringGreen); 280 281 public static Color SteelBlue => new Color(KnownColor.SteelBlue); 282 283 public static Color Tan => new Color(KnownColor.Tan); 284 285 public static Color Teal => new Color(KnownColor.Teal); 286 287 public static Color Thistle => new Color(KnownColor.Thistle); 288 289 public static Color Tomato => new Color(KnownColor.Tomato); 290 291 public static Color Turquoise => new Color(KnownColor.Turquoise); 292 293 public static Color Violet => new Color(KnownColor.Violet); 294 295 public static Color Wheat => new Color(KnownColor.Wheat); 296 297 public static Color White => new Color(KnownColor.White); 298 299 public static Color WhiteSmoke => new Color(KnownColor.WhiteSmoke); 300 301 public static Color Yellow => new Color(KnownColor.Yellow); 302 303 public static Color YellowGreen => new Color(KnownColor.YellowGreen); 304 305 // 306 // end "web" colors 307 // ------------------------------------------------------------------- 308 309 // NOTE : The "zero" pattern (all members being 0) must represent 310 // : "not set". This allows "Color c;" to be correct. 311 312 private const short StateKnownColorValid = 0x0001; 313 private const short StateARGBValueValid = 0x0002; 314 private const short StateValueMask = StateARGBValueValid; 315 private const short StateNameValid = 0x0008; 316 private const long NotDefinedValue = 0; 317 318 /** 319 * Shift count and bit mask for A, R, G, B components in ARGB mode! 320 */ 321 private const int ARGBAlphaShift = 24; 322 private const int ARGBRedShift = 16; 323 private const int ARGBGreenShift = 8; 324 private const int ARGBBlueShift = 0; 325 326 327 // user supplied name of color. Will not be filled in if 328 // we map to a "knowncolor" 329 // 330 private readonly string name; // Do not rename (binary serialization) 331 332 // will contain standard 32bit sRGB (ARGB) 333 // 334 private readonly long value; // Do not rename (binary serialization) 335 336 // ignored, unless "state" says it is valid 337 // 338 private readonly short knownColor; // Do not rename (binary serialization) 339 340 // implementation specific information 341 // 342 private readonly short state; // Do not rename (binary serialization) 343 344 ColorSystem.Drawing.Color345 internal Color(KnownColor knownColor) 346 { 347 value = 0; 348 state = StateKnownColorValid; 349 name = null; 350 this.knownColor = unchecked((short)knownColor); 351 } 352 ColorSystem.Drawing.Color353 private Color(long value, short state, string name, KnownColor knownColor) 354 { 355 this.value = value; 356 this.state = state; 357 this.name = name; 358 this.knownColor = unchecked((short)knownColor); 359 } 360 361 public byte R => (byte)((Value >> ARGBRedShift) & 0xFF); 362 363 public byte G => (byte)((Value >> ARGBGreenShift) & 0xFF); 364 365 public byte B => (byte)((Value >> ARGBBlueShift) & 0xFF); 366 367 public byte A => (byte)((Value >> ARGBAlphaShift) & 0xFF); 368 369 public bool IsKnownColor => ((state & StateKnownColorValid) != 0); 370 371 public bool IsEmpty => state == 0; 372 373 public bool IsNamedColor => ((state & StateNameValid) != 0) || IsKnownColor; 374 375 public bool IsSystemColor => IsKnownColor && ((((KnownColor) knownColor) <= KnownColor.WindowText) || (((KnownColor) knownColor) > KnownColor.YellowGreen)); 376 377 // Not localized because it's only used for the DebuggerDisplayAttribute, and the values are 378 // programmatic items. 379 // Also, don't inline into the attribute for performance reasons. This way means the debugger 380 // does 1 func-eval instead of 5. 381 [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters")] 382 private string NameAndARGBValue => $"{{Name={Name}, ARGB=({A}, {R}, {G}, {B})}}"; 383 384 public string Name 385 { 386 get 387 { 388 if ((state & StateNameValid) != 0) 389 { 390 return name; 391 } 392 393 if (IsKnownColor) 394 { 395 string tablename = KnownColorTable.KnownColorToName((KnownColor)knownColor); 396 Debug.Assert(tablename != null, $"Could not find known color '{(KnownColor)knownColor}' in the KnownColorTable"); 397 398 return tablename; 399 } 400 401 // if we reached here, just encode the value 402 // 403 return Convert.ToString(value, 16); 404 } 405 } 406 407 private long Value 408 { 409 get 410 { 411 if ((state & StateValueMask) != 0) 412 { 413 return value; 414 } 415 416 if (IsKnownColor) 417 { 418 return KnownColorTable.KnownColorToArgb((KnownColor)knownColor); 419 } 420 421 return NotDefinedValue; 422 } 423 } 424 CheckByteSystem.Drawing.Color425 private static void CheckByte(int value, string name) 426 { 427 if (value < 0 || value > 255) 428 throw new ArgumentException(SR.Format(SR.InvalidEx2BoundArgument, name, value, 0, 255)); 429 } 430 MakeArgbSystem.Drawing.Color431 private static long MakeArgb(byte alpha, byte red, byte green, byte blue) => 432 (long)unchecked((uint)(red << ARGBRedShift | 433 green << ARGBGreenShift | 434 blue << ARGBBlueShift | 435 alpha << ARGBAlphaShift)) & 0xffffffff; 436 FromArgbSystem.Drawing.Color437 public static Color FromArgb(int argb) => new Color(argb & 0xffffffff, StateARGBValueValid, null, 0); 438 FromArgbSystem.Drawing.Color439 public static Color FromArgb(int alpha, int red, int green, int blue) 440 { 441 CheckByte(alpha, nameof(alpha)); 442 CheckByte(red, nameof(red)); 443 CheckByte(green, nameof(green)); 444 CheckByte(blue, nameof(blue)); 445 return new Color(MakeArgb((byte)alpha, (byte)red, (byte)green, (byte)blue), StateARGBValueValid, null, (KnownColor)0); 446 } 447 FromArgbSystem.Drawing.Color448 public static Color FromArgb(int alpha, Color baseColor) 449 { 450 CheckByte(alpha, nameof(alpha)); 451 // unchecked - because we already checked that alpha is a byte in CheckByte above 452 return new Color(MakeArgb(unchecked((byte)alpha), baseColor.R, baseColor.G, baseColor.B), StateARGBValueValid, null, (KnownColor)0); 453 } 454 FromArgbSystem.Drawing.Color455 public static Color FromArgb(int red, int green, int blue) => FromArgb(255, red, green, blue); 456 457 public static Color FromKnownColor(KnownColor color) => 458 color <= 0 || color > KnownColor.MenuHighlight ? FromName(color.ToString()) : new Color(color); 459 FromNameSystem.Drawing.Color460 public static Color FromName(string name) 461 { 462 // try to get a known color first 463 Color color; 464 if (ColorTable.TryGetNamedColor(name, out color)) 465 { 466 return color; 467 } 468 // otherwise treat it as a named color 469 return new Color(NotDefinedValue, StateNameValid, name, (KnownColor)0); 470 } 471 GetBrightnessSystem.Drawing.Color472 public float GetBrightness() 473 { 474 float r = R / 255.0f; 475 float g = G / 255.0f; 476 float b = B / 255.0f; 477 478 float max, min; 479 480 max = r; min = r; 481 482 if (g > max) 483 { 484 max = g; 485 } 486 else if (g < min) 487 { 488 min = g; 489 } 490 491 if (b > max) 492 { 493 max = b; 494 } 495 else if (b < min) 496 { 497 min = b; 498 } 499 500 return (max + min) / 2; 501 } 502 503 GetHueSystem.Drawing.Color504 public Single GetHue() 505 { 506 if (R == G && G == B) 507 return 0; // 0 makes as good an UNDEFINED value as any 508 509 float r = R / 255.0f; 510 float g = G / 255.0f; 511 float b = B / 255.0f; 512 513 float max, min; 514 float delta; 515 float hue; 516 517 max = r; min = r; 518 519 if (g > max) 520 { 521 max = g; 522 } 523 else if (g < min) 524 { 525 min = g; 526 } 527 528 if (b > max) 529 { 530 max = b; 531 } 532 else if (b < min) 533 { 534 min = b; 535 } 536 537 delta = max - min; 538 539 if (r == max) 540 { 541 hue = (g - b) / delta; 542 } 543 else if (g == max) 544 { 545 hue = 2 + (b - r) / delta; 546 } 547 else 548 { 549 Debug.Assert(b == max); 550 hue = 4 + (r - g) / delta; 551 } 552 hue *= 60; 553 554 if (hue < 0.0f) 555 { 556 hue += 360.0f; 557 } 558 return hue; 559 } 560 GetSaturationSystem.Drawing.Color561 public float GetSaturation() 562 { 563 float r = R / 255.0f; 564 float g = G / 255.0f; 565 float b = B / 255.0f; 566 567 float s = 0; 568 569 float max = r; 570 float min = r; 571 572 if (g > max) 573 { 574 max = g; 575 } 576 else if (g < min) 577 { 578 min = g; 579 } 580 581 if (b > max) 582 { 583 max = b; 584 } 585 else if (b < min) 586 { 587 min = b; 588 } 589 590 // if max == min, then there is no color and 591 // the saturation is zero. 592 // 593 if (max != min) 594 { 595 float l = (max + min) / 2; 596 597 if (l <= .5) 598 { 599 s = (max - min) / (max + min); 600 } 601 else 602 { 603 s = (max - min) / (2 - max - min); 604 } 605 } 606 return s; 607 } 608 ToArgbSystem.Drawing.Color609 public int ToArgb() => unchecked((int)Value); 610 ToKnownColorSystem.Drawing.Color611 public KnownColor ToKnownColor() => (KnownColor)knownColor; 612 ToStringSystem.Drawing.Color613 public override string ToString() 614 { 615 if ((state & StateNameValid) != 0 || (state & StateKnownColorValid) != 0) 616 { 617 return nameof(Color) + " [" + Name + "]"; 618 } 619 else if ((state & StateValueMask) != 0) 620 { 621 return nameof(Color) + " [A=" + A.ToString() + ", R=" + R.ToString() + ", G=" + G.ToString() + ", B=" + B.ToString() + "]"; 622 } 623 else 624 { 625 return nameof(Color) + " [Empty]"; 626 } 627 } 628 operator ==System.Drawing.Color629 public static bool operator ==(Color left, Color right) => 630 left.value == right.value 631 && left.state == right.state 632 && left.knownColor == right.knownColor 633 && left.name == right.name; 634 operator !=System.Drawing.Color635 public static bool operator !=(Color left, Color right) => !(left == right); 636 637 public override bool Equals(object obj) => obj is Color && Equals((Color)obj); 638 639 public bool Equals(Color other) => this == other; 640 GetHashCodeSystem.Drawing.Color641 public override int GetHashCode() 642 { 643 // Three cases: 644 // 1. We don't have a name. All relevant data, including this fact, is in the remaining fields. 645 // 2. We have a known name. The name will be the same instance of any other with the same 646 // knownColor value, so we can ignore it for hashing. Note this also hashes different to 647 // an unnamed color with the same ARGB value. 648 // 3. Have an unknown name. Will differ from other unknown-named colors only by name, so we 649 // can usefully use the names hash code alone. 650 if (name != null & !IsKnownColor) 651 return name.GetHashCode(); 652 653 return HashHelpers.Combine( 654 HashHelpers.Combine(value.GetHashCode(), state.GetHashCode()), knownColor.GetHashCode()); 655 } 656 } 657 } 658