A game framework written with osu! in mind.
at master 228 lines 8.5 kB view raw
1// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. 2// See the LICENCE file in the repository root for full licence text. 3 4using System; 5using osuTK; 6using osu.Framework.Graphics.Primitives; 7using System.Diagnostics; 8using osuTK.Graphics; 9 10namespace osu.Framework.Graphics.Colour 11{ 12 /// <summary> 13 /// ColourInfo contains information about the colours of all 4 vertices of a quad. 14 /// These colours are always stored in linear space. 15 /// </summary> 16 public struct ColourInfo : IEquatable<ColourInfo>, IEquatable<SRGBColour> 17 { 18 public SRGBColour TopLeft; 19 public SRGBColour BottomLeft; 20 public SRGBColour TopRight; 21 public SRGBColour BottomRight; 22 public bool HasSingleColour; 23 24 /// <summary> 25 /// Creates a ColourInfo with a single linear colour assigned to all vertices. 26 /// </summary> 27 /// <param name="colour">The single linear colour to be assigned to all vertices.</param> 28 /// <returns>The created ColourInfo.</returns> 29 public static ColourInfo SingleColour(SRGBColour colour) 30 { 31 ColourInfo result = new ColourInfo(); 32 result.TopLeft = result.BottomLeft = result.TopRight = result.BottomRight = colour; 33 result.HasSingleColour = true; 34 return result; 35 } 36 37 /// <summary> 38 /// Creates a ColourInfo with a horizontal gradient. 39 /// </summary> 40 /// <param name="c1">The left colour of the gradient.</param> 41 /// <param name="c2">The right colour of the gradient.</param> 42 /// <returns>The created ColourInfo.</returns> 43 public static ColourInfo GradientHorizontal(SRGBColour c1, SRGBColour c2) 44 { 45 ColourInfo result = new ColourInfo(); 46 result.TopLeft = result.BottomLeft = c1; 47 result.TopRight = result.BottomRight = c2; 48 result.HasSingleColour = false; 49 return result; 50 } 51 52 /// <summary> 53 /// Creates a ColourInfo with a vertical gradient. 54 /// </summary> 55 /// <param name="c1">The top colour of the gradient.</param> 56 /// <param name="c2">The bottom colour of the gradient.</param> 57 /// <returns>The created ColourInfo.</returns> 58 public static ColourInfo GradientVertical(SRGBColour c1, SRGBColour c2) 59 { 60 ColourInfo result = new ColourInfo(); 61 result.TopLeft = result.TopRight = c1; 62 result.BottomLeft = result.BottomRight = c2; 63 result.HasSingleColour = false; 64 return result; 65 } 66 67 private SRGBColour singleColour 68 { 69 readonly get 70 { 71 if (!HasSingleColour) 72 throw new InvalidOperationException("Attempted to read single colour from multi-colour ColourInfo."); 73 74 return TopLeft; 75 } 76 77 set 78 { 79 TopLeft = BottomLeft = TopRight = BottomRight = value; 80 HasSingleColour = true; 81 } 82 } 83 84 public readonly SRGBColour Interpolate(Vector2 interp) => SRGBColour.FromVector( 85 (1 - interp.Y) * ((1 - interp.X) * TopLeft.ToVector() + interp.X * TopRight.ToVector()) + 86 interp.Y * ((1 - interp.X) * BottomLeft.ToVector() + interp.X * BottomRight.ToVector())); 87 88 public void ApplyChild(ColourInfo childColour) 89 { 90 if (!HasSingleColour) 91 { 92 ApplyChild(childColour, new Quad(0, 0, 1, 1)); 93 return; 94 } 95 96 if (childColour.HasSingleColour) 97 singleColour *= childColour.singleColour; 98 else 99 { 100 HasSingleColour = false; 101 BottomLeft = childColour.BottomLeft * TopLeft; 102 TopRight = childColour.TopRight * TopLeft; 103 BottomRight = childColour.BottomRight * TopLeft; 104 105 // Need to assign TopLeft last to keep correctness. 106 TopLeft = childColour.TopLeft * TopLeft; 107 } 108 } 109 110 public void ApplyChild(ColourInfo childColour, Quad interp) 111 { 112 Trace.Assert(!HasSingleColour); 113 114 SRGBColour newTopLeft = Interpolate(interp.TopLeft) * childColour.TopLeft; 115 SRGBColour newTopRight = Interpolate(interp.TopRight) * childColour.TopRight; 116 SRGBColour newBottomLeft = Interpolate(interp.BottomLeft) * childColour.BottomLeft; 117 SRGBColour newBottomRight = Interpolate(interp.BottomRight) * childColour.BottomRight; 118 119 TopLeft = newTopLeft; 120 TopRight = newTopRight; 121 BottomLeft = newBottomLeft; 122 BottomRight = newBottomRight; 123 } 124 125 /// <summary> 126 /// Created a new ColourInfo with the alpha value of the colours of all vertices 127 /// multiplied by a given alpha parameter. 128 /// </summary> 129 /// <param name="alpha">The alpha parameter to multiply the alpha values of all vertices with.</param> 130 /// <returns>The new ColourInfo.</returns> 131 public readonly ColourInfo MultiplyAlpha(float alpha) 132 { 133 if (alpha == 1.0) 134 return this; 135 136 ColourInfo result = this; 137 result.TopLeft.MultiplyAlpha(alpha); 138 139 if (HasSingleColour) 140 result.BottomLeft = result.TopRight = result.BottomRight = result.TopLeft; 141 else 142 { 143 result.BottomLeft.MultiplyAlpha(alpha); 144 result.TopRight.MultiplyAlpha(alpha); 145 result.BottomRight.MultiplyAlpha(alpha); 146 } 147 148 return result; 149 } 150 151 public readonly bool Equals(ColourInfo other) 152 { 153 if (!HasSingleColour) 154 { 155 if (other.HasSingleColour) 156 return false; 157 158 return 159 TopLeft.Equals(other.TopLeft) && 160 TopRight.Equals(other.TopRight) && 161 BottomLeft.Equals(other.BottomLeft) && 162 BottomRight.Equals(other.BottomRight); 163 } 164 165 return other.HasSingleColour && TopLeft.Equals(other.TopLeft); 166 } 167 168 public readonly bool Equals(SRGBColour other) => HasSingleColour && TopLeft.Equals(other); 169 170 /// <summary> 171 /// The average colour of all corners. 172 /// </summary> 173 public readonly SRGBColour AverageColour 174 { 175 get 176 { 177 if (HasSingleColour) 178 return TopLeft; 179 180 return SRGBColour.FromVector( 181 (TopLeft.ToVector() + TopRight.ToVector() + BottomLeft.ToVector() + BottomRight.ToVector()) / 4); 182 } 183 } 184 185 /// <summary> 186 /// The maximum alpha value of all four corners. 187 /// </summary> 188 public readonly float MaxAlpha 189 { 190 get 191 { 192 float max = TopLeft.Linear.A; 193 if (TopRight.Linear.A > max) max = TopRight.Linear.A; 194 if (BottomLeft.Linear.A > max) max = BottomLeft.Linear.A; 195 if (BottomRight.Linear.A > max) max = BottomRight.Linear.A; 196 197 return max; 198 } 199 } 200 201 /// <summary> 202 /// The minimum alpha value of all four corners. 203 /// </summary> 204 public readonly float MinAlpha 205 { 206 get 207 { 208 float min = TopLeft.Linear.A; 209 if (TopRight.Linear.A < min) min = TopRight.Linear.A; 210 if (BottomLeft.Linear.A < min) min = BottomLeft.Linear.A; 211 if (BottomRight.Linear.A < min) min = BottomRight.Linear.A; 212 213 return min; 214 } 215 } 216 217 public override readonly string ToString() => HasSingleColour ? $@"{TopLeft} (Single)" : $@"{TopLeft}, {TopRight}, {BottomLeft}, {BottomRight}"; 218 219 public static implicit operator ColourInfo(SRGBColour colour) => SingleColour(colour); 220 public static implicit operator SRGBColour(ColourInfo colour) => colour.singleColour; 221 222 public static implicit operator ColourInfo(Color4 colour) => (SRGBColour)colour; 223 public static implicit operator Color4(ColourInfo colour) => (SRGBColour)colour; 224 225 public static implicit operator ColourInfo(Colour4 colour) => (SRGBColour)colour; 226 public static implicit operator Colour4(ColourInfo colour) => (SRGBColour)colour; 227 } 228}