Browse Source

Merge branch 'master' into js/densityunits

af/merge-core
James Jackson-South 8 years ago
parent
commit
aadcf92b32
  1. 2
      src/ImageSharp.Drawing/Primitives/ShapePath.cs
  2. 2
      src/ImageSharp.Drawing/Processing/BrushApplicator.cs
  3. 2
      src/ImageSharp.Drawing/Processing/Brushes.cs
  4. 7
      src/ImageSharp.Drawing/Processing/ColorStop{TPixel}.cs
  5. 4
      src/ImageSharp.Drawing/Processing/DrawBezierExtensions.cs
  6. 4
      src/ImageSharp.Drawing/Processing/DrawImageExtensions.cs
  7. 4
      src/ImageSharp.Drawing/Processing/DrawLineExtensions.cs
  8. 4
      src/ImageSharp.Drawing/Processing/DrawPathCollectionExtensions.cs
  9. 4
      src/ImageSharp.Drawing/Processing/DrawPathExtensions.cs
  10. 4
      src/ImageSharp.Drawing/Processing/DrawPolygonExtensions.cs
  11. 4
      src/ImageSharp.Drawing/Processing/DrawRectangleExtensions.cs
  12. 8
      src/ImageSharp.Drawing/Processing/DrawTextExtensions.cs
  13. 7
      src/ImageSharp.Drawing/Processing/EllipticGradientBrush{TPixel}.cs
  14. 3
      src/ImageSharp.Drawing/Processing/FillPathBuilderExtensions.cs
  15. 3
      src/ImageSharp.Drawing/Processing/FillPathCollectionExtensions.cs
  16. 3
      src/ImageSharp.Drawing/Processing/FillPathExtensions.cs
  17. 3
      src/ImageSharp.Drawing/Processing/FillPolygonExtensions.cs
  18. 3
      src/ImageSharp.Drawing/Processing/FillRectangleExtensions.cs
  19. 5
      src/ImageSharp.Drawing/Processing/FillRegionExtensions.cs
  20. 7
      src/ImageSharp.Drawing/Processing/GradientBrushBase{TPixel}.cs
  21. 5
      src/ImageSharp.Drawing/Processing/GradientRepetitionMode.cs
  22. 2
      src/ImageSharp.Drawing/Processing/IBrush.cs
  23. 3
      src/ImageSharp.Drawing/Processing/IPen.cs
  24. 2
      src/ImageSharp.Drawing/Processing/ImageBrush{TPixel}.cs
  25. 7
      src/ImageSharp.Drawing/Processing/LinearGradientBrush{TPixel}.cs
  26. 2
      src/ImageSharp.Drawing/Processing/PatternBrush{TPixel}.cs
  27. 3
      src/ImageSharp.Drawing/Processing/Pens.cs
  28. 11
      src/ImageSharp.Drawing/Processing/Pen{TPixel}.cs
  29. 3
      src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs
  30. 5
      src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs
  31. 4
      src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs
  32. 5
      src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs
  33. 7
      src/ImageSharp.Drawing/Processing/RadialGradientBrush{TPixel}.cs
  34. 4
      src/ImageSharp.Drawing/Processing/RecolorBrush{TPixel}.cs
  35. 2
      src/ImageSharp.Drawing/Processing/SolidBrush{TPixel}.cs
  36. 2
      src/ImageSharp.Drawing/Processing/TextGraphicsOptions.cs
  37. 7
      src/ImageSharp/ColorSpaces/CieLab.cs
  38. 9
      src/ImageSharp/Common/Constants.cs
  39. 46
      src/ImageSharp/Common/Extensions/ListExtensions.cs
  40. 9
      src/ImageSharp/Common/Helpers/DebugGuard.cs
  41. 3
      src/ImageSharp/Common/Helpers/Guard.cs
  42. 22
      src/ImageSharp/Common/Helpers/InliningOptions.cs
  43. 21
      src/ImageSharp/Formats/Gif/GifColorTableMode.cs
  44. 7
      src/ImageSharp/Formats/Gif/GifEncoder.cs
  45. 115
      src/ImageSharp/Formats/Gif/GifEncoderCore.cs
  46. 7
      src/ImageSharp/Formats/Gif/IGifEncoderOptions.cs
  47. 39
      src/ImageSharp/Formats/Gif/LzwEncoder.cs
  48. 26
      src/ImageSharp/Formats/Jpeg/JpegThrowHelper.cs
  49. 96
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FastACTables.cs
  50. 24
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedByteBuffer512.cs
  51. 6
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedInt16Buffer257.cs
  52. 8
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedInt32Buffer18.cs
  53. 8
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedUInt32Buffer18.cs
  54. 10
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrameComponent.cs
  55. 195
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs
  56. 2
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTables.cs
  57. 866
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsScanDecoder.cs
  58. 958
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/ScanDecoder.cs
  59. 57
      src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs
  60. 2
      src/ImageSharp/Formats/Png/IPngEncoderOptions.cs
  61. 2
      src/ImageSharp/Formats/Png/PngEncoder.cs
  62. 31
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  63. 9
      src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs
  64. 2
      src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs
  65. 12
      src/ImageSharp/MetaData/Profiles/ICC/Curves/IccParametricCurve.cs
  66. 10
      src/ImageSharp/MetaData/Profiles/ICC/Curves/IccResponseCurve.cs
  67. 4
      src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.NonPrimitives.cs
  68. 9
      src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Primitives.cs
  69. 16
      src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.NonPrimitives.cs
  70. 17
      src/ImageSharp/MetaData/Profiles/ICC/IccProfile.cs
  71. 34
      src/ImageSharp/MetaData/Profiles/ICC/IccProfileHeader.cs
  72. 28
      src/ImageSharp/MetaData/Profiles/ICC/IccTagDataEntry.cs
  73. 14
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccChromaticityTagDataEntry.cs
  74. 10
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccColorantOrderTagDataEntry.cs
  75. 10
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccColorantTableTagDataEntry.cs
  76. 10
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccCrdInfoTagDataEntry.cs
  77. 10
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccCurveTagDataEntry.cs
  78. 10
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccDataTagDataEntry.cs
  79. 12
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccDateTimeTagDataEntry.cs
  80. 10
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccFix16ArrayTagDataEntry.cs
  81. 12
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut16TagDataEntry.cs
  82. 12
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut8TagDataEntry.cs
  83. 10
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutAToBTagDataEntry.cs
  84. 10
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutBToATagDataEntry.cs
  85. 12
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMeasurementTagDataEntry.cs
  86. 12
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiLocalizedUnicodeTagDataEntry.cs
  87. 10
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiProcessElementsTagDataEntry.cs
  88. 12
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccNamedColor2TagDataEntry.cs
  89. 12
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccParametricCurveTagDataEntry.cs
  90. 12
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccProfileSequenceDescTagDataEntry.cs
  91. 12
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccProfileSequenceIdentifierTagDataEntry.cs
  92. 14
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccResponseCurveSet16TagDataEntry.cs
  93. 12
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccScreeningTagDataEntry.cs
  94. 12
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccSignatureTagDataEntry.cs
  95. 14
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccTextDescriptionTagDataEntry.cs
  96. 12
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccTextTagDataEntry.cs
  97. 12
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUFix16ArrayTagDataEntry.cs
  98. 12
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt16ArrayTagDataEntry.cs
  99. 12
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt32ArrayTagDataEntry.cs
  100. 12
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt64ArrayTagDataEntry.cs

2
src/ImageSharp.Drawing/Primitives/ShapePath.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Processing.Drawing.Pens;
using SixLabors.ImageSharp.Processing;
using SixLabors.Shapes;
namespace SixLabors.ImageSharp.Primitives

2
src/ImageSharp.Drawing/Processing/Drawing/Brushes/BrushApplicator.cs → src/ImageSharp.Drawing/Processing/BrushApplicator.cs

@ -6,7 +6,7 @@ using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
namespace SixLabors.ImageSharp.Processing.Drawing.Brushes
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// primitive that converts a point in to a color for discovering the fill color based on an implementation

2
src/ImageSharp.Drawing/Processing/Drawing/Brushes/Brushes.cs → src/ImageSharp.Drawing/Processing/Brushes.cs

@ -3,7 +3,7 @@
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Drawing.Brushes
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// A collection of methods for creating generic brushes.

7
src/ImageSharp.Drawing/Processing/Drawing/Brushes/GradientBrushes/ColorStop{TPixel}.cs → src/ImageSharp.Drawing/Processing/ColorStop{TPixel}.cs

@ -1,8 +1,11 @@
using System.Diagnostics;
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Diagnostics;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Drawing.Brushes.GradientBrushes
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// A struct that defines a single color stop.

4
src/ImageSharp.Drawing/Processing/Drawing/DrawBezierExtensions.cs → src/ImageSharp.Drawing/Processing/DrawBezierExtensions.cs

@ -2,12 +2,10 @@
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Drawing.Brushes;
using SixLabors.ImageSharp.Processing.Drawing.Pens;
using SixLabors.Primitives;
using SixLabors.Shapes;
namespace SixLabors.ImageSharp.Processing.Drawing
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Adds extensions that allow the drawing of Bezier paths to the <see cref="Image{TPixel}"/> type.

4
src/ImageSharp.Drawing/Processing/Drawing/DrawImageExtensions.cs → src/ImageSharp.Drawing/Processing/DrawImageExtensions.cs

@ -2,10 +2,10 @@
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Drawing.Processors;
using SixLabors.ImageSharp.Processing.Processors.Drawing;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Drawing
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Adds extensions that allow the drawing of images to the <see cref="Image{TPixel}"/> type.

4
src/ImageSharp.Drawing/Processing/Drawing/DrawLineExtensions.cs → src/ImageSharp.Drawing/Processing/DrawLineExtensions.cs

@ -2,12 +2,10 @@
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Drawing.Brushes;
using SixLabors.ImageSharp.Processing.Drawing.Pens;
using SixLabors.Primitives;
using SixLabors.Shapes;
namespace SixLabors.ImageSharp.Processing.Drawing
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Adds extensions that allow the drawing of lines to the <see cref="Image{TPixel}"/> type.

4
src/ImageSharp.Drawing/Processing/Drawing/DrawPathCollectionExtensions.cs → src/ImageSharp.Drawing/Processing/DrawPathCollectionExtensions.cs

@ -2,11 +2,9 @@
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Drawing.Brushes;
using SixLabors.ImageSharp.Processing.Drawing.Pens;
using SixLabors.Shapes;
namespace SixLabors.ImageSharp.Processing.Drawing
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Adds extensions that allow the drawing of collections of polygon outlines to the <see cref="Image{TPixel}"/> type.

4
src/ImageSharp.Drawing/Processing/Drawing/DrawPathExtensions.cs → src/ImageSharp.Drawing/Processing/DrawPathExtensions.cs

@ -3,11 +3,9 @@
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Primitives;
using SixLabors.ImageSharp.Processing.Drawing.Brushes;
using SixLabors.ImageSharp.Processing.Drawing.Pens;
using SixLabors.Shapes;
namespace SixLabors.ImageSharp.Processing.Drawing
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Adds extensions that allow the drawing of polygon outlines to the <see cref="Image{TPixel}"/> type.

4
src/ImageSharp.Drawing/Processing/Drawing/DrawPolygonExtensions.cs → src/ImageSharp.Drawing/Processing/DrawPolygonExtensions.cs

@ -2,12 +2,10 @@
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Drawing.Brushes;
using SixLabors.ImageSharp.Processing.Drawing.Pens;
using SixLabors.Primitives;
using SixLabors.Shapes;
namespace SixLabors.ImageSharp.Processing.Drawing
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Adds extensions that allow the drawing of closed linear polygons to the <see cref="Image{TPixel}"/> type.

4
src/ImageSharp.Drawing/Processing/Drawing/DrawRectangleExtensions.cs → src/ImageSharp.Drawing/Processing/DrawRectangleExtensions.cs

@ -2,12 +2,10 @@
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Drawing.Brushes;
using SixLabors.ImageSharp.Processing.Drawing.Pens;
using SixLabors.Primitives;
using SixLabors.Shapes;
namespace SixLabors.ImageSharp.Processing.Drawing
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Adds extensions that allow the drawing of rectangles to the <see cref="Image{TPixel}"/> type.

8
src/ImageSharp.Drawing/Processing/Text/DrawTextExtensions.cs → src/ImageSharp.Drawing/Processing/DrawTextExtensions.cs

@ -3,17 +3,15 @@
using SixLabors.Fonts;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Drawing.Brushes;
using SixLabors.ImageSharp.Processing.Drawing.Pens;
using SixLabors.ImageSharp.Processing.Text.Processors;
using SixLabors.ImageSharp.Processing.Processors.Text;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Text
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Adds extensions that allow the drawing of text to the <see cref="Image{TPixel}"/> type.
/// </summary>
public static partial class DrawTextExtensions
public static class DrawTextExtensions
{
/// <summary>
/// Draws the text onto the the image filled via the brush.

7
src/ImageSharp.Drawing/Processing/Drawing/Brushes/GradientBrushes/EllipticGradientBrush{TPixel}.cs → src/ImageSharp.Drawing/Processing/EllipticGradientBrush{TPixel}.cs

@ -1,9 +1,12 @@
using System;
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Drawing.Brushes.GradientBrushes
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Gradient Brush with elliptic shape.

3
src/ImageSharp.Drawing/Processing/Drawing/FillPathBuilderExtensions.cs → src/ImageSharp.Drawing/Processing/FillPathBuilderExtensions.cs

@ -3,10 +3,9 @@
using System;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Drawing.Brushes;
using SixLabors.Shapes;
namespace SixLabors.ImageSharp.Processing.Drawing
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Adds extensions that allow the filling of polygons with various brushes to the <see cref="Image{TPixel}"/> type.

3
src/ImageSharp.Drawing/Processing/Drawing/FillPathCollectionExtensions.cs → src/ImageSharp.Drawing/Processing/FillPathCollectionExtensions.cs

@ -2,10 +2,9 @@
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Drawing.Brushes;
using SixLabors.Shapes;
namespace SixLabors.ImageSharp.Processing.Drawing
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Adds extensions that allow the filling of collections of polygon outlines to the <see cref="Image{TPixel}"/> type.

3
src/ImageSharp.Drawing/Processing/Drawing/FillPathExtensions.cs → src/ImageSharp.Drawing/Processing/FillPathExtensions.cs

@ -3,10 +3,9 @@
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Primitives;
using SixLabors.ImageSharp.Processing.Drawing.Brushes;
using SixLabors.Shapes;
namespace SixLabors.ImageSharp.Processing.Drawing
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Adds extensions that allow the filling of polygon outlines to the <see cref="Image{TPixel}"/> type.

3
src/ImageSharp.Drawing/Processing/Drawing/FillPolygonExtensions.cs → src/ImageSharp.Drawing/Processing/FillPolygonExtensions.cs

@ -2,11 +2,10 @@
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Drawing.Brushes;
using SixLabors.Primitives;
using SixLabors.Shapes;
namespace SixLabors.ImageSharp.Processing.Drawing
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Adds extensions that allow the filling of closed linear polygons to the <see cref="Image{TPixel}"/> type.

3
src/ImageSharp.Drawing/Processing/Drawing/FillRectangleExtensions.cs → src/ImageSharp.Drawing/Processing/FillRectangleExtensions.cs

@ -2,11 +2,10 @@
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Drawing.Brushes;
using SixLabors.Primitives;
using SixLabors.Shapes;
namespace SixLabors.ImageSharp.Processing.Drawing
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Adds extensions that allow the filling of rectangles to the <see cref="Image{TPixel}"/> type.

5
src/ImageSharp.Drawing/Processing/Drawing/FillRegionExtensions.cs → src/ImageSharp.Drawing/Processing/FillRegionExtensions.cs

@ -3,10 +3,9 @@
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Primitives;
using SixLabors.ImageSharp.Processing.Drawing.Brushes;
using SixLabors.ImageSharp.Processing.Drawing.Processors;
using SixLabors.ImageSharp.Processing.Processors.Drawing;
namespace SixLabors.ImageSharp.Processing.Drawing
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Adds extensions that allow the filling of regions with various brushes to the <see cref="Image{TPixel}"/> type.

7
src/ImageSharp.Drawing/Processing/Drawing/Brushes/GradientBrushes/GradientBrushBase{TPixel}.cs → src/ImageSharp.Drawing/Processing/GradientBrushBase{TPixel}.cs

@ -1,11 +1,14 @@
using System;
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.PixelFormats.PixelBlenders;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Drawing.Brushes.GradientBrushes
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Base class for Gradient brushes

5
src/ImageSharp.Drawing/Processing/Drawing/Brushes/GradientBrushes/GradientRepetitionMode.cs → src/ImageSharp.Drawing/Processing/GradientRepetitionMode.cs

@ -1,4 +1,7 @@
namespace SixLabors.ImageSharp.Processing.Drawing.Brushes.GradientBrushes
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Modes to repeat a gradient.

2
src/ImageSharp.Drawing/Processing/Drawing/Brushes/IBrush.cs → src/ImageSharp.Drawing/Processing/IBrush.cs

@ -4,7 +4,7 @@
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Drawing.Brushes
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Brush represents a logical configuration of a brush which can be used to source pixel colors

3
src/ImageSharp.Drawing/Processing/Drawing/Pens/IPen.cs → src/ImageSharp.Drawing/Processing/IPen.cs

@ -3,9 +3,8 @@
using System;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Drawing.Brushes;
namespace SixLabors.ImageSharp.Processing.Drawing.Pens
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Interface representing a Pen

2
src/ImageSharp.Drawing/Processing/Drawing/Brushes/ImageBrush{TPixel}.cs → src/ImageSharp.Drawing/Processing/ImageBrush{TPixel}.cs

@ -7,7 +7,7 @@ using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Drawing.Brushes
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Provides an implementation of an image brush for painting images within areas.

7
src/ImageSharp.Drawing/Processing/Drawing/Brushes/GradientBrushes/LinearGradientBrush{TPixel}.cs → src/ImageSharp.Drawing/Processing/LinearGradientBrush{TPixel}.cs

@ -1,9 +1,12 @@
using System;
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Drawing.Brushes.GradientBrushes
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Provides an implementation of a brush for painting linear gradients within areas.

2
src/ImageSharp.Drawing/Processing/Drawing/Brushes/PatternBrush{TPixel}.cs → src/ImageSharp.Drawing/Processing/PatternBrush{TPixel}.cs

@ -9,7 +9,7 @@ using SixLabors.ImageSharp.Primitives;
using SixLabors.Memory;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Drawing.Brushes
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Provides an implementation of a pattern brush for painting patterns.

3
src/ImageSharp.Drawing/Processing/Drawing/Pens/Pens.cs → src/ImageSharp.Drawing/Processing/Pens.cs

@ -2,9 +2,8 @@
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Drawing.Brushes;
namespace SixLabors.ImageSharp.Processing.Drawing.Pens
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Contains a collection of common Pen styles

11
src/ImageSharp.Drawing/Processing/Drawing/Pens/Pen{TPixel}.cs → src/ImageSharp.Drawing/Processing/Pen{TPixel}.cs

@ -3,9 +3,8 @@
using System;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Drawing.Brushes;
namespace SixLabors.ImageSharp.Processing.Drawing.Pens
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Provides a pen that can apply a pattern to a line with a set brush and thickness
@ -25,7 +24,7 @@ namespace SixLabors.ImageSharp.Processing.Drawing.Pens
private readonly float[] pattern;
/// <summary>
/// Initializes a new instance of the <see cref="Drawing.Pens.Pen{TPixel}"/> class.
/// Initializes a new instance of the <see cref="Pen{TPixel}"/> class.
/// </summary>
/// <param name="color">The color.</param>
/// <param name="width">The width.</param>
@ -36,7 +35,7 @@ namespace SixLabors.ImageSharp.Processing.Drawing.Pens
}
/// <summary>
/// Initializes a new instance of the <see cref="Drawing.Pens.Pen{TPixel}"/> class.
/// Initializes a new instance of the <see cref="Pen{TPixel}"/> class.
/// </summary>
/// <param name="brush">The brush.</param>
/// <param name="width">The width.</param>
@ -49,7 +48,7 @@ namespace SixLabors.ImageSharp.Processing.Drawing.Pens
}
/// <summary>
/// Initializes a new instance of the <see cref="Drawing.Pens.Pen{TPixel}"/> class.
/// Initializes a new instance of the <see cref="Pen{TPixel}"/> class.
/// </summary>
/// <param name="color">The color.</param>
/// <param name="width">The width.</param>
@ -59,7 +58,7 @@ namespace SixLabors.ImageSharp.Processing.Drawing.Pens
}
/// <summary>
/// Initializes a new instance of the <see cref="Drawing.Pens.Pen{TPixel}"/> class.
/// Initializes a new instance of the <see cref="Pen{TPixel}"/> class.
/// </summary>
/// <param name="brush">The brush.</param>
/// <param name="width">The width.</param>

3
src/ImageSharp.Drawing/Processing/Drawing/Processors/DrawImageProcessor.cs → src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs

