Browse Source

Merge branch 'master' into js/new-jpeg-scan-decoder

pull/643/head
James Jackson-South 8 years ago
parent
commit
31d5fa476c
  1. 4
      .travis.yml
  2. 2
      ImageSharp.ruleset
  3. 14
      README.md
  4. 27
      appveyor.yml
  5. 5
      src/ImageSharp.Drawing/ImageSharp.Drawing.csproj
  6. 4
      src/ImageSharp.Drawing/Processing/Drawing/Brushes/BrushApplicator.cs
  7. 36
      src/ImageSharp.Drawing/Processing/Drawing/Brushes/GradientBrushes/ColorStop{TPixel}.cs
  8. 167
      src/ImageSharp.Drawing/Processing/Drawing/Brushes/GradientBrushes/EllipticGradientBrush{TPixel}.cs
  9. 174
      src/ImageSharp.Drawing/Processing/Drawing/Brushes/GradientBrushes/GradientBrushBase{TPixel}.cs
  10. 34
      src/ImageSharp.Drawing/Processing/Drawing/Brushes/GradientBrushes/GradientRepetitionMode.cs
  11. 152
      src/ImageSharp.Drawing/Processing/Drawing/Brushes/GradientBrushes/LinearGradientBrush{TPixel}.cs
  12. 102
      src/ImageSharp.Drawing/Processing/Drawing/Brushes/GradientBrushes/RadialGradientBrush{TPixel}.cs
  13. 2
      src/ImageSharp.Drawing/Processing/Drawing/FillPathExtensions.cs
  14. 2
      src/ImageSharp/Advanced/IConfigurable.cs
  15. 55
      src/ImageSharp/Common/Extensions/ByteExtensions.cs
  16. 4
      src/ImageSharp/Common/Helpers/DebugGuard.cs
  17. 95
      src/ImageSharp/Common/Helpers/Guard.cs
  18. 2
      src/ImageSharp/Configuration.cs
  19. 24
      src/ImageSharp/Formats/Gif/GifDecoderCore.cs
  20. 7
      src/ImageSharp/Formats/ImageFormatManager.cs
  21. 2
      src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs
  22. 3
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.CopyTo.cs
  23. 2
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs
  24. 2
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt
  25. 10
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
  26. 4
      src/ImageSharp/Formats/Jpeg/Components/Decoder/AdobeMarker.cs
  27. 6
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmyk.cs
  28. 6
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs
  29. 6
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgb.cs
  30. 6
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrBasic.cs
  31. 7
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs
  32. 7
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs
  33. 4
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccK.cs
  34. 7
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs
  35. 5
      src/ImageSharp/Formats/Jpeg/Components/Decoder/IJpegComponent.cs
  36. 7
      src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs
  37. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs
  38. 3
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs
  39. 5
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegColorSpace.cs
  40. 6
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs
  41. 18
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs
  42. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ProfileResolver.cs
  43. 8
      src/ImageSharp/Formats/Jpeg/Components/Encoder/BlockQuad.cs
  44. 2
      src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffIndex.cs
  45. 2
      src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanLut.cs
  46. 2
      src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanSpec.cs
  47. 2
      src/ImageSharp/Formats/Jpeg/Components/Encoder/QuantIndex.cs
  48. 12
      src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrTables.cs
  49. 4
      src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs
  50. 2
      src/ImageSharp/Formats/Jpeg/Components/FastFloatingPointDCT.cs
  51. 5
      src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.Generated.cs
  52. 5
      src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.Generated.tt
  53. 9
      src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs
  54. 3
      src/ImageSharp/Formats/Jpeg/Components/SizeExtensions.cs
  55. 3
      src/ImageSharp/Formats/Jpeg/Components/ZigZag.cs
  56. 36
      src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bits.cs
  57. 62
      src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bytes.cs
  58. 28
      src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/DecoderThrowHelper.cs
  59. 21
      src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangComponent.cs
  60. 2
      src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangComponentScan.cs
  61. 2
      src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangDecoderErrorCode.cs
  62. 12
      src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangHuffmanTree.cs
  63. 11
      src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangJpegScanDecoder.ComputationData.cs
  64. 8
      src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangJpegScanDecoder.DataPointers.cs
  65. 67
      src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangJpegScanDecoder.cs
  66. 80
      src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs
  67. 6
      src/ImageSharp/Formats/Jpeg/GolangPort/GolangJpegDecoder.cs
  68. 86
      src/ImageSharp/Formats/Jpeg/GolangPort/GolangJpegDecoderCore.cs
  69. 189
      src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegConstants.cs
  70. 69
      src/ImageSharp/Formats/Jpeg/JpegConstants.cs
  71. 6
      src/ImageSharp/Formats/Jpeg/JpegDecoder.cs
  72. 1
      src/ImageSharp/Formats/Jpeg/JpegEncoder.cs
  73. 74
      src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
  74. 5
      src/ImageSharp/Formats/Jpeg/JpegFormat.cs
  75. 5
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrameComponent.cs
  76. 7
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsScanDecoder.cs
  77. 162
      src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs
  78. 7
      src/ImageSharp/Formats/Png/Filters/NoneFilter.cs
  79. 16
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  80. 12
      src/ImageSharp/Image.LoadPixelData.cs
  81. 90
      src/ImageSharp/ImageExtensions.cs
  82. 15
      src/ImageSharp/ImageFrame.LoadPixelData.cs
  83. 12
      src/ImageSharp/ImageFrameCollection.cs
  84. 80
      src/ImageSharp/ImageFrame{TPixel}.cs
  85. 21
      src/ImageSharp/ImageSharp.csproj
  86. 2
      src/ImageSharp/Image{TPixel}.cs
  87. 48
      src/ImageSharp/Memory/SpanHelper.cs
  88. 2
      src/ImageSharp/MetaData/ImageProperty.cs
  89. 2
      src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs
  90. 10
      src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.cs
  91. 80
      src/ImageSharp/MetaData/Profiles/ICC/IccProfile.cs
  92. 47
      src/ImageSharp/MetaData/Profiles/ICC/IccReader.cs
  93. 5
      src/ImageSharp/PixelFormats/Bgr24.cs
  94. 28
      src/ImageSharp/PixelFormats/ColorBuilder{TPixel}.cs
  95. 5
      src/ImageSharp/PixelFormats/Rgb24.cs
  96. 14
      src/ImageSharp/Processing/Quantization/FrameQuantizers/WuFrameQuantizer{TPixel}.cs
  97. 2
      src/ImageSharp/Processing/Transforms/Processors/AffineTransformProcessor.cs
  98. 2
      src/ImageSharp/Processing/Transforms/Processors/CropProcessor.cs
  99. 2
      src/ImageSharp/Processing/Transforms/Processors/ProjectiveTransformProcessor.cs
  100. 30
      src/ImageSharp/Processing/Transforms/Processors/ResizeProcessor.cs

4
.travis.yml

@ -6,7 +6,7 @@ matrix:
- os: linux # Ubuntu 14.04 - os: linux # Ubuntu 14.04
dist: trusty dist: trusty
sudo: required sudo: required
dotnet: 2.1.4 dotnet: 2.1.300-rc1-008673
mono: latest mono: latest
# - os: osx # OSX 10.11 # - os: osx # OSX 10.11
# osx_image: xcode7.3.1 # osx_image: xcode7.3.1
@ -21,7 +21,7 @@ branches:
script: script:
- git submodule -q update --init - git submodule -q update --init
- dotnet restore - dotnet restore
- dotnet test tests/ImageSharp.Tests/ImageSharp.Tests.csproj -c Release -f "netcoreapp2.0" - dotnet test tests/ImageSharp.Tests/ImageSharp.Tests.csproj -c Release -f "netcoreapp2.1"
env: env:
global: global:

2
ImageSharp.ruleset

@ -9,5 +9,7 @@
<Rule Id="SA1633" Action="None" /> <Rule Id="SA1633" Action="None" />
<!--1.1.0-beta6 incorrectly throws a wobbler here for multiline matrices--> <!--1.1.0-beta6 incorrectly throws a wobbler here for multiline matrices-->
<Rule Id="SA1500" Action="None" /> <Rule Id="SA1500" Action="None" />
<!--Comments should end with a period. I like this but there's 3000+ errors to fix-->
<Rule Id="SA1629" Action="None" />
</Rules> </Rules>
</RuleSet> </RuleSet>

14
README.md

