Skip to content

Commit a5cb2ac

Browse files
Merge pull request #2047 from SixLabors/bp/predictorwithalpha
TIFF: Add support for horizontal predictor with alpha data
2 parents a54d8b5 + 184ea74 commit a5cb2ac

File tree

8 files changed

+246
-1
lines changed

8 files changed

+246
-1
lines changed

src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs

Lines changed: 221 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ internal static class HorizontalPredictor
2222
/// <param name="pixelBytes">Buffer with decompressed pixel data.</param>
2323
/// <param name="width">The width of the image or strip.</param>
2424
/// <param name="colorType">The color type of the pixel data.</param>
25-
/// <param name="isBigEndian">if set to <c>true</c> decodes the pixel data as big endian, otherwise as little endian.</param>
25+
/// <param name="isBigEndian">If set to <c>true</c> decodes the pixel data as big endian, otherwise as little endian.</param>
2626
public static void Undo(Span<byte> pixelBytes, int width, TiffColorType colorType, bool isBigEndian)
2727
{
2828
switch (colorType)
@@ -43,12 +43,21 @@ public static void Undo(Span<byte> pixelBytes, int width, TiffColorType colorTyp
4343
case TiffColorType.Rgb888:
4444
UndoRgb24Bit(pixelBytes, width);
4545
break;
46+
case TiffColorType.Rgba8888:
47+
UndoRgba32Bit(pixelBytes, width);
48+
break;
4649
case TiffColorType.Rgb161616:
4750
UndoRgb48Bit(pixelBytes, width, isBigEndian);
4851
break;
52+
case TiffColorType.Rgba16161616:
53+
UndoRgba64Bit(pixelBytes, width, isBigEndian);
54+
break;
4955
case TiffColorType.Rgb323232:
5056
UndoRgb96Bit(pixelBytes, width, isBigEndian);
5157
break;
58+
case TiffColorType.Rgba32323232:
59+
UndoRgba128Bit(pixelBytes, width, isBigEndian);
60+
break;
5261
}
5362
}
5463

@@ -243,6 +252,33 @@ private static void UndoRgb24Bit(Span<byte> pixelBytes, int width)
243252
}
244253
}
245254

255+
private static void UndoRgba32Bit(Span<byte> pixelBytes, int width)
256+
{
257+
int rowBytesCount = width * 4;
258+
int height = pixelBytes.Length / rowBytesCount;
259+
for (int y = 0; y < height; y++)
260+
{
261+
Span<byte> rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount);
262+
Span<Rgba32> rowRgb = MemoryMarshal.Cast<byte, Rgba32>(rowBytes).Slice(0, width);
263+
ref Rgba32 rowRgbBase = ref MemoryMarshal.GetReference(rowRgb);
264+
byte r = rowRgbBase.R;
265+
byte g = rowRgbBase.G;
266+
byte b = rowRgbBase.B;
267+
byte a = rowRgbBase.A;
268+
269+
for (int x = 1; x < rowRgb.Length; x++)
270+
{
271+
ref Rgba32 pixel = ref rowRgb[x];
272+
r += pixel.R;
273+
g += pixel.G;
274+
b += pixel.B;
275+
a += pixel.A;
276+
var rgb = new Rgba32(r, g, b, a);
277+
pixel.FromRgba32(rgb);
278+
}
279+
}
280+
}
281+
246282
private static void UndoRgb48Bit(Span<byte> pixelBytes, int width, bool isBigEndian)
247283
{
248284
int rowBytesCount = width * 6;
@@ -319,6 +355,98 @@ private static void UndoRgb48Bit(Span<byte> pixelBytes, int width, bool isBigEnd
319355
}
320356
}
321357

