mirror of
https://github.com/UglyToad/PdfPig.git
synced 2025-04-05 20:55:01 +08:00
Handle unpacking components for 16 bits per component images and use Span in RemoveStridePadding
This commit is contained in:
parent
5c168f9cd0
commit
ad785328e1
@ -40,6 +40,25 @@
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BitsPerComponents16()
|
||||
{
|
||||
var path = IntegrationHelpers.GetDocumentPath("MOZILLA-3136-0.pdf");
|
||||
|
||||
using (var document = PdfDocument.Open(path))
|
||||
{
|
||||
var page1 = document.GetPage(3);
|
||||
var images1 = page1.GetImages().ToArray();
|
||||
|
||||
var image9 = images1[9];
|
||||
|
||||
Assert.Equal(16 , image9.BitsPerComponent);
|
||||
|
||||
Assert.True(image9.TryGetPng(out byte[] bytes_3_9));
|
||||
File.WriteAllBytes(Path.Combine(OutputFolder, "MOZILLA-3136-0_3_9_16bits.png"), bytes_3_9);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DeviceNColorSpaceImages()
|
||||
{
|
||||
|
@ -1,56 +1,73 @@
|
||||
namespace UglyToad.PdfPig.Images
|
||||
{
|
||||
using Content;
|
||||
namespace UglyToad.PdfPig.Images
|
||||
{
|
||||
using Content;
|
||||
using Graphics.Colors;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// Utility for working with the bytes in <see cref="IPdfImage"/>s and converting according to their <see cref="ColorSpaceDetails"/>.s
|
||||
/// </summary>
|
||||
public static class ColorSpaceDetailsByteConverter
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts the output bytes (if available) of <see cref="IPdfImage.TryGetBytesAsMemory"/>
|
||||
/// to actual pixel values using the <see cref="IPdfImage.ColorSpaceDetails"/>. For most images this doesn't
|
||||
/// change the data but for <see cref="ColorSpace.Indexed"/> it will convert the bytes which are indexes into the
|
||||
/// real pixel data into the real pixel data.
|
||||
/// </summary>
|
||||
public static ReadOnlySpan<byte> Convert(ColorSpaceDetails details, ReadOnlySpan<byte> decoded, int bitsPerComponent, int imageWidth, int imageHeight)
|
||||
{
|
||||
if (decoded.IsEmpty)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
if (details is null)
|
||||
{
|
||||
return decoded;
|
||||
/// <summary>
|
||||
/// Utility for working with the bytes in <see cref="IPdfImage"/>s and converting according to their <see cref="ColorSpaceDetails"/>.s
|
||||
/// </summary>
|
||||
public static class ColorSpaceDetailsByteConverter
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts the output bytes (if available) of <see cref="IPdfImage.TryGetBytesAsMemory"/>
|
||||
/// to actual pixel values using the <see cref="IPdfImage.ColorSpaceDetails"/>. For most images this doesn't
|
||||
/// change the data but for <see cref="ColorSpace.Indexed"/> it will convert the bytes which are indexes into the
|
||||
/// real pixel data into the real pixel data.
|
||||
/// </summary>
|
||||
public static ReadOnlySpan<byte> Convert(ColorSpaceDetails details, ReadOnlySpan<byte> decoded, int bitsPerComponent, int imageWidth, int imageHeight)
|
||||
{
|
||||
if (decoded.IsEmpty)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
if (details is null)
|
||||
{
|
||||
return decoded;
|
||||
}
|
||||
|
||||
// TODO - We should aim at removing this alloc.
|
||||
// The decoded input variable needs to become a Span<byte>
|
||||
Span<byte> data = decoded.ToArray();
|
||||
|
||||
if (bitsPerComponent != 8)
|
||||
{
|
||||
// Unpack components such that they occupy one byte each
|
||||
decoded = UnpackComponents(decoded, bitsPerComponent);
|
||||
data = UnpackComponents(data, bitsPerComponent);
|
||||
}
|
||||
|
||||
// Remove padding bytes when the stride width differs from the image width
|
||||
var bytesPerPixel = details.NumberOfColorComponents;
|
||||
var strideWidth = decoded.Length / imageHeight / bytesPerPixel;
|
||||
var strideWidth = data.Length / imageHeight / bytesPerPixel;
|
||||
if (strideWidth != imageWidth)
|
||||
{
|
||||
decoded = RemoveStridePadding(decoded.ToArray(), strideWidth, imageWidth, imageHeight, bytesPerPixel);
|
||||
data = RemoveStridePadding(data, strideWidth, imageWidth, imageHeight, bytesPerPixel);
|
||||
}
|
||||
|
||||
decoded = details.Transform(decoded);
|
||||
|
||||
return decoded;
|
||||
return details.Transform(data);
|
||||
}
|
||||
|
||||
private static byte[] UnpackComponents(ReadOnlySpan<byte> input, int bitsPerComponent)
|
||||
private static Span<byte> UnpackComponents(Span<byte> input, int bitsPerComponent)
|
||||
{
|
||||
if (bitsPerComponent == 16) // Example with MOZILLA-3136-0.pdf (page 3)
|
||||
{
|
||||
int size = input.Length / 2;
|
||||
var unpacked16 = input.Slice(0, size); // In place
|
||||
|
||||
for (int b = 0; b < size; ++b)
|
||||
{
|
||||
int i = 2 * b;
|
||||
// Convert to UInt16 and divide by 256
|
||||
unpacked16[b] = (byte)((ushort)(input[i + 1] | input[i] << 8) / 256);
|
||||
}
|
||||
|
||||
return unpacked16;
|
||||
}
|
||||
|
||||
int end = 8 - bitsPerComponent;
|
||||
var unpacked = new byte[input.Length * (int)Math.Ceiling((end + 1) / (double)bitsPerComponent)];
|
||||
|
||||
Span<byte> unpacked = new byte[input.Length * (int)Math.Ceiling((end + 1) / (double)bitsPerComponent)];
|
||||
|
||||
int right = (int)Math.Pow(2, bitsPerComponent) - 1;
|
||||
|
||||
@ -65,19 +82,20 @@
|
||||
}
|
||||
|
||||
return unpacked;
|
||||
}
|
||||
|
||||
private static byte[] RemoveStridePadding(byte[] input, int strideWidth, int imageWidth, int imageHeight, int multiplier)
|
||||
}
|
||||
|
||||
|
||||
private static Span<byte> RemoveStridePadding(Span<byte> input, int strideWidth, int imageWidth, int imageHeight, int multiplier)
|
||||
{
|
||||
var result = new byte[imageWidth * imageHeight * multiplier];
|
||||
Span<byte> result = new byte[imageWidth * imageHeight * multiplier];
|
||||
for (int y = 0; y < imageHeight; y++)
|
||||
{
|
||||
int sourceIndex = y * strideWidth;
|
||||
int targetIndex = y * imageWidth;
|
||||
Array.Copy(input, sourceIndex, result, targetIndex, imageWidth);
|
||||
input.Slice(sourceIndex, imageWidth).CopyTo(result.Slice(targetIndex, imageWidth));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user