mirror of https://github.com/SixLabors/ImageSharp
15 changed files with 432 additions and 36 deletions
@ -0,0 +1,144 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Buffers; |
|||
using System.Numerics; |
|||
using System.Runtime.InteropServices; |
|||
using SixLabors.ImageSharp.ColorProfiles; |
|||
using SixLabors.ImageSharp.ColorProfiles.Icc; |
|||
using SixLabors.ImageSharp.Formats.Tiff.Utils; |
|||
using SixLabors.ImageSharp.Memory; |
|||
using SixLabors.ImageSharp.Metadata; |
|||
using SixLabors.ImageSharp.Metadata.Profiles.Icc; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; |
|||
|
|||
/// <summary>
|
|||
/// Implements decoding pixel data with photometric interpretation of type 'CieLab' with the planar configuration.
|
|||
/// Each channel is represented with 16 bits.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The type of pixel format.</typeparam>
|
|||
internal class CieLab16PlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel> |
|||
where TPixel : unmanaged, IPixel<TPixel> |
|||
{ |
|||
private readonly ColorProfileConverter colorProfileConverter; |
|||
private readonly Configuration configuration; |
|||
private readonly bool isBigEndian; |
|||
|
|||
// libtiff encodes 16-bit Lab as:
|
|||
// L* : unsigned [0, 65535] mapping to [0, 100]
|
|||
// a*, b* : signed [-32768, 32767], values are 256x the 1976 a*, b* values.
|
|||
private const float Inv65535 = 1f / 65535f; |
|||
private const float Inv256 = 1f / 256f; |
|||
|
|||
public CieLab16PlanarTiffColor( |
|||
Configuration configuration, |
|||
DecoderOptions decoderOptions, |
|||
ImageFrameMetadata metadata, |
|||
MemoryAllocator allocator, |
|||
bool isBigEndian) |
|||
{ |
|||
this.isBigEndian = isBigEndian; |
|||
this.configuration = configuration; |
|||
|
|||
if (decoderOptions.TryGetIccProfileForColorConversion(metadata.IccProfile, out IccProfile? iccProfile)) |
|||
{ |
|||
ColorConversionOptions options = new() |
|||
{ |
|||
SourceIccProfile = iccProfile, |
|||
TargetIccProfile = CompactSrgbV4Profile.Profile, |
|||
MemoryAllocator = allocator |
|||
}; |
|||
|
|||
this.colorProfileConverter = new ColorProfileConverter(options); |
|||
} |
|||
else |
|||
{ |
|||
ColorConversionOptions options = new() |
|||
{ |
|||
MemoryAllocator = allocator |
|||
}; |
|||
|
|||
this.colorProfileConverter = new ColorProfileConverter(options); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override void Decode(IMemoryOwner<byte>[] data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) |
|||
{ |
|||
Span<byte> lPlane = data[0].GetSpan(); |
|||
Span<byte> aPlane = data[1].GetSpan(); |
|||
Span<byte> bPlane = data[2].GetSpan(); |
|||
|
|||
// Allocate temporary buffers to hold the LAB -> RGB conversion.
|
|||
// This should be the maximum width of a row.
|
|||
using IMemoryOwner<Rgb> rgbBuffer = this.colorProfileConverter.Options.MemoryAllocator.Allocate<Rgb>(width); |
|||
using IMemoryOwner<Vector4> vectorBuffer = this.colorProfileConverter.Options.MemoryAllocator.Allocate<Vector4>(width); |
|||
|
|||
Span<Rgb> rgbRow = rgbBuffer.Memory.Span; |
|||
Span<Vector4> vectorRow = vectorBuffer.Memory.Span; |
|||
|
|||
// Reuse the rgbRow span for lab data since both are 3-float structs, avoiding an extra allocation.
|
|||
Span<CieLab> cieLabRow = MemoryMarshal.Cast<Rgb, CieLab>(rgbRow); |
|||
|
|||
int stride = width * 2; |
|||
|
|||
if (this.isBigEndian) |
|||
{ |
|||
for (int y = 0; y < height; y++) |
|||
{ |
|||
int rowBase = y * stride; |
|||
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(top + y).Slice(left, width); |
|||
|
|||
for (int x = 0; x < width; x++) |
|||
{ |
|||
int i = rowBase + (x * 2); |
|||
|
|||
ushort lRaw = TiffUtilities.ConvertToUShortBigEndian(lPlane.Slice(i, 2)); |
|||
short aRaw = unchecked((short)TiffUtilities.ConvertToUShortBigEndian(aPlane.Slice(i, 2))); |
|||
short bRaw = unchecked((short)TiffUtilities.ConvertToUShortBigEndian(bPlane.Slice(i, 2))); |
|||
|
|||
float l = lRaw * 100f * Inv65535; |
|||
float a = aRaw * Inv256; |
|||
float b = bRaw * Inv256; |
|||
|
|||
cieLabRow[x] = new CieLab(l, a, b); |
|||
} |
|||
|
|||
// Convert CIE Lab -> Rgb -> Vector4 -> TPixel
|
|||
this.colorProfileConverter.Convert<CieLab, Rgb>(cieLabRow, rgbRow); |
|||
Rgb.ToScaledVector4(rgbRow, vectorRow); |
|||
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, vectorRow, pixelRow, PixelConversionModifiers.Scale); |
|||
} |
|||
|
|||
return; |
|||
} |
|||
|
|||
for (int y = 0; y < height; y++) |
|||
{ |
|||
int rowBase = y * stride; |
|||
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(top + y).Slice(left, width); |
|||
|
|||
for (int x = 0; x < width; x++) |
|||
{ |
|||
int i = rowBase + (x * 2); |
|||
|
|||
ushort lRaw = TiffUtilities.ConvertToUShortLittleEndian(lPlane.Slice(i, 2)); |
|||
short aRaw = unchecked((short)TiffUtilities.ConvertToUShortLittleEndian(aPlane.Slice(i, 2))); |
|||
short bRaw = unchecked((short)TiffUtilities.ConvertToUShortLittleEndian(bPlane.Slice(i, 2))); |
|||
|
|||
float l = lRaw * 100f * Inv65535; |
|||
float a = aRaw * Inv256; |
|||
float b = bRaw * Inv256; |
|||
|
|||
cieLabRow[x] = new CieLab(l, a, b); |
|||
} |
|||
|
|||
// Convert CIE Lab -> Rgb -> Vector4 -> TPixel
|
|||
this.colorProfileConverter.Convert<CieLab, Rgb>(cieLabRow, rgbRow); |
|||
Rgb.ToScaledVector4(rgbRow, vectorRow); |
|||
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, vectorRow, pixelRow, PixelConversionModifiers.Scale); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,140 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Buffers; |
|||
using System.Numerics; |
|||
using System.Runtime.InteropServices; |
|||
using SixLabors.ImageSharp.ColorProfiles; |
|||
using SixLabors.ImageSharp.ColorProfiles.Icc; |
|||
using SixLabors.ImageSharp.Formats.Tiff.Utils; |
|||
using SixLabors.ImageSharp.Memory; |
|||
using SixLabors.ImageSharp.Metadata; |
|||
using SixLabors.ImageSharp.Metadata.Profiles.Icc; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; |
|||
|
|||
/// <summary>
|
|||
/// Implements decoding pixel data with photometric interpretation of type 'CieLab'.
|
|||
/// Each channel is represented with 16 bits.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The type of pixel format.</typeparam>
|
|||
internal class CieLab16TiffColor<TPixel> : TiffBaseColorDecoder<TPixel> |
|||
where TPixel : unmanaged, IPixel<TPixel> |
|||
{ |
|||
private readonly ColorProfileConverter colorProfileConverter; |
|||
private readonly Configuration configuration; |
|||
private readonly bool isBigEndian; |
|||
|
|||
// libtiff encodes 16-bit Lab as:
|
|||
// L* : unsigned [0, 65535] mapping to [0, 100]
|
|||
// a*, b* : signed [-32768, 32767], values are 256x the 1976 a*, b* values.
|
|||
private const float Inv65535 = 1f / 65535f; |
|||
private const float Inv256 = 1f / 256f; |
|||
|
|||
public CieLab16TiffColor( |
|||
Configuration configuration, |
|||
DecoderOptions decoderOptions, |
|||
ImageFrameMetadata metadata, |
|||
MemoryAllocator allocator, |
|||
bool isBigEndian) |
|||
{ |
|||
this.isBigEndian = isBigEndian; |
|||
this.configuration = configuration; |
|||
|
|||
if (decoderOptions.TryGetIccProfileForColorConversion(metadata.IccProfile, out IccProfile? iccProfile)) |
|||
{ |
|||
ColorConversionOptions options = new() |
|||
{ |
|||
SourceIccProfile = iccProfile, |
|||
TargetIccProfile = CompactSrgbV4Profile.Profile, |
|||
MemoryAllocator = allocator |
|||
}; |
|||
|
|||
this.colorProfileConverter = new ColorProfileConverter(options); |
|||
} |
|||
else |
|||
{ |
|||
ColorConversionOptions options = new() |
|||
{ |
|||
MemoryAllocator = allocator |
|||
}; |
|||
|
|||
this.colorProfileConverter = new ColorProfileConverter(options); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) |
|||
{ |
|||
int offset = 0; |
|||
|
|||
// Allocate temporary buffers to hold the LAB -> RGB conversion.
|
|||
// This should be the maximum width of a row.
|
|||
using IMemoryOwner<Rgb> rgbBuffer = this.colorProfileConverter.Options.MemoryAllocator.Allocate<Rgb>(width); |
|||
using IMemoryOwner<Vector4> vectorBuffer = this.colorProfileConverter.Options.MemoryAllocator.Allocate<Vector4>(width); |
|||
|
|||
Span<Rgb> rgbRow = rgbBuffer.Memory.Span; |
|||
Span<Vector4> vectorRow = vectorBuffer.Memory.Span; |
|||
|
|||
// Reuse the rgbRow span for lab data since both are 3-float structs, avoiding an extra allocation.
|
|||
Span<CieLab> cieLabRow = MemoryMarshal.Cast<Rgb, CieLab>(rgbRow); |
|||
|
|||
if (this.isBigEndian) |
|||
{ |
|||
for (int y = top; y < top + height; y++) |
|||
{ |
|||
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width); |
|||
|
|||
for (int x = 0; x < pixelRow.Length; x++) |
|||
{ |
|||
ushort lRaw = TiffUtilities.ConvertToUShortBigEndian(data.Slice(offset, 2)); |
|||
offset += 2; |
|||
short aRaw = unchecked((short)TiffUtilities.ConvertToUShortBigEndian(data.Slice(offset, 2))); |
|||
offset += 2; |
|||
short bRaw = unchecked((short)TiffUtilities.ConvertToUShortBigEndian(data.Slice(offset, 2))); |
|||
offset += 2; |
|||
|
|||
float l = lRaw * 100f * Inv65535; |
|||
float a = aRaw * Inv256; |
|||
float b = bRaw * Inv256; |
|||
|
|||
cieLabRow[x] = new CieLab(l, a, b); |
|||
} |
|||
|
|||
// Convert CIE Lab -> Rgb -> Vector4 -> TPixel
|
|||
this.colorProfileConverter.Convert<CieLab, Rgb>(cieLabRow, rgbRow); |
|||
Rgb.ToScaledVector4(rgbRow, vectorRow); |
|||
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, vectorRow, pixelRow, PixelConversionModifiers.Scale); |
|||
} |
|||
|
|||
return; |
|||
} |
|||
|
|||
for (int y = top; y < top + height; y++) |
|||
{ |
|||
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width); |
|||
|
|||
for (int x = 0; x < pixelRow.Length; x++) |
|||
{ |
|||
ushort lRaw = TiffUtilities.ConvertToUShortLittleEndian(data.Slice(offset, 2)); |
|||
offset += 2; |
|||
short aRaw = unchecked((short)TiffUtilities.ConvertToUShortLittleEndian(data.Slice(offset, 2))); |
|||
offset += 2; |
|||
short bRaw = unchecked((short)TiffUtilities.ConvertToUShortLittleEndian(data.Slice(offset, 2))); |
|||
offset += 2; |
|||
|
|||
float l = lRaw * 100f * Inv65535; |
|||
float a = aRaw * Inv256; |
|||
float b = bRaw * Inv256; |
|||
|
|||
cieLabRow[x] = new CieLab(l, a, b); |
|||
} |
|||
|
|||
// Convert CIE Lab -> Rgb -> Vector4 -> TPixel
|
|||
this.colorProfileConverter.Convert<CieLab, Rgb>(cieLabRow, rgbRow); |
|||
Rgb.ToScaledVector4(rgbRow, vectorRow); |
|||
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, vectorRow, pixelRow, PixelConversionModifiers.Scale); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:8dd7d54f362b33c2d2d4b4b3cb3bbece23c14138073403234db7b53012939101 |
|||
size 383 |
|||
@ -0,0 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:370af73b800622a671c0718b2c137ead8401adf20c39b45f153d2c9bb09b40ed |
|||
size 385 |
|||
@ -0,0 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:9494eea65b2c5b6ef033fb89b4fee2ef97d07773ae4b3988da972e1ba152b890 |
|||
size 64418 |
|||
@ -0,0 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:cfb7fe9e93362fa121d97fa05f61242733d79fa9fa06bb7153e1773e827567dd |
|||
size 601124 |
|||
Loading…
Reference in new issue