diff --git a/HEIF_IMPLEMENTATION_PLAN.md b/HEIF_IMPLEMENTATION_PLAN.md
index bb0cfbfeb7..506b0f454d 100644
--- a/HEIF_IMPLEMENTATION_PLAN.md
+++ b/HEIF_IMPLEMENTATION_PLAN.md
@@ -156,6 +156,7 @@ This assessment is based on the current source after the upstream ImageSharp mer
### AV1 decoder
- The single-still `Av1Decoder` path now parses tile state before allocating and reconstructing one independently decodable frame, and it disposes the reconstruction planes after pixel conversion. It deliberately does not retain animation/video reference frames or implement `show_existing_frame` playback state.
+- Transform coefficient entropy derivation and updates now address the above contexts relative to the tile column and the left contexts relative to the current superblock row, preserve luma coordinates independently of chroma subsampling, and test every packed context entry for the libaom any-nonzero rule. Extended vertical partition updates advance the mode-information column rather than the row. The existing multi-superblock 4:4:4 AVIF fixture now completes tile parsing; independent coefficient-context vectors across tile boundaries, chroma layouts, bit depths, and edge-clipped transforms remain required.
- The reconstruction pipeline now records plane-relative transform geometry, preserves tile-local delta-Q and delta-LF predictors, derives segmentation and reference-adjusted filter levels, and runs the exact scalar low/high-bit-depth AV1 4-, 6-, 8-, and 14-tap deblocking kernels in normative vertical-then-horizontal order. It then applies scalar CDEF direction search, luma variance adjustment, primary and secondary constrained taps, chroma direction conversion, high-bit-depth scaling, skipped-block selection, and frame-edge sentinel handling from immutable per-plane snapshots. Active super-resolution derives the Appendix A bounded coded width and applies the exact 64-phase, 8-tap horizontal filter with aligned reconstruction-edge input, 8/10/12-bit clipping, and the existing cross-platform `Vector128_.MultiplyAddAdjacent` helper. Loop restoration follows super-resolution, preserves the required pre-CDEF deblocked context at internal stripes, and applies decoded Wiener or self-guided units from immutable plane snapshots. The visible still-image path then applies the complete self-contained film-grain parameter set after all in-loop filters. Independent 8-, 10-, and 12-bit AVIF vectors exercising every active filter and grain stage remain required before these paths have external pixel-level verification.
- Palette mode now reads the normative luma/chroma mode and size CDFs, neighbor color caches, high-bit-depth color syntax, diagonal color-index maps, clipped-edge padding, and direct sample prediction through the existing reconstruction pipeline. The scalar implementation matches the pinned libaom source, but an independently encoded palette AVIF fixture is still required before this path is independently verified.
- Non-reduced still-image sequence parsing now consumes decoder-model operating parameters, temporal presentation fields, and OBU-layer-applicable buffer-removal fields only to preserve AV1 bit alignment. The scheduling values are not retained, and no movie, track, timing, playback, or generic ISO BMFF surface has been introduced. Existing focused sequence-header coverage exercises only reduced-still syntax, so an independent non-reduced still AVIF vector remains required.
diff --git a/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolDecoder.cs b/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolDecoder.cs
index f0d8ded3f7..05867e9be2 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolDecoder.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolDecoder.cs
@@ -757,11 +757,11 @@ internal ref struct Av1SymbolDecoder
/// Decodes one transform block's coefficient syntax and updates its neighboring entropy contexts.
///
/// The current block prediction and segment modes.
- /// The transform-block position in four-sample units.
+ /// The transform-block offset within the coding block in four-sample units.
/// The above coefficient contexts for the current plane.
/// The left coefficient contexts for the current plane.
- /// The first above context covered by the transform.
- /// The first left context covered by the transform.
+ /// The first tile-relative above context covered by the transform.
+ /// The first superblock-row-relative left context covered by the transform.
/// The zero-based Y, U, or V plane index.
/// The available plane width in four-sample units.
/// The available plane height in four-sample units.
@@ -813,7 +813,7 @@ internal ref struct Av1SymbolDecoder
transformInfo.CodeBlockFlag = false;
}
- UpdateCoefficientContext(modeInfo, aboveContexts, leftContexts, blocksWide, blocksHigh, transformSize, blockPosition, aboveOffset, leftOffset, culLevel, modeBlocksToRightEdge, modeBlocksToBottomEdge);
+ UpdateCoefficientContext(aboveContexts, leftContexts, blocksWide, blocksHigh, transformSize, blockPosition, aboveOffset, leftOffset, culLevel, modeBlocksToRightEdge, modeBlocksToBottomEdge);
return 0;
}
@@ -849,7 +849,7 @@ internal ref struct Av1SymbolDecoder
DebugGuard.MustBeGreaterThan(scan.Length, 0, nameof(scan));
culLevel = this.ReadCoefficientsSign(coefficientBuffer, endOfBlock, scan, levels, transformBlockContext.DcSignContext, planeType);
- UpdateCoefficientContext(modeInfo, aboveContexts, leftContexts, blocksWide, blocksHigh, transformSize, blockPosition, aboveOffset, leftOffset, culLevel, modeBlocksToRightEdge, modeBlocksToBottomEdge);
+ UpdateCoefficientContext(aboveContexts, leftContexts, blocksWide, blocksHigh, transformSize, blockPosition, aboveOffset, leftOffset, culLevel, modeBlocksToRightEdge, modeBlocksToBottomEdge);
transformInfo.CodeBlockFlag = true;
return endOfBlock;
@@ -1164,20 +1164,18 @@ internal ref struct Av1SymbolDecoder
///
/// Stores a transform block's packed coefficient context into the above and left neighbor arrays.
///
- /// The current block mode information.
/// The above contexts for the current plane.
/// The left contexts for the current plane.
/// The available plane width in four-sample units.
/// The available plane height in four-sample units.
/// The signaled transform size.
- /// The transform-block position in four-sample units.
- /// The first above context covered by the transform.
- /// The first left context covered by the transform.
+ /// The transform-block offset within the coding block in four-sample units.
+ /// The first tile-relative above context covered by the transform.
+ /// The first superblock-row-relative left context covered by the transform.
/// The packed coefficient magnitude and DC sign context.
/// The signed distance from the mode block to the right frame edge.
/// The signed distance from the mode block to the bottom frame edge.
private static void UpdateCoefficientContext(
- Av1BlockModeInfo modeInfo,
int[] aboveContexts,
int[] leftContexts,
int blocksWide,
@@ -1195,24 +1193,24 @@ internal ref struct Av1SymbolDecoder
if (modeBlockToRightEdge < 0)
{
- int aboveContextCount = Math.Min(transformSizeWide, blocksWide - aboveOffset);
- Array.Fill(aboveContexts, culLevel, 0, aboveContextCount);
- Array.Fill(aboveContexts, 0, aboveContextCount, transformSizeWide - aboveContextCount);
+ int aboveContextCount = Math.Min(transformSizeWide, blocksWide - blockPosition.X);
+ Array.Fill(aboveContexts, culLevel, aboveOffset, aboveContextCount);
+ Array.Fill(aboveContexts, 0, aboveOffset + aboveContextCount, transformSizeWide - aboveContextCount);
}
else
{
- Array.Fill(aboveContexts, culLevel, 0, transformSizeWide);
+ Array.Fill(aboveContexts, culLevel, aboveOffset, transformSizeWide);
}
if (modeBlockToBottomEdge < 0)
{
- int leftContextCount = Math.Min(transformSizeHigh, blocksHigh - leftOffset);
- Array.Fill(leftContexts, culLevel, 0, leftContextCount);
- Array.Fill(leftContexts, 0, leftContextCount, transformSizeHigh - leftContextCount);
+ int leftContextCount = Math.Min(transformSizeHigh, blocksHigh - blockPosition.Y);
+ Array.Fill(leftContexts, culLevel, leftOffset, leftContextCount);
+ Array.Fill(leftContexts, 0, leftOffset + leftContextCount, transformSizeHigh - leftContextCount);
}
else
{
- Array.Fill(leftContexts, culLevel, 0, transformSizeHigh);
+ Array.Fill(leftContexts, culLevel, leftOffset, transformSizeHigh);
}
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileReader.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileReader.cs
index d3572dbf8a..bd7d7c2a16 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileReader.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileReader.cs
@@ -790,7 +790,20 @@ internal class Av1TileReader : IAv1TileReader
if (!partitionInfo.ModeInfo.Skip)
{
Span coefficientBuffer = superblockInfo.GetCoefficients((Av1Plane)plane)[coefficientIndex..];
- endOfBlock = this.ParseTransformBlock(ref reader, partitionInfo, coefficientBuffer, transformInfo, plane, blockColumn, blockRow, startX, startY, transformInfo.Size, subX != 0, subY != 0);
+ endOfBlock = this.ParseTransformBlock(
+ ref reader,
+ partitionInfo,
+ tileInfo,
+ coefficientBuffer,
+ transformInfo,
+ plane,
+ blockColumn,
+ blockRow,
+ startX,
+ startY,
+ transformInfo.Size,
+ subX != 0,
+ subY != 0);
}
if (endOfBlock != 0)
@@ -837,6 +850,7 @@ internal class Av1TileReader : IAv1TileReader
///
/// The tile symbol decoder.
/// The containing coding block.
+ /// The active tile boundaries.
/// The destination beginning at this transform's coefficient slot.
/// The transform geometry and syntax state to populate.
/// The zero-based color-plane index.
@@ -854,6 +868,7 @@ internal class Av1TileReader : IAv1TileReader
private int ParseTransformBlock(
ref Av1SymbolDecoder reader,
Av1PartitionInfo partitionInfo,
+ Av1TileInfo tileInfo,
Span coefficientBuffer,
Av1TransformInfo transformInfo,
int plane,
@@ -882,8 +897,33 @@ internal class Av1TileReader : IAv1TileReader
transformBlockUnitHighCount = Math.Min(transformBlockUnitHighCount, blocksHigh - blockRow);
}
- Av1TransformBlockContext transformBlockContext = this.GetTransformBlockContext(transformSize, plane, planeBlockSize, transformBlockUnitHighCount, transformBlockUnitWideCount, startY, startX);
- endOfBlock = this.ParseCoefficients(ref reader, partitionInfo, startY, startX, blockRow, blockColumn, plane, transformBlockContext, transformSize, transformInfo, coefficientBuffer);
+ int aboveContextOffset = startX - (tileInfo.ModeInfoColumnStart >> (subX ? 1 : 0));
+ int superblockRow = partitionInfo.SuperblockInfo.ModeInfoPosition.Y >> (subY ? 1 : 0);
+ int leftContextOffset = startY - superblockRow;
+
+ // Above contexts are tile-column relative, while left contexts are reused from the start of each
+ // superblock row. Slicing both arrays here gives the entropy derivation the same pointer bases as libaom.
+ Av1TransformBlockContext transformBlockContext = this.GetTransformBlockContext(
+ transformSize,
+ plane,
+ planeBlockSize,
+ transformBlockUnitHighCount,
+ transformBlockUnitWideCount,
+ aboveContextOffset,
+ leftContextOffset);
+
+ endOfBlock = this.ParseCoefficients(
+ ref reader,
+ partitionInfo,
+ blockRow,
+ blockColumn,
+ aboveContextOffset,
+ leftContextOffset,
+ plane,
+ transformBlockContext,
+ transformSize,
+ transformInfo,
+ coefficientBuffer);
return endOfBlock;
}
@@ -893,10 +933,10 @@ internal class Av1TileReader : IAv1TileReader
///
/// The tile symbol decoder.
/// The containing coding block.
- /// The frame-relative transform row in 4x4 units of the target plane.
- /// The frame-relative transform column in 4x4 units of the target plane.
- /// The horizontal transform offset within the coding block in 4x4 units.
- /// The vertical transform offset within the coding block in 4x4 units.
+ /// The transform row within the coding block in 4x4 units of the target plane.
+ /// The transform column within the coding block in 4x4 units of the target plane.
+ /// The first tile-relative above context covered by the transform.
+ /// The first superblock-row-relative left context covered by the transform.
/// The zero-based color-plane index.
/// The coefficient skip and DC-sign entropy contexts.
/// The transform size.
@@ -914,8 +954,8 @@ internal class Av1TileReader : IAv1TileReader
Av1PlaneType planeType = (Av1PlaneType)Math.Min(plane, 1);
Point blockPosition = new(blockColumn, blockRow);
bool isLossless = this.FrameHeader.LosslessArray[partitionInfo.ModeInfo.SegmentId];
- bool subX = this.SequenceHeader.ColorConfig.SubSamplingX;
- bool subY = this.SequenceHeader.ColorConfig.SubSamplingY;
+ bool subX = plane > 0 && this.SequenceHeader.ColorConfig.SubSamplingX;
+ bool subY = plane > 0 && this.SequenceHeader.ColorConfig.SubSamplingY;
Av1BlockSize planeBlockSize = partitionInfo.ModeInfo.BlockSize.GetSubsampled(subX, subY);
int blocksWide = partitionInfo.GetMaxBlockWide(planeBlockSize, subX);
int blocksHigh = partitionInfo.GetMaxBlockHigh(planeBlockSize, subY);
@@ -931,14 +971,21 @@ internal class Av1TileReader : IAv1TileReader
/// The containing block size on the target plane.
/// The transform height clipped to the frame in 4x4 units.
/// The transform width clipped to the frame in 4x4 units.
- /// The frame-relative transform row in 4x4 units of the target plane.
- /// The frame-relative transform column in 4x4 units of the target plane.
+ /// The first tile-relative above context covered by the transform.
+ /// The first superblock-row-relative left context covered by the transform.
/// The derived transform-block entropy contexts.
- private Av1TransformBlockContext GetTransformBlockContext(Av1TransformSize transformSize, int plane, Av1BlockSize planeBlockSize, int transformBlockUnitHighCount, int transformBlockUnitWideCount, int startY, int startX)
+ private Av1TransformBlockContext GetTransformBlockContext(
+ Av1TransformSize transformSize,
+ int plane,
+ Av1BlockSize planeBlockSize,
+ int transformBlockUnitHighCount,
+ int transformBlockUnitWideCount,
+ int aboveOffset,
+ int leftOffset)
{
Av1TransformBlockContext transformBlockContext = new();
- int[] aboveContext = this.aboveNeighborContext.GetContext(plane);
- int[] leftContext = this.leftNeighborContext.GetContext(plane);
+ ReadOnlySpan aboveContext = this.aboveNeighborContext.GetContext(plane).AsSpan(aboveOffset);
+ ReadOnlySpan leftContext = this.leftNeighborContext.GetContext(plane).AsSpan(leftOffset);
int dcSign = 0;
int k = 0;
int mask = (1 << Av1Constants.CoefficientContextBitCount) - 1;
@@ -1017,134 +1064,31 @@ internal class Av1TileReader : IAv1TileReader
/// The above coefficient contexts.
/// The left coefficient contexts.
/// The sum of the nonzero-above and nonzero-left flags.
- private static int GetEntropyContext(Av1TransformSize transformSize, int[] above, int[] left)
+ private static int GetEntropyContext(Av1TransformSize transformSize, ReadOnlySpan above, ReadOnlySpan left)
{
bool aboveEntropyContext = false;
bool leftEntropyContext = false;
+ int transformBlockUnitWideCount = transformSize.Get4x4WideCount();
+ int transformBlockUnitHighCount = transformSize.Get4x4HighCount();
- // The reference implementation tests packed 16, 32, 64, or 128-bit edge groups. Enumerating
- // each transform shape keeps those exact edge widths without unaligned native memory reads.
- switch (transformSize)
+ // libaom tests the context bytes through packed native loads. Enumerating the same transform-width and
+ // transform-height entries avoids unaligned reads while preserving the required any-nonzero result.
+ for (int i = 0; i < transformBlockUnitWideCount; i++)
{
- case Av1TransformSize.Size4x4:
- aboveEntropyContext = above[0] != 0;
- leftEntropyContext = left[0] != 0;
- break;
- case Av1TransformSize.Size4x8:
- aboveEntropyContext = above[0] != 0;
- leftEntropyContext = (left[0] & (left[1] << 8)) != 0; // !!*(const uint16_t*)left;
- break;
- case Av1TransformSize.Size8x4:
- aboveEntropyContext = (above[0] & (above[1] << 8)) != 0; // !!*(const uint16_t*)above;
- leftEntropyContext = left[0] != 0;
- break;
- case Av1TransformSize.Size8x16:
- aboveEntropyContext = (above[0] & (above[1] << 8)) != 0; // !!*(const uint16_t*)above;
- leftEntropyContext = (left[0] & (left[1] << 8) & (left[2] << 16) & (left[3] << 24)) != 0; // !!*(const uint32_t*)left;
- break;
- case Av1TransformSize.Size16x8:
- aboveEntropyContext = (above[0] & (above[1] << 8) & (above[2] << 16) & (above[3] << 24)) != 0; // !!*(const uint32_t*)above;
- leftEntropyContext = (left[0] & (left[1] << 8)) != 0; // !!*(const uint16_t*)left;
- break;
- case Av1TransformSize.Size16x32:
- aboveEntropyContext = (above[0] & (above[1] << 8) & (above[2] << 16) & (above[3] << 24)) != 0; // !!*(const uint32_t*)above;
- leftEntropyContext =
- (left[0] & (left[1] << 8) & (left[2] << 16) & (left[3] << 24)) != 0 ||
- (left[4] & (left[5] << 8) & (left[6] << 16) & (left[7] << 24)) != 0; // !!*(const uint64_t*)left;
- break;
- case Av1TransformSize.Size32x16:
- aboveEntropyContext =
- (above[0] & (above[1] << 8) & (above[2] << 16) & (above[3] << 24)) != 0 ||
- (above[4] & (above[5] << 8) & (above[6] << 16) & (above[7] << 24)) != 0; // !!*(const uint64_t*)above;
- leftEntropyContext = (left[0] & (left[1] << 8) & (left[2] << 16) & (left[3] << 24)) != 0; // !!*(const uint32_t*)left;
- break;
- case Av1TransformSize.Size8x8:
- aboveEntropyContext = (above[0] & (above[1] << 8)) != 0; // !!*(const uint16_t*)above;
- leftEntropyContext = (left[0] & (left[1] << 8)) != 0; // !!*(const uint16_t*)left;
- break;
- case Av1TransformSize.Size16x16:
- aboveEntropyContext = (above[0] & (above[1] << 8) & (above[2] << 16) & (above[3] << 24)) != 0; // !!*(const uint32_t*)above;
- leftEntropyContext = (left[0] & (left[1] << 8) & (left[2] << 16) & (left[3] << 24)) != 0; // !!*(const uint32_t*)left;
- break;
- case Av1TransformSize.Size32x32:
- aboveEntropyContext =
- (above[0] & (above[1] << 8) & (above[2] << 16) & (above[3] << 24)) != 0 ||
- (above[4] & (above[5] << 8) & (above[6] << 16) & (above[7] << 24)) != 0; // !!*(const uint64_t*)above;
- leftEntropyContext =
- (left[0] & (left[1] << 8) & (left[2] << 16) & (left[3] << 24)) != 0 ||
- (left[4] & (left[5] << 8) & (left[6] << 16) & (left[7] << 24)) != 0; // !!*(const uint64_t*)left;
- break;
- case Av1TransformSize.Size64x64:
- aboveEntropyContext =
- (above[0] & (above[1] << 8) & (above[2] << 16) & (above[3] << 24)) != 0 ||
- (above[4] & (above[5] << 8) & (above[6] << 16) & (above[7] << 24)) != 0 ||
- (above[8] & (above[9] << 8) & (above[10] << 16) & (above[11] << 24)) != 0 ||
- (above[12] & (above[13] << 8) & (above[14] << 16) & (above[15] << 24)) != 0; // !!(*(const uint64_t*)above | *(const uint64_t*)(above + 8));
- leftEntropyContext =
- (left[0] & (left[1] << 8) & (left[2] << 16) & (left[3] << 24)) != 0 ||
- (left[4] & (left[5] << 8) & (left[6] << 16) & (left[7] << 24)) != 0 ||
- (left[8] & (left[9] << 8) & (left[10] << 16) & (left[11] << 24)) != 0 ||
- (left[12] & (left[13] << 8) & (left[14] << 16) & (left[15] << 24)) != 0; // !!(*(const uint64_t*)left | *(const uint64_t*)(left + 8));
- break;
- case Av1TransformSize.Size32x64:
- aboveEntropyContext =
- (above[0] & (above[1] << 8) & (above[2] << 16) & (above[3] << 24)) != 0 ||
- (above[4] & (above[5] << 8) & (above[6] << 16) & (above[7] << 24)) != 0; // !!*(const uint64_t*)above;
- leftEntropyContext =
- (left[0] & (left[1] << 8) & (left[2] << 16) & (left[3] << 24)) != 0 ||
- (left[4] & (left[5] << 8) & (left[6] << 16) & (left[7] << 24)) != 0 ||
- (left[8] & (left[9] << 8) & (left[10] << 16) & (left[11] << 24)) != 0 ||
- (left[12] & (left[13] << 8) & (left[14] << 16) & (left[15] << 24)) != 0; // !!(*(const uint64_t*)left | *(const uint64_t*)(left + 8));
- break;
- case Av1TransformSize.Size64x32:
- aboveEntropyContext =
- (above[0] & (above[1] << 8) & (above[2] << 16) & (above[3] << 24)) != 0 ||
- (above[4] & (above[5] << 8) & (above[6] << 16) & (above[7] << 24)) != 0 ||
- (above[8] & (above[9] << 8) & (above[10] << 16) & (above[11] << 24)) != 0 ||
- (above[12] & (above[13] << 8) & (above[14] << 16) & (above[15] << 24)) != 0; // !!(*(const uint64_t*)above | *(const uint64_t*)(above + 8));
- leftEntropyContext =
- (left[0] & (left[1] << 8) & (left[2] << 16) & (left[3] << 24)) != 0 ||
- (left[4] & (left[5] << 8) & (left[6] << 16) & (left[7] << 24)) != 0; // !!*(const uint64_t*)left;
- break;
- case Av1TransformSize.Size4x16:
- aboveEntropyContext = above[0] != 0;
- leftEntropyContext = (left[0] & (left[1] << 8) & (left[2] << 16) & (left[3] << 24)) != 0; // !!*(const uint32_t*)left;
- break;
- case Av1TransformSize.Size16x4:
- aboveEntropyContext = (above[0] & (above[1] << 8) & (above[2] << 16) & (above[3] << 24)) != 0; // !!*(const uint32_t*)above;
- leftEntropyContext = left[0] != 0;
- break;
- case Av1TransformSize.Size8x32:
- aboveEntropyContext = (above[0] & (above[1] << 8)) != 0; // !!*(const uint16_t*)above;
- leftEntropyContext =
- (left[0] & (left[1] << 8) & (left[2] << 16) & (left[3] << 24)) != 0 ||
- (left[4] & (left[5] << 8) & (left[6] << 16) & (left[7] << 24)) != 0; // !!*(const uint64_t*)left;
- break;
- case Av1TransformSize.Size32x8:
- aboveEntropyContext =
- (above[0] & (above[1] << 8) & (above[2] << 16) & (above[3] << 24)) != 0 ||
- (above[4] & (above[5] << 8) & (above[6] << 16) & (above[7] << 24)) != 0; // !!*(const uint64_t*)above;
- leftEntropyContext = (left[0] & (left[1] << 8)) != 0; // !!*(const uint16_t*)left;
- break;
- case Av1TransformSize.Size16x64:
- aboveEntropyContext = (above[0] & (above[1] << 8) & (above[2] << 16) & (above[3] << 24)) != 0; // !!*(const uint32_t*)above;
- leftEntropyContext =
- (left[0] & (left[1] << 8) & (left[2] << 16) & (left[3] << 24)) != 0 ||
- (left[4] & (left[5] << 8) & (left[6] << 16) & (left[7] << 24)) != 0 ||
- (left[8] & (left[9] << 8) & (left[10] << 16) & (left[11] << 24)) != 0 ||
- (left[12] & (left[13] << 8) & (left[14] << 16) & (left[15] << 24)) != 0; // !!(*(const uint64_t*)left | *(const uint64_t*)(left + 8));
- break;
- case Av1TransformSize.Size64x16:
- aboveEntropyContext =
- (above[0] & (above[1] << 8) & (above[2] << 16) & (above[3] << 24)) != 0 ||
- (above[4] & (above[5] << 8) & (above[6] << 16) & (above[7] << 24)) != 0 ||
- (above[8] & (above[9] << 8) & (above[10] << 16) & (above[11] << 24)) != 0 ||
- (above[12] & (above[13] << 8) & (above[14] << 16) & (above[15] << 24)) != 0; // !!(*(const uint64_t*)above | *(const uint64_t*)(above + 8));
- leftEntropyContext = (left[0] & (left[1] << 8) & (left[2] << 16) & (left[3] << 24)) != 0; // !!*(const uint32_t*)left;
+ if (above[i] != 0)
+ {
+ aboveEntropyContext = true;
break;
- default:
- Guard.IsTrue(false, nameof(transformSize), "Invalid transform size.");
+ }
+ }
+
+ for (int i = 0; i < transformBlockUnitHighCount; i++)
+ {
+ if (left[i] != 0)
+ {
+ leftEntropyContext = true;
break;
+ }
}
return (aboveEntropyContext ? 1 : 0) + (leftEntropyContext ? 1 : 0);
@@ -2370,7 +2314,7 @@ internal class Av1TileReader : IAv1TileReader
case Av1PartitionType.VerticalB:
this.aboveNeighborContext.UpdatePartition(modeInfoLocation, tileLoc, subSize, subSize);
this.leftNeighborContext.UpdatePartition(modeInfoLocation, superblockInfo, subSize, subSize);
- Point locVerticalB = new(modeInfoLocation.X, modeInfoLocation.Y + hbs);
+ Point locVerticalB = new(modeInfoLocation.X + hbs, modeInfoLocation.Y);
this.aboveNeighborContext.UpdatePartition(locVerticalB, tileLoc, blockSize2, subSize);
this.leftNeighborContext.UpdatePartition(locVerticalB, superblockInfo, blockSize2, subSize);
break;