Browse Source

Merge branch 'main' into sn/net8

pull/2583/head
Stefan Nikolei 2 years ago
committed by GitHub
parent
commit
517ab62d14
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 48
      src/ImageSharp/Formats/AnimationUtilities.cs
  2. 6
      src/ImageSharp/Formats/Png/PngChunkType.cs
  3. 33
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  4. 27
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  5. 26
      src/ImageSharp/Formats/Webp/WebpDecoderCore.cs
  6. 7
      src/ImageSharp/Metadata/ImageFrameMetadata.cs
  7. 7
      src/ImageSharp/Metadata/ImageMetadata.cs
  8. 72
      src/ImageSharp/Metadata/Profiles/CICP/CicpProfile.cs
  9. 86
      src/ImageSharp/Metadata/Profiles/CICP/Enums/CicpColorPrimaries.cs
  10. 96
      src/ImageSharp/Metadata/Profiles/CICP/Enums/CicpMatrixCoefficients.cs
  11. 109
      src/ImageSharp/Metadata/Profiles/CICP/Enums/CicpTransferCharacteristics.cs
  12. BIN
      src/ImageSharp/Metadata/Profiles/CICP/T-REC-H.273-202107-S!!PDF-E.pdf
  13. 5
      src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgb24.PixelOperations.cs
  14. 16
      src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs
  15. 11
      tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs
  16. 36
      tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs
  17. 1
      tests/ImageSharp.Tests/Formats/Png/PngChunkTypeTests.cs
  18. 29
      tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs
  19. 82
      tests/ImageSharp.Tests/Metadata/Profiles/CICP/CicpProfileTests.cs
  20. 2
      tests/ImageSharp.Tests/TestImages.cs
  21. 3
      tests/Images/Input/Jpg/issues/issue2517-bad-d7.jpg
  22. 3
      tests/Images/Input/Png/adamHeadsHLG.png

48
src/ImageSharp/Formats/AnimationUtilities.cs

@ -6,6 +6,7 @@ using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.Arm;
using System.Runtime.Intrinsics.X86;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
@ -175,7 +176,52 @@ internal static class AnimationUtilities
}
}
// TODO: v4 AdvSimd when we can use .NET 8
if (AdvSimd.IsSupported && remaining >= 4)
{
// Update offset since we may be operating on the remainder previously incremented by pixel steps of 8.
x *= 2;
Vector128<uint> r128 = previousFrame != null ? Vector128.Create(bg.PackedValue) : Vector128<uint>.Zero;
Vector128<uint> vmb128 = Vector128<uint>.Zero;
if (blend)
{
vmb128 = AdvSimd.CompareEqual(vmb128, vmb128);
}
while (remaining >= 4)
{
Vector128<uint> p = Unsafe.Add(ref Unsafe.As<Vector256<byte>, Vector128<uint>>(ref previousBase256), x);
Vector128<uint> c = Unsafe.Add(ref Unsafe.As<Vector256<byte>, Vector128<uint>>(ref currentBase256), x);
Vector128<uint> eq = AdvSimd.CompareEqual(p, c);
Vector128<uint> r = SimdUtils.HwIntrinsics.BlendVariable(c, r128, AdvSimd.And(eq, vmb128));
if (nextFrame != null)
{
Vector128<int> n = AdvSimd.ShiftRightLogical(Unsafe.Add(ref Unsafe.As<Vector256<byte>, Vector128<uint>>(ref nextBase256), x), 24).AsInt32();
eq = AdvSimd.BitwiseClear(eq, AdvSimd.CompareGreaterThan(AdvSimd.ShiftRightLogical(c, 24).AsInt32(), n).AsUInt32());
}
Unsafe.Add(ref Unsafe.As<Vector256<byte>, Vector128<uint>>(ref resultBase256), x) = r;
ulong msk = ~AdvSimd.ExtractNarrowingLower(eq).AsUInt64().ToScalar();
if (msk != 0)
{
// If is diff is found, the left side is marked by the min of previously found left side and the start position.
// The right is the max of the previously found right side and the end position.
int start = i + (BitOperations.TrailingZeroCount(msk) / 16);
int end = i + (4 - (BitOperations.LeadingZeroCount(msk) / 16));
left = Math.Min(left, start);
right = Math.Max(right, end);
hasRowDiff = true;
hasDiff = true;
}
x++;
i += 4;
remaining -= 4;
}
}
for (i = remaining; i > 0; i--)
{
x = (uint)(length - i);

6
src/ImageSharp/Formats/Png/PngChunkType.cs

@ -140,6 +140,12 @@ internal enum PngChunkType : uint
/// <remarks>cHRM (Single)</remarks>
Chroma = 0x6348524d,
/// <summary>
/// If this chunk is present, it specifies the color space, transfer function, matrix coefficients of the image
/// using the code points specified in [ITU-T-H.273]
/// </summary>
Cicp = 0x63494350,
/// <summary>
/// This chunk is an ancillary chunk as defined in the PNG Specification.
/// It must appear before the first IDAT chunk within a valid PNG stream.

33
src/ImageSharp/Formats/Png/PngDecoderCore.cs

@ -17,6 +17,7 @@ using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Memory.Internals;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.Metadata.Profiles.Cicp;
using SixLabors.ImageSharp.Metadata.Profiles.Exif;
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
using SixLabors.ImageSharp.Metadata.Profiles.Xmp;
@ -191,6 +192,9 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
case PngChunkType.Gamma:
ReadGammaChunk(pngMetadata, chunk.Data.GetSpan());
break;
case PngChunkType.Cicp:
ReadCicpChunk(metadata, chunk.Data.GetSpan());
break;
case PngChunkType.FrameControl:
frameCount++;
if (frameCount == this.maxFrames)
@ -360,6 +364,15 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
ReadGammaChunk(pngMetadata, chunk.Data.GetSpan());
break;
case PngChunkType.Cicp:
if (this.colorMetadataOnly)
{
this.SkipChunkDataAndCrc(chunk);
break;
}
ReadCicpChunk(metadata, chunk.Data.GetSpan());
break;
case PngChunkType.FrameControl:
++frameCount;
if (frameCount == this.maxFrames)
@ -1426,6 +1439,26 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
return false;
}
/// <summary>
/// Reads the CICP color profile chunk.
/// </summary>
/// <param name="metadata">The metadata.</param>
/// <param name="data">The bytes containing the profile.</param>
private static void ReadCicpChunk(ImageMetadata metadata, ReadOnlySpan<byte> data)
{
if (data.Length < 4)
{
// Ignore invalid cICP chunks.
return;
}
byte colorPrimaries = data[0];
byte transferFunction = data[1];
byte matrixCoefficients = data[2];
bool? fullRange = data[3] == 1 ? true : data[3] == 0 ? false : null;
metadata.CicpProfile = new CicpProfile(colorPrimaries, transferFunction, matrixCoefficients, fullRange);
}
/// <summary>
/// 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

27
src/ImageSharp/Formats/Png/PngEncoderCore.cs

@ -177,6 +177,7 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable
this.WriteHeaderChunk(stream);
this.WriteGammaChunk(stream);
this.WriteCicpChunk(stream, metadata);
this.WriteColorProfileChunk(stream, metadata);
this.WritePaletteChunk(stream, quantized);
this.WriteTransparencyChunk(stream, pngMetadata);
@ -852,6 +853,32 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable
this.WriteChunk(stream, PngChunkType.InternationalText, payload);
}
/// <summary>
/// Writes the CICP profile chunk
/// </summary>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="metaData">The image meta data.</param>
private void WriteCicpChunk(Stream stream, ImageMetadata metaData)
{
if (metaData.CicpProfile is null)
{
return;
}
// by spec, the matrix coefficients must be set to Identity
if (metaData.CicpProfile.MatrixCoefficients != Metadata.Profiles.Cicp.CicpMatrixCoefficients.Identity)
{
throw new NotSupportedException("CICP matrix coefficients other than Identity are not supported in PNG");
}
Span<byte> outputBytes = this.chunkDataBuffer.Span[..4];
outputBytes[0] = (byte)metaData.CicpProfile.ColorPrimaries;
outputBytes[1] = (byte)metaData.CicpProfile.TransferCharacteristics;
outputBytes[2] = (byte)metaData.CicpProfile.MatrixCoefficients;
outputBytes[3] = (byte)(metaData.CicpProfile.FullRange ? 1 : 0);
this.WriteChunk(stream, PngChunkType.Cicp, outputBytes);
}
/// <summary>
/// Writes the color profile chunk.
/// </summary>

26
src/ImageSharp/Formats/Webp/WebpDecoderCore.cs

