mirror of https://github.com/SixLabors/ImageSharp
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
164 lines
6.4 KiB
164 lines
6.4 KiB
// Copyright (c) Six Labors and contributors.
|
|
// Licensed under the Apache License, Version 2.0.
|
|
using System;
|
|
using System.Runtime.CompilerServices;
|
|
|
|
using SixLabors.Primitives;
|
|
|
|
namespace SixLabors.ImageSharp.Memory
|
|
{
|
|
/// <summary>
|
|
/// Represents a rectangular area inside a 2D memory buffer (<see cref="Buffer2D{T}"/>).
|
|
/// This type is kind-of 2D Span, but it can live on heap.
|
|
/// </summary>
|
|
/// <typeparam name="T">The element type</typeparam>
|
|
internal readonly struct BufferArea<T>
|
|
where T : struct
|
|
{
|
|
/// <summary>
|
|
/// The rectangle specifying the boundaries of the area in <see cref="DestinationBuffer"/>.
|
|
/// </summary>
|
|
public readonly Rectangle Rectangle;
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public BufferArea(Buffer2D<T> destinationBuffer, Rectangle rectangle)
|
|
{
|
|
ImageSharp.DebugGuard.MustBeGreaterThanOrEqualTo(rectangle.X, 0, nameof(rectangle));
|
|
ImageSharp.DebugGuard.MustBeGreaterThanOrEqualTo(rectangle.Y, 0, nameof(rectangle));
|
|
ImageSharp.DebugGuard.MustBeLessThanOrEqualTo(rectangle.Width, destinationBuffer.Width, nameof(rectangle));
|
|
ImageSharp.DebugGuard.MustBeLessThanOrEqualTo(rectangle.Height, destinationBuffer.Height, nameof(rectangle));
|
|
|
|
this.DestinationBuffer = destinationBuffer;
|
|
this.Rectangle = rectangle;
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public BufferArea(Buffer2D<T> destinationBuffer)
|
|
: this(destinationBuffer, destinationBuffer.FullRectangle())
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the <see cref="Buffer2D{T}"/> being pointed by this instance.
|
|
/// </summary>
|
|
public Buffer2D<T> DestinationBuffer { get; }
|
|
|
|
/// <summary>
|
|
/// Gets the size of the area.
|
|
/// </summary>
|
|
public Size Size => this.Rectangle.Size;
|
|
|
|
/// <summary>
|
|
/// Gets the width
|
|
/// </summary>
|
|
public int Width => this.Rectangle.Width;
|
|
|
|
/// <summary>
|
|
/// Gets the height
|
|
/// </summary>
|
|
public int Height => this.Rectangle.Height;
|
|
|
|
/// <summary>
|
|
/// Gets the pixel stride which is equal to the width of <see cref="DestinationBuffer"/>.
|
|
/// </summary>
|
|
public int Stride => this.DestinationBuffer.Width;
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating whether the area refers to the entire <see cref="DestinationBuffer"/>
|
|
/// </summary>
|
|
public bool IsFullBufferArea => this.Size == this.DestinationBuffer.Size();
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value at the given index.
|
|
/// </summary>
|
|
/// <param name="x">The position inside a row</param>
|
|
/// <param name="y">The row index</param>
|
|
/// <returns>The reference to the value</returns>
|
|
public ref T this[int x, int y] => ref this.DestinationBuffer.GetSpan()[this.GetIndexOf(x, y)];
|
|
|
|
/// <summary>
|
|
/// Gets a reference to the [0,0] element.
|
|
/// </summary>
|
|
/// <returns>The reference to the [0,0] element</returns>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public ref T GetReferenceToOrigin() =>
|
|
ref this.DestinationBuffer.GetSpan()[(this.Rectangle.Y * this.DestinationBuffer.Width) + this.Rectangle.X];
|
|
|
|
/// <summary>
|
|
/// Gets a span to row 'y' inside this area.
|
|
/// </summary>
|
|
/// <param name="y">The row index</param>
|
|
/// <returns>The span</returns>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public Span<T> GetRowSpan(int y)
|
|
{
|
|
int yy = this.GetRowIndex(y);
|
|
int xx = this.Rectangle.X;
|
|
int width = this.Rectangle.Width;
|
|
|
|
return this.DestinationBuffer.GetSpan().Slice(yy + xx, width);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a sub-area as <see cref="BufferArea{T}"/>. (Similar to <see cref="Span{T}.Slice(int, int)"/>.)
|
|
/// </summary>
|
|
/// <param name="x">The x index at the subarea origo</param>
|
|
/// <param name="y">The y index at the subarea origo</param>
|
|
/// <param name="width">The desired width of the subarea</param>
|
|
/// <param name="height">The desired height of the subarea</param>
|
|
/// <returns>The subarea</returns>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public BufferArea<T> GetSubArea(int x, int y, int width, int height)
|
|
{
|
|
var rectangle = new Rectangle(x, y, width, height);
|
|
return this.GetSubArea(rectangle);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a sub-area as <see cref="BufferArea{T}"/>. (Similar to <see cref="Span{T}.Slice(int, int)"/>.)
|
|
/// </summary>
|
|
/// <param name="rectangle">The <see cref="Rectangle"/> specifying the boundaries of the subarea</param>
|
|
/// <returns>The subarea</returns>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public BufferArea<T> GetSubArea(Rectangle rectangle)
|
|
{
|
|
ImageSharp.DebugGuard.MustBeLessThanOrEqualTo(rectangle.Width, this.Rectangle.Width, nameof(rectangle));
|
|
ImageSharp.DebugGuard.MustBeLessThanOrEqualTo(rectangle.Height, this.Rectangle.Height, nameof(rectangle));
|
|
|
|
int x = this.Rectangle.X + rectangle.X;
|
|
int y = this.Rectangle.Y + rectangle.Y;
|
|
rectangle = new Rectangle(x, y, rectangle.Width, rectangle.Height);
|
|
return new BufferArea<T>(this.DestinationBuffer, rectangle);
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private int GetIndexOf(int x, int y)
|
|
{
|
|
int yy = this.GetRowIndex(y);
|
|
int xx = this.Rectangle.X + x;
|
|
return yy + xx;
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
internal int GetRowIndex(int y)
|
|
{
|
|
return (y + this.Rectangle.Y) * this.DestinationBuffer.Width;
|
|
}
|
|
|
|
public void Clear()
|
|
{
|
|
// Optimization for when the size of the area is the same as the buffer size.
|
|
if (this.IsFullBufferArea)
|
|
{
|
|
this.DestinationBuffer.GetSpan().Clear();
|
|
return;
|
|
}
|
|
|
|
for (int y = 0; y < this.Rectangle.Height; y++)
|
|
{
|
|
Span<T> row = this.GetRowSpan(y);
|
|
row.Clear();
|
|
}
|
|
}
|
|
}
|
|
}
|