mirror of https://github.com/SixLabors/ImageSharp
359 changed files with 10915 additions and 3244 deletions
@ -1,33 +0,0 @@ |
|||
--- |
|||
name: "Commercial License : Bug Report" |
|||
about: | |
|||
Create a report to help us improve the project. For Commercial License holders only. |
|||
Please contact help@sixlabors.com for issues requiring private support. |
|||
labels: commercial, needs triage |
|||
|
|||
--- |
|||
|
|||
|
|||
### Prerequisites |
|||
|
|||
- [ ] I have written a descriptive issue title |
|||
- [ ] I have verified that I am running the latest version of ImageSharp |
|||
- [ ] I have verified if the problem exist in both `DEBUG` and `RELEASE` mode |
|||
- [ ] I have searched [open](https://github.com/SixLabors/ImageSharp/issues) and [closed](https://github.com/SixLabors/ImageSharp/issues?q=is%3Aissue+is%3Aclosed) issues to ensure it has not already been reported |
|||
|
|||
### Description |
|||
<!-- A description of the bug or feature --> |
|||
|
|||
### Steps to Reproduce |
|||
<!-- List of steps, sample code, failing test or link to a project that reproduces the behavior --> |
|||
|
|||
### System Configuration |
|||
<!-- Tell us about the environment where you are experiencing the bug --> |
|||
|
|||
- ImageSharp version: |
|||
- Other ImageSharp packages and versions: |
|||
- Environment (Operating system, version and so on): |
|||
- .NET Framework version: |
|||
- Additional information: |
|||
|
|||
<!-- Thanks for reporting the issue to ImageSharp! --> |
|||
@ -0,0 +1,56 @@ |
|||
name: "Commercial License : Bug Report" |
|||
description: | |
|||
Create a report to help us improve the project. For Commercial License holders only. |
|||
Please contact help@sixlabors.com for issues requiring private support. |
|||
labels: ["commercial", "needs triage"] |
|||
body: |
|||
- type: checkboxes |
|||
attributes: |
|||
label: Prerequisites |
|||
options: |
|||
- label: I have bought a Commercial License |
|||
required: true |
|||
- label: I have written a descriptive issue title |
|||
required: true |
|||
- label: I have verified that I am running the latest version of ImageSharp |
|||
required: true |
|||
- label: I have verified if the problem exist in both `DEBUG` and `RELEASE` mode |
|||
required: true |
|||
- label: I have searched [open](https://github.com/SixLabors/ImageSharp/issues) and [closed](https://github.com/SixLabors/ImageSharp/issues?q=is%3Aissue+is%3Aclosed) issues to ensure it has not already been reported |
|||
required: true |
|||
- type: input |
|||
attributes: |
|||
label: ImageSharp version |
|||
validations: |
|||
required: true |
|||
- type: input |
|||
attributes: |
|||
label: Other ImageSharp packages and versions |
|||
validations: |
|||
required: true |
|||
- type: input |
|||
attributes: |
|||
label: Environment (Operating system, version and so on) |
|||
validations: |
|||
required: true |
|||
- type: input |
|||
attributes: |
|||
label: .NET Framework version |
|||
validations: |
|||
required: true |
|||
- type: textarea |
|||
attributes: |
|||
label: Description |
|||
description: A description of the bug |
|||
validations: |
|||
required: true |
|||
- type: textarea |
|||
attributes: |
|||
label: Steps to Reproduce |
|||
description: List of steps, sample code, failing test or link to a project that reproduces the behavior. Make sure you place a stack trace inside a code (```) block to avoid linking unrelated issues. |
|||
validations: |
|||
required: true |
|||
- type: textarea |
|||
attributes: |
|||
label: Images |
|||
description: Please upload images that can be used to reproduce issues in the area below. If the file type is not supported the file can be zipped and then uploaded instead. |
|||
@ -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. |
|||
|
|||
@ -1,30 +0,0 @@ |
|||
--- |
|||
name: "OSS : Bug Report" |
|||
about: Create a report to help us improve the project. OSS Issues are not guaranteed to be triaged. |
|||
labels: needs triage |
|||
|
|||
--- |
|||
|
|||
### Prerequisites |
|||
|
|||
- [ ] I have written a descriptive issue title |
|||
- [ ] I have verified that I am running the latest version of ImageSharp |
|||
- [ ] I have verified if the problem exist in both `DEBUG` and `RELEASE` mode |
|||
- [ ] I have searched [open](https://github.com/SixLabors/ImageSharp/issues) and [closed](https://github.com/SixLabors/ImageSharp/issues?q=is%3Aissue+is%3Aclosed) issues to ensure it has not already been reported |
|||
|
|||
### Description |
|||
<!-- A description of the bug or feature --> |
|||
|
|||
### Steps to Reproduce |
|||
<!-- List of steps, sample code, failing test or link to a project that reproduces the behavior --> |
|||
|
|||
### System Configuration |
|||
<!-- Tell us about the environment where you are experiencing the bug --> |
|||
|
|||
- ImageSharp version: |
|||
- Other ImageSharp packages and versions: |
|||
- Environment (Operating system, version and so on): |
|||
- .NET Framework version: |
|||
- Additional information: |
|||
|
|||
<!-- Thanks for reporting the issue to ImageSharp! --> |
|||
@ -0,0 +1,52 @@ |
|||
name: "OSS : Bug Report" |
|||
description: Create a report to help us improve the project. OSS Issues are not guaranteed to be triaged. |
|||
labels: ["needs triage"] |
|||
body: |
|||
- type: checkboxes |
|||
attributes: |
|||
label: Prerequisites |
|||
options: |
|||
- label: I have written a descriptive issue title |
|||
required: true |
|||
- label: I have verified that I am running the latest version of ImageSharp |
|||
required: true |
|||
- label: I have verified if the problem exist in both `DEBUG` and `RELEASE` mode |
|||
required: true |
|||
- label: I have searched [open](https://github.com/SixLabors/ImageSharp/issues) and [closed](https://github.com/SixLabors/ImageSharp/issues?q=is%3Aissue+is%3Aclosed) issues to ensure it has not already been reported |
|||
required: true |
|||
- type: input |
|||
attributes: |
|||
label: ImageSharp version |
|||
validations: |
|||
required: true |
|||
- type: input |
|||
attributes: |
|||
label: Other ImageSharp packages and versions |
|||
validations: |
|||
required: true |
|||
- type: input |
|||
attributes: |
|||
label: Environment (Operating system, version and so on) |
|||
validations: |
|||
required: true |
|||
- type: input |
|||
attributes: |
|||
label: .NET Framework version |
|||
validations: |
|||
required: true |
|||
- type: textarea |
|||
attributes: |
|||
label: Description |
|||
description: A description of the bug |
|||
validations: |
|||
required: true |
|||
- type: textarea |
|||
attributes: |
|||
label: Steps to Reproduce |
|||
description: List of steps, sample code, failing test or link to a project that reproduces the behavior. Make sure you place a stack trace inside a code (```) block to avoid linking unrelated issues. |
|||
validations: |
|||
required: true |
|||
- type: textarea |
|||
attributes: |
|||
label: Images |
|||
description: Please upload images that can be used to reproduce issues in the area below. If the file type is not supported the file can be zipped and then uploaded instead. |
|||
@ -0,0 +1,100 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Threading; |
|||
|
|||
namespace SixLabors.ImageSharp.Diagnostics |
|||
{ |
|||
/// <summary>
|
|||
/// Represents the method to handle <see cref="MemoryDiagnostics.UndisposedAllocation"/>.
|
|||
/// </summary>
|
|||
public delegate void UndisposedAllocationDelegate(string allocationStackTrace); |
|||
|
|||
/// <summary>
|
|||
/// Utilities to track memory usage and detect memory leaks from not disposing ImageSharp objects.
|
|||
/// </summary>
|
|||
public static class MemoryDiagnostics |
|||
{ |
|||
private static int totalUndisposedAllocationCount; |
|||
|
|||
private static UndisposedAllocationDelegate undisposedAllocation; |
|||
private static int undisposedAllocationSubscriptionCounter; |
|||
private static readonly object SyncRoot = new(); |
|||
|
|||
/// <summary>
|
|||
/// Fires when an ImageSharp object's undisposed memory resource leaks to the finalizer.
|
|||
/// The event brings significant overhead, and is intended to be used for troubleshooting only.
|
|||
/// For production diagnostics, use <see cref="TotalUndisposedAllocationCount"/>.
|
|||
/// </summary>
|
|||
public static event UndisposedAllocationDelegate UndisposedAllocation |
|||
{ |
|||
add |
|||
{ |
|||
lock (SyncRoot) |
|||
{ |
|||
undisposedAllocationSubscriptionCounter++; |
|||
undisposedAllocation += value; |
|||
} |
|||
} |
|||
|
|||
remove |
|||
{ |
|||
lock (SyncRoot) |
|||
{ |
|||
undisposedAllocation -= value; |
|||
undisposedAllocationSubscriptionCounter--; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Fires when ImageSharp allocates memory from a MemoryAllocator
|
|||
/// </summary>
|
|||
internal static event Action MemoryAllocated; |
|||
|
|||
/// <summary>
|
|||
/// Fires when ImageSharp releases memory allocated from a MemoryAllocator
|
|||
/// </summary>
|
|||
internal static event Action MemoryReleased; |
|||
|
|||
/// <summary>
|
|||
/// Gets a value indicating the total number of memory resource objects leaked to the finalizer.
|
|||
/// </summary>
|
|||
public static int TotalUndisposedAllocationCount => totalUndisposedAllocationCount; |
|||
|
|||
internal static bool UndisposedAllocationSubscribed => Volatile.Read(ref undisposedAllocationSubscriptionCounter) > 0; |
|||
|
|||
internal static void IncrementTotalUndisposedAllocationCount() |
|||
{ |
|||
Interlocked.Increment(ref totalUndisposedAllocationCount); |
|||
MemoryAllocated?.Invoke(); |
|||
} |
|||
|
|||
internal static void DecrementTotalUndisposedAllocationCount() |
|||
{ |
|||
Interlocked.Decrement(ref totalUndisposedAllocationCount); |
|||
MemoryReleased?.Invoke(); |
|||
} |
|||
|
|||
internal static void RaiseUndisposedMemoryResource(string allocationStackTrace) |
|||
{ |
|||
if (undisposedAllocation is null) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
// Schedule on the ThreadPool, to avoid user callback messing up the finalizer thread.
|
|||
#if NETSTANDARD2_1 || NETCOREAPP2_1_OR_GREATER
|
|||
ThreadPool.QueueUserWorkItem( |
|||
stackTrace => undisposedAllocation?.Invoke(stackTrace), |
|||
allocationStackTrace, |
|||
preferLocal: false); |
|||
#else
|
|||
ThreadPool.QueueUserWorkItem( |
|||
stackTrace => undisposedAllocation?.Invoke((string)stackTrace), |
|||
allocationStackTrace); |
|||
#endif
|
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,37 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
// ReSharper disable InconsistentNaming
|
|||
namespace SixLabors.ImageSharp.Formats.Bmp |
|||
{ |
|||
/// <summary>
|
|||
/// Enum for the different color spaces.
|
|||
/// </summary>
|
|||
internal enum BmpColorSpace |
|||
{ |
|||
/// <summary>
|
|||
/// This value implies that endpoints and gamma values are given in the appropriate fields.
|
|||
/// </summary>
|
|||
LCS_CALIBRATED_RGB = 0, |
|||
|
|||
/// <summary>
|
|||
/// The Windows default color space ('Win ').
|
|||
/// </summary>
|
|||
LCS_WINDOWS_COLOR_SPACE = 1466527264, |
|||
|
|||
/// <summary>
|
|||
/// Specifies that the bitmap is in sRGB color space ('sRGB').
|
|||
/// </summary>
|
|||
LCS_sRGB = 1934772034, |
|||
|
|||
/// <summary>
|
|||
/// This value indicates that bV5ProfileData points to the file name of the profile to use (gamma and endpoints values are ignored).
|
|||
/// </summary>
|
|||
PROFILE_LINKED = 1279872587, |
|||
|
|||
/// <summary>
|
|||
/// This value indicates that bV5ProfileData points to a memory buffer that contains the profile to be used (gamma and endpoints values are ignored).
|
|||
/// </summary>
|
|||
PROFILE_EMBEDDED = 1296188740 |
|||
} |
|||
} |
|||
@ -0,0 +1,37 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
// ReSharper disable InconsistentNaming
|
|||
namespace SixLabors.ImageSharp.Formats.Bmp |
|||
{ |
|||
/// <summary>
|
|||
/// Enum for the different rendering intent's.
|
|||
/// </summary>
|
|||
internal enum BmpRenderingIntent |
|||
{ |
|||
/// <summary>
|
|||
/// Invalid default value.
|
|||
/// </summary>
|
|||
Invalid = 0, |
|||
|
|||
/// <summary>
|
|||
/// Maintains saturation. Used for business charts and other situations in which undithered colors are required.
|
|||
/// </summary>
|
|||
LCS_GM_BUSINESS = 1, |
|||
|
|||
/// <summary>
|
|||
/// Maintains colorimetric match. Used for graphic designs and named colors.
|
|||
/// </summary>
|
|||
LCS_GM_GRAPHICS = 2, |
|||
|
|||
/// <summary>
|
|||
/// Maintains contrast. Used for photographs and natural images.
|
|||
/// </summary>
|
|||
LCS_GM_IMAGES = 4, |
|||
|
|||
/// <summary>
|
|||
/// Maintains the white point. Matches the colors to their nearest color in the destination gamut.
|
|||
/// </summary>
|
|||
LCS_GM_ABS_COLORIMETRIC = 8, |
|||
} |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components |
|||
{ |
|||
internal enum ComponentType |
|||
{ |
|||
Huffman = 0, |
|||
|
|||
Arithmetic = 1 |
|||
} |
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using SixLabors.ImageSharp.Memory; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder |
|||
{ |
|||
internal class ArithmeticDecodingComponent : JpegComponent |
|||
{ |
|||
public ArithmeticDecodingComponent(MemoryAllocator memoryAllocator, JpegFrame frame, byte id, int horizontalFactor, int verticalFactor, byte quantizationTableIndex, int index) |
|||
: base(memoryAllocator, frame, id, horizontalFactor, verticalFactor, quantizationTableIndex, index) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the dc context.
|
|||
/// </summary>
|
|||
public int DcContext { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the dc statistics.
|
|||
/// </summary>
|
|||
public ArithmeticStatistics DcStatistics { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the ac statistics.
|
|||
/// </summary>
|
|||
public ArithmeticStatistics AcStatistics { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,43 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder |
|||
{ |
|||
internal class ArithmeticDecodingTable |
|||
{ |
|||
public ArithmeticDecodingTable(byte tableClass, byte identifier) |
|||
{ |
|||
this.TableClass = tableClass; |
|||
this.Identifier = identifier; |
|||
} |
|||
|
|||
public byte TableClass { get; } |
|||
|
|||
public byte Identifier { get; } |
|||
|
|||
public byte ConditioningTableValue { get; private set; } |
|||
|
|||
public int DcL { get; private set; } |
|||
|
|||
public int DcU { get; private set; } |
|||
|
|||
public int AcKx { get; private set; } |
|||
|
|||
public void Configure(byte conditioningTableValue) |
|||
{ |
|||
this.ConditioningTableValue = conditioningTableValue; |
|||
if (this.TableClass == 0) |
|||
{ |
|||
this.DcL = conditioningTableValue & 0x0F; |
|||
this.DcU = conditioningTableValue >> 4; |
|||
this.AcKx = 0; |
|||
} |
|||
else |
|||
{ |
|||
this.DcL = 0; |
|||
this.DcU = 0; |
|||
this.AcKx = conditioningTableValue; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
File diff suppressed because it is too large
@ -0,0 +1,29 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder |
|||
{ |
|||
internal class ArithmeticStatistics |
|||
{ |
|||
private readonly byte[] statistics; |
|||
|
|||
public ArithmeticStatistics(bool dc, int identifier) |
|||
{ |
|||
this.IsDcStatistics = dc; |
|||
this.Identifier = identifier; |
|||
this.statistics = dc ? new byte[64] : new byte[256]; |
|||
} |
|||
|
|||
public bool IsDcStatistics { get; private set; } |
|||
|
|||
public int Identifier { get; private set; } |
|||
|
|||
public ref byte GetReference() => ref this.statistics[0]; |
|||
|
|||
public ref byte GetReference(int offset) => ref this.statistics[offset]; |
|||
|
|||
public void Reset() => this.statistics.AsSpan().Clear(); |
|||
} |
|||
} |
|||
@ -0,0 +1,49 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder |
|||
{ |
|||
/// <summary>
|
|||
/// Interface for a JPEG scan decoder.
|
|||
/// </summary>
|
|||
internal interface IJpegScanDecoder |
|||
{ |
|||
/// <summary>
|
|||
/// Sets the reset interval.
|
|||
/// </summary>
|
|||
int ResetInterval { set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the spectral selection start.
|
|||
/// </summary>
|
|||
int SpectralStart { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the spectral selection end.
|
|||
/// </summary>
|
|||
int SpectralEnd { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the successive approximation high bit end.
|
|||
/// </summary>
|
|||
int SuccessiveHigh { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the successive approximation low bit end.
|
|||
/// </summary>
|
|||
int SuccessiveLow { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Decodes the entropy coded data.
|
|||
/// </summary>
|
|||
/// <param name="scanComponentCount">Component count in the current scan.</param>
|
|||
void ParseEntropyCodedData(int scanComponentCount); |
|||
|
|||
/// <summary>
|
|||
/// Sets the JpegFrame and its components and injects the frame data into the spectral converter.
|
|||
/// </summary>
|
|||
/// <param name="frame">The frame.</param>
|
|||
/// <param name="jpegData">The raw JPEG data.</param>
|
|||
void InjectFrameData(JpegFrame frame, IRawJpegData jpegData); |
|||
} |
|||
} |
|||
@ -0,0 +1,201 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Buffers; |
|||
using System.IO; |
|||
using SixLabors.ImageSharp.Formats.Tiff.Constants; |
|||
using SixLabors.ImageSharp.Memory; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors |
|||
{ |
|||
/// <summary>
|
|||
/// Bitwriter for writing compressed CCITT T6 2D data.
|
|||
/// </summary>
|
|||
internal sealed class T6BitCompressor : TiffCcittCompressor |
|||
{ |
|||
/// <summary>
|
|||
/// Vertical codes from -3 to +3.
|
|||
/// </summary>
|
|||
private static readonly (uint Length, uint Code)[] VerticalCodes = |
|||
{ |
|||
(7u, 3u), |
|||
(6u, 3u), |
|||
(3u, 3u), |
|||
(1u, 1u), |
|||
(3u, 2u), |
|||
(6u, 2u), |
|||
(7u, 2u) |
|||
}; |
|||
|
|||
private IMemoryOwner<byte> referenceLineBuffer; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="T6BitCompressor"/> class.
|
|||
/// </summary>
|
|||
/// <param name="output">The output stream to write the compressed data.</param>
|
|||
/// <param name="allocator">The memory allocator.</param>
|
|||
/// <param name="width">The width of the image.</param>
|
|||
/// <param name="bitsPerPixel">The bits per pixel.</param>
|
|||
public T6BitCompressor(Stream output, MemoryAllocator allocator, int width, int bitsPerPixel) |
|||
: base(output, allocator, width, bitsPerPixel) |
|||
{ |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
public override TiffCompression Method => TiffCompression.CcittGroup4Fax; |
|||
|
|||
/// <summary>
|
|||
/// Writes a image compressed with CCITT T6 to the output buffer.
|
|||
/// </summary>
|
|||
/// <param name="pixelsAsGray">The pixels as 8-bit gray array.</param>
|
|||
/// <param name="height">The strip height.</param>
|
|||
/// <param name="compressedData">The destination for the compressed data.</param>
|
|||
protected override void CompressStrip(Span<byte> pixelsAsGray, int height, Span<byte> compressedData) |
|||
{ |
|||
// Initial reference line is all white.
|
|||
Span<byte> referenceLine = this.referenceLineBuffer.GetSpan(); |
|||
referenceLine.Fill(0xff); |
|||
|
|||
for (int y = 0; y < height; y++) |
|||
{ |
|||
Span<byte> row = pixelsAsGray.Slice(y * this.Width, this.Width); |
|||
uint a0 = 0; |
|||
uint a1 = row[0] == 0 ? 0 : this.FindRunEnd(row, 0); |
|||
uint b1 = referenceLine[0] == 0 ? 0 : this.FindRunEnd(referenceLine, 0); |
|||
|
|||
while (true) |
|||
{ |
|||
uint b2 = this.FindRunEnd(referenceLine, b1); |
|||
if (b2 < a1) |
|||
{ |
|||
// Pass mode.
|
|||
this.WriteCode(4, 1, compressedData); |
|||
a0 = b2; |
|||
} |
|||
else |
|||
{ |
|||
int d = int.MaxValue; |
|||
if ((b1 >= a1) && (b1 - a1 <= 3)) |
|||
{ |
|||
d = (int)(b1 - a1); |
|||
} |
|||
else if ((b1 < a1) && (a1 - b1 <= 3)) |
|||
{ |
|||
d = -(int)(a1 - b1); |
|||
} |
|||
|
|||
if ((d >= -3) && (d <= 3)) |
|||
{ |
|||
// Vertical mode.
|
|||
(uint length, uint code) = VerticalCodes[d + 3]; |
|||
this.WriteCode(length, code, compressedData); |
|||
a0 = a1; |
|||
} |
|||
else |
|||
{ |
|||
// Horizontal mode.
|
|||
this.WriteCode(3, 1, compressedData); |
|||
|
|||
uint a2 = this.FindRunEnd(row, a1); |
|||
if ((a0 + a1 == 0) || (row[(int)a0] != 0)) |
|||
{ |
|||
this.WriteRun(a1 - a0, true, compressedData); |
|||
this.WriteRun(a2 - a1, false, compressedData); |
|||
} |
|||
else |
|||
{ |
|||
this.WriteRun(a1 - a0, false, compressedData); |
|||
this.WriteRun(a2 - a1, true, compressedData); |
|||
} |
|||
|
|||
a0 = a2; |
|||
} |
|||
} |
|||
|
|||
if (a0 >= row.Length) |
|||
{ |
|||
break; |
|||
} |
|||
|
|||
byte thisPixel = row[(int)a0]; |
|||
a1 = this.FindRunEnd(row, a0, thisPixel); |
|||
b1 = this.FindRunEnd(referenceLine, a0, (byte)~thisPixel); |
|||
b1 = this.FindRunEnd(referenceLine, b1, thisPixel); |
|||
} |
|||
|
|||
// This row is now the reference line.
|
|||
row.CopyTo(referenceLine); |
|||
} |
|||
|
|||
this.WriteCode(12, 1, compressedData); |
|||
this.WriteCode(12, 1, compressedData); |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
protected override void Dispose(bool disposing) |
|||
{ |
|||
this.referenceLineBuffer?.Dispose(); |
|||
base.Dispose(disposing); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Finds the end of a pixel run.
|
|||
/// </summary>
|
|||
/// <param name="row">The row of pixels to examine.</param>
|
|||
/// <param name="startIndex">The index of the first pixel in <paramref name="row"/> to examine.</param>
|
|||
/// <param name="color">Color of pixels in the run. If not specified, the color at
|
|||
/// <paramref name="startIndex"/> will be used.</param>
|
|||
/// <returns>The index of the first pixel at or after <paramref name="startIndex"/>
|
|||
/// that does not match <paramref name="color"/>, or the length of <paramref name="row"/>,
|
|||
/// whichever comes first.</returns>
|
|||
private uint FindRunEnd(Span<byte> row, uint startIndex, byte? color = null) |
|||
{ |
|||
if (startIndex >= row.Length) |
|||
{ |
|||
return (uint)row.Length; |
|||
} |
|||
|
|||
byte colorValue = color.GetValueOrDefault(row[(int)startIndex]); |
|||
for (int i = (int)startIndex; i < row.Length; i++) |
|||
{ |
|||
if (row[i] != colorValue) |
|||
{ |
|||
return (uint)i; |
|||
} |
|||
} |
|||
|
|||
return (uint)row.Length; |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
public override void Initialize(int rowsPerStrip) |
|||
{ |
|||
base.Initialize(rowsPerStrip); |
|||
this.referenceLineBuffer = this.Allocator.Allocate<byte>(this.Width); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a run to the output buffer.
|
|||
/// </summary>
|
|||
/// <param name="runLength">The length of the run.</param>
|
|||
/// <param name="isWhiteRun">If <c>true</c> the run is white pixels,
|
|||
/// if <c>false</c> the run is black pixels.</param>
|
|||
/// <param name="compressedData">The destination to write the run to.</param>
|
|||
private void WriteRun(uint runLength, bool isWhiteRun, Span<byte> compressedData) |
|||
{ |
|||
uint code; |
|||
uint codeLength; |
|||
while (runLength > 63) |
|||
{ |
|||
uint makeupLength = this.GetBestFittingMakeupRunLength(runLength); |
|||
code = this.GetMakeupCode(makeupLength, out codeLength, isWhiteRun); |
|||
this.WriteCode(codeLength, code, compressedData); |
|||
runLength -= makeupLength; |
|||
} |
|||
|
|||
code = this.GetTermCode(runLength, out codeLength, isWhiteRun); |
|||
this.WriteCode(codeLength, code, compressedData); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,536 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Buffers; |
|||
using System.Collections.Generic; |
|||
using System.IO; |
|||
using SixLabors.ImageSharp.Memory; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors |
|||
{ |
|||
/// <summary>
|
|||
/// Common functionality for CCITT T4 and T6 Compression
|
|||
/// </summary>
|
|||
internal abstract class TiffCcittCompressor : TiffBaseCompressor |
|||
{ |
|||
protected const uint WhiteZeroRunTermCode = 0x35; |
|||
|
|||
protected const uint BlackZeroRunTermCode = 0x37; |
|||
|
|||
private static readonly uint[] MakeupRunLength = |
|||
{ |
|||
64, 128, 192, 256, 320, 384, 448, 512, 576, 640, 704, 768, 832, 896, 960, 1024, 1088, 1152, 1216, 1280, 1344, 1408, 1472, 1536, 1600, 1664, 1728, 1792, 1856, 1920, 1984, 2048, 2112, 2176, 2240, 2304, 2368, 2432, 2496, 2560 |
|||
}; |
|||
|
|||
private static readonly Dictionary<uint, uint> WhiteLen4TermCodes = new Dictionary<uint, uint>() |
|||
{ |
|||
{ 2, 0x7 }, { 3, 0x8 }, { 4, 0xB }, { 5, 0xC }, { 6, 0xE }, { 7, 0xF } |
|||
}; |
|||
|
|||
private static readonly Dictionary<uint, uint> WhiteLen5TermCodes = new Dictionary<uint, uint>() |
|||
{ |
|||
{ 8, 0x13 }, { 9, 0x14 }, { 10, 0x7 }, { 11, 0x8 } |
|||
}; |
|||
|
|||
private static readonly Dictionary<uint, uint> WhiteLen6TermCodes = new Dictionary<uint, uint>() |
|||
{ |
|||
{ 1, 0x7 }, { 12, 0x8 }, { 13, 0x3 }, { 14, 0x34 }, { 15, 0x35 }, { 16, 0x2A }, { 17, 0x2B } |
|||
}; |
|||
|
|||
private static readonly Dictionary<uint, uint> WhiteLen7TermCodes = new Dictionary<uint, uint>() |
|||
{ |
|||
{ 18, 0x27 }, { 19, 0xC }, { 20, 0x8 }, { 21, 0x17 }, { 22, 0x3 }, { 23, 0x4 }, { 24, 0x28 }, { 25, 0x2B }, { 26, 0x13 }, |
|||
{ 27, 0x24 }, { 28, 0x18 } |
|||
}; |
|||
|
|||
private static readonly Dictionary<uint, uint> WhiteLen8TermCodes = new Dictionary<uint, uint>() |
|||
{ |
|||
{ 0, WhiteZeroRunTermCode }, { 29, 0x2 }, { 30, 0x3 }, { 31, 0x1A }, { 32, 0x1B }, { 33, 0x12 }, { 34, 0x13 }, { 35, 0x14 }, |
|||
{ 36, 0x15 }, { 37, 0x16 }, { 38, 0x17 }, { 39, 0x28 }, { 40, 0x29 }, { 41, 0x2A }, { 42, 0x2B }, { 43, 0x2C }, { 44, 0x2D }, |
|||
{ 45, 0x4 }, { 46, 0x5 }, { 47, 0xA }, { 48, 0xB }, { 49, 0x52 }, { 50, 0x53 }, { 51, 0x54 }, { 52, 0x55 }, { 53, 0x24 }, |
|||
{ 54, 0x25 }, { 55, 0x58 }, { 56, 0x59 }, { 57, 0x5A }, { 58, 0x5B }, { 59, 0x4A }, { 60, 0x4B }, { 61, 0x32 }, { 62, 0x33 }, |
|||
{ 63, 0x34 } |
|||
}; |
|||
|
|||
private static readonly Dictionary<uint, uint> BlackLen2TermCodes = new Dictionary<uint, uint>() |
|||
{ |
|||
{ 2, 0x3 }, { 3, 0x2 } |
|||
}; |
|||
|
|||
private static readonly Dictionary<uint, uint> BlackLen3TermCodes = new Dictionary<uint, uint>() |
|||
{ |
|||
{ 1, 0x2 }, { 4, 0x3 } |
|||
}; |
|||
|
|||
private static readonly Dictionary<uint, uint> BlackLen4TermCodes = new Dictionary<uint, uint>() |
|||
{ |
|||
{ 5, 0x3 }, { 6, 0x2 } |
|||
}; |
|||
|
|||
private static readonly Dictionary<uint, uint> BlackLen5TermCodes = new Dictionary<uint, uint>() |
|||
{ |
|||
{ 7, 0x3 } |
|||
}; |
|||
|
|||
private static readonly Dictionary<uint, uint> BlackLen6TermCodes = new Dictionary<uint, uint>() |
|||
{ |
|||
{ 8, 0x5 }, { 9, 0x4 } |
|||
}; |
|||
|
|||
private static readonly Dictionary<uint, uint> BlackLen7TermCodes = new Dictionary<uint, uint>() |
|||
{ |
|||
{ 10, 0x4 }, { 11, 0x5 }, { 12, 0x7 } |
|||
}; |
|||
|
|||
private static readonly Dictionary<uint, uint> BlackLen8TermCodes = new Dictionary<uint, uint>() |
|||
{ |
|||
{ 13, 0x4 }, { 14, 0x7 } |
|||
}; |
|||
|
|||
private static readonly Dictionary<uint, uint> BlackLen9TermCodes = new Dictionary<uint, uint>() |
|||
{ |
|||
{ 15, 0x18 } |
|||
}; |
|||
|
|||
private static readonly Dictionary<uint, uint> BlackLen10TermCodes = new Dictionary<uint, uint>() |
|||
{ |
|||
{ 0, BlackZeroRunTermCode }, { 16, 0x17 }, { 17, 0x18 }, { 18, 0x8 } |
|||
}; |
|||
|
|||
private static readonly Dictionary<uint, uint> BlackLen11TermCodes = new Dictionary<uint, uint>() |
|||
{ |
|||
{ 19, 0x67 }, { 20, 0x68 }, { 21, 0x6C }, { 22, 0x37 }, { 23, 0x28 }, { 24, 0x17 }, { 25, 0x18 } |
|||
}; |
|||
|
|||
private static readonly Dictionary<uint, uint> BlackLen12TermCodes = new Dictionary<uint, uint>() |
|||
{ |
|||
{ 26, 0xCA }, { 27, 0xCB }, { 28, 0xCC }, { 29, 0xCD }, { 30, 0x68 }, { 31, 0x69 }, { 32, 0x6A }, { 33, 0x6B }, { 34, 0xD2 }, |
|||
{ 35, 0xD3 }, { 36, 0xD4 }, { 37, 0xD5 }, { 38, 0xD6 }, { 39, 0xD7 }, { 40, 0x6C }, { 41, 0x6D }, { 42, 0xDA }, { 43, 0xDB }, |
|||
{ 44, 0x54 }, { 45, 0x55 }, { 46, 0x56 }, { 47, 0x57 }, { 48, 0x64 }, { 49, 0x65 }, { 50, 0x52 }, { 51, 0x53 }, { 52, 0x24 }, |
|||
{ 53, 0x37 }, { 54, 0x38 }, { 55, 0x27 }, { 56, 0x28 }, { 57, 0x58 }, { 58, 0x59 }, { 59, 0x2B }, { 60, 0x2C }, { 61, 0x5A }, |
|||
{ 62, 0x66 }, { 63, 0x67 } |
|||
}; |
|||
|
|||
private static readonly Dictionary<uint, uint> WhiteLen5MakeupCodes = new Dictionary<uint, uint>() |
|||
{ |
|||
{ 64, 0x1B }, { 128, 0x12 } |
|||
}; |
|||
|
|||
private static readonly Dictionary<uint, uint> WhiteLen6MakeupCodes = new Dictionary<uint, uint>() |
|||
{ |
|||
{ 192, 0x17 }, { 1664, 0x18 } |
|||
}; |
|||
|
|||
private static readonly Dictionary<uint, uint> WhiteLen8MakeupCodes = new Dictionary<uint, uint>() |
|||
{ |
|||
{ 320, 0x36 }, { 384, 0x37 }, { 448, 0x64 }, { 512, 0x65 }, { 576, 0x68 }, { 640, 0x67 } |
|||
}; |
|||
|
|||
private static readonly Dictionary<uint, uint> WhiteLen7MakeupCodes = new Dictionary<uint, uint>() |
|||
{ |
|||
{ 256, 0x37 } |
|||
}; |
|||
|
|||
private static readonly Dictionary<uint, uint> WhiteLen9MakeupCodes = new Dictionary<uint, uint>() |
|||
{ |
|||
{ 704, 0xCC }, { 768, 0xCD }, { 832, 0xD2 }, { 896, 0xD3 }, { 960, 0xD4 }, { 1024, 0xD5 }, { 1088, 0xD6 }, |
|||
{ 1152, 0xD7 }, { 1216, 0xD8 }, { 1280, 0xD9 }, { 1344, 0xDA }, { 1408, 0xDB }, { 1472, 0x98 }, { 1536, 0x99 }, |
|||
{ 1600, 0x9A }, { 1728, 0x9B } |
|||
}; |
|||
|
|||
private static readonly Dictionary<uint, uint> WhiteLen11MakeupCodes = new Dictionary<uint, uint>() |
|||
{ |
|||
{ 1792, 0x8 }, { 1856, 0xC }, { 1920, 0xD } |
|||
}; |
|||
|
|||
private static readonly Dictionary<uint, uint> WhiteLen12MakeupCodes = new Dictionary<uint, uint>() |
|||
{ |
|||
{ 1984, 0x12 }, { 2048, 0x13 }, { 2112, 0x14 }, { 2176, 0x15 }, { 2240, 0x16 }, { 2304, 0x17 }, { 2368, 0x1C }, |
|||
{ 2432, 0x1D }, { 2496, 0x1E }, { 2560, 0x1F } |
|||
}; |
|||
|
|||
private static readonly Dictionary<uint, uint> BlackLen10MakeupCodes = new Dictionary<uint, uint>() |
|||
{ |
|||
{ 64, 0xF } |
|||
}; |
|||
|
|||
private static readonly Dictionary<uint, uint> BlackLen11MakeupCodes = new Dictionary<uint, uint>() |
|||
{ |
|||
{ 1792, 0x8 }, { 1856, 0xC }, { 1920, 0xD } |
|||
}; |
|||
|
|||
private static readonly Dictionary<uint, uint> BlackLen12MakeupCodes = new Dictionary<uint, uint>() |
|||
{ |
|||
{ 128, 0xC8 }, { 192, 0xC9 }, { 256, 0x5B }, { 320, 0x33 }, { 384, 0x34 }, { 448, 0x35 }, |
|||
{ 1984, 0x12 }, { 2048, 0x13 }, { 2112, 0x14 }, { 2176, 0x15 }, { 2240, 0x16 }, { 2304, 0x17 }, { 2368, 0x1C }, |
|||
{ 2432, 0x1D }, { 2496, 0x1E }, { 2560, 0x1F } |
|||
}; |
|||
|
|||
private static readonly Dictionary<uint, uint> BlackLen13MakeupCodes = new Dictionary<uint, uint>() |
|||
{ |
|||
{ 512, 0x6C }, { 576, 0x6D }, { 640, 0x4A }, { 704, 0x4B }, { 768, 0x4C }, { 832, 0x4D }, { 896, 0x72 }, |
|||
{ 960, 0x73 }, { 1024, 0x74 }, { 1088, 0x75 }, { 1152, 0x76 }, { 1216, 0x77 }, { 1280, 0x52 }, { 1344, 0x53 }, |
|||
{ 1408, 0x54 }, { 1472, 0x55 }, { 1536, 0x5A }, { 1600, 0x5B }, { 1664, 0x64 }, { 1728, 0x65 } |
|||
}; |
|||
|
|||
private int bytePosition; |
|||
|
|||
private byte bitPosition; |
|||
|
|||
private IMemoryOwner<byte> compressedDataBuffer; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="TiffCcittCompressor" /> class.
|
|||
/// </summary>
|
|||
/// <param name="output">The output.</param>
|
|||
/// <param name="allocator">The allocator.</param>
|
|||
/// <param name="width">The width.</param>
|
|||
/// <param name="bitsPerPixel">The bits per pixel.</param>
|
|||
protected TiffCcittCompressor(Stream output, MemoryAllocator allocator, int width, int bitsPerPixel) |
|||
: base(output, allocator, width, bitsPerPixel) |
|||
{ |
|||
DebugGuard.IsTrue(bitsPerPixel == 1, nameof(bitsPerPixel), "CCITT compression requires one bit per pixel"); |
|||
this.bytePosition = 0; |
|||
this.bitPosition = 0; |
|||
} |
|||
|
|||
private uint GetWhiteMakeupCode(uint runLength, out uint codeLength) |
|||
{ |
|||
codeLength = 0; |
|||
|
|||
if (WhiteLen5MakeupCodes.ContainsKey(runLength)) |
|||
{ |
|||
codeLength = 5; |
|||
return WhiteLen5MakeupCodes[runLength]; |
|||
} |
|||
|
|||
if (WhiteLen6MakeupCodes.ContainsKey(runLength)) |
|||
{ |
|||
codeLength = 6; |
|||
return WhiteLen6MakeupCodes[runLength]; |
|||
} |
|||
|
|||
if (WhiteLen7MakeupCodes.ContainsKey(runLength)) |
|||
{ |
|||
codeLength = 7; |
|||
return WhiteLen7MakeupCodes[runLength]; |
|||
} |
|||
|
|||
if (WhiteLen8MakeupCodes.ContainsKey(runLength)) |
|||
{ |
|||
codeLength = 8; |
|||
return WhiteLen8MakeupCodes[runLength]; |
|||
} |
|||
|
|||
if (WhiteLen9MakeupCodes.ContainsKey(runLength)) |
|||
{ |
|||
codeLength = 9; |
|||
return WhiteLen9MakeupCodes[runLength]; |
|||
} |
|||
|
|||
if (WhiteLen11MakeupCodes.ContainsKey(runLength)) |
|||
{ |
|||
codeLength = 11; |
|||
return WhiteLen11MakeupCodes[runLength]; |
|||
} |
|||
|
|||
if (WhiteLen12MakeupCodes.ContainsKey(runLength)) |
|||
{ |
|||
codeLength = 12; |
|||
return WhiteLen12MakeupCodes[runLength]; |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
private uint GetBlackMakeupCode(uint runLength, out uint codeLength) |
|||
{ |
|||
codeLength = 0; |
|||
|
|||
if (BlackLen10MakeupCodes.ContainsKey(runLength)) |
|||
{ |
|||
codeLength = 10; |
|||
return BlackLen10MakeupCodes[runLength]; |
|||
} |
|||
|
|||
if (BlackLen11MakeupCodes.ContainsKey(runLength)) |
|||
{ |
|||
codeLength = 11; |
|||
return BlackLen11MakeupCodes[runLength]; |
|||
} |
|||
|
|||
if (BlackLen12MakeupCodes.ContainsKey(runLength)) |
|||
{ |
|||
codeLength = 12; |
|||
return BlackLen12MakeupCodes[runLength]; |
|||
} |
|||
|
|||
if (BlackLen13MakeupCodes.ContainsKey(runLength)) |
|||
{ |
|||
codeLength = 13; |
|||
return BlackLen13MakeupCodes[runLength]; |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
private uint GetWhiteTermCode(uint runLength, out uint codeLength) |
|||
{ |
|||
codeLength = 0; |
|||
|
|||
if (WhiteLen4TermCodes.ContainsKey(runLength)) |
|||
{ |
|||
codeLength = 4; |
|||
return WhiteLen4TermCodes[runLength]; |
|||
} |
|||
|
|||
if (WhiteLen5TermCodes.ContainsKey(runLength)) |
|||
{ |
|||
codeLength = 5; |
|||
return WhiteLen5TermCodes[runLength]; |
|||
} |
|||
|
|||
if (WhiteLen6TermCodes.ContainsKey(runLength)) |
|||
{ |
|||
codeLength = 6; |
|||
return WhiteLen6TermCodes[runLength]; |
|||
} |
|||
|
|||
if (WhiteLen7TermCodes.ContainsKey(runLength)) |
|||
{ |
|||
codeLength = 7; |
|||
return WhiteLen7TermCodes[runLength]; |
|||
} |
|||
|
|||
if (WhiteLen8TermCodes.ContainsKey(runLength)) |
|||
{ |
|||
codeLength = 8; |
|||
return WhiteLen8TermCodes[runLength]; |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
private uint GetBlackTermCode(uint runLength, out uint codeLength) |
|||
{ |
|||
codeLength = 0; |
|||
|
|||
if (BlackLen2TermCodes.ContainsKey(runLength)) |
|||
{ |
|||
codeLength = 2; |
|||
return BlackLen2TermCodes[runLength]; |
|||
} |
|||
|
|||
if (BlackLen3TermCodes.ContainsKey(runLength)) |
|||
{ |
|||
codeLength = 3; |
|||
return BlackLen3TermCodes[runLength]; |
|||
} |
|||
|
|||
if (BlackLen4TermCodes.ContainsKey(runLength)) |
|||
{ |
|||
codeLength = 4; |
|||
return BlackLen4TermCodes[runLength]; |
|||
} |
|||
|
|||
if (BlackLen5TermCodes.ContainsKey(runLength)) |
|||
{ |
|||
codeLength = 5; |
|||
return BlackLen5TermCodes[runLength]; |
|||
} |
|||
|
|||
if (BlackLen6TermCodes.ContainsKey(runLength)) |
|||
{ |
|||
codeLength = 6; |
|||
return BlackLen6TermCodes[runLength]; |
|||
} |
|||
|
|||
if (BlackLen7TermCodes.ContainsKey(runLength)) |
|||
{ |
|||
codeLength = 7; |
|||
return BlackLen7TermCodes[runLength]; |
|||
} |
|||
|
|||
if (BlackLen8TermCodes.ContainsKey(runLength)) |
|||
{ |
|||
codeLength = 8; |
|||
return BlackLen8TermCodes[runLength]; |
|||
} |
|||
|
|||
if (BlackLen9TermCodes.ContainsKey(runLength)) |
|||
{ |
|||
codeLength = 9; |
|||
return BlackLen9TermCodes[runLength]; |
|||
} |
|||
|
|||
if (BlackLen10TermCodes.ContainsKey(runLength)) |
|||
{ |
|||
codeLength = 10; |
|||
return BlackLen10TermCodes[runLength]; |
|||
} |
|||
|
|||
if (BlackLen11TermCodes.ContainsKey(runLength)) |
|||
{ |
|||
codeLength = 11; |
|||
return BlackLen11TermCodes[runLength]; |
|||
} |
|||
|
|||
if (BlackLen12TermCodes.ContainsKey(runLength)) |
|||
{ |
|||
codeLength = 12; |
|||
return BlackLen12TermCodes[runLength]; |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the best makeup run length for a given run length
|
|||
/// </summary>
|
|||
/// <param name="runLength">A run length needing a makeup code</param>
|
|||
/// <returns>The makeup length for <paramref name="runLength"/>.</returns>
|
|||
protected uint GetBestFittingMakeupRunLength(uint runLength) |
|||
{ |
|||
DebugGuard.MustBeGreaterThanOrEqualTo(runLength, MakeupRunLength[0], nameof(runLength)); |
|||
|
|||
for (int i = 0; i < MakeupRunLength.Length - 1; i++) |
|||
{ |
|||
if (MakeupRunLength[i] <= runLength && MakeupRunLength[i + 1] > runLength) |
|||
{ |
|||
return MakeupRunLength[i]; |
|||
} |
|||
} |
|||
|
|||
return MakeupRunLength[MakeupRunLength.Length - 1]; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the terminating code for a run length.
|
|||
/// </summary>
|
|||
/// <param name="runLength">The run length to get the terminating code for.</param>
|
|||
/// <param name="codeLength">The length of the terminating code.</param>
|
|||
/// <param name="isWhiteRun">If <c>true</c>, the run is of white pixels.
|
|||
/// If <c>false</c> the run is of black pixels</param>
|
|||
/// <returns>The terminating code for a run of length <paramref name="runLength"/></returns>
|
|||
protected uint GetTermCode(uint runLength, out uint codeLength, bool isWhiteRun) |
|||
{ |
|||
if (isWhiteRun) |
|||
{ |
|||
return this.GetWhiteTermCode(runLength, out codeLength); |
|||
} |
|||
|
|||
return this.GetBlackTermCode(runLength, out codeLength); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the makeup code for a run length.
|
|||
/// </summary>
|
|||
/// <param name="runLength">The run length to get the makeup code for.</param>
|
|||
/// <param name="codeLength">The length of the makeup code.</param>
|
|||
/// <param name="isWhiteRun">If <c>true</c>, the run is of white pixels.
|
|||
/// If <c>false</c> the run is of black pixels</param>
|
|||
/// <returns>The makeup code for a run of length <paramref name="runLength"/></returns>
|
|||
protected uint GetMakeupCode(uint runLength, out uint codeLength, bool isWhiteRun) |
|||
{ |
|||
if (isWhiteRun) |
|||
{ |
|||
return this.GetWhiteMakeupCode(runLength, out codeLength); |
|||
} |
|||
|
|||
return this.GetBlackMakeupCode(runLength, out codeLength); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Pads output to the next byte
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// If the output is not currently on a byte boundary,
|
|||
/// zero-pad it to the next byte
|
|||
/// </remarks>
|
|||
protected void PadByte() |
|||
{ |
|||
// Check if padding is necessary.
|
|||
if (this.bitPosition % 8 != 0) |
|||
{ |
|||
// Skip padding bits, move to next byte.
|
|||
this.bytePosition++; |
|||
this.bitPosition = 0; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a code to the output.
|
|||
/// </summary>
|
|||
/// <param name="codeLength">The length of the code to write.</param>
|
|||
/// <param name="code">The code to be written.</param>
|
|||
/// <param name="compressedData">The destination buffer to write the code to.</param>
|
|||
protected void WriteCode(uint codeLength, uint code, Span<byte> compressedData) |
|||
{ |
|||
while (codeLength > 0) |
|||
{ |
|||
int bitNumber = (int)codeLength; |
|||
bool bit = (code & (1 << (bitNumber - 1))) != 0; |
|||
if (bit) |
|||
{ |
|||
BitWriterUtils.WriteBit(compressedData, this.bytePosition, this.bitPosition); |
|||
} |
|||
else |
|||
{ |
|||
BitWriterUtils.WriteZeroBit(compressedData, this.bytePosition, this.bitPosition); |
|||
} |
|||
|
|||
this.bitPosition++; |
|||
if (this.bitPosition == 8) |
|||
{ |
|||
this.bytePosition++; |
|||
this.bitPosition = 0; |
|||
} |
|||
|
|||
codeLength--; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a image compressed with CCITT T6 to the stream.
|
|||
/// </summary>
|
|||
/// <param name="pixelsAsGray">The pixels as 8-bit gray array.</param>
|
|||
/// <param name="height">The strip height.</param>
|
|||
public override void CompressStrip(Span<byte> pixelsAsGray, int height) |
|||
{ |
|||
DebugGuard.IsTrue(pixelsAsGray.Length / height == this.Width, "Values must be equals"); |
|||
DebugGuard.IsTrue(pixelsAsGray.Length % height == 0, "Values must be equals"); |
|||
|
|||
this.compressedDataBuffer.Clear(); |
|||
Span<byte> compressedData = this.compressedDataBuffer.GetSpan(); |
|||
|
|||
this.bytePosition = 0; |
|||
this.bitPosition = 0; |
|||
|
|||
this.CompressStrip(pixelsAsGray, height, compressedData); |
|||
|
|||
// Write the compressed data to the stream.
|
|||
int bytesToWrite = this.bitPosition != 0 ? this.bytePosition + 1 : this.bytePosition; |
|||
this.Output.Write(compressedData.Slice(0, bytesToWrite)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Compress a data strip
|
|||
/// </summary>
|
|||
/// <param name="pixelsAsGray">The pixels as 8-bit gray array.</param>
|
|||
/// <param name="height">The strip height.</param>
|
|||
/// <param name="compressedData">The destination for the compressed data.</param>
|
|||
protected abstract void CompressStrip(Span<byte> pixelsAsGray, int height, Span<byte> compressedData); |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void Dispose(bool disposing) => this.compressedDataBuffer?.Dispose(); |
|||
|
|||
/// <inheritdoc/>
|
|||
public override void Initialize(int rowsPerStrip) |
|||
{ |
|||
// This is too much memory allocated, but just 1 bit per pixel will not do, if the compression rate is not good.
|
|||
int maxNeededBytes = this.Width * rowsPerStrip; |
|||
this.compressedDataBuffer = this.Allocator.Allocate<byte>(maxNeededBytes); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; |
|||
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors |
|||
{ |
|||
/// <summary>
|
|||
/// Spectral converter for gray TIFF's which use the JPEG compression.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
|
|||
internal sealed class GrayJpegSpectralConverter<TPixel> : SpectralConverter<TPixel> |
|||
where TPixel : unmanaged, IPixel<TPixel> |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="GrayJpegSpectralConverter{TPixel}"/> class.
|
|||
/// </summary>
|
|||
/// <param name="configuration">The configuration.</param>
|
|||
public GrayJpegSpectralConverter(Configuration configuration) |
|||
: base(configuration) |
|||
{ |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override JpegColorConverterBase GetColorConverter(JpegFrame frame, IRawJpegData jpegData) => JpegColorConverterBase.GetConverter(JpegColorSpace.Grayscale, frame.Precision); |
|||
} |
|||
} |
|||
@ -0,0 +1,98 @@ |
|||
// 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 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; |
|||
|
|||
private readonly MemoryAllocator memoryAllocator; |
|||
|
|||
private readonly TiffExtraSampleType? extraSamplesType; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Rgba16161616TiffColor{TPixel}" /> class.
|
|||
/// </summary>
|
|||
/// <param name="configuration">The configuration.</param>
|
|||
/// <param name="memoryAllocator">The memory allocator.</param>
|
|||
/// <param name="isBigEndian">if set to <c>true</c> decodes the pixel data as big endian, otherwise as little endian.</param>
|
|||
/// <param name="extraSamplesType">The type of the extra samples.</param>
|
|||
public Rgba16161616TiffColor(Configuration configuration, MemoryAllocator memoryAllocator, TiffExtraSampleType? extraSamplesType, bool isBigEndian) |
|||
{ |
|||
this.configuration = configuration; |
|||
this.isBigEndian = isBigEndian; |
|||
this.memoryAllocator = memoryAllocator; |
|||
this.extraSamplesType = extraSamplesType; |
|||
} |
|||
|
|||
/// <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.FromScaledVector4(TiffUtils.Vector4Default); |
|||
|
|||
bool hasAssociatedAlpha = this.extraSamplesType.HasValue && this.extraSamplesType == TiffExtraSampleType.AssociatedAlphaData; |
|||
int offset = 0; |
|||
|
|||
using IMemoryOwner<Vector4> vectors = hasAssociatedAlpha ? this.memoryAllocator.Allocate<Vector4>(width) : null; |
|||
Span<Vector4> vectorsSpan = hasAssociatedAlpha ? vectors.GetSpan() : Span<Vector4>.Empty; |
|||
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] = hasAssociatedAlpha ? |
|||
TiffUtils.ColorFromRgba64Premultiplied(rgba, r, g, b, a, color) : |
|||
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); |
|||
|
|||
if (hasAssociatedAlpha) |
|||
{ |
|||
PixelOperations<TPixel>.Instance.ToVector4(this.configuration, pixelRow, vectorsSpan); |
|||
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, vectorsSpan, pixelRow, PixelConversionModifiers.Premultiply | PixelConversionModifiers.Scale); |
|||
} |
|||
|
|||
offset += byteCount; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,87 @@ |
|||
// 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; |
|||
|
|||
private readonly TiffExtraSampleType? extraSamplesType; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Rgba16PlanarTiffColor{TPixel}" /> class.
|
|||
/// </summary>
|
|||
/// <param name="extraSamplesType">The extra samples type.</param>
|
|||
/// <param name="isBigEndian">If set to <c>true</c> decodes the pixel data as big endian, otherwise as little endian.</param>
|
|||
public Rgba16PlanarTiffColor(TiffExtraSampleType? extraSamplesType, bool isBigEndian) |
|||
{ |
|||
this.extraSamplesType = extraSamplesType; |
|||
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.FromScaledVector4(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(); |
|||
|
|||
bool hasAssociatedAlpha = this.extraSamplesType.HasValue && this.extraSamplesType == TiffExtraSampleType.AssociatedAlphaData; |
|||
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] = hasAssociatedAlpha ? |
|||
TiffUtils.ColorFromRgba64Premultiplied(rgba, r, g, b, a, color) : |
|||
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.ConvertToUShortLittleEndian(alphaData.Slice(offset, 2)); |
|||
|
|||
offset += 2; |
|||
|
|||
pixelRow[x] = hasAssociatedAlpha ? |
|||
TiffUtils.ColorFromRgba64Premultiplied(rgba, r, g, b, a, color) : |
|||
TiffUtils.ColorFromRgba64(rgba, r, g, b, a, color); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,104 @@ |
|||
// 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; |
|||
|
|||
private readonly TiffExtraSampleType? extraSamplesType; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Rgba24242424TiffColor{TPixel}" /> class.
|
|||
/// </summary>
|
|||
/// <param name="extraSamplesType">The type of the extra samples.</param>
|
|||
/// <param name="isBigEndian">if set to <c>true</c> decodes the pixel data as big endian, otherwise as little endian.</param>
|
|||
public Rgba24242424TiffColor(TiffExtraSampleType? extraSamplesType, bool isBigEndian) |
|||
{ |
|||
this.extraSamplesType = extraSamplesType; |
|||
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.FromScaledVector4(TiffUtils.Vector4Default); |
|||
|
|||
bool hasAssociatedAlpha = this.extraSamplesType.HasValue && this.extraSamplesType == TiffExtraSampleType.AssociatedAlphaData; |
|||
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] = hasAssociatedAlpha ? |
|||
TiffUtils.ColorScaleTo24BitPremultiplied(r, g, b, a, color) : |
|||
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] = hasAssociatedAlpha ? |
|||
TiffUtils.ColorScaleTo24BitPremultiplied(r, g, b, a, color) : |
|||
TiffUtils.ColorScaleTo24Bit(r, g, b, a, color); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,97 @@ |
|||
// 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; |
|||
|
|||
private readonly TiffExtraSampleType? extraSamplesType; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Rgba24PlanarTiffColor{TPixel}" /> class.
|
|||
/// </summary>
|
|||
/// <param name="extraSamplesType">The extra samples type.</param>
|
|||
/// <param name="isBigEndian">if set to <c>true</c> decodes the pixel data as big endian, otherwise as little endian.</param>
|
|||
public Rgba24PlanarTiffColor(TiffExtraSampleType? extraSamplesType, bool isBigEndian) |
|||
{ |
|||
this.extraSamplesType = extraSamplesType; |
|||
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.FromScaledVector4(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); |
|||
|
|||
bool hasAssociatedAlpha = this.extraSamplesType.HasValue && this.extraSamplesType == TiffExtraSampleType.AssociatedAlphaData; |
|||
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] = hasAssociatedAlpha ? |
|||
TiffUtils.ColorScaleTo24BitPremultiplied(r, g, b, a, color) : |
|||
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] = hasAssociatedAlpha ? |
|||
TiffUtils.ColorScaleTo24BitPremultiplied(r, g, b, a, color) : |
|||
TiffUtils.ColorScaleTo24Bit(r, g, b, a, color); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,92 @@ |
|||
// 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; |
|||
|
|||
private readonly TiffExtraSampleType? extraSamplesType; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Rgba32323232TiffColor{TPixel}" /> class.
|
|||
/// </summary>
|
|||
/// <param name="extraSamplesType">The type of the extra samples.</param>
|
|||
/// <param name="isBigEndian">if set to <c>true</c> decodes the pixel data as big endian, otherwise as little endian.</param>
|
|||
public Rgba32323232TiffColor(TiffExtraSampleType? extraSamplesType, bool isBigEndian) |
|||
{ |
|||
this.extraSamplesType = extraSamplesType; |
|||
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.FromScaledVector4(TiffUtils.Vector4Default); |
|||
|
|||
bool hasAssociatedAlpha = this.extraSamplesType.HasValue && this.extraSamplesType == TiffExtraSampleType.AssociatedAlphaData; |
|||
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] = hasAssociatedAlpha ? |
|||
TiffUtils.ColorScaleTo32BitPremultiplied(r, g, b, a, color) : |
|||
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] = hasAssociatedAlpha ? |
|||
TiffUtils.ColorScaleTo32BitPremultiplied(r, g, b, a, color) : |
|||
TiffUtils.ColorScaleTo32Bit(r, g, b, a, color); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,86 @@ |
|||
// 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; |
|||
|
|||
private readonly TiffExtraSampleType? extraSamplesType; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Rgba32PlanarTiffColor{TPixel}" /> class.
|
|||
/// </summary>
|
|||
/// <param name="extraSamplesType">The extra samples type.</param>
|
|||
/// <param name="isBigEndian">if set to <c>true</c> decodes the pixel data as big endian, otherwise as little endian.</param>
|
|||
public Rgba32PlanarTiffColor(TiffExtraSampleType? extraSamplesType, bool isBigEndian) |
|||
{ |
|||
this.extraSamplesType = extraSamplesType; |
|||
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.FromScaledVector4(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(); |
|||
|
|||
bool hasAssociatedAlpha = this.extraSamplesType.HasValue && this.extraSamplesType == TiffExtraSampleType.AssociatedAlphaData; |
|||
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] = hasAssociatedAlpha ? |
|||
TiffUtils.ColorScaleTo32BitPremultiplied(r, g, b, a, color) : |
|||
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] = hasAssociatedAlpha ? |
|||
TiffUtils.ColorScaleTo32BitPremultiplied(r, g, b, a, color) : |
|||
TiffUtils.ColorScaleTo32Bit(r, g, b, a, color); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,62 @@ |
|||
// 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 8 bits per channel.
|
|||
/// </summary>
|
|||
internal class Rgba8888TiffColor<TPixel> : TiffBaseColorDecoder<TPixel> |
|||
where TPixel : unmanaged, IPixel<TPixel> |
|||
{ |
|||
private readonly Configuration configuration; |
|||
|
|||
private readonly MemoryAllocator memoryAllocator; |
|||
|
|||
private readonly TiffExtraSampleType? extraSamplesType; |
|||
|
|||
public Rgba8888TiffColor(Configuration configuration, MemoryAllocator memoryAllocator, TiffExtraSampleType? extraSamplesType) |
|||
{ |
|||
this.configuration = configuration; |
|||
this.memoryAllocator = memoryAllocator; |
|||
this.extraSamplesType = extraSamplesType; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> pixels, int left, int top, int width, int height) |
|||
{ |
|||
int offset = 0; |
|||
bool hasAssociatedAlpha = this.extraSamplesType.HasValue && this.extraSamplesType == TiffExtraSampleType.AssociatedAlphaData; |
|||
|
|||
var color = default(TPixel); |
|||
color.FromScaledVector4(TiffUtils.Vector4Default); |
|||
using IMemoryOwner<Vector4> vectors = hasAssociatedAlpha ? this.memoryAllocator.Allocate<Vector4>(width) : null; |
|||
Span<Vector4> vectorsSpan = hasAssociatedAlpha ? vectors.GetSpan() : Span<Vector4>.Empty; |
|||
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); |
|||
|
|||
if (hasAssociatedAlpha) |
|||
{ |
|||
PixelOperations<TPixel>.Instance.ToVector4(this.configuration, pixelRow, vectorsSpan); |
|||
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, vectorsSpan, pixelRow, PixelConversionModifiers.Premultiply | PixelConversionModifiers.Scale); |
|||
} |
|||
|
|||
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.FromScaledVector4(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.FromScaledVector4(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.FromScaledVector4(colorVector); |
|||
pixelRow[x] = color; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,101 @@ |
|||
// 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; |
|||
|
|||
private readonly TiffExtraSampleType? extraSampleType; |
|||
|
|||
public RgbaPlanarTiffColor(TiffExtraSampleType? extraSampleType, 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; |
|||
|
|||
this.extraSampleType = extraSampleType; |
|||
} |
|||
|
|||
/// <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); |
|||
bool hasAssociatedAlpha = this.extraSampleType.HasValue && this.extraSampleType == TiffExtraSampleType.AssociatedAlphaData; |
|||
|
|||
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; |
|||
|
|||
var vec = new Vector4(r, g, b, a); |
|||
if (hasAssociatedAlpha) |
|||
{ |
|||
color = TiffUtils.UnPremultiply(ref vec, color); |
|||
} |
|||
else |
|||
{ |
|||
color.FromScaledVector4(vec); |
|||
} |
|||
|
|||
pixelRow[x] = color; |
|||
} |
|||
|
|||
rBitReader.NextRow(); |
|||
gBitReader.NextRow(); |
|||
bBitReader.NextRow(); |
|||
aBitReader.NextRow(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue