Browse Source

Merge branch 'master' into js/projective-transforms

af/merge-core
James Jackson-South 8 years ago
parent
commit
a07dd8f6f2
  1. 11
      README.md
  2. 1
      src/ImageSharp.Drawing/ImageSharp.Drawing.csproj
  3. 2
      src/ImageSharp/ColorSpaces/CieLch.cs
  4. 2
      src/ImageSharp/ColorSpaces/CieLchuv.cs
  5. 2
      src/ImageSharp/ColorSpaces/CieLuv.cs
  6. 2
      src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs
  7. 2
      src/ImageSharp/ColorSpaces/CieXyy.cs
  8. 2
      src/ImageSharp/ColorSpaces/CieXyz.cs
  9. 2
      src/ImageSharp/ColorSpaces/Cmyk.cs
  10. 21
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs
  11. 24
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs
  12. 26
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs
  13. 26
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs
  14. 27
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs
  15. 27
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs
  16. 26
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs
  17. 24
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs
  18. 27
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs
  19. 24
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs
  20. 27
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs
  21. 1
      src/ImageSharp/ColorSpaces/Conversion/Implementation/CieXyy/CieXyzAndCieXyyConverter.cs
  22. 1
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Cmyk/CmykAndRgbConverter.cs
  23. 1
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Hsl/HslAndRgbConverter.cs
  24. 1
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Hsv/HsvAndRgbConverter.cs
  25. 2
      src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/CieXyzAndHunterLabConverterBase.cs
  26. 1
      src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/CieXyzToHunterLabConverter.cs
  27. 3
      src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/HunterLabToCieXyzConverter.cs
  28. 5
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Lms/CieXyzAndLmsConverter.cs
  29. 2
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LCompanding.cs
  30. 5
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbAndCieXyzConverterBase.cs
  31. 2
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbToLinearRgbConverter.cs
  32. 1
      src/ImageSharp/ColorSpaces/Conversion/Implementation/YCbCr/YCbCrAndRgbConverter.cs
  33. 5
      src/ImageSharp/ColorSpaces/Conversion/VonKriesChromaticAdaptation.cs
  34. 2
      src/ImageSharp/ColorSpaces/Hsl.cs
  35. 2
      src/ImageSharp/ColorSpaces/Hsv.cs
  36. 2
      src/ImageSharp/ColorSpaces/HunterLab.cs
  37. 2
      src/ImageSharp/ColorSpaces/LinearRgb.cs
  38. 2
      src/ImageSharp/ColorSpaces/Lms.cs
  39. 2
      src/ImageSharp/ColorSpaces/Rgb.cs
  40. 2
      src/ImageSharp/ColorSpaces/YCbCr.cs
  41. 7
      src/ImageSharp/Common/Exceptions/ImageFormatException.cs
  42. 9
      src/ImageSharp/Common/Exceptions/ImageProcessingException.cs
  43. 23
      src/ImageSharp/Common/Helpers/DebugGuard.cs
  44. 2
      src/ImageSharp/Common/Helpers/ImageMaths.cs
  45. 2
      src/ImageSharp/Formats/Bmp/BmpCompression.cs
  46. 256
      src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
  47. 100
      src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
  48. 19
      src/ImageSharp/Formats/Bmp/BmpFileHeader.cs
  49. 76
      src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs
  50. 7
      src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs
  51. 5
      src/ImageSharp/Formats/Bmp/IBmpEncoderOptions.cs
  52. 2
      src/ImageSharp/Formats/Bmp/ImageExtensions.cs
  53. 13
      src/ImageSharp/Formats/Gif/DisposalMethod.cs
  54. 2
      src/ImageSharp/Formats/Gif/GifConstants.cs
  55. 114
      src/ImageSharp/Formats/Gif/GifDecoderCore.cs
  56. 258
      src/ImageSharp/Formats/Gif/GifEncoderCore.cs
  57. 8
      src/ImageSharp/Formats/Gif/GifImageFormatDetector.cs
  58. 2
      src/ImageSharp/Formats/Gif/IGifDecoderOptions.cs
  59. 6
      src/ImageSharp/Formats/Gif/IGifEncoderOptions.cs
  60. 6
      src/ImageSharp/Formats/Gif/ImageExtensions.cs
  61. 80
      src/ImageSharp/Formats/Gif/LzwDecoder.cs
  62. 180
      src/ImageSharp/Formats/Gif/LzwEncoder.cs
  63. 193
      src/ImageSharp/Formats/Gif/PackedField.cs
  64. 106
      src/ImageSharp/Formats/Gif/Sections/GifGraphicControlExtension.cs
  65. 40
      src/ImageSharp/Formats/Gif/Sections/GifGraphicsControlExtension.cs
  66. 107
      src/ImageSharp/Formats/Gif/Sections/GifImageDescriptor.cs
  67. 113
      src/ImageSharp/Formats/Gif/Sections/GifLogicalScreenDescriptor.cs
  68. 22
      src/ImageSharp/Formats/Gif/Sections/IGifExtension.cs
  69. 4
      src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs
  70. 6
      src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs
  71. 16
      src/ImageSharp/Formats/Jpeg/Common/Decoder/ComponentUtils.cs
  72. 8
      src/ImageSharp/Formats/Jpeg/Common/Decoder/IJpegComponent.cs
  73. 6
      src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegBlockPostProcessor.cs
  74. 23
      src/ImageSharp/Formats/Jpeg/Common/Decoder/ProfileResolver.cs
  75. 48
      src/ImageSharp/Formats/Jpeg/Common/ZigZag.cs
  76. 7
      src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigComponent.cs
  77. 4
      src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.DataPointers.cs
  78. 2
      src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs
  79. 2
      src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoder.cs
  80. 7
      src/ImageSharp/Formats/Jpeg/IJpegDecoderOptions.cs
  81. 7
      src/ImageSharp/Formats/Jpeg/IJpegEncoderOptions.cs
  82. 2
      src/ImageSharp/Formats/Jpeg/ImageExtensions.cs
  83. 2
      src/ImageSharp/Formats/Jpeg/JpegConfigurationModule.cs
  84. 55
      src/ImageSharp/Formats/Jpeg/JpegImageFormatDetector.cs
  85. 24
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedByteBuffer256.cs
  86. 24
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedInt16Buffer18.cs
  87. 24
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedInt16Buffer256.cs
  88. 24
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedInt64Buffer18.cs
  89. 43
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsComponent.cs
  90. 32
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsComponentBlocks.cs
  91. 71
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrameComponent.cs
  92. 204
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs
  93. 19
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTables.cs
  94. 513
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsIDCT.cs
  95. 149
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsJpegPixelArea.cs
  96. 67
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsQuantizationTables.cs
  97. 396
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsScanDecoder.cs
  98. 131
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsYCbCrToRgbTables.cs
  99. 138
      src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegConstants.cs
  100. 13
      src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoder.cs

11
README.md

@ -24,6 +24,10 @@ Compared to `System.Drawing` we have been able to develop something much more fl
Built against .Net Standard 1.1 ImageSharp can be used in device, cloud, and embedded/IoT scenarios.
### Documentation
For all SixLabors projects, including ImageSharp:
https://sixlabors.github.io/docs/
### Installation
Install stable releases via Nuget; development releases are available via MyGet.
@ -61,8 +65,6 @@ The **ImageSharp** library is made up of multiple packages:
### API
API documentation is available at [https://sixlabors.github.io/docs/](https://sixlabors.github.io/docs/)
Our API is designed to be simple to consume. Here's an example of the code required to resize an image using the default Bicubic resampler then turn the colors into their grayscale equivalent using the BT709 standard matrix.
On platforms supporting netstandard 1.3+
@ -106,7 +108,10 @@ using (Image<Rgba32> image = new Image<Rgba32>(400, 400))
`Rgba32` is our default PixelFormat, equivalent to `System.Drawing Color`. For advanced pixel format usage there are multiple [PixelFormat implementations](https://github.com/SixLabors/ImageSharp/tree/master/src/ImageSharp/PixelFormats) available allowing developers to implement their own color models in the same manner as Microsoft XNA Game Studio and MonoGame.
**Check out this [blog post](https://sixlabors.com/blog/announcing-imagesharp-beta-1/) or our [Samples Repository](https://github.com/SixLabors/Samples/tree/master/ImageSharp) for more examples!**
For more examples check out:
- [Our Documentation](https://sixlabors.github.io/docs/)
- Our [Samples Repository](https://github.com/SixLabors/Samples/tree/master/ImageSharp)
- The [beta1 blog post](https://sixlabors.com/blog/announcing-imagesharp-beta-1/)
### Manual build

1
src/ImageSharp.Drawing/ImageSharp.Drawing.csproj

@ -6,6 +6,7 @@
<VersionPrefix Condition="$(packageversion) == ''">0.0.1</VersionPrefix>
<Authors>SixLabors and contributors</Authors>
<TargetFrameworks>netstandard1.1;netstandard2.0</TargetFrameworks>
<LangVersion>7.2</LangVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<AssemblyName>SixLabors.ImageSharp.Drawing</AssemblyName>

2
src/ImageSharp/ColorSpaces/CieLch.cs

@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <summary>
/// Represents a <see cref="CieLch"/> that has L, C, H values set to zero.
/// </summary>
public static readonly CieLch Empty = default(CieLch);
public static readonly CieLch Empty = default;
/// <summary>
/// The backing vector for SIMD support.

2
src/ImageSharp/ColorSpaces/CieLchuv.cs

@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <summary>
/// Represents a <see cref="CieLchuv"/> that has L, C, H values set to zero.
/// </summary>
public static readonly CieLchuv Empty = default(CieLchuv);
public static readonly CieLchuv Empty = default;
/// <summary>
/// The backing vector for SIMD support.

2
src/ImageSharp/ColorSpaces/CieLuv.cs

@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <summary>
/// Represents a <see cref="CieLuv"/> that has L, U, and V values set to zero.
/// </summary>
public static readonly CieLuv Empty = default(CieLuv);
public static readonly CieLuv Empty = default;
/// <summary>
/// The backing vector for SIMD support.

2
src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs

@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <summary>
/// Represents a <see cref="CieXyChromaticityCoordinates"/> that has X, Y values set to zero.
/// </summary>
public static readonly CieXyChromaticityCoordinates Empty = default(CieXyChromaticityCoordinates);
public static readonly CieXyChromaticityCoordinates Empty = default;
/// <summary>
/// The backing vector for SIMD support.

2
src/ImageSharp/ColorSpaces/CieXyy.cs

@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <summary>
/// Represents a <see cref="CieXyy"/> that has X, Y, and Y values set to zero.
/// </summary>
public static readonly CieXyy Empty = default(CieXyy);
public static readonly CieXyy Empty = default;
/// <summary>
/// The backing vector for SIMD support.

2
src/ImageSharp/ColorSpaces/CieXyz.cs

@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <summary>
/// Represents a <see cref="CieXyz"/> that has X, Y, and Z values set to zero.
/// </summary>
public static readonly CieXyz Empty = default(CieXyz);
public static readonly CieXyz Empty = default;
/// <summary>
/// The backing vector for SIMD support.

2
src/ImageSharp/ColorSpaces/Cmyk.cs

@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <summary>
/// Represents a <see cref="Cmyk"/> that has C, M, Y, and K values set to zero.
/// </summary>
public static readonly Cmyk Empty = default(Cmyk);
public static readonly Cmyk Empty = default;
/// <summary>
/// The backing vector for SIMD support.

21
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.ColorSpaces;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLabColorSapce;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchColorSapce;
@ -45,8 +44,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(CieLchuv color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor);
}
@ -58,8 +55,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(CieLuv color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor);
}
@ -71,8 +66,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(CieXyy color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor);
}
@ -92,7 +85,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
: color;
// Conversion
CieXyzToCieLabConverter converter = new CieXyzToCieLabConverter(this.TargetLabWhitePoint);
var converter = new CieXyzToCieLabConverter(this.TargetLabWhitePoint);
return converter.Convert(adapted);
}
@ -116,8 +109,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(Hsl color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor);
}
@ -142,8 +133,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(HunterLab color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor);
}
@ -155,8 +144,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(Lms color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor);
}
@ -168,8 +155,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(LinearRgb color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor);
}
@ -181,8 +166,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(Rgb color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor);
}
@ -194,8 +177,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(YCbCr color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor);
}

24
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs

@ -22,8 +22,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLch"/></returns>
public CieLch ToCieLch(CieLab color)
{
Guard.NotNull(color, nameof(color));
// Adaptation
CieLab adapted = this.IsChromaticAdaptationPerformed ? this.Adapt(color) : color;
@ -38,8 +36,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLch"/></returns>
public CieLch ToCieLch(CieLchuv color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLch(xyzColor);
}
@ -51,8 +47,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLch"/></returns>
public CieLch ToCieLch(CieLuv color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLch(xyzColor);
}
@ -64,8 +58,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLch"/></returns>
public CieLch ToCieLch(CieXyy color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLch(xyzColor);
}
@ -77,8 +69,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLch"/></returns>
public CieLch ToCieLch(CieXyz color)
{
Guard.NotNull(color, nameof(color));
CieLab labColor = this.ToCieLab(color);
return this.ToCieLch(labColor);
}
@ -103,8 +93,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLch"/></returns>
public CieLch ToCieLch(Hsl color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLch(xyzColor);
}
@ -116,8 +104,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLch"/></returns>
public CieLch ToCieLch(Hsv color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLch(xyzColor);
}
@ -129,8 +115,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLch"/></returns>
public CieLch ToCieLch(HunterLab color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLch(xyzColor);
}
@ -142,8 +126,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLch"/></returns>
public CieLch ToCieLch(LinearRgb color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLch(xyzColor);
}
@ -155,8 +137,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLch"/></returns>
public CieLch ToCieLch(Lms color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLch(xyzColor);
}
@ -168,8 +148,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLch"/></returns>
public CieLch ToCieLch(Rgb color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLch(xyzColor);
}
@ -181,8 +159,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLch"/></returns>
public CieLch ToCieLch(YCbCr color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLch(xyzColor);
}

26
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs

@ -22,8 +22,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(CieLab color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLchuv(xyzColor);
}
@ -35,8 +33,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(CieLch color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLchuv(xyzColor);
}
@ -48,8 +44,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(CieLuv color)
{
Guard.NotNull(color, nameof(color));
// Adaptation
CieLuv adapted = this.IsChromaticAdaptationPerformed ? this.Adapt(color) : color;
@ -64,8 +58,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(CieXyy color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLchuv(xyzColor);
}
@ -77,8 +69,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(CieXyz color)
{
Guard.NotNull(color, nameof(color));
CieLab labColor = this.ToCieLab(color);
return this.ToCieLchuv(labColor);
}
@ -90,8 +80,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(Cmyk color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLchuv(xyzColor);
}
@ -103,8 +91,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(Hsl color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLchuv(xyzColor);
}
@ -116,8 +102,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(Hsv color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLchuv(xyzColor);
}
@ -129,8 +113,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(HunterLab color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLchuv(xyzColor);
}
@ -142,8 +124,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(LinearRgb color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLchuv(xyzColor);
}
@ -155,8 +135,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(Lms color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLchuv(xyzColor);
}
@ -168,8 +146,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(Rgb color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLchuv(xyzColor);
}
@ -181,8 +157,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(YCbCr color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLchuv(xyzColor);
}

26
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs

@ -21,8 +21,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLuv"/></returns>
public CieLuv ToCieLuv(CieLab color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToCieLuv(xyzColor);
}
@ -34,8 +32,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLuv"/></returns>
public CieLuv ToCieLuv(CieLch color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToCieLuv(xyzColor);
}
@ -47,8 +43,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns>
public CieLuv ToCieLuv(CieLchuv color)
{
Guard.NotNull(color, nameof(color));
// Conversion (perserving white point)
CieLuv unadapted = CieLchuvToCieLuvConverter.Convert(color);
@ -68,8 +62,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLuv"/></returns>
public CieLuv ToCieLuv(CieXyy color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToCieLuv(xyzColor);
}
@ -81,8 +73,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLuv"/></returns>
public CieLuv ToCieLuv(CieXyz color)
{
Guard.NotNull(color, nameof(color));
// Adaptation
CieXyz adapted = !this.WhitePoint.Equals(this.TargetLabWhitePoint) && this.IsChromaticAdaptationPerformed
? this.ChromaticAdaptation.Transform(color, this.WhitePoint, this.TargetLabWhitePoint)
@ -100,8 +90,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLuv"/></returns>
public CieLuv ToCieLuv(Cmyk color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToCieLuv(xyzColor);
}
@ -113,8 +101,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLuv"/></returns>
public CieLuv ToCieLuv(Hsl color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToCieLuv(xyzColor);
}
@ -126,8 +112,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLuv"/></returns>
public CieLuv ToCieLuv(Hsv color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToCieLuv(xyzColor);
}
@ -139,8 +123,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLuv"/></returns>
public CieLuv ToCieLuv(HunterLab color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToCieLuv(xyzColor);
}
@ -152,8 +134,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLuv"/></returns>
public CieLuv ToCieLuv(Lms color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToCieLuv(xyzColor);
}
@ -165,8 +145,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLuv"/></returns>
public CieLuv ToCieLuv(LinearRgb color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToCieLuv(xyzColor);
}
@ -178,8 +156,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLuv"/></returns>
public CieLuv ToCieLuv(Rgb color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToCieLuv(xyzColor);
}
@ -191,8 +167,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLuv"/></returns>
public CieLuv ToCieLuv(YCbCr color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToCieLuv(xyzColor);
}

27
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.ColorSpaces;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CmykColorSapce;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion
@ -20,8 +19,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Cmyk"/></returns>
public Cmyk ToCmyk(CieLab color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToCmyk(xyzColor);
@ -34,8 +31,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Cmyk"/></returns>
public Cmyk ToCmyk(CieLch color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToCmyk(xyzColor);
@ -48,8 +43,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Cmyk"/></returns>
public Cmyk ToCmyk(CieLchuv color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToCmyk(xyzColor);
@ -62,8 +55,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Cmyk"/></returns>
public Cmyk ToCmyk(CieLuv color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToCmyk(xyzColor);
@ -76,8 +67,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Cmyk"/></returns>
public Cmyk ToCmyk(CieXyy color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToCmyk(xyzColor);
@ -90,8 +79,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Cmyk"/></returns>
public Cmyk ToCmyk(CieXyz color)
{
Guard.NotNull(color, nameof(color));
var rgb = this.ToRgb(color);
return CmykAndRgbConverter.Convert(rgb);
@ -104,8 +91,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Cmyk"/></returns>
public Cmyk ToCmyk(Hsl color)
{
Guard.NotNull(color, nameof(color));
var rgb = this.ToRgb(color);
return CmykAndRgbConverter.Convert(rgb);
@ -118,8 +103,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Cmyk"/></returns>
public Cmyk ToCmyk(Hsv color)
{
Guard.NotNull(color, nameof(color));
var rgb = this.ToRgb(color);
return CmykAndRgbConverter.Convert(rgb);
@ -132,8 +115,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Cmyk"/></returns>
public Cmyk ToCmyk(HunterLab color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToCmyk(xyzColor);
@ -146,8 +127,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Cmyk"/></returns>
public Cmyk ToCmyk(LinearRgb color)
{
Guard.NotNull(color, nameof(color));
var rgb = this.ToRgb(color);
return CmykAndRgbConverter.Convert(rgb);
@ -160,8 +139,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Cmyk"/></returns>
public Cmyk ToCmyk(Lms color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToCmyk(xyzColor);
@ -174,8 +151,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Cmyk"/></returns>
public Cmyk ToCmyk(Rgb color)
{
Guard.NotNull(color, nameof(color));
return CmykAndRgbConverter.Convert(color);
}
@ -186,8 +161,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Cmyk"/></returns>
public Cmyk ToCmyk(YCbCr color)
{
Guard.NotNull(color, nameof(color));
var rgb = this.ToRgb(color);
return CmykAndRgbConverter.Convert(rgb);

27
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.ColorSpaces;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HslColorSapce;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion
@ -20,8 +19,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Hsl"/></returns>
public Hsl ToHsl(CieLab color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToHsl(xyzColor);
@ -34,8 +31,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Hsl"/></returns>
public Hsl ToHsl(CieLch color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToHsl(xyzColor);
@ -48,8 +43,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Hsl"/></returns>
public Hsl ToHsl(CieLchuv color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToHsl(xyzColor);
@ -62,8 +55,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Hsl"/></returns>
public Hsl ToHsl(CieLuv color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToHsl(xyzColor);
@ -76,8 +67,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Hsl"/></returns>
public Hsl ToHsl(CieXyy color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToHsl(xyzColor);
@ -90,8 +79,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Hsl"/></returns>
public Hsl ToHsl(CieXyz color)
{
Guard.NotNull(color, nameof(color));
var rgb = this.ToRgb(color);
return HslAndRgbConverter.Convert(rgb);
@ -104,8 +91,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Hsl"/></returns>
public Hsl ToHsl(Cmyk color)
{
Guard.NotNull(color, nameof(color));
var rgb = this.ToRgb(color);
return HslAndRgbConverter.Convert(rgb);
@ -118,8 +103,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Hsl"/></returns>
public Hsl ToHsl(Hsv color)
{
Guard.NotNull(color, nameof(color));
var rgb = this.ToRgb(color);
return HslAndRgbConverter.Convert(rgb);
@ -132,8 +115,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Hsl"/></returns>
public Hsl ToHsl(HunterLab color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToHsl(xyzColor);
@ -146,8 +127,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Hsl"/></returns>
public Hsl ToHsl(LinearRgb color)
{
Guard.NotNull(color, nameof(color));
var rgb = this.ToRgb(color);
return HslAndRgbConverter.Convert(rgb);
@ -160,8 +139,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Hsl"/></returns>
public Hsl ToHsl(Lms color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToHsl(xyzColor);
@ -174,8 +151,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Hsl"/></returns>
public Hsl ToHsl(Rgb color)
{
Guard.NotNull(color, nameof(color));
return HslAndRgbConverter.Convert(color);
}
@ -186,8 +161,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Hsl"/></returns>
public Hsl ToHsl(YCbCr color)
{
Guard.NotNull(color, nameof(color));
var rgb = this.ToRgb(color);
return HslAndRgbConverter.Convert(rgb);

26
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs

@ -17,8 +17,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="HunterLab"/></returns>
public HunterLab ToHunterLab(CieLab color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToHunterLab(xyzColor);
}
@ -30,8 +28,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="HunterLab"/></returns>
public HunterLab ToHunterLab(CieLch color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToHunterLab(xyzColor);
}
@ -43,8 +39,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="HunterLab"/></returns>
public HunterLab ToHunterLab(CieLchuv color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToHunterLab(xyzColor);
}
@ -56,8 +50,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="HunterLab"/></returns>
public HunterLab ToHunterLab(CieLuv color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToHunterLab(xyzColor);
}
@ -69,8 +61,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="HunterLab"/></returns>
public HunterLab ToHunterLab(CieXyy color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToHunterLab(xyzColor);
}
@ -82,8 +72,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="HunterLab"/></returns>
public HunterLab ToHunterLab(CieXyz color)
{
Guard.NotNull(color, nameof(color));
// Adaptation
CieXyz adapted = !this.WhitePoint.Equals(this.TargetHunterLabWhitePoint) && this.IsChromaticAdaptationPerformed
? this.ChromaticAdaptation.Transform(color, this.WhitePoint, this.TargetHunterLabWhitePoint)
@ -100,8 +88,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="HunterLab"/></returns>
public HunterLab ToHunterLab(Cmyk color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToHunterLab(xyzColor);
}
@ -113,8 +99,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="HunterLab"/></returns>
public HunterLab ToHunterLab(Hsl color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToHunterLab(xyzColor);
}
@ -126,8 +110,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="HunterLab"/></returns>
public HunterLab ToHunterLab(Hsv color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToHunterLab(xyzColor);
}
@ -139,8 +121,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="HunterLab"/></returns>
public HunterLab ToHunterLab(LinearRgb color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToHunterLab(xyzColor);
}
@ -152,8 +132,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="HunterLab"/></returns>
public HunterLab ToHunterLab(Lms color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToHunterLab(xyzColor);
}
@ -165,8 +143,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="HunterLab"/></returns>
public HunterLab ToHunterLab(Rgb color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToHunterLab(xyzColor);
}
@ -178,8 +154,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="HunterLab"/></returns>
public HunterLab ToHunterLab(YCbCr color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToHunterLab(xyzColor);
}

24
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs

@ -21,8 +21,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(CieLab color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToLinearRgb(xyzColor);
}
@ -34,8 +32,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(CieLch color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToLinearRgb(xyzColor);
}
@ -47,8 +43,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(CieLchuv color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToLinearRgb(xyzColor);
}
@ -60,8 +54,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(CieLuv color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToLinearRgb(xyzColor);
}
@ -73,8 +65,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(CieXyy color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToLinearRgb(xyzColor);
}
@ -86,8 +76,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(CieXyz color)
{
Guard.NotNull(color, nameof(color));
// Adaptation
CieXyz adapted = this.TargetRgbWorkingSpace.WhitePoint.Equals(this.WhitePoint) || !this.IsChromaticAdaptationPerformed
? color
@ -105,8 +93,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(Cmyk color)
{
Guard.NotNull(color, nameof(color));
var rgb = this.ToRgb(color);
return this.ToLinearRgb(rgb);
}
@ -118,8 +104,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(Hsl color)
{
Guard.NotNull(color, nameof(color));
var rgb = this.ToRgb(color);
return this.ToLinearRgb(rgb);
}
@ -144,8 +128,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(HunterLab color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToLinearRgb(xyzColor);
}
@ -157,8 +139,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(Lms color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToLinearRgb(xyzColor);
}
@ -170,8 +150,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(Rgb color)
{
Guard.NotNull(color, nameof(color));
// Conversion
return RgbToLinearRgbConverter.Convert(color);
}
@ -183,8 +161,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(YCbCr color)
{
Guard.NotNull(color, nameof(color));
var rgb = this.ToRgb(color);
return this.ToLinearRgb(rgb);
}

27
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs

@ -17,8 +17,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Lms"/></returns>
public Lms ToLms(CieLab color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToLms(xyzColor);
}
@ -30,8 +28,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Lms"/></returns>
public Lms ToLms(CieLch color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToLms(xyzColor);
}
@ -43,8 +39,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Lms"/></returns>
public Lms ToLms(CieLchuv color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToLms(xyzColor);
}
@ -56,8 +50,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Lms"/></returns>
public Lms ToLms(CieLuv color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToLms(xyzColor);
}
@ -69,8 +61,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Lms"/></returns>
public Lms ToLms(CieXyy color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToLms(xyzColor);
}
@ -82,9 +72,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Lms"/></returns>
public Lms ToLms(CieXyz color)
{
Guard.NotNull(color, nameof(color));
// Conversion
return this.cachedCieXyzAndLmsConverter.Convert(color);
}
@ -95,8 +82,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Lms"/></returns>
public Lms ToLms(Cmyk color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToLms(xyzColor);
}
@ -108,8 +93,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Lms"/></returns>
public Lms ToLms(Hsl color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToLms(xyzColor);
}
@ -121,8 +104,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Lms"/></returns>
public Lms ToLms(Hsv color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToLms(xyzColor);
}
@ -134,8 +115,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Lms"/></returns>
public Lms ToLms(HunterLab color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToLms(xyzColor);
}
@ -147,8 +126,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Lms"/></returns>
public Lms ToLms(LinearRgb color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToLms(xyzColor);
}
@ -160,8 +137,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Lms"/></returns>
public Lms ToLms(Rgb color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToLms(xyzColor);
}
@ -173,8 +148,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Lms"/></returns>
public Lms ToLms(YCbCr color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToLms(xyzColor);
}

24
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs

@ -19,8 +19,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(CieLab color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToRgb(xyzColor);
}
@ -32,8 +30,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(CieLch color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToRgb(xyzColor);
}
@ -45,8 +41,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(CieLchuv color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToRgb(xyzColor);
}
@ -58,8 +52,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(CieLuv color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToRgb(xyzColor);
}
@ -71,8 +63,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(CieXyy color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToRgb(xyzColor);
}
@ -84,8 +74,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(CieXyz color)
{
Guard.NotNull(color, nameof(color));
// Conversion
var linear = this.ToLinearRgb(color);
@ -100,8 +88,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(Cmyk color)
{
Guard.NotNull(color, nameof(color));
// Conversion
return CmykAndRgbConverter.Convert(color);
}
@ -113,8 +99,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(Hsv color)
{
Guard.NotNull(color, nameof(color));
// Conversion
return HsvAndRgbConverter.Convert(color);
}
@ -139,8 +123,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(HunterLab color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToRgb(xyzColor);
}
@ -152,8 +134,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(LinearRgb color)
{
Guard.NotNull(color, nameof(color));
// Conversion
return LinearRgbToRgbConverter.Convert(color);
}
@ -165,8 +145,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(Lms color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToRgb(xyzColor);
}
@ -178,8 +156,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(YCbCr color)
{
Guard.NotNull(color, nameof(color));
// Conversion
Rgb rgb = YCbCrAndRgbConverter.Convert(color);

27
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.ColorSpaces;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.YCbCrColorSapce;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion
@ -20,8 +19,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(CieLab color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToYCbCr(xyzColor);
@ -34,8 +31,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(CieLch color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToYCbCr(xyzColor);
@ -48,8 +43,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(CieLchuv color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToYCbCr(xyzColor);
@ -62,8 +55,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(CieLuv color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToYCbCr(xyzColor);
@ -76,8 +67,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(CieXyy color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToYCbCr(xyzColor);
@ -90,8 +79,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(CieXyz color)
{
Guard.NotNull(color, nameof(color));
var rgb = this.ToRgb(color);
return YCbCrAndRgbConverter.Convert(rgb);
@ -104,8 +91,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(Cmyk color)
{
Guard.NotNull(color, nameof(color));
var rgb = this.ToRgb(color);
return YCbCrAndRgbConverter.Convert(rgb);
@ -118,8 +103,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(Hsl color)
{
Guard.NotNull(color, nameof(color));
var rgb = this.ToRgb(color);
return YCbCrAndRgbConverter.Convert(rgb);
@ -132,8 +115,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(Hsv color)
{
Guard.NotNull(color, nameof(color));
var rgb = this.ToRgb(color);
return YCbCrAndRgbConverter.Convert(rgb);
@ -146,8 +127,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(HunterLab color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToYCbCr(xyzColor);
@ -160,8 +139,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(LinearRgb color)
{
Guard.NotNull(color, nameof(color));
var rgb = this.ToRgb(color);
return YCbCrAndRgbConverter.Convert(rgb);
@ -174,8 +151,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(Lms color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToYCbCr(xyzColor);
@ -188,8 +163,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(Rgb color)
{
Guard.NotNull(color, nameof(color));
return YCbCrAndRgbConverter.Convert(color);
}
}

1
src/ImageSharp/ColorSpaces/Conversion/Implementation/CieXyy/CieXyzAndCieXyyConverter.cs

@ -3,7 +3,6 @@
using System;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieXyyColorSapce
{

1
src/ImageSharp/ColorSpaces/Conversion/Implementation/Cmyk/CmykAndRgbConverter.cs

@ -3,7 +3,6 @@
using System;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CmykColorSapce
{

1
src/ImageSharp/ColorSpaces/Conversion/Implementation/Hsl/HslAndRgbConverter.cs

@ -3,7 +3,6 @@
using System;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HslColorSapce
{

1
src/ImageSharp/ColorSpaces/Conversion/Implementation/Hsv/HsvAndRgbConverter.cs

@ -3,7 +3,6 @@
using System;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HsvColorSapce
{

2
src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/CieXyzAndHunterLabConverterBase.cs

@ -18,8 +18,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabCo
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float ComputeKa(CieXyz whitePoint)
{
DebugGuard.NotNull(whitePoint, nameof(whitePoint));
if (whitePoint.Equals(Illuminants.C))
{
return 175F;

1
src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/CieXyzToHunterLabConverter.cs

@ -3,7 +3,6 @@
using System;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabColorSapce
{

3
src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/HunterLabToCieXyzConverter.cs

@ -3,7 +3,6 @@
using System;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabColorSapce
{
@ -16,8 +15,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabCo
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieXyz Convert(HunterLab input)
{
DebugGuard.NotNull(input, nameof(input));
// Conversion algorithm described here: http://en.wikipedia.org/wiki/Lab_color_space#Hunter_Lab
float l = input.L, a = input.A, b = input.B;
float xn = input.WhitePoint.X, yn = input.WhitePoint.Y, zn = input.WhitePoint.Z;

5
src/ImageSharp/ColorSpaces/Conversion/Implementation/Lms/CieXyzAndLmsConverter.cs

@ -3,7 +3,6 @@
using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSapce
{
@ -63,8 +62,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSap
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Lms Convert(CieXyz input)
{
DebugGuard.NotNull(input, nameof(input));
Vector3 vector = Vector3.Transform(input.Vector, this.transformationMatrix);
return new Lms(vector);
}
@ -73,8 +70,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSap
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieXyz Convert(Lms input)
{
DebugGuard.NotNull(input, nameof(input));
Vector3 vector = Vector3.Transform(input.Vector, this.inverseTransformationMatrix);
return new CieXyz(vector);
}

2
src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LCompanding.cs

@ -32,4 +32,4 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap
: MathF.Pow(1.16F * channel, 0.3333333F) - 0.16F;
}
}
}
}

5
src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbAndCieXyzConverterBase.cs

@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap
const float Yb = 1;
float mZb = (1 - xb - yb) / yb;
Matrix4x4 xyzMatrix = new Matrix4x4
var xyzMatrix = new Matrix4x4
{
M11 = mXr, M21 = mXg, M31 = mXb,
M12 = Yr, M22 = Yg, M32 = Yb,
@ -48,8 +48,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap
M44 = 1F
};
Matrix4x4 inverseXyzMatrix;
Matrix4x4.Invert(xyzMatrix, out inverseXyzMatrix);
Matrix4x4.Invert(xyzMatrix, out Matrix4x4 inverseXyzMatrix);
Vector3 vector = Vector3.Transform(workingSpace.WhitePoint.Vector, inverseXyzMatrix);

2
src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbToLinearRgbConverter.cs

@ -13,8 +13,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap
/// <inheritdoc/>
public LinearRgb Convert(Rgb input)
{
Guard.NotNull(input, nameof(input));
Vector3 vector = input.Vector;
vector.X = input.WorkingSpace.Companding.Expand(vector.X);
vector.Y = input.WorkingSpace.Companding.Expand(vector.Y);

1
src/ImageSharp/ColorSpaces/Conversion/Implementation/YCbCr/YCbCrAndRgbConverter.cs

@ -4,7 +4,6 @@
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.YCbCrColorSapce
{

5
src/ImageSharp/ColorSpaces/Conversion/VonKriesChromaticAdaptation.cs

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.ColorSpaces;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSapce;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion
@ -50,10 +49,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <inheritdoc/>
public CieXyz Transform(CieXyz sourceColor, CieXyz sourceWhitePoint, CieXyz targetWhitePoint)
{
Guard.NotNull(sourceColor, nameof(sourceColor));
Guard.NotNull(sourceWhitePoint, nameof(sourceWhitePoint));
Guard.NotNull(targetWhitePoint, nameof(targetWhitePoint));
if (sourceWhitePoint.Equals(targetWhitePoint))
{
return sourceColor;

2
src/ImageSharp/ColorSpaces/Hsl.cs

@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <summary>
/// Represents a <see cref="Hsl"/> that has H, S, and L values set to zero.
/// </summary>
public static readonly Hsl Empty = default(Hsl);
public static readonly Hsl Empty = default;
/// <summary>
/// Max range used for clamping

2
src/ImageSharp/ColorSpaces/Hsv.cs

@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <summary>
/// Represents a <see cref="Hsv"/> that has H, S, and V values set to zero.
/// </summary>
public static readonly Hsv Empty = default(Hsv);
public static readonly Hsv Empty = default;
/// <summary>
/// Max range used for clamping

2
src/ImageSharp/ColorSpaces/HunterLab.cs

@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <summary>
/// Represents a <see cref="HunterLab"/> that has L, A, B values set to zero.
/// </summary>
public static readonly HunterLab Empty = default(HunterLab);
public static readonly HunterLab Empty = default;
/// <summary>
/// The backing vector for SIMD support.

2
src/ImageSharp/ColorSpaces/LinearRgb.cs

@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <summary>
/// Represents a <see cref="LinearRgb"/> that has R, G, and B values set to zero.
/// </summary>
public static readonly LinearRgb Empty = default(LinearRgb);
public static readonly LinearRgb Empty = default;
/// <summary>
/// The default LinearRgb working space

2
src/ImageSharp/ColorSpaces/Lms.cs

@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <summary>
/// Represents a <see cref="Lms"/> that has L, M, and S values set to zero.
/// </summary>
public static readonly Lms Empty = default(Lms);
public static readonly Lms Empty = default;
/// <summary>
/// The backing vector for SIMD support.

2
src/ImageSharp/ColorSpaces/Rgb.cs

@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <summary>
/// Represents a <see cref="Rgb"/> that has R, G, and B values set to zero.
/// </summary>
public static readonly Rgb Empty = default(Rgb);
public static readonly Rgb Empty = default;
/// <summary>
/// The default rgb working space

2
src/ImageSharp/ColorSpaces/YCbCr.cs

@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <summary>
/// Represents a <see cref="YCbCr"/> that has Y, Cb, and Cr values set to zero.
/// </summary>
public static readonly YCbCr Empty = default(YCbCr);
public static readonly YCbCr Empty = default;
/// <summary>
/// Vector which is used in clamping to the max value

7
src/ImageSharp/Common/Exceptions/ImageFormatException.cs

@ -11,13 +11,6 @@ namespace SixLabors.ImageSharp
/// </summary>
public sealed class ImageFormatException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="ImageFormatException"/> class.
/// </summary>
public ImageFormatException()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ImageFormatException"/> class with the name of the
/// parameter that causes this exception.

9
src/ImageSharp/Common/Exceptions/ImageProcessingException.cs

@ -10,13 +10,6 @@ namespace SixLabors.ImageSharp
/// </summary>
public sealed class ImageProcessingException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="ImageProcessingException"/> class.
/// </summary>
public ImageProcessingException()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ImageProcessingException"/> class with the name of the
/// parameter that causes this exception.
@ -39,4 +32,4 @@ namespace SixLabors.ImageSharp
{
}
}
}
}

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

@ -4,6 +4,7 @@
using System;
using System.Diagnostics;
// TODO: These should just call the guard equivalents
namespace SixLabors.ImageSharp
{
/// <summary>
@ -114,6 +115,28 @@ namespace SixLabors.ImageSharp
}
}
/// <summary>
/// Verifies that the specified value is greater than or equal to a minimum value and less than
/// or equal to a maximum value and throws an exception if it is not.
/// </summary>
/// <param name="value">The target value, which should be validated.</param>
/// <param name="min">The minimum value.</param>
/// <param name="max">The maximum value.</param>
/// <param name="parameterName">The name of the parameter that is to be checked.</param>
/// <typeparam name="TValue">The type of the value.</typeparam>
/// <exception cref="ArgumentException">
/// <paramref name="value"/> is less than the minimum value of greater than the maximum value.
/// </exception>
[Conditional("DEBUG")]
public static void MustBeBetweenOrEqualTo<TValue>(TValue value, TValue min, TValue max, string parameterName)
where TValue : IComparable<TValue>
{
if (value.CompareTo(min) < 0 || value.CompareTo(max) > 0)
{
throw new ArgumentOutOfRangeException(parameterName, $"Value {value} must be greater than or equal to {min} and less than or equal to {max}.");
}
}
/// <summary>
/// Verifies, that the method parameter with specified target value is true
/// and throws an exception if it is found to be so.

2
src/ImageSharp/Common/Helpers/ImageMaths.cs

@ -2,8 +2,6 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Linq;
using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;

2
src/ImageSharp/Formats/Bmp/BmpCompression.cs

@ -7,7 +7,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// Defines how the compression type of the image data
/// in the bitmap file.
/// </summary>
internal enum BmpCompression
internal enum BmpCompression : int
{
/// <summary>
/// Each image row has a multiple of four elements. If the

256
src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs

@ -34,29 +34,29 @@ namespace SixLabors.ImageSharp.Formats.Bmp
private const int Rgb16BMask = 0x1F;
/// <summary>
/// RLE8 flag value that indicates following byte has special meaning
/// RLE8 flag value that indicates following byte has special meaning.
/// </summary>
private const int RleCommand = 0x00;
/// <summary>
/// RLE8 flag value marking end of a scan line
/// RLE8 flag value marking end of a scan line.
/// </summary>
private const int RleEndOfLine = 0x00;
/// <summary>
/// RLE8 flag value marking end of bitmap data
/// RLE8 flag value marking end of bitmap data.
/// </summary>
private const int RleEndOfBitmap = 0x01;
/// <summary>
/// RLE8 flag value marking the start of [x,y] offset instruction
/// RLE8 flag value marking the start of [x,y] offset instruction.
/// </summary>
private const int RleDelta = 0x02;
/// <summary>
/// The stream to decode from.
/// </summary>
private Stream currentStream;
private Stream stream;
/// <summary>
/// The file header containing general information.
@ -163,18 +163,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int Invert(int y, int height, bool inverted)
{
int row;
if (!inverted)
{
row = height - y - 1;
}
else
{
row = y;
}
return row;
return (!inverted) ? height - y - 1 : y;
}
/// <summary>
@ -261,7 +250,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
while (count < buffer.Length)
{
if (this.currentStream.Read(cmd, 0, cmd.Length) != 2)
if (this.stream.Read(cmd, 0, cmd.Length) != 2)
{
throw new Exception("Failed to read 2 bytes from stream");
}
@ -283,27 +272,29 @@ namespace SixLabors.ImageSharp.Formats.Bmp
break;
case RleDelta:
int dx = this.currentStream.ReadByte();
int dy = this.currentStream.ReadByte();
int dx = this.stream.ReadByte();
int dy = this.stream.ReadByte();
count += (w * dy) + dx;
break;
default:
// If the second byte > 2, signals 'absolute mode'
// If the second byte > 2, we are in 'absolute mode'
// Take this number of bytes from the stream as uncompressed data
int length = cmd[1];
int copyLength = length;
byte[] run = new byte[length];
this.stream.Read(run, 0, run.Length);
run.AsSpan().CopyTo(buffer.Slice(count));
count += run.Length;
// Absolute mode data is aligned to two-byte word-boundary
length += length & 1;
int padding = length & 1;
byte[] run = new byte[length];
this.currentStream.Read(run, 0, run.Length);
for (int i = 0; i < copyLength; i++)
{
buffer[count++] = run[i];
}
this.stream.Skip(padding);
break;
}
@ -348,7 +339,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
using (IManagedByteBuffer row = this.memoryManager.AllocateCleanManagedByteBuffer(arrayWidth + padding))
{
var color = default(TPixel);
TPixel color = default;
var rgba = new Rgba32(0, 0, 0, 255);
Span<byte> rowSpan = row.Span;
@ -356,7 +347,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
for (int y = 0; y < height; y++)
{
int newY = Invert(y, height, inverted);
this.currentStream.Read(row.Array, 0, row.Length());
this.stream.Read(row.Array, 0, row.Length());
int offset = 0;
Span<TPixel> pixelRow = pixels.GetRowSpan(newY);
@ -402,7 +393,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
{
for (int y = 0; y < height; y++)
{
this.currentStream.Read(buffer.Array, 0, stride);
this.stream.Read(buffer.Array, 0, stride);
int newY = Invert(y, height, inverted);
Span<TPixel> pixelRow = pixels.GetRowSpan(newY);
@ -440,7 +431,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
{
for (int y = 0; y < height; y++)
{
this.currentStream.Read(row);
this.stream.Read(row);
int newY = Invert(y, height, inverted);
Span<TPixel> pixelSpan = pixels.GetRowSpan(newY);
PixelOperations<TPixel>.Instance.PackFromBgr24Bytes(row.Span, pixelSpan, width);
@ -465,7 +456,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
{
for (int y = 0; y < height; y++)
{
this.currentStream.Read(row);
this.stream.Read(row);
int newY = Invert(y, height, inverted);
Span<TPixel> pixelSpan = pixels.GetRowSpan(newY);
PixelOperations<TPixel>.Instance.PackFromBgra32Bytes(row.Span, pixelSpan, width);
@ -478,98 +469,44 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// </summary>
private void ReadInfoHeader()
{
byte[] data = new byte[BmpInfoHeader.MaxHeaderSize];
byte[] buffer = new byte[BmpInfoHeader.MaxHeaderSize];
// read header size
this.currentStream.Read(data, 0, BmpInfoHeader.HeaderSizeSize);
int headerSize = BitConverter.ToInt32(data, 0);
if (headerSize < BmpInfoHeader.BitmapCoreHeaderSize)
this.stream.Read(buffer, 0, BmpInfoHeader.HeaderSizeSize);
int headerSize = BitConverter.ToInt32(buffer, 0);
if (headerSize < BmpInfoHeader.CoreSize)
{
throw new NotSupportedException($"This kind of bitmap files (header size $headerSize) is not supported.");
throw new NotSupportedException($"ImageSharp does not support this BMP file. HeaderSize: {headerSize}.");
}
int skipAmmount = 0;
int skipAmount = 0;
if (headerSize > BmpInfoHeader.MaxHeaderSize)
{
skipAmmount = headerSize - BmpInfoHeader.MaxHeaderSize;
skipAmount = headerSize - BmpInfoHeader.MaxHeaderSize;
headerSize = BmpInfoHeader.MaxHeaderSize;
}
// read the rest of the header
this.currentStream.Read(data, BmpInfoHeader.HeaderSizeSize, headerSize - BmpInfoHeader.HeaderSizeSize);
this.stream.Read(buffer, BmpInfoHeader.HeaderSizeSize, headerSize - BmpInfoHeader.HeaderSizeSize);
switch (headerSize)
if (headerSize == BmpInfoHeader.CoreSize)
{
case BmpInfoHeader.BitmapCoreHeaderSize:
this.infoHeader = this.ParseBitmapCoreHeader(data);
break;
case BmpInfoHeader.BitmapInfoHeaderSize:
this.infoHeader = this.ParseBitmapInfoHeader(data);
break;
default:
if (headerSize > BmpInfoHeader.BitmapInfoHeaderSize)
{
this.infoHeader = this.ParseBitmapInfoHeader(data);
break;
}
else
{
throw new NotSupportedException($"This kind of bitmap files (header size $headerSize) is not supported.");
}
// 12 bytes
this.infoHeader = BmpInfoHeader.ParseCore(buffer);
}
// skip the remaining header because we can't read those parts
this.currentStream.Skip(skipAmmount);
}
/// <summary>
/// Parses the <see cref="BmpInfoHeader"/> from the stream, assuming it uses the BITMAPCOREHEADER format.
/// </summary>
/// <param name="data">Header bytes read from the stream</param>
/// <returns>Parsed header</returns>
/// <seealso href="https://msdn.microsoft.com/en-us/library/windows/desktop/dd183372.aspx"/>
private BmpInfoHeader ParseBitmapCoreHeader(byte[] data)
{
return new BmpInfoHeader
else if (headerSize >= BmpInfoHeader.Size)
{
HeaderSize = BitConverter.ToInt32(data, 0),
Width = BitConverter.ToUInt16(data, 4),
Height = BitConverter.ToUInt16(data, 6),
Planes = BitConverter.ToInt16(data, 8),
BitsPerPixel = BitConverter.ToInt16(data, 10),
// the rest is not present in the core header
ImageSize = 0,
XPelsPerMeter = 0,
YPelsPerMeter = 0,
ClrUsed = 0,
ClrImportant = 0,
Compression = BmpCompression.RGB
};
}
/// <summary>
/// Parses the <see cref="BmpInfoHeader"/> from the stream, assuming it uses the BITMAPINFOHEADER format.
/// </summary>
/// <param name="data">Header bytes read from the stream</param>
/// <returns>Parsed header</returns>
/// <seealso href="https://msdn.microsoft.com/en-us/library/windows/desktop/dd183376.aspx"/>
private BmpInfoHeader ParseBitmapInfoHeader(byte[] data)
{
return new BmpInfoHeader
// >= 40 bytes
this.infoHeader = BmpInfoHeader.Parse(buffer.AsSpan(0, 40));
}
else
{
HeaderSize = BitConverter.ToInt32(data, 0),
Width = BitConverter.ToInt32(data, 4),
Height = BitConverter.ToInt32(data, 8),
Planes = BitConverter.ToInt16(data, 12),
BitsPerPixel = BitConverter.ToInt16(data, 14),
ImageSize = BitConverter.ToInt32(data, 20),
XPelsPerMeter = BitConverter.ToInt32(data, 24),
YPelsPerMeter = BitConverter.ToInt32(data, 28),
ClrUsed = BitConverter.ToInt32(data, 32),
ClrImportant = BitConverter.ToInt32(data, 36),
Compression = (BmpCompression)BitConverter.ToInt32(data, 16)
};
throw new NotSupportedException($"ImageSharp does not support this BMP file. HeaderSize: {headerSize}.");
}
// skip the remaining header because we can't read those parts
this.stream.Skip(skipAmount);
}
/// <summary>
@ -577,15 +514,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// </summary>
private void ReadFileHeader()
{
byte[] data = new byte[BmpFileHeader.Size];
byte[] buffer = new byte[BmpFileHeader.Size];
this.currentStream.Read(data, 0, BmpFileHeader.Size);
this.stream.Read(buffer, 0, BmpFileHeader.Size);
this.fileHeader = new BmpFileHeader(
type: BitConverter.ToInt16(data, 0),
fileSize: BitConverter.ToInt32(data, 2),
reserved: BitConverter.ToInt32(data, 6),
offset: BitConverter.ToInt32(data, 10));
this.fileHeader = BmpFileHeader.Parse(buffer);
}
/// <summary>
@ -593,66 +526,59 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// </summary>
private void ReadImageHeaders(Stream stream, out bool inverted, out byte[] palette)
{
this.currentStream = stream;
try
this.stream = stream;
this.ReadFileHeader();
this.ReadInfoHeader();
// see http://www.drdobbs.com/architecture-and-design/the-bmp-file-format-part-1/184409517
// If the height is negative, then this is a Windows bitmap whose origin
// is the upper-left corner and not the lower-left. The inverted flag
// indicates a lower-left origin.Our code will be outputting an
// upper-left origin pixel array.
inverted = false;
if (this.infoHeader.Height < 0)
{
this.ReadFileHeader();
this.ReadInfoHeader();
// see http://www.drdobbs.com/architecture-and-design/the-bmp-file-format-part-1/184409517
// If the height is negative, then this is a Windows bitmap whose origin
// is the upper-left corner and not the lower-left.The inverted flag
// indicates a lower-left origin.Our code will be outputting an
// upper-left origin pixel array.
inverted = false;
if (this.infoHeader.Height < 0)
{
inverted = true;
this.infoHeader.Height = -this.infoHeader.Height;
}
inverted = true;
this.infoHeader.Height = -this.infoHeader.Height;
}
int colorMapSize = -1;
int colorMapSize = -1;
if (this.infoHeader.ClrUsed == 0)
{
if (this.infoHeader.BitsPerPixel == 1 ||
this.infoHeader.BitsPerPixel == 4 ||
this.infoHeader.BitsPerPixel == 8)
{
colorMapSize = (int)Math.Pow(2, this.infoHeader.BitsPerPixel) * 4;
}
}
else
if (this.infoHeader.ClrUsed == 0)
{
if (this.infoHeader.BitsPerPixel == 1 ||
this.infoHeader.BitsPerPixel == 4 ||
this.infoHeader.BitsPerPixel == 8)
{
colorMapSize = this.infoHeader.ClrUsed * 4;
colorMapSize = (int)Math.Pow(2, this.infoHeader.BitsPerPixel) * 4;
}
}
else
{
colorMapSize = this.infoHeader.ClrUsed * 4;
}
palette = null;
palette = null;
if (colorMapSize > 0)
if (colorMapSize > 0)
{
// 256 * 4
if (colorMapSize > 1024)
{
// 256 * 4
if (colorMapSize > 1024)
{
throw new ImageFormatException($"Invalid bmp colormap size '{colorMapSize}'");
}
palette = new byte[colorMapSize];
this.currentStream.Read(palette, 0, colorMapSize);
throw new ImageFormatException($"Invalid bmp colormap size '{colorMapSize}'");
}
if (this.infoHeader.Width > int.MaxValue || this.infoHeader.Height > int.MaxValue)
{
throw new ArgumentOutOfRangeException(
$"The input bitmap '{this.infoHeader.Width}x{this.infoHeader.Height}' is "
+ $"bigger then the max allowed size '{int.MaxValue}x{int.MaxValue}'");
}
palette = new byte[colorMapSize];
this.stream.Read(palette, 0, colorMapSize);
}
catch (IndexOutOfRangeException e)
if (this.infoHeader.Width > int.MaxValue || this.infoHeader.Height > int.MaxValue)
{
throw new ImageFormatException("Bitmap does not have a valid format.", e);
throw new ArgumentOutOfRangeException(
$"The input bmp '{this.infoHeader.Width}x{this.infoHeader.Height}' is "
+ $"bigger then the max allowed size '{int.MaxValue}x{int.MaxValue}'");
}
}
}

100
src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs

@ -3,7 +3,6 @@
using System;
using System.IO;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
@ -19,9 +18,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// </summary>
private int padding;
/// <summary>
/// Gets or sets the number of bits per pixel.
/// </summary>
private readonly BmpBitsPerPixel bitsPerPixel;
private readonly MemoryManager memoryManager;
@ -54,20 +50,15 @@ namespace SixLabors.ImageSharp.Formats.Bmp
int bytesPerLine = 4 * (((image.Width * bpp) + 31) / 32);
this.padding = bytesPerLine - (image.Width * (int)this.bitsPerPixel);
// Do not use IDisposable pattern here as we want to preserve the stream.
var writer = new EndianBinaryWriter(Endianness.LittleEndian, stream);
var infoHeader = new BmpInfoHeader
{
HeaderSize = BmpInfoHeader.BitmapInfoHeaderSize,
Height = image.Height,
Width = image.Width,
BitsPerPixel = bpp,
Planes = 1,
ImageSize = image.Height * bytesPerLine,
ClrUsed = 0,
ClrImportant = 0
};
var infoHeader = new BmpInfoHeader(
headerSize: BmpInfoHeader.Size,
height: image.Height,
width: image.Width,
bitsPerPixel: bpp,
planes: 1,
imageSize: image.Height * bytesPerLine,
clrUsed: 0,
clrImportant: 0);
var fileHeader = new BmpFileHeader(
type: 19778, // BM
@ -75,63 +66,30 @@ namespace SixLabors.ImageSharp.Formats.Bmp
reserved: 0,
fileSize: 54 + infoHeader.ImageSize);
WriteHeader(writer, fileHeader);
this.WriteInfo(writer, infoHeader);
this.WriteImage(writer, image.Frames.RootFrame);
byte[] buffer = new byte[40]; // TODO: stackalloc
writer.Flush();
}
fileHeader.WriteTo(buffer);
/// <summary>
/// Writes the bitmap header data to the binary stream.
/// </summary>
/// <param name="writer">
/// The <see cref="EndianBinaryWriter"/> containing the stream to write to.
/// </param>
/// <param name="fileHeader">
/// The <see cref="BmpFileHeader"/> containing the header data.
/// </param>
private static void WriteHeader(EndianBinaryWriter writer, in BmpFileHeader fileHeader)
{
writer.Write(fileHeader.Type);
writer.Write(fileHeader.FileSize);
writer.Write(fileHeader.Reserved);
writer.Write(fileHeader.Offset);
}
stream.Write(buffer, 0, BmpFileHeader.Size);
/// <summary>
/// Writes the bitmap information to the binary stream.
/// </summary>
/// <param name="writer">
/// The <see cref="EndianBinaryWriter"/> containing the stream to write to.
/// </param>
/// <param name="infoHeader">
/// The <see cref="BmpFileHeader"/> containing the detailed information about the image.
/// </param>
private void WriteInfo(EndianBinaryWriter writer, BmpInfoHeader infoHeader)
{
writer.Write(infoHeader.HeaderSize);
writer.Write(infoHeader.Width);
writer.Write(infoHeader.Height);
writer.Write(infoHeader.Planes);
writer.Write(infoHeader.BitsPerPixel);
writer.Write((int)infoHeader.Compression);
writer.Write(infoHeader.ImageSize);
writer.Write(infoHeader.XPelsPerMeter);
writer.Write(infoHeader.YPelsPerMeter);
writer.Write(infoHeader.ClrUsed);
writer.Write(infoHeader.ClrImportant);
infoHeader.WriteTo(buffer);
stream.Write(buffer, 0, 40);
this.WriteImage(stream, image.Frames.RootFrame);
stream.Flush();
}
/// <summary>
/// Writes the pixel data to the binary stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="writer">The <see cref="EndianBinaryWriter"/> containing the stream to write to.</param>
/// <param name="stream">The <see cref="Stream"/> to write to.</param>
/// <param name="image">
/// The <see cref="ImageFrame{TPixel}"/> containing pixel data.
/// </param>
private void WriteImage<TPixel>(EndianBinaryWriter writer, ImageFrame<TPixel> image)
private void WriteImage<TPixel>(Stream stream, ImageFrame<TPixel> image)
where TPixel : struct, IPixel<TPixel>
{
using (PixelAccessor<TPixel> pixels = image.Lock())
@ -139,11 +97,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp
switch (this.bitsPerPixel)
{
case BmpBitsPerPixel.Pixel32:
this.Write32Bit(writer, pixels);
this.Write32Bit(stream, pixels);
break;
case BmpBitsPerPixel.Pixel24:
this.Write24Bit(writer, pixels);
this.Write24Bit(stream, pixels);
break;
}
}
@ -158,9 +116,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// Writes the 32bit color palette to the stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="writer">The <see cref="EndianBinaryWriter"/> containing the stream to write to.</param>
/// <param name="stream">The <see cref="Stream"/> to write to.</param>
/// <param name="pixels">The <see cref="PixelAccessor{TPixel}"/> containing pixel data.</param>
private void Write32Bit<TPixel>(EndianBinaryWriter writer, PixelAccessor<TPixel> pixels)
private void Write32Bit<TPixel>(Stream stream, PixelAccessor<TPixel> pixels)
where TPixel : struct, IPixel<TPixel>
{
using (IManagedByteBuffer row = this.AllocateRow(pixels.Width, 4))
@ -169,7 +127,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
{
Span<TPixel> pixelSpan = pixels.GetRowSpan(y);
PixelOperations<TPixel>.Instance.ToBgra32Bytes(pixelSpan, row.Span, pixelSpan.Length);
writer.Write(row.Array, 0, row.Length());
stream.Write(row.Array, 0, row.Length());
}
}
}
@ -178,9 +136,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// Writes the 24bit color palette to the stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="writer">The <see cref="EndianBinaryWriter"/> containing the stream to write to.</param>
/// <param name="stream">The <see cref="Stream"/> to write to.</param>
/// <param name="pixels">The <see cref="PixelAccessor{TPixel}"/> containing pixel data.</param>
private void Write24Bit<TPixel>(EndianBinaryWriter writer, PixelAccessor<TPixel> pixels)
private void Write24Bit<TPixel>(Stream stream, PixelAccessor<TPixel> pixels)
where TPixel : struct, IPixel<TPixel>
{
using (IManagedByteBuffer row = this.AllocateRow(pixels.Width, 3))
@ -189,7 +147,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
{
Span<TPixel> pixelSpan = pixels.GetRowSpan(y);
PixelOperations<TPixel>.Instance.ToBgr24Bytes(pixelSpan, row.Span, pixelSpan.Length);
writer.Write(row.Array, 0, row.Length());
stream.Write(row.Array, 0, row.Length());
}
}
}

19
src/ImageSharp/Formats/Bmp/BmpFileHeader.cs

@ -1,6 +1,10 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.Formats.Bmp
{
/// <summary>
@ -13,6 +17,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// All of the other integer values are stored in little-endian format
/// (i.e. least-significant byte first).
/// </remarks>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal readonly struct BmpFileHeader
{
/// <summary>
@ -44,12 +49,24 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// Gets any reserved data; actual value depends on the application
/// that creates the image.
/// </summary>
public int Reserved { get; }
public int Reserved { get; }
/// <summary>
/// Gets the offset, i.e. starting address, of the byte where
/// the bitmap data can be found.
/// </summary>
public int Offset { get; }
public static BmpFileHeader Parse(Span<byte> data)
{
return MemoryMarshal.Cast<byte, BmpFileHeader>(data)[0];
}
public unsafe void WriteTo(Span<byte> buffer)
{
ref BmpFileHeader dest = ref Unsafe.As<byte, BmpFileHeader>(ref MemoryMarshal.GetReference(buffer));
dest = this;
}
}
}

76
src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs

@ -1,5 +1,10 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers.Binary;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.Formats.Bmp
{
/// <summary>
@ -8,28 +13,55 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// the screen.
/// <see href="https://en.wikipedia.org/wiki/BMP_file_format"/>
/// </summary>
internal sealed class BmpInfoHeader
[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal struct BmpInfoHeader
{
/// <summary>
/// Defines the size of the BITMAPINFOHEADER data structure in the bitmap file.
/// </summary>
public const int BitmapInfoHeaderSize = 40;
public const int Size = 40;
/// <summary>
/// Defines the size of the BITMAPCOREHEADER data structure in the bitmap file.
/// </summary>
public const int BitmapCoreHeaderSize = 12;
public const int CoreSize = 12;
/// <summary>
/// Defines the size of the biggest supported header data structure in the bitmap file.
/// </summary>
public const int MaxHeaderSize = BitmapInfoHeaderSize;
public const int MaxHeaderSize = Size;
/// <summary>
/// Defines the size of the <see cref="HeaderSize"/> field.
/// </summary>
public const int HeaderSizeSize = 4;
public BmpInfoHeader(
int headerSize,
int width,
int height,
short planes,
short bitsPerPixel,
BmpCompression compression = default,
int imageSize = 0,
int xPelsPerMeter = 0,
int yPelsPerMeter = 0,
int clrUsed = 0,
int clrImportant = 0)
{
this.HeaderSize = headerSize;
this.Width = width;
this.Height = height;
this.Planes = planes;
this.BitsPerPixel = bitsPerPixel;
this.Compression = compression;
this.ImageSize = imageSize;
this.XPelsPerMeter = xPelsPerMeter;
this.YPelsPerMeter = yPelsPerMeter;
this.ClrUsed = clrUsed;
this.ClrImportant = clrImportant;
}
/// <summary>
/// Gets or sets the size of this header
/// </summary>
@ -91,5 +123,39 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// or 0 when every color is important{ get; set; } generally ignored.
/// </summary>
public int ClrImportant { get; set; }
/// <summary>
/// Parses the full BITMAPINFOHEADER header (40 bytes).
/// </summary>
/// <param name="data">The data to parse.</param>
/// <returns>Parsed header</returns>
/// <seealso href="https://msdn.microsoft.com/en-us/library/windows/desktop/dd183376.aspx"/>
public static BmpInfoHeader Parse(ReadOnlySpan<byte> data)
{
return MemoryMarshal.Cast<byte, BmpInfoHeader>(data)[0];
}
/// <summary>
/// Parses the BITMAPCOREHEADER consisting of the headerSize, width, height, planes, and bitsPerPixel fields (12 bytes).
/// </summary>
/// <param name="data">The data to parse,</param>
/// <returns>Parsed header</returns>
/// <seealso href="https://msdn.microsoft.com/en-us/library/windows/desktop/dd183372.aspx"/>
public static BmpInfoHeader ParseCore(ReadOnlySpan<byte> data)
{
return new BmpInfoHeader(
headerSize: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(0, 4)),
width: BinaryPrimitives.ReadUInt16LittleEndian(data.Slice(4, 2)),
height: BinaryPrimitives.ReadUInt16LittleEndian(data.Slice(6, 2)),
planes: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(8, 2)),
bitsPerPixel: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(10, 2)));
}
public unsafe void WriteTo(Span<byte> buffer)
{
ref BmpInfoHeader dest = ref Unsafe.As<byte, BmpInfoHeader>(ref MemoryMarshal.GetReference(buffer));
dest = this;
}
}
}
}

7
src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs

@ -1,11 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Generic;
using System.IO;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Bmp
{
/// <summary>
@ -15,4 +10,4 @@ namespace SixLabors.ImageSharp.Formats.Bmp
{
// added this for consistancy so we can add stuff as required, no options currently availible
}
}
}

5
src/ImageSharp/Formats/Bmp/IBmpEncoderOptions.cs

@ -1,11 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Generic;
using System.IO;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Bmp
{
/// <summary>

2
src/ImageSharp/Formats/Bmp/ImageExtensions.cs

@ -1,10 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.IO;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.PixelFormats;

13
src/ImageSharp/Formats/Gif/DisposalMethod.cs

@ -11,23 +11,26 @@ namespace SixLabors.ImageSharp.Formats.Gif
public enum DisposalMethod
{
/// <summary>
/// No disposal specified. The decoder is not required to take any action.
/// No disposal specified.
/// The decoder is not required to take any action.
/// </summary>
Unspecified = 0,
/// <summary>
/// Do not dispose. The graphic is to be left in place.
/// Do not dispose.
/// The graphic is to be left in place.
/// </summary>
NotDispose = 1,
/// <summary>
/// Restore to background color. The area used by the graphic must be restored to
/// the background color.
/// Restore to background color.
/// The area used by the graphic must be restored to the background color.
/// </summary>
RestoreToBackground = 2,
/// <summary>
/// Restore to previous. The decoder is required to restore the area overwritten by the
/// Restore to previous.
/// The decoder is required to restore the area overwritten by the
/// graphic with what was there prior to rendering the graphic.
/// </summary>
RestoreToPrevious = 3

2
src/ImageSharp/Formats/Gif/GifConstants.cs

@ -54,7 +54,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary>
/// The application block size.
/// </summary>
public const byte ApplicationBlockSize = 0x0b;
public const byte ApplicationBlockSize = 11;
/// <summary>
/// The comment label.

114
src/ImageSharp/Formats/Gif/GifDecoderCore.cs

@ -2,9 +2,9 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary>
/// The currently loaded stream.
/// </summary>
private Stream currentStream;
private Stream stream;
/// <summary>
/// The global color table.
@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary>
/// The graphics control extension.
/// </summary>
private GifGraphicsControlExtension graphicsControlExtension;
private GifGraphicControlExtension graphicsControlExtension;
/// <summary>
/// The metadata
@ -236,17 +236,9 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
private void ReadGraphicalControlExtension()
{
this.currentStream.Read(this.buffer, 0, 6);
this.stream.Read(this.buffer, 0, 6);
byte packed = this.buffer[1];
this.graphicsControlExtension = new GifGraphicsControlExtension
{
DelayTime = BitConverter.ToInt16(this.buffer, 2),
TransparencyIndex = this.buffer[4],
TransparencyFlag = (packed & 0x01) == 1,
DisposalMethod = (DisposalMethod)((packed & 0x1C) >> 2)
};
this.graphicsControlExtension = GifGraphicControlExtension.Parse(this.buffer);
}
/// <summary>
@ -255,22 +247,9 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <returns><see cref="GifImageDescriptor"/></returns>
private GifImageDescriptor ReadImageDescriptor()
{
this.currentStream.Read(this.buffer, 0, 9);
this.stream.Read(this.buffer, 0, 9);
byte packed = this.buffer[8];
var imageDescriptor = new GifImageDescriptor
{
Left = BitConverter.ToInt16(this.buffer, 0),
Top = BitConverter.ToInt16(this.buffer, 2),
Width = BitConverter.ToInt16(this.buffer, 4),
Height = BitConverter.ToInt16(this.buffer, 6),
LocalColorTableFlag = ((packed & 0x80) >> 7) == 1,
LocalColorTableSize = 2 << (packed & 0x07),
InterlaceFlag = ((packed & 0x40) >> 6) == 1
};
return imageDescriptor;
return GifImageDescriptor.Parse(this.buffer);
}
/// <summary>
@ -278,25 +257,9 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
private void ReadLogicalScreenDescriptor()
{
this.currentStream.Read(this.buffer, 0, 7);
this.stream.Read(this.buffer, 0, 7);
byte packed = this.buffer[4];
this.logicalScreenDescriptor = new GifLogicalScreenDescriptor
{
Width = BitConverter.ToInt16(this.buffer, 0),
Height = BitConverter.ToInt16(this.buffer, 2),
BitsPerPixel = (this.buffer[4] & 0x07) + 1, // The lowest 3 bits represent the bit depth minus 1
BackgroundColorIndex = this.buffer[5],
PixelAspectRatio = this.buffer[6],
GlobalColorTableFlag = ((packed & 0x80) >> 7) == 1,
GlobalColorTableSize = 2 << (packed & 0x07)
};
if (this.logicalScreenDescriptor.GlobalColorTableSize > 255 * 4)
{
throw new ImageFormatException($"Invalid gif colormap size '{this.logicalScreenDescriptor.GlobalColorTableSize}'");
}
this.logicalScreenDescriptor = GifLogicalScreenDescriptor.Parse(this.buffer);
}
/// <summary>
@ -305,13 +268,13 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <param name="length">The number of bytes to skip.</param>
private void Skip(int length)
{
this.currentStream.Skip(length);
this.stream.Skip(length);
int flag;
while ((flag = this.currentStream.ReadByte()) != 0)
while ((flag = this.stream.ReadByte()) != 0)
{
this.currentStream.Skip(flag);
this.stream.Skip(flag);
}
}
@ -322,7 +285,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
{
int length;
while ((length = this.currentStream.ReadByte()) != 0)
while ((length = this.stream.ReadByte()) != 0)
{
if (length > GifConstants.MaxCommentLength)
{
@ -331,13 +294,13 @@ namespace SixLabors.ImageSharp.Formats.Gif
if (this.IgnoreMetadata)
{
this.currentStream.Seek(length, SeekOrigin.Current);
this.stream.Seek(length, SeekOrigin.Current);
continue;
}
using (IManagedByteBuffer commentsBuffer = this.MemoryManager.AllocateManagedByteBuffer(length))
{
this.currentStream.Read(commentsBuffer.Array, 0, length);
this.stream.Read(commentsBuffer.Array, 0, length);
string comments = this.TextEncoding.GetString(commentsBuffer.Array, 0, length);
this.metaData.Properties.Add(new ImageProperty(GifConstants.Comments, comments));
}
@ -364,7 +327,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
{
int length = imageDescriptor.LocalColorTableSize * 3;
localColorTable = this.configuration.MemoryManager.AllocateManagedByteBuffer(length, true);
this.currentStream.Read(localColorTable.Array, 0, length);
this.stream.Read(localColorTable.Array, 0, length);
}
indices = this.configuration.MemoryManager.AllocateManagedByteBuffer(imageDescriptor.Width * imageDescriptor.Height, true);
@ -389,10 +352,10 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <param name="imageDescriptor">The <see cref="GifImageDescriptor"/>.</param>
/// <param name="indices">The pixel array to write to.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ReadFrameIndices(GifImageDescriptor imageDescriptor, Span<byte> indices)
private void ReadFrameIndices(in GifImageDescriptor imageDescriptor, Span<byte> indices)
{
int dataSize = this.currentStream.ReadByte();
using (var lzwDecoder = new LzwDecoder(this.configuration.MemoryManager, this.currentStream))
int dataSize = this.stream.ReadByte();
using (var lzwDecoder = new LzwDecoder(this.configuration.MemoryManager, this.stream))
{
lzwDecoder.DecodePixels(imageDescriptor.Width, imageDescriptor.Height, dataSize, indices);
}
@ -407,16 +370,15 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <param name="indices">The indexed pixels.</param>
/// <param name="colorTable">The color table containing the available colors.</param>
/// <param name="descriptor">The <see cref="GifImageDescriptor"/></param>
private void ReadFrameColors<TPixel>(ref Image<TPixel> image, ref ImageFrame<TPixel> previousFrame, Span<byte> indices, Span<byte> colorTable, GifImageDescriptor descriptor)
private void ReadFrameColors<TPixel>(ref Image<TPixel> image, ref ImageFrame<TPixel> previousFrame, Span<byte> indices, Span<byte> colorTable, in GifImageDescriptor descriptor)
where TPixel : struct, IPixel<TPixel>
{
ref byte indicesRef = ref MemoryMarshal.GetReference(indices);
int imageWidth = this.logicalScreenDescriptor.Width;
int imageHeight = this.logicalScreenDescriptor.Height;
ImageFrame<TPixel> prevFrame = null;
ImageFrame<TPixel> currentFrame = null;
ImageFrame<TPixel> imageFrame;
if (previousFrame == null)
@ -430,8 +392,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
}
else
{
if (this.graphicsControlExtension != null &&
this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToPrevious)
if (this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToPrevious)
{
prevFrame = previousFrame;
}
@ -479,7 +440,6 @@ namespace SixLabors.ImageSharp.Formats.Gif
}
writeY = interlaceY + descriptor.Top;
interlaceY += interlaceIncrement;
}
else
@ -487,22 +447,20 @@ namespace SixLabors.ImageSharp.Formats.Gif
writeY = y;
}
Span<TPixel> rowSpan = imageFrame.GetPixelRowSpan(writeY);
ref TPixel rowRef = ref MemoryMarshal.GetReference(imageFrame.GetPixelRowSpan(writeY));
var rgba = new Rgba32(0, 0, 0, 255);
// #403 The left + width value can be larger than the image width
for (int x = descriptor.Left; x < descriptor.Left + descriptor.Width && x < rowSpan.Length; x++)
for (int x = descriptor.Left; x < descriptor.Left + descriptor.Width && x < imageWidth; x++)
{
int index = indices[i];
int index = Unsafe.Add(ref indicesRef, i);
if (this.graphicsControlExtension == null ||
this.graphicsControlExtension.TransparencyFlag == false ||
if (this.graphicsControlExtension.TransparencyFlag == false ||
this.graphicsControlExtension.TransparencyIndex != index)
{
int indexOffset = index * 3;
ref TPixel pixel = ref rowSpan[x];
ref TPixel pixel = ref Unsafe.Add(ref rowRef, x);
rgba.Rgb = colorTable.GetRgb24(indexOffset);
pixel.PackFromRgba32(rgba);
@ -520,8 +478,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
previousFrame = currentFrame ?? image.Frames.RootFrame;
if (this.graphicsControlExtension != null &&
this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToBackground)
if (this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToBackground)
{
this.restoreArea = new Rectangle(descriptor.Left, descriptor.Top, descriptor.Width, descriptor.Height);
}
@ -553,15 +510,12 @@ namespace SixLabors.ImageSharp.Formats.Gif
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void SetFrameMetaData(ImageFrameMetaData meta)
{
if (this.graphicsControlExtension != null)
if (this.graphicsControlExtension.DelayTime > 0)
{
if (this.graphicsControlExtension.DelayTime > 0)
{
meta.FrameDelay = this.graphicsControlExtension.DelayTime;
}
meta.DisposalMethod = this.graphicsControlExtension.DisposalMethod;
meta.FrameDelay = this.graphicsControlExtension.DelayTime;
}
meta.DisposalMethod = this.graphicsControlExtension.DisposalMethod;
}
/// <summary>
@ -572,10 +526,10 @@ namespace SixLabors.ImageSharp.Formats.Gif
{
this.metaData = new ImageMetaData();
this.currentStream = stream;
this.stream = stream;
// Skip the identifier
this.currentStream.Skip(6);
this.stream.Skip(6);
this.ReadLogicalScreenDescriptor();
if (this.logicalScreenDescriptor.GlobalColorTableFlag)

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

@ -2,10 +2,11 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers.Binary;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.PixelFormats;
@ -14,42 +15,37 @@ using SixLabors.ImageSharp.Processing.Quantization;
namespace SixLabors.ImageSharp.Formats.Gif
{
/// <summary>
/// Performs the gif encoding operation.
/// Implements the GIF encoding protocol.
/// </summary>
internal sealed class GifEncoderCore
{
private readonly MemoryManager memoryManager;
/// <summary>
/// The temp buffer used to reduce allocations.
/// A reusable buffer used to reduce allocations.
/// </summary>
private readonly byte[] buffer = new byte[16];
private readonly byte[] buffer = new byte[20];
/// <summary>
/// Gets the TextEncoding
/// Gets the text encoding used to write comments.
/// </summary>
private readonly Encoding textEncoding;
/// <summary>
/// Gets or sets the quantizer for reducing the color count.
/// Gets or sets the quantizer used to generate the color palette.
/// </summary>
private readonly IQuantizer quantizer;
/// <summary>
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded.
/// A flag indicating whether to ingore the metadata when writing the image.
/// </summary>
private readonly bool ignoreMetadata;
/// <summary>
/// The number of bits requires to store the image palette.
/// The number of bits requires to store the color palette.
/// </summary>
private int bitDepth;
/// <summary>
/// Whether the current image has multiple frames.
/// </summary>
private bool hasFrames;
/// <summary>
/// Initializes a new instance of the <see cref="GifEncoderCore"/> class.
/// </summary>
@ -75,11 +71,6 @@ namespace SixLabors.ImageSharp.Formats.Gif
Guard.NotNull(image, nameof(image));
Guard.NotNull(stream, nameof(stream));
// Do not use IDisposable pattern here as we want to preserve the stream.
var writer = new EndianBinaryWriter(Endianness.LittleEndian, stream);
this.hasFrames = image.Frames.Count > 1;
// Quantize the image returning a palette.
QuantizedFrame<TPixel> quantized = this.quantizer.CreateFrameQuantizer<TPixel>().QuantizeFrame(image.Frames.RootFrame);
@ -89,18 +80,18 @@ namespace SixLabors.ImageSharp.Formats.Gif
int index = this.GetTransparentIndex(quantized);
// Write the header.
this.WriteHeader(writer);
this.WriteHeader(stream);
// Write the LSD. We'll use local color tables for now.
this.WriteLogicalScreenDescriptor(image, writer, index);
this.WriteLogicalScreenDescriptor(image, stream, index);
// Write the first frame.
this.WriteComments(image, writer);
this.WriteComments(image.MetaData, stream);
// Write additional frames.
if (this.hasFrames)
if (image.Frames.Count > 1)
{
this.WriteApplicationExtension(writer, image.MetaData.RepeatCount, image.Frames.Count);
this.WriteApplicationExtension(stream, image.MetaData.RepeatCount);
}
foreach (ImageFrame<TPixel> frame in image.Frames)
@ -110,16 +101,16 @@ namespace SixLabors.ImageSharp.Formats.Gif
quantized = this.quantizer.CreateFrameQuantizer<TPixel>().QuantizeFrame(frame);
}
this.WriteGraphicalControlExtension(frame.MetaData, writer, this.GetTransparentIndex(quantized));
this.WriteImageDescriptor(frame, writer);
this.WriteColorTable(quantized, writer);
this.WriteImageData(quantized, writer);
this.WriteGraphicalControlExtension(frame.MetaData, stream, this.GetTransparentIndex(quantized));
this.WriteImageDescriptor(frame, stream);
this.WriteColorTable(quantized, stream);
this.WriteImageData(quantized, stream);
quantized = null; // So next frame can regenerate it
}
// TODO: Write extension etc
writer.Write(GifConstants.EndIntroducer);
stream.WriteByte(GifConstants.EndIntroducer);
}
/// <summary>
@ -137,12 +128,14 @@ namespace SixLabors.ImageSharp.Formats.Gif
{
// Transparent pixels are much more likely to be found at the end of a palette
int index = -1;
var trans = default(Rgba32);
Rgba32 trans = default;
ref TPixel paletteRef = ref MemoryMarshal.GetReference(quantized.Palette.AsSpan());
for (int i = quantized.Palette.Length - 1; i >= 0; i--)
{
quantized.Palette[i].ToRgba32(ref trans);
if (trans.Equals(default(Rgba32)))
ref TPixel entry = ref Unsafe.Add(ref paletteRef, i);
entry.ToRgba32(ref trans);
if (trans.Equals(default))
{
index = i;
}
@ -154,10 +147,11 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary>
/// Writes the file header signature and version to the stream.
/// </summary>
/// <param name="writer">The writer to write to the stream with.</param>
private void WriteHeader(EndianBinaryWriter writer)
/// <param name="stream">The stream to write to.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void WriteHeader(Stream stream)
{
writer.Write(GifConstants.MagicNumber);
stream.Write(GifConstants.MagicNumber, 0, GifConstants.MagicNumber.Length);
}
/// <summary>
@ -165,82 +159,69 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The image to encode.</param>
/// <param name="writer">The writer to write to the stream with.</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, EndianBinaryWriter writer, int transparencyIndex)
private void WriteLogicalScreenDescriptor<TPixel>(Image<TPixel> image, Stream stream, int transparencyIndex)
where TPixel : struct, IPixel<TPixel>
{
var descriptor = new GifLogicalScreenDescriptor
{
Width = (short)image.Width,
Height = (short)image.Height,
GlobalColorTableFlag = false, // TODO: Always false for now.
GlobalColorTableSize = this.bitDepth - 1,
BackgroundColorIndex = unchecked((byte)transparencyIndex)
};
writer.Write((ushort)descriptor.Width);
writer.Write((ushort)descriptor.Height);
var field = default(PackedField);
field.SetBit(0, descriptor.GlobalColorTableFlag); // 1 : Global color table flag = 1 || 0 (GCT used/ not used)
field.SetBits(1, 3, descriptor.GlobalColorTableSize); // 2-4 : color resolution
field.SetBit(4, false); // 5 : GCT sort flag = 0
field.SetBits(5, 3, descriptor.GlobalColorTableSize); // 6-8 : GCT size. 2^(N+1)
// Reduce the number of writes
this.buffer[0] = field.Byte;
this.buffer[1] = descriptor.BackgroundColorIndex; // Background Color Index
this.buffer[2] = descriptor.PixelAspectRatio; // Pixel aspect ratio. Assume 1:1
writer.Write(this.buffer, 0, 3);
byte packedValue = GifLogicalScreenDescriptor.GetPackedValue(false, this.bitDepth - 1, false, this.bitDepth - 1);
var descriptor = new GifLogicalScreenDescriptor(
width: (ushort)image.Width,
height: (ushort)image.Height,
packed: packedValue,
backgroundColorIndex: unchecked((byte)transparencyIndex));
descriptor.WriteTo(this.buffer);
stream.Write(this.buffer, 0, GifLogicalScreenDescriptor.Size);
}
/// <summary>
/// Writes the application extension to the stream.
/// </summary>
/// <param name="writer">The writer to write to the stream with.</param>
/// <param name="stream">The stream to write to.</param>
/// <param name="repeatCount">The animated image repeat count.</param>
/// <param name="frames">The number of image frames.</param>
private void WriteApplicationExtension(EndianBinaryWriter writer, ushort repeatCount, int frames)
private void WriteApplicationExtension(Stream stream, ushort repeatCount)
{
// Application Extension Header
if (repeatCount != 1 && frames > 0)
if (repeatCount != 1)
{
this.buffer[0] = GifConstants.ExtensionIntroducer;
this.buffer[1] = GifConstants.ApplicationExtensionLabel;
this.buffer[2] = GifConstants.ApplicationBlockSize;
writer.Write(this.buffer, 0, 3);
// Write NETSCAPE2.0
GifConstants.ApplicationIdentificationBytes.AsSpan().CopyTo(this.buffer.AsSpan(3, 11));
writer.Write(GifConstants.ApplicationIdentificationBytes); // NETSCAPE2.0
writer.Write((byte)3); // Application block length
writer.Write((byte)1); // Data sub-block index (always 1)
// Application Data ----
this.buffer[14] = 3; // Application block length
this.buffer[15] = 1; // Data sub-block index (always 1)
// 0 means loop indefinitely. Count is set as play n + 1 times.
repeatCount = (ushort)Math.Max(0, repeatCount - 1);
writer.Write(repeatCount); // Repeat count for images.
writer.Write(GifConstants.Terminator); // Terminator
BinaryPrimitives.WriteUInt16LittleEndian(this.buffer.AsSpan(16, 2), repeatCount); // Repeat count for images.
this.buffer[18] = GifConstants.Terminator; // Terminator
stream.Write(this.buffer, 0, 19);
}
}
/// <summary>
/// Writes the image comments to the stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The <see cref="ImageFrame{TPixel}"/> to be encoded.</param>
/// <param name="writer">The stream to write to.</param>
private void WriteComments<TPixel>(Image<TPixel> image, EndianBinaryWriter writer)
where TPixel : struct, IPixel<TPixel>
/// <param name="metadata">The metadata to be extract the comment data.</param>
/// <param name="stream">The stream to write to.</param>
private void WriteComments(ImageMetaData metadata, Stream stream)
{
if (this.ignoreMetadata)
{
return;
}
ImageProperty property = image.MetaData.Properties.FirstOrDefault(p => p.Name == GifConstants.Comments);
if (property == null || string.IsNullOrEmpty(property.Value))
if (!metadata.TryGetProperty(GifConstants.Comments, out ImageProperty property) || string.IsNullOrEmpty(property.Value))
{
return;
}
@ -253,44 +234,46 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.buffer[1] = GifConstants.CommentLabel;
this.buffer[2] = (byte)count;
writer.Write(this.buffer, 0, 3);
writer.Write(comments, 0, count);
writer.Write(GifConstants.Terminator);
stream.Write(this.buffer, 0, 3);
stream.Write(comments, 0, count);
stream.WriteByte(GifConstants.Terminator);
}
/// <summary>
/// Writes the graphics control extension to the stream.
/// </summary>
/// <param name="metaData">The metadata of the image or frame.</param>
/// <param name="writer">The stream to write to.</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, EndianBinaryWriter writer, int transparencyIndex)
private void WriteGraphicalControlExtension(ImageFrameMetaData metaData, Stream stream, int transparencyIndex)
{
var extension = new GifGraphicsControlExtension
{
DisposalMethod = metaData.DisposalMethod,
TransparencyFlag = transparencyIndex > -1,
TransparencyIndex = unchecked((byte)transparencyIndex),
DelayTime = metaData.FrameDelay
};
byte packedValue = GifGraphicControlExtension.GetPackedValue(
disposalMethod: metaData.DisposalMethod,
transparencyFlag: transparencyIndex > -1);
var extension = new GifGraphicControlExtension(
packed: packedValue,
transparencyIndex: unchecked((byte)transparencyIndex),
delayTime: (ushort)metaData.FrameDelay);
// Write the intro.
this.WriteExtension(extension, stream);
}
/// <summary>
/// Writes the provided extension to the stream.
/// </summary>
/// <param name="extension">The extension to write to the stream.</param>
/// <param name="stream">The stream to write to.</param>
public void WriteExtension(IGifExtension extension, Stream stream)
{
this.buffer[0] = GifConstants.ExtensionIntroducer;
this.buffer[1] = GifConstants.GraphicControlLabel;
this.buffer[2] = 4;
writer.Write(this.buffer, 0, 3);
this.buffer[1] = extension.Label;
var field = default(PackedField);
field.SetBits(3, 3, (int)extension.DisposalMethod); // 1-3 : Reserved, 4-6 : Disposal
int extensionSize = extension.WriteTo(this.buffer.AsSpan(2));
// TODO: Allow this as an option.
field.SetBit(6, false); // 7 : User input - 0 = none
field.SetBit(7, extension.TransparencyFlag); // 8: Has transparent.
this.buffer[extensionSize + 2] = GifConstants.Terminator;
writer.Write(field.Byte);
writer.Write((ushort)extension.DelayTime);
writer.Write(extension.TransparencyIndex);
writer.Write(GifConstants.Terminator);
stream.Write(this.buffer, 0, extensionSize + 3);
}
/// <summary>
@ -298,25 +281,26 @@ 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="writer">The stream to write to.</param>
private void WriteImageDescriptor<TPixel>(ImageFrame<TPixel> image, EndianBinaryWriter writer)
/// <param name="stream">The stream to write to.</param>
private void WriteImageDescriptor<TPixel>(ImageFrame<TPixel> image, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
writer.Write(GifConstants.ImageDescriptorLabel); // 2c
// TODO: Can we capture this?
writer.Write((ushort)0); // Left position
writer.Write((ushort)0); // Top position
writer.Write((ushort)image.Width);
writer.Write((ushort)image.Height);
var field = default(PackedField);
field.SetBit(0, true); // 1: Local color table flag = 1 (LCT used)
field.SetBit(1, false); // 2: Interlace flag 0
field.SetBit(2, false); // 3: Sort flag 0
field.SetBits(5, 3, this.bitDepth - 1); // 4-5: Reserved, 6-8 : LCT size. 2^(N+1)
writer.Write(field.Byte);
byte packedValue = GifImageDescriptor.GetPackedValue(
localColorTableFlag: true,
interfaceFlag: false,
sortFlag: false,
localColorTableSize: (byte)this.bitDepth); // Note: we subtract 1 from the colorTableSize writing
var descriptor = new GifImageDescriptor(
left: 0,
top: 0,
width: (ushort)image.Width,
height: (ushort)image.Height,
packed: packedValue);
descriptor.WriteTo(this.buffer);
stream.Write(this.buffer, 0, GifImageDescriptor.Size);
}
/// <summary>
@ -324,30 +308,28 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The <see cref="ImageFrame{TPixel}"/> to encode.</param>
/// <param name="writer">The writer to write to the stream with.</param>
private void WriteColorTable<TPixel>(QuantizedFrame<TPixel> image, EndianBinaryWriter writer)
/// <param name="stream">The stream to write to.</param>
private void WriteColorTable<TPixel>(QuantizedFrame<TPixel> image, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
// Grab the palette and write it to the stream.
int pixelCount = image.Palette.Length;
// Get max colors for bit depth.
int colorTableLength = (int)Math.Pow(2, this.bitDepth) * 3;
var rgb = default(Rgb24);
int colorTableLength = (int)Math.Pow(2, this.bitDepth) * 3; // The maximium number of colors for the bit depth
Rgb24 rgb = default;
using (IManagedByteBuffer colorTable = this.memoryManager.AllocateManagedByteBuffer(colorTableLength))
{
Span<byte> colorTableSpan = colorTable.Span;
ref TPixel paletteRef = ref MemoryMarshal.GetReference(image.Palette.AsSpan());
ref Rgb24 rgb24Ref = ref Unsafe.As<byte, Rgb24>(ref MemoryMarshal.GetReference(colorTable.Span));
for (int i = 0; i < pixelCount; i++)
{
int offset = i * 3;
image.Palette[i].ToRgb24(ref rgb);
colorTableSpan[offset] = rgb.R;
colorTableSpan[offset + 1] = rgb.G;
colorTableSpan[offset + 2] = rgb.B;
ref TPixel entry = ref Unsafe.Add(ref paletteRef, i);
entry.ToRgb24(ref rgb);
Unsafe.Add(ref rgb24Ref, i) = rgb;
}
writer.Write(colorTable.Array, 0, colorTableLength);
// Write the palette to the stream
stream.Write(colorTable.Array, 0, colorTableLength);
}
}
@ -356,13 +338,13 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The <see cref="QuantizedFrame{TPixel}"/> containing indexed pixels.</param>
/// <param name="writer">The stream to write to.</param>
private void WriteImageData<TPixel>(QuantizedFrame<TPixel> image, EndianBinaryWriter writer)
/// <param name="stream">The stream to write to.</param>
private void WriteImageData<TPixel>(QuantizedFrame<TPixel> image, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
using (var encoder = new LzwEncoder(this.memoryManager, image.Pixels, (byte)this.bitDepth))
{
encoder.Encode(writer.BaseStream);
encoder.Encode(stream);
}
}
}

8
src/ImageSharp/Formats/Gif/GifImageFormatDetector.cs

@ -16,17 +16,11 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <inheritdoc/>
public IImageFormat DetectFormat(ReadOnlySpan<byte> header)
{
if (this.IsSupportedFileFormat(header))
{
return ImageFormats.Gif;
}
return null;
return this.IsSupportedFileFormat(header) ? ImageFormats.Gif : null;
}
private bool IsSupportedFileFormat(ReadOnlySpan<byte> header)
{
// TODO: This should be in constants
return header.Length >= this.HeaderSize &&
header[0] == 0x47 && // G
header[1] == 0x49 && // I

2
src/ImageSharp/Formats/Gif/IGifDecoderOptions.cs

@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
bool IgnoreMetadata { get; }
/// <summary>
/// Gets the encoding that should be used when reading comments.
/// Gets the text encoding that should be used when reading comments.
/// </summary>
Encoding TextEncoding { get; }

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

@ -7,7 +7,7 @@ using SixLabors.ImageSharp.Processing.Quantization;
namespace SixLabors.ImageSharp.Formats.Gif
{
/// <summary>
/// The configuration options used for encoding gifs
/// The configuration options used for encoding gifs.
/// </summary>
internal interface IGifEncoderOptions
{
@ -17,12 +17,12 @@ namespace SixLabors.ImageSharp.Formats.Gif
bool IgnoreMetadata { get; }
/// <summary>
/// Gets the encoding that should be used when writing comments.
/// Gets the text encoding used to write comments.
/// </summary>
Encoding TextEncoding { get; }
/// <summary>
/// Gets the quantizer for reducing the color count.
/// Gets the quantizer used to generate the color palette.
/// </summary>
IQuantizer Quantizer { get; }
}

6
src/ImageSharp/Formats/Gif/ImageExtensions.cs

@ -1,10 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.IO;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.PixelFormats;
@ -16,7 +14,7 @@ namespace SixLabors.ImageSharp
public static partial class ImageExtensions
{
/// <summary>
/// Saves the image to the given stream with the gif format.
/// Saves the image to the given stream in the gif format.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
@ -27,7 +25,7 @@ namespace SixLabors.ImageSharp
=> source.SaveAsGif(stream, null);
/// <summary>
/// Saves the image to the given stream with the gif format.
/// Saves the image to the given stream in the gif format.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>

80
src/ImageSharp/Formats/Gif/LzwDecoder.cs

@ -2,9 +2,9 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Gif
@ -44,19 +44,6 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
private readonly IBuffer<int> pixelStack;
/// <summary>
/// A value indicating whether this instance of the given entity has been disposed.
/// </summary>
/// <value><see langword="true"/> if this instance has been disposed; otherwise, <see langword="false"/>.</value>
/// <remarks>
/// If the entity is disposed, it must not be disposed a second
/// time. The isDisposed field is set the first time the entity
/// is disposed. If the isDisposed field is true, then the Dispose()
/// method will not dispose again. This help not to prolong the entity's
/// life in the Garbage Collector.
/// </remarks>
private bool isDisposed;
/// <summary>
/// Initializes a new instance of the <see cref="LzwDecoder"/> class
/// and sets the stream, where the compressed data should be read from.
@ -115,14 +102,14 @@ namespace SixLabors.ImageSharp.Formats.Gif
int data = 0;
int first = 0;
Span<int> prefixSpan = this.prefix.Span;
Span<int> suffixSpan = this.suffix.Span;
Span<int> pixelStackSpan = this.pixelStack.Span;
ref int prefixRef = ref MemoryMarshal.GetReference(this.prefix.Span);
ref int suffixRef = ref MemoryMarshal.GetReference(this.suffix.Span);
ref int pixelStackRef = ref MemoryMarshal.GetReference(this.pixelStack.Span);
ref byte pixelsRef = ref MemoryMarshal.GetReference(pixels);
for (code = 0; code < clearCode; code++)
{
prefixSpan[code] = 0;
suffixSpan[code] = (byte)code;
Unsafe.Add(ref suffixRef, code) = (byte)code;
}
byte[] buffer = new byte[255];
@ -176,7 +163,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
if (oldCode == NullCode)
{
pixelStackSpan[top++] = suffixSpan[code];
Unsafe.Add(ref pixelStackRef, top++) = Unsafe.Add(ref suffixRef, code);
oldCode = code;
first = code;
continue;
@ -185,27 +172,27 @@ namespace SixLabors.ImageSharp.Formats.Gif
int inCode = code;
if (code == availableCode)
{
pixelStackSpan[top++] = (byte)first;
Unsafe.Add(ref pixelStackRef, top++) = (byte)first;
code = oldCode;
}
while (code > clearCode)
{
pixelStackSpan[top++] = suffixSpan[code];
code = prefixSpan[code];
Unsafe.Add(ref pixelStackRef, top++) = Unsafe.Add(ref suffixRef, code);
code = Unsafe.Add(ref prefixRef, code);
}
first = suffixSpan[code];
pixelStackSpan[top++] = suffixSpan[code];
int suffixCode = Unsafe.Add(ref suffixRef, code);
first = suffixCode;
Unsafe.Add(ref pixelStackRef, top++) = suffixCode;
// Fix for Gifs that have "deferred clear code" as per here :
// https://bugzilla.mozilla.org/show_bug.cgi?id=55918
if (availableCode < MaxStackSize)
{
prefixSpan[availableCode] = oldCode;
suffixSpan[availableCode] = first;
Unsafe.Add(ref prefixRef, availableCode) = oldCode;
Unsafe.Add(ref suffixRef, availableCode) = first;
availableCode++;
if (availableCode == codeMask + 1 && availableCode < MaxStackSize)
{
@ -221,25 +208,19 @@ namespace SixLabors.ImageSharp.Formats.Gif
top--;
// Clear missing pixels
pixels[xyz++] = (byte)pixelStackSpan[top];
Unsafe.Add(ref pixelsRef, xyz++) = (byte)Unsafe.Add(ref pixelStackRef, top);
}
}
/// <inheritdoc />
public void Dispose()
{
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
this.Dispose(true);
}
/// <summary>
/// Reads the next data block from the stream. A data block begins with a byte,
/// which defines the size of the block, followed by the block itself.
/// </summary>
/// <param name="buffer">The buffer to store the block in.</param>
/// <returns>
/// The <see cref="T:byte[]"/>.
/// The <see cref="int"/>.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int ReadBlock(byte[] buffer)
{
int bufferSize = this.stream.ReadByte();
@ -252,25 +233,12 @@ namespace SixLabors.ImageSharp.Formats.Gif
return count != bufferSize ? 0 : bufferSize;
}
/// <summary>
/// Disposes the object and frees resources for the Garbage Collector.
/// </summary>
/// <param name="disposing">If true, the object gets disposed.</param>
private void Dispose(bool disposing)
/// <inheritdoc />
public void Dispose()
{
if (this.isDisposed)
{
return;
}
if (disposing)
{
this.prefix?.Dispose();
this.suffix?.Dispose();
this.pixelStack?.Dispose();
}
this.isDisposed = true;
this.prefix.Dispose();
this.suffix.Dispose();
this.pixelStack.Dispose();
}
}
}

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

@ -2,9 +2,9 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Gif
@ -34,16 +34,6 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </remarks>
internal sealed class LzwEncoder : IDisposable
{
/// <summary>
/// The end-of-file marker
/// </summary>
private const int Eof = -1;
/// <summary>
/// The maximum number of bits.
/// </summary>
private const int Bits = 12;
/// <summary>
/// 80% occupancy
/// </summary>
@ -59,7 +49,17 @@ namespace SixLabors.ImageSharp.Formats.Gif
};
/// <summary>
/// The working pixel array
/// The maximium number of bits/code.
/// </summary>
private const int MaxBits = 12;
/// <summary>
/// Should NEVER generate this code.
/// </summary>
private const int MaxMaxCode = 1 << MaxBits;
/// <summary>
/// The working pixel array.
/// </summary>
private readonly byte[] pixelArray;
@ -84,42 +84,19 @@ namespace SixLabors.ImageSharp.Formats.Gif
private readonly byte[] accumulators = new byte[256];
/// <summary>
/// A value indicating whether this instance of the given entity has been disposed.
/// </summary>
/// <value><see langword="true"/> if this instance has been disposed; otherwise, <see langword="false"/>.</value>
/// <remarks>
/// If the entity is disposed, it must not be disposed a second
/// time. The isDisposed field is set the first time the entity
/// is disposed. If the isDisposed field is true, then the Dispose()
/// method will not dispose again. This help not to prolong the entity's
/// life in the Garbage Collector.
/// </remarks>
private bool isDisposed;
/// <summary>
/// The current pixel
/// The current position within the pixelArray.
/// </summary>
private int currentPixel;
private int position;
/// <summary>
/// Number of bits/code
/// </summary>
private int bitCount;
/// <summary>
/// User settable max # bits/code
/// </summary>
private int maxbits = Bits;
/// <summary>
/// maximum code, given bitCount
/// </summary>
private int maxcode;
/// <summary>
/// should NEVER generate this code
/// </summary>
private int maxmaxcode = 1 << Bits;
private int maxCode;
/// <summary>
/// For dynamic table sizing
@ -212,7 +189,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
// Write "initial code size" byte
stream.WriteByte((byte)this.initialCodeSize);
this.currentPixel = 0;
this.position = 0;
// Compress and write the pixel data
this.Compress(this.initialCodeSize + 1, stream);
@ -221,18 +198,12 @@ namespace SixLabors.ImageSharp.Formats.Gif
stream.WriteByte(GifConstants.Terminator);
}
/// <inheritdoc />
public void Dispose()
{
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
this.Dispose(true);
}
/// <summary>
/// Gets the maximum code value
/// Gets the maximum code value.
/// </summary>
/// <param name="bitCount">The number of bits</param>
/// <returns>See <see cref="int"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int GetMaxcode(int bitCount)
{
return (1 << bitCount) - 1;
@ -243,10 +214,12 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// flush the packet to disk.
/// </summary>
/// <param name="c">The character to add.</param>
/// <param name="accumulatorsRef">The reference to the storage for packat accumulators</param>
/// <param name="stream">The stream to write to.</param>
private void AddCharacter(byte c, Stream stream)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void AddCharacter(byte c, ref byte accumulatorsRef, Stream stream)
{
this.accumulators[this.accumulatorCount++] = c;
Unsafe.Add(ref accumulatorsRef, this.accumulatorCount++) = c;
if (this.accumulatorCount >= 254)
{
this.FlushPacket(stream);
@ -254,9 +227,10 @@ namespace SixLabors.ImageSharp.Formats.Gif
}
/// <summary>
/// Table clear for block compress
/// Table clear for block compress.
/// </summary>
/// <param name="stream">The output stream.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ClearBlock(Stream stream)
{
this.ResetCodeTable();
@ -269,15 +243,10 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary>
/// Reset the code table.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ResetCodeTable()
{
this.hashTable.Span.Fill(-1);
// Original code:
// for (int i = 0; i < size; ++i)
// {
// this.hashTable[i] = -1;
// }
}
/// <summary>
@ -293,13 +262,13 @@ namespace SixLabors.ImageSharp.Formats.Gif
int hsizeReg;
int hshift;
// Set up the globals: globalInitialBits - initial number of bits
// Set up the globals: globalInitialBits - initial number of bits
this.globalInitialBits = intialBits;
// Set up the necessary values
this.clearFlag = false;
this.bitCount = this.globalInitialBits;
this.maxcode = GetMaxcode(this.bitCount);
this.maxCode = GetMaxcode(this.bitCount);
this.clearCode = 1 << (intialBits - 1);
this.eofCode = this.clearCode + 1;
@ -309,6 +278,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
ent = this.NextPixel();
// TODO: PERF: It looks likt hshift could be calculated once statically.
hshift = 0;
for (fcode = this.hsize; fcode < 65536; fcode *= 2)
{
@ -323,22 +293,24 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.Output(this.clearCode, stream);
Span<int> hashTableSpan = this.hashTable.Span;
Span<int> codeTableSpan = this.codeTable.Span;
ref int hashTableRef = ref MemoryMarshal.GetReference(this.hashTable.Span);
ref int codeTableRef = ref MemoryMarshal.GetReference(this.codeTable.Span);
while ((c = this.NextPixel()) != Eof)
while (this.position < this.pixelArray.Length)
{
fcode = (c << this.maxbits) + ent;
c = this.NextPixel();
fcode = (c << MaxBits) + ent;
int i = (c << hshift) ^ ent /* = 0 */;
if (hashTableSpan[i] == fcode)
if (Unsafe.Add(ref hashTableRef, i) == fcode)
{
ent = codeTableSpan[i];
ent = Unsafe.Add(ref codeTableRef, i);
continue;
}
// Non-empty slot
if (hashTableSpan[i] >= 0)
if (Unsafe.Add(ref hashTableRef, i) >= 0)
{
int disp = hsizeReg - i;
if (i == 0)
@ -353,15 +325,15 @@ namespace SixLabors.ImageSharp.Formats.Gif
i += hsizeReg;
}
if (hashTableSpan[i] == fcode)
if (Unsafe.Add(ref hashTableRef, i) == fcode)
{
ent = codeTableSpan[i];
ent = Unsafe.Add(ref codeTableRef, i);
break;
}
}
while (hashTableSpan[i] >= 0);
while (Unsafe.Add(ref hashTableRef, i) >= 0);
if (hashTableSpan[i] == fcode)
if (Unsafe.Add(ref hashTableRef, i) == fcode)
{
continue;
}
@ -369,10 +341,10 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.Output(ent, stream);
ent = c;
if (this.freeEntry < this.maxmaxcode)
if (this.freeEntry < MaxMaxCode)
{
codeTableSpan[i] = this.freeEntry++; // code -> hashtable
hashTableSpan[i] = fcode;
Unsafe.Add(ref codeTableRef, i) = this.freeEntry++; // code -> hashtable
Unsafe.Add(ref hashTableRef, i) = fcode;
}
else
{
@ -387,34 +359,27 @@ namespace SixLabors.ImageSharp.Formats.Gif
}
/// <summary>
/// Flush the packet to disk, and reset the accumulator.
/// Flush the packet to disk and reset the accumulator.
/// </summary>
/// <param name="outStream">The output stream.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void FlushPacket(Stream outStream)
{
if (this.accumulatorCount > 0)
{
outStream.WriteByte((byte)this.accumulatorCount);
outStream.Write(this.accumulators, 0, this.accumulatorCount);
this.accumulatorCount = 0;
}
outStream.WriteByte((byte)this.accumulatorCount);
outStream.Write(this.accumulators, 0, this.accumulatorCount);
this.accumulatorCount = 0;
}
/// <summary>
/// Return the next pixel from the image
/// Reads the next pixel from the image.
/// </summary>
/// <returns>
/// The <see cref="int"/>
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int NextPixel()
{
if (this.currentPixel == this.pixelArray.Length)
{
return Eof;
}
this.currentPixel++;
return this.pixelArray[this.currentPixel - 1] & 0xff;
return this.pixelArray[this.position++] & 0xff;
}
/// <summary>
@ -424,6 +389,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <param name="outs">The stream to write to.</param>
private void Output(int code, Stream outs)
{
ref byte accumulatorsRef = ref MemoryMarshal.GetReference(this.accumulators.AsSpan());
this.currentAccumulator &= Masks[this.currentBits];
if (this.currentBits > 0)
@ -439,25 +405,25 @@ namespace SixLabors.ImageSharp.Formats.Gif
while (this.currentBits >= 8)
{
this.AddCharacter((byte)(this.currentAccumulator & 0xff), outs);
this.AddCharacter((byte)(this.currentAccumulator & 0xFF), ref accumulatorsRef, outs);
this.currentAccumulator >>= 8;
this.currentBits -= 8;
}
// If the next entry is going to be too big for the code size,
// then increase it, if possible.
if (this.freeEntry > this.maxcode || this.clearFlag)
if (this.freeEntry > this.maxCode || this.clearFlag)
{
if (this.clearFlag)
{
this.maxcode = GetMaxcode(this.bitCount = this.globalInitialBits);
this.maxCode = GetMaxcode(this.bitCount = this.globalInitialBits);
this.clearFlag = false;
}
else
{
++this.bitCount;
this.maxcode = this.bitCount == this.maxbits
? this.maxmaxcode
this.maxCode = this.bitCount == MaxBits
? MaxMaxCode
: GetMaxcode(this.bitCount);
}
}
@ -467,33 +433,23 @@ namespace SixLabors.ImageSharp.Formats.Gif
// At EOF, write the rest of the buffer.
while (this.currentBits > 0)
{
this.AddCharacter((byte)(this.currentAccumulator & 0xff), outs);
this.AddCharacter((byte)(this.currentAccumulator & 0xFF), ref accumulatorsRef, outs);
this.currentAccumulator >>= 8;
this.currentBits -= 8;
}
this.FlushPacket(outs);
if (this.accumulatorCount > 0)
{
this.FlushPacket(outs);
}
}
}
/// <summary>
/// Disposes the object and frees resources for the Garbage Collector.
/// </summary>
/// <param name="disposing">If true, the object gets disposed.</param>
private void Dispose(bool disposing)
/// <inheritdoc />
public void Dispose()
{
if (this.isDisposed)
{
return;
}
if (disposing)
{
this.hashTable?.Dispose();
this.codeTable?.Dispose();
}
this.isDisposed = true;
this.hashTable?.Dispose();
this.codeTable?.Dispose();
}
}
}

193
src/ImageSharp/Formats/Gif/PackedField.cs

@ -1,193 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
namespace SixLabors.ImageSharp.Formats.Gif
{
/// <summary>
/// Represents a byte of data in a GIF data stream which contains a number
/// of data items.
/// </summary>
internal readonly struct PackedField : IEquatable<PackedField>
{
/// <summary>
/// The individual bits representing the packed byte.
/// </summary>
private static readonly bool[] Bits = new bool[8];
/// <summary>
/// Gets the byte which represents the data items held in this instance.
/// </summary>
public byte Byte
{
get
{
int returnValue = 0;
int bitShift = 7;
foreach (bool bit in Bits)
{
int bitValue;
if (bit)
{
bitValue = 1 << bitShift;
}
else
{
bitValue = 0;
}
returnValue |= bitValue;
bitShift--;
}
return Convert.ToByte(returnValue & 0xFF);
}
}
/// <summary>
/// Returns a new <see cref="PackedField"/> with the bits in the packed fields to
/// the corresponding bits from the supplied byte.
/// </summary>
/// <param name="value">The value to pack.</param>
/// <returns>The <see cref="PackedField"/></returns>
public static PackedField FromInt(byte value)
{
PackedField packed = default(PackedField);
packed.SetBits(0, 8, value);
return packed;
}
/// <summary>
/// Sets the specified bit within the packed fields to the supplied
/// value.
/// </summary>
/// <param name="index">
/// The zero-based index within the packed fields of the bit to set.
/// </param>
/// <param name="valueToSet">
/// The value to set the bit to.
/// </param>
public void SetBit(int index, bool valueToSet)
{
if (index < 0 || index > 7)
{
string message = $"Index must be between 0 and 7. Supplied index: {index}";
throw new ArgumentOutOfRangeException(nameof(index), message);
}
Bits[index] = valueToSet;
}
/// <summary>
/// Sets the specified bits within the packed fields to the supplied
/// value.
/// </summary>
/// <param name="startIndex">The zero-based index within the packed fields of the first bit to set.</param>
/// <param name="length">The number of bits to set.</param>
/// <param name="valueToSet">The value to set the bits to.</param>
public void SetBits(int startIndex, int length, int valueToSet)
{
if (startIndex < 0 || startIndex > 7)
{
string message = $"Start index must be between 0 and 7. Supplied index: {startIndex}";
throw new ArgumentOutOfRangeException(nameof(startIndex), message);
}
if (length < 1 || startIndex + length > 8)
{
string message = "Length must be greater than zero and the sum of length and start index must be less than 8. "
+ $"Supplied length: {length}. Supplied start index: {startIndex}";
throw new ArgumentOutOfRangeException(nameof(length), message);
}
int bitShift = length - 1;
for (int i = startIndex; i < startIndex + length; i++)
{
int bitValueIfSet = 1 << bitShift;
int bitValue = valueToSet & bitValueIfSet;
int bitIsSet = bitValue >> bitShift;
Bits[i] = bitIsSet == 1;
bitShift--;
}
}
/// <summary>
/// Gets the value of the specified bit within the byte.
/// </summary>
/// <param name="index">The zero-based index of the bit to get.</param>
/// <returns>
/// The value of the specified bit within the byte.
/// </returns>
public bool GetBit(int index)
{
if (index < 0 || index > 7)
{
string message = $"Index must be between 0 and 7. Supplied index: {index}";
throw new ArgumentOutOfRangeException(nameof(index), message);
}
return Bits[index];
}
/// <summary>
/// Gets the value of the specified bits within the byte.
/// </summary>
/// <param name="startIndex">The zero-based index of the first bit to get.</param>
/// <param name="length">The number of bits to get.</param>
/// <returns>
/// The value of the specified bits within the byte.
/// </returns>
public int GetBits(int startIndex, int length)
{
if (startIndex < 0 || startIndex > 7)
{
string message = $"Start index must be between 0 and 7. Supplied index: {startIndex}";
throw new ArgumentOutOfRangeException(nameof(startIndex), message);
}
if (length < 1 || startIndex + length > 8)
{
string message = "Length must be greater than zero and the sum of length and start index must be less than 8. "
+ $"Supplied length: {length}. Supplied start index: {startIndex}";
throw new ArgumentOutOfRangeException(nameof(length), message);
}
int returnValue = 0;
int bitShift = length - 1;
for (int i = startIndex; i < startIndex + length; i++)
{
int bitValue = (Bits[i] ? 1 : 0) << bitShift;
returnValue += bitValue;
bitShift--;
}
return returnValue;
}
/// <inheritdoc/>
public override bool Equals(object obj)
{
return obj is PackedField other && this.Equals(other);
}
/// <inheritdoc/>
public bool Equals(PackedField other)
{
return this.Byte.Equals(other.Byte);
}
/// <inheritdoc/>
public override string ToString()
{
return $"PackedField [ Byte={this.Byte} ]";
}
/// <inheritdoc/>
public override int GetHashCode()
{
return this.Byte.GetHashCode();
}
}
}

106
src/ImageSharp/Formats/Gif/Sections/GifGraphicControlExtension.cs

@ -0,0 +1,106 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.Formats.Gif
{
/// <summary>
/// The Graphic Control Extension contains parameters used when
/// processing a graphic rendering block.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal readonly struct GifGraphicControlExtension : IGifExtension
{
public GifGraphicControlExtension(
byte packed,
ushort delayTime,
byte transparencyIndex)
{
this.BlockSize = 4;
this.Packed = packed;
this.DelayTime = delayTime;
this.TransparencyIndex = transparencyIndex;
}
/// <summary>
/// Gets the size of the block.
/// </summary>
public byte BlockSize { get; }
/// <summary>
/// Gets the packed disposalMethod and transparencyFlag value.
/// </summary>
public byte Packed { get; }
/// <summary>
/// Gets the delay time in of hundredths (1/100) of a second
/// to wait before continuing with the processing of the Data Stream.
/// The clock starts ticking immediately after the graphic is rendered.
/// </summary>
public ushort DelayTime { get; }
/// <summary>
/// Gets the transparency index.
/// The Transparency Index is such that when encountered, the corresponding pixel
/// of the display device is not modified and processing goes on to the next pixel.
/// </summary>
public byte TransparencyIndex { get; }
/// <summary>
/// Gets the disposal method which indicates the way in which the
/// graphic is to be treated after being displayed.
/// </summary>
public DisposalMethod DisposalMethod => (DisposalMethod)((this.Packed & 0x1C) >> 2);
/// <summary>
/// Gets a value indicating whether transparency flag is to be set.
/// This indicates whether a transparency index is given in the Transparent Index field.
/// </summary>
public bool TransparencyFlag => (this.Packed & 0x01) == 1;
byte IGifExtension.Label => GifConstants.GraphicControlLabel;
public int WriteTo(Span<byte> buffer)
{
ref GifGraphicControlExtension dest = ref Unsafe.As<byte, GifGraphicControlExtension>(ref MemoryMarshal.GetReference(buffer));
dest = this;
return 5;
}
public static GifGraphicControlExtension Parse(ReadOnlySpan<byte> buffer)
{
return MemoryMarshal.Cast<byte, GifGraphicControlExtension>(buffer)[0];
}
public static byte GetPackedValue(DisposalMethod disposalMethod, bool userInputFlag = false, bool transparencyFlag = false)
{
/*
Reserved | 3 Bits
Disposal Method | 3 Bits
User Input Flag | 1 Bit
Transparent Color Flag | 1 Bit
*/
byte value = 0;
value |= (byte)((int)disposalMethod << 2);
if (userInputFlag)
{
value |= 1 << 1;
}
if (transparencyFlag)
{
value |= 1;
}
return value;
}
}
}

40
src/ImageSharp/Formats/Gif/Sections/GifGraphicsControlExtension.cs

@ -1,40 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Gif
{
/// <summary>
/// The Graphic Control Extension contains parameters used when
/// processing a graphic rendering block.
/// </summary>
internal sealed class GifGraphicsControlExtension
{
/// <summary>
/// Gets or sets the disposal method which indicates the way in which the
/// graphic is to be treated after being displayed.
/// </summary>
public DisposalMethod DisposalMethod { get; set; }
/// <summary>
/// Gets or sets a value indicating whether transparency flag is to be set.
/// This indicates whether a transparency index is given in the Transparent Index field.
/// (This field is the least significant bit of the byte.)
/// </summary>
public bool TransparencyFlag { get; set; }
/// <summary>
/// Gets or sets the transparency index.
/// The Transparency Index is such that when encountered, the corresponding pixel
/// of the display device is not modified and processing goes on to the next pixel.
/// </summary>
public byte TransparencyIndex { get; set; }
/// <summary>
/// Gets or sets the delay time.
/// If not 0, this field specifies the number of hundredths (1/100) of a second to
/// wait before continuing with the processing of the Data Stream.
/// The clock starts ticking immediately after the graphic is rendered.
/// </summary>
public int DelayTime { get; set; }
}
}

107
src/ImageSharp/Formats/Gif/Sections/GifImageDescriptor.cs

@ -1,6 +1,10 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.Formats.Gif
{
/// <summary>
@ -9,49 +13,104 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// Each image must fit within the boundaries of the
/// Logical Screen, as defined in the Logical Screen Descriptor.
/// </summary>
internal sealed class GifImageDescriptor
[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal readonly struct GifImageDescriptor
{
public const int Size = 10;
public GifImageDescriptor(
ushort left,
ushort top,
ushort width,
ushort height,
byte packed)
{
this.Left = left;
this.Top = top;
this.Width = width;
this.Height = height;
this.Packed = packed;
}
/// <summary>
/// Gets or sets the column number, in pixels, of the left edge of the image,
/// Gets the column number, in pixels, of the left edge of the image,
/// with respect to the left edge of the Logical Screen.
/// Leftmost column of the Logical Screen is 0.
/// </summary>
public short Left { get; set; }
public ushort Left { get; }
/// <summary>
/// Gets or sets the row number, in pixels, of the top edge of the image with
/// Gets the row number, in pixels, of the top edge of the image with
/// respect to the top edge of the Logical Screen.
/// Top row of the Logical Screen is 0.
/// </summary>
public short Top { get; set; }
public ushort Top { get; }
/// <summary>
/// Gets or sets the width of the image in pixels.
/// Gets the width of the image in pixels.
/// </summary>
public short Width { get; set; }
public ushort Width { get; }
/// <summary>
/// Gets or sets the height of the image in pixels.
/// Gets the height of the image in pixels.
/// </summary>
public short Height { get; set; }
public ushort Height { get; }
/// <summary>
/// Gets or sets a value indicating whether the presence of a Local Color Table immediately
/// follows this Image Descriptor.
/// Gets the packed value of localColorTableFlag, interlaceFlag, sortFlag, and localColorTableSize.
/// </summary>
public bool LocalColorTableFlag { get; set; }
public byte Packed { get; }
/// <summary>
/// Gets or sets the local color table size.
/// If the Local Color Table Flag is set to 1, the value in this field
/// is used to calculate the number of bytes contained in the Local Color Table.
/// </summary>
public int LocalColorTableSize { get; set; }
public bool LocalColorTableFlag => ((this.Packed & 0x80) >> 7) == 1;
/// <summary>
/// Gets or sets a value indicating whether the image is to be interlaced.
/// An image is interlaced in a four-pass interlace pattern.
/// </summary>
public bool InterlaceFlag { get; set; }
public int LocalColorTableSize => 2 << (this.Packed & 0x07);
public bool InterlaceFlag => ((this.Packed & 0x40) >> 6) == 1;
public void WriteTo(Span<byte> buffer)
{
buffer[0] = GifConstants.ImageDescriptorLabel;
ref GifImageDescriptor dest = ref Unsafe.As<byte, GifImageDescriptor>(ref MemoryMarshal.GetReference(buffer.Slice(1)));
dest = this;
}
public static GifImageDescriptor Parse(ReadOnlySpan<byte> buffer)
{
return MemoryMarshal.Cast<byte, GifImageDescriptor>(buffer)[0];
}
public static byte GetPackedValue(bool localColorTableFlag, bool interfaceFlag, bool sortFlag, byte localColorTableSize)
{
/*
Local Color Table Flag | 1 Bit
Interlace Flag | 1 Bit
Sort Flag | 1 Bit
Reserved | 2 Bits
Size of Local Color Table | 3 Bits
*/
byte value = 0;
if (localColorTableFlag)
{
value |= 1 << 7;
}
if (interfaceFlag)
{
value |= 1 << 6;
}
if (sortFlag)
{
value |= 1 << 5;
}
value |= (byte)(localColorTableSize - 1);
return value;
}
}
}
}

113
src/ImageSharp/Formats/Gif/Sections/GifLogicalScreenDescriptor.cs

@ -1,6 +1,10 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.Formats.Gif
{
/// <summary>
@ -8,51 +12,122 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// necessary to define the area of the display device
/// within which the images will be rendered
/// </summary>
internal sealed class GifLogicalScreenDescriptor
[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal readonly struct GifLogicalScreenDescriptor
{
public const int Size = 7;
public GifLogicalScreenDescriptor(
ushort width,
ushort height,
byte packed,
byte backgroundColorIndex,
byte pixelAspectRatio = 0)
{
this.Width = width;
this.Height = height;
this.Packed = packed;
this.BackgroundColorIndex = backgroundColorIndex;
this.PixelAspectRatio = pixelAspectRatio;
}
/// <summary>
/// Gets or sets the width, in pixels, of the Logical Screen where the images will
/// Gets the width, in pixels, of the Logical Screen where the images will
/// be rendered in the displaying device.
/// </summary>
public short Width { get; set; }
public ushort Width { get; }
/// <summary>
/// Gets or sets the height, in pixels, of the Logical Screen where the images will be
/// Gets the height, in pixels, of the Logical Screen where the images will be
/// rendered in the displaying device.
/// </summary>
public short Height { get; set; }
public ushort Height { get; }
/// <summary>
/// Gets or sets the color depth, in number of bits per pixel.
/// Gets the packed value consisting of:
/// globalColorTableFlag, colorResolution, sortFlag, and sizeOfGlobalColorTable.
/// </summary>
public int BitsPerPixel { get; set; }
public byte Packed { get; }
/// <summary>
/// Gets or sets the index at the Global Color Table for the Background Color.
/// Gets the index at the Global Color Table for the Background Color.
/// The Background Color is the color used for those
/// pixels on the screen that are not covered by an image.
/// </summary>
public byte BackgroundColorIndex { get; set; }
public byte BackgroundColorIndex { get; }
/// <summary>
/// Gets or sets the pixel aspect ratio. Default to 0.
/// Gets the pixel aspect ratio.
/// </summary>
public byte PixelAspectRatio { get; set; }
public byte PixelAspectRatio { get; }
/// <summary>
/// Gets or sets a value indicating whether a flag denoting the presence of a Global Color Table
/// Gets a value indicating whether a flag denoting the presence of a Global Color Table
/// should be set.
/// If the flag is set, the Global Color Table will immediately
/// follow the Logical Screen Descriptor.
/// If the flag is set, the Global Color Table will included after
/// the Logical Screen Descriptor.
/// </summary>
public bool GlobalColorTableFlag { get; set; }
public bool GlobalColorTableFlag => ((this.Packed & 0x80) >> 7) == 1;
/// <summary>
/// Gets or sets the global color table size.
/// If the Global Color Table Flag is set to 1,
/// Gets the global color table size.
/// If the Global Color Table Flag is set,
/// the value in this field is used to calculate the number of
/// bytes contained in the Global Color Table.
/// </summary>
public int GlobalColorTableSize { get; set; }
public int GlobalColorTableSize => 2 << (this.Packed & 0x07);
/// <summary>
/// Gets the color depth, in number of bits per pixel.
/// The lowest 3 packed bits represent the bit depth minus 1.
/// </summary>
public int BitsPerPixel => (this.Packed & 0x07) + 1;
public void WriteTo(Span<byte> buffer)
{
ref GifLogicalScreenDescriptor dest = ref Unsafe.As<byte, GifLogicalScreenDescriptor>(ref MemoryMarshal.GetReference(buffer));
dest = this;
}
public static GifLogicalScreenDescriptor Parse(ReadOnlySpan<byte> buffer)
{
GifLogicalScreenDescriptor result = MemoryMarshal.Cast<byte, GifLogicalScreenDescriptor>(buffer)[0];
if (result.GlobalColorTableSize > 255 * 4)
{
throw new ImageFormatException($"Invalid gif colormap size '{result.GlobalColorTableSize}'");
}
return result;
}
public static byte GetPackedValue(bool globalColorTableFlag, int colorResolution, bool sortFlag, int globalColorTableSize)
{
/*
Global Color Table Flag | 1 Bit
Color Resolution | 3 Bits
Sort Flag | 1 Bit
Size of Global Color Table | 3 Bits
*/
byte value = 0;
if (globalColorTableFlag)
{
value |= 1 << 7;
}
value |= (byte)(colorResolution << 4);
if (sortFlag)
{
value |= 1 << 3;
}
value |= (byte)globalColorTableSize;
return value;
}
}
}
}

22
src/ImageSharp/Formats/Gif/Sections/IGifExtension.cs

@ -0,0 +1,22 @@
using System;
namespace SixLabors.ImageSharp.Formats.Gif
{
/// <summary>
/// A base interface for GIF extensions.
/// </summary>
public interface IGifExtension
{
/// <summary>
/// Gets the label identifying the extensions.
/// </summary>
byte Label { get; }
/// <summary>
/// Writes the extension data to the buffer.
/// </summary>
/// <param name="buffer">The buffer to write the extension to.</param>
/// <returns>The number of bytes written to the buffer.</returns>
int WriteTo(Span<byte> buffer);
}
}

4
src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs

@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
public Block8x8(Span<short> coefficients)
{
ref byte selfRef = ref Unsafe.As<Block8x8, byte>(ref this);
ref byte sourceRef = ref MemoryMarshal.GetReference(coefficients.NonPortableCast<short, byte>());
ref byte sourceRef = ref MemoryMarshal.GetReference(MemoryMarshal.Cast<short, byte>(coefficients));
Unsafe.CopyBlock(ref selfRef, ref sourceRef, Size * sizeof(short));
}
@ -205,7 +205,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
public void CopyTo(Span<short> destination)
{
ref byte selfRef = ref Unsafe.As<Block8x8, byte>(ref this);
ref byte destRef = ref MemoryMarshal.GetReference(destination.NonPortableCast<short, byte>());
ref byte destRef = ref MemoryMarshal.GetReference(MemoryMarshal.Cast<short, byte>(destination));
Unsafe.CopyBlock(ref destRef, ref selfRef, Size * sizeof(short));
}

6
src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs

@ -353,13 +353,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
/// <param name="qtPtr">Qt pointer</param>
/// <param name="unzigPtr">Unzig pointer</param>
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void DequantizeBlock(Block8x8F* blockPtr, Block8x8F* qtPtr, int* unzigPtr)
public static unsafe void DequantizeBlock(Block8x8F* blockPtr, Block8x8F* qtPtr, byte* unzigPtr)
{
float* b = (float*)blockPtr;
float* qtp = (float*)qtPtr;
for (int qtIndex = 0; qtIndex < Size; qtIndex++)
{
int blockIndex = unzigPtr[qtIndex];
byte blockIndex = unzigPtr[qtIndex];
float* unzigPos = b + blockIndex;
float val = *unzigPos;
@ -381,7 +381,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
Block8x8F* block,
Block8x8F* dest,
Block8x8F* qt,
int* unzigPtr)
byte* unzigPtr)
{
float* s = (float*)block;
float* d = (float*)dest;

16
src/ImageSharp/Formats/Jpeg/Common/Decoder/ComponentUtils.cs

@ -1,16 +0,0 @@
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
{
/// <summary>
/// Various utilities for <see cref="IJpegComponent"/>.
/// </summary>
internal static class ComponentUtils
{
/// <summary>
/// Gets a reference to the <see cref="Block8x8"/> at the given row and column index from <see cref="IJpegComponent.SpectralBlocks"/>
/// </summary>
public static ref Block8x8 GetBlockReference(this IJpegComponent component, int bx, int by)
{
return ref component.SpectralBlocks[bx, by];
}
}
}

8
src/ImageSharp/Formats/Jpeg/Common/Decoder/IJpegComponent.cs

@ -42,5 +42,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
/// We need to apply IDCT and dequantiazition to transform them into color-space blocks.
/// </summary>
Buffer2D<Block8x8> SpectralBlocks { get; }
/// <summary>
/// Gets a reference to the <see cref="Block8x8"/> at the given row and column index from <see cref="SpectralBlocks"/>
/// </summary>
/// <param name="column">The column</param>
/// <param name="row">The row</param>
/// <returns>The <see cref="Block8x8"/></returns>
ref Block8x8 GetBlockReference(int column, int row);
}
}

6
src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegBlockPostProcessor.cs

@ -47,9 +47,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
this.DequantiazationTable = ZigZag.CreateDequantizationTable(ref decoder.QuantizationTables[qtIndex]);
this.subSamplingDivisors = component.SubSamplingDivisors;
this.SourceBlock = default(Block8x8F);
this.WorkspaceBlock1 = default(Block8x8F);
this.WorkspaceBlock2 = default(Block8x8F);
this.SourceBlock = default;
this.WorkspaceBlock1 = default;
this.WorkspaceBlock2 = default;
}
/// <summary>

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

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Text;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
{
@ -13,22 +14,22 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
/// <summary>
/// Describes the EXIF specific markers
/// </summary>
public static readonly byte[] JFifMarker = ToAsciiBytes("JFIF\0");
public static readonly byte[] JFifMarker = Encoding.UTF8.GetBytes("JFIF\0");
/// <summary>
/// Describes the EXIF specific markers
/// </summary>
public static readonly byte[] IccMarker = ToAsciiBytes("ICC_PROFILE\0");
public static readonly byte[] IccMarker = Encoding.UTF8.GetBytes("ICC_PROFILE\0");
/// <summary>
/// Describes the ICC specific markers
/// </summary>
public static readonly byte[] ExifMarker = ToAsciiBytes("Exif\0\0");
public static readonly byte[] ExifMarker = Encoding.UTF8.GetBytes("Exif\0\0");
/// <summary>
/// Describes Adobe specific markers <see href="http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#Adobe"/>
/// </summary>
public static readonly byte[] AdobeMarker = ToAsciiBytes("Adobe");
public static readonly byte[] AdobeMarker = Encoding.UTF8.GetBytes("Adobe");
/// <summary>
/// Returns a value indicating whether the passed bytes are a match to the profile identifer
@ -41,19 +42,5 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
return bytesToCheck.Length >= profileIdentifier.Length
&& bytesToCheck.Slice(0, profileIdentifier.Length).SequenceEqual(profileIdentifier);
}
// No Encoding.ASCII nor Linq.Select on NetStandard 1.1
private static byte[] ToAsciiBytes(string str)
{
int length = str.Length;
byte[] bytes = new byte[length];
char[] chars = str.ToCharArray();
for (int i = 0; i < length; i++)
{
bytes[i] = (byte)chars[i];
}
return bytes;
}
}
}

48
src/ImageSharp/Formats/Jpeg/Common/ZigZag.cs

@ -1,6 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common
@ -11,25 +12,52 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
/// unzig[3] is the column and row of the fourth element in zigzag order. The
/// value is 16, which means first column (16%8 == 0) and third row (16/8 == 2).
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal unsafe struct ZigZag
{
/// <summary>
/// Copy of <see cref="Unzig"/> in a value type
/// </summary>
public fixed int Data[64];
public fixed byte Data[64];
/// <summary>
/// Unzig maps from the zigzag ordering to the natural ordering. For example,
/// unzig[3] is the column and row of the fourth element in zigzag order. The
/// value is 16, which means first column (16%8 == 0) and third row (16/8 == 2).
/// </summary>
private static readonly int[] Unzig =
private static readonly byte[] Unzig =
{
0,
1, 8,
16, 9, 2,
3, 10, 17, 24,
32, 25, 18, 11, 4,
5, 12, 19, 26, 33, 40,
48, 41, 34, 27, 20, 13, 6,
7, 14, 21, 28, 35, 42, 49, 56,
57, 50, 43, 36, 29, 22, 15,
23, 30, 37, 44, 51, 58,
59, 52, 45, 38, 31,
39, 46, 53, 60,
61, 54, 47,
55, 62,
63
};
/// <summary>
/// Returns the value at the given index
/// </summary>
/// <param name="idx">The index</param>
/// <returns>The <see cref="byte"/></returns>
public byte this[int idx]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33,
40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50,
43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46,
53, 60, 61, 54, 47, 55, 62, 63,
};
ref byte self = ref Unsafe.As<ZigZag, byte>(ref this);
return Unsafe.Add(ref self, idx);
}
}
/// <summary>
/// Creates and fills an instance of <see cref="ZigZag"/> with Jpeg unzig indices
@ -37,8 +65,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
/// <returns>The new instance</returns>
public static ZigZag CreateUnzigTable()
{
ZigZag result = default(ZigZag);
int* unzigPtr = result.Data;
ZigZag result = default;
byte* unzigPtr = result.Data;
Marshal.Copy(Unzig, 0, (IntPtr)unzigPtr, 64);
return result;
}
@ -48,7 +76,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
/// </summary>
public static Block8x8F CreateDequantizationTable(ref Block8x8F qt)
{
Block8x8F result = default(Block8x8F);
Block8x8F result = default;
for (int i = 0; i < 64; i++)
{

7
src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigComponent.cs

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Formats.Jpeg.Common;
using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder;
using SixLabors.ImageSharp.Memory;
@ -237,6 +238,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
this.SamplingFactors = new Size(h, v);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref Block8x8 GetBlockReference(int column, int row)
{
return ref this.SpectralBlocks[column, row];
}
public void Dispose()
{
this.SpectralBlocks.Dispose();

4
src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.DataPointers.cs

@ -21,9 +21,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
public Block8x8* Block;
/// <summary>
/// Pointer to <see cref="ComputationData.Unzig"/> as int*
/// Pointer to <see cref="ComputationData.Unzig"/> as byte*
/// </summary>
public int* Unzig;
public byte* Unzig;
/// <summary>
/// Pointer to <see cref="ComputationData.ScanData"/> as Scan*

2
src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs

@ -489,7 +489,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
Block8x8F* tempDest1,
Block8x8F* tempDest2,
Block8x8F* quant,
int* unzigPtr)
byte* unzigPtr)
{
FastFloatingPointDCT.TransformFDCT(ref *src, ref *tempDest1, ref *tempDest2);

2
src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoder.cs

@ -31,7 +31,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
/// <inheritdoc/>
public IImageInfo Identify(Configuration configuration, Stream stream)
{
Guard.NotNull(stream, "stream");
Guard.NotNull(stream, nameof(stream));
using (var decoder = new OrigJpegDecoderCore(configuration, this))
{

7
src/ImageSharp/Formats/Jpeg/IJpegDecoderOptions.cs

@ -1,11 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Generic;
using System.IO;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Jpeg
{
/// <summary>
@ -18,4 +13,4 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// </summary>
bool IgnoreMetadata { get; }
}
}
}

7
src/ImageSharp/Formats/Jpeg/IJpegEncoderOptions.cs

@ -1,11 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Generic;
using System.IO;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Jpeg
{
/// <summary>
@ -31,4 +26,4 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <value>The subsample ratio of the jpg image.</value>
JpegSubsample? Subsample { get; }
}
}
}

2
src/ImageSharp/Formats/Jpeg/ImageExtensions.cs

@ -1,10 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.IO;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.PixelFormats;

2
src/ImageSharp/Formats/Jpeg/JpegConfigurationModule.cs

@ -17,4 +17,4 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
config.ImageFormatsManager.AddImageFormatDetector(new JpegImageFormatDetector());
}
}
}
}

55
src/ImageSharp/Formats/Jpeg/JpegImageFormatDetector.cs

@ -16,12 +16,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <inheritdoc/>
public IImageFormat DetectFormat(ReadOnlySpan<byte> header)
{
if (this.IsSupportedFileFormat(header))
{
return ImageFormats.Jpeg;
}
return null;
return this.IsSupportedFileFormat(header) ? ImageFormats.Jpeg : null;
}
private bool IsSupportedFileFormat(ReadOnlySpan<byte> header)
@ -35,36 +30,24 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// </summary>
/// <param name="header">The bytes representing the file header.</param>
/// <returns>The <see cref="bool"/></returns>
private bool IsJfif(ReadOnlySpan<byte> header)
{
// TODO: This should be in constants
bool isJfif =
header[6] == 0x4A && // J
header[7] == 0x46 && // F
header[8] == 0x49 && // I
header[9] == 0x46 && // F
header[10] == 0x00;
return isJfif;
}
private bool IsJfif(ReadOnlySpan<byte> header) =>
header[6] == 0x4A && // J
header[7] == 0x46 && // F
header[8] == 0x49 && // I
header[9] == 0x46 && // F
header[10] == 0x00;
/// <summary>
/// Returns a value indicating whether the given bytes identify EXIF data.
/// </summary>
/// <param name="header">The bytes representing the file header.</param>
/// <returns>The <see cref="bool"/></returns>
private bool IsExif(ReadOnlySpan<byte> header)
{
// TODO: This should be in constants
bool isExif =
header[6] == 0x45 && // E
header[7] == 0x78 && // X
header[8] == 0x69 && // I
header[9] == 0x66 && // F
header[10] == 0x00;
return isExif;
}
private bool IsExif(ReadOnlySpan<byte> header) =>
header[6] == 0x45 && // E
header[7] == 0x78 && // X
header[8] == 0x69 && // I
header[9] == 0x66 && // F
header[10] == 0x00;
/// <summary>
/// Returns a value indicating whether the given bytes identify Jpeg data.
@ -72,14 +55,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// </summary>
/// <param name="header">The bytes representing the file header.</param>
/// <returns>The <see cref="bool"/></returns>
private bool IsJpeg(ReadOnlySpan<byte> header)
{
// TODO: This should be in constants
bool isJpg =
header[0] == 0xFF && // 255
header[1] == 0xD8; // 216
return isJpg;
}
private bool IsJpeg(ReadOnlySpan<byte> header) =>
header[0] == 0xFF && // 255
header[1] == 0xD8; // 216
}
}

24
src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedByteBuffer256.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 FixedByteBuffer256
{
public fixed byte Data[256];
public byte this[int idx]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
ref byte self = ref Unsafe.As<FixedByteBuffer256, byte>(ref this);
return Unsafe.Add(ref self, idx);
}
}
}
}

24
src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedInt16Buffer18.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 FixedInt16Buffer18
{
public fixed short Data[18];
public short this[int idx]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
ref short self = ref Unsafe.As<FixedInt16Buffer18, short>(ref this);
return Unsafe.Add(ref self, idx);
}
}
}
}

24
src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedInt16Buffer256.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 FixedInt16Buffer256
{
public fixed short Data[256];
public short this[int idx]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
ref short self = ref Unsafe.As<FixedInt16Buffer256, short>(ref this);
return Unsafe.Add(ref self, idx);
}
}
}
}

24
src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedInt64Buffer18.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 FixedInt64Buffer18
{
public fixed long Data[18];
public long this[int idx]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
ref long self = ref Unsafe.As<FixedInt64Buffer18, long>(ref this);
return Unsafe.Add(ref self, idx);
}
}
}
}

43
src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsComponent.cs

@ -1,43 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
{
/// <summary>
/// Represents a component block
/// </summary>
internal class PdfJsComponent : IDisposable
{
#pragma warning disable SA1401
/// <summary>
/// Gets or sets the output
/// </summary>
public IBuffer<short> Output;
/// <summary>
/// Gets or sets the scaling factors
/// </summary>
public Vector2 Scale;
/// <summary>
/// Gets or sets the number of blocks per line
/// </summary>
public int BlocksPerLine;
/// <summary>
/// Gets or sets the number of blocks per column
/// </summary>
public int BlocksPerColumn;
/// <inheritdoc/>
public void Dispose()
{
this.Output?.Dispose();
this.Output = null;
}
}
}

32
src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsComponentBlocks.cs

@ -1,32 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
{
/// <summary>
/// Contains all the decoded component blocks
/// </summary>
internal sealed class PdfJsComponentBlocks : IDisposable
{
/// <summary>
/// Gets or sets the component blocks
/// </summary>
public PdfJsComponent[] Components { get; set; }
/// <inheritdoc/>
public void Dispose()
{
if (this.Components != null)
{
for (int i = 0; i < this.Components.Length; i++)
{
this.Components[i].Dispose();
}
this.Components = null;
}
}
}
}

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

@ -3,6 +3,7 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Formats.Jpeg.Common;
using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder;
using SixLabors.ImageSharp.Memory;
@ -16,7 +17,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
internal class PdfJsFrameComponent : IDisposable, IJpegComponent
{
private readonly MemoryManager memoryManager;
#pragma warning disable SA1401 // Fields should be private
public PdfJsFrameComponent(MemoryManager memoryManager, PdfJsFrame frame, byte id, int horizontalFactor, int verticalFactor, byte quantizationTableIndex, int index)
{
@ -25,6 +25,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
this.Id = id;
this.HorizontalSamplingFactor = horizontalFactor;
this.VerticalSamplingFactor = verticalFactor;
this.SamplingFactors = new Size(this.HorizontalSamplingFactor, this.VerticalSamplingFactor);
this.QuantizationTableIndex = quantizationTableIndex;
this.Index = index;
}
@ -49,25 +50,23 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
/// </summary>
public int VerticalSamplingFactor { get; }
Buffer2D<Block8x8> IJpegComponent.SpectralBlocks => throw new NotImplementedException();
/// <inheritdoc />
public Buffer2D<Block8x8> SpectralBlocks { get; private set; }
// TODO: Should be derived from PdfJsComponent.Scale
public Size SubSamplingDivisors => throw new NotImplementedException();
/// <inheritdoc />
public Size SubSamplingDivisors { get; private set; }
/// <inheritdoc />
public int QuantizationTableIndex { get; }
/// <summary>
/// Gets the block data
/// </summary>
public IBuffer<short> BlockData { get; private set; }
/// <inheritdoc />
public int Index { get; }
public Size SizeInBlocks => new Size(this.WidthInBlocks, this.HeightInBlocks);
/// <inheritdoc />
public Size SizeInBlocks { get; private set; }
public Size SamplingFactors => new Size(this.HorizontalSamplingFactor, this.VerticalSamplingFactor);
/// <inheritdoc />
public Size SamplingFactors { get; set; }
/// <summary>
/// Gets the number of blocks per line
@ -89,17 +88,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
/// </summary>
public int ACHuffmanTableId { get; set; }
internal int BlocksPerLineForMcu { get; private set; }
internal int BlocksPerColumnForMcu { get; private set; }
public PdfJsFrame Frame { get; }
/// <inheritdoc/>
public void Dispose()
{
this.BlockData?.Dispose();
this.BlockData = null;
this.SpectralBlocks?.Dispose();
this.SpectralBlocks = null;
}
public void Init()
@ -110,25 +105,43 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
this.HeightInBlocks = (int)MathF.Ceiling(
MathF.Ceiling(this.Frame.Scanlines / 8F) * this.VerticalSamplingFactor / this.Frame.MaxVerticalFactor);
this.BlocksPerLineForMcu = this.Frame.McusPerLine * this.HorizontalSamplingFactor;
this.BlocksPerColumnForMcu = this.Frame.McusPerColumn * this.VerticalSamplingFactor;
int blocksBufferSize = 64 * this.BlocksPerColumnForMcu * (this.BlocksPerLineForMcu + 1);
// Pooled. Disposed via frame disposal
this.BlockData = this.memoryManager.Allocate<short>(blocksBufferSize, true);
int blocksPerLineForMcu = this.Frame.McusPerLine * this.HorizontalSamplingFactor;
int blocksPerColumnForMcu = this.Frame.McusPerColumn * this.VerticalSamplingFactor;
this.SizeInBlocks = new Size(blocksPerLineForMcu, blocksPerColumnForMcu);
// For 4-component images (either CMYK or YCbCrK), we only support two
// hv vectors: [0x11 0x11 0x11 0x11] and [0x22 0x11 0x11 0x22].
// Theoretically, 4-component JPEG images could mix and match hv values
// but in practice, those two combinations are the only ones in use,
// and it simplifies the applyBlack code below if we can assume that:
// - for CMYK, the C and K channels have full samples, and if the M
// and Y channels subsample, they subsample both horizontally and
// vertically.
// - for YCbCrK, the Y and K channels have full samples.
if (this.Index == 0 || this.Index == 3)
{
this.SubSamplingDivisors = new Size(1, 1);
}
else
{
PdfJsFrameComponent c0 = this.Frame.Components[0];
this.SubSamplingDivisors = c0.SamplingFactors.DivideBy(this.SamplingFactors);
}
this.SpectralBlocks = this.memoryManager.Allocate2D<Block8x8>(blocksPerColumnForMcu, blocksPerLineForMcu + 1, true);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int GetBlockBufferOffset(int row, int col)
public ref Block8x8 GetBlockReference(int column, int row)
{
return 64 * (((this.WidthInBlocks + 1) * row) + col);
int offset = ((this.WidthInBlocks + 1) * row) + column;
return ref Unsafe.Add(ref MemoryMarshal.GetReference(this.SpectralBlocks.Span), offset);
}
public Span<short> GetBlockBuffer(int row, int col)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int GetBlockBufferOffset(int row, int col)
{
int offset = this.GetBlockBufferOffset(row, col);
return this.BlockData.Span.Slice(offset, 64);
return 64 * (((this.WidthInBlocks + 1) * row) + col);
}
}
}

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

@ -3,6 +3,7 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
@ -10,100 +11,64 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
/// <summary>
/// Represents a Huffman Table
/// </summary>
internal struct PdfJsHuffmanTable : IDisposable
[StructLayout(LayoutKind.Sequential)]
internal unsafe struct PdfJsHuffmanTable
{
private BasicArrayBuffer<short> lookahead;
private BasicArrayBuffer<short> valOffset;
private BasicArrayBuffer<long> maxcode;
private IManagedByteBuffer huffval;
/// <summary>
/// Initializes a new instance of the <see cref="PdfJsHuffmanTable"/> struct.
/// </summary>
/// <param name="memoryManager">The <see cref="MemoryManager"/> to use for buffer allocations.</param>
/// <param name="lengths">The code lengths</param>
/// <param name="values">The huffman values</param>
public PdfJsHuffmanTable(MemoryManager memoryManager, byte[] lengths, byte[] values)
{
// TODO: Replace FakeBuffer<T> usages with standard or array orfixed-sized arrays
this.lookahead = memoryManager.AllocateFake<short>(256);
this.valOffset = memoryManager.AllocateFake<short>(18);
this.maxcode = memoryManager.AllocateFake<long>(18);
using (IBuffer<short> huffsize = memoryManager.Allocate<short>(257))
using (IBuffer<short> huffcode = memoryManager.Allocate<short>(257))
{
GenerateSizeTable(lengths, huffsize.Span);
GenerateCodeTable(huffsize.Span, huffcode.Span);
GenerateDecoderTables(lengths, huffcode.Span, this.valOffset.Span, this.maxcode.Span);
GenerateLookaheadTables(lengths, values, this.lookahead.Span);
}
this.huffval = memoryManager.AllocateManagedByteBuffer(values.Length, true);
Buffer.BlockCopy(values, 0, this.huffval.Array, 0, values.Length);
this.MaxCode = this.maxcode.Array;
this.ValOffset = this.valOffset.Array;
this.HuffVal = this.huffval.Array;
this.Lookahead = this.lookahead.Array;
}
/// <summary>
/// Gets the max code array
/// </summary>
public long[] MaxCode
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get;
}
public FixedInt64Buffer18 MaxCode;
/// <summary>
/// Gets the value offset array
/// </summary>
public short[] ValOffset
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get;
}
public FixedInt16Buffer18 ValOffset;
/// <summary>
/// Gets the huffman value array
/// </summary>
public byte[] HuffVal
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get;
}
public FixedByteBuffer256 HuffVal;
/// <summary>
/// Gets the lookahead array
/// </summary>
public short[] Lookahead
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get;
}
public FixedInt16Buffer256 Lookahead;
/// <inheritdoc/>
public void Dispose()
/// <summary>
/// Initializes a new instance of the <see cref="PdfJsHuffmanTable"/> struct.
/// </summary>
/// <param name="memoryManager">The <see cref="MemoryManager"/> to use for buffer allocations.</param>
/// <param name="lengths">The code lengths</param>
/// <param name="values">The huffman values</param>
public PdfJsHuffmanTable(MemoryManager memoryManager, ReadOnlySpan<byte> lengths, ReadOnlySpan<byte> values)
{
this.lookahead?.Dispose();
this.valOffset?.Dispose();
this.maxcode?.Dispose();
this.huffval?.Dispose();
this.lookahead = null;
this.valOffset = null;
this.maxcode = null;
this.huffval = null;
const int length = 257;
using (IBuffer<short> huffsize = memoryManager.Allocate<short>(length))
using (IBuffer<short> huffcode = memoryManager.Allocate<short>(length))
{
ref short huffsizeRef = ref MemoryMarshal.GetReference(huffsize.Span);
ref short huffcodeRef = ref MemoryMarshal.GetReference(huffcode.Span);
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="huffsize">The huffman size span</param>
private static void GenerateSizeTable(byte[] lengths, Span<short> huffsize)
/// <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++)
@ -111,29 +76,30 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
byte i = lengths[l];
for (short j = 0; j < i; j++)
{
huffsize[index] = l;
Unsafe.Add(ref huffsizeRef, index) = l;
index++;
}
}
huffsize[index] = 0;
Unsafe.Add(ref huffsizeRef, index) = 0;
}
/// <summary>
/// Figure C.2: generate the codes themselves
/// </summary>
/// <param name="huffsize">The huffman size span</param>
/// <param name="huffcode">The huffman code span</param>
private static void GenerateCodeTable(Span<short> huffsize, Span<short> huffcode)
/// <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 = huffsize[0];
short si = huffsizeRef;
short code = 0;
for (short i = 0; i < huffsize.Length; i++)
for (short i = 0; i < length; i++)
{
while (huffsize[k] == si)
while (Unsafe.Add(ref huffsizeRef, k) == si)
{
huffcode[k] = code;
Unsafe.Add(ref huffcodeRef, k) = code;
code++;
k++;
}
@ -147,30 +113,31 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
/// Figure F.15: generate decoding tables for bit-sequential decoding
/// </summary>
/// <param name="lengths">The code lengths</param>
/// <param name="huffcode">The huffman code span</param>
/// <param name="valOffset">The value offset span</param>
/// <param name="maxcode">The max code span</param>
private static void GenerateDecoderTables(byte[] lengths, Span<short> huffcode, Span<short> valOffset, Span<long> maxcode)
/// <param name="huffcodeRef">The huffman code span ref</param>
private void GenerateDecoderTables(ReadOnlySpan<byte> lengths, ref short huffcodeRef)
{
short bitcount = 0;
for (int i = 1; i <= 16; i++)
fixed (short* valOffsetRef = this.ValOffset.Data)
fixed (long* maxcodeRef = this.MaxCode.Data)
{
if (lengths[i] != 0)
{
// valoffset[l] = huffval[] index of 1st symbol of code length i,
// minus the minimum code of length i
valOffset[i] = (short)(bitcount - huffcode[bitcount]);
bitcount += lengths[i];
maxcode[i] = huffcode[bitcount - 1]; // maximum code of length i
}
else
short bitcount = 0;
for (int i = 1; i <= 16; i++)
{
maxcode[i] = -1; // -1 if no codes of this length
if (lengths[i] != 0)
{
// 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
}
}
}
valOffset[17] = 0;
maxcode[17] = 0xFFFFFL;
valOffsetRef[17] = 0;
maxcodeRef[17] = 0xFFFFFL;
}
}
/// <summary>
@ -178,32 +145,33 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
/// </summary>
/// <param name="lengths">The code lengths</param>
/// <param name="huffval">The huffman value array</param>
/// <param name="lookahead">The lookahead span</param>
private static void GenerateLookaheadTables(byte[] lengths, byte[] huffval, Span<short> lookahead)
/// <param name="huffcodeRef">The huffman code span ref</param>
private void GenerateLookaheadTables(ReadOnlySpan<byte> lengths, ReadOnlySpan<byte> huffval, ref short huffcodeRef)
{
int x = 0, code = 0;
for (int i = 0; i < 8; i++)
// 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)
{
code <<= 1;
var lookaheadSpan = new Span<short>(lookaheadRef, 256);
for (int j = 0; j < lengths[i + 1]; j++)
lookaheadSpan.Fill(2034); // 9 << 8;
int p = 0;
for (int l = 1; l <= 8; l++)
{
// The codeLength is 1+i, so shift code by 8-(1+i) to
// calculate the high bits for every 8-bit sequence
// whose codeLength's high bits matches code.
// The high 8 bits of lutValue are the encoded value.
// The low 8 bits are 1 plus the codeLength.
byte base2 = (byte)(code << (7 - i));
short lutValue = (short)((short)(huffval[x] << 8) | (short)(2 + i));
for (int k = 0; k < 1 << (7 - i); k++)
for (int i = 1; i <= lengths[l]; i++, p++)
{
lookahead[base2 | k] = lutValue;
// 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--)
{
lookaheadRef[lookBits] = (short)((l << 8) | huffval[p]);
lookBits++;
}
}
code++;
x++;
}
}
}

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

@ -1,16 +1,15 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
{
/// <summary>
/// Defines a pair of huffman tables
/// Defines a 2 pairs of huffman tables
/// </summary>
internal sealed class PdfJsHuffmanTables : IDisposable
internal sealed class PdfJsHuffmanTables
{
private readonly PdfJsHuffmanTable[] tables = new PdfJsHuffmanTable[4];
@ -22,19 +21,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
public ref PdfJsHuffmanTable this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return ref this.tables[index];
}
}
/// <inheritdoc/>
public void Dispose()
{
for (int i = 0; i < this.tables.Length; i++)
{
this.tables[i].Dispose();
}
get => ref this.tables[index];
}
}
}

513
src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsIDCT.cs

@ -1,513 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
{
/// <summary>
/// Performs the inverse Descrete Cosine Transform on each frame component.
/// </summary>
internal static class PdfJsIDCT
{
/// <summary>
/// Precomputed values scaled up by 14 bits
/// </summary>
public static readonly short[] Aanscales =
{
16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, 22725, 31521, 29692, 26722, 22725, 17855,
12299, 6270, 21407, 29692, 27969, 25172, 21407, 16819, 11585,
5906, 19266, 26722, 25172, 22654, 19266, 15137, 10426, 5315,
16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, 12873,
17855, 16819, 15137, 12873, 10114, 6967, 3552, 8867, 12299,
11585, 10426, 8867, 6967, 4799, 2446, 4520, 6270, 5906, 5315,
4520, 3552, 2446, 1247
};
private const int DctCos1 = 4017; // cos(pi/16)
private const int DctSin1 = 799; // sin(pi/16)
private const int DctCos3 = 3406; // cos(3*pi/16)
private const int DctSin3 = 2276; // sin(3*pi/16)
private const int DctCos6 = 1567; // cos(6*pi/16)
private const int DctSin6 = 3784; // sin(6*pi/16)
private const int DctSqrt2 = 5793; // sqrt(2)
private const int DctSqrt1D2 = 2896; // sqrt(2) / 2
#pragma warning disable SA1310 // Field names must not contain underscore
private const int FIX_1_082392200 = 277; // FIX(1.082392200)
private const int FIX_1_414213562 = 362; // FIX(1.414213562)
private const int FIX_1_847759065 = 473; // FIX(1.847759065)
private const int FIX_2_613125930 = 669; // FIX(2.613125930)
#pragma warning restore SA1310 // Field names must not contain underscore
private const int ConstBits = 8;
private const int Pass1Bits = 2; // Factional bits in scale factors
private const int MaxJSample = 255;
private const int CenterJSample = 128;
private const int RangeCenter = (MaxJSample * 2) + 2;
// First segment of range limit table: limit[x] = 0 for x < 0
// allow negative subscripts of simple table
private const int TableOffset = 2 * (MaxJSample + 1);
private const int LimitOffset = TableOffset - (RangeCenter - CenterJSample);
// Each IDCT routine is responsible for range-limiting its results and
// converting them to unsigned form (0..MaxJSample). The raw outputs could
// be quite far out of range if the input data is corrupt, so a bulletproof
// range-limiting step is required. We use a mask-and-table-lookup method
// to do the combined operations quickly, assuming that MaxJSample+1
// is a power of 2.
private const int RangeMask = (MaxJSample * 4) + 3; // 2 bits wider than legal samples
private static readonly byte[] Limit = new byte[5 * (MaxJSample + 1)];
static PdfJsIDCT()
{
// Main part of range limit table: limit[x] = x
int i;
for (i = 0; i <= MaxJSample; i++)
{
Limit[TableOffset + i] = (byte)i;
}
// End of range limit table: Limit[x] = MaxJSample for x > MaxJSample
for (; i < 3 * (MaxJSample + 1); i++)
{
Limit[TableOffset + i] = MaxJSample;
}
}
/// <summary>
/// A port of Poppler's IDCT method which in turn is taken from:
/// Christoph Loeffler, Adriaan Ligtenberg, George S. Moschytz,
/// 'Practical Fast 1-D DCT Algorithms with 11 Multiplications',
/// IEEE Intl. Conf. on Acoustics, Speech &amp; Signal Processing, 1989, 988-991.
/// </summary>
/// <param name="component">The fram component</param>
/// <param name="blockBufferOffset">The block buffer offset</param>
/// <param name="computationBuffer">The computational buffer for holding temp values</param>
/// <param name="quantizationTable">The quantization table</param>
public static void QuantizeAndInverse(PdfJsFrameComponent component, int blockBufferOffset, ref Span<short> computationBuffer, ref Span<short> quantizationTable)
{
Span<short> blockData = component.BlockData.Slice(blockBufferOffset);
int v0, v1, v2, v3, v4, v5, v6, v7;
int p0, p1, p2, p3, p4, p5, p6, p7;
int t;
// inverse DCT on rows
for (int row = 0; row < 64; row += 8)
{
// gather block data
p0 = blockData[row];
p1 = blockData[row + 1];
p2 = blockData[row + 2];
p3 = blockData[row + 3];
p4 = blockData[row + 4];
p5 = blockData[row + 5];
p6 = blockData[row + 6];
p7 = blockData[row + 7];
// dequant p0
p0 *= quantizationTable[row];
// check for all-zero AC coefficients
if ((p1 | p2 | p3 | p4 | p5 | p6 | p7) == 0)
{
t = ((DctSqrt2 * p0) + 512) >> 10;
short st = (short)t;
computationBuffer[row] = st;
computationBuffer[row + 1] = st;
computationBuffer[row + 2] = st;
computationBuffer[row + 3] = st;
computationBuffer[row + 4] = st;
computationBuffer[row + 5] = st;
computationBuffer[row + 6] = st;
computationBuffer[row + 7] = st;
continue;
}
// dequant p1 ... p7
p1 *= quantizationTable[row + 1];
p2 *= quantizationTable[row + 2];
p3 *= quantizationTable[row + 3];
p4 *= quantizationTable[row + 4];
p5 *= quantizationTable[row + 5];
p6 *= quantizationTable[row + 6];
p7 *= quantizationTable[row + 7];
// stage 4
v0 = ((DctSqrt2 * p0) + 128) >> 8;
v1 = ((DctSqrt2 * p4) + 128) >> 8;
v2 = p2;
v3 = p6;
v4 = ((DctSqrt1D2 * (p1 - p7)) + 128) >> 8;
v7 = ((DctSqrt1D2 * (p1 + p7)) + 128) >> 8;
v5 = p3 << 4;
v6 = p5 << 4;
// stage 3
v0 = (v0 + v1 + 1) >> 1;
v1 = v0 - v1;
t = ((v2 * DctSin6) + (v3 * DctCos6) + 128) >> 8;
v2 = ((v2 * DctCos6) - (v3 * DctSin6) + 128) >> 8;
v3 = t;
v4 = (v4 + v6 + 1) >> 1;
v6 = v4 - v6;
v7 = (v7 + v5 + 1) >> 1;
v5 = v7 - v5;
// stage 2
v0 = (v0 + v3 + 1) >> 1;
v3 = v0 - v3;
v1 = (v1 + v2 + 1) >> 1;
v2 = v1 - v2;
t = ((v4 * DctSin3) + (v7 * DctCos3) + 2048) >> 12;
v4 = ((v4 * DctCos3) - (v7 * DctSin3) + 2048) >> 12;
v7 = t;
t = ((v5 * DctSin1) + (v6 * DctCos1) + 2048) >> 12;
v5 = ((v5 * DctCos1) - (v6 * DctSin1) + 2048) >> 12;
v6 = t;
// stage 1
computationBuffer[row] = (short)(v0 + v7);
computationBuffer[row + 7] = (short)(v0 - v7);
computationBuffer[row + 1] = (short)(v1 + v6);
computationBuffer[row + 6] = (short)(v1 - v6);
computationBuffer[row + 2] = (short)(v2 + v5);
computationBuffer[row + 5] = (short)(v2 - v5);
computationBuffer[row + 3] = (short)(v3 + v4);
computationBuffer[row + 4] = (short)(v3 - v4);
}
// inverse DCT on columns
for (int col = 0; col < 8; ++col)
{
p0 = computationBuffer[col];
p1 = computationBuffer[col + 8];
p2 = computationBuffer[col + 16];
p3 = computationBuffer[col + 24];
p4 = computationBuffer[col + 32];
p5 = computationBuffer[col + 40];
p6 = computationBuffer[col + 48];
p7 = computationBuffer[col + 56];
// check for all-zero AC coefficients
if ((p1 | p2 | p3 | p4 | p5 | p6 | p7) == 0)
{
t = ((DctSqrt2 * p0) + 8192) >> 14;
// convert to 8 bit
t = (t < -2040) ? 0 : (t >= 2024) ? MaxJSample : (t + 2056) >> 4;
short st = (short)t;
blockData[col] = st;
blockData[col + 8] = st;
blockData[col + 16] = st;
blockData[col + 24] = st;
blockData[col + 32] = st;
blockData[col + 40] = st;
blockData[col + 48] = st;
blockData[col + 56] = st;
continue;
}
// stage 4
v0 = ((DctSqrt2 * p0) + 2048) >> 12;
v1 = ((DctSqrt2 * p4) + 2048) >> 12;
v2 = p2;
v3 = p6;
v4 = ((DctSqrt1D2 * (p1 - p7)) + 2048) >> 12;
v7 = ((DctSqrt1D2 * (p1 + p7)) + 2048) >> 12;
v5 = p3;
v6 = p5;
// stage 3
// Shift v0 by 128.5 << 5 here, so we don't need to shift p0...p7 when
// converting to UInt8 range later.
v0 = ((v0 + v1 + 1) >> 1) + 4112;
v1 = v0 - v1;
t = ((v2 * DctSin6) + (v3 * DctCos6) + 2048) >> 12;
v2 = ((v2 * DctCos6) - (v3 * DctSin6) + 2048) >> 12;
v3 = t;
v4 = (v4 + v6 + 1) >> 1;
v6 = v4 - v6;
v7 = (v7 + v5 + 1) >> 1;
v5 = v7 - v5;
// stage 2
v0 = (v0 + v3 + 1) >> 1;
v3 = v0 - v3;
v1 = (v1 + v2 + 1) >> 1;
v2 = v1 - v2;
t = ((v4 * DctSin3) + (v7 * DctCos3) + 2048) >> 12;
v4 = ((v4 * DctCos3) - (v7 * DctSin3) + 2048) >> 12;
v7 = t;
t = ((v5 * DctSin1) + (v6 * DctCos1) + 2048) >> 12;
v5 = ((v5 * DctCos1) - (v6 * DctSin1) + 2048) >> 12;
v6 = t;
// stage 1
p0 = v0 + v7;
p7 = v0 - v7;
p1 = v1 + v6;
p6 = v1 - v6;
p2 = v2 + v5;
p5 = v2 - v5;
p3 = v3 + v4;
p4 = v3 - v4;
// convert to 8-bit integers
p0 = (p0 < 16) ? 0 : (p0 >= 4080) ? MaxJSample : p0 >> 4;
p1 = (p1 < 16) ? 0 : (p1 >= 4080) ? MaxJSample : p1 >> 4;
p2 = (p2 < 16) ? 0 : (p2 >= 4080) ? MaxJSample : p2 >> 4;
p3 = (p3 < 16) ? 0 : (p3 >= 4080) ? MaxJSample : p3 >> 4;
p4 = (p4 < 16) ? 0 : (p4 >= 4080) ? MaxJSample : p4 >> 4;
p5 = (p5 < 16) ? 0 : (p5 >= 4080) ? MaxJSample : p5 >> 4;
p6 = (p6 < 16) ? 0 : (p6 >= 4080) ? MaxJSample : p6 >> 4;
p7 = (p7 < 16) ? 0 : (p7 >= 4080) ? MaxJSample : p7 >> 4;
// store block data
blockData[col] = (short)p0;
blockData[col + 8] = (short)p1;
blockData[col + 16] = (short)p2;
blockData[col + 24] = (short)p3;
blockData[col + 32] = (short)p4;
blockData[col + 40] = (short)p5;
blockData[col + 48] = (short)p6;
blockData[col + 56] = (short)p7;
}
}
/// <summary>
/// A port of <see href="https://github.com/libjpeg-turbo/libjpeg-turbo/blob/master/jidctfst.c#L171"/>
/// A 2-D IDCT can be done by 1-D IDCT on each column followed by 1-D IDCT
/// on each row(or vice versa, but it's more convenient to emit a row at
/// a time). Direct algorithms are also available, but they are much more
/// complex and seem not to be any faster when reduced to code.
///
/// This implementation is based on Arai, Agui, and Nakajima's algorithm for
/// scaled DCT.Their original paper (Trans.IEICE E-71(11):1095) is in
/// Japanese, but the algorithm is described in the Pennebaker &amp; Mitchell
/// JPEG textbook(see REFERENCES section in file README.ijg). The following
/// code is based directly on figure 4-8 in P&amp;M.
/// While an 8-point DCT cannot be done in less than 11 multiplies, it is
/// possible to arrange the computation so that many of the multiplies are
/// simple scalings of the final outputs.These multiplies can then be
/// folded into the multiplications or divisions by the JPEG quantization
/// table entries. The AA&amp;N method leaves only 5 multiplies and 29 adds
/// to be done in the DCT itself.
/// The primary disadvantage of this method is that with fixed-point math,
/// accuracy is lost due to imprecise representation of the scaled
/// quantization values.The smaller the quantization table entry, the less
/// precise the scaled value, so this implementation does worse with high -
/// quality - setting files than with low - quality ones.
/// </summary>
/// <param name="component">The frame component</param>
/// <param name="blockBufferOffset">The block buffer offset</param>
/// <param name="computationBuffer">The computational buffer for holding temp values</param>
/// <param name="multiplierTable">The multiplier table</param>
public static void QuantizeAndInverseFast(PdfJsFrameComponent component, int blockBufferOffset, ref Span<short> computationBuffer, ref Span<short> multiplierTable)
{
Span<short> blockData = component.BlockData.Slice(blockBufferOffset);
int p0, p1, p2, p3, p4, p5, p6, p7;
for (int col = 0; col < 8; col++)
{
// Gather block data
p0 = blockData[col];
p1 = blockData[col + 8];
p2 = blockData[col + 16];
p3 = blockData[col + 24];
p4 = blockData[col + 32];
p5 = blockData[col + 40];
p6 = blockData[col + 48];
p7 = blockData[col + 56];
int tmp0 = p0 * multiplierTable[col];
// Due to quantization, we will usually find that many of the input
// coefficients are zero, especially the AC terms. We can exploit this
// by short-circuiting the IDCT calculation for any column in which all
// the AC terms are zero. In that case each output is equal to the
// DC coefficient (with scale factor as needed).
// With typical images and quantization tables, half or more of the
// column DCT calculations can be simplified this way.
if ((p1 | p2 | p3 | p4 | p5 | p6 | p7) == 0)
{
short dcval = (short)tmp0;
computationBuffer[col] = dcval;
computationBuffer[col + 8] = dcval;
computationBuffer[col + 16] = dcval;
computationBuffer[col + 24] = dcval;
computationBuffer[col + 32] = dcval;
computationBuffer[col + 40] = dcval;
computationBuffer[col + 48] = dcval;
computationBuffer[col + 56] = dcval;
continue;
}
// Even part
int tmp1 = p2 * multiplierTable[col + 16];
int tmp2 = p4 * multiplierTable[col + 32];
int tmp3 = p6 * multiplierTable[col + 48];
int tmp10 = tmp0 + tmp2; // Phase 3
int tmp11 = tmp0 - tmp2;
int tmp13 = tmp1 + tmp3; // Phases 5-3
int tmp12 = Multiply(tmp1 - tmp3, FIX_1_414213562) - tmp13; // 2*c4
tmp0 = tmp10 + tmp13; // Phase 2
tmp3 = tmp10 - tmp13;
tmp1 = tmp11 + tmp12;
tmp2 = tmp11 - tmp12;
// Odd Part
int tmp4 = p1 * multiplierTable[col + 8];
int tmp5 = p3 * multiplierTable[col + 24];
int tmp6 = p5 * multiplierTable[col + 40];
int tmp7 = p7 * multiplierTable[col + 56];
int z13 = tmp6 + tmp5; // Phase 6
int z10 = tmp6 - tmp5;
int z11 = tmp4 + tmp7;
int z12 = tmp4 - tmp7;
tmp7 = z11 + z13; // Phase 5
tmp11 = Multiply(z11 - z13, FIX_1_414213562); // 2*c4
int z5 = Multiply(z10 + z12, FIX_1_847759065); // 2*c2
tmp10 = z5 - Multiply(z12, FIX_1_082392200); // 2*(c2-c6)
tmp12 = z5 - Multiply(z10, FIX_2_613125930); // 2*(c2+c6)
tmp6 = tmp12 - tmp7; // Phase 2
tmp5 = tmp11 - tmp6;
tmp4 = tmp10 - tmp5;
computationBuffer[col] = (short)(tmp0 + tmp7);
computationBuffer[col + 56] = (short)(tmp0 - tmp7);
computationBuffer[col + 8] = (short)(tmp1 + tmp6);
computationBuffer[col + 48] = (short)(tmp1 - tmp6);
computationBuffer[col + 16] = (short)(tmp2 + tmp5);
computationBuffer[col + 40] = (short)(tmp2 - tmp5);
computationBuffer[col + 24] = (short)(tmp3 + tmp4);
computationBuffer[col + 32] = (short)(tmp3 - tmp4);
}
// Pass 2: process rows from work array, store into output array.
// Note that we must descale the results by a factor of 8 == 2**3,
// and also undo the pass 1 bits scaling.
for (int row = 0; row < 64; row += 8)
{
p1 = computationBuffer[row + 1];
p2 = computationBuffer[row + 2];
p3 = computationBuffer[row + 3];
p4 = computationBuffer[row + 4];
p5 = computationBuffer[row + 5];
p6 = computationBuffer[row + 6];
p7 = computationBuffer[row + 7];
// Add range center and fudge factor for final descale and range-limit.
int z5 = computationBuffer[row] + (RangeCenter << (Pass1Bits + 3)) + (1 << (Pass1Bits + 2));
// Check for all-zero AC coefficients
if ((p1 | p2 | p3 | p4 | p5 | p6 | p7) == 0)
{
byte dcval = Limit[LimitOffset + (RightShift(z5, Pass1Bits + 3) & RangeMask)];
blockData[row] = dcval;
blockData[row + 1] = dcval;
blockData[row + 2] = dcval;
blockData[row + 3] = dcval;
blockData[row + 4] = dcval;
blockData[row + 5] = dcval;
blockData[row + 6] = dcval;
blockData[row + 7] = dcval;
continue;
}
// Even part
int tmp10 = z5 + p4;
int tmp11 = z5 - p4;
int tmp13 = p2 + p6;
int tmp12 = Multiply(p2 - p6, FIX_1_414213562) - tmp13; // 2*c4
int tmp0 = tmp10 + tmp13;
int tmp3 = tmp10 - tmp13;
int tmp1 = tmp11 + tmp12;
int tmp2 = tmp11 - tmp12;
// Odd part
int z13 = p5 + p3;
int z10 = p5 - p3;
int z11 = p1 + p7;
int z12 = p1 - p7;
int tmp7 = z11 + z13; // Phase 5
tmp11 = Multiply(z11 - z13, FIX_1_414213562); // 2*c4
z5 = Multiply(z10 + z12, FIX_1_847759065); // 2*c2
tmp10 = z5 - Multiply(z12, FIX_1_082392200); // 2*(c2-c6)
tmp12 = z5 - Multiply(z10, FIX_2_613125930); // 2*(c2+c6)
int tmp6 = tmp12 - tmp7; // Phase 2
int tmp5 = tmp11 - tmp6;
int tmp4 = tmp10 - tmp5;
// Final output stage: scale down by a factor of 8, offset, and range-limit
blockData[row] = Limit[LimitOffset + (RightShift(tmp0 + tmp7, Pass1Bits + 3) & RangeMask)];
blockData[row + 7] = Limit[LimitOffset + (RightShift(tmp0 - tmp7, Pass1Bits + 3) & RangeMask)];
blockData[row + 1] = Limit[LimitOffset + (RightShift(tmp1 + tmp6, Pass1Bits + 3) & RangeMask)];
blockData[row + 6] = Limit[LimitOffset + (RightShift(tmp1 - tmp6, Pass1Bits + 3) & RangeMask)];
blockData[row + 2] = Limit[LimitOffset + (RightShift(tmp2 + tmp5, Pass1Bits + 3) & RangeMask)];
blockData[row + 5] = Limit[LimitOffset + (RightShift(tmp2 - tmp5, Pass1Bits + 3) & RangeMask)];
blockData[row + 3] = Limit[LimitOffset + (RightShift(tmp3 + tmp4, Pass1Bits + 3) & RangeMask)];
blockData[row + 4] = Limit[LimitOffset + (RightShift(tmp3 - tmp4, Pass1Bits + 3) & RangeMask)];
}
}
/// <summary>
/// Descale and correctly round an int value that's scaled by <paramref name="n"/> bits.
/// We assume <see cref="RightShift"/> rounds towards minus infinity, so adding
/// the fudge factor is correct for either sign of <paramref name="value"/>.
/// </summary>
/// <param name="value">The value</param>
/// <param name="n">The number of bits</param>
/// <returns>The <see cref="int"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Descale(int value, int n)
{
return RightShift(value + (1 << (n - 1)), n);
}
/// <summary>
/// Multiply a variable by an int constant, and immediately descale.
/// </summary>
/// <param name="val">The value</param>
/// <param name="c">The multiplier</param>
/// <returns>The <see cref="int"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int Multiply(int val, int c)
{
return Descale(val * c, ConstBits);
}
/// <summary>
/// Right-shifts the value by the given amount
/// </summary>
/// <param name="value">The value</param>
/// <param name="shift">The amount to shift by</param>
/// <returns>The <see cref="int"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int RightShift(int value, int shift)
{
return value >> shift;
}
}
}

149
src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsJpegPixelArea.cs

@ -1,149 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Diagnostics;
using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
{
/// <summary>
/// Represents a section of the jpeg component data laid out in pixel order.
/// </summary>
internal struct PdfJsJpegPixelArea : IDisposable
{
private readonly MemoryManager memoryManager;
private readonly int imageWidth;
private readonly int imageHeight;
private IBuffer<byte> componentData;
private int rowStride;
/// <summary>
/// Initializes a new instance of the <see cref="PdfJsJpegPixelArea"/> struct.
/// </summary>
/// <param name="memoryManager">The <see cref="MemoryManager"/> to use for buffer allocations.</param>
/// <param name="imageWidth">The image width</param>
/// <param name="imageHeight">The image height</param>
/// <param name="numberOfComponents">The number of components</param>
public PdfJsJpegPixelArea(MemoryManager memoryManager, int imageWidth, int imageHeight, int numberOfComponents)
{
this.memoryManager = memoryManager;
this.imageWidth = imageWidth;
this.imageHeight = imageHeight;
this.Width = 0;
this.Height = 0;
this.NumberOfComponents = numberOfComponents;
this.componentData = null;
this.rowStride = 0;
}
/// <summary>
/// Gets the number of components
/// </summary>
public int NumberOfComponents { get; }
/// <summary>
/// Gets the width
/// </summary>
public int Width { get; private set; }
/// <summary>
/// Gets the height
/// </summary>
public int Height { get; private set; }
/// <summary>
/// Organsizes the decoded jpeg components into a linear array ordered by component.
/// This must be called before attempting to retrieve the data.
/// </summary>
/// <param name="components">The jpeg component blocks</param>
/// <param name="width">The pixel area width</param>
/// <param name="height">The pixel area height</param>
public void LinearizeBlockData(PdfJsComponentBlocks components, int width, int height)
{
this.Width = width;
this.Height = height;
int numberOfComponents = this.NumberOfComponents;
this.rowStride = width * numberOfComponents;
var scale = new Vector2(this.imageWidth / (float)width, this.imageHeight / (float)height);
this.componentData = this.memoryManager.Allocate<byte>(width * height * numberOfComponents);
Span<byte> componentDataSpan = this.componentData.Span;
const uint Mask3Lsb = 0xFFFFFFF8; // Used to clear the 3 LSBs
using (IBuffer<int> xScaleBlockOffset = this.memoryManager.Allocate<int>(width))
{
Span<int> xScaleBlockOffsetSpan = xScaleBlockOffset.Span;
for (int i = 0; i < numberOfComponents; i++)
{
ref PdfJsComponent component = ref components.Components[i];
Vector2 componentScale = component.Scale * scale;
int offset = i;
Span<short> output = component.Output.Span;
int blocksPerScanline = (component.BlocksPerLine + 1) << 3;
// Precalculate the xScaleBlockOffset
int j;
for (int x = 0; x < width; x++)
{
j = (int)(x * componentScale.X);
xScaleBlockOffsetSpan[x] = (int)((j & Mask3Lsb) << 3) | (j & 7);
}
// Linearize the blocks of the component
for (int y = 0; y < height; y++)
{
j = (int)(y * componentScale.Y);
int index = blocksPerScanline * (int)(j & Mask3Lsb) | ((j & 7) << 3);
for (int x = 0; x < width; x++)
{
componentDataSpan[offset] = (byte)output[index + xScaleBlockOffsetSpan[x]];
offset += numberOfComponents;
}
}
}
}
}
/// <summary>
/// Gets a <see cref="Span{Byte}"/> representing the row 'y' beginning from the the first byte on that row.
/// </summary>
/// <param name="y">The y-coordinate of the pixel row. Must be greater than or equal to zero and less than the height of the pixel area.</param>
/// <returns>The <see cref="Span{TPixel}"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span<byte> GetRowSpan(int y)
{
this.CheckCoordinates(y);
return this.componentData.Slice(y * this.rowStride, this.rowStride);
}
/// <inheritdoc/>
public void Dispose()
{
this.componentData?.Dispose();
this.componentData = null;
}
/// <summary>
/// Checks the coordinates to ensure they are within bounds.
/// </summary>
/// <param name="y">The y-coordinate of the row. Must be greater than zero and less than the height of the area.</param>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown if the coordinates are not within the bounds of the image.
/// </exception>
[Conditional("DEBUG")]
private void CheckCoordinates(int y)
{
if (y < 0 || y >= this.Height)
{
throw new ArgumentOutOfRangeException(nameof(y), y, $"{y} is outwith the area bounds.");
}
}
}
}

67
src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsQuantizationTables.cs

@ -1,67 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
{
/// <summary>
/// Contains the quantization tables.
/// </summary>
internal sealed class PdfJsQuantizationTables : IDisposable
{
public PdfJsQuantizationTables(MemoryManager memoryManager)
{
this.Tables = memoryManager.Allocate2D<short>(64, 4);
}
/// <summary>
/// Gets the ZigZag scan table
/// </summary>
public static byte[] DctZigZag
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get;
}
=
{
0,
1, 8,
16, 9, 2,
3, 10, 17, 24,
32, 25, 18, 11, 4,
5, 12, 19, 26, 33, 40,
48, 41, 34, 27, 20, 13, 6,
7, 14, 21, 28, 35, 42, 49, 56,
57, 50, 43, 36, 29, 22, 15,
23, 30, 37, 44, 51, 58,
59, 52, 45, 38, 31,
39, 46, 53, 60,
61, 54, 47,
55, 62,
63
};
/// <summary>
/// Gets or sets the quantization tables.
/// </summary>
public Buffer2D<short> Tables
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get; set;
}
/// <inheritdoc/>
public void Dispose()
{
if (this.Tables != null)
{
this.Tables.Dispose();
this.Tables = null;
}
}
}
}

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

@ -2,9 +2,13 @@
// Licensed under the Apache License, Version 2.0.
using System;
#if DEBUG
using System.Diagnostics;
#endif
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Formats.Jpeg.Common;
namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
{
@ -13,18 +17,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
/// </summary>
internal struct PdfJsScanDecoder
{
private ZigZag dctZigZag;
private byte[] markerBuffer;
private int bitsData;
private int bitsCount;
#pragma warning disable 414
private int bitsUnRead;
private int accumulator;
#pragma warning restore 414
private int specStart;
private int specEnd;
@ -72,6 +72,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
int successivePrev,
int successive)
{
this.dctZigZag = ZigZag.CreateUnzigTable();
this.markerBuffer = new byte[2];
this.compIndex = componentIndex;
this.specStart = spectralStart;
@ -113,34 +114,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
}
else
{
if (this.specStart == 0)
{
if (successivePrev == 0)
{
this.DecodeScanDCFirst(dcHuffmanTables, components, componentsLength, mcusPerLine, mcuToRead, ref mcu, stream);
}
else
{
this.DecodeScanDCSuccessive(components, componentsLength, mcusPerLine, mcuToRead, ref mcu, stream);
}
}
else
{
if (successivePrev == 0)
{
this.DecodeScanACFirst(acHuffmanTables, components, componentsLength, mcusPerLine, mcuToRead, ref mcu, stream);
}
else
{
this.DecodeScanACSuccessive(acHuffmanTables, components, componentsLength, mcusPerLine, mcuToRead, ref mcu, stream);
}
}
bool isAc = this.specStart != 0;
bool isFirst = successivePrev == 0;
PdfJsHuffmanTables huffmanTables = isAc ? acHuffmanTables : dcHuffmanTables;
this.DecodeScanProgressive(huffmanTables, isAc, isFirst, components, componentsLength, mcusPerLine, mcuToRead, ref mcu, stream);
}
// Find marker
this.bitsCount = 0;
this.accumulator = 0;
this.bitsUnRead = 0;
fileMarker = PdfJsJpegDecoderCore.FindNextFileMarker(this.markerBuffer, stream);
// Some bad images seem to pad Scan blocks with e.g. zero bytes, skip past
@ -172,7 +153,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
fileMarker = PdfJsJpegDecoderCore.FindNextFileMarker(this.markerBuffer, stream);
// Some images include more Scan blocks than expected, skip past those and
// attempt to find the next valid marker (fixes issue8182.pdf) in original code.
// attempt to find the next valid marker (fixes issue8182.pdf) ref original code.
if (fileMarker.Invalid)
{
#if DEBUG
@ -187,7 +168,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeScanBaseline(
PdfJsHuffmanTables dcHuffmanTables,
PdfJsHuffmanTables acHuffmanTables,
@ -201,6 +181,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
if (componentsLength == 1)
{
PdfJsFrameComponent component = components[this.compIndex];
ref short blockDataRef = ref MemoryMarshal.GetReference(MemoryMarshal.Cast<Block8x8, short>(component.SpectralBlocks.Span));
ref PdfJsHuffmanTable dcHuffmanTable = ref dcHuffmanTables[component.DCHuffmanTableId];
ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId];
@ -211,7 +192,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
continue;
}
this.DecodeBlockBaseline(ref dcHuffmanTable, ref acHuffmanTable, component, mcu, stream);
this.DecodeBlockBaseline(ref dcHuffmanTable, ref acHuffmanTable, component, ref blockDataRef, mcu, stream);
mcu++;
}
}
@ -222,6 +203,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
for (int i = 0; i < componentsLength; i++)
{
PdfJsFrameComponent component = components[i];
ref short blockDataRef = ref MemoryMarshal.GetReference(MemoryMarshal.Cast<Block8x8, short>(component.SpectralBlocks.Span));
ref PdfJsHuffmanTable dcHuffmanTable = ref dcHuffmanTables[component.DCHuffmanTableId];
ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId];
int h = component.HorizontalSamplingFactor;
@ -236,7 +218,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
continue;
}
this.DecodeMcuBaseline(ref dcHuffmanTable, ref acHuffmanTable, component, mcusPerLine, mcu, j, k, stream);
this.DecodeMcuBaseline(ref dcHuffmanTable, ref acHuffmanTable, component, ref blockDataRef, mcusPerLine, mcu, j, k, stream);
}
}
}
@ -246,9 +228,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeScanDCFirst(
PdfJsHuffmanTables dcHuffmanTables,
private void DecodeScanProgressive(
PdfJsHuffmanTables huffmanTables,
bool isAC,
bool isFirst,
PdfJsFrameComponent[] components,
int componentsLength,
int mcusPerLine,
@ -259,7 +242,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
if (componentsLength == 1)
{
PdfJsFrameComponent component = components[this.compIndex];
ref PdfJsHuffmanTable dcHuffmanTable = ref dcHuffmanTables[component.DCHuffmanTableId];
ref short blockDataRef = ref MemoryMarshal.GetReference(MemoryMarshal.Cast<Block8x8, short>(component.SpectralBlocks.Span));
ref PdfJsHuffmanTable huffmanTable = ref huffmanTables[isAC ? component.ACHuffmanTableId : component.DCHuffmanTableId];
for (int n = 0; n < mcuToRead; n++)
{
@ -268,173 +252,32 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
continue;
}
this.DecodeBlockDCFirst(ref dcHuffmanTable, component, mcu, stream);
mcu++;
}
}
else
{
for (int n = 0; n < mcuToRead; n++)
{
for (int i = 0; i < componentsLength; i++)
if (isAC)
{
PdfJsFrameComponent component = components[i];
ref PdfJsHuffmanTable dcHuffmanTable = ref dcHuffmanTables[component.DCHuffmanTableId];
int h = component.HorizontalSamplingFactor;
int v = component.VerticalSamplingFactor;
for (int j = 0; j < v; j++)
if (isFirst)
{
for (int k = 0; k < h; k++)
{
if (this.endOfStreamReached || this.unexpectedMarkerReached)
{
continue;
}
this.DecodeMcuDCFirst(ref dcHuffmanTable, component, mcusPerLine, mcu, j, k, stream);
}
this.DecodeBlockACFirst(ref huffmanTable, component, ref blockDataRef, mcu, stream);
}
}
mcu++;
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeScanDCSuccessive(
PdfJsFrameComponent[] components,
int componentsLength,
int mcusPerLine,
int mcuToRead,
ref int mcu,
Stream stream)
{
if (componentsLength == 1)
{
PdfJsFrameComponent component = components[this.compIndex];
for (int n = 0; n < mcuToRead; n++)
{
if (this.endOfStreamReached || this.unexpectedMarkerReached)
{
continue;
}
this.DecodeBlockDCSuccessive(component, mcu, stream);
mcu++;
}
}
else
{
for (int n = 0; n < mcuToRead; n++)
{
for (int i = 0; i < componentsLength; i++)
{
PdfJsFrameComponent component = components[i];
int h = component.HorizontalSamplingFactor;
int v = component.VerticalSamplingFactor;
for (int j = 0; j < v; j++)
else
{
for (int k = 0; k < h; k++)
{
if (this.endOfStreamReached || this.unexpectedMarkerReached)
{
continue;
}
this.DecodeMcuDCSuccessive(component, mcusPerLine, mcu, j, k, stream);
}
this.DecodeBlockACSuccessive(ref huffmanTable, component, ref blockDataRef, mcu, stream);
}
}
mcu++;
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeScanACFirst(
PdfJsHuffmanTables acHuffmanTables,
PdfJsFrameComponent[] components,
int componentsLength,
int mcusPerLine,
int mcuToRead,
ref int mcu,
Stream stream)
{
if (componentsLength == 1)
{
PdfJsFrameComponent component = components[this.compIndex];
ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId];
for (int n = 0; n < mcuToRead; n++)
{
if (this.endOfStreamReached || this.unexpectedMarkerReached)
{
continue;
}
this.DecodeBlockACFirst(ref acHuffmanTable, component, mcu, stream);
mcu++;
}
}
else
{
for (int n = 0; n < mcuToRead; n++)
{
for (int i = 0; i < componentsLength; i++)
else
{
PdfJsFrameComponent component = components[i];
ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId];
int h = component.HorizontalSamplingFactor;
int v = component.VerticalSamplingFactor;
for (int j = 0; j < v; j++)
if (isFirst)
{
for (int k = 0; k < h; k++)
{
if (this.endOfStreamReached || this.unexpectedMarkerReached)
{
continue;
}
this.DecodeMcuACFirst(ref acHuffmanTable, component, mcusPerLine, mcu, j, k, stream);
}
this.DecodeBlockDCFirst(ref huffmanTable, component, ref blockDataRef, mcu, stream);
}
else
{
this.DecodeBlockDCSuccessive(component, ref blockDataRef, mcu, stream);
}
}
mcu++;
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeScanACSuccessive(
PdfJsHuffmanTables acHuffmanTables,
PdfJsFrameComponent[] components,
int componentsLength,
int mcusPerLine,
int mcuToRead,
ref int mcu,
Stream stream)
{
if (componentsLength == 1)
{
PdfJsFrameComponent component = components[this.compIndex];
ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId];
for (int n = 0; n < mcuToRead; n++)
{
if (this.endOfStreamReached || this.unexpectedMarkerReached)
{
continue;
}
this.DecodeBlockACSuccessive(ref acHuffmanTable, component, mcu, stream);
mcu++;
}
}
else
{
for (int n = 0; n < mcuToRead; n++)
@ -442,7 +285,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
for (int i = 0; i < componentsLength; i++)
{
PdfJsFrameComponent component = components[i];
ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId];
ref short blockDataRef = ref MemoryMarshal.GetReference(MemoryMarshal.Cast<Block8x8, short>(component.SpectralBlocks.Span));
ref PdfJsHuffmanTable huffmanTable = ref huffmanTables[isAC ? component.ACHuffmanTableId : component.DCHuffmanTableId];
int h = component.HorizontalSamplingFactor;
int v = component.VerticalSamplingFactor;
@ -455,7 +299,28 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
continue;
}
this.DecodeMcuACSuccessive(ref acHuffmanTable, component, mcusPerLine, mcu, j, k, stream);
if (isAC)
{
if (isFirst)
{
this.DecodeMcuACFirst(ref huffmanTable, component, ref blockDataRef, mcusPerLine, mcu, j, k, stream);
}
else
{
this.DecodeMcuACSuccessive(ref huffmanTable, component, ref blockDataRef, mcusPerLine, mcu, j, k, stream);
}
}
else
{
if (isFirst)
{
this.DecodeMcuDCFirst(ref huffmanTable, component, ref blockDataRef, mcusPerLine, mcu, j, k, stream);
}
else
{
this.DecodeMcuDCSuccessive(component, ref blockDataRef, mcusPerLine, mcu, j, k, stream);
}
}
}
}
}
@ -466,103 +331,103 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeBlockBaseline(ref PdfJsHuffmanTable dcHuffmanTable, ref PdfJsHuffmanTable acHuffmanTable, PdfJsFrameComponent component, int mcu, Stream stream)
private void DecodeBlockBaseline(ref PdfJsHuffmanTable dcHuffmanTable, ref PdfJsHuffmanTable acHuffmanTable, PdfJsFrameComponent component, ref short blockDataRef, int mcu, Stream stream)
{
int blockRow = mcu / component.WidthInBlocks;
int blockCol = mcu % component.WidthInBlocks;
int offset = component.GetBlockBufferOffset(blockRow, blockCol);
this.DecodeBaseline(component, offset, ref dcHuffmanTable, ref acHuffmanTable, stream);
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, int mcusPerLine, int mcu, int row, int col, Stream stream)
private void DecodeMcuBaseline(ref PdfJsHuffmanTable dcHuffmanTable, ref PdfJsHuffmanTable acHuffmanTable, PdfJsFrameComponent component, ref short blockDataRef, int mcusPerLine, int mcu, int row, int col, Stream stream)
{
int mcuRow = mcu / mcusPerLine;
int mcuCol = mcu % mcusPerLine;
int blockRow = (mcuRow * component.VerticalSamplingFactor) + row;
int blockCol = (mcuCol * component.HorizontalSamplingFactor) + col;
int offset = component.GetBlockBufferOffset(blockRow, blockCol);
this.DecodeBaseline(component, offset, ref dcHuffmanTable, ref acHuffmanTable, stream);
this.DecodeBaseline(component, ref blockDataRef, offset, ref dcHuffmanTable, ref acHuffmanTable, stream);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeBlockDCFirst(ref PdfJsHuffmanTable dcHuffmanTable, PdfJsFrameComponent component, int mcu, Stream stream)
private void DecodeBlockDCFirst(ref PdfJsHuffmanTable dcHuffmanTable, PdfJsFrameComponent component, ref short blockDataRef, int mcu, Stream stream)
{
int blockRow = mcu / component.WidthInBlocks;
int blockCol = mcu % component.WidthInBlocks;
int offset = component.GetBlockBufferOffset(blockRow, blockCol);
this.DecodeDCFirst(component, offset, ref dcHuffmanTable, stream);
this.DecodeDCFirst(component, ref blockDataRef, offset, ref dcHuffmanTable, stream);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeMcuDCFirst(ref PdfJsHuffmanTable dcHuffmanTable, PdfJsFrameComponent component, int mcusPerLine, int mcu, int row, int col, Stream stream)
private void DecodeMcuDCFirst(ref PdfJsHuffmanTable dcHuffmanTable, PdfJsFrameComponent component, ref short blockDataRef, int mcusPerLine, int mcu, int row, int col, Stream stream)
{
int mcuRow = mcu / mcusPerLine;
int mcuCol = mcu % mcusPerLine;
int blockRow = (mcuRow * component.VerticalSamplingFactor) + row;
int blockCol = (mcuCol * component.HorizontalSamplingFactor) + col;
int offset = component.GetBlockBufferOffset(blockRow, blockCol);
this.DecodeDCFirst(component, offset, ref dcHuffmanTable, stream);
this.DecodeDCFirst(component, ref blockDataRef, offset, ref dcHuffmanTable, stream);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeBlockDCSuccessive(PdfJsFrameComponent component, int mcu, Stream stream)
private void DecodeBlockDCSuccessive(PdfJsFrameComponent component, ref short blockDataRef, int mcu, Stream stream)
{
int blockRow = mcu / component.WidthInBlocks;
int blockCol = mcu % component.WidthInBlocks;
int offset = component.GetBlockBufferOffset(blockRow, blockCol);
this.DecodeDCSuccessive(component, offset, stream);
this.DecodeDCSuccessive(component, ref blockDataRef, offset, stream);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeMcuDCSuccessive(PdfJsFrameComponent component, int mcusPerLine, int mcu, int row, int col, Stream stream)
private void DecodeMcuDCSuccessive(PdfJsFrameComponent component, ref short blockDataRef, int mcusPerLine, int mcu, int row, int col, Stream stream)
{
int mcuRow = mcu / mcusPerLine;
int mcuCol = mcu % mcusPerLine;
int blockRow = (mcuRow * component.VerticalSamplingFactor) + row;
int blockCol = (mcuCol * component.HorizontalSamplingFactor) + col;
int offset = component.GetBlockBufferOffset(blockRow, blockCol);
this.DecodeDCSuccessive(component, offset, stream);
this.DecodeDCSuccessive(component, ref blockDataRef, offset, stream);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeBlockACFirst(ref PdfJsHuffmanTable acHuffmanTable, PdfJsFrameComponent component, int mcu, Stream stream)
private void DecodeBlockACFirst(ref PdfJsHuffmanTable acHuffmanTable, PdfJsFrameComponent component, ref short blockDataRef, int mcu, Stream stream)
{
int blockRow = mcu / component.WidthInBlocks;
int blockCol = mcu % component.WidthInBlocks;
int offset = component.GetBlockBufferOffset(blockRow, blockCol);
this.DecodeACFirst(component, offset, ref acHuffmanTable, stream);
this.DecodeACFirst(component, ref blockDataRef, offset, ref acHuffmanTable, stream);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeMcuACFirst(ref PdfJsHuffmanTable acHuffmanTable, PdfJsFrameComponent component, int mcusPerLine, int mcu, int row, int col, Stream stream)
private void DecodeMcuACFirst(ref PdfJsHuffmanTable acHuffmanTable, PdfJsFrameComponent component, ref short blockDataRef, int mcusPerLine, int mcu, int row, int col, Stream stream)
{
int mcuRow = mcu / mcusPerLine;
int mcuCol = mcu % mcusPerLine;
int blockRow = (mcuRow * component.VerticalSamplingFactor) + row;
int blockCol = (mcuCol * component.HorizontalSamplingFactor) + col;
int offset = component.GetBlockBufferOffset(blockRow, blockCol);
this.DecodeACFirst(component, offset, ref acHuffmanTable, stream);
this.DecodeACFirst(component, ref blockDataRef, offset, ref acHuffmanTable, stream);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeBlockACSuccessive(ref PdfJsHuffmanTable acHuffmanTable, PdfJsFrameComponent component, int mcu, Stream stream)
private void DecodeBlockACSuccessive(ref PdfJsHuffmanTable acHuffmanTable, PdfJsFrameComponent component, ref short blockDataRef, int mcu, Stream stream)
{
int blockRow = mcu / component.WidthInBlocks;
int blockCol = mcu % component.WidthInBlocks;
int offset = component.GetBlockBufferOffset(blockRow, blockCol);
this.DecodeACSuccessive(component, offset, ref acHuffmanTable, stream);
this.DecodeACSuccessive(component, ref blockDataRef, offset, ref acHuffmanTable, stream);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeMcuACSuccessive(ref PdfJsHuffmanTable acHuffmanTable, PdfJsFrameComponent component, int mcusPerLine, int mcu, int row, int col, Stream stream)
private void DecodeMcuACSuccessive(ref PdfJsHuffmanTable acHuffmanTable, PdfJsFrameComponent component, ref short blockDataRef, int mcusPerLine, int mcu, int row, int col, Stream stream)
{
int mcuRow = mcu / mcusPerLine;
int mcuCol = mcu % mcusPerLine;
int blockRow = (mcuRow * component.VerticalSamplingFactor) + row;
int blockCol = (mcuCol * component.HorizontalSamplingFactor) + col;
int offset = component.GetBlockBufferOffset(blockRow, blockCol);
this.DecodeACSuccessive(component, offset, ref acHuffmanTable, stream);
this.DecodeACSuccessive(component, ref blockDataRef, offset, ref acHuffmanTable, stream);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -579,7 +444,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
if (this.bitsData == -0x1)
{
// We've encountered the end of the file stream which means there's no EOI marker in the image
// We've encountered the end of the file stream which means there's no EOI marker ref the image
this.endOfStreamReached = true;
}
@ -608,44 +473,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private short DecodeHuffman(ref PdfJsHuffmanTable tree, Stream stream)
{
short code = -1;
// TODO: Adding this code introduces error into the decoder.
// TODO: Implement fast Huffman decoding.
// NOTES # During investigation of the libjpeg implementation it appears that they pull 32bits at a time and operate on those bits
// using 3 methods: FillBits, PeekBits, and ReadBits. We should attempt to do the same.
// It doesn't appear to speed anything up either.
// if (this.bitsUnRead < 8)
// {
// if (this.bitsCount <= 0)
// {
// code = (short)this.ReadBit(stream);
// if (this.endOfStreamReached || this.unexpectedMarkerReached)
// {
// return -1;
// }
//
// this.bitsUnRead += 8;
// }
//
// this.accumulator = (this.accumulator << 8) | this.bitsData;
// int lutIndex = (this.accumulator >> (8 - this.bitsUnRead)) & 0xFF;
// int v = tree.Lookahead[lutIndex];
// if (v != 0)
// {
// int nb = (v & 0xFF) - 1;
// this.bitsCount -= nb - 1;
// this.bitsUnRead -= nb;
// v = v >> 8;
// return (short)v;
// }
// }
if (code == -1)
// using 3 methods: FillBits, PeekBits, and ReadBits. We should attempt to do the same.
short code = (short)this.ReadBit(stream);
if (this.endOfStreamReached || this.unexpectedMarkerReached)
{
code = (short)this.ReadBit(stream);
if (this.endOfStreamReached || this.unexpectedMarkerReached)
{
return -1;
}
return -1;
}
// "DECODE", section F.2.2.3, figure F.16, page 109 of T.81
@ -704,24 +538,21 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
return n + (-1 << length) + 1;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeBaseline(PdfJsFrameComponent component, int offset, ref PdfJsHuffmanTable dcHuffmanTable, ref PdfJsHuffmanTable acHuffmanTable, Stream stream)
private void DecodeBaseline(PdfJsFrameComponent component, ref short blockDataRef, int offset, ref PdfJsHuffmanTable dcHuffmanTable, ref PdfJsHuffmanTable acHuffmanTable, Stream stream)
{
Span<short> blockDataSpan = component.BlockData.Span;
int t = this.DecodeHuffman(ref dcHuffmanTable, stream);
short t = this.DecodeHuffman(ref dcHuffmanTable, stream);
if (this.endOfStreamReached || this.unexpectedMarkerReached)
{
return;
}
int diff = t == 0 ? 0 : this.ReceiveAndExtend(t, stream);
blockDataSpan[offset] = (short)(component.Pred += diff);
Unsafe.Add(ref blockDataRef, offset) = (short)(component.Pred += diff);
int k = 1;
while (k < 64)
{
int rs = this.DecodeHuffman(ref acHuffmanTable, stream);
short rs = this.DecodeHuffman(ref acHuffmanTable, stream);
if (this.endOfStreamReached || this.unexpectedMarkerReached)
{
return;
@ -748,44 +579,39 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
break;
}
byte z = PdfJsQuantizationTables.DctZigZag[k];
byte z = this.dctZigZag[k];
short re = (short)this.ReceiveAndExtend(s, stream);
blockDataSpan[offset + z] = re;
Unsafe.Add(ref blockDataRef, offset + z) = re;
k++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeDCFirst(PdfJsFrameComponent component, int offset, ref PdfJsHuffmanTable dcHuffmanTable, Stream stream)
private void DecodeDCFirst(PdfJsFrameComponent component, ref short blockDataRef, int offset, ref PdfJsHuffmanTable dcHuffmanTable, Stream stream)
{
Span<short> blockDataSpan = component.BlockData.Span;
int t = this.DecodeHuffman(ref dcHuffmanTable, stream);
short t = this.DecodeHuffman(ref dcHuffmanTable, stream);
if (this.endOfStreamReached || this.unexpectedMarkerReached)
{
return;
}
int diff = t == 0 ? 0 : this.ReceiveAndExtend(t, stream) << this.successiveState;
blockDataSpan[offset] = (short)(component.Pred += diff);
Unsafe.Add(ref blockDataRef, offset) = (short)(component.Pred += diff);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeDCSuccessive(PdfJsFrameComponent component, int offset, Stream stream)
private void DecodeDCSuccessive(PdfJsFrameComponent component, ref short blockDataRef, int offset, Stream stream)
{
Span<short> blockDataSpan = component.BlockData.Span;
int bit = this.ReadBit(stream);
if (this.endOfStreamReached || this.unexpectedMarkerReached)
{
return;
}
blockDataSpan[offset] |= (short)(bit << this.successiveState);
Unsafe.Add(ref blockDataRef, offset) |= (short)(bit << this.successiveState);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeACFirst(PdfJsFrameComponent component, int offset, ref PdfJsHuffmanTable acHuffmanTable, Stream stream)
private void DecodeACFirst(PdfJsFrameComponent component, ref short blockDataRef, int offset, ref PdfJsHuffmanTable acHuffmanTable, Stream stream)
{
if (this.eobrun > 0)
{
@ -793,7 +619,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
return;
}
Span<short> componentBlockDataSpan = component.BlockData.Span;
int k = this.specStart;
int e = this.specEnd;
while (k <= e)
@ -820,22 +645,25 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
}
k += r;
byte z = PdfJsQuantizationTables.DctZigZag[k];
componentBlockDataSpan[offset + z] = (short)(this.ReceiveAndExtend(s, stream) * (1 << this.successiveState));
byte z = this.dctZigZag[k];
Unsafe.Add(ref blockDataRef, offset + z) = (short)(this.ReceiveAndExtend(s, stream) * (1 << this.successiveState));
k++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeACSuccessive(PdfJsFrameComponent component, int offset, ref PdfJsHuffmanTable acHuffmanTable, Stream stream)
private void DecodeACSuccessive(PdfJsFrameComponent component, ref short blockDataRef, int offset, ref PdfJsHuffmanTable acHuffmanTable, Stream stream)
{
int k = this.specStart;
int e = this.specEnd;
int r = 0;
Span<short> componentBlockDataSpan = component.BlockData.Span;
while (k <= e)
{
byte z = PdfJsQuantizationTables.DctZigZag[k];
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
@ -874,7 +702,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
continue;
case 1: // Skipping r zero items
case 2:
if (componentBlockDataSpan[offset + z] != 0)
if (blockOffsetZRef != 0)
{
int bit = this.ReadBit(stream);
if (this.endOfStreamReached || this.unexpectedMarkerReached)
@ -882,7 +710,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
return;
}
componentBlockDataSpan[offset + z] += (short)(bit << this.successiveState);
blockOffsetZRef += (short)(sign * (bit << this.successiveState));
}
else
{
@ -895,7 +723,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
break;
case 3: // Set value for a zero item
if (componentBlockDataSpan[offset + z] != 0)
if (blockOffsetZRef != 0)
{
int bit = this.ReadBit(stream);
if (this.endOfStreamReached || this.unexpectedMarkerReached)
@ -903,17 +731,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
return;
}
componentBlockDataSpan[offset + z] += (short)(bit << this.successiveState);
blockOffsetZRef += (short)(sign * (bit << this.successiveState));
}
else
{
componentBlockDataSpan[offset + z] = (short)(this.successiveACNextValue << this.successiveState);
blockOffsetZRef = (short)(this.successiveACNextValue << this.successiveState);
this.successiveACState = 0;
}
break;
case 4: // Eob
if (componentBlockDataSpan[offset + z] != 0)
if (blockOffsetZRef != 0)
{
int bit = this.ReadBit(stream);
if (this.endOfStreamReached || this.unexpectedMarkerReached)
@ -921,7 +749,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
return;
}
componentBlockDataSpan[offset + z] += (short)(bit << this.successiveState);
blockOffsetZRef += (short)(sign * (bit << this.successiveState));
}
break;

131
src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsYCbCrToRgbTables.cs

@ -1,131 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
{
/// <summary>
/// Provides 8-bit lookup tables for converting from YCbCr to Rgb colorspace.
/// Methods to build the tables are based on libjpeg implementation.
/// </summary>
internal readonly struct PdfJsYCbCrToRgbTables
{
/// <summary>
/// The red red-chrominance table
/// </summary>
public static int[] CrRTable = new int[256];
/// <summary>
/// The blue blue-chrominance table
/// </summary>
public static int[] CbBTable = new int[256];
/// <summary>
/// The green red-chrominance table
/// </summary>
public static int[] CrGTable = new int[256];
/// <summary>
/// The green blue-chrominance table
/// </summary>
public static int[] CbGTable = new int[256];
// Speediest right-shift on some machines and gives us enough accuracy at 4 decimal places.
private const int ScaleBits = 16;
private const int Half = 1 << (ScaleBits - 1);
private const int MinSample = 0;
private const int HalfSample = 128;
private const int MaxSample = 255;
/// <summary>
/// Initializes the YCbCr tables
/// </summary>
public static void Create()
{
for (int i = 0, x = -128; i <= 255; i++, x++)
{
// i is the actual input pixel value, in the range 0..255
// The Cb or Cr value we are thinking of is x = i - 128
// Cr=>R value is nearest int to 1.402 * x
CrRTable[i] = RightShift((Fix(1.402F) * x) + Half);
// Cb=>B value is nearest int to 1.772 * x
CbBTable[i] = RightShift((Fix(1.772F) * x) + Half);
// Cr=>G value is scaled-up -0.714136286
CrGTable[i] = (-Fix(0.714136286F)) * x;
// Cb => G value is scaled - up - 0.344136286 * x
// We also add in Half so that need not do it in inner loop
CbGTable[i] = ((-Fix(0.344136286F)) * x) + Half;
}
}
/// <summary>
/// Optimized method to pack bytes to the image from the YCbCr color space.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="packed">The packed pixel.</param>
/// <param name="y">The y luminance component.</param>
/// <param name="cb">The cb chroma component.</param>
/// <param name="cr">The cr chroma component.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void PackYCbCr<TPixel>(ref TPixel packed, byte y, byte cb, byte cr)
where TPixel : struct, IPixel<TPixel>
{
byte r = (byte)(y + CrRTable[cr]).Clamp(0, 255);
// The values for the G calculation are left scaled up, since we must add them together before rounding.
byte g = (byte)(y + RightShift(CbGTable[cb] + CrGTable[cr])).Clamp(0, 255);
byte b = (byte)(y + CbBTable[cb]).Clamp(0, 255);
packed.PackFromRgba32(new Rgba32(r, g, b, 255));
}
/// <summary>
/// Optimized method to pack bytes to the image from the YccK color space.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="packed">The packed pixel.</param>
/// <param name="y">The y luminance component.</param>
/// <param name="cb">The cb chroma component.</param>
/// <param name="cr">The cr chroma component.</param>
/// <param name="k">The keyline component.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void PackYccK<TPixel>(ref TPixel packed, byte y, byte cb, byte cr, byte k)
where TPixel : struct, IPixel<TPixel>
{
int c = (MaxSample - (y + CrRTable[cr])).Clamp(0, 255);
// The values for the G calculation are left scaled up, since we must add them together before rounding.
int m = (MaxSample - (y + RightShift(CbGTable[cb] + CrGTable[cr]))).Clamp(0, 255);
int cy = (MaxSample - (y + CbBTable[cb])).Clamp(0, 255);
byte r = (byte)((c * k) / MaxSample);
byte g = (byte)((m * k) / MaxSample);
byte b = (byte)((cy * k) / MaxSample);
packed.PackFromRgba32(new Rgba32(r, g, b, MaxSample));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int Fix(float x)
{
return (int)((x * (1L << ScaleBits)) + 0.5F);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int RightShift(int x)
{
return x >> ScaleBits;
}
}
}

138
src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegConstants.cs

@ -194,62 +194,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
/// </summary>
public const ushort RST7 = 0xFFD7;
/// <summary>
/// Contains JFIF specific markers
/// </summary>
public static class JFif
{
/// <summary>
/// Represents J in ASCII
/// </summary>
public const byte J = 0x4A;
/// <summary>
/// Represents F in ASCII
/// </summary>
public const byte F = 0x46;
/// <summary>
/// Represents I in ASCII
/// </summary>
public const byte I = 0x49;
/// <summary>
/// Represents the null "0" marker
/// </summary>
public const byte Null = 0x0;
}
/// <summary>
/// Contains Adobe specific markers
/// </summary>
public static class Adobe
{
/// <summary>
/// Represents A in ASCII
/// </summary>
public const byte A = 0x41;
/// <summary>
/// Represents d in ASCII
/// </summary>
public const byte D = 0x64;
/// <summary>
/// Represents b in ASCII
/// </summary>
public const byte O = 0x6F;
/// <summary>
/// Represents b in ASCII
/// </summary>
public const byte B = 0x62;
/// <summary>
/// Represents e in ASCII
/// </summary>
public const byte E = 0x65;
/// <summary>
/// The color transform is unknown.(RGB or CMYK)
/// </summary>
@ -265,93 +214,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
/// </summary>
public const byte ColorTransformYcck = 2;
}
/// <summary>
/// Contains EXIF specific markers
/// </summary>
public static class Exif
{
/// <summary>
/// Represents E in ASCII
/// </summary>
public const byte E = 0x45;
/// <summary>
/// Represents x in ASCII
/// </summary>
public const byte X = 0x78;
/// <summary>
/// Represents i in ASCII
/// </summary>
public const byte I = 0x69;
/// <summary>
/// Represents f in ASCII
/// </summary>
public const byte F = 0x66;
/// <summary>
/// Represents the null "0" marker
/// </summary>
public const byte Null = 0x0;
}
/// <summary>
/// Contains ICC specific markers
/// </summary>
public static class ICC
{
/// <summary>
/// Represents I in ASCII
/// </summary>
public const byte I = 0x49;
/// <summary>
/// Represents C in ASCII
/// </summary>
public const byte C = 0x43;
/// <summary>
/// Represents _ in ASCII
/// </summary>
public const byte UnderScore = 0x5F;
/// <summary>
/// Represents P in ASCII
/// </summary>
public const byte P = 0x50;
/// <summary>
/// Represents R in ASCII
/// </summary>
public const byte R = 0x52;
/// <summary>
/// Represents O in ASCII
/// </summary>
public const byte O = 0x4F;
/// <summary>
/// Represents F in ASCII
/// </summary>
public const byte F = 0x46;
/// <summary>
/// Represents L in ASCII
/// </summary>
public const byte L = 0x4C;
/// <summary>
/// Represents E in ASCII
/// </summary>
public const byte E = 0x45;
/// <summary>
/// Represents the null "0" marker
/// </summary>
public const byte Null = 0x0;
}
}
}
}

13
src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoder.cs

@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
/// <summary>
/// Image decoder for generating an image out of a jpg stream.
/// </summary>
internal sealed class PdfJsJpegDecoder : IImageDecoder, IJpegDecoderOptions
internal sealed class PdfJsJpegDecoder : IImageDecoder, IJpegDecoderOptions, IImageInfoDetector
{
/// <summary>
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded.
@ -27,5 +27,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
return decoder.Decode<TPixel>(stream);
}
}
/// <inheritdoc/>
public IImageInfo Identify(Configuration configuration, Stream stream)
{
Guard.NotNull(stream, nameof(stream));
using (var decoder = new PdfJsJpegDecoderCore(configuration, this))
{
return decoder.Identify(stream);
}
}
}
}

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

Loading…
Cancel
Save