A game framework written with osu! in mind.
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}