Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -64,18 +64,19 @@
# Set explicit file behavior to:
# treat as text
# normalize to Unix-style line endings and
# use a union merge when resoling conflicts
# use a union merge when resolving conflicts
###############################################################################
*.csproj text eol=lf merge=union
*.dbproj text eol=lf merge=union
*.fsproj text eol=lf merge=union
*.ncrunchproject text eol=lf merge=union
*.vbproj text eol=lf merge=union
*.shproj text eol=lf merge=union
###############################################################################
# Set explicit file behavior to:
# treat as text
# normalize to Windows-style line endings and
# use a union merge when resoling conflicts
# use a union merge when resolving conflicts
###############################################################################
*.sln text eol=crlf merge=union
###############################################################################
Expand Down
68 changes: 31 additions & 37 deletions src/ImageSharp/Formats/Webp/AlphaDecoder.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -38,7 +38,7 @@ public AlphaDecoder(int width, int height, IMemoryOwner<byte> data, byte alphaCh
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");
Expand All @@ -59,7 +59,7 @@ public AlphaDecoder(int width, int height, IMemoryOwner<byte> data, byte alphaCh

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);

Expand Down Expand Up @@ -110,6 +110,7 @@ public AlphaDecoder(int width, int height, IMemoryOwner<byte> data, byte alphaCh
/// <summary>
/// Gets a value indicating whether the alpha channel uses compression.
/// </summary>
[MemberNotNullWhen(true, nameof(LosslessDecoder))]
private bool Compressed { get; }

/// <summary>
Expand All @@ -120,7 +121,7 @@ public AlphaDecoder(int width, int height, IMemoryOwner<byte> data, byte alphaCh
/// <summary>
/// Gets the Vp8L decoder which is used to de compress the alpha channel, if needed.
/// </summary>
private WebpLosslessDecoder LosslessDecoder { get; }
private WebpLosslessDecoder? LosslessDecoder { get; }

/// <summary>
/// Gets a value indicating whether the decoding needs 1 byte per pixel for decoding.
Expand Down Expand Up @@ -173,17 +174,14 @@ public void Decode()
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);
}
}

Expand Down Expand Up @@ -261,8 +259,7 @@ private void ExtractAlphaRows(Vp8LDecoder dec)
{
int numRowsToProcess = dec.Height;
int width = dec.Width;
Span<uint> pixels = dec.Pixels.Memory.Span;
Span<uint> input = pixels;
Span<uint> input = dec.Pixels.Memory.Span;
Span<byte> output = this.Alpha.Memory.Span;

// Extract alpha (which is stored in the green plane).
Expand Down Expand Up @@ -327,7 +324,7 @@ private static void HorizontalUnfilter(Span<byte> prev, Span<byte> input, Span<b
ref byte srcRef = ref MemoryMarshal.GetReference(input);
for (i = 1; i + 8 <= width; i += 8)
{
var a0 = Vector128.Create(Unsafe.As<byte, long>(ref Unsafe.Add(ref srcRef, i)), 0);
Vector128<long> a0 = Vector128.Create(Unsafe.As<byte, long>(ref Unsafe.Add(ref srcRef, i)), 0);
Vector128<byte> a1 = Sse2.Add(a0.AsByte(), last.AsByte());
Vector128<byte> a2 = Sse2.ShiftLeftLogical128BitLane(a1, 1);
Vector128<byte> a3 = Sse2.Add(a1, a2);
Expand Down Expand Up @@ -365,32 +362,29 @@ private static void VerticalUnfilter(Span<byte> prev, Span<byte> input, Span<byt
{
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<int> a0 = Unsafe.As<byte, Vector256<int>>(ref Unsafe.Add(ref MemoryMarshal.GetReference(input), i));
Vector256<int> b0 = Unsafe.As<byte, Vector256<int>>(ref Unsafe.Add(ref MemoryMarshal.GetReference(prev), i));
Vector256<byte> c0 = Avx2.Add(a0.AsByte(), b0.AsByte());
ref byte outputRef = ref Unsafe.Add(ref MemoryMarshal.GetReference(dst), i);
Unsafe.As<byte, Vector256<byte>>(ref outputRef) = c0;
}
Vector256<int> a0 = Unsafe.As<byte, Vector256<int>>(ref Unsafe.Add(ref MemoryMarshal.GetReference(input), i));
Vector256<int> b0 = Unsafe.As<byte, Vector256<int>>(ref Unsafe.Add(ref MemoryMarshal.GetReference(prev), i));
Vector256<byte> c0 = Avx2.Add(a0.AsByte(), b0.AsByte());
ref byte outputRef = ref Unsafe.Add(ref MemoryMarshal.GetReference(dst), i);
Unsafe.As<byte, Vector256<byte>>(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]);
}
}
}
Expand Down
26 changes: 13 additions & 13 deletions src/ImageSharp/Formats/Webp/AlphaEncoder.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable

