From 933f54b148691122cec49266899e80d70e311219 Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Tue, 16 May 2017 20:11:57 +0100 Subject: [PATCH] Add optimised implementation for 8-bit RGB images --- .../Rgb888TiffColor.cs | 50 +++++++++++++++++++ .../TiffColorType.cs | 5 ++ .../Formats/Tiff/TiffDecoderCore.cs | 5 +- .../RgbTiffColorTests.cs | 10 ++++ .../Formats/Tiff/TiffDecoderImageTests.cs | 4 +- 5 files changed, 71 insertions(+), 3 deletions(-) create mode 100644 src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs new file mode 100644 index 0000000000..afe88510e0 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb888TiffColor.cs @@ -0,0 +1,50 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats.Tiff +{ + using System; + using System.Numerics; + using System.Runtime.CompilerServices; + using ImageSharp; + using ImageSharp.PixelFormats; + + /// + /// Implements the 'RGB' photometric interpretation (optimised for 8-bit full color images). + /// + internal static class Rgb888TiffColor + { + /// + /// Decodes pixel data using the current photometric interpretation. + /// + /// The pixel format. + /// The buffer to read image data from. + /// The image buffer to write pixels to. + /// The x-coordinate of the left-hand side of the image block. + /// The y-coordinate of the top of the image block. + /// The width of the image block. + /// The height of the image block. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Decode(byte[] data, PixelAccessor pixels, int left, int top, int width, int height) + where TPixel : struct, IPixel + { + TPixel color = default(TPixel); + + uint offset = 0; + + for (int y = top; y < top + height; y++) + { + for (int x = left; x < left + width; x++) + { + byte r = data[offset++]; + byte g = data[offset++]; + byte b = data[offset++]; + color.PackFromBytes(r, g, b, 255); + pixels[x, y] = color; + } + } + } + } +} diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs index 5d85f6553a..630696b777 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs @@ -59,5 +59,10 @@ namespace ImageSharp.Formats.Tiff /// RGB Full Color. /// Rgb, + + /// + /// RGB Full Color. Optimised implementation for 8-bit images. + /// + Rgb888 } } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index e31706674c..7419f17590 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -395,7 +395,7 @@ namespace ImageSharp.Formats { if (this.BitsPerSample[0] == 8 && this.BitsPerSample[1] == 8 && this.BitsPerSample[2] == 8) { - this.ColorType = TiffColorType.Rgb; + this.ColorType = TiffColorType.Rgb888; } else { @@ -529,6 +529,9 @@ namespace ImageSharp.Formats case TiffColorType.Rgb: RgbTiffColor.Decode(data, this.BitsPerSample, pixels, left, top, width, height); break; + case TiffColorType.Rgb888: + Rgb888TiffColor.Decode(data, pixels, left, top, width, height); + break; case TiffColorType.PaletteColor: PaletteTiffColor.Decode(data, this.BitsPerSample, this.ColorMap, pixels, left, top, width, height); break; diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs index c67913d655..06122484bf 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs @@ -147,5 +147,15 @@ namespace ImageSharp.Tests RgbTiffColor.Decode(inputData, bitsPerSample, pixels, left, top, width, height); }); } + + [Theory] + [MemberData(nameof(Rgb8_Data))] + public void Decode_WritesPixelData_8Bit(byte[] inputData, uint[] bitsPerSample, int left, int top, int width, int height, Rgba32[][] expectedResult) + { + AssertDecode(expectedResult, pixels => + { + Rgb888TiffColor.Decode(inputData, pixels, left, top, width, height); + }); + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs index 2ba37ac494..693ddfea1a 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderImageTests.cs @@ -202,8 +202,8 @@ namespace ImageSharp.Tests [InlineData(true, TiffPhotometricInterpretation.PaletteColor, new[] { 1 }, TiffColorType.PaletteColor)] [InlineData(false, TiffPhotometricInterpretation.Rgb, new[] { 4, 4, 4 }, TiffColorType.Rgb)] [InlineData(true, TiffPhotometricInterpretation.Rgb, new[] { 4, 4, 4 }, TiffColorType.Rgb)] - [InlineData(false, TiffPhotometricInterpretation.Rgb, new[] { 8, 8, 8 }, TiffColorType.Rgb)] - [InlineData(true, TiffPhotometricInterpretation.Rgb, new[] { 8, 8, 8 }, TiffColorType.Rgb)] + [InlineData(false, TiffPhotometricInterpretation.Rgb, new[] { 8, 8, 8 }, TiffColorType.Rgb888)] + [InlineData(true, TiffPhotometricInterpretation.Rgb, new[] { 8, 8, 8 }, TiffColorType.Rgb888)] public void ReadImageFormat_DeterminesCorrectColorImplementation(bool isLittleEndian, ushort photometricInterpretation, int[] bitsPerSample, int colorType) { Stream stream = CreateTiffGenIfd()