mirror of https://github.com/SixLabors/ImageSharp
16 changed files with 846 additions and 126 deletions
File diff suppressed because one or more lines are too long
@ -0,0 +1,75 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Diagnostics.CodeAnalysis; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling; |
|||
|
|||
/// <summary>
|
|||
/// Stores the selected luma and chroma palette sizes and colors for one encoder block.
|
|||
/// </summary>
|
|||
[StructLayout(LayoutKind.Sequential, Size = StorageSize)] |
|||
internal struct Av1EncoderPaletteInfo |
|||
{ |
|||
/// <summary>
|
|||
/// The packed size of two palette sizes and three eight-color planes.
|
|||
/// </summary>
|
|||
public const int StorageSize = 50; |
|||
|
|||
private InlineArray2<byte> paletteSizes; |
|||
private InlineArray24<ushort> paletteColors; |
|||
|
|||
/// <summary>
|
|||
/// Gets the writable luma and shared chroma palette sizes.
|
|||
/// </summary>
|
|||
[UnscopedRef] |
|||
public Span<byte> PaletteSizes => this.paletteSizes; |
|||
|
|||
/// <summary>
|
|||
/// Gets the selected colors for one color plane.
|
|||
/// </summary>
|
|||
/// <param name="plane">The color plane.</param>
|
|||
/// <returns>The selected colors in palette-index order.</returns>
|
|||
[UnscopedRef] |
|||
public Span<ushort> GetColors(Av1Plane plane) |
|||
{ |
|||
int planeIndex = (int)plane; |
|||
int paletteSize = this.paletteSizes[planeIndex == 0 ? 0 : 1]; |
|||
Span<ushort> colors = this.paletteColors; |
|||
return colors.Slice(planeIndex * Av1Constants.PaletteMaxSize, paletteSize); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Stores the selected colors for one color plane.
|
|||
/// </summary>
|
|||
/// <param name="plane">The color plane.</param>
|
|||
/// <param name="colors">The colors in palette-index order.</param>
|
|||
public void SetColors(Av1Plane plane, ReadOnlySpan<ushort> colors) |
|||
{ |
|||
int offset = (int)plane * Av1Constants.PaletteMaxSize; |
|||
Span<ushort> destination = this.paletteColors; |
|||
colors.CopyTo(destination[offset..]); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Provides fixed storage for the luma and shared chroma palette sizes.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The stored value type.</typeparam>
|
|||
[InlineArray(2)] |
|||
private struct InlineArray2<T> |
|||
{ |
|||
private T element; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Provides fixed storage for all three eight-color palette planes.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The stored value type.</typeparam>
|
|||
[InlineArray(3 * Av1Constants.PaletteMaxSize)] |
|||
private struct InlineArray24<T> |
|||
{ |
|||
private T element; |
|||
} |
|||
} |
|||
@ -0,0 +1,62 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Buffers; |
|||
using SixLabors.ImageSharp.Memory; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling; |
|||
|
|||
/// <summary>
|
|||
/// Owns the reusable luma and chroma palette color-index maps for encoder block decisions.
|
|||
/// </summary>
|
|||
internal sealed class Av1EncoderPaletteMapBuffer : IDisposable |
|||
{ |
|||
/// <summary>
|
|||
/// The width and height of each maximum-superblock map.
|
|||
/// </summary>
|
|||
public const int MapLength = 1 << Av1Constants.MaxSuperBlockSizeLog2; |
|||
|
|||
/// <summary>
|
|||
/// The combined byte length of the luma and chroma maps.
|
|||
/// </summary>
|
|||
public const int StorageLength = 2 * MapLength * MapLength; |
|||
|
|||
private readonly IMemoryOwner<byte> owner; |
|||
private readonly Buffer2D<byte> luma; |
|||
private readonly Buffer2D<byte> chroma; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Av1EncoderPaletteMapBuffer"/> class.
|
|||
/// </summary>
|
|||
/// <param name="configuration">The configuration providing the encoder allocator.</param>
|
|||
public Av1EncoderPaletteMapBuffer(Configuration configuration) |
|||
{ |
|||
this.owner = configuration.MemoryAllocator.Allocate<byte>(StorageLength); |
|||
int mapArea = MapLength * MapLength; |
|||
Memory<byte> storage = this.owner.Memory[..StorageLength]; |
|||
this.luma = Buffer2D<byte>.WrapMemory(storage[..mapArea], MapLength, MapLength); |
|||
this.chroma = Buffer2D<byte>.WrapMemory(storage[mapArea..], MapLength, MapLength); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets a block-sized view of the luma or shared chroma color-index map.
|
|||
/// </summary>
|
|||
/// <param name="planeType">The luma or shared chroma plane class.</param>
|
|||
/// <param name="width">The padded plane-block width.</param>
|
|||
/// <param name="height">The padded plane-block height.</param>
|
|||
/// <returns>The reusable map region beginning at the workspace origin.</returns>
|
|||
public Buffer2DRegion<byte> GetMap(Av1PlaneType planeType, int width, int height) |
|||
=> new( |
|||
planeType == Av1PlaneType.Y ? this.luma : this.chroma, |
|||
new Rectangle(0, 0, width, height)); |
|||
|
|||
/// <summary>
|
|||
/// Returns the shared palette-map owner to the configured allocator.
|
|||
/// </summary>
|
|||
public void Dispose() |
|||
{ |
|||
this.luma.Dispose(); |
|||
this.chroma.Dispose(); |
|||
this.owner.Dispose(); |
|||
} |
|||
} |
|||
@ -0,0 +1,66 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling; |
|||
|
|||
/// <summary>
|
|||
/// Builds the sorted unique palette-color cache shared by AV1 encoder and decoder syntax.
|
|||
/// </summary>
|
|||
internal static class Av1PaletteCache |
|||
{ |
|||
/// <summary>
|
|||
/// Merges the sorted colors from the available above and left block palettes.
|
|||
/// </summary>
|
|||
/// <param name="aboveColors">The above block's sorted base colors.</param>
|
|||
/// <param name="leftColors">The left block's sorted base colors.</param>
|
|||
/// <param name="cache">The destination cache, which can hold both palettes.</param>
|
|||
/// <returns>The number of unique colors written to <paramref name="cache"/>.</returns>
|
|||
public static int Merge( |
|||
ReadOnlySpan<ushort> aboveColors, |
|||
ReadOnlySpan<ushort> leftColors, |
|||
Span<ushort> cache) |
|||
{ |
|||
int aboveIndex = 0; |
|||
int leftIndex = 0; |
|||
int count = 0; |
|||
while (aboveIndex < aboveColors.Length && leftIndex < leftColors.Length) |
|||
{ |
|||
ushort aboveColor = aboveColors[aboveIndex]; |
|||
ushort leftColor = leftColors[leftIndex]; |
|||
if (leftColor < aboveColor) |
|||
{ |
|||
Add(cache, ref count, leftColor); |
|||
leftIndex++; |
|||
} |
|||
else |
|||
{ |
|||
Add(cache, ref count, aboveColor); |
|||
aboveIndex++; |
|||
if (leftColor == aboveColor) |
|||
{ |
|||
leftIndex++; |
|||
} |
|||
} |
|||
} |
|||
|
|||
while (aboveIndex < aboveColors.Length) |
|||
{ |
|||
Add(cache, ref count, aboveColors[aboveIndex++]); |
|||
} |
|||
|
|||
while (leftIndex < leftColors.Length) |
|||
{ |
|||
Add(cache, ref count, leftColors[leftIndex++]); |
|||
} |
|||
|
|||
return count; |
|||
} |
|||
|
|||
private static void Add(Span<ushort> cache, ref int count, ushort color) |
|||
{ |
|||
if (count == 0 || cache[count - 1] != color) |
|||
{ |
|||
cache[count++] = color; |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue