ImageSharp/ImageSharp.Drawing/Processing/Backends/DefaultRasterizer.RasterizableGeometry.cs
2026-08-03 22:31:27 +02:00

175 lines
7.9 KiB
C#

// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System;
using System.Buffers;
namespace SixLabors.ImageSharp.Drawing.Processing.Backends {
internal static partial class DefaultRasterizer
{
/// <summary>
/// Flush-scoped retained row-local raster payload for one prepared fill geometry.
/// </summary>
internal sealed class RasterizableGeometry : IDisposable
{
private readonly RasterizableBandInfo[] bandInfos;
private readonly LineArrayX16Y16Block?[]? linesX16;
private readonly LineArrayX32Y16Block?[]? linesX32;
private readonly int[] firstBlockLineCounts;
private readonly IMemoryOwner<int>?[] startCoverTable;
/// <summary>
/// Initializes a new instance of the <see cref="RasterizableGeometry"/> class.
/// </summary>
/// <param name="firstRowBandIndex">The first absolute row-band index touched by the geometry.</param>
/// <param name="rowBandCount">The number of retained local row bands owned by the geometry.</param>
/// <param name="width">The geometry-local visible band width in pixels.</param>
/// <param name="wordsPerRow">The bit-vector width in machine words required by the geometry.</param>
/// <param name="coverStride">The scanner cover/area stride required by the geometry.</param>
/// <param name="bandHeight">The retained row-band height in pixels.</param>
/// <param name="isX16">Indicates whether the geometry uses the narrow X16Y16 line encoding.</param>
/// <param name="bandInfos">The retained metadata for each local row band.</param>
/// <param name="linesX16">The retained narrow line chains for each local row band.</param>
/// <param name="linesX32">The retained wide line chains for each local row band.</param>
/// <param name="firstBlockLineCounts">The valid line count in each front retained block.</param>
/// <param name="startCoverTable">The retained start-cover table for each local row band.</param>
public RasterizableGeometry(
int firstRowBandIndex,
int rowBandCount,
int width,
int wordsPerRow,
int coverStride,
int bandHeight,
bool isX16,
RasterizableBandInfo[] bandInfos,
LineArrayX16Y16Block?[]? linesX16,
LineArrayX32Y16Block?[]? linesX32,
int[] firstBlockLineCounts,
IMemoryOwner<int>?[] startCoverTable)
{
this.FirstRowBandIndex = firstRowBandIndex;
this.RowBandCount = rowBandCount;
this.Width = width;
this.WordsPerRow = wordsPerRow;
this.CoverStride = coverStride;
this.BandHeight = bandHeight;
this.IsX16 = isX16;
this.bandInfos = bandInfos;
this.linesX16 = linesX16;
this.linesX32 = linesX32;
this.firstBlockLineCounts = firstBlockLineCounts;
this.startCoverTable = startCoverTable;
}
/// <summary>
/// Gets the first absolute row-band index touched by this geometry.
/// </summary>
public int FirstRowBandIndex { get; }
/// <summary>
/// Gets the number of retained local row bands owned by this geometry.
/// </summary>
public int RowBandCount { get; }
/// <summary>
/// Gets the geometry-local visible band width in pixels.
/// </summary>
public int Width { get; }
/// <summary>
/// Gets the bit-vector width in machine words required by this geometry.
/// </summary>
public int WordsPerRow { get; }
/// <summary>
/// Gets the scanner cover/area stride required by this geometry.
/// </summary>
public int CoverStride { get; }
/// <summary>
/// Gets the retained row-band height in pixels.
/// </summary>
public int BandHeight { get; }
/// <summary>
/// Gets a value indicating whether this geometry uses Blaze's narrow X16Y16 line arrays.
/// </summary>
public bool IsX16 { get; }
/// <summary>
/// Returns <see langword="true"/> when the given local row band has retained coverage payload.
/// </summary>
/// <param name="localRowIndex">The local row band index.</param>
/// <returns><see langword="true"/> when the row band has retained coverage; otherwise <see langword="false"/>.</returns>
public bool HasCoverage(int localRowIndex) => this.bandInfos[localRowIndex].HasCoverage;
/// <summary>
/// Gets the retained narrow line block chain for one local row.
/// </summary>
/// <param name="localRowIndex">The local row band index.</param>
/// <returns>The retained narrow line chain for the row.</returns>
public LineArrayX16Y16Block? GetLinesX16ForRow(int localRowIndex) => this.linesX16![localRowIndex];
/// <summary>
/// Gets the retained wide line block chain for one local row.
/// </summary>
/// <param name="localRowIndex">The local row band index.</param>
/// <returns>The retained wide line chain for the row.</returns>
public LineArrayX32Y16Block? GetLinesX32ForRow(int localRowIndex) => this.linesX32![localRowIndex];
/// <summary>
/// Gets the number of valid lines in the first retained block for a local row.
/// </summary>
/// <param name="localRowIndex">The local row band index.</param>
/// <returns>The valid line count in the front retained block.</returns>
public int GetFirstBlockLineCountForRow(int localRowIndex) => this.firstBlockLineCounts[localRowIndex];
/// <summary>
/// Gets the retained start-cover table entry for a local row, if one exists.
/// </summary>
/// <param name="localRowIndex">The local row band index.</param>
/// <returns>The retained start-cover span for the row.</returns>
public ReadOnlySpan<int> GetCoversForRow(int localRowIndex)
{
IMemoryOwner<int>? covers = this.startCoverTable[localRowIndex];
return covers is null ? ReadOnlySpan<int>.Empty : covers.Memory.Span[..this.BandHeight];
}
/// <summary>
/// Gets the retained start-cover row payload without further interpretation, matching Blaze naming.
/// </summary>
/// <param name="localRowIndex">The local row band index.</param>
/// <returns>The retained start-cover span for the row.</returns>
public ReadOnlySpan<int> GetActualCoversForRow(int localRowIndex) => this.GetCoversForRow(localRowIndex);
/// <summary>
/// Gets retained metadata for one local row band.
/// </summary>
/// <param name="localRowIndex">The local row band index.</param>
/// <returns>The retained band metadata.</returns>
public RasterizableBandInfo GetBandInfo(int localRowIndex) => this.bandInfos[localRowIndex];
/// <summary>
/// Releases the retained line blocks and start-cover storage.
/// </summary>
public void Dispose()
{
if (this.linesX16 is not null)
{
Array.Clear(this.linesX16);
}
if (this.linesX32 is not null)
{
Array.Clear(this.linesX32);
}
for (int i = 0; i < this.startCoverTable.Length; i++)
{
this.startCoverTable[i]?.Dispose();
}
}
}
}
}