Browse Source

Bounds checks for Predictors

pull/2633/head
Ynse Hoornenborg 2 years ago
parent
commit
51d54c68d7
  1. 13
      src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DcFillPredictor.cs
  2. 17
      src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DcLeftPredictor.cs
  3. 21
      src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DcPredictor.cs
  4. 17
      src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DcTopPredictor.cs
  5. 73
      src/ImageSharp/Formats/Heif/Av1/Prediction/Av1PredictionDecoder.cs
  6. 16
      src/ImageSharp/Formats/Heif/Av1/Prediction/Av1PredictorFactory.cs
  7. 2
      src/ImageSharp/Formats/Heif/Av1/Prediction/IAv1Predictor.cs
  8. 2
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileInfo.cs
  9. 22
      tests/ImageSharp.Tests/Formats/Heif/Av1/Av1PredictorTests.cs

13
src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DcFillPredictor.cs

@ -23,16 +23,19 @@ internal class Av1DcFillPredictor : IAv1Predictor
this.blockHeight = (uint)transformSize.GetHeight();
}
public static void PredictScalar(Av1TransformSize transformSize, ref byte destination, nuint stride, ref byte above, ref byte left)
=> new Av1DcFillPredictor(transformSize).PredictScalar(ref destination, stride, ref above, ref left);
public static void PredictScalar(Av1TransformSize transformSize, Span<byte> destination, nuint stride, Span<byte> above, Span<byte> left)
=> new Av1DcFillPredictor(transformSize).PredictScalar(destination, stride, above, left);
public void PredictScalar(ref byte destination, nuint stride, ref byte above, ref byte left)
public void PredictScalar(Span<byte> destination, nuint stride, Span<byte> above, Span<byte> left)
{
const byte expectedDc = 0x80;
Guard.MustBeGreaterThanOrEqualTo(stride, this.blockWidth, nameof(stride));
Guard.MustBeSizedAtLeast(destination, (int)this.blockHeight * (int)stride, nameof(destination));
ref byte destinationRef = ref destination[0];
for (uint r = 0; r < this.blockHeight; r++)
{
Unsafe.InitBlock(ref destination, expectedDc, this.blockWidth);
destination = ref Unsafe.Add(ref destination, stride);
Unsafe.InitBlock(ref destinationRef, expectedDc, this.blockWidth);
destinationRef = ref Unsafe.Add(ref destinationRef, stride);
}
}
}

17
src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DcLeftPredictor.cs

@ -23,22 +23,27 @@ internal class Av1DcLeftPredictor : IAv1Predictor
this.blockHeight = (uint)transformSize.GetHeight();
}
public static void PredictScalar(Av1TransformSize transformSize, ref byte destination, nuint stride, ref byte above, ref byte left)
=> new Av1DcLeftPredictor(transformSize).PredictScalar(ref destination, stride, ref above, ref left);
public static void PredictScalar(Av1TransformSize transformSize, Span<byte> destination, nuint stride, Span<byte> above, Span<byte> left)
=> new Av1DcLeftPredictor(transformSize).PredictScalar(destination, stride, above, left);
public void PredictScalar(ref byte destination, nuint stride, ref byte above, ref byte left)
public void PredictScalar(Span<byte> destination, nuint stride, Span<byte> above, Span<byte> left)
{
int sum = 0;
Guard.MustBeGreaterThanOrEqualTo(stride, this.blockWidth, nameof(stride));
Guard.MustBeSizedAtLeast(left, (int)this.blockHeight, nameof(left));
Guard.MustBeSizedAtLeast(destination, (int)this.blockHeight * (int)stride, nameof(destination));
ref byte leftRef = ref left[0];
ref byte destinationRef = ref destination[0];
for (uint i = 0; i < this.blockHeight; i++)
{
sum += Unsafe.Add(ref left, i);
sum += Unsafe.Add(ref leftRef, i);
}
byte expectedDc = (byte)((sum + (this.blockHeight >> 1)) / this.blockHeight);
for (uint r = 0; r < this.blockHeight; r++)
{
Unsafe.InitBlock(ref destination, expectedDc, this.blockWidth);
destination = ref Unsafe.Add(ref destination, stride);
Unsafe.InitBlock(ref destinationRef, expectedDc, this.blockWidth);
destinationRef = ref Unsafe.Add(ref destinationRef, stride);
}
}
}

