Skip to content

Commit

Permalink
Merge pull request #6111 from EVAST9919/icons-shadow
Browse files Browse the repository at this point in the history
Simplify `SpriteIcon`
  • Loading branch information
peppy authored Jan 9, 2024
2 parents eeaf32a + 95f1816 commit 126bd17
Showing 1 changed file with 100 additions and 80 deletions.
180 changes: 100 additions & 80 deletions osu.Framework/Graphics/Sprites/SpriteIcon.cs
Original file line number Diff line number Diff line change
@@ -1,80 +1,44 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

#nullable disable

using System;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Rendering;
using osu.Framework.Graphics.Shaders;
using osu.Framework.Graphics.Textures;
using osu.Framework.IO.Stores;
using osu.Framework.Layout;
using osuTK;
using osuTK.Graphics;

namespace osu.Framework.Graphics.Sprites
{
/// <summary>
/// A sprite representing an icon.
/// A drawable representing an icon.
/// Uses <see cref="FontStore"/> to perform character lookups.
/// </summary>
public partial class SpriteIcon : CompositeDrawable
public partial class SpriteIcon : Drawable, ITexturedShaderDrawable
{
private Sprite spriteShadow;
private Sprite spriteMain;

private readonly LayoutValue layout = new LayoutValue(Invalidation.Colour, conditions: (s, _) => ((SpriteIcon)s).Shadow);
private Container shadowVisibility;
public IShader? TextureShader { get; private set; }

private FontStore store;

public SpriteIcon()
{
AddLayout(layout);
}
private FontStore store = null!;

[BackgroundDependencyLoader]
private void load(FontStore store)
private void load(FontStore store, ShaderManager shaders)
{
this.store = store;

InternalChildren = new Drawable[]
{
shadowVisibility = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Child = spriteShadow = new Sprite
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fit,
Position = shadowOffset,
Colour = shadowColour
},
Alpha = shadow ? 1 : 0,
},
spriteMain = new Sprite
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fit
},
};

updateTexture();
TextureShader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE);
}

protected override void LoadComplete()
{
base.LoadComplete();
updateTexture();
updateShadow();
}

private IconUsage loadedIcon;
private Texture? texture;

private void updateTexture()
{
Expand All @@ -86,8 +50,7 @@ private void updateTexture()

if (glyph != null)
{
spriteMain.Texture = glyph.Texture;
spriteShadow.Texture = glyph.Texture;
texture = glyph.Texture;

if (Size == Vector2.Zero)
Size = new Vector2(glyph.Width, glyph.Height);
Expand All @@ -96,31 +59,18 @@ private void updateTexture()
loadedIcon = loadableIcon;
}

protected override void Update()
{
if (!layout.IsValid)
{
//adjust shadow alpha based on highest component intensity to avoid muddy display of darker text.
//squared result for quadratic fall-off seems to give the best result.
var avgColour = (Color4)DrawColourInfo.Colour.AverageColour;

spriteShadow.Alpha = MathF.Pow(Math.Max(Math.Max(avgColour.R, avgColour.G), avgColour.B), 2);

layout.Validate();
}
}

private bool shadow;

public bool Shadow
{
get => shadow;
set
{
shadow = value;
if (shadow == value)
return;

if (IsLoaded)
updateShadow();
shadow = value;
Invalidate(Invalidation.DrawNode);
}
}

Expand All @@ -134,10 +84,11 @@ public Color4 ShadowColour
get => shadowColour;
set
{
shadowColour = value;
if (shadowColour == value)
return;

if (IsLoaded)
updateShadow();
shadowColour = value;
Invalidate(Invalidation.DrawNode);
}
}

Expand All @@ -151,20 +102,14 @@ public Vector2 ShadowOffset
get => shadowOffset;
set
{
shadowOffset = value;
if (shadowOffset == value)
return;

if (IsLoaded)
updateShadow();
shadowOffset = value;
Invalidate(Invalidation.DrawNode);
}
}

private void updateShadow()
{
shadowVisibility.Alpha = shadow ? 1 : 0;
spriteShadow.Colour = shadowColour;
spriteShadow.Position = shadowOffset;
}

private IconUsage icon;

public IconUsage Icon
Expand All @@ -179,5 +124,80 @@ public IconUsage Icon
updateTexture();
}
}

protected override DrawNode CreateDrawNode() => new SpriteIconDrawNode(this);

private class SpriteIconDrawNode : TexturedShaderDrawNode
{
protected new SpriteIcon Source => (SpriteIcon)base.Source;

public SpriteIconDrawNode(SpriteIcon source)
: base(source)
{
}

private bool shadow;
private ColourInfo shadowDrawColour;
private Quad shadowDrawQuad;
private Quad screenSpaceDrawQuad;
private Texture? texture;

public override void ApplyState()
{
base.ApplyState();

texture = Source.texture;
if (texture == null)
return;

shadow = Source.shadow;

RectangleF drawRect = Source.DrawRectangle;

// scale texture to fit into drawable
float scale = Math.Min(drawRect.Width / texture.Width, drawRect.Height / texture.Height);
drawRect.Size = texture.Size * scale;

// move draw rectangle to make texture centered
drawRect.Location += (Source.DrawRectangle.Size - drawRect.Size) * 0.5f;
screenSpaceDrawQuad = Source.ToScreenSpace(drawRect);

if (!shadow)
return;

RectangleF offsetRect = drawRect;
offsetRect.Location += Source.shadowOffset;
shadowDrawQuad = Source.ToScreenSpace(offsetRect);

ColourInfo shadowCol = Source.shadowColour;

//adjust shadow alpha based on highest component intensity to avoid muddy display of darker text.
//squared result for quadratic fall-off seems to give the best result.
var avgColour = (Color4)DrawColourInfo.Colour.AverageColour;
float alpha = MathF.Pow(Math.Max(Math.Max(avgColour.R, avgColour.G), avgColour.B), 2);

shadowCol = shadowCol.MultiplyAlpha(alpha);

shadowDrawColour = DrawColourInfo.Colour;
shadowDrawColour.ApplyChild(shadowCol);
}

protected override void Draw(IRenderer renderer)
{
base.Draw(renderer);

if (texture?.Available != true)
return;

BindTextureShader(renderer);

if (shadow)
renderer.DrawQuad(texture, shadowDrawQuad, shadowDrawColour);

renderer.DrawQuad(texture, screenSpaceDrawQuad, DrawColourInfo.Colour);

UnbindTextureShader(renderer);
}
}
}
}

0 comments on commit 126bd17

Please sign in to comment.