// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using System; using System.Collections.Generic; using System.Numerics; namespace SixLabors.ImageSharp.Drawing.Processing.Backends { /// /// Identifies the flush-time role carried by a . /// public enum CompositionCommandKind : byte { /// /// A fill-path command. /// FillLayer = 0, /// /// Starts an isolated compositing layer. /// BeginLayer = 1, /// /// Ends the most recently opened layer. /// EndLayer = 2 } /// /// One normalized fill-path or layer-based composition command queued for backend execution. /// /// /// This type carries fill-path commands plus inline layer boundaries. /// public readonly struct CompositionCommand { private readonly IPath? sourcePath; private readonly Brush? brush; private readonly DrawingOptions? drawingOptions; private readonly GraphicsOptions? layerGraphicsOptions; private readonly IReadOnlyList? clipPaths; private CompositionCommand( CompositionCommandKind kind, IPath? sourcePath, Brush? brush, DrawingOptions? drawingOptions, GraphicsOptions? layerGraphicsOptions, in RasterizerOptions rasterizerOptions, Rectangle targetBounds, Rectangle layerBounds, Point destinationOffset, IReadOnlyList? clipPaths, bool isInsideLayer) { this.Kind = kind; this.sourcePath = sourcePath; this.brush = brush; this.drawingOptions = drawingOptions; this.layerGraphicsOptions = layerGraphicsOptions; this.RasterizerOptions = rasterizerOptions; this.TargetBounds = targetBounds; this.LayerBounds = layerBounds; this.DestinationOffset = destinationOffset; this.clipPaths = clipPaths; this.IsInsideLayer = isInsideLayer; } /// /// Gets the command kind. /// public CompositionCommandKind Kind { get; } /// /// Gets the absolute bounds of the logical target for this command. /// public Rectangle TargetBounds { get; } /// /// Gets the absolute bounds of the layer opened by this command. /// /// /// Only meaningful for and /// . /// public Rectangle LayerBounds { get; } /// /// Gets the brush used during composition. /// public Brush Brush => this.brush ?? throw new InvalidOperationException("Layer commands do not carry a brush."); /// /// Gets the drawing options carried by the command. /// public DrawingOptions DrawingOptions => this.drawingOptions ?? throw new InvalidOperationException("Layer commands do not carry drawing options."); /// /// Gets graphics options used for composition or layer compositing. /// public GraphicsOptions GraphicsOptions => this.drawingOptions?.GraphicsOptions ?? this.layerGraphicsOptions!; /// /// Gets rasterizer options used to generate coverage. /// public RasterizerOptions RasterizerOptions { get; } /// /// Gets the absolute destination offset where the local coverage should be composited. /// public Point DestinationOffset { get; } /// /// Gets the source path carried by the command. /// public IPath SourcePath => this.sourcePath ?? throw new InvalidOperationException("Layer commands do not carry path geometry."); /// /// Gets the command transform. /// public Matrix4x4 Transform => this.drawingOptions?.Transform ?? Matrix4x4.Identity; /// /// Gets the clip paths carried by the command. /// public IReadOnlyList? ClipPaths => this.clipPaths; /// /// Gets the shape options carried by the command. /// public ShapeOptions ShapeOptions => this.drawingOptions?.ShapeOptions ?? throw new InvalidOperationException("Layer commands do not carry shape options."); /// /// Gets a value indicating whether the command was recorded inside a layer. /// public bool IsInsideLayer { get; } /// /// Creates a fill-path composition command. /// /// Path in target-local coordinates. /// Brush used during composition. /// Drawing options (graphics, shape, transform) used during composition. /// Rasterizer options used to generate coverage. /// The absolute bounds of the logical target for this command. /// Absolute destination offset where coverage is composited. /// Optional clip paths supplied with the command. /// True if the command was recorded inside a layer. /// The composition command. public static CompositionCommand Create( IPath path, Brush brush, DrawingOptions drawingOptions, in RasterizerOptions rasterizerOptions, Rectangle targetBounds, Point destinationOffset, IReadOnlyList? clipPaths, bool isInsideLayer) => new( CompositionCommandKind.FillLayer, path, brush, drawingOptions, null, in rasterizerOptions, targetBounds, default, destinationOffset, clipPaths, isInsideLayer); /// /// Creates a begin-layer composition command. is false on the /// BeginLayer marker itself; the flag is only meaningful for fills/strokes that follow it. /// /// The absolute bounds of the layer. /// The compositing options used when the layer closes. /// The begin-layer command. public static CompositionCommand CreateBeginLayer(Rectangle layerBounds, GraphicsOptions graphicsOptions) => new( CompositionCommandKind.BeginLayer, null, null, null, graphicsOptions, default, layerBounds, layerBounds, default, null, false); /// /// Creates an end-layer composition command. is false on the /// EndLayer marker itself; the flag is only meaningful for fills/strokes that preceded it. /// /// The absolute bounds of the layer being closed. /// The compositing options used by the layer. /// The end-layer command. public static CompositionCommand CreateEndLayer(Rectangle layerBounds, GraphicsOptions graphicsOptions) => new( CompositionCommandKind.EndLayer, null, null, null, graphicsOptions, default, layerBounds, layerBounds, default, null, false); } }