PageRenderTime 417ms CodeModel.GetById 254ms app.highlight 81ms RepoModel.GetById 48ms app.codeStats 0ms

/Utilities/Datatypes/Color.cs

#
C# | 988 lines | 560 code | 89 blank | 339 comment | 13 complexity | 3ca85ffd6206a7af1216ab0c8d41b1c8 MD5 | raw file
  1using System;
  2using System.ComponentModel;
  3using System.Diagnostics;
  4using System.IO;
  5using System.Runtime.InteropServices;
  6using Delta.Utilities.Helpers;
  7using NUnit.Framework;
  8
  9
 10namespace Delta.Utilities.Datatypes
 11{
 12	/// <summary>
 13	/// Color datatype, originally contained 4 floats for all the color
 14	/// components. Now we still got those, but as properties because the
 15	/// internal format is a packed uint RGBA value (4 bytes instead of 16):
 16	/// R for red, G for green, B for blue and A for alpha, all between 0 and 1.
 17	/// </summary>
 18	/// <remarks> 
 19	/// The change to uint (and thanks to unions to the 4 byte values RedByte,
 20	/// GreenByte, BlueByte, and AlphaByte) was done to increase rendering
 21	/// performance. Especially on mobile platforms bandwidth is key and
 22	/// reducing the amount of data send to the vertex and fragment shaders
 23	/// can speed up rendering a lot (e.g. 36 bytes per vertex lead to max. 33
 24	/// fps on the iPad, while 16 bytes (2 floats for position, 2 ushorts for UV
 25	/// and 4 bytes for color) is almost 3 times faster!
 26	/// Here can find a lot of "color codes": System.Drawing.Color
 27	/// Use Reflector or the MSDN documentation to learn more.
 28	/// http://www.htmlhelp.com/cgi-bin/color.cgi
 29	/// http://www.pamramsey.com/colors.htm
 30	/// </remarks>
 31	[Serializable]
 32	[StructLayout(LayoutKind.Explicit)]
 33	[DebuggerDisplay("Color(R={R}, G={G}, B={B}, A={A})")]
 34	[Description("Expand to edit this Color")]
 35	[TypeConverter(typeof(ExpandableObjectConverter))]
 36	public struct Color : ISaveLoadBinary, IEquatable<Color>
 37	{
 38		#region Constants
 39		/// <summary>
 40		/// Represents the size in bytes of that R,G,B,A Color (just 4 bytes).
 41		/// </summary>
 42		public const int DataSize = 4;
 43
 44		/// <summary>
 45		/// Strings of the known color names that can be passed into FromColor!
 46		/// Note: These names are not used in ToString, but they may be used for
 47		/// UI, ComboBoxes, DropDown lists, etc.
 48		/// </summary>
 49		public static readonly string[] KnownColorNames = new string[]
 50		{
 51			"Black",
 52			"BlackHalfTransparent",
 53			"Grey",
 54			"White",
 55			"WhiteHalfTransparent",
 56			"Transparent",
 57			"Red",
 58			"Green",
 59			"Blue",
 60			"Yellow",
 61			"CornflowerBlue",
 62			"DarkBlue",
 63			"Purple",
 64			"Cyan",
 65			"Teal",
 66			"Orange",
 67			"Brown",
 68			"Gold",
 69			"LightGrey",
 70			"LightRed",
 71			"LightGreen",
 72			"LightPurple",
 73			"LightBlue",
 74			"LightTeal",
 75			"LightOrange",
 76			"DarkGrey"
 77		};
 78
 79		/// <summary>
 80		/// Unused color, all values including alpha are set to 0. Usually used to
 81		/// indicate that a color is unused, e.g. if Graphic.Instances.ClearColor
 82		/// is set to Unused, we won't clear the backbuffer with the ClearColor
 83		/// (which is a bit faster for games that do not need to have the
 84		/// backbuffer cleared). Note that we usually have premultiplied alpha
 85		/// so it is faster to check if Alpha == 0 to see if a color is unused!
 86		/// </summary>
 87		public static readonly Color Unused =
 88			new Color(0.0f, 0.0f, 0.0f, 0.0f);
 89
 90		/// <summary>
 91		/// Transparent color (0, 0, 0, 0), same as Unused.
 92		/// </summary>
 93		public static readonly Color Transparent = Unused;
 94
 95		/// <summary>
 96		/// Black color (0, 0, 0), but has alpha (1), use Unused for (0, 0, 0, 0)
 97		/// </summary>
 98		public static readonly Color Black =
 99			new Color(0.0f, 0.0f, 0.0f);
100
101		/// <summary>
102		/// Black color (0, 0, 0) but with an alpha value of 0.5
103		/// </summary>
104		public static readonly Color BlackHalfTransparent =
105			new Color(Black, 0.5f);
106
107		/// <summary>
108		/// Grey color (0.5, 0.5, 0.5) = (127, 127, 127)
109		/// http://en.wikipedia.org/wiki/Grey
110		/// </summary>
111		public static readonly Color Grey =
112			new Color(0.5f, 0.5f, 0.5f);
113
114		/// <summary>
115		/// White color (1, 1, 1)
116		/// </summary>
117		public static readonly Color White =
118			new Color(1.0f, 1.0f, 1.0f);
119
120		/// <summary>
121		/// White color (1, 1, 1) but with an alpha value of 0.5
122		/// </summary>
123		public static readonly Color WhiteHalfTransparent =
124			new Color(White, 0.5f);
125
126		/// <summary>
127		/// Red color (1, 0, 0)
128		/// </summary>
129		public static readonly Color Red =
130			new Color(1.0f, 0.0f, 0.0f);
131
132		/// <summary>
133		/// Green color (0, 1, 0)
134		/// </summary>
135		public static readonly Color Green =
136			new Color(0.0f, 1.0f, 0.0f);
137
138		/// <summary>
139		/// Blue color (0, 0, 1)
140		/// </summary>
141		public static readonly Color Blue =
142			new Color(0.0f, 0.0f, 1.0f);
143
144		/// <summary>
145		/// Yellow color (1, 1, 0)
146		/// </summary>
147		public static readonly Color Yellow =
148			new Color(1.0f, 1.0f, 0.0f);
149
150		/// <summary>
151		/// CornflowerBlue color (0.39, 0.58, 0.93)
152		/// </summary>
153		public static readonly Color CornflowerBlue =
154			new Color(0.392f, 0.584f, 0.93f);
155
156		/// <summary>
157		/// Dark blue color (0.19, 0.28, 0.43)
158		/// </summary>
159		public static readonly Color DarkBlue =
160			new Color(0.192f, 0.284f, 0.43f);
161
162		/// <summary>
163		/// Purple color (0.5, 0, 0.5)
164		/// </summary>
165		public static readonly Color Purple =
166			new Color(0.5f, 0, 0.5f);
167
168		/// <summary>
169		/// Cyan color (0.0, 1.0, 1.0)
170		/// </summary>
171		public static readonly Color Cyan =
172			new Color(0.0f, 1.0f, 1.0f);
173
174		/// <summary>
175		/// Teal color (0, 0.5, 0.5)
176		/// </summary>
177		public static readonly Color Teal =
178			new Color(0, 0.5f, 0.5f);
179
180		/// <summary>
181		/// Orange color (1, 0.65, 0)
182		/// </summary>
183		public static readonly Color Orange =
184			new Color(1, 0.65f, 0);
185
186		/// <summary>
187		/// Brown color (0.65, 0.16, 0.16)
188		/// </summary>
189		public static readonly Color Brown =
190			new Color(0.65f, 0.165f, 0.165f);
191
192		/// <summary>
193		/// Gold color (1.0, 0.84, 0.0)
194		/// </summary>
195		public static readonly Color Gold =
196			new Color(1.0f, 0.843f, 0.0f);
197
198		/// <summary>
199		/// Light grey color (0.65, 0.65, 0.65) = (165, 165, 165)
200		/// http://en.wikipedia.org/wiki/Grey
201		/// </summary>
202		public static readonly Color LightGrey =
203			new Color(0.65f, 0.65f, 0.65f);
204
205		/// <summary>
206		/// LightRed color (1, 0.25, 0.25)
207		/// </summary>
208		public static readonly Color LightRed =
209			new Color(1.0f, 0.25f, 0.25f);
210
211		/// <summary>
212		/// LightGreen color (0.25, 1, 0.25)
213		/// </summary>
214		public static readonly Color LightGreen =
215			new Color(0.25f, 1.0f, 0.25f);
216
217		/// <summary>
218		/// LightPurple color (0.576, 0.44, 0.86)
219		/// </summary>
220		public static readonly Color LightPurple =
221			new Color(0.576f, 0.44f, 0.86f);
222
223		/// <summary>
224		/// LightBlue color (0.65f, 0.795f, 1.0f)
225		/// </summary>
226		public static readonly Color LightBlue =
227			new Color(0.65f, 0.795f, 1.0f);
228
229		/// <summary>
230		/// LightTeal color (0.25, 0.8, 0.8)
231		/// </summary>
232		public static readonly Color LightTeal =
233			new Color(0.25f, 0.8f, 0.8f);
234
235		/// <summary>
236		/// LightOrange color (1, 0.8, 0.25)
237		/// </summary>
238		public static readonly Color LightOrange =
239			new Color(1, 0.8f, 0.25f);
240
241		/// <summary>
242		/// Dark grey color (0.35, 0.35, 0.35) = (89, 89, 89)
243		/// http://en.wikipedia.org/wiki/Grey
244		/// </summary>
245		public static readonly Color DarkGrey =
246			new Color(0.35f, 0.35f, 0.35f);
247		#endregion
248
249		#region FromString (Static)
250		/// <summary>
251		/// Convert a string to a Color. The expected format is
252		/// (r.r, g.g, b.b, a.a), e.g. (0.2, 0.3, 0.4, 1.0). Alpha is optional.
253		/// </summary>
254		/// <param name="colorString">String containing the color values</param>
255		/// <returns>
256		/// Color from the colorString or White if no valid string was used.
257		/// </returns>
258		public static Color FromString(string colorString)
259		{
260			// First remove the brackets and split the string up into seperate values
261			colorString = colorString.Replace("(", "");
262			colorString = colorString.Replace(")", "");
263			string[] colorStrings = colorString.Split(
264				new[]
265				{
266					',', ' '
267				},
268				StringSplitOptions.RemoveEmptyEntries);
269
270			// Then check if the length is 4 for 4 values and return the new vector.
271			// If the length is not 4 than return Vector.Zero.
272			if (colorStrings.Length == 4)
273			{
274				return new Color(
275					colorStrings[0].FromInvariantString(1.0f),
276					colorStrings[1].FromInvariantString(1.0f),
277					colorStrings[2].FromInvariantString(1.0f),
278					colorStrings[3].FromInvariantString(1.0f));
279			}
280			if (colorStrings.Length == 3)
281			{
282				return new Color(
283					colorStrings[0].FromInvariantString(1.0f),
284					colorStrings[1].FromInvariantString(1.0f),
285					colorStrings[2].FromInvariantString(1.0f));
286			}
287
288			// Check if it is one of the supported color names
289			switch (colorString)
290			{
291				case "Black":
292					return Black;
293				case "BlackHalfTransparent":
294					return BlackHalfTransparent;
295				case "Grey":
296					return Grey;
297				case "White":
298					return White;
299				case "WhiteHalfTransparent":
300					return WhiteHalfTransparent;
301				case "Transparent":
302					return Transparent;
303				case "Red":
304					return Red;
305				case "Green":
306					return Green;
307				case "Blue":
308					return Blue;
309				case "Yellow":
310					return Yellow;
311				case "CornflowerBlue":
312					return CornflowerBlue;
313				case "DarkBlue":
314					return DarkBlue;
315				case "Purple":
316					return Purple;
317				case "Cyan":
318					return Cyan;
319				case "Teal":
320					return Teal;
321				case "Orange":
322					return Orange;
323				case "Brown":
324					return Brown;
325				case "Gold":
326					return Gold;
327				case "LightGrey":
328					return LightGrey;
329				case "LightRed":
330					return LightRed;
331				case "LightGreen":
332					return LightGreen;
333				case "LightPurple":
334					return LightPurple;
335				case "LightBlue":
336					return LightBlue;
337				case "LightTeal":
338					return LightTeal;
339				case "LightOrange":
340					return LightOrange;
341				case "DarkGrey":
342					return DarkGrey;
343			}
344
345			// Not found? Then just return White, the default for loading colors.
346			return White;
347		}
348		#endregion
349
350		#region Lerp (Static)
351		/// <summary>
352		/// Lerp (interpolate) between to colors based on the given amount between
353		/// 0.0 and 1.0. If the amount value is closer to 0, the output color will
354		/// be more like c1, otherwise it will be closer to c2.
355		/// </summary>
356		/// <param name="c1">Color 1</param>
357		/// <param name="c2">Color 2</param>
358		/// <param name="amount">Amount for interpolation</param>
359		/// <returns>Color in between c1 and c2</returns>
360		public static Color Lerp(Color c1, Color c2, float amount)
361		{
362			return new Color(
363				MathHelper.Lerp(c1.R, c2.R, amount),
364				MathHelper.Lerp(c1.G, c2.G, amount),
365				MathHelper.Lerp(c1.B, c2.B, amount),
366				MathHelper.Lerp(c1.A, c2.A, amount));
367		}
368		#endregion
369
370		#region Random (Static)
371		/// <summary>
372		/// Returns a random color (0-1, 0-1, 0-1)
373		/// </summary>
374		public static Color Random
375		{
376			get
377			{
378				return new Color(
379					RandomHelper.RandomFloat(0, 1),
380					RandomHelper.RandomFloat(0, 1),
381					RandomHelper.RandomFloat(0, 1));
382			}
383		}
384		#endregion
385
386		#region SolidColors (Static)
387		/// <summary>
388		/// List of 15 predefined solid colors, which look better than random
389		/// colors if used for profiling or effects (Red, Blue,
390		/// Green, Yellow, Purple, Teal, Orange, White, Brown, Gray,
391		/// CornflowerBlue, Gold, LightGreen, LightRed, LightPurple).
392		/// </summary>
393		public static Color[] SolidColors = new[]
394		{
395			Red,
396			Blue,
397			Green,
398			Yellow,
399			Purple,
400			Teal,
401			Orange,
402			White,
403			LightGreen,
404			LightRed,
405			LightPurple,
406			LightTeal,
407			LightBlue,
408			LightOrange,
409			Brown,
410			LightGrey,
411			CornflowerBlue,
412			Gold,
413		};
414		#endregion
415
416		#region LightColors (Static)
417		/// <summary>
418		/// List of 15 predefined solid colors, which look better than random
419		/// colors if used for profiling or effects (Red, Blue,
420		/// Green, Yellow, Purple, Teal, Orange, White, Brown, Gray,
421		/// CornflowerBlue, Gold, LightGreen, LightRed, LightPurple).
422		/// </summary>
423		public static Color[] LightColors = new[]
424		{
425			White,
426			LightRed,
427			CornflowerBlue,
428			LightBlue,
429			LightGreen,
430			LightOrange,
431		};
432		#endregion
433
434		#region NextSolidColor (Static)
435		/// <summary>
436		/// Returns the next solid color from a predefined array (Red, Blue,
437		/// Green, Yellow, Purple, Teal, Orange, White, Brown, Gray,
438		/// CornflowerBlue, Gold, LightGreen, LightRed, LightPurple).
439		/// </summary>
440		public static Color NextSolidColor
441		{
442			get
443			{
444				return SolidColors[(SolidColorNumber++) % SolidColors.Length];
445			}
446		}
447		#endregion
448
449		#region NextLightColor (Static)
450		/// <summary>
451		/// Returns the next light color from a predefined array (White,
452		/// LightRed, CornflowerBlue, LightBlue, LightGreen, LightOrange).
453		/// </summary>
454		public static Color NextLightColor
455		{
456			get
457			{
458				return LightColors[(LightColorNumber++) % LightColors.Length];
459			}
460		}
461		#endregion
462
463		#region Framework Union Defines (Public)
464		#endregion
465
466		#region PackedRGBA (Public)
467		/// <summary>
468		/// This color as 4 packed bytes in an uint value. This is the same format
469		/// used for OpenGL colors, xna colors and RGBA values in general :)
470		/// </summary>
471		[FieldOffset(0)]
472		public uint PackedRGBA;
473		#endregion
474
475		#region RedByte (Public)
476		/// <summary>
477		/// Red byte in a range of [0,255]. Use the property R for a float [0,1].
478		/// </summary>
479		[FieldOffset(0)]
480		public byte RedByte;
481		#endregion
482
483		#region GreenByte (Public)
484		/// <summary>
485		/// Green byte in a range of [0,255]. Use the property G for a float [0,1].
486		/// </summary>
487		[FieldOffset(1)]
488		public byte GreenByte;
489		#endregion
490
491		#region BlueByte (Public)
492		/// <summary>
493		/// Blue byte in a range of [0,255]. Use the property B for a float [0,1].
494		/// </summary>
495		[FieldOffset(2)]
496		public byte BlueByte;
497		#endregion
498
499		#region AlphaByte (Public)
500		/// <summary>
501		/// Alpha channel byte in a range of [0,255]. Use the property B for a
502		/// float [0,1]. 0=transparent, 255 (or A=1)=fully visible. Note: Like
503		/// xna 4.0 we use premultiplied alpha by default, which makes
504		/// multiplying colors a little easier (just newAlpha*Color).
505		/// </summary>
506		[FieldOffset(3)]
507		public byte AlphaByte;
508		#endregion
509
510		#region R (Public)
511		/// <summary>
512		/// Red channel (in a range of [0,1], caller has to make sure not to set
513		/// this to any crazy value). Set to the RedByte value of this color.
514		/// </summary>
515		[Browsable(true)]
516		[DisplayName("Red")]
517		public float R
518		{
519			get
520			{
521				return RedByte / 255.0f;
522			}
523			set
524			{
525				RedByte = (byte)(value * 255.0f);
526			}
527		}
528		#endregion
529
530		#region G (Public)
531		/// <summary>
532		/// Green channel (in a range of [0,1]). Set to the GreenByte value of
533		/// this color.
534		/// </summary>
535		[Browsable(true)]
536		[DisplayName("Green")]
537		public float G
538		{
539			get
540			{
541				return GreenByte / 255.0f;
542			}
543			set
544			{
545				GreenByte = (byte)(value * 255.0f);
546			}
547		}
548		#endregion
549
550		#region B (Public)
551		/// <summary>
552		/// Blue channel (in a range of [0,1]). Set to the BlueByte value of this
553		/// color.
554		/// </summary>
555		[Browsable(true)]
556		[DisplayName("Blue")]
557		public float B
558		{
559			get
560			{
561				return BlueByte / 255.0f;
562			}
563			set
564			{
565				BlueByte = (byte)(value * 255.0f);
566			}
567		}
568		#endregion
569
570		#region A (Public)
571		/// <summary>
572		/// Alpha channel (in a range of [0,1]). 0=transparent, 1=fully visible.
573		/// Note: Like XNA 4.0 we use premultiplied alpha by default, which makes
574		/// multiplying colors a little easier (just newAlpha*Color). Set to the
575		/// AlphaByte value of this color.
576		/// </summary>
577		[Browsable(true)]
578		[DisplayName("Alpha")]
579		public float A
580		{
581			get
582			{
583				return AlphaByte / 255.0f;
584			}
585			set
586			{
587				AlphaByte = (byte)(value * 255.0f);
588			}
589		}
590		#endregion
591
592		#region Private
593
594		#region SolidColorNumber (Private)
595		/// <summary>
596		/// Current index for NextSolidColor, will increase each call.
597		/// </summary>
598		private static int SolidColorNumber;
599		#endregion
600
601		#region LightColorNumber (Private)
602		/// <summary>
603		/// Current index for NextLightColor, will increase each call.
604		/// </summary>
605		private static int LightColorNumber;
606		#endregion
607
608		#endregion
609
610		#region Constructors
611		/// <summary>
612		/// Creates a color with an alpha of 1.0
613		/// </summary>
614		/// <param name="setBaseColor">Sets the "base color" RGB</param>
615		/// <param name="setA">Set alpha channel in a range of [0,1]</param>
616		public Color(Color setBaseColor, float setA)
617			: this(setBaseColor.R, setBaseColor.G, setBaseColor.B, setA)
618		{
619		}
620
621		/// <summary>
622		/// Creates a color with an alpha of 1.0
623		/// </summary>
624		/// <param name="setR">Set red channel in a range of [0,1]</param>
625		/// <param name="setG">Set green channel in a range of [0,1]</param>
626		/// <param name="setB">Set blue channel in a range of [0,1]</param>
627		public Color(float setR, float setG, float setB)
628			: this(setR, setG, setB, 1.0f)
629		{
630		}
631
632		/// <summary>
633		/// Creates a color with values for R, G, B, A.
634		/// </summary>
635		/// <param name="setR">Set red channel in a range of [0,1]</param>
636		/// <param name="setG">Set green channel in a range of [0,1]</param>
637		/// <param name="setB">Set blue channel in a range of [0,1]</param>
638		/// <param name="setA">Set alpha channel in a range of [0,1]</param>
639		public Color(float setR, float setG, float setB, float setA)
640			// Needed now because of PackedRGBA, XNA and SlimDx unions
641			: this()
642		{
643			// If any value is above 10 interpret it as a byte value, not a float!
644			// This is basically not allowed in the Delta Engine, but if any byte
645			// values come into this method (e.g. via FromString), fix the issue.
646			if (setR > 10)
647			{
648				setR = setR / 255.0f;
649			}
650			if (setG > 10)
651			{
652				setG = setG / 255.0f;
653			}
654			if (setB > 10)
655			{
656				setB = setB / 255.0f;
657			}
658			if (setA > 10)
659			{
660				setA = setA / 255.0f;
661			}
662
663			// Note: Nothing is clamped here, make sure the input does not go out of
664			// range at the caller level.
665			RedByte = (byte)(setR * 255.0f);
666			GreenByte = (byte)(setG * 255.0f);
667			BlueByte = (byte)(setB * 255.0f);
668			AlphaByte = (byte)(setA * 255.0f);
669		}
670
671		/// <summary>
672		/// Creates a color from the byte RGB values in the range [0-255] for each
673		/// channel.
674		/// </summary>
675		/// <param name="setR">The red value in the range [0-255].</param>
676		/// <param name="setG">The green value in the range [0-255].</param>
677		/// <param name="setB">The blue value in the range [0-255].</param>
678		public Color(byte setR, byte setG, byte setB)
679			// Needed now because of PackedRGBA, XNA and SlimDx unions
680			: this()
681		{
682			RedByte = setR;
683			GreenByte = setG;
684			BlueByte = setB;
685			AlphaByte = 255;
686		}
687
688		/// <summary>
689		/// Creates a color from the byte RGBA values in the range [0-255] for each
690		/// channel.
691		/// </summary>
692		/// <param name="setR">The red value in the range [0-255].</param>
693		/// <param name="setG">The green value in the range [0-255].</param>
694		/// <param name="setB">The blue value in the range [0-255].</param>
695		/// <param name="setA">The alpha value in the range [0-255].</param>
696		public Color(byte setR, byte setG, byte setB, byte setA)
697			// Needed now because of PackedRGBA, XNA and SlimDx unions
698			: this()
699		{
700			RedByte = setR;
701			GreenByte = setG;
702			BlueByte = setB;
703			AlphaByte = setA;
704		}
705
706		/// <summary>
707		/// Create color from binary data stream (e.g. to load vertices or
708		/// material data).
709		/// </summary>
710		/// <param name="setReader">setReader</param>
711		public Color(BinaryReader setReader)
712			: this()
713		{
714			Load(setReader);
715		}
716		#endregion
717
718		#region IEquatable<Color> Members
719		/// <summary>
720		/// Equals
721		/// </summary>
722		/// <param name="other">Other</param>
723		/// <returns>Value indicating the equality of two colors</returns>
724		public bool Equals(Color other)
725		{
726			return PackedRGBA == other.PackedRGBA;
727		}
728		#endregion
729
730		#region ISaveLoadBinary Members
731		/// <summary>
732		/// Load the color values from a stream (4 bytes).
733		/// </summary>
734		/// <param name="reader">The stream that will be used.</param>
735		public void Load(BinaryReader reader)
736		{
737			PackedRGBA = reader.ReadUInt32();
738		}
739
740		/// <summary>
741		/// Saves the color out to a stream (4 bytes).
742		/// </summary>
743		/// <param name="writer">The stream that will be used.</param>
744		public void Save(BinaryWriter writer)
745		{
746			writer.Write(PackedRGBA);
747		}
748		#endregion
749
750		#region op_Multiply (Operator)
751		/// <summary>
752		/// Multiply operator to multiply all color values with a number.
753		/// </summary>
754		/// <param name="c1">Color to multiply</param>
755		/// <param name="brightScale">Bright scale</param>
756		/// <returns>Color</returns>
757		public static Color operator *(Color c1, float brightScale)
758		{
759			return new Color(
760				c1.R * brightScale,
761				c1.G * brightScale,
762				c1.B * brightScale,
763				c1.A * brightScale);
764		}
765
766		/// <summary>
767		/// Multiply operator to multiply two colors (each value will be multiplied)
768		/// </summary>
769		/// <param name="c1">Color 1</param>
770		/// <param name="c2">Color 2</param>
771		/// <returns>Color</returns>
772		public static Color operator *(Color c1, Color c2)
773		{
774			return new Color(c1.R * c2.R, c1.G * c2.G, c1.B * c2.B, c1.A * c2.A);
775		}
776		#endregion
777
778		#region op_Equality (Operator)
779		/// <summary>
780		/// Check for equality
781		/// </summary>
782		/// <param name="value1">Value 1</param>
783		/// <param name="value2">Value 2</param>
784		/// <returns>True if the values are equal, false otherwise</returns>
785		public static bool operator ==(Color value1, Color value2)
786		{
787			return value1.PackedRGBA == value2.PackedRGBA;
788		}
789		#endregion
790
791		#region op_Inequality (Operator)
792		/// <summary>
793		/// Check for inequality
794		/// </summary>
795		/// <param name="value1">Value 1</param>
796		/// <param name="value2">Value 2</param>
797		/// <returns>True if the values are not equal</returns>
798		public static bool operator !=(Color value1, Color value2)
799		{
800			return value1.PackedRGBA != value2.PackedRGBA;
801		}
802		#endregion
803
804		#region GetHashCode (Public)
805		/// <summary>
806		/// Get hash code
807		/// </summary>
808		/// <returns>hash code</returns>
809		public override int GetHashCode()
810		{
811			return PackedRGBA.GetHashCode();
812		}
813		#endregion
814
815		#region Equals (Public)
816		/// <summary>
817		/// Equals
818		/// </summary>
819		/// <param name="obj">Object to compare to</param>
820		/// <returns>True if the values are equal, false otherwise</returns>
821		public override bool Equals(object obj)
822		{
823			if (obj is Color)
824			{
825				return Equals((Color)obj);
826			}
827			return base.Equals(obj);
828		}
829		#endregion
830
831		#region ToString (Public)
832		/// <summary>
833		/// To string
834		/// </summary>
835		/// <returns>string</returns>
836		public override string ToString()
837		{
838			//return String.Format("({0}, {1}, {2}, {3})", R, G, B, A);
839			return ToString("(", ")");
840		}
841
842		/// <summary>
843		/// To string
844		/// </summary>
845		/// <param name="closeBrace">closeBrace</param>
846		/// <param name="openBrace">openBrace</param>
847		/// <returns>string</returns>
848		public string ToString(string openBrace, string closeBrace)
849		{
850			return openBrace + R.ToInvariantString("0.00") +
851			       ", " + G.ToInvariantString("0.00") +
852			       ", " + B.ToInvariantString("0.00") +
853			       ", " + A.ToInvariantString("0.00") + closeBrace;
854		}
855		#endregion
856
857		/// <summary>
858		/// Tests
859		/// </summary>
860		internal class ColorTests
861		{
862			#region Constructor (Static)
863			/// <summary>
864			/// FromIntRGB
865			/// </summary>
866			[Test]
867			public static void Constructor()
868			{
869				Color testColor = new Color(255, 0, 127, 255);
870				Assert.NearlyEqual(testColor.R, 1f);
871				Assert.NearlyEqual(testColor.G, 0f);
872				Assert.NearlyEqual(testColor.B, 0.498f);
873				Assert.NearlyEqual(testColor.A, 1f);
874			}
875			#endregion
876
877			#region SizeOf (Static)
878			/// <summary>
879			/// Checks if the size of Point is exactly 8 bytes (2 floats: X and Y)
880			/// </summary>
881			[Test]
882			public static void SizeOf()
883			{
884				// Color consists of 4 bytes: R, G, B, and A
885				Assert.Equal(4, Marshal.SizeOf(typeof(Color)));
886			}
887			#endregion
888
889			#region ColorCompare (Static)
890			/// <summary>
891			/// Color compare
892			/// </summary>
893			[Test]
894			public static void ColorCompare()
895			{
896				Color testColor = Black;
897
898				Assert.NotEqual(White, testColor);
899				Assert.NotEqual(new Color(0, 1, 0), testColor);
900
901				Assert.Equal(new Color(0, 0, 0), testColor);
902				Assert.Equal(Black, testColor);
903				Assert.Equal(testColor, testColor);
904			}
905			#endregion
906
907			#region SaveAndLoad (Static)
908			/// <summary>
909			/// Save
910			/// </summary>
911			[Test]
912			public static void SaveAndLoad()
913			{
914				Color color = Yellow;
915
916				#region Saving
917				MemoryStream memHandle = new MemoryStream();
918				Assert.Equal(0, memHandle.Position);
919
920				BinaryWriter writer = new BinaryWriter(memHandle);
921				// save all the current data
922				color.Save(writer);
923
924				// and finally check (for saving) if the file was written correctly
925				Assert.NotEqual(0, memHandle.Length);
926				#endregion
927
928				#region Loading
929				// then we create an "empty" material
930				Color loadColor = Unused;
931				memHandle.Position = 0;
932
933				// which we use to load the material values from the the file
934				// Note: The using closes the file access too
935				BinaryReader reader = new BinaryReader(memHandle);
936				loadColor.Load(reader);
937
938				// before we finally check if everything is loaded correctly
939				Assert.Equal(color, loadColor);
940				#endregion
941
942				writer.Close();
943				reader.Close();
944				memHandle.Close();
945			}
946			#endregion
947
948			#region Multiplication (Static)
949			/// <summary>
950			/// Multiplication
951			/// </summary>
952			[Test]
953			public static void Multiplication()
954			{
955				Assert.Equal(Yellow, Yellow * White);
956				Assert.Equal(new Color(1, 1, 0, 0.5f),
957					Yellow * new Color(1, 1, 1, 0.5f));
958
959				Assert.Equal(Black, Red * Green);
960			}
961			#endregion
962
963			#region FromString (Static)
964			/// <summary>
965			/// FromString
966			/// </summary>
967			[Test]
968			public static void FromString()
969			{
970				Color testColor = Color.FromString("(0.0, 1.0, 0.0, 0.5)");
971				Assert.Equal(testColor, new Color(0f, 1f, 0f, 0.5f));
972			}
973			#endregion
974
975			#region Equality
976			/// <summary>
977			/// Equality
978			/// </summary>
979			[Test]
980			public void Equality()
981			{
982				Assert.Equal(new Color(1f, 1f, 0f, 1f), Yellow);
983				Assert.NotEqual(White, Yellow);
984			}
985			#endregion
986		}
987	}
988}