// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System;
using System.Diagnostics.CodeAnalysis;
using System.Numerics;
using SixLabors.Fonts;
using SixLabors.Fonts.Rendering;
using SixLabors.ImageSharp.Drawing.Helpers;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Drawing.Processing.Processors.Text {
///
/// Utilities to translate format-agnostic paints (from Fonts) into ImageSharp.Drawing brushes.
///
internal sealed partial class RichTextGlyphRenderer
{
///
/// Attempts to create an ImageSharp.Drawing from a .
///
/// The paint definition coming from the interpreter.
/// A transform to apply to the brush coordinates.
/// The resulting brush, or if the paint is unsupported.
/// if a brush could be created; otherwise, .
public static bool TryCreateBrush([NotNullWhen(true)] Paint? paint, Matrix4x4 transform, [NotNullWhen(true)] out Brush? brush)
{
brush = null;
if (paint is null)
{
return false;
}
switch (paint)
{
case SolidPaint sp:
brush = new SolidBrush(ToColor(sp.Color, sp.Opacity));
return true;
case LinearGradientPaint lg:
return TryCreateLinearGradientBrush(lg, transform, out brush);
case RadialGradientPaint rg:
return TryCreateRadialGradientBrush(rg, transform, out brush);
case SweepGradientPaint sg:
return TryCreateSweepGradientBrush(sg, transform, out brush);
default:
return false;
}
}
///
/// Creates a from a .
///
/// The linear gradient paint.
/// The transform to apply to the gradient points.
/// The resulting brush.
/// if created; otherwise, .
private static bool TryCreateLinearGradientBrush(LinearGradientPaint paint, Matrix4x4 transform, out Brush? brush)
{
// Map gradient stops (apply paint opacity multiplier to each stop's alpha).
ColorStop[] stops = ToColorStops(paint.Stops, paint.Opacity);
// Map spread method.
GradientRepetitionMode mode = MapSpread(paint.Spread);
PointF p0 = paint.P0;
PointF p1 = paint.P1;
PointF? p2 = paint.P2;
// Apply any transform defined on the paint.
if (!transform.IsIdentity)
{
p0 = PointF.Transform(p0, transform);
p1 = PointF.Transform(p1, transform);
if (p2.HasValue)
{
p2 = PointF.Transform(p2.Value, transform);
}
}
if (p2.HasValue)
{
brush = new LinearGradientBrush(p0, p1, p2.Value, mode, stops);
return true;
}
brush = new LinearGradientBrush(p0, p1, mode, stops);
return true;
}
///
/// Creates a from a .
///
/// The radial gradient paint.
/// The transform to apply to the gradient center point.
/// The resulting brush.
/// if created; otherwise, .
private static bool TryCreateRadialGradientBrush(RadialGradientPaint paint, Matrix4x4 transform, out Brush? brush)
{
// Map gradient stops (apply paint opacity multiplier to each stop's alpha).
ColorStop[] stops = ToColorStops(paint.Stops, paint.Opacity);
// Map spread method.
GradientRepetitionMode mode = MapSpread(paint.Spread);
// Apply any transform defined on the paint.
PointF center0 = paint.Center0;
PointF center1 = paint.Center1;
float radius0 = paint.Radius0;
float radius1 = paint.Radius1;
if (!transform.IsIdentity)
{
center0 = PointF.Transform(center0, transform);
center1 = PointF.Transform(center1, transform);
float scale = MatrixUtilities.GetAverageScale(in transform);
radius0 *= scale;
radius1 *= scale;
}
brush = new RadialGradientBrush(center0, radius0, center1, radius1, mode, stops);
return true;
}
///
/// Creates a from a .
///
/// The sweep gradient paint.
/// The transform to apply to the gradient center point.
/// The resulting brush.
/// if created; otherwise, .
private static bool TryCreateSweepGradientBrush(SweepGradientPaint paint, Matrix4x4 transform, out Brush? brush)
{
// Map gradient stops (apply paint opacity multiplier to each stop's alpha).
ColorStop[] stops = ToColorStops(paint.Stops, paint.Opacity);
// Map spread method.
GradientRepetitionMode mode = MapSpread(paint.Spread);
// Apply any transform defined on the paint.
PointF center = paint.Center;
if (!transform.IsIdentity)
{
center = PointF.Transform(center, transform);
}
brush = new SweepGradientBrush(center, paint.StartAngle, paint.EndAngle, mode, stops);
return true;
}
///
/// Maps an to .
///
/// The spread method.
/// The repetition mode.
private static GradientRepetitionMode MapSpread(SpreadMethod spread)
=> spread switch
{
SpreadMethod.Reflect => GradientRepetitionMode.Reflect,
SpreadMethod.Repeat => GradientRepetitionMode.Repeat,
// Pad extends edge colors, which matches 'None' (not 'DontFill').
_ => GradientRepetitionMode.None,
};
///
/// Converts gradient stops and applies a paint opacity multiplier.
///
/// The source stops.
/// The paint opacity in range [0,1].
/// An array of .
private static ColorStop[] ToColorStops(ReadOnlySpan stops, float paintOpacity)
{
if (stops.Length == 0)
{
return [];
}
ColorStop[] result = new ColorStop[stops.Length];
for (int i = 0; i < stops.Length; i++)
{
GradientStop s = stops[i];
Color c = ToColor(s.Color, paintOpacity);
result[i] = new ColorStop(s.Offset, c);
}
return result;
}
///
/// Converts a with an additional opacity multiplier to ImageSharp .
///
/// The glyph color.
/// The opacity multiplier in range [0,1].
/// The ImageSharp color.
private static Color ToColor(in GlyphColor c, float opacity)
{
float a = Math.Clamp(c.A / 255f * Math.Clamp(opacity, 0f, 1f), 0f, 1f);
byte aa = (byte)MathF.Round(a * 255f);
return Color.FromPixel(new Rgba32(c.R, c.G, c.B, aa));
}
}
}