Skip to content

Commit 659daff

Browse files
Merge pull request #1734 from SixLabors/bp/tiffjpegcompression
Adds support for Tiff's with jpeg compression
2 parents ce97da5 + f25ce22 commit 659daff

File tree

61 files changed

+1102
-307
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+1102
-307
lines changed

src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,14 @@ internal class HuffmanScanDecoder
3838
/// </summary>
3939
private int restartInterval;
4040

41-
// How many mcu's are left to do.
41+
/// <summary>
42+
/// How many mcu's are left to do.
43+
/// </summary>
4244
private int todo;
4345

44-
// The End-Of-Block countdown for ending the sequence prematurely when the remaining coefficients are zero.
46+
/// <summary>
47+
/// The End-Of-Block countdown for ending the sequence prematurely when the remaining coefficients are zero.
48+
/// </summary>
4549
private int eobrun;
4650

4751
/// <summary>
@@ -54,14 +58,16 @@ internal class HuffmanScanDecoder
5458
/// </summary>
5559
private readonly HuffmanTable[] acHuffmanTables;
5660

57-
// The unzig data.
61+
/// <summary>
62+
/// The unzig data.
63+
/// </summary>
5864
private ZigZag dctZigZag;
5965

6066
private HuffmanScanBuffer scanBuffer;
6167

6268
private readonly SpectralConverter spectralConverter;
6369

64-
private CancellationToken cancellationToken;
70+
private readonly CancellationToken cancellationToken;
6571

6672
/// <summary>
6773
/// Initializes a new instance of the <see cref="HuffmanScanDecoder"/> class.

src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Copyright (c) Six Labors.
22
// Licensed under the Apache License, Version 2.0.
33

4+
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters;
5+
46
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
57
{
68
/// <summary>
@@ -30,5 +32,13 @@ internal abstract class SpectralConverter
3032
/// Actual stride height depends on the subsampling factor of the given component.
3133
/// </remarks>
3234
public abstract void ConvertStrideBaseline();
35+
36+
/// <summary>
37+
/// Gets the color converter.
38+
/// </summary>
39+
/// <param name="frame">The jpeg frame with the color space to convert to.</param>
40+
/// <param name="jpegData">The raw JPEG data.</param>
41+
/// <returns>The color converter.</returns>
42+
public virtual JpegColorConverter GetColorConverter(JpegFrame frame, IRawJpegData jpegData) => JpegColorConverter.GetConverter(jpegData.ColorSpace, frame.Precision);
3343
}
3444
}

src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@
1111

1212
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
1313
{
14-
internal sealed class SpectralConverter<TPixel> : SpectralConverter, IDisposable
14+
internal class SpectralConverter<TPixel> : SpectralConverter, IDisposable
1515
where TPixel : unmanaged, IPixel<TPixel>
1616
{
1717
private readonly Configuration configuration;
1818

19-
private CancellationToken cancellationToken;
19+
private readonly CancellationToken cancellationToken;
2020

2121
private JpegComponentPostProcessor[] componentProcessors;
2222

@@ -59,6 +59,7 @@ public Buffer2D<TPixel> PixelBuffer
5959
}
6060
}
6161

62+
/// <inheritdoc/>
6263
public override void InjectFrameData(JpegFrame frame, IRawJpegData jpegData)
6364
{
6465
MemoryAllocator allocator = this.configuration.MemoryAllocator;
@@ -85,9 +86,10 @@ public override void InjectFrameData(JpegFrame frame, IRawJpegData jpegData)
8586
this.rgbaBuffer = allocator.Allocate<Vector4>(frame.PixelWidth);
8687

8788
// color converter from Rgba32 to TPixel
88-
this.colorConverter = JpegColorConverter.GetConverter(jpegData.ColorSpace, frame.Precision);
89+
this.colorConverter = this.GetColorConverter(frame, jpegData);
8990
}
9091

92+
/// <inheritdoc/>
9193
public override void ConvertStrideBaseline()
9294
{
9395
// Convert next pixel stride using single spectral `stride'

src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs

Lines changed: 67 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ internal class HuffmanScanEncoder
4141
private int emitLen = 0;
4242

4343
/// <summary>
44-
/// Emmited bits 'micro buffer' before being transfered to the <see cref="emitBuffer"/>.
44+
/// Emitted bits 'micro buffer' before being transferred to the <see cref="emitBuffer"/>.
4545
/// </summary>
4646
private int accumulatedBits;
4747

@@ -58,18 +58,15 @@ internal class HuffmanScanEncoder
5858
/// </summary>
5959
private readonly Stream target;
6060

61-
public HuffmanScanEncoder(Stream outputStream)
62-
{
63-
this.target = outputStream;
64-
}
61+
public HuffmanScanEncoder(Stream outputStream) => this.target = outputStream;
6562

6663
/// <summary>
6764
/// Encodes the image with no subsampling.
6865
/// </summary>
6966
/// <typeparam name="TPixel">The pixel format.</typeparam>
7067
/// <param name="pixels">The pixel accessor providing access to the image pixels.</param>
71-
/// <param name="luminanceQuantTable">Luminance quantization table provided by the callee</param>
72-
/// <param name="chrominanceQuantTable">Chrominance quantization table provided by the callee</param>
68+
/// <param name="luminanceQuantTable">Luminance quantization table provided by the callee.</param>
69+
/// <param name="chrominanceQuantTable">Chrominance quantization table provided by the callee.</param>
7370
/// <param name="cancellationToken">The token to monitor for cancellation.</param>
7471
public void Encode444<TPixel>(Image<TPixel> pixels, ref Block8x8F luminanceQuantTable, ref Block8x8F chrominanceQuantTable, CancellationToken cancellationToken)
7572
where TPixel : unmanaged, IPixel<TPixel>
@@ -128,8 +125,8 @@ public void Encode444<TPixel>(Image<TPixel> pixels, ref Block8x8F luminanceQuant
128125
/// </summary>
129126
/// <typeparam name="TPixel">The pixel format.</typeparam>
130127
/// <param name="pixels">The pixel accessor providing access to the image pixels.</param>
131-
/// <param name="luminanceQuantTable">Luminance quantization table provided by the callee</param>
132-
/// <param name="chrominanceQuantTable">Chrominance quantization table provided by the callee</param>
128+
/// <param name="luminanceQuantTable">Luminance quantization table provided by the callee.</param>
129+
/// <param name="chrominanceQuantTable">Chrominance quantization table provided by the callee.</param>
133130
/// <param name="cancellationToken">The token to monitor for cancellation.</param>
134131
public void Encode420<TPixel>(Image<TPixel> pixels, ref Block8x8F luminanceQuantTable, ref Block8x8F chrominanceQuantTable, CancellationToken cancellationToken)
135132
where TPixel : unmanaged, IPixel<TPixel>
@@ -196,7 +193,7 @@ public void Encode420<TPixel>(Image<TPixel> pixels, ref Block8x8F luminanceQuant
196193
/// </summary>
197194
/// <typeparam name="TPixel">The pixel format.</typeparam>
198195
/// <param name="pixels">The pixel accessor providing access to the image pixels.</param>
199-
/// <param name="luminanceQuantTable">Luminance quantization table provided by the callee</param>
196+
/// <param name="luminanceQuantTable">Luminance quantization table provided by the callee.</param>
200197
/// <param name="cancellationToken">The token to monitor for cancellation.</param>
201198
public void EncodeGrayscale<TPixel>(Image<TPixel> pixels, ref Block8x8F luminanceQuantTable, CancellationToken cancellationToken)
202199
where TPixel : unmanaged, IPixel<TPixel>
@@ -234,6 +231,64 @@ public void EncodeGrayscale<TPixel>(Image<TPixel> pixels, ref Block8x8F luminanc
234231
this.FlushInternalBuffer();
235232
}
236233

234+
/// <summary>
235+
/// Encodes the image with no subsampling and keeps the pixel data as Rgb24.
236+
/// </summary>
237+
/// <typeparam name="TPixel">The pixel format.</typeparam>
238+
/// <param name="pixels">The pixel accessor providing access to the image pixels.</param>
239+
/// <param name="luminanceQuantTable">Luminance quantization table provided by the callee.</param>
240+
/// <param name="cancellationToken">The token to monitor for cancellation.</param>
241+
public void EncodeRgb<TPixel>(Image<TPixel> pixels, ref Block8x8F luminanceQuantTable, CancellationToken cancellationToken)
242+
where TPixel : unmanaged, IPixel<TPixel>
243+
{
244+
this.huffmanTables = HuffmanLut.TheHuffmanLut;
245+
246+
var unzig = ZigZag.CreateUnzigTable();
247+
248+
// ReSharper disable once InconsistentNaming
249+
int prevDCR = 0, prevDCG = 0, prevDCB = 0;
250+
251+
ImageFrame<TPixel> frame = pixels.Frames.RootFrame;
252+
Buffer2D<TPixel> pixelBuffer = frame.PixelBuffer;
253+
RowOctet<TPixel> currentRows = default;
254+
255+
var pixelConverter = new RgbForwardConverter<TPixel>(frame);
256+
257+
for (int y = 0; y < pixels.Height; y += 8)
258+
{
259+
cancellationToken.ThrowIfCancellationRequested();
260+
currentRows.Update(pixelBuffer, y);
261+
262+
for (int x = 0; x < pixels.Width; x += 8)
263+
{
264+
pixelConverter.Convert(x, y, ref currentRows);
265+
266+
prevDCR = this.WriteBlock(
267+
QuantIndex.Luminance,
268+
prevDCR,
269+
ref pixelConverter.R,
270+
ref luminanceQuantTable,
271+
ref unzig);
272+
273+
prevDCG = this.WriteBlock(
274+
QuantIndex.Luminance,
275+
prevDCG,
276+
ref pixelConverter.G,
277+
ref luminanceQuantTable,
278+
ref unzig);
279+
280+
prevDCB = this.WriteBlock(
281+
QuantIndex.Luminance,
282+
prevDCB,
283+
ref pixelConverter.B,
284+
ref luminanceQuantTable,
285+
ref unzig);
286+
}
287+
}
288+
289+
this.FlushInternalBuffer();
290+
}
291+
237292
/// <summary>
238293
/// Writes a block of pixel data using the given quantization table,
239294
/// returning the post-quantized DC value of the DCT-transformed block.
@@ -437,7 +492,7 @@ internal static int GetHuffmanEncodingLength(uint value)
437492
DebugGuard.IsTrue(value <= (1 << 16), "Huffman encoder is supposed to encode a value of 16bit size max");
438493
#if SUPPORTS_BITOPERATIONS
439494
// This should have been implemented as (BitOperations.Log2(value) + 1) as in non-intrinsic implementation
440-
// But internal log2 is implementated like this: (31 - (int)Lzcnt.LeadingZeroCount(value))
495+
// But internal log2 is implemented like this: (31 - (int)Lzcnt.LeadingZeroCount(value))
441496

442497
// BitOperations.Log2 implementation also checks if input value is zero for the convention 0->0
443498
// Lzcnt would return 32 for input value of 0 - no need to check that with branching
@@ -449,7 +504,7 @@ internal static int GetHuffmanEncodingLength(uint value)
449504
// if 0 - return 0 in this case
450505
// else - return log2(value) + 1
451506
//
452-
// Hack based on input value constaint:
507+
// Hack based on input value constraint:
453508
// We know that input values are guaranteed to be maximum 16 bit large for huffman encoding
454509
// We can safely shift input value for one bit -> log2(value << 1)
455510
// Because of the 16 bit value constraint it won't overflow
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
1-
// Copyright (c) Six Labors.
1+
// Copyright (c) Six Labors.
22
// Licensed under the Apache License, Version 2.0.
33

44
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
55
{
66
/// <summary>
7-
/// Enumerates the quantization tables
7+
/// Enumerates the quantization tables.
88
/// </summary>
99
internal enum QuantIndex
1010
{
1111
/// <summary>
12-
/// The luminance quantization table index
12+
/// The luminance quantization table index.
1313
/// </summary>
1414
Luminance = 0,
1515

1616
/// <summary>
17-
/// The chrominance quantization table index
17+
/// The chrominance quantization table index.
1818
/// </summary>
1919
Chrominance = 1,
2020
}
21-
}
21+
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Apache License, Version 2.0.
3+
4+
using System;
5+
using System.Runtime.CompilerServices;
6+
using System.Runtime.InteropServices;
7+
using SixLabors.ImageSharp.Advanced;
8+
using SixLabors.ImageSharp.PixelFormats;
9+
10+
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
11+
{
12+
/// <summary>
13+
/// On-stack worker struct to convert TPixel -> Rgb24 of 8x8 pixel blocks.
14+
/// </summary>
15+
/// <typeparam name="TPixel">The pixel type to work on.</typeparam>
16+
internal ref struct RgbForwardConverter<TPixel>
17+
where TPixel : unmanaged, IPixel<TPixel>
18+
{
19+
/// <summary>
20+
/// Number of pixels processed per single <see cref="Convert(int, int, ref RowOctet{TPixel})"/> call
21+
/// </summary>
22+
private const int PixelsPerSample = 8 * 8;
23+
24+
/// <summary>
25+
/// Total byte size of processed pixels converted from TPixel to <see cref="Rgb24"/>
26+
/// </summary>
27+
private const int RgbSpanByteSize = PixelsPerSample * 3;
28+
29+
/// <summary>
30+
/// <see cref="Size"/> of sampling area from given frame pixel buffer.
31+
/// </summary>
32+
private static readonly Size SampleSize = new Size(8, 8);
33+
34+
/// <summary>
35+
/// The Red component.
36+
/// </summary>
37+
public Block8x8F R;
38+
39+
/// <summary>
40+
/// The Green component.
41+
/// </summary>
42+
public Block8x8F G;
43+
44+
/// <summary>
45+
/// The Blue component.
46+
/// </summary>
47+
public Block8x8F B;
48+
49+
/// <summary>
50+
/// Temporal 64-byte span to hold unconverted TPixel data.
51+
/// </summary>
52+
private readonly Span<TPixel> pixelSpan;
53+
54+
/// <summary>
55+
/// Temporal 64-byte span to hold converted Rgb24 data.
56+
/// </summary>
57+
private readonly Span<Rgb24> rgbSpan;
58+
59+
/// <summary>
60+
/// Sampled pixel buffer size.
61+
/// </summary>
62+
private readonly Size samplingAreaSize;
63+
64+
/// <summary>
65+
/// <see cref="Configuration"/> for internal operations.
66+
/// </summary>
67+
private readonly Configuration config;
68+
69+
public RgbForwardConverter(ImageFrame<TPixel> frame)
70+
{
71+
this.R = default;
72+
this.G = default;
73+
this.B = default;
74+
75+
// temporal pixel buffers
76+
this.pixelSpan = new TPixel[PixelsPerSample].AsSpan();
77+
this.rgbSpan = MemoryMarshal.Cast<byte, Rgb24>(new byte[RgbSpanByteSize + RgbToYCbCrConverterVectorized.AvxCompatibilityPadding].AsSpan());
78+
79+
// frame data
80+
this.samplingAreaSize = new Size(frame.Width, frame.Height);
81+
this.config = frame.GetConfiguration();
82+
}
83+
84+
/// <summary>
85+
/// Converts a 8x8 image area inside 'pixels' at position (x, y) to Rgb24.
86+
/// </summary>
87+
public void Convert(int x, int y, ref RowOctet<TPixel> currentRows)
88+
{
89+
YCbCrForwardConverter<TPixel>.LoadAndStretchEdges(currentRows, this.pixelSpan, new Point(x, y), SampleSize, this.samplingAreaSize);
90+
91+
PixelOperations<TPixel>.Instance.ToRgb24(this.config, this.pixelSpan, this.rgbSpan);
92+
93+
ref Block8x8F redBlock = ref this.R;
94+
ref Block8x8F greenBlock = ref this.G;
95+
ref Block8x8F blueBlock = ref this.B;
96+
97+
CopyToBlock(this.rgbSpan, ref redBlock, ref greenBlock, ref blueBlock);
98+
}
99+
100+
private static void CopyToBlock(Span<Rgb24> rgbSpan, ref Block8x8F redBlock, ref Block8x8F greenBlock, ref Block8x8F blueBlock)
101+
{
102+
ref Rgb24 rgbStart = ref MemoryMarshal.GetReference(rgbSpan);
103+
104+
for (int i = 0; i < Block8x8F.Size; i++)
105+
{
106+
Rgb24 c = Unsafe.Add(ref rgbStart, i);
107+
108+
redBlock[i] = c.R;
109+
greenBlock[i] = c.G;
110+
blueBlock[i] = c.B;
111+
}
112+
}
113+
}
114+
}

src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter420{TPixel}.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,22 +58,22 @@ internal ref struct YCbCrForwardConverter420<TPixel>
5858
/// <summary>
5959
/// Temporal 16x8 block to hold TPixel data
6060
/// </summary>
61-
private Span<TPixel> pixelSpan;
61+
private readonly Span<TPixel> pixelSpan;
6262

6363
/// <summary>
6464
/// Temporal RGB block
6565
/// </summary>
66-
private Span<Rgb24> rgbSpan;
66+
private readonly Span<Rgb24> rgbSpan;
6767

6868
/// <summary>
6969
/// Sampled pixel buffer size
7070
/// </summary>
71-
private Size samplingAreaSize;
71+
private readonly Size samplingAreaSize;
7272

7373
/// <summary>
7474
/// <see cref="Configuration"/> for internal operations
7575
/// </summary>
76-
private Configuration config;
76+
private readonly Configuration config;
7777

7878
public YCbCrForwardConverter420(ImageFrame<TPixel> frame)
7979
{

0 commit comments

Comments
 (0)