// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.Fonts.Rendering; namespace SixLabors.Fonts.Tables.Cff { /// /// Used to apply a transform against any glyphs rendered by the engine. /// internal struct TransformingGlyphRenderer : IGlyphRenderer { private static readonly Vector2 YInverter = new(1, -1); private readonly IGlyphRenderer renderer; private Vector2 origin; private Vector2 scale; private Vector2 offset; private Matrix3x2 transform; /// /// Initializes a new instance of the struct. /// /// The underlying glyph renderer to delegate to. /// The origin point for rendering. /// The scale factor to apply. /// The offset to apply before transformation. /// The transformation matrix to apply. public TransformingGlyphRenderer(IGlyphRenderer renderer, Vector2 origin, Vector2 scale, Vector2 offset, Matrix3x2 transform) { this.renderer = renderer; this.origin = origin; this.scale = scale; this.offset = offset; this.transform = transform; this.IsOpen = false; } /// /// Gets or sets a value indicating whether a figure is currently open. /// public bool IsOpen { get; set; } /// public void BeginFigure() { this.IsOpen = true; this.renderer.BeginFigure(); } /// public bool BeginGlyph(in FontRectangle bounds, in GlyphRendererParameters parameters) { this.IsOpen = false; return this.renderer.BeginGlyph(in bounds, in parameters); } /// public void BeginText(in FontRectangle bounds) { this.IsOpen = false; this.renderer.BeginText(in bounds); } /// public void EndFigure() { this.IsOpen = false; this.renderer.EndFigure(); } /// public void EndGlyph() { this.IsOpen = false; this.renderer.EndGlyph(); } /// public void EndText() { this.IsOpen = false; this.renderer.EndText(); } /// public void LineTo(Vector2 point) { this.IsOpen = true; this.renderer.LineTo(this.Transform(point)); } /// public void MoveTo(Vector2 point) { if (this.IsOpen) { this.EndFigure(); } this.BeginFigure(); this.renderer.MoveTo(this.Transform(point)); this.IsOpen = true; } /// public void ArcTo(float radiusX, float radiusY, float rotationDegrees, bool largeArc, bool sweep, Vector2 point) { this.IsOpen = true; this.renderer.ArcTo(radiusX * this.scale.X, radiusY * this.scale.Y, rotationDegrees, largeArc, sweep, this.Transform(point)); } /// public void CubicBezierTo(Vector2 secondControlPoint, Vector2 thirdControlPoint, Vector2 point) { this.IsOpen = true; this.renderer.CubicBezierTo(this.Transform(secondControlPoint), this.Transform(thirdControlPoint), this.Transform(point)); } /// public void QuadraticBezierTo(Vector2 secondControlPoint, Vector2 point) { this.IsOpen = true; this.renderer.QuadraticBezierTo(this.Transform(secondControlPoint), this.Transform(point)); } /// public readonly TextDecorations EnabledDecorations() => this.renderer.EnabledDecorations(); /// public readonly void SetDecoration(TextDecorations textDecorations, Vector2 start, Vector2 end, float thickness) => this.renderer.SetDecoration(textDecorations, this.Transform(start), this.Transform(end), thickness); /// /// Applies the scale, offset, transform matrix, and Y-axis inversion to the given point. /// /// The point to transform. /// The transformed point. [MethodImpl(MethodImplOptions.AggressiveInlining)] private readonly Vector2 Transform(Vector2 point) => (Vector2.Transform((point * this.scale) + this.offset, this.transform) * YInverter) + this.origin; /// public readonly void BeginLayer(Paint? paint, FillRule fillRule, ClipQuad? clipBounds) => this.renderer.BeginLayer(paint, fillRule, clipBounds); /// public readonly void EndLayer() => this.renderer.EndLayer(); } }