From a56be02cfd1a2397969bd1a07eb69f95df0189e4 Mon Sep 17 00:00:00 2001 From: Kasper Frank Date: Tue, 11 May 2021 10:06:17 +0200 Subject: [PATCH] General support for extraction of images with BitsPerComponent < 8 --- .../Images/ColorSpaceDetailsByteConverter.cs | 74 +++++++++++-------- .../Images/Png/PngFromPdfImageFactory.cs | 1 - 2 files changed, 45 insertions(+), 30 deletions(-) diff --git a/src/UglyToad.PdfPig/Images/ColorSpaceDetailsByteConverter.cs b/src/UglyToad.PdfPig/Images/ColorSpaceDetailsByteConverter.cs index 643c5deb..3a2393d6 100644 --- a/src/UglyToad.PdfPig/Images/ColorSpaceDetailsByteConverter.cs +++ b/src/UglyToad.PdfPig/Images/ColorSpaceDetailsByteConverter.cs @@ -28,48 +28,64 @@ if (details == null) { return decoded.ToArray(); + } + + if (bitsPerComponent != 8) + { + // Unpack components such that they occupy one byte each + decoded = UnpackComponents(decoded, bitsPerComponent); + } + + // Remove padding bytes when the stride width differs from the image width + var bytesPerPixel = details is IndexedColorSpaceDetails ? 1 : GetBytesPerPixel(details); + var strideWidth = decoded.Count / imageHeight / bytesPerPixel; + if (strideWidth != imageWidth) + { + decoded = RemoveStridePadding(decoded.ToArray(), strideWidth, imageWidth, imageHeight, bytesPerPixel); } - switch (details) + // In case of indexed color space images, unwrap indices to actual pixel component values + if (details is IndexedColorSpaceDetails indexed) { - case IndexedColorSpaceDetails indexed: - if (bitsPerComponent != 8) - { - // To ease unwrapping further below the indices are unpacked to occupy a single byte each - decoded = UnpackIndices(decoded, bitsPerComponent); - - // Remove padding bytes when the stride width differs from the image width - var stride = (imageWidth * bitsPerComponent + 7) / 8; - var strideWidth = stride * (8 / bitsPerComponent); - if (strideWidth != imageWidth) - { - decoded = RemoveStridePadding(decoded.ToArray(), strideWidth, imageWidth, imageHeight); - } - } - - return UnwrapIndexedColorSpaceBytes(indexed, decoded); + decoded = UnwrapIndexedColorSpaceBytes(indexed, decoded); } return decoded.ToArray(); } - private static byte[] UnpackIndices(IReadOnlyList input, int bitsPerComponent) - { - IEnumerable Unpack(byte b) + private static int GetBytesPerPixel(ColorSpaceDetails details) + { + var colorSpace = (details is IndexedColorSpaceDetails indexed) ? indexed.BaseColorSpaceDetails.Type : details.Type; + switch (colorSpace) + { + case ColorSpace.DeviceRGB: + return 3; + + case ColorSpace.DeviceCMYK: + return 4; + + default: + return 1; + } + } + + private static byte[] UnpackComponents(IReadOnlyList input, int bitsPerComponent) + { + IEnumerable Unpack(byte b) + { + // Enumerate bits in bitsPerComponent-sized chunks from MSB to LSB, masking on the appropriate bits + for (int i = 8 - bitsPerComponent; i >= 0; i -= bitsPerComponent) { - // Enumerate bits in bitsPerComponent-sized chunks from MSB to LSB, masking on the appropriate bits - for (int i = 8 - bitsPerComponent; i >= 0; i -= bitsPerComponent) - { - yield return (byte)((b >> i) & ((int)Math.Pow(2, bitsPerComponent) - 1)); - } + yield return (byte)((b >> i) & ((int)Math.Pow(2, bitsPerComponent) - 1)); } - - return input.SelectMany(b => Unpack(b)).ToArray(); + } + + return input.SelectMany(b => Unpack(b)).ToArray(); } - private static byte[] RemoveStridePadding(byte[] input, int strideWidth, int imageWidth, int imageHeight) + private static byte[] RemoveStridePadding(byte[] input, int strideWidth, int imageWidth, int imageHeight, int multiplier) { - var result = new byte[imageWidth * imageHeight]; + var result = new byte[imageWidth * imageHeight * multiplier]; for (int y = 0; y < imageHeight; y++) { int sourceIndex = y * strideWidth; diff --git a/src/UglyToad.PdfPig/Images/Png/PngFromPdfImageFactory.cs b/src/UglyToad.PdfPig/Images/Png/PngFromPdfImageFactory.cs index 3ee8b93e..0a12c5b0 100644 --- a/src/UglyToad.PdfPig/Images/Png/PngFromPdfImageFactory.cs +++ b/src/UglyToad.PdfPig/Images/Png/PngFromPdfImageFactory.cs @@ -2,7 +2,6 @@ { using Content; using Graphics.Colors; - using System.Collections.Generic; using UglyToad.PdfPig.Core; internal static class PngFromPdfImageFactory