@ -115,17 +115,21 @@ For more examples check out:
### Manual build ### Manual build
If you prefer, you can compile ImageSharp yourself (please do and help!), you'll need: If you prefer, you can compile ImageSharp yourself (please do and help!)
- [Visual Studio 2017 (or above)](https://www.visualstudio.com/en-us/news/releasenotes/vs2017-relnotes) - Using [Visual Studio 2017 Preview](https://docs.microsoft.com/en-us/visualstudio/releasenotes/vs2017-preview-relnotes)
- The [.NET Core SDK Installer](https://www.microsoft.com/net/core#windows) - Non VSCode link. - Make sure you have the latest version installed
- Make sure you have [the newest 2.1 RC1 SDK installed](https://www.microsoft.com/net/core#windows)
Alternatively on Linux you can use: - Using [Visual Studio 2017](https://www.visualstudio.com/en-us/news/releasenotes/vs2017-relnotes)
- If you are unable and/or don't want to build ImageSharp.Tests against 2.1 RC, remove the `netcoreapp2.1` target [from TargetFrameworks](https://github.com/SixLabors/ImageSharp/blob/master/tests/ImageSharp.Tests/ImageSharp.Tests.csproj#L3) locally
Alternatively, you can work from command line and/or with a lightweight editor on **both Linux/Unix and Windows**:
- [Visual Studio Code](https://code.visualstudio.com/) with [C# Extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode.csharp) - [Visual Studio Code](https://code.visualstudio.com/) with [C# Extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode.csharp)
- [.Net Core](https://www.microsoft.com/net/core#linuxubuntu) - [.Net Core](https://www.microsoft.com/net/core#linuxubuntu)
To clone it locally click the "Clone in Windows" button above or run the following git commands. To clone ImageSharp locally click the "Clone in Windows" button above or run the following git commands.
```bash ```bash
git clone https://github.com/SixLabors/ImageSharp git clone https://github.com/SixLabors/ImageSharp

27
appveyor.yml

@ -1,21 +1,34 @@
version: 1.0.0.{build} version: 1.0.0.{build}
image: Visual Studio 2017 image: Visual Studio 2017 Preview
# prevent the double build when a branch has an active PR # prevent the double build when a branch has an active PR
skip_branch_with_pr: true skip_branch_with_pr: true
environment: environment:
matrix: matrix:
### TODO: Enable the netcoreapp2.1 target when RC2 has been released!
#- target_framework: netcoreapp2.1
# is_32bit: False
#- target_framework: netcoreapp2.1
# is_32bit: True
- target_framework: netcoreapp2.0
is_32bit: False
- target_framework: net471
is_32bit: False
- target_framework: net471 - target_framework: net471
is_32bit: True is_32bit: True
- target_framework: net462 - target_framework: net462
is_32bit: False is_32bit: False
- target_framework: net462 - target_framework: net462
is_32bit: True is_32bit: True
- target_framework: net471
is_32bit: False
- target_framework: netcoreapp2.0
is_32bit: False
#- target_framework: netcoreapp2.0 # As far as I understand, 32 bit test execution is not supported by "dotnet xunit" #- target_framework: netcoreapp2.0 # As far as I understand, 32 bit test execution is not supported by "dotnet xunit"
# is_32bit: True # is_32bit: True
#- target_framework: mono #- target_framework: mono
@ -39,7 +52,7 @@ install:
before_build: before_build:
- git submodule -q update --init - git submodule -q update --init
- cmd: dotnet --version - cmd: dotnet --info
build_script: build_script:
- cmd: build.cmd - cmd: build.cmd
@ -60,4 +73,4 @@ deploy:
secure: V/lEHP0UeMWIpWd0fiNlY2IgbCnJKQlGdRksECdJbOBdaE20Fl0RNL7WyqHe02o4 secure: V/lEHP0UeMWIpWd0fiNlY2IgbCnJKQlGdRksECdJbOBdaE20Fl0RNL7WyqHe02o4
artifact: /.*\.nupkg/ artifact: /.*\.nupkg/
on: on:
branch: master branch: master

5
src/ImageSharp.Drawing/ImageSharp.Drawing.csproj

@ -38,10 +38,9 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="SixLabors.Core" Version="1.0.0-beta0005" /> <PackageReference Include="SixLabors.Core" Version="1.0.0-beta0005" />
<PackageReference Include="SixLabors.Shapes" Version="1.0.0-beta0004" />
<PackageReference Include="SixLabors.Shapes.Text" Version="1.0.0-beta0004" />
<AdditionalFiles Include="..\..\stylecop.json" /> <AdditionalFiles Include="..\..\stylecop.json" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.0-beta004"> <PackageReference Include="SixLabors.Shapes.Text" Version="1.0.0-beta0005" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.0-beta007">
<PrivateAssets>All</PrivateAssets> <PrivateAssets>All</PrivateAssets>
</PackageReference> </PackageReference>
</ItemGroup> </ItemGroup>

4
src/ImageSharp.Drawing/Processing/Drawing/Brushes/BrushApplicator.cs

@ -79,6 +79,10 @@ namespace SixLabors.ImageSharp.Processing.Drawing.Brushes
{ {
amountSpan[i] = scanline[i] * this.Options.BlendPercentage; amountSpan[i] = scanline[i] * this.Options.BlendPercentage;
} }
else
{
amountSpan[i] = scanline[i];
}
overlaySpan[i] = this[x + i, y]; overlaySpan[i] = this[x + i, y];
} }

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

@ -0,0 +1,36 @@
using System.Diagnostics;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Drawing.Brushes.GradientBrushes
{
/// <summary>
/// A struct that defines a single color stop.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
[DebuggerDisplay("ColorStop({Ratio} -> {Color}")]
public struct ColorStop<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="ColorStop{TPixel}" /> struct.
/// </summary>
/// <param name="ratio">Where should it be? 0 is at the start, 1 at the end of the Gradient.</param>
/// <param name="color">What color should be used at that point?</param>
public ColorStop(float ratio, TPixel color)
{
this.Ratio = ratio;
this.Color = color;
}
/// <summary>
/// Gets the point along the defined gradient axis.
/// </summary>
public float Ratio { get; }
/// <summary>
/// Gets the color to be used.
/// </summary>
public TPixel Color { get; }
}
}

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

@ -0,0 +1,167 @@
using System;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Drawing.Brushes.GradientBrushes
{
/// <summary>
/// Gradient Brush with elliptic shape.
/// The ellipse is defined by a center point,
/// a point on the longest extension of the ellipse and
/// the ratio between longest and shortest extension.
/// </summary>
/// <typeparam name="TPixel">The Pixel format that is used.</typeparam>
public sealed class EllipticGradientBrush<TPixel> : GradientBrushBase<TPixel>
where TPixel : struct, IPixel<TPixel>
{
private readonly Point center;
private readonly Point referenceAxisEnd;
private readonly float axisRatio;
/// <inheritdoc cref="GradientBrushBase{TPixel}" />
/// <param name="center">The center of the elliptical gradient and 0 for the color stops.</param>
/// <param name="referenceAxisEnd">The end point of the reference axis of the ellipse.</param>
/// <param name="axisRatio">
/// The ratio of the axis widths.
/// The second axis' is perpendicular to the reference axis and
/// it's length is the reference axis' length multiplied by this factor.
/// </param>
/// <param name="repetitionMode">Defines how the colors of the gradients are repeated.</param>
/// <param name="colorStops">the color stops as defined in base class.</param>
public EllipticGradientBrush(
Point center,
Point referenceAxisEnd,
float axisRatio,
GradientRepetitionMode repetitionMode,
params ColorStop<TPixel>[] colorStops)
: base(repetitionMode, colorStops)
{
this.center = center;
this.referenceAxisEnd = referenceAxisEnd;
this.axisRatio = axisRatio;
}
/// <inheritdoc cref="CreateApplicator" />
public override BrushApplicator<TPixel> CreateApplicator(
ImageFrame<TPixel> source,
RectangleF region,
GraphicsOptions options) =>
new RadialGradientBrushApplicator(
source,
options,
this.center,
this.referenceAxisEnd,
this.axisRatio,
this.ColorStops,
this.RepetitionMode);
/// <inheritdoc />
private sealed class RadialGradientBrushApplicator : GradientBrushApplicatorBase
{
private readonly Point center;
private readonly Point referenceAxisEnd;
private readonly float axisRatio;
private readonly double rotation;
private readonly float referenceRadius;
private readonly float secondRadius;
private readonly float cosRotation;
private readonly float sinRotation;
private readonly float secondRadiusSquared;
private readonly float referenceRadiusSquared;
/// <summary>
/// Initializes a new instance of the <see cref="RadialGradientBrushApplicator" /> class.
/// </summary>
/// <param name="target">The target image</param>
/// <param name="options">The options</param>
/// <param name="center">Center of the ellipse</param>
/// <param name="referenceAxisEnd">Point on one angular points of the ellipse.</param>
/// <param name="axisRatio">
/// Ratio of the axis length's. Used to determine the length of the second axis,
/// the first is defined by <see cref="center"/> and <see cref="referenceAxisEnd"/>.</param>
/// <param name="colorStops">Definition of colors</param>
/// <param name="repetitionMode">Defines how the gradient colors are repeated.</param>
public RadialGradientBrushApplicator(
ImageFrame<TPixel> target,
GraphicsOptions options,
Point center,
Point referenceAxisEnd,
float axisRatio,
ColorStop<TPixel>[] colorStops,
GradientRepetitionMode repetitionMode)
: base(target, options, colorStops, repetitionMode)
{
this.center = center;
this.referenceAxisEnd = referenceAxisEnd;
this.axisRatio = axisRatio;
this.rotation = this.AngleBetween(
this.center,
new PointF(this.center.X + 1, this.center.Y),
this.referenceAxisEnd);
this.referenceRadius = this.DistanceBetween(this.center, this.referenceAxisEnd);
this.secondRadius = this.referenceRadius * this.axisRatio;
this.referenceRadiusSquared = this.referenceRadius * this.referenceRadius;
this.secondRadiusSquared = this.secondRadius * this.secondRadius;
this.sinRotation = (float)Math.Sin(this.rotation);
this.cosRotation = (float)Math.Cos(this.rotation);
}
/// <inheritdoc />
public override void Dispose()
{
}
/// <inheritdoc />
protected override float PositionOnGradient(int xt, int yt)
{
float x0 = xt - this.center.X;
float y0 = yt - this.center.Y;
float x = (x0 * this.cosRotation) - (y0 * this.sinRotation);
float y = (x0 * this.sinRotation) + (y0 * this.cosRotation);
float xSquared = x * x;
float ySquared = y * y;
var inBoundaryChecker = (xSquared / this.referenceRadiusSquared)
+ (ySquared / this.secondRadiusSquared);
return inBoundaryChecker;
}
private float AngleBetween(PointF junction, PointF a, PointF b)
{
var vA = a - junction;
var vB = b - junction;
return (float)(Math.Atan2(vB.Y, vB.X)
- Math.Atan2(vA.Y, vA.X));
}
private float DistanceBetween(
PointF p1,
PointF p2)
{
float dX = p1.X - p2.X;
float dXsquared = dX * dX;
float dY = p1.Y - p2.Y;
float dYsquared = dY * dY;
return (float)Math.Sqrt(dXsquared + dYsquared);
}
}
}
}

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

@ -0,0 +1,174 @@
using System;
using System.Numerics;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.PixelFormats.PixelBlenders;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Drawing.Brushes.GradientBrushes
{
/// <summary>
/// Base class for Gradient brushes
/// </summary>
/// <typeparam name="TPixel">The pixel format</typeparam>
public abstract class GradientBrushBase<TPixel> : IBrush<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <inheritdoc cref="IBrush{TPixel}"/>
/// <param name="repetitionMode">Defines how the colors are repeated beyond the interval [0..1]</param>
/// <param name="colorStops">The gradient colors.</param>
protected GradientBrushBase(
GradientRepetitionMode repetitionMode,
params ColorStop<TPixel>[] colorStops)
{
this.RepetitionMode = repetitionMode;
this.ColorStops = colorStops;
}
/// <summary>
/// Gets how the colors are repeated beyond the interval [0..1].
/// </summary>
protected GradientRepetitionMode RepetitionMode { get; }
/// <summary>
/// Gets the list of color stops for this gradient.
/// </summary>
protected ColorStop<TPixel>[] ColorStops { get; }
/// <inheritdoc cref="IBrush{TPixel}" />
public abstract BrushApplicator<TPixel> CreateApplicator(
ImageFrame<TPixel> source,
RectangleF region,
GraphicsOptions options);
/// <summary>
/// Base class for gradient brush applicators
/// </summary>
protected abstract class GradientBrushApplicatorBase : BrushApplicator<TPixel>
{
private readonly ColorStop<TPixel>[] colorStops;
private readonly GradientRepetitionMode repetitionMode;
/// <summary>
/// Initializes a new instance of the <see cref="GradientBrushApplicatorBase"/> class.
/// </summary>
/// <param name="target">The target.</param>
/// <param name="options">The options.</param>
/// <param name="colorStops">An array of color stops sorted by their position.</param>
/// <param name="repetitionMode">Defines if and how the gradient should be repeated.</param>
protected GradientBrushApplicatorBase(
ImageFrame<TPixel> target,
GraphicsOptions options,
ColorStop<TPixel>[] colorStops,
GradientRepetitionMode repetitionMode)
: base(target, options)
{
this.colorStops = colorStops; // TODO: requires colorStops to be sorted by position - should that be checked?
this.repetitionMode = repetitionMode;
}
/// <summary>
/// Base implementation of the indexer for gradients
/// (follows the facade pattern, using abstract methods)
/// </summary>
/// <param name="x">X coordinate of the Pixel.</param>
/// <param name="y">Y coordinate of the Pixel.</param>
internal override TPixel this[int x, int y]
{
get
{
float positionOnCompleteGradient = this.PositionOnGradient(x, y);
switch (this.repetitionMode)
{
case GradientRepetitionMode.None:
// do nothing. The following could be done, but is not necessary:
// onLocalGradient = Math.Min(0, Math.Max(1, onLocalGradient));
break;
case GradientRepetitionMode.Repeat:
positionOnCompleteGradient = positionOnCompleteGradient % 1;
break;
case GradientRepetitionMode.Reflect:
positionOnCompleteGradient = positionOnCompleteGradient % 2;
if (positionOnCompleteGradient > 1)
{
positionOnCompleteGradient = 2 - positionOnCompleteGradient;
}
break;
case GradientRepetitionMode.DontFill:
if (positionOnCompleteGradient > 1 || positionOnCompleteGradient < 0)
{
return NamedColors<TPixel>.Transparent;
}
break;
default:
throw new ArgumentOutOfRangeException();
}
var (from, to) = this.GetGradientSegment(positionOnCompleteGradient);
if (from.Color.Equals(to.Color))
{
return from.Color;
}
else
{
var fromAsVector = from.Color.ToVector4();
var toAsVector = to.Color.ToVector4();
float onLocalGradient = (positionOnCompleteGradient - from.Ratio) / to.Ratio;
// TODO: this should be changeble for different gradienting functions
Vector4 result = PorterDuffFunctions.Normal(
fromAsVector,
toAsVector,
onLocalGradient);
TPixel resultColor = default;
resultColor.PackFromVector4(result);
return resultColor;
}
}
}
/// <summary>
/// calculates the position on the gradient for a given pixel.
/// This method is abstract as it's content depends on the shape of the gradient.
/// </summary>
/// <param name="x">The x coordinate of the pixel</param>
/// <param name="y">The y coordinate of the pixel</param>
/// <returns>
/// The position the given pixel has on the gradient.
/// The position is not bound to the [0..1] interval.
/// Values outside of that interval may be treated differently,
/// e.g. for the <see cref="GradientRepetitionMode" /> enum.
/// </returns>
protected abstract float PositionOnGradient(int x, int y);
private (ColorStop<TPixel> from, ColorStop<TPixel> to) GetGradientSegment(
float positionOnCompleteGradient)
{
var localGradientFrom = this.colorStops[0];
ColorStop<TPixel> localGradientTo = default;
// TODO: ensure colorStops has at least 2 items (technically 1 would be okay, but that's no gradient)
foreach (var colorStop in this.colorStops)
{
localGradientTo = colorStop;
if (colorStop.Ratio > positionOnCompleteGradient)
{
// we're done here, so break it!
break;
}
localGradientFrom = localGradientTo;
}
return (localGradientFrom, localGradientTo);
}
}
}
}

34
src/ImageSharp.Drawing/Processing/Drawing/Brushes/GradientBrushes/GradientRepetitionMode.cs

@ -0,0 +1,34 @@
namespace SixLabors.ImageSharp.Processing.Drawing.Brushes.GradientBrushes
{
/// <summary>
/// Modes to repeat a gradient.
/// </summary>
public enum GradientRepetitionMode
{
/// <summary>
/// don't repeat, keep the color of start and end beyond those points stable.
/// </summary>
None,
/// <summary>
/// Repeat the gradient.
/// If it's a black-white gradient, with Repeat it will be Black->{gray}->White|Black->{gray}->White|...
/// </summary>
Repeat,
/// <summary>
/// Reflect the gradient.
/// Similar to <see cref="Repeat"/>, but each other repetition uses inverse order of <see cref="ColorStop{TPixel}"/>s.
/// Used on a Black-White gradient, Reflect leads to Black->{gray}->White->{gray}->White...
/// </summary>
Reflect,
/// <summary>
/// With DontFill a gradient does not touch any pixel beyond it's borders.
/// For the <see cref="LinearGradientBrush{TPixel}" /> this is beyond the orthogonal through start and end,
/// TODO For the cref="PolygonalGradientBrush" it's outside the polygon,
/// For <see cref="RadialGradientBrush{TPixel}" /> and <see cref="EllipticGradientBrush{TPixel}" /> it's beyond 1.0.
/// </summary>
DontFill
}
}

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

@ -0,0 +1,152 @@
using System;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Drawing.Brushes.GradientBrushes
{
/// <summary>
/// Provides an implementation of a brush for painting linear gradients within areas.
/// Supported right now:
/// - a set of colors in relative distances to each other.
/// </summary>
/// <typeparam name="TPixel">The pixel format</typeparam>
public sealed class LinearGradientBrush<TPixel> : GradientBrushBase<TPixel>
where TPixel : struct, IPixel<TPixel>
{
private readonly Point p1;
private readonly Point p2;
/// <summary>
/// Initializes a new instance of the <see cref="LinearGradientBrush{TPixel}"/> class.
/// </summary>
/// <param name="p1">Start point</param>
/// <param name="p2">End point</param>
/// <param name="repetitionMode">defines how colors are repeated.</param>
/// <param name="colorStops"><inheritdoc /></param>
public LinearGradientBrush(
Point p1,
Point p2,
GradientRepetitionMode repetitionMode,
params ColorStop<TPixel>[] colorStops)
: base(repetitionMode, colorStops)
{
this.p1 = p1;
this.p2 = p2;
}
/// <inheritdoc />
public override BrushApplicator<TPixel> CreateApplicator(ImageFrame<TPixel> source, RectangleF region, GraphicsOptions options)
=> new LinearGradientBrushApplicator(source, this.p1, this.p2, this.ColorStops, this.RepetitionMode, options);
/// <summary>
/// The linear gradient brush applicator.
/// </summary>
private sealed class LinearGradientBrushApplicator : GradientBrushApplicatorBase
{
private readonly Point start;
private readonly Point end;
/// <summary>
/// the vector along the gradient, x component
/// </summary>
private readonly float alongX;
/// <summary>
/// the vector along the gradient, y component
/// </summary>
private readonly float alongY;
/// <summary>
/// the vector perpendicular to the gradient, y component
/// </summary>
private readonly float acrossY;
/// <summary>
/// the vector perpendicular to the gradient, x component
/// </summary>
private readonly float acrossX;
/// <summary>
/// the result of <see cref="alongX"/>^2 + <see cref="alongY"/>^2
/// </summary>
private readonly float alongsSquared;
/// <summary>
/// the length of the defined gradient (between source and end)
/// </summary>
private readonly float length;
/// <summary>
/// Initializes a new instance of the <see cref="LinearGradientBrushApplicator" /> class.
/// </summary>
/// <param name="source">The source</param>
/// <param name="start">start point of the gradient</param>
/// <param name="end">end point of the gradient</param>
/// <param name="colorStops">tuple list of colors and their respective position between 0 and 1 on the line</param>
/// <param name="repetitionMode">defines how the gradient colors are repeated.</param>
/// <param name="options">the graphics options</param>
public LinearGradientBrushApplicator(
ImageFrame<TPixel> source,
Point start,
Point end,
ColorStop<TPixel>[] colorStops,
GradientRepetitionMode repetitionMode,
GraphicsOptions options)
: base(source, options, colorStops, repetitionMode)
{
this.start = start;
this.end = end;
// the along vector:
this.alongX = this.end.X - this.start.X;
this.alongY = this.end.Y - this.start.Y;
// the cross vector:
this.acrossX = this.alongY;
this.acrossY = -this.alongX;
// some helpers:
this.alongsSquared = (this.alongX * this.alongX) + (this.alongY * this.alongY);
this.length = (float)Math.Sqrt(this.alongsSquared);
}
protected override float PositionOnGradient(int x, int y)
{
if (this.acrossX == 0)
{
return (x - this.start.X) / (float)(this.end.X - this.start.X);
}
else if (this.acrossY == 0)
{
return (y - this.start.Y) / (float)(this.end.Y - this.start.Y);
}
else
{
float deltaX = x - this.start.X;
float deltaY = y - this.start.Y;
float k = ((this.alongY * deltaX) - (this.alongX * deltaY)) / this.alongsSquared;
// point on the line:
float x4 = x - (k * this.alongY);
float y4 = y + (k * this.alongX);
// get distance from (x4,y4) to start
float distance = (float)Math.Sqrt(
Math.Pow(x4 - this.start.X, 2)
+ Math.Pow(y4 - this.start.Y, 2));
// get and return ratio
float ratio = distance / this.length;
return ratio;
}
}
public override void Dispose()
{
}
}
}
}

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

@ -0,0 +1,102 @@
using System;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Drawing.Brushes.GradientBrushes
{
/// <summary>
/// A Circular Gradient Brush, defined by center point and radius.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
public sealed class RadialGradientBrush<TPixel> : GradientBrushBase<TPixel>
where TPixel : struct, IPixel<TPixel>
{
private readonly Point center;
private readonly float radius;
/// <inheritdoc cref="GradientBrushBase{TPixel}" />
/// <param name="center">The center of the circular gradient and 0 for the color stops.</param>
/// <param name="radius">The radius of the circular gradient and 1 for the color stops.</param>
/// <param name="repetitionMode">Defines how the colors in the gradient are repeated.</param>
/// <param name="colorStops">the color stops as defined in base class.</param>
public RadialGradientBrush(
Point center,
float radius,
GradientRepetitionMode repetitionMode,
params ColorStop<TPixel>[] colorStops)
: base(repetitionMode, colorStops)
{
this.center = center;
this.radius = radius;
}
/// <inheritdoc cref="CreateApplicator" />
public override BrushApplicator<TPixel> CreateApplicator(
ImageFrame<TPixel> source,
RectangleF region,
GraphicsOptions options) =>
new RadialGradientBrushApplicator(
source,
options,
this.center,
this.radius,
this.ColorStops,
this.RepetitionMode);
/// <inheritdoc />
private sealed class RadialGradientBrushApplicator : GradientBrushApplicatorBase
{
private readonly Point center;
private readonly float radius;
/// <summary>
/// Initializes a new instance of the <see cref="RadialGradientBrushApplicator" /> class.
/// </summary>
/// <param name="target">The target image</param>
/// <param name="options">The options.</param>
/// <param name="center">Center point of the gradient.</param>
/// <param name="radius">Radius of the gradient.</param>
/// <param name="colorStops">Definition of colors.</param>
/// <param name="repetitionMode">How the colors are repeated beyond the first gradient.</param>
public RadialGradientBrushApplicator(
ImageFrame<TPixel> target,
GraphicsOptions options,
Point center,
float radius,
ColorStop<TPixel>[] colorStops,
GradientRepetitionMode repetitionMode)
: base(target, options, colorStops, repetitionMode)
{
this.center = center;
this.radius = radius;
}
/// <inheritdoc cref="Dispose" />
public override void Dispose()
{
}
/// <summary>
/// As this is a circular gradient, the position on the gradient is based on
/// the distance of the point to the center.
/// </summary>
/// <param name="x">The X coordinate of the target pixel.</param>
/// <param name="y">The Y coordinate of the target pixel.</param>
/// <returns>the position on the color gradient.</returns>
protected override float PositionOnGradient(int x, int y)
{
float distance = (float)Math.Sqrt(Math.Pow(this.center.X - x, 2) + Math.Pow(this.center.Y - y, 2));
return distance / this.radius;
}
internal override void Apply(Span<float> scanline, int x, int y)
{
// TODO: each row is symmetric across center, so we can calculate half of it and mirror it to improve performance.
base.Apply(scanline, x, y);
}
}
}
}

2
src/ImageSharp.Drawing/Processing/Drawing/FillPathExtensions.cs

@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Processing.Drawing
public static class FillPathExtensions public static class FillPathExtensions
{ {
/// <summary> /// <summary>
/// Flood fills the image in the shape of the provided polygon with the specified brush.. /// Flood fills the image in the shape of the provided polygon with the specified brush.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam> /// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>

2
src/ImageSharp/Advanced/IConfigurable.cs

@ -4,7 +4,7 @@
namespace SixLabors.ImageSharp.Advanced namespace SixLabors.ImageSharp.Advanced
{ {
/// <summary> /// <summary>
/// Encapsulates the properties for configuration /// Encapsulates the properties for configuration.
/// </summary> /// </summary>
internal interface IConfigurable internal interface IConfigurable
{ {

55
src/ImageSharp/Common/Extensions/ByteExtensions.cs

@ -1,55 +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.PixelFormats;
namespace SixLabors.ImageSharp
{
/// <summary>
/// Extension methods for the <see cref="byte"/> struct buffers.
/// </summary>
internal static class ByteExtensions
{
/// <summary>
/// Returns a reference to the given position of the array unsafe casted to <see cref="ImageSharp.PixelFormats.Rgb24"/>.
/// </summary>
/// <param name="bytes">The byte array.</param>
/// <param name="offset">The offset in bytes.</param>
/// <returns>The <see cref="ImageSharp.PixelFormats.Rgb24"/> reference at the given offset.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref Rgb24 GetRgb24(this byte[] bytes, int offset)
{
DebugGuard.MustBeLessThan(offset + 2, bytes.Length, nameof(offset));
return ref Unsafe.As<byte, Rgb24>(ref bytes[offset]);
}
/// <summary>
/// Returns a reference to the given position of the span unsafe casted to <see cref="ImageSharp.PixelFormats.Rgb24"/>.
/// </summary>
/// <param name="bytes">The byte span.</param>
/// <param name="offset">The offset in bytes.</param>
/// <returns>The <see cref="ImageSharp.PixelFormats.Rgb24"/> reference at the given offset.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref Rgb24 GetRgb24(this Span<byte> bytes, int offset)
{
DebugGuard.MustBeLessThan(offset + 2, bytes.Length, nameof(offset));
return ref Unsafe.As<byte, Rgb24>(ref bytes[offset]);
}
/// <summary>
/// Returns a reference to the given position of the buffer pointed by `baseRef` unsafe casted to <see cref="ImageSharp.PixelFormats.Rgb24"/>.
/// </summary>
/// <param name="baseRef">A reference to the beginning of the buffer</param>
/// <param name="offset">The offset in bytes.</param>
/// <returns>The <see cref="ImageSharp.PixelFormats.Rgb24"/> reference at the given offset.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref Rgb24 GetRgb24(ref byte baseRef, int offset)
{
return ref Unsafe.As<byte, Rgb24>(ref Unsafe.Add(ref baseRef, offset));
}
}
}

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

@ -189,7 +189,7 @@ namespace SixLabors.ImageSharp
/// <param name="other">The 'other' span to compare 'target' to.</param> /// <param name="other">The 'other' span to compare 'target' to.</param>
/// <param name="parameterName">The name of the parameter that is to be checked.</param> /// <param name="parameterName">The name of the parameter that is to be checked.</param>
/// <exception cref="ArgumentException"> /// <exception cref="ArgumentException">
/// <paramref name="target"/> is true /// <paramref name="target"/> has a different size than <paramref name="other"/>
/// </exception> /// </exception>
[Conditional("DEBUG")] [Conditional("DEBUG")]
public static void MustBeSameSized<T>(Span<T> target, Span<T> other, string parameterName) public static void MustBeSameSized<T>(Span<T> target, Span<T> other, string parameterName)
@ -209,7 +209,7 @@ namespace SixLabors.ImageSharp
/// <param name="minSpan">The 'minSpan' span to compare 'target' to.</param> /// <param name="minSpan">The 'minSpan' span to compare 'target' to.</param>
/// <param name="parameterName">The name of the parameter that is to be checked.</param> /// <param name="parameterName">The name of the parameter that is to be checked.</param>
/// <exception cref="ArgumentException"> /// <exception cref="ArgumentException">
/// <paramref name="target"/> is true /// <paramref name="target"/> has less items than <paramref name="minSpan"/>
/// </exception> /// </exception>
[Conditional("DEBUG")] [Conditional("DEBUG")]
public static void MustBeSizedAtLeast<T>(Span<T> target, Span<T> minSpan, string parameterName) public static void MustBeSizedAtLeast<T>(Span<T> target, Span<T> minSpan, string parameterName)

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

@ -4,7 +4,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq;
namespace SixLabors.ImageSharp namespace SixLabors.ImageSharp
{ {
@ -15,78 +14,62 @@ namespace SixLabors.ImageSharp
internal static class Guard internal static class Guard
{ {
/// <summary> /// <summary>
/// Verifies, that the method parameter with specified object value is not null /// Ensures that the value is not null.
/// and throws an exception if it is found to be so.
/// </summary> /// </summary>
/// <param name="target">The target object, which cannot be null.</param> /// <param name="value">The target object, which cannot be null.</param>
/// <param name="parameterName">The name of the parameter that is to be checked.</param> /// <param name="parameterName">The name of the parameter that is to be checked.</param>
/// <param name="message">The error message, if any to add to the exception.</param> /// <exception cref="ArgumentNullException"><paramref name="value"/> is null</exception>
/// <exception cref="ArgumentNullException"><paramref name="target"/> is null</exception> public static void NotNull(object value, string parameterName)
public static void NotNull(object target, string parameterName, string message = "")
{ {
if (target == null) if (value == null)
{ {
if (!string.IsNullOrWhiteSpace(message))
{
throw new ArgumentNullException(parameterName, message);
}
throw new ArgumentNullException(parameterName); throw new ArgumentNullException(parameterName);
} }
} }
/// <summary> /// <summary>
/// Verifies, that the string method parameter with specified object value and message /// Ensures that the target value is not null, empty, or whitespace.
/// is not null, not empty and does not contain only blanks and throws an exception
/// if the object is null.
/// </summary> /// </summary>
/// <param name="target">The target string, which should be checked against being null or empty.</param> /// <param name="value">The target string, which should be checked against being null or empty.</param>
/// <param name="parameterName">Name of the parameter.</param> /// <param name="parameterName">Name of the parameter.</param>
/// <param name="message">The error message, if any to add to the exception.</param> /// <exception cref="ArgumentNullException"><paramref name="value"/> is null.</exception>
/// <exception cref="ArgumentNullException"><paramref name="target"/> is null.</exception> /// <exception cref="ArgumentException"><paramref name="value"/> is empty or contains only blanks.</exception>
/// <exception cref="ArgumentException"><paramref name="target"/> is empty or contains only blanks.</exception> public static void NotNullOrWhiteSpace(string value, string parameterName)
public static void NotNullOrEmpty(string target, string parameterName, string message = "")
{ {
NotNull(target, parameterName, message); if (value == null)
if (string.IsNullOrWhiteSpace(target))
{ {
if (!string.IsNullOrWhiteSpace(message)) throw new ArgumentNullException(parameterName);
{ }
throw new ArgumentException(message, parameterName);
}
throw new ArgumentException("Value cannot be null or empty and cannot contain only blanks.", parameterName); if (string.IsNullOrWhiteSpace(value))
{
throw new ArgumentException("Must not be empty or whitespace.", parameterName);
} }
} }
/// <summary> /// <summary>
/// Verifies, that the enumeration is not null and not empty. /// Ensures that the enumeration is not null or empty.
/// </summary> /// </summary>
/// <typeparam name="T">The type of objects in the <paramref name="target"/></typeparam> /// <typeparam name="T">The type of objects in the <paramref name="value"/></typeparam>
/// <param name="target">The target enumeration, which should be checked against being null or empty.</param> /// <param name="value">The target enumeration, which should be checked against being null or empty.</param>
/// <param name="parameterName">Name of the parameter.</param> /// <param name="parameterName">Name of the parameter.</param>
/// <param name="message">The error message, if any to add to the exception.</param> /// <exception cref="ArgumentNullException"><paramref name="value"/> is null.</exception>
/// <exception cref="ArgumentNullException"><paramref name="target"/> is null.</exception> /// <exception cref="ArgumentException"><paramref name="value"/> is empty.</exception>
/// <exception cref="ArgumentException"><paramref name="target"/> is empty.</exception> public static void NotNullOrEmpty<T>(ICollection<T> value, string parameterName)
public static void NotNullOrEmpty<T>(IEnumerable<T> target, string parameterName, string message = "")
{ {
NotNull(target, parameterName, message); if (value == null)
if (!target.Any())
{ {
if (!string.IsNullOrWhiteSpace(message)) throw new ArgumentNullException(parameterName);
{ }
throw new ArgumentException(message, parameterName);
}
throw new ArgumentException("Value cannot be empty.", parameterName); if (value.Count == 0)
{
throw new ArgumentException("Must not be empty.", parameterName);
} }
} }
/// <summary> /// <summary>
/// Verifies that the specified value is less than a maximum value /// Ensures that the specified value is less than a maximum value.
/// and throws an exception if it is not.
/// </summary> /// </summary>
/// <param name="value">The target value, which should be validated.</param> /// <param name="value">The target value, which should be validated.</param>
/// <param name="max">The maximum value.</param> /// <param name="max">The maximum value.</param>
@ -191,15 +174,9 @@ namespace SixLabors.ImageSharp
/// Verifies, that the method parameter with specified target value is true /// Verifies, that the method parameter with specified target value is true
/// and throws an exception if it is found to be so. /// and throws an exception if it is found to be so.
/// </summary> /// </summary>
/// <param name="target"> /// <param name="target">The target value, which cannot be false.</param>
/// The target value, which cannot be false. /// <param name="parameterName">The name of the parameter that is to be checked.</param>
/// </param> /// <param name="message">The error message, if any to add to the exception.</param>
/// <param name="parameterName">
/// The name of the parameter that is to be checked.
/// </param>
/// <param name="message">
/// The error message, if any to add to the exception.
/// </param>
/// <exception cref="ArgumentException"> /// <exception cref="ArgumentException">
/// <paramref name="target"/> is false /// <paramref name="target"/> is false
/// </exception> /// </exception>
@ -230,14 +207,14 @@ namespace SixLabors.ImageSharp
} }
/// <summary> /// <summary>
/// Verifies, that the `source` span has the length of 'minSpan', or longer. /// Verifies, that the `source` span has the length of 'minLength', or longer.
/// </summary> /// </summary>
/// <typeparam name="T">The element type of the spans</typeparam> /// <typeparam name="T">The element type of the spans</typeparam>
/// <param name="source">The source span.</param> /// <param name="source">The source span.</param>
/// <param name="minLength">The minimum length.</param> /// <param name="minLength">The minimum length.</param>
/// <param name="parameterName">The name of the parameter that is to be checked.</param> /// <param name="parameterName">The name of the parameter that is to be checked.</param>
/// <exception cref="ArgumentException"> /// <exception cref="ArgumentException">
/// <paramref name="source"/> is true /// <paramref name="source"/> has less than <paramref name="minLength"/> items
/// </exception> /// </exception>
public static void MustBeSizedAtLeast<T>(ReadOnlySpan<T> source, int minLength, string parameterName) public static void MustBeSizedAtLeast<T>(ReadOnlySpan<T> source, int minLength, string parameterName)
{ {
@ -248,14 +225,14 @@ namespace SixLabors.ImageSharp
} }
/// <summary> /// <summary>
/// Verifies, that the `source` span has the length of 'minSpan', or longer. /// Verifies, that the `source` span has the length of 'minLength', or longer.
/// </summary> /// </summary>
/// <typeparam name="T">The element type of the spans</typeparam> /// <typeparam name="T">The element type of the spans</typeparam>
/// <param name="source">The target span.</param> /// <param name="source">The target span.</param>
/// <param name="minLength">The minimum length.</param> /// <param name="minLength">The minimum length.</param>
/// <param name="parameterName">The name of the parameter that is to be checked.</param> /// <param name="parameterName">The name of the parameter that is to be checked.</param>
/// <exception cref="ArgumentException"> /// <exception cref="ArgumentException">
/// <paramref name="source"/> is true /// <paramref name="source"/> has less than <paramref name="minLength"/> items
/// </exception> /// </exception>
public static void MustBeSizedAtLeast<T>(Span<T> source, int minLength, string parameterName) public static void MustBeSizedAtLeast<T>(Span<T> source, int minLength, string parameterName)
{ {

2
src/ImageSharp/Configuration.cs

@ -18,7 +18,7 @@ using SixLabors.ImageSharp.Processing;
namespace SixLabors.ImageSharp namespace SixLabors.ImageSharp
{ {
/// <summary> /// <summary>
/// Provides initialization code which allows extending the library. /// Provides configuration code which allows altering default behaviour or extending the library.
/// </summary> /// </summary>
public sealed class Configuration public sealed class Configuration
{ {

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

@ -39,11 +39,6 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary> /// </summary>
private IManagedByteBuffer globalColorTable; private IManagedByteBuffer globalColorTable;
/// <summary>
/// The global color table length
/// </summary>
private int globalColorTableLength;
/// <summary> /// <summary>
/// The area to restore. /// The area to restore.
/// </summary> /// </summary>
@ -333,8 +328,8 @@ namespace SixLabors.ImageSharp.Formats.Gif
indices = this.configuration.MemoryManager.AllocateManagedByteBuffer(imageDescriptor.Width * imageDescriptor.Height, true); indices = this.configuration.MemoryManager.AllocateManagedByteBuffer(imageDescriptor.Width * imageDescriptor.Height, true);
this.ReadFrameIndices(imageDescriptor, indices.Span); this.ReadFrameIndices(imageDescriptor, indices.Span);
IManagedByteBuffer colorTable = localColorTable ?? this.globalColorTable; ReadOnlySpan<Rgb24> colorTable = MemoryMarshal.Cast<byte, Rgb24>((localColorTable ?? this.globalColorTable).Span);
this.ReadFrameColors(ref image, ref previousFrame, indices.Span, colorTable.Span, imageDescriptor); this.ReadFrameColors(ref image, ref previousFrame, indices.Span, colorTable, imageDescriptor);
// Skip any remaining blocks // Skip any remaining blocks
this.Skip(0); this.Skip(0);
@ -370,7 +365,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <param name="indices">The indexed pixels.</param> /// <param name="indices">The indexed pixels.</param>
/// <param name="colorTable">The color table containing the available colors.</param> /// <param name="colorTable">The color table containing the available colors.</param>
/// <param name="descriptor">The <see cref="GifImageDescriptor"/></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, in GifImageDescriptor descriptor) private void ReadFrameColors<TPixel>(ref Image<TPixel> image, ref ImageFrame<TPixel> previousFrame, Span<byte> indices, ReadOnlySpan<Rgb24> colorTable, in GifImageDescriptor descriptor)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
ref byte indicesRef = ref MemoryMarshal.GetReference(indices); ref byte indicesRef = ref MemoryMarshal.GetReference(indices);
@ -458,11 +453,8 @@ namespace SixLabors.ImageSharp.Formats.Gif
if (this.graphicsControlExtension.TransparencyFlag == false || if (this.graphicsControlExtension.TransparencyFlag == false ||
this.graphicsControlExtension.TransparencyIndex != index) this.graphicsControlExtension.TransparencyIndex != index)
{ {
int indexOffset = index * 3;
ref TPixel pixel = ref Unsafe.Add(ref rowRef, x); ref TPixel pixel = ref Unsafe.Add(ref rowRef, x);
rgba.Rgb = colorTable.GetRgb24(indexOffset); rgba.Rgb = colorTable[index];
pixel.PackFromRgba32(rgba); pixel.PackFromRgba32(rgba);
} }
@ -534,12 +526,12 @@ namespace SixLabors.ImageSharp.Formats.Gif
if (this.logicalScreenDescriptor.GlobalColorTableFlag) if (this.logicalScreenDescriptor.GlobalColorTableFlag)
{ {
this.globalColorTableLength = this.logicalScreenDescriptor.GlobalColorTableSize * 3; int globalColorTableLength = this.logicalScreenDescriptor.GlobalColorTableSize * 3;
this.globalColorTable = this.MemoryManager.AllocateManagedByteBuffer(this.globalColorTableLength, true); this.globalColorTable = this.MemoryManager.AllocateManagedByteBuffer(globalColorTableLength, true);
// Read the global color table from the stream // Read the global color table data from the stream
stream.Read(this.globalColorTable.Array, 0, this.globalColorTableLength); stream.Read(this.globalColorTable.Array, 0, globalColorTableLength);
} }
} }
} }

7
src/ImageSharp/Formats/ImageFormatManager.cs

@ -85,6 +85,13 @@ namespace SixLabors.ImageSharp.Formats
/// <returns>The <see cref="IImageFormat"/> if found otherwise null</returns> /// <returns>The <see cref="IImageFormat"/> if found otherwise null</returns>
public IImageFormat FindFormatByFileExtension(string extension) public IImageFormat FindFormatByFileExtension(string extension)
{ {
Guard.NotNullOrWhiteSpace(extension, nameof(extension));
if (extension[0] == '.')
{
extension = extension.Substring(1);
}
return this.imageFormats.FirstOrDefault(x => x.FileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)); return this.imageFormats.FirstOrDefault(x => x.FileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase));
} }

2
src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs → src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs

@ -7,7 +7,7 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{ {
/// <summary> /// <summary>
/// Represents a Jpeg block with <see cref="short"/> coefficiens. /// Represents a Jpeg block with <see cref="short"/> coefficiens.

3
src/ImageSharp/Formats/Jpeg/Common/Block8x8F.CopyTo.cs → src/ImageSharp/Formats/Jpeg/Components/Block8x8F.CopyTo.cs

@ -3,10 +3,11 @@
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
// ReSharper disable InconsistentNaming // ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Formats.Jpeg.Common namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{ {
internal partial struct Block8x8F internal partial struct Block8x8F
{ {

2
src/ImageSharp/Formats/Jpeg/Common/Block8x8F.Generated.cs → src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs

@ -5,7 +5,7 @@ using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
// <auto-generated /> // <auto-generated />
namespace SixLabors.ImageSharp.Formats.Jpeg.Common namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{ {
internal partial struct Block8x8F internal partial struct Block8x8F
{ {

2
src/ImageSharp/Formats/Jpeg/Common/Block8x8F.Generated.tt → src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt

@ -18,7 +18,7 @@ using System.Runtime.CompilerServices;
<# <#
char[] coordz = {'X', 'Y', 'Z', 'W'}; char[] coordz = {'X', 'Y', 'Z', 'W'};
#> #>
namespace SixLabors.ImageSharp.Formats.Jpeg.Common namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{ {
internal partial struct Block8x8F internal partial struct Block8x8F
{ {

10
src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs → src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs

@ -9,7 +9,7 @@ using System.Runtime.InteropServices;
using System.Text; using System.Text;
// ReSharper disable InconsistentNaming // ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Formats.Jpeg.Common namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{ {
/// <summary> /// <summary>
/// Represents a Jpeg block with <see cref="float"/> coefficients. /// Represents a Jpeg block with <see cref="float"/> coefficients.
@ -100,7 +100,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
{ {
float val = result[i]; float val = result[i];
val /= value; val /= value;
result[i] = (float)val; result[i] = val;
} }
return result; return result;
@ -113,7 +113,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
{ {
float val = result[i]; float val = result[i];
val += value; val += value;
result[i] = (float)val; result[i] = val;
} }
return result; return result;
@ -126,7 +126,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
{ {
float val = result[i]; float val = result[i];
val -= value; val -= value;
result[i] = (float)val; result[i] = val;
} }
return result; return result;
@ -153,7 +153,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
public void Clear() public void Clear()
{ {
// The cheapest way to do this in C#: // The cheapest way to do this in C#:
this = default(Block8x8F); this = default;
} }
/// <summary> /// <summary>

4
src/ImageSharp/Formats/Jpeg/Common/Decoder/AdobeMarker.cs → src/ImageSharp/Formats/Jpeg/Components/Decoder/AdobeMarker.cs

@ -4,7 +4,7 @@
using System; using System;
// ReSharper disable InconsistentNaming // ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{ {
/// <summary> /// <summary>
/// Provides information about the Adobe marker segment. /// Provides information about the Adobe marker segment.
@ -78,7 +78,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
return true; return true;
} }
marker = default(AdobeMarker); marker = default;
return false; return false;
} }

6
src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromCmyk.cs → src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmyk.cs

@ -4,18 +4,18 @@
using System; using System;
using System.Numerics; using System.Numerics;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{ {
internal abstract partial class JpegColorConverter internal abstract partial class JpegColorConverter
{ {
internal class FromCmyk : ColorConverters.JpegColorConverter internal class FromCmyk : JpegColorConverter
{ {
public FromCmyk() public FromCmyk()
: base(JpegColorSpace.Cmyk) : base(JpegColorSpace.Cmyk)
{ {
} }
public override void ConvertToRGBA(ComponentValues values, Span<Vector4> result) public override void ConvertToRgba(ComponentValues values, Span<Vector4> result)
{ {
// TODO: We can optimize a lot here with Vector<float> and SRCS.Unsafe()! // TODO: We can optimize a lot here with Vector<float> and SRCS.Unsafe()!
ReadOnlySpan<float> cVals = values.Component0; ReadOnlySpan<float> cVals = values.Component0;

6
src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs → src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs

@ -4,18 +4,18 @@
using System; using System;
using System.Numerics; using System.Numerics;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{ {
internal abstract partial class JpegColorConverter internal abstract partial class JpegColorConverter
{ {
internal class FromGrayscale : ColorConverters.JpegColorConverter internal class FromGrayscale : JpegColorConverter
{ {
public FromGrayscale() public FromGrayscale()
: base(JpegColorSpace.Grayscale) : base(JpegColorSpace.Grayscale)
{ {
} }
public override void ConvertToRGBA(ComponentValues values, Span<Vector4> result) public override void ConvertToRgba(ComponentValues values, Span<Vector4> result)
{ {
// TODO: We can optimize a lot here with Vector<float> and SRCS.Unsafe()! // TODO: We can optimize a lot here with Vector<float> and SRCS.Unsafe()!
ReadOnlySpan<float> yVals = values.Component0; ReadOnlySpan<float> yVals = values.Component0;

6
src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromRgb.cs → src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgb.cs

@ -4,18 +4,18 @@
using System; using System;
using System.Numerics; using System.Numerics;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{ {
internal abstract partial class JpegColorConverter internal abstract partial class JpegColorConverter
{ {
internal class FromRgb : ColorConverters.JpegColorConverter internal class FromRgb : JpegColorConverter
{ {
public FromRgb() public FromRgb()
: base(JpegColorSpace.RGB) : base(JpegColorSpace.RGB)
{ {
} }
public override void ConvertToRGBA(ComponentValues values, Span<Vector4> result) public override void ConvertToRgba(ComponentValues values, Span<Vector4> result)
{ {
// TODO: We can optimize a lot here with Vector<float> and SRCS.Unsafe()! // TODO: We can optimize a lot here with Vector<float> and SRCS.Unsafe()!
ReadOnlySpan<float> rVals = values.Component0; ReadOnlySpan<float> rVals = values.Component0;

6
src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrBasic.cs → src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrBasic.cs

@ -4,18 +4,18 @@
using System; using System;
using System.Numerics; using System.Numerics;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{ {
internal abstract partial class JpegColorConverter internal abstract partial class JpegColorConverter
{ {
internal class FromYCbCrBasic : ColorConverters.JpegColorConverter internal class FromYCbCrBasic : JpegColorConverter
{ {
public FromYCbCrBasic() public FromYCbCrBasic()
: base(JpegColorSpace.YCbCr) : base(JpegColorSpace.YCbCr)
{ {
} }
public override void ConvertToRGBA(ComponentValues values, Span<Vector4> result) public override void ConvertToRgba(ComponentValues values, Span<Vector4> result)
{ {
ConvertCore(values, result); ConvertCore(values, result);
} }

7
src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs → src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs

@ -5,20 +5,21 @@ using System;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Common.Tuples; using SixLabors.ImageSharp.Common.Tuples;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{ {
internal abstract partial class JpegColorConverter internal abstract partial class JpegColorConverter
{ {
internal class FromYCbCrSimd : ColorConverters.JpegColorConverter internal class FromYCbCrSimd : JpegColorConverter
{ {
public FromYCbCrSimd() public FromYCbCrSimd()
: base(JpegColorSpace.YCbCr) : base(JpegColorSpace.YCbCr)
{ {
} }
public override void ConvertToRGBA(ComponentValues values, Span<Vector4> result) public override void ConvertToRgba(ComponentValues values, Span<Vector4> result)
{ {
int remainder = result.Length % 8; int remainder = result.Length % 8;
int simdCount = result.Length - remainder; int simdCount = result.Length - remainder;

7
src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs → src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs

@ -5,14 +5,15 @@ using System;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Common.Tuples; using SixLabors.ImageSharp.Common.Tuples;
// ReSharper disable ImpureMethodCallOnReadonlyValueField // ReSharper disable ImpureMethodCallOnReadonlyValueField
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{ {
internal abstract partial class JpegColorConverter internal abstract partial class JpegColorConverter
{ {
internal class FromYCbCrSimdAvx2 : ColorConverters.JpegColorConverter internal class FromYCbCrSimdAvx2 : JpegColorConverter
{ {
public FromYCbCrSimdAvx2() public FromYCbCrSimdAvx2()
: base(JpegColorSpace.YCbCr) : base(JpegColorSpace.YCbCr)
@ -21,7 +22,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters
public static bool IsAvailable => Vector.IsHardwareAccelerated && SimdUtils.IsAvx2CompatibleArchitecture; public static bool IsAvailable => Vector.IsHardwareAccelerated && SimdUtils.IsAvx2CompatibleArchitecture;
public override void ConvertToRGBA(ComponentValues values, Span<Vector4> result) public override void ConvertToRgba(ComponentValues values, Span<Vector4> result)
{ {
int remainder = result.Length % 8; int remainder = result.Length % 8;
int simdCount = result.Length - remainder; int simdCount = result.Length - remainder;

4
src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYccK.cs → src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccK.cs

@ -4,7 +4,7 @@
using System; using System;
using System.Numerics; using System.Numerics;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{ {
internal abstract partial class JpegColorConverter internal abstract partial class JpegColorConverter
{ {
@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters
{ {
} }
public override void ConvertToRGBA(ComponentValues values, Span<Vector4> result) public override void ConvertToRgba(ComponentValues values, Span<Vector4> result)
{ {
// TODO: We can optimize a lot here with Vector<float> and SRCS.Unsafe()! // TODO: We can optimize a lot here with Vector<float> and SRCS.Unsafe()!
ReadOnlySpan<float> yVals = values.Component0; ReadOnlySpan<float> yVals = values.Component0;

7
src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.cs → src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs

@ -5,10 +5,11 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using SixLabors.ImageSharp.Common.Tuples; using SixLabors.ImageSharp.Common.Tuples;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{ {
/// <summary> /// <summary>
/// Encapsulates the conversion of Jpeg channels to RGBA values packed in <see cref="Vector4"/> buffer. /// Encapsulates the conversion of Jpeg channels to RGBA values packed in <see cref="Vector4"/> buffer.
@ -55,13 +56,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters
/// </summary> /// </summary>
/// <param name="values">The input as a stack-only <see cref="ComponentValues"/> struct</param> /// <param name="values">The input as a stack-only <see cref="ComponentValues"/> struct</param>
/// <param name="result">The destination buffer of <see cref="Vector4"/> values</param> /// <param name="result">The destination buffer of <see cref="Vector4"/> values</param>
public abstract void ConvertToRGBA(ComponentValues values, Span<Vector4> result); public abstract void ConvertToRgba(ComponentValues values, Span<Vector4> result);
/// <summary> /// <summary>
/// Returns the <see cref="JpegColorConverter"/> for the YCbCr colorspace that matches the current CPU architecture. /// Returns the <see cref="JpegColorConverter"/> for the YCbCr colorspace that matches the current CPU architecture.
/// </summary> /// </summary>
private static JpegColorConverter GetYCbCrConverter() => private static JpegColorConverter GetYCbCrConverter() =>
FromYCbCrSimdAvx2.IsAvailable ? (JpegColorConverter)new FromYCbCrSimdAvx2() : new FromYCbCrSimd(); JpegColorConverter.FromYCbCrSimdAvx2.IsAvailable ? (JpegColorConverter)new JpegColorConverter.FromYCbCrSimdAvx2() : new JpegColorConverter.FromYCbCrSimd();
/// <summary> /// <summary>
/// A stack-only struct to reference the input buffers using <see cref="ReadOnlySpan{T}"/>-s. /// A stack-only struct to reference the input buffers using <see cref="ReadOnlySpan{T}"/>-s.

5
src/ImageSharp/Formats/Jpeg/Common/Decoder/IJpegComponent.cs → src/ImageSharp/Formats/Jpeg/Components/Decoder/IJpegComponent.cs

@ -1,7 +1,10 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.Primitives; using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{ {
/// <summary> /// <summary>
/// Common interface to represent raw Jpeg components. /// Common interface to represent raw Jpeg components.

7
src/ImageSharp/Formats/Jpeg/Common/Decoder/IRawJpegData.cs → src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs

@ -1,13 +1,16 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using SixLabors.Primitives; using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{ {
/// <inheritdoc /> /// <inheritdoc />
/// <summary> /// <summary>
/// Represents decompressed, unprocessed jpeg data with spectral space <see cref="T:SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.IJpegComponent" />-s. /// Represents decompressed, unprocessed jpeg data with spectral space <see cref="IJpegComponent" />-s.
/// </summary> /// </summary>
internal interface IRawJpegData : IDisposable internal interface IRawJpegData : IDisposable
{ {

2
src/ImageSharp/Formats/Jpeg/Common/Decoder/JFifMarker.cs → src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs

@ -3,7 +3,7 @@
using System; using System;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{ {
/// <summary> /// <summary>
/// Provides information about the JFIF marker segment /// Provides information about the JFIF marker segment

3
src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegBlockPostProcessor.cs → src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs

@ -2,10 +2,11 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.Primitives; using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{ {
/// <summary> /// <summary>
/// Encapsulates the implementation of processing "raw" <see cref="IBuffer{T}"/>-s into Jpeg image channels. /// Encapsulates the implementation of processing "raw" <see cref="IBuffer{T}"/>-s into Jpeg image channels.

5
src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegColorSpace.cs → src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegColorSpace.cs

@ -1,4 +1,7 @@
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{ {
/// <summary> /// <summary>
/// Identifies the colorspace of a Jpeg image /// Identifies the colorspace of a Jpeg image

6
src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegComponentPostProcessor.cs → src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs

@ -1,8 +1,12 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System; using System;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.Primitives; using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{ {
/// <summary> /// <summary>
/// Encapsulates postprocessing data for one component for <see cref="JpegImagePostProcessor"/>. /// Encapsulates postprocessing data for one component for <see cref="JpegImagePostProcessor"/>.

18
src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs → src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs

@ -1,12 +1,18 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives; using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder using JpegColorConverter = SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters.JpegColorConverter;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{ {
/// <summary> /// <summary>
/// Encapsulates the execution od post-processing algorithms to be applied on a <see cref="IRawJpegData"/> to produce a valid <see cref="Image{TPixel}"/>: <br/> /// Encapsulates the execution od post-processing algorithms to be applied on a <see cref="IRawJpegData"/> to produce a valid <see cref="Image{TPixel}"/>: <br/>
@ -36,9 +42,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
private readonly IBuffer<Vector4> rgbaBuffer; private readonly IBuffer<Vector4> rgbaBuffer;
/// <summary> /// <summary>
/// The <see cref="ColorConverters.JpegColorConverter"/> corresponding to the current <see cref="JpegColorSpace"/> determined by <see cref="IRawJpegData.ColorSpace"/>. /// The <see cref="JpegColorConverter"/> corresponding to the current <see cref="JpegColorSpace"/> determined by <see cref="IRawJpegData.ColorSpace"/>.
/// </summary> /// </summary>
private ColorConverters.JpegColorConverter colorConverter; private readonly JpegColorConverter colorConverter;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="JpegImagePostProcessor"/> class. /// Initializes a new instance of the <see cref="JpegImagePostProcessor"/> class.
@ -54,7 +60,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
this.ComponentProcessors = rawJpeg.Components.Select(c => new JpegComponentPostProcessor(memoryManager, this, c)).ToArray(); this.ComponentProcessors = rawJpeg.Components.Select(c => new JpegComponentPostProcessor(memoryManager, this, c)).ToArray();
this.rgbaBuffer = memoryManager.Allocate<Vector4>(rawJpeg.ImageSizeInPixels.Width); this.rgbaBuffer = memoryManager.Allocate<Vector4>(rawJpeg.ImageSizeInPixels.Width);
this.colorConverter = ColorConverters.JpegColorConverter.GetConverter(rawJpeg.ColorSpace); this.colorConverter = JpegColorConverter.GetConverter(rawJpeg.ColorSpace);
} }
/// <summary> /// <summary>
@ -148,8 +154,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
{ {
int y = yy - this.PixelRowCounter; int y = yy - this.PixelRowCounter;
var values = new ColorConverters.JpegColorConverter.ComponentValues(buffers, y); var values = new JpegColorConverter.ComponentValues(buffers, y);
this.colorConverter.ConvertToRGBA(values, this.rgbaBuffer.Span); this.colorConverter.ConvertToRgba(values, this.rgbaBuffer.Span);
Span<TPixel> destRow = destination.GetPixelRowSpan(yy); Span<TPixel> destRow = destination.GetPixelRowSpan(yy);

2
src/ImageSharp/Formats/Jpeg/Common/Decoder/ProfileResolver.cs → src/ImageSharp/Formats/Jpeg/Components/Decoder/ProfileResolver.cs

@ -4,7 +4,7 @@
using System; using System;
using System.Text; using System.Text;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{ {
/// <summary> /// <summary>
/// Provides methods for identifying metadata and color profiles within jpeg images. /// Provides methods for identifying metadata and color profiles within jpeg images.

8
src/ImageSharp/Formats/Jpeg/GolangPort/Components/BlockQuad.cs → src/ImageSharp/Formats/Jpeg/Components/Encoder/BlockQuad.cs

@ -1,18 +1,16 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using Block8x8F = SixLabors.ImageSharp.Formats.Jpeg.Common.Block8x8F; namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components
{ {
/// <summary> /// <summary>
/// Poor man's stackalloc: Contains a value-type <see cref="float"/> buffer sized for 4 <see cref="Common.Block8x8F"/> instances. /// Poor man's stackalloc: Contains a value-type <see cref="float"/> buffer sized for 4 <see cref="Block8x8F"/> instances.
/// Useful for decoder/encoder operations allocating a block for each Jpeg component. /// Useful for decoder/encoder operations allocating a block for each Jpeg component.
/// </summary> /// </summary>
internal unsafe struct BlockQuad internal unsafe struct BlockQuad
{ {
/// <summary> /// <summary>
/// The value-type <see cref="float"/> buffer sized for 4 <see cref="Common.Block8x8F"/> instances. /// The value-type <see cref="float"/> buffer sized for 4 <see cref="Block8x8F"/> instances.
/// </summary> /// </summary>
public fixed float Data[4 * Block8x8F.Size]; public fixed float Data[4 * Block8x8F.Size];
} }

2
src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/HuffIndex.cs → src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffIndex.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
{ {
/// <summary> /// <summary>
/// Enumerates the Huffman tables /// Enumerates the Huffman tables

2
src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/HuffmanLut.cs → src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanLut.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
{ {
/// <summary> /// <summary>
/// A compiled look-up table representation of a huffmanSpec. /// A compiled look-up table representation of a huffmanSpec.

2
src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/HuffmanSpec.cs → src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanSpec.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
{ {
/// <summary> /// <summary>
/// The Huffman encoding specifications. /// The Huffman encoding specifications.

2
src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/QuantIndex.cs → src/ImageSharp/Formats/Jpeg/Components/Encoder/QuantIndex.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
{ {
/// <summary> /// <summary>
/// Enumerates the quantization tables /// Enumerates the quantization tables

12
src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/RgbToYCbCrTables.cs → src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrTables.cs

@ -3,9 +3,7 @@
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder
{ {
/// <summary> /// <summary>
/// Provides 8-bit lookup tables for converting from Rgb to YCbCr colorspace. /// Provides 8-bit lookup tables for converting from Rgb to YCbCr colorspace.
@ -68,7 +66,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder
/// <returns>The intialized <see cref="RgbToYCbCrTables"/></returns> /// <returns>The intialized <see cref="RgbToYCbCrTables"/></returns>
public static RgbToYCbCrTables Create() public static RgbToYCbCrTables Create()
{ {
RgbToYCbCrTables tables = default(RgbToYCbCrTables); RgbToYCbCrTables tables = default;
for (int i = 0; i <= 255; i++) for (int i = 0; i <= 255; i++)
{ {
@ -123,11 +121,5 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder
{ {
return (int)((x * (1L << ScaleBits)) + 0.5F); return (int)((x * (1L << ScaleBits)) + 0.5F);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int RightShift(int x)
{
return x >> ScaleBits;
}
} }
} }

4
src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/YCbCrForwardConverter{TPixel}.cs → src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs

@ -1,10 +1,10 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats.Jpeg.Common;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
{ {
/// <summary> /// <summary>
/// On-stack worker struct to efficiently encapsulate the TPixel -> Rgb24 -> YCbCr conversion chain of 8x8 pixel blocks. /// On-stack worker struct to efficiently encapsulate the TPixel -> Rgb24 -> YCbCr conversion chain of 8x8 pixel blocks.

2
src/ImageSharp/Formats/Jpeg/Common/FastFloatingPointDCT.cs → src/ImageSharp/Formats/Jpeg/Components/FastFloatingPointDCT.cs

@ -5,7 +5,7 @@ using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
// ReSharper disable InconsistentNaming // ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Formats.Jpeg.Common namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{ {
/// <summary> /// <summary>
/// Contains inaccurate, but fast forward and inverse DCT implementations. /// Contains inaccurate, but fast forward and inverse DCT implementations.

5
src/ImageSharp/Formats/Jpeg/Common/GenericBlock8x8.Generated.cs → src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.Generated.cs

@ -1,11 +1,8 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System.Numerics;
using System.Runtime.CompilerServices;
// <auto-generated /> // <auto-generated />
namespace SixLabors.ImageSharp.Formats.Jpeg.Common namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{ {
internal unsafe partial struct GenericBlock8x8<T> internal unsafe partial struct GenericBlock8x8<T>
{ {

5
src/ImageSharp/Formats/Jpeg/Common/GenericBlock8x8.Generated.tt → src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.Generated.tt

@ -11,11 +11,8 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System.Numerics;
using System.Runtime.CompilerServices;
// <auto-generated /> // <auto-generated />
namespace SixLabors.ImageSharp.Formats.Jpeg.Common namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{ {
internal unsafe partial struct GenericBlock8x8<T> internal unsafe partial struct GenericBlock8x8<T>
{ {

9
src/ImageSharp/Formats/Jpeg/Common/GenericBlock8x8.cs → src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs

@ -1,13 +1,16 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
// ReSharper disable InconsistentNaming // ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Formats.Jpeg.Common namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{ {
/// <summary> /// <summary>
/// A generic 8x8 block implementation, useful for manipulating custom 8x8 pixel data. /// A generic 8x8 block implementation, useful for manipulating custom 8x8 pixel data.
@ -18,8 +21,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
{ {
public const int Size = 64; public const int Size = 64;
public const int SizeInBytes = Size * 3;
/// <summary> /// <summary>
/// FOR TESTING ONLY! /// FOR TESTING ONLY!
/// Gets or sets a <see cref="Rgb24"/> value at the given index /// Gets or sets a <see cref="Rgb24"/> value at the given index

3
src/ImageSharp/Formats/Jpeg/Common/SizeExtensions.cs → src/ImageSharp/Formats/Jpeg/Components/SizeExtensions.cs

@ -3,9 +3,10 @@
using System; using System;
using System.Numerics; using System.Numerics;
using SixLabors.Primitives; using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{ {
/// <summary> /// <summary>
/// Extension methods for <see cref="Size"/> /// Extension methods for <see cref="Size"/>

3
src/ImageSharp/Formats/Jpeg/Common/ZigZag.cs → src/ImageSharp/Formats/Jpeg/Components/ZigZag.cs

@ -1,10 +1,11 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{ {
/// <summary> /// <summary>
/// Holds the Jpeg UnZig array in a value/stack type. /// Holds the Jpeg UnZig array in a value/stack type.

36
src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bits.cs

@ -38,7 +38,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void EnsureNBits(int n, ref InputProcessor inputProcessor) public void EnsureNBits(int n, ref InputProcessor inputProcessor)
{ {
OrigDecoderErrorCode errorCode = this.EnsureNBitsUnsafe(n, ref inputProcessor); GolangDecoderErrorCode errorCode = this.EnsureNBitsUnsafe(n, ref inputProcessor);
errorCode.EnsureNoError(); errorCode.EnsureNoError();
} }
@ -46,17 +46,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// Reads bytes from the byte buffer to ensure that bits.UnreadBits is at /// Reads bytes from the byte buffer to ensure that bits.UnreadBits is at
/// least n. For best performance (avoiding function calls inside hot loops), /// least n. For best performance (avoiding function calls inside hot loops),
/// the caller is the one responsible for first checking that bits.UnreadBits &lt; n. /// the caller is the one responsible for first checking that bits.UnreadBits &lt; n.
/// This method does not throw. Returns <see cref="OrigDecoderErrorCode"/> instead. /// This method does not throw. Returns <see cref="GolangDecoderErrorCode"/> instead.
/// </summary> /// </summary>
/// <param name="n">The number of bits to ensure.</param> /// <param name="n">The number of bits to ensure.</param>
/// <param name="inputProcessor">The <see cref="InputProcessor"/></param> /// <param name="inputProcessor">The <see cref="InputProcessor"/></param>
/// <returns>Error code</returns> /// <returns>Error code</returns>
public OrigDecoderErrorCode EnsureNBitsUnsafe(int n, ref InputProcessor inputProcessor) public GolangDecoderErrorCode EnsureNBitsUnsafe(int n, ref InputProcessor inputProcessor)
{ {
while (true) while (true)
{ {
OrigDecoderErrorCode errorCode = this.EnsureBitsStepImpl(ref inputProcessor); GolangDecoderErrorCode errorCode = this.EnsureBitsStepImpl(ref inputProcessor);
if (errorCode != OrigDecoderErrorCode.NoError || this.UnreadBits >= n) if (errorCode != GolangDecoderErrorCode.NoError || this.UnreadBits >= n)
{ {
return errorCode; return errorCode;
} }
@ -67,8 +67,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// Unrolled version of <see cref="EnsureNBitsUnsafe"/> for n==8 /// Unrolled version of <see cref="EnsureNBitsUnsafe"/> for n==8
/// </summary> /// </summary>
/// <param name="inputProcessor">The <see cref="InputProcessor"/></param> /// <param name="inputProcessor">The <see cref="InputProcessor"/></param>
/// <returns>A <see cref="OrigDecoderErrorCode"/></returns> /// <returns>A <see cref="GolangDecoderErrorCode"/></returns>
public OrigDecoderErrorCode Ensure8BitsUnsafe(ref InputProcessor inputProcessor) public GolangDecoderErrorCode Ensure8BitsUnsafe(ref InputProcessor inputProcessor)
{ {
return this.EnsureBitsStepImpl(ref inputProcessor); return this.EnsureBitsStepImpl(ref inputProcessor);
} }
@ -77,8 +77,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// Unrolled version of <see cref="EnsureNBitsUnsafe"/> for n==1 /// Unrolled version of <see cref="EnsureNBitsUnsafe"/> for n==1
/// </summary> /// </summary>
/// <param name="inputProcessor">The <see cref="InputProcessor"/></param> /// <param name="inputProcessor">The <see cref="InputProcessor"/></param>
/// <returns>A <see cref="OrigDecoderErrorCode"/></returns> /// <returns>A <see cref="GolangDecoderErrorCode"/></returns>
public OrigDecoderErrorCode Ensure1BitUnsafe(ref InputProcessor inputProcessor) public GolangDecoderErrorCode Ensure1BitUnsafe(ref InputProcessor inputProcessor)
{ {
return this.EnsureBitsStepImpl(ref inputProcessor); return this.EnsureBitsStepImpl(ref inputProcessor);
} }
@ -92,7 +92,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public int ReceiveExtend(int t, ref InputProcessor inputProcessor) public int ReceiveExtend(int t, ref InputProcessor inputProcessor)
{ {
OrigDecoderErrorCode errorCode = this.ReceiveExtendUnsafe(t, ref inputProcessor, out int x); GolangDecoderErrorCode errorCode = this.ReceiveExtendUnsafe(t, ref inputProcessor, out int x);
errorCode.EnsureNoError(); errorCode.EnsureNoError();
return x; return x;
} }
@ -103,13 +103,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// <param name="t">Byte</param> /// <param name="t">Byte</param>
/// <param name="inputProcessor">The <see cref="InputProcessor"/></param> /// <param name="inputProcessor">The <see cref="InputProcessor"/></param>
/// <param name="x">Read bits value</param> /// <param name="x">Read bits value</param>
/// <returns>The <see cref="OrigDecoderErrorCode"/></returns> /// <returns>The <see cref="GolangDecoderErrorCode"/></returns>
public OrigDecoderErrorCode ReceiveExtendUnsafe(int t, ref InputProcessor inputProcessor, out int x) public GolangDecoderErrorCode ReceiveExtendUnsafe(int t, ref InputProcessor inputProcessor, out int x)
{ {
if (this.UnreadBits < t) if (this.UnreadBits < t)
{ {
OrigDecoderErrorCode errorCode = this.EnsureNBitsUnsafe(t, ref inputProcessor); GolangDecoderErrorCode errorCode = this.EnsureNBitsUnsafe(t, ref inputProcessor);
if (errorCode != OrigDecoderErrorCode.NoError) if (errorCode != GolangDecoderErrorCode.NoError)
{ {
x = int.MaxValue; x = int.MaxValue;
return errorCode; return errorCode;
@ -126,14 +126,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
x += ((-1) << t) + 1; x += ((-1) << t) + 1;
} }
return OrigDecoderErrorCode.NoError; return GolangDecoderErrorCode.NoError;
} }
private OrigDecoderErrorCode EnsureBitsStepImpl(ref InputProcessor inputProcessor) private GolangDecoderErrorCode EnsureBitsStepImpl(ref InputProcessor inputProcessor)
{ {
OrigDecoderErrorCode errorCode = inputProcessor.Bytes.ReadByteStuffedByteUnsafe(inputProcessor.InputStream, out int c); GolangDecoderErrorCode errorCode = inputProcessor.Bytes.ReadByteStuffedByteUnsafe(inputProcessor.InputStream, out int c);
if (errorCode != OrigDecoderErrorCode.NoError) if (errorCode != GolangDecoderErrorCode.NoError)
{ {
return errorCode; return errorCode;
} }

62
src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bytes.cs

@ -77,8 +77,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// </summary> /// </summary>
/// <param name="inputStream">Input stream</param> /// <param name="inputStream">Input stream</param>
/// <param name="x">The result byte as <see cref="int"/></param> /// <param name="x">The result byte as <see cref="int"/></param>
/// <returns>The <see cref="OrigDecoderErrorCode"/></returns> /// <returns>The <see cref="GolangDecoderErrorCode"/></returns>
public OrigDecoderErrorCode ReadByteStuffedByteUnsafe(Stream inputStream, out int x) public GolangDecoderErrorCode ReadByteStuffedByteUnsafe(Stream inputStream, out int x)
{ {
// Take the fast path if bytes.buf contains at least two bytes. // Take the fast path if bytes.buf contains at least two bytes.
if (this.I + 2 <= this.J) if (this.I + 2 <= this.J)
@ -86,50 +86,50 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
x = this.BufferAsInt[this.I]; x = this.BufferAsInt[this.I];
this.I++; this.I++;
this.UnreadableBytes = 1; this.UnreadableBytes = 1;
if (x != OrigJpegConstants.Markers.XFFInt) if (x != JpegConstants.Markers.XFFInt)
{ {
return OrigDecoderErrorCode.NoError; return GolangDecoderErrorCode.NoError;
} }
if (this.BufferAsInt[this.I] != 0x00) if (this.BufferAsInt[this.I] != 0x00)
{ {
return OrigDecoderErrorCode.MissingFF00; return GolangDecoderErrorCode.MissingFF00;
} }
this.I++; this.I++;
this.UnreadableBytes = 2; this.UnreadableBytes = 2;
x = OrigJpegConstants.Markers.XFF; x = JpegConstants.Markers.XFF;
return OrigDecoderErrorCode.NoError; return GolangDecoderErrorCode.NoError;
} }
this.UnreadableBytes = 0; this.UnreadableBytes = 0;
OrigDecoderErrorCode errorCode = this.ReadByteAsIntUnsafe(inputStream, out x); GolangDecoderErrorCode errorCode = this.ReadByteAsIntUnsafe(inputStream, out x);
this.UnreadableBytes = 1; this.UnreadableBytes = 1;
if (errorCode != OrigDecoderErrorCode.NoError) if (errorCode != GolangDecoderErrorCode.NoError)
{ {
return errorCode; return errorCode;
} }
if (x != OrigJpegConstants.Markers.XFF) if (x != JpegConstants.Markers.XFF)
{ {
return OrigDecoderErrorCode.NoError; return GolangDecoderErrorCode.NoError;
} }
errorCode = this.ReadByteAsIntUnsafe(inputStream, out x); errorCode = this.ReadByteAsIntUnsafe(inputStream, out x);
this.UnreadableBytes = 2; this.UnreadableBytes = 2;
if (errorCode != OrigDecoderErrorCode.NoError) if (errorCode != GolangDecoderErrorCode.NoError)
{ {
return errorCode; return errorCode;
} }
if (x != 0x00) if (x != 0x00)
{ {
return OrigDecoderErrorCode.MissingFF00; return GolangDecoderErrorCode.MissingFF00;
} }
x = OrigJpegConstants.Markers.XFF; x = JpegConstants.Markers.XFF;
return OrigDecoderErrorCode.NoError; return GolangDecoderErrorCode.NoError;
} }
/// <summary> /// <summary>
@ -140,25 +140,25 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public byte ReadByte(Stream inputStream) public byte ReadByte(Stream inputStream)
{ {
OrigDecoderErrorCode errorCode = this.ReadByteUnsafe(inputStream, out byte result); GolangDecoderErrorCode errorCode = this.ReadByteUnsafe(inputStream, out byte result);
errorCode.EnsureNoError(); errorCode.EnsureNoError();
return result; return result;
} }
/// <summary> /// <summary>
/// Extracts the next byte, whether buffered or not buffered into the result out parameter. It does not care about byte stuffing. /// Extracts the next byte, whether buffered or not buffered into the result out parameter. It does not care about byte stuffing.
/// This method does not throw on format error, it returns a <see cref="OrigDecoderErrorCode"/> instead. /// This method does not throw on format error, it returns a <see cref="GolangDecoderErrorCode"/> instead.
/// </summary> /// </summary>
/// <param name="inputStream">Input stream</param> /// <param name="inputStream">Input stream</param>
/// <param name="result">The result <see cref="byte"/> as out parameter</param> /// <param name="result">The result <see cref="byte"/> as out parameter</param>
/// <returns>The <see cref="OrigDecoderErrorCode"/></returns> /// <returns>The <see cref="GolangDecoderErrorCode"/></returns>
public OrigDecoderErrorCode ReadByteUnsafe(Stream inputStream, out byte result) public GolangDecoderErrorCode ReadByteUnsafe(Stream inputStream, out byte result)
{ {
OrigDecoderErrorCode errorCode = OrigDecoderErrorCode.NoError; GolangDecoderErrorCode errorCode = GolangDecoderErrorCode.NoError;
while (this.I == this.J) while (this.I == this.J)
{ {
errorCode = this.FillUnsafe(inputStream); errorCode = this.FillUnsafe(inputStream);
if (errorCode != OrigDecoderErrorCode.NoError) if (errorCode != GolangDecoderErrorCode.NoError)
{ {
result = 0; result = 0;
return errorCode; return errorCode;
@ -176,15 +176,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// </summary> /// </summary>
/// <param name="inputStream">The input stream</param> /// <param name="inputStream">The input stream</param>
/// <param name="result">The result <see cref="int"/></param> /// <param name="result">The result <see cref="int"/></param>
/// <returns>A <see cref="OrigDecoderErrorCode"/></returns> /// <returns>A <see cref="GolangDecoderErrorCode"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public OrigDecoderErrorCode ReadByteAsIntUnsafe(Stream inputStream, out int result) public GolangDecoderErrorCode ReadByteAsIntUnsafe(Stream inputStream, out int result)
{ {
OrigDecoderErrorCode errorCode = OrigDecoderErrorCode.NoError; GolangDecoderErrorCode errorCode = GolangDecoderErrorCode.NoError;
while (this.I == this.J) while (this.I == this.J)
{ {
errorCode = this.FillUnsafe(inputStream); errorCode = this.FillUnsafe(inputStream);
if (errorCode != OrigDecoderErrorCode.NoError) if (errorCode != GolangDecoderErrorCode.NoError)
{ {
result = 0; result = 0;
return errorCode; return errorCode;
@ -206,18 +206,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Fill(Stream inputStream) public void Fill(Stream inputStream)
{ {
OrigDecoderErrorCode errorCode = this.FillUnsafe(inputStream); GolangDecoderErrorCode errorCode = this.FillUnsafe(inputStream);
errorCode.EnsureNoError(); errorCode.EnsureNoError();
} }
/// <summary> /// <summary>
/// Fills up the bytes buffer from the underlying stream. /// Fills up the bytes buffer from the underlying stream.
/// It should only be called when there are no unread bytes in bytes. /// It should only be called when there are no unread bytes in bytes.
/// This method does not throw <see cref="EOFException"/>, returns a <see cref="OrigDecoderErrorCode"/> instead! /// This method does not throw <see cref="EOFException"/>, returns a <see cref="GolangDecoderErrorCode"/> instead!
/// </summary> /// </summary>
/// <param name="inputStream">Input stream</param> /// <param name="inputStream">Input stream</param>
/// <returns>The <see cref="OrigDecoderErrorCode"/></returns> /// <returns>The <see cref="GolangDecoderErrorCode"/></returns>
public OrigDecoderErrorCode FillUnsafe(Stream inputStream) public GolangDecoderErrorCode FillUnsafe(Stream inputStream)
{ {
if (this.I != this.J) if (this.I != this.J)
{ {
@ -239,7 +239,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
int n = inputStream.Read(this.Buffer, this.J, this.Buffer.Length - this.J); int n = inputStream.Read(this.Buffer, this.J, this.Buffer.Length - this.J);
if (n == 0) if (n == 0)
{ {
return OrigDecoderErrorCode.UnexpectedEndOfStream; return GolangDecoderErrorCode.UnexpectedEndOfStream;
} }
this.J += n; this.J += n;
@ -249,7 +249,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
this.BufferAsInt[i] = this.Buffer[i]; this.BufferAsInt[i] = this.Buffer[i];
} }
return OrigDecoderErrorCode.NoError; return GolangDecoderErrorCode.NoError;
} }
} }
} }

28
src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/DecoderThrowHelper.cs

@ -12,22 +12,22 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
internal static class DecoderThrowHelper internal static class DecoderThrowHelper
{ {
/// <summary> /// <summary>
/// Throws an exception that belongs to the given <see cref="OrigDecoderErrorCode"/> /// Throws an exception that belongs to the given <see cref="GolangDecoderErrorCode"/>
/// </summary> /// </summary>
/// <param name="errorCode">The <see cref="OrigDecoderErrorCode"/></param> /// <param name="errorCode">The <see cref="GolangDecoderErrorCode"/></param>
[MethodImpl(MethodImplOptions.NoInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
public static void ThrowExceptionForErrorCode(this OrigDecoderErrorCode errorCode) public static void ThrowExceptionForErrorCode(this GolangDecoderErrorCode errorCode)
{ {
// REMARK: If this method throws for an image that is expected to be decodable, // REMARK: If this method throws for an image that is expected to be decodable,
// consider using the ***Unsafe variant of the parsing method that asks for ThrowExceptionForErrorCode() // consider using the ***Unsafe variant of the parsing method that asks for ThrowExceptionForErrorCode()
// then verify the error code + implement fallback logic manually! // then verify the error code + implement fallback logic manually!
switch (errorCode) switch (errorCode)
{ {
case OrigDecoderErrorCode.NoError: case GolangDecoderErrorCode.NoError:
throw new ArgumentException("ThrowExceptionForErrorCode() called with NoError!", nameof(errorCode)); throw new ArgumentException("ThrowExceptionForErrorCode() called with NoError!", nameof(errorCode));
case OrigDecoderErrorCode.MissingFF00: case GolangDecoderErrorCode.MissingFF00:
throw new MissingFF00Exception(); throw new MissingFF00Exception();
case OrigDecoderErrorCode.UnexpectedEndOfStream: case GolangDecoderErrorCode.UnexpectedEndOfStream:
throw new EOFException(); throw new EOFException();
default: default:
throw new ArgumentOutOfRangeException(nameof(errorCode), errorCode, null); throw new ArgumentOutOfRangeException(nameof(errorCode), errorCode, null);
@ -35,26 +35,26 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
} }
/// <summary> /// <summary>
/// Throws an exception if the given <see cref="OrigDecoderErrorCode"/> defines an error. /// Throws an exception if the given <see cref="GolangDecoderErrorCode"/> defines an error.
/// </summary> /// </summary>
/// <param name="errorCode">The <see cref="OrigDecoderErrorCode"/></param> /// <param name="errorCode">The <see cref="GolangDecoderErrorCode"/></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void EnsureNoError(this OrigDecoderErrorCode errorCode) public static void EnsureNoError(this GolangDecoderErrorCode errorCode)
{ {
if (errorCode != OrigDecoderErrorCode.NoError) if (errorCode != GolangDecoderErrorCode.NoError)
{ {
ThrowExceptionForErrorCode(errorCode); ThrowExceptionForErrorCode(errorCode);
} }
} }
/// <summary> /// <summary>
/// Throws an exception if the given <see cref="OrigDecoderErrorCode"/> is <see cref="OrigDecoderErrorCode.UnexpectedEndOfStream"/>. /// Throws an exception if the given <see cref="GolangDecoderErrorCode"/> is <see cref="GolangDecoderErrorCode.UnexpectedEndOfStream"/>.
/// </summary> /// </summary>
/// <param name="errorCode">The <see cref="OrigDecoderErrorCode"/></param> /// <param name="errorCode">The <see cref="GolangDecoderErrorCode"/></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void EnsureNoEOF(this OrigDecoderErrorCode errorCode) public static void EnsureNoEOF(this GolangDecoderErrorCode errorCode)
{ {
if (errorCode == OrigDecoderErrorCode.UnexpectedEndOfStream) if (errorCode == GolangDecoderErrorCode.UnexpectedEndOfStream)
{ {
errorCode.ThrowExceptionForErrorCode(); errorCode.ThrowExceptionForErrorCode();
} }

21
src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigComponent.cs → src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangComponent.cs

@ -3,8 +3,9 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Formats.Jpeg.Common;
using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder; using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.Primitives; using SixLabors.Primitives;
@ -14,9 +15,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// <summary> /// <summary>
/// Represents a single color component /// Represents a single color component
/// </summary> /// </summary>
internal class OrigComponent : IDisposable, IJpegComponent internal class GolangComponent : IDisposable, IJpegComponent
{ {
public OrigComponent(byte identifier, int index) public GolangComponent(byte identifier, int index)
{ {
this.Identifier = identifier; this.Identifier = identifier;
this.Index = index; this.Index = index;
@ -56,8 +57,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// Initializes <see cref="SpectralBlocks"/> /// Initializes <see cref="SpectralBlocks"/>
/// </summary> /// </summary>
/// <param name="memoryManager">The <see cref="MemoryManager"/> to use for buffer allocations.</param> /// <param name="memoryManager">The <see cref="MemoryManager"/> to use for buffer allocations.</param>
/// <param name="decoder">The <see cref="OrigJpegDecoderCore"/> instance</param> /// <param name="decoder">The <see cref="GolangJpegDecoderCore"/> instance</param>
public void InitializeDerivedData(MemoryManager memoryManager, OrigJpegDecoderCore decoder) public void InitializeDerivedData(MemoryManager memoryManager, GolangJpegDecoderCore decoder)
{ {
// For 4-component images (either CMYK or YCbCrK), we only support two // For 4-component images (either CMYK or YCbCrK), we only support two
// hv vectors: [0x11 0x11 0x11 0x11] and [0x22 0x11 0x11 0x22]. // hv vectors: [0x11 0x11 0x11 0x11] and [0x22 0x11 0x11 0x22].
@ -76,7 +77,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
} }
else else
{ {
OrigComponent c0 = decoder.Components[0]; GolangComponent c0 = decoder.Components[0];
this.SubSamplingDivisors = c0.SamplingFactors.DivideBy(this.SamplingFactors); this.SubSamplingDivisors = c0.SamplingFactors.DivideBy(this.SamplingFactors);
} }
@ -86,8 +87,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// <summary> /// <summary>
/// Initializes all component data except <see cref="SpectralBlocks"/>. /// Initializes all component data except <see cref="SpectralBlocks"/>.
/// </summary> /// </summary>
/// <param name="decoder">The <see cref="OrigJpegDecoderCore"/> instance</param> /// <param name="decoder">The <see cref="GolangJpegDecoderCore"/> instance</param>
public void InitializeCoreData(OrigJpegDecoderCore decoder) public void InitializeCoreData(GolangJpegDecoderCore decoder)
{ {
// Section B.2.2 states that "the value of C_i shall be different from // Section B.2.2 states that "the value of C_i shall be different from
// the values of C_1 through C_(i-1)". // the values of C_1 through C_(i-1)".
@ -102,7 +103,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
} }
this.QuantizationTableIndex = decoder.Temp[8 + (3 * i)]; this.QuantizationTableIndex = decoder.Temp[8 + (3 * i)];
if (this.QuantizationTableIndex > OrigJpegDecoderCore.MaxTq) if (this.QuantizationTableIndex > GolangJpegDecoderCore.MaxTq)
{ {
throw new ImageFormatException("Bad Tq value"); throw new ImageFormatException("Bad Tq value");
} }

2
src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigComponentScan.cs → src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangComponentScan.cs

@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// Represents a component scan /// Represents a component scan
/// </summary> /// </summary>
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
internal struct OrigComponentScan internal struct GolangComponentScan
{ {
/// <summary> /// <summary>
/// Gets or sets the component index. /// Gets or sets the component index.

2
src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigDecoderErrorCode.cs → src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangDecoderErrorCode.cs

@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// <summary> /// <summary>
/// Represents "recoverable" decoder errors. /// Represents "recoverable" decoder errors.
/// </summary> /// </summary>
internal enum OrigDecoderErrorCode internal enum GolangDecoderErrorCode
{ {
/// <summary> /// <summary>
/// NoError /// NoError

12
src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigHuffmanTree.cs → src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangHuffmanTree.cs

@ -1,8 +1,6 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@ -12,7 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// Represents a Huffman tree /// Represents a Huffman tree
/// </summary> /// </summary>
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
internal unsafe struct OrigHuffmanTree internal unsafe struct GolangHuffmanTree
{ {
/// <summary> /// <summary>
/// The index of the AC table row /// The index of the AC table row
@ -95,12 +93,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
public FixedInt32Buffer16 Indices; public FixedInt32Buffer16 Indices;
/// <summary> /// <summary>
/// Creates and initializes an array of <see cref="OrigHuffmanTree" /> instances of size <see cref="NumberOfTrees" /> /// Creates and initializes an array of <see cref="GolangHuffmanTree" /> instances of size <see cref="NumberOfTrees" />
/// </summary> /// </summary>
/// <returns>An array of <see cref="OrigHuffmanTree" /> instances representing the Huffman tables</returns> /// <returns>An array of <see cref="GolangHuffmanTree" /> instances representing the Huffman tables</returns>
public static OrigHuffmanTree[] CreateHuffmanTrees() public static GolangHuffmanTree[] CreateHuffmanTrees()
{ {
return new OrigHuffmanTree[NumberOfTrees]; return new GolangHuffmanTree[NumberOfTrees];
} }
/// <summary> /// <summary>

11
src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.ComputationData.cs → src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangJpegScanDecoder.ComputationData.cs

@ -2,14 +2,15 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Formats.Jpeg.Common;
using SixLabors.ImageSharp.Formats.Jpeg.Components;
namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
{ {
/// <content> /// <content>
/// Conains the definition of <see cref="ComputationData"/> /// Conains the definition of <see cref="ComputationData"/>
/// </content> /// </content>
internal unsafe partial struct OrigJpegScanDecoder internal unsafe partial struct GolangJpegScanDecoder
{ {
/// <summary> /// <summary>
/// Holds the "large" data blocks needed for computations. /// Holds the "large" data blocks needed for computations.
@ -28,14 +29,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
public ZigZag Unzig; public ZigZag Unzig;
/// <summary> /// <summary>
/// The buffer storing the <see cref="OrigComponentScan"/>-s for each component /// The buffer storing the <see cref="GolangComponentScan"/>-s for each component
/// </summary> /// </summary>
public fixed byte ScanData[3 * OrigJpegDecoderCore.MaxComponents]; public fixed byte ScanData[3 * GolangJpegDecoderCore.MaxComponents];
/// <summary> /// <summary>
/// The DC values for each component /// The DC values for each component
/// </summary> /// </summary>
public fixed int Dc[OrigJpegDecoderCore.MaxComponents]; public fixed int Dc[GolangJpegDecoderCore.MaxComponents];
/// <summary> /// <summary>
/// Creates and initializes a new <see cref="ComputationData"/> instance /// Creates and initializes a new <see cref="ComputationData"/> instance

8
src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.DataPointers.cs → src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangJpegScanDecoder.DataPointers.cs

@ -1,14 +1,14 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Formats.Jpeg.Common; using SixLabors.ImageSharp.Formats.Jpeg.Components;
namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
{ {
/// <content> /// <content>
/// Conains the definition of <see cref="DataPointers"/> /// Conains the definition of <see cref="DataPointers"/>
/// </content> /// </content>
internal unsafe partial struct OrigJpegScanDecoder internal unsafe partial struct GolangJpegScanDecoder
{ {
/// <summary> /// <summary>
/// Contains pointers to the memory regions of <see cref="ComputationData"/> so they can be easily passed around to pointer based utility methods of <see cref="Block8x8F"/> /// Contains pointers to the memory regions of <see cref="ComputationData"/> so they can be easily passed around to pointer based utility methods of <see cref="Block8x8F"/>
@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// <summary> /// <summary>
/// Pointer to <see cref="ComputationData.ScanData"/> as Scan* /// Pointer to <see cref="ComputationData.ScanData"/> as Scan*
/// </summary> /// </summary>
public OrigComponentScan* ComponentScan; public GolangComponentScan* ComponentScan;
/// <summary> /// <summary>
/// Pointer to <see cref="ComputationData.Dc"/> /// Pointer to <see cref="ComputationData.Dc"/>
@ -43,7 +43,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
{ {
this.Block = &basePtr->Block; this.Block = &basePtr->Block;
this.Unzig = basePtr->Unzig.Data; this.Unzig = basePtr->Unzig.Data;
this.ComponentScan = (OrigComponentScan*)basePtr->ScanData; this.ComponentScan = (GolangComponentScan*)basePtr->ScanData;
this.Dc = basePtr->Dc; this.Dc = basePtr->Dc;
} }
} }

67
src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.cs → src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangJpegScanDecoder.cs

@ -3,9 +3,8 @@
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Formats.Jpeg.Common;
using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder; using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.ImageSharp.Memory;
// ReSharper disable InconsistentNaming // ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
@ -29,7 +28,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// For baseline JPEGs, these parameters are hard-coded to 0/63/0/0. /// For baseline JPEGs, these parameters are hard-coded to 0/63/0/0.
/// </summary> /// </summary>
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
internal unsafe partial struct OrigJpegScanDecoder internal unsafe partial struct GolangJpegScanDecoder
{ {
// The JpegScanDecoder members should be ordered in a way that results in optimal memory layout. // The JpegScanDecoder members should be ordered in a way that results in optimal memory layout.
#pragma warning disable SA1202 // ElementsMustBeOrderedByAccess #pragma warning disable SA1202 // ElementsMustBeOrderedByAccess
@ -110,12 +109,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
private byte expectedRst; private byte expectedRst;
/// <summary> /// <summary>
/// Initializes a default-constructed <see cref="OrigJpegScanDecoder"/> instance for reading data from <see cref="OrigJpegDecoderCore"/>-s stream. /// Initializes a default-constructed <see cref="GolangJpegScanDecoder"/> instance for reading data from <see cref="GolangJpegDecoderCore"/>-s stream.
/// </summary> /// </summary>
/// <param name="p">Pointer to <see cref="OrigJpegScanDecoder"/> on the stack</param> /// <param name="p">Pointer to <see cref="GolangJpegScanDecoder"/> on the stack</param>
/// <param name="decoder">The <see cref="OrigJpegDecoderCore"/> instance</param> /// <param name="decoder">The <see cref="GolangJpegDecoderCore"/> instance</param>
/// <param name="remaining">The remaining bytes in the segment block.</param> /// <param name="remaining">The remaining bytes in the segment block.</param>
public static void InitStreamReading(OrigJpegScanDecoder* p, OrigJpegDecoderCore decoder, int remaining) public static void InitStreamReading(GolangJpegScanDecoder* p, GolangJpegDecoderCore decoder, int remaining)
{ {
p->data = ComputationData.Create(); p->data = ComputationData.Create();
p->pointers = new DataPointers(&p->data); p->pointers = new DataPointers(&p->data);
@ -123,8 +122,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
} }
/// <summary> /// <summary>
/// Read Huffman data from Jpeg scans in <see cref="OrigJpegDecoderCore.InputStream"/>, /// Read Huffman data from Jpeg scans in <see cref="GolangJpegDecoderCore.InputStream"/>,
/// and decode it as <see cref="Block8x8"/> into <see cref="OrigComponent.SpectralBlocks"/>. /// and decode it as <see cref="Block8x8"/> into <see cref="GolangComponent.SpectralBlocks"/>.
/// ///
/// The blocks are traversed one MCU at a time. For 4:2:0 chroma /// The blocks are traversed one MCU at a time. For 4:2:0 chroma
/// subsampling, there are four Y 8x8 blocks in every 16x16 MCU. /// subsampling, there are four Y 8x8 blocks in every 16x16 MCU.
@ -149,14 +148,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// 0 1 2 /// 0 1 2
/// 3 4 5 /// 3 4 5
/// </summary> /// </summary>
/// <param name="decoder">The <see cref="OrigJpegDecoderCore"/> instance</param> /// <param name="decoder">The <see cref="GolangJpegDecoderCore"/> instance</param>
public void DecodeBlocks(OrigJpegDecoderCore decoder) public void DecodeBlocks(GolangJpegDecoderCore decoder)
{ {
decoder.InputProcessor.ResetErrorState(); decoder.InputProcessor.ResetErrorState();
this.blockCounter = 0; this.blockCounter = 0;
this.mcuCounter = 0; this.mcuCounter = 0;
this.expectedRst = OrigJpegConstants.Markers.RST0; this.expectedRst = JpegConstants.Markers.RST0;
for (int my = 0; my < decoder.MCUCountY; my++) for (int my = 0; my < decoder.MCUCountY; my++)
{ {
@ -177,12 +176,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
} }
} }
private void DecodeBlocksAtMcuIndex(OrigJpegDecoderCore decoder, int mx, int my) private void DecodeBlocksAtMcuIndex(GolangJpegDecoderCore decoder, int mx, int my)
{ {
for (int scanIndex = 0; scanIndex < this.componentScanCount; scanIndex++) for (int scanIndex = 0; scanIndex < this.componentScanCount; scanIndex++)
{ {
this.ComponentIndex = this.pointers.ComponentScan[scanIndex].ComponentIndex; this.ComponentIndex = this.pointers.ComponentScan[scanIndex].ComponentIndex;
OrigComponent component = decoder.Components[this.ComponentIndex]; GolangComponent component = decoder.Components[this.ComponentIndex];
this.hi = component.HorizontalSamplingFactor; this.hi = component.HorizontalSamplingFactor;
int vi = component.VerticalSamplingFactor; int vi = component.VerticalSamplingFactor;
@ -223,7 +222,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
} }
} }
private void ProcessRSTMarker(OrigJpegDecoderCore decoder) private void ProcessRSTMarker(GolangJpegDecoderCore decoder)
{ {
// Attempt to look for RST[0-7] markers to resynchronize from corrupt input. // Attempt to look for RST[0-7] markers to resynchronize from corrupt input.
if (!decoder.InputProcessor.ReachedEOF) if (!decoder.InputProcessor.ReachedEOF)
@ -262,15 +261,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
} }
this.expectedRst++; this.expectedRst++;
if (this.expectedRst == OrigJpegConstants.Markers.RST7 + 1) if (this.expectedRst == JpegConstants.Markers.RST7 + 1)
{ {
this.expectedRst = OrigJpegConstants.Markers.RST0; this.expectedRst = JpegConstants.Markers.RST0;
} }
} }
} }
} }
private void Reset(OrigJpegDecoderCore decoder) private void Reset(GolangJpegDecoderCore decoder)
{ {
decoder.InputProcessor.ResetHuffmanDecoder(); decoder.InputProcessor.ResetHuffmanDecoder();
@ -285,15 +284,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// </summary> /// </summary>
private void ResetDcValues() private void ResetDcValues()
{ {
Unsafe.InitBlock(this.pointers.Dc, default(byte), sizeof(int) * OrigJpegDecoderCore.MaxComponents); Unsafe.InitBlock(this.pointers.Dc, default, sizeof(int) * GolangJpegDecoderCore.MaxComponents);
} }
/// <summary> /// <summary>
/// The implementation part of <see cref="InitStreamReading"/> as an instance method. /// The implementation part of <see cref="InitStreamReading"/> as an instance method.
/// </summary> /// </summary>
/// <param name="decoder">The <see cref="OrigJpegDecoderCore"/></param> /// <param name="decoder">The <see cref="GolangJpegDecoderCore"/></param>
/// <param name="remaining">The remaining bytes</param> /// <param name="remaining">The remaining bytes</param>
private void InitStreamReadingImpl(OrigJpegDecoderCore decoder, int remaining) private void InitStreamReadingImpl(GolangJpegDecoderCore decoder, int remaining)
{ {
if (decoder.ComponentCount == 0) if (decoder.ComponentCount == 0)
{ {
@ -360,10 +359,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// </summary> /// </summary>
/// <param name="decoder">The decoder</param> /// <param name="decoder">The decoder</param>
/// <param name="scanIndex">The index of the scan</param> /// <param name="scanIndex">The index of the scan</param>
private void DecodeBlock(OrigJpegDecoderCore decoder, int scanIndex) private void DecodeBlock(GolangJpegDecoderCore decoder, int scanIndex)
{ {
Block8x8* b = this.pointers.Block; Block8x8* b = this.pointers.Block;
int huffmannIdx = (OrigHuffmanTree.AcTableIndex * OrigHuffmanTree.ThRowSize) + this.pointers.ComponentScan[scanIndex].AcTableSelector; int huffmannIdx = (GolangHuffmanTree.AcTableIndex * GolangHuffmanTree.ThRowSize) + this.pointers.ComponentScan[scanIndex].AcTableSelector;
if (this.ah != 0) if (this.ah != 0)
{ {
this.Refine(ref decoder.InputProcessor, ref decoder.HuffmanTrees[huffmannIdx], 1 << this.al); this.Refine(ref decoder.InputProcessor, ref decoder.HuffmanTrees[huffmannIdx], 1 << this.al);
@ -377,7 +376,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
zig++; zig++;
// Decode the DC coefficient, as specified in section F.2.2.1. // Decode the DC coefficient, as specified in section F.2.2.1.
int huffmanIndex = (OrigHuffmanTree.DcTableIndex * OrigHuffmanTree.ThRowSize) + this.pointers.ComponentScan[scanIndex].DcTableSelector; int huffmanIndex = (GolangHuffmanTree.DcTableIndex * GolangHuffmanTree.ThRowSize) + this.pointers.ComponentScan[scanIndex].DcTableSelector;
decoder.InputProcessor.DecodeHuffmanUnsafe( decoder.InputProcessor.DecodeHuffmanUnsafe(
ref decoder.HuffmanTrees[huffmanIndex], ref decoder.HuffmanTrees[huffmanIndex],
out int value); out int value);
@ -467,7 +466,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
private void DecodeEobRun(int count, ref InputProcessor processor) private void DecodeEobRun(int count, ref InputProcessor processor)
{ {
processor.DecodeBitsUnsafe(count, out int bitsResult); processor.DecodeBitsUnsafe(count, out int bitsResult);
if (processor.LastErrorCode != OrigDecoderErrorCode.NoError) if (processor.LastErrorCode != GolangDecoderErrorCode.NoError)
{ {
return; return;
} }
@ -475,7 +474,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
this.eobRun |= bitsResult; this.eobRun |= bitsResult;
} }
private void InitComponentScan(OrigJpegDecoderCore decoder, int i, ref OrigComponentScan currentComponentScan, ref int totalHv) private void InitComponentScan(GolangJpegDecoderCore decoder, int i, ref GolangComponentScan currentComponentScan, ref int totalHv)
{ {
// Component selector. // Component selector.
int cs = decoder.Temp[1 + (2 * i)]; int cs = decoder.Temp[1 + (2 * i)];
@ -500,11 +499,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
} }
private void ProcessComponentImpl( private void ProcessComponentImpl(
OrigJpegDecoderCore decoder, GolangJpegDecoderCore decoder,
int i, int i,
ref OrigComponentScan currentComponentScan, ref GolangComponentScan currentComponentScan,
ref int totalHv, ref int totalHv,
OrigComponent currentComponent) GolangComponent currentComponent)
{ {
// Section B.2.3 states that "the value of Cs_j shall be different from // Section B.2.3 states that "the value of Cs_j shall be different from
// the values of Cs_1 through Cs_(j-1)". Since we have previously // the values of Cs_1 through Cs_(j-1)". Since we have previously
@ -522,13 +521,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
totalHv += currentComponent.HorizontalSamplingFactor * currentComponent.VerticalSamplingFactor; totalHv += currentComponent.HorizontalSamplingFactor * currentComponent.VerticalSamplingFactor;
currentComponentScan.DcTableSelector = (byte)(decoder.Temp[2 + (2 * i)] >> 4); currentComponentScan.DcTableSelector = (byte)(decoder.Temp[2 + (2 * i)] >> 4);
if (currentComponentScan.DcTableSelector > OrigHuffmanTree.MaxTh) if (currentComponentScan.DcTableSelector > GolangHuffmanTree.MaxTh)
{ {
throw new ImageFormatException("Bad DC table selector value"); throw new ImageFormatException("Bad DC table selector value");
} }
currentComponentScan.AcTableSelector = (byte)(decoder.Temp[2 + (2 * i)] & 0x0f); currentComponentScan.AcTableSelector = (byte)(decoder.Temp[2 + (2 * i)] & 0x0f);
if (currentComponentScan.AcTableSelector > OrigHuffmanTree.MaxTh) if (currentComponentScan.AcTableSelector > GolangHuffmanTree.MaxTh)
{ {
throw new ImageFormatException("Bad AC table selector value"); throw new ImageFormatException("Bad AC table selector value");
} }
@ -540,7 +539,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// <param name="bp">The <see cref="InputProcessor"/> instance</param> /// <param name="bp">The <see cref="InputProcessor"/> instance</param>
/// <param name="h">The Huffman tree</param> /// <param name="h">The Huffman tree</param>
/// <param name="delta">The low transform offset</param> /// <param name="delta">The low transform offset</param>
private void Refine(ref InputProcessor bp, ref OrigHuffmanTree h, int delta) private void Refine(ref InputProcessor bp, ref GolangHuffmanTree h, int delta)
{ {
Block8x8* b = this.pointers.Block; Block8x8* b = this.pointers.Block;
@ -560,7 +559,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
if (bit) if (bit)
{ {
int stuff = (int)Block8x8.GetScalarAt(b, 0); int stuff = Block8x8.GetScalarAt(b, 0);
// int stuff = (int)b[0]; // int stuff = (int)b[0];
stuff |= delta; stuff |= delta;

80
src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs

@ -8,7 +8,7 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
{ {
/// <summary> /// <summary>
/// Encapsulates stream reading and processing data and operations for <see cref="OrigJpegDecoderCore"/>. /// Encapsulates stream reading and processing data and operations for <see cref="GolangJpegDecoderCore"/>.
/// It's a value type for imporved data locality, and reduced number of CALLVIRT-s /// It's a value type for imporved data locality, and reduced number of CALLVIRT-s
/// </summary> /// </summary>
internal struct InputProcessor : IDisposable internal struct InputProcessor : IDisposable
@ -27,14 +27,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// Initializes a new instance of the <see cref="InputProcessor"/> struct. /// Initializes a new instance of the <see cref="InputProcessor"/> struct.
/// </summary> /// </summary>
/// <param name="inputStream">The input <see cref="Stream"/></param> /// <param name="inputStream">The input <see cref="Stream"/></param>
/// <param name="temp">Temporal buffer, same as <see cref="OrigJpegDecoderCore.Temp"/></param> /// <param name="temp">Temporal buffer, same as <see cref="GolangJpegDecoderCore.Temp"/></param>
public InputProcessor(Stream inputStream, byte[] temp) public InputProcessor(Stream inputStream, byte[] temp)
{ {
this.Bits = default(Bits); this.Bits = default;
this.Bytes = Bytes.Create(); this.Bytes = Bytes.Create();
this.InputStream = inputStream; this.InputStream = inputStream;
this.Temp = temp; this.Temp = temp;
this.LastErrorCode = OrigDecoderErrorCode.NoError; this.LastErrorCode = GolangDecoderErrorCode.NoError;
} }
/// <summary> /// <summary>
@ -43,20 +43,20 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
public Stream InputStream { get; } public Stream InputStream { get; }
/// <summary> /// <summary>
/// Gets the temporary buffer, same instance as <see cref="OrigJpegDecoderCore.Temp"/> /// Gets the temporary buffer, same instance as <see cref="GolangJpegDecoderCore.Temp"/>
/// </summary> /// </summary>
public byte[] Temp { get; } public byte[] Temp { get; }
/// <summary> /// <summary>
/// Gets a value indicating whether an unexpected EOF reached in <see cref="InputStream"/>. /// Gets a value indicating whether an unexpected EOF reached in <see cref="InputStream"/>.
/// </summary> /// </summary>
public bool ReachedEOF => this.LastErrorCode == OrigDecoderErrorCode.UnexpectedEndOfStream; public bool ReachedEOF => this.LastErrorCode == GolangDecoderErrorCode.UnexpectedEndOfStream;
public bool HasError => this.LastErrorCode != OrigDecoderErrorCode.NoError; public bool HasError => this.LastErrorCode != GolangDecoderErrorCode.NoError;
public OrigDecoderErrorCode LastErrorCode { get; private set; } public GolangDecoderErrorCode LastErrorCode { get; private set; }
public void ResetErrorState() => this.LastErrorCode = OrigDecoderErrorCode.NoError; public void ResetErrorState() => this.LastErrorCode = GolangDecoderErrorCode.NoError;
/// <summary> /// <summary>
/// If errorCode indicates unexpected EOF, sets <see cref="ReachedEOF"/> to true and returns false. /// If errorCode indicates unexpected EOF, sets <see cref="ReachedEOF"/> to true and returns false.
@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// <returns>A <see cref="bool"/> indicating whether EOF reached</returns> /// <returns>A <see cref="bool"/> indicating whether EOF reached</returns>
public bool CheckEOFEnsureNoError() public bool CheckEOFEnsureNoError()
{ {
if (this.LastErrorCode == OrigDecoderErrorCode.UnexpectedEndOfStream) if (this.LastErrorCode == GolangDecoderErrorCode.UnexpectedEndOfStream)
{ {
return false; return false;
} }
@ -81,7 +81,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// <returns>A <see cref="bool"/> indicating whether EOF reached</returns> /// <returns>A <see cref="bool"/> indicating whether EOF reached</returns>
public bool CheckEOF() public bool CheckEOF()
{ {
if (this.LastErrorCode == OrigDecoderErrorCode.UnexpectedEndOfStream) if (this.LastErrorCode == GolangDecoderErrorCode.UnexpectedEndOfStream)
{ {
return false; return false;
} }
@ -106,7 +106,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public OrigDecoderErrorCode ReadByteUnsafe(out byte result) public GolangDecoderErrorCode ReadByteUnsafe(out byte result)
{ {
this.LastErrorCode = this.Bytes.ReadByteUnsafe(this.InputStream, out result); this.LastErrorCode = this.Bytes.ReadByteUnsafe(this.InputStream, out result);
return this.LastErrorCode; return this.LastErrorCode;
@ -117,13 +117,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// TODO: This method (and also the usages) could be optimized by batching! /// TODO: This method (and also the usages) could be optimized by batching!
/// </summary> /// </summary>
/// <param name="result">The decoded bit as a <see cref="bool"/></param> /// <param name="result">The decoded bit as a <see cref="bool"/></param>
/// <returns>The <see cref="OrigDecoderErrorCode" /></returns> /// <returns>The <see cref="GolangDecoderErrorCode" /></returns>
public OrigDecoderErrorCode DecodeBitUnsafe(out bool result) public GolangDecoderErrorCode DecodeBitUnsafe(out bool result)
{ {
if (this.Bits.UnreadBits == 0) if (this.Bits.UnreadBits == 0)
{ {
this.LastErrorCode = this.Bits.Ensure1BitUnsafe(ref this); this.LastErrorCode = this.Bits.Ensure1BitUnsafe(ref this);
if (this.LastErrorCode != OrigDecoderErrorCode.NoError) if (this.LastErrorCode != GolangDecoderErrorCode.NoError)
{ {
result = false; result = false;
return this.LastErrorCode; return this.LastErrorCode;
@ -133,18 +133,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
result = (this.Bits.Accumulator & this.Bits.Mask) != 0; result = (this.Bits.Accumulator & this.Bits.Mask) != 0;
this.Bits.UnreadBits--; this.Bits.UnreadBits--;
this.Bits.Mask >>= 1; this.Bits.Mask >>= 1;
return this.LastErrorCode = OrigDecoderErrorCode.NoError; return this.LastErrorCode = GolangDecoderErrorCode.NoError;
} }
/// <summary> /// <summary>
/// Reads exactly length bytes into data. It does not care about byte stuffing. /// Reads exactly length bytes into data. It does not care about byte stuffing.
/// Does not throw on errors, returns <see cref="OrigJpegDecoderCore"/> instead! /// Does not throw on errors, returns <see cref="GolangJpegDecoderCore"/> instead!
/// </summary> /// </summary>
/// <param name="data">The data to write to.</param> /// <param name="data">The data to write to.</param>
/// <param name="offset">The offset in the source buffer</param> /// <param name="offset">The offset in the source buffer</param>
/// <param name="length">The number of bytes to read</param> /// <param name="length">The number of bytes to read</param>
/// <returns>The <see cref="OrigDecoderErrorCode"/></returns> /// <returns>The <see cref="GolangDecoderErrorCode"/></returns>
public OrigDecoderErrorCode ReadFullUnsafe(byte[] data, int offset, int length) public GolangDecoderErrorCode ReadFullUnsafe(byte[] data, int offset, int length)
{ {
// Unread the overshot bytes, if any. // Unread the overshot bytes, if any.
if (this.Bytes.UnreadableBytes != 0) if (this.Bytes.UnreadableBytes != 0)
@ -157,8 +157,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
this.Bytes.UnreadableBytes = 0; this.Bytes.UnreadableBytes = 0;
} }
this.LastErrorCode = OrigDecoderErrorCode.NoError; this.LastErrorCode = GolangDecoderErrorCode.NoError;
while (length > 0 && this.LastErrorCode == OrigDecoderErrorCode.NoError) while (length > 0 && this.LastErrorCode == GolangDecoderErrorCode.NoError)
{ {
if (this.Bytes.J - this.Bytes.I >= length) if (this.Bytes.J - this.Bytes.I >= length)
{ {
@ -185,13 +185,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// </summary> /// </summary>
/// <param name="count">The number of bits to decode.</param> /// <param name="count">The number of bits to decode.</param>
/// <param name="result">The <see cref="uint" /> result</param> /// <param name="result">The <see cref="uint" /> result</param>
/// <returns>The <see cref="OrigDecoderErrorCode"/></returns> /// <returns>The <see cref="GolangDecoderErrorCode"/></returns>
public OrigDecoderErrorCode DecodeBitsUnsafe(int count, out int result) public GolangDecoderErrorCode DecodeBitsUnsafe(int count, out int result)
{ {
if (this.Bits.UnreadBits < count) if (this.Bits.UnreadBits < count)
{ {
this.LastErrorCode = this.Bits.EnsureNBitsUnsafe(count, ref this); this.LastErrorCode = this.Bits.EnsureNBitsUnsafe(count, ref this);
if (this.LastErrorCode != OrigDecoderErrorCode.NoError) if (this.LastErrorCode != GolangDecoderErrorCode.NoError)
{ {
result = 0; result = 0;
return this.LastErrorCode; return this.LastErrorCode;
@ -202,7 +202,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
result = result & ((1 << count) - 1); result = result & ((1 << count) - 1);
this.Bits.UnreadBits -= count; this.Bits.UnreadBits -= count;
this.Bits.Mask >>= count; this.Bits.Mask >>= count;
return this.LastErrorCode = OrigDecoderErrorCode.NoError; return this.LastErrorCode = GolangDecoderErrorCode.NoError;
} }
/// <summary> /// <summary>
@ -210,8 +210,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// </summary> /// </summary>
/// <param name="huffmanTree">The huffman value</param> /// <param name="huffmanTree">The huffman value</param>
/// <param name="result">The decoded <see cref="byte" /></param> /// <param name="result">The decoded <see cref="byte" /></param>
/// <returns>The <see cref="OrigDecoderErrorCode"/></returns> /// <returns>The <see cref="GolangDecoderErrorCode"/></returns>
public OrigDecoderErrorCode DecodeHuffmanUnsafe(ref OrigHuffmanTree huffmanTree, out int result) public GolangDecoderErrorCode DecodeHuffmanUnsafe(ref GolangHuffmanTree huffmanTree, out int result)
{ {
result = 0; result = 0;
@ -224,9 +224,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
{ {
this.LastErrorCode = this.Bits.Ensure8BitsUnsafe(ref this); this.LastErrorCode = this.Bits.Ensure8BitsUnsafe(ref this);
if (this.LastErrorCode == OrigDecoderErrorCode.NoError) if (this.LastErrorCode == GolangDecoderErrorCode.NoError)
{ {
int lutIndex = (this.Bits.Accumulator >> (this.Bits.UnreadBits - OrigHuffmanTree.LutSizeLog2)) & 0xFF; int lutIndex = (this.Bits.Accumulator >> (this.Bits.UnreadBits - GolangHuffmanTree.LutSizeLog2)) & 0xFF;
int v = huffmanTree.Lut[lutIndex]; int v = huffmanTree.Lut[lutIndex];
if (v != 0) if (v != 0)
@ -246,7 +246,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
} }
int code = 0; int code = 0;
for (int i = 0; i < OrigHuffmanTree.MaxCodeLength; i++) for (int i = 0; i < GolangHuffmanTree.MaxCodeLength; i++)
{ {
if (this.Bits.UnreadBits == 0) if (this.Bits.UnreadBits == 0)
{ {
@ -269,7 +269,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
if (code <= huffmanTree.MaxCodes[i]) if (code <= huffmanTree.MaxCodes[i])
{ {
result = huffmanTree.GetValue(code, i); result = huffmanTree.GetValue(code, i);
return this.LastErrorCode = OrigDecoderErrorCode.NoError; return this.LastErrorCode = GolangDecoderErrorCode.NoError;
} }
code <<= 1; code <<= 1;
@ -279,7 +279,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
DecoderThrowHelper.ThrowImageFormatException.BadHuffmanCode(); DecoderThrowHelper.ThrowImageFormatException.BadHuffmanCode();
// DUMMY RETURN! C# doesn't know we have thrown an exception! // DUMMY RETURN! C# doesn't know we have thrown an exception!
return OrigDecoderErrorCode.NoError; return GolangDecoderErrorCode.NoError;
} }
/// <summary> /// <summary>
@ -295,11 +295,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// <summary> /// <summary>
/// Skips the next n bytes. /// Skips the next n bytes.
/// Does not throw, returns <see cref="OrigDecoderErrorCode"/> instead! /// Does not throw, returns <see cref="GolangDecoderErrorCode"/> instead!
/// </summary> /// </summary>
/// <param name="count">The number of bytes to ignore.</param> /// <param name="count">The number of bytes to ignore.</param>
/// <returns>The <see cref="OrigDecoderErrorCode"/></returns> /// <returns>The <see cref="GolangDecoderErrorCode"/></returns>
public OrigDecoderErrorCode SkipUnsafe(int count) public GolangDecoderErrorCode SkipUnsafe(int count)
{ {
// Unread the overshot bytes, if any. // Unread the overshot bytes, if any.
if (this.Bytes.UnreadableBytes != 0) if (this.Bytes.UnreadableBytes != 0)
@ -328,13 +328,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
} }
this.LastErrorCode = this.Bytes.FillUnsafe(this.InputStream); this.LastErrorCode = this.Bytes.FillUnsafe(this.InputStream);
if (this.LastErrorCode != OrigDecoderErrorCode.NoError) if (this.LastErrorCode != GolangDecoderErrorCode.NoError)
{ {
return this.LastErrorCode; return this.LastErrorCode;
} }
} }
return this.LastErrorCode = OrigDecoderErrorCode.NoError; return this.LastErrorCode = GolangDecoderErrorCode.NoError;
} }
/// <summary> /// <summary>
@ -374,8 +374,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// </summary> /// </summary>
/// <param name="t">Byte</param> /// <param name="t">Byte</param>
/// <param name="x">Read bits value</param> /// <param name="x">Read bits value</param>
/// <returns>The <see cref="OrigDecoderErrorCode"/></returns> /// <returns>The <see cref="GolangDecoderErrorCode"/></returns>
public OrigDecoderErrorCode ReceiveExtendUnsafe(int t, out int x) public GolangDecoderErrorCode ReceiveExtendUnsafe(int t, out int x)
{ {
this.LastErrorCode = this.Bits.ReceiveExtendUnsafe(t, ref this, out x); this.LastErrorCode = this.Bits.ReceiveExtendUnsafe(t, ref this, out x);
return this.LastErrorCode; return this.LastErrorCode;
@ -386,7 +386,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// </summary> /// </summary>
public void ResetHuffmanDecoder() public void ResetHuffmanDecoder()
{ {
this.Bits = default(Bits); this.Bits = default;
} }
} }
} }

6
src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoder.cs → src/ImageSharp/Formats/Jpeg/GolangPort/GolangJpegDecoder.cs

@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
/// <summary> /// <summary>
/// Image decoder for generating an image out of a jpg stream. /// Image decoder for generating an image out of a jpg stream.
/// </summary> /// </summary>
internal sealed class OrigJpegDecoder : IImageDecoder, IJpegDecoderOptions, IImageInfoDetector internal sealed class GolangJpegDecoder : IImageDecoder, IJpegDecoderOptions, IImageInfoDetector
{ {
/// <summary> /// <summary>
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded.
@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
{ {
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
using (var decoder = new OrigJpegDecoderCore(configuration, this)) using (var decoder = new GolangJpegDecoderCore(configuration, this))
{ {
return decoder.Decode<TPixel>(stream); return decoder.Decode<TPixel>(stream);
} }
@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
{ {
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
using (var decoder = new OrigJpegDecoderCore(configuration, this)) using (var decoder = new GolangJpegDecoderCore(configuration, this))
{ {
return decoder.Identify(stream); return decoder.Identify(stream);
} }

86
src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs → src/ImageSharp/Formats/Jpeg/GolangPort/GolangJpegDecoderCore.cs

@ -3,8 +3,9 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using SixLabors.ImageSharp.Formats.Jpeg.Common;
using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder; using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder;
using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder; using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder;
using SixLabors.ImageSharp.MetaData; using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.MetaData.Profiles.Exif; using SixLabors.ImageSharp.MetaData.Profiles.Exif;
@ -19,7 +20,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
/// <summary> /// <summary>
/// Performs the jpeg decoding operation. /// Performs the jpeg decoding operation.
/// </summary> /// </summary>
internal sealed unsafe class OrigJpegDecoderCore : IRawJpegData internal sealed unsafe class GolangJpegDecoderCore : IRawJpegData
{ {
/// <summary> /// <summary>
/// The maximum number of color components /// The maximum number of color components
@ -40,7 +41,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
#pragma warning disable SA1401 // FieldsMustBePrivate #pragma warning disable SA1401 // FieldsMustBePrivate
/// <summary> /// <summary>
/// Encapsulates stream reading and processing data and operations for <see cref="OrigJpegDecoderCore"/>. /// Encapsulates stream reading and processing data and operations for <see cref="GolangJpegDecoderCore"/>.
/// It's a value type for improved data locality, and reduced number of CALLVIRT-s /// It's a value type for improved data locality, and reduced number of CALLVIRT-s
/// </summary> /// </summary>
public InputProcessor InputProcessor; public InputProcessor InputProcessor;
@ -79,11 +80,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
private AdobeMarker adobe; private AdobeMarker adobe;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="OrigJpegDecoderCore" /> class. /// Initializes a new instance of the <see cref="GolangJpegDecoderCore" /> class.
/// </summary> /// </summary>
/// <param name="configuration">The configuration.</param> /// <param name="configuration">The configuration.</param>
/// <param name="options">The options.</param> /// <param name="options">The options.</param>
public OrigJpegDecoderCore(Configuration configuration, IJpegDecoderOptions options) public GolangJpegDecoderCore(Configuration configuration, IJpegDecoderOptions options)
{ {
this.IgnoreMetadata = options.IgnoreMetadata; this.IgnoreMetadata = options.IgnoreMetadata;
this.configuration = configuration ?? Configuration.Default; this.configuration = configuration ?? Configuration.Default;
@ -96,12 +97,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
/// <summary> /// <summary>
/// Gets the component array /// Gets the component array
/// </summary> /// </summary>
public OrigComponent[] Components { get; private set; } public GolangComponent[] Components { get; private set; }
/// <summary> /// <summary>
/// Gets the huffman trees /// Gets the huffman trees
/// </summary> /// </summary>
public OrigHuffmanTree[] HuffmanTrees { get; private set; } public GolangHuffmanTree[] HuffmanTrees { get; private set; }
/// <inheritdoc /> /// <inheritdoc />
public Block8x8F[] QuantizationTables { get; private set; } public Block8x8F[] QuantizationTables { get; private set; }
@ -209,7 +210,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
{ {
if (this.Components != null) if (this.Components != null)
{ {
foreach (OrigComponent component in this.Components) foreach (GolangComponent component in this.Components)
{ {
component?.Dispose(); component?.Dispose();
} }
@ -219,7 +220,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
} }
/// <summary> /// <summary>
/// Read metadata from stream and read the blocks in the scans into <see cref="OrigComponent.SpectralBlocks"/>. /// Read metadata from stream and read the blocks in the scans into <see cref="GolangComponent.SpectralBlocks"/>.
/// </summary> /// </summary>
/// <param name="stream">The stream</param> /// <param name="stream">The stream</param>
/// <param name="metadataOnly">Whether to decode metadata only.</param> /// <param name="metadataOnly">Whether to decode metadata only.</param>
@ -231,14 +232,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
if (!metadataOnly) if (!metadataOnly)
{ {
this.HuffmanTrees = OrigHuffmanTree.CreateHuffmanTrees(); this.HuffmanTrees = GolangHuffmanTree.CreateHuffmanTrees();
this.QuantizationTables = new Block8x8F[MaxTq + 1]; this.QuantizationTables = new Block8x8F[MaxTq + 1];
} }
// Check for the Start Of Image marker. // Check for the Start Of Image marker.
this.InputProcessor.ReadFull(this.Temp, 0, 2); this.InputProcessor.ReadFull(this.Temp, 0, 2);
if (this.Temp[0] != OrigJpegConstants.Markers.XFF || this.Temp[1] != OrigJpegConstants.Markers.SOI) if (this.Temp[0] != JpegConstants.Markers.XFF || this.Temp[1] != JpegConstants.Markers.SOI)
{ {
throw new ImageFormatException("Missing SOI marker."); throw new ImageFormatException("Missing SOI marker.");
} }
@ -302,12 +303,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
} }
// End Of Image. // End Of Image.
if (marker == OrigJpegConstants.Markers.EOI) if (marker == JpegConstants.Markers.EOI)
{ {
break; break;
} }
if (marker >= OrigJpegConstants.Markers.RST0 && marker <= OrigJpegConstants.Markers.RST7) if (marker >= JpegConstants.Markers.RST0 && marker <= JpegConstants.Markers.RST7)
{ {
// Figures B.2 and B.16 of the specification suggest that restart markers should // Figures B.2 and B.16 of the specification suggest that restart markers should
// only occur between Entropy Coded Segments and not after the final ECS. // only occur between Entropy Coded Segments and not after the final ECS.
@ -329,14 +330,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
switch (marker) switch (marker)
{ {
case OrigJpegConstants.Markers.SOF0: case JpegConstants.Markers.SOF0:
case OrigJpegConstants.Markers.SOF1: case JpegConstants.Markers.SOF1:
case OrigJpegConstants.Markers.SOF2: case JpegConstants.Markers.SOF2:
this.IsProgressive = marker == OrigJpegConstants.Markers.SOF2; this.IsProgressive = marker == JpegConstants.Markers.SOF2;
this.ProcessStartOfFrameMarker(remaining, metadataOnly); this.ProcessStartOfFrameMarker(remaining, metadataOnly);
break; break;
case OrigJpegConstants.Markers.DHT: case JpegConstants.Markers.DHT:
if (metadataOnly) if (metadataOnly)
{ {
this.InputProcessor.Skip(remaining); this.InputProcessor.Skip(remaining);
@ -347,7 +348,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
} }
break; break;
case OrigJpegConstants.Markers.DQT: case JpegConstants.Markers.DQT:
if (metadataOnly) if (metadataOnly)
{ {
this.InputProcessor.Skip(remaining); this.InputProcessor.Skip(remaining);
@ -358,7 +359,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
} }
break; break;
case OrigJpegConstants.Markers.SOS: case JpegConstants.Markers.SOS:
if (!metadataOnly) if (!metadataOnly)
{ {
this.ProcessStartOfScanMarker(remaining); this.ProcessStartOfScanMarker(remaining);
@ -377,7 +378,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
break; break;
case OrigJpegConstants.Markers.DRI: case JpegConstants.Markers.DRI:
if (metadataOnly) if (metadataOnly)
{ {
this.InputProcessor.Skip(remaining); this.InputProcessor.Skip(remaining);
@ -388,21 +389,21 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
} }
break; break;
case OrigJpegConstants.Markers.APP0: case JpegConstants.Markers.APP0:
this.ProcessApplicationHeaderMarker(remaining); this.ProcessApplicationHeaderMarker(remaining);
break; break;
case OrigJpegConstants.Markers.APP1: case JpegConstants.Markers.APP1:
this.ProcessApp1Marker(remaining); this.ProcessApp1Marker(remaining);
break; break;
case OrigJpegConstants.Markers.APP2: case JpegConstants.Markers.APP2:
this.ProcessApp2Marker(remaining); this.ProcessApp2Marker(remaining);
break; break;
case OrigJpegConstants.Markers.APP14: case JpegConstants.Markers.APP14:
this.ProcessApp14Marker(remaining); this.ProcessApp14Marker(remaining);
break; break;
default: default:
if ((marker >= OrigJpegConstants.Markers.APP0 && marker <= OrigJpegConstants.Markers.APP15) if ((marker >= JpegConstants.Markers.APP0 && marker <= JpegConstants.Markers.APP15)
|| marker == OrigJpegConstants.Markers.COM) || marker == JpegConstants.Markers.COM)
{ {
this.InputProcessor.Skip(remaining); this.InputProcessor.Skip(remaining);
} }
@ -449,6 +450,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
this.MetaData.VerticalResolution = verticalValue; this.MetaData.VerticalResolution = verticalValue;
} }
} }
if (this.MetaData.IccProfile?.CheckIsValid() == false)
{
this.MetaData.IccProfile = null;
}
} }
/// <summary> /// <summary>
@ -680,12 +686,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
if (!metadataOnly) if (!metadataOnly)
{ {
this.Components = new OrigComponent[this.ComponentCount]; this.Components = new GolangComponent[this.ComponentCount];
for (int i = 0; i < this.ComponentCount; i++) for (int i = 0; i < this.ComponentCount; i++)
{ {
byte componentIdentifier = this.Temp[6 + (3 * i)]; byte componentIdentifier = this.Temp[6 + (3 * i)];
var component = new OrigComponent(componentIdentifier, i); var component = new GolangComponent(componentIdentifier, i);
component.InitializeCoreData(this); component.InitializeCoreData(this);
this.Components[i] = component; this.Components[i] = component;
} }
@ -697,7 +703,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
this.ColorSpace = this.DeduceJpegColorSpace(); this.ColorSpace = this.DeduceJpegColorSpace();
foreach (OrigComponent component in this.Components) foreach (GolangComponent component in this.Components)
{ {
component.InitializeDerivedData(this.configuration.MemoryManager, this); component.InitializeDerivedData(this.configuration.MemoryManager, this);
} }
@ -721,18 +727,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
this.InputProcessor.ReadFull(this.Temp, 0, 17); this.InputProcessor.ReadFull(this.Temp, 0, 17);
int tc = this.Temp[0] >> 4; int tc = this.Temp[0] >> 4;
if (tc > OrigHuffmanTree.MaxTc) if (tc > GolangHuffmanTree.MaxTc)
{ {
throw new ImageFormatException("Bad Tc value"); throw new ImageFormatException("Bad Tc value");
} }
int th = this.Temp[0] & 0x0f; int th = this.Temp[0] & 0x0f;
if (th > OrigHuffmanTree.MaxTh) if (th > GolangHuffmanTree.MaxTh)
{ {
throw new ImageFormatException("Bad Th value"); throw new ImageFormatException("Bad Th value");
} }
int huffTreeIndex = (tc * OrigHuffmanTree.ThRowSize) + th; int huffTreeIndex = (tc * GolangHuffmanTree.ThRowSize) + th;
this.HuffmanTrees[huffTreeIndex].ProcessDefineHuffmanTablesMarkerLoop( this.HuffmanTrees[huffTreeIndex].ProcessDefineHuffmanTablesMarkerLoop(
ref this.InputProcessor, ref this.InputProcessor,
this.Temp, this.Temp,
@ -766,9 +772,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
/// </exception> /// </exception>
private void ProcessStartOfScanMarker(int remaining) private void ProcessStartOfScanMarker(int remaining)
{ {
var scan = default(OrigJpegScanDecoder); var scan = default(GolangJpegScanDecoder);
OrigJpegScanDecoder.InitStreamReading(&scan, this, remaining); GolangJpegScanDecoder.InitStreamReading(&scan, this, remaining);
this.InputProcessor.Bits = default(Bits); this.InputProcessor.Bits = default;
scan.DecodeBlocks(this); scan.DecodeBlocks(this);
} }
@ -779,19 +785,19 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
case 1: case 1:
return JpegColorSpace.Grayscale; return JpegColorSpace.Grayscale;
case 3: case 3:
if (!this.isAdobe || this.adobe.ColorTransform == OrigJpegConstants.Adobe.ColorTransformYCbCr) if (!this.isAdobe || this.adobe.ColorTransform == JpegConstants.Adobe.ColorTransformYCbCr)
{ {
return JpegColorSpace.YCbCr; return JpegColorSpace.YCbCr;
} }
if (this.adobe.ColorTransform == OrigJpegConstants.Adobe.ColorTransformUnknown) if (this.adobe.ColorTransform == JpegConstants.Adobe.ColorTransformUnknown)
{ {
return JpegColorSpace.RGB; return JpegColorSpace.RGB;
} }
break; break;
case 4: case 4:
if (this.adobe.ColorTransform == OrigJpegConstants.Adobe.ColorTransformYcck) if (this.adobe.ColorTransform == JpegConstants.Adobe.ColorTransformYcck)
{ {
return JpegColorSpace.Ycck; return JpegColorSpace.Ycck;
} }

189
src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegConstants.cs

@ -1,189 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Collections.Generic;
namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
{
/// <summary>
/// Defines jpeg constants defined in the specification.
/// </summary>
internal static class OrigJpegConstants
{
/// <summary>
/// The maximum allowable length in each dimension of a jpeg image.
/// </summary>
public const ushort MaxLength = 65535;
/// <summary>
/// The list of mimetypes that equate to a jpeg.
/// </summary>
public static readonly IEnumerable<string> MimeTypes = new[] { "image/jpeg", "image/pjpeg" };
/// <summary>
/// The list of file extensions that equate to a jpeg.
/// </summary>
public static readonly IEnumerable<string> FileExtensions = new[] { "jpg", "jpeg", "jfif" };
/// <summary>
/// Describes common Jpeg markers
/// </summary>
internal static class Markers
{
/// <summary>
/// Marker prefix. Next byte is a marker.
/// </summary>
public const byte XFF = 0xff;
/// <summary>
/// Same as <see cref="XFF"/> but of type <see cref="int"/>
/// </summary>
public const int XFFInt = XFF;
/// <summary>
/// Start of Image
/// </summary>
public const byte SOI = 0xd8;
/// <summary>
/// Start of Frame (baseline DCT)
/// <remarks>
/// Indicates that this is a baseline DCT-based JPEG, and specifies the width, height, number of components,
/// and component subsampling (e.g., 4:2:0).
/// </remarks>
/// </summary>
public const byte SOF0 = 0xc0;
/// <summary>
/// Start Of Frame (Extended Sequential DCT)
/// <remarks>
/// Indicates that this is a progressive DCT-based JPEG, and specifies the width, height, number of components,
/// and component subsampling (e.g., 4:2:0).
/// </remarks>
/// </summary>
public const byte SOF1 = 0xc1;
/// <summary>
/// Start Of Frame (progressive DCT)
/// <remarks>
/// Indicates that this is a progressive DCT-based JPEG, and specifies the width, height, number of components,
/// and component subsampling (e.g., 4:2:0).
/// </remarks>
/// </summary>
public const byte SOF2 = 0xc2;
/// <summary>
/// Define Huffman Table(s)
/// <remarks>
/// Specifies one or more Huffman tables.
/// </remarks>
/// </summary>
public const byte DHT = 0xc4;
/// <summary>
/// Define Quantization Table(s)
/// <remarks>
/// Specifies one or more quantization tables.
/// </remarks>
/// </summary>
public const byte DQT = 0xdb;
/// <summary>
/// Define Restart Interval
/// <remarks>
/// Specifies the interval between RSTn markers, in macroblocks. This marker is followed by two bytes
/// indicating the fixed size so it can be treated like any other variable size segment.
/// </remarks>
/// </summary>
public const byte DRI = 0xdd;
/// <summary>
/// Define First Restart
/// <remarks>
/// Inserted every r macroblocks, where r is the restart interval set by a DRI marker.
/// Not used if there was no DRI marker. The low three bits of the marker code cycle in value from 0 to 7.
/// </remarks>
/// </summary>
public const byte RST0 = 0xd0;
/// <summary>
/// Define Eigth Restart
/// <remarks>
/// Inserted every r macroblocks, where r is the restart interval set by a DRI marker.
/// Not used if there was no DRI marker. The low three bits of the marker code cycle in value from 0 to 7.
/// </remarks>
/// </summary>
public const byte RST7 = 0xd7;
/// <summary>
/// Start of Scan
/// <remarks>
/// Begins a top-to-bottom scan of the image. In baseline DCT JPEG images, there is generally a single scan.
/// Progressive DCT JPEG images usually contain multiple scans. This marker specifies which slice of data it
/// will contain, and is immediately followed by entropy-coded data.
/// </remarks>
/// </summary>
public const byte SOS = 0xda;
/// <summary>
/// Comment
/// <remarks>
/// Contains a text comment.
/// </remarks>
/// </summary>
public const byte COM = 0xfe;
/// <summary>
/// End of Image
/// </summary>
public const byte EOI = 0xd9;
/// <summary>
/// Application specific marker for marking the jpeg format.
/// <see href="http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html"/>
/// </summary>
public const byte APP0 = 0xe0;
/// <summary>
/// Application specific marker for marking where to store metadata.
/// </summary>
public const byte APP1 = 0xe1;
/// <summary>
/// Application specific marker for marking where to store ICC profile information.
/// </summary>
public const byte APP2 = 0xe2;
/// <summary>
/// Application specific marker used by Adobe for storing encoding information for DCT filters.
/// </summary>
public const byte APP14 = 0xee;
/// <summary>
/// Application specific marker used by GraphicConverter to store JPEG quality.
/// </summary>
public const byte APP15 = 0xef;
}
/// <summary>
/// Describes Adobe specific markers <see href="http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#Adobe"/>
/// </summary>
internal static class Adobe
{
/// <summary>
/// The color transform is unknown.(RGB or CMYK)
/// </summary>
public const int ColorTransformUnknown = 0;
/// <summary>
/// The color transform is YCbCr (luminance, red chroma, blue chroma)
/// </summary>
public const int ColorTransformYCbCr = 1;
/// <summary>
/// The color transform is YCCK (luminance, red chroma, blue chroma, keyline)
/// </summary>
public const int ColorTransformYcck = 2;
}
}
}

69
src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegConstants.cs → src/ImageSharp/Formats/Jpeg/JpegConstants.cs

@ -1,23 +1,45 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// ReSharper disable InconsistentNaming using System.Collections.Generic;
namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
namespace SixLabors.ImageSharp.Formats.Jpeg
{ {
/// <summary> /// <summary>
/// Contains jpeg constant values /// Contains jpeg constant values defined in the specification.
/// </summary> /// </summary>
internal static class PdfJsJpegConstants internal static class JpegConstants
{ {
/// <summary>
/// The maximum allowable length in each dimension of a jpeg image.
/// </summary>
public const ushort MaxLength = 65535;
/// <summary>
/// The list of mimetypes that equate to a jpeg.
/// </summary>
public static readonly IEnumerable<string> MimeTypes = new[] { "image/jpeg", "image/pjpeg" };
/// <summary>
/// The list of file extensions that equate to a jpeg.
/// </summary>
public static readonly IEnumerable<string> FileExtensions = new[] { "jpg", "jpeg", "jfif" };
/// <summary> /// <summary>
/// Contains marker specific constants /// Contains marker specific constants
/// </summary> /// </summary>
public static class Markers // ReSharper disable InconsistentNaming
internal static class Markers
{ {
/// <summary> /// <summary>
/// The prefix used for all markers. /// The prefix used for all markers.
/// </summary> /// </summary>
public const byte Prefix = 0xFF; public const byte XFF = 0xFF;
/// <summary>
/// Same as <see cref="XFF"/> but of type <see cref="int"/>
/// </summary>
public const int XFFInt = XFF;
/// <summary> /// <summary>
/// The Start of Image marker /// The Start of Image marker
@ -161,7 +183,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
/// <summary> /// <summary>
/// Define Restart Interval /// Define Restart Interval
/// <remarks> /// <remarks>
/// Specifies the interval between RSTn markers, in macroblocks.This marker is followed by two bytes indicating the fixed size so it can be treated like any other variable size segment. /// Specifies the interval between RSTn markers, in macroblocks.This marker is followed by two bytes indicating the fixed size so
/// it can be treated like any other variable size segment.
/// </remarks> /// </remarks>
/// </summary> /// </summary>
public const byte DRI = 0xDD; public const byte DRI = 0xDD;
@ -193,27 +216,27 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
/// </remarks> /// </remarks>
/// </summary> /// </summary>
public const byte RST7 = 0xD7; public const byte RST7 = 0xD7;
}
/// <summary>
/// Contains Adobe specific constants
/// </summary>
internal static class Adobe
{
/// <summary> /// <summary>
/// Contains Adobe specific markers /// The color transform is unknown.(RGB or CMYK)
/// </summary> /// </summary>
public static class Adobe public const byte ColorTransformUnknown = 0;
{
/// <summary>
/// The color transform is unknown.(RGB or CMYK)
/// </summary>
public const byte ColorTransformUnknown = 0;
/// <summary> /// <summary>
/// The color transform is YCbCr (luminance, red chroma, blue chroma) /// The color transform is YCbCr (luminance, red chroma, blue chroma)
/// </summary> /// </summary>
public const byte ColorTransformYCbCr = 1; public const byte ColorTransformYCbCr = 1;
/// <summary> /// <summary>
/// The color transform is YCCK (luminance, red chroma, blue chroma, keyline) /// The color transform is YCCK (luminance, red chroma, blue chroma, keyline)
/// </summary> /// </summary>
public const byte ColorTransformYcck = 2; public const byte ColorTransformYcck = 2;
}
} }
} }
} }

6
src/ImageSharp/Formats/Jpeg/JpegDecoder.cs

@ -3,7 +3,7 @@
using System.IO; using System.IO;
using SixLabors.ImageSharp.Formats.Jpeg.GolangPort; using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Jpeg namespace SixLabors.ImageSharp.Formats.Jpeg
@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
{ {
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
using (var decoder = new OrigJpegDecoderCore(configuration, this)) using (var decoder = new PdfJsJpegDecoderCore(configuration, this))
{ {
return decoder.Decode<TPixel>(stream); return decoder.Decode<TPixel>(stream);
} }
@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
{ {
Guard.NotNull(stream, "stream"); Guard.NotNull(stream, "stream");
using (var decoder = new OrigJpegDecoderCore(configuration, this)) using (var decoder = new PdfJsJpegDecoderCore(configuration, this))
{ {
return decoder.Identify(stream); return decoder.Identify(stream);
} }

1
src/ImageSharp/Formats/Jpeg/JpegEncoder.cs

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System.IO; using System.IO;
using SixLabors.ImageSharp.Formats.Jpeg.GolangPort;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Jpeg namespace SixLabors.ImageSharp.Formats.Jpeg

74
src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs → src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs

@ -1,19 +1,16 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System.Buffers;
using System.IO; using System.IO;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Formats.Jpeg.Common;
using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components; using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder; using SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.MetaData.Profiles.Exif; using SixLabors.ImageSharp.MetaData.Profiles.Exif;
using SixLabors.ImageSharp.MetaData.Profiles.Icc; using SixLabors.ImageSharp.MetaData.Profiles.Icc;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using Block8x8F = SixLabors.ImageSharp.Formats.Jpeg.Common.Block8x8F;
namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort namespace SixLabors.ImageSharp.Formats.Jpeg
{ {
/// <summary> /// <summary>
/// Image encoder for writing an image to a stream as a jpeg. /// Image encoder for writing an image to a stream as a jpeg.
@ -58,7 +55,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
/// </summary> /// </summary>
private static readonly byte[] SosHeaderYCbCr = private static readonly byte[] SosHeaderYCbCr =
{ {
OrigJpegConstants.Markers.XFF, OrigJpegConstants.Markers.SOS, JpegConstants.Markers.XFF, JpegConstants.Markers.SOS,
// Marker // Marker
0x00, 0x0c, 0x00, 0x0c,
@ -104,11 +101,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
} }
}; };
/// <summary>
/// Lookup tables for converting Rgb to YCbCr
/// </summary>
private static RgbToYCbCrTables rgbToYCbCrTables = RgbToYCbCrTables.Create();
/// <summary> /// <summary>
/// A scratch buffer to reduce allocations. /// A scratch buffer to reduce allocations.
/// </summary> /// </summary>
@ -190,7 +182,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
Guard.NotNull(image, nameof(image)); Guard.NotNull(image, nameof(image));
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
ushort max = OrigJpegConstants.MaxLength; ushort max = JpegConstants.MaxLength;
if (image.Width >= max || image.Height >= max) if (image.Width >= max || image.Height >= max)
{ {
throw new ImageFormatException($"Image is too large to encode at {image.Width}x{image.Height}."); throw new ImageFormatException($"Image is too large to encode at {image.Width}x{image.Height}.");
@ -234,8 +226,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
this.WriteStartOfScan(image); this.WriteStartOfScan(image);
// Write the End Of Image marker. // Write the End Of Image marker.
this.buffer[0] = OrigJpegConstants.Markers.XFF; this.buffer[0] = JpegConstants.Markers.XFF;
this.buffer[1] = OrigJpegConstants.Markers.EOI; this.buffer[1] = JpegConstants.Markers.EOI;
stream.Write(this.buffer, 0, 2); stream.Write(this.buffer, 0, 2);
stream.Flush(); stream.Flush();
} }
@ -382,18 +374,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
{ {
// TODO: Need a JpegScanEncoder<TPixel> class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.) // TODO: Need a JpegScanEncoder<TPixel> class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.)
// (Partially done with YCbCrForwardConverter<TPixel>) // (Partially done with YCbCrForwardConverter<TPixel>)
Block8x8F temp1 = default(Block8x8F); Block8x8F temp1 = default;
Block8x8F temp2 = default(Block8x8F); Block8x8F temp2 = default;
Block8x8F onStackLuminanceQuantTable = this.luminanceQuantTable; Block8x8F onStackLuminanceQuantTable = this.luminanceQuantTable;
Block8x8F onStackChrominanceQuantTable = this.chrominanceQuantTable; Block8x8F onStackChrominanceQuantTable = this.chrominanceQuantTable;
ZigZag unzig = ZigZag.CreateUnzigTable(); var unzig = ZigZag.CreateUnzigTable();
// ReSharper disable once InconsistentNaming // ReSharper disable once InconsistentNaming
int prevDCY = 0, prevDCCb = 0, prevDCCr = 0; int prevDCY = 0, prevDCCb = 0, prevDCCr = 0;
YCbCrForwardConverter<TPixel> pixelConverter = YCbCrForwardConverter<TPixel>.Create(); var pixelConverter = YCbCrForwardConverter<TPixel>.Create();
for (int y = 0; y < pixels.Height; y += 8) for (int y = 0; y < pixels.Height; y += 8)
{ {
@ -437,12 +429,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
private void WriteApplicationHeader(short horizontalResolution, short verticalResolution) private void WriteApplicationHeader(short horizontalResolution, short verticalResolution)
{ {
// Write the start of image marker. Markers are always prefixed with with 0xff. // Write the start of image marker. Markers are always prefixed with with 0xff.
this.buffer[0] = OrigJpegConstants.Markers.XFF; this.buffer[0] = JpegConstants.Markers.XFF;
this.buffer[1] = OrigJpegConstants.Markers.SOI; this.buffer[1] = JpegConstants.Markers.SOI;
// Write the JFIF headers // Write the JFIF headers
this.buffer[2] = OrigJpegConstants.Markers.XFF; this.buffer[2] = JpegConstants.Markers.XFF;
this.buffer[3] = OrigJpegConstants.Markers.APP0; // Application Marker this.buffer[3] = JpegConstants.Markers.APP0; // Application Marker
this.buffer[4] = 0x00; this.buffer[4] = 0x00;
this.buffer[5] = 0x10; this.buffer[5] = 0x10;
this.buffer[6] = 0x4a; // J this.buffer[6] = 0x4a; // J
@ -502,7 +494,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
this.EmitHuffRLE((HuffIndex)((2 * (int)index) + 0), 0, dc - prevDC); this.EmitHuffRLE((HuffIndex)((2 * (int)index) + 0), 0, dc - prevDC);
// Emit the AC components. // Emit the AC components.
HuffIndex h = (HuffIndex)((2 * (int)index) + 1); var h = (HuffIndex)((2 * (int)index) + 1);
int runLength = 0; int runLength = 0;
for (int zig = 1; zig < Block8x8F.Size; zig++) for (int zig = 1; zig < Block8x8F.Size; zig++)
@ -556,7 +548,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
markerlen += 1 + 16 + s.Values.Length; markerlen += 1 + 16 + s.Values.Length;
} }
this.WriteMarkerHeader(OrigJpegConstants.Markers.DHT, markerlen); this.WriteMarkerHeader(JpegConstants.Markers.DHT, markerlen);
for (int i = 0; i < specs.Length; i++) for (int i = 0; i < specs.Length; i++)
{ {
HuffmanSpec spec = specs[i]; HuffmanSpec spec = specs[i];
@ -590,7 +582,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
{ {
// Marker + quantization table lengths // Marker + quantization table lengths
int markerlen = 2 + (QuantizationTableCount * (1 + Block8x8F.Size)); int markerlen = 2 + (QuantizationTableCount * (1 + Block8x8F.Size));
this.WriteMarkerHeader(OrigJpegConstants.Markers.DQT, markerlen); this.WriteMarkerHeader(JpegConstants.Markers.DQT, markerlen);
// Loop through and collect the tables as one array. // Loop through and collect the tables as one array.
// This allows us to reduce the number of writes to the stream. // This allows us to reduce the number of writes to the stream.
@ -627,8 +619,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
int length = data.Length + 2; int length = data.Length + 2;
this.buffer[0] = OrigJpegConstants.Markers.XFF; this.buffer[0] = JpegConstants.Markers.XFF;
this.buffer[1] = OrigJpegConstants.Markers.APP1; // Application Marker this.buffer[1] = JpegConstants.Markers.APP1; // Application Marker
this.buffer[2] = (byte)((length >> 8) & 0xFF); this.buffer[2] = (byte)((length >> 8) & 0xFF);
this.buffer[3] = (byte)(length & 0xFF); this.buffer[3] = (byte)(length & 0xFF);
@ -686,8 +678,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
dataLength -= length; dataLength -= length;
this.buffer[0] = OrigJpegConstants.Markers.XFF; this.buffer[0] = JpegConstants.Markers.XFF;
this.buffer[1] = OrigJpegConstants.Markers.APP2; // Application Marker this.buffer[1] = JpegConstants.Markers.APP2; // Application Marker
int markerLength = length + 16; int markerLength = length + 16;
this.buffer[2] = (byte)((markerLength >> 8) & 0xFF); this.buffer[2] = (byte)((markerLength >> 8) & 0xFF);
this.buffer[3] = (byte)(markerLength & 0xFF); this.buffer[3] = (byte)(markerLength & 0xFF);
@ -759,7 +751,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
// Length (high byte, low byte), 8 + components * 3. // Length (high byte, low byte), 8 + components * 3.
int markerlen = 8 + (3 * componentCount); int markerlen = 8 + (3 * componentCount);
this.WriteMarkerHeader(OrigJpegConstants.Markers.SOF0, markerlen); this.WriteMarkerHeader(JpegConstants.Markers.SOF0, markerlen);
this.buffer[0] = 8; // Data Precision. 8 for now, 12 and 16 bit jpegs not supported this.buffer[0] = 8; // Data Precision. 8 for now, 12 and 16 bit jpegs not supported
this.buffer[1] = (byte)(height >> 8); this.buffer[1] = (byte)(height >> 8);
this.buffer[2] = (byte)(height & 0xff); // (2 bytes, Hi-Lo), must be > 0 if DNL not supported this.buffer[2] = (byte)(height & 0xff); // (2 bytes, Hi-Lo), must be > 0 if DNL not supported
@ -827,20 +819,20 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
// TODO: Need a JpegScanEncoder<TPixel> class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.) // TODO: Need a JpegScanEncoder<TPixel> class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.)
Block8x8F b = default(Block8x8F); Block8x8F b = default;
BlockQuad cb = default(BlockQuad); BlockQuad cb = default;
BlockQuad cr = default(BlockQuad); BlockQuad cr = default;
Block8x8F* cbPtr = (Block8x8F*)cb.Data; var cbPtr = (Block8x8F*)cb.Data;
Block8x8F* crPtr = (Block8x8F*)cr.Data; var crPtr = (Block8x8F*)cr.Data;
Block8x8F temp1 = default(Block8x8F); Block8x8F temp1 = default;
Block8x8F temp2 = default(Block8x8F); Block8x8F temp2 = default;
Block8x8F onStackLuminanceQuantTable = this.luminanceQuantTable; Block8x8F onStackLuminanceQuantTable = this.luminanceQuantTable;
Block8x8F onStackChrominanceQuantTable = this.chrominanceQuantTable; Block8x8F onStackChrominanceQuantTable = this.chrominanceQuantTable;
ZigZag unzig = ZigZag.CreateUnzigTable(); var unzig = ZigZag.CreateUnzigTable();
var pixelConverter = YCbCrForwardConverter<TPixel>.Create(); var pixelConverter = YCbCrForwardConverter<TPixel>.Create();
@ -902,7 +894,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
private void WriteMarkerHeader(byte marker, int length) private void WriteMarkerHeader(byte marker, int length)
{ {
// Markers are always prefixed with with 0xff. // Markers are always prefixed with with 0xff.
this.buffer[0] = OrigJpegConstants.Markers.XFF; this.buffer[0] = JpegConstants.Markers.XFF;
this.buffer[1] = marker; this.buffer[1] = marker;
this.buffer[2] = (byte)(length >> 8); this.buffer[2] = (byte)(length >> 8);
this.buffer[3] = (byte)(length & 0xff); this.buffer[3] = (byte)(length & 0xff);

5
src/ImageSharp/Formats/Jpeg/JpegFormat.cs

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System.Collections.Generic; using System.Collections.Generic;
using SixLabors.ImageSharp.Formats.Jpeg.GolangPort;
namespace SixLabors.ImageSharp.Formats.Jpeg namespace SixLabors.ImageSharp.Formats.Jpeg
{ {
@ -18,9 +17,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
public string DefaultMimeType => "image/jpeg"; public string DefaultMimeType => "image/jpeg";
/// <inheritdoc/> /// <inheritdoc/>
public IEnumerable<string> MimeTypes => OrigJpegConstants.MimeTypes; public IEnumerable<string> MimeTypes => JpegConstants.MimeTypes;
/// <inheritdoc/> /// <inheritdoc/>
public IEnumerable<string> FileExtensions => OrigJpegConstants.FileExtensions; public IEnumerable<string> FileExtensions => JpegConstants.FileExtensions;
} }
} }

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

@ -4,8 +4,9 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Formats.Jpeg.Common;
using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder; using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.Primitives; using SixLabors.Primitives;

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

@ -7,7 +7,8 @@ using System.Diagnostics;
#endif #endif
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Formats.Jpeg.Common;
using SixLabors.ImageSharp.Formats.Jpeg.Components;
namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
{ {
@ -136,7 +137,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
byte marker = fileMarker.Marker; byte marker = fileMarker.Marker;
// RSTn - We've already read the bytes and altered the position so no need to skip // RSTn - We've already read the bytes and altered the position so no need to skip
if (marker >= PdfJsJpegConstants.Markers.RST0 && marker <= PdfJsJpegConstants.Markers.RST7) if (marker >= JpegConstants.Markers.RST0 && marker <= JpegConstants.Markers.RST7)
{ {
continue; continue;
} }
@ -452,7 +453,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
this.endOfStreamReached = true; this.endOfStreamReached = true;
return false; return false;
case PdfJsJpegConstants.Markers.Prefix: case JpegConstants.Markers.XFF:
int nextByte = stream.ReadByte(); int nextByte = stream.ReadByte();
if (nextByte == -0x1) if (nextByte == -0x1)

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

@ -7,8 +7,9 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Formats.Jpeg.Common;
using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder; using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder;
using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components; using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.MetaData; using SixLabors.ImageSharp.MetaData;
@ -98,15 +99,23 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
/// </summary> /// </summary>
public PdfJsFrame Frame { get; private set; } public PdfJsFrame Frame { get; private set; }
/// <inheritdoc/>
public Size ImageSizeInPixels { get; private set; }
/// <summary>
/// Gets the number of MCU blocks in the image as <see cref="Size"/>.
/// </summary>
public Size ImageSizeInMCU { get; private set; }
/// <summary> /// <summary>
/// Gets the image width /// Gets the image width
/// </summary> /// </summary>
public int ImageWidth { get; private set; } public int ImageWidth => this.ImageSizeInPixels.Width;
/// <summary> /// <summary>
/// Gets the image height /// Gets the image height
/// </summary> /// </summary>
public int ImageHeight { get; private set; } public int ImageHeight => this.ImageSizeInPixels.Height;
/// <summary> /// <summary>
/// Gets the color depth, in number of bits per pixel. /// Gets the color depth, in number of bits per pixel.
@ -128,17 +137,19 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
/// </summary> /// </summary>
public ImageMetaData MetaData { get; private set; } public ImageMetaData MetaData { get; private set; }
/// <inheritdoc/>
public Size ImageSizeInPixels => new Size(this.ImageWidth, this.ImageHeight);
/// <inheritdoc/> /// <inheritdoc/>
public int ComponentCount { get; private set; } public int ComponentCount { get; private set; }
/// <inheritdoc/> /// <inheritdoc/>
public JpegColorSpace ColorSpace { get; private set; } public JpegColorSpace ColorSpace { get; private set; }
/// <summary>
/// Gets the components.
/// </summary>
public PdfJsFrameComponent[] Components => this.Frame.Components;
/// <inheritdoc/> /// <inheritdoc/>
public IEnumerable<IJpegComponent> Components => this.Frame.Components; IEnumerable<IJpegComponent> IRawJpegData.Components => this.Components;
/// <inheritdoc/> /// <inheritdoc/>
public Block8x8F[] QuantizationTables { get; private set; } public Block8x8F[] QuantizationTables { get; private set; }
@ -155,20 +166,20 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
if (value == 0) if (value == 0)
{ {
return new PdfJsFileMarker(PdfJsJpegConstants.Markers.EOI, stream.Length - 2); return new PdfJsFileMarker(JpegConstants.Markers.EOI, stream.Length - 2);
} }
if (marker[0] == PdfJsJpegConstants.Markers.Prefix) if (marker[0] == JpegConstants.Markers.XFF)
{ {
// According to Section B.1.1.2: // According to Section B.1.1.2:
// "Any marker may optionally be preceded by any number of fill bytes, which are bytes assigned code 0xFF." // "Any marker may optionally be preceded by any number of fill bytes, which are bytes assigned code 0xFF."
int m = marker[1]; int m = marker[1];
while (m == PdfJsJpegConstants.Markers.Prefix) while (m == JpegConstants.Markers.XFF)
{ {
int suffix = stream.ReadByte(); int suffix = stream.ReadByte();
if (suffix == -1) if (suffix == -1)
{ {
return new PdfJsFileMarker(PdfJsJpegConstants.Markers.EOI, stream.Length - 2); return new PdfJsFileMarker(JpegConstants.Markers.EOI, stream.Length - 2);
} }
m = suffix; m = suffix;
@ -190,7 +201,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
this.ParseStream(stream); this.ParseStream(stream);
this.AssignResolution(); this.InitDerivedMetaDataProperties();
return this.PostProcessIntoImage<TPixel>(); return this.PostProcessIntoImage<TPixel>();
} }
@ -201,7 +212,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
public IImageInfo Identify(Stream stream) public IImageInfo Identify(Stream stream)
{ {
this.ParseStream(stream, true); this.ParseStream(stream, true);
this.AssignResolution(); this.InitDerivedMetaDataProperties();
return new ImageInfo(new PixelTypeInfo(this.BitsPerPixel), this.ImageWidth, this.ImageHeight, this.MetaData); return new ImageInfo(new PixelTypeInfo(this.BitsPerPixel), this.ImageWidth, this.ImageHeight, this.MetaData);
} }
@ -218,7 +229,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
// Check for the Start Of Image marker. // Check for the Start Of Image marker.
this.InputStream.Read(this.markerBuffer, 0, 2); this.InputStream.Read(this.markerBuffer, 0, 2);
var fileMarker = new PdfJsFileMarker(this.markerBuffer[1], 0); var fileMarker = new PdfJsFileMarker(this.markerBuffer[1], 0);
if (fileMarker.Marker != PdfJsJpegConstants.Markers.SOI) if (fileMarker.Marker != JpegConstants.Markers.SOI)
{ {
throw new ImageFormatException("Missing SOI marker."); throw new ImageFormatException("Missing SOI marker.");
} }
@ -236,7 +247,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
this.fastACTables = new FastACTables(this.configuration.MemoryManager); this.fastACTables = new FastACTables(this.configuration.MemoryManager);
} }
while (fileMarker.Marker != PdfJsJpegConstants.Markers.EOI) while (fileMarker.Marker != JpegConstants.Markers.EOI)
{ {
if (!fileMarker.Invalid) if (!fileMarker.Invalid)
{ {
@ -245,13 +256,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
switch (fileMarker.Marker) switch (fileMarker.Marker)
{ {
case PdfJsJpegConstants.Markers.SOF0: case JpegConstants.Markers.SOF0:
case PdfJsJpegConstants.Markers.SOF1: case JpegConstants.Markers.SOF1:
case PdfJsJpegConstants.Markers.SOF2: case JpegConstants.Markers.SOF2:
this.ProcessStartOfFrameMarker(remaining, fileMarker, metadataOnly); this.ProcessStartOfFrameMarker(remaining, fileMarker, metadataOnly);
break; break;
case PdfJsJpegConstants.Markers.SOS: case JpegConstants.Markers.SOS:
if (!metadataOnly) if (!metadataOnly)
{ {
this.ProcessStartOfScanMarker(); this.ProcessStartOfScanMarker();
@ -264,7 +275,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
return; return;
} }
case PdfJsJpegConstants.Markers.DHT: case JpegConstants.Markers.DHT:
if (metadataOnly) if (metadataOnly)
{ {
this.InputStream.Skip(remaining); this.InputStream.Skip(remaining);
@ -276,7 +287,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
break; break;
case PdfJsJpegConstants.Markers.DQT: case JpegConstants.Markers.DQT:
if (metadataOnly) if (metadataOnly)
{ {
this.InputStream.Skip(remaining); this.InputStream.Skip(remaining);
@ -288,7 +299,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
break; break;
case PdfJsJpegConstants.Markers.DRI: case JpegConstants.Markers.DRI:
if (metadataOnly) if (metadataOnly)
{ {
this.InputStream.Skip(remaining); this.InputStream.Skip(remaining);
@ -300,38 +311,38 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
break; break;
case PdfJsJpegConstants.Markers.APP0: case JpegConstants.Markers.APP0:
this.ProcessApplicationHeaderMarker(remaining); this.ProcessApplicationHeaderMarker(remaining);
break; break;
case PdfJsJpegConstants.Markers.APP1: case JpegConstants.Markers.APP1:
this.ProcessApp1Marker(remaining); this.ProcessApp1Marker(remaining);
break; break;
case PdfJsJpegConstants.Markers.APP2: case JpegConstants.Markers.APP2:
this.ProcessApp2Marker(remaining); this.ProcessApp2Marker(remaining);
break; break;
case PdfJsJpegConstants.Markers.APP3: case JpegConstants.Markers.APP3:
case PdfJsJpegConstants.Markers.APP4: case JpegConstants.Markers.APP4:
case PdfJsJpegConstants.Markers.APP5: case JpegConstants.Markers.APP5:
case PdfJsJpegConstants.Markers.APP6: case JpegConstants.Markers.APP6:
case PdfJsJpegConstants.Markers.APP7: case JpegConstants.Markers.APP7:
case PdfJsJpegConstants.Markers.APP8: case JpegConstants.Markers.APP8:
case PdfJsJpegConstants.Markers.APP9: case JpegConstants.Markers.APP9:
case PdfJsJpegConstants.Markers.APP10: case JpegConstants.Markers.APP10:
case PdfJsJpegConstants.Markers.APP11: case JpegConstants.Markers.APP11:
case PdfJsJpegConstants.Markers.APP12: case JpegConstants.Markers.APP12:
case PdfJsJpegConstants.Markers.APP13: case JpegConstants.Markers.APP13:
this.InputStream.Skip(remaining); this.InputStream.Skip(remaining);
break; break;
case PdfJsJpegConstants.Markers.APP14: case JpegConstants.Markers.APP14:
this.ProcessApp14Marker(remaining); this.ProcessApp14Marker(remaining);
break; break;
case PdfJsJpegConstants.Markers.APP15: case JpegConstants.Markers.APP15:
case PdfJsJpegConstants.Markers.COM: case JpegConstants.Markers.COM:
this.InputStream.Skip(remaining); this.InputStream.Skip(remaining);
break; break;
} }
@ -370,11 +381,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
if (this.ComponentCount == 3) if (this.ComponentCount == 3)
{ {
if (this.adobe.Equals(default) || this.adobe.ColorTransform == PdfJsJpegConstants.Markers.Adobe.ColorTransformYCbCr) if (this.adobe.Equals(default) || this.adobe.ColorTransform == JpegConstants.Adobe.ColorTransformYCbCr)
{ {
return JpegColorSpace.YCbCr; return JpegColorSpace.YCbCr;
} }
else if (this.adobe.ColorTransform == PdfJsJpegConstants.Markers.Adobe.ColorTransformUnknown)
if (this.adobe.ColorTransform == JpegConstants.Adobe.ColorTransformUnknown)
{ {
return JpegColorSpace.RGB; return JpegColorSpace.RGB;
} }
@ -382,7 +394,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
if (this.ComponentCount == 4) if (this.ComponentCount == 4)
{ {
return this.adobe.ColorTransform == PdfJsJpegConstants.Markers.Adobe.ColorTransformYcck return this.adobe.ColorTransform == JpegConstants.Adobe.ColorTransformYcck
? JpegColorSpace.Ycck ? JpegColorSpace.Ycck
: JpegColorSpace.Cmyk; : JpegColorSpace.Cmyk;
} }
@ -391,13 +403,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
} }
/// <summary> /// <summary>
/// Assigns the horizontal and vertical resolution to the image if it has a JFIF header or EXIF metadata. /// Assigns derived metadata properties to <see cref="MetaData"/>, eg. horizontal and vertical resolution if it has a JFIF header.
/// </summary> /// </summary>
private void AssignResolution() private void InitDerivedMetaDataProperties()
{ {
this.ImageWidth = this.Frame.SamplesPerLine;
this.ImageHeight = this.Frame.Scanlines;
if (this.jFif.XDensity > 0 && this.jFif.YDensity > 0) if (this.jFif.XDensity > 0 && this.jFif.YDensity > 0)
{ {
this.MetaData.HorizontalResolution = this.jFif.XDensity; this.MetaData.HorizontalResolution = this.jFif.XDensity;
@ -419,6 +428,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
this.MetaData.VerticalResolution = verticalValue; this.MetaData.VerticalResolution = verticalValue;
} }
} }
if (this.MetaData.IccProfile?.CheckIsValid() == false)
{
this.MetaData.IccProfile = null;
}
} }
/// <summary> /// <summary>
@ -630,59 +644,58 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
this.Frame = new PdfJsFrame this.Frame = new PdfJsFrame
{ {
Extended = frameMarker.Marker == PdfJsJpegConstants.Markers.SOF1, Extended = frameMarker.Marker == JpegConstants.Markers.SOF1,
Progressive = frameMarker.Marker == PdfJsJpegConstants.Markers.SOF2, Progressive = frameMarker.Marker == JpegConstants.Markers.SOF2,
Precision = this.temp[0], Precision = this.temp[0],
Scanlines = (short)((this.temp[1] << 8) | this.temp[2]), Scanlines = (short)((this.temp[1] << 8) | this.temp[2]),
SamplesPerLine = (short)((this.temp[3] << 8) | this.temp[4]), SamplesPerLine = (short)((this.temp[3] << 8) | this.temp[4]),
ComponentCount = this.temp[5] ComponentCount = this.temp[5]
}; };
this.ImageSizeInPixels = new Size(this.Frame.SamplesPerLine, this.Frame.Scanlines);
int maxH = 0; int maxH = 0;
int maxV = 0; int maxV = 0;
int index = 6; int index = 6;
this.ComponentCount = this.Frame.ComponentCount; this.ComponentCount = this.Frame.ComponentCount;
if (!metadataOnly) if (!metadataOnly)
{ {
// No need to pool this. They max out at 4 // No need to pool this. They max out at 4
this.Frame.ComponentIds = new byte[this.Frame.ComponentCount]; this.Frame.ComponentIds = new byte[this.Frame.ComponentCount];
this.Frame.Components = new PdfJsFrameComponent[this.Frame.ComponentCount]; this.Frame.Components = new PdfJsFrameComponent[this.Frame.ComponentCount];
} this.ColorSpace = this.DeduceJpegColorSpace();
for (int i = 0; i < this.Frame.ComponentCount; i++)
{
byte hv = this.temp[index + 1];
int h = hv >> 4;
int v = hv & 15;
if (maxH < h) for (int i = 0; i < this.Frame.ComponentCount; i++)
{ {
maxH = h; byte hv = this.temp[index + 1];
} int h = hv >> 4;
int v = hv & 15;
if (maxV < v) if (maxH < h)
{ {
maxV = v; maxH = h;
} }
if (maxV < v)
{
maxV = v;
}
if (!metadataOnly)
{
var component = new PdfJsFrameComponent(this.configuration.MemoryManager, this.Frame, this.temp[index], h, v, this.temp[index + 2], i); var component = new PdfJsFrameComponent(this.configuration.MemoryManager, this.Frame, this.temp[index], h, v, this.temp[index + 2], i);
this.Frame.Components[i] = component; this.Frame.Components[i] = component;
this.Frame.ComponentIds[i] = component.Id; this.Frame.ComponentIds[i] = component.Id;
}
index += 3; index += 3;
} }
this.Frame.MaxHorizontalFactor = maxH;
this.Frame.MaxVerticalFactor = maxV;
if (!metadataOnly) this.Frame.MaxHorizontalFactor = maxH;
{ this.Frame.MaxVerticalFactor = maxV;
this.ColorSpace = this.DeduceJpegColorSpace();
this.Frame.InitComponents(); this.Frame.InitComponents();
this.ImageSizeInMCU = new Size(this.Frame.McusPerLine, this.Frame.McusPerColumn);
} }
} }
@ -852,7 +865,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
private Image<TPixel> PostProcessIntoImage<TPixel>() private Image<TPixel> PostProcessIntoImage<TPixel>()
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
this.ColorSpace = this.DeduceJpegColorSpace();
using (var postProcessor = new JpegImagePostProcessor(this.configuration.MemoryManager, this)) using (var postProcessor = new JpegImagePostProcessor(this.configuration.MemoryManager, this))
{ {
var image = new Image<TPixel>(this.configuration, this.ImageWidth, this.ImageHeight, this.MetaData); var image = new Image<TPixel>(this.configuration, this.ImageWidth, this.ImageHeight, this.MetaData);

7
src/ImageSharp/Formats/Png/Filters/NoneFilter.cs

@ -3,7 +3,6 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Png.Filters namespace SixLabors.ImageSharp.Formats.Png.Filters
{ {
@ -20,12 +19,12 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
/// <param name="scanline">The scanline to encode</param> /// <param name="scanline">The scanline to encode</param>
/// <param name="result">The filtered scanline result.</param> /// <param name="result">The filtered scanline result.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Encode(Span<byte> scanline, Span<byte> result) public static void Encode(ReadOnlySpan<byte> scanline, Span<byte> result)
{ {
// Insert a byte before the data. // Insert a byte before the data.
result[0] = 0; result[0] = 0;
result = result.Slice(1); result = result.Slice(1);
SpanHelper.Copy(scanline, result); scanline.Slice(0, Math.Min(scanline.Length, result.Length)).CopyTo(result);
} }
} }
} }

16
src/ImageSharp/Formats/Png/PngDecoderCore.cs

@ -853,7 +853,7 @@ namespace SixLabors.ImageSharp.Formats.Png
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
ReadOnlySpan<byte> newScanline = ToArrayByBitsLength(defilteredScanline, this.bytesPerScanline, this.header.BitDepth); ReadOnlySpan<byte> newScanline = ToArrayByBitsLength(defilteredScanline, this.bytesPerScanline, this.header.BitDepth);
byte[] pal = this.palette; ReadOnlySpan<Rgb24> pal = MemoryMarshal.Cast<byte, Rgb24>(this.palette);
var color = default(TPixel); var color = default(TPixel);
var rgba = default(Rgba32); var rgba = default(Rgba32);
@ -865,10 +865,9 @@ namespace SixLabors.ImageSharp.Formats.Png
for (int x = 0; x < this.header.Width; x++) for (int x = 0; x < this.header.Width; x++)
{ {
int index = newScanline[x]; int index = newScanline[x];
int pixelOffset = index * 3;
rgba.A = this.paletteAlpha.Length > index ? this.paletteAlpha[index] : (byte)255; rgba.A = this.paletteAlpha.Length > index ? this.paletteAlpha[index] : (byte)255;
rgba.Rgb = pal.GetRgb24(pixelOffset); rgba.Rgb = pal[index];
color.PackFromRgba32(rgba); color.PackFromRgba32(rgba);
row[x] = color; row[x] = color;
@ -881,9 +880,8 @@ namespace SixLabors.ImageSharp.Formats.Png
for (int x = 0; x < this.header.Width; x++) for (int x = 0; x < this.header.Width; x++)
{ {
int index = newScanline[x]; int index = newScanline[x];
int pixelOffset = index * 3;
rgba.Rgb = pal.GetRgb24(pixelOffset); rgba.Rgb = pal[index];
color.PackFromRgba32(rgba); color.PackFromRgba32(rgba);
row[x] = color; row[x] = color;
@ -946,6 +944,7 @@ namespace SixLabors.ImageSharp.Formats.Png
ReadOnlySpan<byte> newScanline = ToArrayByBitsLength(scanlineBuffer, this.bytesPerScanline, this.header.BitDepth); ReadOnlySpan<byte> newScanline = ToArrayByBitsLength(scanlineBuffer, this.bytesPerScanline, this.header.BitDepth);
var rgba = default(Rgba32); var rgba = default(Rgba32);
Span<Rgb24> pal = MemoryMarshal.Cast<byte, Rgb24>(this.palette);
if (this.paletteAlpha != null && this.paletteAlpha.Length > 0) if (this.paletteAlpha != null && this.paletteAlpha.Length > 0)
{ {
@ -954,10 +953,9 @@ namespace SixLabors.ImageSharp.Formats.Png
for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o++) for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o++)
{ {
int index = newScanline[o]; int index = newScanline[o];
int offset = index * 3;
rgba.A = this.paletteAlpha.Length > index ? this.paletteAlpha[index] : (byte)255; rgba.A = this.paletteAlpha.Length > index ? this.paletteAlpha[index] : (byte)255;
rgba.Rgb = this.palette.GetRgb24(offset); rgba.Rgb = pal[index];
color.PackFromRgba32(rgba); color.PackFromRgba32(rgba);
rowSpan[x] = color; rowSpan[x] = color;
@ -970,10 +968,8 @@ namespace SixLabors.ImageSharp.Formats.Png
for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o++) for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o++)
{ {
int index = newScanline[o]; int index = newScanline[o];
int offset = index * 3;
rgba.Rgb = this.palette.GetRgb24(offset);
rgba.Rgb = pal[index];
color.PackFromRgba32(rgba); color.PackFromRgba32(rgba);
rowSpan[x] = color; rowSpan[x] = color;
} }

12
src/ImageSharp/Image.LoadPixelData.cs

@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp
/// <param name="height">The height of the final image.</param> /// <param name="height">The height of the final image.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A new <see cref="Image{TPixel}"/>.</returns> /// <returns>A new <see cref="Image{TPixel}"/>.</returns>
private static Image<TPixel> LoadPixelData<TPixel>(Span<TPixel> data, int width, int height) public static Image<TPixel> LoadPixelData<TPixel>(ReadOnlySpan<TPixel> data, int width, int height)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
=> LoadPixelData(Configuration.Default, data, width, height); => LoadPixelData(Configuration.Default, data, width, height);
@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp
/// <param name="height">The height of the final image.</param> /// <param name="height">The height of the final image.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A new <see cref="Image{TPixel}"/>.</returns> /// <returns>A new <see cref="Image{TPixel}"/>.</returns>
private static Image<TPixel> LoadPixelData<TPixel>(Span<byte> data, int width, int height) public static Image<TPixel> LoadPixelData<TPixel>(ReadOnlySpan<byte> data, int width, int height)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
=> LoadPixelData<TPixel>(Configuration.Default, data, width, height); => LoadPixelData<TPixel>(Configuration.Default, data, width, height);
@ -72,7 +72,7 @@ namespace SixLabors.ImageSharp
/// <returns>A new <see cref="Image{TPixel}"/>.</returns> /// <returns>A new <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> LoadPixelData<TPixel>(Configuration config, byte[] data, int width, int height) public static Image<TPixel> LoadPixelData<TPixel>(Configuration config, byte[] data, int width, int height)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
=> LoadPixelData(config, MemoryMarshal.Cast<byte, TPixel>(data.AsSpan()), width, height); => LoadPixelData(config, MemoryMarshal.Cast<byte, TPixel>(new ReadOnlySpan<byte>(data)), width, height);
/// <summary> /// <summary>
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the given byte array in <typeparamref name="TPixel"/> format. /// Create a new instance of the <see cref="Image{TPixel}"/> class from the given byte array in <typeparamref name="TPixel"/> format.
@ -83,7 +83,7 @@ namespace SixLabors.ImageSharp
/// <param name="height">The height of the final image.</param> /// <param name="height">The height of the final image.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A new <see cref="Image{TPixel}"/>.</returns> /// <returns>A new <see cref="Image{TPixel}"/>.</returns>
private static Image<TPixel> LoadPixelData<TPixel>(Configuration config, Span<byte> data, int width, int height) public static Image<TPixel> LoadPixelData<TPixel>(Configuration config, ReadOnlySpan<byte> data, int width, int height)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
=> LoadPixelData(config, MemoryMarshal.Cast<byte, TPixel>(data), width, height); => LoadPixelData(config, MemoryMarshal.Cast<byte, TPixel>(data), width, height);
@ -99,7 +99,7 @@ namespace SixLabors.ImageSharp
public static Image<TPixel> LoadPixelData<TPixel>(Configuration config, TPixel[] data, int width, int height) public static Image<TPixel> LoadPixelData<TPixel>(Configuration config, TPixel[] data, int width, int height)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
return LoadPixelData(config, data.AsSpan(), width, height); return LoadPixelData(config, new ReadOnlySpan<TPixel>(data), width, height);
} }
/// <summary> /// <summary>
@ -111,7 +111,7 @@ namespace SixLabors.ImageSharp
/// <param name="height">The height of the final image.</param> /// <param name="height">The height of the final image.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A new <see cref="Image{TPixel}"/>.</returns> /// <returns>A new <see cref="Image{TPixel}"/>.</returns>
private static Image<TPixel> LoadPixelData<TPixel>(Configuration config, Span<TPixel> data, int width, int height) public static Image<TPixel> LoadPixelData<TPixel>(Configuration config, ReadOnlySpan<TPixel> data, int width, int height)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
int count = width * height; int count = width * height;

90
src/ImageSharp/ImageExtensions.cs

@ -4,13 +4,11 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp namespace SixLabors.ImageSharp
{ {
@ -21,18 +19,18 @@ namespace SixLabors.ImageSharp
{ {
#if !NETSTANDARD1_1 #if !NETSTANDARD1_1
/// <summary> /// <summary>
/// Saves the image to the given stream using the currently loaded image format. /// Writes the image to the given stream using the currently loaded image format.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The Pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The source image</param> /// <param name="source">The source image.</param>
/// <param name="filePath">The file path to save the image to.</param> /// <param name="filePath">The file path to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception> /// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static void Save<TPixel>(this Image<TPixel> source, string filePath) public static void Save<TPixel>(this Image<TPixel> source, string filePath)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
Guard.NotNullOrEmpty(filePath, nameof(filePath)); Guard.NotNullOrWhiteSpace(filePath, nameof(filePath));
string ext = Path.GetExtension(filePath).Trim('.'); string ext = Path.GetExtension(filePath);
IImageFormat format = source.GetConfiguration().ImageFormatsManager.FindFormatByFileExtension(ext); IImageFormat format = source.GetConfiguration().ImageFormatsManager.FindFormatByFileExtension(ext);
if (format == null) if (format == null)
{ {
@ -64,13 +62,13 @@ namespace SixLabors.ImageSharp
} }
/// <summary> /// <summary>
/// Saves the image to the given stream using the currently loaded image format. /// Writes the image to the given stream using the currently loaded image format.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The Pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The source image</param> /// <param name="source">The source image.</param>
/// <param name="filePath">The file path to save the image to.</param> /// <param name="filePath">The file path to save the image to.</param>
/// <param name="encoder">The encoder to save the image with.</param> /// <param name="encoder">The encoder to save the image with.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the encoder is null.</exception> /// <exception cref="ArgumentNullException">Thrown if the encoder is null.</exception>
public static void Save<TPixel>(this Image<TPixel> source, string filePath, IImageEncoder encoder) public static void Save<TPixel>(this Image<TPixel> source, string filePath, IImageEncoder encoder)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
@ -83,13 +81,13 @@ namespace SixLabors.ImageSharp
#endif #endif
/// <summary> /// <summary>
/// Saves the image to the given stream using the currently loaded image format. /// Writes the image to the given stream using the currently loaded image format.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The Pixel format.</typeparam> /// <typeparam name="TPixel">The Pixel format.</typeparam>
/// <param name="source">The source image</param> /// <param name="source">The source image.</param>
/// <param name="stream">The stream to save the image to.</param> /// <param name="stream">The stream to save the image to.</param>
/// <param name="format">The format to save the image to.</param> /// <param name="format">The format to save the image in.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception> /// <exception cref="ArgumentNullException">Thrown if the stream is null.</exception>
public static void Save<TPixel>(this Image<TPixel> source, Stream stream, IImageFormat format) public static void Save<TPixel>(this Image<TPixel> source, Stream stream, IImageFormat format)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
@ -113,67 +111,67 @@ namespace SixLabors.ImageSharp
} }
/// <summary> /// <summary>
/// Saves the raw image pixels to a byte array in row-major order. /// Returns the a copy of the image pixels as a byte array in row-major order.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The Pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The source image</param> /// <param name="source">The source image</param>
/// <returns>A copy of the pixel data as bytes from this frame.</returns> /// <returns>A copy of the pixel data as bytes from this frame.</returns>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception> /// <exception cref="ArgumentNullException">Thrown if the stream is null.</exception>
public static byte[] SavePixelData<TPixel>(this ImageFrame<TPixel> source) public static byte[] SavePixelData<TPixel>(this ImageFrame<TPixel> source)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
=> MemoryMarshal.AsBytes(source.GetPixelSpan()).ToArray(); => MemoryMarshal.AsBytes(source.GetPixelSpan()).ToArray();
/// <summary> /// <summary>
/// Saves the raw image pixels to the given byte array in row-major order. /// Writes the raw image pixels to the given byte array in row-major order.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The Pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The source image</param> /// <param name="source">The source image.</param>
/// <param name="buffer">The buffer to save the raw pixel data to.</param> /// <param name="buffer">The buffer to save the raw pixel data to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception> /// <exception cref="ArgumentNullException">Thrown if the stream is null.</exception>
public static void SavePixelData<TPixel>(this ImageFrame<TPixel> source, byte[] buffer) public static void SavePixelData<TPixel>(this ImageFrame<TPixel> source, byte[] buffer)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
=> SavePixelData(source, MemoryMarshal.Cast<byte, TPixel>(buffer.AsSpan())); => SavePixelData(source, MemoryMarshal.Cast<byte, TPixel>(buffer.AsSpan()));
/// <summary> /// <summary>
/// Saves the raw image pixels to the given TPixel array in row-major order. /// Writes the raw image pixels to the given TPixel array in row-major order.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The Pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The source image</param> /// <param name="source">The source image</param>
/// <param name="buffer">The buffer to save the raw pixel data to.</param> /// <param name="buffer">The buffer to save the raw pixel data to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception> /// <exception cref="ArgumentNullException">Thrown if the stream is null.</exception>
public static void SavePixelData<TPixel>(this ImageFrame<TPixel> source, TPixel[] buffer) public static void SavePixelData<TPixel>(this ImageFrame<TPixel> source, TPixel[] buffer)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
=> SavePixelData(source, buffer.AsSpan()); => SavePixelData(source, buffer.AsSpan());
/// <summary> /// <summary>
/// Saves the raw image pixels to a byte array in row-major order. /// Returns a copy of the raw image pixels as a byte array in row-major order.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The Pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The source image</param> /// <param name="source">The source image.</param>
/// <returns>A copy of the pixel data from the first frame as bytes.</returns> /// <returns>A copy of the pixel data from the first frame as bytes.</returns>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception> /// <exception cref="ArgumentNullException">Thrown if the stream is null.</exception>
public static byte[] SavePixelData<TPixel>(this Image<TPixel> source) public static byte[] SavePixelData<TPixel>(this Image<TPixel> source)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
=> source.Frames.RootFrame.SavePixelData(); => source.Frames.RootFrame.SavePixelData();
/// <summary> /// <summary>
/// Saves the raw image pixels to the given byte array in row-major order. /// Writes the raw image pixels to the given byte array in row-major order.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The Pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The source image</param> /// <param name="source">The source image.</param>
/// <param name="buffer">The buffer to save the raw pixel data to.</param> /// <param name="buffer">The buffer to save the raw pixel data to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception> /// <exception cref="ArgumentNullException">Thrown if the stream is null.</exception>
public static void SavePixelData<TPixel>(this Image<TPixel> source, byte[] buffer) public static void SavePixelData<TPixel>(this Image<TPixel> source, byte[] buffer)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
=> source.Frames.RootFrame.SavePixelData(buffer); => source.Frames.RootFrame.SavePixelData(buffer);
/// <summary> /// <summary>
/// Saves the raw image pixels to the given TPixel array in row-major order. /// Writes the raw image pixels to the given TPixel array in row-major order.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The Pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The source image</param> /// <param name="source">The source image</param>
/// <param name="buffer">The buffer to save the raw pixel data to.</param> /// <param name="buffer">The buffer to save the raw pixel data to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception> /// <exception cref="ArgumentNullException">Thrown if the stream is null.</exception>
public static void SavePixelData<TPixel>(this Image<TPixel> source, TPixel[] buffer) public static void SavePixelData<TPixel>(this Image<TPixel> source, TPixel[] buffer)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
=> source.Frames.RootFrame.SavePixelData(buffer); => source.Frames.RootFrame.SavePixelData(buffer);
@ -182,7 +180,7 @@ namespace SixLabors.ImageSharp
/// Returns a Base64 encoded string from the given image. /// Returns a Base64 encoded string from the given image.
/// </summary> /// </summary>
/// <example><see href="data:image/gif;base64,R0lGODlhAQABAIABAEdJRgAAACwAAAAAAQABAAACAkQBAA=="/></example> /// <example><see href="data:image/gif;base64,R0lGODlhAQABAIABAEdJRgAAACwAAAAAAQABAAACAkQBAA=="/></example>
/// <typeparam name="TPixel">The Pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The source image</param> /// <param name="source">The source image</param>
/// <param name="format">The format.</param> /// <param name="format">The format.</param>
/// <returns>The <see cref="string"/></returns> /// <returns>The <see cref="string"/></returns>
@ -198,24 +196,24 @@ namespace SixLabors.ImageSharp
} }
/// <summary> /// <summary>
/// Saves the raw image to the given bytes. /// Writes the raw image bytes to the given byte span.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The Pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The source image</param> /// <param name="source">The source image</param>
/// <param name="buffer">The buffer to save the raw pixel data to.</param> /// <param name="buffer">The span to save the raw pixel data to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception> /// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
internal static void SavePixelData<TPixel>(this Image<TPixel> source, Span<byte> buffer) public static void SavePixelData<TPixel>(this Image<TPixel> source, Span<byte> buffer)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
=> source.Frames.RootFrame.SavePixelData(MemoryMarshal.Cast<byte, TPixel>(buffer)); => source.Frames.RootFrame.SavePixelData(MemoryMarshal.Cast<byte, TPixel>(buffer));
/// <summary> /// <summary>
/// Saves the raw image to the given bytes. /// Writes the raw image pixels to the given TPixel span.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The Pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The source image</param> /// <param name="source">The source image</param>
/// <param name="buffer">The buffer to save the raw pixel data to.</param> /// <param name="buffer">The span to save the raw pixel data to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception> /// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
internal static void SavePixelData<TPixel>(this ImageFrame<TPixel> source, Span<TPixel> buffer) public static void SavePixelData<TPixel>(this ImageFrame<TPixel> source, Span<TPixel> buffer)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
Span<TPixel> sourceBuffer = source.GetPixelSpan(); Span<TPixel> sourceBuffer = source.GetPixelSpan();
@ -224,4 +222,4 @@ namespace SixLabors.ImageSharp
sourceBuffer.CopyTo(buffer); sourceBuffer.CopyTo(buffer);
} }
} }
} }

15
src/ImageSharp/ImageFrame.LoadPixelData.cs

@ -4,7 +4,6 @@
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp namespace SixLabors.ImageSharp
@ -12,37 +11,37 @@ namespace SixLabors.ImageSharp
/// <content> /// <content>
/// Adds static methods allowing the creation of new image from raw pixel data. /// Adds static methods allowing the creation of new image from raw pixel data.
/// </content> /// </content>
internal static partial class ImageFrame internal static class ImageFrame
{ {
/// <summary> /// <summary>
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the given byte array in <typeparamref name="TPixel"/> format. /// Create a new instance of the <see cref="Image{TPixel}"/> class from the given byte array in <typeparamref name="TPixel"/> format.
/// </summary> /// </summary>
/// <param name="memoryManager">The memory manager to use for allocations</param> /// <param name="configuration">The configuration which allows altering default behaviour or extending the library.</param>
/// <param name="data">The byte array containing image data.</param> /// <param name="data">The byte array containing image data.</param>
/// <param name="width">The width of the final image.</param> /// <param name="width">The width of the final image.</param>
/// <param name="height">The height of the final image.</param> /// <param name="height">The height of the final image.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A new <see cref="Image{TPixel}"/>.</returns> /// <returns>A new <see cref="Image{TPixel}"/>.</returns>
public static ImageFrame<TPixel> LoadPixelData<TPixel>(MemoryManager memoryManager, Span<byte> data, int width, int height) public static ImageFrame<TPixel> LoadPixelData<TPixel>(Configuration configuration, ReadOnlySpan<byte> data, int width, int height)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
=> LoadPixelData(memoryManager, MemoryMarshal.Cast<byte, TPixel>(data), width, height); => LoadPixelData(configuration, MemoryMarshal.Cast<byte, TPixel>(data), width, height);
/// <summary> /// <summary>
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the raw <typeparamref name="TPixel"/> data. /// Create a new instance of the <see cref="Image{TPixel}"/> class from the raw <typeparamref name="TPixel"/> data.
/// </summary> /// </summary>
/// <param name="memoryManager">The memory manager to use for allocations</param> /// <param name="configuration">The configuration which allows altering default behaviour or extending the library.</param>
/// <param name="data">The Span containing the image Pixel data.</param> /// <param name="data">The Span containing the image Pixel data.</param>
/// <param name="width">The width of the final image.</param> /// <param name="width">The width of the final image.</param>
/// <param name="height">The height of the final image.</param> /// <param name="height">The height of the final image.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A new <see cref="Image{TPixel}"/>.</returns> /// <returns>A new <see cref="Image{TPixel}"/>.</returns>
public static ImageFrame<TPixel> LoadPixelData<TPixel>(MemoryManager memoryManager, Span<TPixel> data, int width, int height) public static ImageFrame<TPixel> LoadPixelData<TPixel>(Configuration configuration, ReadOnlySpan<TPixel> data, int width, int height)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
int count = width * height; int count = width * height;
Guard.MustBeGreaterThanOrEqualTo(data.Length, count, nameof(data)); Guard.MustBeGreaterThanOrEqualTo(data.Length, count, nameof(data));
var image = new ImageFrame<TPixel>(memoryManager, width, height); var image = new ImageFrame<TPixel>(configuration, width, height);
data.Slice(0, count).CopyTo(image.GetPixelSpan()); data.Slice(0, count).CopyTo(image.GetPixelSpan());

12
src/ImageSharp/ImageFrameCollection.cs

@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp
internal ImageFrameCollection(Image<TPixel> parent, IEnumerable<ImageFrame<TPixel>> frames) internal ImageFrameCollection(Image<TPixel> parent, IEnumerable<ImageFrame<TPixel>> frames)
{ {
Guard.NotNull(parent, nameof(parent)); Guard.NotNull(parent, nameof(parent));
Guard.NotNullOrEmpty(frames, nameof(frames)); Guard.NotNull(frames, nameof(frames));
this.parent = parent; this.parent = parent;
@ -42,6 +42,12 @@ namespace SixLabors.ImageSharp
this.ValidateFrame(f); this.ValidateFrame(f);
this.frames.Add(f); this.frames.Add(f);
} }
// Ensure at least 1 frame was added to the frames collection
if (this.frames.Count == 0)
{
throw new ArgumentException("Must not be empty.", nameof(frames));
}
} }
/// <summary> /// <summary>
@ -110,8 +116,8 @@ namespace SixLabors.ImageSharp
Guard.NotNull(source, nameof(source)); Guard.NotNull(source, nameof(source));
var frame = ImageFrame.LoadPixelData( var frame = ImageFrame.LoadPixelData(
this.parent.GetMemoryManager(), this.parent.GetConfiguration(),
new Span<TPixel>(source), new ReadOnlySpan<TPixel>(source),
this.RootFrame.Width, this.RootFrame.Width,
this.RootFrame.Height); this.RootFrame.Height);
this.frames.Add(frame); this.frames.Add(frame);

80
src/ImageSharp/ImageFrame{TPixel}.cs

@ -4,7 +4,6 @@
using System; using System;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading.Tasks; using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
@ -19,53 +18,61 @@ namespace SixLabors.ImageSharp
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
public sealed class ImageFrame<TPixel> : IPixelSource<TPixel>, IDisposable public sealed class ImageFrame<TPixel> : IPixelSource<TPixel>, IDisposable
where TPixel : struct, IPixel<TPixel> { where TPixel : struct, IPixel<TPixel>
{
private readonly Configuration configuration;
private bool isDisposed; private bool isDisposed;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ImageFrame{TPixel}" /> class. /// Initializes a new instance of the <see cref="ImageFrame{TPixel}" /> class.
/// </summary> /// </summary>
/// <param name="memoryManager">The <see cref="MemoryManager"/> to use for buffer allocations.</param> /// <param name="configuration">The configuration which allows altering default behaviour or extending the library.</param>
/// <param name="width">The width of the image in pixels.</param> /// <param name="width">The width of the image in pixels.</param>
/// <param name="height">The height of the image in pixels.</param> /// <param name="height">The height of the image in pixels.</param>
internal ImageFrame(MemoryManager memoryManager, int width, int height) internal ImageFrame(Configuration configuration, int width, int height)
: this(memoryManager, width, height, new ImageFrameMetaData()) { : this(configuration, width, height, new ImageFrameMetaData())
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ImageFrame{TPixel}" /> class.
/// </summary>
/// <param name="configuration">The configuration which allows altering default behaviour or extending the library.</param>
/// <param name="size">The <see cref="Size"/> of the frame.</param>
/// <param name="metaData">The meta data.</param>
internal ImageFrame(Configuration configuration, Size size, ImageFrameMetaData metaData)
: this(configuration, size.Width, size.Height, metaData)
{
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ImageFrame{TPixel}" /> class. /// Initializes a new instance of the <see cref="ImageFrame{TPixel}" /> class.
/// </summary> /// </summary>
/// <param name="memoryManager">The <see cref="MemoryManager"/> to use for buffer allocations.</param> /// <param name="configuration">The configuration which allows altering default behaviour or extending the library.</param>
/// <param name="width">The width of the image in pixels.</param> /// <param name="width">The width of the image in pixels.</param>
/// <param name="height">The height of the image in pixels.</param> /// <param name="height">The height of the image in pixels.</param>
/// <param name="metaData">The meta data.</param> /// <param name="metaData">The meta data.</param>
internal ImageFrame(MemoryManager memoryManager, int width, int height, ImageFrameMetaData metaData) internal ImageFrame(Configuration configuration, int width, int height, ImageFrameMetaData metaData)
: this(configuration, width, height, default, metaData)
{ {
Guard.NotNull(memoryManager, nameof(memoryManager));
Guard.MustBeGreaterThan(width, 0, nameof(width));
Guard.MustBeGreaterThan(height, 0, nameof(height));
Guard.NotNull(metaData, nameof(metaData));
this.MemoryManager = memoryManager;
this.PixelBuffer = memoryManager.AllocateClean2D<TPixel>(width, height);
this.MetaData = metaData;
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ImageFrame{TPixel}" /> class. /// Initializes a new instance of the <see cref="ImageFrame{TPixel}" /> class.
/// </summary> /// </summary>
/// <param name="configuration">The <see cref="Configuration"/> to use for buffer allocation and parallel options to clear the buffer with.</param> /// <param name="configuration">The configuration which allows altering default behaviour or extending the library.</param>
/// <param name="width">The width of the image in pixels.</param> /// <param name="width">The width of the image in pixels.</param>
/// <param name="height">The height of the image in pixels.</param> /// <param name="height">The height of the image in pixels.</param>
/// <param name="backgroundColor">The color to clear the image with.</param> /// <param name="backgroundColor">The color to clear the image with.</param>
internal ImageFrame(Configuration configuration, int width, int height, TPixel backgroundColor) internal ImageFrame(Configuration configuration, int width, int height, TPixel backgroundColor)
: this(configuration, width, height, backgroundColor, new ImageFrameMetaData()) { : this(configuration, width, height, backgroundColor, new ImageFrameMetaData())
{
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ImageFrame{TPixel}" /> class. /// Initializes a new instance of the <see cref="ImageFrame{TPixel}" /> class.
/// </summary> /// </summary>
/// <param name="configuration">The <see cref="Configuration"/> to use for buffer allocation and parallel options to clear the buffer with.</param> /// <param name="configuration">The configuration which allows altering default behaviour or extending the library.</param>
/// <param name="width">The width of the image in pixels.</param> /// <param name="width">The width of the image in pixels.</param>
/// <param name="height">The height of the image in pixels.</param> /// <param name="height">The height of the image in pixels.</param>
/// <param name="backgroundColor">The color to clear the image with.</param> /// <param name="backgroundColor">The color to clear the image with.</param>
@ -77,31 +84,26 @@ namespace SixLabors.ImageSharp
Guard.MustBeGreaterThan(height, 0, nameof(height)); Guard.MustBeGreaterThan(height, 0, nameof(height));
Guard.NotNull(metaData, nameof(metaData)); Guard.NotNull(metaData, nameof(metaData));
this.configuration = configuration;
this.MemoryManager = configuration.MemoryManager; this.MemoryManager = configuration.MemoryManager;
this.PixelBuffer = this.MemoryManager.Allocate2D<TPixel>(width, height, false); this.PixelBuffer = this.MemoryManager.Allocate2D<TPixel>(width, height, false);
this.Clear(configuration.ParallelOptions, backgroundColor);
this.MetaData = metaData; this.MetaData = metaData;
this.Clear(configuration.ParallelOptions, backgroundColor);
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ImageFrame{TPixel}" /> class. /// Initializes a new instance of the <see cref="ImageFrame{TPixel}" /> class.
/// </summary> /// </summary>
/// <param name="memoryManager">The <see cref="MemoryManager"/> to use for buffer allocations.</param> /// <param name="configuration">The configuration which allows altering default behaviour or extending the library.</param>
/// <param name="size">The <see cref="Size"/> of the frame.</param>
/// <param name="metaData">The meta data.</param>
internal ImageFrame(MemoryManager memoryManager, Size size, ImageFrameMetaData metaData)
: this(memoryManager, size.Width, size.Height, metaData) {
}
/// <summary>
/// Initializes a new instance of the <see cref="ImageFrame{TPixel}" /> class.
/// </summary>
/// <param name="memoryManager">The <see cref="MemoryManager"/> to use for buffer allocations.</param>
/// <param name="source">The source.</param> /// <param name="source">The source.</param>
internal ImageFrame(MemoryManager memoryManager, ImageFrame<TPixel> source) internal ImageFrame(Configuration configuration, ImageFrame<TPixel> source)
{ {
this.MemoryManager = memoryManager; Guard.NotNull(configuration, nameof(configuration));
this.PixelBuffer = memoryManager.Allocate2D<TPixel>(source.PixelBuffer.Width, source.PixelBuffer.Height); Guard.NotNull(source, nameof(source));
this.configuration = configuration;
this.MemoryManager = configuration.MemoryManager;
this.PixelBuffer = this.MemoryManager.Allocate2D<TPixel>(source.PixelBuffer.Width, source.PixelBuffer.Height);
source.PixelBuffer.Span.CopyTo(this.PixelBuffer.Span); source.PixelBuffer.Span.CopyTo(this.PixelBuffer.Span);
this.MetaData = source.MetaData.Clone(); this.MetaData = source.MetaData.Clone();
} }
@ -276,13 +278,12 @@ namespace SixLabors.ImageSharp
return this.Clone() as ImageFrame<TPixel2>; return this.Clone() as ImageFrame<TPixel2>;
} }
var target = new ImageFrame<TPixel2>(this.MemoryManager, this.Width, this.Height, this.MetaData.Clone()); var target = new ImageFrame<TPixel2>(this.configuration, this.Width, this.Height, this.MetaData.Clone());
// TODO: ImageFrame has no visibility of the current configuration. It should have.
ParallelFor.WithTemporaryBuffer( ParallelFor.WithTemporaryBuffer(
0, 0,
this.Height, this.Height,
Configuration.Default, this.configuration,
this.Width, this.Width,
(int y, IBuffer<Vector4> tempRowBuffer) => (int y, IBuffer<Vector4> tempRowBuffer) =>
{ {
@ -302,12 +303,13 @@ namespace SixLabors.ImageSharp
/// </summary> /// </summary>
/// <param name="parallelOptions">The parallel options.</param> /// <param name="parallelOptions">The parallel options.</param>
/// <param name="value">The value to initialize the bitmap with.</param> /// <param name="value">The value to initialize the bitmap with.</param>
internal void Clear(ParallelOptions parallelOptions, TPixel value) { internal void Clear(ParallelOptions parallelOptions, TPixel value)
{
Parallel.For( Parallel.For(
0, 0,
this.Height, this.Height,
parallelOptions, parallelOptions,
(int y) => y =>
{ {
Span<TPixel> targetRow = this.GetPixelRowSpan(y); Span<TPixel> targetRow = this.GetPixelRowSpan(y);
targetRow.Fill(value); targetRow.Fill(value);
@ -320,7 +322,7 @@ namespace SixLabors.ImageSharp
/// <returns>The <see cref="ImageFrame{TPixel}"/></returns> /// <returns>The <see cref="ImageFrame{TPixel}"/></returns>
internal ImageFrame<TPixel> Clone() internal ImageFrame<TPixel> Clone()
{ {
return new ImageFrame<TPixel>(this.MemoryManager, this); return new ImageFrame<TPixel>(this.configuration, this);
} }
/// <inheritdoc/> /// <inheritdoc/>

21
src/ImageSharp/ImageSharp.csproj

@ -37,17 +37,17 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="SixLabors.Core" Version="1.0.0-beta0005" /> <PackageReference Include="SixLabors.Core" Version="1.0.0-beta0005" />
<AdditionalFiles Include="..\..\stylecop.json" /> <AdditionalFiles Include="..\..\stylecop.json" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.0-beta006"> <PackageReference Include="StyleCop.Analyzers" Version="1.1.0-beta007">
<PrivateAssets>All</PrivateAssets> <PrivateAssets>All</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="System.Buffers" Version="4.4.0" /> <PackageReference Include="System.Buffers" Version="4.5.0" />
<PackageReference Include="System.Memory" Version="4.5.0-rc1" /> <PackageReference Include="System.Memory" Version="4.5.0" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.5.0-rc1" /> <PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.5.0" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.1' OR '$(TargetFramework)' == 'netstandard1.3'"> <ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.1' OR '$(TargetFramework)' == 'netstandard1.3'">
<PackageReference Include="System.IO.Compression" Version="4.3.0" /> <PackageReference Include="System.IO.Compression" Version="4.3.0" />
<PackageReference Include="System.Threading.Tasks.Parallel" Version="4.3.0" /> <PackageReference Include="System.Threading.Tasks.Parallel" Version="4.3.0" />
<PackageReference Include="System.ValueTuple" Version="4.4.0" /> <PackageReference Include="System.ValueTuple" Version="4.5.0" />
</ItemGroup> </ItemGroup>
<PropertyGroup> <PropertyGroup>
<CodeAnalysisRuleSet>..\..\ImageSharp.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>..\..\ImageSharp.ruleset</CodeAnalysisRuleSet>
@ -57,11 +57,11 @@
<TreatWarningsAsErrors>true</TreatWarningsAsErrors> <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<None Update="Formats\Jpeg\Common\Block8x8F.Generated.tt"> <None Update="Formats\Jpeg\Components\Block8x8F.Generated.tt">
<Generator>TextTemplatingFileGenerator</Generator> <Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>Block8x8F.Generated.cs</LastGenOutput> <LastGenOutput>Block8x8F.Generated.cs</LastGenOutput>
</None> </None>
<None Update="Formats\Jpeg\Common\GenericBlock8x8.Generated.tt"> <None Update="Formats\Jpeg\Components\GenericBlock8x8.Generated.tt">
<Generator>TextTemplatingFileGenerator</Generator> <Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>GenericBlock8x8.Generated.cs</LastGenOutput> <LastGenOutput>GenericBlock8x8.Generated.cs</LastGenOutput>
</None> </None>
@ -87,12 +87,12 @@
</None> </None>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Update="Formats\Jpeg\Common\Block8x8F.Generated.cs"> <Compile Update="Formats\Jpeg\Components\Block8x8F.Generated.cs">
<DesignTime>True</DesignTime> <DesignTime>True</DesignTime>
<AutoGen>True</AutoGen> <AutoGen>True</AutoGen>
<DependentUpon>Block8x8F.Generated.tt</DependentUpon> <DependentUpon>Block8x8F.Generated.tt</DependentUpon>
</Compile> </Compile>
<Compile Update="Formats\Jpeg\Common\GenericBlock8x8.Generated.cs"> <Compile Update="Formats\Jpeg\Components\GenericBlock8x8.Generated.cs">
<DesignTime>True</DesignTime> <DesignTime>True</DesignTime>
<AutoGen>True</AutoGen> <AutoGen>True</AutoGen>
<DependentUpon>GenericBlock8x8.Generated.tt</DependentUpon> <DependentUpon>GenericBlock8x8.Generated.tt</DependentUpon>
@ -123,4 +123,7 @@
<DependentUpon>PorterDuffFunctions.Generated.tt</DependentUpon> <DependentUpon>PorterDuffFunctions.Generated.tt</DependentUpon>
</Compile> </Compile>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Service Include="{508349b6-6b84-4df5-91f0-309beebad82d}" />
</ItemGroup>
</Project> </Project>

2
src/ImageSharp/Image{TPixel}.cs

@ -78,7 +78,7 @@ namespace SixLabors.ImageSharp
this.configuration = configuration ?? Configuration.Default; this.configuration = configuration ?? Configuration.Default;
this.PixelType = new PixelTypeInfo(Unsafe.SizeOf<TPixel>() * 8); this.PixelType = new PixelTypeInfo(Unsafe.SizeOf<TPixel>() * 8);
this.MetaData = metadata ?? new ImageMetaData(); this.MetaData = metadata ?? new ImageMetaData();
this.frames = new ImageFrameCollection<TPixel>(this, width, height, default(TPixel)); this.frames = new ImageFrameCollection<TPixel>(this, width, height, default);
} }
/// <summary> /// <summary>

48
src/ImageSharp/Memory/SpanHelper.cs

@ -1,48 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Memory
{
/// <summary>
/// Utility methods for <see cref="Span{T}"/>
/// </summary>
internal static class SpanHelper
{
/// <summary>
/// Copy all elements of 'source' into 'destination'.
/// </summary>
/// <typeparam name="T">The element type.</typeparam>
/// <param name="source">The <see cref="Span{T}"/> to copy elements from.</param>
/// <param name="destination">The destination <see cref="Span{T}"/>.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Copy<T>(ReadOnlySpan<T> source, Span<T> destination)
where T : struct
{
source.Slice(0, Math.Min(source.Length, destination.Length)).CopyTo(destination);
}
/// <summary>
/// Gets the size of `count` elements in bytes.
/// </summary>
/// <typeparam name="T">The element type.</typeparam>
/// <param name="count">The count of the elements</param>
/// <returns>The size in bytes as int</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int SizeOf<T>(int count)
where T : struct => Unsafe.SizeOf<T>() * count;
/// <summary>
/// Gets the size of `count` elements in bytes as UInt32
/// </summary>
/// <typeparam name="T">The element type.</typeparam>
/// <param name="count">The count of the elements</param>
/// <returns>The size in bytes as UInt32</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint USizeOf<T>(int count)
where T : struct
=> (uint)SizeOf<T>(count);
}
}

2
src/ImageSharp/MetaData/ImageProperty.cs

@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.MetaData
/// <param name="value">The value of the property.</param> /// <param name="value">The value of the property.</param>
public ImageProperty(string name, string value) public ImageProperty(string name, string value)
{ {
Guard.NotNullOrEmpty(name, nameof(name)); Guard.NotNullOrWhiteSpace(name, nameof(name));
this.Name = name; this.Name = name;
this.Value = value; this.Value = value;

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

@ -387,7 +387,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
value = this.ConvertValue(dataType, offsetBuffer, numberOfComponents); value = this.ConvertValue(dataType, offsetBuffer, numberOfComponents);
} }
exifValue = new ExifValue(tag, dataType, value, isArray: value != null && numberOfComponents > 1); exifValue = new ExifValue(tag, dataType, value, isArray: value != null && numberOfComponents != 1);
return true; return true;
} }

10
src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System;
using System.Text; using System.Text;
namespace SixLabors.ImageSharp.MetaData.Profiles.Icc namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
@ -11,7 +10,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// </summary> /// </summary>
internal sealed partial class IccDataReader internal sealed partial class IccDataReader
{ {
private static readonly bool IsLittleEndian = BitConverter.IsLittleEndian;
private static readonly Encoding AsciiEncoding = Encoding.GetEncoding("ASCII"); private static readonly Encoding AsciiEncoding = Encoding.GetEncoding("ASCII");
/// <summary> /// <summary>
@ -34,6 +32,14 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
this.data = data; this.data = data;
} }
/// <summary>
/// Gets the length in bytes of the raw data
/// </summary>
public int DataLength
{
get { return this.data.Length; }
}
/// <summary> /// <summary>
/// Sets the reading position to the given value /// Sets the reading position to the given value
/// </summary> /// </summary>

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

@ -52,17 +52,12 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// by making a copy from another ICC profile. /// by making a copy from another ICC profile.
/// </summary> /// </summary>
/// <param name="other">The other ICC profile, where the clone should be made from.</param> /// <param name="other">The other ICC profile, where the clone should be made from.</param>
/// <exception cref="System.ArgumentNullException"><paramref name="other"/> is null.</exception>> /// <exception cref="ArgumentNullException"><paramref name="other"/> is null.</exception>>
public IccProfile(IccProfile other) public IccProfile(IccProfile other)
{ {
Guard.NotNull(other, nameof(other)); Guard.NotNull(other, nameof(other));
// TODO: Do we need to copy anything else? this.data = other.ToByteArray();
if (other.data != null)
{
this.data = new byte[other.data.Length];
Buffer.BlockCopy(other.data, 0, this.data, 0, other.data.Length);
}
} }
/// <summary> /// <summary>
@ -108,7 +103,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
#if !NETSTANDARD1_1 #if !NETSTANDARD1_1
/// <summary> /// <summary>
/// Calculates the MD5 hash value of an ICC profile header /// Calculates the MD5 hash value of an ICC profile
/// </summary> /// </summary>
/// <param name="data">The data of which to calculate the hash value</param> /// <param name="data">The data of which to calculate the hash value</param>
/// <returns>The calculated hash</returns> /// <returns>The calculated hash</returns>
@ -117,22 +112,38 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
Guard.NotNull(data, nameof(data)); Guard.NotNull(data, nameof(data));
Guard.IsTrue(data.Length >= 128, nameof(data), "Data length must be at least 128 to be a valid profile header"); Guard.IsTrue(data.Length >= 128, nameof(data), "Data length must be at least 128 to be a valid profile header");
byte[] header = new byte[128]; const int profileFlagPos = 44;
Buffer.BlockCopy(data, 0, header, 0, 128); const int renderingIntentPos = 64;
const int profileIdPos = 84;
// need to copy some values because they need to be zero for the hashing
byte[] temp = new byte[24];
Buffer.BlockCopy(data, profileFlagPos, temp, 0, 4);
Buffer.BlockCopy(data, renderingIntentPos, temp, 4, 4);
Buffer.BlockCopy(data, profileIdPos, temp, 8, 16);
using (var md5 = MD5.Create()) using (var md5 = MD5.Create())
{ {
// Zero out some values try
Array.Clear(header, 44, 4); // Profile flags {
Array.Clear(header, 64, 4); // Rendering Intent // Zero out some values
Array.Clear(header, 84, 16); // Profile ID Array.Clear(data, profileFlagPos, 4);
Array.Clear(data, renderingIntentPos, 4);
// Calculate hash Array.Clear(data, profileIdPos, 16);
byte[] hash = md5.ComputeHash(data);
// Calculate hash
// Read values from hash byte[] hash = md5.ComputeHash(data);
var reader = new IccDataReader(hash);
return reader.ReadProfileId(); // Read values from hash
var reader = new IccDataReader(hash);
return reader.ReadProfileId();
}
finally
{
Buffer.BlockCopy(temp, 0, data, profileFlagPos, 4);
Buffer.BlockCopy(temp, 4, data, renderingIntentPos, 4);
Buffer.BlockCopy(temp, 8, data, profileIdPos, 16);
}
} }
} }
@ -149,14 +160,37 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
Buffer.BlockCopy(bytes, 0, this.data, currentLength, bytes.Length); Buffer.BlockCopy(bytes, 0, this.data, currentLength, bytes.Length);
} }
/// <summary>
/// Checks for signs of a corrupt profile.
/// </summary>
/// <remarks>This is not an absolute proof of validity but should weed out most corrupt data.</remarks>
/// <returns>True if the profile is valid; False otherwise</returns>
public bool CheckIsValid()
{
return Enum.IsDefined(typeof(IccColorSpaceType), this.Header.DataColorSpace) &&
Enum.IsDefined(typeof(IccColorSpaceType), this.Header.ProfileConnectionSpace) &&
Enum.IsDefined(typeof(IccRenderingIntent), this.Header.RenderingIntent) &&
this.Header.Size >= 128 &&
this.Header.Size < 50_000_000; // it's unlikely there is a profile bigger than 50MB
}
/// <summary> /// <summary>
/// Converts this instance to a byte array. /// Converts this instance to a byte array.
/// </summary> /// </summary>
/// <returns>The <see cref="T:byte[]"/></returns> /// <returns>The <see cref="T:byte[]"/></returns>
public byte[] ToByteArray() public byte[] ToByteArray()
{ {
var writer = new IccWriter(); if (this.data != null)
return writer.Write(this); {
byte[] copy = new byte[this.data.Length];
Buffer.BlockCopy(this.data, 0, copy, 0, copy.Length);
return copy;
}
else
{
var writer = new IccWriter();
return writer.Write(this);
}
} }
private void InitializeHeader() private void InitializeHeader()

47
src/ImageSharp/MetaData/Profiles/ICC/IccReader.cs

@ -84,27 +84,36 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
private IccTagDataEntry[] ReadTagData(IccDataReader reader) private IccTagDataEntry[] ReadTagData(IccDataReader reader)
{ {
IccTagTableEntry[] tagTable = this.ReadTagTable(reader); IccTagTableEntry[] tagTable = this.ReadTagTable(reader);
var entries = new IccTagDataEntry[tagTable.Length]; var entries = new List<IccTagDataEntry>(tagTable.Length);
var store = new Dictionary<uint, IccTagDataEntry>(); var store = new Dictionary<uint, IccTagDataEntry>();
for (int i = 0; i < tagTable.Length; i++)
foreach (IccTagTableEntry tag in tagTable)
{ {
IccTagDataEntry entry; IccTagDataEntry entry;
uint offset = tagTable[i].Offset; if (store.ContainsKey(tag.Offset))
if (store.ContainsKey(offset))
{ {
entry = store[offset]; entry = store[tag.Offset];
} }
else else
{ {
entry = reader.ReadTagDataEntry(tagTable[i]); try
store.Add(offset, entry); {
entry = reader.ReadTagDataEntry(tag);
}
catch
{
// Ignore tags that could not be read
continue;
}
store.Add(tag.Offset, entry);
} }
entry.TagSignature = tagTable[i].Signature; entry.TagSignature = tag.Signature;
entries[i] = entry; entries.Add(entry);
} }
return entries; return entries.ToArray();
} }
private IccTagTableEntry[] ReadTagTable(IccDataReader reader) private IccTagTableEntry[] ReadTagTable(IccDataReader reader)
@ -112,17 +121,29 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
reader.SetIndex(128); // An ICC header is 128 bytes long reader.SetIndex(128); // An ICC header is 128 bytes long
uint tagCount = reader.ReadUInt32(); uint tagCount = reader.ReadUInt32();
var table = new IccTagTableEntry[tagCount];
// Prevent creating huge arrays because of corrupt profiles.
// A normal profile usually has 5-15 entries
if (tagCount > 100)
{
return new IccTagTableEntry[0];
}
var table = new List<IccTagTableEntry>((int)tagCount);
for (int i = 0; i < tagCount; i++) for (int i = 0; i < tagCount; i++)
{ {
uint tagSignature = reader.ReadUInt32(); uint tagSignature = reader.ReadUInt32();
uint tagOffset = reader.ReadUInt32(); uint tagOffset = reader.ReadUInt32();
uint tagSize = reader.ReadUInt32(); uint tagSize = reader.ReadUInt32();
table[i] = new IccTagTableEntry((IccProfileTag)tagSignature, tagOffset, tagSize);
// Exclude entries that have nonsense values and could cause exceptions further on
if (tagOffset < reader.DataLength && tagSize < reader.DataLength - 128)
{
table.Add(new IccTagTableEntry((IccProfileTag)tagSignature, tagOffset, tagSize));
}
} }
return table; return table.ToArray();
} }
} }
} }

5
src/ImageSharp/PixelFormats/Bgr24.cs

@ -14,22 +14,25 @@ namespace SixLabors.ImageSharp.PixelFormats
/// Ranges from [0, 0, 0, 1] to [1, 1, 1, 1] in vector form. /// Ranges from [0, 0, 0, 1] to [1, 1, 1, 1] in vector form.
/// </para> /// </para>
/// </summary> /// </summary>
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Explicit)]
public struct Bgr24 : IPixel<Bgr24> public struct Bgr24 : IPixel<Bgr24>
{ {
/// <summary> /// <summary>
/// The blue component. /// The blue component.
/// </summary> /// </summary>
[FieldOffset(0)]
public byte B; public byte B;
/// <summary> /// <summary>
/// The green component. /// The green component.
/// </summary> /// </summary>
[FieldOffset(1)]
public byte G; public byte G;
/// <summary> /// <summary>
/// The red component. /// The red component.
/// </summary> /// </summary>
[FieldOffset(2)]
public byte R; public byte R;
/// <summary> /// <summary>

28
src/ImageSharp/PixelFormats/ColorBuilder{TPixel}.cs

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Buffers.Binary;
using System.Globalization; using System.Globalization;
namespace SixLabors.ImageSharp.PixelFormats namespace SixLabors.ImageSharp.PixelFormats
@ -23,21 +24,17 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <returns>Returns a <typeparamref name="TPixel"/> that represents the color defined by the provided RGBA heax string.</returns> /// <returns>Returns a <typeparamref name="TPixel"/> that represents the color defined by the provided RGBA heax string.</returns>
public static TPixel FromHex(string hex) public static TPixel FromHex(string hex)
{ {
Guard.NotNullOrEmpty(hex, nameof(hex)); Guard.NotNullOrWhiteSpace(hex, nameof(hex));
hex = ToRgbaHex(hex); hex = ToRgbaHex(hex);
uint packedValue;
if (hex == null || !uint.TryParse(hex, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out packedValue)) if (hex == null || !uint.TryParse(hex, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out uint packedValue))
{ {
throw new ArgumentException("Hexadecimal string is not in the correct format.", nameof(hex)); throw new ArgumentException("Hexadecimal string is not in the correct format.", nameof(hex));
} }
TPixel result = default; TPixel result = default;
var rgba = new Rgba32( var rgba = new Rgba32(BinaryPrimitives.ReverseEndianness(packedValue));
(byte)(packedValue >> 24),
(byte)(packedValue >> 16),
(byte)(packedValue >> 8),
(byte)(packedValue >> 0));
result.PackFromRgba32(rgba); result.PackFromRgba32(rgba);
return result; return result;
@ -76,7 +73,10 @@ namespace SixLabors.ImageSharp.PixelFormats
/// </returns> /// </returns>
private static string ToRgbaHex(string hex) private static string ToRgbaHex(string hex)
{ {
hex = hex.StartsWith("#") ? hex.Substring(1) : hex; if (hex[0] == '#')
{
hex = hex.Substring(1);
}
if (hex.Length == 8) if (hex.Length == 8)
{ {
@ -93,12 +93,12 @@ namespace SixLabors.ImageSharp.PixelFormats
return null; return null;
} }
string red = char.ToString(hex[0]); char r = hex[0];
string green = char.ToString(hex[1]); char g = hex[1];
string blue = char.ToString(hex[2]); char b = hex[2];
string alpha = hex.Length == 3 ? "F" : char.ToString(hex[3]); char a = hex.Length == 3 ? 'F' : hex[3];
return string.Concat(red, red, green, green, blue, blue, alpha, alpha); return new string(new[] { r, r, g, g, b, b, a, a });
} }
} }
} }

5
src/ImageSharp/PixelFormats/Rgb24.cs

@ -15,22 +15,25 @@ namespace SixLabors.ImageSharp.PixelFormats
/// Ranges from [0, 0, 0, 1] to [1, 1, 1, 1] in vector form. /// Ranges from [0, 0, 0, 1] to [1, 1, 1, 1] in vector form.
/// </para> /// </para>
/// </summary> /// </summary>
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Explicit)]
public struct Rgb24 : IPixel<Rgb24> public struct Rgb24 : IPixel<Rgb24>
{ {
/// <summary> /// <summary>
/// The red component. /// The red component.
/// </summary> /// </summary>
[FieldOffset(0)]
public byte R; public byte R;
/// <summary> /// <summary>
/// The green component. /// The green component.
/// </summary> /// </summary>
[FieldOffset(1)]
public byte G; public byte G;
/// <summary> /// <summary>
/// The blue component. /// The blue component.
/// </summary> /// </summary>
[FieldOffset(2)]
public byte B; public byte B;
/// <summary> /// <summary>

14
src/ImageSharp/Processing/Quantization/FrameQuantizers/WuFrameQuantizer{TPixel}.cs

@ -157,13 +157,13 @@ namespace SixLabors.ImageSharp.Processing.Quantization.FrameQuantizers
} }
finally finally
{ {
this.vwt.Dispose(); this.vwt?.Dispose();
this.vmr.Dispose(); this.vmr?.Dispose();
this.vmg.Dispose(); this.vmg?.Dispose();
this.vmb.Dispose(); this.vmb?.Dispose();
this.vma.Dispose(); this.vma?.Dispose();
this.m2.Dispose(); this.m2?.Dispose();
this.tag.Dispose(); this.tag?.Dispose();
} }
} }

2
src/ImageSharp/Processing/Transforms/Processors/AffineTransformProcessor.cs

@ -51,7 +51,7 @@ namespace SixLabors.ImageSharp.Processing.Transforms.Processors
{ {
// We will always be creating the clone even for mutate because we may need to resize the canvas // We will always be creating the clone even for mutate because we may need to resize the canvas
IEnumerable<ImageFrame<TPixel>> frames = IEnumerable<ImageFrame<TPixel>> frames =
source.Frames.Select(x => new ImageFrame<TPixel>(source.GetMemoryManager(), this.TargetDimensions, x.MetaData.Clone())); source.Frames.Select(x => new ImageFrame<TPixel>(source.GetConfiguration(), this.TargetDimensions, x.MetaData.Clone()));
// Use the overload to prevent an extra frame being added // Use the overload to prevent an extra frame being added
return new Image<TPixel>(source.GetConfiguration(), source.MetaData.Clone(), frames); return new Image<TPixel>(source.GetConfiguration(), source.MetaData.Clone(), frames);

2
src/ImageSharp/Processing/Transforms/Processors/CropProcessor.cs

@ -37,7 +37,7 @@ namespace SixLabors.ImageSharp.Processing.Transforms.Processors
protected override Image<TPixel> CreateDestination(Image<TPixel> source, Rectangle sourceRectangle) protected override Image<TPixel> CreateDestination(Image<TPixel> source, Rectangle sourceRectangle)
{ {
// We will always be creating the clone even for mutate because we may need to resize the canvas // We will always be creating the clone even for mutate because we may need to resize the canvas
IEnumerable<ImageFrame<TPixel>> frames = source.Frames.Select(x => new ImageFrame<TPixel>(source.GetMemoryManager(), this.CropRectangle.Width, this.CropRectangle.Height, x.MetaData.Clone())); IEnumerable<ImageFrame<TPixel>> frames = source.Frames.Select(x => new ImageFrame<TPixel>(source.GetConfiguration(), this.CropRectangle.Width, this.CropRectangle.Height, x.MetaData.Clone()));
// Use the overload to prevent an extra frame being added // Use the overload to prevent an extra frame being added
return new Image<TPixel>(source.GetConfiguration(), source.MetaData.Clone(), frames); return new Image<TPixel>(source.GetConfiguration(), source.MetaData.Clone(), frames);

2
src/ImageSharp/Processing/Transforms/Processors/ProjectiveTransformProcessor.cs

@ -52,7 +52,7 @@ namespace SixLabors.ImageSharp.Processing.Transforms.Processors
{ {
// We will always be creating the clone even for mutate because we may need to resize the canvas // We will always be creating the clone even for mutate because we may need to resize the canvas
IEnumerable<ImageFrame<TPixel>> frames = IEnumerable<ImageFrame<TPixel>> frames =
source.Frames.Select(x => new ImageFrame<TPixel>(source.GetMemoryManager(), this.TargetDimensions.Width, this.TargetDimensions.Height, x.MetaData.Clone())); source.Frames.Select(x => new ImageFrame<TPixel>(source.GetConfiguration(), this.TargetDimensions.Width, this.TargetDimensions.Height, x.MetaData.Clone()));
// Use the overload to prevent an extra frame being added // Use the overload to prevent an extra frame being added
return new Image<TPixel>(source.GetConfiguration(), source.MetaData.Clone(), frames); return new Image<TPixel>(source.GetConfiguration(), source.MetaData.Clone(), frames);

30
src/ImageSharp/Processing/Transforms/Processors/ResizeProcessor.cs

@ -6,11 +6,11 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading.Tasks; using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors;
using SixLabors.ImageSharp.Processing.Transforms.Resamplers; using SixLabors.ImageSharp.Processing.Transforms.Resamplers;
using SixLabors.Primitives; using SixLabors.Primitives;
@ -56,12 +56,12 @@ namespace SixLabors.ImageSharp.Processing.Transforms.Processors
Guard.MustBeGreaterThan(tempWidth, 0, nameof(tempWidth)); Guard.MustBeGreaterThan(tempWidth, 0, nameof(tempWidth));
Guard.MustBeGreaterThan(tempHeight, 0, nameof(tempHeight)); Guard.MustBeGreaterThan(tempHeight, 0, nameof(tempHeight));
(Size size, Rectangle rectangle) locationBounds = ResizeHelper.CalculateTargetLocationAndBounds(sourceSize, options, tempWidth, tempHeight); (Size size, Rectangle rectangle) = ResizeHelper.CalculateTargetLocationAndBounds(sourceSize, options, tempWidth, tempHeight);
this.Sampler = options.Sampler; this.Sampler = options.Sampler;
this.Width = locationBounds.size.Width; this.Width = size.Width;
this.Height = locationBounds.size.Height; this.Height = size.Height;
this.ResizeRectangle = locationBounds.rectangle; this.ResizeRectangle = rectangle;
this.Compand = options.Compand; this.Compand = options.Compand;
} }
@ -167,13 +167,13 @@ namespace SixLabors.ImageSharp.Processing.Transforms.Processors
float center = ((i + .5F) * ratio) - .5F; float center = ((i + .5F) * ratio) - .5F;
// Keep inside bounds. // Keep inside bounds.
int left = (int)Math.Ceiling(center - radius); int left = (int)MathF.Ceiling(center - radius);
if (left < 0) if (left < 0)
{ {
left = 0; left = 0;
} }
int right = (int)Math.Floor(center + radius); int right = (int)MathF.Floor(center + radius);
if (right > sourceSize - 1) if (right > sourceSize - 1)
{ {
right = sourceSize - 1; right = sourceSize - 1;
@ -214,7 +214,7 @@ namespace SixLabors.ImageSharp.Processing.Transforms.Processors
protected override Image<TPixel> CreateDestination(Image<TPixel> source, Rectangle sourceRectangle) protected override Image<TPixel> CreateDestination(Image<TPixel> source, Rectangle sourceRectangle)
{ {
// We will always be creating the clone even for mutate because we may need to resize the canvas // We will always be creating the clone even for mutate because we may need to resize the canvas
IEnumerable<ImageFrame<TPixel>> frames = source.Frames.Select(x => new ImageFrame<TPixel>(source.GetMemoryManager(), this.Width, this.Height, x.MetaData.Clone())); IEnumerable<ImageFrame<TPixel>> frames = source.Frames.Select(x => new ImageFrame<TPixel>(source.GetConfiguration(), this.Width, this.Height, x.MetaData.Clone()));
// Use the overload to prevent an extra frame being added // Use the overload to prevent an extra frame being added
return new Image<TPixel>(source.GetConfiguration(), source.MetaData.Clone(), frames); return new Image<TPixel>(source.GetConfiguration(), source.MetaData.Clone(), frames);
@ -245,7 +245,7 @@ namespace SixLabors.ImageSharp.Processing.Transforms.Processors
// Handle resize dimensions identical to the original // Handle resize dimensions identical to the original
if (source.Width == destination.Width && source.Height == destination.Height && sourceRectangle == this.ResizeRectangle) if (source.Width == destination.Width && source.Height == destination.Height && sourceRectangle == this.ResizeRectangle)
{ {
// the cloned will be blank here copy all the pixel data over // The cloned will be blank here copy all the pixel data over
source.GetPixelSpan().CopyTo(destination.GetPixelSpan()); source.GetPixelSpan().CopyTo(destination.GetPixelSpan());
return; return;
} }
@ -306,7 +306,7 @@ namespace SixLabors.ImageSharp.Processing.Transforms.Processors
source.Width, source.Width,
(int y, IBuffer<Vector4> tempRowBuffer) => (int y, IBuffer<Vector4> tempRowBuffer) =>
{ {
Span<Vector4> firstPassRow = firstPassPixels.GetRowSpan(y); ref Vector4 firstPassRow = ref MemoryMarshal.GetReference(firstPassPixels.GetRowSpan(y));
Span<TPixel> sourceRow = source.GetPixelRowSpan(y); Span<TPixel> sourceRow = source.GetPixelRowSpan(y);
Span<Vector4> tempRowSpan = tempRowBuffer.Span; Span<Vector4> tempRowSpan = tempRowBuffer.Span;
@ -317,7 +317,7 @@ namespace SixLabors.ImageSharp.Processing.Transforms.Processors
for (int x = minX; x < maxX; x++) for (int x = minX; x < maxX; x++)
{ {
WeightsWindow window = this.horizontalWeights.Weights[x - startX]; WeightsWindow window = this.horizontalWeights.Weights[x - startX];
firstPassRow[x] = window.ComputeExpandedWeightedRowSum(tempRowSpan, sourceX); Unsafe.Add(ref firstPassRow, x) = window.ComputeExpandedWeightedRowSum(tempRowSpan, sourceX);
} }
} }
else else
@ -325,7 +325,7 @@ namespace SixLabors.ImageSharp.Processing.Transforms.Processors
for (int x = minX; x < maxX; x++) for (int x = minX; x < maxX; x++)
{ {
WeightsWindow window = this.horizontalWeights.Weights[x - startX]; WeightsWindow window = this.horizontalWeights.Weights[x - startX];
firstPassRow[x] = window.ComputeWeightedRowSum(tempRowSpan, sourceX); Unsafe.Add(ref firstPassRow, x) = window.ComputeWeightedRowSum(tempRowSpan, sourceX);
} }
} }
}); });
@ -339,7 +339,7 @@ namespace SixLabors.ImageSharp.Processing.Transforms.Processors
{ {
// Ensure offsets are normalized for cropping and padding. // Ensure offsets are normalized for cropping and padding.
WeightsWindow window = this.verticalWeights.Weights[y - startY]; WeightsWindow window = this.verticalWeights.Weights[y - startY];
Span<TPixel> targetRow = destination.GetPixelRowSpan(y); ref TPixel targetRow = ref MemoryMarshal.GetReference(destination.GetPixelRowSpan(y));
if (this.Compand) if (this.Compand)
{ {
@ -349,7 +349,7 @@ namespace SixLabors.ImageSharp.Processing.Transforms.Processors
Vector4 destinationVector = window.ComputeWeightedColumnSum(firstPassPixels, x, sourceY); Vector4 destinationVector = window.ComputeWeightedColumnSum(firstPassPixels, x, sourceY);
destinationVector = destinationVector.Compress(); destinationVector = destinationVector.Compress();
ref TPixel pixel = ref targetRow[x]; ref TPixel pixel = ref Unsafe.Add(ref targetRow, x);
pixel.PackFromVector4(destinationVector); pixel.PackFromVector4(destinationVector);
} }
} }
@ -360,7 +360,7 @@ namespace SixLabors.ImageSharp.Processing.Transforms.Processors
// Destination color components // Destination color components
Vector4 destinationVector = window.ComputeWeightedColumnSum(firstPassPixels, x, sourceY); Vector4 destinationVector = window.ComputeWeightedColumnSum(firstPassPixels, x, sourceY);
ref TPixel pixel = ref targetRow[x]; ref TPixel pixel = ref Unsafe.Add(ref targetRow, x);
pixel.PackFromVector4(destinationVector); pixel.PackFromVector4(destinationVector);
} }
} }

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

Loading…
Cancel
Save