using System.Buffers;
using SixLabors.ImageSharp.Advanced;
Expand All @@ -13,10 +12,8 @@ namespace SixLabors.ImageSharp.Formats.Webp;
/// <summary>
/// Methods for encoding the alpha data of a VP8 image.
/// </summary>
internal class AlphaEncoder : IDisposable
internal static class AlphaEncoder
{
private IMemoryOwner<byte> alphaData;

/// <summary>
/// Encodes the alpha channel data.
/// Data is either compressed as lossless webp image or uncompressed.
Expand All @@ -29,12 +26,18 @@ internal class AlphaEncoder : IDisposable
/// <param name="compress">Indicates, if the data should be compressed with the lossless webp compression.</param>
/// <param name="size">The size in bytes of the alpha data.</param>
/// <returns>The encoded alpha data.</returns>
public IMemoryOwner<byte> EncodeAlpha<TPixel>(Image<TPixel> image, Configuration configuration, MemoryAllocator memoryAllocator, bool skipMetadata, bool compress, out int size)
public static IMemoryOwner<byte> EncodeAlpha<TPixel>(
Image<TPixel> image,
Configuration configuration,
MemoryAllocator memoryAllocator,
bool skipMetadata,
bool compress,
out int size)
where TPixel : unmanaged, IPixel<TPixel>
{
int width = image.Width;
int height = image.Height;
this.alphaData = ExtractAlphaChannel(image, configuration, memoryAllocator);
IMemoryOwner<byte> alphaData = ExtractAlphaChannel(image, configuration, memoryAllocator);

if (compress)
{
Expand All @@ -55,15 +58,15 @@ public IMemoryOwner<byte> EncodeAlpha<TPixel>(Image<TPixel> image, Configuration
// 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<Rgba32> alphaAsImage = DispatchAlphaToGreen(image, this.alphaData.GetSpan());
using Image<Rgba32> 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;
}

/// <summary>
Expand Down Expand Up @@ -128,7 +131,4 @@ private static IMemoryOwner<byte> ExtractAlphaChannel<TPixel>(Image<TPixel> imag

return alphaDataBuffer;
}

/// <inheritdoc/>
public void Dispose() => this.alphaData?.Dispose();
}
21 changes: 14 additions & 7 deletions src/ImageSharp/Formats/Webp/BitReader/BitReaderBase.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable

using System.Buffers;
using SixLabors.ImageSharp.Memory;
Expand All @@ -14,22 +13,30 @@ internal abstract class BitReaderBase : IDisposable
{
private bool isDisposed;

protected BitReaderBase(IMemoryOwner<byte> data)
=> this.Data = data;

protected BitReaderBase(Stream inputStream, int imageDataSize, MemoryAllocator memoryAllocator)
=> this.Data = ReadImageDataFromStream(inputStream, imageDataSize, memoryAllocator);

/// <summary>
/// Gets or sets the raw encoded image data.
/// Gets the raw encoded image data.
/// </summary>
public IMemoryOwner<byte> Data { get; set; }
public IMemoryOwner<byte> Data { get; }

/// <summary>
/// Copies the raw encoded image data from the stream into a byte array.
/// </summary>
/// <param name="input">The input stream.</param>
/// <param name="bytesToRead">Number of bytes to read as indicated from the chunk size.</param>
/// <param name="memoryAllocator">Used for allocating memory during reading data from the stream.</param>
protected void ReadImageDataFromStream(Stream input, int bytesToRead, MemoryAllocator memoryAllocator)
protected static IMemoryOwner<byte> ReadImageDataFromStream(Stream input, int bytesToRead, MemoryAllocator memoryAllocator)
{
this.Data = memoryAllocator.Allocate<byte>(bytesToRead);
Span<byte> dataSpan = this.Data.Memory.Span;
IMemoryOwner<byte> data = memoryAllocator.Allocate<byte>(bytesToRead);
Span<byte> dataSpan = data.Memory.Span;
input.Read(dataSpan[..bytesToRead], 0, bytesToRead);

return data;
}

protected virtual void Dispose(bool disposing)
Expand All @@ -41,7 +48,7 @@ protected virtual void Dispose(bool disposing)

if (disposing)
{
this.Data?.Dispose();
this.Data.Dispose();
}

this.isDisposed = true;
Expand Down
4 changes: 2 additions & 2 deletions src/ImageSharp/Formats/Webp/BitReader/Vp8BitReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,12 @@ internal class Vp8BitReader : BitReaderBase
/// <param name="partitionLength">The partition length.</param>
/// <param name="startPos">Start index in the data array. Defaults to 0.</param>
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);
}

Expand All @@ -73,8 +73,8 @@ public Vp8BitReader(Stream inputStream, uint imageDataSize, MemoryAllocator memo
/// <param name="partitionLength">The partition length.</param>
/// <param name="startPos">Start index in the data array. Defaults to 0.</param>
public Vp8BitReader(IMemoryOwner<byte> imageData, uint partitionLength, int startPos = 0)
: base(imageData)
{
this.Data = imageData;
this.ImageDataSize = (uint)imageData.Memory.Length;
this.PartitionLength = partitionLength;
this.InitBitreader(partitionLength, startPos);
Expand Down
7 changes: 3 additions & 4 deletions src/ImageSharp/Formats/Webp/BitReader/Vp8LBitReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ internal class Vp8LBitReader : BitReaderBase
/// </summary>
/// <param name="data">Lossless compressed image data.</param>
public Vp8LBitReader(IMemoryOwner<byte> data)
: base(data)
{
this.Data = data;
this.len = data.Memory.Length;
this.value = 0;
this.bitPos = 0;
Expand All @@ -88,11 +88,10 @@ public Vp8LBitReader(IMemoryOwner<byte> data)
/// <param name="imageDataSize">The raw image data size in bytes.</param>
/// <param name="memoryAllocator">Used for allocating memory during reading data from the stream.</param>
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;
Expand Down Expand Up @@ -193,7 +192,7 @@ public void FillBitWindow()
[MethodImpl(InliningOptions.ShortMethod)]
private void ShiftBytes()
{
System.Span<byte> dataSpan = this.Data.Memory.Span;
System.Span<byte> dataSpan = this.Data!.Memory.Span;
while (this.bitPos >= 8 && this.pos < this.len)
{
this.value >>= 8;
Expand Down
4 changes: 2 additions & 2 deletions src/ImageSharp/Formats/Webp/BitWriter/BitWriterBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ protected static uint AlphaChunkSize(Span<byte> alphaBytes)
/// <param name="stream">The stream to write to.</param>
/// <param name="metadataBytes">The metadata profile's bytes.</param>
/// <param name="chunkType">The chuck type to write.</param>
protected void WriteMetadataProfile(Stream stream, byte[] metadataBytes, WebpChunkType chunkType)
protected void WriteMetadataProfile(Stream stream, byte[]? metadataBytes, WebpChunkType chunkType)
{
DebugGuard.NotNull(metadataBytes, nameof(metadataBytes));

Expand Down Expand Up @@ -207,7 +207,7 @@ protected void WriteColorProfile(Stream stream, byte[] iccProfileBytes)
/// <param name="width">The width of the image.</param>
/// <param name="height">The height of the image.</param>
/// <param name="hasAlpha">Flag indicating, if a alpha channel is present.</param>
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)
{
Expand Down
Loading