mirror of https://github.com/SixLabors/ImageSharp
120 changed files with 2477 additions and 1121 deletions
@ -1,5 +1,5 @@ |
|||
blank_issues_enabled: false |
|||
contact_links: |
|||
- name: Feature Request |
|||
url: https://github.com/SixLabors/ImageSharp/discussions?discussions_q=category%3AIdeas |
|||
url: https://github.com/SixLabors/ImageSharp/discussions/categories/ideas |
|||
about: Share ideas for new features for this project. |
|||
|
|||
@ -0,0 +1,77 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using SixLabors.ImageSharp.Formats.Tiff.Utils; |
|||
using SixLabors.ImageSharp.Memory; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation |
|||
{ |
|||
/// <summary>
|
|||
/// Implements the 'RGB' photometric interpretation with an alpha channel and with 16 bits for each channel.
|
|||
/// </summary>
|
|||
internal class Rgba16161616TiffColor<TPixel> : TiffBaseColorDecoder<TPixel> |
|||
where TPixel : unmanaged, IPixel<TPixel> |
|||
{ |
|||
private readonly bool isBigEndian; |
|||
|
|||
private readonly Configuration configuration; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Rgba16161616TiffColor{TPixel}" /> class.
|
|||
/// </summary>
|
|||
/// <param name="configuration">The configuration.</param>
|
|||
/// <param name="isBigEndian">if set to <c>true</c> decodes the pixel data as big endian, otherwise as little endian.</param>
|
|||
public Rgba16161616TiffColor(Configuration configuration, bool isBigEndian) |
|||
{ |
|||
this.configuration = configuration; |
|||
this.isBigEndian = isBigEndian; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) |
|||
{ |
|||
// Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those,
|
|||
// we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623
|
|||
Rgba64 rgba = TiffUtils.Rgba64Default; |
|||
var color = default(TPixel); |
|||
color.FromVector4(TiffUtils.Vector4Default); |
|||
|
|||
int offset = 0; |
|||
|
|||
for (int y = top; y < top + height; y++) |
|||
{ |
|||
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width); |
|||
|
|||
if (this.isBigEndian) |
|||
{ |
|||
for (int x = 0; x < pixelRow.Length; x++) |
|||
{ |
|||
ulong r = TiffUtils.ConvertToUShortBigEndian(data.Slice(offset, 2)); |
|||
offset += 2; |
|||
ulong g = TiffUtils.ConvertToUShortBigEndian(data.Slice(offset, 2)); |
|||
offset += 2; |
|||
ulong b = TiffUtils.ConvertToUShortBigEndian(data.Slice(offset, 2)); |
|||
offset += 2; |
|||
ulong a = TiffUtils.ConvertToUShortBigEndian(data.Slice(offset, 2)); |
|||
offset += 2; |
|||
|
|||
pixelRow[x] = TiffUtils.ColorFromRgba64(rgba, r, g, b, a, color); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
int byteCount = pixelRow.Length * 8; |
|||
PixelOperations<TPixel>.Instance.FromRgba64Bytes( |
|||
this.configuration, |
|||
data.Slice(offset, byteCount), |
|||
pixelRow, |
|||
pixelRow.Length); |
|||
|
|||
offset += byteCount; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,75 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Buffers; |
|||
using SixLabors.ImageSharp.Formats.Tiff.Utils; |
|||
using SixLabors.ImageSharp.Memory; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation |
|||
{ |
|||
/// <summary>
|
|||
/// Implements the 'RGB' photometric interpretation with an alpha channel and with 'Planar' layout for each color channel with 16 bit.
|
|||
/// </summary>
|
|||
internal class Rgba16PlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel> |
|||
where TPixel : unmanaged, IPixel<TPixel> |
|||
{ |
|||
private readonly bool isBigEndian; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Rgba16PlanarTiffColor{TPixel}" /> class.
|
|||
/// </summary>
|
|||
/// <param name="isBigEndian">if set to <c>true</c> decodes the pixel data as big endian, otherwise as little endian.</param>
|
|||
public Rgba16PlanarTiffColor(bool isBigEndian) => this.isBigEndian = isBigEndian; |
|||
|
|||
/// <inheritdoc/>
|
|||
public override void Decode(IMemoryOwner<byte>[] data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) |
|||
{ |
|||
// Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those,
|
|||
// we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623
|
|||
Rgba64 rgba = TiffUtils.Rgba64Default; |
|||
var color = default(TPixel); |
|||
color.FromVector4(TiffUtils.Vector4Default); |
|||
|
|||
Span<byte> redData = data[0].GetSpan(); |
|||
Span<byte> greenData = data[1].GetSpan(); |
|||
Span<byte> blueData = data[2].GetSpan(); |
|||
Span<byte> alphaData = data[3].GetSpan(); |
|||
|
|||
int offset = 0; |
|||
for (int y = top; y < top + height; y++) |
|||
{ |
|||
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width); |
|||
if (this.isBigEndian) |
|||
{ |
|||
for (int x = 0; x < pixelRow.Length; x++) |
|||
{ |
|||
ulong r = TiffUtils.ConvertToUShortBigEndian(redData.Slice(offset, 2)); |
|||
ulong g = TiffUtils.ConvertToUShortBigEndian(greenData.Slice(offset, 2)); |
|||
ulong b = TiffUtils.ConvertToUShortBigEndian(blueData.Slice(offset, 2)); |
|||
ulong a = TiffUtils.ConvertToUShortBigEndian(alphaData.Slice(offset, 2)); |
|||
|
|||
offset += 2; |
|||
|
|||
pixelRow[x] = TiffUtils.ColorFromRgba64(rgba, r, g, b, a, color); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
for (int x = 0; x < pixelRow.Length; x++) |
|||
{ |
|||
ulong r = TiffUtils.ConvertToUShortLittleEndian(redData.Slice(offset, 2)); |
|||
ulong g = TiffUtils.ConvertToUShortLittleEndian(greenData.Slice(offset, 2)); |
|||
ulong b = TiffUtils.ConvertToUShortLittleEndian(blueData.Slice(offset, 2)); |
|||
ulong a = TiffUtils.ConvertToUShortBigEndian(alphaData.Slice(offset, 2)); |
|||
|
|||
offset += 2; |
|||
|
|||
pixelRow[x] = TiffUtils.ColorFromRgba64(rgba, r, g, b, a, color); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,90 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using SixLabors.ImageSharp.Formats.Tiff.Utils; |
|||
using SixLabors.ImageSharp.Memory; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation |
|||
{ |
|||
/// <summary>
|
|||
/// Implements the 'RGB' photometric interpretation with an alpha channel and with 24 bits for each channel.
|
|||
/// </summary>
|
|||
internal class Rgba24242424TiffColor<TPixel> : TiffBaseColorDecoder<TPixel> |
|||
where TPixel : unmanaged, IPixel<TPixel> |
|||
{ |
|||
private readonly bool isBigEndian; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Rgba24242424TiffColor{TPixel}" /> class.
|
|||
/// </summary>
|
|||
/// <param name="isBigEndian">if set to <c>true</c> decodes the pixel data as big endian, otherwise as little endian.</param>
|
|||
public Rgba24242424TiffColor(bool isBigEndian) => this.isBigEndian = isBigEndian; |
|||
|
|||
/// <inheritdoc/>
|
|||
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) |
|||
{ |
|||
// Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those,
|
|||
// we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623
|
|||
var color = default(TPixel); |
|||
color.FromVector4(TiffUtils.Vector4Default); |
|||
int offset = 0; |
|||
Span<byte> buffer = stackalloc byte[4]; |
|||
int bufferStartIdx = this.isBigEndian ? 1 : 0; |
|||
|
|||
Span<byte> bufferSpan = buffer.Slice(bufferStartIdx); |
|||
for (int y = top; y < top + height; y++) |
|||
{ |
|||
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width); |
|||
|
|||
if (this.isBigEndian) |
|||
{ |
|||
for (int x = 0; x < pixelRow.Length; x++) |
|||
{ |
|||
data.Slice(offset, 3).CopyTo(bufferSpan); |
|||
ulong r = TiffUtils.ConvertToUIntBigEndian(buffer); |
|||
offset += 3; |
|||
|
|||
data.Slice(offset, 3).CopyTo(bufferSpan); |
|||
ulong g = TiffUtils.ConvertToUIntBigEndian(buffer); |
|||
offset += 3; |
|||
|
|||
data.Slice(offset, 3).CopyTo(bufferSpan); |
|||
ulong b = TiffUtils.ConvertToUIntBigEndian(buffer); |
|||
offset += 3; |
|||
|
|||
data.Slice(offset, 3).CopyTo(bufferSpan); |
|||
ulong a = TiffUtils.ConvertToUIntBigEndian(buffer); |
|||
offset += 3; |
|||
|
|||
pixelRow[x] = TiffUtils.ColorScaleTo24Bit(r, g, b, a, color); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
for (int x = 0; x < pixelRow.Length; x++) |
|||
{ |
|||
data.Slice(offset, 3).CopyTo(bufferSpan); |
|||
ulong r = TiffUtils.ConvertToUIntLittleEndian(buffer); |
|||
offset += 3; |
|||
|
|||
data.Slice(offset, 3).CopyTo(bufferSpan); |
|||
ulong g = TiffUtils.ConvertToUIntLittleEndian(buffer); |
|||
offset += 3; |
|||
|
|||
data.Slice(offset, 3).CopyTo(bufferSpan); |
|||
ulong b = TiffUtils.ConvertToUIntLittleEndian(buffer); |
|||
offset += 3; |
|||
|
|||
data.Slice(offset, 3).CopyTo(bufferSpan); |
|||
ulong a = TiffUtils.ConvertToUIntLittleEndian(buffer); |
|||
offset += 3; |
|||
|
|||
pixelRow[x] = TiffUtils.ColorScaleTo24Bit(r, g, b, a, color); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,85 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Buffers; |
|||
using SixLabors.ImageSharp.Formats.Tiff.Utils; |
|||
using SixLabors.ImageSharp.Memory; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation |
|||
{ |
|||
/// <summary>
|
|||
/// Implements the 'RGB' photometric interpretation with an alpha channel and with 'Planar' layout for each color channel with 24 bit.
|
|||
/// </summary>
|
|||
internal class Rgba24PlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel> |
|||
where TPixel : unmanaged, IPixel<TPixel> |
|||
{ |
|||
private readonly bool isBigEndian; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Rgba24PlanarTiffColor{TPixel}" /> class.
|
|||
/// </summary>
|
|||
/// <param name="isBigEndian">if set to <c>true</c> decodes the pixel data as big endian, otherwise as little endian.</param>
|
|||
public Rgba24PlanarTiffColor(bool isBigEndian) => this.isBigEndian = isBigEndian; |
|||
|
|||
/// <inheritdoc/>
|
|||
public override void Decode(IMemoryOwner<byte>[] data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) |
|||
{ |
|||
// Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those,
|
|||
// we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623
|
|||
var color = default(TPixel); |
|||
color.FromVector4(TiffUtils.Vector4Default); |
|||
Span<byte> buffer = stackalloc byte[4]; |
|||
int bufferStartIdx = this.isBigEndian ? 1 : 0; |
|||
|
|||
Span<byte> redData = data[0].GetSpan(); |
|||
Span<byte> greenData = data[1].GetSpan(); |
|||
Span<byte> blueData = data[2].GetSpan(); |
|||
Span<byte> alphaData = data[3].GetSpan(); |
|||
Span<byte> bufferSpan = buffer.Slice(bufferStartIdx); |
|||
|
|||
int offset = 0; |
|||
for (int y = top; y < top + height; y++) |
|||
{ |
|||
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width); |
|||
if (this.isBigEndian) |
|||
{ |
|||
for (int x = 0; x < pixelRow.Length; x++) |
|||
{ |
|||
redData.Slice(offset, 3).CopyTo(bufferSpan); |
|||
ulong r = TiffUtils.ConvertToUIntBigEndian(buffer); |
|||
greenData.Slice(offset, 3).CopyTo(bufferSpan); |
|||
ulong g = TiffUtils.ConvertToUIntBigEndian(buffer); |
|||
blueData.Slice(offset, 3).CopyTo(bufferSpan); |
|||
ulong b = TiffUtils.ConvertToUIntBigEndian(buffer); |
|||
alphaData.Slice(offset, 3).CopyTo(bufferSpan); |
|||
ulong a = TiffUtils.ConvertToUIntBigEndian(buffer); |
|||
|
|||
offset += 3; |
|||
|
|||
pixelRow[x] = TiffUtils.ColorScaleTo24Bit(r, g, b, a, color); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
for (int x = 0; x < pixelRow.Length; x++) |
|||
{ |
|||
redData.Slice(offset, 3).CopyTo(bufferSpan); |
|||
ulong r = TiffUtils.ConvertToUIntLittleEndian(buffer); |
|||
greenData.Slice(offset, 3).CopyTo(bufferSpan); |
|||
ulong g = TiffUtils.ConvertToUIntLittleEndian(buffer); |
|||
blueData.Slice(offset, 3).CopyTo(bufferSpan); |
|||
ulong b = TiffUtils.ConvertToUIntLittleEndian(buffer); |
|||
alphaData.Slice(offset, 3).CopyTo(bufferSpan); |
|||
ulong a = TiffUtils.ConvertToUIntLittleEndian(buffer); |
|||
|
|||
offset += 3; |
|||
|
|||
pixelRow[x] = TiffUtils.ColorScaleTo24Bit(r, g, b, a, color); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,79 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using SixLabors.ImageSharp.Formats.Tiff.Utils; |
|||
using SixLabors.ImageSharp.Memory; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation |
|||
{ |
|||
/// <summary>
|
|||
/// Implements the 'RGB' photometric interpretation with an alpha channel and with 32 bits for each channel.
|
|||
/// </summary>
|
|||
internal class Rgba32323232TiffColor<TPixel> : TiffBaseColorDecoder<TPixel> |
|||
where TPixel : unmanaged, IPixel<TPixel> |
|||
{ |
|||
private readonly bool isBigEndian; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Rgba32323232TiffColor{TPixel}" /> class.
|
|||
/// </summary>
|
|||
/// <param name="isBigEndian">if set to <c>true</c> decodes the pixel data as big endian, otherwise as little endian.</param>
|
|||
public Rgba32323232TiffColor(bool isBigEndian) => this.isBigEndian = isBigEndian; |
|||
|
|||
/// <inheritdoc/>
|
|||
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) |
|||
{ |
|||
// Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those,
|
|||
// we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623
|
|||
var color = default(TPixel); |
|||
color.FromVector4(TiffUtils.Vector4Default); |
|||
int offset = 0; |
|||
|
|||
for (int y = top; y < top + height; y++) |
|||
{ |
|||
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width); |
|||
|
|||
if (this.isBigEndian) |
|||
{ |
|||
for (int x = 0; x < pixelRow.Length; x++) |
|||
{ |
|||
ulong r = TiffUtils.ConvertToUIntBigEndian(data.Slice(offset, 4)); |
|||
offset += 4; |
|||
|
|||
ulong g = TiffUtils.ConvertToUIntBigEndian(data.Slice(offset, 4)); |
|||
offset += 4; |
|||
|
|||
ulong b = TiffUtils.ConvertToUIntBigEndian(data.Slice(offset, 4)); |
|||
offset += 4; |
|||
|
|||
ulong a = TiffUtils.ConvertToUIntBigEndian(data.Slice(offset, 4)); |
|||
offset += 4; |
|||
|
|||
pixelRow[x] = TiffUtils.ColorScaleTo32Bit(r, g, b, a, color); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
for (int x = 0; x < pixelRow.Length; x++) |
|||
{ |
|||
ulong r = TiffUtils.ConvertToUIntLittleEndian(data.Slice(offset, 4)); |
|||
offset += 4; |
|||
|
|||
ulong g = TiffUtils.ConvertToUIntLittleEndian(data.Slice(offset, 4)); |
|||
offset += 4; |
|||
|
|||
ulong b = TiffUtils.ConvertToUIntLittleEndian(data.Slice(offset, 4)); |
|||
offset += 4; |
|||
|
|||
ulong a = TiffUtils.ConvertToUIntLittleEndian(data.Slice(offset, 4)); |
|||
offset += 4; |
|||
|
|||
pixelRow[x] = TiffUtils.ColorScaleTo32Bit(r, g, b, a, color); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,74 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Buffers; |
|||
using SixLabors.ImageSharp.Formats.Tiff.Utils; |
|||
using SixLabors.ImageSharp.Memory; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation |
|||
{ |
|||
/// <summary>
|
|||
/// Implements the 'RGB' photometric interpretation with an alpha channel and a 'Planar' layout for each color channel with 32 bit.
|
|||
/// </summary>
|
|||
internal class Rgba32PlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel> |
|||
where TPixel : unmanaged, IPixel<TPixel> |
|||
{ |
|||
private readonly bool isBigEndian; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Rgba32PlanarTiffColor{TPixel}" /> class.
|
|||
/// </summary>
|
|||
/// <param name="isBigEndian">if set to <c>true</c> decodes the pixel data as big endian, otherwise as little endian.</param>
|
|||
public Rgba32PlanarTiffColor(bool isBigEndian) => this.isBigEndian = isBigEndian; |
|||
|
|||
/// <inheritdoc/>
|
|||
public override void Decode(IMemoryOwner<byte>[] data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) |
|||
{ |
|||
// Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those,
|
|||
// we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623
|
|||
var color = default(TPixel); |
|||
color.FromVector4(TiffUtils.Vector4Default); |
|||
|
|||
Span<byte> redData = data[0].GetSpan(); |
|||
Span<byte> greenData = data[1].GetSpan(); |
|||
Span<byte> blueData = data[2].GetSpan(); |
|||
Span<byte> alphaData = data[3].GetSpan(); |
|||
|
|||
int offset = 0; |
|||
for (int y = top; y < top + height; y++) |
|||
{ |
|||
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width); |
|||
if (this.isBigEndian) |
|||
{ |
|||
for (int x = 0; x < pixelRow.Length; x++) |
|||
{ |
|||
ulong r = TiffUtils.ConvertToUIntBigEndian(redData.Slice(offset, 4)); |
|||
ulong g = TiffUtils.ConvertToUIntBigEndian(greenData.Slice(offset, 4)); |
|||
ulong b = TiffUtils.ConvertToUIntBigEndian(blueData.Slice(offset, 4)); |
|||
ulong a = TiffUtils.ConvertToUIntBigEndian(alphaData.Slice(offset, 4)); |
|||
|
|||
offset += 4; |
|||
|
|||
pixelRow[x] = TiffUtils.ColorScaleTo32Bit(r, g, b, a, color); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
for (int x = 0; x < pixelRow.Length; x++) |
|||
{ |
|||
ulong r = TiffUtils.ConvertToUIntLittleEndian(redData.Slice(offset, 4)); |
|||
ulong g = TiffUtils.ConvertToUIntLittleEndian(greenData.Slice(offset, 4)); |
|||
ulong b = TiffUtils.ConvertToUIntLittleEndian(blueData.Slice(offset, 4)); |
|||
ulong a = TiffUtils.ConvertToUIntLittleEndian(alphaData.Slice(offset, 4)); |
|||
|
|||
offset += 4; |
|||
|
|||
pixelRow[x] = TiffUtils.ColorScaleTo32Bit(r, g, b, a, color); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using SixLabors.ImageSharp.Memory; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation |
|||
{ |
|||
/// <summary>
|
|||
/// Implements the 'RGB' photometric interpretation with an alpha channel and 8 bits per channel.
|
|||
/// </summary>
|
|||
internal class Rgba8888TiffColor<TPixel> : TiffBaseColorDecoder<TPixel> |
|||
where TPixel : unmanaged, IPixel<TPixel> |
|||
{ |
|||
private readonly Configuration configuration; |
|||
|
|||
public Rgba8888TiffColor(Configuration configuration) => this.configuration = configuration; |
|||
|
|||
/// <inheritdoc/>
|
|||
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) |
|||
{ |
|||
int offset = 0; |
|||
|
|||
for (int y = top; y < top + height; y++) |
|||
{ |
|||
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width); |
|||
int byteCount = pixelRow.Length * 4; |
|||
PixelOperations<TPixel>.Instance.FromRgba32Bytes( |
|||
this.configuration, |
|||
data.Slice(offset, byteCount), |
|||
pixelRow, |
|||
pixelRow.Length); |
|||
|
|||
offset += byteCount; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,97 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Numerics; |
|||
using SixLabors.ImageSharp.Formats.Tiff.Utils; |
|||
using SixLabors.ImageSharp.Memory; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation |
|||
{ |
|||
/// <summary>
|
|||
/// Implements the 'RGB' photometric interpretation with an alpha channel and with 32 bits for each channel.
|
|||
/// </summary>
|
|||
internal class RgbaFloat32323232TiffColor<TPixel> : TiffBaseColorDecoder<TPixel> |
|||
where TPixel : unmanaged, IPixel<TPixel> |
|||
{ |
|||
private readonly bool isBigEndian; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="RgbaFloat32323232TiffColor{TPixel}" /> class.
|
|||
/// </summary>
|
|||
/// <param name="isBigEndian">if set to <c>true</c> decodes the pixel data as big endian, otherwise as little endian.</param>
|
|||
public RgbaFloat32323232TiffColor(bool isBigEndian) => this.isBigEndian = isBigEndian; |
|||
|
|||
/// <inheritdoc/>
|
|||
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) |
|||
{ |
|||
// Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those,
|
|||
// we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623
|
|||
var color = default(TPixel); |
|||
color.FromVector4(TiffUtils.Vector4Default); |
|||
int offset = 0; |
|||
byte[] buffer = new byte[4]; |
|||
|
|||
for (int y = top; y < top + height; y++) |
|||
{ |
|||
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width); |
|||
|
|||
if (this.isBigEndian) |
|||
{ |
|||
for (int x = 0; x < pixelRow.Length; x++) |
|||
{ |
|||
data.Slice(offset, 4).CopyTo(buffer); |
|||
Array.Reverse(buffer); |
|||
float r = BitConverter.ToSingle(buffer, 0); |
|||
offset += 4; |
|||
|
|||
data.Slice(offset, 4).CopyTo(buffer); |
|||
Array.Reverse(buffer); |
|||
float g = BitConverter.ToSingle(buffer, 0); |
|||
offset += 4; |
|||
|
|||
data.Slice(offset, 4).CopyTo(buffer); |
|||
Array.Reverse(buffer); |
|||
float b = BitConverter.ToSingle(buffer, 0); |
|||
offset += 4; |
|||
|
|||
data.Slice(offset, 4).CopyTo(buffer); |
|||
Array.Reverse(buffer); |
|||
float a = BitConverter.ToSingle(buffer, 0); |
|||
offset += 4; |
|||
|
|||
var colorVector = new Vector4(r, g, b, a); |
|||
color.FromVector4(colorVector); |
|||
pixelRow[x] = color; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
for (int x = 0; x < pixelRow.Length; x++) |
|||
{ |
|||
data.Slice(offset, 4).CopyTo(buffer); |
|||
float r = BitConverter.ToSingle(buffer, 0); |
|||
offset += 4; |
|||
|
|||
data.Slice(offset, 4).CopyTo(buffer); |
|||
float g = BitConverter.ToSingle(buffer, 0); |
|||
offset += 4; |
|||
|
|||
data.Slice(offset, 4).CopyTo(buffer); |
|||
float b = BitConverter.ToSingle(buffer, 0); |
|||
offset += 4; |
|||
|
|||
data.Slice(offset, 4).CopyTo(buffer); |
|||
float a = BitConverter.ToSingle(buffer, 0); |
|||
offset += 4; |
|||
|
|||
var colorVector = new Vector4(r, g, b, a); |
|||
color.FromVector4(colorVector); |
|||
pixelRow[x] = color; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,87 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Buffers; |
|||
using System.Numerics; |
|||
using SixLabors.ImageSharp.Formats.Tiff.Utils; |
|||
using SixLabors.ImageSharp.Memory; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation |
|||
{ |
|||
/// <summary>
|
|||
/// Implements the 'RGB' photometric interpretation with an alpha channel and with 'Planar' layout (for all bit depths).
|
|||
/// </summary>
|
|||
internal class RgbaPlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel> |
|||
where TPixel : unmanaged, IPixel<TPixel> |
|||
{ |
|||
private readonly float rFactor; |
|||
|
|||
private readonly float gFactor; |
|||
|
|||
private readonly float bFactor; |
|||
|
|||
private readonly float aFactor; |
|||
|
|||
private readonly ushort bitsPerSampleR; |
|||
|
|||
private readonly ushort bitsPerSampleG; |
|||
|
|||
private readonly ushort bitsPerSampleB; |
|||
|
|||
private readonly ushort bitsPerSampleA; |
|||
|
|||
public RgbaPlanarTiffColor(TiffBitsPerSample bitsPerSample) |
|||
{ |
|||
this.bitsPerSampleR = bitsPerSample.Channel0; |
|||
this.bitsPerSampleG = bitsPerSample.Channel1; |
|||
this.bitsPerSampleB = bitsPerSample.Channel2; |
|||
this.bitsPerSampleA = bitsPerSample.Channel3; |
|||
|
|||
this.rFactor = (1 << this.bitsPerSampleR) - 1.0f; |
|||
this.gFactor = (1 << this.bitsPerSampleG) - 1.0f; |
|||
this.bFactor = (1 << this.bitsPerSampleB) - 1.0f; |
|||
this.aFactor = (1 << this.bitsPerSampleA) - 1.0f; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Decodes pixel data using the current photometric interpretation.
|
|||
/// </summary>
|
|||
/// <param name="data">The buffers 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>
|
|||
public override void Decode(IMemoryOwner<byte>[] data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) |
|||
{ |
|||
var color = default(TPixel); |
|||
|
|||
var rBitReader = new BitReader(data[0].GetSpan()); |
|||
var gBitReader = new BitReader(data[1].GetSpan()); |
|||
var bBitReader = new BitReader(data[2].GetSpan()); |
|||
var aBitReader = new BitReader(data[3].GetSpan()); |
|||
|
|||
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++) |
|||
{ |
|||
float r = rBitReader.ReadBits(this.bitsPerSampleR) / this.rFactor; |
|||
float g = gBitReader.ReadBits(this.bitsPerSampleG) / this.gFactor; |
|||
float b = bBitReader.ReadBits(this.bitsPerSampleB) / this.bFactor; |
|||
float a = aBitReader.ReadBits(this.bitsPerSampleA) / this.aFactor; |
|||
|
|||
color.FromVector4(new Vector4(r, g, b, a)); |
|||
pixelRow[x] = color; |
|||
} |
|||
|
|||
rBitReader.NextRow(); |
|||
gBitReader.NextRow(); |
|||
bBitReader.NextRow(); |
|||
aBitReader.NextRow(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,72 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Numerics; |
|||
using SixLabors.ImageSharp.Formats.Tiff.Utils; |
|||
using SixLabors.ImageSharp.Memory; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation |
|||
{ |
|||
/// <summary>
|
|||
/// Implements the 'RGB' photometric interpretation with alpha channel (for all bit depths).
|
|||
/// </summary>
|
|||
internal class RgbaTiffColor<TPixel> : TiffBaseColorDecoder<TPixel> |
|||
where TPixel : unmanaged, IPixel<TPixel> |
|||
{ |
|||
private readonly float rFactor; |
|||
|
|||
private readonly float gFactor; |
|||
|
|||
private readonly float bFactor; |
|||
|
|||
private readonly float aFactor; |
|||
|
|||
private readonly ushort bitsPerSampleR; |
|||
|
|||
private readonly ushort bitsPerSampleG; |
|||
|
|||
private readonly ushort bitsPerSampleB; |
|||
|
|||
private readonly ushort bitsPerSampleA; |
|||
|
|||
public RgbaTiffColor(TiffBitsPerSample bitsPerSample) |
|||
{ |
|||
this.bitsPerSampleR = bitsPerSample.Channel0; |
|||
this.bitsPerSampleG = bitsPerSample.Channel1; |
|||
this.bitsPerSampleB = bitsPerSample.Channel2; |
|||
this.bitsPerSampleA = bitsPerSample.Channel3; |
|||
|
|||
this.rFactor = (1 << this.bitsPerSampleR) - 1.0f; |
|||
this.gFactor = (1 << this.bitsPerSampleG) - 1.0f; |
|||
this.bFactor = (1 << this.bitsPerSampleB) - 1.0f; |
|||
this.aFactor = (1 << this.bitsPerSampleA) - 1.0f; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) |
|||
{ |
|||
var color = default(TPixel); |
|||
|
|||
var bitReader = new BitReader(data); |
|||
|
|||
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++) |
|||
{ |
|||
float r = bitReader.ReadBits(this.bitsPerSampleR) / this.rFactor; |
|||
float g = bitReader.ReadBits(this.bitsPerSampleG) / this.gFactor; |
|||
float b = bitReader.ReadBits(this.bitsPerSampleB) / this.bFactor; |
|||
float a = bitReader.ReadBits(this.bitsPerSampleB) / this.aFactor; |
|||
|
|||
color.FromVector4(new Vector4(r, g, b, a)); |
|||
pixelRow[x] = color; |
|||
} |
|||
|
|||
bitReader.NextRow(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Tiff |
|||
{ |
|||
/// <summary>
|
|||
/// Description of extra components.
|
|||
/// </summary>
|
|||
internal enum TiffExtraSampleType |
|||
{ |
|||
/// <summary>
|
|||
/// The data is unspecified, not supported.
|
|||
/// </summary>
|
|||
UnspecifiedData = 0, |
|||
|
|||
/// <summary>
|
|||
/// The extra data is associated alpha data (with pre-multiplied color).
|
|||
/// </summary>
|
|||
AssociatedAlphaData = 1, |
|||
|
|||
/// <summary>
|
|||
/// The extra data is unassociated alpha data is transparency information that logically exists independent of an image;
|
|||
/// it is commonly called a soft matte.
|
|||
/// </summary>
|
|||
UnassociatedAlphaData = 2 |
|||
} |
|||
} |
|||
@ -1,134 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System.Drawing.Imaging; |
|||
using System.IO; |
|||
using BenchmarkDotNet.Attributes; |
|||
using SixLabors.ImageSharp.Formats.Jpeg; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
using SixLabors.ImageSharp.Tests; |
|||
using SDImage = System.Drawing.Image; |
|||
|
|||
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg |
|||
{ |
|||
public class EncodeJpeg |
|||
{ |
|||
[Params(75, 90, 100)] |
|||
public int Quality; |
|||
|
|||
private const string TestImage = TestImages.Jpeg.BenchmarkSuite.Jpeg420Exif_MidSizeYCbCr; |
|||
|
|||
// System.Drawing
|
|||
private SDImage bmpDrawing; |
|||
private Stream bmpStream; |
|||
private ImageCodecInfo jpegCodec; |
|||
private EncoderParameters encoderParameters; |
|||
|
|||
// ImageSharp
|
|||
private Image<Rgba32> bmpCore; |
|||
private JpegEncoder encoder420; |
|||
private JpegEncoder encoder444; |
|||
|
|||
private MemoryStream destinationStream; |
|||
|
|||
[GlobalSetup] |
|||
public void ReadImages() |
|||
{ |
|||
if (this.bmpStream == null) |
|||
{ |
|||
this.bmpStream = File.OpenRead(Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImage)); |
|||
|
|||
this.bmpCore = Image.Load<Rgba32>(this.bmpStream); |
|||
this.bmpCore.Metadata.ExifProfile = null; |
|||
this.encoder420 = new JpegEncoder { Quality = this.Quality, ColorType = JpegColorType.YCbCrRatio420 }; |
|||
this.encoder444 = new JpegEncoder { Quality = this.Quality, ColorType = JpegColorType.YCbCrRatio444 }; |
|||
|
|||
this.bmpStream.Position = 0; |
|||
this.bmpDrawing = SDImage.FromStream(this.bmpStream); |
|||
this.jpegCodec = GetEncoder(ImageFormat.Jpeg); |
|||
this.encoderParameters = new EncoderParameters(1); |
|||
|
|||
// Quality cast to long is necessary
|
|||
#pragma warning disable IDE0004 // Remove Unnecessary Cast
|
|||
this.encoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, (long)this.Quality); |
|||
#pragma warning restore IDE0004 // Remove Unnecessary Cast
|
|||
|
|||
this.destinationStream = new MemoryStream(); |
|||
} |
|||
} |
|||
|
|||
[GlobalCleanup] |
|||
public void Cleanup() |
|||
{ |
|||
this.bmpStream.Dispose(); |
|||
this.bmpStream = null; |
|||
|
|||
this.destinationStream.Dispose(); |
|||
this.destinationStream = null; |
|||
|
|||
this.bmpCore.Dispose(); |
|||
this.bmpDrawing.Dispose(); |
|||
|
|||
this.encoderParameters.Dispose(); |
|||
} |
|||
|
|||
[Benchmark(Baseline = true, Description = "System.Drawing Jpeg 4:2:0")] |
|||
public void JpegSystemDrawing() |
|||
{ |
|||
this.bmpDrawing.Save(this.destinationStream, this.jpegCodec, this.encoderParameters); |
|||
this.destinationStream.Seek(0, SeekOrigin.Begin); |
|||
} |
|||
|
|||
[Benchmark(Description = "ImageSharp Jpeg 4:2:0")] |
|||
public void JpegCore420() |
|||
{ |
|||
this.bmpCore.SaveAsJpeg(this.destinationStream, this.encoder420); |
|||
this.destinationStream.Seek(0, SeekOrigin.Begin); |
|||
} |
|||
|
|||
[Benchmark(Description = "ImageSharp Jpeg 4:4:4")] |
|||
public void JpegCore444() |
|||
{ |
|||
this.bmpCore.SaveAsJpeg(this.destinationStream, this.encoder444); |
|||
this.destinationStream.Seek(0, SeekOrigin.Begin); |
|||
} |
|||
|
|||
// https://docs.microsoft.com/en-us/dotnet/api/system.drawing.imaging.encoderparameter?redirectedfrom=MSDN&view=net-5.0
|
|||
private static ImageCodecInfo GetEncoder(ImageFormat format) |
|||
{ |
|||
ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders(); |
|||
foreach (ImageCodecInfo codec in codecs) |
|||
{ |
|||
if (codec.FormatID == format.Guid) |
|||
{ |
|||
return codec; |
|||
} |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* |
|||
BenchmarkDotNet=v0.13.0, OS=Windows 10.0.19042 |
|||
Intel Core i7-6700K CPU 4.00GHz (Skylake), 1 CPU, 8 logical and 4 physical cores |
|||
.NET SDK=6.0.100-preview.3.21202.5 |
|||
[Host] : .NET Core 3.1.18 (CoreCLR 4.700.21.35901, CoreFX 4.700.21.36305), X64 RyuJIT |
|||
DefaultJob : .NET Core 3.1.18 (CoreCLR 4.700.21.35901, CoreFX 4.700.21.36305), X64 RyuJIT |
|||
|
|||
|
|||
| Method | Quality | Mean | Error | StdDev | Ratio | |
|||
|---------------------------- |-------- |---------:|---------:|---------:|------:| |
|||
| 'System.Drawing Jpeg 4:2:0' | 75 | 30.04 ms | 0.540 ms | 0.479 ms | 1.00 | |
|||
| 'ImageSharp Jpeg 4:2:0' | 75 | 19.32 ms | 0.290 ms | 0.257 ms | 0.64 | |
|||
| 'ImageSharp Jpeg 4:4:4' | 75 | 26.76 ms | 0.332 ms | 0.294 ms | 0.89 | |
|||
| | | | | | | |
|||
| 'System.Drawing Jpeg 4:2:0' | 90 | 32.82 ms | 0.184 ms | 0.163 ms | 1.00 | |
|||
| 'ImageSharp Jpeg 4:2:0' | 90 | 25.00 ms | 0.408 ms | 0.361 ms | 0.76 | |
|||
| 'ImageSharp Jpeg 4:4:4' | 90 | 31.83 ms | 0.636 ms | 0.595 ms | 0.97 | |
|||
| | | | | | | |
|||
| 'System.Drawing Jpeg 4:2:0' | 100 | 39.30 ms | 0.359 ms | 0.318 ms | 1.00 | |
|||
| 'ImageSharp Jpeg 4:2:0' | 100 | 34.49 ms | 0.265 ms | 0.235 ms | 0.88 | |
|||
| 'ImageSharp Jpeg 4:4:4' | 100 | 56.40 ms | 0.565 ms | 0.501 ms | 1.44 | |
|||
*/ |
|||
@ -0,0 +1,112 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System.Drawing.Imaging; |
|||
using System.IO; |
|||
using BenchmarkDotNet.Attributes; |
|||
using SixLabors.ImageSharp.Formats.Jpeg; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
using SixLabors.ImageSharp.Tests; |
|||
using SkiaSharp; |
|||
using SDImage = System.Drawing.Image; |
|||
|
|||
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg |
|||
{ |
|||
/// <summary>
|
|||
/// Benchmark for performance comparison between other codecs.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// This benchmarks tests baseline 4:2:0 chroma sampling path.
|
|||
/// </remarks>
|
|||
public class EncodeJpegComparison |
|||
{ |
|||
// Big enough, 4:4:4 chroma sampling
|
|||
private const string TestImage = TestImages.Jpeg.Baseline.Calliphora; |
|||
|
|||
// Change/add parameters for extra benchmarks
|
|||
[Params(75, 90, 100)] |
|||
public int Quality; |
|||
|
|||
private MemoryStream destinationStream; |
|||
|
|||
// ImageSharp
|
|||
private Image<Rgba32> imageImageSharp; |
|||
private JpegEncoder encoderImageSharp; |
|||
|
|||
// SkiaSharp
|
|||
private SKBitmap imageSkiaSharp; |
|||
|
|||
[GlobalSetup(Target = nameof(BenchmarkImageSharp))] |
|||
public void SetupImageSharp() |
|||
{ |
|||
using FileStream imageBinaryStream = File.OpenRead(Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImage)); |
|||
|
|||
this.imageImageSharp = Image.Load<Rgba32>(imageBinaryStream); |
|||
this.encoderImageSharp = new JpegEncoder { Quality = this.Quality, ColorType = JpegColorType.YCbCrRatio420 }; |
|||
|
|||
this.destinationStream = new MemoryStream(); |
|||
} |
|||
|
|||
[GlobalCleanup(Target = nameof(BenchmarkImageSharp))] |
|||
public void CleanupImageSharp() |
|||
{ |
|||
this.imageImageSharp.Dispose(); |
|||
this.imageImageSharp = null; |
|||
|
|||
this.destinationStream.Dispose(); |
|||
this.destinationStream = null; |
|||
} |
|||
|
|||
[Benchmark(Description = "ImageSharp")] |
|||
public void BenchmarkImageSharp() |
|||
{ |
|||
this.imageImageSharp.SaveAsJpeg(this.destinationStream, this.encoderImageSharp); |
|||
this.destinationStream.Seek(0, SeekOrigin.Begin); |
|||
} |
|||
|
|||
[GlobalSetup(Target = nameof(BenchmarkSkiaSharp))] |
|||
public void SetupSkiaSharp() |
|||
{ |
|||
using FileStream imageBinaryStream = File.OpenRead(Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImage)); |
|||
|
|||
this.imageSkiaSharp = SKBitmap.Decode(imageBinaryStream); |
|||
|
|||
this.destinationStream = new MemoryStream(); |
|||
} |
|||
|
|||
[GlobalCleanup(Target = nameof(BenchmarkSkiaSharp))] |
|||
public void CleanupSkiaSharp() |
|||
{ |
|||
this.imageSkiaSharp.Dispose(); |
|||
this.imageSkiaSharp = null; |
|||
|
|||
this.destinationStream.Dispose(); |
|||
this.destinationStream = null; |
|||
} |
|||
|
|||
[Benchmark(Description = "SkiaSharp")] |
|||
public void BenchmarkSkiaSharp() |
|||
{ |
|||
this.imageSkiaSharp.Encode(SKEncodedImageFormat.Jpeg, this.Quality).SaveTo(this.destinationStream); |
|||
this.destinationStream.Seek(0, SeekOrigin.Begin); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* |
|||
BenchmarkDotNet=v0.13.0, OS=Windows 10.0.19044 |
|||
Intel Core i7-6700K CPU 4.00GHz (Skylake), 1 CPU, 8 logical and 4 physical cores |
|||
.NET SDK=6.0.100-preview.3.21202.5 |
|||
[Host] : .NET Core 3.1.21 (CoreCLR 4.700.21.51404, CoreFX 4.700.21.51508), X64 RyuJIT |
|||
DefaultJob : .NET Core 3.1.21 (CoreCLR 4.700.21.51404, CoreFX 4.700.21.51508), X64 RyuJIT |
|||
|
|||
|
|||
| Method | Quality | Mean | Error | StdDev | |
|||
|----------- |-------- |----------:|----------:|----------:| |
|||
| ImageSharp | 75 | 6.820 ms | 0.0374 ms | 0.0312 ms | |
|||
| SkiaSharp | 75 | 16.417 ms | 0.3238 ms | 0.4747 ms | |
|||
| ImageSharp | 90 | 7.849 ms | 0.1565 ms | 0.3126 ms | |
|||
| SkiaSharp | 90 | 16.893 ms | 0.2200 ms | 0.2058 ms | |
|||
| ImageSharp | 100 | 11.016 ms | 0.2087 ms | 0.1850 ms | |
|||
| SkiaSharp | 100 | 20.410 ms | 0.2583 ms | 0.2290 ms | |
|||
*/ |
|||
@ -0,0 +1,89 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System.Collections.Generic; |
|||
using System.IO; |
|||
using BenchmarkDotNet.Attributes; |
|||
using SixLabors.ImageSharp.Formats.Jpeg; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
using SixLabors.ImageSharp.Tests; |
|||
|
|||
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg |
|||
{ |
|||
/// <summary>
|
|||
/// Benchmark for all available encoding features of the Jpeg file type.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// This benchmark does NOT compare ImageSharp to any other jpeg codecs.
|
|||
/// </remarks>
|
|||
public class EncodeJpegFeatures |
|||
{ |
|||
// Big enough, 4:4:4 chroma sampling
|
|||
// No metadata
|
|||
private const string TestImage = TestImages.Jpeg.Baseline.Calliphora; |
|||
|
|||
public static IEnumerable<JpegColorType> ColorSpaceValues => |
|||
new[] { JpegColorType.Luminance, JpegColorType.Rgb, JpegColorType.YCbCrRatio420, JpegColorType.YCbCrRatio444 }; |
|||
|
|||
[Params(75, 90, 100)] |
|||
public int Quality; |
|||
|
|||
[ParamsSource(nameof(ColorSpaceValues), Priority = -100)] |
|||
public JpegColorType TargetColorSpace; |
|||
|
|||
private Image<Rgb24> bmpCore; |
|||
private JpegEncoder encoder; |
|||
|
|||
private MemoryStream destinationStream; |
|||
|
|||
[GlobalSetup] |
|||
public void Setup() |
|||
{ |
|||
using FileStream imageBinaryStream = File.OpenRead(Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImage)); |
|||
this.bmpCore = Image.Load<Rgb24>(imageBinaryStream); |
|||
this.encoder = new JpegEncoder { Quality = this.Quality, ColorType = this.TargetColorSpace }; |
|||
this.destinationStream = new MemoryStream(); |
|||
} |
|||
|
|||
[GlobalCleanup] |
|||
public void Cleanup() |
|||
{ |
|||
this.bmpCore.Dispose(); |
|||
this.bmpCore = null; |
|||
|
|||
this.destinationStream.Dispose(); |
|||
this.destinationStream = null; |
|||
} |
|||
|
|||
[Benchmark] |
|||
public void Benchmark() |
|||
{ |
|||
this.bmpCore.SaveAsJpeg(this.destinationStream, this.encoder); |
|||
this.destinationStream.Seek(0, SeekOrigin.Begin); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* |
|||
BenchmarkDotNet=v0.13.0, OS=Windows 10.0.19044 |
|||
Intel Core i7-6700K CPU 4.00GHz (Skylake), 1 CPU, 8 logical and 4 physical cores |
|||
.NET SDK=6.0.100-preview.3.21202.5 |
|||
[Host] : .NET Core 3.1.21 (CoreCLR 4.700.21.51404, CoreFX 4.700.21.51508), X64 RyuJIT |
|||
DefaultJob : .NET Core 3.1.21 (CoreCLR 4.700.21.51404, CoreFX 4.700.21.51508), X64 RyuJIT |
|||
|
|||
|
|||
| Method | TargetColorSpace | Quality | Mean | Error | StdDev | |
|||
|---------- |----------------- |-------- |----------:|----------:|----------:| |
|||
| Benchmark | Luminance | 75 | 7.055 ms | 0.1411 ms | 0.3297 ms | |
|||
| Benchmark | Rgb | 75 | 12.139 ms | 0.0645 ms | 0.0538 ms | |
|||
| Benchmark | YCbCrRatio420 | 75 | 6.463 ms | 0.0282 ms | 0.0235 ms | |
|||
| Benchmark | YCbCrRatio444 | 75 | 8.616 ms | 0.0422 ms | 0.0374 ms | |
|||
| Benchmark | Luminance | 90 | 7.011 ms | 0.0361 ms | 0.0301 ms | |
|||
| Benchmark | Rgb | 90 | 13.119 ms | 0.0947 ms | 0.0886 ms | |
|||
| Benchmark | YCbCrRatio420 | 90 | 6.786 ms | 0.0328 ms | 0.0274 ms | |
|||
| Benchmark | YCbCrRatio444 | 90 | 8.672 ms | 0.0772 ms | 0.0722 ms | |
|||
| Benchmark | Luminance | 100 | 9.554 ms | 0.1211 ms | 0.1012 ms | |
|||
| Benchmark | Rgb | 100 | 19.475 ms | 0.1080 ms | 0.0958 ms | |
|||
| Benchmark | YCbCrRatio420 | 100 | 10.146 ms | 0.0585 ms | 0.0519 ms | |
|||
| Benchmark | YCbCrRatio444 | 100 | 15.317 ms | 0.0709 ms | 0.0592 ms | |
|||
*/ |
|||
@ -1,34 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System.Collections.Generic; |
|||
using System.Drawing.Imaging; |
|||
using BenchmarkDotNet.Attributes; |
|||
using SixLabors.ImageSharp.Formats.Jpeg; |
|||
|
|||
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg |
|||
{ |
|||
[Config(typeof(Config.ShortMultiFramework))] |
|||
public class EncodeJpegMultiple : MultiImageBenchmarkBase.WithImagesPreloaded |
|||
{ |
|||
protected override IEnumerable<string> InputImageSubfoldersOrFiles => new[] { "Bmp/", "Jpg/baseline" }; |
|||
|
|||
protected override IEnumerable<string> SearchPatterns => new[] { "*.bmp", "*.jpg" }; |
|||
|
|||
[Benchmark(Description = "EncodeJpegMultiple - ImageSharp")] |
|||
public void EncodeJpegImageSharp() |
|||
=> this.ForEachImageSharpImage((img, ms) => |
|||
{ |
|||
img.Save(ms, new JpegEncoder()); |
|||
return null; |
|||
}); |
|||
|
|||
[Benchmark(Baseline = true, Description = "EncodeJpegMultiple - System.Drawing")] |
|||
public void EncodeJpegSystemDrawing() |
|||
=> this.ForEachSystemDrawingImage((img, ms) => |
|||
{ |
|||
img.Save(ms, ImageFormat.Jpeg); |
|||
return null; |
|||
}); |
|||
} |
|||
} |
|||
@ -1,3 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:1642520a9d4491f55af5a83280423929529e3d528637538d148b9dc2ecdd6d2d |
|||
size 365839 |
|||
oid sha256:77086032bab11b91c68bc1686179063edbadc9d453574e4f087b2bbd677b4c8e |
|||
size 402367 |
|||
|
|||
@ -1,3 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:060826324dcd4baa1df1b075707a9bd9527cf87c1d156e3beb3d8abb499bdcd5 |
|||
size 361829 |
|||
oid sha256:5c0653aa2b726574fbea4cc308c269ff5e534d38bb48c0e77470c11042a395fd |
|||
size 400267 |
|||
|
|||
@ -0,0 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:7048dee15946bf981e5b0d2481ffcb8a64684fddca07172275b13a05f01b6b63 |
|||
size 1631109 |
|||
@ -0,0 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:4c52500be37a8ea1ee1caeb78c79e44b02e10912df4f6db65313c6745573c8ee |
|||
size 250451 |
|||
@ -0,0 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:7f78ef11e4044d13ea3bf699e33472a708df3a5cc817dc41edb4df184f127f2b |
|||
size 294278 |
|||
@ -0,0 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:d9c2d6f4e16677d9fdfb38cc2bfb7df05eedbb8dc0e3c26a6dba9b427c2c698a |
|||
size 294278 |
|||
@ -0,0 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:53e9ff25da2a2a7a613328cfaf33799df51fe150586fb8de52070e8cc8830d97 |
|||
size 353078 |
|||
@ -0,0 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:caff76e01bc39b7a295f01a11e3787a6487ac002af5586dd956166a9c91eb048 |
|||
size 353078 |
|||
@ -0,0 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:9193b6a194be970b2cfb26369fa487fd6ec2f1656af11df2e48f1d6b0971bbf8 |
|||
size 411878 |
|||
@ -0,0 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:888bc84af8dffc4565b215412a8a2bb56f0c78211a082b893d87595cd9f555c1 |
|||
size 411878 |
|||
@ -0,0 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:6eae92c012ad56c084929e0a2aff7c93091224d9f8ab7f52f71b845792d6b763 |
|||
size 470678 |
|||
@ -0,0 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:cbab54f221956215266c35bfd26fdfb123e092e3836e2401b9f24e1c5b23516e |
|||
size 470678 |
|||
@ -0,0 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:fd9fa514619604275cede0b4747291db2f8e5ad02095565c891ace2b537d6336 |
|||
size 705878 |
|||
@ -0,0 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:915ca9bbda952fc9ac78b44be07dab603948d51fb1a274935905e73cfe5bb0b9 |
|||
size 705878 |
|||
@ -0,0 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:f650c49faed4fd19b5527a0771489110090948e4ed33daa53b42c1776e288d89 |
|||
size 59078 |
|||
@ -0,0 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:8cad4c5f42d77539ce1f67efa7e0ed1fa4f5dd32b3269e5862453d878c6b18d7 |
|||
size 941078 |
|||
@ -0,0 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:8874322776b8620573c26a3c84b8c7c9bf0aeaa7d68a7fef009f8838d14dca5b |
|||
size 941078 |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue