diff --git a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuReader.cs b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuReader.cs
index 4823712f18..dec0602523 100644
--- a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuReader.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuReader.cs
@@ -1,6 +1,7 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
+using System.Collections.Generic;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
@@ -11,6 +12,34 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
///
internal class ObuReader
{
+ ///
+ /// Maximum value used for loop filtering.
+ ///
+ private const int MaxLoopFilter = 63;
+
+ ///
+ /// Number of segments allowed in segmentation map.
+ ///
+ private const int MaxSegments = 0;
+
+ ///
+ /// Number of segment features.
+ ///
+ private const int SegLvlMax = 8;
+
+ ///
+ /// Index for reference frame segment feature.
+ ///
+ private const int SegLvlRefFrame = 5;
+
+ private const int PrimaryRefNone = 7;
+
+ private static readonly int[] SegmentationFeatureBits = [8, 6, 6, 6, 6, 3, 0, 0];
+
+ private static readonly int[] SegmentationFeatureSigned = [1, 1, 1, 1, 1, 0, 0, 0];
+
+ private static readonly int[] SegmentationFeatureMax = [255, MaxLoopFilter, MaxLoopFilter, MaxLoopFilter, MaxLoopFilter, 7, 0, 0];
+
public ObuSequenceHeader? SequenceHeader { get; set; }
public ObuFrameHeader? FrameHeader { get; set; }
@@ -1188,9 +1217,84 @@ internal class ObuReader
private static void ReadSegmentationParameters(ref Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, int planesCount)
{
frameInfo.SegmentationParameters.Enabled = reader.ReadBoolean();
- Guard.IsFalse(frameInfo.SegmentationParameters.Enabled, nameof(frameInfo.SegmentationParameters.Enabled), "Segmentation not supported yet.");
- // TODO: Parse more stuff.
+ if (frameInfo.SegmentationParameters.Enabled)
+ {
+ if (frameInfo.PrimaryReferenceFrame == PrimaryRefNone)
+ {
+ frameInfo.SegmentationParameters.SegmentationUpdateMap = 1;
+ frameInfo.SegmentationParameters.SegmentationTemporalUpdate = 0;
+ frameInfo.SegmentationParameters.SegmentationUpdateData = 1;
+ }
+ else
+ {
+ frameInfo.SegmentationParameters.SegmentationUpdateMap = reader.ReadBoolean() ? 1 : 0;
+ if (frameInfo.SegmentationParameters.SegmentationUpdateMap == 1)
+ {
+ frameInfo.SegmentationParameters.SegmentationTemporalUpdate = reader.ReadBoolean() ? 1 : 0;
+ }
+
+ frameInfo.SegmentationParameters.SegmentationUpdateData = reader.ReadBoolean() ? 1 : 0;
+ }
+
+ if (frameInfo.SegmentationParameters.SegmentationUpdateData == 1)
+ {
+ for (int i = 0; i < MaxSegments; i++)
+ {
+ for (int j = 0; j < SegLvlMax; j++)
+ {
+ int featureValue = 0;
+ bool featureEnabled = reader.ReadBoolean();
+ frameInfo.SegmentationParameters.FeatureEnabled[i, j] = featureEnabled;
+ int clippedValue = 0;
+ if (featureEnabled)
+ {
+ int bitsToRead = SegmentationFeatureBits[j];
+ int limit = SegmentationFeatureMax[j];
+ if (SegmentationFeatureSigned[j] == 1)
+ {
+ featureValue = reader.ReadSignedFromUnsigned(1 + bitsToRead);
+ clippedValue = Av1Math.Clip3(-limit, limit, featureValue);
+ }
+ else
+ {
+ featureValue = (int)reader.ReadLiteral(bitsToRead);
+ }
+ }
+
+ frameInfo.SegmentationParameters.FeatureData[i, j] = clippedValue;
+ }
+ }
+ }
+ }
+ else
+ {
+ for (int i = 0; i < MaxSegments; i++)
+ {
+ for (int j = 0; j < SegLvlMax; j++)
+ {
+ frameInfo.SegmentationParameters.FeatureEnabled[i, j] = false;
+ frameInfo.SegmentationParameters.FeatureData[i, j] = 0;
+ }
+ }
+ }
+
+ frameInfo.SegmentationParameters.SegmentIdPrecedesSkip = false;
+ frameInfo.SegmentationParameters.LastActiveSegmentId = 0;
+ for (int i = 0; i < MaxSegments; i++)
+ {
+ for (int j = 0; j < SegLvlMax; j++)
+ {
+ if (frameInfo.SegmentationParameters.FeatureEnabled[i, j])
+ {
+ frameInfo.SegmentationParameters.LastActiveSegmentId = i;
+ if (j >= SegLvlRefFrame)
+ {
+ frameInfo.SegmentationParameters.SegmentIdPrecedesSkip = true;
+ }
+ }
+ }
+ }
}
///
@@ -1426,6 +1530,9 @@ internal class ObuReader
private static bool IsValidSequenceLevel(int sequenceLevelIndex)
=> sequenceLevelIndex is < 24 or 31;
+ ///
+ /// Returns the smallest value for k such that blockSize << k is greater than or equal to target.
+ ///
public static int TileLog2(int blockSize, int target)
{
int k;
diff --git a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuSegmentationParameters.cs b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuSegmentationParameters.cs
index 07d4a6cd8d..a31235322c 100644
--- a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuSegmentationParameters.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuSegmentationParameters.cs
@@ -17,6 +17,25 @@ internal class ObuSegmentationParameters
public int LastActiveSegmentId { get; internal set; }
+ ///
+ /// Gets or sets the SegmentationUpdateMap. A value of 1 indicates that the segmentation map are updated during the decoding of this
+ /// frame. SegmentationUpdateMap equal to 0 means that the segmentation map from the previous frame is used.
+ ///
+ public int SegmentationUpdateMap { get; internal set; }
+
+ ///
+ /// Gets or sets the SegmentationTemporalUpdate. A value of 1 indicates that the updates to the segmentation map are coded relative to the
+ /// existing segmentation map. SegmentationTemporalUpdate equal to 0 indicates that the new segmentation map is coded
+ /// without reference to the existing segmentation map.
+ ///
+ public int SegmentationTemporalUpdate { get; internal set; }
+
+ ///
+ /// Gets or sets SegmentationUpdateData. A value of 1 indicates that new parameters are about to be specified for each segment.
+ /// SegmentationUpdateData equal to 0 indicates that the segmentation parameters should keep their existing values.
+ ///
+ public int SegmentationUpdateData { get; internal set; }
+
internal bool IsFeatureActive(int segmentId, ObuSegmentationLevelFeature feature)
=> this.FeatureEnabled[segmentId, (int)feature];
}