@ -3,6 +3,7 @@
using System.Buffers;
using System.Buffers.Binary;
using SixLabors.ImageSharp.Common.Helpers;
using SixLabors.ImageSharp.Formats.Webp.Lossless;
using SixLabors.ImageSharp.Formats.Webp.Lossy;
using SixLabors.ImageSharp.IO;
@ -339,10 +340,33 @@ internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable
return;
}
metadata.ExifProfile = new ExifProfile(exifData);
ExifProfile exifProfile = new(exifData);
// Set the resolution from the metadata.
double horizontalValue = GetExifResolutionValue(exifProfile, ExifTag.XResolution);
double verticalValue = GetExifResolutionValue(exifProfile, ExifTag.YResolution);
if (horizontalValue > 0 && verticalValue > 0)
{
metadata.HorizontalResolution = horizontalValue;
metadata.VerticalResolution = verticalValue;
metadata.ResolutionUnits = UnitConverter.ExifProfileToResolutionUnit(exifProfile);
}
metadata.ExifProfile = exifProfile;
}
}
private static double GetExifResolutionValue(ExifProfile exifProfile, ExifTag<Rational> tag)
{
if (exifProfile.TryGetValue(tag, out IExifValue<Rational>? resolution))
{
return resolution.Value.ToDouble();
}
return 0;
}
/// <summary>
/// Reads the XMP profile the stream.
/// </summary>

7
src/ImageSharp/Metadata/ImageFrameMetadata.cs

@ -2,6 +2,7 @@
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Metadata.Profiles.Cicp;
using SixLabors.ImageSharp.Metadata.Profiles.Exif;
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
using SixLabors.ImageSharp.Metadata.Profiles.Iptc;
@ -43,6 +44,7 @@ public sealed class ImageFrameMetadata : IDeepCloneable<ImageFrameMetadata>
this.IccProfile = other.IccProfile?.DeepClone();
this.IptcProfile = other.IptcProfile?.DeepClone();
this.XmpProfile = other.XmpProfile?.DeepClone();
this.CicpProfile = other.CicpProfile?.DeepClone();
}
/// <summary>
@ -65,6 +67,11 @@ public sealed class ImageFrameMetadata : IDeepCloneable<ImageFrameMetadata>
/// </summary>
public IptcProfile? IptcProfile { get; set; }
/// <summary>
/// Gets or sets the CICP profile
/// </summary>
public CicpProfile? CicpProfile { get; set; }
/// <inheritdoc/>
public ImageFrameMetadata DeepClone() => new(this);

7
src/ImageSharp/Metadata/ImageMetadata.cs

@ -2,6 +2,7 @@
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Metadata.Profiles.Cicp;
using SixLabors.ImageSharp.Metadata.Profiles.Exif;
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
using SixLabors.ImageSharp.Metadata.Profiles.Iptc;
@ -68,6 +69,7 @@ public sealed class ImageMetadata : IDeepCloneable<ImageMetadata>
this.IccProfile = other.IccProfile?.DeepClone();
this.IptcProfile = other.IptcProfile?.DeepClone();
this.XmpProfile = other.XmpProfile?.DeepClone();
this.CicpProfile = other.CicpProfile?.DeepClone();
// NOTE: This clone is actually shallow but we share the same format
// instances for all images in the configuration.
@ -157,6 +159,11 @@ public sealed class ImageMetadata : IDeepCloneable<ImageMetadata>
/// </summary>
public IptcProfile? IptcProfile { get; set; }
/// <summary>
/// Gets or sets the CICP profile.
/// </summary>
public CicpProfile? CicpProfile { get; set; }
/// <summary>
/// Gets the original format, if any, the image was decode from.
/// </summary>

72
src/ImageSharp/Metadata/Profiles/CICP/CicpProfile.cs

