mirror of https://github.com/SixLabors/ImageSharp
5 changed files with 131 additions and 3 deletions
@ -0,0 +1,79 @@ |
|||||
|
// <copyright file="PackBitsTiffCompression.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.Formats.Tiff |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Buffers; |
||||
|
using System.IO; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Class to handle cases where TIFF image data is compressed using PackBits compression.
|
||||
|
/// </summary>
|
||||
|
internal static class PackBitsTiffCompression |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Decompresses image data into the supplied buffer.
|
||||
|
/// </summary>
|
||||
|
/// <param name="stream">The <see cref="Stream"/> to read image data from.</param>
|
||||
|
/// <param name="byteCount">The number of bytes to read from the input stream.</param>
|
||||
|
/// <param name="buffer">The output buffer for uncompressed data.</param>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void Decompress(Stream stream, int byteCount, byte[] buffer) |
||||
|
{ |
||||
|
byte[] compressedData = ArrayPool<byte>.Shared.Rent(byteCount); |
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
stream.ReadFull(compressedData, byteCount); |
||||
|
int compressedOffset = 0; |
||||
|
int decompressedOffset = 0; |
||||
|
|
||||
|
while (compressedOffset < byteCount) |
||||
|
{ |
||||
|
byte headerByte = compressedData[compressedOffset]; |
||||
|
|
||||
|
if (headerByte <= (byte)127) |
||||
|
{ |
||||
|
int literalOffset = compressedOffset + 1; |
||||
|
int literalLength = compressedData[compressedOffset] + 1; |
||||
|
|
||||
|
Array.Copy(compressedData, literalOffset, buffer, decompressedOffset, literalLength); |
||||
|
|
||||
|
compressedOffset += literalLength + 1; |
||||
|
decompressedOffset += literalLength; |
||||
|
} |
||||
|
else if (headerByte == (byte)0x80) |
||||
|
{ |
||||
|
compressedOffset += 1; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
byte repeatData = compressedData[compressedOffset + 1]; |
||||
|
int repeatLength = 257 - headerByte; |
||||
|
|
||||
|
ArrayCopyRepeat(repeatData, buffer, decompressedOffset, repeatLength); |
||||
|
|
||||
|
compressedOffset += 2; |
||||
|
decompressedOffset += repeatLength; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
finally |
||||
|
{ |
||||
|
ArrayPool<byte>.Shared.Return(compressedData); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static void ArrayCopyRepeat(byte value, byte[] destinationArray, int destinationIndex, int length) |
||||
|
{ |
||||
|
for (int i = 0; i < length; i++) |
||||
|
{ |
||||
|
destinationArray[i + destinationIndex] = value; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,35 @@ |
|||||
|
// <copyright file="PackBitsTiffCompressionTests.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.Tests |
||||
|
{ |
||||
|
using System.IO; |
||||
|
using Xunit; |
||||
|
|
||||
|
using ImageSharp.Formats.Tiff; |
||||
|
|
||||
|
public class PackBitsTiffCompressionTests |
||||
|
{ |
||||
|
[Theory] |
||||
|
[InlineData(new byte[] { }, new byte[] { })] |
||||
|
[InlineData(new byte[] { 0x00, 0x2A }, new byte[] { 0x2A })] // Read one byte
|
||||
|
[InlineData(new byte[] { 0x01, 0x15, 0x32 }, new byte[] { 0x15, 0x32 })] // Read two bytes
|
||||
|
[InlineData(new byte[] { 0xFF, 0x2A }, new byte[] { 0x2A, 0x2A })] // Repeat two bytes
|
||||
|
[InlineData(new byte[] { 0xFE, 0x2A }, new byte[] { 0x2A, 0x2A, 0x2A })] // Repeat three bytes
|
||||
|
[InlineData(new byte[] { 0x80 }, new byte[] { })] // Read a 'No operation' byte
|
||||
|
[InlineData(new byte[] { 0x01, 0x15, 0x32, 0x80, 0xFF, 0xA2 }, new byte[] { 0x15, 0x32, 0xA2, 0xA2 })] // Read two bytes, nop, repeat two bytes
|
||||
|
[InlineData(new byte[] { 0xFE, 0xAA, 0x02, 0x80, 0x00, 0x2A, 0xFD, 0xAA, 0x03, 0x80, 0x00, 0x2A, 0x22, 0xF7, 0xAA }, |
||||
|
new byte[] { 0xAA, 0xAA, 0xAA, 0x80, 0x00, 0x2A, 0xAA, 0xAA, 0xAA, 0xAA, 0x80, 0x00, 0x2A, 0x22, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA })] // Apple PackBits sample
|
||||
|
public void Decompress_ReadsData(byte[] inputData, byte[] expectedResult) |
||||
|
{ |
||||
|
Stream stream = new MemoryStream(inputData); |
||||
|
byte[] buffer = new byte[expectedResult.Length]; |
||||
|
|
||||
|
PackBitsTiffCompression.Decompress(stream, inputData.Length, buffer); |
||||
|
|
||||
|
Assert.Equal(expectedResult, buffer); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue