Skip to content

Commit 07a811c

Browse files
Merge pull request #537 from carbon/jpeg-cleanup
Improve JPEG CQ
2 parents 4b7538f + c968e29 commit 07a811c

37 files changed

+286
-368
lines changed

src/ImageSharp/Formats/Jpeg/Common/Decoder/ProfileResolver.cs

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the Apache License, Version 2.0.
33

44
using System;
5+
using System.Text;
56

67
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
78
{
@@ -13,22 +14,22 @@ internal static class ProfileResolver
1314
/// <summary>
1415
/// Describes the EXIF specific markers
1516
/// </summary>
16-
public static readonly byte[] JFifMarker = ToAsciiBytes("JFIF\0");
17+
public static readonly byte[] JFifMarker = Encoding.UTF8.GetBytes("JFIF\0");
1718

1819
/// <summary>
1920
/// Describes the EXIF specific markers
2021
/// </summary>
21-
public static readonly byte[] IccMarker = ToAsciiBytes("ICC_PROFILE\0");
22+
public static readonly byte[] IccMarker = Encoding.UTF8.GetBytes("ICC_PROFILE\0");
2223

2324
/// <summary>
2425
/// Describes the ICC specific markers
2526
/// </summary>
26-
public static readonly byte[] ExifMarker = ToAsciiBytes("Exif\0\0");
27+
public static readonly byte[] ExifMarker = Encoding.UTF8.GetBytes("Exif\0\0");
2728

2829
/// <summary>
2930
/// Describes Adobe specific markers <see href="http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#Adobe"/>
3031
/// </summary>
31-
public static readonly byte[] AdobeMarker = ToAsciiBytes("Adobe");
32+
public static readonly byte[] AdobeMarker = Encoding.UTF8.GetBytes("Adobe");
3233

3334
/// <summary>
3435
/// Returns a value indicating whether the passed bytes are a match to the profile identifer
@@ -41,19 +42,5 @@ public static bool IsProfile(Span<byte> bytesToCheck, Span<byte> profileIdentifi
4142
return bytesToCheck.Length >= profileIdentifier.Length
4243
&& bytesToCheck.Slice(0, profileIdentifier.Length).SequenceEqual(profileIdentifier);
4344
}
44-
45-
// No Encoding.ASCII nor Linq.Select on NetStandard 1.1
46-
private static byte[] ToAsciiBytes(string str)
47-
{
48-
int length = str.Length;
49-
byte[] bytes = new byte[length];
50-
char[] chars = str.ToCharArray();
51-
for (int i = 0; i < length; i++)
52-
{
53-
bytes[i] = (byte)chars[i];
54-
}
55-
56-
return bytes;
57-
}
5845
}
5946
}
Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
11
// Copyright (c) Six Labors and contributors.
22
// Licensed under the Apache License, Version 2.0.
33

4-
using System;
5-
using System.Collections.Generic;
6-
using System.IO;
7-
using SixLabors.ImageSharp.PixelFormats;
8-
94
namespace SixLabors.ImageSharp.Formats.Jpeg
105
{
116
/// <summary>
@@ -18,4 +13,4 @@ internal interface IJpegDecoderOptions
1813
/// </summary>
1914
bool IgnoreMetadata { get; }
2015
}
21-
}
16+
}

src/ImageSharp/Formats/Jpeg/IJpegEncoderOptions.cs

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

4-
using System;
5-
using System.Collections.Generic;
6-
using System.IO;
7-
using SixLabors.ImageSharp.PixelFormats;
8-
94
namespace SixLabors.ImageSharp.Formats.Jpeg
105
{
116
/// <summary>
@@ -31,4 +26,4 @@ internal interface IJpegEncoderOptions
3126
/// <value>The subsample ratio of the jpg image.</value>
3227
JpegSubsample? Subsample { get; }
3328
}
34-
}
29+
}

src/ImageSharp/Formats/Jpeg/ImageExtensions.cs

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

4-
using System;
54
using System.IO;
65
using SixLabors.ImageSharp.Advanced;
7-
using SixLabors.ImageSharp.Formats;
86
using SixLabors.ImageSharp.Formats.Jpeg;
97
using SixLabors.ImageSharp.PixelFormats;
108

src/ImageSharp/Formats/Jpeg/JpegConfigurationModule.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,4 @@ public void Configure(Configuration config)
1717
config.ImageFormatsManager.AddImageFormatDetector(new JpegImageFormatDetector());
1818
}
1919
}
20-
}
20+
}

src/ImageSharp/Formats/Jpeg/JpegImageFormatDetector.cs

Lines changed: 16 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,7 @@ public sealed class JpegImageFormatDetector : IImageFormatDetector
1616
/// <inheritdoc/>
1717
public IImageFormat DetectFormat(ReadOnlySpan<byte> header)
1818
{
19-
if (this.IsSupportedFileFormat(header))
20-
{
21-
return ImageFormats.Jpeg;
22-
}
23-
24-
return null;
19+
return this.IsSupportedFileFormat(header) ? ImageFormats.Jpeg : null;
2520
}
2621

2722
private bool IsSupportedFileFormat(ReadOnlySpan<byte> header)
@@ -35,51 +30,33 @@ private bool IsSupportedFileFormat(ReadOnlySpan<byte> header)
3530
/// </summary>
3631
/// <param name="header">The bytes representing the file header.</param>
3732
/// <returns>The <see cref="bool"/></returns>
38-
private bool IsJfif(ReadOnlySpan<byte> header)
39-
{
40-
// TODO: This should be in constants
41-
bool isJfif =
42-
header[6] == 0x4A && // J
43-
header[7] == 0x46 && // F
44-
header[8] == 0x49 && // I
45-
header[9] == 0x46 && // F
46-
header[10] == 0x00;
47-
48-
return isJfif;
49-
}
33+
private bool IsJfif(ReadOnlySpan<byte> header) =>
34+
header[6] == 0x4A && // J
35+
header[7] == 0x46 && // F
36+
header[8] == 0x49 && // I
37+
header[9] == 0x46 && // F
38+
header[10] == 0x00;
5039

5140
/// <summary>
5241
/// Returns a value indicating whether the given bytes identify EXIF data.
5342
/// </summary>
5443
/// <param name="header">The bytes representing the file header.</param>
5544
/// <returns>The <see cref="bool"/></returns>
56-
private bool IsExif(ReadOnlySpan<byte> header)
57-
{
58-
// TODO: This should be in constants
59-
bool isExif =
60-
header[6] == 0x45 && // E
61-
header[7] == 0x78 && // X
62-
header[8] == 0x69 && // I
63-
header[9] == 0x66 && // F
64-
header[10] == 0x00;
65-
66-
return isExif;
67-
}
45+
private bool IsExif(ReadOnlySpan<byte> header) =>
46+
header[6] == 0x45 && // E
47+
header[7] == 0x78 && // X
48+
header[8] == 0x69 && // I
49+
header[9] == 0x66 && // F
50+
header[10] == 0x00;
6851

6952
/// <summary>
7053
/// Returns a value indicating whether the given bytes identify Jpeg data.
7154
/// This is a last chance resort for jpegs that contain ICC information.
7255
/// </summary>
7356
/// <param name="header">The bytes representing the file header.</param>
7457
/// <returns>The <see cref="bool"/></returns>
75-
private bool IsJpeg(ReadOnlySpan<byte> header)
76-
{
77-
// TODO: This should be in constants
78-
bool isJpg =
79-
header[0] == 0xFF && // 255
80-
header[1] == 0xD8; // 216
81-
82-
return isJpg;
83-
}
58+
private bool IsJpeg(ReadOnlySpan<byte> header) =>
59+
header[0] == 0xFF && // 255
60+
header[1] == 0xD8; // 216
8461
}
8562
}

