Browse Source

Fix HEIF encoder ownership

pull/2633/head
James Jackson-South 1 month ago
parent
commit
d2338aac8c
  1. 2
      HEIF_IMPLEMENTATION_PLAN.md
  2. 100
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileWriter.cs
  3. 39
      src/ImageSharp/Formats/Heif/HeifEncoderCore.cs
  4. 4
      tests/ImageSharp.Tests/Formats/Heif/Av1/Av1CoefficientsEntropyTests.cs

2
HEIF_IMPLEMENTATION_PLAN.md

@ -826,7 +826,7 @@ Encoder verification contract:
- [~] Finalized transform coefficients and packed EOB/type state now use raster-ordered, per-superblock plane segments matching current libaom's coefficient-pool geometry. One ImageSharp allocator owner replaces libaom's separate coefficient, EOB, and entropy-context allocations while preserving the full 1024 luma and 256-per-chroma 4x4 state capacity of a 128x128 4:2:0 superblock; the forward transform and mode-decision stages still need to populate this owner. - [~] Finalized transform coefficients and packed EOB/type state now use raster-ordered, per-superblock plane segments matching current libaom's coefficient-pool geometry. One ImageSharp allocator owner replaces libaom's separate coefficient, EOB, and entropy-context allocations while preserving the full 1024 luma and 256-per-chroma 4x4 state capacity of a 128x128 4:2:0 superblock; the forward transform and mode-decision stages still need to populate this owner.
- [~] Tile partition writing now follows current libaom's recursive `write_modes_sb` preorder traversal and `update_ext_partition_context` edge updates directly. The obsolete SVT-derived global geometry catalog and its unimplemented lookup are removed; transform geometry is derived in libaom's bounded 64x64 residual order, fixed intra transform-size symbols use the reference depth and neighbor contexts, frame-edge and segmentation syntax use mode-information units, and 128x128 CDEF units use libaom's 0-to-3 indexing and first-block strength ownership. Partition and mode analysis still need to populate these retained decisions; variable inter-transform syntax remains part of later inter-frame support. - [~] Tile partition writing now follows current libaom's recursive `write_modes_sb` preorder traversal and `update_ext_partition_context` edge updates directly. The obsolete SVT-derived global geometry catalog and its unimplemented lookup are removed; transform geometry is derived in libaom's bounded 64x64 residual order, fixed intra transform-size symbols use the reference depth and neighbor contexts, frame-edge and segmentation syntax use mode-information units, and 128x128 CDEF units use libaom's 0-to-3 indexing and first-block strength ownership. Partition and mode analysis still need to populate these retained decisions; variable inter-transform syntax remains part of later inter-frame support.
- [ ] Implement legal deblocking, CDEF, restoration, super-resolution, and film-grain signaling decisions. - [ ] Implement legal deblocking, CDEF, restoration, super-resolution, and film-grain signaling decisions.
- [~] The coefficient symbol encoder now reuses tile-lifetime level and context workspaces instead of allocating per transform. Every remaining encoder fragment must be audited before it becomes active. - [~] The coefficient symbol encoder now reuses tile-lifetime level and context workspaces instead of allocating per transform. The reference-type symbol encoder is passed normally through tile traversal, and the operation boundary owns the allocator-backed item payload stream for exactly one synchronous encode. Every remaining encoder fragment must be audited before it becomes active.
- [~] The planar conversion, forward transform, and forward quantizer use descending SIMD dispatch: Vector512, Vector256, Vector128, then scalar. Apply the same rule to every later hot-path family. - [~] The planar conversion, forward transform, and forward quantizer use descending SIMD dispatch: Vector512, Vector256, Vector128, then scalar. Apply the same rule to every later hot-path family.
- [~] Forward-quantizer FeatureTestRunner and zero-allocation tests compare every hardware tier with an independent scan-order scalar oracle shaped from current-main libaom. Both passed direct net11 VSTest in Release. - [~] Forward-quantizer FeatureTestRunner and zero-allocation tests compare every hardware tier with an independent scan-order scalar oracle shaped from current-main libaom. Both passed direct net11 VSTest in Release.
- [~] The combined-frame writer now completes the byte-counted uncompressed frame header before starting the optional multi-tile tile-group flag, matching current libaom's separate frame-header and tile-group writers. A non-uniform two-tile round trip verifies the explicit boundaries, both tile payloads, and complete stream consumption through direct net11 VSTest in Release. - [~] The combined-frame writer now completes the byte-counted uncompressed frame header before starting the optional multi-tile tile-group flag, matching current libaom's separate frame-header and tile-group writers. A non-uniform two-tile round trip verifies the explicit boundaries, both tile payloads, and complete stream consumption through direct net11 VSTest in Release.

