diff --git a/src/ImageSharp/Formats/Heif/Av1/Av1BitStreamReader.cs b/src/ImageSharp/Formats/Heif/Av1/Av1BitStreamReader.cs index 4dbbfe56e..99ba383ce 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Av1BitStreamReader.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Av1BitStreamReader.cs @@ -1,14 +1,16 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.Formats.Heif.Av1; internal ref struct Av1BitStreamReader { - private const int WordSize = 32; + private const int WordSize = 1 << WordSizeLog2; + private const int WordSizeLog2 = 5; + private const int WordSizeInBytesLog2 = WordSizeLog2 - Log2Of8; + private const int Log2Of8 = 3; private readonly Span data; private uint currentWord; @@ -175,11 +177,12 @@ internal ref struct Av1BitStreamReader public Span GetSymbolReader(int tileDataSize) { - DebugGuard.IsTrue((this.bitOffset & (WordSize - 1)) == 0, "Symbol reading needs to start on byte boundary."); + DebugGuard.IsTrue((this.bitOffset & 0x7) == 0, "Symbol reading needs to start on byte boundary."); // TODO: Pass exact byte iso Word start. - Span span = this.data.Slice(this.bitOffset >> WordSize, tileDataSize); - this.bitOffset += tileDataSize << 8; + int spanLength = tileDataSize >> WordSizeInBytesLog2; + Span span = this.data.Slice(this.bitOffset >> WordSizeLog2, spanLength); + this.bitOffset += tileDataSize << Log2Of8; return MemoryMarshal.Cast(span); } } diff --git a/src/ImageSharp/Formats/Heif/Av1/Av1Constants.cs b/src/ImageSharp/Formats/Heif/Av1/Av1Constants.cs index d83389d9d..36e3d19c4 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Av1Constants.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Av1Constants.cs @@ -118,4 +118,9 @@ internal static class Av1Constants /// Number of segments allowed in segmentation map. /// public const int MaxSegments = 8; + + /// + /// Number of reference frame types (including intra type). + /// + public const int TotalReferencesPerFrame = 8; } diff --git a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuLoopFilterParameters.cs b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuLoopFilterParameters.cs index ff510c8ae..ad32d0c01 100644 --- a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuLoopFilterParameters.cs +++ b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuLoopFilterParameters.cs @@ -5,6 +5,12 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit; internal class ObuLoopFilterParameters { + public ObuLoopFilterParameters() + { + this.ReferenceDeltas = [1, 0, 0, 0, 0, -1, -1, -1]; + this.ModeDeltas = [0, 0]; + } + public int[] FilterLevel { get; internal set; } = new int[2]; public int FilterLevelU { get; internal set; } @@ -14,4 +20,10 @@ internal class ObuLoopFilterParameters public int SharpnessLevel { get; internal set; } public bool ReferenceDeltaModeEnabled { get; internal set; } + + public bool ReferenceDeltaModeUpdate { get; internal set; } + + public int[] ReferenceDeltas { get; } + + public int[] ModeDeltas { get; } } diff --git a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuReader.cs b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuReader.cs index aa35df213..4823712f1 100644 --- a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuReader.cs +++ b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuReader.cs @@ -714,7 +714,7 @@ internal class ObuReader } frameInfo.DisableCdfUpdate = reader.ReadBoolean(); - frameInfo.AllowScreenContentTools = sequenceHeader.ForceScreenContentTools == 1; + frameInfo.AllowScreenContentTools = sequenceHeader.ForceScreenContentTools == 2; if (frameInfo.AllowScreenContentTools) { frameInfo.AllowScreenContentTools = reader.ReadBoolean(); @@ -936,7 +936,7 @@ internal class ObuReader } frameInfo.AllLossless = frameInfo.CodedLossless && frameInfo.FrameSize.FrameWidth == frameInfo.FrameSize.SuperResolutionUpscaledWidth; - ReadLoopFilterParameters(ref reader, sequenceHeader, frameInfo, planesCount); + this.ReadLoopFilterParameters(ref reader, planesCount); ReadCdefParameters(ref reader, sequenceHeader, frameInfo, planesCount); ReadLoopRestorationParameters(ref reader, sequenceHeader, frameInfo, planesCount); ReadTransformMode(ref reader, frameInfo); @@ -1024,7 +1024,7 @@ internal class ObuReader int tileGroupEnd = tileCount - 1; if (tileCount != 1 && tileStartAndEndPresentFlag) { - int tileBits = Av1Math.Log2(tileInfo.TileColumnCount) + Av1Math.Log2(tileInfo.TileRowCount); + int tileBits = tileInfo.TileColumnCountLog2 + tileInfo.TileRowCountLog2; tileGroupStart = (int)reader.ReadLiteral(tileBits); tileGroupEnd = (int)reader.ReadLiteral(tileBits); } @@ -1196,8 +1196,9 @@ internal class ObuReader /// /// 5.9.11. Loop filter params syntax /// - private static void ReadLoopFilterParameters(ref Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, int planesCount) + private void ReadLoopFilterParameters(ref Av1BitStreamReader reader, int planesCount) { + ObuFrameHeader frameInfo = this.FrameHeader!; frameInfo.LoopFilterParameters.FilterLevel = new int[2]; if (frameInfo.CodedLossless || frameInfo.AllowIntraBlockCopy) { @@ -1220,8 +1221,25 @@ internal class ObuReader frameInfo.LoopFilterParameters.ReferenceDeltaModeEnabled = reader.ReadBoolean(); if (frameInfo.LoopFilterParameters.ReferenceDeltaModeEnabled) { - // TODO: Implement. - throw new NotImplementedException(); + frameInfo.LoopFilterParameters.ReferenceDeltaModeUpdate = reader.ReadBoolean(); + if (frameInfo.LoopFilterParameters.ReferenceDeltaModeUpdate) + { + for (int i = 0; i < Av1Constants.TotalReferencesPerFrame; i++) + { + if (reader.ReadBoolean()) + { + frameInfo.LoopFilterParameters.ReferenceDeltas[i] = reader.ReadSignedFromUnsigned(7); + } + } + + for (int i = 0; i < 2; i++) + { + if (reader.ReadBoolean()) + { + frameInfo.LoopFilterParameters.ModeDeltas[i] = reader.ReadSignedFromUnsigned(7); + } + } + } } }