mirror of https://github.com/SixLabors/ImageSharp
9 changed files with 342 additions and 60 deletions
@ -0,0 +1,53 @@ |
|||||
|
// <copyright file="WhiteIsZero1TiffColor.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.Runtime.CompilerServices; |
||||
|
using ImageSharp; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Implements the 'WhiteIsZero' photometric interpretation (optimised for bilevel images).
|
||||
|
/// </summary>
|
||||
|
internal static class WhiteIsZero1TiffColor |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Decodes pixel data using the current photometric interpretation.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TColor">The pixel format.</typeparam>
|
||||
|
/// <param name="data">The buffer to read image data from.</param>
|
||||
|
/// <param name="pixels">The image buffer to write pixels to.</param>
|
||||
|
/// <param name="left">The x-coordinate of the left-hand side of the image block.</param>
|
||||
|
/// <param name="top">The y-coordinate of the top of the image block.</param>
|
||||
|
/// <param name="width">The width of the image block.</param>
|
||||
|
/// <param name="height">The height of the image block.</param>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void Decode<TColor>(byte[] data, PixelAccessor<TColor> pixels, int left, int top, int width, int height) |
||||
|
where TColor : struct, IPixel<TColor> |
||||
|
{ |
||||
|
TColor color = default(TColor); |
||||
|
|
||||
|
uint offset = 0; |
||||
|
|
||||
|
for (int y = top; y < top + height; y++) |
||||
|
{ |
||||
|
for (int x = left; x < left + width; x += 8) |
||||
|
{ |
||||
|
byte b = data[offset++]; |
||||
|
int maxShift = Math.Min(left + width - x, 8); |
||||
|
|
||||
|
for (int shift = 0; shift < maxShift; shift++) |
||||
|
{ |
||||
|
int bit = (b >> (7 - shift)) & 1; |
||||
|
byte intensity = (bit == 1) ? (byte)0 : (byte)255; |
||||
|
color.PackFromBytes(intensity, intensity, intensity, 255); |
||||
|
pixels[x + shift, y] = color; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,61 @@ |
|||||
|
// <copyright file="WhiteIsZero4TiffColor.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.Runtime.CompilerServices; |
||||
|
using ImageSharp; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Implements the 'WhiteIsZero' photometric interpretation (optimised for 4-bit grayscale images).
|
||||
|
/// </summary>
|
||||
|
internal static class WhiteIsZero4TiffColor |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Decodes pixel data using the current photometric interpretation.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TColor">The pixel format.</typeparam>
|
||||
|
/// <param name="data">The buffer to read image data from.</param>
|
||||
|
/// <param name="pixels">The image buffer to write pixels to.</param>
|
||||
|
/// <param name="left">The x-coordinate of the left-hand side of the image block.</param>
|
||||
|
/// <param name="top">The y-coordinate of the top of the image block.</param>
|
||||
|
/// <param name="width">The width of the image block.</param>
|
||||
|
/// <param name="height">The height of the image block.</param>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void Decode<TColor>(byte[] data, PixelAccessor<TColor> pixels, int left, int top, int width, int height) |
||||
|
where TColor : struct, IPixel<TColor> |
||||
|
{ |
||||
|
TColor color = default(TColor); |
||||
|
|
||||
|
uint offset = 0; |
||||
|
bool isOddWidth = (width & 1) == 1; |
||||
|
|
||||
|
for (int y = top; y < top + height; y++) |
||||
|
{ |
||||
|
for (int x = left; x < left + width - 1; x += 2) |
||||
|
{ |
||||
|
byte byteData = data[offset++]; |
||||
|
|
||||
|
byte intensity1 = (byte)((15 - ((byteData & 0xF0) >> 4)) * 17); |
||||
|
color.PackFromBytes(intensity1, intensity1, intensity1, 255); |
||||
|
pixels[x, y] = color; |
||||
|
|
||||
|
byte intensity2 = (byte)((15 - (byteData & 0x0F)) * 17); |
||||
|
color.PackFromBytes(intensity2, intensity2, intensity2, 255); |
||||
|
pixels[x + 1, y] = color; |
||||
|
} |
||||
|
|
||||
|
if (isOddWidth) |
||||
|
{ |
||||
|
byte byteData = data[offset++]; |
||||
|
|
||||
|
byte intensity1 = (byte)((15 - ((byteData & 0xF0) >> 4)) * 17); |
||||
|
color.PackFromBytes(intensity1, intensity1, intensity1, 255); |
||||
|
pixels[left + width - 1, y] = color; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,51 +0,0 @@ |
|||||
// <copyright file="WhiteIsZero8TiffColorTests.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.Collections.Generic; |
|
||||
using Xunit; |
|
||||
|
|
||||
using ImageSharp.Formats.Tiff; |
|
||||
|
|
||||
public class WhiteIsZero8TiffColorTests : PhotometricInterpretationTestBase |
|
||||
{ |
|
||||
private static Color Gray000 = new Color(255, 255, 255, 255); |
|
||||
private static Color Gray128 = new Color(127, 127, 127, 255); |
|
||||
private static Color Gray255 = new Color(0, 0, 0, 255); |
|
||||
|
|
||||
private static byte[] GrayscaleBytes4x4 = new byte[] { 128, 255, 000, 255, |
|
||||
255, 255, 255, 255, |
|
||||
000, 128, 128, 255, |
|
||||
255, 000, 255, 128 }; |
|
||||
|
|
||||
private static Color[][] GrayscaleResult4x4 = new[] { new[] { Gray128, Gray255, Gray000, Gray255 }, |
|
||||
new[] { Gray255, Gray255, Gray255, Gray255 }, |
|
||||
new[] { Gray000, Gray128, Gray128, Gray255 }, |
|
||||
new[] { Gray255, Gray000, Gray255, Gray128 }}; |
|
||||
|
|
||||
public static IEnumerable<object[]> DecodeData |
|
||||
{ |
|
||||
get |
|
||||
{ |
|
||||
yield return new object[] { GrayscaleBytes4x4, 0, 0, 4, 4, GrayscaleResult4x4 }; |
|
||||
yield return new object[] { GrayscaleBytes4x4, 0, 0, 4, 4, Offset(GrayscaleResult4x4, 0, 0, 6, 6) }; |
|
||||
yield return new object[] { GrayscaleBytes4x4, 1, 0, 4, 4, Offset(GrayscaleResult4x4, 1, 0, 6, 6) }; |
|
||||
yield return new object[] { GrayscaleBytes4x4, 0, 1, 4, 4, Offset(GrayscaleResult4x4, 0, 1, 6, 6) }; |
|
||||
yield return new object[] { GrayscaleBytes4x4, 1, 1, 4, 4, Offset(GrayscaleResult4x4, 1, 1, 6, 6) }; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
[Theory] |
|
||||
[MemberData(nameof(DecodeData))] |
|
||||
public void Decode_WritesPixelData(byte[] inputData, int left, int top, int width, int height, Color[][] expectedResult) |
|
||||
{ |
|
||||
AssertDecode(expectedResult, pixels => |
|
||||
{ |
|
||||
WhiteIsZero8TiffColor.Decode(inputData, pixels, left, top, width, height); |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,152 @@ |
|||||
|
// <copyright file="WhiteIsZeroTiffColorTests.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.Collections.Generic; |
||||
|
using Xunit; |
||||
|
|
||||
|
using ImageSharp.Formats.Tiff; |
||||
|
|
||||
|
public class WhiteIsZeroTiffColorTests : PhotometricInterpretationTestBase |
||||
|
{ |
||||
|
private static Color Gray000 = new Color(255, 255, 255, 255); |
||||
|
private static Color Gray128 = new Color(127, 127, 127, 255); |
||||
|
private static Color Gray255 = new Color(0, 0, 0, 255); |
||||
|
private static Color Gray0 = new Color(255, 255, 255, 255); |
||||
|
private static Color Gray8 = new Color(119, 119, 119, 255); |
||||
|
private static Color GrayF = new Color(0, 0, 0, 255); |
||||
|
private static Color Bit0 = new Color(255, 255, 255, 255); |
||||
|
private static Color Bit1 = new Color(0, 0, 0, 255); |
||||
|
|
||||
|
private static byte[] Bilevel_Bytes4x4 = new byte[] { 0b01010000, |
||||
|
0b11110000, |
||||
|
0b01110000, |
||||
|
0b10010000 }; |
||||
|
|
||||
|
private static Color[][] Bilevel_Result4x4 = new[] { new[] { Bit0, Bit1, Bit0, Bit1 }, |
||||
|
new[] { Bit1, Bit1, Bit1, Bit1 }, |
||||
|
new[] { Bit0, Bit1, Bit1, Bit1 }, |
||||
|
new[] { Bit1, Bit0, Bit0, Bit1 }}; |
||||
|
|
||||
|
private static byte[] Bilevel_Bytes12x4 = new byte[] { 0b01010101, 0b01010000, |
||||
|
0b11111111, 0b11111111, |
||||
|
0b01101001, 0b10100000, |
||||
|
0b10010000, 0b01100000}; |
||||
|
|
||||
|
private static Color[][] Bilevel_Result12x4 = new[] { new[] { Bit0, Bit1, Bit0, Bit1, Bit0, Bit1, Bit0, Bit1, Bit0, Bit1, Bit0, Bit1 }, |
||||
|
new[] { Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1, Bit1 }, |
||||
|
new[] { Bit0, Bit1, Bit1, Bit0, Bit1, Bit0, Bit0, Bit1, Bit1, Bit0, Bit1, Bit0 }, |
||||
|
new[] { Bit1, Bit0, Bit0, Bit1, Bit0, Bit0, Bit0, Bit0, Bit0, Bit1, Bit1, Bit0 }}; |
||||
|
|
||||
|
private static byte[] Grayscale4_Bytes4x4 = new byte[] { 0x8F, 0x0F, |
||||
|
0xFF, 0xFF, |
||||
|
0x08, 0x8F, |
||||
|
0xF0, 0xF8 }; |
||||
|
|
||||
|
private static Color[][] Grayscale4_Result4x4 = new[] { new[] { Gray8, GrayF, Gray0, GrayF }, |
||||
|
new[] { GrayF, GrayF, GrayF, GrayF }, |
||||
|
new[] { Gray0, Gray8, Gray8, GrayF }, |
||||
|
new[] { GrayF, Gray0, GrayF, Gray8 }}; |
||||
|
|
||||
|
private static byte[] Grayscale4_Bytes3x4 = new byte[] { 0x8F, 0x00, |
||||
|
0xFF, 0xF0, |
||||
|
0x08, 0x80, |
||||
|
0xF0, 0xF0 }; |
||||
|
|
||||
|
private static Color[][] Grayscale4_Result3x4 = new[] { new[] { Gray8, GrayF, Gray0 }, |
||||
|
new[] { GrayF, GrayF, GrayF }, |
||||
|
new[] { Gray0, Gray8, Gray8 }, |
||||
|
new[] { GrayF, Gray0, GrayF }}; |
||||
|
|
||||
|
private static byte[] Grayscale8_Bytes4x4 = new byte[] { 128, 255, 000, 255, |
||||
|
255, 255, 255, 255, |
||||
|
000, 128, 128, 255, |
||||
|
255, 000, 255, 128 }; |
||||
|
|
||||
|
private static Color[][] Grayscale8_Result4x4 = new[] { new[] { Gray128, Gray255, Gray000, Gray255 }, |
||||
|
new[] { Gray255, Gray255, Gray255, Gray255 }, |
||||
|
new[] { Gray000, Gray128, Gray128, Gray255 }, |
||||
|
new[] { Gray255, Gray000, Gray255, Gray128 }}; |
||||
|
|
||||
|
public static IEnumerable<object[]> Bilevel_Data |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
yield return new object[] { Bilevel_Bytes4x4, 1, 0, 0, 4, 4, Bilevel_Result4x4 }; |
||||
|
yield return new object[] { Bilevel_Bytes4x4, 1, 0, 0, 4, 4, Offset(Bilevel_Result4x4, 0, 0, 6, 6) }; |
||||
|
yield return new object[] { Bilevel_Bytes4x4, 1, 1, 0, 4, 4, Offset(Bilevel_Result4x4, 1, 0, 6, 6) }; |
||||
|
yield return new object[] { Bilevel_Bytes4x4, 1, 0, 1, 4, 4, Offset(Bilevel_Result4x4, 0, 1, 6, 6) }; |
||||
|
yield return new object[] { Bilevel_Bytes4x4, 1, 1, 1, 4, 4, Offset(Bilevel_Result4x4, 1, 1, 6, 6) }; |
||||
|
|
||||
|
yield return new object[] { Bilevel_Bytes12x4, 1, 0, 0, 12, 4, Bilevel_Result12x4 }; |
||||
|
yield return new object[] { Bilevel_Bytes12x4, 1, 0, 0, 12, 4, Offset(Bilevel_Result12x4, 0, 0, 18, 6) }; |
||||
|
yield return new object[] { Bilevel_Bytes12x4, 1, 1, 0, 12, 4, Offset(Bilevel_Result12x4, 1, 0, 18, 6) }; |
||||
|
yield return new object[] { Bilevel_Bytes12x4, 1, 0, 1, 12, 4, Offset(Bilevel_Result12x4, 0, 1, 18, 6) }; |
||||
|
yield return new object[] { Bilevel_Bytes12x4, 1, 1, 1, 12, 4, Offset(Bilevel_Result12x4, 1, 1, 18, 6) }; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static IEnumerable<object[]> Grayscale4_Data |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
yield return new object[] { Grayscale4_Bytes4x4, 4, 0, 0, 4, 4, Grayscale4_Result4x4 }; |
||||
|
yield return new object[] { Grayscale4_Bytes4x4, 4, 0, 0, 4, 4, Offset(Grayscale4_Result4x4, 0, 0, 6, 6) }; |
||||
|
yield return new object[] { Grayscale4_Bytes4x4, 4, 1, 0, 4, 4, Offset(Grayscale4_Result4x4, 1, 0, 6, 6) }; |
||||
|
yield return new object[] { Grayscale4_Bytes4x4, 4, 0, 1, 4, 4, Offset(Grayscale4_Result4x4, 0, 1, 6, 6) }; |
||||
|
yield return new object[] { Grayscale4_Bytes4x4, 4, 1, 1, 4, 4, Offset(Grayscale4_Result4x4, 1, 1, 6, 6) }; |
||||
|
|
||||
|
yield return new object[] { Grayscale4_Bytes3x4, 4, 0, 0, 3, 4, Grayscale4_Result3x4 }; |
||||
|
yield return new object[] { Grayscale4_Bytes3x4, 4, 0, 0, 3, 4, Offset(Grayscale4_Result3x4, 0, 0, 6, 6) }; |
||||
|
yield return new object[] { Grayscale4_Bytes3x4, 4, 1, 0, 3, 4, Offset(Grayscale4_Result3x4, 1, 0, 6, 6) }; |
||||
|
yield return new object[] { Grayscale4_Bytes3x4, 4, 0, 1, 3, 4, Offset(Grayscale4_Result3x4, 0, 1, 6, 6) }; |
||||
|
yield return new object[] { Grayscale4_Bytes3x4, 4, 1, 1, 3, 4, Offset(Grayscale4_Result3x4, 1, 1, 6, 6) }; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static IEnumerable<object[]> Grayscale8_Data |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
yield return new object[] { Grayscale8_Bytes4x4, 8, 0, 0, 4, 4, Grayscale8_Result4x4 }; |
||||
|
yield return new object[] { Grayscale8_Bytes4x4, 8, 0, 0, 4, 4, Offset(Grayscale8_Result4x4, 0, 0, 6, 6) }; |
||||
|
yield return new object[] { Grayscale8_Bytes4x4, 8, 1, 0, 4, 4, Offset(Grayscale8_Result4x4, 1, 0, 6, 6) }; |
||||
|
yield return new object[] { Grayscale8_Bytes4x4, 8, 0, 1, 4, 4, Offset(Grayscale8_Result4x4, 0, 1, 6, 6) }; |
||||
|
yield return new object[] { Grayscale8_Bytes4x4, 8, 1, 1, 4, 4, Offset(Grayscale8_Result4x4, 1, 1, 6, 6) }; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[MemberData(nameof(Bilevel_Data))] |
||||
|
public void Decode_WritesPixelData_Bilevel(byte[] inputData, int bitsPerSample, int left, int top, int width, int height, Color[][] expectedResult) |
||||
|
{ |
||||
|
AssertDecode(expectedResult, pixels => |
||||
|
{ |
||||
|
WhiteIsZero1TiffColor.Decode(inputData, pixels, left, top, width, height); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[MemberData(nameof(Grayscale4_Data))] |
||||
|
public void Decode_WritesPixelData_4Bit(byte[] inputData, int bitsPerSample, int left, int top, int width, int height, Color[][] expectedResult) |
||||
|
{ |
||||
|
AssertDecode(expectedResult, pixels => |
||||
|
{ |
||||
|
WhiteIsZero4TiffColor.Decode(inputData, pixels, left, top, width, height); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[MemberData(nameof(Grayscale8_Data))] |
||||
|
public void Decode_WritesPixelData_8Bit(byte[] inputData, int bitsPerSample, int left, int top, int width, int height, Color[][] expectedResult) |
||||
|
{ |
||||
|
AssertDecode(expectedResult, pixels => |
||||
|
{ |
||||
|
WhiteIsZero8TiffColor.Decode(inputData, pixels, left, top, width, height); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue