diff --git a/src/ImageSharp/Formats/Webp/AlphaDecoder.cs b/src/ImageSharp/Formats/Webp/AlphaDecoder.cs
index d45fa2a427..8875ae1150 100644
--- a/src/ImageSharp/Formats/Webp/AlphaDecoder.cs
+++ b/src/ImageSharp/Formats/Webp/AlphaDecoder.cs
@@ -1,8 +1,8 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
-#nullable disable
using System.Buffers;
+using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
@@ -38,7 +38,7 @@ internal class AlphaDecoder : IDisposable
this.LastRow = 0;
int totalPixels = width * height;
- var compression = (WebpAlphaCompressionMethod)(alphaChunkHeader & 0x03);
+ WebpAlphaCompressionMethod compression = (WebpAlphaCompressionMethod)(alphaChunkHeader & 0x03);
if (compression is not WebpAlphaCompressionMethod.NoCompression and not WebpAlphaCompressionMethod.WebpLosslessCompression)
{
WebpThrowHelper.ThrowImageFormatException($"unexpected alpha compression method {compression} found");
@@ -59,7 +59,7 @@ internal class AlphaDecoder : IDisposable
if (this.Compressed)
{
- var bitReader = new Vp8LBitReader(data);
+ Vp8LBitReader bitReader = new(data);
this.LosslessDecoder = new WebpLosslessDecoder(bitReader, memoryAllocator, configuration);
this.LosslessDecoder.DecodeImageStream(this.Vp8LDec, width, height, true);
@@ -110,6 +110,7 @@ internal class AlphaDecoder : IDisposable
///
/// Gets a value indicating whether the alpha channel uses compression.
///
+ [MemberNotNullWhen(true, nameof(LosslessDecoder))]
private bool Compressed { get; }
///
@@ -120,7 +121,7 @@ internal class AlphaDecoder : IDisposable
///
/// Gets the Vp8L decoder which is used to de compress the alpha channel, if needed.
///
- private WebpLosslessDecoder LosslessDecoder { get; }
+ private WebpLosslessDecoder? LosslessDecoder { get; }
///
/// Gets a value indicating whether the decoding needs 1 byte per pixel for decoding.
@@ -173,17 +174,14 @@ internal class AlphaDecoder : IDisposable
dst = dst[this.Width..];
}
}
+ else if (this.Use8BDecode)
+ {
+ this.LosslessDecoder.DecodeAlphaData(this);
+ }
else
{
- if (this.Use8BDecode)
- {
- this.LosslessDecoder.DecodeAlphaData(this);
- }
- else
- {
- this.LosslessDecoder.DecodeImageData(this.Vp8LDec, this.Vp8LDec.Pixels.Memory.Span);
- this.ExtractAlphaRows(this.Vp8LDec);
- }
+ this.LosslessDecoder.DecodeImageData(this.Vp8LDec, this.Vp8LDec.Pixels.Memory.Span);
+ this.ExtractAlphaRows(this.Vp8LDec);
}
}
@@ -261,8 +259,7 @@ internal class AlphaDecoder : IDisposable
{
int numRowsToProcess = dec.Height;
int width = dec.Width;
- Span pixels = dec.Pixels.Memory.Span;
- Span input = pixels;
+ Span input = dec.Pixels.Memory.Span;
Span output = this.Alpha.Memory.Span;
// Extract alpha (which is stored in the green plane).
@@ -327,7 +324,7 @@ internal class AlphaDecoder : IDisposable
ref byte srcRef = ref MemoryMarshal.GetReference(input);
for (i = 1; i + 8 <= width; i += 8)
{
- var a0 = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref srcRef, i)), 0);
+ Vector128 a0 = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref srcRef, i)), 0);
Vector128 a1 = Sse2.Add(a0.AsByte(), last.AsByte());
Vector128 a2 = Sse2.ShiftLeftLogical128BitLane(a1, 1);
Vector128 a3 = Sse2.Add(a1, a2);
@@ -365,32 +362,29 @@ internal class AlphaDecoder : IDisposable
{
HorizontalUnfilter(null, input, dst, width);
}
- else
+ else if (Avx2.IsSupported)
{
- if (Avx2.IsSupported)
+ nint i;
+ int maxPos = width & ~31;
+ for (i = 0; i < maxPos; i += 32)
{
- nint i;
- int maxPos = width & ~31;
- for (i = 0; i < maxPos; i += 32)
- {
- Vector256 a0 = Unsafe.As>(ref Unsafe.Add(ref MemoryMarshal.GetReference(input), i));
- Vector256 b0 = Unsafe.As>(ref Unsafe.Add(ref MemoryMarshal.GetReference(prev), i));
- Vector256 c0 = Avx2.Add(a0.AsByte(), b0.AsByte());
- ref byte outputRef = ref Unsafe.Add(ref MemoryMarshal.GetReference(dst), i);
- Unsafe.As>(ref outputRef) = c0;
- }
+ Vector256 a0 = Unsafe.As>(ref Unsafe.Add(ref MemoryMarshal.GetReference(input), i));
+ Vector256 b0 = Unsafe.As>(ref Unsafe.Add(ref MemoryMarshal.GetReference(prev), i));
+ Vector256 c0 = Avx2.Add(a0.AsByte(), b0.AsByte());
+ ref byte outputRef = ref Unsafe.Add(ref MemoryMarshal.GetReference(dst), i);
+ Unsafe.As>(ref outputRef) = c0;
+ }
- for (; i < width; i++)
- {
- dst[(int)i] = (byte)(prev[(int)i] + input[(int)i]);
- }
+ for (; i < width; i++)
+ {
+ dst[(int)i] = (byte)(prev[(int)i] + input[(int)i]);
}
- else
+ }
+ else
+ {
+ for (int i = 0; i < width; i++)
{
- for (int i = 0; i < width; i++)
- {
- dst[i] = (byte)(prev[i] + input[i]);
- }
+ dst[i] = (byte)(prev[i] + input[i]);
}
}
}
diff --git a/src/ImageSharp/Formats/Webp/AlphaEncoder.cs b/src/ImageSharp/Formats/Webp/AlphaEncoder.cs
index 292c31f6a5..596715b205 100644
--- a/src/ImageSharp/Formats/Webp/AlphaEncoder.cs
+++ b/src/ImageSharp/Formats/Webp/AlphaEncoder.cs
@@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
-#nullable disable
using System.Buffers;
using SixLabors.ImageSharp.Advanced;
@@ -13,10 +12,8 @@ namespace SixLabors.ImageSharp.Formats.Webp;
///
/// Methods for encoding the alpha data of a VP8 image.
///
-internal class AlphaEncoder : IDisposable
+internal static class AlphaEncoder
{
- private IMemoryOwner alphaData;
-
///
/// Encodes the alpha channel data.
/// Data is either compressed as lossless webp image or uncompressed.
@@ -29,12 +26,18 @@ internal class AlphaEncoder : IDisposable
/// Indicates, if the data should be compressed with the lossless webp compression.
/// The size in bytes of the alpha data.
/// The encoded alpha data.
- public IMemoryOwner EncodeAlpha(Image image, Configuration configuration, MemoryAllocator memoryAllocator, bool skipMetadata, bool compress, out int size)
+ public static IMemoryOwner EncodeAlpha(
+ Image image,
+ Configuration configuration,
+ MemoryAllocator memoryAllocator,
+ bool skipMetadata,
+ bool compress,
+ out int size)
where TPixel : unmanaged, IPixel
{
int width = image.Width;
int height = image.Height;
- this.alphaData = ExtractAlphaChannel(image, configuration, memoryAllocator);
+ IMemoryOwner alphaData = ExtractAlphaChannel(image, configuration, memoryAllocator);
if (compress)
{
@@ -55,15 +58,15 @@ internal class AlphaEncoder : IDisposable
// The transparency information will be stored in the green channel of the ARGB quadruplet.
// The green channel is allowed extra transformation steps in the specification -- unlike the other channels,
// that can improve compression.
- using Image alphaAsImage = DispatchAlphaToGreen(image, this.alphaData.GetSpan());
+ using Image alphaAsImage = DispatchAlphaToGreen(image, alphaData.GetSpan());
- size = lossLessEncoder.EncodeAlphaImageData(alphaAsImage, this.alphaData);
+ size = lossLessEncoder.EncodeAlphaImageData(alphaAsImage, alphaData);
- return this.alphaData;
+ return alphaData;
}
size = width * height;
- return this.alphaData;
+ return alphaData;
}
///
@@ -128,7 +131,4 @@ internal class AlphaEncoder : IDisposable
return alphaDataBuffer;
}
-
- ///
- public void Dispose() => this.alphaData?.Dispose();
}
diff --git a/src/ImageSharp/Formats/Webp/BitReader/BitReaderBase.cs b/src/ImageSharp/Formats/Webp/BitReader/BitReaderBase.cs
index 2586690fd3..83f9e797ab 100644
--- a/src/ImageSharp/Formats/Webp/BitReader/BitReaderBase.cs
+++ b/src/ImageSharp/Formats/Webp/BitReader/BitReaderBase.cs
@@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
-#nullable disable
using System.Buffers;
using SixLabors.ImageSharp.Memory;
@@ -14,10 +13,16 @@ internal abstract class BitReaderBase : IDisposable
{
private bool isDisposed;
+ protected BitReaderBase(IMemoryOwner data)
+ => this.Data = data;
+
+ protected BitReaderBase(Stream inputStream, int imageDataSize, MemoryAllocator memoryAllocator)
+ => this.Data = ReadImageDataFromStream(inputStream, imageDataSize, memoryAllocator);
+
///
- /// Gets or sets the raw encoded image data.
+ /// Gets the raw encoded image data.
///
- public IMemoryOwner Data { get; set; }
+ public IMemoryOwner Data { get; }
///
/// Copies the raw encoded image data from the stream into a byte array.
@@ -25,11 +30,13 @@ internal abstract class BitReaderBase : IDisposable
/// The input stream.
/// Number of bytes to read as indicated from the chunk size.
/// Used for allocating memory during reading data from the stream.
- protected void ReadImageDataFromStream(Stream input, int bytesToRead, MemoryAllocator memoryAllocator)
+ protected static IMemoryOwner ReadImageDataFromStream(Stream input, int bytesToRead, MemoryAllocator memoryAllocator)
{
- this.Data = memoryAllocator.Allocate(bytesToRead);
- Span dataSpan = this.Data.Memory.Span;
+ IMemoryOwner data = memoryAllocator.Allocate(bytesToRead);
+ Span dataSpan = data.Memory.Span;
input.Read(dataSpan[..bytesToRead], 0, bytesToRead);
+
+ return data;
}
protected virtual void Dispose(bool disposing)
@@ -41,7 +48,7 @@ internal abstract class BitReaderBase : IDisposable
if (disposing)
{
- this.Data?.Dispose();
+ this.Data.Dispose();
}
this.isDisposed = true;
diff --git a/src/ImageSharp/Formats/Webp/BitReader/Vp8BitReader.cs b/src/ImageSharp/Formats/Webp/BitReader/Vp8BitReader.cs
index 07bfcccd91..7b64d8329c 100644
--- a/src/ImageSharp/Formats/Webp/BitReader/Vp8BitReader.cs
+++ b/src/ImageSharp/Formats/Webp/BitReader/Vp8BitReader.cs
@@ -57,12 +57,12 @@ internal class Vp8BitReader : BitReaderBase
/// The partition length.
/// Start index in the data array. Defaults to 0.
public Vp8BitReader(Stream inputStream, uint imageDataSize, MemoryAllocator memoryAllocator, uint partitionLength, int startPos = 0)
+ : base(inputStream, (int)imageDataSize, memoryAllocator)
{
Guard.MustBeLessThan(imageDataSize, int.MaxValue, nameof(imageDataSize));
this.ImageDataSize = imageDataSize;
this.PartitionLength = partitionLength;
- this.ReadImageDataFromStream(inputStream, (int)imageDataSize, memoryAllocator);
this.InitBitreader(partitionLength, startPos);
}
@@ -73,8 +73,8 @@ internal class Vp8BitReader : BitReaderBase
/// The partition length.
/// Start index in the data array. Defaults to 0.
public Vp8BitReader(IMemoryOwner imageData, uint partitionLength, int startPos = 0)
+ : base(imageData)
{
- this.Data = imageData;
this.ImageDataSize = (uint)imageData.Memory.Length;
this.PartitionLength = partitionLength;
this.InitBitreader(partitionLength, startPos);
diff --git a/src/ImageSharp/Formats/Webp/BitReader/Vp8LBitReader.cs b/src/ImageSharp/Formats/Webp/BitReader/Vp8LBitReader.cs
index 057abf134a..8da717545f 100644
--- a/src/ImageSharp/Formats/Webp/BitReader/Vp8LBitReader.cs
+++ b/src/ImageSharp/Formats/Webp/BitReader/Vp8LBitReader.cs
@@ -63,8 +63,8 @@ internal class Vp8LBitReader : BitReaderBase
///
/// Lossless compressed image data.
public Vp8LBitReader(IMemoryOwner data)
+ : base(data)
{
- this.Data = data;
this.len = data.Memory.Length;
this.value = 0;
this.bitPos = 0;
@@ -88,11 +88,10 @@ internal class Vp8LBitReader : BitReaderBase
/// The raw image data size in bytes.
/// Used for allocating memory during reading data from the stream.
public Vp8LBitReader(Stream inputStream, uint imageDataSize, MemoryAllocator memoryAllocator)
+ : base(inputStream, (int)imageDataSize, memoryAllocator)
{
long length = imageDataSize;
- this.ReadImageDataFromStream(inputStream, (int)imageDataSize, memoryAllocator);
-
this.len = length;
this.value = 0;
this.bitPos = 0;
@@ -193,7 +192,7 @@ internal class Vp8LBitReader : BitReaderBase
[MethodImpl(InliningOptions.ShortMethod)]
private void ShiftBytes()
{
- System.Span dataSpan = this.Data.Memory.Span;
+ System.Span dataSpan = this.Data!.Memory.Span;
while (this.bitPos >= 8 && this.pos < this.len)
{
this.value >>= 8;
diff --git a/src/ImageSharp/Formats/Webp/BitWriter/BitWriterBase.cs b/src/ImageSharp/Formats/Webp/BitWriter/BitWriterBase.cs
index 2df02727e0..02b1d0ab6a 100644
--- a/src/ImageSharp/Formats/Webp/BitWriter/BitWriterBase.cs
+++ b/src/ImageSharp/Formats/Webp/BitWriter/BitWriterBase.cs
@@ -123,7 +123,7 @@ internal abstract class BitWriterBase
/// The stream to write to.
/// The metadata profile's bytes.
/// The chuck type to write.
- protected void WriteMetadataProfile(Stream stream, byte[] metadataBytes, WebpChunkType chunkType)
+ protected void WriteMetadataProfile(Stream stream, byte[]? metadataBytes, WebpChunkType chunkType)
{
DebugGuard.NotNull(metadataBytes, nameof(metadataBytes));
@@ -207,7 +207,7 @@ internal abstract class BitWriterBase
/// The width of the image.
/// The height of the image.
/// Flag indicating, if a alpha channel is present.
- protected void WriteVp8XHeader(Stream stream, ExifProfile exifProfile, XmpProfile xmpProfile, byte[] iccProfileBytes, uint width, uint height, bool hasAlpha)
+ protected void WriteVp8XHeader(Stream stream, ExifProfile? exifProfile, XmpProfile? xmpProfile, byte[]? iccProfileBytes, uint width, uint height, bool hasAlpha)
{
if (width > MaxDimension || height > MaxDimension)
{
diff --git a/src/ImageSharp/Formats/Webp/BitWriter/Vp8BitWriter.cs b/src/ImageSharp/Formats/Webp/BitWriter/Vp8BitWriter.cs
index 6fa02c1161..b83b44fa14 100644
--- a/src/ImageSharp/Formats/Webp/BitWriter/Vp8BitWriter.cs
+++ b/src/ImageSharp/Formats/Webp/BitWriter/Vp8BitWriter.cs
@@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
-#nullable disable
using System.Buffers.Binary;
using SixLabors.ImageSharp.Formats.Webp.Lossy;
@@ -58,7 +57,8 @@ internal class Vp8BitWriter : BitWriterBase
/// Initializes a new instance of the class.
///
/// The expected size in bytes.
- public Vp8BitWriter(int expectedSize)
+ /// The Vp8Encoder.
+ public Vp8BitWriter(int expectedSize, Vp8Encoder enc)
: base(expectedSize)
{
this.range = 255 - 1;
@@ -67,15 +67,9 @@ internal class Vp8BitWriter : BitWriterBase
this.nbBits = -8;
this.pos = 0;
this.maxPos = 0;
- }
- ///
- /// Initializes a new instance of the class.
- ///
- /// The expected size in bytes.
- /// The Vp8Encoder.
- public Vp8BitWriter(int expectedSize, Vp8Encoder enc)
- : this(expectedSize) => this.enc = enc;
+ this.enc = enc;
+ }
///
public override int NumBytes() => (int)this.pos;
@@ -414,9 +408,9 @@ internal class Vp8BitWriter : BitWriterBase
/// Indicates, if the alpha data is compressed.
public void WriteEncodedImageToStream(
Stream stream,
- ExifProfile exifProfile,
- XmpProfile xmpProfile,
- IccProfile iccProfile,
+ ExifProfile? exifProfile,
+ XmpProfile? xmpProfile,
+ IccProfile? iccProfile,
uint width,
uint height,
bool hasAlpha,
@@ -424,22 +418,22 @@ internal class Vp8BitWriter : BitWriterBase
bool alphaDataIsCompressed)
{
bool isVp8X = false;
- byte[] exifBytes = null;
- byte[] xmpBytes = null;
- byte[] iccProfileBytes = null;
+ byte[]? exifBytes = null;
+ byte[]? xmpBytes = null;
+ byte[]? iccProfileBytes = null;
uint riffSize = 0;
if (exifProfile != null)
{
isVp8X = true;
exifBytes = exifProfile.ToByteArray();
- riffSize += MetadataChunkSize(exifBytes);
+ riffSize += MetadataChunkSize(exifBytes!);
}
if (xmpProfile != null)
{
isVp8X = true;
xmpBytes = xmpProfile.Data;
- riffSize += MetadataChunkSize(xmpBytes);
+ riffSize += MetadataChunkSize(xmpBytes!);
}
if (iccProfile != null)
@@ -465,7 +459,7 @@ internal class Vp8BitWriter : BitWriterBase
int mbSize = this.enc.Mbw * this.enc.Mbh;
int expectedSize = mbSize * 7 / 8;
- var bitWriterPartZero = new Vp8BitWriter(expectedSize);
+ Vp8BitWriter bitWriterPartZero = new(expectedSize, this.enc);
// Partition #0 with header and partition sizes.
uint size0 = this.GeneratePartition0(bitWriterPartZero);
@@ -676,9 +670,9 @@ internal class Vp8BitWriter : BitWriterBase
bool isVp8X,
uint width,
uint height,
- ExifProfile exifProfile,
- XmpProfile xmpProfile,
- byte[] iccProfileBytes,
+ ExifProfile? exifProfile,
+ XmpProfile? xmpProfile,
+ byte[]? iccProfileBytes,
bool hasAlpha,
Span alphaData,
bool alphaDataIsCompressed)
diff --git a/src/ImageSharp/Formats/Webp/BitWriter/Vp8LBitWriter.cs b/src/ImageSharp/Formats/Webp/BitWriter/Vp8LBitWriter.cs
index 42c1af8040..22bc195d64 100644
--- a/src/ImageSharp/Formats/Webp/BitWriter/Vp8LBitWriter.cs
+++ b/src/ImageSharp/Formats/Webp/BitWriter/Vp8LBitWriter.cs
@@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
-#nullable disable
using System.Buffers.Binary;
using SixLabors.ImageSharp.Formats.Webp.Lossless;
@@ -138,25 +137,25 @@ internal class Vp8LBitWriter : BitWriterBase
/// The width of the image.
/// The height of the image.
/// Flag indicating, if a alpha channel is present.
- public void WriteEncodedImageToStream(Stream stream, ExifProfile exifProfile, XmpProfile xmpProfile, IccProfile iccProfile, uint width, uint height, bool hasAlpha)
+ public void WriteEncodedImageToStream(Stream stream, ExifProfile? exifProfile, XmpProfile? xmpProfile, IccProfile? iccProfile, uint width, uint height, bool hasAlpha)
{
bool isVp8X = false;
- byte[] exifBytes = null;
- byte[] xmpBytes = null;
- byte[] iccBytes = null;
+ byte[]? exifBytes = null;
+ byte[]? xmpBytes = null;
+ byte[]? iccBytes = null;
uint riffSize = 0;
if (exifProfile != null)
{
isVp8X = true;
exifBytes = exifProfile.ToByteArray();
- riffSize += MetadataChunkSize(exifBytes);
+ riffSize += MetadataChunkSize(exifBytes!);
}
if (xmpProfile != null)
{
isVp8X = true;
xmpBytes = xmpProfile.Data;
- riffSize += MetadataChunkSize(xmpBytes);
+ riffSize += MetadataChunkSize(xmpBytes!);
}
if (iccProfile != null)
diff --git a/src/ImageSharp/Formats/Webp/Lossless/BackwardReferenceEncoder.cs b/src/ImageSharp/Formats/Webp/Lossless/BackwardReferenceEncoder.cs
index b3589b52c7..df19c26e0b 100644
--- a/src/ImageSharp/Formats/Webp/Lossless/BackwardReferenceEncoder.cs
+++ b/src/ImageSharp/Formats/Webp/Lossless/BackwardReferenceEncoder.cs
@@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
-#nullable disable
using System.Buffers;
using SixLabors.ImageSharp.Memory;
@@ -50,7 +49,7 @@ internal static class BackwardReferenceEncoder
int lz77TypeBest = 0;
double bitCostBest = -1;
int cacheBitsInitial = cacheBits;
- Vp8LHashChain hashChainBox = null;
+ Vp8LHashChain? hashChainBox = null;
var stats = new Vp8LStreaks();
var bitsEntropy = new Vp8LBitEntropy();
for (int lz77Type = 1; lz77TypesToTry > 0; lz77TypesToTry &= ~lz77Type, lz77Type <<= 1)
@@ -101,7 +100,7 @@ internal static class BackwardReferenceEncoder
// Improve on simple LZ77 but only for high quality (TraceBackwards is costly).
if ((lz77TypeBest == (int)Vp8LLz77Type.Lz77Standard || lz77TypeBest == (int)Vp8LLz77Type.Lz77Box) && quality >= 25)
{
- Vp8LHashChain hashChainTmp = lz77TypeBest == (int)Vp8LLz77Type.Lz77Standard ? hashChain : hashChainBox;
+ Vp8LHashChain hashChainTmp = lz77TypeBest == (int)Vp8LLz77Type.Lz77Standard ? hashChain : hashChainBox!;
BackwardReferencesTraceBackwards(width, height, memoryAllocator, bgra, cacheBits, hashChainTmp, best, worst);
var histo = new Vp8LHistogram(worst, cacheBits);
double bitCostTrace = histo.EstimateBits(stats, bitsEntropy);
@@ -140,8 +139,7 @@ internal static class BackwardReferenceEncoder
for (int i = 0; i <= WebpConstants.MaxColorCacheBits; i++)
{
histos[i] = new Vp8LHistogram(paletteCodeBits: i);
- colorCache[i] = new ColorCache();
- colorCache[i].Init(i);
+ colorCache[i] = new ColorCache(i);
}
// Find the cacheBits giving the lowest entropy.
@@ -274,11 +272,11 @@ internal static class BackwardReferenceEncoder
double offsetCost = -1;
int firstOffsetIsConstant = -1; // initialized with 'impossible' value.
int reach = 0;
- var colorCache = new ColorCache();
+ ColorCache? colorCache = null;
if (useColorCache)
{
- colorCache.Init(cacheBits);
+ colorCache = new ColorCache(cacheBits);
}
costModel.Build(xSize, cacheBits, refs);
@@ -375,12 +373,12 @@ internal static class BackwardReferenceEncoder
private static void BackwardReferencesHashChainFollowChosenPath(ReadOnlySpan bgra, int cacheBits, Span chosenPath, int chosenPathSize, Vp8LHashChain hashChain, Vp8LBackwardRefs backwardRefs)
{
bool useColorCache = cacheBits > 0;
- var colorCache = new ColorCache();
+ ColorCache? colorCache = null;
int i = 0;
if (useColorCache)
{
- colorCache.Init(cacheBits);
+ colorCache = new ColorCache(cacheBits);
}
backwardRefs.Refs.Clear();
@@ -396,7 +394,7 @@ internal static class BackwardReferenceEncoder
{
for (int k = 0; k < len; k++)
{
- colorCache.Insert(bgra[i + k]);
+ colorCache!.Insert(bgra[i + k]);
}
}
@@ -405,7 +403,7 @@ internal static class BackwardReferenceEncoder
else
{
PixOrCopy v;
- int idx = useColorCache ? colorCache.Contains(bgra[i]) : -1;
+ int idx = useColorCache ? colorCache!.Contains(bgra[i]) : -1;
if (idx >= 0)
{
// useColorCache is true and color cache contains bgra[i]
@@ -416,7 +414,7 @@ internal static class BackwardReferenceEncoder
{
if (useColorCache)
{
- colorCache.Insert(bgra[i]);
+ colorCache!.Insert(bgra[i]);
}
v = PixOrCopy.CreateLiteral(bgra[i]);
@@ -430,7 +428,7 @@ internal static class BackwardReferenceEncoder
private static void AddSingleLiteralWithCostModel(
ReadOnlySpan bgra,
- ColorCache colorCache,
+ ColorCache? colorCache,
CostModel costModel,
int idx,
bool useColorCache,
@@ -440,7 +438,7 @@ internal static class BackwardReferenceEncoder
{
double costVal = prevCost;
uint color = bgra[idx];
- int ix = useColorCache ? colorCache.Contains(color) : -1;
+ int ix = useColorCache ? colorCache!.Contains(color) : -1;
if (ix >= 0)
{
double mul0 = 0.68;
@@ -451,7 +449,7 @@ internal static class BackwardReferenceEncoder
double mul1 = 0.82;
if (useColorCache)
{
- colorCache.Insert(color);
+ colorCache!.Insert(color);
}
costVal += costModel.GetLiteralCost(color) * mul1;
@@ -469,10 +467,10 @@ internal static class BackwardReferenceEncoder
int iLastCheck = -1;
bool useColorCache = cacheBits > 0;
int pixCount = xSize * ySize;
- var colorCache = new ColorCache();
+ ColorCache? colorCache = null;
if (useColorCache)
{
- colorCache.Init(cacheBits);
+ colorCache = new ColorCache(cacheBits);
}
refs.Refs.Clear();
@@ -529,7 +527,7 @@ internal static class BackwardReferenceEncoder
{
for (j = i; j < i + len; j++)
{
- colorCache.Insert(bgra[j]);
+ colorCache!.Insert(bgra[j]);
}
}
}
@@ -725,11 +723,11 @@ internal static class BackwardReferenceEncoder
{
int pixelCount = xSize * ySize;
bool useColorCache = cacheBits > 0;
- var colorCache = new ColorCache();
+ ColorCache? colorCache = null;
if (useColorCache)
{
- colorCache.Init(cacheBits);
+ colorCache = new ColorCache(cacheBits);
}
refs.Refs.Clear();
@@ -757,7 +755,7 @@ internal static class BackwardReferenceEncoder
{
for (int k = 0; k < prevRowLen; ++k)
{
- colorCache.Insert(bgra[i + k]);
+ colorCache!.Insert(bgra[i + k]);
}
}
@@ -777,8 +775,7 @@ internal static class BackwardReferenceEncoder
private static void BackwardRefsWithLocalCache(ReadOnlySpan bgra, int cacheBits, Vp8LBackwardRefs refs)
{
int pixelIndex = 0;
- var colorCache = new ColorCache();
- colorCache.Init(cacheBits);
+ ColorCache colorCache = new(cacheBits);
for (int idx = 0; idx < refs.Refs.Count; idx++)
{
PixOrCopy v = refs.Refs[idx];
@@ -825,12 +822,12 @@ internal static class BackwardReferenceEncoder
}
}
- private static void AddSingleLiteral(uint pixel, bool useColorCache, ColorCache colorCache, Vp8LBackwardRefs refs)
+ private static void AddSingleLiteral(uint pixel, bool useColorCache, ColorCache? colorCache, Vp8LBackwardRefs refs)
{
PixOrCopy v;
if (useColorCache)
{
- int key = colorCache.GetIndex(pixel);
+ int key = colorCache!.GetIndex(pixel);
if (colorCache.Lookup(key) == pixel)
{
v = PixOrCopy.CreateCacheIdx(key);
diff --git a/src/ImageSharp/Formats/Webp/Lossless/ColorCache.cs b/src/ImageSharp/Formats/Webp/Lossless/ColorCache.cs
index 08ff60b69f..e683fb5605 100644
--- a/src/ImageSharp/Formats/Webp/Lossless/ColorCache.cs
+++ b/src/ImageSharp/Formats/Webp/Lossless/ColorCache.cs
@@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
-#nullable disable
using System.Runtime.CompilerServices;
@@ -14,31 +13,31 @@ internal class ColorCache
private const uint HashMul = 0x1e35a7bdu;
///
- /// Gets the color entries.
+ /// Initializes a new instance of the class.
///
- public uint[] Colors { get; private set; }
+ /// The hashBits determine the size of cache. It will be 1 left shifted by hashBits.
+ public ColorCache(int hashBits)
+ {
+ int hashSize = 1 << hashBits;
+ this.Colors = new uint[hashSize];
+ this.HashBits = hashBits;
+ this.HashShift = 32 - hashBits;
+ }
///
- /// Gets the hash shift: 32 - hashBits.
+ /// Gets the color entries.
///
- public int HashShift { get; private set; }
+ public uint[] Colors { get; }
///
- /// Gets the hash bits.
+ /// Gets the hash shift: 32 - hashBits.
///
- public int HashBits { get; private set; }
+ public int HashShift { get; }
///
- /// Initializes a new color cache.
+ /// Gets the hash bits.
///
- /// The hashBits determine the size of cache. It will be 1 left shifted by hashBits.
- public void Init(int hashBits)
- {
- int hashSize = 1 << hashBits;
- this.Colors = new uint[hashSize];
- this.HashBits = hashBits;
- this.HashShift = 32 - hashBits;
- }
+ public int HashBits { get; }
///
/// Inserts a new color into the cache.
diff --git a/src/ImageSharp/Formats/Webp/Lossless/CostInterval.cs b/src/ImageSharp/Formats/Webp/Lossless/CostInterval.cs
index a63c786049..0cc4a30fd7 100644
--- a/src/ImageSharp/Formats/Webp/Lossless/CostInterval.cs
+++ b/src/ImageSharp/Formats/Webp/Lossless/CostInterval.cs
@@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
-#nullable disable
using System.Diagnostics;
@@ -33,7 +32,7 @@ internal class CostInterval
public int Index { get; set; }
- public CostInterval Previous { get; set; }
+ public CostInterval? Previous { get; set; }
- public CostInterval Next { get; set; }
+ public CostInterval? Next { get; set; }
}
diff --git a/src/ImageSharp/Formats/Webp/Lossless/CostManager.cs b/src/ImageSharp/Formats/Webp/Lossless/CostManager.cs
index 7b9fdff247..e393c065ec 100644
--- a/src/ImageSharp/Formats/Webp/Lossless/CostManager.cs
+++ b/src/ImageSharp/Formats/Webp/Lossless/CostManager.cs
@@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
-#nullable disable
using System.Buffers;
using SixLabors.ImageSharp.Memory;
@@ -14,7 +13,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless;
///
internal sealed class CostManager : IDisposable
{
- private CostInterval head;
+ private CostInterval? head;
private const int FreeIntervalsStartCount = 25;
@@ -103,10 +102,10 @@ internal sealed class CostManager : IDisposable
/// If 'doCleanIntervals' is true, intervals that end before 'i' will be popped.
public void UpdateCostAtIndex(int i, bool doCleanIntervals)
{
- CostInterval current = this.head;
+ CostInterval? current = this.head;
while (current != null && current.Start <= i)
{
- CostInterval next = current.Next;
+ CostInterval? next = current.Next;
if (current.End <= i)
{
if (doCleanIntervals)
@@ -155,7 +154,7 @@ internal sealed class CostManager : IDisposable
return;
}
- CostInterval interval = this.head;
+ CostInterval? interval = this.head;
for (int i = 0; i < this.CacheIntervalsSize && this.CacheIntervals[i].Start < len; i++)
{
// Define the intersection of the ith interval with the new one.
@@ -163,7 +162,7 @@ internal sealed class CostManager : IDisposable
int end = position + (this.CacheIntervals[i].End > len ? len : this.CacheIntervals[i].End);
float cost = (float)(distanceCost + this.CacheIntervals[i].Cost);
- CostInterval intervalNext;
+ CostInterval? intervalNext;
for (; interval != null && interval.Start < end; interval = intervalNext)
{
intervalNext = interval.Next;
@@ -225,7 +224,7 @@ internal sealed class CostManager : IDisposable
/// Pop an interval from the manager.
///
/// The interval to remove.
- private void PopInterval(CostInterval interval)
+ private void PopInterval(CostInterval? interval)
{
if (interval == null)
{
@@ -240,7 +239,7 @@ internal sealed class CostManager : IDisposable
this.freeIntervals.Push(interval);
}
- private void InsertInterval(CostInterval intervalIn, float cost, int position, int start, int end)
+ private void InsertInterval(CostInterval? intervalIn, float cost, int position, int start, int end)
{
if (start >= end)
{
@@ -271,7 +270,7 @@ internal sealed class CostManager : IDisposable
/// it was orphaned (which can be NULL), set it at the right place in the list
/// of intervals using the start_ ordering and the previous interval as a hint.
///
- private void PositionOrphanInterval(CostInterval current, CostInterval previous)
+ private void PositionOrphanInterval(CostInterval current, CostInterval? previous)
{
previous ??= this.head;
@@ -292,7 +291,7 @@ internal sealed class CostManager : IDisposable
///
/// Given two intervals, make 'prev' be the previous one of 'next' in 'manager'.
///
- private void ConnectIntervals(CostInterval prev, CostInterval next)
+ private void ConnectIntervals(CostInterval? prev, CostInterval? next)
{
if (prev != null)
{
diff --git a/src/ImageSharp/Formats/Webp/Lossless/WebpLosslessDecoder.cs b/src/ImageSharp/Formats/Webp/Lossless/WebpLosslessDecoder.cs
index abfb67bc7e..84ddd4b785 100644
--- a/src/ImageSharp/Formats/Webp/Lossless/WebpLosslessDecoder.cs
+++ b/src/ImageSharp/Formats/Webp/Lossless/WebpLosslessDecoder.cs
@@ -158,10 +158,9 @@ internal sealed class WebpLosslessDecoder
// Finish setting up the color-cache.
if (isColorCachePresent)
{
- decoder.Metadata.ColorCache = new ColorCache();
+ decoder.Metadata.ColorCache = new ColorCache(colorCacheBits);
colorCacheSize = 1 << colorCacheBits;
decoder.Metadata.ColorCacheSize = colorCacheSize;
- decoder.Metadata.ColorCache.Init(colorCacheBits);
}
else
{
diff --git a/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs b/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs
index a5d1900b2a..16b4c827ef 100644
--- a/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs
+++ b/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs
@@ -348,12 +348,18 @@ internal class Vp8Encoder : IDisposable
// Extract and encode alpha channel data, if present.
int alphaDataSize = 0;
bool alphaCompressionSucceeded = false;
- using AlphaEncoder alphaEncoder = new();
Span alphaData = Span.Empty;
if (hasAlpha)
{
// TODO: This can potentially run in an separate task.
- IMemoryOwner encodedAlphaData = alphaEncoder.EncodeAlpha(image, this.configuration, this.memoryAllocator, this.skipMetadata, this.alphaCompression, out alphaDataSize);
+ using IMemoryOwner encodedAlphaData = AlphaEncoder.EncodeAlpha(
+ image,
+ this.configuration,
+ this.memoryAllocator,
+ this.skipMetadata,
+ this.alphaCompression,
+ out alphaDataSize);
+
alphaData = encodedAlphaData.GetSpan();
if (alphaDataSize < pixelCount)
{
diff --git a/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs b/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs
index 4c1c60591d..abaa85ef18 100644
--- a/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs
+++ b/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs
@@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
-#nullable disable
using System.Buffers;
using System.Runtime.CompilerServices;
@@ -46,17 +45,17 @@ internal class WebpAnimationDecoder : IDisposable
///
/// The abstract metadata.
///
- private ImageMetadata metadata;
+ private ImageMetadata? metadata;
///
/// The gif specific metadata.
///
- private WebpMetadata webpMetadata;
+ private WebpMetadata? webpMetadata;
///
/// The alpha data, if an ALPH chunk is present.
///
- private IMemoryOwner alphaData;
+ private IMemoryOwner? alphaData;
///
/// Initializes a new instance of the class.
@@ -83,8 +82,8 @@ internal class WebpAnimationDecoder : IDisposable
public Image Decode(BufferedReadStream stream, WebpFeatures features, uint width, uint height, uint completeDataSize)
where TPixel : unmanaged, IPixel
{
- Image image = null;
- ImageFrame previousFrame = null;
+ Image? image = null;
+ ImageFrame? previousFrame = null;
this.metadata = new ImageMetadata();
this.webpMetadata = this.metadata.GetWebpMetadata();
@@ -99,12 +98,12 @@ internal class WebpAnimationDecoder : IDisposable
switch (chunkType)
{
case WebpChunkType.Animation:
- uint dataSize = this.ReadFrame(stream, ref image, ref previousFrame, width, height, features.AnimationBackgroundColor.Value);
+ uint dataSize = this.ReadFrame(stream, ref image, ref previousFrame, width, height, features.AnimationBackgroundColor!.Value);
remainingBytes -= (int)dataSize;
break;
case WebpChunkType.Xmp:
case WebpChunkType.Exif:
- WebpChunkParsingUtils.ParseOptionalChunks(stream, chunkType, image.Metadata, false, this.buffer);
+ WebpChunkParsingUtils.ParseOptionalChunks(stream, chunkType, image!.Metadata, false, this.buffer);
break;
default:
WebpThrowHelper.ThrowImageFormatException("Read unexpected webp chunk data");
@@ -117,7 +116,7 @@ internal class WebpAnimationDecoder : IDisposable
}
}
- return image;
+ return image!;
}
///
@@ -130,7 +129,7 @@ internal class WebpAnimationDecoder : IDisposable
/// The width of the image.
/// The height of the image.
/// The default background color of the canvas in.
- private uint ReadFrame(BufferedReadStream stream, ref Image image, ref ImageFrame previousFrame, uint width, uint height, Color backgroundColor)
+ private uint ReadFrame(BufferedReadStream stream, ref Image? image, ref ImageFrame? previousFrame, uint width, uint height, Color backgroundColor)
where TPixel : unmanaged, IPixel
{
AnimationFrameData frameData = this.ReadFrameHeader(stream);
@@ -146,7 +145,7 @@ internal class WebpAnimationDecoder : IDisposable
chunkType = WebpChunkParsingUtils.ReadChunkType(stream, this.buffer);
}
- WebpImageInfo webpInfo = null;
+ WebpImageInfo? webpInfo = null;
WebpFeatures features = new();
switch (chunkType)
{
@@ -163,7 +162,7 @@ internal class WebpAnimationDecoder : IDisposable
break;
}
- ImageFrame currentFrame = null;
+ ImageFrame? currentFrame = null;
ImageFrame imageFrame;
if (previousFrame is null)
{
@@ -175,7 +174,7 @@ internal class WebpAnimationDecoder : IDisposable
}
else
{
- currentFrame = image.Frames.AddFrame(previousFrame); // This clones the frame and adds it the collection.
+ currentFrame = image!.Frames.AddFrame(previousFrame); // This clones the frame and adds it the collection.
SetFrameMetadata(currentFrame.Metadata, frameData.Duration);
diff --git a/src/ImageSharp/Formats/Webp/WebpChunkParsingUtils.cs b/src/ImageSharp/Formats/Webp/WebpChunkParsingUtils.cs
index bfe415a6e4..e4acdf311c 100644
--- a/src/ImageSharp/Formats/Webp/WebpChunkParsingUtils.cs
+++ b/src/ImageSharp/Formats/Webp/WebpChunkParsingUtils.cs
@@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
-#nullable disable
using System.Buffers.Binary;
using SixLabors.ImageSharp.Formats.Webp.BitReader;
diff --git a/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs b/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs
index 29be86e22f..181f01632f 100644
--- a/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs
+++ b/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs
@@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
-#nullable disable
using System.Buffers;
using System.Buffers.Binary;
@@ -9,9 +8,7 @@ using SixLabors.ImageSharp.Formats.Webp.Lossy;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
-using SixLabors.ImageSharp.Metadata.Profiles.Exif;
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
-using SixLabors.ImageSharp.Metadata.Profiles.Xmp;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Webp;
@@ -41,35 +38,20 @@ internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable
///
private readonly uint maxFrames;
- ///
- /// Gets the decoded by this decoder instance.
- ///
- private ImageMetadata metadata;
-
///
/// Gets or sets the alpha data, if an ALPH chunk is present.
///
- private IMemoryOwner alphaData;
+ private IMemoryOwner? alphaData;
///
/// Used for allocating memory during the decoding operations.
///
private readonly MemoryAllocator memoryAllocator;
- ///
- /// The stream to decode from.
- ///
- private BufferedReadStream currentStream;
-
- ///
- /// The webp specific metadata.
- ///
- private WebpMetadata webpMetadata;
-
///
/// Information about the webp image.
///
- private WebpImageInfo webImageInfo;
+ private WebpImageInfo? webImageInfo;
///
/// Initializes a new instance of the class.
@@ -88,25 +70,24 @@ internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable
public DecoderOptions Options { get; }
///
- public Size Dimensions => new((int)this.webImageInfo.Width, (int)this.webImageInfo.Height);
+ public Size Dimensions => new((int)this.webImageInfo!.Width, (int)this.webImageInfo.Height);
///
public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel
{
- Image image = null;
+ Image? image = null;
try
{
- this.metadata = new ImageMetadata();
- this.currentStream = stream;
+ ImageMetadata metadata = new();
- uint fileSize = this.ReadImageHeader();
+ uint fileSize = this.ReadImageHeader(stream);
- using (this.webImageInfo = this.ReadVp8Info())
+ using (this.webImageInfo = this.ReadVp8Info(stream, metadata))
{
if (this.webImageInfo.Features is { Animation: true })
{
- using var animationDecoder = new WebpAnimationDecoder(this.memoryAllocator, this.configuration, this.maxFrames);
+ using WebpAnimationDecoder animationDecoder = new(this.memoryAllocator, this.configuration, this.maxFrames);
return animationDecoder.Decode(stream, this.webImageInfo.Features, this.webImageInfo.Width, this.webImageInfo.Height, fileSize);
}
@@ -115,23 +96,23 @@ internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable
WebpThrowHelper.ThrowNotSupportedException("Animations are not supported");
}
- image = new Image(this.configuration, (int)this.webImageInfo.Width, (int)this.webImageInfo.Height, this.metadata);
+ image = new Image(this.configuration, (int)this.webImageInfo.Width, (int)this.webImageInfo.Height, metadata);
Buffer2D pixels = image.GetRootFramePixelBuffer();
if (this.webImageInfo.IsLossless)
{
- var losslessDecoder = new WebpLosslessDecoder(this.webImageInfo.Vp8LBitReader, this.memoryAllocator, this.configuration);
+ WebpLosslessDecoder losslessDecoder = new(this.webImageInfo.Vp8LBitReader, this.memoryAllocator, this.configuration);
losslessDecoder.Decode(pixels, image.Width, image.Height);
}
else
{
- var lossyDecoder = new WebpLossyDecoder(this.webImageInfo.Vp8BitReader, this.memoryAllocator, this.configuration);
+ WebpLossyDecoder lossyDecoder = new(this.webImageInfo.Vp8BitReader, this.memoryAllocator, this.configuration);
lossyDecoder.Decode(pixels, image.Width, image.Height, this.webImageInfo, this.alphaData);
}
// There can be optional chunks after the image data, like EXIF and XMP.
if (this.webImageInfo.Features != null)
{
- this.ParseOptionalChunks(this.webImageInfo.Features);
+ this.ParseOptionalChunks(stream, metadata, this.webImageInfo.Features);
}
return image;
@@ -147,31 +128,32 @@ internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable
///
public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken)
{
- this.currentStream = stream;
+ this.ReadImageHeader(stream);
- this.ReadImageHeader();
- using (this.webImageInfo = this.ReadVp8Info(true))
+ ImageMetadata metadata = new();
+ using (this.webImageInfo = this.ReadVp8Info(stream, metadata, true))
{
- return new ImageInfo(new PixelTypeInfo((int)this.webImageInfo.BitsPerPixel), (int)this.webImageInfo.Width, (int)this.webImageInfo.Height, this.metadata);
+ return new ImageInfo(new PixelTypeInfo((int)this.webImageInfo.BitsPerPixel), (int)this.webImageInfo.Width, (int)this.webImageInfo.Height, metadata);
}
}
///
/// Reads and skips over the image header.
///
+ /// The stream to decode from.
/// The file size in bytes.
- private uint ReadImageHeader()
+ private uint ReadImageHeader(BufferedReadStream stream)
{
// Skip FourCC header, we already know its a RIFF file at this point.
- this.currentStream.Skip(4);
+ stream.Skip(4);
// Read file size.
// The size of the file in bytes starting at offset 8.
// The file size in the header is the total size of the chunks that follow plus 4 bytes for the ‘WEBP’ FourCC.
- uint fileSize = WebpChunkParsingUtils.ReadChunkSize(this.currentStream, this.buffer);
+ uint fileSize = WebpChunkParsingUtils.ReadChunkSize(stream, this.buffer);
// Skip 'WEBP' from the header.
- this.currentStream.Skip(4);
+ stream.Skip(4);
return fileSize;
}
@@ -179,42 +161,43 @@ internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable
///
/// Reads information present in the image header, about the image content and how to decode the image.
///
+ /// The stream to decode from.
+ /// The image metadata.
/// For identify, the alpha data should not be read.
/// Information about the webp image.
- private WebpImageInfo ReadVp8Info(bool ignoreAlpha = false)
+ private WebpImageInfo ReadVp8Info(BufferedReadStream stream, ImageMetadata metadata, bool ignoreAlpha = false)
{
- this.metadata = new ImageMetadata();
- this.webpMetadata = this.metadata.GetFormatMetadata(WebpFormat.Instance);
+ WebpMetadata webpMetadata = metadata.GetFormatMetadata(WebpFormat.Instance);
- WebpChunkType chunkType = WebpChunkParsingUtils.ReadChunkType(this.currentStream, this.buffer);
+ WebpChunkType chunkType = WebpChunkParsingUtils.ReadChunkType(stream, this.buffer);
- var features = new WebpFeatures();
+ WebpFeatures features = new();
switch (chunkType)
{
case WebpChunkType.Vp8:
- this.webpMetadata.FileFormat = WebpFileFormatType.Lossy;
- return WebpChunkParsingUtils.ReadVp8Header(this.memoryAllocator, this.currentStream, this.buffer, features);
+ webpMetadata.FileFormat = WebpFileFormatType.Lossy;
+ return WebpChunkParsingUtils.ReadVp8Header(this.memoryAllocator, stream, this.buffer, features);
case WebpChunkType.Vp8L:
- this.webpMetadata.FileFormat = WebpFileFormatType.Lossless;
- return WebpChunkParsingUtils.ReadVp8LHeader(this.memoryAllocator, this.currentStream, this.buffer, features);
+ webpMetadata.FileFormat = WebpFileFormatType.Lossless;
+ return WebpChunkParsingUtils.ReadVp8LHeader(this.memoryAllocator, stream, this.buffer, features);
case WebpChunkType.Vp8X:
- WebpImageInfo webpInfos = WebpChunkParsingUtils.ReadVp8XHeader(this.currentStream, this.buffer, features);
- while (this.currentStream.Position < this.currentStream.Length)
+ WebpImageInfo webpInfos = WebpChunkParsingUtils.ReadVp8XHeader(stream, this.buffer, features);
+ while (stream.Position < stream.Length)
{
- chunkType = WebpChunkParsingUtils.ReadChunkType(this.currentStream, this.buffer);
+ chunkType = WebpChunkParsingUtils.ReadChunkType(stream, this.buffer);
if (chunkType == WebpChunkType.Vp8)
{
- this.webpMetadata.FileFormat = WebpFileFormatType.Lossy;
- webpInfos = WebpChunkParsingUtils.ReadVp8Header(this.memoryAllocator, this.currentStream, this.buffer, features);
+ webpMetadata.FileFormat = WebpFileFormatType.Lossy;
+ webpInfos = WebpChunkParsingUtils.ReadVp8Header(this.memoryAllocator, stream, this.buffer, features);
}
else if (chunkType == WebpChunkType.Vp8L)
{
- this.webpMetadata.FileFormat = WebpFileFormatType.Lossless;
- webpInfos = WebpChunkParsingUtils.ReadVp8LHeader(this.memoryAllocator, this.currentStream, this.buffer, features);
+ webpMetadata.FileFormat = WebpFileFormatType.Lossless;
+ webpInfos = WebpChunkParsingUtils.ReadVp8LHeader(this.memoryAllocator, stream, this.buffer, features);
}
else if (WebpChunkParsingUtils.IsOptionalVp8XChunk(chunkType))
{
- bool isAnimationChunk = this.ParseOptionalExtendedChunks(chunkType, features, ignoreAlpha);
+ bool isAnimationChunk = this.ParseOptionalExtendedChunks(stream, metadata, chunkType, features, ignoreAlpha);
if (isAnimationChunk)
{
return webpInfos;
@@ -223,8 +206,8 @@ internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable
else
{
// Ignore unknown chunks.
- uint chunkSize = this.ReadChunkSize();
- this.currentStream.Skip((int)chunkSize);
+ uint chunkSize = this.ReadChunkSize(stream);
+ stream.Skip((int)chunkSize);
}
}
@@ -239,32 +222,39 @@ internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable
///
/// Parses optional VP8X chunks, which can be ICCP, XMP, ANIM or ALPH chunks.
///
+ /// The stream to decode from.
+ /// The image metadata.
/// The chunk type.
/// The webp image features.
/// For identify, the alpha data should not be read.
/// true, if its a alpha chunk.
- private bool ParseOptionalExtendedChunks(WebpChunkType chunkType, WebpFeatures features, bool ignoreAlpha)
+ private bool ParseOptionalExtendedChunks(
+ BufferedReadStream stream,
+ ImageMetadata metadata,
+ WebpChunkType chunkType,
+ WebpFeatures features,
+ bool ignoreAlpha)
{
switch (chunkType)
{
case WebpChunkType.Iccp:
- this.ReadIccProfile();
+ this.ReadIccProfile(stream, metadata);
break;
case WebpChunkType.Exif:
- this.ReadExifProfile();
+ this.ReadExifProfile(stream, metadata);
break;
case WebpChunkType.Xmp:
- this.ReadXmpProfile();
+ this.ReadXmpProfile(stream, metadata);
break;
case WebpChunkType.AnimationParameter:
- this.ReadAnimationParameters(features);
+ this.ReadAnimationParameters(stream, features);
return true;
case WebpChunkType.Alpha:
- this.ReadAlphaData(features, ignoreAlpha);
+ this.ReadAlphaData(stream, features, ignoreAlpha);
break;
default:
WebpThrowHelper.ThrowImageFormatException("Unexpected chunk followed VP8X header");
@@ -277,32 +267,34 @@ internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable
///
/// Reads the optional metadata EXIF of XMP profiles, which can follow the image data.
///
+ /// The stream to decode from.
+ /// The image metadata.
/// The webp features.
- private void ParseOptionalChunks(WebpFeatures features)
+ private void ParseOptionalChunks(BufferedReadStream stream, ImageMetadata metadata, WebpFeatures features)
{
if (this.skipMetadata || (!features.ExifProfile && !features.XmpMetaData))
{
return;
}
- long streamLength = this.currentStream.Length;
- while (this.currentStream.Position < streamLength)
+ long streamLength = stream.Length;
+ while (stream.Position < streamLength)
{
// Read chunk header.
- WebpChunkType chunkType = this.ReadChunkType();
- if (chunkType == WebpChunkType.Exif && this.metadata.ExifProfile == null)
+ WebpChunkType chunkType = this.ReadChunkType(stream);
+ if (chunkType == WebpChunkType.Exif && metadata.ExifProfile == null)
{
- this.ReadExifProfile();
+ this.ReadExifProfile(stream, metadata);
}
- else if (chunkType == WebpChunkType.Xmp && this.metadata.XmpProfile == null)
+ else if (chunkType == WebpChunkType.Xmp && metadata.XmpProfile == null)
{
- this.ReadXmpProfile();
+ this.ReadXmpProfile(stream, metadata);
}
else
{
// Skip duplicate XMP or EXIF chunk.
- uint chunkLength = this.ReadChunkSize();
- this.currentStream.Skip((int)chunkLength);
+ uint chunkLength = this.ReadChunkSize(stream);
+ stream.Skip((int)chunkLength);
}
}
}
@@ -310,76 +302,80 @@ internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable
///
/// Reads the EXIF profile from the stream.
///
- private void ReadExifProfile()
+ /// The stream to decode from.
+ /// The image metadata.
+ private void ReadExifProfile(BufferedReadStream stream, ImageMetadata metadata)
{
- uint exifChunkSize = this.ReadChunkSize();
+ uint exifChunkSize = this.ReadChunkSize(stream);
if (this.skipMetadata)
{
- this.currentStream.Skip((int)exifChunkSize);
+ stream.Skip((int)exifChunkSize);
}
else
{
byte[] exifData = new byte[exifChunkSize];
- int bytesRead = this.currentStream.Read(exifData, 0, (int)exifChunkSize);
+ int bytesRead = stream.Read(exifData, 0, (int)exifChunkSize);
if (bytesRead != exifChunkSize)
{
// Ignore invalid chunk.
return;
}
- var profile = new ExifProfile(exifData);
- this.metadata.ExifProfile = profile;
+ metadata.ExifProfile = new(exifData);
}
}
///
/// Reads the XMP profile the stream.
///
- private void ReadXmpProfile()
+ /// The stream to decode from.
+ /// The image metadata.
+ private void ReadXmpProfile(BufferedReadStream stream, ImageMetadata metadata)
{
- uint xmpChunkSize = this.ReadChunkSize();
+ uint xmpChunkSize = this.ReadChunkSize(stream);
if (this.skipMetadata)
{
- this.currentStream.Skip((int)xmpChunkSize);
+ stream.Skip((int)xmpChunkSize);
}
else
{
byte[] xmpData = new byte[xmpChunkSize];
- int bytesRead = this.currentStream.Read(xmpData, 0, (int)xmpChunkSize);
+ int bytesRead = stream.Read(xmpData, 0, (int)xmpChunkSize);
if (bytesRead != xmpChunkSize)
{
// Ignore invalid chunk.
return;
}
- var profile = new XmpProfile(xmpData);
- this.metadata.XmpProfile = profile;
+ metadata.XmpProfile = new(xmpData);
}
}
///
/// Reads the ICCP chunk from the stream.
///
- private void ReadIccProfile()
+ /// The stream to decode from.
+ /// The image metadata.
+ private void ReadIccProfile(BufferedReadStream stream, ImageMetadata metadata)
{
- uint iccpChunkSize = this.ReadChunkSize();
+ uint iccpChunkSize = this.ReadChunkSize(stream);
if (this.skipMetadata)
{
- this.currentStream.Skip((int)iccpChunkSize);
+ stream.Skip((int)iccpChunkSize);
}
else
{
byte[] iccpData = new byte[iccpChunkSize];
- int bytesRead = this.currentStream.Read(iccpData, 0, (int)iccpChunkSize);
+ int bytesRead = stream.Read(iccpData, 0, (int)iccpChunkSize);
if (bytesRead != iccpChunkSize)
{
WebpThrowHelper.ThrowInvalidImageContentException("Not enough data to read the iccp chunk");
}
- var profile = new IccProfile(iccpData);
+ IccProfile profile = new(iccpData);
if (profile.CheckIsValid())
{
- this.metadata.IccProfile = profile;
+ metadata.IccProfile = profile;
}
}
}
@@ -387,17 +383,18 @@ internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable
///
/// Reads the animation parameters chunk from the stream.
///
+ /// The stream to decode from.
/// The webp features.
- private void ReadAnimationParameters(WebpFeatures features)
+ private void ReadAnimationParameters(BufferedReadStream stream, WebpFeatures features)
{
features.Animation = true;
- uint animationChunkSize = WebpChunkParsingUtils.ReadChunkSize(this.currentStream, this.buffer);
- byte blue = (byte)this.currentStream.ReadByte();
- byte green = (byte)this.currentStream.ReadByte();
- byte red = (byte)this.currentStream.ReadByte();
- byte alpha = (byte)this.currentStream.ReadByte();
+ uint animationChunkSize = WebpChunkParsingUtils.ReadChunkSize(stream, this.buffer);
+ byte blue = (byte)stream.ReadByte();
+ byte green = (byte)stream.ReadByte();
+ byte red = (byte)stream.ReadByte();
+ byte alpha = (byte)stream.ReadByte();
features.AnimationBackgroundColor = new Color(new Rgba32(red, green, blue, alpha));
- int bytesRead = this.currentStream.Read(this.buffer, 0, 2);
+ int bytesRead = stream.Read(this.buffer, 0, 2);
if (bytesRead != 2)
{
WebpThrowHelper.ThrowInvalidImageContentException("Not enough data to read the animation loop count");
@@ -409,22 +406,23 @@ internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable
///
/// Reads the alpha data chunk data from the stream.
///
+ /// The stream to decode from.
/// The features.
/// if set to true, skips the chunk data.
- private void ReadAlphaData(WebpFeatures features, bool ignoreAlpha)
+ private void ReadAlphaData(BufferedReadStream stream, WebpFeatures features, bool ignoreAlpha)
{
- uint alphaChunkSize = WebpChunkParsingUtils.ReadChunkSize(this.currentStream, this.buffer);
+ uint alphaChunkSize = WebpChunkParsingUtils.ReadChunkSize(stream, this.buffer);
if (ignoreAlpha)
{
- this.currentStream.Skip((int)alphaChunkSize);
+ stream.Skip((int)alphaChunkSize);
return;
}
- features.AlphaChunkHeader = (byte)this.currentStream.ReadByte();
+ features.AlphaChunkHeader = (byte)stream.ReadByte();
int alphaDataSize = (int)(alphaChunkSize - 1);
this.alphaData = this.memoryAllocator.Allocate(alphaDataSize);
Span alphaData = this.alphaData.GetSpan();
- int bytesRead = this.currentStream.Read(alphaData, 0, alphaDataSize);
+ int bytesRead = stream.Read(alphaData, 0, alphaDataSize);
if (bytesRead != alphaDataSize)
{
WebpThrowHelper.ThrowInvalidImageContentException("Not enough data to read the alpha data from the stream");
@@ -434,15 +432,15 @@ internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable
///
/// Identifies the chunk type from the chunk.
///
+ /// The stream to decode from.
///
/// Thrown if the input stream is not valid.
///
- private WebpChunkType ReadChunkType()
+ private WebpChunkType ReadChunkType(BufferedReadStream stream)
{
- if (this.currentStream.Read(this.buffer, 0, 4) == 4)
+ if (stream.Read(this.buffer, 0, 4) == 4)
{
- var chunkType = (WebpChunkType)BinaryPrimitives.ReadUInt32BigEndian(this.buffer);
- return chunkType;
+ return (WebpChunkType)BinaryPrimitives.ReadUInt32BigEndian(this.buffer);
}
throw new ImageFormatException("Invalid Webp data.");
@@ -452,10 +450,12 @@ internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable
/// Reads the chunk size. If Chunk Size is odd, a single padding byte will be added to the payload,
/// so the chunk size will be increased by 1 in those cases.
///
+ /// The stream to decode from.
/// The chunk size in bytes.
- private uint ReadChunkSize()
+ /// Invalid data.
+ private uint ReadChunkSize(BufferedReadStream stream)
{
- if (this.currentStream.Read(this.buffer, 0, 4) == 4)
+ if (stream.Read(this.buffer, 0, 4) == 4)
{
uint chunkSize = BinaryPrimitives.ReadUInt32LittleEndian(this.buffer);
return (chunkSize % 2 == 0) ? chunkSize : chunkSize + 1;
diff --git a/src/ImageSharp/Formats/Webp/WebpEncoder.cs b/src/ImageSharp/Formats/Webp/WebpEncoder.cs
index e314d38017..bd8303f1c8 100644
--- a/src/ImageSharp/Formats/Webp/WebpEncoder.cs
+++ b/src/ImageSharp/Formats/Webp/WebpEncoder.cs
@@ -82,7 +82,7 @@ public sealed class WebpEncoder : ImageEncoder
///
protected override void Encode(Image image, Stream stream, CancellationToken cancellationToken)
{
- WebpEncoderCore encoder = new(this, image.GetMemoryAllocator());
+ WebpEncoderCore encoder = new(this, image.GetConfiguration());
encoder.Encode(image, stream, cancellationToken);
}
}
diff --git a/src/ImageSharp/Formats/Webp/WebpEncoderCore.cs b/src/ImageSharp/Formats/Webp/WebpEncoderCore.cs
index 68951e5ec0..33189ba845 100644
--- a/src/ImageSharp/Formats/Webp/WebpEncoderCore.cs
+++ b/src/ImageSharp/Formats/Webp/WebpEncoderCore.cs
@@ -1,8 +1,6 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
-#nullable disable
-using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats.Webp.Lossless;
using SixLabors.ImageSharp.Formats.Webp.Lossy;
using SixLabors.ImageSharp.Memory;
@@ -81,16 +79,17 @@ internal sealed class WebpEncoderCore : IImageEncoderInternals
///
/// The global configuration.
///
- private Configuration configuration;
+ private readonly Configuration configuration;
///
/// Initializes a new instance of the class.
///
/// The encoder with options.
- /// The memory manager.
- public WebpEncoderCore(WebpEncoder encoder, MemoryAllocator memoryAllocator)
+ /// The global configuration.
+ public WebpEncoderCore(WebpEncoder encoder, Configuration configuration)
{
- this.memoryAllocator = memoryAllocator;
+ this.configuration = configuration;
+ this.memoryAllocator = configuration.MemoryAllocator;
this.alphaCompression = encoder.UseAlphaCompression;
this.fileFormat = encoder.FileFormat;
this.quality = encoder.Quality;
@@ -117,7 +116,6 @@ internal sealed class WebpEncoderCore : IImageEncoderInternals
Guard.NotNull(image, nameof(image));
Guard.NotNull(stream, nameof(stream));
- this.configuration = image.GetConfiguration();
bool lossless;
if (this.fileFormat is not null)
{
diff --git a/src/ImageSharp/Formats/Webp/WebpImageInfo.cs b/src/ImageSharp/Formats/Webp/WebpImageInfo.cs
index c41403211f..5f7301b262 100644
--- a/src/ImageSharp/Formats/Webp/WebpImageInfo.cs
+++ b/src/ImageSharp/Formats/Webp/WebpImageInfo.cs
@@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
-#nullable disable
using SixLabors.ImageSharp.Formats.Webp.BitReader;
using SixLabors.ImageSharp.Formats.Webp.Lossy;
@@ -36,7 +35,7 @@ internal class WebpImageInfo : IDisposable
///
/// Gets or sets additional features present in a VP8X image.
///
- public WebpFeatures Features { get; set; }
+ public WebpFeatures? Features { get; set; }
///
/// Gets or sets the VP8 profile / version. Valid values are between 0 and 3. Default value will be the invalid value -1.
@@ -46,17 +45,17 @@ internal class WebpImageInfo : IDisposable
///
/// Gets or sets the VP8 frame header.
///
- public Vp8FrameHeader Vp8FrameHeader { get; set; }
+ public Vp8FrameHeader? Vp8FrameHeader { get; set; }
///
/// Gets or sets the VP8L bitreader. Will be , if its not a lossless image.
///
- public Vp8LBitReader Vp8LBitReader { get; set; }
+ public Vp8LBitReader? Vp8LBitReader { get; set; }
///
/// Gets or sets the VP8 bitreader. Will be , if its not a lossy image.
///
- public Vp8BitReader Vp8BitReader { get; set; }
+ public Vp8BitReader? Vp8BitReader { get; set; }
///
public void Dispose()
diff --git a/src/ImageSharp/Formats/Webp/WebpThrowHelper.cs b/src/ImageSharp/Formats/Webp/WebpThrowHelper.cs
index 944cefa745..c633c52738 100644
--- a/src/ImageSharp/Formats/Webp/WebpThrowHelper.cs
+++ b/src/ImageSharp/Formats/Webp/WebpThrowHelper.cs
@@ -1,15 +1,21 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
+using System.Diagnostics.CodeAnalysis;
+
namespace SixLabors.ImageSharp.Formats.Webp;
internal static class WebpThrowHelper
{
+ [DoesNotReturn]
public static void ThrowInvalidImageContentException(string errorMessage) => throw new InvalidImageContentException(errorMessage);
+ [DoesNotReturn]
public static void ThrowImageFormatException(string errorMessage) => throw new ImageFormatException(errorMessage);
+ [DoesNotReturn]
public static void ThrowNotSupportedException(string errorMessage) => throw new NotSupportedException(errorMessage);
+ [DoesNotReturn]
public static void ThrowInvalidImageDimensions(string errorMessage) => throw new InvalidImageContentException(errorMessage);
}