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);