Skip to content
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
dc9bcee
Remove old frameworks
brianpopow May 29, 2022
54ff65c
Add fax4 image for benchmarks
brianpopow May 29, 2022
0cf4d91
Add InliningOptions.ShortMethod for WriteBit methods
brianpopow May 29, 2022
8459828
Use GetRowSpan to access pixel data
brianpopow May 29, 2022
c0059fc
Avoid calculating bit position multiple times
brianpopow May 29, 2022
cecd7f2
Add MethodImpl(InliningOptions.ShortMethod) for SwapColor
brianpopow May 29, 2022
0ae59fd
Avoid bounds checks in GetBit()
brianpopow May 29, 2022
07d2e79
Avoid more bounds checks
brianpopow May 29, 2022
57349fd
Avoid writing zero bit, we already have a clean buffer
brianpopow May 29, 2022
c7aaf10
Use GetRowSpan
brianpopow May 29, 2022
432c0d7
Avoid bounds checks in BlackIsZero and WhiteIsZero
brianpopow May 29, 2022
2876f2f
Use nint, determine what's white only once
brianpopow May 29, 2022
a26bfe0
Access data span only for new byte, not for every bit
brianpopow May 30, 2022
f0f08e8
Use Numerics.Modulo8
brianpopow May 30, 2022
62de458
Stream -> BufferedReadStream
brianpopow May 30, 2022
9624661
Avoid reading all compressed data at once: instead read byte by byte …
brianpopow May 30, 2022
98b96fe
Use nint
brianpopow May 30, 2022
d5a3de1
Avoid using Dictionary's
brianpopow May 30, 2022
2675511
Merge branch 'main' into bp/Issue2132
brianpopow May 30, 2022
188b5cd
Use more nint
brianpopow May 31, 2022
29b4647
Remove no longer needed Vector4.Zero workaround for netcore2.1
brianpopow May 31, 2022
affb143
Remove no longer valid comments
brianpopow May 31, 2022
10ff24f
Loop unroll
brianpopow May 31, 2022
50c1aab
Use stackalloc
brianpopow May 31, 2022
8ff29a1
PackBitsTiffCompression: Use Slice.Fill to repeat the data
brianpopow Jun 1, 2022
7434643
Remove no longer valid comment
brianpopow Jun 1, 2022
1b7f800
Minor: Another instance of use Numerics.Modulo8 instead of % 8
brianpopow Jun 2, 2022
34584b9
Add another test case for Fax4 compressed with min is black
brianpopow Jun 2, 2022
035c31e
Make sure 1 Bit compression is only used with 1 bit pixel type
brianpopow Jun 3, 2022
5675aa8
Fix encode tiff benchmarks
brianpopow Jun 3, 2022
3af2dd7
Multiply by inv max value instead of dividing in GeneratePalette
brianpopow Jun 3, 2022
d02854f
Merge branch 'main' into bp/Issue2132
brianpopow Jun 6, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/ImageSharp/Common/Helpers/Numerics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,12 @@ public static int LeastCommonMultiple(int a, int b)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Modulo8(int x) => x & 7;

/// <summary>
/// Calculates <paramref name="x"/> % 8
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static nint Modulo8(nint x) => x & 7;

/// <summary>
/// Fast (x mod m) calculator, with the restriction that
/// <paramref name="m"/> should be power of 2.
Expand Down
30 changes: 21 additions & 9 deletions src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,23 @@
// Licensed under the Apache License, Version 2.0.

using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace SixLabors.ImageSharp.Formats.Tiff.Compression
{
internal static class BitWriterUtils
{
public static void WriteBits(Span<byte> buffer, int pos, uint count, byte value)
public static void WriteBits(Span<byte> buffer, nint pos, nint count, byte value)
{
int bitPos = pos % 8;
int bufferPos = pos / 8;
int startIdx = bufferPos + bitPos;
int endIdx = (int)(startIdx + count);
nint bitPos = Numerics.Modulo8(pos);
nint bufferPos = pos / 8;
nint startIdx = bufferPos + bitPos;
nint endIdx = startIdx + count;

if (value == 1)
{
for (int i = startIdx; i < endIdx; i++)
for (nint i = startIdx; i < endIdx; i++)
{
WriteBit(buffer, bufferPos, bitPos);

Expand All @@ -30,7 +32,7 @@ public static void WriteBits(Span<byte> buffer, int pos, uint count, byte value)
}
else
{
for (int i = startIdx; i < endIdx; i++)
for (nint i = startIdx; i < endIdx; i++)
{
WriteZeroBit(buffer, bufferPos, bitPos);

Expand All @@ -44,8 +46,18 @@ public static void WriteBits(Span<byte> buffer, int pos, uint count, byte value)
}
}

public static void WriteBit(Span<byte> buffer, int bufferPos, int bitPos) => buffer[bufferPos] |= (byte)(1 << (7 - bitPos));
[MethodImpl(InliningOptions.ShortMethod)]
public static void WriteBit(Span<byte> buffer, nint bufferPos, nint bitPos)
{
ref byte b = ref Unsafe.Add(ref MemoryMarshal.GetReference(buffer), bufferPos);
b |= (byte)(1 << (int)(7 - bitPos));
}

public static void WriteZeroBit(Span<byte> buffer, int bufferPos, int bitPos) => buffer[bufferPos] = (byte)(buffer[bufferPos] & ~(1 << (7 - bitPos)));
[MethodImpl(InliningOptions.ShortMethod)]
public static void WriteZeroBit(Span<byte> buffer, nint bufferPos, nint bitPos)
{
ref byte b = ref Unsafe.Add(ref MemoryMarshal.GetReference(buffer), bufferPos);
b = (byte)(b & ~(1 << (int)(7 - bitPos)));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -442,16 +442,16 @@ protected uint GetMakeupCode(uint runLength, out uint codeLength, bool isWhiteRu
}

/// <summary>
/// Pads output to the next byte
/// Pads output to the next byte.
/// </summary>
/// <remarks>
/// If the output is not currently on a byte boundary,
/// zero-pad it to the next byte
/// zero-pad it to the next byte.
/// </remarks>
protected void PadByte()
{
// Check if padding is necessary.
if (this.bitPosition % 8 != 0)
if (Numerics.Modulo8(this.bitPosition) != 0)
{
// Skip padding bits, move to next byte.
this.bytePosition++;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.

using System;
using System.Runtime.CompilerServices;

namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
{
Expand Down Expand Up @@ -130,6 +131,7 @@ private int FindB1ForNormalLine(int a0, byte a0Byte)
return index + offset;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int FindB2ForImaginaryWhiteLine() => this.width;

private int FindB2ForNormalLine(int b1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,21 @@ internal readonly struct CcittTwoDimensionalCode
/// <summary>
/// Initializes a new instance of the <see cref="CcittTwoDimensionalCode"/> struct.
/// </summary>
/// <param name="type">The type.</param>
/// <param name="code">The code word.</param>
/// <param name="type">The type of the code.</param>
/// <param name="bitsRequired">The bits required.</param>
/// <param name="extensionBits">The extension bits.</param>
public CcittTwoDimensionalCode(CcittTwoDimensionalCodeType type, int bitsRequired, int extensionBits = 0)
=> this.value = (ushort)((byte)type | ((bitsRequired & 0b1111) << 8) | ((extensionBits & 0b111) << 11));
public CcittTwoDimensionalCode(int code, CcittTwoDimensionalCodeType type, int bitsRequired, int extensionBits = 0)
{
this.Code = code;
this.value = (ushort)((byte)type | ((bitsRequired & 0b1111) << 8) | ((extensionBits & 0b111) << 11));
}

/// <summary>
/// Gets the code type.
/// </summary>
public CcittTwoDimensionalCodeType Type => (CcittTwoDimensionalCodeType)(this.value & 0b11111111);

public int Code { get; }
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.

using System.IO;
using SixLabors.ImageSharp.Formats.Tiff.Constants;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.IO;

namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
{
Expand All @@ -19,14 +18,13 @@ internal sealed class ModifiedHuffmanBitReader : T4BitReader
/// <param name="input">The compressed input stream.</param>
/// <param name="fillOrder">The logical order of bits within a byte.</param>
/// <param name="bytesToRead">The number of bytes to read from the stream.</param>
/// <param name="allocator">The memory allocator.</param>
public ModifiedHuffmanBitReader(Stream input, TiffFillOrder fillOrder, int bytesToRead, MemoryAllocator allocator)
: base(input, fillOrder, bytesToRead, allocator)
public ModifiedHuffmanBitReader(BufferedReadStream input, TiffFillOrder fillOrder, int bytesToRead)
: base(input, fillOrder, bytesToRead)
{
}

/// <inheritdoc/>
public override bool HasMoreData => this.Position < (ulong)this.DataLength - 1 || ((uint)(this.BitsRead - 1) < (7 - 1));
public override bool HasMoreData => this.Position < (ulong)this.DataLength - 1 || (uint)(this.BitsRead - 1) < 6;

/// <inheritdoc/>
public override bool IsEndOfScanLine
Expand All @@ -53,12 +51,11 @@ public override void StartNewRow()
{
base.StartNewRow();

int remainder = this.BitsRead & 7; // bit-hack for % 8
int remainder = Numerics.Modulo8(this.BitsRead);
if (remainder != 0)
{
// Skip padding bits, move to next byte.
this.Position++;
this.ResetBitsRead();
this.AdvancePosition();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,12 @@ public ModifiedHuffmanTiffCompression(MemoryAllocator allocator, TiffFillOrder f
/// <inheritdoc/>
protected override void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span<byte> buffer)
{
using var bitReader = new ModifiedHuffmanBitReader(stream, this.FillOrder, byteCount, this.Allocator);
var bitReader = new ModifiedHuffmanBitReader(stream, this.FillOrder, byteCount);

buffer.Clear();
uint bitsWritten = 0;
uint pixelsWritten = 0;
nint bitsWritten = 0;
nuint pixelsWritten = 0;
nint rowsWritten = 0;
while (bitReader.HasMoreData)
{
bitReader.ReadNextRun();
Expand All @@ -55,32 +56,39 @@ protected override void Decompress(BufferedReadStream stream, int byteCount, int
{
if (bitReader.IsWhiteRun)
{
BitWriterUtils.WriteBits(buffer, (int)bitsWritten, bitReader.RunLength, this.whiteValue);
BitWriterUtils.WriteBits(buffer, bitsWritten, (int)bitReader.RunLength, this.whiteValue);
}
else
{
BitWriterUtils.WriteBits(buffer, (int)bitsWritten, bitReader.RunLength, this.blackValue);
BitWriterUtils.WriteBits(buffer, bitsWritten, (int)bitReader.RunLength, this.blackValue);
}

bitsWritten += bitReader.RunLength;
bitsWritten += (int)bitReader.RunLength;
pixelsWritten += bitReader.RunLength;
}

if (pixelsWritten == this.Width)
if (pixelsWritten == (ulong)this.Width)
{
bitReader.StartNewRow();
rowsWritten++;
pixelsWritten = 0;

// Write padding bits, if necessary.
uint pad = 8 - (bitsWritten % 8);
nint pad = 8 - Numerics.Modulo8(bitsWritten);
if (pad != 8)
{
BitWriterUtils.WriteBits(buffer, (int)bitsWritten, pad, 0);
BitWriterUtils.WriteBits(buffer, bitsWritten, pad, 0);
bitsWritten += pad;
}

if (rowsWritten >= stripHeight)
{
break;
}

bitReader.StartNewRow();
}

if (pixelsWritten > this.Width)
if (pixelsWritten > (ulong)this.Width)
{
TiffThrowHelper.ThrowImageFormatException("ccitt compression parsing error, decoded more pixels then image width");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,22 +73,14 @@ protected override void Decompress(BufferedReadStream stream, int byteCount, int
byte repeatData = compressedData[compressedOffset + 1];
int repeatLength = 257 - headerByte;

ArrayCopyRepeat(repeatData, buffer, decompressedOffset, repeatLength);
buffer.Slice(decompressedOffset, repeatLength).Fill(repeatData);

compressedOffset += 2;
decompressedOffset += repeatLength;
}
}
}

private static void ArrayCopyRepeat(byte value, Span<byte> destinationArray, int destinationIndex, int length)
{
for (int i = 0; i < length; i++)
{
destinationArray[i + destinationIndex] = value;
}
}

/// <inheritdoc/>
protected override void Dispose(bool disposing) => this.compressedDataMemory?.Dispose();
}
Expand Down
Loading