@ -0,0 +1,72 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Metadata.Profiles.Cicp;
/// <summary>
/// Represents a Cicp profile as per ITU-T H.273 / ISO/IEC 23091-2_2019 providing access to color space information
/// </summary>
public sealed class CicpProfile : IDeepCloneable<CicpProfile>
{
/// <summary>
/// Initializes a new instance of the <see cref="CicpProfile"/> class.
/// </summary>
public CicpProfile()
: this(2, 2, 2, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="CicpProfile"/> class.
/// </summary>
/// <param name="colorPrimaries">The color primaries as number according to ITU-T H.273 / ISO/IEC 23091-2_2019.</param>
/// <param name="transferCharacteristics">The transfer characteristics as number according to ITU-T H.273 / ISO/IEC 23091-2_2019.</param>
/// <param name="matrixCoefficients">The matrix coefficients as number according to ITU-T H.273 / ISO/IEC 23091-2_2019.</param>
/// <param name="fullRange">The full range flag, or null if unknown.</param>
public CicpProfile(byte colorPrimaries, byte transferCharacteristics, byte matrixCoefficients, bool? fullRange)
{
this.ColorPrimaries = Enum.IsDefined(typeof(CicpColorPrimaries), colorPrimaries) ? (CicpColorPrimaries)colorPrimaries : CicpColorPrimaries.Unspecified;
this.TransferCharacteristics = Enum.IsDefined(typeof(CicpTransferCharacteristics), transferCharacteristics) ? (CicpTransferCharacteristics)transferCharacteristics : CicpTransferCharacteristics.Unspecified;
this.MatrixCoefficients = Enum.IsDefined(typeof(CicpMatrixCoefficients), matrixCoefficients) ? (CicpMatrixCoefficients)matrixCoefficients : CicpMatrixCoefficients.Unspecified;
this.FullRange = fullRange ?? (this.MatrixCoefficients == CicpMatrixCoefficients.Identity);
}
/// <summary>
/// Initializes a new instance of the <see cref="CicpProfile"/> class
/// by making a copy from another CICP profile.
/// </summary>
/// <param name="other">The other CICP profile, where the clone should be made from.</param>
/// <exception cref="ArgumentNullException"><paramref name="other"/> is null.</exception>>
private CicpProfile(CicpProfile other)
{
Guard.NotNull(other, nameof(other));
this.ColorPrimaries = other.ColorPrimaries;
this.TransferCharacteristics = other.TransferCharacteristics;
this.MatrixCoefficients = other.MatrixCoefficients;
this.FullRange = other.FullRange;
}
/// <summary>
/// Gets or sets the color primaries
/// </summary>
public CicpColorPrimaries ColorPrimaries { get; set; }
/// <summary>
/// Gets or sets the transfer characteristics
/// </summary>
public CicpTransferCharacteristics TransferCharacteristics { get; set; }
/// <summary>
/// Gets or sets the matrix coefficients
/// </summary>
public CicpMatrixCoefficients MatrixCoefficients { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the colors use the full numeric range
/// </summary>
public bool FullRange { get; set; }
/// <inheritdoc/>
public CicpProfile DeepClone() => new(this);
}

86
src/ImageSharp/Metadata/Profiles/CICP/Enums/CicpColorPrimaries.cs

@ -0,0 +1,86 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Metadata.Profiles.Cicp;
#pragma warning disable CA1707 // Underscores in enum members
/// <summary>
/// Color primaries according to ITU-T H.273 / ISO/IEC 23091-2_2019 subclause 8.1
/// </summary>
public enum CicpColorPrimaries : byte
{
/// <summary>
/// Rec. ITU-R BT.709-6
/// IEC 61966-2-1 sRGB or sYCC
/// IEC 61966-2-4
/// SMPTE RP 177 (1993) Annex B
/// </summary>
ItuRBt709_6 = 1,
/// <summary>
/// Image characteristics are unknown or are determined by the application.
/// </summary>
Unspecified = 2,
/// <summary>
/// Rec. ITU-R BT.470-6 System M (historical)
/// </summary>
ItuRBt470_6M = 4,
/// <summary>
/// Rec. ITU-R BT.601-7 625
/// Rec. ITU-R BT.1700-0 625 PAL and 625 SECAM
/// </summary>
ItuRBt601_7_625 = 5,
/// <summary>
/// Rec. ITU-R BT.601-7 525
/// Rec. ITU-R BT.1700-0 NTSC
/// SMPTE ST 170 (2004)
/// (functionally the same as the value 7)
/// </summary>
ItuRBt601_7_525 = 6,
/// <summary>
/// SMPTE ST 240 (1999)
/// (functionally the same as the value 6)
/// </summary>
SmpteSt240 = 7,
/// <summary>
/// Generic film (colour filters using Illuminant C)
/// </summary>
GenericFilm = 8,
/// <summary>
/// Rec. ITU-R BT.2020-2
/// Rec. ITU-R BT.2100-2
/// </summary>
ItuRBt2020_2 = 9,
/// <summary>
/// SMPTE ST 428-1 (2019)
/// (CIE 1931 XYZ as in ISO 11664-1)
/// </summary>
SmpteSt428_1 = 10,
/// <summary>
/// SMPTE RP 431-2 (2011)
/// DCI P3
/// </summary>
SmpteRp431_2 = 11,
/// <summary>
/// SMPTE ST 432-1 (2010)
/// P3 D65 / Display P3
/// </summary>
SmpteEg432_1 = 12,
/// <summary>
/// EBU Tech.3213-E
/// </summary>
EbuTech3213E = 22,
}
#pragma warning restore CA1707 // Underscores in enum members

96
src/ImageSharp/Metadata/Profiles/CICP/Enums/CicpMatrixCoefficients.cs

@ -0,0 +1,96 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Metadata.Profiles.Cicp;
#pragma warning disable CA1707 // Underscores in enum members
/// <summary>
/// Matrix coefficients according to ITU-T H.273 / ISO/IEC 23091-2_2019 subclause 8.3
/// </summary>
public enum CicpMatrixCoefficients : byte
{
/// <summary>
/// The identity matrix.
/// IEC 61966-2-1 sRGB
/// SMPTE ST 428-1 (2019)
/// </summary>
Identity = 0,
/// <summary>
/// Rec. ITU-R BT.709-6
/// IEC 61966-2-4 xvYCC709
/// SMPTE RP 177 (1993) Annex B
/// </summary>
ItuRBt709_6 = 1,
/// <summary>
/// Image characteristics are unknown or are determined by the application.
/// </summary>
Unspecified = 2,
/// <summary>
/// FCC Title 47 Code of Federal Regulations 73.682 (a) (20)
/// </summary>
Fcc47 = 4,
/// <summary>
/// Rec. ITU-R BT.601-7 625
/// Rec. ITU-R BT.1700-0 625 PAL and 625 SECAM
/// IEC 61966-2-1 sYCC
/// IEC 61966-2-4 xvYCC601
/// (functionally the same as the value 6)
/// </summary>
ItuRBt601_7_625 = 5,
/// <summary>
/// Rec. ITU-R BT.601-7 525
/// Rec. ITU-R BT.1700-0 NTSC
/// SMPTE ST 170 (2004)
/// (functionally the same as the value 5)
/// </summary>
ItuRBt601_7_525 = 6,
/// <summary>
/// SMPTE ST 240 (1999)
/// </summary>
SmpteSt240 = 7,
/// <summary>
/// YCgCo
/// </summary>
YCgCo = 8,
/// <summary>
/// Rec. ITU-R BT.2020-2 (non-constant luminance)
/// Rec. ITU-R BT.2100-2 Y′CbCr
/// </summary>
ItuRBt2020_2_Ncl = 9,
/// <summary>
/// Rec. ITU-R BT.2020-2 (constant luminance)
/// </summary>
ItuRBt2020_2_Cl = 10,
/// <summary>
/// SMPTE ST 2085 (2015)
/// </summary>
SmpteSt2085 = 11,
/// <summary>
/// Chromaticity-derived non-constant luminance system
/// </summary>
ChromaDerivedNcl = 12,
/// <summary>
/// Chromaticity-derived constant luminance system
/// </summary>
ChromaDerivedCl = 13,
/// <summary>
/// Rec. ITU-R BT.2100-2 ICtCp
/// </summary>
ICtCp = 14,
}
#pragma warning restore CA1707 // Underscores in enum members

109
src/ImageSharp/Metadata/Profiles/CICP/Enums/CicpTransferCharacteristics.cs

@ -0,0 +1,109 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Metadata.Profiles.Cicp;
#pragma warning disable CA1707 // Underscores in enum values
/// <summary>
/// Transfer characteristics according to ITU-T H.273 / ISO/IEC 23091-2_2019 subclause 8.2
/// /// </summary>
public enum CicpTransferCharacteristics : byte
{
/// <summary>
/// Rec. ITU-R BT.709-6
/// (functionally the same as the values 6, 14 and 15)
/// </summary>
ItuRBt709_6 = 1,
/// <summary>
/// Image characteristics are unknown or are determined by the application.
/// </summary>
Unspecified = 2,
/// <summary>
/// Assumed display gamma 2.2
/// Rec. ITU-R BT.1700-0 625 PAL and 625 SECAM
/// </summary>
Gamma2_2 = 4,
/// <summary>
/// Assumed display gamma 2.8
/// Rec. ITU-R BT.470-6 System B, G (historical)
/// </summary>
Gamma2_8 = 5,
/// <summary>
/// Rec. ITU-R BT.601-7 525 or 625
/// Rec. ITU-R BT.1700-0 NTSC
/// SMPTE ST 170 (2004)
/// (functionally the same as the values 1, 14 and 15)
/// </summary>
ItuRBt601_7 = 6,
/// <summary>
/// SMPTE ST 240 (1999)
/// </summary>
SmpteSt240 = 7,
/// <summary>
/// Linear transfer characteristics
/// </summary>
Linear = 8,
/// <summary>
/// Logarithmic transfer characteristic (100:1 range)
/// </summary>
Log100 = 9,
/// <summary>
/// Logarithmic transfer characteristic (100 * Sqrt( 10 ) : 1 range)
/// </summary>
Log100Sqrt = 10,
/// <summary>
/// IEC 61966-2-4
/// </summary>
Iec61966_2_4 = 11,
/// <summary>
/// Rec. ITU-R BT.1361-0 extended colour gamut system (historical)
/// </summary>
ItuRBt1361_0 = 12,
/// <summary>
/// IEC 61966-2-1 sRGB or sYCC / Display P3
/// </summary>
Iec61966_2_1 = 13,
/// <summary>
/// Rec. ITU-R BT.2020-2 (10-bit system)
/// (functionally the same as the values 1, 6 and 15)
/// </summary>
ItuRBt2020_2_10bit = 14,
/// <summary>
/// Rec. ITU-R BT.2020-2 (12-bit system)
/// (functionally the same as the values 1, 6 and 14)
/// /// </summary>
ItuRBt2020_2_12bit = 15,
/// <summary>
/// SMPTE ST 2084 (2014) for 10-, 12-, 14- and 16-bit systems
/// Rec. ITU-R BT.2100-2 perceptual quantization (PQ) system
/// </summary>
SmpteSt2084 = 16,
/// <summary>
/// SMPTE ST 428-1 (2019)
/// </summary>
SmpteSt428_1 = 17,
/// <summary>
/// ARIB STD-B67 (2015)
/// Rec. ITU-R BT.2100-2 hybrid log-gamma (HLG) system
/// </summary>
AribStdB67 = 18,
}
#pragma warning restore CA1707 // Underscores in enum members

BIN
src/ImageSharp/Metadata/Profiles/CICP/T-REC-H.273-202107-S!!PDF-E.pdf

Binary file not shown.

5
src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgb24.PixelOperations.cs

@ -40,6 +40,9 @@ public partial struct Rgb24
Span<float> greenChannel,
Span<float> blueChannel,
ReadOnlySpan<Rgb24> source)
=> SimdUtils.UnpackToRgbPlanes(redChannel, greenChannel, blueChannel, source);
{
GuardUnpackIntoRgbPlanes(redChannel, greenChannel, blueChannel, source);
SimdUtils.UnpackToRgbPlanes(redChannel, greenChannel, blueChannel, source);
}
}
}

16
src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs

@ -165,7 +165,7 @@ public partial class PixelOperations<TPixel>
}
/// <summary>
/// Bulk operation that packs 3 seperate RGB channels to <paramref name="destination"/>.
/// Bulk operation that packs 3 separate RGB channels to <paramref name="destination"/>.
/// The destination must have a padding of 3.
/// </summary>
/// <param name="redChannel">A <see cref="ReadOnlySpan{T}"/> to the red values.</param>
@ -198,7 +198,7 @@ public partial class PixelOperations<TPixel>
/// <summary>
/// Bulk operation that unpacks pixels from <paramref name="source"/>
/// into 3 seperate RGB channels. The destination must have a padding of 3.
/// into 3 separate RGB channels.
/// </summary>
/// <param name="redChannel">A <see cref="ReadOnlySpan{T}"/> to the red values.</param>
/// <param name="greenChannel">A <see cref="ReadOnlySpan{T}"/> to the green values.</param>
@ -210,7 +210,9 @@ public partial class PixelOperations<TPixel>
Span<float> blueChannel,
ReadOnlySpan<TPixel> source)
{
int count = redChannel.Length;
GuardUnpackIntoRgbPlanes(redChannel, greenChannel, blueChannel, source);
int count = source.Length;
Rgba32 rgba32 = default;
@ -227,6 +229,14 @@ public partial class PixelOperations<TPixel>
}
}
[MethodImpl(InliningOptions.ShortMethod)]
internal static void GuardUnpackIntoRgbPlanes(Span<float> redChannel, Span<float> greenChannel, Span<float> blueChannel, ReadOnlySpan<TPixel> source)
{
Guard.IsTrue(greenChannel.Length == redChannel.Length, nameof(greenChannel), "Channels must be of same size!");
Guard.IsTrue(blueChannel.Length == redChannel.Length, nameof(blueChannel), "Channels must be of same size!");
Guard.IsTrue(source.Length <= redChannel.Length, nameof(source), "'source' span should not be bigger than the destination channels!");
}
[MethodImpl(InliningOptions.ShortMethod)]
internal static void GuardPackFromRgbPlanes(ReadOnlySpan<byte> greenChannel, ReadOnlySpan<byte> blueChannel, Span<TPixel> destination, int count)
{

11
tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs

@ -353,4 +353,15 @@ public partial class JpegDecoderTests
Assert.Equal(65503, image.Width);
Assert.Equal(65503, image.Height);
}
// https://github.com/SixLabors/ImageSharp/issues/2517
[Theory]
[WithFile(TestImages.Jpeg.Issues.Issue2517, PixelTypes.Rgba32)]
public void Issue2517_DecodeWorks<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(JpegDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider);
}
}

36
tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs

@ -3,6 +3,7 @@
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Tests.TestUtilities;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
@ -87,7 +88,7 @@ public partial class JpegEncoderTests
{
using Image<TPixel> image = provider.GetImage();
var encoder = new JpegEncoder
JpegEncoder encoder = new()
{
Quality = quality,
ColorType = colorType,
@ -164,8 +165,8 @@ public partial class JpegEncoderTests
[InlineData(JpegEncodingColor.YCbCrRatio444)]
public async Task Encode_IsCancellable(JpegEncodingColor colorType)
{
var cts = new CancellationTokenSource();
using var pausedStream = new PausedStream(new MemoryStream());
CancellationTokenSource cts = new();
using PausedStream pausedStream = new(new MemoryStream());
pausedStream.OnWaiting(s =>
{
// after some writing
@ -181,14 +182,37 @@ public partial class JpegEncoderTests
}
});
using var image = new Image<Rgba32>(5000, 5000);
using Image<Rgba32> image = new(5000, 5000);
await Assert.ThrowsAsync<TaskCanceledException>(async () =>
{
var encoder = new JpegEncoder() { ColorType = colorType };
JpegEncoder encoder = new() { ColorType = colorType };
await image.SaveAsync(pausedStream, encoder, cts.Token);
});
}
// https://github.com/SixLabors/ImageSharp/issues/2595
[Theory]
[WithFile(TestImages.Jpeg.Baseline.ForestBridgeDifferentComponentsQuality, PixelTypes.Bgra32)]
[WithFile(TestImages.Jpeg.Baseline.ForestBridgeDifferentComponentsQuality, PixelTypes.Rgb24)]
public static void Issue2595<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage();
image.Mutate(x => x.Crop(132, 1606));
int[] quality = new int[] { 100, 50 };
JpegEncodingColor[] colors = new[] { JpegEncodingColor.YCbCrRatio444, JpegEncodingColor.YCbCrRatio420 };
for (int i = 0; i < quality.Length; i++)
{
int q = quality[i];
for (int j = 0; j < colors.Length; j++)
{
JpegEncodingColor c = colors[j];
image.VerifyEncoder(provider, "jpeg", $"{q}-{c}", new JpegEncoder() { Quality = q, ColorType = c }, GetComparer(q, c));
}
}
}
/// <summary>
/// Anton's SUPER-SCIENTIFIC tolerance threshold calculation
/// </summary>
@ -225,7 +249,7 @@ public partial class JpegEncoderTests
{
using Image<TPixel> image = provider.GetImage();
var encoder = new JpegEncoder
JpegEncoder encoder = new()
{
Quality = quality,
ColorType = colorType

1
tests/ImageSharp.Tests/Formats/Png/PngChunkTypeTests.cs

@ -29,6 +29,7 @@ public class PngChunkTypeTests
Assert.Equal(PngChunkType.Background, GetType("bKGD"));
Assert.Equal(PngChunkType.EmbeddedColorProfile, GetType("iCCP"));
Assert.Equal(PngChunkType.StandardRgbColourSpace, GetType("sRGB"));
Assert.Equal(PngChunkType.Cicp, GetType("cICP"));
Assert.Equal(PngChunkType.SignificantBits, GetType("sBIT"));
Assert.Equal(PngChunkType.Histogram, GetType("hIST"));
Assert.Equal(PngChunkType.SuggestedPalette, GetType("sPLT"));

29
tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs

@ -4,6 +4,7 @@
using System.Runtime.Intrinsics.X86;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Webp;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tests.TestUtilities;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
@ -493,4 +494,32 @@ public class WebpDecoderTests
[Fact]
public void DecodeLossyWithComplexFilterTest_WithoutHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunDecodeLossyWithComplexFilterTest, HwIntrinsics.DisableHWIntrinsic);
[Theory]
[InlineData(Lossy.BikeWithExif)]
public void Decode_VerifyRatio(string imagePath)
{
TestFile testFile = TestFile.Create(imagePath);
using MemoryStream stream = new(testFile.Bytes, false);
using Image image = WebpDecoder.Instance.Decode(DecoderOptions.Default, stream);
ImageMetadata meta = image.Metadata;
Assert.Equal(37.8, meta.HorizontalResolution);
Assert.Equal(37.8, meta.VerticalResolution);
Assert.Equal(PixelResolutionUnit.PixelsPerCentimeter, meta.ResolutionUnits);
}
[Theory]
[InlineData(Lossy.BikeWithExif)]
public void Identify_VerifyRatio(string imagePath)
{
TestFile testFile = TestFile.Create(imagePath);
using MemoryStream stream = new(testFile.Bytes, false);
ImageInfo image = WebpDecoder.Instance.Identify(DecoderOptions.Default, stream);
ImageMetadata meta = image.Metadata;
Assert.Equal(37.8, meta.HorizontalResolution);
Assert.Equal(37.8, meta.VerticalResolution);
Assert.Equal(PixelResolutionUnit.PixelsPerCentimeter, meta.ResolutionUnits);
}
}

