diff --git a/src/ImageSharp/Advanced/AotCompilerTools.cs b/src/ImageSharp/Advanced/AotCompilerTools.cs index dd0cdf849..82a146dc7 100644 --- a/src/ImageSharp/Advanced/AotCompilerTools.cs +++ b/src/ImageSharp/Advanced/AotCompilerTools.cs @@ -75,6 +75,7 @@ namespace SixLabors.ImageSharp.Advanced Seed(); Seed(); + Seed(); Seed(); Seed(); Seed(); @@ -149,6 +150,7 @@ namespace SixLabors.ImageSharp.Advanced Image img = default; img.CloneAs(default); img.CloneAs(default); + img.CloneAs(default); img.CloneAs(default); img.CloneAs(default); img.CloneAs(default); diff --git a/src/ImageSharp/Color/Color.Conversions.cs b/src/ImageSharp/Color/Color.Conversions.cs index bf7869e53..5c10bfaa0 100644 --- a/src/ImageSharp/Color/Color.Conversions.cs +++ b/src/ImageSharp/Color/Color.Conversions.cs @@ -89,6 +89,17 @@ namespace SixLabors.ImageSharp this.boxedHighPrecisionPixel = null; } + /// + /// Initializes a new instance of the struct. + /// + /// The containing the color information. + [MethodImpl(InliningOptions.ShortMethod)] + public Color(Abgr32 pixel) + { + this.data = new Rgba64(pixel); + this.boxedHighPrecisionPixel = null; + } + /// /// Initializes a new instance of the struct. /// @@ -177,6 +188,19 @@ namespace SixLabors.ImageSharp return value; } + [MethodImpl(InliningOptions.ShortMethod)] + internal Abgr32 ToAbgr32() + { + if (this.boxedHighPrecisionPixel is null) + { + return this.data.ToAbgr32(); + } + + Abgr32 value = default; + value.FromScaledVector4(this.boxedHighPrecisionPixel.ToScaledVector4()); + return value; + } + [MethodImpl(InliningOptions.ShortMethod)] internal Rgb24 ToRgb24() { diff --git a/src/ImageSharp/Common/Helpers/HexConverter.cs b/src/ImageSharp/Common/Helpers/HexConverter.cs new file mode 100644 index 000000000..c55e9bbd9 --- /dev/null +++ b/src/ImageSharp/Common/Helpers/HexConverter.cs @@ -0,0 +1,98 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.Common.Helpers +{ + internal static class HexConverter + { + /// + /// Parses a hexadecimal string into a byte array without allocations. Throws on non-hexadecimal character. + /// Adapted from https://source.dot.net/#System.Private.CoreLib/Convert.cs,c9e4fbeaca708991. + /// + /// The hexadecimal string to parse. + /// The destination for the parsed bytes. Must be at least .Length / 2 bytes long. + /// The number of bytes written to . + public static int HexStringToBytes(ReadOnlySpan chars, Span bytes) + { + if ((chars.Length % 2) != 0) + { + throw new ArgumentException("Input string length must be a multiple of 2", nameof(chars)); + } + + if ((bytes.Length * 2) < chars.Length) + { + throw new ArgumentException("Output span must be at least half the length of the input string"); + } + else + { + // Slightly better performance in the loop below, allows us to skip a bounds check + // while still supporting output buffers that are larger than necessary + bytes = bytes.Slice(0, chars.Length / 2); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static int FromChar(int c) + { + // Map from an ASCII char to its hex value, e.g. arr['b'] == 11. 0xFF means it's not a hex digit. + // This doesn't actually allocate. + ReadOnlySpan charToHexLookup = new byte[] + { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 15 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 31 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 47 + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 63 + 0xFF, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 79 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 95 + 0xFF, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 111 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 127 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 143 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 159 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 175 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 191 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 207 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 223 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 239 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 255 + }; + + return c >= charToHexLookup.Length ? 0xFF : charToHexLookup[c]; + } + + // See https://source.dot.net/#System.Private.CoreLib/HexConverter.cs,4681d45a0aa0b361 + int i = 0; + int j = 0; + int byteLo = 0; + int byteHi = 0; + while (j < bytes.Length) + { + byteLo = FromChar(chars[i + 1]); + byteHi = FromChar(chars[i]); + + // byteHi hasn't been shifted to the high half yet, so the only way the bitwise or produces this pattern + // is if either byteHi or byteLo was not a hex character. + if ((byteLo | byteHi) == 0xFF) + { + break; + } + + bytes[j++] = (byte)((byteHi << 4) | byteLo); + i += 2; + } + + if (byteLo == 0xFF) + { + i++; + } + + if ((byteLo | byteHi) == 0xFF) + { + throw new ArgumentException("Input string contained non-hexadecimal characters", nameof(chars)); + } + + return j; + } + } +} diff --git a/src/ImageSharp/Common/Helpers/Numerics.cs b/src/ImageSharp/Common/Helpers/Numerics.cs index fa0af823d..7de838bc9 100644 --- a/src/ImageSharp/Common/Helpers/Numerics.cs +++ b/src/ImageSharp/Common/Helpers/Numerics.cs @@ -907,5 +907,71 @@ namespace SixLabors.ImageSharp /// Divisor value. /// Ceiled division result. public static uint DivideCeil(uint value, uint divisor) => (value + divisor - 1) / divisor; + + /// + /// Rotates the specified value left by the specified number of bits. + /// + /// The value to rotate. + /// The number of bits to rotate with. + /// The rotated value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint RotateLeft(uint value, int offset) + { +#if SUPPORTS_BITOPERATIONS + return BitOperations.RotateLeft(value, offset); +#else + return RotateLeftSoftwareFallback(value, offset); +#endif + } + +#if !SUPPORTS_BITOPERATIONS + /// + /// Rotates the specified value left by the specified number of bits. + /// + /// The value to rotate. + /// The number of bits to rotate with. + /// The rotated value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint RotateLeftSoftwareFallback(uint value, int offset) + => (value << offset) | (value >> (32 - offset)); +#endif + + /// + /// Rotates the specified value right by the specified number of bits. + /// + /// The value to rotate. + /// The number of bits to rotate with. + /// The rotated value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint RotateRight(uint value, int offset) + { +#if SUPPORTS_BITOPERATIONS + return BitOperations.RotateRight(value, offset); +#else + return RotateRightSoftwareFallback(value, offset); +#endif + } + +#if !SUPPORTS_BITOPERATIONS + /// + /// Rotates the specified value right by the specified number of bits. + /// + /// The value to rotate. + /// The number of bits to rotate with. + /// The rotated value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint RotateRightSoftwareFallback(uint value, int offset) + => (value >> offset) | (value << (32 - offset)); +#endif + + /// + /// Tells whether input value is outside of the given range. + /// + /// Value. + /// Mininum value, inclusive. + /// Maximum value, inclusive. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsOutOfRange(int value, int min, int max) + => (uint)(value - min) > (uint)(max - min); } } diff --git a/src/ImageSharp/Common/Helpers/Shuffle/IComponentShuffle.cs b/src/ImageSharp/Common/Helpers/Shuffle/IComponentShuffle.cs index 929b78692..049c61185 100644 --- a/src/ImageSharp/Common/Helpers/Shuffle/IComponentShuffle.cs +++ b/src/ImageSharp/Common/Helpers/Shuffle/IComponentShuffle.cs @@ -157,7 +157,7 @@ namespace SixLabors.ImageSharp // packed = [W Z Y X] // ROTR(8, packedArgb) = [Y Z W X] - Unsafe.Add(ref dBase, i) = (packed >> 8) | (packed << 24); + Unsafe.Add(ref dBase, i) = Numerics.RotateRight(packed, 8); } } } @@ -188,7 +188,40 @@ namespace SixLabors.ImageSharp // tmp1 + tmp3 = [W X Y Z] uint tmp1 = packed & 0xFF00FF00; uint tmp2 = packed & 0x00FF00FF; - uint tmp3 = (tmp2 << 16) | (tmp2 >> 16); + uint tmp3 = Numerics.RotateLeft(tmp2, 16); + + Unsafe.Add(ref dBase, i) = tmp1 + tmp3; + } + } + } + + internal readonly struct XWZYShuffle4 : IShuffle4 + { + public byte Control + { + [MethodImpl(InliningOptions.ShortMethod)] + get => SimdUtils.Shuffle.MmShuffle(1, 2, 3, 0); + } + + [MethodImpl(InliningOptions.ShortMethod)] + public void RunFallbackShuffle(ReadOnlySpan source, Span dest) + { + ref uint sBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); + ref uint dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(dest)); + int n = source.Length / 4; + + for (int i = 0; i < n; i++) + { + uint packed = Unsafe.Add(ref sBase, i); + + // packed = [W Z Y X] + // tmp1 = [0 Z 0 X] + // tmp2 = [W 0 Y 0] + // tmp3=ROTL(16, tmp2) = [Y 0 W 0] + // tmp1 + tmp3 = [Y Z W X] + uint tmp1 = packed & 0x00FF00FF; + uint tmp2 = packed & 0xFF00FF00; + uint tmp3 = Numerics.RotateLeft(tmp2, 16); Unsafe.Add(ref dBase, i) = tmp1 + tmp3; } diff --git a/src/ImageSharp/Formats/ImageExtensions.Save.cs b/src/ImageSharp/Formats/ImageExtensions.Save.cs index a6a65aef6..84f9d69b7 100644 --- a/src/ImageSharp/Formats/ImageExtensions.Save.cs +++ b/src/ImageSharp/Formats/ImageExtensions.Save.cs @@ -105,7 +105,6 @@ namespace SixLabors.ImageSharp /// The stream to save the image to. /// The encoder to save the image with. /// Thrown if the stream is null. - /// A representing the asynchronous operation. public static void SaveAsBmp(this Image source, Stream stream, BmpEncoder encoder) => source.Save( stream, @@ -208,7 +207,6 @@ namespace SixLabors.ImageSharp /// The stream to save the image to. /// The encoder to save the image with. /// Thrown if the stream is null. - /// A representing the asynchronous operation. public static void SaveAsGif(this Image source, Stream stream, GifEncoder encoder) => source.Save( stream, @@ -311,7 +309,6 @@ namespace SixLabors.ImageSharp /// The stream to save the image to. /// The encoder to save the image with. /// Thrown if the stream is null. - /// A representing the asynchronous operation. public static void SaveAsJpeg(this Image source, Stream stream, JpegEncoder encoder) => source.Save( stream, @@ -414,7 +411,6 @@ namespace SixLabors.ImageSharp /// The stream to save the image to. /// The encoder to save the image with. /// Thrown if the stream is null. - /// A representing the asynchronous operation. public static void SaveAsPbm(this Image source, Stream stream, PbmEncoder encoder) => source.Save( stream, @@ -517,7 +513,6 @@ namespace SixLabors.ImageSharp /// The stream to save the image to. /// The encoder to save the image with. /// Thrown if the stream is null. - /// A representing the asynchronous operation. public static void SaveAsPng(this Image source, Stream stream, PngEncoder encoder) => source.Save( stream, @@ -620,7 +615,6 @@ namespace SixLabors.ImageSharp /// The stream to save the image to. /// The encoder to save the image with. /// Thrown if the stream is null. - /// A representing the asynchronous operation. public static void SaveAsTga(this Image source, Stream stream, TgaEncoder encoder) => source.Save( stream, @@ -723,7 +717,6 @@ namespace SixLabors.ImageSharp /// The stream to save the image to. /// The encoder to save the image with. /// Thrown if the stream is null. - /// A representing the asynchronous operation. public static void SaveAsWebp(this Image source, Stream stream, WebpEncoder encoder) => source.Save( stream, @@ -826,7 +819,6 @@ namespace SixLabors.ImageSharp /// The stream to save the image to. /// The encoder to save the image with. /// Thrown if the stream is null. - /// A representing the asynchronous operation. public static void SaveAsTiff(this Image source, Stream stream, TiffEncoder encoder) => source.Save( stream, diff --git a/src/ImageSharp/Formats/ImageExtensions.Save.tt b/src/ImageSharp/Formats/ImageExtensions.Save.tt index c4a00b37c..ae7648522 100644 --- a/src/ImageSharp/Formats/ImageExtensions.Save.tt +++ b/src/ImageSharp/Formats/ImageExtensions.Save.tt @@ -124,7 +124,6 @@ namespace SixLabors.ImageSharp /// The stream to save the image to. /// The encoder to save the image with. /// Thrown if the stream is null. - /// A representing the asynchronous operation. public static void SaveAs<#= fmt #>(this Image source, Stream stream, <#= fmt #>Encoder encoder) => source.Save( stream, diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs index 4da422e7f..3804e1c6c 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs @@ -19,21 +19,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder this.Frame = frame; this.Id = id; - // Validate sampling factors. - if (horizontalFactor == 0 || verticalFactor == 0) - { - JpegThrowHelper.ThrowBadSampling(); - } - this.HorizontalSamplingFactor = horizontalFactor; this.VerticalSamplingFactor = verticalFactor; this.SamplingFactors = new Size(this.HorizontalSamplingFactor, this.VerticalSamplingFactor); - if (quantizationTableIndex > 3) - { - JpegThrowHelper.ThrowBadQuantizationTableIndex(quantizationTableIndex); - } - this.QuantizationTableIndex = quantizationTableIndex; this.Index = index; } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs index 87f11a085..40411ef28 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs @@ -179,7 +179,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder // PackFromRgbPlanes expects the destination to be padded, so try to get padded span containing extra elements from the next row. // If we can't get such a padded row because we are on a MemoryGroup boundary or at the last row, // pack pixels to a temporary, padded proxy buffer, then copy the relevant values to the destination row. - if (this.pixelBuffer.TryGetPaddedRowSpan(yy, 3, out Span destRow)) + if (this.pixelBuffer.DangerousTryGetPaddedRowSpan(yy, 3, out Span destRow)) { PixelOperations.Instance.PackFromRgbPlanes(this.configuration, r, g, b, destRow); } diff --git a/src/ImageSharp/Formats/Jpeg/Components/FastFloatingPointDCT.cs b/src/ImageSharp/Formats/Jpeg/Components/FastFloatingPointDCT.cs index 81bfe2135..c27ad5b82 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/FastFloatingPointDCT.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/FastFloatingPointDCT.cs @@ -146,11 +146,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components /// /// Apply floating point IDCT inplace using API. /// + /// + /// This method can be used even if there's no SIMD intrinsics available + /// as can be compiled to scalar instructions. + /// /// Input block. private static void IDCT_Vector4(ref Block8x8F transposedBlock) { - DebugGuard.IsTrue(Vector.IsHardwareAccelerated, "Scalar implementation should be called for non-accelerated hardware."); - // First pass - process columns IDCT8x4_Vector4(ref transposedBlock.V0L); IDCT8x4_Vector4(ref transposedBlock.V0R); diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index eda7a8dbe..61c5d7c66 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -485,7 +485,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg : JpegColorSpace.Cmyk; } - JpegThrowHelper.ThrowInvalidImageContentException($"Unsupported color mode. Supported component counts 1, 3, and 4; found {componentCount}"); + JpegThrowHelper.ThrowNotSupportedComponentCount(componentCount); return default; } @@ -1039,6 +1039,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg // 1 byte: Number of components byte componentCount = this.temp[5]; + // Validate: componentCount more than 4 can lead to a buffer overflow during stream + // reading so we must limit it to 4 + // We do not support jpeg images with more than 4 components anyway + if (componentCount > 4) + { + JpegThrowHelper.ThrowNotSupportedComponentCount(componentCount); + } + this.Frame = new JpegFrame(frameMarker, precision, frameWidth, frameHeight, componentCount); remaining -= length; @@ -1063,10 +1071,26 @@ namespace SixLabors.ImageSharp.Formats.Jpeg int index = 0; for (int i = 0; i < componentCount; i++) { + // 1 byte: component identifier + byte componentId = this.temp[index]; + + // 1 byte: component sampling factors byte hv = this.temp[index + 1]; int h = (hv >> 4) & 15; int v = hv & 15; + // Validate: 1-4 range + if (Numerics.IsOutOfRange(h, 1, 4)) + { + JpegThrowHelper.ThrowBadSampling(h); + } + + // Validate: 1-4 range + if (Numerics.IsOutOfRange(v, 1, 4)) + { + JpegThrowHelper.ThrowBadSampling(v); + } + if (maxH < h) { maxH = h; @@ -1077,10 +1101,19 @@ namespace SixLabors.ImageSharp.Formats.Jpeg maxV = v; } - var component = new JpegComponent(this.Configuration.MemoryAllocator, this.Frame, this.temp[index], h, v, this.temp[index + 2], i); + // 1 byte: quantization table destination selector + byte quantTableIndex = this.temp[index + 2]; + + // Validate: 0-3 range + if (quantTableIndex > 3) + { + JpegThrowHelper.ThrowBadQuantizationTableIndex(quantTableIndex); + } + + var component = new JpegComponent(this.Configuration.MemoryAllocator, this.Frame, componentId, h, v, quantTableIndex, i); this.Frame.Components[i] = component; - this.Frame.ComponentIds[i] = component.Id; + this.Frame.ComponentIds[i] = componentId; index += componentBytes; } diff --git a/src/ImageSharp/Formats/Jpeg/JpegThrowHelper.cs b/src/ImageSharp/Formats/Jpeg/JpegThrowHelper.cs index b6c7e7626..b238e45ef 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegThrowHelper.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegThrowHelper.cs @@ -22,23 +22,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg [MethodImpl(InliningOptions.ColdPath)] public static void ThrowInvalidImageContentException(string errorMessage) => throw new InvalidImageContentException(errorMessage); - /// - /// Cold path optimization for throwing 's. - /// - /// The error message for the exception. - /// The exception that is the cause of the current exception, or a null reference - /// if no inner exception is specified. - [MethodImpl(InliningOptions.ColdPath)] - public static void ThrowInvalidImageContentException(string errorMessage, Exception innerException) => throw new InvalidImageContentException(errorMessage, innerException); - - /// - /// Cold path optimization for throwing 's - /// - /// The error message for the exception. - [MethodImpl(InliningOptions.ColdPath)] - public static void ThrowNotImplementedException(string errorMessage) - => throw new NotImplementedException(errorMessage); - [MethodImpl(InliningOptions.ColdPath)] public static void ThrowBadMarker(string marker, int length) => throw new InvalidImageContentException($"Marker {marker} has bad length {length}."); @@ -51,6 +34,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg [MethodImpl(InliningOptions.ColdPath)] public static void ThrowBadSampling() => throw new InvalidImageContentException("Bad sampling factor."); + [MethodImpl(InliningOptions.ColdPath)] + public static void ThrowBadSampling(int factor) => throw new InvalidImageContentException($"Bad sampling factor: {factor}"); + [MethodImpl(InliningOptions.ColdPath)] public static void ThrowBadProgressiveScan(int ss, int se, int ah, int al) => throw new InvalidImageContentException($"Invalid progressive parameters Ss={ss} Se={se} Ah={ah} Al={al}."); @@ -59,5 +45,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg [MethodImpl(InliningOptions.ColdPath)] public static void ThrowDimensionsTooLarge(int width, int height) => throw new ImageFormatException($"Image is too large to encode at {width}x{height} for JPEG format."); + + [MethodImpl(InliningOptions.ColdPath)] + public static void ThrowNotSupportedComponentCount(int componentCount) => throw new NotSupportedException($"Images with {componentCount} components are not supported."); } } diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index b32e79299..f5fc86ee4 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -11,6 +11,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; using System.Threading; +using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Compression.Zlib; using SixLabors.ImageSharp.Formats.Png.Chunks; using SixLabors.ImageSharp.Formats.Png.Filters; @@ -188,10 +189,10 @@ namespace SixLabors.ImageSharp.Formats.Png this.AssignTransparentMarkers(alpha, pngMetadata); break; case PngChunkType.Text: - this.ReadTextChunk(pngMetadata, chunk.Data.GetSpan()); + this.ReadTextChunk(metadata, pngMetadata, chunk.Data.GetSpan()); break; case PngChunkType.CompressedText: - this.ReadCompressedTextChunk(pngMetadata, chunk.Data.GetSpan()); + this.ReadCompressedTextChunk(metadata, pngMetadata, chunk.Data.GetSpan()); break; case PngChunkType.InternationalText: this.ReadInternationalTextChunk(metadata, chunk.Data.GetSpan()); @@ -201,7 +202,7 @@ namespace SixLabors.ImageSharp.Formats.Png { byte[] exifData = new byte[chunk.Length]; chunk.Data.GetSpan().CopyTo(exifData); - metadata.ExifProfile = new ExifProfile(exifData); + this.MergeOrSetExifProfile(metadata, new ExifProfile(exifData), replaceExistingKeys: true); } break; @@ -298,7 +299,7 @@ namespace SixLabors.ImageSharp.Formats.Png break; } - this.ReadTextChunk(pngMetadata, chunk.Data.GetSpan()); + this.ReadTextChunk(metadata, pngMetadata, chunk.Data.GetSpan()); break; case PngChunkType.CompressedText: if (this.colorMetadataOnly) @@ -307,7 +308,7 @@ namespace SixLabors.ImageSharp.Formats.Png break; } - this.ReadCompressedTextChunk(pngMetadata, chunk.Data.GetSpan()); + this.ReadCompressedTextChunk(metadata, pngMetadata, chunk.Data.GetSpan()); break; case PngChunkType.InternationalText: if (this.colorMetadataOnly) @@ -329,7 +330,7 @@ namespace SixLabors.ImageSharp.Formats.Png { byte[] exifData = new byte[chunk.Length]; chunk.Data.GetSpan().CopyTo(exifData); - metadata.ExifProfile = new ExifProfile(exifData); + this.MergeOrSetExifProfile(metadata, new ExifProfile(exifData), replaceExistingKeys: true); } break; @@ -968,9 +969,10 @@ namespace SixLabors.ImageSharp.Formats.Png /// /// Reads a text chunk containing image properties from the data. /// + /// The object. /// The metadata to decode to. /// The containing the data. - private void ReadTextChunk(PngMetadata metadata, ReadOnlySpan data) + private void ReadTextChunk(ImageMetadata baseMetadata, PngMetadata metadata, ReadOnlySpan data) { if (this.ignoreMetadata) { @@ -993,15 +995,19 @@ namespace SixLabors.ImageSharp.Formats.Png string value = PngConstants.Encoding.GetString(data.Slice(zeroIndex + 1)); - metadata.TextData.Add(new PngTextData(name, value, string.Empty, string.Empty)); + if (!this.TryReadTextChunkMetadata(baseMetadata, name, value)) + { + metadata.TextData.Add(new PngTextData(name, value, string.Empty, string.Empty)); + } } /// /// Reads the compressed text chunk. Contains a uncompressed keyword and a compressed text string. /// + /// The object. /// The metadata to decode to. /// The containing the data. - private void ReadCompressedTextChunk(PngMetadata metadata, ReadOnlySpan data) + private void ReadCompressedTextChunk(ImageMetadata baseMetadata, PngMetadata metadata, ReadOnlySpan data) { if (this.ignoreMetadata) { @@ -1029,12 +1035,185 @@ namespace SixLabors.ImageSharp.Formats.Png ReadOnlySpan compressedData = data.Slice(zeroIndex + 2); - if (this.TryUncompressTextData(compressedData, PngConstants.Encoding, out string uncompressed)) + if (this.TryUncompressTextData(compressedData, PngConstants.Encoding, out string uncompressed) && + !this.TryReadTextChunkMetadata(baseMetadata, name, uncompressed)) { metadata.TextData.Add(new PngTextData(name, uncompressed, string.Empty, string.Empty)); } } + /// + /// Checks if the given text chunk is actually storing parsable metadata. + /// + /// The object to store the parsed metadata in. + /// The name of the text chunk. + /// The contents of the text chunk. + /// True if metadata was successfully parsed from the text chunk. False if the + /// text chunk was not identified as metadata, and should be stored in the metadata + /// object unmodified. + private bool TryReadTextChunkMetadata(ImageMetadata baseMetadata, string chunkName, string chunkText) + { + if (chunkName.Equals("Raw profile type exif", StringComparison.OrdinalIgnoreCase) && + this.TryReadLegacyExifTextChunk(baseMetadata, chunkText)) + { + // Successfully parsed legacy exif data from text + return true; + } + + // TODO: "Raw profile type iptc", potentially others? + + // No special chunk data identified + return false; + } + + /// + /// Reads exif data encoded into a text chunk with the name "raw profile type exif". + /// This method was used by ImageMagick, exiftool, exiv2, digiKam, etc, before the + /// 2017 update to png that allowed a true exif chunk. + /// + /// The to store the decoded exif tags into. + /// The contents of the "raw profile type exif" text chunk. + private bool TryReadLegacyExifTextChunk(ImageMetadata metadata, string data) + { + ReadOnlySpan dataSpan = data.AsSpan(); + dataSpan = dataSpan.TrimStart(); + + if (!StringEqualsInsensitive(dataSpan.Slice(0, 4), "exif".AsSpan())) + { + // "exif" identifier is missing from the beginning of the text chunk + return false; + } + + // Skip to the data length + dataSpan = dataSpan.Slice(4).TrimStart(); + int dataLengthEnd = dataSpan.IndexOf('\n'); + int dataLength = ParseInt32(dataSpan.Slice(0, dataSpan.IndexOf('\n'))); + + // Skip to the hex-encoded data + dataSpan = dataSpan.Slice(dataLengthEnd).Trim(); + + // Sequence of bytes for the exif header ("Exif" ASCII and two zero bytes). + // This doesn't actually allocate. + ReadOnlySpan exifHeader = new byte[] { 0x45, 0x78, 0x69, 0x66, 0x00, 0x00 }; + + if (dataLength < exifHeader.Length) + { + // Not enough room for the required exif header, this data couldn't possibly be valid + return false; + } + + // Parse the hex-encoded data into the byte array we are going to hand off to ExifProfile + byte[] exifBlob = new byte[dataLength - exifHeader.Length]; + + try + { + // Check for the presence of the exif header in the hex-encoded binary data + byte[] tempExifBuf = exifBlob; + if (exifBlob.Length < exifHeader.Length) + { + // Need to allocate a temporary array, this should be an extremely uncommon (TODO: impossible?) case + tempExifBuf = new byte[exifHeader.Length]; + } + + HexConverter.HexStringToBytes(dataSpan.Slice(0, exifHeader.Length * 2), tempExifBuf); + if (!tempExifBuf.AsSpan().Slice(0, exifHeader.Length).SequenceEqual(exifHeader)) + { + // Exif header in the hex data is not valid + return false; + } + + // Skip over the exif header we just tested + dataSpan = dataSpan.Slice(exifHeader.Length * 2); + dataLength -= exifHeader.Length; + + // Load the hex-encoded data, one line at a time + for (int i = 0; i < dataLength;) + { + ReadOnlySpan lineSpan = dataSpan; + + int newlineIndex = dataSpan.IndexOf('\n'); + if (newlineIndex != -1) + { + lineSpan = dataSpan.Slice(0, newlineIndex); + } + + i += HexConverter.HexStringToBytes(lineSpan, exifBlob.AsSpan().Slice(i)); + + dataSpan = dataSpan.Slice(newlineIndex + 1); + } + } + catch + { + return false; + } + + this.MergeOrSetExifProfile(metadata, new ExifProfile(exifBlob), replaceExistingKeys: false); + return true; + } + + /// + /// Compares two ReadOnlySpan<char>s in a case-insensitive method. + /// This is only needed because older frameworks are missing the extension method. + /// + /// The first to compare. + /// The second to compare. + /// True if the spans were identical, false otherwise. + private static bool StringEqualsInsensitive(ReadOnlySpan span1, ReadOnlySpan span2) + { +#pragma warning disable IDE0022 // Use expression body for methods +#if NETSTANDARD2_1 || NETCOREAPP2_1_OR_GREATER + return span1.Equals(span2, StringComparison.OrdinalIgnoreCase); +#else + return span1.ToString().Equals(span2.ToString(), StringComparison.OrdinalIgnoreCase); +#endif +#pragma warning restore IDE0022 // Use expression body for methods + } + + /// + /// int.Parse() a ReadOnlySpan<char>, with a fallback for older frameworks. + /// + /// The to parse. + /// The parsed . + private static int ParseInt32(ReadOnlySpan span) + { +#pragma warning disable IDE0022 // Use expression body for methods +#if NETSTANDARD2_1 || NETCOREAPP2_1_OR_GREATER + return int.Parse(span); +#else + return int.Parse(span.ToString()); +#endif +#pragma warning restore IDE0022 // Use expression body for methods + } + + /// + /// Sets the in to , + /// or copies exif tags if already contains an . + /// + /// The to store the exif data in. + /// The to copy exif tags from. + /// If already contains an , + /// controls whether existing exif tags in will be overwritten with any conflicting + /// tags from . + private void MergeOrSetExifProfile(ImageMetadata metadata, ExifProfile newProfile, bool replaceExistingKeys) + { + if (metadata.ExifProfile is null) + { + // No exif metadata was loaded yet, so just assign it + metadata.ExifProfile = newProfile; + } + else + { + // Try to merge existing keys with the ones from the new profile + foreach (IExifValue newKey in newProfile.Values) + { + if (replaceExistingKeys || metadata.ExifProfile.GetValueInternal(newKey.Tag) is null) + { + metadata.ExifProfile.SetValueInternal(newKey.Tag, newKey.GetValue()); + } + } + } + } + /// /// Reads a iTXt chunk, which contains international text data. It contains: /// - A uncompressed keyword. @@ -1213,16 +1392,16 @@ namespace SixLabors.ImageSharp.Formats.Png PngChunkType type = this.ReadChunkType(); - // NOTE: Reading the Data chunk is the responsible of the caller // If we're reading color metadata only we're only interested in the IHDR and tRNS chunks. // We can skip all other chunk data in the stream for better performance. - if (type == PngChunkType.Data || (this.colorMetadataOnly && type != PngChunkType.Header && type != PngChunkType.Transparency)) + if (this.colorMetadataOnly && type != PngChunkType.Header && type != PngChunkType.Transparency) { chunk = new PngChunk(length, type); return true; } + long pos = this.currentStream.Position; chunk = new PngChunk( length: length, type: type, @@ -1230,6 +1409,13 @@ namespace SixLabors.ImageSharp.Formats.Png this.ValidateChunk(chunk); + // Restore the stream position for IDAT chunks, because it will be decoded later and + // was only read to verifying the CRC is correct. + if (type == PngChunkType.Data) + { + this.currentStream.Position = pos; + } + return true; } diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffBaseDecompressor.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffBaseDecompressor.cs index d183a33bd..986d585d3 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffBaseDecompressor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffBaseDecompressor.cs @@ -34,17 +34,15 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression /// The number of bytes to read from the input stream. /// The height of the strip. /// The output buffer for uncompressed data. - public void Decompress(BufferedReadStream stream, uint stripOffset, uint stripByteCount, int stripHeight, Span buffer) + public void Decompress(BufferedReadStream stream, ulong stripOffset, ulong stripByteCount, int stripHeight, Span buffer) { - if (stripByteCount > int.MaxValue) - { - TiffThrowHelper.ThrowImageFormatException("The StripByteCount value is too big."); - } + DebugGuard.MustBeLessThanOrEqualTo(stripOffset, (ulong)long.MaxValue, nameof(stripOffset)); + DebugGuard.MustBeLessThanOrEqualTo(stripByteCount, (ulong)long.MaxValue, nameof(stripByteCount)); - stream.Seek(stripOffset, SeekOrigin.Begin); + stream.Seek((long)stripOffset, SeekOrigin.Begin); this.Decompress(stream, (int)stripByteCount, stripHeight, buffer); - if (stripOffset + stripByteCount < stream.Position) + if ((long)stripOffset + (long)stripByteCount < stream.Position) { TiffThrowHelper.ThrowImageFormatException("Out of range when reading a strip."); } diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs index b54545141..b5548c707 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs @@ -35,6 +35,16 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Constants /// public const ushort HeaderMagicNumber = 42; + /// + /// The big tiff header magic number + /// + public const ushort BigTiffHeaderMagicNumber = 43; + + /// + /// The big tiff bytesize of offsets value. + /// + public const ushort BigTiffBytesize = 8; + /// /// RowsPerStrip default value, which is effectively infinity. /// diff --git a/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs b/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs index 26cb12d42..cce1d278c 100644 --- a/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs +++ b/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.IO; using SixLabors.ImageSharp.Formats.Tiff.Constants; +using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata.Profiles.Exif; namespace SixLabors.ImageSharp.Formats.Tiff @@ -14,23 +15,27 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// internal class DirectoryReader { - private readonly Stream stream; + private const int DirectoryMax = 65534; - private uint nextIfdOffset; + private readonly Stream stream; - private const int DirectoryMax = 65534; + private readonly MemoryAllocator allocator; - // used for sequential read big values (actual for multiframe big files) - // todo: different tags can link to the same data (stream offset) - investigate - private readonly SortedList lazyLoaders = new(new DuplicateKeyComparer()); + private ulong nextIfdOffset; - public DirectoryReader(Stream stream) => this.stream = stream; + public DirectoryReader(Stream stream, MemoryAllocator allocator) + { + this.stream = stream; + this.allocator = allocator; + } /// /// Gets the byte order. /// public ByteOrder ByteOrder { get; private set; } + public bool IsBigTiff { get; private set; } + /// /// Reads image file directories. /// @@ -38,14 +43,19 @@ namespace SixLabors.ImageSharp.Formats.Tiff public IEnumerable Read() { this.ByteOrder = ReadByteOrder(this.stream); - this.nextIfdOffset = new HeaderReader(this.stream, this.ByteOrder).ReadFileHeader(); - return this.ReadIfds(); + var headerReader = new HeaderReader(this.stream, this.ByteOrder); + headerReader.ReadFileHeader(); + + this.nextIfdOffset = headerReader.FirstIfdOffset; + this.IsBigTiff = headerReader.IsBigTiff; + + return this.ReadIfds(headerReader.IsBigTiff); } private static ByteOrder ReadByteOrder(Stream stream) { - var headerBytes = new byte[2]; - stream.Read(headerBytes, 0, 2); + Span headerBytes = stackalloc byte[2]; + stream.Read(headerBytes); if (headerBytes[0] == TiffConstants.ByteOrderLittleEndian && headerBytes[1] == TiffConstants.ByteOrderLittleEndian) { return ByteOrder.LittleEndian; @@ -59,16 +69,26 @@ namespace SixLabors.ImageSharp.Formats.Tiff throw TiffThrowHelper.ThrowInvalidHeader(); } - private IEnumerable ReadIfds() + private IEnumerable ReadIfds(bool isBigTiff) { var readers = new List(); - while (this.nextIfdOffset != 0 && this.nextIfdOffset < this.stream.Length) + while (this.nextIfdOffset != 0 && this.nextIfdOffset < (ulong)this.stream.Length) { - var reader = new EntryReader(this.stream, this.ByteOrder, this.nextIfdOffset, this.lazyLoaders); - reader.ReadTags(); + var reader = new EntryReader(this.stream, this.ByteOrder, this.allocator); + reader.ReadTags(isBigTiff, this.nextIfdOffset); - this.nextIfdOffset = reader.NextIfdOffset; + if (reader.BigValues.Count > 0) + { + reader.BigValues.Sort((t1, t2) => t1.Offset.CompareTo(t2.Offset)); + + // this means that most likely all elements are placed before next IFD + if (reader.BigValues[0].Offset < reader.NextIfdOffset) + { + reader.ReadBigValues(); + } + } + this.nextIfdOffset = reader.NextIfdOffset; readers.Add(reader); if (readers.Count >= DirectoryMax) @@ -77,36 +97,15 @@ namespace SixLabors.ImageSharp.Formats.Tiff } } - // Sequential reading big values. - foreach (Action loader in this.lazyLoaders.Values) - { - loader(); - } - - var list = new List(); + var list = new List(readers.Count); foreach (EntryReader reader in readers) { + reader.ReadBigValues(); var profile = new ExifProfile(reader.Values, reader.InvalidTags); list.Add(profile); } return list; } - - /// - /// used for possibility add a duplicate offsets (but tags don't duplicate). - /// - /// The type of the key. - private class DuplicateKeyComparer : IComparer - where TKey : IComparable - { - public int Compare(TKey x, TKey y) - { - int result = x.CompareTo(y); - - // Handle equality as being greater. - return (result == 0) ? 1 : result; - } - } } } diff --git a/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs b/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs index be4f59bff..922b7bf07 100644 --- a/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs +++ b/src/ImageSharp/Formats/Tiff/Ifd/EntryReader.cs @@ -1,64 +1,78 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using System; using System.Collections.Generic; using System.IO; using SixLabors.ImageSharp.Formats.Tiff.Constants; +using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata.Profiles.Exif; namespace SixLabors.ImageSharp.Formats.Tiff { internal class EntryReader : BaseExifReader { - private readonly uint startOffset; - - private readonly SortedList lazyLoaders; - - public EntryReader(Stream stream, ByteOrder byteOrder, uint ifdOffset, SortedList lazyLoaders) - : base(stream) - { + public EntryReader(Stream stream, ByteOrder byteOrder, MemoryAllocator allocator) + : base(stream, allocator) => this.IsBigEndian = byteOrder == ByteOrder.BigEndian; - this.startOffset = ifdOffset; - this.lazyLoaders = lazyLoaders; - } public List Values { get; } = new(); - public uint NextIfdOffset { get; private set; } + public ulong NextIfdOffset { get; private set; } - public void ReadTags() + public void ReadTags(bool isBigTiff, ulong ifdOffset) { - this.ReadValues(this.Values, this.startOffset); - this.NextIfdOffset = this.ReadUInt32(); + if (!isBigTiff) + { + this.ReadValues(this.Values, (uint)ifdOffset); + this.NextIfdOffset = this.ReadUInt32(); + + this.ReadSubIfd(this.Values); + } + else + { + this.ReadValues64(this.Values, ifdOffset); + this.NextIfdOffset = this.ReadUInt64(); - this.ReadSubIfd(this.Values); + //// this.ReadSubIfd64(this.Values); + } } - protected override void RegisterExtLoader(uint offset, Action reader) => - this.lazyLoaders.Add(offset, reader); + public void ReadBigValues() => this.ReadBigValues(this.Values); } internal class HeaderReader : BaseExifReader { public HeaderReader(Stream stream, ByteOrder byteOrder) - : base(stream) => + : base(stream, null) => this.IsBigEndian = byteOrder == ByteOrder.BigEndian; - public uint FirstIfdOffset { get; private set; } + public bool IsBigTiff { get; private set; } - public uint ReadFileHeader() + public ulong FirstIfdOffset { get; private set; } + + public void ReadFileHeader() { ushort magic = this.ReadUInt16(); - if (magic != TiffConstants.HeaderMagicNumber) + if (magic == TiffConstants.HeaderMagicNumber) { - TiffThrowHelper.ThrowInvalidHeader(); + this.IsBigTiff = false; + this.FirstIfdOffset = this.ReadUInt32(); + return; } + else if (magic == TiffConstants.BigTiffHeaderMagicNumber) + { + this.IsBigTiff = true; - this.FirstIfdOffset = this.ReadUInt32(); - return this.FirstIfdOffset; - } + ushort bytesize = this.ReadUInt16(); + ushort reserve = this.ReadUInt16(); + if (bytesize == TiffConstants.BigTiffBytesize && reserve == 0) + { + this.FirstIfdOffset = this.ReadUInt64(); + return; + } + } - protected override void RegisterExtLoader(uint offset, Action reader) => throw new NotSupportedException(); + TiffThrowHelper.ThrowInvalidHeader(); + } } } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 20661b982..05c5358f5 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System; using System.Buffers; using System.Collections.Generic; using System.Linq; @@ -42,6 +43,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// private ByteOrder byteOrder; + /// + /// Indicating whether is BigTiff format. + /// + private bool isBigTiff; + /// /// Initializes a new instance of the class. /// @@ -142,10 +148,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff where TPixel : unmanaged, IPixel { this.inputStream = stream; - var reader = new DirectoryReader(stream); + var reader = new DirectoryReader(stream, this.Configuration.MemoryAllocator); IEnumerable directories = reader.Read(); this.byteOrder = reader.ByteOrder; + this.isBigTiff = reader.IsBigTiff; var frames = new List>(); foreach (ExifProfile ifd in directories) @@ -155,7 +162,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff frames.Add(frame); } - ImageMetadata metadata = TiffDecoderMetadataCreator.Create(frames, this.ignoreMetadata, reader.ByteOrder); + ImageMetadata metadata = TiffDecoderMetadataCreator.Create(frames, this.ignoreMetadata, reader.ByteOrder, reader.IsBigTiff); // TODO: Tiff frames can have different sizes ImageFrame root = frames[0]; @@ -175,13 +182,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff public IImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) { this.inputStream = stream; - var reader = new DirectoryReader(stream); + var reader = new DirectoryReader(stream, this.Configuration.MemoryAllocator); IEnumerable directories = reader.Read(); ExifProfile rootFrameExifProfile = directories.First(); var rootMetadata = TiffFrameMetadata.Parse(rootFrameExifProfile); - ImageMetadata metadata = TiffDecoderMetadataCreator.Create(reader.ByteOrder, rootFrameExifProfile); + ImageMetadata metadata = TiffDecoderMetadataCreator.Create(reader.ByteOrder, reader.IsBigTiff, rootFrameExifProfile); int width = GetImageWidth(rootFrameExifProfile); int height = GetImageHeight(rootFrameExifProfile); @@ -214,21 +221,58 @@ namespace SixLabors.ImageSharp.Formats.Tiff var frame = new ImageFrame(this.Configuration, width, height, imageFrameMetaData); int rowsPerStrip = tags.GetValue(ExifTag.RowsPerStrip) != null ? (int)tags.GetValue(ExifTag.RowsPerStrip).Value : TiffConstants.RowsPerStripInfinity; - Number[] stripOffsets = tags.GetValue(ExifTag.StripOffsets)?.Value; - Number[] stripByteCounts = tags.GetValue(ExifTag.StripByteCounts)?.Value; + + var stripOffsetsArray = (Array)tags.GetValueInternal(ExifTag.StripOffsets).GetValue(); + var stripByteCountsArray = (Array)tags.GetValueInternal(ExifTag.StripByteCounts).GetValue(); + + IMemoryOwner stripOffsetsMemory = this.ConvertNumbers(stripOffsetsArray, out Span stripOffsets); + IMemoryOwner stripByteCountsMemory = this.ConvertNumbers(stripByteCountsArray, out Span stripByteCounts); if (this.PlanarConfiguration == TiffPlanarConfiguration.Planar) { - this.DecodeStripsPlanar(frame, rowsPerStrip, stripOffsets, stripByteCounts, cancellationToken); + this.DecodeStripsPlanar( + frame, + rowsPerStrip, + stripOffsets, + stripByteCounts, + cancellationToken); } else { - this.DecodeStripsChunky(frame, rowsPerStrip, stripOffsets, stripByteCounts, cancellationToken); + this.DecodeStripsChunky( + frame, + rowsPerStrip, + stripOffsets, + stripByteCounts, + cancellationToken); } + stripOffsetsMemory?.Dispose(); + stripByteCountsMemory?.Dispose(); return frame; } + private IMemoryOwner ConvertNumbers(Array array, out Span span) + { + if (array is Number[] numbers) + { + IMemoryOwner memory = this.memoryAllocator.Allocate(numbers.Length); + span = memory.GetSpan(); + for (int i = 0; i < numbers.Length; i++) + { + span[i] = (uint)numbers[i]; + } + + return memory; + } + else + { + DebugGuard.IsTrue(array is ulong[], $"Expected {nameof(UInt64)} array."); + span = (ulong[])array; + return null; + } + } + /// /// Calculates the size (in bytes) for a pixel buffer using the determined color format. /// @@ -279,7 +323,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// An array of byte offsets to each strip in the image. /// An array of the size of each strip (in bytes). /// The token to monitor cancellation. - private void DecodeStripsPlanar(ImageFrame frame, int rowsPerStrip, Number[] stripOffsets, Number[] stripByteCounts, CancellationToken cancellationToken) + private void DecodeStripsPlanar(ImageFrame frame, int rowsPerStrip, Span stripOffsets, Span stripByteCounts, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { int stripsPerPixel = this.BitsPerSample.Channels; @@ -332,10 +376,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff { decompressor.Decompress( this.inputStream, - (uint)stripOffsets[stripIndex], - (uint)stripByteCounts[stripIndex], + stripOffsets[stripIndex], + stripByteCounts[stripIndex], stripHeight, stripBuffers[planeIndex].GetSpan()); + stripIndex += stripsPerPlane; } @@ -360,7 +405,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The strip offsets. /// The strip byte counts. /// The token to monitor cancellation. - private void DecodeStripsChunky(ImageFrame frame, int rowsPerStrip, Number[] stripOffsets, Number[] stripByteCounts, CancellationToken cancellationToken) + private void DecodeStripsChunky(ImageFrame frame, int rowsPerStrip, Span stripOffsets, Span stripByteCounts, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { // If the rowsPerStrip has the default value, which is effectively infinity. That is, the entire image is one strip. @@ -373,7 +418,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff int bitsPerPixel = this.BitsPerPixel; using IMemoryOwner stripBuffer = this.memoryAllocator.Allocate(uncompressedStripSize, AllocationOptions.Clean); - System.Span stripBufferSpan = stripBuffer.GetSpan(); + Span stripBufferSpan = stripBuffer.GetSpan(); Buffer2D pixels = frame.PixelBuffer; using TiffBaseDecompressor decompressor = TiffDecompressorsFactory.Create( @@ -416,7 +461,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff break; } - decompressor.Decompress(this.inputStream, (uint)stripOffsets[stripIndex], (uint)stripByteCounts[stripIndex], stripHeight, stripBufferSpan); + decompressor.Decompress( + this.inputStream, + stripOffsets[stripIndex], + stripByteCounts[stripIndex], + stripHeight, + stripBufferSpan); colorDecoder.Decode(stripBufferSpan, pixels, 0, top, frame.Width, stripHeight); } @@ -435,6 +485,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff TiffThrowHelper.ThrowImageFormatException("The TIFF image frame is missing the ImageWidth"); } + DebugGuard.MustBeLessThanOrEqualTo((ulong)width.Value, (ulong)int.MaxValue, nameof(ExifTag.ImageWidth)); + return (int)width.Value; } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs index ebfe146ca..ddbfbcb48 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs @@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// internal static class TiffDecoderMetadataCreator { - public static ImageMetadata Create(List> frames, bool ignoreMetadata, ByteOrder byteOrder) + public static ImageMetadata Create(List> frames, bool ignoreMetadata, ByteOrder byteOrder, bool isBigTiff) where TPixel : unmanaged, IPixel { if (frames.Count < 1) @@ -27,13 +27,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff TiffThrowHelper.ThrowImageFormatException("Expected at least one frame."); } - var imageMetaData = new ImageMetadata(); - ExifProfile exifProfileRootFrame = frames[0].Metadata.ExifProfile; - - SetResolution(imageMetaData, exifProfileRootFrame); - - TiffMetadata tiffMetadata = imageMetaData.GetTiffMetadata(); - tiffMetadata.ByteOrder = byteOrder; + ImageMetadata imageMetaData = Create(byteOrder, isBigTiff, frames[0].Metadata.ExifProfile); if (!ignoreMetadata) { @@ -63,13 +57,14 @@ namespace SixLabors.ImageSharp.Formats.Tiff return imageMetaData; } - public static ImageMetadata Create(ByteOrder byteOrder, ExifProfile exifProfile) + public static ImageMetadata Create(ByteOrder byteOrder, bool isBigTiff, ExifProfile exifProfile) { var imageMetaData = new ImageMetadata(); SetResolution(imageMetaData, exifProfile); TiffMetadata tiffMetadata = imageMetaData.GetTiffMetadata(); tiffMetadata.ByteOrder = byteOrder; + tiffMetadata.FormatType = isBigTiff ? TiffFormatType.BigTIFF : TiffFormatType.Default; return imageMetaData; } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs index a8420fabb..a187d444a 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs @@ -24,12 +24,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The IFD entries container to read the image format information for current frame. public static void VerifyAndParse(this TiffDecoderCore options, ExifProfile exifProfile, TiffFrameMetadata frameMetadata) { - if (exifProfile.GetValue(ExifTag.TileOffsets)?.Value != null) + if (exifProfile.GetValueInternal(ExifTag.TileOffsets) is not null || exifProfile.GetValueInternal(ExifTag.TileByteCounts) is not null) { TiffThrowHelper.ThrowNotSupported("Tiled images are not supported."); } - if (exifProfile.GetValue(ExifTag.ExtraSamples)?.Value != null) + if (exifProfile.GetValueInternal(ExifTag.ExtraSamples) is not null) { TiffThrowHelper.ThrowNotSupported("ExtraSamples is not supported."); } @@ -95,12 +95,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff private static void VerifyRequiredFieldsArePresent(ExifProfile exifProfile, TiffFrameMetadata frameMetadata) { - if (exifProfile.GetValue(ExifTag.StripOffsets) == null) + if (exifProfile.GetValueInternal(ExifTag.StripOffsets) is null) { TiffThrowHelper.ThrowImageFormatException("StripOffsets are missing and are required for decoding the TIFF image!"); } - if (exifProfile.GetValue(ExifTag.StripByteCounts) == null) + if (exifProfile.GetValueInternal(ExifTag.StripByteCounts) is null) { TiffThrowHelper.ThrowImageFormatException("StripByteCounts are missing and are required for decoding the TIFF image!"); } @@ -384,7 +384,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff private static void ParseCompression(this TiffDecoderCore options, TiffCompression? compression, ExifProfile exifProfile) { - switch (compression) + // Default 1 (No compression) https://www.awaresystems.be/imaging/tiff/tifftags/compression.html + switch (compression ?? TiffCompression.None) { case TiffCompression.None: { @@ -441,7 +442,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff default: { - TiffThrowHelper.ThrowNotSupported($"The specified TIFF compression format {compression} is not supported"); + TiffThrowHelper.ThrowNotSupported($"The specified TIFF compression format '{compression}' is not supported"); break; } } diff --git a/src/ImageSharp/Formats/Tiff/TiffFormatType.cs b/src/ImageSharp/Formats/Tiff/TiffFormatType.cs new file mode 100644 index 000000000..320386e4c --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/TiffFormatType.cs @@ -0,0 +1,21 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Formats.Tiff +{ + /// + /// The TIFF format type enum. + /// + public enum TiffFormatType + { + /// + /// The TIFF file format type. + /// + Default, + + /// + /// The BigTIFF format type. + /// + BigTIFF + } +} diff --git a/src/ImageSharp/Formats/Tiff/TiffImageFormatDetector.cs b/src/ImageSharp/Formats/Tiff/TiffImageFormatDetector.cs index fc2ad3e13..51280b4bf 100644 --- a/src/ImageSharp/Formats/Tiff/TiffImageFormatDetector.cs +++ b/src/ImageSharp/Formats/Tiff/TiffImageFormatDetector.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff public sealed class TiffImageFormatDetector : IImageFormatDetector { /// - public int HeaderSize => 4; + public int HeaderSize => 8; /// public IImageFormat DetectFormat(ReadOnlySpan header) @@ -26,9 +26,42 @@ namespace SixLabors.ImageSharp.Formats.Tiff private bool IsSupportedFileFormat(ReadOnlySpan header) { - return header.Length >= this.HeaderSize && - ((header[0] == 0x49 && header[1] == 0x49 && header[2] == 0x2A && header[3] == 0x00) || // Little-endian - (header[0] == 0x4D && header[1] == 0x4D && header[2] == 0x00 && header[3] == 0x2A)); // Big-endian + if (header.Length >= this.HeaderSize) + { + if (header[0] == 0x49 && header[1] == 0x49) + { + // Little-endian + if (header[2] == 0x2A && header[3] == 0x00) + { + // tiff + return true; + } + else if (header[2] == 0x2B && header[3] == 0x00 + && header[4] == 8 && header[5] == 0 && header[6] == 0 && header[7] == 0) + { + // big tiff + return true; + } + } + else if (header[0] == 0x4D && header[1] == 0x4D) + { + // Big-endian + if (header[2] == 0 && header[3] == 0x2A) + { + // tiff + return true; + } + else + if (header[2] == 0 && header[3] == 0x2B + && header[4] == 0 && header[5] == 8 && header[6] == 0 && header[7] == 0) + { + // big tiff + return true; + } + } + } + + return false; } } } diff --git a/src/ImageSharp/Formats/Tiff/TiffMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffMetadata.cs index cf1ab3754..f5124a78a 100644 --- a/src/ImageSharp/Formats/Tiff/TiffMetadata.cs +++ b/src/ImageSharp/Formats/Tiff/TiffMetadata.cs @@ -26,6 +26,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// public ByteOrder ByteOrder { get; set; } + /// + /// Gets or sets the format type. + /// + public TiffFormatType FormatType { get; set; } + /// public IDeepCloneable DeepClone() => new TiffMetadata(this); } diff --git a/src/ImageSharp/Formats/Tiff/TiffThrowHelper.cs b/src/ImageSharp/Formats/Tiff/TiffThrowHelper.cs index 3c541a786..8223c445a 100644 --- a/src/ImageSharp/Formats/Tiff/TiffThrowHelper.cs +++ b/src/ImageSharp/Formats/Tiff/TiffThrowHelper.cs @@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// /// The error message for the exception. [MethodImpl(MethodImplOptions.NoInlining)] - public static void ThrowImageFormatException(string errorMessage) => throw new ImageFormatException(errorMessage); + public static Exception ThrowImageFormatException(string errorMessage) => throw new ImageFormatException(errorMessage); [MethodImpl(InliningOptions.ColdPath)] public static Exception NotSupportedDecompressor(string compressionType) => throw new NotSupportedException($"Not supported decoder compression method: {compressionType}"); diff --git a/src/ImageSharp/Formats/Webp/Lossy/Vp8Residual.cs b/src/ImageSharp/Formats/Webp/Lossy/Vp8Residual.cs index 4eeeedd37..5e4591894 100644 --- a/src/ImageSharp/Formats/Webp/Lossy/Vp8Residual.cs +++ b/src/ImageSharp/Formats/Webp/Lossy/Vp8Residual.cs @@ -3,6 +3,11 @@ using System; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +#if SUPPORTS_RUNTIME_INTRINSICS +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; +#endif namespace SixLabors.ImageSharp.Formats.Webp.Lossy { @@ -11,6 +16,16 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy /// internal class Vp8Residual { +#if SUPPORTS_RUNTIME_INTRINSICS + private static readonly Vector256 Cst2 = Vector256.Create((byte)2); + + private static readonly Vector256 Cst67 = Vector256.Create((byte)67); +#endif + + private readonly byte[] scratch = new byte[32]; + + private readonly ushort[] scratchUShort = new ushort[16]; + public int First { get; set; } public int Last { get; set; } @@ -37,14 +52,39 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy public void SetCoeffs(Span coeffs) { - int n; - this.Last = -1; - for (n = 15; n >= 0; --n) +#if SUPPORTS_RUNTIME_INTRINSICS + if (Sse2.IsSupported) + { + ref short coeffsRef = ref MemoryMarshal.GetReference(coeffs); + Vector128 c0 = Unsafe.As>(ref coeffsRef); + Vector128 c1 = Unsafe.As>(ref Unsafe.Add(ref coeffsRef, 8)); + + // Use SSE2 to compare 16 values with a single instruction. + Vector128 m0 = Sse2.PackSignedSaturate(c0.AsInt16(), c1.AsInt16()); + Vector128 m1 = Sse2.CompareEqual(m0, Vector128.Zero); + + // Get the comparison results as a bitmask into 16bits. Negate the mask to get + // the position of entries that are not equal to zero. We don't need to mask + // out least significant bits according to res->first, since coeffs[0] is 0 + // if res->first > 0. + uint mask = 0x0000ffffu ^ (uint)Sse2.MoveMask(m1); + + // The position of the most significant non-zero bit indicates the position of + // the last non-zero value. + this.Last = mask != 0 ? Numerics.Log2(mask) : -1; + } + else +#endif { - if (coeffs[n] != 0) + int n; + this.Last = -1; + for (n = 15; n >= 0; --n) { - this.Last = n; - break; + if (coeffs[n] != 0) + { + this.Last = n; + break; + } } } @@ -129,27 +169,78 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy return LossyUtils.Vp8BitCost(0, (byte)p0); } - int v; - for (; n < this.Last; ++n) +#if SUPPORTS_RUNTIME_INTRINSICS + if (Avx2.IsSupported) { + Span ctxs = this.scratch.AsSpan(0, 16); + Span levels = this.scratch.AsSpan(16, 16); + Span absLevels = this.scratchUShort.AsSpan(); + + // Precompute clamped levels and contexts, packed to 8b. + ref short outputRef = ref MemoryMarshal.GetReference(this.Coeffs); + Vector256 c0 = Unsafe.As>(ref outputRef).AsInt16(); + Vector256 d0 = Avx2.Subtract(Vector256.Zero, c0); + Vector256 e0 = Avx2.Max(c0, d0); // abs(v), 16b + Vector256 f = Avx2.PackSignedSaturate(e0, e0); + Vector256 g = Avx2.Min(f.AsByte(), Cst2); + Vector256 h = Avx2.Min(f.AsByte(), Cst67); // clampLevel in [0..67] + + ref byte ctxsRef = ref MemoryMarshal.GetReference(ctxs); + ref byte levelsRef = ref MemoryMarshal.GetReference(levels); + ref ushort absLevelsRef = ref MemoryMarshal.GetReference(absLevels); + Unsafe.As>(ref ctxsRef) = g.GetLower(); + Unsafe.As>(ref levelsRef) = h.GetLower(); + Unsafe.As>(ref absLevelsRef) = e0.AsUInt16(); + + int level; + int flevel; + for (; n < this.Last; ++n) + { + int ctx = ctxs[n]; + level = levels[n]; + flevel = absLevels[n]; + cost += WebpLookupTables.Vp8LevelFixedCosts[flevel] + t.Costs[level]; + t = costs[n + 1].Costs[ctx]; + } + + // Last coefficient is always non-zero. + level = levels[n]; + flevel = absLevels[n]; + cost += WebpLookupTables.Vp8LevelFixedCosts[flevel] + t.Costs[level]; + if (n < 15) + { + int b = WebpConstants.Vp8EncBands[n + 1]; + int ctx = ctxs[n]; + int lastP0 = this.Prob[b].Probabilities[ctx].Probabilities[0]; + cost += LossyUtils.Vp8BitCost(0, (byte)lastP0); + } + + return cost; + } +#endif + { + int v; + for (; n < this.Last; ++n) + { + v = Math.Abs(this.Coeffs[n]); + int ctx = v >= 2 ? 2 : v; + cost += LevelCost(t.Costs, v); + t = costs[n + 1].Costs[ctx]; + } + + // Last coefficient is always non-zero v = Math.Abs(this.Coeffs[n]); - int ctx = v >= 2 ? 2 : v; cost += LevelCost(t.Costs, v); - t = costs[n + 1].Costs[ctx]; - } + if (n < 15) + { + int b = WebpConstants.Vp8EncBands[n + 1]; + int ctx = v == 1 ? 1 : 2; + int lastP0 = this.Prob[b].Probabilities[ctx].Probabilities[0]; + cost += LossyUtils.Vp8BitCost(0, (byte)lastP0); + } - // Last coefficient is always non-zero - v = Math.Abs(this.Coeffs[n]); - cost += LevelCost(t.Costs, v); - if (n < 15) - { - int b = WebpConstants.Vp8EncBands[n + 1]; - int ctx = v == 1 ? 1 : 2; - int lastP0 = this.Prob[b].Probabilities[ctx].Probabilities[0]; - cost += LossyUtils.Vp8BitCost(0, (byte)lastP0); + return cost; } - - return cost; } [MethodImpl(InliningOptions.ShortMethod)] diff --git a/src/ImageSharp/IO/ChunkedMemoryStream.cs b/src/ImageSharp/IO/ChunkedMemoryStream.cs index e28baf879..e39930def 100644 --- a/src/ImageSharp/IO/ChunkedMemoryStream.cs +++ b/src/ImageSharp/IO/ChunkedMemoryStream.cs @@ -168,7 +168,6 @@ namespace SixLabors.ImageSharp.IO // Position is out of range this.readChunk = backupReadChunk; this.readOffset = backupReadOffset; - ThrowArgumentOutOfRange(nameof(value)); } } } diff --git a/src/ImageSharp/ImageFrame{TPixel}.cs b/src/ImageSharp/ImageFrame{TPixel}.cs index 4753e90e0..fe037003e 100644 --- a/src/ImageSharp/ImageFrame{TPixel}.cs +++ b/src/ImageSharp/ImageFrame{TPixel}.cs @@ -145,13 +145,8 @@ namespace SixLabors.ImageSharp source.PixelBuffer.FastMemoryGroup.CopyTo(this.PixelBuffer.FastMemoryGroup); } - /// - /// Gets the image pixels. Not private as Buffer2D requires an array in its constructor. - /// - internal Buffer2D PixelBuffer { get; private set; } - /// - Buffer2D IPixelSource.PixelBuffer => this.PixelBuffer; + public Buffer2D PixelBuffer { get; private set; } /// /// Gets or sets the pixel at the specified position. @@ -443,12 +438,12 @@ namespace SixLabors.ImageSharp [MethodImpl(InliningOptions.ShortMethod)] private void VerifyCoords(int x, int y) { - if (x < 0 || x >= this.Width) + if ((uint)x >= (uint)this.Width) { ThrowArgumentOutOfRangeException(nameof(x)); } - if (y < 0 || y >= this.Height) + if ((uint)y >= (uint)this.Height) { ThrowArgumentOutOfRangeException(nameof(y)); } diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index 0a75c06db..1b3a8e27f 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -75,6 +75,11 @@ True Block8x8F.Generated.tt + + True + True + Abgr32.PixelOperations.Generated.tt + True True @@ -166,6 +171,10 @@ TextTemplatingFileGenerator Block8x8F.Generated.cs + + TextTemplatingFileGenerator + Abgr32.PixelOperations.Generated.cs + TextTemplatingFileGenerator PixelOperations{TPixel}.Generated.cs diff --git a/src/ImageSharp/Image{TPixel}.cs b/src/ImageSharp/Image{TPixel}.cs index d01706b01..403a0f7ea 100644 --- a/src/ImageSharp/Image{TPixel}.cs +++ b/src/ImageSharp/Image{TPixel}.cs @@ -452,12 +452,12 @@ namespace SixLabors.ImageSharp [MethodImpl(InliningOptions.ShortMethod)] private void VerifyCoords(int x, int y) { - if (x < 0 || x >= this.Width) + if ((uint)x >= (uint)this.Width) { ThrowArgumentOutOfRangeException(nameof(x)); } - if (y < 0 || y >= this.Height) + if ((uint)y >= (uint)this.Height) { ThrowArgumentOutOfRangeException(nameof(y)); } diff --git a/src/ImageSharp/Memory/Allocators/Internals/SharedArrayPoolBuffer{T}.cs b/src/ImageSharp/Memory/Allocators/Internals/SharedArrayPoolBuffer{T}.cs index 21673215a..9302c67e7 100644 --- a/src/ImageSharp/Memory/Allocators/Internals/SharedArrayPoolBuffer{T}.cs +++ b/src/ImageSharp/Memory/Allocators/Internals/SharedArrayPoolBuffer{T}.cs @@ -13,34 +13,35 @@ namespace SixLabors.ImageSharp.Memory.Internals where T : struct { private readonly int lengthInBytes; - private byte[] array; private LifetimeGuard lifetimeGuard; public SharedArrayPoolBuffer(int lengthInElements) { this.lengthInBytes = lengthInElements * Unsafe.SizeOf(); - this.array = ArrayPool.Shared.Rent(this.lengthInBytes); - this.lifetimeGuard = new LifetimeGuard(this.array); + this.Array = ArrayPool.Shared.Rent(this.lengthInBytes); + this.lifetimeGuard = new LifetimeGuard(this.Array); } + public byte[] Array { get; private set; } + protected override void Dispose(bool disposing) { - if (this.array == null) + if (this.Array == null) { return; } this.lifetimeGuard.Dispose(); - this.array = null; + this.Array = null; } public override Span GetSpan() { this.CheckDisposed(); - return MemoryMarshal.Cast(this.array.AsSpan(0, this.lengthInBytes)); + return MemoryMarshal.Cast(this.Array.AsSpan(0, this.lengthInBytes)); } - protected override object GetPinnableObject() => this.array; + protected override object GetPinnableObject() => this.Array; public void AddRef() { @@ -53,7 +54,7 @@ namespace SixLabors.ImageSharp.Memory.Internals [Conditional("DEBUG")] private void CheckDisposed() { - if (this.array == null) + if (this.Array == null) { throw new ObjectDisposedException("SharedArrayPoolBuffer"); } diff --git a/src/ImageSharp/Memory/Allocators/Internals/UniformUnmanagedMemoryPool.cs b/src/ImageSharp/Memory/Allocators/Internals/UniformUnmanagedMemoryPool.cs index 6504787a8..584de4464 100644 --- a/src/ImageSharp/Memory/Allocators/Internals/UniformUnmanagedMemoryPool.cs +++ b/src/ImageSharp/Memory/Allocators/Internals/UniformUnmanagedMemoryPool.cs @@ -8,12 +8,10 @@ using System.Threading; namespace SixLabors.ImageSharp.Memory.Internals { - internal partial class UniformUnmanagedMemoryPool -#if !NETSTANDARD1_3 - // In case UniformUnmanagedMemoryPool is finalized, we prefer to run its finalizer after the guard finalizers, - // but we should not rely on this. - : System.Runtime.ConstrainedExecution.CriticalFinalizerObject -#endif + // CriticalFinalizerObject: + // In case UniformUnmanagedMemoryPool is finalized, we prefer to run its finalizer after the guard finalizers, + // but we should not rely on this. + internal partial class UniformUnmanagedMemoryPool : System.Runtime.ConstrainedExecution.CriticalFinalizerObject { private static int minTrimPeriodMilliseconds = int.MaxValue; private static readonly List> AllPools = new(); diff --git a/src/ImageSharp/Memory/Allocators/Internals/UnmanagedBuffer{T}.cs b/src/ImageSharp/Memory/Allocators/Internals/UnmanagedBuffer{T}.cs index 5d0c6dd61..a948bf848 100644 --- a/src/ImageSharp/Memory/Allocators/Internals/UnmanagedBuffer{T}.cs +++ b/src/ImageSharp/Memory/Allocators/Internals/UnmanagedBuffer{T}.cs @@ -31,7 +31,7 @@ namespace SixLabors.ImageSharp.Memory.Internals this.lifetimeGuard = lifetimeGuard; } - private void* Pointer => this.lifetimeGuard.Handle.Pointer; + public void* Pointer => this.lifetimeGuard.Handle.Pointer; public override Span GetSpan() { diff --git a/src/ImageSharp/Memory/Buffer2D{T}.cs b/src/ImageSharp/Memory/Buffer2D{T}.cs index ba39a924e..7ffaae312 100644 --- a/src/ImageSharp/Memory/Buffer2D{T}.cs +++ b/src/ImageSharp/Memory/Buffer2D{T}.cs @@ -56,7 +56,7 @@ namespace SixLabors.ImageSharp.Memory /// It's public counterpart is , /// which only exposes the view of the MemoryGroup. /// - internal MemoryGroup FastMemoryGroup { get; } + internal MemoryGroup FastMemoryGroup { get; private set; } /// /// Gets a reference to the element at the specified position. @@ -97,35 +97,37 @@ namespace SixLabors.ImageSharp.Memory [MethodImpl(InliningOptions.ShortMethod)] public Span DangerousGetRowSpan(int y) { - DebugGuard.MustBeGreaterThanOrEqualTo(y, 0, nameof(y)); - DebugGuard.MustBeLessThan(y, this.Height, nameof(y)); + if ((uint)y >= (uint)this.Height) + { + this.ThrowYOutOfRangeException(y); + } - return this.GetRowMemoryCore(y).Span; + return this.FastMemoryGroup.GetRowSpanCoreUnsafe(y, this.Width); } - internal bool TryGetPaddedRowSpan(int y, int padding, out Span paddedSpan) + internal bool DangerousTryGetPaddedRowSpan(int y, int padding, out Span paddedSpan) { DebugGuard.MustBeGreaterThanOrEqualTo(y, 0, nameof(y)); DebugGuard.MustBeLessThan(y, this.Height, nameof(y)); int stride = this.Width + padding; - Memory memory = this.FastMemoryGroup.GetRemainingSliceOfBuffer(y * (long)this.Width); + Span slice = this.FastMemoryGroup.GetRemainingSliceOfBuffer(y * (long)this.Width); - if (memory.Length < stride) + if (slice.Length < stride) { paddedSpan = default; return false; } - paddedSpan = memory.Span.Slice(0, stride); + paddedSpan = slice.Slice(0, stride); return true; } [MethodImpl(InliningOptions.ShortMethod)] internal ref T GetElementUnsafe(int x, int y) { - Span span = this.GetRowMemoryCore(y).Span; + Span span = this.FastMemoryGroup.GetRowSpanCoreUnsafe(y, this.Width); return ref span[x]; } @@ -139,7 +141,7 @@ namespace SixLabors.ImageSharp.Memory { DebugGuard.MustBeGreaterThanOrEqualTo(y, 0, nameof(y)); DebugGuard.MustBeLessThan(y, this.Height, nameof(y)); - return this.FastMemoryGroup.View.GetBoundedSlice(y * (long)this.Width, this.Width); + return this.FastMemoryGroup.View.GetBoundedMemorySlice(y * (long)this.Width, this.Width); } /// @@ -168,25 +170,36 @@ namespace SixLabors.ImageSharp.Memory /// Swaps the contents of 'destination' with 'source' if the buffers are owned (1), /// copies the contents of 'source' to 'destination' otherwise (2). Buffers should be of same size in case 2! /// - internal static void SwapOrCopyContent(Buffer2D destination, Buffer2D source) - { - MemoryGroup.SwapOrCopyContent(destination.FastMemoryGroup, source.FastMemoryGroup); - SwapOwnData(destination, source); - } - - [MethodImpl(InliningOptions.ShortMethod)] - private Memory GetRowMemoryCore(int y) => this.FastMemoryGroup.GetBoundedSlice(y * (long)this.Width, this.Width); - - private static void SwapOwnData(Buffer2D a, Buffer2D b) + internal static bool SwapOrCopyContent(Buffer2D destination, Buffer2D source) { - Size aSize = a.Size(); - Size bSize = b.Size(); + bool swapped = false; + if (MemoryGroup.CanSwapContent(destination.FastMemoryGroup, source.FastMemoryGroup)) + { + (destination.FastMemoryGroup, source.FastMemoryGroup) = + (source.FastMemoryGroup, destination.FastMemoryGroup); + destination.FastMemoryGroup.RecreateViewAfterSwap(); + source.FastMemoryGroup.RecreateViewAfterSwap(); + swapped = true; + } + else + { + if (destination.FastMemoryGroup.TotalLength != source.FastMemoryGroup.TotalLength) + { + throw new InvalidMemoryOperationException( + "Trying to copy/swap incompatible buffers. This is most likely caused by applying an unsupported processor to wrapped-memory images."); + } - b.Width = aSize.Width; - b.Height = aSize.Height; + source.FastMemoryGroup.CopyTo(destination.MemoryGroup); + } - a.Width = bSize.Width; - a.Height = bSize.Height; + (destination.Width, source.Width) = (source.Width, destination.Width); + (destination.Height, source.Height) = (source.Height, destination.Height); + return swapped; } + + [MethodImpl(InliningOptions.ColdPath)] + private void ThrowYOutOfRangeException(int y) => + throw new ArgumentOutOfRangeException( + $"DangerousGetRowSpan({y}). Y was out of range. Height={this.Height}"); } } diff --git a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupExtensions.cs b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupExtensions.cs index 8e6f38d14..d200b223a 100644 --- a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupExtensions.cs +++ b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupExtensions.cs @@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Memory /// Returns a slice that is expected to be within the bounds of a single buffer. /// Otherwise is thrown. /// - internal static Memory GetBoundedSlice(this IMemoryGroup group, long start, int length) + internal static Memory GetBoundedMemorySlice(this IMemoryGroup group, long start, int length) where T : struct { Guard.NotNull(group, nameof(group)); @@ -37,7 +37,8 @@ namespace SixLabors.ImageSharp.Memory Guard.MustBeGreaterThanOrEqualTo(length, 0, nameof(length)); Guard.MustBeLessThan(start, group.TotalLength, nameof(start)); - int bufferIdx = (int)(start / group.BufferLength); + int bufferIdx = (int)Math.DivRem(start, group.BufferLength, out long bufferStartLong); + int bufferStart = (int)bufferStartLong; // if (bufferIdx < 0 || bufferIdx >= group.Count) if ((uint)bufferIdx >= group.Count) @@ -45,7 +46,6 @@ namespace SixLabors.ImageSharp.Memory throw new ArgumentOutOfRangeException(nameof(start)); } - int bufferStart = (int)(start % group.BufferLength); int bufferEnd = bufferStart + length; Memory memory = group[bufferIdx]; @@ -57,31 +57,6 @@ namespace SixLabors.ImageSharp.Memory return memory.Slice(bufferStart, length); } - /// - /// Returns the slice of the buffer starting at global index that goes until the end of the buffer. - /// - internal static Memory GetRemainingSliceOfBuffer(this IMemoryGroup group, long start) - where T : struct - { - Guard.NotNull(group, nameof(group)); - Guard.IsTrue(group.IsValid, nameof(group), "Group must be valid!"); - Guard.MustBeLessThan(start, group.TotalLength, nameof(start)); - - int bufferIdx = (int)(start / group.BufferLength); - - // if (bufferIdx < 0 || bufferIdx >= group.Count) - if ((uint)bufferIdx >= group.Count) - { - throw new ArgumentOutOfRangeException(nameof(start)); - } - - int bufferStart = (int)(start % group.BufferLength); - - Memory memory = group[bufferIdx]; - - return memory.Slice(bufferStart); - } - internal static void CopyTo(this IMemoryGroup source, Span target) where T : struct { diff --git a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupSpanCache.cs b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupSpanCache.cs new file mode 100644 index 000000000..f2e02bcfe --- /dev/null +++ b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupSpanCache.cs @@ -0,0 +1,52 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers; +using SixLabors.ImageSharp.Memory.Internals; + +namespace SixLabors.ImageSharp.Memory +{ + /// + /// Cached pointer or array data enabling fast access from + /// known implementations. + /// + internal unsafe struct MemoryGroupSpanCache + { + public SpanCacheMode Mode; + public byte[] SingleArray; + public void* SinglePointer; + public void*[] MultiPointer; + + public static MemoryGroupSpanCache Create(IMemoryOwner[] memoryOwners) + where T : struct + { + IMemoryOwner owner0 = memoryOwners[0]; + MemoryGroupSpanCache memoryGroupSpanCache = default; + if (memoryOwners.Length == 1) + { + if (owner0 is SharedArrayPoolBuffer sharedPoolBuffer) + { + memoryGroupSpanCache.Mode = SpanCacheMode.SingleArray; + memoryGroupSpanCache.SingleArray = sharedPoolBuffer.Array; + } + else if (owner0 is UnmanagedBuffer unmanagedBuffer) + { + memoryGroupSpanCache.Mode = SpanCacheMode.SinglePointer; + memoryGroupSpanCache.SinglePointer = unmanagedBuffer.Pointer; + } + } + else if (owner0 is UnmanagedBuffer) + { + memoryGroupSpanCache.Mode = SpanCacheMode.MultiPointer; + memoryGroupSpanCache.MultiPointer = new void*[memoryOwners.Length]; + for (int i = 0; i < memoryOwners.Length; i++) + { + memoryGroupSpanCache.MultiPointer[i] = ((UnmanagedBuffer)memoryOwners[i]).Pointer; + } + } + + return memoryGroupSpanCache; + } + } +} diff --git a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Owned.cs b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Owned.cs index 3b9241383..21faf8e56 100644 --- a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Owned.cs +++ b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Owned.cs @@ -26,6 +26,7 @@ namespace SixLabors.ImageSharp.Memory this.memoryOwners = memoryOwners; this.Swappable = swappable; this.View = new MemoryGroupView(this); + this.memoryGroupSpanCache = MemoryGroupSpanCache.Create(memoryOwners); } public Owned( @@ -122,6 +123,12 @@ namespace SixLabors.ImageSharp.Memory } } + public override void RecreateViewAfterSwap() + { + this.View.Invalidate(); + this.View = new MemoryGroupView(this); + } + /// IEnumerator> IEnumerable>.GetEnumerator() { @@ -167,32 +174,6 @@ namespace SixLabors.ImageSharp.Memory [MethodImpl(MethodImplOptions.NoInlining)] private static void ThrowObjectDisposedException() => throw new ObjectDisposedException(nameof(MemoryGroup)); - internal static void SwapContents(Owned a, Owned b) - { - a.EnsureNotDisposed(); - b.EnsureNotDisposed(); - - IMemoryOwner[] tempOwners = a.memoryOwners; - long tempTotalLength = a.TotalLength; - int tempBufferLength = a.BufferLength; - RefCountedLifetimeGuard tempGroupOwner = a.groupLifetimeGuard; - - a.memoryOwners = b.memoryOwners; - a.TotalLength = b.TotalLength; - a.BufferLength = b.BufferLength; - a.groupLifetimeGuard = b.groupLifetimeGuard; - - b.memoryOwners = tempOwners; - b.TotalLength = tempTotalLength; - b.BufferLength = tempBufferLength; - b.groupLifetimeGuard = tempGroupOwner; - - a.View.Invalidate(); - b.View.Invalidate(); - a.View = new MemoryGroupView(a); - b.View = new MemoryGroupView(b); - } - // When the MemoryGroup points to multiple buffers via `groupLifetimeGuard`, // the lifetime of the individual buffers is managed by the guard. // Group buffer IMemoryOwner-s d not manage ownership. diff --git a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.cs b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.cs index cdd8e6a75..9844f4a3b 100644 --- a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.cs +++ b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.cs @@ -6,6 +6,8 @@ using System.Buffers; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; using SixLabors.ImageSharp.Memory.Internals; namespace SixLabors.ImageSharp.Memory @@ -21,6 +23,8 @@ namespace SixLabors.ImageSharp.Memory { private static readonly int ElementSize = Unsafe.SizeOf(); + private MemoryGroupSpanCache memoryGroupSpanCache; + private MemoryGroup(int bufferLength, long totalLength) { this.BufferLength = bufferLength; @@ -31,10 +35,10 @@ namespace SixLabors.ImageSharp.Memory public abstract int Count { get; } /// - public int BufferLength { get; private set; } + public int BufferLength { get; } /// - public long TotalLength { get; private set; } + public long TotalLength { get; } /// public bool IsValid { get; private set; } = true; @@ -241,32 +245,62 @@ namespace SixLabors.ImageSharp.Memory return new Owned(source, bufferLength, totalLength, false); } - /// - /// Swaps the contents of 'target' with 'source' if the buffers are allocated (1), - /// copies the contents of 'source' to 'target' otherwise (2). - /// Groups should be of same TotalLength in case 2. - /// - public static bool SwapOrCopyContent(MemoryGroup target, MemoryGroup source) + [MethodImpl(InliningOptions.ShortMethod)] + public unsafe Span GetRowSpanCoreUnsafe(int y, int width) { - if (source is Owned ownedSrc && ownedSrc.Swappable && - target is Owned ownedTarget && ownedTarget.Swappable) + switch (this.memoryGroupSpanCache.Mode) { - Owned.SwapContents(ownedTarget, ownedSrc); - return true; - } - else - { - if (target.TotalLength != source.TotalLength) + case SpanCacheMode.SingleArray: + { +#if SUPPORTS_CREATESPAN + ref byte b0 = ref MemoryMarshal.GetReference(this.memoryGroupSpanCache.SingleArray); + ref T e0 = ref Unsafe.As(ref b0); + e0 = ref Unsafe.Add(ref e0, y * width); + return MemoryMarshal.CreateSpan(ref e0, width); +#else + return MemoryMarshal.Cast(this.memoryGroupSpanCache.SingleArray).Slice(y * width, width); +#endif + + } + + case SpanCacheMode.SinglePointer: { - throw new InvalidMemoryOperationException( - "Trying to copy/swap incompatible buffers. This is most likely caused by applying an unsupported processor to wrapped-memory images."); + void* start = Unsafe.Add(this.memoryGroupSpanCache.SinglePointer, y * width); + return new Span(start, width); } - source.CopyTo(target); - return false; + case SpanCacheMode.MultiPointer: + { + this.GetMultiBufferPosition(y, width, out int bufferIdx, out int bufferStart); + void* start = Unsafe.Add(this.memoryGroupSpanCache.MultiPointer[bufferIdx], bufferStart); + return new Span(start, width); + } + + default: + { + this.GetMultiBufferPosition(y, width, out int bufferIdx, out int bufferStart); + return this[bufferIdx].Span.Slice(bufferStart, width); + } } } + /// + /// Returns the slice of the buffer starting at global index that goes until the end of the buffer. + /// + public Span GetRemainingSliceOfBuffer(long start) + { + long bufferIdx = Math.DivRem(start, this.BufferLength, out long bufferStart); + Memory memory = this[(int)bufferIdx]; + return memory.Span.Slice((int)bufferStart); + } + + public static bool CanSwapContent(MemoryGroup target, MemoryGroup source) => + source is Owned { Swappable: true } && target is Owned { Swappable: true }; + + public virtual void RecreateViewAfterSwap() + { + } + public virtual void IncreaseRefCounts() { } @@ -274,5 +308,14 @@ namespace SixLabors.ImageSharp.Memory public virtual void DecreaseRefCounts() { } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void GetMultiBufferPosition(int y, int width, out int bufferIdx, out int bufferStart) + { + long start = y * (long)width; + long bufferIdxLong = Math.DivRem(start, this.BufferLength, out long bufferStartLong); + bufferIdx = (int)bufferIdxLong; + bufferStart = (int)bufferStartLong; + } } } diff --git a/src/ImageSharp/Memory/DiscontiguousBuffers/SpanCacheMode.cs b/src/ImageSharp/Memory/DiscontiguousBuffers/SpanCacheMode.cs new file mode 100644 index 000000000..8bd32efa9 --- /dev/null +++ b/src/ImageSharp/Memory/DiscontiguousBuffers/SpanCacheMode.cs @@ -0,0 +1,16 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Memory +{ + /// + /// Selects active values in . + /// + internal enum SpanCacheMode + { + Default = default, + SingleArray, + SinglePointer, + MultiPointer + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifDataType.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifDataType.cs index 733eb4a79..0185afb50 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifDataType.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifDataType.cs @@ -80,6 +80,21 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif /// /// Reference to an IFD (32-bit (4-byte) unsigned integer). /// - Ifd = 13 + Ifd = 13, + + /// + /// A 64-bit (8-byte) unsigned integer. + /// + Long8 = 16, + + /// + /// A 64-bit (8-byte) signed integer (2's complement notation). + /// + SignedLong8 = 17, + + /// + /// Reference to an IFD (64-bit (8-byte) unsigned integer). + /// + Ifd8 = 18, } } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifDataTypes.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifDataTypes.cs index 4f75999bb..729efb390 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifDataTypes.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifDataTypes.cs @@ -32,10 +32,14 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif case ExifDataType.Long: case ExifDataType.SignedLong: case ExifDataType.SingleFloat: + case ExifDataType.Ifd: return 4; case ExifDataType.DoubleFloat: case ExifDataType.Rational: case ExifDataType.SignedRational: + case ExifDataType.Long8: + case ExifDataType.SignedLong8: + case ExifDataType.Ifd8: return 8; default: throw new NotSupportedException(dataType.ToString()); diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs index 6e671b3ec..2fcd1cc07 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.Buffers.Binary; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -9,15 +10,19 @@ using System.Diagnostics; using System.IO; using System.Runtime.CompilerServices; using System.Text; +using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Metadata.Profiles.Exif { internal class ExifReader : BaseExifReader { - private readonly List loaders = new List(); - public ExifReader(byte[] exifData) - : base(new MemoryStream(exifData ?? throw new ArgumentNullException(nameof(exifData)))) + : this(exifData, null) + { + } + + public ExifReader(byte[] exifData, MemoryAllocator allocator) + : base(new MemoryStream(exifData ?? throw new ArgumentNullException(nameof(exifData))), allocator) { } @@ -47,16 +52,11 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif this.ReadSubIfd(values); - foreach (Action loader in this.loaders) - { - loader(); - } + this.ReadBigValues(values); return values; } - protected override void RegisterExtLoader(uint offset, Action loader) => this.loaders.Add(loader); - private void GetThumbnail(uint offset) { if (offset == 0) @@ -86,19 +86,21 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif /// internal abstract class BaseExifReader { - private readonly byte[] offsetBuffer = new byte[4]; + private readonly byte[] buf8 = new byte[8]; private readonly byte[] buf4 = new byte[4]; private readonly byte[] buf2 = new byte[2]; + private readonly MemoryAllocator allocator; private readonly Stream data; private List invalidTags; - private uint exifOffset; - - private uint gpsOffset; + private List subIfds; - protected BaseExifReader(Stream stream) => + protected BaseExifReader(Stream stream, MemoryAllocator allocator) + { this.data = stream ?? throw new ArgumentNullException(nameof(stream)); + this.allocator = allocator; + } private delegate TDataType ConverterMethod(ReadOnlySpan data); @@ -119,7 +121,51 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif public bool IsBigEndian { get; protected set; } - protected abstract void RegisterExtLoader(uint offset, Action loader); + public List<(ulong Offset, ExifDataType DataType, ulong NumberOfComponents, ExifValue Exif)> BigValues { get; } = new(); + + protected void ReadBigValues(List values) + { + if (this.BigValues.Count == 0) + { + return; + } + + int maxSize = 0; + foreach ((ulong offset, ExifDataType dataType, ulong numberOfComponents, ExifValue exif) in this.BigValues) + { + ulong size = numberOfComponents * ExifDataTypes.GetSize(dataType); + DebugGuard.MustBeLessThanOrEqualTo(size, int.MaxValue, nameof(size)); + + if ((int)size > maxSize) + { + maxSize = (int)size; + } + } + + if (this.allocator != null) + { + // tiff, bigTiff + using IMemoryOwner memory = this.allocator.Allocate(maxSize); + Span buf = memory.GetSpan(); + foreach ((ulong Offset, ExifDataType DataType, ulong NumberOfComponents, ExifValue Exif) tag in this.BigValues) + { + ulong size = tag.NumberOfComponents * ExifDataTypes.GetSize(tag.DataType); + this.ReadBigValue(values, tag, buf.Slice(0, (int)size)); + } + } + else + { + // embedded exif + Span buf = maxSize <= 256 ? stackalloc byte[256] : new byte[maxSize]; + foreach ((ulong Offset, ExifDataType DataType, ulong NumberOfComponents, ExifValue Exif) tag in this.BigValues) + { + ulong size = tag.NumberOfComponents * ExifDataTypes.GetSize(tag.DataType); + this.ReadBigValue(values, tag, buf.Slice(0, (int)size)); + } + } + + this.BigValues.Clear(); + } /// /// Reads the values to the values collection. @@ -136,22 +182,45 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif this.Seek(offset); int count = this.ReadUInt16(); + Span offsetBuffer = stackalloc byte[4]; for (int i = 0; i < count; i++) { - this.ReadValue(values); + this.ReadValue(values, offsetBuffer); } } protected void ReadSubIfd(List values) { - if (this.exifOffset != 0) + if (this.subIfds is not null) { - this.ReadValues(values, this.exifOffset); + foreach (ulong subIfdOffset in this.subIfds) + { + this.ReadValues(values, (uint)subIfdOffset); + } } + } + + protected void ReadValues64(List values, ulong offset) + { + DebugGuard.MustBeLessThanOrEqualTo(offset, (ulong)this.data.Length, "By spec UInt64.MaxValue is supported, but .Net Stream.Length can Int64.MaxValue."); - if (this.gpsOffset != 0) + this.Seek(offset); + ulong count = this.ReadUInt64(); + + Span offsetBuffer = stackalloc byte[8]; + for (ulong i = 0; i < count; i++) + { + this.ReadValue64(values, offsetBuffer); + } + } + + protected void ReadBigValue(IList values, (ulong Offset, ExifDataType DataType, ulong NumberOfComponents, ExifValue Exif) tag, Span buffer) + { + this.Seek(tag.Offset); + if (this.TryReadSpan(buffer)) { - this.ReadValues(values, this.gpsOffset); + object value = this.ConvertValue(tag.DataType, buffer, tag.NumberOfComponents > 1 || tag.Exif.IsArray); + this.Add(values, tag.Exif, value); } } @@ -186,7 +255,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif return Encoding.UTF8.GetString(buffer); } - private object ConvertValue(ExifDataType dataType, ReadOnlySpan buffer, uint numberOfComponents) + private object ConvertValue(ExifDataType dataType, ReadOnlySpan buffer, bool isArray) { if (buffer.Length == 0) { @@ -200,88 +269,104 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif case ExifDataType.Ascii: return this.ConvertToString(buffer); case ExifDataType.Byte: - if (numberOfComponents == 1) + if (!isArray) { return this.ConvertToByte(buffer); } return buffer.ToArray(); case ExifDataType.DoubleFloat: - if (numberOfComponents == 1) + if (!isArray) { return this.ConvertToDouble(buffer); } return ToArray(dataType, buffer, this.ConvertToDouble); case ExifDataType.Long: - if (numberOfComponents == 1) + case ExifDataType.Ifd: + if (!isArray) { return this.ConvertToUInt32(buffer); } return ToArray(dataType, buffer, this.ConvertToUInt32); case ExifDataType.Rational: - if (numberOfComponents == 1) + if (!isArray) { return this.ToRational(buffer); } return ToArray(dataType, buffer, this.ToRational); case ExifDataType.Short: - if (numberOfComponents == 1) + if (!isArray) { return this.ConvertToShort(buffer); } return ToArray(dataType, buffer, this.ConvertToShort); case ExifDataType.SignedByte: - if (numberOfComponents == 1) + if (!isArray) { return this.ConvertToSignedByte(buffer); } return ToArray(dataType, buffer, this.ConvertToSignedByte); case ExifDataType.SignedLong: - if (numberOfComponents == 1) + if (!isArray) { return this.ConvertToInt32(buffer); } return ToArray(dataType, buffer, this.ConvertToInt32); case ExifDataType.SignedRational: - if (numberOfComponents == 1) + if (!isArray) { return this.ToSignedRational(buffer); } return ToArray(dataType, buffer, this.ToSignedRational); case ExifDataType.SignedShort: - if (numberOfComponents == 1) + if (!isArray) { return this.ConvertToSignedShort(buffer); } return ToArray(dataType, buffer, this.ConvertToSignedShort); case ExifDataType.SingleFloat: - if (numberOfComponents == 1) + if (!isArray) { return this.ConvertToSingle(buffer); } return ToArray(dataType, buffer, this.ConvertToSingle); + case ExifDataType.Long8: + case ExifDataType.Ifd8: + if (!isArray) + { + return this.ConvertToUInt64(buffer); + } + + return ToArray(dataType, buffer, this.ConvertToUInt64); + case ExifDataType.SignedLong8: + if (!isArray) + { + return this.ConvertToInt64(buffer); + } + + return ToArray(dataType, buffer, this.ConvertToUInt64); case ExifDataType.Undefined: - if (numberOfComponents == 1) + if (!isArray) { return this.ConvertToByte(buffer); } return buffer.ToArray(); default: - throw new NotSupportedException(); + throw new NotSupportedException($"Data type {dataType} is not supported."); } } - private void ReadValue(List values) + private void ReadValue(List values, Span offsetBuffer) { // 2 | 2 | 4 | 4 // tag | type | count | value offset @@ -295,7 +380,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif uint numberOfComponents = this.ReadUInt32(); - this.TryReadSpan(this.offsetBuffer); + this.TryReadSpan(offsetBuffer); // Ensure that the data type is valid if (dataType == ExifDataType.Unknown) @@ -305,9 +390,9 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif // Issue #132: ExifDataType == Undefined is treated like a byte array. // If numberOfComponents == 0 this value can only be handled as an inline value and must fallback to 4 (bytes) - if (dataType == ExifDataType.Undefined && numberOfComponents == 0) + if (numberOfComponents == 0) { - numberOfComponents = 4; + numberOfComponents = 4 / ExifDataTypes.GetSize(dataType); } ExifValue exifValue = ExifValues.Create(tag) ?? ExifValues.Create(tag, dataType, numberOfComponents); @@ -321,7 +406,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif uint size = numberOfComponents * ExifDataTypes.GetSize(dataType); if (size > 4) { - uint newOffset = this.ConvertToUInt32(this.offsetBuffer); + uint newOffset = this.ConvertToUInt32(offsetBuffer); // Ensure that the new index does not overrun the data. if (newOffset > int.MaxValue || (newOffset + size) > this.data.Length) @@ -330,21 +415,85 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif return; } - this.RegisterExtLoader(newOffset, () => - { - byte[] dataBuffer = new byte[size]; - this.Seek(newOffset); - if (this.TryReadSpan(dataBuffer)) - { - object value = this.ConvertValue(dataType, dataBuffer, numberOfComponents); - this.Add(values, exifValue, value); - } - }); + this.BigValues.Add((newOffset, dataType, numberOfComponents, exifValue)); } else { - object value = this.ConvertValue(dataType, this.offsetBuffer, numberOfComponents); + object value = this.ConvertValue(dataType, offsetBuffer.Slice(0, (int)size), numberOfComponents > 1 || exifValue.IsArray); + this.Add(values, exifValue, value); + } + } + private void ReadValue64(List values, Span offsetBuffer) + { + if ((this.data.Length - this.data.Position) < 20) + { + return; + } + + var tag = (ExifTagValue)this.ReadUInt16(); + ExifDataType dataType = EnumUtils.Parse(this.ReadUInt16(), ExifDataType.Unknown); + + ulong numberOfComponents = this.ReadUInt64(); + + this.TryReadSpan(offsetBuffer); + + if (dataType == ExifDataType.Unknown) + { + return; + } + + if (numberOfComponents == 0) + { + numberOfComponents = 8 / ExifDataTypes.GetSize(dataType); + } + + // The StripOffsets, StripByteCounts, TileOffsets, and TileByteCounts tags are allowed to have the datatype TIFF_LONG8 in BigTIFF. + // Old datatypes TIFF_LONG, and TIFF_SHORT where allowed in the TIFF 6.0 specification, are still valid in BigTIFF, too. + // Likewise, tags that point to other IFDs, like e.g. the SubIFDs tag, are now allowed to have the datatype TIFF_IFD8 in BigTIFF. + // Again, the old datatypes TIFF_IFD, and the hardly recommendable TIFF_LONG, are still valid, too. + // https://www.awaresystems.be/imaging/tiff/bigtiff.html + ExifValue exifValue = null; + switch (tag) + { + case ExifTagValue.StripOffsets: + exifValue = new ExifLong8Array(ExifTagValue.StripOffsets); + break; + case ExifTagValue.StripByteCounts: + exifValue = new ExifLong8Array(ExifTagValue.StripByteCounts); + break; + case ExifTagValue.TileOffsets: + exifValue = new ExifLong8Array(ExifTagValue.TileOffsets); + break; + case ExifTagValue.TileByteCounts: + exifValue = new ExifLong8Array(ExifTagValue.TileByteCounts); + break; + default: + exifValue = ExifValues.Create(tag) ?? ExifValues.Create(tag, dataType, numberOfComponents); + break; + } + + if (exifValue is null) + { + this.AddInvalidTag(new UnkownExifTag(tag)); + return; + } + + ulong size = numberOfComponents * ExifDataTypes.GetSize(dataType); + if (size > 8) + { + ulong newOffset = this.ConvertToUInt64(offsetBuffer); + if (newOffset > ulong.MaxValue || newOffset > ((ulong)this.data.Length - size)) + { + this.AddInvalidTag(new UnkownExifTag(tag)); + return; + } + + this.BigValues.Add((newOffset, dataType, numberOfComponents, exifValue)); + } + else + { + object value = this.ConvertValue(dataType, offsetBuffer.Slice(0, (int)size), numberOfComponents > 1 || exifValue.IsArray); this.Add(values, exifValue, value); } } @@ -368,11 +517,11 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif if (exif.Tag == ExifTag.SubIFDOffset) { - this.exifOffset = (uint)value; + this.AddSubIfd(value); } else if (exif.Tag == ExifTag.GPSIFDOffset) { - this.gpsOffset = (uint)value; + this.AddSubIfd(value); } else { @@ -383,8 +532,11 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif private void AddInvalidTag(ExifTag tag) => (this.invalidTags ??= new List()).Add(tag); - private void Seek(long pos) - => this.data.Seek(pos, SeekOrigin.Begin); + private void AddSubIfd(object val) + => (this.subIfds ??= new List()).Add(Convert.ToUInt64(val)); + + private void Seek(ulong pos) + => this.data.Seek((long)pos, SeekOrigin.Begin); private bool TryReadSpan(Span span) { @@ -398,6 +550,11 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif return read == length; } + protected ulong ReadUInt64() => + this.TryReadSpan(this.buf8) + ? this.ConvertToUInt64(this.buf8) + : default; + // Known as Long in Exif Specification. protected uint ReadUInt32() => this.TryReadSpan(this.buf4) @@ -408,6 +565,30 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif ? this.ConvertToShort(this.buf2) : default; + private long ConvertToInt64(ReadOnlySpan buffer) + { + if (buffer.Length < 8) + { + return default; + } + + return this.IsBigEndian + ? BinaryPrimitives.ReadInt64BigEndian(buffer) + : BinaryPrimitives.ReadInt64LittleEndian(buffer); + } + + private ulong ConvertToUInt64(ReadOnlySpan buffer) + { + if (buffer.Length < 8) + { + return default; + } + + return this.IsBigEndian + ? BinaryPrimitives.ReadUInt64BigEndian(buffer) + : BinaryPrimitives.ReadUInt64LittleEndian(buffer); + } + private double ConvertToDouble(ReadOnlySpan buffer) { if (buffer.Length < 8) diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs index e7a01b070..498eec952 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs @@ -150,6 +150,20 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif return offset + 4; } + private static int WriteInt64(long value, Span destination, int offset) + { + BinaryPrimitives.WriteInt64LittleEndian(destination.Slice(offset, 8), value); + + return offset + 8; + } + + private static int WriteUInt64(ulong value, Span destination, int offset) + { + BinaryPrimitives.WriteUInt64LittleEndian(destination.Slice(offset, 8), value); + + return offset + 8; + } + private static int WriteInt32(int value, Span destination, int offset) { BinaryPrimitives.WriteInt32LittleEndian(destination.Slice(offset, 4), value); @@ -390,6 +404,10 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif } return WriteUInt32((uint)value, destination, offset); + case ExifDataType.Long8: + return WriteUInt64((ulong)value, destination, offset); + case ExifDataType.SignedLong8: + return WriteInt64((long)value, destination, offset); case ExifDataType.Rational: WriteRational(destination.Slice(offset, 8), (Rational)value); return offset + 8; diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.LongArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.LongArray.cs index ac4b0a1bf..390599b73 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.LongArray.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.LongArray.cs @@ -65,5 +65,10 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif /// Gets the TimeZoneOffset exif tag. /// public static ExifTag TimeZoneOffset { get; } = new ExifTag(ExifTagValue.TimeZoneOffset); + + /// + /// Gets the offset to child IFDs exif tag. + /// + public static ExifTag SubIFDs { get; } = new ExifTag(ExifTagValue.SubIFDs); } } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8.cs new file mode 100644 index 000000000..a4a1dd3df --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8.cs @@ -0,0 +1,66 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System.Globalization; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifLong8 : ExifValue + { + public ExifLong8(ExifTag tag) + : base(tag) + { + } + + public ExifLong8(ExifTagValue tag) + : base(tag) + { + } + + private ExifLong8(ExifLong8 value) + : base(value) + { + } + + public override ExifDataType DataType => ExifDataType.Long8; + + protected override string StringValue => this.Value.ToString(CultureInfo.InvariantCulture); + + public override bool TrySetValue(object value) + { + if (base.TrySetValue(value)) + { + return true; + } + + switch (value) + { + case int intValue: + if (intValue >= uint.MinValue) + { + this.Value = (uint)intValue; + return true; + } + + return false; + case uint uintValue: + this.Value = uintValue; + + return true; + case long intValue: + if (intValue >= 0) + { + this.Value = (ulong)intValue; + return true; + } + + return false; + default: + + return false; + } + } + + public override IExifValue DeepClone() => new ExifLong8(this); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs new file mode 100644 index 000000000..eced4e3de --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong8Array.cs @@ -0,0 +1,171 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifLong8Array : ExifArrayValue + { + public ExifLong8Array(ExifTagValue tag) + : base(tag) + { + } + + private ExifLong8Array(ExifLong8Array value) + : base(value) + { + } + + public override ExifDataType DataType + { + get + { + if (this.Value is not null) + { + foreach (ulong value in this.Value) + { + if (value > uint.MaxValue) + { + return ExifDataType.Long8; + } + } + } + + return ExifDataType.Long; + } + } + + public override bool TrySetValue(object value) + { + if (base.TrySetValue(value)) + { + return true; + } + + switch (value) + { + case int val: + return this.SetSingle((ulong)Numerics.Clamp(val, 0, int.MaxValue)); + + case uint val: + return this.SetSingle((ulong)val); + + case short val: + return this.SetSingle((ulong)Numerics.Clamp(val, 0, short.MaxValue)); + + case ushort val: + return this.SetSingle((ulong)val); + + case long val: + return this.SetSingle((ulong)Numerics.Clamp(val, 0, long.MaxValue)); + + case long[] array: + { + if (value.GetType() == typeof(ulong[])) + { + return this.SetArray((ulong[])value); + } + + return this.SetArray(array); + } + + case int[] array: + { + if (value.GetType() == typeof(uint[])) + { + return this.SetArray((uint[])value); + } + + return this.SetArray(array); + } + + case short[] array: + { + if (value.GetType() == typeof(ushort[])) + { + return this.SetArray((ushort[])value); + } + + return this.SetArray(array); + } + } + + return false; + } + + public override IExifValue DeepClone() => new ExifLong8Array(this); + + private bool SetSingle(ulong value) + { + this.Value = new[] { value }; + return true; + } + + private bool SetArray(long[] values) + { + var numbers = new ulong[values.Length]; + for (int i = 0; i < values.Length; i++) + { + numbers[i] = (ulong)(values[i] < 0 ? 0 : values[i]); + } + + this.Value = numbers; + return true; + } + + private bool SetArray(ulong[] values) + { + this.Value = values; + return true; + } + + private bool SetArray(int[] values) + { + var numbers = new ulong[values.Length]; + for (int i = 0; i < values.Length; i++) + { + numbers[i] = (ulong)Numerics.Clamp(values[i], 0, int.MaxValue); + } + + this.Value = numbers; + return true; + } + + private bool SetArray(uint[] values) + { + var numbers = new ulong[values.Length]; + for (int i = 0; i < values.Length; i++) + { + numbers[i] = (ulong)values[i]; + } + + this.Value = numbers; + return true; + } + + private bool SetArray(short[] values) + { + var numbers = new ulong[values.Length]; + for (int i = 0; i < values.Length; i++) + { + numbers[i] = (ulong)Numerics.Clamp(values[i], 0, short.MaxValue); + } + + this.Value = numbers; + return true; + } + + private bool SetArray(ushort[] values) + { + var numbers = new ulong[values.Length]; + for (int i = 0; i < values.Length; i++) + { + numbers[i] = (ulong)values[i]; + } + + this.Value = numbers; + return true; + } + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs index 2d3a93aed..bf9c2cf9a 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs @@ -19,16 +19,14 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif { get { - if (this.Value is null) + if (this.Value is not null) { - return ExifDataType.Short; - } - - for (int i = 0; i < this.Value.Length; i++) - { - if (this.Value[i] > ushort.MaxValue) + foreach (Number value in this.Value) { - return ExifDataType.Long; + if (value > ushort.MaxValue) + { + return ExifDataType.Long; + } } } @@ -54,13 +52,25 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif case ushort val: return this.SetSingle(val); case int[] array: + { + // workaround for inconsistent covariance of value-typed arrays + if (value.GetType() == typeof(uint[])) + { + return this.SetArray((uint[])value); + } + return this.SetArray(array); - case uint[] array: - return this.SetArray(array); + } + case short[] array: + { + if (value.GetType() == typeof(ushort[])) + { + return this.SetArray((ushort[])value); + } + return this.SetArray(array); - case ushort[] array: - return this.SetArray(array); + } } return false; diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedLong8.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedLong8.cs new file mode 100644 index 000000000..8362dcf2c --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedLong8.cs @@ -0,0 +1,26 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System.Globalization; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifSignedLong8 : ExifValue + { + public ExifSignedLong8(ExifTagValue tag) + : base(tag) + { + } + + private ExifSignedLong8(ExifSignedLong8 value) + : base(value) + { + } + + public override ExifDataType DataType => ExifDataType.SignedLong8; + + protected override string StringValue => this.Value.ToString(CultureInfo.InvariantCulture); + + public override IExifValue DeepClone() => new ExifSignedLong8(this); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedLong8Array.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedLong8Array.cs new file mode 100644 index 000000000..34ce20c8f --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedLong8Array.cs @@ -0,0 +1,22 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifSignedLong8Array : ExifArrayValue + { + public ExifSignedLong8Array(ExifTagValue tag) + : base(tag) + { + } + + private ExifSignedLong8Array(ExifSignedLong8Array value) + : base(value) + { + } + + public override ExifDataType DataType => ExifDataType.SignedLong8; + + public override IExifValue DeepClone() => new ExifSignedLong8Array(this); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValues.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValues.cs index af1eee2dc..33fb90cc0 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValues.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValues.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif public static ExifValue Create(ExifTag tag) => (ExifValue)CreateValue((ExifTagValue)(ushort)tag); - public static ExifValue Create(ExifTagValue tag, ExifDataType dataType, uint numberOfComponents) => Create(tag, dataType, numberOfComponents != 1); + public static ExifValue Create(ExifTagValue tag, ExifDataType dataType, ulong numberOfComponents) => Create(tag, dataType, numberOfComponents != 1); public static ExifValue Create(ExifTagValue tag, ExifDataType dataType, bool isArray) { @@ -19,10 +19,12 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif case ExifDataType.DoubleFloat: return isArray ? (ExifValue)new ExifDoubleArray(tag) : new ExifDouble(tag); case ExifDataType.SingleFloat: return isArray ? (ExifValue)new ExifFloatArray(tag) : new ExifFloat(tag); case ExifDataType.Long: return isArray ? (ExifValue)new ExifLongArray(tag) : new ExifLong(tag); + case ExifDataType.Long8: return isArray ? (ExifValue)new ExifLong8Array(tag) : new ExifLong8(tag); case ExifDataType.Rational: return isArray ? (ExifValue)new ExifRationalArray(tag) : new ExifRational(tag); case ExifDataType.Short: return isArray ? (ExifValue)new ExifShortArray(tag) : new ExifShort(tag); case ExifDataType.SignedByte: return isArray ? (ExifValue)new ExifSignedByteArray(tag) : new ExifSignedByte(tag); case ExifDataType.SignedLong: return isArray ? (ExifValue)new ExifSignedLongArray(tag) : new ExifSignedLong(tag); + case ExifDataType.SignedLong8: return isArray ? (ExifValue)new ExifSignedLong8Array(tag) : new ExifSignedLong8(tag); case ExifDataType.SignedRational: return isArray ? (ExifValue)new ExifSignedRationalArray(tag) : new ExifSignedRational(tag); case ExifDataType.SignedShort: return isArray ? (ExifValue)new ExifSignedShortArray(tag) : new ExifSignedShort(tag); case ExifDataType.Ascii: return new ExifString(tag); @@ -90,6 +92,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif case ExifTagValue.StripRowCounts: return new ExifLongArray(ExifTag.StripRowCounts); case ExifTagValue.IntergraphRegisters: return new ExifLongArray(ExifTag.IntergraphRegisters); case ExifTagValue.TimeZoneOffset: return new ExifLongArray(ExifTag.TimeZoneOffset); + case ExifTagValue.SubIFDs: return new ExifLongArray(ExifTag.SubIFDs); case ExifTagValue.ImageWidth: return new ExifNumber(ExifTag.ImageWidth); case ExifTagValue.ImageLength: return new ExifNumber(ExifTag.ImageLength); diff --git a/src/ImageSharp/PixelFormats/IPixel.cs b/src/ImageSharp/PixelFormats/IPixel.cs index 12b5bc784..09f8cc955 100644 --- a/src/ImageSharp/PixelFormats/IPixel.cs +++ b/src/ImageSharp/PixelFormats/IPixel.cs @@ -79,6 +79,12 @@ namespace SixLabors.ImageSharp.PixelFormats /// The value. void FromBgra32(Bgra32 source); + /// + /// Initializes the pixel instance from an value. + /// + /// The value. + void FromAbgr32(Abgr32 source); + /// /// Initializes the pixel instance from an value. /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs b/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs index cca7ff7db..afb07433f 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs @@ -87,6 +87,10 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra32(Bgra32 source) => this.PackedValue = source.A; + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromAbgr32(Abgr32 source) => this.PackedValue = source.A; + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs new file mode 100644 index 000000000..ca8f5c144 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs @@ -0,0 +1,396 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Packed pixel type containing four 8-bit unsigned normalized values ranging from 0 to 255. + /// The color components are stored in alpha, red, green, and blue order (least significant to most significant byte). + /// + /// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form. + /// + /// + /// + /// This struct is fully mutable. This is done (against the guidelines) for the sake of performance, + /// as it avoids the need to create new values for modification operations. + /// + [StructLayout(LayoutKind.Sequential)] + public partial struct Abgr32 : IPixel, IPackedVector + { + /// + /// Gets or sets the alpha component. + /// + public byte A; + + /// + /// Gets or sets the blue component. + /// + public byte B; + + /// + /// Gets or sets the green component. + /// + public byte G; + + /// + /// Gets or sets the red component. + /// + public byte R; + + /// + /// The maximum byte value. + /// + private static readonly Vector4 MaxBytes = new(255); + + /// + /// The half vector value. + /// + private static readonly Vector4 Half = new(0.5F); + + /// + /// Initializes a new instance of the struct. + /// + /// The red component. + /// The green component. + /// The blue component. + [MethodImpl(InliningOptions.ShortMethod)] + public Abgr32(byte r, byte g, byte b) + { + this.R = r; + this.G = g; + this.B = b; + this.A = byte.MaxValue; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The red component. + /// The green component. + /// The blue component. + /// The alpha component. + [MethodImpl(InliningOptions.ShortMethod)] + public Abgr32(byte r, byte g, byte b, byte a) + { + this.R = r; + this.G = g; + this.B = b; + this.A = a; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The red component. + /// The green component. + /// The blue component. + /// The alpha component. + [MethodImpl(InliningOptions.ShortMethod)] + public Abgr32(float r, float g, float b, float a = 1) + : this() => this.Pack(r, g, b, a); + + /// + /// Initializes a new instance of the struct. + /// + /// + /// The vector containing the components for the packed vector. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Abgr32(Vector3 vector) + : this() => this.Pack(ref vector); + + /// + /// Initializes a new instance of the struct. + /// + /// + /// The vector containing the components for the packed vector. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Abgr32(Vector4 vector) + : this() => this.Pack(ref vector); + + /// + /// Initializes a new instance of the struct. + /// + /// + /// The packed value. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Abgr32(uint packed) + : this() => this.Abgr = packed; + + /// + /// Gets or sets the packed representation of the Abgrb32 struct. + /// + public uint Abgr + { + [MethodImpl(InliningOptions.ShortMethod)] + readonly get => Unsafe.As(ref Unsafe.AsRef(this)); + + [MethodImpl(InliningOptions.ShortMethod)] + set => Unsafe.As(ref this) = value; + } + + /// + public uint PackedValue + { + [MethodImpl(InliningOptions.ShortMethod)] + readonly get => this.Abgr; + + [MethodImpl(InliningOptions.ShortMethod)] + set => this.Abgr = value; + } + + /// + /// Converts an to . + /// + /// The . + /// The . + [MethodImpl(InliningOptions.ShortMethod)] + public static implicit operator Color(Abgr32 source) => new(source); + + /// + /// Converts a to . + /// + /// The . + /// The . + [MethodImpl(InliningOptions.ShortMethod)] + public static implicit operator Abgr32(Color color) => color.ToAbgr32(); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator ==(Abgr32 left, Abgr32 right) => left.Equals(right); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(Abgr32 left, Abgr32 right) => !left.Equals(right); + + /// + public readonly PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public readonly Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromVector4(Vector4 vector) => this.Pack(ref vector); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public readonly Vector4 ToVector4() => new Vector4(this.R, this.G, this.B, this.A) / MaxBytes; + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromAbgr32(Abgr32 source) => this = source; + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgr24(Bgr24 source) + { + // We can assign the Bgr24 value directly to last three bytes of this instance. + ref byte thisRef = ref Unsafe.As(ref this); + ref byte thisRefFromB = ref Unsafe.AddByteOffset(ref thisRef, new IntPtr(1)); + Unsafe.As(ref thisRefFromB) = source; + this.A = byte.MaxValue; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromArgb32(Argb32 source) + { + this.R = source.R; + this.G = source.G; + this.B = source.B; + this.A = source.A; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra32(Bgra32 source) + { + this.R = source.R; + this.G = source.G; + this.B = source.B; + this.A = source.A; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromL8(L8 source) + { + this.R = source.PackedValue; + this.G = source.PackedValue; + this.B = source.PackedValue; + this.A = byte.MaxValue; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromL16(L16 source) + { + byte rgb = ColorNumerics.DownScaleFrom16BitTo8Bit(source.PackedValue); + this.R = rgb; + this.G = rgb; + this.B = rgb; + this.A = byte.MaxValue; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa16(La16 source) + { + this.R = source.L; + this.G = source.L; + this.B = source.L; + this.A = source.A; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa32(La32 source) + { + byte rgb = ColorNumerics.DownScaleFrom16BitTo8Bit(source.L); + this.R = rgb; + this.G = rgb; + this.B = rgb; + this.A = ColorNumerics.DownScaleFrom16BitTo8Bit(source.A); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb24(Rgb24 source) + { + this.R = source.R; + this.G = source.G; + this.B = source.B; + this.A = byte.MaxValue; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba32(Rgba32 source) + { + this.R = source.R; + this.G = source.G; + this.B = source.B; + this.A = source.A; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void ToRgba32(ref Rgba32 dest) + { + dest.R = this.R; + dest.G = this.G; + dest.B = this.B; + dest.A = this.A; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb48(Rgb48 source) + { + this.R = ColorNumerics.DownScaleFrom16BitTo8Bit(source.R); + this.G = ColorNumerics.DownScaleFrom16BitTo8Bit(source.G); + this.B = ColorNumerics.DownScaleFrom16BitTo8Bit(source.B); + this.A = byte.MaxValue; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba64(Rgba64 source) + { + this.R = ColorNumerics.DownScaleFrom16BitTo8Bit(source.R); + this.G = ColorNumerics.DownScaleFrom16BitTo8Bit(source.G); + this.B = ColorNumerics.DownScaleFrom16BitTo8Bit(source.B); + this.A = ColorNumerics.DownScaleFrom16BitTo8Bit(source.A); + } + + /// + public override readonly bool Equals(object obj) => obj is Abgr32 abgr32 && this.Equals(abgr32); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public readonly bool Equals(Abgr32 other) => this.Abgr == other.Abgr; + + /// + /// Gets a string representation of the packed vector. + /// + /// A string representation of the packed vector. + public override readonly string ToString() => $"Abgr({this.A}, {this.B}, {this.G}, {this.R})"; + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public override readonly int GetHashCode() => this.Abgr.GetHashCode(); + + /// + /// Packs the four floats into a color. + /// + /// The x-component + /// The y-component + /// The z-component + /// The w-component + [MethodImpl(InliningOptions.ShortMethod)] + private void Pack(float x, float y, float z, float w) + { + var value = new Vector4(x, y, z, w); + this.Pack(ref value); + } + + /// + /// Packs a into a uint. + /// + /// The vector containing the values to pack. + [MethodImpl(InliningOptions.ShortMethod)] + private void Pack(ref Vector3 vector) + { + var value = new Vector4(vector, 1); + this.Pack(ref value); + } + + /// + /// Packs a into a color. + /// + /// The vector containing the values to pack. + [MethodImpl(InliningOptions.ShortMethod)] + private void Pack(ref Vector4 vector) + { + vector *= MaxBytes; + vector += Half; + vector = Numerics.Clamp(vector, Vector4.Zero, MaxBytes); + + this.R = (byte)vector.X; + this.G = (byte)vector.Y; + this.B = (byte)vector.Z; + this.A = (byte)vector.W; + } + } +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs index 8c1b04ff1..2ec85de93 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs @@ -230,6 +230,16 @@ namespace SixLabors.ImageSharp.PixelFormats this.A = source.A; } + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromAbgr32(Abgr32 source) + { + this.R = source.R; + this.G = source.G; + this.B = source.B; + this.A = source.A; + } + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromL8(L8 source) diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs index 22e983a65..81c178348 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs @@ -185,6 +185,16 @@ namespace SixLabors.ImageSharp.PixelFormats this.B = source.B; } + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromAbgr32(Abgr32 source) + { + // We can assign this instances value directly to last three bytes of the Abgr32. + ref byte sourceRef = ref Unsafe.As(ref source); + ref byte sourceRefFromB = ref Unsafe.AddByteOffset(ref sourceRef, new IntPtr(1)); + this = Unsafe.As(ref sourceRefFromB); + } + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromRgba32(Rgba32 source) => this = source.Bgr; diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs index 5585310b9..bd21d0425 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs @@ -99,6 +99,10 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra32(Bgra32 source) => this.FromVector4(source.ToVector4()); + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromAbgr32(Abgr32 source) => this.FromVector4(source.ToVector4()); + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs index be4e178c2..34769e32d 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs @@ -165,6 +165,16 @@ namespace SixLabors.ImageSharp.PixelFormats this.A = source.A; } + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromAbgr32(Abgr32 source) + { + this.R = source.R; + this.G = source.G; + this.B = source.B; + this.A = source.A; + } + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromBgr24(Bgr24 source) diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs index 3578f1dd3..059d61136 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs @@ -102,6 +102,10 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs index 0254397c3..b6cc1f878 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs @@ -124,6 +124,10 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// [MethodImpl(InliningOptions.ShortMethod)] public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs index 0995f8417..e1ed4577e 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs @@ -124,6 +124,10 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// [MethodImpl(InliningOptions.ShortMethod)] public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs b/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs index b0ef0f6a9..51b6af640 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs @@ -88,6 +88,10 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs index 8be826130..1fff37757 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs @@ -99,6 +99,10 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs index 955b274ac..94051e263 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs @@ -104,6 +104,10 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs b/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs index 6d1128dd2..c40e301de 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs @@ -91,6 +91,13 @@ namespace SixLabors.ImageSharp.PixelFormats ColorNumerics.UpscaleFrom8BitTo16Bit(source.G), ColorNumerics.UpscaleFrom8BitTo16Bit(source.B)); + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromAbgr32(Abgr32 source) => this.PackedValue = ColorNumerics.Get16BitBT709Luminance( + ColorNumerics.UpscaleFrom8BitTo16Bit(source.R), + ColorNumerics.UpscaleFrom8BitTo16Bit(source.G), + ColorNumerics.UpscaleFrom8BitTo16Bit(source.B)); + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs b/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs index ffff60be5..70d031aa1 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs @@ -83,6 +83,10 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra32(Bgra32 source) => this.PackedValue = ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B); + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromAbgr32(Abgr32 source) => this.PackedValue = ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B); + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs b/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs index 877aaed81..72f188fa3 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs @@ -112,6 +112,14 @@ namespace SixLabors.ImageSharp.PixelFormats this.A = source.A; } + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromAbgr32(Abgr32 source) + { + this.L = ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B); + this.A = source.A; + } + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs index f19f22813..d9104aa4f 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs @@ -127,6 +127,18 @@ namespace SixLabors.ImageSharp.PixelFormats this.A = ColorNumerics.UpscaleFrom8BitTo16Bit(source.A); } + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromAbgr32(Abgr32 source) + { + this.L = ColorNumerics.Get16BitBT709Luminance( + ColorNumerics.UpscaleFrom8BitTo16Bit(source.R), + ColorNumerics.UpscaleFrom8BitTo16Bit(source.G), + ColorNumerics.UpscaleFrom8BitTo16Bit(source.B)); + + this.A = ColorNumerics.UpscaleFrom8BitTo16Bit(source.A); + } + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs index 62eaf949d..78c83a543 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs @@ -107,6 +107,10 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs index 2e81b3e2d..432461f75 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs @@ -109,6 +109,10 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs index b97aaacec..60fe2368a 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs @@ -108,6 +108,10 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs index f2e8aedd8..01b9777b0 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs @@ -110,6 +110,10 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Abgr32.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Abgr32.PixelOperations.cs new file mode 100644 index 000000000..439c5529a --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Abgr32.PixelOperations.cs @@ -0,0 +1,26 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.Formats; + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Provides optimized overrides for bulk operations. + /// + public partial struct Abgr32 + { + /// + /// Provides optimized overrides for bulk operations. + /// + internal partial class PixelOperations : PixelOperations + { + private static readonly Lazy LazyInfo = + new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.Unassociated), true); + + /// + public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; + } + } +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Abgr32.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Abgr32.PixelOperations.Generated.cs new file mode 100644 index 000000000..026025f76 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Abgr32.PixelOperations.Generated.cs @@ -0,0 +1,346 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +// + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using SixLabors.ImageSharp.PixelFormats.Utils; + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Provides optimized overrides for bulk operations. + /// + public partial struct Abgr32 + { + /// + /// Provides optimized overrides for bulk operations. + /// + internal partial class PixelOperations : PixelOperations + { + /// + public override void FromAbgr32(Configuration configuration, ReadOnlySpan source, Span destinationPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(source, destinationPixels, nameof(destinationPixels)); + + source.CopyTo(destinationPixels.Slice(0, source.Length)); + } + + /// + public override void ToAbgr32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + + sourcePixels.CopyTo(destinationPixels.Slice(0, sourcePixels.Length)); + } + /// + public override void FromVector4Destructive( + Configuration configuration, + Span sourceVectors, + Span destinationPixels, + PixelConversionModifiers modifiers) + { + Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destinationPixels, modifiers.Remove(PixelConversionModifiers.Scale)); + } + + /// + public override void ToVector4( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destVectors, + PixelConversionModifiers modifiers) + { + Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, modifiers.Remove(PixelConversionModifiers.Scale)); + } + /// + public override void ToRgba32( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destinationPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + + ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); + Span dest = MemoryMarshal.Cast(destinationPixels); + PixelConverter.FromAbgr32.ToRgba32(source, dest); + } + + /// + public override void FromRgba32( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destinationPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + + ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); + Span dest = MemoryMarshal.Cast(destinationPixels); + PixelConverter.FromRgba32.ToAbgr32(source, dest); + } + /// + public override void ToArgb32( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destinationPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + + ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); + Span dest = MemoryMarshal.Cast(destinationPixels); + PixelConverter.FromAbgr32.ToArgb32(source, dest); + } + + /// + public override void FromArgb32( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destinationPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + + ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); + Span dest = MemoryMarshal.Cast(destinationPixels); + PixelConverter.FromArgb32.ToAbgr32(source, dest); + } + /// + public override void ToBgra32( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destinationPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + + ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); + Span dest = MemoryMarshal.Cast(destinationPixels); + PixelConverter.FromAbgr32.ToBgra32(source, dest); + } + + /// + public override void FromBgra32( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destinationPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + + ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); + Span dest = MemoryMarshal.Cast(destinationPixels); + PixelConverter.FromBgra32.ToAbgr32(source, dest); + } + /// + public override void ToRgb24( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destinationPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + + ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); + Span dest = MemoryMarshal.Cast(destinationPixels); + PixelConverter.FromAbgr32.ToRgb24(source, dest); + } + + /// + public override void FromRgb24( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destinationPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + + ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); + Span dest = MemoryMarshal.Cast(destinationPixels); + PixelConverter.FromRgb24.ToAbgr32(source, dest); + } + /// + public override void ToBgr24( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destinationPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + + ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); + Span dest = MemoryMarshal.Cast(destinationPixels); + PixelConverter.FromAbgr32.ToBgr24(source, dest); + } + + /// + public override void FromBgr24( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destinationPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + + ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); + Span dest = MemoryMarshal.Cast(destinationPixels); + PixelConverter.FromBgr24.ToAbgr32(source, dest); + } + /// + public override void ToL8( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destinationPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + + ref Abgr32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref L8 destRef = ref MemoryMarshal.GetReference(destinationPixels); + + for (nint i = 0; i < sourcePixels.Length; i++) + { + ref Abgr32 sp = ref Unsafe.Add(ref sourceRef, i); + ref L8 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromAbgr32(sp); + } + } + /// + public override void ToL16( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destinationPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + + ref Abgr32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref L16 destRef = ref MemoryMarshal.GetReference(destinationPixels); + + for (nint i = 0; i < sourcePixels.Length; i++) + { + ref Abgr32 sp = ref Unsafe.Add(ref sourceRef, i); + ref L16 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromAbgr32(sp); + } + } + /// + public override void ToLa16( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destinationPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + + ref Abgr32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref La16 destRef = ref MemoryMarshal.GetReference(destinationPixels); + + for (nint i = 0; i < sourcePixels.Length; i++) + { + ref Abgr32 sp = ref Unsafe.Add(ref sourceRef, i); + ref La16 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromAbgr32(sp); + } + } + /// + public override void ToLa32( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destinationPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + + ref Abgr32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref La32 destRef = ref MemoryMarshal.GetReference(destinationPixels); + + for (nint i = 0; i < sourcePixels.Length; i++) + { + ref Abgr32 sp = ref Unsafe.Add(ref sourceRef, i); + ref La32 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromAbgr32(sp); + } + } + /// + public override void ToRgb48( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destinationPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + + ref Abgr32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgb48 destRef = ref MemoryMarshal.GetReference(destinationPixels); + + for (nint i = 0; i < sourcePixels.Length; i++) + { + ref Abgr32 sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgb48 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromAbgr32(sp); + } + } + /// + public override void ToRgba64( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destinationPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + + ref Abgr32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgba64 destRef = ref MemoryMarshal.GetReference(destinationPixels); + + for (nint i = 0; i < sourcePixels.Length; i++) + { + ref Abgr32 sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgba64 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromAbgr32(sp); + } + } + /// + public override void ToBgra5551( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destinationPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + + ref Abgr32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Bgra5551 destRef = ref MemoryMarshal.GetReference(destinationPixels); + + for (nint i = 0; i < sourcePixels.Length; i++) + { + ref Abgr32 sp = ref Unsafe.Add(ref sourceRef, i); + ref Bgra5551 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromAbgr32(sp); + } + } + /// + public override void From( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destinationPixels) + { + PixelOperations.Instance.ToAbgr32(configuration, sourcePixels, destinationPixels.Slice(0, sourcePixels.Length)); + } + + } + } +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Abgr32.PixelOperations.Generated.tt b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Abgr32.PixelOperations.Generated.tt new file mode 100644 index 000000000..071c74fbb --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Abgr32.PixelOperations.Generated.tt @@ -0,0 +1,18 @@ +<#@include file="_Common.ttinclude" #> +<#@ output extension=".cs" #> +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Provides optimized overrides for bulk operations. + /// + public partial struct Abgr32 + { + /// + /// Provides optimized overrides for bulk operations. + /// + internal partial class PixelOperations : PixelOperations + { + <# GenerateAllDefaultConversionMethods("Abgr32"); #> + } + } +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Argb32.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Argb32.PixelOperations.Generated.cs index cedd1762d..4b9f68a68 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Argb32.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Argb32.PixelOperations.Generated.cs @@ -85,6 +85,33 @@ namespace SixLabors.ImageSharp.PixelFormats PixelConverter.FromRgba32.ToArgb32(source, dest); } /// + public override void ToAbgr32( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destinationPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + + ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); + Span dest = MemoryMarshal.Cast(destinationPixels); + PixelConverter.FromArgb32.ToAbgr32(source, dest); + } + + /// + public override void FromAbgr32( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destinationPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + + ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); + Span dest = MemoryMarshal.Cast(destinationPixels); + PixelConverter.FromAbgr32.ToArgb32(source, dest); + } + /// public override void ToBgra32( Configuration configuration, ReadOnlySpan sourcePixels, @@ -177,7 +204,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Argb32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref L8 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Argb32 sp = ref Unsafe.Add(ref sourceRef, i); ref L8 dp = ref Unsafe.Add(ref destRef, i); @@ -197,7 +224,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Argb32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref L16 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Argb32 sp = ref Unsafe.Add(ref sourceRef, i); ref L16 dp = ref Unsafe.Add(ref destRef, i); @@ -217,7 +244,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Argb32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref La16 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Argb32 sp = ref Unsafe.Add(ref sourceRef, i); ref La16 dp = ref Unsafe.Add(ref destRef, i); @@ -237,7 +264,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Argb32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref La32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Argb32 sp = ref Unsafe.Add(ref sourceRef, i); ref La32 dp = ref Unsafe.Add(ref destRef, i); @@ -257,7 +284,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Argb32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb48 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Argb32 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb48 dp = ref Unsafe.Add(ref destRef, i); @@ -277,7 +304,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Argb32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba64 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Argb32 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgba64 dp = ref Unsafe.Add(ref destRef, i); @@ -297,7 +324,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Argb32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra5551 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Argb32 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgra5551 dp = ref Unsafe.Add(ref destRef, i); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Bgr24.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Bgr24.PixelOperations.Generated.cs index c98e35656..c85e4ee3b 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Bgr24.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Bgr24.PixelOperations.Generated.cs @@ -112,6 +112,33 @@ namespace SixLabors.ImageSharp.PixelFormats PixelConverter.FromArgb32.ToBgr24(source, dest); } /// + public override void ToAbgr32( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destinationPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + + ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); + Span dest = MemoryMarshal.Cast(destinationPixels); + PixelConverter.FromBgr24.ToAbgr32(source, dest); + } + + /// + public override void FromAbgr32( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destinationPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + + ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); + Span dest = MemoryMarshal.Cast(destinationPixels); + PixelConverter.FromAbgr32.ToBgr24(source, dest); + } + /// public override void ToBgra32( Configuration configuration, ReadOnlySpan sourcePixels, @@ -177,7 +204,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Bgr24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref L8 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Bgr24 sp = ref Unsafe.Add(ref sourceRef, i); ref L8 dp = ref Unsafe.Add(ref destRef, i); @@ -197,7 +224,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Bgr24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref L16 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Bgr24 sp = ref Unsafe.Add(ref sourceRef, i); ref L16 dp = ref Unsafe.Add(ref destRef, i); @@ -217,7 +244,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Bgr24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref La16 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Bgr24 sp = ref Unsafe.Add(ref sourceRef, i); ref La16 dp = ref Unsafe.Add(ref destRef, i); @@ -237,7 +264,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Bgr24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref La32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Bgr24 sp = ref Unsafe.Add(ref sourceRef, i); ref La32 dp = ref Unsafe.Add(ref destRef, i); @@ -257,7 +284,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Bgr24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb48 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Bgr24 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb48 dp = ref Unsafe.Add(ref destRef, i); @@ -277,7 +304,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Bgr24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba64 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Bgr24 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgba64 dp = ref Unsafe.Add(ref destRef, i); @@ -297,7 +324,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Bgr24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra5551 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Bgr24 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgra5551 dp = ref Unsafe.Add(ref destRef, i); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Bgra32.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Bgra32.PixelOperations.Generated.cs index 02bb67532..ed1366b0a 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Bgra32.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Bgra32.PixelOperations.Generated.cs @@ -112,6 +112,33 @@ namespace SixLabors.ImageSharp.PixelFormats PixelConverter.FromArgb32.ToBgra32(source, dest); } /// + public override void ToAbgr32( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destinationPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + + ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); + Span dest = MemoryMarshal.Cast(destinationPixels); + PixelConverter.FromBgra32.ToAbgr32(source, dest); + } + + /// + public override void FromAbgr32( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destinationPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + + ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); + Span dest = MemoryMarshal.Cast(destinationPixels); + PixelConverter.FromAbgr32.ToBgra32(source, dest); + } + /// public override void ToRgb24( Configuration configuration, ReadOnlySpan sourcePixels, @@ -177,7 +204,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Bgra32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref L8 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Bgra32 sp = ref Unsafe.Add(ref sourceRef, i); ref L8 dp = ref Unsafe.Add(ref destRef, i); @@ -197,7 +224,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Bgra32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref L16 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Bgra32 sp = ref Unsafe.Add(ref sourceRef, i); ref L16 dp = ref Unsafe.Add(ref destRef, i); @@ -217,7 +244,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Bgra32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref La16 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Bgra32 sp = ref Unsafe.Add(ref sourceRef, i); ref La16 dp = ref Unsafe.Add(ref destRef, i); @@ -237,7 +264,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Bgra32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref La32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Bgra32 sp = ref Unsafe.Add(ref sourceRef, i); ref La32 dp = ref Unsafe.Add(ref destRef, i); @@ -257,7 +284,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Bgra32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb48 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Bgra32 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb48 dp = ref Unsafe.Add(ref destRef, i); @@ -277,7 +304,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Bgra32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba64 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Bgra32 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgba64 dp = ref Unsafe.Add(ref destRef, i); @@ -297,7 +324,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Bgra32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra5551 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Bgra32 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgra5551 dp = ref Unsafe.Add(ref destRef, i); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Bgra5551.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Bgra5551.PixelOperations.Generated.cs index a02ffc3a4..0dfa46c14 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Bgra5551.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Bgra5551.PixelOperations.Generated.cs @@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Bgra5551 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Argb32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Bgra5551 sp = ref Unsafe.Add(ref sourceRef, i); ref Argb32 dp = ref Unsafe.Add(ref destRef, i); @@ -59,6 +59,26 @@ namespace SixLabors.ImageSharp.PixelFormats } } /// + public override void ToAbgr32( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destinationPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + + ref Bgra5551 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Abgr32 destRef = ref MemoryMarshal.GetReference(destinationPixels); + + for (nint i = 0; i < sourcePixels.Length; i++) + { + ref Bgra5551 sp = ref Unsafe.Add(ref sourceRef, i); + ref Abgr32 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromBgra5551(sp); + } + } + /// public override void ToBgr24( Configuration configuration, ReadOnlySpan sourcePixels, @@ -70,7 +90,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Bgra5551 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgr24 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Bgra5551 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgr24 dp = ref Unsafe.Add(ref destRef, i); @@ -90,7 +110,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Bgra5551 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Bgra5551 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgra32 dp = ref Unsafe.Add(ref destRef, i); @@ -110,7 +130,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Bgra5551 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref L8 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Bgra5551 sp = ref Unsafe.Add(ref sourceRef, i); ref L8 dp = ref Unsafe.Add(ref destRef, i); @@ -130,7 +150,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Bgra5551 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref L16 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Bgra5551 sp = ref Unsafe.Add(ref sourceRef, i); ref L16 dp = ref Unsafe.Add(ref destRef, i); @@ -150,7 +170,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Bgra5551 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref La16 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Bgra5551 sp = ref Unsafe.Add(ref sourceRef, i); ref La16 dp = ref Unsafe.Add(ref destRef, i); @@ -170,7 +190,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Bgra5551 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref La32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Bgra5551 sp = ref Unsafe.Add(ref sourceRef, i); ref La32 dp = ref Unsafe.Add(ref destRef, i); @@ -190,7 +210,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Bgra5551 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb24 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Bgra5551 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb24 dp = ref Unsafe.Add(ref destRef, i); @@ -210,7 +230,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Bgra5551 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Bgra5551 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgba32 dp = ref Unsafe.Add(ref destRef, i); @@ -230,7 +250,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Bgra5551 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb48 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Bgra5551 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb48 dp = ref Unsafe.Add(ref destRef, i); @@ -250,7 +270,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Bgra5551 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba64 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Bgra5551 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgba64 dp = ref Unsafe.Add(ref destRef, i); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/L16.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/L16.PixelOperations.Generated.cs index 954ef2d98..19e0548ae 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/L16.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/L16.PixelOperations.Generated.cs @@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref L16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Argb32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref L16 sp = ref Unsafe.Add(ref sourceRef, i); ref Argb32 dp = ref Unsafe.Add(ref destRef, i); @@ -59,6 +59,26 @@ namespace SixLabors.ImageSharp.PixelFormats } } /// + public override void ToAbgr32( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destinationPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + + ref L16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Abgr32 destRef = ref MemoryMarshal.GetReference(destinationPixels); + + for (nint i = 0; i < sourcePixels.Length; i++) + { + ref L16 sp = ref Unsafe.Add(ref sourceRef, i); + ref Abgr32 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromL16(sp); + } + } + /// public override void ToBgr24( Configuration configuration, ReadOnlySpan sourcePixels, @@ -70,7 +90,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref L16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgr24 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref L16 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgr24 dp = ref Unsafe.Add(ref destRef, i); @@ -90,7 +110,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref L16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref L16 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgra32 dp = ref Unsafe.Add(ref destRef, i); @@ -110,7 +130,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref L16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref L8 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref L16 sp = ref Unsafe.Add(ref sourceRef, i); ref L8 dp = ref Unsafe.Add(ref destRef, i); @@ -130,7 +150,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref L16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref La16 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref L16 sp = ref Unsafe.Add(ref sourceRef, i); ref La16 dp = ref Unsafe.Add(ref destRef, i); @@ -150,7 +170,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref L16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref La32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref L16 sp = ref Unsafe.Add(ref sourceRef, i); ref La32 dp = ref Unsafe.Add(ref destRef, i); @@ -170,7 +190,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref L16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb24 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref L16 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb24 dp = ref Unsafe.Add(ref destRef, i); @@ -190,7 +210,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref L16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref L16 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgba32 dp = ref Unsafe.Add(ref destRef, i); @@ -210,7 +230,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref L16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb48 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref L16 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb48 dp = ref Unsafe.Add(ref destRef, i); @@ -230,7 +250,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref L16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba64 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref L16 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgba64 dp = ref Unsafe.Add(ref destRef, i); @@ -250,7 +270,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref L16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra5551 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref L16 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgra5551 dp = ref Unsafe.Add(ref destRef, i); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/L8.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/L8.PixelOperations.Generated.cs index b3d809de5..8997449d3 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/L8.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/L8.PixelOperations.Generated.cs @@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref L8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Argb32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref L8 sp = ref Unsafe.Add(ref sourceRef, i); ref Argb32 dp = ref Unsafe.Add(ref destRef, i); @@ -59,6 +59,26 @@ namespace SixLabors.ImageSharp.PixelFormats } } /// + public override void ToAbgr32( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destinationPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + + ref L8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Abgr32 destRef = ref MemoryMarshal.GetReference(destinationPixels); + + for (nint i = 0; i < sourcePixels.Length; i++) + { + ref L8 sp = ref Unsafe.Add(ref sourceRef, i); + ref Abgr32 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromL8(sp); + } + } + /// public override void ToBgr24( Configuration configuration, ReadOnlySpan sourcePixels, @@ -70,7 +90,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref L8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgr24 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref L8 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgr24 dp = ref Unsafe.Add(ref destRef, i); @@ -90,7 +110,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref L8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref L8 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgra32 dp = ref Unsafe.Add(ref destRef, i); @@ -110,7 +130,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref L8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref L16 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref L8 sp = ref Unsafe.Add(ref sourceRef, i); ref L16 dp = ref Unsafe.Add(ref destRef, i); @@ -130,7 +150,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref L8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref La16 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref L8 sp = ref Unsafe.Add(ref sourceRef, i); ref La16 dp = ref Unsafe.Add(ref destRef, i); @@ -150,7 +170,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref L8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref La32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref L8 sp = ref Unsafe.Add(ref sourceRef, i); ref La32 dp = ref Unsafe.Add(ref destRef, i); @@ -170,7 +190,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref L8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb24 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref L8 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb24 dp = ref Unsafe.Add(ref destRef, i); @@ -190,7 +210,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref L8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref L8 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgba32 dp = ref Unsafe.Add(ref destRef, i); @@ -210,7 +230,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref L8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb48 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref L8 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb48 dp = ref Unsafe.Add(ref destRef, i); @@ -230,7 +250,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref L8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba64 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref L8 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgba64 dp = ref Unsafe.Add(ref destRef, i); @@ -250,7 +270,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref L8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra5551 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref L8 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgra5551 dp = ref Unsafe.Add(ref destRef, i); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/La16.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/La16.PixelOperations.Generated.cs index 14618d026..8166862fe 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/La16.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/La16.PixelOperations.Generated.cs @@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref La16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Argb32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref La16 sp = ref Unsafe.Add(ref sourceRef, i); ref Argb32 dp = ref Unsafe.Add(ref destRef, i); @@ -59,6 +59,26 @@ namespace SixLabors.ImageSharp.PixelFormats } } /// + public override void ToAbgr32( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destinationPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + + ref La16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Abgr32 destRef = ref MemoryMarshal.GetReference(destinationPixels); + + for (nint i = 0; i < sourcePixels.Length; i++) + { + ref La16 sp = ref Unsafe.Add(ref sourceRef, i); + ref Abgr32 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromLa16(sp); + } + } + /// public override void ToBgr24( Configuration configuration, ReadOnlySpan sourcePixels, @@ -70,7 +90,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref La16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgr24 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref La16 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgr24 dp = ref Unsafe.Add(ref destRef, i); @@ -90,7 +110,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref La16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref La16 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgra32 dp = ref Unsafe.Add(ref destRef, i); @@ -110,7 +130,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref La16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref L8 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref La16 sp = ref Unsafe.Add(ref sourceRef, i); ref L8 dp = ref Unsafe.Add(ref destRef, i); @@ -130,7 +150,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref La16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref L16 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref La16 sp = ref Unsafe.Add(ref sourceRef, i); ref L16 dp = ref Unsafe.Add(ref destRef, i); @@ -150,7 +170,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref La16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref La32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref La16 sp = ref Unsafe.Add(ref sourceRef, i); ref La32 dp = ref Unsafe.Add(ref destRef, i); @@ -170,7 +190,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref La16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb24 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref La16 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb24 dp = ref Unsafe.Add(ref destRef, i); @@ -190,7 +210,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref La16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref La16 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgba32 dp = ref Unsafe.Add(ref destRef, i); @@ -210,7 +230,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref La16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb48 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref La16 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb48 dp = ref Unsafe.Add(ref destRef, i); @@ -230,7 +250,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref La16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba64 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref La16 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgba64 dp = ref Unsafe.Add(ref destRef, i); @@ -250,7 +270,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref La16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra5551 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref La16 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgra5551 dp = ref Unsafe.Add(ref destRef, i); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/La32.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/La32.PixelOperations.Generated.cs index 9620a1df4..32a0f24e3 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/La32.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/La32.PixelOperations.Generated.cs @@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref La32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Argb32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref La32 sp = ref Unsafe.Add(ref sourceRef, i); ref Argb32 dp = ref Unsafe.Add(ref destRef, i); @@ -59,6 +59,26 @@ namespace SixLabors.ImageSharp.PixelFormats } } /// + public override void ToAbgr32( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destinationPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + + ref La32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Abgr32 destRef = ref MemoryMarshal.GetReference(destinationPixels); + + for (nint i = 0; i < sourcePixels.Length; i++) + { + ref La32 sp = ref Unsafe.Add(ref sourceRef, i); + ref Abgr32 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromLa32(sp); + } + } + /// public override void ToBgr24( Configuration configuration, ReadOnlySpan sourcePixels, @@ -70,7 +90,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref La32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgr24 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref La32 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgr24 dp = ref Unsafe.Add(ref destRef, i); @@ -90,7 +110,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref La32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref La32 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgra32 dp = ref Unsafe.Add(ref destRef, i); @@ -110,7 +130,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref La32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref L8 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref La32 sp = ref Unsafe.Add(ref sourceRef, i); ref L8 dp = ref Unsafe.Add(ref destRef, i); @@ -130,7 +150,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref La32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref L16 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref La32 sp = ref Unsafe.Add(ref sourceRef, i); ref L16 dp = ref Unsafe.Add(ref destRef, i); @@ -150,7 +170,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref La32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref La16 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref La32 sp = ref Unsafe.Add(ref sourceRef, i); ref La16 dp = ref Unsafe.Add(ref destRef, i); @@ -170,7 +190,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref La32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb24 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref La32 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb24 dp = ref Unsafe.Add(ref destRef, i); @@ -190,7 +210,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref La32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref La32 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgba32 dp = ref Unsafe.Add(ref destRef, i); @@ -210,7 +230,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref La32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb48 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref La32 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb48 dp = ref Unsafe.Add(ref destRef, i); @@ -230,7 +250,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref La32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba64 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref La32 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgba64 dp = ref Unsafe.Add(ref destRef, i); @@ -250,7 +270,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref La32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra5551 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref La32 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgra5551 dp = ref Unsafe.Add(ref destRef, i); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgb24.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgb24.PixelOperations.Generated.cs index 2fe7f3c20..53a82989d 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgb24.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgb24.PixelOperations.Generated.cs @@ -112,6 +112,33 @@ namespace SixLabors.ImageSharp.PixelFormats PixelConverter.FromArgb32.ToRgb24(source, dest); } /// + public override void ToAbgr32( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destinationPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + + ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); + Span dest = MemoryMarshal.Cast(destinationPixels); + PixelConverter.FromRgb24.ToAbgr32(source, dest); + } + + /// + public override void FromAbgr32( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destinationPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + + ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); + Span dest = MemoryMarshal.Cast(destinationPixels); + PixelConverter.FromAbgr32.ToRgb24(source, dest); + } + /// public override void ToBgra32( Configuration configuration, ReadOnlySpan sourcePixels, @@ -177,7 +204,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Rgb24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref L8 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Rgb24 sp = ref Unsafe.Add(ref sourceRef, i); ref L8 dp = ref Unsafe.Add(ref destRef, i); @@ -197,7 +224,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Rgb24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref L16 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Rgb24 sp = ref Unsafe.Add(ref sourceRef, i); ref L16 dp = ref Unsafe.Add(ref destRef, i); @@ -217,7 +244,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Rgb24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref La16 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Rgb24 sp = ref Unsafe.Add(ref sourceRef, i); ref La16 dp = ref Unsafe.Add(ref destRef, i); @@ -237,7 +264,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Rgb24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref La32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Rgb24 sp = ref Unsafe.Add(ref sourceRef, i); ref La32 dp = ref Unsafe.Add(ref destRef, i); @@ -257,7 +284,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Rgb24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb48 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Rgb24 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb48 dp = ref Unsafe.Add(ref destRef, i); @@ -277,7 +304,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Rgb24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba64 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Rgb24 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgba64 dp = ref Unsafe.Add(ref destRef, i); @@ -297,7 +324,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Rgb24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra5551 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Rgb24 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgra5551 dp = ref Unsafe.Add(ref destRef, i); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgb48.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgb48.PixelOperations.Generated.cs index 031008fe1..d1c5ab2e3 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgb48.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgb48.PixelOperations.Generated.cs @@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Argb32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Rgb48 sp = ref Unsafe.Add(ref sourceRef, i); ref Argb32 dp = ref Unsafe.Add(ref destRef, i); @@ -59,6 +59,26 @@ namespace SixLabors.ImageSharp.PixelFormats } } /// + public override void ToAbgr32( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destinationPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + + ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Abgr32 destRef = ref MemoryMarshal.GetReference(destinationPixels); + + for (nint i = 0; i < sourcePixels.Length; i++) + { + ref Rgb48 sp = ref Unsafe.Add(ref sourceRef, i); + ref Abgr32 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromRgb48(sp); + } + } + /// public override void ToBgr24( Configuration configuration, ReadOnlySpan sourcePixels, @@ -70,7 +90,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgr24 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Rgb48 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgr24 dp = ref Unsafe.Add(ref destRef, i); @@ -90,7 +110,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Rgb48 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgra32 dp = ref Unsafe.Add(ref destRef, i); @@ -110,7 +130,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref L8 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Rgb48 sp = ref Unsafe.Add(ref sourceRef, i); ref L8 dp = ref Unsafe.Add(ref destRef, i); @@ -130,7 +150,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref L16 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Rgb48 sp = ref Unsafe.Add(ref sourceRef, i); ref L16 dp = ref Unsafe.Add(ref destRef, i); @@ -150,7 +170,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref La16 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Rgb48 sp = ref Unsafe.Add(ref sourceRef, i); ref La16 dp = ref Unsafe.Add(ref destRef, i); @@ -170,7 +190,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref La32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Rgb48 sp = ref Unsafe.Add(ref sourceRef, i); ref La32 dp = ref Unsafe.Add(ref destRef, i); @@ -190,7 +210,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb24 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Rgb48 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb24 dp = ref Unsafe.Add(ref destRef, i); @@ -210,7 +230,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Rgb48 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgba32 dp = ref Unsafe.Add(ref destRef, i); @@ -230,7 +250,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba64 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Rgb48 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgba64 dp = ref Unsafe.Add(ref destRef, i); @@ -250,7 +270,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra5551 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Rgb48 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgra5551 dp = ref Unsafe.Add(ref destRef, i); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgba32.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgba32.PixelOperations.Generated.cs index 16f96d2da..2608a74fc 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgba32.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgba32.PixelOperations.Generated.cs @@ -66,6 +66,33 @@ namespace SixLabors.ImageSharp.PixelFormats PixelConverter.FromArgb32.ToRgba32(source, dest); } /// + public override void ToAbgr32( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destinationPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + + ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); + Span dest = MemoryMarshal.Cast(destinationPixels); + PixelConverter.FromRgba32.ToAbgr32(source, dest); + } + + /// + public override void FromAbgr32( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destinationPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + + ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); + Span dest = MemoryMarshal.Cast(destinationPixels); + PixelConverter.FromAbgr32.ToRgba32(source, dest); + } + /// public override void ToBgra32( Configuration configuration, ReadOnlySpan sourcePixels, @@ -158,7 +185,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Rgba32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref L8 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Rgba32 sp = ref Unsafe.Add(ref sourceRef, i); ref L8 dp = ref Unsafe.Add(ref destRef, i); @@ -178,7 +205,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Rgba32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref L16 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Rgba32 sp = ref Unsafe.Add(ref sourceRef, i); ref L16 dp = ref Unsafe.Add(ref destRef, i); @@ -198,7 +225,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Rgba32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref La16 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Rgba32 sp = ref Unsafe.Add(ref sourceRef, i); ref La16 dp = ref Unsafe.Add(ref destRef, i); @@ -218,7 +245,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Rgba32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref La32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Rgba32 sp = ref Unsafe.Add(ref sourceRef, i); ref La32 dp = ref Unsafe.Add(ref destRef, i); @@ -238,7 +265,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Rgba32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb48 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Rgba32 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb48 dp = ref Unsafe.Add(ref destRef, i); @@ -258,7 +285,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Rgba32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba64 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Rgba32 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgba64 dp = ref Unsafe.Add(ref destRef, i); @@ -278,7 +305,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Rgba32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra5551 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Rgba32 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgra5551 dp = ref Unsafe.Add(ref destRef, i); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgba64.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgba64.PixelOperations.Generated.cs index 1f1571e91..f6445039a 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgba64.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgba64.PixelOperations.Generated.cs @@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Argb32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Rgba64 sp = ref Unsafe.Add(ref sourceRef, i); ref Argb32 dp = ref Unsafe.Add(ref destRef, i); @@ -59,6 +59,26 @@ namespace SixLabors.ImageSharp.PixelFormats } } /// + public override void ToAbgr32( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destinationPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + + ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Abgr32 destRef = ref MemoryMarshal.GetReference(destinationPixels); + + for (nint i = 0; i < sourcePixels.Length; i++) + { + ref Rgba64 sp = ref Unsafe.Add(ref sourceRef, i); + ref Abgr32 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromRgba64(sp); + } + } + /// public override void ToBgr24( Configuration configuration, ReadOnlySpan sourcePixels, @@ -70,7 +90,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgr24 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Rgba64 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgr24 dp = ref Unsafe.Add(ref destRef, i); @@ -90,7 +110,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Rgba64 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgra32 dp = ref Unsafe.Add(ref destRef, i); @@ -110,7 +130,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref L8 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Rgba64 sp = ref Unsafe.Add(ref sourceRef, i); ref L8 dp = ref Unsafe.Add(ref destRef, i); @@ -130,7 +150,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref L16 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Rgba64 sp = ref Unsafe.Add(ref sourceRef, i); ref L16 dp = ref Unsafe.Add(ref destRef, i); @@ -150,7 +170,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref La16 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Rgba64 sp = ref Unsafe.Add(ref sourceRef, i); ref La16 dp = ref Unsafe.Add(ref destRef, i); @@ -170,7 +190,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref La32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Rgba64 sp = ref Unsafe.Add(ref sourceRef, i); ref La32 dp = ref Unsafe.Add(ref destRef, i); @@ -190,7 +210,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb24 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Rgba64 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb24 dp = ref Unsafe.Add(ref destRef, i); @@ -210,7 +230,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Rgba64 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgba32 dp = ref Unsafe.Add(ref destRef, i); @@ -230,7 +250,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb48 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Rgba64 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb48 dp = ref Unsafe.Add(ref destRef, i); @@ -250,7 +270,7 @@ namespace SixLabors.ImageSharp.PixelFormats ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra5551 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref Rgba64 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgra5551 dp = ref Unsafe.Add(ref destRef, i); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/_Common.ttinclude b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/_Common.ttinclude index 784ecf6fb..9a6ddd7d4 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/_Common.ttinclude +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/_Common.ttinclude @@ -17,6 +17,7 @@ using SixLabors.ImageSharp.PixelFormats.Utils; private static readonly string[] CommonPixelTypes = { "Argb32", + "Abgr32", "Bgr24", "Bgra32", "L8", @@ -34,6 +35,7 @@ using SixLabors.ImageSharp.PixelFormats.Utils; { "Rgba32", "Argb32", + "Abgr32", "Bgra32", "Rgb24", "Bgr24" @@ -43,6 +45,7 @@ using SixLabors.ImageSharp.PixelFormats.Utils; private static readonly string[] Rgba32CompatibleTypes = { "Argb32", + "Abgr32", "Bgra32", "Rgb24", "Bgr24" @@ -101,7 +104,7 @@ using SixLabors.ImageSharp.PixelFormats.Utils; ref <#=fromPixelType#> sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref <#=toPixelType#> destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nint i = 0; i < sourcePixels.Length; i++) { ref <#=fromPixelType#> sp = ref Unsafe.Add(ref sourceRef, i); ref <#=toPixelType#> dp = ref Unsafe.Add(ref destRef, i); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs index 12b6e153f..d408f301b 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs @@ -93,6 +93,10 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs index 3b5bdb3d5..00f072268 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs @@ -153,6 +153,15 @@ namespace SixLabors.ImageSharp.PixelFormats this.B = source.B; } + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromAbgr32(Abgr32 source) + { + this.R = source.R; + this.G = source.G; + this.B = source.B; + } + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromL8(L8 source) diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs index d16b7db7a..90c15ae26 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs @@ -186,6 +186,15 @@ namespace SixLabors.ImageSharp.PixelFormats this.B = ColorNumerics.UpscaleFrom8BitTo16Bit(source.B); } + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromAbgr32(Abgr32 source) + { + this.R = ColorNumerics.UpscaleFrom8BitTo16Bit(source.R); + this.G = ColorNumerics.UpscaleFrom8BitTo16Bit(source.G); + this.B = ColorNumerics.UpscaleFrom8BitTo16Bit(source.B); + } + /// [MethodImpl(InliningOptions.ShortMethod)] public void ToRgba32(ref Rgba32 dest) diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs index e68726018..11e11bc6c 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs @@ -96,6 +96,10 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs index 3dc6490f1..2d250f71d 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs @@ -333,6 +333,16 @@ namespace SixLabors.ImageSharp.PixelFormats this.A = source.A; } + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromAbgr32(Abgr32 source) + { + this.R = source.R; + this.G = source.G; + this.B = source.B; + this.A = source.A; + } + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs index 4cfa0bf97..bf7452592 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs @@ -94,6 +94,19 @@ namespace SixLabors.ImageSharp.PixelFormats this.A = ColorNumerics.UpscaleFrom8BitTo16Bit(source.A); } + /// + /// Initializes a new instance of the struct. + /// + /// A structure of 4 bytes in ABGR byte order. + [MethodImpl(InliningOptions.ShortMethod)] + public Rgba64(Abgr32 source) + { + this.R = ColorNumerics.UpscaleFrom8BitTo16Bit(source.R); + this.G = ColorNumerics.UpscaleFrom8BitTo16Bit(source.G); + this.B = ColorNumerics.UpscaleFrom8BitTo16Bit(source.B); + this.A = ColorNumerics.UpscaleFrom8BitTo16Bit(source.A); + } + /// /// Initializes a new instance of the struct. /// @@ -250,6 +263,16 @@ namespace SixLabors.ImageSharp.PixelFormats this.A = ColorNumerics.UpscaleFrom8BitTo16Bit(source.A); } + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromAbgr32(Abgr32 source) + { + this.R = ColorNumerics.UpscaleFrom8BitTo16Bit(source.R); + this.G = ColorNumerics.UpscaleFrom8BitTo16Bit(source.G); + this.B = ColorNumerics.UpscaleFrom8BitTo16Bit(source.B); + this.A = ColorNumerics.UpscaleFrom8BitTo16Bit(source.A); + } + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); @@ -380,6 +403,20 @@ namespace SixLabors.ImageSharp.PixelFormats return new Argb32(r, g, b, a); } + /// + /// Convert to . + /// + /// The . + [MethodImpl(InliningOptions.ShortMethod)] + public readonly Abgr32 ToAbgr32() + { + byte r = ColorNumerics.DownScaleFrom16BitTo8Bit(this.R); + byte g = ColorNumerics.DownScaleFrom16BitTo8Bit(this.G); + byte b = ColorNumerics.DownScaleFrom16BitTo8Bit(this.B); + byte a = ColorNumerics.DownScaleFrom16BitTo8Bit(this.A); + return new Abgr32(r, g, b, a); + } + /// /// Convert to . /// diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs index cd6f53c4e..e582e6166 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs @@ -134,6 +134,10 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs index 24f6b4d1d..f0117707c 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs @@ -111,6 +111,10 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs index 86a519297..fd58477d9 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs @@ -116,6 +116,10 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.Generated.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.Generated.cs index cc36c7d13..ea107b35a 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.Generated.cs @@ -82,6 +82,78 @@ namespace SixLabors.ImageSharp.PixelFormats this.ToArgb32(configuration, sourcePixels.Slice(0, count), MemoryMarshal.Cast(destBytes)); } + /// + /// Converts all pixels in 'source` span of into a span of -s. + /// + /// A to configure internal operations. + /// The source of data. + /// The to the destination pixels. + public virtual void FromAbgr32(Configuration configuration, ReadOnlySpan source, Span destinationPixels) + { + Guard.DestinationShouldNotBeTooShort(source, destinationPixels, nameof(destinationPixels)); + + ref Abgr32 sourceBaseRef = ref MemoryMarshal.GetReference(source); + ref TPixel destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); + + for (int i = 0; i < source.Length; i++) + { + ref Abgr32 sp = ref Unsafe.Add(ref sourceBaseRef, i); + ref TPixel dp = ref Unsafe.Add(ref destBaseRef, i); + + dp.FromAbgr32(sp); + } + } + + /// + /// A helper for that expects a byte span. + /// The layout of the data in 'sourceBytes' must be compatible with layout. + /// + /// A to configure internal operations. + /// The to the source bytes. + /// The to the destination pixels. + /// The number of pixels to convert. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromAbgr32Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destinationPixels, int count) + { + this.FromAbgr32(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destinationPixels); + } + + /// + /// Converts all pixels of the 'sourcePixels` span to a span of -s. + /// + /// A to configure internal operations + /// The span of source pixels + /// The destination span of data. + public virtual void ToAbgr32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) + { + Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); + + ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Abgr32 destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); + ref Abgr32 dp = ref Unsafe.Add(ref destBaseRef, i); + + dp.FromScaledVector4(sp.ToScaledVector4()); + } + } + + /// + /// A helper for that expects a byte span as destination. + /// The layout of the data in 'destBytes' must be compatible with layout. + /// + /// A to configure internal operations + /// The to the source pixels. + /// The to the destination bytes. + /// The number of pixels to convert. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ToAbgr32Bytes(Configuration configuration, ReadOnlySpan sourcePixels, Span destBytes, int count) + { + this.ToAbgr32(configuration, sourcePixels.Slice(0, count), MemoryMarshal.Cast(destBytes)); + } + /// /// Converts all pixels in 'source` span of into a span of -s. /// diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.Generated.tt b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.Generated.tt index 21ed328fa..e8cf6f9a5 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.Generated.tt @@ -112,6 +112,9 @@ namespace SixLabors.ImageSharp.PixelFormats GenerateFromMethods("Argb32"); GenerateToDestFormatMethods("Argb32"); + GenerateFromMethods("Abgr32"); + GenerateToDestFormatMethods("Abgr32"); + GenerateFromMethods("Bgr24"); GenerateToDestFormatMethods("Bgr24"); diff --git a/src/ImageSharp/PixelFormats/Utils/PixelConverter.cs b/src/ImageSharp/PixelFormats/Utils/PixelConverter.cs index 7215fa860..907399d5f 100644 --- a/src/ImageSharp/PixelFormats/Utils/PixelConverter.cs +++ b/src/ImageSharp/PixelFormats/Utils/PixelConverter.cs @@ -18,8 +18,13 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils /// internal static class PixelConverter { + /// + /// Optimized converters from . + /// public static class FromRgba32 { + // Input pixels have: X = R, Y = G, Z = B and W = A. + /// /// Converts a representing a collection of /// pixels to a representing @@ -38,6 +43,15 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils public static void ToBgra32(ReadOnlySpan source, Span dest) => SimdUtils.Shuffle4(source, dest, default); + /// + /// Converts a representing a collection of + /// pixels to a representing + /// a collection of pixels. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static void ToAbgr32(ReadOnlySpan source, Span dest) + => SimdUtils.Shuffle4(source, dest, default); + /// /// Converts a representing a collection of /// pixels to a representing @@ -57,8 +71,13 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils => SimdUtils.Shuffle4Slice3(source, dest, new DefaultShuffle4Slice3(3, 0, 1, 2)); } + /// + /// Optimized converters from . + /// public static class FromArgb32 { + // Input pixels have: X = A, Y = R, Z = G and W = B. + /// /// Converts a representing a collection of /// pixels to a representing @@ -77,6 +96,15 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils public static void ToBgra32(ReadOnlySpan source, Span dest) => SimdUtils.Shuffle4(source, dest, default); + /// + /// Converts a representing a collection of + /// pixels to a representing + /// a collection of pixels. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static void ToAbgr32(ReadOnlySpan source, Span dest) + => SimdUtils.Shuffle4(source, dest, default); + /// /// Converts a representing a collection of /// pixels to a representing @@ -96,8 +124,13 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils => SimdUtils.Shuffle4Slice3(source, dest, new DefaultShuffle4Slice3(0, 1, 2, 3)); } + /// + /// Optimized converters from . + /// public static class FromBgra32 { + // Input pixels have: X = B, Y = G, Z = R and W = A. + /// /// Converts a representing a collection of /// pixels to a representing @@ -110,12 +143,21 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils /// /// Converts a representing a collection of /// pixels to a representing - /// a collection of pixels. + /// a collection of pixels. /// [MethodImpl(InliningOptions.ShortMethod)] public static void ToRgba32(ReadOnlySpan source, Span dest) => SimdUtils.Shuffle4(source, dest, default); + /// + /// Converts a representing a collection of + /// pixels to a representing + /// a collection of pixels. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static void ToAbgr32(ReadOnlySpan source, Span dest) + => SimdUtils.Shuffle4(source, dest, default); + /// /// Converts a representing a collection of /// pixels to a representing @@ -135,8 +177,66 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils => SimdUtils.Shuffle4Slice3(source, dest, default); } + /// + /// Optimized converters from . + /// + public static class FromAbgr32 + { + // Input pixels have: X = A, Y = B, Z = G and W = R. + + /// + /// Converts a representing a collection of + /// pixels to a representing + /// a collection of pixels. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static void ToArgb32(ReadOnlySpan source, Span dest) + => SimdUtils.Shuffle4(source, dest, default); + + /// + /// Converts a representing a collection of + /// pixels to a representing + /// a collection of pixels. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static void ToRgba32(ReadOnlySpan source, Span dest) + => SimdUtils.Shuffle4(source, dest, default); + + /// + /// Converts a representing a collection of + /// pixels to a representing + /// a collection of pixels. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static void ToBgra32(ReadOnlySpan source, Span dest) + => SimdUtils.Shuffle4(source, dest, default); + + /// + /// Converts a representing a collection of + /// pixels to a representing + /// a collection of pixels. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static void ToRgb24(ReadOnlySpan source, Span dest) + => SimdUtils.Shuffle4Slice3(source, dest, new DefaultShuffle4Slice3(0, 1, 2, 3)); + + /// + /// Converts a representing a collection of + /// pixels to a representing + /// a collection of pixels. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static void ToBgr24(ReadOnlySpan source, Span dest) + => SimdUtils.Shuffle4Slice3(source, dest, new DefaultShuffle4Slice3(0, 3, 2, 1)); + } + + /// + /// Optimized converters from . + /// public static class FromRgb24 { + // Input pixels have: X = R, Y = G and Z = B. + /// /// Converts a representing a collection of /// pixels to a representing @@ -164,6 +264,15 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils public static void ToBgra32(ReadOnlySpan source, Span dest) => SimdUtils.Pad3Shuffle4(source, dest, new DefaultPad3Shuffle4(3, 0, 1, 2)); + /// + /// Converts a representing a collection of + /// pixels to a representing + /// a collection of pixels. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static void ToAbgr32(ReadOnlySpan source, Span dest) + => SimdUtils.Pad3Shuffle4(source, dest, new DefaultPad3Shuffle4(0, 1, 2, 3)); + /// /// Converts a representing a collection of /// pixels to a representing @@ -174,8 +283,13 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils => SimdUtils.Shuffle3(source, dest, new DefaultShuffle3(0, 1, 2)); } + /// + /// Optimized converters from . + /// public static class FromBgr24 { + // Input pixels have: X = B, Y = G and Z = R. + /// /// Converts a representing a collection of /// pixels to a representing @@ -203,6 +317,15 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils public static void ToBgra32(ReadOnlySpan source, Span dest) => SimdUtils.Pad3Shuffle4(source, dest, default); + /// + /// Converts a representing a collection of + /// pixels to a representing + /// a collection of pixels. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static void ToAbgr32(ReadOnlySpan source, Span dest) + => SimdUtils.Pad3Shuffle4(source, dest, new DefaultPad3Shuffle4(2, 1, 0, 3)); + /// /// Converts a representing a collection of /// pixels to a representing diff --git a/tests/Directory.Build.targets b/tests/Directory.Build.targets index ddceaff1f..525860189 100644 --- a/tests/Directory.Build.targets +++ b/tests/Directory.Build.targets @@ -33,6 +33,7 @@ + diff --git a/tests/ImageSharp.Benchmarks/General/Buffer2D_DangerousGetRowSpan.cs b/tests/ImageSharp.Benchmarks/General/Buffer2D_DangerousGetRowSpan.cs new file mode 100644 index 000000000..01d06bf75 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/General/Buffer2D_DangerousGetRowSpan.cs @@ -0,0 +1,46 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System.Runtime.CompilerServices; +using BenchmarkDotNet.Attributes; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Benchmarks.General +{ + public class Buffer2D_DangerousGetRowSpan + { + private const int Height = 1024; + + [Params(0.5, 2.0, 10.0)] public double SizeMegaBytes { get; set; } + + private Buffer2D buffer; + + [GlobalSetup] + public unsafe void Setup() + { + int totalElements = (int)(1024 * 1024 * this.SizeMegaBytes) / sizeof(Rgba32); + + int width = totalElements / Height; + MemoryAllocator allocator = Configuration.Default.MemoryAllocator; + this.buffer = allocator.Allocate2D(width, Height); + } + + [GlobalCleanup] + public void Cleanup() => this.buffer.Dispose(); + + [Benchmark] + public int DangerousGetRowSpan() => + this.buffer.DangerousGetRowSpan(1).Length + + this.buffer.DangerousGetRowSpan(Height - 1).Length; + + // BenchmarkDotNet=v0.13.0, OS=Windows 10.0.19044 + // Intel Core i9-10900X CPU 3.70GHz, 1 CPU, 20 logical and 10 physical cores + // + // | Method | SizeMegaBytes | Mean | Error | StdDev | + // |-------------------- |-------------- |----------:|----------:|----------:| + // | DangerousGetRowSpan | 0.5 | 7.498 ns | 0.1784 ns | 0.3394 ns | + // | DangerousGetRowSpan | 2 | 6.542 ns | 0.1565 ns | 0.3659 ns | + // | DangerousGetRowSpan | 10 | 38.556 ns | 0.6604 ns | 0.8587 ns | + } +} diff --git a/tests/ImageSharp.Tests/Color/ColorTests.CastFrom.cs b/tests/ImageSharp.Tests/Color/ColorTests.CastFrom.cs index 38b94f486..ec03847df 100644 --- a/tests/ImageSharp.Tests/Color/ColorTests.CastFrom.cs +++ b/tests/ImageSharp.Tests/Color/ColorTests.CastFrom.cs @@ -63,6 +63,19 @@ namespace SixLabors.ImageSharp.Tests Assert.Equal(source, data); } + [Fact] + public void Abgr32() + { + var source = new Abgr32(1, 22, 33, 231); + + // Act: + Color color = source; + + // Assert: + Abgr32 data = color.ToPixel(); + Assert.Equal(source, data); + } + [Fact] public void Rgb24() { diff --git a/tests/ImageSharp.Tests/Color/ColorTests.CastTo.cs b/tests/ImageSharp.Tests/Color/ColorTests.CastTo.cs index bfc290c2e..1997cc6b1 100644 --- a/tests/ImageSharp.Tests/Color/ColorTests.CastTo.cs +++ b/tests/ImageSharp.Tests/Color/ColorTests.CastTo.cs @@ -64,6 +64,19 @@ namespace SixLabors.ImageSharp.Tests Assert.Equal(source, data); } + [Fact] + public void Abgr32() + { + var source = new Abgr32(1, 22, 33, 231); + + // Act: + var color = new Color(source); + + // Assert: + Abgr32 data = color; + Assert.Equal(source, data); + } + [Fact] public void Rgb24() { diff --git a/tests/ImageSharp.Tests/Color/ColorTests.ConstructFrom.cs b/tests/ImageSharp.Tests/Color/ColorTests.ConstructFrom.cs index 89276014b..d61361c80 100644 --- a/tests/ImageSharp.Tests/Color/ColorTests.ConstructFrom.cs +++ b/tests/ImageSharp.Tests/Color/ColorTests.ConstructFrom.cs @@ -63,6 +63,19 @@ namespace SixLabors.ImageSharp.Tests Assert.Equal(source, data); } + [Fact] + public void Abgr32() + { + var source = new Abgr32(1, 22, 33, 231); + + // Act: + var color = new Color(source); + + // Assert: + Abgr32 data = color.ToPixel(); + Assert.Equal(source, data); + } + [Fact] public void Rgb24() { diff --git a/tests/ImageSharp.Tests/Common/NumericsTests.cs b/tests/ImageSharp.Tests/Common/NumericsTests.cs index 62819af49..a82997477 100644 --- a/tests/ImageSharp.Tests/Common/NumericsTests.cs +++ b/tests/ImageSharp.Tests/Common/NumericsTests.cs @@ -16,6 +16,8 @@ namespace SixLabors.ImageSharp.Tests.Common this.Output = output; } + public static TheoryData IsOutOfRangeTestData = new() { int.MinValue, -1, 0, 1, 6, 7, 8, 91, 92, 93, int.MaxValue }; + private static int Log2_ReferenceImplementation(uint value) { int n = 0; @@ -97,5 +99,20 @@ namespace SixLabors.ImageSharp.Tests.Common Assert.True(expected == actual, $"Expected: {expected}\nActual: {actual}\n{value} / {divisor} = {expected}"); } } + + private static bool IsOutOfRange_ReferenceImplementation(int value, int min, int max) => value < min || value > max; + + [Theory] + [MemberData(nameof(IsOutOfRangeTestData))] + public void IsOutOfRange(int value) + { + const int min = 7; + const int max = 92; + + bool expected = IsOutOfRange_ReferenceImplementation(value, min, max); + bool actual = Numerics.IsOutOfRange(value, min, max); + + Assert.True(expected == actual, $"IsOutOfRange({value}, {min}, {max})"); + } } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs index 85f30d28d..36570ce55 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs @@ -149,12 +149,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg // 4 paths: // 1. AllowAll - call avx/fma implementation // 2. DisableFMA - call avx without fma implementation - // 3. DisableAvx - call sse Vector4 implementation - // 4. DisableHWIntrinsic - call scalar fallback implementation + // 3. DisableAvx - call sse implementation + // 4. DisableSIMD - call Vector4 fallback implementation FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, seed, - HwIntrinsics.AllowAll | HwIntrinsics.DisableFMA | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); + HwIntrinsics.AllowAll | HwIntrinsics.DisableFMA | HwIntrinsics.DisableAVX | HwIntrinsics.DisableSIMD); } // Forward transform @@ -197,12 +197,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg // 4 paths: // 1. AllowAll - call avx/fma implementation // 2. DisableFMA - call avx without fma implementation - // 3. DisableAvx - call sse Vector4 implementation - // 4. DisableHWIntrinsic - call scalar fallback implementation + // 3. DisableAvx - call Vector4 implementation + // 4. DisableSIMD - call scalar fallback implementation FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, seed, - HwIntrinsics.AllowAll | HwIntrinsics.DisableFMA | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); + HwIntrinsics.AllowAll | HwIntrinsics.DisableFMA | HwIntrinsics.DisableAVX | HwIntrinsics.DisableSIMD); } } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs index ef817154d..db5169a04 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs @@ -10,61 +10,72 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg public partial class JpegDecoderTests { public static string[] BaselineTestJpegs = - { - TestImages.Jpeg.Baseline.Calliphora, - TestImages.Jpeg.Baseline.Cmyk, - TestImages.Jpeg.Baseline.Ycck, - TestImages.Jpeg.Baseline.Jpeg400, - TestImages.Jpeg.Baseline.Turtle420, - TestImages.Jpeg.Baseline.Testorig420, - TestImages.Jpeg.Baseline.Jpeg420Small, - TestImages.Jpeg.Issues.Fuzz.AccessViolationException922, - TestImages.Jpeg.Baseline.Jpeg444, - TestImages.Jpeg.Baseline.Jpeg422, - TestImages.Jpeg.Baseline.Bad.BadEOF, - TestImages.Jpeg.Baseline.MultiScanBaselineCMYK, - TestImages.Jpeg.Baseline.YcckSubsample1222, - TestImages.Jpeg.Baseline.Bad.BadRST, - TestImages.Jpeg.Issues.MultiHuffmanBaseline394, - TestImages.Jpeg.Issues.ExifDecodeOutOfRange694, - TestImages.Jpeg.Issues.InvalidEOI695, - TestImages.Jpeg.Issues.ExifResizeOutOfRange696, - TestImages.Jpeg.Issues.InvalidAPP0721, - TestImages.Jpeg.Issues.ExifGetString750Load, - TestImages.Jpeg.Issues.ExifGetString750Transform, - TestImages.Jpeg.Issues.BadSubSampling1076, - - // LibJpeg can open this despite the invalid density units. - TestImages.Jpeg.Issues.Fuzz.ArgumentOutOfRangeException825B, + { + TestImages.Jpeg.Baseline.Calliphora, + TestImages.Jpeg.Baseline.Cmyk, + TestImages.Jpeg.Baseline.Ycck, + TestImages.Jpeg.Baseline.Jpeg400, + TestImages.Jpeg.Baseline.Turtle420, + TestImages.Jpeg.Baseline.Testorig420, + TestImages.Jpeg.Baseline.Jpeg420Small, + TestImages.Jpeg.Issues.Fuzz.AccessViolationException922, + TestImages.Jpeg.Baseline.Jpeg444, + TestImages.Jpeg.Baseline.Jpeg422, + TestImages.Jpeg.Baseline.Bad.BadEOF, + TestImages.Jpeg.Baseline.MultiScanBaselineCMYK, + TestImages.Jpeg.Baseline.YcckSubsample1222, + TestImages.Jpeg.Baseline.Bad.BadRST, + TestImages.Jpeg.Issues.MultiHuffmanBaseline394, + TestImages.Jpeg.Issues.ExifDecodeOutOfRange694, + TestImages.Jpeg.Issues.InvalidEOI695, + TestImages.Jpeg.Issues.ExifResizeOutOfRange696, + TestImages.Jpeg.Issues.InvalidAPP0721, + TestImages.Jpeg.Issues.ExifGetString750Load, + TestImages.Jpeg.Issues.ExifGetString750Transform, + TestImages.Jpeg.Issues.BadSubSampling1076, - // LibJpeg can open this despite incorrect colorspace metadata. - TestImages.Jpeg.Issues.IncorrectColorspace855, + // LibJpeg can open this despite the invalid density units. + TestImages.Jpeg.Issues.Fuzz.ArgumentOutOfRangeException825B, - // LibJpeg can open this despite the invalid subsampling units. - TestImages.Jpeg.Issues.Fuzz.IndexOutOfRangeException824C, + // LibJpeg can open this despite incorrect colorspace metadata. + TestImages.Jpeg.Issues.IncorrectColorspace855, - // High depth images - TestImages.Jpeg.Baseline.Testorig12bit, - }; + // High depth images + TestImages.Jpeg.Baseline.Testorig12bit, + }; public static string[] ProgressiveTestJpegs = - { - TestImages.Jpeg.Progressive.Fb, - TestImages.Jpeg.Progressive.Progress, - TestImages.Jpeg.Progressive.Festzug, - TestImages.Jpeg.Progressive.Bad.BadEOF, - TestImages.Jpeg.Issues.BadCoeffsProgressive178, - TestImages.Jpeg.Issues.MissingFF00ProgressiveGirl159, - TestImages.Jpeg.Issues.MissingFF00ProgressiveBedroom159, - TestImages.Jpeg.Issues.BadZigZagProgressive385, - TestImages.Jpeg.Progressive.Bad.ExifUndefType, - TestImages.Jpeg.Issues.NoEoiProgressive517, - TestImages.Jpeg.Issues.BadRstProgressive518, - TestImages.Jpeg.Issues.DhtHasWrongLength624, - TestImages.Jpeg.Issues.OrderedInterleavedProgressive723A, - TestImages.Jpeg.Issues.OrderedInterleavedProgressive723B, - TestImages.Jpeg.Issues.OrderedInterleavedProgressive723C - }; + { + TestImages.Jpeg.Progressive.Fb, + TestImages.Jpeg.Progressive.Progress, + TestImages.Jpeg.Progressive.Festzug, + TestImages.Jpeg.Progressive.Bad.BadEOF, + TestImages.Jpeg.Issues.BadCoeffsProgressive178, + TestImages.Jpeg.Issues.MissingFF00ProgressiveGirl159, + TestImages.Jpeg.Issues.MissingFF00ProgressiveBedroom159, + TestImages.Jpeg.Issues.BadZigZagProgressive385, + TestImages.Jpeg.Progressive.Bad.ExifUndefType, + TestImages.Jpeg.Issues.NoEoiProgressive517, + TestImages.Jpeg.Issues.BadRstProgressive518, + TestImages.Jpeg.Issues.DhtHasWrongLength624, + TestImages.Jpeg.Issues.OrderedInterleavedProgressive723A, + TestImages.Jpeg.Issues.OrderedInterleavedProgressive723B, + TestImages.Jpeg.Issues.OrderedInterleavedProgressive723C + }; + + public static string[] UnsupportedTestJpegs = + { + // Invalid componentCount value (2 or > 4) + TestImages.Jpeg.Issues.Fuzz.NullReferenceException823, + TestImages.Jpeg.Issues.MalformedUnsupportedComponentCount, + + // Arithmetic coding + TestImages.Jpeg.Baseline.ArithmeticCoding, + TestImages.Jpeg.Baseline.ArithmeticCodingProgressive, + + // Lossless jpeg + TestImages.Jpeg.Baseline.Lossless + }; public static string[] UnrecoverableTestJpegs = { @@ -73,7 +84,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg TestImages.Jpeg.Issues.Fuzz.AccessViolationException798, TestImages.Jpeg.Issues.Fuzz.DivideByZeroException821, TestImages.Jpeg.Issues.Fuzz.DivideByZeroException822, - TestImages.Jpeg.Issues.Fuzz.NullReferenceException823, TestImages.Jpeg.Issues.Fuzz.IndexOutOfRangeException824A, TestImages.Jpeg.Issues.Fuzz.IndexOutOfRangeException824B, TestImages.Jpeg.Issues.Fuzz.IndexOutOfRangeException824D, @@ -90,31 +100,31 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg TestImages.Jpeg.Issues.Fuzz.AccessViolationException827, TestImages.Jpeg.Issues.Fuzz.ExecutionEngineException839, TestImages.Jpeg.Issues.Fuzz.IndexOutOfRangeException1693A, - TestImages.Jpeg.Issues.Fuzz.IndexOutOfRangeException1693B + TestImages.Jpeg.Issues.Fuzz.IndexOutOfRangeException1693B, + TestImages.Jpeg.Issues.Fuzz.IndexOutOfRangeException824C, }; - private static readonly Dictionary CustomToleranceValues = - new Dictionary - { - // Baseline: - [TestImages.Jpeg.Baseline.Calliphora] = 0.00002f / 100, - [TestImages.Jpeg.Baseline.Bad.BadEOF] = 0.38f / 100, - [TestImages.Jpeg.Baseline.Bad.BadRST] = 0.0589f / 100, + private static readonly Dictionary CustomToleranceValues = new() + { + // Baseline: + [TestImages.Jpeg.Baseline.Calliphora] = 0.00002f / 100, + [TestImages.Jpeg.Baseline.Bad.BadEOF] = 0.38f / 100, + [TestImages.Jpeg.Baseline.Bad.BadRST] = 0.0589f / 100, - [TestImages.Jpeg.Baseline.Jpeg422] = 0.0013f / 100, - [TestImages.Jpeg.Baseline.Testorig420] = 0.38f / 100, - [TestImages.Jpeg.Baseline.Jpeg420Small] = 0.287f / 100, - [TestImages.Jpeg.Baseline.Turtle420] = 1.0f / 100, + [TestImages.Jpeg.Baseline.Jpeg422] = 0.0013f / 100, + [TestImages.Jpeg.Baseline.Testorig420] = 0.38f / 100, + [TestImages.Jpeg.Baseline.Jpeg420Small] = 0.287f / 100, + [TestImages.Jpeg.Baseline.Turtle420] = 1.0f / 100, - // Progressive: - [TestImages.Jpeg.Issues.MissingFF00ProgressiveGirl159] = 0.34f / 100, - [TestImages.Jpeg.Issues.BadCoeffsProgressive178] = 0.38f / 100, - [TestImages.Jpeg.Progressive.Bad.BadEOF] = 0.3f / 100, - [TestImages.Jpeg.Progressive.Festzug] = 0.02f / 100, - [TestImages.Jpeg.Progressive.Fb] = 0.16f / 100, - [TestImages.Jpeg.Progressive.Progress] = 0.31f / 100, - [TestImages.Jpeg.Issues.BadZigZagProgressive385] = 0.23f / 100, - [TestImages.Jpeg.Progressive.Bad.ExifUndefType] = 0.011f / 100, - }; + // Progressive: + [TestImages.Jpeg.Issues.MissingFF00ProgressiveGirl159] = 0.34f / 100, + [TestImages.Jpeg.Issues.BadCoeffsProgressive178] = 0.38f / 100, + [TestImages.Jpeg.Progressive.Bad.BadEOF] = 0.3f / 100, + [TestImages.Jpeg.Progressive.Festzug] = 0.02f / 100, + [TestImages.Jpeg.Progressive.Fb] = 0.16f / 100, + [TestImages.Jpeg.Progressive.Progress] = 0.31f / 100, + [TestImages.Jpeg.Issues.BadZigZagProgressive385] = 0.23f / 100, + [TestImages.Jpeg.Progressive.Bad.ExifUndefType] = 0.011f / 100, + }; } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index 08d8d9038..7a2446959 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -190,9 +190,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg } [Theory] - [WithFile(TestImages.Jpeg.Baseline.ArithmeticCoding, PixelTypes.Rgba32)] - [WithFile(TestImages.Jpeg.Baseline.ArithmeticCodingProgressive, PixelTypes.Rgba32)] - [WithFile(TestImages.Jpeg.Baseline.Lossless, PixelTypes.Rgba32)] + [WithFileCollection(nameof(UnsupportedTestJpegs), PixelTypes.Rgba32)] public void ThrowsNotSupported_WithUnsupportedJpegs(TestImageProvider provider) where TPixel : unmanaged, IPixel { diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs index d63dad8d4..c29f8c589 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs @@ -255,7 +255,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png public void Decode_MissingDataChunk_ThrowsException(TestImageProvider provider) where TPixel : unmanaged, IPixel { - System.Exception ex = Record.Exception( + Exception ex = Record.Exception( () => { using Image image = provider.GetImage(PngDecoder); @@ -271,7 +271,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png public void Decode_InvalidBitDepth_ThrowsException(TestImageProvider provider) where TPixel : unmanaged, IPixel { - System.Exception ex = Record.Exception( + Exception ex = Record.Exception( () => { using Image image = provider.GetImage(PngDecoder); @@ -287,7 +287,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png public void Decode_InvalidColorType_ThrowsException(TestImageProvider provider) where TPixel : unmanaged, IPixel { - System.Exception ex = Record.Exception( + Exception ex = Record.Exception( () => { using Image image = provider.GetImage(PngDecoder); @@ -297,13 +297,27 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png Assert.Contains("Invalid or unsupported color type", ex.Message); } + [Theory] + [WithFile(TestImages.Png.Bad.WrongCrcDataChunk, PixelTypes.Rgba32)] + public void Decode_InvalidDataChunkCrc_ThrowsException(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + InvalidImageContentException ex = Assert.Throws( + () => + { + using Image image = provider.GetImage(PngDecoder); + }); + Assert.NotNull(ex); + Assert.Contains("CRC Error. PNG IDAT chunk is corrupt!", ex.Message); + } + // https://github.com/SixLabors/ImageSharp/issues/1014 [Theory] [WithFileCollection(nameof(TestImagesIssue1014), PixelTypes.Rgba32)] public void Issue1014_DataSplitOverMultipleIDatChunks(TestImageProvider provider) where TPixel : unmanaged, IPixel { - System.Exception ex = Record.Exception( + Exception ex = Record.Exception( () => { using Image image = provider.GetImage(PngDecoder); @@ -319,7 +333,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png public void Issue1177_CRC_Omitted(TestImageProvider provider) where TPixel : unmanaged, IPixel { - System.Exception ex = Record.Exception( + Exception ex = Record.Exception( () => { using Image image = provider.GetImage(PngDecoder); @@ -335,7 +349,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png public void Issue1127(TestImageProvider provider) where TPixel : unmanaged, IPixel { - System.Exception ex = Record.Exception( + Exception ex = Record.Exception( () => { using Image image = provider.GetImage(PngDecoder); @@ -351,7 +365,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png public void Issue1047(TestImageProvider provider) where TPixel : unmanaged, IPixel { - System.Exception ex = Record.Exception( + Exception ex = Record.Exception( () => { using Image image = provider.GetImage(PngDecoder); @@ -372,7 +386,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png public void Issue1765(TestImageProvider provider) where TPixel : unmanaged, IPixel { - System.Exception ex = Record.Exception( + Exception ex = Record.Exception( () => { using Image image = provider.GetImage(PngDecoder); @@ -388,7 +402,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png public void Issue410_MalformedApplePng(TestImageProvider provider) where TPixel : unmanaged, IPixel { - System.Exception ex = Record.Exception( + Exception ex = Record.Exception( () => { using Image image = provider.GetImage(PngDecoder); diff --git a/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs index b4307af5d..8db1d1aaf 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -289,5 +290,34 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png Assert.Contains(meta.TextData, m => m.Keyword is "NoLang" && m.Value is "this text chunk is missing a language tag"); Assert.Contains(meta.TextData, m => m.Keyword is "NoTranslatedKeyword" && m.Value is "dieser chunk hat kein übersetztes Schlüßelwort"); } + + [Theory] + [InlineData(TestImages.Png.Issue1875)] + public void Identify_ReadsLegacyExifData(string imagePath) + { + var testFile = TestFile.Create(imagePath); + using (var stream = new MemoryStream(testFile.Bytes, false)) + { + IImageInfo imageInfo = Image.Identify(stream); + Assert.NotNull(imageInfo); + Assert.NotNull(imageInfo.Metadata.ExifProfile); + + PngMetadata meta = imageInfo.Metadata.GetFormatMetadata(PngFormat.Instance); + Assert.DoesNotContain(meta.TextData, t => t.Keyword.Equals("Raw profile type exif", StringComparison.OrdinalIgnoreCase)); + + ExifProfile exif = imageInfo.Metadata.ExifProfile; + Assert.Equal(0, exif.InvalidTags.Count); + Assert.Equal(3, exif.Values.Count); + + Assert.Equal( + "A colorful tiling of blue, red, yellow, and green 4x4 pixel blocks.", + exif.GetValue(ExifTag.ImageDescription).Value); + Assert.Equal( + "Duplicated from basn3p02.png, then image metadata modified with exiv2", + exif.GetValue(ExifTag.ImageHistory).Value); + + Assert.Equal(42, (int)exif.GetValue(ExifTag.ImageNumber).Value); + } + } } } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs new file mode 100644 index 000000000..7a02c91b8 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffDecoderTests.cs @@ -0,0 +1,130 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +// ReSharper disable InconsistentNaming +using System; +using System.IO; +using System.Linq; +using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Metadata; +using SixLabors.ImageSharp.Metadata.Profiles.Exif; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; +using Xunit; +using static SixLabors.ImageSharp.Tests.TestImages.BigTiff; + +namespace SixLabors.ImageSharp.Tests.Formats.Tiff +{ + [Collection("RunSerial")] + [Trait("Format", "Tiff")] + public class BigTiffDecoderTests : TiffDecoderBaseTester + { + [Theory] + [WithFile(BigTIFF, PixelTypes.Rgba32)] + [WithFile(BigTIFFLong, PixelTypes.Rgba32)] + [WithFile(BigTIFFLong8, PixelTypes.Rgba32)] + [WithFile(BigTIFFMotorola, PixelTypes.Rgba32)] + [WithFile(BigTIFFMotorolaLongStrips, PixelTypes.Rgba32)] + [WithFile(BigTIFFSubIFD4, PixelTypes.Rgba32)] + [WithFile(BigTIFFSubIFD8, PixelTypes.Rgba32)] + [WithFile(Indexed4_Deflate, PixelTypes.Rgba32)] + [WithFile(Indexed8_LZW, PixelTypes.Rgba32)] + [WithFile(MinIsBlack, PixelTypes.Rgba32)] + [WithFile(MinIsWhite, PixelTypes.Rgba32)] + public void TiffDecoder_CanDecode(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffDecoder(provider); + + [Theory] + [WithFile(BigTIFFLong8Tiles, PixelTypes.Rgba32)] + public void ThrowsNotSupported(TestImageProvider provider) + where TPixel : unmanaged, IPixel => Assert.Throws(() => provider.GetImage(TiffDecoder)); + + [Theory] + [WithFile(Damaged_MinIsWhite_RLE, PixelTypes.Rgba32)] + [WithFile(Damaged_MinIsBlack_RLE, PixelTypes.Rgba32)] + public void DamagedFiles(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + Assert.Throws(() => TestTiffDecoder(provider)); + + using Image image = provider.GetImage(TiffDecoder); + ExifProfile exif = image.Frames.RootFrame.Metadata.ExifProfile; + + // PhotometricInterpretation is required tag: https://www.awaresystems.be/imaging/tiff/tifftags/photometricinterpretation.html + Assert.Null(exif.GetValueInternal(ExifTag.PhotometricInterpretation)); + } + + [Theory] + [InlineData(BigTIFF, 24, 64, 64, 96, 96, PixelResolutionUnit.PixelsPerInch)] + [InlineData(BigTIFFLong, 24, 64, 64, 96, 96, PixelResolutionUnit.PixelsPerInch)] + [InlineData(BigTIFFLong8, 24, 64, 64, 96, 96, PixelResolutionUnit.PixelsPerInch)] + [InlineData(BigTIFFMotorola, 24, 64, 64, 96, 96, PixelResolutionUnit.PixelsPerInch)] + [InlineData(BigTIFFMotorolaLongStrips, 24, 64, 64, 96, 96, PixelResolutionUnit.PixelsPerInch)] + [InlineData(BigTIFFSubIFD4, 24, 64, 64, 96, 96, PixelResolutionUnit.PixelsPerInch)] + [InlineData(BigTIFFSubIFD8, 24, 64, 64, 96, 96, PixelResolutionUnit.PixelsPerInch)] + [InlineData(Indexed4_Deflate, 4, 64, 64, 96, 96, PixelResolutionUnit.PixelsPerInch)] + [InlineData(Indexed8_LZW, 8, 64, 64, 96, 96, PixelResolutionUnit.PixelsPerInch)] + [InlineData(MinIsWhite, 1, 32, 32, 96, 96, PixelResolutionUnit.PixelsPerInch)] + [InlineData(MinIsBlack, 1, 32, 32, 96, 96, PixelResolutionUnit.PixelsPerInch)] + public void Identify(string imagePath, int expectedPixelSize, int expectedWidth, int expectedHeight, double expectedHResolution, double expectedVResolution, PixelResolutionUnit expectedResolutionUnit) + { + var testFile = TestFile.Create(imagePath); + using (var stream = new MemoryStream(testFile.Bytes, false)) + { + IImageInfo info = Image.Identify(stream); + + Assert.Equal(expectedPixelSize, info.PixelType?.BitsPerPixel); + Assert.Equal(expectedWidth, info.Width); + Assert.Equal(expectedHeight, info.Height); + Assert.NotNull(info.Metadata); + Assert.Equal(expectedHResolution, info.Metadata.HorizontalResolution); + Assert.Equal(expectedVResolution, info.Metadata.VerticalResolution); + Assert.Equal(expectedResolutionUnit, info.Metadata.ResolutionUnits); + + TiffMetadata tiffmeta = info.Metadata.GetTiffMetadata(); + Assert.NotNull(tiffmeta); + Assert.Equal(TiffFormatType.BigTIFF, tiffmeta.FormatType); + } + } + + [Theory] + [InlineData(BigTIFFLong, ImageSharp.ByteOrder.LittleEndian)] + [InlineData(BigTIFFMotorola, ImageSharp.ByteOrder.BigEndian)] + public void ByteOrder(string imagePath, ByteOrder expectedByteOrder) + { + var testFile = TestFile.Create(imagePath); + using (var stream = new MemoryStream(testFile.Bytes, false)) + { + IImageInfo info = Image.Identify(stream); + + Assert.NotNull(info.Metadata); + Assert.Equal(expectedByteOrder, info.Metadata.GetTiffMetadata().ByteOrder); + + stream.Seek(0, SeekOrigin.Begin); + + using var img = Image.Load(stream); + Assert.Equal(expectedByteOrder, img.Metadata.GetTiffMetadata().ByteOrder); + } + } + + [Theory] + [WithFile(BigTIFFSubIFD8, PixelTypes.Rgba32)] + public void TiffDecoder_SubIfd8(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(TiffDecoder); + + ExifProfile meta = image.Frames.RootFrame.Metadata.ExifProfile; + + Assert.Equal(0, meta.InvalidTags.Count); + Assert.Equal(6, meta.Values.Count); + Assert.Equal(64, (int)meta.GetValue(ExifTag.ImageWidth).Value); + Assert.Equal(64, (int)meta.GetValue(ExifTag.ImageLength).Value); + Assert.Equal(64, (int)meta.GetValue(ExifTag.RowsPerStrip).Value); + + Assert.Equal(1, meta.Values.Count(v => (ushort)v.Tag == (ushort)ExifTagValue.ImageWidth)); + Assert.Equal(1, meta.Values.Count(v => (ushort)v.Tag == (ushort)ExifTagValue.StripOffsets)); + Assert.Equal(1, meta.Values.Count(v => (ushort)v.Tag == (ushort)ExifTagValue.StripByteCounts)); + } + } +} diff --git a/tests/ImageSharp.Tests/Formats/Tiff/BigTiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffMetadataTests.cs new file mode 100644 index 000000000..9f5b78cc3 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffMetadataTests.cs @@ -0,0 +1,260 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers.Binary; +using System.Collections.Generic; +using System.IO; +using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Formats.Tiff.Writers; +using SixLabors.ImageSharp.Metadata; +using SixLabors.ImageSharp.Metadata.Profiles.Exif; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Formats.Tiff +{ + [Trait("Format", "Tiff")] + public class BigTiffMetadataTests + { + [Fact] + public void ExifLong8() + { + var long8 = new ExifLong8(ExifTagValue.StripByteCounts); + + Assert.True(long8.TrySetValue(0)); + Assert.Equal(0UL, long8.GetValue()); + + Assert.True(long8.TrySetValue(100u)); + Assert.Equal(100UL, long8.GetValue()); + + Assert.True(long8.TrySetValue(ulong.MaxValue)); + Assert.Equal(ulong.MaxValue, long8.GetValue()); + + Assert.False(long8.TrySetValue(-65)); + Assert.Equal(ulong.MaxValue, long8.GetValue()); + } + + [Fact] + public void ExifSignedLong8() + { + var long8 = new ExifSignedLong8(ExifTagValue.ImageID); + + Assert.False(long8.TrySetValue(0)); + + Assert.True(long8.TrySetValue(0L)); + Assert.Equal(0L, long8.GetValue()); + + Assert.True(long8.TrySetValue(-100L)); + Assert.Equal(-100L, long8.GetValue()); + Assert.Equal(ExifDataType.SignedLong8, long8.DataType); + + Assert.True(long8.TrySetValue(long.MaxValue)); + Assert.Equal(long.MaxValue, long8.GetValue()); + Assert.Equal(ExifDataType.SignedLong8, long8.DataType); + } + + [Fact] + public void ExifLong8Array() + { + var long8 = new ExifLong8Array(ExifTagValue.StripOffsets); + + Assert.True(long8.TrySetValue((short)-123)); + Assert.Equal(new[] { 0UL }, long8.GetValue()); + + Assert.True(long8.TrySetValue((ushort)123)); + Assert.Equal(new[] { 123UL }, long8.GetValue()); + + Assert.True(long8.TrySetValue((short)123)); + Assert.Equal(new[] { 123UL }, long8.GetValue()); + + Assert.True(long8.TrySetValue(123)); + Assert.Equal(new[] { 123UL }, long8.GetValue()); + + Assert.True(long8.TrySetValue(123u)); + Assert.Equal(new[] { 123UL }, long8.GetValue()); + + Assert.True(long8.TrySetValue(123L)); + Assert.Equal(new[] { 123UL }, long8.GetValue()); + + Assert.True(long8.TrySetValue(123UL)); + Assert.Equal(new[] { 123UL }, long8.GetValue()); + + Assert.True(long8.TrySetValue(new short[] { -1, 2, -3, 4 })); + Assert.Equal(new ulong[] { 0, 2UL, 0, 4UL }, long8.GetValue()); + + Assert.True(long8.TrySetValue(new[] { 1, 2, 3, 4 })); + Assert.Equal(new[] { 1UL, 2UL, 3UL, 4UL }, long8.GetValue()); + Assert.Equal(ExifDataType.Long, long8.DataType); + + Assert.True(long8.TrySetValue(new[] { 1, 2, 3, 4, long.MaxValue })); + Assert.Equal(new[] { 1UL, 2UL, 3UL, 4UL, (ulong)long.MaxValue }, long8.GetValue()); + Assert.Equal(ExifDataType.Long8, long8.DataType); + } + + [Fact] + public void ExifSignedLong8Array() + { + var long8 = new ExifSignedLong8Array(ExifTagValue.StripOffsets); + + Assert.True(long8.TrySetValue(new[] { 0L })); + Assert.Equal(new[] { 0L }, long8.GetValue()); + Assert.Equal(ExifDataType.SignedLong8, long8.DataType); + + Assert.True(long8.TrySetValue(new[] { -1L, 2L, long.MinValue, 4L })); + Assert.Equal(new[] { -1L, 2L, long.MinValue, 4L }, long8.GetValue()); + Assert.Equal(ExifDataType.SignedLong8, long8.DataType); + } + + [Fact] + public void NotCoveredTags() + { + using var input = new Image(10, 10); + + var testTags = new Dictionary + { + { new ExifTag((ExifTagValue)0xdd01), (ExifDataType.SingleFloat, new float[] { 1.2f, 2.3f, 4.5f }) }, + { new ExifTag((ExifTagValue)0xdd02), (ExifDataType.SingleFloat, 2.345f) }, + { new ExifTag((ExifTagValue)0xdd03), (ExifDataType.DoubleFloat, new double[] { 4.5, 6.7 }) }, + { new ExifTag((ExifTagValue)0xdd04), (ExifDataType.DoubleFloat, 8.903) }, + { new ExifTag((ExifTagValue)0xdd05), (ExifDataType.SignedByte, (sbyte)-3) }, + { new ExifTag((ExifTagValue)0xdd06), (ExifDataType.SignedByte, new sbyte[] { -3, 0, 5 }) }, + { new ExifTag((ExifTagValue)0xdd07), (ExifDataType.SignedLong, new int[] { int.MinValue, 1, int.MaxValue }) }, + { new ExifTag((ExifTagValue)0xdd08), (ExifDataType.Long, new uint[] { 0, 1, uint.MaxValue }) }, + { new ExifTag((ExifTagValue)0xdd09), (ExifDataType.SignedShort, (short)-1234) }, + { new ExifTag((ExifTagValue)0xdd10), (ExifDataType.Short, (ushort)1234) }, + }; + + // arrange + var values = new List(); + foreach (KeyValuePair tag in testTags) + { + ExifValue newExifValue = ExifValues.Create((ExifTagValue)(ushort)tag.Key, tag.Value.DataType, tag.Value.Value is Array); + + Assert.True(newExifValue.TrySetValue(tag.Value.Value)); + values.Add(newExifValue); + } + + input.Frames.RootFrame.Metadata.ExifProfile = new ExifProfile(values, Array.Empty()); + + // act + var encoder = new TiffEncoder(); + using var memStream = new MemoryStream(); + input.Save(memStream, encoder); + + // assert + memStream.Position = 0; + using var output = Image.Load(memStream); + ImageFrameMetadata loadedFrameMetadata = output.Frames.RootFrame.Metadata; + foreach (KeyValuePair tag in testTags) + { + IExifValue exifValue = loadedFrameMetadata.ExifProfile.GetValueInternal(tag.Key); + Assert.NotNull(exifValue); + object value = exifValue.GetValue(); + + Assert.Equal(tag.Value.DataType, exifValue.DataType); + { + Assert.Equal(value, tag.Value.Value); + } + } + } + + [Fact] + public void NotCoveredTags64bit() + { + var testTags = new Dictionary + { + { new ExifTag((ExifTagValue)0xdd11), (ExifDataType.Long8, ulong.MaxValue) }, + { new ExifTag((ExifTagValue)0xdd12), (ExifDataType.SignedLong8, long.MaxValue) }, + //// WriteIfdTags64Bit: arrays aren't support (by our code) + ////{ new ExifTag((ExifTagValue)0xdd13), (ExifDataType.Long8, new ulong[] { 0, 1234, 56789UL, ulong.MaxValue }) }, + ////{ new ExifTag((ExifTagValue)0xdd14), (ExifDataType.SignedLong8, new long[] { -1234, 56789L, long.MaxValue }) }, + }; + + var values = new List(); + foreach (KeyValuePair tag in testTags) + { + ExifValue newExifValue = ExifValues.Create((ExifTagValue)(ushort)tag.Key, tag.Value.DataType, tag.Value.Value is Array); + + Assert.True(newExifValue.TrySetValue(tag.Value.Value)); + values.Add(newExifValue); + } + + // act + byte[] inputBytes = WriteIfdTags64Bit(values); + Configuration config = Configuration.Default; + var reader = new EntryReader( + new MemoryStream(inputBytes), + BitConverter.IsLittleEndian ? ByteOrder.LittleEndian : ByteOrder.BigEndian, + config.MemoryAllocator); + + reader.ReadTags(true, 0); + + List outputTags = reader.Values; + + // assert + foreach (KeyValuePair tag in testTags) + { + IExifValue exifValue = outputTags.Find(t => t.Tag == tag.Key); + Assert.NotNull(exifValue); + object value = exifValue.GetValue(); + + Assert.Equal(tag.Value.DataType, exifValue.DataType); + { + Assert.Equal(value, tag.Value.Value); + } + } + } + + private static byte[] WriteIfdTags64Bit(List values) + { + byte[] buffer = new byte[8]; + var ms = new MemoryStream(); + var writer = new TiffStreamWriter(ms); + WriteLong8(writer, buffer, (ulong)values.Count); + + foreach (IExifValue entry in values) + { + writer.Write((ushort)entry.Tag); + writer.Write((ushort)entry.DataType); + WriteLong8(writer, buffer, ExifWriter.GetNumberOfComponents(entry)); + + uint length = ExifWriter.GetLength(entry); + + Assert.True(length <= 8); + + if (length <= 8) + { + int sz = ExifWriter.WriteValue(entry, buffer, 0); + DebugGuard.IsTrue(sz == length, "Incorrect number of bytes written"); + + // write padded + writer.BaseStream.Write(buffer.AsSpan(0, sz)); + int d = sz % 8; + if (d != 0) + { + writer.BaseStream.Write(new byte[d]); + } + } + } + + WriteLong8(writer, buffer, 0); + + return ms.ToArray(); + } + + private static void WriteLong8(TiffStreamWriter writer, byte[] buffer, ulong value) + { + if (writer.IsLittleEndian) + { + BinaryPrimitives.WriteUInt64LittleEndian(buffer, value); + } + else + { + BinaryPrimitives.WriteUInt64BigEndian(buffer, value); + } + + writer.BaseStream.Write(buffer); + } + } +} diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderBaseTester.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderBaseTester.cs new file mode 100644 index 000000000..aeadae255 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderBaseTester.cs @@ -0,0 +1,30 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +// ReSharper disable InconsistentNaming +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; +using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; + +namespace SixLabors.ImageSharp.Tests.Formats.Tiff +{ + public abstract class TiffDecoderBaseTester + { + protected static TiffDecoder TiffDecoder => new TiffDecoder(); + + protected static MagickReferenceDecoder ReferenceDecoder => new MagickReferenceDecoder(); + + protected static void TestTiffDecoder(TestImageProvider provider, IImageDecoder referenceDecoder = null, bool useExactComparer = true, float compareTolerance = 0.001f) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(TiffDecoder); + image.DebugSave(provider); + image.CompareToOriginal( + provider, + useExactComparer ? ImageComparer.Exact : ImageComparer.Tolerant(compareTolerance), + referenceDecoder ?? ReferenceDecoder); + } + } +} diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index f8256ead9..ea0544acf 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -4,32 +4,25 @@ // ReSharper disable InconsistentNaming using System; using System.IO; -using SixLabors.ImageSharp.Formats; -using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; -using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; using Xunit; using static SixLabors.ImageSharp.Tests.TestImages.Tiff; namespace SixLabors.ImageSharp.Tests.Formats.Tiff { [Trait("Format", "Tiff")] - public class TiffDecoderTests + public class TiffDecoderTests : TiffDecoderBaseTester { public static readonly string[] MultiframeTestImages = Multiframes; - private static TiffDecoder TiffDecoder => new(); - - private static MagickReferenceDecoder ReferenceDecoder => new(); - [Theory] [WithFile(RgbUncompressedTiled, PixelTypes.Rgba32)] [WithFile(MultiframeDifferentSize, PixelTypes.Rgba32)] [WithFile(MultiframeDifferentVariants, PixelTypes.Rgba32)] public void ThrowsNotSupported(TestImageProvider provider) - where TPixel : unmanaged, IPixel => Assert.Throws(() => provider.GetImage(TiffDecoder)); + where TPixel : unmanaged, IPixel => Assert.Throws(() => provider.GetImage(TiffDecoder)); [Theory] [InlineData(RgbUncompressed, 24, 256, 256, 300, 300, PixelResolutionUnit.PixelsPerInch)] @@ -406,16 +399,5 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff image.DebugSaveMultiFrame(provider); image.CompareToOriginalMultiFrame(provider, ImageComparer.Exact, ReferenceDecoder); } - - private static void TestTiffDecoder(TestImageProvider provider, IImageDecoder referenceDecoder = null, bool useExactComparer = true, float compareTolerance = 0.001f) - where TPixel : unmanaged, IPixel - { - using Image image = provider.GetImage(TiffDecoder); - image.DebugSave(provider); - image.CompareToOriginal( - provider, - useExactComparer ? ImageComparer.Exact : ImageComparer.Tolerant(compareTolerance), - referenceDecoder ?? ReferenceDecoder); - } } } diff --git a/tests/ImageSharp.Tests/Formats/WebP/Vp8ResidualTests.cs b/tests/ImageSharp.Tests/Formats/WebP/Vp8ResidualTests.cs new file mode 100644 index 000000000..2bca632db --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/WebP/Vp8ResidualTests.cs @@ -0,0 +1,37 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Formats.Webp.Lossy; +using SixLabors.ImageSharp.Tests.TestUtilities; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Formats.WebP +{ + [Trait("Format", "Webp")] + public class Vp8ResidualTests + { + private static void RunSetCoeffsTest() + { + // arrange + var residual = new Vp8Residual(); + short[] coeffs = { 110, 0, -2, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0 }; + + // act + residual.SetCoeffs(coeffs); + + // assert + Assert.Equal(9, residual.Last); + } + + [Fact] + public void RunSetCoeffsTest_Works() => RunSetCoeffsTest(); + +#if SUPPORTS_RUNTIME_INTRINSICS + [Fact] + public void RunSetCoeffsTest_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunSetCoeffsTest, HwIntrinsics.AllowAll); + + [Fact] + public void RunSetCoeffsTest_WithoutHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunSetCoeffsTest, HwIntrinsics.DisableHWIntrinsic); +#endif + } +} diff --git a/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs index 607d4c764..cad39224e 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs @@ -5,6 +5,7 @@ using System.IO; using SixLabors.ImageSharp.Formats.Webp; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using Xunit; using static SixLabors.ImageSharp.Tests.TestImages.Webp; @@ -14,6 +15,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp [Trait("Format", "Webp")] public class WebpEncoderTests { + private static string TestImageLossyFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, Lossy.NoFilter06); + [Theory] [WithFile(Flag, PixelTypes.Rgba32, WebpFileFormatType.Lossless)] // if its not a webp input image, it should default to lossless. [WithFile(Lossless.NoTransform1, PixelTypes.Rgba32, WebpFileFormatType.Lossless)] @@ -288,6 +291,23 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp image.VerifyEncoder(provider, "webp", string.Empty, encoder, ImageComparer.Tolerant(0.04f)); } + public static void RunEncodeLossy_WithPeakImage() + { + var provider = TestImageProvider.File(TestImageLossyFullPath); + using Image image = provider.GetImage(); + + var encoder = new WebpEncoder() { FileFormat = WebpFileFormatType.Lossy }; + image.VerifyEncoder(provider, "webp", string.Empty, encoder, ImageComparer.Tolerant(0.04f)); + } + +#if SUPPORTS_RUNTIME_INTRINSICS + [Fact] + public void RunEncodeLossy_WithPeakImage_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunEncodeLossy_WithPeakImage, HwIntrinsics.AllowAll); + + [Fact] + public void RunEncodeLossy_WithPeakImage_WithoutHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunEncodeLossy_WithPeakImage, HwIntrinsics.DisableHWIntrinsic); +#endif + private static ImageComparer GetComparer(int quality) { float tolerance = 0.01f; // ~1.0% diff --git a/tests/ImageSharp.Tests/Image/ImageCloneTests.cs b/tests/ImageSharp.Tests/Image/ImageCloneTests.cs index f60264334..9d978ee52 100644 --- a/tests/ImageSharp.Tests/Image/ImageCloneTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageCloneTests.cs @@ -56,6 +56,33 @@ namespace SixLabors.ImageSharp.Tests }); } + [Theory] + [WithTestPatternImages(9, 9, PixelTypes.Rgba32)] + public void CloneAs_ToAbgr32(TestImageProvider provider) + { + using Image image = provider.GetImage(); + using Image clone = image.CloneAs(); + + image.ProcessPixelRows(clone, static (imageAccessor, cloneAccessor) => + { + for (int y = 0; y < imageAccessor.Height; y++) + { + Span row = imageAccessor.GetRowSpan(y); + Span rowClone = cloneAccessor.GetRowSpan(y); + + for (int x = 0; x < cloneAccessor.Width; x++) + { + Rgba32 expected = row[x]; + Abgr32 actual = rowClone[x]; + + Assert.Equal(expected.R, actual.R); + Assert.Equal(expected.G, actual.G); + Assert.Equal(expected.B, actual.B); + } + } + }); + } + [Theory] [WithTestPatternImages(9, 9, PixelTypes.Rgba32)] public void CloneAs_ToBgr24(TestImageProvider provider) diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Identify.cs b/tests/ImageSharp.Tests/Image/ImageTests.Identify.cs index 271aa30cf..93f73c3d3 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.Identify.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.Identify.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System.IO; +using System.IO.Compression; using System.Threading.Tasks; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Tests.TestUtilities; @@ -138,6 +139,32 @@ namespace SixLabors.ImageSharp.Tests Assert.Null(type); } + [Fact] + public void FromStream_ZeroLength_ReturnsNull() + { + // https://github.com/SixLabors/ImageSharp/issues/1903 + using var zipFile = new ZipArchive(new MemoryStream( + new byte[] + { + 0x50, 0x4B, 0x03, 0x04, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0xAF, + 0x94, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x6D, 0x79, 0x73, 0x74, 0x65, 0x72, + 0x79, 0x50, 0x4B, 0x01, 0x02, 0x3F, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x77, 0xAF, 0x94, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6D, + 0x79, 0x73, 0x74, 0x65, 0x72, 0x79, 0x0A, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x46, 0x82, 0xFF, 0x91, 0x27, 0xF6, + 0xD7, 0x01, 0x55, 0xA1, 0xF9, 0x91, 0x27, 0xF6, 0xD7, 0x01, 0x55, 0xA1, + 0xF9, 0x91, 0x27, 0xF6, 0xD7, 0x01, 0x50, 0x4B, 0x05, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x59, 0x00, 0x00, 0x00, 0x25, 0x00, + 0x00, 0x00, 0x00, 0x00 + })); + using Stream stream = zipFile.Entries[0].Open(); + IImageInfo info = Image.Identify(stream); + Assert.Null(info); + } + [Fact] public async Task FromStreamAsync_GlobalConfiguration_NoFormat() { @@ -188,6 +215,32 @@ namespace SixLabors.ImageSharp.Tests Assert.Equal(ExpectedGlobalFormat, res.Format); } + [Fact] + public async Task FromStreamAsync_ZeroLength_ReturnsNull() + { + // https://github.com/SixLabors/ImageSharp/issues/1903 + using var zipFile = new ZipArchive(new MemoryStream( + new byte[] + { + 0x50, 0x4B, 0x03, 0x04, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0xAF, + 0x94, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x6D, 0x79, 0x73, 0x74, 0x65, 0x72, + 0x79, 0x50, 0x4B, 0x01, 0x02, 0x3F, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x77, 0xAF, 0x94, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6D, + 0x79, 0x73, 0x74, 0x65, 0x72, 0x79, 0x0A, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x46, 0x82, 0xFF, 0x91, 0x27, 0xF6, + 0xD7, 0x01, 0x55, 0xA1, 0xF9, 0x91, 0x27, 0xF6, 0xD7, 0x01, 0x55, 0xA1, + 0xF9, 0x91, 0x27, 0xF6, 0xD7, 0x01, 0x50, 0x4B, 0x05, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x59, 0x00, 0x00, 0x00, 0x25, 0x00, + 0x00, 0x00, 0x00, 0x00 + })); + using Stream stream = zipFile.Entries[0].Open(); + IImageInfo info = await Image.IdentifyAsync(stream); + Assert.Null(info); + } + [Fact] public async Task FromPathAsync_CustomConfiguration() { diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index 471287006..28c778787 100644 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj @@ -47,6 +47,7 @@ + diff --git a/tests/ImageSharp.Tests/Memory/Buffer2DTests.SwapOrCopyContent.cs b/tests/ImageSharp.Tests/Memory/Buffer2DTests.SwapOrCopyContent.cs new file mode 100644 index 000000000..f58136f73 --- /dev/null +++ b/tests/ImageSharp.Tests/Memory/Buffer2DTests.SwapOrCopyContent.cs @@ -0,0 +1,160 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Linq; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Memory +{ + public partial class Buffer2DTests + { + public class SwapOrCopyContent + { + private readonly TestMemoryAllocator MemoryAllocator = new TestMemoryAllocator(); + + [Fact] + public void SwapOrCopyContent_WhenBothAllocated() + { + using (Buffer2D a = this.MemoryAllocator.Allocate2D(10, 5, AllocationOptions.Clean)) + using (Buffer2D b = this.MemoryAllocator.Allocate2D(3, 7, AllocationOptions.Clean)) + { + a[1, 3] = 666; + b[1, 3] = 444; + + Memory aa = a.FastMemoryGroup.Single(); + Memory bb = b.FastMemoryGroup.Single(); + + Buffer2D.SwapOrCopyContent(a, b); + + Assert.Equal(bb, a.FastMemoryGroup.Single()); + Assert.Equal(aa, b.FastMemoryGroup.Single()); + + Assert.Equal(new Size(3, 7), a.Size()); + Assert.Equal(new Size(10, 5), b.Size()); + + Assert.Equal(666, b[1, 3]); + Assert.Equal(444, a[1, 3]); + } + } + + [Fact] + public void SwapOrCopyContent_WhenDestinationIsOwned_ShouldNotSwapInDisposedSourceBuffer() + { + using var destData = MemoryGroup.Wrap(new int[100]); + using var dest = new Buffer2D(destData, 10, 10); + + using (Buffer2D source = this.MemoryAllocator.Allocate2D(10, 10, AllocationOptions.Clean)) + { + source[0, 0] = 1; + dest[0, 0] = 2; + + Buffer2D.SwapOrCopyContent(dest, source); + } + + int actual1 = dest.DangerousGetRowSpan(0)[0]; + int actual2 = dest.DangerousGetRowSpan(0)[0]; + int actual3 = dest.GetSafeRowMemory(0).Span[0]; + int actual5 = dest[0, 0]; + + Assert.Equal(1, actual1); + Assert.Equal(1, actual2); + Assert.Equal(1, actual3); + Assert.Equal(1, actual5); + } + + [Fact] + public void WhenBothAreMemoryOwners_ShouldSwap() + { + this.MemoryAllocator.BufferCapacityInBytes = sizeof(int) * 50; + using Buffer2D a = this.MemoryAllocator.Allocate2D(48, 2); + using Buffer2D b = this.MemoryAllocator.Allocate2D(50, 2); + + Memory a0 = a.FastMemoryGroup[0]; + Memory a1 = a.FastMemoryGroup[1]; + Memory b0 = b.FastMemoryGroup[0]; + Memory b1 = b.FastMemoryGroup[1]; + + bool swap = Buffer2D.SwapOrCopyContent(a, b); + Assert.True(swap); + + Assert.Equal(b0, a.FastMemoryGroup[0]); + Assert.Equal(b1, a.FastMemoryGroup[1]); + Assert.Equal(a0, b.FastMemoryGroup[0]); + Assert.Equal(a1, b.FastMemoryGroup[1]); + Assert.NotEqual(a.FastMemoryGroup[0], b.FastMemoryGroup[0]); + } + + [Fact] + public void WhenBothAreMemoryOwners_ShouldReplaceViews() + { + using Buffer2D a = this.MemoryAllocator.Allocate2D(100, 1); + using Buffer2D b = this.MemoryAllocator.Allocate2D(100, 2); + + a.FastMemoryGroup[0].Span[42] = 1; + b.FastMemoryGroup[0].Span[33] = 2; + MemoryGroupView aView0 = (MemoryGroupView)a.MemoryGroup; + MemoryGroupView bView0 = (MemoryGroupView)b.MemoryGroup; + + Buffer2D.SwapOrCopyContent(a, b); + Assert.False(aView0.IsValid); + Assert.False(bView0.IsValid); + Assert.ThrowsAny(() => _ = aView0[0].Span); + Assert.ThrowsAny(() => _ = bView0[0].Span); + + Assert.True(a.MemoryGroup.IsValid); + Assert.True(b.MemoryGroup.IsValid); + Assert.Equal(2, a.MemoryGroup[0].Span[33]); + Assert.Equal(1, b.MemoryGroup[0].Span[42]); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void WhenDestIsNotAllocated_SameSize_ShouldCopy(bool sourceIsAllocated) + { + var data = new Rgba32[21]; + var color = new Rgba32(1, 2, 3, 4); + + using var destOwner = new TestMemoryManager(data); + using var dest = new Buffer2D(MemoryGroup.Wrap(destOwner.Memory), 21, 1); + + using Buffer2D source = this.MemoryAllocator.Allocate2D(21, 1); + + source.FastMemoryGroup[0].Span[10] = color; + + // Act: + bool swap = Buffer2D.SwapOrCopyContent(dest, source); + + // Assert: + Assert.False(swap); + Assert.Equal(color, dest.MemoryGroup[0].Span[10]); + Assert.NotEqual(source.FastMemoryGroup[0], dest.FastMemoryGroup[0]); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void WhenDestIsNotMemoryOwner_DifferentSize_Throws(bool sourceIsOwner) + { + var data = new Rgba32[21]; + var color = new Rgba32(1, 2, 3, 4); + + using var destOwner = new TestMemoryManager(data); + using var dest = new Buffer2D(MemoryGroup.Wrap(destOwner.Memory), 21, 1); + + using Buffer2D source = this.MemoryAllocator.Allocate2D(22, 1); + + source.FastMemoryGroup[0].Span[10] = color; + + // Act: + Assert.ThrowsAny(() => Buffer2D.SwapOrCopyContent(dest, source)); + + Assert.Equal(color, source.MemoryGroup[0].Span[10]); + Assert.NotEqual(color, dest.MemoryGroup[0].Span[10]); + } + } + } +} diff --git a/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs b/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs index 73a0f4d60..486fbe464 100644 --- a/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs +++ b/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs @@ -4,7 +4,6 @@ using System; using System.Buffers; using System.Diagnostics; -using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Memory; @@ -14,7 +13,7 @@ using Xunit; // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests.Memory { - public class Buffer2DTests + public partial class Buffer2DTests { // ReSharper disable once ClassNeverInstantiated.Local private class Assert : Xunit.Assert @@ -121,7 +120,7 @@ namespace SixLabors.ImageSharp.Tests.Memory [InlineData(200, 100, 30, 1, 0)] [InlineData(200, 100, 30, 2, 1)] [InlineData(200, 100, 30, 4, 2)] - public unsafe void GetRowSpanY(int bufferCapacity, int width, int height, int y, int expectedBufferIndex) + public unsafe void DangerousGetRowSpan_TestAllocator(int bufferCapacity, int width, int height, int y, int expectedBufferIndex) { this.MemoryAllocator.BufferCapacityInBytes = sizeof(TestStructs.Foo) * bufferCapacity; @@ -136,6 +135,57 @@ namespace SixLabors.ImageSharp.Tests.Memory } } + [Theory] + [InlineData(100, 5)] // Within shared pool + [InlineData(77, 11)] // Within shared pool + [InlineData(100, 19)] // Single unmanaged pooled buffer + [InlineData(103, 17)] // Single unmanaged pooled buffer + [InlineData(100, 22)] // 2 unmanaged pooled buffers + [InlineData(100, 99)] // 9 unmanaged pooled buffers + [InlineData(100, 120)] // 2 unpooled buffers + public unsafe void DangerousGetRowSpan_UnmanagedAllocator(int width, int height) + { + const int sharedPoolThreshold = 1_000; + const int poolBufferSize = 2_000; + const int maxPoolSize = 10_000; + const int unpooledBufferSize = 8_000; + + int elementSize = sizeof(TestStructs.Foo); + var allocator = new UniformUnmanagedMemoryPoolMemoryAllocator( + sharedPoolThreshold * elementSize, + poolBufferSize * elementSize, + maxPoolSize * elementSize, + unpooledBufferSize * elementSize); + + using Buffer2D buffer = allocator.Allocate2D(width, height); + + var rnd = new Random(42); + + for (int y = 0; y < buffer.Height; y++) + { + Span span = buffer.DangerousGetRowSpan(y); + for (int x = 0; x < span.Length; x++) + { + ref TestStructs.Foo e = ref span[x]; + e.A = rnd.Next(); + e.B = rnd.NextDouble(); + } + } + + // Re-seed + rnd = new Random(42); + for (int y = 0; y < buffer.Height; y++) + { + Span span = buffer.GetSafeRowMemory(y).Span; + for (int x = 0; x < span.Length; x++) + { + ref TestStructs.Foo e = ref span[x]; + Assert.True(rnd.Next() == e.A, $"Mismatch @ y={y} x={x}"); + Assert.True(rnd.NextDouble() == e.B, $"Mismatch @ y={y} x={x}"); + } + } + } + [Theory] [InlineData(10, 0, 0, 0)] [InlineData(10, 0, 2, 0)] @@ -153,7 +203,7 @@ namespace SixLabors.ImageSharp.Tests.Memory using Buffer2D buffer = this.MemoryAllocator.Allocate2D(3, 5); bool expectSuccess = expectedBufferIndex >= 0; - bool success = buffer.TryGetPaddedRowSpan(y, padding, out Span paddedSpan); + bool success = buffer.DangerousTryGetPaddedRowSpan(y, padding, out Span paddedSpan); Xunit.Assert.Equal(expectSuccess, success); if (success) { @@ -229,56 +279,6 @@ namespace SixLabors.ImageSharp.Tests.Memory } } - [Fact] - public void SwapOrCopyContent_WhenBothAllocated() - { - using (Buffer2D a = this.MemoryAllocator.Allocate2D(10, 5, AllocationOptions.Clean)) - using (Buffer2D b = this.MemoryAllocator.Allocate2D(3, 7, AllocationOptions.Clean)) - { - a[1, 3] = 666; - b[1, 3] = 444; - - Memory aa = a.FastMemoryGroup.Single(); - Memory bb = b.FastMemoryGroup.Single(); - - Buffer2D.SwapOrCopyContent(a, b); - - Assert.Equal(bb, a.FastMemoryGroup.Single()); - Assert.Equal(aa, b.FastMemoryGroup.Single()); - - Assert.Equal(new Size(3, 7), a.Size()); - Assert.Equal(new Size(10, 5), b.Size()); - - Assert.Equal(666, b[1, 3]); - Assert.Equal(444, a[1, 3]); - } - } - - [Fact] - public void SwapOrCopyContent_WhenDestinationIsOwned_ShouldNotSwapInDisposedSourceBuffer() - { - using var destData = MemoryGroup.Wrap(new int[100]); - using var dest = new Buffer2D(destData, 10, 10); - - using (Buffer2D source = this.MemoryAllocator.Allocate2D(10, 10, AllocationOptions.Clean)) - { - source[0, 0] = 1; - dest[0, 0] = 2; - - Buffer2D.SwapOrCopyContent(dest, source); - } - - int actual1 = dest.DangerousGetRowSpan(0)[0]; - int actual2 = dest.DangerousGetRowSpan(0)[0]; - int actual3 = dest.GetSafeRowMemory(0).Span[0]; - int actual5 = dest[0, 0]; - - Assert.Equal(1, actual1); - Assert.Equal(1, actual2); - Assert.Equal(1, actual3); - Assert.Equal(1, actual5); - } - [Theory] [InlineData(100, 20, 0, 90, 10)] [InlineData(100, 3, 0, 50, 50)] diff --git a/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.SwapOrCopyContent.cs b/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.SwapOrCopyContent.cs deleted file mode 100644 index 61b9f7a89..000000000 --- a/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.SwapOrCopyContent.cs +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. - -using System; -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.PixelFormats; -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Memory.DiscontiguousBuffers -{ - public partial class MemoryGroupTests - { - public class SwapOrCopyContent : MemoryGroupTestsBase - { - [Fact] - public void WhenBothAreMemoryOwners_ShouldSwap() - { - this.MemoryAllocator.BufferCapacityInBytes = sizeof(int) * 50; - using MemoryGroup a = this.MemoryAllocator.AllocateGroup(100, 50); - using MemoryGroup b = this.MemoryAllocator.AllocateGroup(120, 50); - - Memory a0 = a[0]; - Memory a1 = a[1]; - Memory b0 = b[0]; - Memory b1 = b[1]; - - bool swap = MemoryGroup.SwapOrCopyContent(a, b); - - Assert.True(swap); - Assert.Equal(b0, a[0]); - Assert.Equal(b1, a[1]); - Assert.Equal(a0, b[0]); - Assert.Equal(a1, b[1]); - Assert.NotEqual(a[0], b[0]); - } - - [Fact] - public void WhenBothAreMemoryOwners_ShouldReplaceViews() - { - using MemoryGroup a = this.MemoryAllocator.AllocateGroup(100, 100); - using MemoryGroup b = this.MemoryAllocator.AllocateGroup(120, 100); - - a[0].Span[42] = 1; - b[0].Span[33] = 2; - MemoryGroupView aView0 = a.View; - MemoryGroupView bView0 = b.View; - - MemoryGroup.SwapOrCopyContent(a, b); - Assert.False(aView0.IsValid); - Assert.False(bView0.IsValid); - Assert.ThrowsAny(() => _ = aView0[0].Span); - Assert.ThrowsAny(() => _ = bView0[0].Span); - - Assert.True(a.View.IsValid); - Assert.True(b.View.IsValid); - Assert.Equal(2, a.View[0].Span[33]); - Assert.Equal(1, b.View[0].Span[42]); - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void WhenDestIsNotAllocated_SameSize_ShouldCopy(bool sourceIsAllocated) - { - var data = new Rgba32[21]; - var color = new Rgba32(1, 2, 3, 4); - - using var destOwner = new TestMemoryManager(data); - using var dest = MemoryGroup.Wrap(destOwner.Memory); - - using MemoryGroup source = this.MemoryAllocator.AllocateGroup(21, 30); - - source[0].Span[10] = color; - - // Act: - bool swap = MemoryGroup.SwapOrCopyContent(dest, source); - - // Assert: - Assert.False(swap); - Assert.Equal(color, dest[0].Span[10]); - Assert.NotEqual(source[0], dest[0]); - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void WhenDestIsNotMemoryOwner_DifferentSize_Throws(bool sourceIsOwner) - { - var data = new Rgba32[21]; - var color = new Rgba32(1, 2, 3, 4); - - using var destOwner = new TestMemoryManager(data); - var dest = MemoryGroup.Wrap(destOwner.Memory); - - using MemoryGroup source = this.MemoryAllocator.AllocateGroup(22, 30); - - source[0].Span[10] = color; - - // Act: - Assert.ThrowsAny(() => MemoryGroup.SwapOrCopyContent(dest, source)); - - Assert.Equal(color, source[0].Span[10]); - Assert.NotEqual(color, dest[0].Span[10]); - } - } - } -} diff --git a/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.cs b/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.cs index 13e47bdee..cb6c34b77 100644 --- a/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.cs +++ b/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.cs @@ -146,7 +146,7 @@ namespace SixLabors.ImageSharp.Tests.Memory.DiscontiguousBuffers { using MemoryGroup group = this.CreateTestGroup(totalLength, bufferLength, true); - Memory slice = group.GetBoundedSlice(start, length); + Memory slice = group.GetBoundedMemorySlice(start, length); Assert.Equal(length, slice.Length); @@ -172,7 +172,7 @@ namespace SixLabors.ImageSharp.Tests.Memory.DiscontiguousBuffers public void GetBoundedSlice_WhenOverlapsBuffers_Throws(long totalLength, int bufferLength, long start, int length) { using MemoryGroup group = this.CreateTestGroup(totalLength, bufferLength, true); - Assert.ThrowsAny(() => group.GetBoundedSlice(start, length)); + Assert.ThrowsAny(() => group.GetBoundedMemorySlice(start, length)); } [Fact] diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs index ebc096852..3ee20cbd1 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs @@ -185,9 +185,9 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif 2 x due to use of non-standard padding tag 0xEA1C listed in EXIF Tool. We can read those values but adhere strictly to the 2.3.1 specification when writing. (TODO: Support 2.3.2) https://exiftool.org/TagNames/EXIF.html */ - [InlineData(TestImageWriteFormat.Jpeg, 16)] - [InlineData(TestImageWriteFormat.Png, 16)] - [InlineData(TestImageWriteFormat.WebpLossless, 16)] + [InlineData(TestImageWriteFormat.Jpeg, 18)] + [InlineData(TestImageWriteFormat.Png, 18)] + [InlineData(TestImageWriteFormat.WebpLossless, 18)] public void SetValue(TestImageWriteFormat imageFormat, int expectedProfileValueCount) { Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateRgba32Image(); @@ -557,7 +557,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif // todo: duplicate tags Assert.Equal(2, profile.Values.Count(v => (ushort)v.Tag == 59932)); - Assert.Equal(16, profile.Values.Count); + Assert.Equal(18, profile.Values.Count); foreach (IExifValue value in profile.Values) { diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/Values/ExifValuesTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/Values/ExifValuesTests.cs index 3358b1f97..a5ca115d4 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/Values/ExifValuesTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/Values/ExifValuesTests.cs @@ -394,6 +394,34 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif.Values Assert.Equal(expected, typed.Value); } + [Fact] + public void NumberTests() + { + Number value1 = ushort.MaxValue; + Number value2 = ushort.MaxValue; + Assert.True(value1 == value2); + + value2 = short.MaxValue; + Assert.True(value1 != value2); + + value1 = -1; + value2 = -2; + Assert.True(value1 > value2); + + value1 = -6; + Assert.True(value1 <= value2); + + value1 = 10; + value2 = 10; + Assert.True(value1 >= value2); + + Assert.True(value1.Equals(value2)); + Assert.True(value1.GetHashCode() == value2.GetHashCode()); + + value1 = 1; + Assert.False(value1.Equals(value2)); + } + [Theory] [MemberData(nameof(NumberTags))] public void ExifNumberTests(ExifTag tag) @@ -408,6 +436,9 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif.Values var typed = (ExifNumber)value; Assert.Equal(expected, typed.Value); + + typed.Value = ushort.MaxValue + 1; + Assert.True(expected < typed.Value); } [Theory] @@ -422,6 +453,15 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif.Values var typed = (ExifNumberArray)value; Assert.Equal(expected, typed.Value); + + Assert.True(value.TrySetValue(int.MaxValue)); + Assert.Equal(new[] { (Number)int.MaxValue }, value.GetValue()); + + Assert.True(value.TrySetValue(new[] { 1u, 2u, 5u })); + Assert.Equal(new[] { (Number)1u, (Number)2u, (Number)5u }, value.GetValue()); + + Assert.True(value.TrySetValue(new[] { (short)1, (short)2, (short)5 })); + Assert.Equal(new[] { (Number)(short)1, (Number)(short)2, (Number)(short)5 }, value.GetValue()); } [Theory] diff --git a/tests/ImageSharp.Tests/PixelFormats/Abgr32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Abgr32Tests.cs new file mode 100644 index 000000000..278de3357 --- /dev/null +++ b/tests/ImageSharp.Tests/PixelFormats/Abgr32Tests.cs @@ -0,0 +1,148 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.PixelFormats +{ + [Trait("Category", "PixelFormats")] + public class Abgr32Tests + { + /// + /// Tests the equality operators for equality. + /// + [Fact] + public void AreEqual() + { + var color1 = new Abgr32(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue); + var color2 = new Abgr32(byte.MaxValue, byte.MaxValue, byte.MaxValue); + + Assert.Equal(color1, color2); + } + + /// + /// Tests the equality operators for inequality. + /// + [Fact] + public void AreNotEqual() + { + var color1 = new Abgr32(0, 0, byte.MaxValue, byte.MaxValue); + var color2 = new Abgr32(byte.MaxValue, byte.MaxValue, byte.MaxValue); + + Assert.NotEqual(color1, color2); + } + + public static readonly TheoryData ColorData = + new() + { + { 1, 2, 3, 4 }, + { 4, 5, 6, 7 }, + { 0, 255, 42, 0 }, + { 1, 2, 3, 255 } + }; + + [Theory] + [MemberData(nameof(ColorData))] + public void Constructor(byte b, byte g, byte r, byte a) + { + var p = new Abgr32(r, g, b, a); + + Assert.Equal(r, p.R); + Assert.Equal(g, p.G); + Assert.Equal(b, p.B); + Assert.Equal(a, p.A); + } + + [Fact] + public unsafe void ByteLayoutIsSequentialBgra() + { + var color = new Abgr32(1, 2, 3, 4); + byte* ptr = (byte*)&color; + + Assert.Equal(4, ptr[0]); + Assert.Equal(3, ptr[1]); + Assert.Equal(2, ptr[2]); + Assert.Equal(1, ptr[3]); + } + + [Theory] + [MemberData(nameof(ColorData))] + public void Equality_WhenTrue(byte r, byte g, byte b, byte a) + { + var x = new Abgr32(r, g, b, a); + var y = new Abgr32(r, g, b, a); + + Assert.True(x.Equals(y)); + Assert.True(x.Equals((object)y)); + Assert.Equal(x.GetHashCode(), y.GetHashCode()); + } + + [Theory] + [InlineData(1, 2, 3, 4, 1, 2, 3, 5)] + [InlineData(0, 0, 255, 0, 0, 0, 244, 0)] + [InlineData(0, 255, 0, 0, 0, 244, 0, 0)] + [InlineData(1, 255, 0, 0, 0, 255, 0, 0)] + public void Equality_WhenFalse(byte b1, byte g1, byte r1, byte a1, byte b2, byte g2, byte r2, byte a2) + { + var x = new Abgr32(r1, g1, b1, a1); + var y = new Abgr32(r2, g2, b2, a2); + + Assert.False(x.Equals(y)); + Assert.False(x.Equals((object)y)); + } + + [Fact] + public void FromRgba32() + { + var abgr = default(Abgr32); + abgr.FromRgba32(new Rgba32(1, 2, 3, 4)); + + Assert.Equal(1, abgr.R); + Assert.Equal(2, abgr.G); + Assert.Equal(3, abgr.B); + Assert.Equal(4, abgr.A); + } + + private static Vector4 Vec(byte r, byte g, byte b, byte a = 255) => new Vector4( + r / 255f, + g / 255f, + b / 255f, + a / 255f); + + [Fact] + public void FromVector4() + { + var c = default(Abgr32); + c.FromVector4(Vec(1, 2, 3, 4)); + + Assert.Equal(1, c.R); + Assert.Equal(2, c.G); + Assert.Equal(3, c.B); + Assert.Equal(4, c.A); + } + + [Fact] + public void ToVector4() + { + var abgr = new Abgr32(1, 2, 3, 4); + + Assert.Equal(Vec(1, 2, 3, 4), abgr.ToVector4()); + } + + [Fact] + public void Abgr32_FromBgra5551() + { + // arrange + var abgr = default(Abgr32); + uint expected = uint.MaxValue; + + // act + abgr.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + + // assert + Assert.Equal(expected, abgr.PackedValue); + } + } +} diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs index 4b8f4c2ea..7a2f29e2c 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs @@ -96,12 +96,13 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats [Fact] public void FromRgba32() { - var rgb = default(Rgb24); - rgb.FromRgba32(new Rgba32(1, 2, 3, 4)); + var bgra = default(Bgra32); + bgra.FromRgba32(new Rgba32(1, 2, 3, 4)); - Assert.Equal(1, rgb.R); - Assert.Equal(2, rgb.G); - Assert.Equal(3, rgb.B); + Assert.Equal(1, bgra.R); + Assert.Equal(2, bgra.G); + Assert.Equal(3, bgra.B); + Assert.Equal(4, bgra.A); } private static Vector4 Vec(byte r, byte g, byte b, byte a = 255) => new Vector4( diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelConverterTests.ReferenceImplementations.cs b/tests/ImageSharp.Tests/PixelFormats/PixelConverterTests.ReferenceImplementations.cs index 8b3483145..e51ee233c 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelConverterTests.ReferenceImplementations.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelConverterTests.ReferenceImplementations.cs @@ -60,6 +60,21 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats return buffer; } + public static byte[] MakeAbgr32ByteArray(byte r, byte g, byte b, byte a) + { + var buffer = new byte[256]; + + for (int i = 0; i < buffer.Length; i += 4) + { + buffer[i] = a; + buffer[i + 1] = b; + buffer[i + 2] = g; + buffer[i + 3] = r; + } + + return buffer; + } + internal static void To( Configuration configuration, ReadOnlySpan sourcePixels, diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelConverterTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelConverterTests.cs index 315f9f776..986f4189b 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelConverterTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelConverterTests.cs @@ -56,6 +56,20 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Assert.Equal(expected, actual); } + + [Theory] + [MemberData(nameof(RgbaData))] + public void ToAbgr32(byte r, byte g, byte b, byte a) + { + byte[] source = ReferenceImplementations.MakeRgba32ByteArray(r, g, b, a); + byte[] actual = new byte[source.Length]; + + PixelConverter.FromRgba32.ToAbgr32(source, actual); + + byte[] expected = ReferenceImplementations.MakeAbgr32ByteArray(r, g, b, a); + + Assert.Equal(expected, actual); + } } public class FromArgb32 : PixelConverterTests @@ -119,5 +133,50 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Assert.Equal(expected, actual); } } + + public class FromAbgr32 : PixelConverterTests + { + [Theory] + [MemberData(nameof(RgbaData))] + public void ToArgb32(byte r, byte g, byte b, byte a) + { + byte[] source = ReferenceImplementations.MakeAbgr32ByteArray(r, g, b, a); + byte[] actual = new byte[source.Length]; + + PixelConverter.FromAbgr32.ToArgb32(source, actual); + + byte[] expected = ReferenceImplementations.MakeArgb32ByteArray(r, g, b, a); + + Assert.Equal(expected, actual); + } + + [Theory] + [MemberData(nameof(RgbaData))] + public void ToRgba32(byte r, byte g, byte b, byte a) + { + byte[] source = ReferenceImplementations.MakeAbgr32ByteArray(r, g, b, a); + byte[] actual = new byte[source.Length]; + + PixelConverter.FromAbgr32.ToRgba32(source, actual); + + byte[] expected = ReferenceImplementations.MakeRgba32ByteArray(r, g, b, a); + + Assert.Equal(expected, actual); + } + + [Theory] + [MemberData(nameof(RgbaData))] + public void ToBgra32(byte r, byte g, byte b, byte a) + { + byte[] source = ReferenceImplementations.MakeAbgr32ByteArray(r, g, b, a); + byte[] actual = new byte[source.Length]; + + PixelConverter.FromAbgr32.ToBgra32(source, actual); + + byte[] expected = ReferenceImplementations.MakeBgra32ByteArray(r, g, b, a); + + Assert.Equal(expected, actual); + } + } } } diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/Generated/PixelOperationsTests.Specialized.Generated.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/Generated/PixelOperationsTests.Specialized.Generated.cs index 1069eb9ac..10b06df3b 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/Generated/PixelOperationsTests.Specialized.Generated.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/Generated/PixelOperationsTests.Specialized.Generated.cs @@ -12,8 +12,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations { public partial class PixelOperationsTests { - - public partial class A8_OperationsTests : PixelOperationsTests + public partial class A8_OperationsTests : PixelOperationsTests { public A8_OperationsTests(ITestOutputHelper output) : base(output) @@ -32,7 +31,6 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations Assert.Equal(PixelAlphaRepresentation.Unassociated, alphaRepresentation); } } - public partial class Argb32_OperationsTests : PixelOperationsTests { public Argb32_OperationsTests(ITestOutputHelper output) @@ -52,7 +50,25 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations Assert.Equal(PixelAlphaRepresentation.Unassociated, alphaRepresentation); } } + public partial class Abgr32_OperationsTests : PixelOperationsTests + { + public Abgr32_OperationsTests(ITestOutputHelper output) + : base(output) + { + } + protected override PixelOperations Operations => Abgr32.PixelOperations.Instance; + + [Fact] + public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); + + [Fact] + public void PixelTypeInfoHasCorrectAlphaRepresentation() + { + var alphaRepresentation = this.Operations.GetPixelTypeInfo().AlphaRepresentation; + Assert.Equal(PixelAlphaRepresentation.Unassociated, alphaRepresentation); + } + } public partial class Bgr24_OperationsTests : PixelOperationsTests { public Bgr24_OperationsTests(ITestOutputHelper output) @@ -72,7 +88,6 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations Assert.Equal(PixelAlphaRepresentation.None, alphaRepresentation); } } - public partial class Bgr565_OperationsTests : PixelOperationsTests { public Bgr565_OperationsTests(ITestOutputHelper output) @@ -92,7 +107,6 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations Assert.Equal(PixelAlphaRepresentation.None, alphaRepresentation); } } - public partial class Bgra32_OperationsTests : PixelOperationsTests { public Bgra32_OperationsTests(ITestOutputHelper output) @@ -112,7 +126,6 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations Assert.Equal(PixelAlphaRepresentation.Unassociated, alphaRepresentation); } } - public partial class Bgra4444_OperationsTests : PixelOperationsTests { public Bgra4444_OperationsTests(ITestOutputHelper output) @@ -132,7 +145,6 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations Assert.Equal(PixelAlphaRepresentation.Unassociated, alphaRepresentation); } } - public partial class Bgra5551_OperationsTests : PixelOperationsTests { public Bgra5551_OperationsTests(ITestOutputHelper output) @@ -152,7 +164,6 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations Assert.Equal(PixelAlphaRepresentation.Unassociated, alphaRepresentation); } } - public partial class Byte4_OperationsTests : PixelOperationsTests { public Byte4_OperationsTests(ITestOutputHelper output) @@ -172,7 +183,6 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations Assert.Equal(PixelAlphaRepresentation.Unassociated, alphaRepresentation); } } - public partial class HalfSingle_OperationsTests : PixelOperationsTests { public HalfSingle_OperationsTests(ITestOutputHelper output) @@ -192,7 +202,6 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations Assert.Equal(PixelAlphaRepresentation.None, alphaRepresentation); } } - public partial class HalfVector2_OperationsTests : PixelOperationsTests { public HalfVector2_OperationsTests(ITestOutputHelper output) @@ -212,7 +221,6 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations Assert.Equal(PixelAlphaRepresentation.None, alphaRepresentation); } } - public partial class HalfVector4_OperationsTests : PixelOperationsTests { public HalfVector4_OperationsTests(ITestOutputHelper output) @@ -232,7 +240,6 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations Assert.Equal(PixelAlphaRepresentation.Unassociated, alphaRepresentation); } } - public partial class L16_OperationsTests : PixelOperationsTests { public L16_OperationsTests(ITestOutputHelper output) @@ -252,7 +259,6 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations Assert.Equal(PixelAlphaRepresentation.None, alphaRepresentation); } } - public partial class L8_OperationsTests : PixelOperationsTests { public L8_OperationsTests(ITestOutputHelper output) @@ -272,7 +278,6 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations Assert.Equal(PixelAlphaRepresentation.None, alphaRepresentation); } } - public partial class La16_OperationsTests : PixelOperationsTests { public La16_OperationsTests(ITestOutputHelper output) @@ -292,7 +297,6 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations Assert.Equal(PixelAlphaRepresentation.Unassociated, alphaRepresentation); } } - public partial class La32_OperationsTests : PixelOperationsTests { public La32_OperationsTests(ITestOutputHelper output) @@ -312,7 +316,6 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations Assert.Equal(PixelAlphaRepresentation.Unassociated, alphaRepresentation); } } - public partial class NormalizedByte2_OperationsTests : PixelOperationsTests { public NormalizedByte2_OperationsTests(ITestOutputHelper output) @@ -332,7 +335,6 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations Assert.Equal(PixelAlphaRepresentation.None, alphaRepresentation); } } - public partial class NormalizedByte4_OperationsTests : PixelOperationsTests { public NormalizedByte4_OperationsTests(ITestOutputHelper output) @@ -352,7 +354,6 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations Assert.Equal(PixelAlphaRepresentation.Unassociated, alphaRepresentation); } } - public partial class NormalizedShort2_OperationsTests : PixelOperationsTests { public NormalizedShort2_OperationsTests(ITestOutputHelper output) @@ -372,7 +373,6 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations Assert.Equal(PixelAlphaRepresentation.None, alphaRepresentation); } } - public partial class NormalizedShort4_OperationsTests : PixelOperationsTests { public NormalizedShort4_OperationsTests(ITestOutputHelper output) @@ -392,7 +392,6 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations Assert.Equal(PixelAlphaRepresentation.Unassociated, alphaRepresentation); } } - public partial class Rg32_OperationsTests : PixelOperationsTests { public Rg32_OperationsTests(ITestOutputHelper output) @@ -412,7 +411,6 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations Assert.Equal(PixelAlphaRepresentation.None, alphaRepresentation); } } - public partial class Rgb24_OperationsTests : PixelOperationsTests { public Rgb24_OperationsTests(ITestOutputHelper output) @@ -432,7 +430,6 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations Assert.Equal(PixelAlphaRepresentation.None, alphaRepresentation); } } - public partial class Rgb48_OperationsTests : PixelOperationsTests { public Rgb48_OperationsTests(ITestOutputHelper output) @@ -452,7 +449,6 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations Assert.Equal(PixelAlphaRepresentation.None, alphaRepresentation); } } - public partial class Rgba1010102_OperationsTests : PixelOperationsTests { public Rgba1010102_OperationsTests(ITestOutputHelper output) @@ -472,7 +468,6 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations Assert.Equal(PixelAlphaRepresentation.Unassociated, alphaRepresentation); } } - public partial class Rgba32_OperationsTests : PixelOperationsTests { public Rgba32_OperationsTests(ITestOutputHelper output) @@ -492,7 +487,6 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations Assert.Equal(PixelAlphaRepresentation.Unassociated, alphaRepresentation); } } - public partial class Rgba64_OperationsTests : PixelOperationsTests { public Rgba64_OperationsTests(ITestOutputHelper output) @@ -512,7 +506,6 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations Assert.Equal(PixelAlphaRepresentation.Unassociated, alphaRepresentation); } } - public partial class RgbaVector_OperationsTests : PixelOperationsTests { public RgbaVector_OperationsTests(ITestOutputHelper output) @@ -532,7 +525,6 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations Assert.Equal(PixelAlphaRepresentation.Unassociated, alphaRepresentation); } } - public partial class Short2_OperationsTests : PixelOperationsTests { public Short2_OperationsTests(ITestOutputHelper output) @@ -552,7 +544,6 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations Assert.Equal(PixelAlphaRepresentation.None, alphaRepresentation); } } - public partial class Short4_OperationsTests : PixelOperationsTests { public Short4_OperationsTests(ITestOutputHelper output) diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/Generated/_Common.ttinclude b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/Generated/_Common.ttinclude index 8c436eecc..6ef3e914f 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/Generated/_Common.ttinclude +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/Generated/_Common.ttinclude @@ -16,6 +16,7 @@ using Xunit.Abstractions; { "A8", "Argb32", + "Abgr32", "Bgra32", "Bgra4444", "Bgra5551", @@ -38,6 +39,7 @@ using Xunit.Abstractions; { "A8", "Argb32", + "Abgr32", "Bgr24", "Bgr565", "Bgra32", diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs index a2688359f..2a89c1d39 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs @@ -322,6 +322,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations public static readonly TheoryData Generic_To_Data = new TheoryData { + new TestPixel(), new TestPixel(), new TestPixel(), new TestPixel(), @@ -586,6 +587,50 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations (s, d) => this.Operations.ToBgra32Bytes(this.Configuration, s, d.GetSpan(), count)); } + [Theory] + [MemberData(nameof(ArraySizesData))] + public void FromAbgr32Bytes(int count) + { + byte[] source = CreateByteTestData(count * 4); + var expected = new TPixel[count]; + + for (int i = 0; i < count; i++) + { + int i4 = i * 4; + + expected[i].FromAbgr32(new Abgr32(source[i4 + 3], source[i4 + 2], source[i4 + 1], source[i4 + 0])); + } + + TestOperation( + source, + expected, + (s, d) => this.Operations.FromAbgr32Bytes(this.Configuration, s, d.GetSpan(), count)); + } + + [Theory] + [MemberData(nameof(ArraySizesData))] + public void ToAbgr32Bytes(int count) + { + TPixel[] source = CreatePixelTestData(count); + byte[] expected = new byte[count * 4]; + var abgr = default(Abgr32); + + for (int i = 0; i < count; i++) + { + int i4 = i * 4; + abgr.FromScaledVector4(source[i].ToScaledVector4()); + expected[i4] = abgr.A; + expected[i4 + 1] = abgr.B; + expected[i4 + 2] = abgr.G; + expected[i4 + 3] = abgr.R; + } + + TestOperation( + source, + expected, + (s, d) => this.Operations.ToAbgr32Bytes(this.Configuration, s, d.GetSpan(), count)); + } + [Theory] [MemberData(nameof(ArraySizesData))] public void FromBgra5551Bytes(int count) diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs index bc66d404b..aff16d6d8 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs @@ -230,6 +230,22 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Assert.Equal(expected, actual); } + [Fact] + public void Rgba32_FromAbgr32_ToRgba32() + { + // arrange + var rgba = default(Rgba32); + var actual = default(Abgr32); + var expected = new Abgr32(0x1a, 0, 0x80, 0); + + // act + rgba.FromAbgr32(expected); + actual.FromRgba32(rgba); + + // assert + Assert.Equal(expected, actual); + } + [Fact] public void Rgba32_FromArgb32_ToArgb32() { diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgba64Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgba64Tests.cs index 8daf960d6..e08cd2950 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgba64Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgba64Tests.cs @@ -183,6 +183,16 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Assert.Equal(expected, actual); } + [Fact] + public void ConstructFrom_Abgr32() + { + var expected = new Rgba64(5140, 9766, 19532, 29555); + var source = new Abgr32(20, 38, 76, 115); + var actual = new Rgba64(source); + + Assert.Equal(expected, actual); + } + [Fact] public void ConstructFrom_Rgb24() { @@ -257,6 +267,20 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Assert.Equal(expected, actual); } + [Fact] + public void ToAbgr32_Retval() + { + // arrange + var source = new Rgba64(5140, 9766, 19532, 29555); + var expected = new Abgr32(20, 38, 76, 115); + + // act + var actual = source.ToAbgr32(); + + // assert + Assert.Equal(expected, actual); + } + [Fact] public void ToRgb24_Retval() { diff --git a/tests/ImageSharp.Tests/PixelFormats/Short4Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Short4Tests.cs index 3709205ac..a95ac9ab9 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Short4Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Short4Tests.cs @@ -150,6 +150,24 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Assert.Equal(expected, actual); } + [Fact] + public void Short4_FromAbgrb32_ToRgba32() + { + // arrange + var short4 = default(Short4); + var actual = default(Abgr32); + var expected = new Abgr32(20, 38, 0, 255); + + // act + short4.FromAbgr32(expected); + Rgba32 temp = default; + short4.ToRgba32(ref temp); + actual.FromRgba32(temp); + + // assert + Assert.Equal(expected, actual); + } + [Fact] public void Short4_FromRgb48_ToRgb48() { diff --git a/tests/ImageSharp.Tests/TestFormat.cs b/tests/ImageSharp.Tests/TestFormat.cs index c8d0633d7..6c2b97eb6 100644 --- a/tests/ImageSharp.Tests/TestFormat.cs +++ b/tests/ImageSharp.Tests/TestFormat.cs @@ -310,6 +310,10 @@ namespace SixLabors.ImageSharp.Tests { } + public void FromAbgr32(Abgr32 source) + { + } + public void FromL8(L8 source) { } diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 25e8de2aa..7e396d107 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -118,9 +118,13 @@ namespace SixLabors.ImageSharp.Tests // Issue 1765: https://github.com/SixLabors/ImageSharp/issues/1765 public const string Issue1765_Net6DeflateStreamRead = "Png/issues/Issue_1765_Net6DeflateStreamRead.png"; + // Discussion 1875: https://github.com/SixLabors/ImageSharp/discussions/1875 + public const string Issue1875 = "Png/raw-profile-type-exif.png"; + public static class Bad { public const string MissingDataChunk = "Png/xdtn0g01.png"; + public const string WrongCrcDataChunk = "Png/xcsn0g01.png"; public const string CorruptedChunk = "Png/big-corrupted-chunk.png"; // Zlib errors. @@ -255,6 +259,7 @@ namespace SixLabors.ImageSharp.Tests public const string BadSubSampling1076 = "Jpg/issues/issue-1076-invalid-subsampling.jpg"; public const string IdentifyMultiFrame1211 = "Jpg/issues/issue-1221-identify-multi-frame.jpg"; public const string WrongColorSpace = "Jpg/issues/Issue1732-WrongColorSpace.jpg"; + public const string MalformedUnsupportedComponentCount = "Jpg/issues/issue-1900-malformed-unsupported-255-components.jpg"; public static class Fuzz { @@ -872,6 +877,29 @@ namespace SixLabors.ImageSharp.Tests public static readonly string[] Metadata = { SampleMetadata }; } + public static class BigTiff + { + public const string Base = "Tiff/BigTiff/"; + + public const string BigTIFF = Base + "BigTIFF.tif"; + public const string BigTIFFLong = Base + "BigTIFFLong.tif"; + public const string BigTIFFLong8 = Base + "BigTIFFLong8.tif"; + public const string BigTIFFLong8Tiles = Base + "BigTIFFLong8Tiles.tif"; + public const string BigTIFFMotorola = Base + "BigTIFFMotorola.tif"; + public const string BigTIFFMotorolaLongStrips = Base + "BigTIFFMotorolaLongStrips.tif"; + + public const string BigTIFFSubIFD4 = Base + "BigTIFFSubIFD4.tif"; + public const string BigTIFFSubIFD8 = Base + "BigTIFFSubIFD8.tif"; + + public const string Indexed4_Deflate = Base + "BigTIFF_Indexed4_Deflate.tif"; + public const string Indexed8_LZW = Base + "BigTIFF_Indexed8_LZW.tif"; + public const string MinIsBlack = Base + "BigTIFF_MinIsBlack.tif"; + public const string MinIsWhite = Base + "BigTIFF_MinIsWhite.tif"; + + public const string Damaged_MinIsWhite_RLE = Base + "BigTIFF_MinIsWhite_RLE.tif"; + public const string Damaged_MinIsBlack_RLE = Base + "BigTIFF_MinIsBlack_RLE.tif"; + } + public static class Pbm { public const string BlackAndWhitePlain = "Pbm/blackandwhite_plain.pbm"; diff --git a/tests/ImageSharp.Tests/TestUtilities/PixelTypes.cs b/tests/ImageSharp.Tests/TestUtilities/PixelTypes.cs index eb840231c..8ba383125 100644 --- a/tests/ImageSharp.Tests/TestUtilities/PixelTypes.cs +++ b/tests/ImageSharp.Tests/TestUtilities/PixelTypes.cs @@ -69,6 +69,8 @@ namespace SixLabors.ImageSharp.Tests La32 = 1 << 26, + Abgr32 = 1 << 27, + // TODO: Add multi-flag entries by rules defined in PackedPixelConverterHelper // "All" is handled as a separate, individual case instead of using bitwise OR diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs index bb9ed8260..1a46f91e5 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs @@ -112,14 +112,15 @@ namespace SixLabors.ImageSharp.Tests [Fact] public void ExpandAllTypes_2() { - PixelTypes pixelTypes = PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.RgbaVector; + PixelTypes pixelTypes = PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.Abgr32 | PixelTypes.RgbaVector; IEnumerable> expanded = pixelTypes.ExpandAllTypes(); - Assert.Equal(3, expanded.Count()); + Assert.Equal(4, expanded.Count()); AssertContainsPixelType(PixelTypes.Rgba32, expanded); AssertContainsPixelType(PixelTypes.Bgra32, expanded); + AssertContainsPixelType(PixelTypes.Abgr32, expanded); AssertContainsPixelType(PixelTypes.RgbaVector, expanded); } diff --git a/tests/Images/Input/Jpg/issues/issue-1900-malformed-unsupported-255-components.jpg b/tests/Images/Input/Jpg/issues/issue-1900-malformed-unsupported-255-components.jpg new file mode 100644 index 000000000..7b6d7c572 --- /dev/null +++ b/tests/Images/Input/Jpg/issues/issue-1900-malformed-unsupported-255-components.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f3553f76b72b98986df13a7508cd073a30b7c846dc441d1d68a518bf7b93bc66 +size 3951 diff --git a/tests/Images/Input/Png/raw-profile-type-exif.png b/tests/Images/Input/Png/raw-profile-type-exif.png new file mode 100644 index 000000000..efd9b35aa --- /dev/null +++ b/tests/Images/Input/Png/raw-profile-type-exif.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2259b08fd0c4681ecd068244df358b486f5eca1fcd18edbc7d9207eeef3ca5ed +size 392 diff --git a/tests/Images/Input/Png/xcsn0g01.png b/tests/Images/Input/Png/xcsn0g01.png new file mode 100644 index 000000000..6908a107c --- /dev/null +++ b/tests/Images/Input/Png/xcsn0g01.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:71e4b2826f61556eda39f3a93c8769b14d3ac90f135177b9373061199dbef39a +size 164 diff --git a/tests/Images/Input/Tiff/BigTiff/BigTIFF.tif b/tests/Images/Input/Tiff/BigTiff/BigTIFF.tif new file mode 100644 index 000000000..b27834479 --- /dev/null +++ b/tests/Images/Input/Tiff/BigTiff/BigTIFF.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ddb202145a9bce7670cc372ee578de5a53cd52cc8d5ae8a9ebdc9f9c4f4a7e81 +size 12480 diff --git a/tests/Images/Input/Tiff/BigTiff/BigTIFFLong.tif b/tests/Images/Input/Tiff/BigTiff/BigTIFFLong.tif new file mode 100644 index 000000000..b390c0a97 --- /dev/null +++ b/tests/Images/Input/Tiff/BigTiff/BigTIFFLong.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:90178643a159ec50335e9314836df924233debeb100763af0f77cd1be3cf58ab +size 12480 diff --git a/tests/Images/Input/Tiff/BigTiff/BigTIFFLong8.tif b/tests/Images/Input/Tiff/BigTiff/BigTIFFLong8.tif new file mode 100644 index 000000000..e1815c750 --- /dev/null +++ b/tests/Images/Input/Tiff/BigTiff/BigTIFFLong8.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b38e61ccb01e10e26fb10c335fc6fca9ad087b0fb0df833e54bb02d1246e20d5 +size 12480 diff --git a/tests/Images/Input/Tiff/BigTiff/BigTIFFLong8Tiles.tif b/tests/Images/Input/Tiff/BigTiff/BigTIFFLong8Tiles.tif new file mode 100644 index 000000000..c69218948 --- /dev/null +++ b/tests/Images/Input/Tiff/BigTiff/BigTIFFLong8Tiles.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a2d92b0c430cefc390f13961e00950ee7246b013335594dd249ba823eb3c3fdb +size 12564 diff --git a/tests/Images/Input/Tiff/BigTiff/BigTIFFMotorola.tif b/tests/Images/Input/Tiff/BigTiff/BigTIFFMotorola.tif new file mode 100644 index 000000000..68c2b3a41 --- /dev/null +++ b/tests/Images/Input/Tiff/BigTiff/BigTIFFMotorola.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ace8a27dbed9f918993615e545a12310b84ad94bc6af8e256258e69731f1c7ce +size 12480 diff --git a/tests/Images/Input/Tiff/BigTiff/BigTIFFMotorolaLongStrips.tif b/tests/Images/Input/Tiff/BigTiff/BigTIFFMotorolaLongStrips.tif new file mode 100644 index 000000000..cc62e0362 --- /dev/null +++ b/tests/Images/Input/Tiff/BigTiff/BigTIFFMotorolaLongStrips.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2c5ebdc3774955d3b47644f57bd023e764a93ca2271118152ae920b34c1784bb +size 12480 diff --git a/tests/Images/Input/Tiff/BigTiff/BigTIFFSamples.md b/tests/Images/Input/Tiff/BigTiff/BigTIFFSamples.md new file mode 100644 index 000000000..0401109fd --- /dev/null +++ b/tests/Images/Input/Tiff/BigTiff/BigTIFFSamples.md @@ -0,0 +1,220 @@ +These images were created by [AWare Systems](http://www.awaresystems.be/). + +# Index + +[Classic.tif](#classictif) +[BigTIFF.tif](#bigtifftif) +[BigTIFFMotorola.tif](#bigtiffmotorolatif) +[BigTIFFLong.tif](#bigtifflongtif) +[BigTIFFLong8.tif](#bigtifflong8tif) +[BigTIFFMotorolaLongStrips.tif](#bigtiffmotorolalongstripstif) +[BigTIFFLong8Tiles.tif](#bigtifflong8tilestif) +[BigTIFFSubIFD4.tif](#bigtiffsubifd4tif) +[BigTIFFSubIFD8.tif](#bigtiffsubifd8tif) + +# Classic.tif + +Classic.tif is a basic Classic TIFF file. All files in this package have the same actual image content, so this TIFF file serves as a reference. + +Format: Classic TIFF +Byte Order: Intel +Ifd Offset: 12302 +    ImageWidth (1 Short): 64 +    ImageLength (1 Short): 64 +    BitsPerSample (3 Short): 8, 8, 8 +    PhotometricInterpretation (1 Short): RGB +    StripOffsets (1 Short): 8 +    SamplesPerPixel (1 Short): 3 +    RowsPerStrip (1 Short): 64 +    StripByteCounts (1 Short): 12288 + +# BigTIFF.tif + +BigTIFF.tif ressembles Classic.tif as close as possible. Except that it's a BigTIFF, that is... + +Format: BigTIFF +Byte Order: Intel +Ifd Offset: 12304 +    ImageWidth (1 Short): 64 +    ImageLength (1 Short): 64 +    BitsPerSample (3 Short): 8, 8, 8 +    PhotometricInterpretation (1 Short): RGB +    StripOffsets (1 Short): 16 +    SamplesPerPixel (1 Short): 3 +    RowsPerStrip (1 Short): 64 +    StripByteCounts (1 Short): 12288 + +# BigTIFFMotorola.tif + +BigTIFFMotorola.tif reverses the byte order. + +Format: BigTIFF +Byte Order: Motorola +Ifd Offset: 12304 +    ImageWidth (1 Short): 64 +    ImageLength (1 Short): 64 +    BitsPerSample (3 Short): 8, 8, 8 +    PhotometricInterpretation (1 Short): RGB +    StripOffsets (1 Short): 16 +    SamplesPerPixel (1 Short): 3 +    RowsPerStrip (1 Short): 64 +    StripByteCounts (1 Short): 12288 + +# BigTIFFLong.tif + +All previous TIFFs specify DataType Short for StripOffsets and StripByteCounts tags. This BigTIFF instead specifies DataType Long, for these tags. + +Format: BigTIFF +Byte Order: Intel +Ifd Offset: 12304 +    ImageWidth (1 Short): 64 +    ImageLength (1 Short): 64 +    BitsPerSample (3 Short): 8, 8, 8 +    PhotometricInterpretation (1 Short): RGB +    StripOffsets (1 Long): 16 +    SamplesPerPixel (1 Short): 3 +    RowsPerStrip (1 Short): 64 +    StripByteCounts (1 Long): 12288 + +# BigTIFFLong8.tif + +This next one specifies DataType Long8, for StripOffsets and StripByteCounts tags. + +Format: BigTIFF +Byte Order: Intel +Ifd Offset: 12304 +    ImageWidth (1 Short): 64 +    ImageLength (1 Short): 64 +    BitsPerSample (3 Short): 8, 8, 8 +    PhotometricInterpretation (1 Short): RGB +    StripOffsets (1 Long8): 16 +    SamplesPerPixel (1 Short): 3 +    RowsPerStrip (1 Short): 64 +    StripByteCounts (1 Long8): 12288 + +# BigTIFFMotorolaLongStrips.tif + +This BigTIFF has Motorola byte order, plus, it's divided over two strips. StripOffsets and StripByteCounts tags have DataType Long, so their actual value fits inside the IFD. + +Format: BigTIFF +Byte Order: Motorola +Ifd Offset: 12304 +    ImageWidth (1 Short): 64 +    ImageLength (1 Short): 64 +    BitsPerSample (3 Short): 8, 8, 8 +    PhotometricInterpretation (1 Short): RGB +    StripOffsets (2 Long): 16, 6160 +    SamplesPerPixel (1 Short): 3 +    RowsPerStrip (1 Short): 32 +    StripByteCounts (2 Long): 6144, 6144 + +# BigTIFFLong8Tiles.tif + +BigTIFFLong8Tiles.tif is a tiled BigTIFF. TileOffsets and TileByteCounts tags specify DataType Long8. + +Format: BigTIFF +Byte Order: Intel +Ifd Offset: 12368 +    ImageWidth (1 Short): 64 +    ImageLength (1 Short): 64 +    BitsPerSample (3 Short): 8, 8, 8 +    PhotometricInterpretation (1 Short): RGB +    SamplesPerPixel (1 Short): 3 +    TileWidth (1 Short): 32 +    TileLength (1 Short): 32 +    TileOffsets (4 Long8): 16, 3088, 6160, 9232 +    TileByteCounts (4 Long8): 3072, 3072, 3072, 3072 + +# BigTIFFSubIFD4.tif + +This BigTIFF contains two pages, the second page showing almost the same image content as the first, except that the black square is white, and text color is black. Both pages point to a downsample SubIFD, using SubIFDs DataType TIFF_IFD. + +Format: BigTIFF +Byte Order: Intel +Ifd Offset: 15572 +    ImageWidth (1 Short): 64 +    ImageLength (1 Short): 64 +    BitsPerSample (3 Short): 8, 8, 8 +    PhotometricInterpretation (1 Short): RGB +    StripOffsets (1 Short): 3284 +    SamplesPerPixel (1 Short): 3 +    RowsPerStrip (1 Short): 64 +    StripByteCounts (1 Short): 12288 +    SubIFDs (1 IFD): 3088 +SubIfd Offset: 3088 +    NewSubFileType (1 Long): 1 +    ImageWidth (1 Short): 32 +    ImageLength (1 Short): 32 +    BitsPerSample (3 Short): 8, 8, 8 +    PhotometricInterpretation (1 Short): RGB +    StripOffsets (1 Short): 16 +    SamplesPerPixel (1 Short): 3 +    RowsPerStrip (1 Short): 32 +    StripByteCounts (1 Short): 3072 +Ifd Offset: 31324 +    ImageWidth (1 Short): 64 +    ImageLength (1 Short): 64 +    BitsPerSample (3 Short): 8, 8, 8 +    PhotometricInterpretation (1 Short): RGB +    StripOffsets (1 Short): 19036 +    SamplesPerPixel (1 Short): 3 +    RowsPerStrip (1 Short): 64 +    StripByteCounts (1 Short): 12288 +    SubIFDs (1 IFD): 18840 +SubIfd Offset: 18840 +    NewSubFileType (1 Long): 1 +    ImageWidth (1 Short): 32 +    ImageLength (1 Short): 32 +    BitsPerSample (3 Short): 8, 8, 8 +    PhotometricInterpretation (1 Short): RGB +    StripOffsets (1 Short): 15768 +    SamplesPerPixel (1 Short): 3 +    RowsPerStrip (1 Short): 32 +    StripByteCounts (1 Short): 3072 + +# BigTIFFSubIFD8.tif + +BigTIFFSubIFD4.tif is very much the same as BigTIFFSubIFD4.tif, except that the new DataType TIFF_IFD8 is used for the SubIFDs tag. + +Format: BigTIFF +Byte Order: Intel +Ifd Offset: 15572 +    ImageWidth (1 Short): 64 +    ImageLength (1 Short): 64 +    BitsPerSample (3 Short): 8, 8, 8 +    PhotometricInterpretation (1 Short): RGB +    StripOffsets (1 Short): 3284 +    SamplesPerPixel (1 Short): 3 +    RowsPerStrip (1 Short): 64 +    StripByteCounts (1 Short): 12288 +    SubIFDs (1 IFD8): 3088 +SubIfd Offset: 3088 +    NewSubFileType (1 Long): 1 +    ImageWidth (1 Short): 32 +    ImageLength (1 Short): 32 +    BitsPerSample (3 Short): 8, 8, 8 +    PhotometricInterpretation (1 Short): RGB +    StripOffsets (1 Short): 16 +    SamplesPerPixel (1 Short): 3 +    RowsPerStrip (1 Short): 32 +    StripByteCounts (1 Short): 3072 +Ifd Offset: 31324 +    ImageWidth (1 Short): 64 +    ImageLength (1 Short): 64 +    BitsPerSample (3 Short): 8, 8, 8 +    PhotometricInterpretation (1 Short): RGB +    StripOffsets (1 Short): 19036 +    SamplesPerPixel (1 Short): 3 +    RowsPerStrip (1 Short): 64 +    StripByteCounts (1 Short): 12288 +    SubIFDs (1 IFD8): 18840 +SubIfd Offset: 18840 +    NewSubFileType (1 Long): 1 +    ImageWidth (1 Short): 32 +    ImageLength (1 Short): 32 +    BitsPerSample (3 Short): 8, 8, 8 +    PhotometricInterpretation (1 Short): RGB +    StripOffsets (1 Short): 15768 +    SamplesPerPixel (1 Short): 3 +    RowsPerStrip (1 Short): 32 +    StripByteCounts (1 Short): 3072 \ No newline at end of file diff --git a/tests/Images/Input/Tiff/BigTiff/BigTIFFSubIFD4.tif b/tests/Images/Input/Tiff/BigTiff/BigTIFFSubIFD4.tif new file mode 100644 index 000000000..cd7bdf0b5 --- /dev/null +++ b/tests/Images/Input/Tiff/BigTiff/BigTIFFSubIFD4.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:85c8da46abc2284f0ddac10bd03a7c98ee51024840b7e43f41f1c6a09bc02e7e +size 31520 diff --git a/tests/Images/Input/Tiff/BigTiff/BigTIFFSubIFD8.tif b/tests/Images/Input/Tiff/BigTiff/BigTIFFSubIFD8.tif new file mode 100644 index 000000000..bb0b3bf18 --- /dev/null +++ b/tests/Images/Input/Tiff/BigTiff/BigTIFFSubIFD8.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:93e5ac30f507bec7936746ad6e109631c09f9b2332081e986063219ad2452a4a +size 31520 diff --git a/tests/Images/Input/Tiff/BigTiff/BigTIFF_Indexed4_Deflate.tif b/tests/Images/Input/Tiff/BigTiff/BigTIFF_Indexed4_Deflate.tif new file mode 100644 index 000000000..bc736790a --- /dev/null +++ b/tests/Images/Input/Tiff/BigTiff/BigTIFF_Indexed4_Deflate.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:38ba3717b284d7914243609576d0f9b75d732692bf05e2e1ec8b119feb1409fd +size 687 diff --git a/tests/Images/Input/Tiff/BigTiff/BigTIFF_Indexed8_LZW.tif b/tests/Images/Input/Tiff/BigTiff/BigTIFF_Indexed8_LZW.tif new file mode 100644 index 000000000..edff7c40a --- /dev/null +++ b/tests/Images/Input/Tiff/BigTiff/BigTIFF_Indexed8_LZW.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ca404b3ec5560b82169855f0ae69e64c6bc7286117b95fc0e0d505e5e356fa0e +size 2548 diff --git a/tests/Images/Input/Tiff/BigTiff/BigTIFF_MinIsBlack.tif b/tests/Images/Input/Tiff/BigTiff/BigTIFF_MinIsBlack.tif new file mode 100644 index 000000000..491b4092e --- /dev/null +++ b/tests/Images/Input/Tiff/BigTiff/BigTIFF_MinIsBlack.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ac0471c1600f6e5fb47037dab07172aff524abc866a40c9ec54279bd49cbef77 +size 517 diff --git a/tests/Images/Input/Tiff/BigTiff/BigTIFF_MinIsBlack_RLE.tif b/tests/Images/Input/Tiff/BigTiff/BigTIFF_MinIsBlack_RLE.tif new file mode 100644 index 000000000..d15188c17 --- /dev/null +++ b/tests/Images/Input/Tiff/BigTiff/BigTIFF_MinIsBlack_RLE.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:038a298bcace02810054af650f490b6858863c8755e41b786605aa807b43350a +size 509 diff --git a/tests/Images/Input/Tiff/BigTiff/BigTIFF_MinIsWhite.tif b/tests/Images/Input/Tiff/BigTiff/BigTIFF_MinIsWhite.tif new file mode 100644 index 000000000..fb26e58a4 --- /dev/null +++ b/tests/Images/Input/Tiff/BigTiff/BigTIFF_MinIsWhite.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a95b0b46bf4f75babb86d9ec74694e6d684087504be214df48a6c8a54338834c +size 517 diff --git a/tests/Images/Input/Tiff/BigTiff/BigTIFF_MinIsWhite_RLE.tif b/tests/Images/Input/Tiff/BigTiff/BigTIFF_MinIsWhite_RLE.tif new file mode 100644 index 000000000..d15188c17 --- /dev/null +++ b/tests/Images/Input/Tiff/BigTiff/BigTIFF_MinIsWhite_RLE.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:038a298bcace02810054af650f490b6858863c8755e41b786605aa807b43350a +size 509 diff --git a/tests/Images/Input/Tiff/BigTiff/Classic.tif b/tests/Images/Input/Tiff/BigTiff/Classic.tif new file mode 100644 index 000000000..1fa6be78b --- /dev/null +++ b/tests/Images/Input/Tiff/BigTiff/Classic.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cfa7fcf6927be5de644beb238067479e8d834d0cbe2257b00302f5dde84a1c1a +size 12404 diff --git a/tests/Images/Input/Tiff/BigTiff/readme.md b/tests/Images/Input/Tiff/BigTiff/readme.md new file mode 100644 index 000000000..29f9c8ea9 --- /dev/null +++ b/tests/Images/Input/Tiff/BigTiff/readme.md @@ -0,0 +1,5 @@ +#### BigTIFF samples. + +For details: [BigTIFFSamples.md](BigTIFFSamples.md) + +Downloaded from https://www.awaresystems.be/imaging/tiff/bigtiff.html \ No newline at end of file