mirror of https://github.com/SixLabors/ImageSharp
27 changed files with 357 additions and 35 deletions
@ -0,0 +1,128 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Tiff.Compression |
|||
{ |
|||
/// <summary>
|
|||
/// Pack Bits compression for tiff images. See Tiff Spec v6, section 9.
|
|||
/// </summary>
|
|||
internal static class PackBitsWriter |
|||
{ |
|||
public static int PackBits(Span<byte> rowSpan, Span<byte> compressedRowSpan) |
|||
{ |
|||
int maxRunLength = 127; |
|||
int posInRowSpan = 0; |
|||
int bytesWritten = 0; |
|||
int literalRunLength = 0; |
|||
|
|||
while (posInRowSpan < rowSpan.Length) |
|||
{ |
|||
var useReplicateRun = IsReplicateRun(rowSpan, posInRowSpan); |
|||
if (useReplicateRun) |
|||
{ |
|||
if (literalRunLength > 0) |
|||
{ |
|||
WriteLiteralRun(rowSpan, posInRowSpan, literalRunLength, compressedRowSpan, bytesWritten); |
|||
bytesWritten += literalRunLength + 1; |
|||
} |
|||
|
|||
// Write a run with the same bytes.
|
|||
var runLength = FindRunLength(rowSpan, posInRowSpan, maxRunLength); |
|||
WriteRun(rowSpan, posInRowSpan, runLength, compressedRowSpan, bytesWritten); |
|||
|
|||
bytesWritten += 2; |
|||
literalRunLength = 0; |
|||
posInRowSpan += runLength; |
|||
continue; |
|||
} |
|||
|
|||
literalRunLength++; |
|||
posInRowSpan++; |
|||
|
|||
if (literalRunLength >= maxRunLength) |
|||
{ |
|||
WriteLiteralRun(rowSpan, posInRowSpan, literalRunLength, compressedRowSpan, bytesWritten); |
|||
bytesWritten += literalRunLength + 1; |
|||
literalRunLength = 0; |
|||
} |
|||
} |
|||
|
|||
if (literalRunLength > 0) |
|||
{ |
|||
WriteLiteralRun(rowSpan, posInRowSpan, literalRunLength, compressedRowSpan, bytesWritten); |
|||
bytesWritten += literalRunLength + 1; |
|||
} |
|||
|
|||
return bytesWritten; |
|||
} |
|||
|
|||
private static void WriteLiteralRun(Span<byte> rowSpan, int end, int literalRunLength, Span<byte> compressedRowSpan, int compressedRowPos) |
|||
{ |
|||
DebugGuard.MustBeLessThanOrEqualTo(literalRunLength, 127, nameof(literalRunLength)); |
|||
|
|||
int literalRunStart = end - literalRunLength; |
|||
sbyte runLength = (sbyte)(literalRunLength - 1); |
|||
compressedRowSpan[compressedRowPos] = (byte)runLength; |
|||
rowSpan.Slice(literalRunStart, literalRunLength).CopyTo(compressedRowSpan.Slice(compressedRowPos + 1)); |
|||
} |
|||
|
|||
private static void WriteRun(Span<byte> rowSpan, int start, int runLength, Span<byte> compressedRowSpan, int compressedRowPos) |
|||
{ |
|||
DebugGuard.MustBeLessThanOrEqualTo(runLength, 127, nameof(runLength)); |
|||
|
|||
sbyte headerByte = (sbyte)(-runLength + 1); |
|||
compressedRowSpan[compressedRowPos] = (byte)headerByte; |
|||
compressedRowSpan[compressedRowPos + 1] = rowSpan[start]; |
|||
} |
|||
|
|||
private static bool IsReplicateRun(Span<byte> rowSpan, int startPos) |
|||
{ |
|||
// We consider run which has at least 3 same consecutive bytes a candidate for a run.
|
|||
var startByte = rowSpan[startPos]; |
|||
int count = 0; |
|||
for (int i = startPos + 1; i < rowSpan.Length; i++) |
|||
{ |
|||
if (rowSpan[i] == startByte) |
|||
{ |
|||
count++; |
|||
if (count >= 2) |
|||
{ |
|||
return true; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
break; |
|||
} |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
private static int FindRunLength(Span<byte> rowSpan, int startPos, int maxRunLength) |
|||
{ |
|||
var startByte = rowSpan[startPos]; |
|||
int count = 1; |
|||
for (int i = startPos + 1; i < rowSpan.Length; i++) |
|||
{ |
|||
if (rowSpan[i] == startByte) |
|||
{ |
|||
count++; |
|||
} |
|||
else |
|||
{ |
|||
break; |
|||
} |
|||
|
|||
if (count == maxRunLength) |
|||
{ |
|||
break; |
|||
} |
|||
} |
|||
|
|||
return count; |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue