From e38989d630f40eb19c19a408def68bde6299f213 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 8 Sep 2026 10:35:34 +1000 Subject: [PATCH] Restore JPEG and PNG implementations to upstream main Remove unrelated codec changes introduced during the HEIF work. Restore all three changed JPEG production files and the PNG cICP writer to upstream/main. The restored files match upstream/main exactly and were included in the successful Release net11.0 build. HEIF region and ICC changes remain uncommitted. --- .../JpegColorConverter.Operator.cs | 1 + .../Decoder/SpectralConverter{TPixel}.cs | 22 +++++++++++++++++-- .../Formats/Jpeg/JpegEncoderCore.cs | 12 +++++----- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 2 +- 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.Operator.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.Operator.cs index 39dec7ba98..3b2ab914b3 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.Operator.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.Operator.cs @@ -4,6 +4,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; +using SixLabors.ImageSharp.Common.Helpers; namespace SixLabors.ImageSharp.Formats.Jpeg.Components; diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs index d70e85476e..2bd4b95fdd 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs @@ -42,6 +42,11 @@ internal class SpectralConverter : SpectralConverter, IDisposable /// private IMemoryOwner rgbBuffer; + /// + /// Proxy buffer used in packing from RGB to target TPixel pixels. + /// + private IMemoryOwner paddedProxyPixelRow; + /// /// Resulting 2D pixel buffer. /// @@ -158,8 +163,19 @@ internal class SpectralConverter : SpectralConverter, IDisposable SimdUtils.NormalizedFloatToByteSaturate(values.Component1, g); SimdUtils.NormalizedFloatToByteSaturate(values.Component2, b); - Span destination = this.pixelBuffer.DangerousGetRowSpan(yy); - PixelOperations.Instance.PackFromRgbPlanes(r, g, b, destination); + // 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.DangerousTryGetPaddedRowSpan(yy, 3, out Span destRow)) + { + PixelOperations.Instance.PackFromRgbPlanes(r, g, b, destRow); + } + else + { + Span proxyRow = this.paddedProxyPixelRow.GetSpan(); + PixelOperations.Instance.PackFromRgbPlanes(r, g, b, proxyRow); + proxyRow[..width].CopyTo(this.pixelBuffer.DangerousGetRowSpan(yy)); + } } this.pixelRowCounter += this.pixelRowsPerStep; @@ -198,6 +214,7 @@ internal class SpectralConverter : SpectralConverter, IDisposable pixelSize.Height, this.Configuration.PreferContiguousImageBuffers, AllocationOptions.Clean); + this.paddedProxyPixelRow = allocator.Allocate(pixelSize.Width + 3); // Component processors from spectral to RGB int bufferWidth = majorBlockWidth * blockPixelSize; @@ -258,6 +275,7 @@ internal class SpectralConverter : SpectralConverter, IDisposable } this.rgbBuffer?.Dispose(); + this.paddedProxyPixelRow?.Dispose(); this.pixelBuffer?.Dispose(); } } diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs index 47c8d45a63..c9b684009e 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs @@ -90,13 +90,11 @@ internal sealed unsafe partial class JpegEncoderCore this.WriteApp14Marker(frameConfig.AdobeColorTransformMarkerFlag.Value, buffer); } - if (!this.encoder.SkipMetadata) - { - // Profiles and comments are optional application metadata and must not leak into a containing format - // when the containing encoder has requested a metadata-free embedded JPEG payload. - this.WriteProfiles(metadata, buffer); - this.WriteComments(image.Configuration, jpegMetadata); - } + // Write Exif, XMP, ICC and IPTC profiles + this.WriteProfiles(metadata, buffer); + + // Write comments + this.WriteComments(image.Configuration, jpegMetadata); // Write the image dimensions. this.WriteStartOfFrame(image.Width, image.Height, frameConfig, buffer); diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index fe1de830f0..caf2393d89 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -1057,7 +1057,7 @@ internal sealed class PngEncoderCore : IDisposable /// CICP matrix coefficients other than Identity are not supported in PNG. private void WriteCicpChunk(Stream stream, ImageMetadata metaData) { - if (this.chunkFilter == PngChunkFilter.ExcludeAll || metaData.CicpProfile is null) + if (metaData.CicpProfile is null) { return; }