mirror of https://github.com/SixLabors/ImageSharp
5 changed files with 251 additions and 44 deletions
@ -0,0 +1,165 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System.Buffers; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using SixLabors.ImageSharp.Memory; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Owns the aligned luma and chroma planes used by one AV1 encoder frame.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TSample">The native unsigned sample storage type.</typeparam>
|
||||
|
internal sealed class Av1EncoderFrameBuffer<TSample> : IDisposable |
||||
|
where TSample : unmanaged |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// The complete frame owner, or <see langword="null"/> after disposal.
|
||||
|
/// </summary>
|
||||
|
private IMemoryOwner<TSample>? owner; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="Av1EncoderFrameBuffer{TSample}"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="configuration">The configuration providing the frame allocator.</param>
|
||||
|
/// <param name="width">The visible luma width.</param>
|
||||
|
/// <param name="height">The visible luma height.</param>
|
||||
|
/// <param name="bitDepth">The native component precision.</param>
|
||||
|
/// <param name="colorFormat">The native luma and chroma sampling layout.</param>
|
||||
|
/// <param name="chromaPositionX">The horizontal chroma position in half-luma-sample units.</param>
|
||||
|
/// <param name="chromaPositionY">The vertical chroma position in half-luma-sample units.</param>
|
||||
|
public Av1EncoderFrameBuffer( |
||||
|
Configuration configuration, |
||||
|
int width, |
||||
|
int height, |
||||
|
int bitDepth, |
||||
|
Av1ColorFormat colorFormat, |
||||
|
int chromaPositionX, |
||||
|
int chromaPositionY) |
||||
|
{ |
||||
|
int subsamplingX = colorFormat is Av1ColorFormat.Yuv420 or Av1ColorFormat.Yuv422 ? 1 : 0; |
||||
|
int subsamplingY = colorFormat == Av1ColorFormat.Yuv420 ? 1 : 0; |
||||
|
Size codedSize = Av1EncoderFrame<TSample>.GetCodedSize(width, height); |
||||
|
Size lumaSize = Av1EncoderFrame<TSample>.GetPlaneBufferSize(width, height, 0, 0); |
||||
|
int lumaElementCount = checked(lumaSize.Width * lumaSize.Height); |
||||
|
Size chromaSize = colorFormat == Av1ColorFormat.Yuv400 |
||||
|
? Size.Empty |
||||
|
: Av1EncoderFrame<TSample>.GetPlaneBufferSize(width, height, subsamplingX, subsamplingY); |
||||
|
|
||||
|
int chromaElementCount = checked(chromaSize.Width * chromaSize.Height); |
||||
|
int planeAlignment = Math.Max(32 / Unsafe.SizeOf<TSample>(), 1); |
||||
|
int chromaBlueOffset = Align(lumaElementCount, planeAlignment); |
||||
|
int chromaRedOffset = Align(checked(chromaBlueOffset + chromaElementCount), planeAlignment); |
||||
|
int storageLength = colorFormat == Av1ColorFormat.Yuv400 |
||||
|
? lumaElementCount |
||||
|
: checked(chromaRedOffset + chromaElementCount); |
||||
|
|
||||
|
// Libaom keeps the three component planes in one 32-byte-aligned frame allocation. The non-owning
|
||||
|
// Buffer2D views preserve ImageSharp's row API without introducing separate plane rents or copies.
|
||||
|
IMemoryOwner<TSample> owner = configuration.MemoryAllocator.Allocate<TSample>(storageLength); |
||||
|
Memory<TSample> storage = owner.Memory; |
||||
|
Buffer2D<TSample> luma = Buffer2D<TSample>.WrapMemory( |
||||
|
storage[..lumaElementCount], |
||||
|
lumaSize.Width, |
||||
|
lumaSize.Height); |
||||
|
|
||||
|
this.Luma = luma; |
||||
|
|
||||
|
Buffer2DRegion<TSample> lumaRegion = luma.GetRegion( |
||||
|
Av1EncoderFrame<TSample>.LumaBorder, |
||||
|
Av1EncoderFrame<TSample>.LumaBorder, |
||||
|
codedSize.Width, |
||||
|
codedSize.Height); |
||||
|
|
||||
|
Buffer2DRegion<TSample> chromaBlueRegion = default; |
||||
|
Buffer2DRegion<TSample> chromaRedRegion = default; |
||||
|
if (colorFormat != Av1ColorFormat.Yuv400) |
||||
|
{ |
||||
|
Buffer2D<TSample> chromaBlue = Buffer2D<TSample>.WrapMemory( |
||||
|
storage.Slice(chromaBlueOffset, chromaElementCount), |
||||
|
chromaSize.Width, |
||||
|
chromaSize.Height); |
||||
|
|
||||
|
Buffer2D<TSample> chromaRed = Buffer2D<TSample>.WrapMemory( |
||||
|
storage.Slice(chromaRedOffset, chromaElementCount), |
||||
|
chromaSize.Width, |
||||
|
chromaSize.Height); |
||||
|
|
||||
|
this.ChromaBlue = chromaBlue; |
||||
|
this.ChromaRed = chromaRed; |
||||
|
|
||||
|
int chromaBorderX = Av1EncoderFrame<TSample>.LumaBorder >> subsamplingX; |
||||
|
int chromaBorderY = Av1EncoderFrame<TSample>.LumaBorder >> subsamplingY; |
||||
|
int codedChromaWidth = codedSize.Width >> subsamplingX; |
||||
|
int codedChromaHeight = codedSize.Height >> subsamplingY; |
||||
|
chromaBlueRegion = chromaBlue.GetRegion( |
||||
|
chromaBorderX, |
||||
|
chromaBorderY, |
||||
|
codedChromaWidth, |
||||
|
codedChromaHeight); |
||||
|
|
||||
|
chromaRedRegion = chromaRed.GetRegion( |
||||
|
chromaBorderX, |
||||
|
chromaBorderY, |
||||
|
codedChromaWidth, |
||||
|
codedChromaHeight); |
||||
|
} |
||||
|
|
||||
|
this.owner = owner; |
||||
|
this.Frame = new( |
||||
|
lumaRegion, |
||||
|
chromaBlueRegion, |
||||
|
chromaRedRegion, |
||||
|
width, |
||||
|
height, |
||||
|
bitDepth, |
||||
|
colorFormat, |
||||
|
chromaPositionX, |
||||
|
chromaPositionY); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the non-owning coded frame view.
|
||||
|
/// </summary>
|
||||
|
public Av1EncoderFrame<TSample> Frame { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the complete padded luma plane.
|
||||
|
/// </summary>
|
||||
|
public Buffer2D<TSample> Luma { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the complete padded blue-difference chroma plane.
|
||||
|
/// </summary>
|
||||
|
public Buffer2D<TSample>? ChromaBlue { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the complete padded red-difference chroma plane.
|
||||
|
/// </summary>
|
||||
|
public Buffer2D<TSample>? ChromaRed { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Releases the complete frame allocation.
|
||||
|
/// </summary>
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
IMemoryOwner<TSample>? ownedMemory = this.owner; |
||||
|
this.owner = null; |
||||
|
if (ownedMemory is null) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
this.Luma.Dispose(); |
||||
|
this.ChromaBlue?.Dispose(); |
||||
|
this.ChromaRed?.Dispose(); |
||||
|
ownedMemory.Dispose(); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Aligns an element offset to the next component-plane boundary.
|
||||
|
/// </summary>
|
||||
|
private static int Align(int value, int alignment) |
||||
|
=> checked((value + alignment - 1) & -alignment); |
||||
|
} |
||||
Loading…
Reference in new issue