100
src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileWriter.cs

@ -63,7 +63,7 @@ internal partial class Av1TileWriter
public static void WriteSuperblock( public static void WriteSuperblock(
Av1PictureControlSet pcs, Av1PictureControlSet pcs,
Av1EntropyCodingContext ec_ctx, Av1EntropyCodingContext ec_ctx,
ref Av1SymbolEncoder writer, Av1SymbolEncoder writer,
Av1Superblock superblock, Av1Superblock superblock,
Av1EncoderCoefficientBuffer coefficientBuffer, Av1EncoderCoefficientBuffer coefficientBuffer,
ushort tileIndex) ushort tileIndex)
@ -79,7 +79,7 @@ internal partial class Av1TileWriter
WritePartitionTree( WritePartitionTree(
pcs, pcs,
ec_ctx, ec_ctx,
ref writer, writer,
superblock, superblock,
coefficientBuffer, coefficientBuffer,
tileIndex, tileIndex,
@ -95,7 +95,7 @@ internal partial class Av1TileWriter
private static void WritePartitionTree( private static void WritePartitionTree(
Av1PictureControlSet pcs, Av1PictureControlSet pcs,
Av1EntropyCodingContext entropyCodingContext, Av1EntropyCodingContext entropyCodingContext,
ref Av1SymbolEncoder writer, Av1SymbolEncoder writer,
Av1Superblock superblock, Av1Superblock superblock,
Av1EncoderCoefficientBuffer coefficientBuffer, Av1EncoderCoefficientBuffer coefficientBuffer,
ushort tileIndex, ushort tileIndex,
@ -119,7 +119,7 @@ internal partial class Av1TileWriter
EncodePartition( EncodePartition(
pcs, pcs,
ref writer, writer,
blockSize, blockSize,
partition, partition,
blockOrigin, blockOrigin,
@ -131,7 +131,7 @@ internal partial class Av1TileWriter
WriteFinalBlock( WriteFinalBlock(
pcs, pcs,
entropyCodingContext, entropyCodingContext,
ref writer, writer,
superblock, superblock,
coefficientBuffer, coefficientBuffer,
tileIndex, tileIndex,
@ -143,7 +143,7 @@ internal partial class Av1TileWriter
WriteFinalBlock( WriteFinalBlock(
pcs, pcs,
entropyCodingContext, entropyCodingContext,
ref writer, writer,
superblock, superblock,
coefficientBuffer, coefficientBuffer,
tileIndex, tileIndex,
@ -155,7 +155,7 @@ internal partial class Av1TileWriter
WriteFinalBlock( WriteFinalBlock(
pcs, pcs,
entropyCodingContext, entropyCodingContext,
ref writer, writer,
superblock, superblock,
coefficientBuffer, coefficientBuffer,
tileIndex, tileIndex,
@ -168,7 +168,7 @@ internal partial class Av1TileWriter
WriteFinalBlock( WriteFinalBlock(
pcs, pcs,
entropyCodingContext, entropyCodingContext,
ref writer, writer,
superblock, superblock,
coefficientBuffer, coefficientBuffer,
tileIndex, tileIndex,
@ -180,7 +180,7 @@ internal partial class Av1TileWriter
WriteFinalBlock( WriteFinalBlock(
pcs, pcs,
entropyCodingContext, entropyCodingContext,
ref writer, writer,
superblock, superblock,
coefficientBuffer, coefficientBuffer,
tileIndex, tileIndex,
@ -193,7 +193,7 @@ internal partial class Av1TileWriter
WritePartitionTree( WritePartitionTree(
pcs, pcs,
entropyCodingContext, entropyCodingContext,
ref writer, writer,
superblock, superblock,
coefficientBuffer, coefficientBuffer,
tileIndex, tileIndex,
@ -204,7 +204,7 @@ internal partial class Av1TileWriter
WritePartitionTree( WritePartitionTree(
pcs, pcs,
entropyCodingContext, entropyCodingContext,
ref writer, writer,
superblock, superblock,
coefficientBuffer, coefficientBuffer,
tileIndex, tileIndex,
@ -215,7 +215,7 @@ internal partial class Av1TileWriter
WritePartitionTree( WritePartitionTree(
pcs, pcs,
entropyCodingContext, entropyCodingContext,
ref writer, writer,
superblock, superblock,
coefficientBuffer, coefficientBuffer,
tileIndex, tileIndex,
@ -226,7 +226,7 @@ internal partial class Av1TileWriter
WritePartitionTree( WritePartitionTree(
pcs, pcs,
entropyCodingContext, entropyCodingContext,
ref writer, writer,
superblock, superblock,
coefficientBuffer, coefficientBuffer,
tileIndex, tileIndex,
@ -240,7 +240,7 @@ internal partial class Av1TileWriter
WriteFinalBlock( WriteFinalBlock(
pcs, pcs,
entropyCodingContext, entropyCodingContext,
ref writer, writer,
superblock, superblock,
coefficientBuffer, coefficientBuffer,
tileIndex, tileIndex,
@ -249,7 +249,7 @@ internal partial class Av1TileWriter
WriteFinalBlock( WriteFinalBlock(
pcs, pcs,
entropyCodingContext, entropyCodingContext,
ref writer, writer,
superblock, superblock,
coefficientBuffer, coefficientBuffer,
tileIndex, tileIndex,
@ -258,7 +258,7 @@ internal partial class Av1TileWriter
WriteFinalBlock( WriteFinalBlock(
pcs, pcs,
entropyCodingContext, entropyCodingContext,
ref writer, writer,
superblock, superblock,
coefficientBuffer, coefficientBuffer,
tileIndex, tileIndex,
@ -270,7 +270,7 @@ internal partial class Av1TileWriter
WriteFinalBlock( WriteFinalBlock(
pcs, pcs,
entropyCodingContext, entropyCodingContext,
ref writer, writer,
superblock, superblock,
coefficientBuffer, coefficientBuffer,
tileIndex, tileIndex,
@ -279,7 +279,7 @@ internal partial class Av1TileWriter
WriteFinalBlock( WriteFinalBlock(
pcs, pcs,
entropyCodingContext, entropyCodingContext,
ref writer, writer,
superblock, superblock,
coefficientBuffer, coefficientBuffer,
tileIndex, tileIndex,
@ -288,7 +288,7 @@ internal partial class Av1TileWriter
WriteFinalBlock( WriteFinalBlock(
pcs, pcs,
entropyCodingContext, entropyCodingContext,
ref writer, writer,
superblock, superblock,
coefficientBuffer, coefficientBuffer,
tileIndex, tileIndex,
@ -300,7 +300,7 @@ internal partial class Av1TileWriter
WriteFinalBlock( WriteFinalBlock(
pcs, pcs,
entropyCodingContext, entropyCodingContext,
ref writer, writer,
superblock, superblock,
coefficientBuffer, coefficientBuffer,
tileIndex, tileIndex,
@ -309,7 +309,7 @@ internal partial class Av1TileWriter
WriteFinalBlock( WriteFinalBlock(
pcs, pcs,
entropyCodingContext, entropyCodingContext,
ref writer, writer,
superblock, superblock,
coefficientBuffer, coefficientBuffer,
tileIndex, tileIndex,
@ -318,7 +318,7 @@ internal partial class Av1TileWriter
WriteFinalBlock( WriteFinalBlock(
pcs, pcs,
entropyCodingContext, entropyCodingContext,
ref writer, writer,
superblock, superblock,
coefficientBuffer, coefficientBuffer,
tileIndex, tileIndex,
@ -330,7 +330,7 @@ internal partial class Av1TileWriter
WriteFinalBlock( WriteFinalBlock(
pcs, pcs,
entropyCodingContext, entropyCodingContext,
ref writer, writer,
superblock, superblock,
coefficientBuffer, coefficientBuffer,
tileIndex, tileIndex,
@ -339,7 +339,7 @@ internal partial class Av1TileWriter
WriteFinalBlock( WriteFinalBlock(
pcs, pcs,
entropyCodingContext, entropyCodingContext,
ref writer, writer,
superblock, superblock,
coefficientBuffer, coefficientBuffer,
tileIndex, tileIndex,
@ -348,7 +348,7 @@ internal partial class Av1TileWriter
WriteFinalBlock( WriteFinalBlock(
pcs, pcs,
entropyCodingContext, entropyCodingContext,
ref writer, writer,
superblock, superblock,
coefficientBuffer, coefficientBuffer,
tileIndex, tileIndex,
@ -369,7 +369,7 @@ internal partial class Av1TileWriter
WriteFinalBlock( WriteFinalBlock(
pcs, pcs,
entropyCodingContext, entropyCodingContext,
ref writer, writer,
superblock, superblock,
coefficientBuffer, coefficientBuffer,
tileIndex, tileIndex,
@ -391,7 +391,7 @@ internal partial class Av1TileWriter
WriteFinalBlock( WriteFinalBlock(
pcs, pcs,
entropyCodingContext, entropyCodingContext,
ref writer, writer,
superblock, superblock,
coefficientBuffer, coefficientBuffer,
tileIndex, tileIndex,
@ -416,7 +416,7 @@ internal partial class Av1TileWriter
private static void WriteFinalBlock( private static void WriteFinalBlock(
Av1PictureControlSet pcs, Av1PictureControlSet pcs,
Av1EntropyCodingContext entropyCodingContext, Av1EntropyCodingContext entropyCodingContext,
ref Av1SymbolEncoder writer, Av1SymbolEncoder writer,
Av1Superblock superblock, Av1Superblock superblock,
Av1EncoderCoefficientBuffer coefficientBuffer, Av1EncoderCoefficientBuffer coefficientBuffer,
ushort tileIndex, ushort tileIndex,
@ -427,7 +427,7 @@ internal partial class Av1TileWriter
WriteModesBlock( WriteModesBlock(
pcs, pcs,
entropyCodingContext, entropyCodingContext,
ref writer, writer,
superblock, superblock,
ref block, ref block,
tileIndex, tileIndex,
@ -540,7 +540,7 @@ internal partial class Av1TileWriter
/// <param name="partition_context_na">The partition neighbor arrays for the tile.</param> /// <param name="partition_context_na">The partition neighbor arrays for the tile.</param>
private static void EncodePartition( private static void EncodePartition(
Av1PictureControlSet pcs, Av1PictureControlSet pcs,
ref Av1SymbolEncoder writer, Av1SymbolEncoder writer,
Av1BlockSize blockSize, Av1BlockSize blockSize,
Av1PartitionType partitionType, Av1PartitionType partitionType,
Point blockOrigin, Point blockOrigin,
@ -617,7 +617,7 @@ internal partial class Av1TileWriter
private static void WriteModesBlock( private static void WriteModesBlock(
Av1PictureControlSet pcs, Av1PictureControlSet pcs,
Av1EntropyCodingContext entropyCodingContext, Av1EntropyCodingContext entropyCodingContext,
ref Av1SymbolEncoder writer, Av1SymbolEncoder writer,
Av1Superblock tb_ptr, Av1Superblock tb_ptr,
ref Av1EncoderBlockStruct blk_ptr, ref Av1EncoderBlockStruct blk_ptr,
ushort tile_idx, ushort tile_idx,
@ -655,14 +655,14 @@ internal partial class Av1TileWriter
{ {
if (pcs.Parent.FrameHeader.SegmentationParameters.Enabled && pcs.Parent.FrameHeader.SegmentationParameters.SegmentIdPrecedesSkip) if (pcs.Parent.FrameHeader.SegmentationParameters.Enabled && pcs.Parent.FrameHeader.SegmentationParameters.SegmentIdPrecedesSkip)
{ {
WriteSegmentId(pcs, ref writer, blockSize, blockOrigin, macroBlock, ref blk_ptr, skipWritingCoefficients); WriteSegmentId(pcs, writer, blockSize, blockOrigin, macroBlock, ref blk_ptr, skipWritingCoefficients);
} }
EncodeSkipCoefficients(ref writer, macroBlock, skipWritingCoefficients); EncodeSkipCoefficients(writer, macroBlock, skipWritingCoefficients);
if (pcs.Parent.FrameHeader.SegmentationParameters.Enabled && !pcs.Parent.FrameHeader.SegmentationParameters.SegmentIdPrecedesSkip) if (pcs.Parent.FrameHeader.SegmentationParameters.Enabled && !pcs.Parent.FrameHeader.SegmentationParameters.SegmentIdPrecedesSkip)
{ {
WriteSegmentId(pcs, ref writer, blockSize, blockOrigin, macroBlock, ref blk_ptr, skipWritingCoefficients); WriteSegmentId(pcs, writer, blockSize, blockOrigin, macroBlock, ref blk_ptr, skipWritingCoefficients);
} }
WriteCdef( WriteCdef(
@ -693,12 +693,12 @@ internal partial class Av1TileWriter
Av1ChromaPredictionMode intra_chroma_mode = macroBlockModeInfo.Block.UvMode; Av1ChromaPredictionMode intra_chroma_mode = macroBlockModeInfo.Block.UvMode;
if (IsIntraBlockCopyAllowed(pcs.Parent.FrameHeader/*, pcs.Parent.SliceType*/)) if (IsIntraBlockCopyAllowed(pcs.Parent.FrameHeader/*, pcs.Parent.SliceType*/))
{ {
WriteIntraBlockCopyInfo(ref writer, macroBlockModeInfo); WriteIntraBlockCopyInfo(writer, macroBlockModeInfo);
} }
if (!macroBlockModeInfo.Block.UseIntraBlockCopy) if (!macroBlockModeInfo.Block.UseIntraBlockCopy)
{ {
EncodeIntraLumaMode(ref writer, macroBlockModeInfo, macroBlock, ref blk_ptr, blockSize, intra_luma_mode); EncodeIntraLumaMode(writer, macroBlockModeInfo, macroBlock, ref blk_ptr, blockSize, intra_luma_mode);
} }
if (!macroBlockModeInfo.Block.UseIntraBlockCopy) if (!macroBlockModeInfo.Block.UseIntraBlockCopy)
@ -706,7 +706,7 @@ internal partial class Av1TileWriter
if (blk_ptr.HasChroma) if (blk_ptr.HasChroma)
{ {
EncodeIntraChromaMode( EncodeIntraChromaMode(
ref writer, writer,
macroBlockModeInfo, macroBlockModeInfo,
ref blk_ptr, ref blk_ptr,
blockSize, blockSize,
@ -720,7 +720,7 @@ internal partial class Av1TileWriter
{ {
WritePaletteModeInfo( WritePaletteModeInfo(
scs, scs,
ref writer, writer,
macroBlockModeInfo, macroBlockModeInfo,
ref blk_ptr, ref blk_ptr,
blockSize, blockSize,
@ -762,7 +762,7 @@ internal partial class Av1TileWriter
EncodeCoefficients1d( EncodeCoefficients1d(
pcs, pcs,
entropyCodingContext, entropyCodingContext,
ref writer, writer,
ref blk_ptr, ref blk_ptr,
blockOrigin, blockOrigin,
intra_luma_mode, intra_luma_mode,
@ -850,7 +850,7 @@ internal partial class Av1TileWriter
/// <param name="chromaMode">The selected chroma prediction mode.</param> /// <param name="chromaMode">The selected chroma prediction mode.</param>
/// <param name="isChromaFromLumaAllowed">A value indicating whether chroma-from-luma mode is available.</param> /// <param name="isChromaFromLumaAllowed">A value indicating whether chroma-from-luma mode is available.</param>
private static void EncodeIntraChromaMode( private static void EncodeIntraChromaMode(
ref Av1SymbolEncoder writer, Av1SymbolEncoder writer,
Av1MacroBlockModeInfo macroBlockModeInfo, Av1MacroBlockModeInfo macroBlockModeInfo,
ref Av1EncoderBlockStruct blk_ptr, ref Av1EncoderBlockStruct blk_ptr,
Av1BlockSize blockSize, Av1BlockSize blockSize,
@ -910,7 +910,7 @@ internal partial class Av1TileWriter
/// <param name="blockSize">The block size.</param> /// <param name="blockSize">The block size.</param>
/// <param name="lumaMode">The selected luma prediction mode.</param> /// <param name="lumaMode">The selected luma prediction mode.</param>
private static void EncodeIntraLumaMode( private static void EncodeIntraLumaMode(
ref Av1SymbolEncoder writer, Av1SymbolEncoder writer,
Av1MacroBlockModeInfo macroBlockModeInfo, Av1MacroBlockModeInfo macroBlockModeInfo,
Av1MacroBlockD macroBlock, Av1MacroBlockD macroBlock,
ref Av1EncoderBlockStruct blk_ptr, ref Av1EncoderBlockStruct blk_ptr,
@ -938,7 +938,7 @@ internal partial class Av1TileWriter
/// <exception cref="NotImplementedException">Palette-mode encoding is not implemented.</exception> /// <exception cref="NotImplementedException">Palette-mode encoding is not implemented.</exception>
private static void WritePaletteModeInfo( private static void WritePaletteModeInfo(
Av1SequenceControlSet scs, Av1SequenceControlSet scs,
ref Av1SymbolEncoder writer, Av1SymbolEncoder writer,
Av1MacroBlockModeInfo macroBlockModeInfo, Av1MacroBlockModeInfo macroBlockModeInfo,
ref Av1EncoderBlockStruct blk_ptr, ref Av1EncoderBlockStruct blk_ptr,
Av1BlockSize blockSize, Av1BlockSize blockSize,
@ -985,7 +985,7 @@ internal partial class Av1TileWriter
/// <param name="macroBlockModeInfo">The selected block modes.</param> /// <param name="macroBlockModeInfo">The selected block modes.</param>
/// <exception cref="NotImplementedException">The displacement-vector syntax is not implemented when intra block copy is selected.</exception> /// <exception cref="NotImplementedException">The displacement-vector syntax is not implemented when intra block copy is selected.</exception>
private static void WriteIntraBlockCopyInfo( private static void WriteIntraBlockCopyInfo(
ref Av1SymbolEncoder writer, Av1SymbolEncoder writer,
Av1MacroBlockModeInfo macroBlockModeInfo) Av1MacroBlockModeInfo macroBlockModeInfo)
{ {
bool use_intrabc = macroBlockModeInfo.Block.UseIntraBlockCopy; bool use_intrabc = macroBlockModeInfo.Block.UseIntraBlockCopy;
@ -1220,7 +1220,7 @@ internal partial class Av1TileWriter
private static void EncodeCoefficients1d( private static void EncodeCoefficients1d(
Av1PictureControlSet pcs, Av1PictureControlSet pcs,
Av1EntropyCodingContext ec_ctx, Av1EntropyCodingContext ec_ctx,
ref Av1SymbolEncoder writer, Av1SymbolEncoder writer,
ref Av1EncoderBlockStruct blk_ptr, ref Av1EncoderBlockStruct blk_ptr,
Point blockOrigin, Point blockOrigin,
Av1PredictionMode intraLumaDir, Av1PredictionMode intraLumaDir,
@ -1234,7 +1234,7 @@ internal partial class Av1TileWriter
EncodeTransformCoefficientsY( EncodeTransformCoefficientsY(
pcs, pcs,
ec_ctx, ec_ctx,
ref writer, writer,
ref blk_ptr, ref blk_ptr,
blockOrigin, blockOrigin,
intraLumaDir, intraLumaDir,
@ -1246,7 +1246,7 @@ internal partial class Av1TileWriter
EncodeTransformCoefficientsUv( EncodeTransformCoefficientsUv(
pcs, pcs,
ec_ctx, ec_ctx,
ref writer, writer,
ref blk_ptr, ref blk_ptr,
blockOrigin, blockOrigin,
intraLumaDir, intraLumaDir,
@ -1273,7 +1273,7 @@ internal partial class Av1TileWriter
public static void EncodeTransformCoefficientsY( public static void EncodeTransformCoefficientsY(
Av1PictureControlSet pcs, Av1PictureControlSet pcs,
Av1EntropyCodingContext entropyCodingContext, Av1EntropyCodingContext entropyCodingContext,
ref Av1SymbolEncoder writer, Av1SymbolEncoder writer,
ref Av1EncoderBlockStruct blk_ptr, ref Av1EncoderBlockStruct blk_ptr,
Point blockOrigin, Point blockOrigin,
Av1PredictionMode intraLumaDir, Av1PredictionMode intraLumaDir,
@ -1387,7 +1387,7 @@ internal partial class Av1TileWriter
private static void EncodeTransformCoefficientsUv( private static void EncodeTransformCoefficientsUv(
Av1PictureControlSet pcs, Av1PictureControlSet pcs,
Av1EntropyCodingContext entropyCodingContext, Av1EntropyCodingContext entropyCodingContext,
ref Av1SymbolEncoder writer, Av1SymbolEncoder writer,
ref Av1EncoderBlockStruct blk_ptr, ref Av1EncoderBlockStruct blk_ptr,
Point blockOrigin, Point blockOrigin,
Av1PredictionMode intraLumaDir, Av1PredictionMode intraLumaDir,
@ -1631,7 +1631,7 @@ internal partial class Av1TileWriter
/// <param name="skip">A value indicating whether residual coefficients are omitted.</param> /// <param name="skip">A value indicating whether residual coefficients are omitted.</param>
private static void WriteSegmentId( private static void WriteSegmentId(
Av1PictureControlSet pcs, Av1PictureControlSet pcs,
ref Av1SymbolEncoder writer, Av1SymbolEncoder writer,
Av1BlockSize blockSize, Av1BlockSize blockSize,
Point blockOrigin, Point blockOrigin,
Av1MacroBlockD macroBlock, Av1MacroBlockD macroBlock,
@ -1738,7 +1738,7 @@ internal partial class Av1TileWriter
/// <param name="writer">The tile symbol encoder.</param> /// <param name="writer">The tile symbol encoder.</param>
/// <param name="macroBlock">The reusable macroblock edge and neighbor state.</param> /// <param name="macroBlock">The reusable macroblock edge and neighbor state.</param>
/// <param name="skip">The skip value to write.</param> /// <param name="skip">The skip value to write.</param>
public static void EncodeSkipCoefficients(ref Av1SymbolEncoder writer, Av1MacroBlockD macroBlock, bool skip) public static void EncodeSkipCoefficients(Av1SymbolEncoder writer, Av1MacroBlockD macroBlock, bool skip)
{ {
Av1MacroBlockModeInfo? above_mi = macroBlock.AboveMacroBlock; Av1MacroBlockModeInfo? above_mi = macroBlock.AboveMacroBlock;
Av1MacroBlockModeInfo? left_mi = macroBlock.LeftMacroBlock; Av1MacroBlockModeInfo? left_mi = macroBlock.LeftMacroBlock;

39
src/ImageSharp/Formats/Heif/HeifEncoderCore.cs

@ -48,12 +48,17 @@ internal sealed class HeifEncoderCore
Guard.NotNull(image, nameof(image)); Guard.NotNull(image, nameof(image));
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
using ChunkedMemoryStream compressedPixels = this.encoder.CompressionMethod switch using ChunkedMemoryStream compressedPixels = new(this.configuration.MemoryAllocator);
switch (this.encoder.CompressionMethod)
{ {
HeifCompressionMethod.LegacyJpeg => this.CompressPixels(image, cancellationToken), case HeifCompressionMethod.LegacyJpeg:
HeifCompressionMethod.Av1 => throw new NotSupportedException("AV1 encoding is not implemented."), this.CompressPixels(image, compressedPixels, cancellationToken);
_ => throw new NotSupportedException($"HEIF compression method '{this.encoder.CompressionMethod}' is not supported.") break;
}; case HeifCompressionMethod.Av1:
throw new NotSupportedException("AV1 encoding is not implemented.");
default:
throw new NotSupportedException($"HEIF compression method '{this.encoder.CompressionMethod}' is not supported.");
}
List<HeifItem> items = new(); List<HeifItem> items = new();
List<HeifItemLink> links = new(); List<HeifItemLink> links = new();
@ -430,9 +435,12 @@ internal sealed class HeifEncoderCore
/// </summary> /// </summary>
/// <typeparam name="TPixel">The source pixel format.</typeparam> /// <typeparam name="TPixel">The source pixel format.</typeparam>
/// <param name="image">The source image.</param> /// <param name="image">The source image.</param>
/// <param name="stream">The destination for the encoded JPEG item bytes.</param>
/// <param name="cancellationToken">The token used to cancel payload encoding.</param> /// <param name="cancellationToken">The token used to cancel payload encoding.</param>
/// <returns>The pooled stream containing the encoded JPEG item bytes.</returns> private void CompressPixels<TPixel>(
private ChunkedMemoryStream CompressPixels<TPixel>(Image<TPixel> image, CancellationToken cancellationToken) Image<TPixel> image,
ChunkedMemoryStream stream,
CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
if (this.encoder.Lossless) if (this.encoder.Lossless)
@ -454,7 +462,6 @@ internal sealed class HeifEncoderCore
_ => throw new NotSupportedException($"HEIF chroma sampling '{this.encoder.ChromaSubsampling}' is not supported.") _ => throw new NotSupportedException($"HEIF chroma sampling '{this.encoder.ChromaSubsampling}' is not supported.")
}; };
ChunkedMemoryStream stream = new(this.configuration.MemoryAllocator);
JpegEncoder encoder = new() JpegEncoder encoder = new()
{ {
// The HEIF quality scale includes zero while the JPEG payload encoder starts at one. // The HEIF quality scale includes zero while the JPEG payload encoder starts at one.
@ -463,18 +470,8 @@ internal sealed class HeifEncoderCore
ColorType = colorType ColorType = colorType
}; };
try // ImageEncoder is a synchronous contract. Wait for the cancellable JPEG operation so HEIF encoding
{ // cannot return while its pooled item payload is still being produced.
// ImageEncoder is a synchronous contract. Wait for the cancellable JPEG operation so HEIF encoding image.SaveAsJpegAsync(stream, encoder, cancellationToken).GetAwaiter().GetResult();
// cannot return while its pooled item payload is still being produced.
image.SaveAsJpegAsync(stream, encoder, cancellationToken).GetAwaiter().GetResult();
return stream;
}
catch
{
// Ownership transfers to the caller only after encoding succeeds.
stream.Dispose();
throw;
}
} }
} }

4
tests/ImageSharp.Tests/Formats/Heif/Av1/Av1CoefficientsEntropyTests.cs

@ -299,7 +299,7 @@ public class Av1CoefficientsEntropyTests
Av1TileWriter.EncodeTransformCoefficientsY( Av1TileWriter.EncodeTransformCoefficientsY(
picture, picture,
context, context,
ref writer, writer,
ref block, ref block,
Point.Empty, Point.Empty,
Av1PredictionMode.DC, Av1PredictionMode.DC,
@ -636,7 +636,7 @@ public class Av1CoefficientsEntropyTests
Av1TileWriter.WriteSuperblock( Av1TileWriter.WriteSuperblock(
picture, picture,
context, context,
ref writer, writer,
superblock, superblock,
coefficients, coefficients,
tileIndex: 0); tileIndex: 0);

Loading…
Cancel
Save