diff --git a/src/ImageSharp/Color/Color.Conversions.cs b/src/ImageSharp/Color/Color.Conversions.cs
index 0455fd26a4..bf7869e53d 100644
--- a/src/ImageSharp/Color/Color.Conversions.cs
+++ b/src/ImageSharp/Color/Color.Conversions.cs
@@ -17,56 +17,118 @@ namespace SixLabors.ImageSharp
///
/// The containing the color information.
[MethodImpl(InliningOptions.ShortMethod)]
- public Color(Rgba64 pixel) => this.data = pixel;
+ public Color(Rgba64 pixel)
+ {
+ this.data = pixel;
+ this.boxedHighPrecisionPixel = null;
+ }
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The containing the color information.
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public Color(Rgb48 pixel)
+ {
+ this.data = new Rgba64(pixel.R, pixel.G, pixel.B, ushort.MaxValue);
+ this.boxedHighPrecisionPixel = null;
+ }
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The containing the color information.
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public Color(La32 pixel)
+ {
+ this.data = new Rgba64(pixel.L, pixel.L, pixel.L, pixel.A);
+ this.boxedHighPrecisionPixel = null;
+ }
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The containing the color information.
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public Color(L16 pixel)
+ {
+ this.data = new Rgba64(pixel.PackedValue, pixel.PackedValue, pixel.PackedValue, ushort.MaxValue);
+ this.boxedHighPrecisionPixel = null;
+ }
///
/// Initializes a new instance of the struct.
///
/// The containing the color information.
[MethodImpl(InliningOptions.ShortMethod)]
- public Color(Rgba32 pixel) => this.data = new Rgba64(pixel);
+ public Color(Rgba32 pixel)
+ {
+ this.data = new Rgba64(pixel);
+ this.boxedHighPrecisionPixel = null;
+ }
///
/// Initializes a new instance of the struct.
///
/// The containing the color information.
[MethodImpl(InliningOptions.ShortMethod)]
- public Color(Argb32 pixel) => this.data = new Rgba64(pixel);
+ public Color(Argb32 pixel)
+ {
+ this.data = new Rgba64(pixel);
+ this.boxedHighPrecisionPixel = null;
+ }
///
/// Initializes a new instance of the struct.
///
/// The containing the color information.
[MethodImpl(InliningOptions.ShortMethod)]
- public Color(Bgra32 pixel) => this.data = new Rgba64(pixel);
+ public Color(Bgra32 pixel)
+ {
+ this.data = new Rgba64(pixel);
+ this.boxedHighPrecisionPixel = null;
+ }
///
/// Initializes a new instance of the struct.
///
/// The containing the color information.
[MethodImpl(InliningOptions.ShortMethod)]
- public Color(Rgb24 pixel) => this.data = new Rgba64(pixel);
+ public Color(Rgb24 pixel)
+ {
+ this.data = new Rgba64(pixel);
+ this.boxedHighPrecisionPixel = null;
+ }
///
/// Initializes a new instance of the struct.
///
/// The containing the color information.
[MethodImpl(InliningOptions.ShortMethod)]
- public Color(Bgr24 pixel) => this.data = new Rgba64(pixel);
+ public Color(Bgr24 pixel)
+ {
+ this.data = new Rgba64(pixel);
+ this.boxedHighPrecisionPixel = null;
+ }
///
/// Initializes a new instance of the struct.
///
/// The containing the color information.
[MethodImpl(InliningOptions.ShortMethod)]
- public Color(Vector4 vector) => this.data = new Rgba64(vector);
+ public Color(Vector4 vector)
+ {
+ vector = Numerics.Clamp(vector, Vector4.Zero, Vector4.One);
+ this.boxedHighPrecisionPixel = new RgbaVector(vector.X, vector.Y, vector.Z, vector.W);
+ this.data = default;
+ }
///
/// Converts a to .
///
/// The .
/// The .
- public static explicit operator Vector4(Color color) => color.data.ToVector4();
+ public static explicit operator Vector4(Color color) => color.ToVector4();
///
/// Converts an to .
@@ -74,24 +136,82 @@ namespace SixLabors.ImageSharp
/// The .
/// The .
[MethodImpl(InliningOptions.ShortMethod)]
- public static explicit operator Color(Vector4 source) => new Color(source);
+ public static explicit operator Color(Vector4 source) => new(source);
[MethodImpl(InliningOptions.ShortMethod)]
- internal Rgba32 ToRgba32() => this.data.ToRgba32();
+ internal Rgba32 ToRgba32()
+ {
+ if (this.boxedHighPrecisionPixel is null)
+ {
+ return this.data.ToRgba32();
+ }
+
+ Rgba32 value = default;
+ this.boxedHighPrecisionPixel.ToRgba32(ref value);
+ return value;
+ }
[MethodImpl(InliningOptions.ShortMethod)]
- internal Bgra32 ToBgra32() => this.data.ToBgra32();
+ internal Bgra32 ToBgra32()
+ {
+ if (this.boxedHighPrecisionPixel is null)
+ {
+ return this.data.ToBgra32();
+ }
+
+ Bgra32 value = default;
+ value.FromScaledVector4(this.boxedHighPrecisionPixel.ToScaledVector4());
+ return value;
+ }
[MethodImpl(InliningOptions.ShortMethod)]
- internal Argb32 ToArgb32() => this.data.ToArgb32();
+ internal Argb32 ToArgb32()
+ {
+ if (this.boxedHighPrecisionPixel is null)
+ {
+ return this.data.ToArgb32();
+ }
+
+ Argb32 value = default;
+ value.FromScaledVector4(this.boxedHighPrecisionPixel.ToScaledVector4());
+ return value;
+ }
[MethodImpl(InliningOptions.ShortMethod)]
- internal Rgb24 ToRgb24() => this.data.ToRgb24();
+ internal Rgb24 ToRgb24()
+ {
+ if (this.boxedHighPrecisionPixel is null)
+ {
+ return this.data.ToRgb24();
+ }
+
+ Rgb24 value = default;
+ value.FromScaledVector4(this.boxedHighPrecisionPixel.ToScaledVector4());
+ return value;
+ }
[MethodImpl(InliningOptions.ShortMethod)]
- internal Bgr24 ToBgr24() => this.data.ToBgr24();
+ internal Bgr24 ToBgr24()
+ {
+ if (this.boxedHighPrecisionPixel is null)
+ {
+ return this.data.ToBgr24();
+ }
+
+ Bgr24 value = default;
+ value.FromScaledVector4(this.boxedHighPrecisionPixel.ToScaledVector4());
+ return value;
+ }
[MethodImpl(InliningOptions.ShortMethod)]
- internal Vector4 ToVector4() => this.data.ToVector4();
+ internal Vector4 ToVector4()
+ {
+ if (this.boxedHighPrecisionPixel is null)
+ {
+ return this.data.ToScaledVector4();
+ }
+
+ return this.boxedHighPrecisionPixel.ToScaledVector4();
+ }
}
}
diff --git a/src/ImageSharp/Color/Color.cs b/src/ImageSharp/Color/Color.cs
index d5eedc160b..7c21d62ddf 100644
--- a/src/ImageSharp/Color/Color.cs
+++ b/src/ImageSharp/Color/Color.cs
@@ -4,7 +4,6 @@
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp
@@ -21,6 +20,7 @@ namespace SixLabors.ImageSharp
public readonly partial struct Color : IEquatable
{
private readonly Rgba64 data;
+ private readonly IPixel boxedHighPrecisionPixel;
[MethodImpl(InliningOptions.ShortMethod)]
private Color(byte r, byte g, byte b, byte a)
@@ -30,6 +30,8 @@ namespace SixLabors.ImageSharp
ColorNumerics.UpscaleFrom8BitTo16Bit(g),
ColorNumerics.UpscaleFrom8BitTo16Bit(b),
ColorNumerics.UpscaleFrom8BitTo16Bit(a));
+
+ this.boxedHighPrecisionPixel = null;
}
[MethodImpl(InliningOptions.ShortMethod)]
@@ -40,6 +42,15 @@ namespace SixLabors.ImageSharp
ColorNumerics.UpscaleFrom8BitTo16Bit(g),
ColorNumerics.UpscaleFrom8BitTo16Bit(b),
ushort.MaxValue);
+
+ this.boxedHighPrecisionPixel = null;
+ }
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private Color(IPixel pixel)
+ {
+ this.boxedHighPrecisionPixel = pixel;
+ this.data = default;
}
///
@@ -52,13 +63,10 @@ namespace SixLabors.ImageSharp
/// otherwise, false.
///
[MethodImpl(InliningOptions.ShortMethod)]
- public static bool operator ==(Color left, Color right)
- {
- return left.Equals(right);
- }
+ public static bool operator ==(Color left, Color right) => left.Equals(right);
///
- /// Checks whether two structures are equal.
+ /// Checks whether two structures are not equal.
///
/// The left hand operand.
/// The right hand operand.
@@ -67,10 +75,7 @@ namespace SixLabors.ImageSharp
/// otherwise, false.
///
[MethodImpl(InliningOptions.ShortMethod)]
- public static bool operator !=(Color left, Color right)
- {
- return !left.Equals(right);
- }
+ public static bool operator !=(Color left, Color right) => !left.Equals(right);
///
/// Creates a from RGBA bytes.
@@ -81,7 +86,7 @@ namespace SixLabors.ImageSharp
/// The alpha component (0-255).
/// The .
[MethodImpl(InliningOptions.ShortMethod)]
- public static Color FromRgba(byte r, byte g, byte b, byte a) => new Color(r, g, b, a);
+ public static Color FromRgba(byte r, byte g, byte b, byte a) => new(r, g, b, a);
///
/// Creates a from RGB bytes.
@@ -91,7 +96,46 @@ namespace SixLabors.ImageSharp
/// The blue component (0-255).
/// The .
[MethodImpl(InliningOptions.ShortMethod)]
- public static Color FromRgb(byte r, byte g, byte b) => new Color(r, g, b);
+ public static Color FromRgb(byte r, byte g, byte b) => new(r, g, b);
+
+ ///
+ /// Creates a from the given .
+ ///
+ /// The pixel to convert from.
+ /// The pixel format.
+ /// The .
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public static Color FromPixel(TPixel pixel)
+ where TPixel : unmanaged, IPixel
+ {
+ // Avoid boxing in case we can convert to Rgba64 safely and efficently
+ if (typeof(TPixel) == typeof(Rgba64))
+ {
+ return new((Rgba64)(object)pixel);
+ }
+ else if (typeof(TPixel) == typeof(Rgb48))
+ {
+ return new((Rgb48)(object)pixel);
+ }
+ else if (typeof(TPixel) == typeof(La32))
+ {
+ return new((La32)(object)pixel);
+ }
+ else if (typeof(TPixel) == typeof(L16))
+ {
+ return new((L16)(object)pixel);
+ }
+ else if (Unsafe.SizeOf() <= Unsafe.SizeOf())
+ {
+ Rgba32 p = default;
+ pixel.ToRgba32(ref p);
+ return new(p);
+ }
+ else
+ {
+ return new(pixel);
+ }
+ }
///
/// Creates a new instance of the struct
@@ -213,7 +257,7 @@ namespace SixLabors.ImageSharp
public override string ToString() => this.ToHex();
///
- /// Converts the color instance to a specified type.
+ /// Converts the color instance to a specified type.
///
/// The pixel type to convert to.
/// The pixel value.
@@ -221,13 +265,18 @@ namespace SixLabors.ImageSharp
public TPixel ToPixel()
where TPixel : unmanaged, IPixel
{
- TPixel pixel = default;
+ if (this.boxedHighPrecisionPixel is TPixel pixel)
+ {
+ return pixel;
+ }
+
+ pixel = default;
pixel.FromRgba64(this.data);
return pixel;
}
///
- /// Bulk converts a span of to a span of a specified type.
+ /// Bulk converts a span of to a span of a specified type.
///
/// The pixel type to convert to.
/// The configuration.
@@ -240,28 +289,38 @@ namespace SixLabors.ImageSharp
Span destination)
where TPixel : unmanaged, IPixel
{
- ReadOnlySpan rgba64Span = MemoryMarshal.Cast(source);
- PixelOperations.Instance.FromRgba64(configuration, rgba64Span, destination);
+ Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
+ for (int i = 0; i < source.Length; i++)
+ {
+ destination[i] = source[i].ToPixel();
+ }
}
///
[MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(Color other)
{
- return this.data.PackedValue == other.data.PackedValue;
+ if (this.boxedHighPrecisionPixel is null && other.boxedHighPrecisionPixel is null)
+ {
+ return this.data.PackedValue == other.data.PackedValue;
+ }
+
+ return this.boxedHighPrecisionPixel?.Equals(other.boxedHighPrecisionPixel) == true;
}
///
- public override bool Equals(object obj)
- {
- return obj is Color other && this.Equals(other);
- }
+ public override bool Equals(object obj) => obj is Color other && this.Equals(other);
///
[MethodImpl(InliningOptions.ShortMethod)]
public override int GetHashCode()
{
- return this.data.PackedValue.GetHashCode();
+ if (this.boxedHighPrecisionPixel is null)
+ {
+ return this.data.PackedValue.GetHashCode();
+ }
+
+ return this.boxedHighPrecisionPixel.GetHashCode();
}
}
}
diff --git a/src/ImageSharp/Formats/Webp/Lossless/ColorCache.cs b/src/ImageSharp/Formats/Webp/Lossless/ColorCache.cs
index 8596d85558..02bbc38fcf 100644
--- a/src/ImageSharp/Formats/Webp/Lossless/ColorCache.cs
+++ b/src/ImageSharp/Formats/Webp/Lossless/ColorCache.cs
@@ -1,6 +1,8 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
+using System.Runtime.CompilerServices;
+
namespace SixLabors.ImageSharp.Formats.Webp.Lossless
{
///
@@ -41,6 +43,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
/// Inserts a new color into the cache.
///
/// The color to insert.
+ [MethodImpl(InliningOptions.ShortMethod)]
public void Insert(uint bgra)
{
int key = HashPix(bgra, this.HashShift);
@@ -52,6 +55,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
///
/// The key to lookup.
/// The color for the key.
+ [MethodImpl(InliningOptions.ShortMethod)]
public uint Lookup(int key) => this.Colors[key];
///
@@ -59,6 +63,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
///
/// The color to check.
/// The index of the color in the cache or -1 if its not present.
+ [MethodImpl(InliningOptions.ShortMethod)]
public int Contains(uint bgra)
{
int key = HashPix(bgra, this.HashShift);
@@ -70,6 +75,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
///
/// The color.
/// The index for the color.
+ [MethodImpl(InliningOptions.ShortMethod)]
public int GetIndex(uint bgra) => HashPix(bgra, this.HashShift);
///
@@ -77,8 +83,10 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
///
/// The key.
/// The color to add.
+ [MethodImpl(InliningOptions.ShortMethod)]
public void Set(uint key, uint bgra) => this.Colors[key] = bgra;
+ [MethodImpl(InliningOptions.ShortMethod)]
public static int HashPix(uint argb, int shift) => (int)((argb * HashMul) >> shift);
}
}
diff --git a/src/ImageSharp/Formats/Webp/Lossless/LosslessUtils.cs b/src/ImageSharp/Formats/Webp/Lossless/LosslessUtils.cs
index 8231464070..f9b97c6c44 100644
--- a/src/ImageSharp/Formats/Webp/Lossless/LosslessUtils.cs
+++ b/src/ImageSharp/Formats/Webp/Lossless/LosslessUtils.cs
@@ -765,6 +765,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
///
/// Fast calculation of log2(v) for integer input.
///
+ [MethodImpl(InliningOptions.ShortMethod)]
public static float FastLog2(uint v) => v < LogLookupIdxMax ? WebpLookupTables.Log2Table[v] : FastLog2Slow(v);
///
@@ -793,7 +794,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
private static float FastSLog2Slow(uint v)
{
- Guard.MustBeGreaterThanOrEqualTo(v, LogLookupIdxMax, nameof(v));
+ DebugGuard.MustBeGreaterThanOrEqualTo(v, LogLookupIdxMax, nameof(v));
if (v < ApproxLogWithCorrectionMax)
{
int logCnt = 0;
@@ -1214,30 +1215,65 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
private static uint ClampedAddSubtractFull(uint c0, uint c1, uint c2)
{
- int a = AddSubtractComponentFull(
- (int)(c0 >> 24),
- (int)(c1 >> 24),
- (int)(c2 >> 24));
- int r = AddSubtractComponentFull(
- (int)((c0 >> 16) & 0xff),
- (int)((c1 >> 16) & 0xff),
- (int)((c2 >> 16) & 0xff));
- int g = AddSubtractComponentFull(
- (int)((c0 >> 8) & 0xff),
- (int)((c1 >> 8) & 0xff),
- (int)((c2 >> 8) & 0xff));
- int b = AddSubtractComponentFull((int)(c0 & 0xff), (int)(c1 & 0xff), (int)(c2 & 0xff));
- return ((uint)a << 24) | ((uint)r << 16) | ((uint)g << 8) | (uint)b;
+#if SUPPORTS_RUNTIME_INTRINSICS
+ if (Sse2.IsSupported)
+ {
+ Vector128 c0Vec = Sse2.UnpackLow(Sse2.ConvertScalarToVector128UInt32(c0).AsByte(), Vector128.Zero);
+ Vector128 c1Vec = Sse2.UnpackLow(Sse2.ConvertScalarToVector128UInt32(c1).AsByte(), Vector128.Zero);
+ Vector128 c2Vec = Sse2.UnpackLow(Sse2.ConvertScalarToVector128UInt32(c2).AsByte(), Vector128.Zero);
+ Vector128 v1 = Sse2.Add(c0Vec.AsInt16(), c1Vec.AsInt16());
+ Vector128 v2 = Sse2.Subtract(v1, c2Vec.AsInt16());
+ Vector128 b = Sse2.PackUnsignedSaturate(v2, v2);
+ uint output = Sse2.ConvertToUInt32(b.AsUInt32());
+ return output;
+ }
+#endif
+ {
+ int a = AddSubtractComponentFull(
+ (int)(c0 >> 24),
+ (int)(c1 >> 24),
+ (int)(c2 >> 24));
+ int r = AddSubtractComponentFull(
+ (int)((c0 >> 16) & 0xff),
+ (int)((c1 >> 16) & 0xff),
+ (int)((c2 >> 16) & 0xff));
+ int g = AddSubtractComponentFull(
+ (int)((c0 >> 8) & 0xff),
+ (int)((c1 >> 8) & 0xff),
+ (int)((c2 >> 8) & 0xff));
+ int b = AddSubtractComponentFull((int)(c0 & 0xff), (int)(c1 & 0xff), (int)(c2 & 0xff));
+ return ((uint)a << 24) | ((uint)r << 16) | ((uint)g << 8) | (uint)b;
+ }
}
private static uint ClampedAddSubtractHalf(uint c0, uint c1, uint c2)
{
- uint ave = Average2(c0, c1);
- int a = AddSubtractComponentHalf((int)(ave >> 24), (int)(c2 >> 24));
- int r = AddSubtractComponentHalf((int)((ave >> 16) & 0xff), (int)((c2 >> 16) & 0xff));
- int g = AddSubtractComponentHalf((int)((ave >> 8) & 0xff), (int)((c2 >> 8) & 0xff));
- int b = AddSubtractComponentHalf((int)(ave & 0xff), (int)(c2 & 0xff));
- return ((uint)a << 24) | ((uint)r << 16) | ((uint)g << 8) | (uint)b;
+#if SUPPORTS_RUNTIME_INTRINSICS
+ if (Sse2.IsSupported)
+ {
+ Vector128 c0Vec = Sse2.UnpackLow(Sse2.ConvertScalarToVector128UInt32(c0).AsByte(), Vector128.Zero);
+ Vector128 c1Vec = Sse2.UnpackLow(Sse2.ConvertScalarToVector128UInt32(c1).AsByte(), Vector128.Zero);
+ Vector128 b0 = Sse2.UnpackLow(Sse2.ConvertScalarToVector128UInt32(c2).AsByte(), Vector128.Zero);
+ Vector128 avg = Sse2.Add(c1Vec.AsInt16(), c0Vec.AsInt16());
+ Vector128 a0 = Sse2.ShiftRightLogical(avg, 1);
+ Vector128 a1 = Sse2.Subtract(a0, b0.AsInt16());
+ Vector128 bgta = Sse2.CompareGreaterThan(b0.AsInt16(), a0.AsInt16());
+ Vector128 a2 = Sse2.Subtract(a1, bgta);
+ Vector128 a3 = Sse2.ShiftRightArithmetic(a2, 1);
+ Vector128 a4 = Sse2.Add(a0, a3).AsInt16();
+ Vector128 a5 = Sse2.PackUnsignedSaturate(a4, a4);
+ uint output = Sse2.ConvertToUInt32(a5.AsUInt32());
+ return output;
+ }
+#endif
+ {
+ uint ave = Average2(c0, c1);
+ int a = AddSubtractComponentHalf((int)(ave >> 24), (int)(c2 >> 24));
+ int r = AddSubtractComponentHalf((int)((ave >> 16) & 0xff), (int)((c2 >> 16) & 0xff));
+ int g = AddSubtractComponentHalf((int)((ave >> 8) & 0xff), (int)((c2 >> 8) & 0xff));
+ int b = AddSubtractComponentHalf((int)(ave & 0xff), (int)(c2 & 0xff));
+ return ((uint)a << 24) | ((uint)r << 16) | ((uint)g << 8) | (uint)b;
+ }
}
[MethodImpl(InliningOptions.ShortMethod)]
@@ -1275,11 +1311,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
Vector128 pb = Sse2.UnpackLow(bc, Vector128.Zero); // |b - c|
Vector128 diff = Sse2.Subtract(pb.AsUInt16(), pa.AsUInt16());
Sse2.Store((ushort*)p, diff);
+ int paMinusPb = output[3] + output[2] + output[1] + output[0];
+ return (paMinusPb <= 0) ? a : b;
}
-
- int paMinusPb = output[0] + output[1] + output[2] + output[3];
-
- return (paMinusPb <= 0) ? a : b;
}
else
#endif
diff --git a/src/ImageSharp/Image.Decode.cs b/src/ImageSharp/Image.Decode.cs
index 94da2c9958..ee340bf86e 100644
--- a/src/ImageSharp/Image.Decode.cs
+++ b/src/ImageSharp/Image.Decode.cs
@@ -58,31 +58,42 @@ namespace SixLabors.ImageSharp
return null;
}
- using (IMemoryOwner buffer = config.MemoryAllocator.Allocate(headerSize, AllocationOptions.Clean))
+ // Header sizes are so small, that headersBuffer will be always stackalloc-ed in practice,
+ // and heap allocation will never happen, there is no need for the usual try-finally ArrayPool dance.
+ // The array case is only a safety mechanism following stackalloc best practices.
+ Span headersBuffer = headerSize > 512 ? new byte[headerSize] : stackalloc byte[headerSize];
+ long startPosition = stream.Position;
+
+ // Read doesn't always guarantee the full returned length so read a byte
+ // at a time until we get either our count or hit the end of the stream.
+ int n = 0;
+ int i;
+ do
{
- Span bufferSpan = buffer.GetSpan();
- long startPosition = stream.Position;
+ i = stream.Read(headersBuffer, n, headerSize - n);
+ n += i;
+ }
+ while (n < headerSize && i > 0);
- // Read doesn't always guarantee the full returned length so read a byte
- // at a time until we get either our count or hit the end of the stream.
- int n = 0;
- int i;
- do
+ stream.Position = startPosition;
+
+ // Does the given stream contain enough data to fit in the header for the format
+ // and does that data match the format specification?
+ // Individual formats should still check since they are public.
+ IImageFormat format = null;
+ foreach (IImageFormatDetector formatDetector in config.ImageFormatsManager.FormatDetectors)
+ {
+ if (formatDetector.HeaderSize <= headerSize)
{
- i = stream.Read(bufferSpan, n, headerSize - n);
- n += i;
+ IImageFormat attemptFormat = formatDetector.DetectFormat(headersBuffer);
+ if (attemptFormat != null)
+ {
+ format = attemptFormat;
+ }
}
- while (n < headerSize && i > 0);
-
- stream.Position = startPosition;
-
- // Does the given stream contain enough data to fit in the header for the format
- // and does that data match the format specification?
- // Individual formats should still check since they are public.
- return config.ImageFormatsManager.FormatDetectors
- .Where(x => x.HeaderSize <= headerSize)
- .Select(x => x.DetectFormat(buffer.GetSpan())).LastOrDefault(x => x != null);
}
+
+ return format;
}
///
diff --git a/tests/ImageSharp.Tests/Color/ColorTests.CastTo.cs b/tests/ImageSharp.Tests/Color/ColorTests.CastTo.cs
index ee1820de77..3003265ca6 100644
--- a/tests/ImageSharp.Tests/Color/ColorTests.CastTo.cs
+++ b/tests/ImageSharp.Tests/Color/ColorTests.CastTo.cs
@@ -66,7 +66,7 @@ namespace SixLabors.ImageSharp.Tests
[Fact]
public void Rgb24()
{
- var source = new Rgb24(1, 22, 231);
+ var source = new Rgb24(1, 22, 231);
// Act:
var color = new Color(source);
@@ -79,7 +79,7 @@ namespace SixLabors.ImageSharp.Tests
[Fact]
public void Bgr24()
{
- var source = new Bgr24(1, 22, 231);
+ var source = new Bgr24(1, 22, 231);
// Act:
var color = new Color(source);
@@ -88,6 +88,28 @@ namespace SixLabors.ImageSharp.Tests
Bgr24 data = color;
Assert.Equal(source, data);
}
+
+ [Fact]
+ public void GenericPixel()
+ {
+ AssertGenericPixel(new RgbaVector(float.Epsilon, 2 * float.Epsilon, float.MaxValue, float.MinValue));
+ AssertGenericPixel(new Rgba64(1, 2, ushort.MaxValue, ushort.MaxValue - 1));
+ AssertGenericPixel(new Rgb48(1, 2, ushort.MaxValue - 1));
+ AssertGenericPixel(new La32(1, ushort.MaxValue - 1));
+ AssertGenericPixel(new L16(ushort.MaxValue - 1));
+ AssertGenericPixel(new Rgba32(1, 2, 255, 254));
+ }
+
+ private static void AssertGenericPixel(TPixel source)
+ where TPixel : unmanaged, IPixel
+ {
+ // Act:
+ var color = Color.FromPixel(source);
+
+ // Assert:
+ TPixel actual = color.ToPixel();
+ Assert.Equal(source, actual);
+ }
}
}
}
diff --git a/tests/ImageSharp.Tests/Formats/WebP/LosslessUtilsTests.cs b/tests/ImageSharp.Tests/Formats/WebP/LosslessUtilsTests.cs
index bf381ebdaa..c70f332ef6 100644
--- a/tests/ImageSharp.Tests/Formats/WebP/LosslessUtilsTests.cs
+++ b/tests/ImageSharp.Tests/Formats/WebP/LosslessUtilsTests.cs
@@ -153,9 +153,55 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
}
}
+ private static void RunPredictor12Test()
+ {
+ // arrange
+ uint[] topData = { 4294844413, 4294779388 };
+ uint left = 4294844413;
+ uint expectedResult = 4294779388;
+
+ // act
+ unsafe
+ {
+ fixed (uint* top = &topData[1])
+ {
+ uint actual = LosslessUtils.Predictor12(left, top);
+
+ // assert
+ Assert.Equal(expectedResult, actual);
+ }
+ }
+ }
+
+ private static void RunPredictor13Test()
+ {
+ // arrange
+ uint[] topData = { 4278193922, 4278193666 };
+ uint left = 4278193410;
+ uint expectedResult = 4278193154;
+
+ // act
+ unsafe
+ {
+ fixed (uint* top = &topData[1])
+ {
+ uint actual = LosslessUtils.Predictor13(left, top);
+
+ // assert
+ Assert.Equal(expectedResult, actual);
+ }
+ }
+ }
+
[Fact]
public void Predictor11_Works() => RunPredictor11Test();
+ [Fact]
+ public void Predictor12_Works() => RunPredictor12Test();
+
+ [Fact]
+ public void Predictor13_Works() => RunPredictor13Test();
+
[Fact]
public void SubtractGreen_Works() => RunSubtractGreenTest();
@@ -175,6 +221,18 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
[Fact]
public void Predictor11_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunPredictor11Test, HwIntrinsics.DisableSSE2);
+ [Fact]
+ public void Predictor12_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunPredictor12Test, HwIntrinsics.AllowAll);
+
+ [Fact]
+ public void Predictor12_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunPredictor12Test, HwIntrinsics.DisableSSE2);
+
+ [Fact]
+ public void Predictor13_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunPredictor13Test, HwIntrinsics.AllowAll);
+
+ [Fact]
+ public void Predictor13_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunPredictor13Test, HwIntrinsics.DisableSSE2);
+
[Fact]
public void SubtractGreen_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunSubtractGreenTest, HwIntrinsics.AllowAll);