// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; using osuTK; using osu.Framework.Graphics.Primitives; using System.Diagnostics; using osuTK.Graphics; namespace osu.Framework.Graphics.Colour { /// /// ColourInfo contains information about the colours of all 4 vertices of a quad. /// These colours are always stored in linear space. /// public struct ColourInfo : IEquatable, IEquatable { public SRGBColour TopLeft; public SRGBColour BottomLeft; public SRGBColour TopRight; public SRGBColour BottomRight; public bool HasSingleColour; /// /// Creates a ColourInfo with a single linear colour assigned to all vertices. /// /// The single linear colour to be assigned to all vertices. /// The created ColourInfo. public static ColourInfo SingleColour(SRGBColour colour) { ColourInfo result = new ColourInfo(); result.TopLeft = result.BottomLeft = result.TopRight = result.BottomRight = colour; result.HasSingleColour = true; return result; } /// /// Creates a ColourInfo with a horizontal gradient. /// /// The left colour of the gradient. /// The right colour of the gradient. /// The created ColourInfo. public static ColourInfo GradientHorizontal(SRGBColour c1, SRGBColour c2) { ColourInfo result = new ColourInfo(); result.TopLeft = result.BottomLeft = c1; result.TopRight = result.BottomRight = c2; result.HasSingleColour = false; return result; } /// /// Creates a ColourInfo with a vertical gradient. /// /// The top colour of the gradient. /// The bottom colour of the gradient. /// The created ColourInfo. public static ColourInfo GradientVertical(SRGBColour c1, SRGBColour c2) { ColourInfo result = new ColourInfo(); result.TopLeft = result.TopRight = c1; result.BottomLeft = result.BottomRight = c2; result.HasSingleColour = false; return result; } private SRGBColour singleColour { readonly get { if (!HasSingleColour) throw new InvalidOperationException("Attempted to read single colour from multi-colour ColourInfo."); return TopLeft; } set { TopLeft = BottomLeft = TopRight = BottomRight = value; HasSingleColour = true; } } public readonly SRGBColour Interpolate(Vector2 interp) => SRGBColour.FromVector( (1 - interp.Y) * ((1 - interp.X) * TopLeft.ToVector() + interp.X * TopRight.ToVector()) + interp.Y * ((1 - interp.X) * BottomLeft.ToVector() + interp.X * BottomRight.ToVector())); public void ApplyChild(ColourInfo childColour) { if (!HasSingleColour) { ApplyChild(childColour, new Quad(0, 0, 1, 1)); return; } if (childColour.HasSingleColour) singleColour *= childColour.singleColour; else { HasSingleColour = false; BottomLeft = childColour.BottomLeft * TopLeft; TopRight = childColour.TopRight * TopLeft; BottomRight = childColour.BottomRight * TopLeft; // Need to assign TopLeft last to keep correctness. TopLeft = childColour.TopLeft * TopLeft; } } public void ApplyChild(ColourInfo childColour, Quad interp) { Trace.Assert(!HasSingleColour); SRGBColour newTopLeft = Interpolate(interp.TopLeft) * childColour.TopLeft; SRGBColour newTopRight = Interpolate(interp.TopRight) * childColour.TopRight; SRGBColour newBottomLeft = Interpolate(interp.BottomLeft) * childColour.BottomLeft; SRGBColour newBottomRight = Interpolate(interp.BottomRight) * childColour.BottomRight; TopLeft = newTopLeft; TopRight = newTopRight; BottomLeft = newBottomLeft; BottomRight = newBottomRight; } /// /// Created a new ColourInfo with the alpha value of the colours of all vertices /// multiplied by a given alpha parameter. /// /// The alpha parameter to multiply the alpha values of all vertices with. /// The new ColourInfo. public readonly ColourInfo MultiplyAlpha(float alpha) { if (alpha == 1.0) return this; ColourInfo result = this; result.TopLeft.MultiplyAlpha(alpha); if (HasSingleColour) result.BottomLeft = result.TopRight = result.BottomRight = result.TopLeft; else { result.BottomLeft.MultiplyAlpha(alpha); result.TopRight.MultiplyAlpha(alpha); result.BottomRight.MultiplyAlpha(alpha); } return result; } public readonly bool Equals(ColourInfo other) { if (!HasSingleColour) { if (other.HasSingleColour) return false; return TopLeft.Equals(other.TopLeft) && TopRight.Equals(other.TopRight) && BottomLeft.Equals(other.BottomLeft) && BottomRight.Equals(other.BottomRight); } return other.HasSingleColour && TopLeft.Equals(other.TopLeft); } public readonly bool Equals(SRGBColour other) => HasSingleColour && TopLeft.Equals(other); /// /// The average colour of all corners. /// public readonly SRGBColour AverageColour { get { if (HasSingleColour) return TopLeft; return SRGBColour.FromVector( (TopLeft.ToVector() + TopRight.ToVector() + BottomLeft.ToVector() + BottomRight.ToVector()) / 4); } } /// /// The maximum alpha value of all four corners. /// public readonly float MaxAlpha { get { float max = TopLeft.Linear.A; if (TopRight.Linear.A > max) max = TopRight.Linear.A; if (BottomLeft.Linear.A > max) max = BottomLeft.Linear.A; if (BottomRight.Linear.A > max) max = BottomRight.Linear.A; return max; } } /// /// The minimum alpha value of all four corners. /// public readonly float MinAlpha { get { float min = TopLeft.Linear.A; if (TopRight.Linear.A < min) min = TopRight.Linear.A; if (BottomLeft.Linear.A < min) min = BottomLeft.Linear.A; if (BottomRight.Linear.A < min) min = BottomRight.Linear.A; return min; } } public override readonly string ToString() => HasSingleColour ? $@"{TopLeft} (Single)" : $@"{TopLeft}, {TopRight}, {BottomLeft}, {BottomRight}"; public static implicit operator ColourInfo(SRGBColour colour) => SingleColour(colour); public static implicit operator SRGBColour(ColourInfo colour) => colour.singleColour; public static implicit operator ColourInfo(Color4 colour) => (SRGBColour)colour; public static implicit operator Color4(ColourInfo colour) => (SRGBColour)colour; public static implicit operator ColourInfo(Colour4 colour) => (SRGBColour)colour; public static implicit operator Colour4(ColourInfo colour) => (SRGBColour)colour; } }