@ -5,11 +5,10 @@ using System;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors;
using SixLabors.Memory;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Drawing.Processors
namespace SixLabors.ImageSharp.Processing.Processors.Drawing
{
/// <summary>
/// Combines two images together by blending the pixels.

5
src/ImageSharp.Drawing/Processing/Drawing/Processors/FillProcessor.cs → src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs

@ -5,12 +5,11 @@ using System;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Drawing.Brushes;
using SixLabors.ImageSharp.Processing.Processors;
using SixLabors.ImageSharp.Processing;
using SixLabors.Memory;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Drawing.Processors
namespace SixLabors.ImageSharp.Processing.Processors.Drawing
{
/// <summary>
/// Using the brush as a source of pixels colors blends the brush color with source.

4
src/ImageSharp.Drawing/Processing/Drawing/Processors/FillRegionProcessor.cs → src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs

@ -4,13 +4,11 @@
using System;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Primitives;
using SixLabors.ImageSharp.Processing.Drawing.Brushes;
using SixLabors.ImageSharp.Processing.Processors;
using SixLabors.ImageSharp.Utils;
using SixLabors.Memory;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Drawing.Processors
namespace SixLabors.ImageSharp.Processing.Processors.Drawing
{
/// <summary>
/// Using a brush and a shape fills shape with contents of brush the

5
src/ImageSharp.Drawing/Processing/Text/Processors/DrawTextProcessor.cs → src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs

@ -6,15 +6,12 @@ using System.Collections.Generic;
using SixLabors.Fonts;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Drawing.Brushes;
using SixLabors.ImageSharp.Processing.Drawing.Pens;
using SixLabors.ImageSharp.Processing.Processors;
using SixLabors.ImageSharp.Utils;
using SixLabors.Memory;
using SixLabors.Primitives;
using SixLabors.Shapes;
namespace SixLabors.ImageSharp.Processing.Text.Processors
namespace SixLabors.ImageSharp.Processing.Processors.Text
{
/// <summary>
/// Using the brush as a source of pixels colors blends the brush color with source.

7
src/ImageSharp.Drawing/Processing/Drawing/Brushes/GradientBrushes/RadialGradientBrush{TPixel}.cs → src/ImageSharp.Drawing/Processing/RadialGradientBrush{TPixel}.cs

@ -1,9 +1,12 @@
using System;
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Drawing.Brushes.GradientBrushes
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// A Circular Gradient Brush, defined by center point and radius.

4
src/ImageSharp.Drawing/Processing/Drawing/Brushes/RecolorBrush{TPixel}.cs → src/ImageSharp.Drawing/Processing/RecolorBrush{TPixel}.cs

@ -8,14 +8,14 @@ using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Drawing.Brushes
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Provides an implementation of a brush that can recolor an image
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
public class RecolorBrush<TPixel> : IBrush<TPixel>
where TPixel : struct, IPixel<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="RecolorBrush{TPixel}" /> class.

2
src/ImageSharp.Drawing/Processing/Drawing/Brushes/SolidBrush{TPixel}.cs → src/ImageSharp.Drawing/Processing/SolidBrush{TPixel}.cs

@ -7,7 +7,7 @@ using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Drawing.Brushes
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Provides an implementation of a solid brush for painting solid color areas.

2
src/ImageSharp.Drawing/Processing/Text/TextGraphicsOptions.cs → src/ImageSharp.Drawing/Processing/TextGraphicsOptions.cs

@ -4,7 +4,7 @@
using SixLabors.Fonts;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Text
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Options for influencing the drawing functions.

7
src/ImageSharp/ColorSpaces/CieLab.cs

@ -194,12 +194,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj)
{
if (obj is CieLab)
{
return this.Equals((CieLab)obj);
}
return false;
return obj is CieLab other && this.Equals(other);
}
/// <inheritdoc/>

9
src/ImageSharp/Common/Constants.cs

@ -9,8 +9,13 @@ namespace SixLabors.ImageSharp
internal static class Constants
{
/// <summary>
/// The epsilon for comparing floating point numbers.
/// The epsilon value for comparing floating point numbers.
/// </summary>
public static readonly float Epsilon = 0.001f;
public static readonly float Epsilon = 0.001F;
/// <summary>
/// The epsilon squared value for comparing floating point numbers.
/// </summary>
public static readonly float EpsilonSquared = Epsilon * Epsilon;
}
}

46
src/ImageSharp/Common/Extensions/ListExtensions.cs

@ -1,46 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Collections.Generic;
namespace SixLabors.ImageSharp.Common.Extensions
{
/// <summary>
/// Encapsulates a series of time saving extension methods to the <see cref="T:System.Collections.Generic.List"/> class.
/// </summary>
internal static class ListExtensions
{
/// <summary>
/// Inserts an item at the given index automatically expanding the capacity if required.
/// </summary>
/// <typeparam name="T">The type of object within the list</typeparam>
/// <param name="list">The list</param>
/// <param name="index">The index</param>
/// <param name="item">The item to insert</param>
public static void SafeInsert<T>(this List<T> list, int index, T item)
{
if (index >= list.Count)
{
list.Add(item);
}
else
{
list[index] = item;
}
}
/// <summary>
/// Removes the last element from a list and returns that element. This method changes the length of the list.
/// </summary>
/// <typeparam name="T">The type of object within the list</typeparam>
/// <param name="list">The list</param>
/// <returns>The last element in the specified sequence.</returns>
public static T Pop<T>(this List<T> list)
{
int last = list.Count - 1;
T item = list[last];
list.RemoveAt(last);
return item;
}
}
}

9
src/ImageSharp/Common/Helpers/DebugGuard.cs

@ -17,13 +17,14 @@ namespace SixLabors.ImageSharp
/// Verifies, that the method parameter with specified object value is not null
/// and throws an exception if it is found to be so.
/// </summary>
/// <param name="target">The target object, which cannot be null.</param>
/// <param name="value">The target object, which cannot be null.</param>
/// <param name="parameterName">The name of the parameter that is to be checked.</param>
/// <exception cref="ArgumentNullException"><paramref name="target"/> is null</exception>
/// <exception cref="ArgumentNullException"><paramref name="value"/> is null</exception>
[Conditional("DEBUG")]
public static void NotNull(object target, string parameterName)
public static void NotNull<T>(T value, string parameterName)
where T : class
{
if (target == null)
if (value == null)
{
throw new ArgumentNullException(parameterName);
}

3
src/ImageSharp/Common/Helpers/Guard.cs

@ -19,7 +19,8 @@ namespace SixLabors.ImageSharp
/// <param name="value">The target object, which cannot be null.</param>
/// <param name="parameterName">The name of the parameter that is to be checked.</param>
/// <exception cref="ArgumentNullException"><paramref name="value"/> is null</exception>
public static void NotNull(object value, string parameterName)
public static void NotNull<T>(T value, string parameterName)
where T : class
{
if (value == null)
{

22
src/ImageSharp/Common/Helpers/InliningOptions.cs

@ -0,0 +1,22 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
// Uncomment this for verbose profiler results:
// #define PROFILING
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp
{
/// <summary>
/// Global inlining options. Helps temporarily disable inling for better profiler output.
/// </summary>
internal static class InliningOptions
{
#if PROFILING
public const MethodImplOptions ShortMethod = 0;
#else
public const MethodImplOptions ShortMethod = MethodImplOptions.AggressiveInlining;
#endif
public const MethodImplOptions ColdPath = MethodImplOptions.NoInlining;
}
}

21
src/ImageSharp/Formats/Gif/GifColorTableMode.cs

@ -0,0 +1,21 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Gif
{
/// <summary>
/// Provides enumeration for the available Gif color table modes.
/// </summary>
public enum GifColorTableMode
{
/// <summary>
/// A single color table is calculated from the first frame and reused for subsequent frames.
/// </summary>
Global,
/// <summary>
/// A unique color table is calculated for each frame.
/// </summary>
Local
}
}

7
src/ImageSharp/Formats/Gif/GifEncoder.cs

@ -5,7 +5,7 @@ using System.IO;
using System.Text;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Quantization;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
namespace SixLabors.ImageSharp.Formats.Gif
{
@ -30,6 +30,11 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
public IQuantizer Quantizer { get; set; } = new OctreeQuantizer();
/// <summary>
/// Gets or sets the color table mode: Global or local.
/// </summary>
public GifColorTableMode ColorTableMode { get; set; }
/// <inheritdoc/>
public void Encode<TPixel>(Image<TPixel> image, Stream stream)
where TPixel : struct, IPixel<TPixel>

115
src/ImageSharp/Formats/Gif/GifEncoderCore.cs

@ -9,7 +9,7 @@ using System.Runtime.InteropServices;
using System.Text;
using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Quantization;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
using SixLabors.Memory;
namespace SixLabors.ImageSharp.Formats.Gif
@ -19,6 +19,9 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
internal sealed class GifEncoderCore
{
/// <summary>
/// Used for allocating memory during procesing operations.
/// </summary>
private readonly MemoryAllocator memoryAllocator;
/// <summary>
@ -27,15 +30,20 @@ namespace SixLabors.ImageSharp.Formats.Gif
private readonly byte[] buffer = new byte[20];
/// <summary>
/// Gets the text encoding used to write comments.
/// The text encoding used to write comments.
/// </summary>
private readonly Encoding textEncoding;
/// <summary>
/// Gets or sets the quantizer used to generate the color palette.
/// The quantizer used to generate the color palette.
/// </summary>
private readonly IQuantizer quantizer;
/// <summary>
/// The color table mode: Global or local.
/// </summary>
private readonly GifColorTableMode colorTableMode;
/// <summary>
/// A flag indicating whether to ingore the metadata when writing the image.
/// </summary>
@ -56,6 +64,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.memoryAllocator = memoryAllocator;
this.textEncoding = options.TextEncoding ?? GifConstants.DefaultEncoding;
this.quantizer = options.Quantizer;
this.colorTableMode = options.ColorTableMode;
this.ignoreMetadata = options.IgnoreMetadata;
}
@ -72,28 +81,80 @@ namespace SixLabors.ImageSharp.Formats.Gif
Guard.NotNull(stream, nameof(stream));
// Quantize the image returning a palette.
QuantizedFrame<TPixel> quantized = this.quantizer.CreateFrameQuantizer<TPixel>().QuantizeFrame(image.Frames.RootFrame);
QuantizedFrame<TPixel> quantized =
this.quantizer.CreateFrameQuantizer<TPixel>().QuantizeFrame(image.Frames.RootFrame);
// Get the number of bits.
this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(quantized.Palette.Length).Clamp(1, 8);
int index = this.GetTransparentIndex(quantized);
// Write the header.
this.WriteHeader(stream);
// Write the LSD. We'll use local color tables for now.
this.WriteLogicalScreenDescriptor(image, stream, index);
// Write the LSD.
int index = this.GetTransparentIndex(quantized);
bool useGlobalTable = this.colorTableMode.Equals(GifColorTableMode.Global);
this.WriteLogicalScreenDescriptor(image, index, useGlobalTable, stream);
if (useGlobalTable)
{
this.WriteColorTable(quantized, stream);
}
// Write the first frame.
// Write the comments.
this.WriteComments(image.MetaData, stream);
// Write additional frames.
// Write application extension to allow additional frames.
if (image.Frames.Count > 1)
{
this.WriteApplicationExtension(stream, image.MetaData.RepeatCount);
}
if (useGlobalTable)
{
this.EncodeGlobal(image, quantized, index, stream);
}
else
{
this.EncodeLocal(image, quantized, stream);
}
// Clean up.
quantized?.Dispose();
quantized = null;
// TODO: Write extension etc
stream.WriteByte(GifConstants.EndIntroducer);
}
private void EncodeGlobal<TPixel>(Image<TPixel> image, QuantizedFrame<TPixel> quantized, int transparencyIndex, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
var palleteQuantizer = new PaletteQuantizer(this.quantizer.Diffuser);
for (int i = 0; i < image.Frames.Count; i++)
{
ImageFrame<TPixel> frame = image.Frames[i];
this.WriteGraphicalControlExtension(frame.MetaData, transparencyIndex, stream);
this.WriteImageDescriptor(frame, false, stream);
if (i == 0)
{
this.WriteImageData(quantized, stream);
}
else
{
using (QuantizedFrame<TPixel> paletteQuantized = palleteQuantizer.CreateFrameQuantizer(() => quantized.Palette).QuantizeFrame(frame))
{
this.WriteImageData(paletteQuantized, stream);
}
}
}
}
private void EncodeLocal<TPixel>(Image<TPixel> image, QuantizedFrame<TPixel> quantized, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
foreach (ImageFrame<TPixel> frame in image.Frames)
{
if (quantized == null)
@ -101,16 +162,14 @@ namespace SixLabors.ImageSharp.Formats.Gif
quantized = this.quantizer.CreateFrameQuantizer<TPixel>().QuantizeFrame(frame);
}
this.WriteGraphicalControlExtension(frame.MetaData, stream, this.GetTransparentIndex(quantized));
this.WriteImageDescriptor(frame, stream);
this.WriteGraphicalControlExtension(frame.MetaData, this.GetTransparentIndex(quantized), stream);
this.WriteImageDescriptor(frame, true, stream);
this.WriteColorTable(quantized, stream);
this.WriteImageData(quantized, stream);
quantized?.Dispose();
quantized = null; // So next frame can regenerate it
}
// TODO: Write extension etc
stream.WriteByte(GifConstants.EndIntroducer);
}
/// <summary>
@ -159,12 +218,13 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The image to encode.</param>
/// <param name="stream">The stream to write to.</param>
/// <param name="transparencyIndex">The transparency index to set the default background index to.</param>
private void WriteLogicalScreenDescriptor<TPixel>(Image<TPixel> image, Stream stream, int transparencyIndex)
/// <param name="useGlobalTable">Whether to use a global or local color table.</param>
/// <param name="stream">The stream to write to.</param>
private void WriteLogicalScreenDescriptor<TPixel>(Image<TPixel> image, int transparencyIndex, bool useGlobalTable, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
byte packedValue = GifLogicalScreenDescriptor.GetPackedValue(false, this.bitDepth - 1, false, this.bitDepth - 1);
byte packedValue = GifLogicalScreenDescriptor.GetPackedValue(useGlobalTable, this.bitDepth - 1, false, this.bitDepth - 1);
var descriptor = new GifLogicalScreenDescriptor(
width: (ushort)image.Width,
@ -243,9 +303,9 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// Writes the graphics control extension to the stream.
/// </summary>
/// <param name="metaData">The metadata of the image or frame.</param>
/// <param name="stream">The stream to write to.</param>
/// <param name="transparencyIndex">The index of the color in the color palette to make transparent.</param>
private void WriteGraphicalControlExtension(ImageFrameMetaData metaData, Stream stream, int transparencyIndex)
/// <param name="stream">The stream to write to.</param>
private void WriteGraphicalControlExtension(ImageFrameMetaData metaData, int transparencyIndex, Stream stream)
{
byte packedValue = GifGraphicControlExtension.GetPackedValue(
disposalMethod: metaData.DisposalMethod,
@ -253,8 +313,8 @@ namespace SixLabors.ImageSharp.Formats.Gif
var extension = new GifGraphicControlExtension(
packed: packedValue,
transparencyIndex: unchecked((byte)transparencyIndex),
delayTime: (ushort)metaData.FrameDelay);
delayTime: (ushort)metaData.FrameDelay,
transparencyIndex: unchecked((byte)transparencyIndex));
this.WriteExtension(extension, stream);
}
@ -281,15 +341,16 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The <see cref="ImageFrame{TPixel}"/> to be encoded.</param>
/// <param name="hasColorTable">Whether to use the global color table.</param>
/// <param name="stream">The stream to write to.</param>
private void WriteImageDescriptor<TPixel>(ImageFrame<TPixel> image, Stream stream)
private void WriteImageDescriptor<TPixel>(ImageFrame<TPixel> image, bool hasColorTable, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
byte packedValue = GifImageDescriptor.GetPackedValue(
localColorTableFlag: true,
localColorTableFlag: hasColorTable,
interfaceFlag: false,
sortFlag: false,
localColorTableSize: (byte)this.bitDepth); // Note: we subtract 1 from the colorTableSize writing
localColorTableSize: (byte)this.bitDepth);
var descriptor = new GifImageDescriptor(
left: 0,
@ -342,9 +403,9 @@ namespace SixLabors.ImageSharp.Formats.Gif
private void WriteImageData<TPixel>(QuantizedFrame<TPixel> image, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
using (var encoder = new LzwEncoder(this.memoryAllocator, image.Pixels, (byte)this.bitDepth))
using (var encoder = new LzwEncoder(this.memoryAllocator, (byte)this.bitDepth))
{
encoder.Encode(stream);
encoder.Encode(image.GetPixelSpan(), stream);
}
}
}

7
src/ImageSharp/Formats/Gif/IGifEncoderOptions.cs

@ -2,7 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System.Text;
using SixLabors.ImageSharp.Processing.Quantization;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
namespace SixLabors.ImageSharp.Formats.Gif
{
@ -25,5 +25,10 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// Gets the quantizer used to generate the color palette.
/// </summary>
IQuantizer Quantizer { get; }
/// <summary>
/// Gets the color table mode: Global or local.
/// </summary>
GifColorTableMode ColorTableMode { get; }
}
}

39
src/ImageSharp/Formats/Gif/LzwEncoder.cs

@ -58,11 +58,6 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
private const int MaxMaxCode = 1 << MaxBits;
/// <summary>
/// The working pixel array.
/// </summary>
private readonly byte[] pixelArray;
/// <summary>
/// The initial code size.
/// </summary>
@ -83,6 +78,11 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
private readonly byte[] accumulators = new byte[256];
/// <summary>
/// For dynamic table sizing
/// </summary>
private readonly int hsize = HashSize;
/// <summary>
/// The current position within the pixelArray.
/// </summary>
@ -98,11 +98,6 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
private int maxCode;
/// <summary>
/// For dynamic table sizing
/// </summary>
private int hsize = HashSize;
/// <summary>
/// First unused entry
/// </summary>
@ -169,13 +164,10 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// Initializes a new instance of the <see cref="LzwEncoder"/> class.
/// </summary>
/// <param name="memoryAllocator">The <see cref="MemoryAllocator"/> to use for buffer allocations.</param>
/// <param name="indexedPixels">The array of indexed pixels.</param>
/// <param name="colorDepth">The color depth in bits.</param>
public LzwEncoder(MemoryAllocator memoryAllocator, byte[] indexedPixels, int colorDepth)
public LzwEncoder(MemoryAllocator memoryAllocator, int colorDepth)
{
this.pixelArray = indexedPixels;
this.initialCodeSize = Math.Max(2, colorDepth);
this.hashTable = memoryAllocator.Allocate<int>(HashSize, true);
this.codeTable = memoryAllocator.Allocate<int>(HashSize, true);
}
@ -183,8 +175,9 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary>
/// Encodes and compresses the indexed pixels to the stream.
/// </summary>
/// <param name="indexedPixels">The span of indexed pixels.</param>
/// <param name="stream">The stream to write to.</param>
public void Encode(Stream stream)
public void Encode(Span<byte> indexedPixels, Stream stream)
{
// Write "initial code size" byte
stream.WriteByte((byte)this.initialCodeSize);
@ -192,7 +185,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.position = 0;
// Compress and write the pixel data
this.Compress(this.initialCodeSize + 1, stream);
this.Compress(indexedPixels, this.initialCodeSize + 1, stream);
// Write block terminator
stream.WriteByte(GifConstants.Terminator);
@ -252,9 +245,10 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary>
/// Compress the packets to the stream.
/// </summary>
/// <param name="indexedPixels">The span of indexed pixels.</param>
/// <param name="intialBits">The initial bits.</param>
/// <param name="stream">The stream to write to.</param>
private void Compress(int intialBits, Stream stream)
private void Compress(Span<byte> indexedPixels, int intialBits, Stream stream)
{
int fcode;
int c;
@ -276,7 +270,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.accumulatorCount = 0; // clear packet
ent = this.NextPixel();
ent = this.NextPixel(indexedPixels);
// TODO: PERF: It looks likt hshift could be calculated once statically.
hshift = 0;
@ -296,9 +290,9 @@ namespace SixLabors.ImageSharp.Formats.Gif
ref int hashTableRef = ref MemoryMarshal.GetReference(this.hashTable.GetSpan());
ref int codeTableRef = ref MemoryMarshal.GetReference(this.codeTable.GetSpan());
while (this.position < this.pixelArray.Length)
while (this.position < indexedPixels.Length)
{
c = this.NextPixel();
c = this.NextPixel(indexedPixels);
fcode = (c << MaxBits) + ent;
int i = (c << hshift) ^ ent /* = 0 */;
@ -373,13 +367,14 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary>
/// Reads the next pixel from the image.
/// </summary>
/// <param name="indexedPixels">The span of indexed pixels.</param>
/// <returns>
/// The <see cref="int"/>
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int NextPixel()
private int NextPixel(Span<byte> indexedPixels)
{
return this.pixelArray[this.position++] & 0xff;
return indexedPixels[this.position++] & 0xFF;
}
/// <summary>

26
src/ImageSharp/Formats/Jpeg/JpegThrowHelper.cs

@ -0,0 +1,26 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Jpeg
{
internal static class JpegThrowHelper
{
/// <summary>
/// Cold path optimization for throwing <see cref="ImageFormatException"/>-s
/// </summary>
/// <param name="errorMessage">The error message for the exception</param>
[MethodImpl(MethodImplOptions.NoInlining)]
public static void ThrowImageFormatException(string errorMessage)
{
throw new ImageFormatException(errorMessage);
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static void ThrowBadHuffmanCode()
{
throw new ImageFormatException("Bad Huffman code.");
}
}
}

96
src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FastACTables.cs

@ -0,0 +1,96 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
using SixLabors.Memory;
namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
{
/// <summary>
/// The collection of lookup tables used for fast AC entropy scan decoding.
/// </summary>
internal sealed class FastACTables : IDisposable
{
private Buffer2D<short> tables;
/// <summary>
/// Initializes a new instance of the <see cref="FastACTables"/> class.
/// </summary>
/// <param name="memoryAllocator">The memory allocator used to allocate memory for image processing operations.</param>
public FastACTables(MemoryAllocator memoryAllocator)
{
this.tables = memoryAllocator.AllocateClean2D<short>(512, 4);
}
/// <summary>
/// Gets the <see cref="Span{Int16}"/> representing the table at the index in the collection.
/// </summary>
/// <param name="index">The table index.</param>
/// <returns><see cref="Span{Int16}"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan<short> GetTableSpan(int index)
{
return this.tables.GetRowSpan(index);
}
/// <summary>
/// Gets a reference to the first element of the AC table indexed by <see cref="PdfJsFrameComponent.ACHuffmanTableId"/>
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref short GetAcTableReference(PdfJsFrameComponent component)
{
return ref this.tables.GetRowSpan(component.ACHuffmanTableId)[0];
}
/// <summary>
/// Builds a lookup table for fast AC entropy scan decoding.
/// </summary>
/// <param name="index">The table index.</param>
/// <param name="acHuffmanTables">The collection of AC Huffman tables.</param>
public void BuildACTableLut(int index, PdfJsHuffmanTables acHuffmanTables)
{
const int FastBits = ScanDecoder.FastBits;
Span<short> fastAC = this.tables.GetRowSpan(index);
ref PdfJsHuffmanTable huffman = ref acHuffmanTables[index];
int i;
for (i = 0; i < (1 << FastBits); i++)
{
byte fast = huffman.Lookahead[i];
fastAC[i] = 0;
if (fast < byte.MaxValue)
{
int rs = huffman.Values[fast];
int run = (rs >> 4) & 15;
int magbits = rs & 15;
int len = huffman.Sizes[fast];
if (magbits > 0 && len + magbits <= FastBits)
{
// Magnitude code followed by receive_extend code
int k = ((i << len) & ((1 << FastBits) - 1)) >> (FastBits - magbits);
int m = 1 << (magbits - 1);
if (k < m)
{
k += (int)((~0U << magbits) + 1);
}
// if the result is small enough, we can fit it in fastAC table
if (k >= -128 && k <= 127)
{
fastAC[i] = (short)((k * 256) + (run * 16) + (len + magbits));
}
}
}
}
}
/// <inheritdoc />
public void Dispose()
{
this.tables?.Dispose();
this.tables = null;
}
}
}

24
src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedByteBuffer512.cs

@ -0,0 +1,24 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
{
[StructLayout(LayoutKind.Sequential)]
internal unsafe struct FixedByteBuffer512
{
public fixed byte Data[1 << ScanDecoder.FastBits];
public byte this[int idx]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
ref byte self = ref Unsafe.As<FixedByteBuffer512, byte>(ref this);
return Unsafe.Add(ref self, idx);
}
}
}
}

6
src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedInt16Buffer256.cs → src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedInt16Buffer257.cs

@ -7,16 +7,16 @@ using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
{
[StructLayout(LayoutKind.Sequential)]
internal unsafe struct FixedInt16Buffer256
internal unsafe struct FixedInt16Buffer257
{
public fixed short Data[256];
public fixed short Data[257];
public short this[int idx]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
ref short self = ref Unsafe.As<FixedInt16Buffer256, short>(ref this);
ref short self = ref Unsafe.As<FixedInt16Buffer257, short>(ref this);
return Unsafe.Add(ref self, idx);
}
}

8
src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedInt64Buffer18.cs → src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedInt32Buffer18.cs

@ -7,16 +7,16 @@ using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
{
[StructLayout(LayoutKind.Sequential)]
internal unsafe struct FixedInt64Buffer18
internal unsafe struct FixedInt32Buffer18
{
public fixed long Data[18];
public fixed int Data[18];
public long this[int idx]
public int this[int idx]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
ref long self = ref Unsafe.As<FixedInt64Buffer18, long>(ref this);
ref int self = ref Unsafe.As<FixedInt32Buffer18, int>(ref this);
return Unsafe.Add(ref self, idx);
}
}

8
src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedInt16Buffer18.cs → src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedUInt32Buffer18.cs

@ -7,16 +7,16 @@ using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
{
[StructLayout(LayoutKind.Sequential)]
internal unsafe struct FixedInt16Buffer18
internal unsafe struct FixedUInt32Buffer18
{
public fixed short Data[18];
public fixed uint Data[18];
public short this[int idx]
public uint this[int idx]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
ref short self = ref Unsafe.As<FixedInt16Buffer18, short>(ref this);
ref uint self = ref Unsafe.As<FixedUInt32Buffer18, uint>(ref this);
return Unsafe.Add(ref self, idx);
}
}

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

@ -129,7 +129,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
this.SubSamplingDivisors = c0.SamplingFactors.DivideBy(this.SamplingFactors);
}
this.SpectralBlocks = this.memoryAllocator.Allocate2D<Block8x8>(blocksPerColumnForMcu, blocksPerLineForMcu + 1, true);
this.SpectralBlocks = this.memoryAllocator.AllocateClean2D<Block8x8>(blocksPerColumnForMcu, blocksPerLineForMcu + 1);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -144,5 +144,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
{
return 64 * (((this.WidthInBlocks + 1) * row) + col);
}
// TODO: we need consistence in (row, col) VS (col, row) ordering
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref short GetBlockDataReference(int row, int col)
{
ref Block8x8 blockRef = ref this.GetBlockReference(col, row);
return ref Unsafe.As<Block8x8, short>(ref blockRef);
}
}
}

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

@ -17,163 +17,114 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
/// <summary>
/// Gets the max code array
/// </summary>
public FixedInt64Buffer18 MaxCode;
public FixedUInt32Buffer18 MaxCode;
/// <summary>
/// Gets the value offset array
/// </summary>
public FixedInt16Buffer18 ValOffset;
public FixedInt32Buffer18 ValOffset;
/// <summary>
/// Gets the huffman value array
/// </summary>
public FixedByteBuffer256 HuffVal;
public FixedByteBuffer256 Values;
/// <summary>
/// Gets the lookahead array
/// </summary>
public FixedInt16Buffer256 Lookahead;
public FixedByteBuffer512 Lookahead;
/// <summary>
/// Gets the sizes array
/// </summary>
public FixedInt16Buffer257 Sizes;
/// <summary>
/// Initializes a new instance of the <see cref="PdfJsHuffmanTable"/> struct.
/// </summary>
/// <param name="memoryAllocator">The <see cref="MemoryAllocator"/> to use for buffer allocations.</param>
/// <param name="lengths">The code lengths</param>
/// <param name="count">The code lengths</param>
/// <param name="values">The huffman values</param>
public PdfJsHuffmanTable(MemoryAllocator memoryAllocator, ReadOnlySpan<byte> lengths, ReadOnlySpan<byte> values)
public PdfJsHuffmanTable(MemoryAllocator memoryAllocator, ReadOnlySpan<byte> count, ReadOnlySpan<byte> values)
{
const int length = 257;
using (IBuffer<short> huffsize = memoryAllocator.Allocate<short>(length))
using (IBuffer<short> huffcode = memoryAllocator.Allocate<short>(length))
const int Length = 257;
using (IBuffer<short> huffcode = memoryAllocator.Allocate<short>(Length))
{
ref short huffsizeRef = ref MemoryMarshal.GetReference(huffsize.GetSpan());
ref short huffcodeRef = ref MemoryMarshal.GetReference(huffcode.GetSpan());
GenerateSizeTable(lengths, ref huffsizeRef);
GenerateCodeTable(ref huffsizeRef, ref huffcodeRef, length);
this.GenerateDecoderTables(lengths, ref huffcodeRef);
this.GenerateLookaheadTables(lengths, values, ref huffcodeRef);
}
fixed (byte* huffValRef = this.HuffVal.Data)
{
var huffValSpan = new Span<byte>(huffValRef, 256);
values.CopyTo(huffValSpan);
}
}
/// <summary>
/// Figure C.1: make table of Huffman code length for each symbol
/// </summary>
/// <param name="lengths">The code lengths</param>
/// <param name="huffsizeRef">The huffman size span ref</param>
private static void GenerateSizeTable(ReadOnlySpan<byte> lengths, ref short huffsizeRef)
{
short index = 0;
for (short l = 1; l <= 16; l++)
{
byte i = lengths[l];
for (short j = 0; j < i; j++)
{
Unsafe.Add(ref huffsizeRef, index) = l;
index++;
}
}
Unsafe.Add(ref huffsizeRef, index) = 0;
}
/// <summary>
/// Figure C.2: generate the codes themselves
/// </summary>
/// <param name="huffsizeRef">The huffman size span ref</param>
/// <param name="huffcodeRef">The huffman code span ref</param>
/// <param name="length">The length of the huffsize span</param>
private static void GenerateCodeTable(ref short huffsizeRef, ref short huffcodeRef, int length)
{
short k = 0;
short si = huffsizeRef;
short code = 0;
for (short i = 0; i < length; i++)
{
while (Unsafe.Add(ref huffsizeRef, k) == si)
{
Unsafe.Add(ref huffcodeRef, k) = code;
code++;
k++;
}
code <<= 1;
si++;
}
}
/// <summary>
/// Figure F.15: generate decoding tables for bit-sequential decoding
/// </summary>
/// <param name="lengths">The code lengths</param>
/// <param name="huffcodeRef">The huffman code span ref</param>
private void GenerateDecoderTables(ReadOnlySpan<byte> lengths, ref short huffcodeRef)
{
fixed (short* valOffsetRef = this.ValOffset.Data)
fixed (long* maxcodeRef = this.MaxCode.Data)
{
short bitcount = 0;
for (int i = 1; i <= 16; i++)
// Figure C.1: make table of Huffman code length for each symbol
fixed (short* sizesRef = this.Sizes.Data)
{
if (lengths[i] != 0)
short x = 0;
for (short i = 1; i < 17; i++)
{
// valOffsetRef[l] = huffcodeRef[] index of 1st symbol of code length i, minus the minimum code of length i
valOffsetRef[i] = (short)(bitcount - Unsafe.Add(ref huffcodeRef, bitcount));
bitcount += lengths[i];
maxcodeRef[i] = Unsafe.Add(ref huffcodeRef, bitcount - 1); // maximum code of length i
}
else
{
maxcodeRef[i] = -1; // -1 if no codes of this length
byte l = count[i];
for (short j = 0; j < l; j++)
{
sizesRef[x] = i;
x++;
}
}
}
valOffsetRef[17] = 0;
maxcodeRef[17] = 0xFFFFFL;
}
}
sizesRef[x] = 0;
/// <summary>
/// Generates lookup tables to speed up decoding
/// </summary>
/// <param name="lengths">The code lengths</param>
/// <param name="huffval">The huffman value array</param>
/// <param name="huffcodeRef">The huffman code span ref</param>
private void GenerateLookaheadTables(ReadOnlySpan<byte> lengths, ReadOnlySpan<byte> huffval, ref short huffcodeRef)
{
// TODO: This generation code matches the libJpeg code but the lookahead table is not actually used yet.
// To use it we need to implement fast lookup path in PdfJsScanDecoder.DecodeHuffman
// This should yield much faster scan decoding as usually, more than 95% of the Huffman codes
// will be 8 or fewer bits long and can be handled without looping.
fixed (short* lookaheadRef = this.Lookahead.Data)
{
var lookaheadSpan = new Span<short>(lookaheadRef, 256);
// Figure C.2: generate the codes themselves
int k = 0;
fixed (int* valOffsetRef = this.ValOffset.Data)
fixed (uint* maxcodeRef = this.MaxCode.Data)
{
uint code = 0;
int j;
for (j = 1; j < 17; j++)
{
// Compute delta to add to code to compute symbol id.
valOffsetRef[j] = (int)(k - code);
if (sizesRef[k] == j)
{
while (sizesRef[k] == j)
{
Unsafe.Add(ref huffcodeRef, k++) = (short)code++;
}
}
// Figure F.15: generate decoding tables for bit-sequential decoding.
// Compute largest code + 1 for this size. preshifted as need later.
maxcodeRef[j] = code << (16 - j);
code <<= 1;
}
lookaheadSpan.Fill(2034); // 9 << 8;
maxcodeRef[j] = 0xFFFFFFFF;
}
int p = 0;
for (int l = 1; l <= 8; l++)
{
for (int i = 1; i <= lengths[l]; i++, p++)
// Generate non-spec lookup tables to speed up decoding.
fixed (byte* lookaheadRef = this.Lookahead.Data)
{
// l = current code's length, p = its index in huffcode[] & huffval[].
// Generate left-justified code followed by all possible bit sequences
int lookBits = Unsafe.Add(ref huffcodeRef, p) << (8 - l);
for (int ctr = 1 << (8 - l); ctr > 0; ctr--)
const int FastBits = ScanDecoder.FastBits;
var fast = new Span<byte>(lookaheadRef, 1 << FastBits);
fast.Fill(0xFF); // Flag for non-accelerated
for (int i = 0; i < k; i++)
{
lookaheadRef[lookBits] = (short)((l << 8) | huffval[p]);
lookBits++;
int s = sizesRef[i];
if (s <= ScanDecoder.FastBits)
{
int c = Unsafe.Add(ref huffcodeRef, i) << (FastBits - s);
int m = 1 << (FastBits - s);
for (int j = 0; j < m; j++)
{
fast[c + j] = (byte)i;
}
}
}
}
}
}
fixed (byte* huffValRef = this.Values.Data)
{
var huffValSpan = new Span<byte>(huffValRef, 256);
values.CopyTo(huffValSpan);
}
}
}
}

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

@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
/// Gets or sets the table at the given index.
/// </summary>
/// <param name="index">The index</param>
/// <returns>The <see cref="List{HuffmanBranch}"/></returns>
/// <returns>The <see cref="PdfJsHuffmanTable"/></returns>
public ref PdfJsHuffmanTable this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]

866
src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsScanDecoder.cs

@ -1,866 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
#if DEBUG
using System.Diagnostics;
#endif
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.Memory;
namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
{
/// <summary>
/// Provides the means to decode a spectral scan
/// </summary>
internal struct PdfJsScanDecoder
{
private ZigZag dctZigZag;
private byte[] markerBuffer;
private int mcuToRead;
private int mcusPerLine;
private int mcu;
private int bitsData;
private int bitsCount;
private int specStart;
private int specEnd;
private int eobrun;
private int compIndex;
private int successiveState;
private int successiveACState;
private int successiveACNextValue;
private bool endOfStreamReached;
private bool unexpectedMarkerReached;
/// <summary>
/// Decodes the spectral scan
/// </summary>
/// <param name="frame">The image frame</param>
/// <param name="stream">The input stream</param>
/// <param name="dcHuffmanTables">The DC Huffman tables</param>
/// <param name="acHuffmanTables">The AC Huffman tables</param>
/// <param name="components">The scan components</param>
/// <param name="componentIndex">The component index within the array</param>
/// <param name="componentsLength">The length of the components. Different to the array length</param>
/// <param name="resetInterval">The reset interval</param>
/// <param name="spectralStart">The spectral selection start</param>
/// <param name="spectralEnd">The spectral selection end</param>
/// <param name="successivePrev">The successive approximation bit high end</param>
/// <param name="successive">The successive approximation bit low end</param>
public void DecodeScan(
PdfJsFrame frame,
DoubleBufferedStreamReader stream,
PdfJsHuffmanTables dcHuffmanTables,
PdfJsHuffmanTables acHuffmanTables,
PdfJsFrameComponent[] components,
int componentIndex,
int componentsLength,
ushort resetInterval,
int spectralStart,
int spectralEnd,
int successivePrev,
int successive)
{
this.dctZigZag = ZigZag.CreateUnzigTable();
this.markerBuffer = new byte[2];
this.compIndex = componentIndex;
this.specStart = spectralStart;
this.specEnd = spectralEnd;
this.successiveState = successive;
this.endOfStreamReached = false;
this.unexpectedMarkerReached = false;
bool progressive = frame.Progressive;
this.mcusPerLine = frame.McusPerLine;
this.mcu = 0;
int mcuExpected;
if (componentsLength == 1)
{
mcuExpected = components[this.compIndex].WidthInBlocks * components[this.compIndex].HeightInBlocks;
}
else
{
mcuExpected = this.mcusPerLine * frame.McusPerColumn;
}
while (this.mcu < mcuExpected)
{
// Reset interval stuff
this.mcuToRead = resetInterval != 0 ? Math.Min(mcuExpected - this.mcu, resetInterval) : mcuExpected;
for (int i = 0; i < components.Length; i++)
{
PdfJsFrameComponent c = components[i];
c.DcPredictor = 0;
}
this.eobrun = 0;
if (!progressive)
{
this.DecodeScanBaseline(dcHuffmanTables, acHuffmanTables, components, componentsLength, stream);
}
else
{
bool isAc = this.specStart != 0;
bool isFirst = successivePrev == 0;
PdfJsHuffmanTables huffmanTables = isAc ? acHuffmanTables : dcHuffmanTables;
this.DecodeScanProgressive(huffmanTables, isAc, isFirst, components, componentsLength, stream);
}
// Reset
// TODO: I do not understand why these values are reset? We should surely be tracking the bits across mcu's?
this.bitsCount = 0;
this.bitsData = 0;
this.unexpectedMarkerReached = false;
// Some images include more scan blocks than expected, skip past those and
// attempt to find the next valid marker
PdfJsFileMarker fileMarker = PdfJsJpegDecoderCore.FindNextFileMarker(this.markerBuffer, stream);
byte marker = fileMarker.Marker;
// RSTn - We've already read the bytes and altered the position so no need to skip
if (marker >= JpegConstants.Markers.RST0 && marker <= JpegConstants.Markers.RST7)
{
continue;
}
if (!fileMarker.Invalid)
{
// We've found a valid marker.
// Rewind the stream to the position of the marker and break
stream.Position = fileMarker.Position;
break;
}
#if DEBUG
Debug.WriteLine($"DecodeScan - Unexpected MCU data at {stream.Position}, next marker is: {fileMarker.Marker:X}");
#endif
}
}
private void DecodeScanBaseline(
PdfJsHuffmanTables dcHuffmanTables,
PdfJsHuffmanTables acHuffmanTables,
PdfJsFrameComponent[] components,
int componentsLength,
DoubleBufferedStreamReader stream)
{
if (componentsLength == 1)
{
PdfJsFrameComponent component = components[this.compIndex];
ref short blockDataRef = ref MemoryMarshal.GetReference(MemoryMarshal.Cast<Block8x8, short>(component.SpectralBlocks.GetSpan()));
ref PdfJsHuffmanTable dcHuffmanTable = ref dcHuffmanTables[component.DCHuffmanTableId];
ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId];
for (int n = 0; n < this.mcuToRead; n++)
{
if (this.endOfStreamReached || this.unexpectedMarkerReached)
{
continue;
}
this.DecodeBlockBaseline(ref dcHuffmanTable, ref acHuffmanTable, component, ref blockDataRef, stream);
this.mcu++;
}
}
else
{
for (int n = 0; n < this.mcuToRead; n++)
{
for (int i = 0; i < componentsLength; i++)
{
PdfJsFrameComponent component = components[i];
ref short blockDataRef = ref MemoryMarshal.GetReference(MemoryMarshal.Cast<Block8x8, short>(component.SpectralBlocks.GetSpan()));
ref PdfJsHuffmanTable dcHuffmanTable = ref dcHuffmanTables[component.DCHuffmanTableId];
ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId];
int h = component.HorizontalSamplingFactor;
int v = component.VerticalSamplingFactor;
for (int j = 0; j < v; j++)
{
for (int k = 0; k < h; k++)
{
if (this.endOfStreamReached || this.unexpectedMarkerReached)
{
continue;
}
this.DecodeMcuBaseline(ref dcHuffmanTable, ref acHuffmanTable, component, ref blockDataRef, j, k, stream);
}
}
}
this.mcu++;
}
}
}
private void DecodeScanProgressive(
PdfJsHuffmanTables huffmanTables,
bool isAC,
bool isFirst,
PdfJsFrameComponent[] components,
int componentsLength,
DoubleBufferedStreamReader stream)
{
if (componentsLength == 1)
{
PdfJsFrameComponent component = components[this.compIndex];
ref short blockDataRef = ref MemoryMarshal.GetReference(MemoryMarshal.Cast<Block8x8, short>(component.SpectralBlocks.GetSpan()));
ref PdfJsHuffmanTable huffmanTable = ref huffmanTables[isAC ? component.ACHuffmanTableId : component.DCHuffmanTableId];
for (int n = 0; n < this.mcuToRead; n++)
{
if (this.endOfStreamReached || this.unexpectedMarkerReached)
{
continue;
}
if (isAC)
{
if (isFirst)
{
this.DecodeBlockACFirst(ref huffmanTable, component, ref blockDataRef, stream);
}
else
{
this.DecodeBlockACSuccessive(ref huffmanTable, component, ref blockDataRef, stream);
}
}
else
{
if (isFirst)
{
this.DecodeBlockDCFirst(ref huffmanTable, component, ref blockDataRef, stream);
}
else
{
this.DecodeBlockDCSuccessive(component, ref blockDataRef, stream);
}
}
this.mcu++;
}
}
else
{
for (int n = 0; n < this.mcuToRead; n++)
{
for (int i = 0; i < componentsLength; i++)
{
PdfJsFrameComponent component = components[i];
ref short blockDataRef = ref MemoryMarshal.GetReference(MemoryMarshal.Cast<Block8x8, short>(component.SpectralBlocks.GetSpan()));
ref PdfJsHuffmanTable huffmanTable = ref huffmanTables[isAC ? component.ACHuffmanTableId : component.DCHuffmanTableId];
int h = component.HorizontalSamplingFactor;
int v = component.VerticalSamplingFactor;
for (int j = 0; j < v; j++)
{
for (int k = 0; k < h; k++)
{
// No need to continue here.
if (this.endOfStreamReached || this.unexpectedMarkerReached)
{
break;
}
if (isAC)
{
if (isFirst)
{
this.DecodeMcuACFirst(ref huffmanTable, component, ref blockDataRef, j, k, stream);
}
else
{
this.DecodeMcuACSuccessive(ref huffmanTable, component, ref blockDataRef, j, k, stream);
}
}
else
{
if (isFirst)
{
this.DecodeMcuDCFirst(ref huffmanTable, component, ref blockDataRef, j, k, stream);
}
else
{
this.DecodeMcuDCSuccessive(component, ref blockDataRef, j, k, stream);
}
}
}
}
}
this.mcu++;
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeBlockBaseline(ref PdfJsHuffmanTable dcHuffmanTable, ref PdfJsHuffmanTable acHuffmanTable, PdfJsFrameComponent component, ref short blockDataRef, DoubleBufferedStreamReader stream)
{
int blockRow = this.mcu / component.WidthInBlocks;
int blockCol = this.mcu % component.WidthInBlocks;
int offset = component.GetBlockBufferOffset(blockRow, blockCol);
this.DecodeBaseline(component, ref blockDataRef, offset, ref dcHuffmanTable, ref acHuffmanTable, stream);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeMcuBaseline(ref PdfJsHuffmanTable dcHuffmanTable, ref PdfJsHuffmanTable acHuffmanTable, PdfJsFrameComponent component, ref short blockDataRef, int row, int col, DoubleBufferedStreamReader stream)
{
int mcuRow = this.mcu / this.mcusPerLine;
int mcuCol = this.mcu % this.mcusPerLine;
int blockRow = (mcuRow * component.VerticalSamplingFactor) + row;
int blockCol = (mcuCol * component.HorizontalSamplingFactor) + col;
int offset = component.GetBlockBufferOffset(blockRow, blockCol);
this.DecodeBaseline(component, ref blockDataRef, offset, ref dcHuffmanTable, ref acHuffmanTable, stream);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeBlockDCFirst(ref PdfJsHuffmanTable dcHuffmanTable, PdfJsFrameComponent component, ref short blockDataRef, DoubleBufferedStreamReader stream)
{
int blockRow = this.mcu / component.WidthInBlocks;
int blockCol = this.mcu % component.WidthInBlocks;
int offset = component.GetBlockBufferOffset(blockRow, blockCol);
this.DecodeDCFirst(component, ref blockDataRef, offset, ref dcHuffmanTable, stream);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeMcuDCFirst(ref PdfJsHuffmanTable dcHuffmanTable, PdfJsFrameComponent component, ref short blockDataRef, int row, int col, DoubleBufferedStreamReader stream)
{
int mcuRow = this.mcu / this.mcusPerLine;
int mcuCol = this.mcu % this.mcusPerLine;
int blockRow = (mcuRow * component.VerticalSamplingFactor) + row;
int blockCol = (mcuCol * component.HorizontalSamplingFactor) + col;
int offset = component.GetBlockBufferOffset(blockRow, blockCol);
this.DecodeDCFirst(component, ref blockDataRef, offset, ref dcHuffmanTable, stream);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeBlockDCSuccessive(PdfJsFrameComponent component, ref short blockDataRef, DoubleBufferedStreamReader stream)
{
int blockRow = this.mcu / component.WidthInBlocks;
int blockCol = this.mcu % component.WidthInBlocks;
int offset = component.GetBlockBufferOffset(blockRow, blockCol);
this.DecodeDCSuccessive(component, ref blockDataRef, offset, stream);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeMcuDCSuccessive(PdfJsFrameComponent component, ref short blockDataRef, int row, int col, DoubleBufferedStreamReader stream)
{
int mcuRow = this.mcu / this.mcusPerLine;
int mcuCol = this.mcu % this.mcusPerLine;
int blockRow = (mcuRow * component.VerticalSamplingFactor) + row;
int blockCol = (mcuCol * component.HorizontalSamplingFactor) + col;
int offset = component.GetBlockBufferOffset(blockRow, blockCol);
this.DecodeDCSuccessive(component, ref blockDataRef, offset, stream);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeBlockACFirst(ref PdfJsHuffmanTable acHuffmanTable, PdfJsFrameComponent component, ref short blockDataRef, DoubleBufferedStreamReader stream)
{
int blockRow = this.mcu / component.WidthInBlocks;
int blockCol = this.mcu % component.WidthInBlocks;
int offset = component.GetBlockBufferOffset(blockRow, blockCol);
this.DecodeACFirst(ref blockDataRef, offset, ref acHuffmanTable, stream);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeMcuACFirst(ref PdfJsHuffmanTable acHuffmanTable, PdfJsFrameComponent component, ref short blockDataRef, int row, int col, DoubleBufferedStreamReader stream)
{
int mcuRow = this.mcu / this.mcusPerLine;
int mcuCol = this.mcu % this.mcusPerLine;
int blockRow = (mcuRow * component.VerticalSamplingFactor) + row;
int blockCol = (mcuCol * component.HorizontalSamplingFactor) + col;
int offset = component.GetBlockBufferOffset(blockRow, blockCol);
this.DecodeACFirst(ref blockDataRef, offset, ref acHuffmanTable, stream);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeBlockACSuccessive(ref PdfJsHuffmanTable acHuffmanTable, PdfJsFrameComponent component, ref short blockDataRef, DoubleBufferedStreamReader stream)
{
int blockRow = this.mcu / component.WidthInBlocks;
int blockCol = this.mcu % component.WidthInBlocks;
int offset = component.GetBlockBufferOffset(blockRow, blockCol);
this.DecodeACSuccessive(ref blockDataRef, offset, ref acHuffmanTable, stream);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeMcuACSuccessive(ref PdfJsHuffmanTable acHuffmanTable, PdfJsFrameComponent component, ref short blockDataRef, int row, int col, DoubleBufferedStreamReader stream)
{
int mcuRow = this.mcu / this.mcusPerLine;
int mcuCol = this.mcu % this.mcusPerLine;
int blockRow = (mcuRow * component.VerticalSamplingFactor) + row;
int blockCol = (mcuCol * component.HorizontalSamplingFactor) + col;
int offset = component.GetBlockBufferOffset(blockRow, blockCol);
this.DecodeACSuccessive(ref blockDataRef, offset, ref acHuffmanTable, stream);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool TryReadBit(DoubleBufferedStreamReader stream, out int bit)
{
if (this.bitsCount == 0)
{
if (!this.TryFillBits(stream))
{
bit = 0;
return false;
}
}
this.bitsCount--;
bit = (this.bitsData >> this.bitsCount) & 1;
return true;
}
[MethodImpl(MethodImplOptions.NoInlining)]
private bool TryFillBits(DoubleBufferedStreamReader stream)
{
// TODO: Read more then 1 byte at a time.
// In LibJpegTurbo this is be 25 bits (32-7) but I cannot get this to work
// for some images, I'm assuming because I am crossing MCU boundaries and not maintining the correct buffer state.
const int MinGetBits = 7;
if (!this.unexpectedMarkerReached)
{
// Attempt to load to the minimum bit count.
while (this.bitsCount < MinGetBits)
{
int c = stream.ReadByte();
switch (c)
{
case -0x1:
// We've encountered the end of the file stream which means there's no EOI marker in the image.
this.endOfStreamReached = true;
return false;
case JpegConstants.Markers.XFF:
int nextByte = stream.ReadByte();
if (nextByte == -0x1)
{
this.endOfStreamReached = true;
return false;
}
if (nextByte != 0)
{
#if DEBUG
Debug.WriteLine($"DecodeScan - Unexpected marker {(c << 8) | nextByte:X} at {stream.Position}");
#endif
// We've encountered an unexpected marker. Reverse the stream and exit.
this.unexpectedMarkerReached = true;
stream.Position -= 2;
// TODO: double check we need this.
// Fill buffer with zero bits.
if (this.bitsCount == 0)
{
this.bitsData <<= MinGetBits;
this.bitsCount = MinGetBits;
}
return true;
}
break;
}
// OK, load the next byte into bitsData
this.bitsData = (this.bitsData << 8) | c;
this.bitsCount += 8;
}
}
return true;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int PeekBits(int count)
{
return this.bitsData >> (this.bitsCount - count) & ((1 << count) - 1);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DropBits(int count)
{
this.bitsCount -= count;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool TryDecodeHuffman(ref PdfJsHuffmanTable tree, DoubleBufferedStreamReader stream, out short value)
{
value = -1;
// TODO: Implement fast Huffman decoding.
// In LibJpegTurbo a minimum of 25 bits (32-7) is collected from the stream
// Then a LUT is used to avoid the loop when decoding the Huffman value.
// using 3 methods: FillBits, PeekBits, and DropBits.
// The LUT has been ported from LibJpegTurbo as has this code but it doesn't work.
// this.TryFillBits(stream);
//
// const int LookAhead = 8;
// int look = this.PeekBits(LookAhead);
// look = tree.Lookahead[look];
// int bits = look >> LookAhead;
//
// if (bits <= LookAhead)
// {
// this.DropBits(bits);
// value = (short)(look & ((1 << LookAhead) - 1));
// return true;
// }
if (!this.TryReadBit(stream, out int bit))
{
return false;
}
short code = (short)bit;
// "DECODE", section F.2.2.3, figure F.16, page 109 of T.81
int i = 1;
while (code > tree.MaxCode[i])
{
if (!this.TryReadBit(stream, out bit))
{
return false;
}
code <<= 1;
code |= (short)bit;
i++;
}
int j = tree.ValOffset[i];
value = tree.HuffVal[(j + code) & 0xFF];
return true;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool TryReceive(int length, DoubleBufferedStreamReader stream, out int value)
{
value = 0;
while (length > 0)
{
if (!this.TryReadBit(stream, out int bit))
{
return false;
}
value = (value << 1) | bit;
length--;
}
return true;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool TryReceiveAndExtend(int length, DoubleBufferedStreamReader stream, out int value)
{
if (length == 1)
{
if (!this.TryReadBit(stream, out value))
{
return false;
}
value = value == 1 ? 1 : -1;
}
else
{
if (!this.TryReceive(length, stream, out value))
{
return false;
}
if (value < 1 << (length - 1))
{
value += (-1 << length) + 1;
}
}
return true;
}
private void DecodeBaseline(PdfJsFrameComponent component, ref short blockDataRef, int offset, ref PdfJsHuffmanTable dcHuffmanTable, ref PdfJsHuffmanTable acHuffmanTable, DoubleBufferedStreamReader stream)
{
if (!this.TryDecodeHuffman(ref dcHuffmanTable, stream, out short t))
{
return;
}
int diff = 0;
if (t != 0)
{
if (!this.TryReceiveAndExtend(t, stream, out diff))
{
return;
}
}
Unsafe.Add(ref blockDataRef, offset) = (short)(component.DcPredictor += diff);
int k = 1;
while (k < 64)
{
if (!this.TryDecodeHuffman(ref acHuffmanTable, stream, out short rs))
{
return;
}
int s = rs & 15;
int r = rs >> 4;
if (s == 0)
{
if (r < 15)
{
break;
}
k += 16;
continue;
}
k += r;
byte z = this.dctZigZag[k];
if (!this.TryReceiveAndExtend(s, stream, out int re))
{
return;
}
Unsafe.Add(ref blockDataRef, offset + z) = (short)re;
k++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeDCFirst(PdfJsFrameComponent component, ref short blockDataRef, int offset, ref PdfJsHuffmanTable dcHuffmanTable, DoubleBufferedStreamReader stream)
{
if (!this.TryDecodeHuffman(ref dcHuffmanTable, stream, out short t))
{
return;
}
int diff = 0;
if (t != 0)
{
if (!this.TryReceiveAndExtend(t, stream, out diff))
{
return;
}
}
Unsafe.Add(ref blockDataRef, offset) = (short)(component.DcPredictor += diff << this.successiveState);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeDCSuccessive(PdfJsFrameComponent component, ref short blockDataRef, int offset, DoubleBufferedStreamReader stream)
{
if (!this.TryReadBit(stream, out int bit))
{
return;
}
Unsafe.Add(ref blockDataRef, offset) |= (short)(bit << this.successiveState);
}
private void DecodeACFirst(ref short blockDataRef, int offset, ref PdfJsHuffmanTable acHuffmanTable, DoubleBufferedStreamReader stream)
{
if (this.eobrun > 0)
{
this.eobrun--;
return;
}
int k = this.specStart;
int e = this.specEnd;
while (k <= e)
{
if (!this.TryDecodeHuffman(ref acHuffmanTable, stream, out short rs))
{
return;
}
int s = rs & 15;
int r = rs >> 4;
if (s == 0)
{
if (r < 15)
{
if (!this.TryReceive(r, stream, out int eob))
{
return;
}
this.eobrun = eob + (1 << r) - 1;
break;
}
k += 16;
continue;
}
k += r;
byte z = this.dctZigZag[k];
if (!this.TryReceiveAndExtend(s, stream, out int v))
{
return;
}
Unsafe.Add(ref blockDataRef, offset + z) = (short)(v * (1 << this.successiveState));
k++;
}
}
private void DecodeACSuccessive(ref short blockDataRef, int offset, ref PdfJsHuffmanTable acHuffmanTable, DoubleBufferedStreamReader stream)
{
int k = this.specStart;
int e = this.specEnd;
int r = 0;
while (k <= e)
{
int offsetZ = offset + this.dctZigZag[k];
ref short blockOffsetZRef = ref Unsafe.Add(ref blockDataRef, offsetZ);
int sign = blockOffsetZRef < 0 ? -1 : 1;
switch (this.successiveACState)
{
case 0: // Initial state
if (!this.TryDecodeHuffman(ref acHuffmanTable, stream, out short rs))
{
return;
}
int s = rs & 15;
r = rs >> 4;
if (s == 0)
{
if (r < 15)
{
if (!this.TryReceive(r, stream, out int eob))
{
return;
}
this.eobrun = eob + (1 << r);
this.successiveACState = 4;
}
else
{
r = 16;
this.successiveACState = 1;
}
}
else
{
if (s != 1)
{
throw new ImageFormatException("Invalid ACn encoding");
}
if (!this.TryReceiveAndExtend(s, stream, out int v))
{
return;
}
this.successiveACNextValue = v;
this.successiveACState = r > 0 ? 2 : 3;
}
continue;
case 1: // Skipping r zero items
case 2:
if (blockOffsetZRef != 0)
{
if (!this.TryReadBit(stream, out int bit))
{
return;
}
blockOffsetZRef += (short)(sign * (bit << this.successiveState));
}
else
{
r--;
if (r == 0)
{
this.successiveACState = this.successiveACState == 2 ? 3 : 0;
}
}
break;
case 3: // Set value for a zero item
if (blockOffsetZRef != 0)
{
if (!this.TryReadBit(stream, out int bit))
{
return;
}
blockOffsetZRef += (short)(sign * (bit << this.successiveState));
}
else
{
blockOffsetZRef = (short)(this.successiveACNextValue << this.successiveState);
this.successiveACState = 0;
}
break;
case 4: // Eob
if (blockOffsetZRef != 0)
{
if (!this.TryReadBit(stream, out int bit))
{
return;
}
blockOffsetZRef += (short)(sign * (bit << this.successiveState));
}
break;
}
k++;
}
if (this.successiveACState == 4)
{
this.eobrun--;
if (this.eobrun == 0)
{
this.successiveACState = 0;
}
}
}
}
}

958
src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/ScanDecoder.cs

@ -0,0 +1,958 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Formats.Jpeg.Components;
namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
{
/// <summary>
/// Decodes the Huffman encoded spectral scan.
/// Originally ported from <see href="https://github.com/rds1983/StbSharp"/>
/// with additional fixes for both performance and common encoding errors.
/// </summary>
internal class ScanDecoder
{
// The number of bits that can be read via a LUT.
public const int FastBits = 9;
// LUT mask for n rightmost bits. Bmask[n] = (1 << n) - 1
private static readonly uint[] Bmask = { 0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191, 16383, 32767, 65535 };
// LUT Bias[n] = (-1 << n) + 1
private static readonly int[] Bias = { 0, -1, -3, -7, -15, -31, -63, -127, -255, -511, -1023, -2047, -4095, -8191, -16383, -32767 };
private readonly PdfJsFrame frame;
private readonly PdfJsHuffmanTables dcHuffmanTables;
private readonly PdfJsHuffmanTables acHuffmanTables;
private readonly FastACTables fastACTables;
private readonly DoubleBufferedStreamReader stream;
private readonly PdfJsFrameComponent[] components;
private readonly ZigZag dctZigZag;
// The restart interval.
private readonly int restartInterval;
// The current component index.
private readonly int componentIndex;
// The number of interleaved components.
private readonly int componentsLength;
// The spectral selection start.
private readonly int spectralStart;
// The spectral selection end.
private readonly int spectralEnd;
// The successive approximation high bit end.
private readonly int successiveHigh;
// The successive approximation low bit end.
private readonly int successiveLow;
// The number of valid bits left to read in the buffer.
private int codeBits;
// The entropy encoded code buffer.
private uint codeBuffer;
// Whether there is more data to pull from the stream for the current mcu.
private bool nomore;
// Whether we have prematurely reached the end of the file.
private bool eof;
// The current, if any, marker in the input stream.
private byte marker;
// Whether we have a bad marker, I.E. One that is not between RST0 and RST7
private bool badMarker;
// The opening position of an identified marker.
private long markerPosition;
// How many mcu's are left to do.
private int todo;
// The End-Of-Block countdown for ending the sequence prematurely when the remaining coefficients are zero.
private int eobrun;
/// <summary>
/// Initializes a new instance of the <see cref="ScanDecoder"/> class.
/// </summary>
/// <param name="stream">The input stream.</param>
/// <param name="frame">The image frame.</param>
/// <param name="dcHuffmanTables">The DC Huffman tables.</param>
/// <param name="acHuffmanTables">The AC Huffman tables.</param>
/// <param name="fastACTables">The fast AC decoding tables.</param>
/// <param name="componentIndex">The component index within the array.</param>
/// <param name="componentsLength">The length of the components. Different to the array length.</param>
/// <param name="restartInterval">The reset interval.</param>
/// <param name="spectralStart">The spectral selection start.</param>
/// <param name="spectralEnd">The spectral selection end.</param>
/// <param name="successiveHigh">The successive approximation bit high end.</param>
/// <param name="successiveLow">The successive approximation bit low end.</param>
public ScanDecoder(
DoubleBufferedStreamReader stream,
PdfJsFrame frame,
PdfJsHuffmanTables dcHuffmanTables,
PdfJsHuffmanTables acHuffmanTables,
FastACTables fastACTables,
int componentIndex,
int componentsLength,
int restartInterval,
int spectralStart,
int spectralEnd,
int successiveHigh,
int successiveLow)
{
this.dctZigZag = ZigZag.CreateUnzigTable();
this.stream = stream;
this.frame = frame;
this.dcHuffmanTables = dcHuffmanTables;
this.acHuffmanTables = acHuffmanTables;
this.fastACTables = fastACTables;
this.components = frame.Components;
this.marker = JpegConstants.Markers.XFF;
this.markerPosition = 0;
this.componentIndex = componentIndex;
this.componentsLength = componentsLength;
this.restartInterval = restartInterval;
this.spectralStart = spectralStart;
this.spectralEnd = spectralEnd;
this.successiveHigh = successiveHigh;
this.successiveLow = successiveLow;
}
/// <summary>
/// Decodes the entropy coded data.
/// </summary>
public void ParseEntropyCodedData()
{
this.Reset();
if (!this.frame.Progressive)
{
this.ParseBaselineData();
}
else
{
this.ParseProgressiveData();
}
if (this.badMarker)
{
this.stream.Position = this.markerPosition;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static uint LRot(uint x, int y) => (x << y) | (x >> (32 - y));
private void ParseBaselineData()
{
if (this.componentsLength == 1)
{
this.ParseBaselineDataNonInterleaved();
}
else
{
this.ParseBaselineDataInterleaved();
}
}
private void ParseBaselineDataInterleaved()
{
// Interleaved
int mcu = 0;
int mcusPerColumn = this.frame.McusPerColumn;
int mcusPerLine = this.frame.McusPerLine;
for (int j = 0; j < mcusPerColumn; j++)
{
for (int i = 0; i < mcusPerLine; i++)
{
// Scan an interleaved mcu... process components in order
for (int k = 0; k < this.componentsLength; k++)
{
PdfJsFrameComponent component = this.components[k];
ref PdfJsHuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId];
ref PdfJsHuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId];
ref short fastACRef = ref this.fastACTables.GetAcTableReference(component);
int h = component.HorizontalSamplingFactor;
int v = component.VerticalSamplingFactor;
// Scan out an mcu's worth of this component; that's just determined
// by the basic H and V specified for the component
for (int y = 0; y < v; y++)
{
for (int x = 0; x < h; x++)
{
if (this.eof)
{
return;
}
int mcuRow = mcu / mcusPerLine;
int mcuCol = mcu % mcusPerLine;
int blockRow = (mcuRow * v) + y;
int blockCol = (mcuCol * h) + x;
this.DecodeBlockBaseline(
component,
blockRow,
blockCol,
ref dcHuffmanTable,
ref acHuffmanTable,
ref fastACRef);
}
}
}
// After all interleaved components, that's an interleaved MCU,
// so now count down the restart interval
mcu++;
if (!this.ContinueOnMcuComplete())
{
return;
}
}
}
}
/// <summary>
/// Non-interleaved data, we just need to process one block at a ti
/// in trivial scanline order
/// number of blocks to do just depends on how many actual "pixels"
/// component has, independent of interleaved MCU blocking and such
/// </summary>
private void ParseBaselineDataNonInterleaved()
{
PdfJsFrameComponent component = this.components[this.componentIndex];
int w = component.WidthInBlocks;
int h = component.HeightInBlocks;
ref PdfJsHuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId];
ref PdfJsHuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId];
ref short fastACRef = ref this.fastACTables.GetAcTableReference(component);
int mcu = 0;
for (int j = 0; j < h; j++)
{
for (int i = 0; i < w; i++)
{
if (this.eof)
{
return;
}
int blockRow = mcu / w;
int blockCol = mcu % w;
this.DecodeBlockBaseline(
component,
blockRow,
blockCol,
ref dcHuffmanTable,
ref acHuffmanTable,
ref fastACRef);
// Every data block is an MCU, so countdown the restart interval
mcu++;
if (!this.ContinueOnMcuComplete())
{
return;
}
}
}
}
private void ParseProgressiveData()
{
if (this.componentsLength == 1)
{
this.ParseProgressiveDataNonInterleaved();
}
else
{
this.ParseProgressiveDataInterleaved();
}
}
private void ParseProgressiveDataInterleaved()
{
// Interleaved
int mcu = 0;
int mcusPerColumn = this.frame.McusPerColumn;
int mcusPerLine = this.frame.McusPerLine;
for (int j = 0; j < mcusPerColumn; j++)
{
for (int i = 0; i < mcusPerLine; i++)
{
// Scan an interleaved mcu... process components in order
for (int k = 0; k < this.componentsLength; k++)
{
PdfJsFrameComponent component = this.components[k];
ref PdfJsHuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId];
int h = component.HorizontalSamplingFactor;
int v = component.VerticalSamplingFactor;
// Scan out an mcu's worth of this component; that's just determined
// by the basic H and V specified for the component
for (int y = 0; y < v; y++)
{
for (int x = 0; x < h; x++)
{
if (this.eof)
{
return;
}
int mcuRow = mcu / mcusPerLine;
int mcuCol = mcu % mcusPerLine;
int blockRow = (mcuRow * v) + y;
int blockCol = (mcuCol * h) + x;
this.DecodeBlockProgressiveDC(
component,
blockRow,
blockCol,
ref dcHuffmanTable);
}
}
}
// After all interleaved components, that's an interleaved MCU,
// so now count down the restart interval
mcu++;
if (!this.ContinueOnMcuComplete())
{
return;
}
}
}
}
/// <summary>
/// Non-interleaved data, we just need to process one block at a time,
/// in trivial scanline order
/// number of blocks to do just depends on how many actual "pixels" this
/// component has, independent of interleaved MCU blocking and such
/// </summary>
private void ParseProgressiveDataNonInterleaved()
{
PdfJsFrameComponent component = this.components[this.componentIndex];
int w = component.WidthInBlocks;
int h = component.HeightInBlocks;
ref PdfJsHuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId];
ref PdfJsHuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId];
ref short fastACRef = ref this.fastACTables.GetAcTableReference(component);
int mcu = 0;
for (int j = 0; j < h; j++)
{
for (int i = 0; i < w; i++)
{
if (this.eof)
{
return;
}
int blockRow = mcu / w;
int blockCol = mcu % w;
if (this.spectralStart == 0)
{
this.DecodeBlockProgressiveDC(
component,
blockRow,
blockCol,
ref dcHuffmanTable);
}
else
{
this.DecodeBlockProgressiveAC(
component,
blockRow,
blockCol,
ref acHuffmanTable,
ref fastACRef);
}
// Every data block is an MCU, so countdown the restart interval
mcu++;
if (!this.ContinueOnMcuComplete())
{
return;
}
}
}
}
private void DecodeBlockBaseline(
PdfJsFrameComponent component,
int row,
int col,
ref PdfJsHuffmanTable dcTable,
ref PdfJsHuffmanTable acTable,
ref short fastACRef)
{
this.CheckBits();
int t = this.DecodeHuffman(ref dcTable);
if (t < 0)
{
JpegThrowHelper.ThrowBadHuffmanCode();
}
ref short blockDataRef = ref component.GetBlockDataReference(row, col);
int diff = t != 0 ? this.ExtendReceive(t) : 0;
int dc = component.DcPredictor + diff;
component.DcPredictor = dc;
blockDataRef = (short)dc;
// Decode AC Components, See Jpeg Spec
int k = 1;
do
{
int zig;
int s;
this.CheckBits();
int c = this.PeekBits();
int r = Unsafe.Add(ref fastACRef, c);
if (r != 0)
{
// Fast AC path
k += (r >> 4) & 15; // Run
s = r & 15; // Combined Length
this.codeBuffer <<= s;
this.codeBits -= s;
// Decode into unzigzag location
zig = this.dctZigZag[k++];
Unsafe.Add(ref blockDataRef, zig) = (short)(r >> 8);
}
else
{
int rs = this.DecodeHuffman(ref acTable);
if (rs < 0)
{
JpegThrowHelper.ThrowBadHuffmanCode();
}
s = rs & 15;
r = rs >> 4;
if (s == 0)
{
if (rs != 0xF0)
{
break; // End block
}
k += 16;
}
else
{
k += r;
// Decode into unzigzag location
zig = this.dctZigZag[k++];
Unsafe.Add(ref blockDataRef, zig) = (short)this.ExtendReceive(s);
}
}
} while (k < 64);
}
private void DecodeBlockProgressiveDC(
PdfJsFrameComponent component,
int row,
int col,
ref PdfJsHuffmanTable dcTable)
{
if (this.spectralEnd != 0)
{
JpegThrowHelper.ThrowImageFormatException("Can't merge DC and AC.");
}
this.CheckBits();
ref short blockDataRef = ref component.GetBlockDataReference(row, col);
if (this.successiveHigh == 0)
{
// First scan for DC coefficient, must be first
int t = this.DecodeHuffman(ref dcTable);
int diff = t != 0 ? this.ExtendReceive(t) : 0;
int dc = component.DcPredictor + diff;
component.DcPredictor = dc;
blockDataRef = (short)(dc << this.successiveLow);
}
else
{
// Refinement scan for DC coefficient
if (this.GetBit() != 0)
{
blockDataRef += (short)(1 << this.successiveLow);
}
}
}
private void DecodeBlockProgressiveAC(
PdfJsFrameComponent component,
int row,
int col,
ref PdfJsHuffmanTable acTable,
ref short fastACRef)
{
if (this.spectralStart == 0)
{
JpegThrowHelper.ThrowImageFormatException("Can't merge DC and AC.");
}
ref short blockDataRef = ref component.GetBlockDataReference(row, col);
if (this.successiveHigh == 0)
{
// MCU decoding for AC initial scan (either spectral selection,
// or first pass of successive approximation).
int shift = this.successiveLow;
if (this.eobrun != 0)
{
this.eobrun--;
return;
}
int k = this.spectralStart;
do
{
int zig;
int s;
this.CheckBits();
int c = this.PeekBits();
int r = Unsafe.Add(ref fastACRef, c);
if (r != 0)
{
// Fast AC path
k += (r >> 4) & 15; // Run
s = r & 15; // Combined length
this.codeBuffer <<= s;
this.codeBits -= s;
// Decode into unzigzag location
zig = this.dctZigZag[k++];
Unsafe.Add(ref blockDataRef, zig) = (short)((r >> 8) << shift);
}
else
{
int rs = this.DecodeHuffman(ref acTable);
if (rs < 0)
{
JpegThrowHelper.ThrowBadHuffmanCode();
}
s = rs & 15;
r = rs >> 4;
if (s == 0)
{
if (r < 15)
{
this.eobrun = 1 << r;
if (r != 0)
{
this.eobrun += this.GetBits(r);
}
this.eobrun--;
break;
}
k += 16;
}
else
{
k += r;
zig = this.dctZigZag[k++];
Unsafe.Add(ref blockDataRef, zig) = (short)(this.ExtendReceive(s) << shift);
}
}
}
while (k <= this.spectralEnd);
}
else
{
// Refinement scan for these AC coefficients
this.DecodeBlockProgressiveACRefined(ref blockDataRef, ref acTable);
}
}
private void DecodeBlockProgressiveACRefined(ref short blockDataRef, ref PdfJsHuffmanTable acTable)
{
int k;
// Refinement scan for these AC coefficients
short bit = (short)(1 << this.successiveLow);
if (this.eobrun != 0)
{
this.eobrun--;
for (k = this.spectralStart; k <= this.spectralEnd; k++)
{
ref short p = ref Unsafe.Add(ref blockDataRef, this.dctZigZag[k]);
if (p != 0)
{
if (this.GetBit() != 0)
{
if ((p & bit) == 0)
{
if (p > 0)
{
p += bit;
}
else
{
p -= bit;
}
}
}
}
}
}
else
{
k = this.spectralStart;
do
{
int rs = this.DecodeHuffman(ref acTable);
if (rs < 0)
{
JpegThrowHelper.ThrowBadHuffmanCode();
}
int s = rs & 15;
int r = rs >> 4;
if (s == 0)
{
// r=15 s=0 should write 16 0s, so we just do
// a run of 15 0s and then write s (which is 0),
// so we don't have to do anything special here
if (r < 15)
{
this.eobrun = (1 << r) - 1;
if (r != 0)
{
this.eobrun += this.GetBits(r);
}
r = 64; // Force end of block
}
}
else
{
if (s != 1)
{
JpegThrowHelper.ThrowBadHuffmanCode();
}
// Sign bit
if (this.GetBit() != 0)
{
s = bit;
}
else
{
s = -bit;
}
}
// Advance by r
while (k <= this.spectralEnd)
{
ref short p = ref Unsafe.Add(ref blockDataRef, this.dctZigZag[k++]);
if (p != 0)
{
if (this.GetBit() != 0)
{
if ((p & bit) == 0)
{
if (p > 0)
{
p += bit;
}
else
{
p -= bit;
}
}
}
}
else
{
if (r == 0)
{
p = (short)s;
break;
}
r--;
}
}
}
while (k <= this.spectralEnd);
}
}
[MethodImpl(InliningOptions.ShortMethod)]
private int GetBits(int n)
{
if (this.codeBits < n)
{
this.FillBuffer();
}
uint k = LRot(this.codeBuffer, n);
this.codeBuffer = k & ~Bmask[n];
k &= Bmask[n];
this.codeBits -= n;
return (int)k;
}
[MethodImpl(InliningOptions.ShortMethod)]
private int GetBit()
{
if (this.codeBits < 1)
{
this.FillBuffer();
}
uint k = this.codeBuffer;
this.codeBuffer <<= 1;
this.codeBits--;
return (int)(k & 0x80000000);
}
[MethodImpl(InliningOptions.ColdPath)]
private void FillBuffer()
{
// Attempt to load at least the minimum nbumber of required bits into the buffer.
// We fail to do so only if we hit a marker or reach the end of the input stream.
do
{
int b = this.nomore ? 0 : this.stream.ReadByte();
if (b == -1)
{
// We've encountered the end of the file stream which means there's no EOI marker in the image
// or the SOS marker has the wrong dimensions set.
this.eof = true;
b = 0;
}
// Found a marker.
if (b == JpegConstants.Markers.XFF)
{
this.markerPosition = this.stream.Position - 1;
int c = this.stream.ReadByte();
while (c == JpegConstants.Markers.XFF)
{
c = this.stream.ReadByte();
if (c == -1)
{
this.eof = true;
c = 0;
break;
}
}
if (c != 0)
{
this.marker = (byte)c;
this.nomore = true;
if (!this.HasRestart())
{
this.badMarker = true;
}
return;
}
}
this.codeBuffer |= (uint)b << (24 - this.codeBits);
this.codeBits += 8;
}
while (this.codeBits <= 24);
}
[MethodImpl(InliningOptions.ShortMethod)]
private int DecodeHuffman(ref PdfJsHuffmanTable table)
{
this.CheckBits();
// Look at the top FastBits and determine what symbol ID it is,
// if the code is <= FastBits.
int c = this.PeekBits();
int k = table.Lookahead[c];
if (k < 0xFF)
{
int s = table.Sizes[k];
if (s > this.codeBits)
{
return -1;
}
this.codeBuffer <<= s;
this.codeBits -= s;
return table.Values[k];
}
return this.DecodeHuffmanSlow(ref table);
}
[MethodImpl(InliningOptions.ColdPath)]
private int DecodeHuffmanSlow(ref PdfJsHuffmanTable table)
{
// Naive test is to shift the code_buffer down so k bits are
// valid, then test against MaxCode. To speed this up, we've
// preshifted maxcode left so that it has (16-k) 0s at the
// end; in other words, regardless of the number of bits, it
// wants to be compared against something shifted to have 16;
// that way we don't need to shift inside the loop.
uint temp = this.codeBuffer >> 16;
int k;
for (k = FastBits + 1; ; k++)
{
if (temp < table.MaxCode[k])
{
break;
}
}
if (k == 17)
{
// Error! code not found
this.codeBits -= 16;
return -1;
}
if (k > this.codeBits)
{
return -1;
}
// Convert the huffman code to the symbol id
int c = (int)(((this.codeBuffer >> (32 - k)) & Bmask[k]) + table.ValOffset[k]);
// Convert the id to a symbol
this.codeBits -= k;
this.codeBuffer <<= k;
return table.Values[c];
}
[MethodImpl(InliningOptions.ShortMethod)]
private int ExtendReceive(int n)
{
if (this.codeBits < n)
{
this.FillBuffer();
}
int sgn = (int)this.codeBuffer >> 31;
uint k = LRot(this.codeBuffer, n);
this.codeBuffer = k & ~Bmask[n];
k &= Bmask[n];
this.codeBits -= n;
return (int)(k + (Bias[n] & ~sgn));
}
[MethodImpl(InliningOptions.ShortMethod)]
private void CheckBits()
{
if (this.codeBits < 16)
{
this.FillBuffer();
}
}
[MethodImpl(InliningOptions.ShortMethod)]
private int PeekBits() => (int)((this.codeBuffer >> (32 - FastBits)) & ((1 << FastBits) - 1));
[MethodImpl(InliningOptions.ShortMethod)]
private bool ContinueOnMcuComplete()
{
if (--this.todo > 0)
{
return true;
}
if (this.codeBits < 24)
{
this.FillBuffer();
}
// If it's NOT a restart, then just bail, so we get corrupt data rather than no data.
// Reset the stream to before any bad markers to ensure we can read sucessive segments.
if (this.badMarker)
{
this.stream.Position = this.markerPosition;
}
if (!this.HasRestart())
{
return false;
}
this.Reset();
return true;
}
[MethodImpl(InliningOptions.ShortMethod)]
private bool HasRestart()
{
byte m = this.marker;
return m >= JpegConstants.Markers.RST0 && m <= JpegConstants.Markers.RST7;
}
private void Reset()
{
this.codeBits = 0;
this.codeBuffer = 0;
for (int i = 0; i < this.components.Length; i++)
{
PdfJsFrameComponent c = this.components[i];
c.DcPredictor = 0;
}
this.nomore = false;
this.marker = JpegConstants.Markers.XFF;
this.markerPosition = 0;
this.badMarker = false;
this.eobrun = 0;
// No more than 1<<31 MCUs if no restartInterval? that's plenty safe since we don't even allow 1<<30 pixels
this.todo = this.restartInterval > 0 ? this.restartInterval : int.MaxValue;
}
}
}

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

@ -58,6 +58,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
/// </summary>
private PdfJsHuffmanTables acHuffmanTables;
/// <summary>
/// The fast AC tables used for entropy decoding
/// </summary>
private FastACTables fastACTables;
/// <summary>
/// The reset interval determined by RST markers
/// </summary>
@ -239,6 +244,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
this.QuantizationTables = new Block8x8F[4];
this.dcHuffmanTables = new PdfJsHuffmanTables();
this.acHuffmanTables = new PdfJsHuffmanTables();
this.fastACTables = new FastACTables(this.configuration.MemoryAllocator);
}
while (fileMarker.Marker != JpegConstants.Markers.EOI)
@ -353,12 +359,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
{
this.InputStream?.Dispose();
this.Frame?.Dispose();
this.fastACTables?.Dispose();
// Set large fields to null.
this.InputStream = null;
this.Frame = null;
this.dcHuffmanTables = null;
this.acHuffmanTables = null;
this.fastACTables = null;
}
/// <summary>
@ -723,11 +731,20 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
i += 17 + codeLengthSum;
int tableType = huffmanTableSpec >> 4;
int tableIndex = huffmanTableSpec & 15;
this.BuildHuffmanTable(
huffmanTableSpec >> 4 == 0 ? this.dcHuffmanTables : this.acHuffmanTables,
huffmanTableSpec & 15,
tableType == 0 ? this.dcHuffmanTables : this.acHuffmanTables,
tableIndex,
codeLengths.GetSpan(),
huffmanValues.GetSpan());
if (huffmanTableSpec >> 4 != 0)
{
// Build a table that decodes both magnitude and value of small ACs in one go.
this.fastACTables.BuildACTableLut(huffmanTableSpec & 15, this.acHuffmanTables);
}
}
}
}
@ -786,21 +803,22 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
int spectralStart = this.temp[0];
int spectralEnd = this.temp[1];
int successiveApproximation = this.temp[2];
PdfJsScanDecoder scanDecoder = default;
scanDecoder.DecodeScan(
this.Frame,
this.InputStream,
this.dcHuffmanTables,
this.acHuffmanTables,
this.Frame.Components,
componentIndex,
selectorsCount,
this.resetInterval,
spectralStart,
spectralEnd,
successiveApproximation >> 4,
successiveApproximation & 15);
var sd = new ScanDecoder(
this.InputStream,
this.Frame,
this.dcHuffmanTables,
this.acHuffmanTables,
this.fastACTables,
componentIndex,
selectorsCount,
this.resetInterval,
spectralStart,
spectralEnd,
successiveApproximation >> 4,
successiveApproximation & 15);
sd.ParseEntropyCodedData();
}
/// <summary>
@ -827,6 +845,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
return BinaryPrimitives.ReadUInt16BigEndian(this.markerBuffer);
}
/// <summary>
/// Post processes the pixels into the destination image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
private Image<TPixel> PostProcessIntoImage<TPixel>()
where TPixel : struct, IPixel<TPixel>
{

2
src/ImageSharp/Formats/Png/IPngEncoderOptions.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Processing.Quantization;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
namespace SixLabors.ImageSharp.Formats.Png
{

2
src/ImageSharp/Formats/Png/PngEncoder.cs

@ -4,7 +4,7 @@
using System.IO;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Quantization;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
namespace SixLabors.ImageSharp.Formats.Png
{

31
src/ImageSharp/Formats/Png/PngEncoderCore.cs

@ -9,7 +9,7 @@ using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats.Png.Filters;
using SixLabors.ImageSharp.Formats.Png.Zlib;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Quantization;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
using SixLabors.Memory;
namespace SixLabors.ImageSharp.Formats.Png
@ -86,11 +86,6 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary>
private readonly bool writeGamma;
/// <summary>
/// Contains the raw pixel data from an indexed image.
/// </summary>
private byte[] palettePixelData;
/// <summary>
/// The image width.
/// </summary>
@ -188,11 +183,12 @@ namespace SixLabors.ImageSharp.Formats.Png
stream.Write(PngConstants.HeaderBytes, 0, PngConstants.HeaderBytes.Length);
QuantizedFrame<TPixel> quantized = null;
ReadOnlySpan<byte> quantizedPixelsSpan = default;
if (this.pngColorType == PngColorType.Palette)
{
// Create quantized frame returning the palette and set the bit depth.
quantized = this.quantizer.CreateFrameQuantizer<TPixel>().QuantizeFrame(image.Frames.RootFrame);
this.palettePixelData = quantized.Pixels;
quantizedPixelsSpan = quantized.GetPixelSpan();
byte bits = (byte)ImageMaths.GetBitsNeededForColorDepth(quantized.Palette.Length).Clamp(1, 8);
// Png only supports in four pixel depths: 1, 2, 4, and 8 bits when using the PLTE chunk
@ -233,9 +229,11 @@ namespace SixLabors.ImageSharp.Formats.Png
this.WritePhysicalChunk(stream, image);
this.WriteGammaChunk(stream);
this.WriteDataChunks(image.Frames.RootFrame, stream);
this.WriteDataChunks(image.Frames.RootFrame, quantizedPixelsSpan, stream);
this.WriteEndChunk(stream);
stream.Flush();
quantized?.Dispose();
}
/// <inheritdoc />
@ -384,9 +382,10 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="rowSpan">The row span.</param>
/// <param name="quantizedPixelsSpan">The span of quantized pixels. Can be null.</param>
/// <param name="row">The row.</param>
/// <returns>The <see cref="IManagedByteBuffer"/></returns>
private IManagedByteBuffer EncodePixelRow<TPixel>(ReadOnlySpan<TPixel> rowSpan, int row)
private IManagedByteBuffer EncodePixelRow<TPixel>(ReadOnlySpan<TPixel> rowSpan, ReadOnlySpan<byte> quantizedPixelsSpan, int row)
where TPixel : struct, IPixel<TPixel>
{
switch (this.pngColorType)
@ -394,7 +393,7 @@ namespace SixLabors.ImageSharp.Formats.Png
case PngColorType.Palette:
int stride = this.rawScanline.Length();
this.palettePixelData.AsSpan(row * stride, stride).CopyTo(this.rawScanline.GetSpan());
quantizedPixelsSpan.Slice(row * stride, stride).CopyTo(this.rawScanline.GetSpan());
break;
case PngColorType.Grayscale:
@ -555,10 +554,11 @@ namespace SixLabors.ImageSharp.Formats.Png
{
Span<byte> colorTableSpan = colorTable.GetSpan();
Span<byte> alphaTableSpan = alphaTable.GetSpan();
Span<byte> quantizedSpan = quantized.GetPixelSpan();
for (byte i = 0; i < pixelCount; i++)
{
if (quantized.Pixels.Contains(i))
if (quantizedSpan.IndexOf(i) > -1)
{
int offset = i * 3;
palette[i].ToRgba32(ref rgba);
@ -571,10 +571,10 @@ namespace SixLabors.ImageSharp.Formats.Png
if (alpha > this.threshold)
{
alpha = 255;
alpha = byte.MaxValue;
}
anyAlpha = anyAlpha || alpha < 255;
anyAlpha = anyAlpha || alpha < byte.MaxValue;
alphaTableSpan[i] = alpha;
}
}
@ -635,8 +635,9 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="pixels">The image.</param>
/// <param name="quantizedPixelsSpan">The span of quantized pixel data. Can be null.</param>
/// <param name="stream">The stream.</param>
private void WriteDataChunks<TPixel>(ImageFrame<TPixel> pixels, Stream stream)
private void WriteDataChunks<TPixel>(ImageFrame<TPixel> pixels, ReadOnlySpan<byte> quantizedPixelsSpan, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
this.bytesPerScanline = this.width * this.bytesPerPixel;
@ -688,7 +689,7 @@ namespace SixLabors.ImageSharp.Formats.Png
{
for (int y = 0; y < this.height; y++)
{
IManagedByteBuffer r = this.EncodePixelRow((ReadOnlySpan<TPixel>)pixels.GetPixelRowSpan(y), y);
IManagedByteBuffer r = this.EncodePixelRow((ReadOnlySpan<TPixel>)pixels.GetPixelRowSpan(y), quantizedPixelsSpan, y);
deflateStream.Write(r.Array, 0, resultLength);
IManagedByteBuffer temp = this.rawScanline;

9
src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs

@ -6,6 +6,7 @@ using System.Buffers.Binary;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using SixLabors.ImageSharp.IO;
@ -462,7 +463,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
}
}
private unsafe double ConvertToDouble(ReadOnlySpan<byte> buffer)
private double ConvertToDouble(ReadOnlySpan<byte> buffer)
{
if (buffer.Length < 8)
{
@ -473,7 +474,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
? BinaryPrimitives.ReadInt64BigEndian(buffer)
: BinaryPrimitives.ReadInt64LittleEndian(buffer);
return *((double*)&intValue);
return Unsafe.As<long, double>(ref intValue);
}
private uint ConvertToUInt32(ReadOnlySpan<byte> buffer)
@ -501,7 +502,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
: BinaryPrimitives.ReadUInt16LittleEndian(buffer);
}
private unsafe float ConvertToSingle(ReadOnlySpan<byte> buffer)
private float ConvertToSingle(ReadOnlySpan<byte> buffer)
{
if (buffer.Length < 4)
{
@ -512,7 +513,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
? BinaryPrimitives.ReadInt32BigEndian(buffer)
: BinaryPrimitives.ReadInt32LittleEndian(buffer);
return *((float*)&intValue);
return Unsafe.As<int, float>(ref intValue);
}
private Rational ToRational(ReadOnlySpan<byte> buffer)

2
src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs

@ -181,7 +181,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// <inheritdoc />
public bool Equals(ExifValue other)
{
if (ReferenceEquals(other, null))
if (other is null)
{
return false;
}

12
src/ImageSharp/MetaData/Profiles/ICC/Curves/IccParametricCurve.cs

@ -125,7 +125,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc/>
public bool Equals(IccParametricCurve other)
{
if (other == null)
if (other is null)
{
return false;
}
@ -148,16 +148,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
return obj is IccParametricCurve other && this.Equals(other);
}

10
src/ImageSharp/MetaData/Profiles/ICC/Curves/IccResponseCurve.cs

@ -67,16 +67,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc />
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
return obj is IccResponseCurve other && this.Equals(other);
}

4
src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.NonPrimitives.cs

@ -38,7 +38,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// Reads an ICC profile version number
/// </summary>
/// <returns>the version number</returns>
public Version ReadVersionNumber()
public IccVersion ReadVersionNumber()
{
int version = this.ReadInt32();
@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
int minor = (version >> 20) & 0x0F;
int bugfix = (version >> 16) & 0x0F;
return new Version(major, minor, bugfix);
return new IccVersion(major, minor, bugfix);
}
/// <summary>

9
src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Primitives.cs

@ -3,6 +3,7 @@
using System;
using System.Buffers.Binary;
using System.Runtime.CompilerServices;
using System.Text;
namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
@ -70,22 +71,22 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// Reads a float.
/// </summary>
/// <returns>the value</returns>
public unsafe float ReadSingle()
public float ReadSingle()
{
int intValue = this.ReadInt32();
return *((float*)&intValue);
return Unsafe.As<int, float>(ref intValue);
}
/// <summary>
/// Reads a double
/// </summary>
/// <returns>the value</returns>
public unsafe double ReadDouble()
public double ReadDouble()
{
long intValue = this.ReadInt64();
return *((double*)&intValue);
return Unsafe.As<long, double>(ref intValue);
}
/// <summary>

16
src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.NonPrimitives.cs

@ -31,11 +31,11 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// </summary>
/// <param name="value">The value to write</param>
/// <returns>the number of bytes written</returns>
public int WriteVersionNumber(Version value)
public int WriteVersionNumber(in IccVersion value)
{
int major = value.Major.Clamp(0, byte.MaxValue);
int minor = value.Minor.Clamp(0, 15);
int bugfix = value.Build.Clamp(0, 15);
int bugfix = value.Patch.Clamp(0, 15);
// TODO: This is not used?
byte mb = (byte)((minor << 4) | bugfix);
@ -61,7 +61,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// </summary>
/// <param name="value">The value to write</param>
/// <returns>the number of bytes written</returns>
public int WriteProfileId(IccProfileId value)
public int WriteProfileId(in IccProfileId value)
{
return this.WriteUInt32(value.Part1)
+ this.WriteUInt32(value.Part2)
@ -74,7 +74,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// </summary>
/// <param name="value">The value to write</param>
/// <returns>the number of bytes written</returns>
public int WritePositionNumber(IccPositionNumber value)
public int WritePositionNumber(in IccPositionNumber value)
{
return this.WriteUInt32(value.Offset)
+ this.WriteUInt32(value.Size);
@ -85,7 +85,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// </summary>
/// <param name="value">The value to write</param>
/// <returns>the number of bytes written</returns>
public int WriteResponseNumber(IccResponseNumber value)
public int WriteResponseNumber(in IccResponseNumber value)
{
return this.WriteUInt16(value.DeviceCode)
+ this.WriteFix16(value.MeasurementValue);
@ -96,7 +96,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// </summary>
/// <param name="value">The value to write</param>
/// <returns>the number of bytes written</returns>
public int WriteNamedColor(IccNamedColor value)
public int WriteNamedColor(in IccNamedColor value)
{
return this.WriteAsciiString(value.Name, 32, true)
+ this.WriteArray(value.PcsCoordinates)
@ -108,7 +108,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// </summary>
/// <param name="value">The value to write</param>
/// <returns>the number of bytes written</returns>
public int WriteProfileDescription(IccProfileDescription value)
public int WriteProfileDescription(in IccProfileDescription value)
{
return this.WriteUInt32(value.DeviceManufacturer)
+ this.WriteUInt32(value.DeviceModel)
@ -125,7 +125,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// </summary>
/// <param name="value">The value to write</param>
/// <returns>the number of bytes written</returns>
public int WriteScreeningChannel(IccScreeningChannel value)
public int WriteScreeningChannel(in IccScreeningChannel value)
{
return this.WriteFix16(value.Frequency)
+ this.WriteFix16(value.Angle)

17
src/ImageSharp/MetaData/Profiles/ICC/IccProfile.cs

@ -167,11 +167,22 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <returns>True if the profile is valid; False otherwise</returns>
public bool CheckIsValid()
{
return Enum.IsDefined(typeof(IccColorSpaceType), this.Header.DataColorSpace) &&
const int minSize = 128;
const int maxSize = 50_000_000; // it's unlikely there is a profile bigger than 50MB
bool arrayValid = true;
if (this.data != null)
{
arrayValid = this.data.Length >= minSize &&
this.data.Length >= this.Header.Size;
}
return arrayValid &&
Enum.IsDefined(typeof(IccColorSpaceType), this.Header.DataColorSpace) &&
Enum.IsDefined(typeof(IccColorSpaceType), this.Header.ProfileConnectionSpace) &&
Enum.IsDefined(typeof(IccRenderingIntent), this.Header.RenderingIntent) &&
this.Header.Size >= 128 &&
this.Header.Size < 50_000_000; // it's unlikely there is a profile bigger than 50MB
this.Header.Size >= minSize &&
this.Header.Size < maxSize;
}
/// <summary>

34
src/ImageSharp/MetaData/Profiles/ICC/IccProfileHeader.cs

@ -7,42 +7,42 @@ using System.Numerics;
namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
{
/// <summary>
/// Contains all values of an ICC profile header
/// Contains all values of an ICC profile header.
/// </summary>
public sealed class IccProfileHeader
{
/// <summary>
/// Gets or sets the profile size in bytes (will be ignored when writing a profile)
/// Gets or sets the profile size in bytes (will be ignored when writing a profile).
/// </summary>
public uint Size { get; set; }
/// <summary>
/// Gets or sets the preferred CMM (Color Management Module) type
/// Gets or sets the preferred CMM (Color Management Module) type.
/// </summary>
public string CmmType { get; set; }
/// <summary>
/// Gets or sets the profiles version number
/// Gets or sets the profiles version number.
/// </summary>
public Version Version { get; set; }
public IccVersion Version { get; set; }
/// <summary>
/// Gets or sets the type of the profile
/// Gets or sets the type of the profile.
/// </summary>
public IccProfileClass Class { get; set; }
/// <summary>
/// Gets or sets the data colorspace
/// Gets or sets the data colorspace.
/// </summary>
public IccColorSpaceType DataColorSpace { get; set; }
/// <summary>
/// Gets or sets the profile connection space
/// Gets or sets the profile connection space.
/// </summary>
public IccColorSpaceType ProfileConnectionSpace { get; set; }
/// <summary>
/// Gets or sets the date and time this profile was created
/// Gets or sets the date and time this profile was created.
/// </summary>
public DateTime CreationDate { get; set; }
@ -59,42 +59,42 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <summary>
/// Gets or sets the profile flags to indicate various options for the CMM
/// such as distributed processing and caching options
/// such as distributed processing and caching options.
/// </summary>
public IccProfileFlag Flags { get; set; }
/// <summary>
/// Gets or sets the device manufacturer of the device for which this profile is created
/// Gets or sets the device manufacturer of the device for which this profile is created.
/// </summary>
public uint DeviceManufacturer { get; set; }
/// <summary>
/// Gets or sets the model of the device for which this profile is created
/// Gets or sets the model of the device for which this profile is created.
/// </summary>
public uint DeviceModel { get; set; }
/// <summary>
/// Gets or sets the device attributes unique to the particular device setup such as media type
/// Gets or sets the device attributes unique to the particular device setup such as media type.
/// </summary>
public IccDeviceAttribute DeviceAttributes { get; set; }
/// <summary>
/// Gets or sets the rendering Intent
/// Gets or sets the rendering Intent.
/// </summary>
public IccRenderingIntent RenderingIntent { get; set; }
/// <summary>
/// Gets or sets The normalized XYZ values of the illuminant of the PCS
/// Gets or sets The normalized XYZ values of the illuminant of the PCS.
/// </summary>
public Vector3 PcsIlluminant { get; set; }
/// <summary>
/// Gets or sets Profile creator signature
/// Gets or sets profile creator signature.
/// </summary>
public string CreatorSignature { get; set; }
/// <summary>
/// Gets or sets the profile ID (hash)
/// Gets or sets the profile ID (hash).
/// </summary>
public IccProfileId Id { get; set; }
}

28
src/ImageSharp/MetaData/Profiles/ICC/IccTagDataEntry.cs

@ -44,28 +44,9 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
return obj is IccTagDataEntry entry && this.Equals(entry);
}
/// <inheritdoc/>
public override int GetHashCode()
{
unchecked
{
return (int)this.Signature * 397;
}
}
/// <inheritdoc/>
public virtual bool Equals(IccTagDataEntry other)
{
@ -81,5 +62,14 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
return this.Signature == other.Signature;
}
/// <inheritdoc/>
public override int GetHashCode()
{
unchecked
{
return (int)this.Signature * 397;
}
}
}
}

14
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccChromaticityTagDataEntry.cs

@ -88,7 +88,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc/>
public bool Equals(IccChromaticityTagDataEntry other)
{
if (ReferenceEquals(null, other))
if (other is null)
{
return false;
}
@ -104,17 +104,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
return obj is IccChromaticityTagDataEntry && this.Equals((IccChromaticityTagDataEntry)obj);
return obj is IccChromaticityTagDataEntry other && this.Equals(other);
}
/// <inheritdoc/>

10
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccColorantOrderTagDataEntry.cs

@ -65,16 +65,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
return obj is IccColorantOrderTagDataEntry other && this.Equals(other);
}

10
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccColorantTableTagDataEntry.cs

@ -66,16 +66,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
return obj is IccColorantTableTagDataEntry other && this.Equals(other);
}

10
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccCrdInfoTagDataEntry.cs

@ -115,16 +115,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
return obj is IccCrdInfoTagDataEntry other && this.Equals(other);
}

10
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccCurveTagDataEntry.cs

@ -113,16 +113,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
return obj is IccCurveTagDataEntry other && this.Equals(other);
}

10
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccDataTagDataEntry.cs

@ -89,16 +89,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
return obj is IccDataTagDataEntry other && this.Equals(other);
}

12
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccDateTimeTagDataEntry.cs

@ -60,17 +60,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
return obj is IccDateTimeTagDataEntry && this.Equals((IccDateTimeTagDataEntry)obj);
return obj is IccDateTimeTagDataEntry other && this.Equals(other);
}
/// <inheritdoc/>

10
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccFix16ArrayTagDataEntry.cs

@ -62,16 +62,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
return obj is IccFix16ArrayTagDataEntry other && this.Equals(other);
}

12
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut16TagDataEntry.cs

@ -137,17 +137,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
return obj is IccLut16TagDataEntry && this.Equals((IccLut16TagDataEntry)obj);
return obj is IccLut16TagDataEntry other && this.Equals(other);
}
/// <inheritdoc/>

12
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut8TagDataEntry.cs

@ -140,17 +140,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
return obj is IccLut8TagDataEntry && this.Equals((IccLut8TagDataEntry)obj);
return obj is IccLut8TagDataEntry other && this.Equals(other);
}
/// <inheritdoc/>

10
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutAToBTagDataEntry.cs

@ -177,16 +177,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
return obj is IccLutAToBTagDataEntry other && this.Equals(other);
}

10
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutBToATagDataEntry.cs

@ -177,16 +177,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
return obj is IccLutBToATagDataEntry other && this.Equals(other);
}

12
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMeasurementTagDataEntry.cs

@ -100,17 +100,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
return obj is IccMeasurementTagDataEntry && this.Equals((IccMeasurementTagDataEntry)obj);
return obj is IccMeasurementTagDataEntry other && this.Equals(other);
}
/// <inheritdoc/>

12
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiLocalizedUnicodeTagDataEntry.cs

@ -63,17 +63,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
return obj is IccMultiLocalizedUnicodeTagDataEntry && this.Equals((IccMultiLocalizedUnicodeTagDataEntry)obj);
return obj is IccMultiLocalizedUnicodeTagDataEntry other && this.Equals(other);
}
/// <inheritdoc />

10
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiProcessElementsTagDataEntry.cs

@ -84,16 +84,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
return obj is IccMultiProcessElementsTagDataEntry other && this.Equals(other);
}

12
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccNamedColor2TagDataEntry.cs

@ -127,7 +127,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc/>
public bool Equals(IccNamedColor2TagDataEntry other)
{
if (ReferenceEquals(null, other))
if (other is null)
{
return false;
}
@ -148,16 +148,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
return obj is IccNamedColor2TagDataEntry other && this.Equals(other);
}

12
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccParametricCurveTagDataEntry.cs

@ -45,7 +45,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc/>
public bool Equals(IccParametricCurveTagDataEntry other)
{
if (ReferenceEquals(null, other))
if (other is null)
{
return false;
}
@ -61,16 +61,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
return obj is IccParametricCurveTagDataEntry other && this.Equals(other);
}

12
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccProfileSequenceDescTagDataEntry.cs

@ -48,7 +48,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc />
public bool Equals(IccProfileSequenceDescTagDataEntry other)
{
if (ReferenceEquals(null, other))
if (other is null)
{
return false;
}
@ -64,16 +64,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
return obj is IccProfileSequenceDescTagDataEntry other && this.Equals(other);
}

12
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccProfileSequenceIdentifierTagDataEntry.cs

@ -47,7 +47,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc />
public bool Equals(IccProfileSequenceIdentifierTagDataEntry other)
{
if (ReferenceEquals(null, other))
if (other is null)
{
return false;
}
@ -63,16 +63,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc />
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
return obj is IccProfileSequenceIdentifierTagDataEntry other && this.Equals(other);
}

14
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccResponseCurveSet16TagDataEntry.cs

@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc />
public bool Equals(IccResponseCurveSet16TagDataEntry other)
{
if (ReferenceEquals(null, other))
if (other is null)
{
return false;
}
@ -77,17 +77,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc />
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
return obj is IccResponseCurveSet16TagDataEntry && this.Equals((IccResponseCurveSet16TagDataEntry)obj);
return obj is IccResponseCurveSet16TagDataEntry other && this.Equals(other);
}
/// <inheritdoc />

12
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccScreeningTagDataEntry.cs

@ -56,7 +56,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc />
public bool Equals(IccScreeningTagDataEntry other)
{
if (ReferenceEquals(null, other))
if (other is null)
{
return false;
}
@ -74,16 +74,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc />
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
return obj is IccScreeningTagDataEntry other && this.Equals(other);
}

12
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccSignatureTagDataEntry.cs

@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc />
public bool Equals(IccSignatureTagDataEntry other)
{
if (ReferenceEquals(null, other))
if (other is null)
{
return false;
}
@ -62,16 +62,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc />
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
return obj is IccSignatureTagDataEntry other && this.Equals(other);
}

14
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccTextDescriptionTagDataEntry.cs

@ -139,7 +139,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc />
public bool Equals(IccTextDescriptionTagDataEntry other)
{
if (ReferenceEquals(null, other))
if (other is null)
{
return false;
}
@ -160,17 +160,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc />
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
return obj is IccTextDescriptionTagDataEntry && this.Equals((IccTextDescriptionTagDataEntry)obj);
return obj is IccTextDescriptionTagDataEntry other && this.Equals(other);
}
/// <inheritdoc />

12
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccTextTagDataEntry.cs

@ -45,7 +45,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc />
public bool Equals(IccTextTagDataEntry other)
{
if (ReferenceEquals(null, other))
if (other is null)
{
return false;
}
@ -61,16 +61,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc />
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
return obj is IccTextTagDataEntry other && this.Equals(other);
}

12
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUFix16ArrayTagDataEntry.cs

@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc/>
public bool Equals(IccUFix16ArrayTagDataEntry other)
{
if (ReferenceEquals(null, other))
if (other is null)
{
return false;
}
@ -62,16 +62,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
return obj is IccUFix16ArrayTagDataEntry other && this.Equals(other);
}

12
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt16ArrayTagDataEntry.cs

@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc/>
public bool Equals(IccUInt16ArrayTagDataEntry other)
{
if (ReferenceEquals(null, other))
if (other is null)
{
return false;
}
@ -62,16 +62,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
return obj is IccUInt16ArrayTagDataEntry other && this.Equals(other);
}

12
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt32ArrayTagDataEntry.cs

@ -62,17 +62,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
return obj is IccUInt32ArrayTagDataEntry && this.Equals((IccUInt32ArrayTagDataEntry)obj);
return obj is IccUInt32ArrayTagDataEntry other && this.Equals(other);
}
/// <inheritdoc/>

12
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt64ArrayTagDataEntry.cs

@ -62,17 +62,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
return obj is IccUInt64ArrayTagDataEntry && this.Equals((IccUInt64ArrayTagDataEntry)obj);
return obj is IccUInt64ArrayTagDataEntry other && this.Equals(other);
}
/// <inheritdoc/>

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save