src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrameComponent.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
1717
internal class PdfJsFrameComponent : IDisposable, IJpegComponent
1818
{
1919
private readonly MemoryManager memoryManager;
20-
#pragma warning disable SA1401 // Fields should be private
2120

2221
public PdfJsFrameComponent(MemoryManager memoryManager, PdfJsFrame frame, byte id, int horizontalFactor, int verticalFactor, byte quantizationTableIndex, int index)
2322
{

src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ internal unsafe struct PdfJsHuffmanTable
4040
/// <param name="memoryManager">The <see cref="MemoryManager"/> to use for buffer allocations.</param>
4141
/// <param name="lengths">The code lengths</param>
4242
/// <param name="values">The huffman values</param>
43-
public PdfJsHuffmanTable(MemoryManager memoryManager, byte[] lengths, byte[] values)
43+
public PdfJsHuffmanTable(MemoryManager memoryManager, ReadOnlySpan<byte> lengths, ReadOnlySpan<byte> values)
4444
{
4545
const int length = 257;
4646
using (IBuffer<short> huffsize = memoryManager.Allocate<short>(length))
@@ -57,10 +57,9 @@ public PdfJsHuffmanTable(MemoryManager memoryManager, byte[] lengths, byte[] val
5757

5858
fixed (byte* huffValRef = this.HuffVal.Data)
5959
{
60-
for (int i = 0; i < values.Length; i++)
61-
{
62-
huffValRef[i] = values[i];
63-
}
60+
var huffValSpan = new Span<byte>(huffValRef, 256);
61+
62+
values.CopyTo(huffValSpan);
6463
}
6564
}
6665

@@ -69,7 +68,7 @@ public PdfJsHuffmanTable(MemoryManager memoryManager, byte[] lengths, byte[] val
6968
/// </summary>
7069
/// <param name="lengths">The code lengths</param>
7170
/// <param name="huffsizeRef">The huffman size span ref</param>
72-
private static void GenerateSizeTable(byte[] lengths, ref short huffsizeRef)
71+
private static void GenerateSizeTable(ReadOnlySpan<byte> lengths, ref short huffsizeRef)
7372
{
7473
short index = 0;
7574
for (short l = 1; l <= 16; l++)
@@ -115,7 +114,7 @@ private static void GenerateCodeTable(ref short huffsizeRef, ref short huffcodeR
115114
/// </summary>
116115
/// <param name="lengths">The code lengths</param>
117116
/// <param name="huffcodeRef">The huffman code span ref</param>
118-
private void GenerateDecoderTables(byte[] lengths, ref short huffcodeRef)
117+
private void GenerateDecoderTables(ReadOnlySpan<byte> lengths, ref short huffcodeRef)
119118
{
120119
fixed (short* valOffsetRef = this.ValOffset.Data)
121120
fixed (long* maxcodeRef = this.MaxCode.Data)
@@ -147,18 +146,17 @@ private void GenerateDecoderTables(byte[] lengths, ref short huffcodeRef)
147146
/// <param name="lengths">The code lengths</param>
148147
/// <param name="huffval">The huffman value array</param>
149148
/// <param name="huffcodeRef">The huffman code span ref</param>
150-
private void GenerateLookaheadTables(byte[] lengths, byte[] huffval, ref short huffcodeRef)
149+
private void GenerateLookaheadTables(ReadOnlySpan<byte> lengths, ReadOnlySpan<byte> huffval, ref short huffcodeRef)
151150
{
152151
// TODO: This generation code matches the libJpeg code but the lookahead table is not actually used yet.
153152
// To use it we need to implement fast lookup path in PdfJsScanDecoder.DecodeHuffman
154153
// This should yield much faster scan decoding as usually, more than 95% of the Huffman codes
155154
// will be 8 or fewer bits long and can be handled without looping.
156155
fixed (short* lookaheadRef = this.Lookahead.Data)
157156
{
158-
for (int i = 0; i < 256; i++)
159-
{
160-
lookaheadRef[i] = 2034; // 9 << 8;
161-
}
157+
var lookaheadSpan = new Span<short>(lookaheadRef, 256);
158+
159+
lookaheadSpan.Fill(2034); // 9 << 8;
162160

163161
int p = 0;
164162
for (int l = 1; l <= 8; l++)

src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTables.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,7 @@ internal sealed class PdfJsHuffmanTables
2121
public ref PdfJsHuffmanTable this[int index]
2222
{
2323
[MethodImpl(MethodImplOptions.AggressiveInlining)]
24-
get
25-
{
26-
return ref this.tables[index];
27-
}
24+
get => ref this.tables[index];
2825
}
2926
}
3027
}

src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
using System.IO;
88
using System.Runtime.CompilerServices;
99
using System.Runtime.InteropServices;
10-
using SixLabors.ImageSharp.Advanced;
1110
using SixLabors.ImageSharp.Formats.Jpeg.Common;
1211
using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder;
1312
using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components;
@@ -367,14 +366,9 @@ private JpegColorSpace DeduceJpegColorSpace()
367366

368367
if (this.ComponentCount == 4)
369368
{
370-
if (this.adobe.ColorTransform == PdfJsJpegConstants.Markers.Adobe.ColorTransformYcck)
371-
{
372-
return JpegColorSpace.Ycck;
373-
}
374-
else
375-
{
376-
return JpegColorSpace.Cmyk;
377-
}
369+
return this.adobe.ColorTransform == PdfJsJpegConstants.Markers.Adobe.ColorTransformYcck
370+
? JpegColorSpace.Ycck
371+
: JpegColorSpace.Cmyk;
378372
}
379373

380374
throw new ImageFormatException($"Unsupported color mode. Max components 4; found {this.ComponentCount}");
@@ -700,8 +694,8 @@ private void ProcessDefineHuffmanTablesMarker(int remaining)
700694
this.BuildHuffmanTable(
701695
huffmanTableSpec >> 4 == 0 ? this.dcHuffmanTables : this.acHuffmanTables,
702696
huffmanTableSpec & 15,
703-
codeLengths.Array,
704-
huffmanValues.Array);
697+
codeLengths.Span,
698+
huffmanValues.Span);
705699
}
706700
}
707701
}
@@ -785,7 +779,7 @@ private void ProcessStartOfScanMarker()
785779
/// <param name="codeLengths">The codelengths</param>
786780
/// <param name="values">The values</param>
787781
[MethodImpl(MethodImplOptions.AggressiveInlining)]
788-
private void BuildHuffmanTable(PdfJsHuffmanTables tables, int index, byte[] codeLengths, byte[] values)
782+
private void BuildHuffmanTable(PdfJsHuffmanTables tables, int index, ReadOnlySpan<byte> codeLengths, ReadOnlySpan<byte> values)
789783
{
790784
tables[index] = new PdfJsHuffmanTable(this.configuration.MemoryManager, codeLengths, values);
791785
}

0 commit comments

Comments
 (0)