358+
private static void UndoRgba64Bit(Span<byte> pixelBytes, int width, bool isBigEndian)
359+
{
360+
int rowBytesCount = width * 8;
361+
int height = pixelBytes.Length / rowBytesCount;
362+
if (isBigEndian)
363+
{
364+
for (int y = 0; y < height; y++)
365+
{
366+
int offset = 0;
367+
Span<byte> rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount);
368+
ushort r = TiffUtils.ConvertToUShortBigEndian(rowBytes.Slice(offset, 2));
369+
offset += 2;
370+
ushort g = TiffUtils.ConvertToUShortBigEndian(rowBytes.Slice(offset, 2));
371+
offset += 2;
372+
ushort b = TiffUtils.ConvertToUShortBigEndian(rowBytes.Slice(offset, 2));
373+
offset += 2;
374+
ushort a = TiffUtils.ConvertToUShortBigEndian(rowBytes.Slice(offset, 2));
375+
offset += 2;
376+
377+
for (int x = 1; x < width; x++)
378+
{
379+
Span<byte> rowSpan = rowBytes.Slice(offset, 2);
380+
ushort deltaR = TiffUtils.ConvertToUShortBigEndian(rowSpan);
381+
r += deltaR;
382+
BinaryPrimitives.WriteUInt16BigEndian(rowSpan, r);
383+
offset += 2;
384+
385+
rowSpan = rowBytes.Slice(offset, 2);
386+
ushort deltaG = TiffUtils.ConvertToUShortBigEndian(rowSpan);
387+
g += deltaG;
388+
BinaryPrimitives.WriteUInt16BigEndian(rowSpan, g);
389+
offset += 2;
390+
391+
rowSpan = rowBytes.Slice(offset, 2);
392+
ushort deltaB = TiffUtils.ConvertToUShortBigEndian(rowSpan);
393+
b += deltaB;
394+
BinaryPrimitives.WriteUInt16BigEndian(rowSpan, b);
395+
offset += 2;
396+
397+
rowSpan = rowBytes.Slice(offset, 2);
398+
ushort deltaA = TiffUtils.ConvertToUShortBigEndian(rowSpan);
399+
a += deltaA;
400+
BinaryPrimitives.WriteUInt16BigEndian(rowSpan, a);
401+
offset += 2;
402+
}
403+
}
404+
}
405+
else
406+
{
407+
for (int y = 0; y < height; y++)
408+
{
409+
int offset = 0;
410+
Span<byte> rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount);
411+
ushort r = TiffUtils.ConvertToUShortLittleEndian(rowBytes.Slice(offset, 2));
412+
offset += 2;
413+
ushort g = TiffUtils.ConvertToUShortLittleEndian(rowBytes.Slice(offset, 2));
414+
offset += 2;
415+
ushort b = TiffUtils.ConvertToUShortLittleEndian(rowBytes.Slice(offset, 2));
416+
offset += 2;
417+
ushort a = TiffUtils.ConvertToUShortLittleEndian(rowBytes.Slice(offset, 2));
418+
offset += 2;
419+
420+
for (int x = 1; x < width; x++)
421+
{
422+
Span<byte> rowSpan = rowBytes.Slice(offset, 2);
423+
ushort deltaR = TiffUtils.ConvertToUShortLittleEndian(rowSpan);
424+
r += deltaR;
425+
BinaryPrimitives.WriteUInt16LittleEndian(rowSpan, r);
426+
offset += 2;
427+
428+
rowSpan = rowBytes.Slice(offset, 2);
429+
ushort deltaG = TiffUtils.ConvertToUShortLittleEndian(rowSpan);
430+
g += deltaG;
431+
BinaryPrimitives.WriteUInt16LittleEndian(rowSpan, g);
432+
offset += 2;
433+
434+
rowSpan = rowBytes.Slice(offset, 2);
435+
ushort deltaB = TiffUtils.ConvertToUShortLittleEndian(rowSpan);
436+
b += deltaB;
437+
BinaryPrimitives.WriteUInt16LittleEndian(rowSpan, b);
438+
offset += 2;
439+
440+
rowSpan = rowBytes.Slice(offset, 2);
441+
ushort deltaA = TiffUtils.ConvertToUShortLittleEndian(rowSpan);
442+
a += deltaA;
443+
BinaryPrimitives.WriteUInt16LittleEndian(rowSpan, a);
444+
offset += 2;
445+
}
446+
}
447+
}
448+
}
449+
322450
private static void UndoRgb96Bit(Span<byte> pixelBytes, int width, bool isBigEndian)
323451
{
324452
int rowBytesCount = width * 12;
@@ -394,5 +522,97 @@ private static void UndoRgb96Bit(Span<byte> pixelBytes, int width, bool isBigEnd
394522
}
395523
}
396524
}
525+
526+
private static void UndoRgba128Bit(Span<byte> pixelBytes, int width, bool isBigEndian)
527+
{
528+
int rowBytesCount = width * 16;
529+
int height = pixelBytes.Length / rowBytesCount;
530+
if (isBigEndian)
531+
{
532+
for (int y = 0; y < height; y++)
533+
{
534+
int offset = 0;
535+
Span<byte> rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount);
536+
uint r = TiffUtils.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4));
537+
offset += 4;
538+
uint g = TiffUtils.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4));
539+
offset += 4;
540+
uint b = TiffUtils.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4));
541+
offset += 4;
542+
uint a = TiffUtils.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4));
543+
offset += 4;
544+
545+
for (int x = 1; x < width; x++)
546+
{
547+
Span<byte> rowSpan = rowBytes.Slice(offset, 4);
548+
uint deltaR = TiffUtils.ConvertToUIntBigEndian(rowSpan);
549+
r += deltaR;
550+
BinaryPrimitives.WriteUInt32BigEndian(rowSpan, r);
551+
offset += 4;
552+
553+
rowSpan = rowBytes.Slice(offset, 4);
554+
uint deltaG = TiffUtils.ConvertToUIntBigEndian(rowSpan);
555+
g += deltaG;
556+
BinaryPrimitives.WriteUInt32BigEndian(rowSpan, g);
557+
offset += 4;
558+
559+
rowSpan = rowBytes.Slice(offset, 4);
560+
uint deltaB = TiffUtils.ConvertToUIntBigEndian(rowSpan);
561+
b += deltaB;
562+
BinaryPrimitives.WriteUInt32BigEndian(rowSpan, b);
563+
offset += 4;
564+
565+
rowSpan = rowBytes.Slice(offset, 4);
566+
uint deltaA = TiffUtils.ConvertToUIntBigEndian(rowSpan);
567+
a += deltaA;
568+
BinaryPrimitives.WriteUInt32BigEndian(rowSpan, a);
569+
offset += 4;
570+
}
571+
}
572+
}
573+
else
574+
{
575+
for (int y = 0; y < height; y++)
576+
{
577+
int offset = 0;
578+
Span<byte> rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount);
579+
uint r = TiffUtils.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4));
580+
offset += 4;
581+
uint g = TiffUtils.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4));
582+
offset += 4;
583+
uint b = TiffUtils.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4));
584+
offset += 4;
585+
uint a = TiffUtils.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4));
586+
offset += 4;
587+
588+
for (int x = 1; x < width; x++)
589+
{
590+
Span<byte> rowSpan = rowBytes.Slice(offset, 4);
591+
uint deltaR = TiffUtils.ConvertToUIntLittleEndian(rowSpan);
592+
r += deltaR;
593+
BinaryPrimitives.WriteUInt32LittleEndian(rowSpan, r);
594+
offset += 4;
595+
596+
rowSpan = rowBytes.Slice(offset, 4);
597+
uint deltaG = TiffUtils.ConvertToUIntLittleEndian(rowSpan);
598+
g += deltaG;
599+
BinaryPrimitives.WriteUInt32LittleEndian(rowSpan, g);
600+
offset += 4;
601+
602+
rowSpan = rowBytes.Slice(offset, 4);
603+
uint deltaB = TiffUtils.ConvertToUIntLittleEndian(rowSpan);
604+
b += deltaB;
605+
BinaryPrimitives.WriteUInt32LittleEndian(rowSpan, b);
606+
offset += 4;
607+
608+
rowSpan = rowBytes.Slice(offset, 4);
609+
uint deltaA = TiffUtils.ConvertToUIntLittleEndian(rowSpan);
610+
a += deltaA;
611+
BinaryPrimitives.WriteUInt32LittleEndian(rowSpan, a);
612+
offset += 4;
613+
}
614+
}
615+
}
616+
}
397617
}
398618
}

tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,7 @@ public void TiffDecoder_CanDecode_32Bit_Gray<TPixel>(TestImageProvider<TPixel> p
287287

288288
[Theory]
289289
[WithFile(Rgba8BitUnassociatedAlpha, PixelTypes.Rgba32)]
290+
[WithFile(Rgba8BitUnassociatedAlphaWithPredictor, PixelTypes.Rgba32)]
290291
public void TiffDecoder_CanDecode_32Bit_WithUnassociatedAlpha<TPixel>(TestImageProvider<TPixel> provider)
291292
where TPixel : unmanaged, IPixel<TPixel>
292293
{
@@ -425,12 +426,16 @@ public void TiffDecoder_CanDecode_Float_96Bit_Gray<TPixel>(TestImageProvider<TPi
425426
[Theory]
426427
[WithFile(Rgba16BitUnassociatedAlphaBigEndian, PixelTypes.Rgba32)]
427428
[WithFile(Rgba16BitUnassociatedAlphaLittleEndian, PixelTypes.Rgba32)]
429+
[WithFile(Rgba16BitUnassociatedAlphaBigEndianWithPredictor, PixelTypes.Rgba32)]
430+
[WithFile(Rgba16BitUnassociatedAlphaLittleEndianWithPredictor, PixelTypes.Rgba32)]
428431
public void TiffDecoder_CanDecode_128Bit_UnassociatedAlpha<TPixel>(TestImageProvider<TPixel> provider)
429432
where TPixel : unmanaged, IPixel<TPixel> => TestTiffDecoder(provider);
430433

431434
[Theory]
432435
[WithFile(Rgba32BitUnassociatedAlphaBigEndian, PixelTypes.Rgba32)]
433436
[WithFile(Rgba32BitUnassociatedAlphaLittleEndian, PixelTypes.Rgba32)]
437+
[WithFile(Rgba32BitUnassociatedAlphaBigEndianWithPredictor, PixelTypes.Rgba32)]
438+
[WithFile(Rgba32BitUnassociatedAlphaLittleEndianWithPredictor, PixelTypes.Rgba32)]
434439
public void TiffDecoder_CanDecode_128Bit_WithUnassociatedAlpha<TPixel>(TestImageProvider<TPixel> provider)
435440
where TPixel : unmanaged, IPixel<TPixel>
436441
{

tests/ImageSharp.Tests/TestImages.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -865,6 +865,7 @@ public static class Tiff
865865
public const string Rgba5BitUnassociatedAlpha = "Tiff/RgbaUnassociatedAlpha5bit.tiff";
866866
public const string Rgba6BitUnassociatedAlpha = "Tiff/RgbaUnassociatedAlpha6bit.tiff";
867867
public const string Rgba8BitUnassociatedAlpha = "Tiff/RgbaUnassociatedAlpha8bit.tiff";
868+
public const string Rgba8BitUnassociatedAlphaWithPredictor = "Tiff/RgbaUnassociatedAlphaPredictor8bit.tiff";
868869
public const string Rgba8BitPlanarUnassociatedAlpha = "Tiff/RgbaUnassociatedAlphaPlanar8bit.tiff";
869870
public const string Rgba10BitUnassociatedAlphaBigEndian = "Tiff/RgbaUnassociatedAlpha10bit_msb.tiff";
870871
public const string Rgba10BitUnassociatedAlphaLittleEndian = "Tiff/RgbaUnassociatedAlpha10bit_lsb.tiff";
@@ -874,6 +875,8 @@ public static class Tiff
874875
public const string Rgba14BitUnassociatedAlphaLittleEndian = "Tiff/RgbaUnassociatedAlpha14bit_lsb.tiff";
875876
public const string Rgba16BitUnassociatedAlphaBigEndian = "Tiff/RgbaUnassociatedAlpha16bit_msb.tiff";
876877
public const string Rgba16BitUnassociatedAlphaLittleEndian = "Tiff/RgbaUnassociatedAlpha16bit_lsb.tiff";
878+
public const string Rgba16BitUnassociatedAlphaBigEndianWithPredictor = "Tiff/RgbaUnassociatedAlphaPredictor16bit_msb.tiff";
879+
public const string Rgba16BitUnassociatedAlphaLittleEndianWithPredictor = "Tiff/RgbaUnassociatedAlphaPredictor16bit_lsb.tiff";
877880
public const string Rgba16BitPlanarUnassociatedAlphaBigEndian = "Tiff/RgbaUnassociatedAlphaPlanar16bit_msb.tiff";
878881
public const string Rgba16BitPlanarUnassociatedAlphaLittleEndian = "Tiff/RgbaUnassociatedAlphaPlanar16bit_lsb.tiff";
879882
public const string Rgba24BitUnassociatedAlphaBigEndian = "Tiff/RgbaUnassociatedAlpha24bit_msb.tiff";
@@ -882,6 +885,8 @@ public static class Tiff
882885
public const string Rgba24BitPlanarUnassociatedAlphaLittleEndian = "Tiff/RgbaUnassociatedAlphaPlanar24bit_lsb.tiff";
883886
public const string Rgba32BitUnassociatedAlphaBigEndian = "Tiff/RgbaUnassociatedAlpha32bit_msb.tiff";
884887
public const string Rgba32BitUnassociatedAlphaLittleEndian = "Tiff/RgbaUnassociatedAlpha32bit_lsb.tiff";
888+
public const string Rgba32BitUnassociatedAlphaBigEndianWithPredictor = "Tiff/RgbaUnassociatedAlphaPredictor32bit_msb.tiff";
889+
public const string Rgba32BitUnassociatedAlphaLittleEndianWithPredictor = "Tiff/RgbaUnassociatedAlphaPredictor32bit_lsb.tiff";
885890
public const string Rgba32BitPlanarUnassociatedAlphaBigEndian = "Tiff/RgbaUnassociatedAlphaPlanar32bit_msb.tiff";
886891
public const string Rgba32BitPlanarUnassociatedAlphaLittleEndian = "Tiff/RgbaUnassociatedAlphaPlanar32bit_lsb.tiff";
887892

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:874ef7a59491ba68364312b7bc27b6620d15ce8b1d5b780f57c6e6d8b919ef1f
3+
size 73922
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:d4faa8617d10ea5f79225c528c0a6d5c36f73d315e46150703df5ca5008ea1bd
3+
size 73922
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:6a17791068b9c3eb40db3157a9103892aaf4a5a74072c93006bfa702ba5545e5
3+
size 80428
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:63fef29d79f8d707c74b6e083de6bb2ad41dde1d9b1aea5bd7729a2f7399132e
3+
size 80344
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:9d91f0740d6df983b5e5fe904c22fe86c2a7ffd86673fb078092d80c96359fc1
3+
size 53666

0 commit comments

Comments
 (0)