82
tests/ImageSharp.Tests/Metadata/Profiles/CICP/CicpProfileTests.cs

@ -0,0 +1,82 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.Metadata.Profiles.Cicp;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Cicp;
public class CicpProfileTests
{
[Theory]
[WithFile(TestImages.Png.AdamHeadsHlg, PixelTypes.Rgba64)]
public async Task ReadCicpMetadata_FromPng_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = await provider.GetImageAsync(PngDecoder.Instance);
CicpProfile actual = image.Metadata.CicpProfile ?? image.Frames.RootFrame.Metadata.CicpProfile;
CicpProfileContainsExpectedValues(actual);
}
[Fact]
public void WritingPng_PreservesCicpProfile()
{
// arrange
using var image = new Image<Rgba32>(1, 1);
var original = CreateCicpProfile();
image.Metadata.CicpProfile = original;
var encoder = new PngEncoder();
// act
using Image<Rgba32> reloadedImage = WriteAndRead(image, encoder);
// assert
CicpProfile actual = reloadedImage.Metadata.CicpProfile ?? reloadedImage.Frames.RootFrame.Metadata.CicpProfile;
CicpProfileIsValidAndEqual(actual, original);
}
private static void CicpProfileContainsExpectedValues(CicpProfile cicp)
{
Assert.NotNull(cicp);
Assert.Equal(CicpColorPrimaries.ItuRBt2020_2, cicp.ColorPrimaries);
Assert.Equal(CicpTransferCharacteristics.AribStdB67, cicp.TransferCharacteristics);
Assert.Equal(CicpMatrixCoefficients.Identity, cicp.MatrixCoefficients);
Assert.True(cicp.FullRange);
}
private static CicpProfile CreateCicpProfile()
{
var profile = new CicpProfile()
{
ColorPrimaries = CicpColorPrimaries.ItuRBt2020_2,
TransferCharacteristics = CicpTransferCharacteristics.SmpteSt2084,
MatrixCoefficients = CicpMatrixCoefficients.Identity,
FullRange = true,
};
return profile;
}
private static void CicpProfileIsValidAndEqual(CicpProfile actual, CicpProfile original)
{
Assert.NotNull(actual);
Assert.Equal(actual.ColorPrimaries, original.ColorPrimaries);
Assert.Equal(actual.TransferCharacteristics, original.TransferCharacteristics);
Assert.Equal(actual.MatrixCoefficients, original.MatrixCoefficients);
Assert.Equal(actual.FullRange, original.FullRange);
}
private static Image<Rgba32> WriteAndRead(Image<Rgba32> image, IImageEncoder encoder)
{
using (var memStream = new MemoryStream())
{
image.Save(memStream, encoder);
image.Dispose();
memStream.Position = 0;
return Image.Load<Rgba32>(memStream);
}
}
}

