diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsPixelCompatible.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsPixelCompatible.cs index 5f3d42afb..2780f04ba 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsPixelCompatible.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsPixelCompatible.cs @@ -47,7 +47,7 @@ internal static class ColorProfileConverterExtensionsPixelCompatible throw new InvalidOperationException("Target ICC profile is missing."); } - // Process the rows in parallel chnks, the converter itself is thread safe. + // Process the rows in parallel chunks, the converter itself is thread safe. source.Mutate(o => o.ProcessPixelRowsAsVector4( row => { @@ -64,7 +64,7 @@ internal static class ColorProfileConverterExtensionsPixelCompatible for (int i = 0; i < rgbSpan.Length; i++) { Vector3 rgb = rgbSpan[i].AsVector3Unsafe(); - Unsafe.As(ref Unsafe.Add(ref rowRef, i)) = rgb; + Unsafe.As(ref Unsafe.Add(ref rowRef, (uint)i)) = rgb; } }, PixelConversionModifiers.Scale)); diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index f0d3f3339..bff4d30ee 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -230,12 +230,6 @@ internal sealed class PngDecoderCore : ImageDecoderCore this.InitializeFrame(previousFrameControl, currentFrameControl.Value, image, previousFrame, out currentFrame); - if (!this.Options.TryGetIccProfileForColorConversion(metadata.IccProfile, out IccProfile? iccProfile)) - { - // TODO: Rework this. We need to preserve metadata - // metadata.IccProfile = null; - } - this.currentStream.Position += 4; this.ReadScanlines( chunk.Length - 4, @@ -243,7 +237,6 @@ internal sealed class PngDecoderCore : ImageDecoderCore pngMetadata, this.ReadNextFrameDataChunk, currentFrameControl.Value, - iccProfile, cancellationToken); // if current frame dispose is restore to previous, then from future frame's perspective, it never happened @@ -268,19 +261,12 @@ internal sealed class PngDecoderCore : ImageDecoderCore AssignColorPalette(this.palette, this.paletteAlpha, pngMetadata); } - if (!this.Options.TryGetIccProfileForColorConversion(metadata.IccProfile, out IccProfile? iccProfile)) - { - // TODO: Rework this. We need to preserve metadata - // metadata.IccProfile = null; - } - this.ReadScanlines( chunk.Length, image.Frames.RootFrame, pngMetadata, this.ReadNextDataChunk, currentFrameControl.Value, - iccProfile, cancellationToken); if (pngMetadata.AnimateRootFrame) { @@ -764,7 +750,6 @@ internal sealed class PngDecoderCore : ImageDecoderCore /// The png metadata /// A delegate to get more data from the inner stream for . /// The frame control - /// Optional ICC profile for color conversion. /// The cancellation token. private void ReadScanlines( int chunkLength, @@ -772,7 +757,6 @@ internal sealed class PngDecoderCore : ImageDecoderCore PngMetadata pngMetadata, Func getData, in FrameControl frameControl, - IccProfile? iccProfile, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { @@ -786,11 +770,11 @@ internal sealed class PngDecoderCore : ImageDecoderCore if (this.header.InterlaceMethod is PngInterlaceMode.Adam7) { - this.DecodeInterlacedPixelData(frameControl, dataStream, image, pngMetadata, iccProfile, cancellationToken); + this.DecodeInterlacedPixelData(frameControl, dataStream, image, pngMetadata, cancellationToken); } else { - this.DecodePixelData(frameControl, dataStream, image, pngMetadata, iccProfile, cancellationToken); + this.DecodePixelData(frameControl, dataStream, image, pngMetadata, cancellationToken); } } @@ -802,14 +786,12 @@ internal sealed class PngDecoderCore : ImageDecoderCore /// The compressed pixel data stream. /// The image frame to decode to. /// The png metadata - /// Optional ICC profile for color conversion. /// The CancellationToken private void DecodePixelData( FrameControl frameControl, DeflateStream compressedStream, ImageFrame imageFrame, PngMetadata pngMetadata, - IccProfile? iccProfile, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { @@ -876,7 +858,7 @@ internal sealed class PngDecoderCore : ImageDecoderCore break; } - this.ProcessDefilteredScanline(frameControl, currentRow, scanSpan, imageFrame, pngMetadata, blendRowBuffer, iccProfile); + this.ProcessDefilteredScanline(frameControl, currentRow, scanSpan, imageFrame, pngMetadata, blendRowBuffer); this.SwapScanlineBuffers(); currentRow++; } @@ -894,14 +876,12 @@ internal sealed class PngDecoderCore : ImageDecoderCore /// The compressed pixel data stream. /// The current image frame. /// The png metadata. - /// Optional ICC profile for color conversion. /// The cancellation token. private void DecodeInterlacedPixelData( in FrameControl frameControl, DeflateStream compressedStream, ImageFrame imageFrame, PngMetadata pngMetadata, - IccProfile? iccProfile, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { @@ -992,7 +972,6 @@ internal sealed class PngDecoderCore : ImageDecoderCore rowSpan, pngMetadata, blendRowBuffer, - iccProfile, pixelOffset: Adam7.FirstColumn[pass], increment: Adam7.ColumnIncrement[pass]); @@ -1031,15 +1010,13 @@ internal sealed class PngDecoderCore : ImageDecoderCore /// The image /// The png metadata. /// A span used to temporarily hold the decoded row pixel data for alpha blending. - /// Optional ICC profile for color conversion. private void ProcessDefilteredScanline( in FrameControl frameControl, int currentRow, ReadOnlySpan scanline, ImageFrame pixels, PngMetadata pngMetadata, - Span blendRowBuffer, - IccProfile? iccProfile) + Span blendRowBuffer) where TPixel : unmanaged, IPixel { Span destination = pixels.PixelBuffer.DangerousGetRowSpan(currentRow); @@ -1073,8 +1050,7 @@ internal sealed class PngDecoderCore : ImageDecoderCore in frameControl, scanlineSpan, rowSpan, - pngMetadata.TransparentColor, - iccProfile); + pngMetadata.TransparentColor); break; @@ -1085,8 +1061,7 @@ internal sealed class PngDecoderCore : ImageDecoderCore scanlineSpan, rowSpan, (uint)this.bytesPerPixel, - (uint)this.bytesPerSample, - iccProfile); + (uint)this.bytesPerSample); break; @@ -1095,8 +1070,7 @@ internal sealed class PngDecoderCore : ImageDecoderCore in frameControl, scanlineSpan, rowSpan, - pngMetadata.ColorTable, - iccProfile); + pngMetadata.ColorTable); break; @@ -1109,8 +1083,7 @@ internal sealed class PngDecoderCore : ImageDecoderCore rowSpan, this.bytesPerPixel, this.bytesPerSample, - pngMetadata.TransparentColor, - iccProfile); + pngMetadata.TransparentColor); break; @@ -1122,8 +1095,7 @@ internal sealed class PngDecoderCore : ImageDecoderCore scanlineSpan, rowSpan, this.bytesPerPixel, - this.bytesPerSample, - iccProfile); + this.bytesPerSample); break; } @@ -1134,8 +1106,6 @@ internal sealed class PngDecoderCore : ImageDecoderCore PixelOperations.Instance.GetPixelBlender(PixelColorBlendingMode.Normal, PixelAlphaCompositionMode.SrcOver); blender.Blend(this.configuration, destination, destination, rowSpan, 1F); } - - // TODO: Here is where we would perform ICC color conversion if needed over the 'destination' span. } finally { @@ -1152,7 +1122,6 @@ internal sealed class PngDecoderCore : ImageDecoderCore /// The current image row. /// The png metadata. /// A span used to temporarily hold the decoded row pixel data for alpha blending. - /// Optional ICC profile for color conversion. /// The column start index. Always 0 for none interlaced images. /// The column increment. Always 1 for none interlaced images. private void ProcessInterlacedDefilteredScanline( @@ -1161,7 +1130,6 @@ internal sealed class PngDecoderCore : ImageDecoderCore Span destination, PngMetadata pngMetadata, Span blendRowBuffer, - IccProfile? iccProfile, int pixelOffset = 0, int increment = 1) where TPixel : unmanaged, IPixel @@ -1196,8 +1164,7 @@ internal sealed class PngDecoderCore : ImageDecoderCore rowSpan, (uint)pixelOffset, (uint)increment, - pngMetadata.TransparentColor, - iccProfile); + pngMetadata.TransparentColor); break; @@ -1210,8 +1177,7 @@ internal sealed class PngDecoderCore : ImageDecoderCore (uint)pixelOffset, (uint)increment, (uint)this.bytesPerPixel, - (uint)this.bytesPerSample, - iccProfile); + (uint)this.bytesPerSample); break; @@ -1222,8 +1188,7 @@ internal sealed class PngDecoderCore : ImageDecoderCore rowSpan, (uint)pixelOffset, (uint)increment, - pngMetadata.ColorTable, - iccProfile); + pngMetadata.ColorTable); break; @@ -1238,8 +1203,7 @@ internal sealed class PngDecoderCore : ImageDecoderCore (uint)increment, this.bytesPerPixel, this.bytesPerSample, - pngMetadata.TransparentColor, - iccProfile); + pngMetadata.TransparentColor); break; @@ -1253,8 +1217,7 @@ internal sealed class PngDecoderCore : ImageDecoderCore (uint)pixelOffset, (uint)increment, this.bytesPerPixel, - this.bytesPerSample, - iccProfile); + this.bytesPerSample); break; } diff --git a/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs b/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs index ca4eaa58d..33ba58f54 100644 --- a/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs +++ b/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs @@ -1,15 +1,10 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.Buffers; using System.Buffers.Binary; -using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.ColorProfiles; -using SixLabors.ImageSharp.ColorProfiles.Icc; using SixLabors.ImageSharp.Formats.Png.Chunks; -using SixLabors.ImageSharp.Metadata.Profiles.Icc; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Png; @@ -25,8 +20,7 @@ internal static class PngScanlineProcessor in FrameControl frameControl, ReadOnlySpan scanlineSpan, Span rowSpan, - Color? transparentColor, - IccProfile? iccProfile) + Color? transparentColor) where TPixel : unmanaged, IPixel => ProcessInterlacedGrayscaleScanline( bitDepth, @@ -35,8 +29,7 @@ internal static class PngScanlineProcessor rowSpan, 0, 1, - transparentColor, - iccProfile); + transparentColor); public static void ProcessInterlacedGrayscaleScanline( int bitDepth, @@ -45,11 +38,9 @@ internal static class PngScanlineProcessor Span rowSpan, uint pixelOffset, uint increment, - Color? transparentColor, - IccProfile? iccProfile) + Color? transparentColor) where TPixel : unmanaged, IPixel { - // FIXME-icc uint offset = pixelOffset + frameControl.XOffset; ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan); ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan); @@ -107,8 +98,7 @@ internal static class PngScanlineProcessor ReadOnlySpan scanlineSpan, Span rowSpan, uint bytesPerPixel, - uint bytesPerSample, - IccProfile? iccProfile) + uint bytesPerSample) where TPixel : unmanaged, IPixel => ProcessInterlacedGrayscaleWithAlphaScanline( bitDepth, @@ -118,8 +108,7 @@ internal static class PngScanlineProcessor 0, 1, bytesPerPixel, - bytesPerSample, - iccProfile); + bytesPerSample); public static void ProcessInterlacedGrayscaleWithAlphaScanline( int bitDepth, @@ -129,11 +118,9 @@ internal static class PngScanlineProcessor uint pixelOffset, uint increment, uint bytesPerPixel, - uint bytesPerSample, - IccProfile? iccProfile) + uint bytesPerSample) where TPixel : unmanaged, IPixel { - // FIXME-icc uint offset = pixelOffset + frameControl.XOffset; ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan); ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan); @@ -166,8 +153,7 @@ internal static class PngScanlineProcessor in FrameControl frameControl, ReadOnlySpan scanlineSpan, Span rowSpan, - ReadOnlyMemory? palette, - IccProfile? iccProfile) + ReadOnlyMemory? palette) where TPixel : unmanaged, IPixel => ProcessInterlacedPaletteScanline( frameControl, @@ -175,8 +161,7 @@ internal static class PngScanlineProcessor rowSpan, 0, 1, - palette, - iccProfile); + palette); public static void ProcessInterlacedPaletteScanline( in FrameControl frameControl, @@ -184,11 +169,9 @@ internal static class PngScanlineProcessor Span rowSpan, uint pixelOffset, uint increment, - ReadOnlyMemory? palette, - IccProfile? iccProfile) + ReadOnlyMemory? palette) where TPixel : unmanaged, IPixel { -// FIXME-icc if (palette is null) { PngThrowHelper.ThrowMissingPalette(); @@ -215,8 +198,7 @@ internal static class PngScanlineProcessor Span rowSpan, int bytesPerPixel, int bytesPerSample, - Color? transparentColor, - IccProfile? iccProfile) + Color? transparentColor) where TPixel : unmanaged, IPixel => ProcessInterlacedRgbScanline( configuration, @@ -228,8 +210,7 @@ internal static class PngScanlineProcessor 1, bytesPerPixel, bytesPerSample, - transparentColor, - iccProfile); + transparentColor); public static void ProcessInterlacedRgbScanline( Configuration configuration, @@ -241,11 +222,9 @@ internal static class PngScanlineProcessor uint increment, int bytesPerPixel, int bytesPerSample, - Color? transparentColor, - IccProfile? iccProfile) + Color? transparentColor) where TPixel : unmanaged, IPixel { - // FIXME-icc uint offset = pixelOffset + frameControl.XOffset; ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan); ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan); @@ -323,8 +302,7 @@ internal static class PngScanlineProcessor ReadOnlySpan scanlineSpan, Span rowSpan, int bytesPerPixel, - int bytesPerSample, - IccProfile? iccProfile) + int bytesPerSample) where TPixel : unmanaged, IPixel => ProcessInterlacedRgbaScanline( configuration, @@ -335,8 +313,7 @@ internal static class PngScanlineProcessor 0, 1, bytesPerPixel, - bytesPerSample, - iccProfile); + bytesPerSample); public static void ProcessInterlacedRgbaScanline( Configuration configuration, @@ -347,104 +324,43 @@ internal static class PngScanlineProcessor uint pixelOffset, uint increment, int bytesPerPixel, - int bytesPerSample, - IccProfile? iccProfile) + int bytesPerSample) where TPixel : unmanaged, IPixel { uint offset = pixelOffset + frameControl.XOffset; ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan); - if (iccProfile != null) + if (bitDepth == 16) { - ColorConversionOptions options = new() - { - SourceIccProfile = iccProfile, - TargetIccProfile = CompactSrgbV4Profile.Profile, - MemoryAllocator = configuration.MemoryAllocator, - }; - - ColorProfileConverter converter = new(options); - using IMemoryOwner rgbBuffer = configuration.MemoryAllocator.Allocate((int)(frameControl.XMax - offset)); - Span rgbPacked = rgbBuffer.Memory.Span; - ref Rgb rgbPackedRef = ref MemoryMarshal.GetReference(rgbPacked); - using IMemoryOwner alphaBuffer = configuration.MemoryAllocator.Allocate((int)(frameControl.XMax - offset)); - Span alphaPacked = alphaBuffer.Memory.Span; - ref float alphaPackedRef = ref MemoryMarshal.GetReference(alphaPacked); - - if (bitDepth == 16) - { - int o = 0; - for (int i = 0; i < rgbPacked.Length; o += bytesPerPixel, i++) - { - ushort r = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, bytesPerSample)); - ushort g = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample)); - ushort b = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + (2 * bytesPerSample), bytesPerSample)); - ushort a = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + (3 * bytesPerSample), bytesPerSample)); - - Unsafe.Add(ref rgbPackedRef, i) = new Rgb(r / (float)ushort.MaxValue, g / (float)ushort.MaxValue, b / (float)ushort.MaxValue); - Unsafe.Add(ref alphaPackedRef, i) = a / (float)ushort.MaxValue; - } - } - else - { - ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan); - int o = 0; - for (int i = 0; i < rgbPacked.Length; o += bytesPerPixel, i++) - { - byte r = Unsafe.Add(ref scanlineSpanRef, (uint)o); - byte g = Unsafe.Add(ref scanlineSpanRef, (uint)(o + bytesPerSample)); - byte b = Unsafe.Add(ref scanlineSpanRef, (uint)(o + (2 * bytesPerSample))); - byte a = Unsafe.Add(ref scanlineSpanRef, (uint)(o + (3 * bytesPerSample))); - - Unsafe.Add(ref rgbPackedRef, i) = new Rgb(r / (float)byte.MaxValue, g / (float)byte.MaxValue, b / (float)byte.MaxValue); - Unsafe.Add(ref alphaPackedRef, i) = a / (float)byte.MaxValue; - } - } - - converter.Convert(rgbPacked, rgbPacked); - - int idx = 0; - for (nuint x = offset; x < frameControl.XMax; x += increment, idx++) + int o = 0; + for (nuint x = offset; x < frameControl.XMax; x += increment, o += bytesPerPixel) { - Rgb rgb = Unsafe.Add(ref rgbPackedRef, idx); - Vector4 rgba = rgb.ToScaledVector4(Unsafe.Add(ref alphaPackedRef, idx)); - Unsafe.Add(ref rowSpanRef, x) = TPixel.FromScaledVector4(rgba); + ushort r = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, bytesPerSample)); + ushort g = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample)); + ushort b = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + (2 * bytesPerSample), bytesPerSample)); + ushort a = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + (3 * bytesPerSample), bytesPerSample)); + Unsafe.Add(ref rowSpanRef, x) = TPixel.FromRgba64(new Rgba64(r, g, b, a)); } } + else if (pixelOffset == 0 && increment == 1) + { + PixelOperations.Instance.FromRgba32Bytes( + configuration, + scanlineSpan[..(int)(frameControl.Width * bytesPerPixel)], + rowSpan.Slice((int)frameControl.XOffset, (int)frameControl.Width), + (int)frameControl.Width); + } else { - if (bitDepth == 16) - { - int o = 0; - for (nuint x = offset; x < frameControl.XMax; x += increment, o += bytesPerPixel) - { - ushort r = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, bytesPerSample)); - ushort g = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample)); - ushort b = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + (2 * bytesPerSample), bytesPerSample)); - ushort a = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + (3 * bytesPerSample), bytesPerSample)); - Unsafe.Add(ref rowSpanRef, x) = TPixel.FromRgba64(new Rgba64(r, g, b, a)); - } - } - else if (pixelOffset == 0 && increment == 1) - { - PixelOperations.Instance.FromRgba32Bytes( - configuration, - scanlineSpan[..(int)(frameControl.Width * bytesPerPixel)], - rowSpan.Slice((int)frameControl.XOffset, (int)frameControl.Width), - (int)frameControl.Width); - } - else + ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan); + int o = 0; + for (nuint x = offset; x < frameControl.XMax; x += increment, o += bytesPerPixel) { - ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan); - int o = 0; - for (nuint x = offset; x < frameControl.XMax; x += increment, o += bytesPerPixel) - { - byte r = Unsafe.Add(ref scanlineSpanRef, (uint)o); - byte g = Unsafe.Add(ref scanlineSpanRef, (uint)(o + bytesPerSample)); - byte b = Unsafe.Add(ref scanlineSpanRef, (uint)(o + (2 * bytesPerSample))); - byte a = Unsafe.Add(ref scanlineSpanRef, (uint)(o + (3 * bytesPerSample))); - Unsafe.Add(ref rowSpanRef, x) = TPixel.FromRgba32(new Rgba32(r, g, b, a)); - } + byte r = Unsafe.Add(ref scanlineSpanRef, (uint)o); + byte g = Unsafe.Add(ref scanlineSpanRef, (uint)(o + bytesPerSample)); + byte b = Unsafe.Add(ref scanlineSpanRef, (uint)(o + (2 * bytesPerSample))); + byte a = Unsafe.Add(ref scanlineSpanRef, (uint)(o + (3 * bytesPerSample))); + Unsafe.Add(ref rowSpanRef, x) = TPixel.FromRgba32(new Rgba32(r, g, b, a)); } } }