// 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)); } } }