2
tests/ImageSharp.Tests/TestImages.cs

@ -61,6 +61,7 @@ public static class TestImages
public const string TestPattern31x31 = "Png/testpattern31x31.png";
public const string TestPattern31x31HalfTransparent = "Png/testpattern31x31-halftransparent.png";
public const string XmpColorPalette = "Png/xmp-colorpalette.png";
public const string AdamHeadsHlg = "Png/adamHeadsHLG.png";
// Animated
// https://philip.html5.org/tests/apng/tests.html
@ -307,6 +308,7 @@ public static class TestImages
public const string Issue2478_JFXX = "Jpg/issues/issue-2478-jfxx.jpg";
public const string Issue2564 = "Jpg/issues/issue-2564.jpg";
public const string HangBadScan = "Jpg/issues/Hang_C438A851.jpg";
public const string Issue2517 = "Jpg/issues/issue2517-bad-d7.jpg";
public static class Fuzz
{

3
tests/Images/Input/Jpg/issues/issue2517-bad-d7.jpg

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:650a933db9c4f76fa3e6a8ed35d061a5740c613acd1026d99461eb014d8947b2
size 179015

3
tests/Images/Input/Png/adamHeadsHLG.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8c50691da3b3af21ff4f8fc30f1313bc412b84fb0a07a5bf3b8b14eae7581ade
size 201440
Loading…
Cancel
Save