From 6a064521036a9172a1895c11d83a9add323fa613 Mon Sep 17 00:00:00 2001 From: BobLd <38405645+BobLd@users.noreply.github.com> Date: Sat, 19 Jul 2025 13:06:56 +0100 Subject: [PATCH] Remove decode parameter application from Stencil color space for consistency --- .../Images/PngFromPdfImageFactoryTests.cs | 5 ++-- .../Content/PdfImageExtensions.cs | 8 +---- .../Graphics/Colors/ColorSpaceDetails.cs | 29 ++++--------------- .../Images/Png/PngFromPdfImageFactory.cs | 25 ++++++++++++---- .../Util/ColorSpaceDetailsParser.cs | 6 +--- .../XObjects/XObjectFactory.cs | 1 - 6 files changed, 31 insertions(+), 43 deletions(-) diff --git a/src/UglyToad.PdfPig.Tests/Images/PngFromPdfImageFactoryTests.cs b/src/UglyToad.PdfPig.Tests/Images/PngFromPdfImageFactoryTests.cs index 43080703..3579bc8d 100644 --- a/src/UglyToad.PdfPig.Tests/Images/PngFromPdfImageFactoryTests.cs +++ b/src/UglyToad.PdfPig.Tests/Images/PngFromPdfImageFactoryTests.cs @@ -172,11 +172,12 @@ var decodedBytes = ImageHelpers.LoadFileBytes("ccittfax-decoded.bin"); var image = new TestPdfImage { - ColorSpaceDetails = IndexedColorSpaceDetails.Stencil(DeviceGrayColorSpaceDetails.Instance, new[] { 1.0, 0 }), + ColorSpaceDetails = IndexedColorSpaceDetails.Stencil(DeviceGrayColorSpaceDetails.Instance), DecodedBytes = decodedBytes, WidthInSamples = 1800, HeightInSamples = 3113, - BitsPerComponent = 1 + BitsPerComponent = 1, + Decode = [1.0, 0] }; Assert.True(PngFromPdfImageFactory.TryGenerate(image, out var bytes)); diff --git a/src/UglyToad.PdfPig/Content/PdfImageExtensions.cs b/src/UglyToad.PdfPig/Content/PdfImageExtensions.cs index 952d214d..3d5b6eed 100644 --- a/src/UglyToad.PdfPig/Content/PdfImageExtensions.cs +++ b/src/UglyToad.PdfPig/Content/PdfImageExtensions.cs @@ -10,13 +10,7 @@ /// public static bool NeedsReverseDecode(this IPdfImage pdfImage) { - if (pdfImage.ColorSpaceDetails?.IsStencil == true) - { - // Stencil color space already takes care of reversing. - return false; - } - - return pdfImage.Decode.Count >= 2 && pdfImage.Decode[0] == 1 && pdfImage.Decode[1] == 0; + return pdfImage.Decode?.Count >= 2 && pdfImage.Decode[0] == 1 && pdfImage.Decode[1] == 0; } } } diff --git a/src/UglyToad.PdfPig/Graphics/Colors/ColorSpaceDetails.cs b/src/UglyToad.PdfPig/Graphics/Colors/ColorSpaceDetails.cs index 19f98804..7bf18d66 100644 --- a/src/UglyToad.PdfPig/Graphics/Colors/ColorSpaceDetails.cs +++ b/src/UglyToad.PdfPig/Graphics/Colors/ColorSpaceDetails.cs @@ -15,12 +15,6 @@ /// public abstract class ColorSpaceDetails { - /// - /// Is the color space a stencil indexed color space. - /// Stencil color spaces take care of inverting colors based on the Decode array. - /// - public bool IsStencil { get; } - /// /// The type of the ColorSpace. /// @@ -45,11 +39,10 @@ /// /// Create a new . /// - protected internal ColorSpaceDetails(ColorSpace type, bool isStencil = false) + protected internal ColorSpaceDetails(ColorSpace type) { Type = type; BaseType = type; - IsStencil = isStencil; } /// @@ -278,15 +271,12 @@ private readonly ConcurrentDictionary cache = new ConcurrentDictionary(); /// - /// Creates a indexed color space useful for extracting stencil masks as black-and-white images, - /// i.e. with a color palette of two colors (black and white). If the decode parameter array is - /// [0, 1] it indicates that black is at index 0 in the color palette, whereas [1, 0] indicates - /// that the black color is at index 1. + /// Creates an indexed color space useful for extracting stencil masks as black-and-white images, + /// i.e. with a color palette of two colors (black and white). /// - internal static ColorSpaceDetails Stencil(ColorSpaceDetails colorSpaceDetails, double[] decode) + internal static ColorSpaceDetails Stencil(ColorSpaceDetails colorSpaceDetails) { - var blackIsOne = decode.Length >= 2 && decode[0] == 1 && decode[1] == 0; - return new IndexedColorSpaceDetails(colorSpaceDetails, 1, blackIsOne ? [255, 0] : [0, 255], true); + return new IndexedColorSpaceDetails(colorSpaceDetails, 1, [0, 255]); } /// @@ -321,14 +311,7 @@ /// Create a new . /// public IndexedColorSpaceDetails(ColorSpaceDetails baseColorSpaceDetails, byte hiVal, byte[] colorTable) - : this(baseColorSpaceDetails, hiVal, colorTable, false) - { } - - /// - /// Create a new . - /// - private IndexedColorSpaceDetails(ColorSpaceDetails baseColorSpaceDetails, byte hiVal, byte[] colorTable, bool isStencil) - : base(ColorSpace.Indexed, isStencil) + : base(ColorSpace.Indexed) { BaseColorSpace = baseColorSpaceDetails ?? throw new ArgumentNullException(nameof(baseColorSpaceDetails)); HiVal = hiVal; diff --git a/src/UglyToad.PdfPig/Images/Png/PngFromPdfImageFactory.cs b/src/UglyToad.PdfPig/Images/Png/PngFromPdfImageFactory.cs index e747624c..229b5039 100644 --- a/src/UglyToad.PdfPig/Images/Png/PngFromPdfImageFactory.cs +++ b/src/UglyToad.PdfPig/Images/Png/PngFromPdfImageFactory.cs @@ -159,13 +159,28 @@ else { int i = 0; - for (int col = 0; col < image.HeightInSamples; col++) + if (!image.NeedsReverseDecode()) // TODO - Need to properly implement decode for other numberOfComponents { - for (int row = 0; row < image.WidthInSamples; row++) + for (int col = 0; col < image.HeightInSamples; col++) { - byte a = getAlphaChannel(i); - byte pixel = bytesPure[i++]; - builder.SetPixel(new Pixel(pixel, pixel, pixel, a, false), row, col); + for (int row = 0; row < image.WidthInSamples; row++) + { + byte a = getAlphaChannel(i); + byte pixel = bytesPure[i++]; + builder.SetPixel(new Pixel(pixel, pixel, pixel, a, false), row, col); + } + } + } + else + { + for (int col = 0; col < image.HeightInSamples; col++) + { + for (int row = 0; row < image.WidthInSamples; row++) + { + byte a = getAlphaChannel(i); + byte pixel = (byte)(255 - bytesPure[i++]); // Inverse decode + builder.SetPixel(new Pixel(pixel, pixel, pixel, a, false), row, col); + } } } } diff --git a/src/UglyToad.PdfPig/Util/ColorSpaceDetailsParser.cs b/src/UglyToad.PdfPig/Util/ColorSpaceDetailsParser.cs index 96c732f7..08c63a99 100644 --- a/src/UglyToad.PdfPig/Util/ColorSpaceDetailsParser.cs +++ b/src/UglyToad.PdfPig/Util/ColorSpaceDetailsParser.cs @@ -54,11 +54,7 @@ } var colorSpaceDetails = GetColorSpaceDetails(colorSpace, imageDictionary.Without(NameToken.Filter).Without(NameToken.F), scanner, resourceStore, filterProvider, true); - - var decodeRaw = imageDictionary.GetObjectOrDefault(NameToken.Decode, NameToken.D) as ArrayToken ?? new ArrayToken([]); - var decode = decodeRaw.Data.OfType().Select(static x => x.Double).ToArray(); - - return IndexedColorSpaceDetails.Stencil(colorSpaceDetails, decode); + return IndexedColorSpaceDetails.Stencil(colorSpaceDetails); } if (!colorSpace.HasValue) diff --git a/src/UglyToad.PdfPig/XObjects/XObjectFactory.cs b/src/UglyToad.PdfPig/XObjects/XObjectFactory.cs index 6a319241..2dd4af46 100644 --- a/src/UglyToad.PdfPig/XObjects/XObjectFactory.cs +++ b/src/UglyToad.PdfPig/XObjects/XObjectFactory.cs @@ -92,7 +92,6 @@ null); softMaskImage = ReadImage(maskImageRecord, pdfScanner, filterProvider, resourceStore); - System.Diagnostics.Debug.Assert(softMaskImage.ColorSpaceDetails?.IsStencil == true); } var isJpxDecode = dictionary.TryGet(NameToken.Filter, out NameToken filterName) && filterName.Equals(NameToken.JpxDecode);