21
src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DcPredictor.cs

@ -23,28 +23,35 @@ internal class Av1DcPredictor : IAv1Predictor
this.blockHeight = (uint)transformSize.GetHeight();
}
public static void PredictScalar(Av1TransformSize transformSize, ref byte destination, nuint stride, ref byte above, ref byte left)
=> new Av1DcPredictor(transformSize).PredictScalar(ref destination, stride, ref above, ref left);
public static void PredictScalar(Av1TransformSize transformSize, Span<byte> destination, nuint stride, Span<byte> above, Span<byte> left)
=> new Av1DcPredictor(transformSize).PredictScalar(destination, stride, above, left);
public void PredictScalar(ref byte destination, nuint stride, ref byte above, ref byte left)
public void PredictScalar(Span<byte> destination, nuint stride, Span<byte> above, Span<byte> left)
{
int sum = 0;
Guard.MustBeGreaterThanOrEqualTo(stride, this.blockWidth, nameof(stride));
Guard.MustBeSizedAtLeast(left, (int)this.blockHeight, nameof(left));
Guard.MustBeSizedAtLeast(above, (int)this.blockWidth, nameof(above));
Guard.MustBeSizedAtLeast(destination, (int)this.blockHeight * (int)stride, nameof(destination));
ref byte leftRef = ref left[0];
ref byte aboveRef = ref above[0];
ref byte destinationRef = ref destination[0];
uint count = this.blockWidth + this.blockHeight;
for (uint i = 0; i < this.blockWidth; i++)
{
sum += Unsafe.Add(ref above, i);
sum += Unsafe.Add(ref aboveRef, i);
}
for (uint i = 0; i < this.blockHeight; i++)
{
sum += Unsafe.Add(ref left, i);
sum += Unsafe.Add(ref leftRef, i);
}
byte expectedDc = (byte)((sum + (count >> 1)) / count);
for (uint r = 0; r < this.blockHeight; r++)
{
Unsafe.InitBlock(ref destination, expectedDc, this.blockWidth);
destination = ref Unsafe.Add(ref destination, stride);
Unsafe.InitBlock(ref destinationRef, expectedDc, this.blockWidth);
destinationRef = ref Unsafe.Add(ref destinationRef, stride);
}
}
}

17
src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DcTopPredictor.cs

@ -23,22 +23,27 @@ internal class Av1DcTopPredictor : IAv1Predictor
this.blockHeight = (uint)transformSize.GetHeight();
}
public static void PredictScalar(Av1TransformSize transformSize, ref byte destination, nuint stride, ref byte above, ref byte left)
=> new Av1DcTopPredictor(transformSize).PredictScalar(ref destination, stride, ref above, ref left);
public static void PredictScalar(Av1TransformSize transformSize, Span<byte> destination, nuint stride, Span<byte> above, Span<byte> left)
=> new Av1DcTopPredictor(transformSize).PredictScalar(destination, stride, above, left);
public void PredictScalar(ref byte destination, nuint stride, ref byte above, ref byte left)
public void PredictScalar(Span<byte> destination, nuint stride, Span<byte> above, Span<byte> left)
{
int sum = 0;
Guard.MustBeGreaterThanOrEqualTo(stride, this.blockWidth, nameof(stride));
Guard.MustBeSizedAtLeast(above, (int)this.blockWidth, nameof(above));
Guard.MustBeSizedAtLeast(destination, (int)this.blockHeight * (int)stride, nameof(destination));
ref byte aboveRef = ref above[0];
ref byte destinationRef = ref destination[0];
for (uint i = 0; i < this.blockWidth; i++)
{
sum += Unsafe.Add(ref above, i);
sum += Unsafe.Add(ref aboveRef, i);
}
byte expectedDc = (byte)((sum + (this.blockWidth >> 1)) / this.blockWidth);
for (uint r = 0; r < this.blockHeight; r++)
{
Unsafe.InitBlock(ref destination, expectedDc, this.blockWidth);
destination = ref Unsafe.Add(ref destination, stride);
Unsafe.InitBlock(ref destinationRef, expectedDc, this.blockWidth);
destinationRef = ref Unsafe.Add(ref destinationRef, stride);
}
}
}

73
src/ImageSharp/Formats/Heif/Av1/Prediction/Av1PredictionDecoder.cs

@ -6,7 +6,6 @@ using SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
using SixLabors.ImageSharp.Formats.Heif.Av1.Prediction.ChromaFromLuma;
using SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction;
@ -37,12 +36,9 @@ internal class Av1PredictionDecoder
int blockModeInfoRowOffset)
{
int bytesPerPixel = (bitDepth == Av1BitDepth.EightBit && !this.is16BitPipeline) ? 2 : 1;
ref byte pixelRef = ref pixelBuffer[0];
ref byte topNeighbor = ref pixelRef;
ref byte leftNeighbor = ref pixelRef;
int stride = pixelStride * bytesPerPixel;
topNeighbor = Unsafe.Subtract(ref topNeighbor, stride);
leftNeighbor = Unsafe.Subtract(ref leftNeighbor, 1);
Span<byte> topNeighbor = pixelBuffer.Slice(-stride);
Span<byte> leftNeighbor = pixelBuffer.Slice(-1);
bool is16BitPipeline = this.is16BitPipeline;
Av1PredictionMode mode = (plane == Av1Plane.Y) ? partitionInfo.ModeInfo.YMode : partitionInfo.ModeInfo.UvMode;
@ -54,10 +50,10 @@ internal class Av1PredictionDecoder
plane,
transformSize,
tileInfo,
ref pixelRef,
pixelBuffer,
stride,
ref topNeighbor,
ref leftNeighbor,
topNeighbor,
leftNeighbor,
stride,
mode,
blockModeInfoColumnOffset,
@ -80,10 +76,10 @@ internal class Av1PredictionDecoder
plane,
transformSize,
tileInfo,
ref pixelRef,
pixelBuffer,
stride,
ref topNeighbor,
ref leftNeighbor,
topNeighbor,
leftNeighbor,
stride,
mode,
blockModeInfoColumnOffset,
@ -199,10 +195,10 @@ internal class Av1PredictionDecoder
Av1Plane plane,
Av1TransformSize transformSize,
Av1TileInfo tileInfo,
ref byte pixelBuffer,
Span<byte> pixelBuffer,
int pixelBufferStride,
ref byte topNeighbor,
ref byte leftNeighbor,
Span<byte> topNeighbor,
Span<byte> leftNeighbor,
int referenceStride,
Av1PredictionMode mode,
int blockModeInfoColumnOffset,
@ -290,10 +286,10 @@ internal class Av1PredictionDecoder
{
this.DecodeBuildIntraPredictors(
partitionInfo,
ref topNeighbor,
ref leftNeighbor,
topNeighbor,
leftNeighbor,
(nuint)referenceStride,
ref pixelBuffer,
pixelBuffer,
(nuint)pixelBufferStride,
mode,
angleDelta,
@ -560,10 +556,10 @@ internal class Av1PredictionDecoder
private void DecodeBuildIntraPredictors(
Av1PartitionInfo partitionInfo,
ref byte aboveNeighbor,
ref byte leftNeighbor,
Span<byte> aboveNeighbor,
Span<byte> leftNeighbor,
nuint referenceStride,
ref byte destination,
Span<byte> destination,
nuint destinationStride,
Av1PredictionMode mode,
int angleDelta,
@ -630,17 +626,18 @@ internal class Av1PredictionDecoder
byte val;
if (needLeft)
{
val = (byte)((topPixelCount > 0) ? aboveNeighbor : 129);
val = (byte)((topPixelCount > 0) ? aboveNeighbor[0] : 129);
}
else
{
val = (byte)((leftPixelCount > 0) ? leftNeighbor : 127);
val = (byte)((leftPixelCount > 0) ? leftNeighbor[0] : 127);
}
ref byte destinationRef = ref destination[0];
for (int i = 0; i < transformHeight; ++i)
{
Unsafe.InitBlock(ref destination, val, (uint)transformWidth);
destination = ref Unsafe.Add(ref destination, destinationStride);
Unsafe.InitBlock(ref destinationRef, val, (uint)transformWidth);
destinationRef = ref Unsafe.Add(ref destinationRef, destinationStride);
}
return;
@ -666,7 +663,7 @@ internal class Av1PredictionDecoder
{
for (; i < leftPixelCount; i++)
{
leftColumn[i] = Unsafe.Add(ref leftNeighbor, i * (int)referenceStride);
leftColumn[i] = leftNeighbor[i * (int)referenceStride];
}
if (needBottom && bottomLeftPixelCount > 0)
@ -674,7 +671,7 @@ internal class Av1PredictionDecoder
Guard.IsTrue(i == transformHeight, nameof(i), string.Empty);
for (; i < transformHeight + bottomLeftPixelCount; i++)
{
leftColumn[i] = Unsafe.Add(ref leftNeighbor, i * (int)referenceStride);
leftColumn[i] = leftNeighbor[i * (int)referenceStride];
}
}
@ -687,7 +684,7 @@ internal class Av1PredictionDecoder
{
if (topPixelCount > 0)
{
Unsafe.InitBlock(ref leftColumn[0], aboveNeighbor, numLeftPixelsNeeded);
Unsafe.InitBlock(ref leftColumn[0], aboveNeighbor[0], numLeftPixelsNeeded);
}
else
{
@ -713,12 +710,12 @@ internal class Av1PredictionDecoder
uint numTopPixelsNeeded = (uint)(transformWidth + (needRight ? transformHeight : 0));
if (topPixelCount > 0)
{
Unsafe.CopyBlock(ref aboveRow[0], ref aboveNeighbor, (uint)topPixelCount);
Unsafe.CopyBlock(ref aboveRow[0], ref aboveNeighbor[0], (uint)topPixelCount);
int i = topPixelCount;
if (needRight && topPixelCount > 0)
{
Guard.IsTrue(topPixelCount == transformWidth, nameof(topPixelCount), string.Empty);
Unsafe.CopyBlock(ref aboveRow[transformWidth], ref Unsafe.Add(ref aboveNeighbor, transformWidth), (uint)topPixelCount);
Unsafe.CopyBlock(ref aboveRow[transformWidth], ref aboveNeighbor[transformWidth], (uint)topPixelCount);
i += topPixelCount;
}
@ -731,7 +728,7 @@ internal class Av1PredictionDecoder
{
if (leftPixelCount > 0)
{
Unsafe.InitBlock(ref aboveRow[0], leftNeighbor, numTopPixelsNeeded);
Unsafe.InitBlock(ref aboveRow[0], leftNeighbor[0], numTopPixelsNeeded);
}
else
{
@ -744,15 +741,15 @@ internal class Av1PredictionDecoder
{
if (topPixelCount > 0 && leftPixelCount > 0)
{
aboveRow[-1] = Unsafe.Subtract(ref aboveNeighbor, 1);
aboveRow[-1] = aboveNeighbor[-1];
}
else if (topPixelCount > 0)
{
aboveRow[-1] = aboveNeighbor;
aboveRow[-1] = aboveNeighbor[0];
}
else if (leftPixelCount > 0)
{
aboveRow[-1] = leftNeighbor;
aboveRow[-1] = leftNeighbor[0];
}
else
{
@ -764,7 +761,7 @@ internal class Av1PredictionDecoder
if (useFilterIntra)
{
Av1PredictorFactory.FilterIntraPredictor(ref destination, destinationStride, transformSize, aboveRow, leftColumn, filterIntraMode);
Av1PredictorFactory.FilterIntraPredictor(destination, destinationStride, transformSize, aboveRow, leftColumn, filterIntraMode);
return;
}
@ -819,18 +816,18 @@ internal class Av1PredictionDecoder
}
}
Av1PredictorFactory.DirectionalPredictor(ref destination, destinationStride, transformSize, aboveRow, leftColumn, upsampleAbove, upsampleLeft, angle);
Av1PredictorFactory.DirectionalPredictor(destination, destinationStride, transformSize, aboveRow, leftColumn, upsampleAbove, upsampleLeft, angle);
return;
}
// predict
if (mode == Av1PredictionMode.DC)
{
Av1PredictorFactory.DcPredictor(leftPixelCount > 0, topPixelCount > 0, transformSize, ref destination, destinationStride, aboveRow, leftColumn);
Av1PredictorFactory.DcPredictor(leftPixelCount > 0, topPixelCount > 0, transformSize, destination, destinationStride, aboveRow, leftColumn);
}
else
{
Av1PredictorFactory.GeneralPredictor(mode, transformSize, ref destination, destinationStride, aboveRow, leftColumn);
Av1PredictorFactory.GeneralPredictor(mode, transformSize, destination, destinationStride, aboveRow, leftColumn);
}
}

16
src/ImageSharp/Formats/Heif/Av1/Prediction/Av1PredictorFactory.cs

@ -8,35 +8,35 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction;
internal class Av1PredictorFactory
{
internal static void DcPredictor(bool hasLeft, bool hasAbove, Av1TransformSize transformSize, ref byte destination, nuint destinationStride, Span<byte> aboveRow, Span<byte> leftColumn)
internal static void DcPredictor(bool hasLeft, bool hasAbove, Av1TransformSize transformSize, Span<byte> destination, nuint destinationStride, Span<byte> aboveRow, Span<byte> leftColumn)
{
if (hasLeft)
{
if (hasAbove)
{
Av1DcPredictor.PredictScalar(transformSize, ref destination, destinationStride, ref aboveRow[0], ref leftColumn[0]);
Av1DcPredictor.PredictScalar(transformSize, destination, destinationStride, aboveRow, leftColumn);
}
else
{
Av1DcLeftPredictor.PredictScalar(transformSize, ref destination, destinationStride, ref aboveRow[0], ref leftColumn[0]);
Av1DcLeftPredictor.PredictScalar(transformSize, destination, destinationStride, aboveRow, leftColumn);
}
}
else
{
if (hasAbove)
{
Av1DcTopPredictor.PredictScalar(transformSize, ref destination, destinationStride, ref aboveRow[0], ref leftColumn[0]);
Av1DcTopPredictor.PredictScalar(transformSize, destination, destinationStride, aboveRow, leftColumn);
}
else
{
Av1DcFillPredictor.PredictScalar(transformSize, ref destination, destinationStride, ref aboveRow[0], ref leftColumn[0]);
Av1DcFillPredictor.PredictScalar(transformSize, destination, destinationStride, aboveRow, leftColumn);
}
}
}
internal static void DirectionalPredictor(ref byte destination, nuint destinationStride, Av1TransformSize transformSize, Span<byte> aboveRow, Span<byte> leftColumn, bool upsampleAbove, bool upsampleLeft, int angle) => throw new NotImplementedException();
internal static void DirectionalPredictor(Span<byte> destination, nuint destinationStride, Av1TransformSize transformSize, Span<byte> aboveRow, Span<byte> leftColumn, bool upsampleAbove, bool upsampleLeft, int angle) => throw new NotImplementedException();
internal static void FilterIntraPredictor(ref byte destination, nuint destinationStride, Av1TransformSize transformSize, Span<byte> aboveRow, Span<byte> leftColumn, Av1FilterIntraMode filterIntraMode) => throw new NotImplementedException();
internal static void FilterIntraPredictor(Span<byte> destination, nuint destinationStride, Av1TransformSize transformSize, Span<byte> aboveRow, Span<byte> leftColumn, Av1FilterIntraMode filterIntraMode) => throw new NotImplementedException();
internal static void GeneralPredictor(Av1PredictionMode mode, Av1TransformSize transformSize, ref byte destination, nuint destinationStride, Span<byte> aboveRow, Span<byte> leftColumn) => throw new NotImplementedException();
internal static void GeneralPredictor(Av1PredictionMode mode, Av1TransformSize transformSize, Span<byte> destination, nuint destinationStride, Span<byte> aboveRow, Span<byte> leftColumn) => throw new NotImplementedException();
}

2
src/ImageSharp/Formats/Heif/Av1/Prediction/IAv1Predictor.cs

@ -15,5 +15,5 @@ internal interface IAv1Predictor
/// <param name="stride">The stride of the destination buffer.</param>
/// <param name="above">Pointer to the first element of the block above.</param>
/// <param name="left">Pointer to the first element of the block to the left.</param>
public void PredictScalar(ref byte destination, nuint stride, ref byte above, ref byte left);
public void PredictScalar(Span<byte> destination, nuint stride, Span<byte> above, Span<byte> left);
}

2
src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileInfo.cs

@ -23,8 +23,6 @@ internal class Av1TileInfo
public Point TileIndex { get; private set; }
public int TileIndexInRasterOrder { get; }
public void SetTileRow(ObuTileGroupHeader tileGroupHeader, int modeInfoRowCount, int row)
{
this.ModeInfoRowStart = tileGroupHeader.TileRowStartModeInfo[row];

22
tests/ImageSharp.Tests/Formats/Heif/Av1/PredictorTests.cs → tests/ImageSharp.Tests/Formats/Heif/Av1/Av1PredictorTests.cs

@ -7,7 +7,7 @@ using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
namespace SixLabors.ImageSharp.Tests.Formats.Heif.Av1;
[Trait("Format", "Avif")]
public class PredictorTests
public class Av1PredictorTests
{
[Theory]
[MemberData(nameof(GetTransformSizes))]
@ -21,7 +21,7 @@ public class PredictorTests
// Act
Av1DcFillPredictor predictor = new(new Size(width, height));
predictor.PredictScalar(ref destination[0], (nuint)width, ref above[0], ref left[0]);
predictor.PredictScalar(destination, (nuint)width, above, left);
// Assert
Assert.All(destination, (b) => AssertValue(expected, b));
@ -33,8 +33,8 @@ public class PredictorTests
{
// Assign
byte[] destination = new byte[width * height];
byte[] left = new byte[width * height];
byte[] above = new byte[width * height];
byte[] left = new byte[height];
byte[] above = new byte[width];
Array.Fill(left, (byte)5);
Array.Fill(above, (byte)28);
int count = width + height;
@ -43,7 +43,7 @@ public class PredictorTests
// Act
Av1DcPredictor predictor = new(new Size(width, height));
predictor.PredictScalar(ref destination[0], (nuint)width, ref above[0], ref left[0]);
predictor.PredictScalar(destination, (nuint)width, above, left);
// Assert
Assert.Equal((5 * height) + (28 * width), sum);
@ -56,15 +56,15 @@ public class PredictorTests
{
// Assign
byte[] destination = new byte[width * height];
byte[] left = new byte[width * height];
byte[] above = new byte[width * height];
byte[] left = new byte[height];
byte[] above = new byte[width];
Array.Fill(left, (byte)5);
Array.Fill(above, (byte)28);
byte expected = left[0];
// Act
Av1DcLeftPredictor predictor = new(new Size(width, height));
predictor.PredictScalar(ref destination[0], (nuint)width, ref above[0], ref left[0]);
predictor.PredictScalar(destination, (nuint)width, above, left);
// Assert
Assert.All(destination, (b) => AssertValue(expected, b));
@ -76,15 +76,15 @@ public class PredictorTests
{
// Assign
byte[] destination = new byte[width * height];
byte[] left = new byte[width * height];
byte[] above = new byte[width * height];
byte[] left = new byte[height];
byte[] above = new byte[width];
Array.Fill(left, (byte)5);
Array.Fill(above, (byte)28);
byte expected = above[0];
// Act
Av1DcTopPredictor predictor = new(new Size(width, height));
predictor.PredictScalar(ref destination[0], (nuint)width, ref above[0], ref left[0]);
predictor.PredictScalar(destination, (nuint)width, above, left);
// Assert
Assert.All(destination, (b) => AssertValue(expected, b));
Loading…
Cancel
Save