Browse Source

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

af/merge-core
James Jackson-South 8 years ago
parent
commit
a1f93d5adc
  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
dist: trusty
sudo: required
dotnet: 2.1.4
dotnet: 2.1.300-rc1-008673
mono: latest
# - os: osx # OSX 10.11
# osx_image: xcode7.3.1
@ -21,7 +21,7 @@ branches:
script:
- git submodule -q update --init
- 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:
global:

2
ImageSharp.ruleset

@ -9,5 +9,7 @@
<Rule Id="SA1633" Action="None" />
<!--1.1.0-beta6 incorrectly throws a wobbler here for multiline matrices-->
<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>
</RuleSet>

14
README.md

@ -115,17 +115,21 @@ For more examples check out:
### 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)
- The [.NET Core SDK Installer](https://www.microsoft.com/net/core#windows) - Non VSCode link.
- Using [Visual Studio 2017 Preview](https://docs.microsoft.com/en-us/visualstudio/releasenotes/vs2017-preview-relnotes)
- 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)
- [.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
git clone https://github.com/SixLabors/ImageSharp

27
appveyor.yml

@ -1,21 +1,34 @@
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
skip_branch_with_pr: true
environment:
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
is_32bit: True
- target_framework: net462
is_32bit: False
- target_framework: net462
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"
# is_32bit: True
#- target_framework: mono
@ -39,7 +52,7 @@ install:
before_build:
- git submodule -q update --init
- cmd: dotnet --version
- cmd: dotnet --info
build_script:
- cmd: build.cmd
@ -60,4 +73,4 @@ deploy:
secure: V/lEHP0UeMWIpWd0fiNlY2IgbCnJKQlGdRksECdJbOBdaE20Fl0RNL7WyqHe02o4
artifact: /.*\.nupkg/
on:
branch: master
branch: master

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

@ -38,10 +38,9 @@
</ItemGroup>
<ItemGroup>
<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" />
<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>
</PackageReference>
</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;
}
else
{
amountSpan[i] = scanline[i];
}
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
{
/// <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>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>

2
src/ImageSharp/Advanced/IConfigurable.cs

@ -4,7 +4,7 @@
namespace SixLabors.ImageSharp.Advanced
{
/// <summary>
/// Encapsulates the properties for configuration
/// Encapsulates the properties for configuration.
/// </summary>
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="parameterName">The name of the parameter that is to be checked.</param>
/// <exception cref="ArgumentException">
/// <paramref name="target"/> is true
/// <paramref name="target"/> has a different size than <paramref name="other"/>
/// </exception>
[Conditional("DEBUG")]
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="parameterName">The name of the parameter that is to be checked.</param>
/// <exception cref="ArgumentException">
/// <paramref name="target"/> is true
/// <paramref name="target"/> has less items than <paramref name="minSpan"/>
/// </exception>
[Conditional("DEBUG")]
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.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace SixLabors.ImageSharp
{
@ -15,78 +14,62 @@ namespace SixLabors.ImageSharp
internal static class Guard
{
/// <summary>
/// Verifies, that the method parameter with specified object value is not null
/// and throws an exception if it is found to be so.
/// Ensures that the value is not null.
/// </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="message">The error message, if any to add to the exception.</param>
/// <exception cref="ArgumentNullException"><paramref name="target"/> is null</exception>
public static void NotNull(object target, string parameterName, string message = "")
/// <exception cref="ArgumentNullException"><paramref name="value"/> is null</exception>
public static void NotNull(object value, string parameterName)
{
if (target == null)
if (value == null)
{
if (!string.IsNullOrWhiteSpace(message))
{
throw new ArgumentNullException(parameterName, message);
}
throw new ArgumentNullException(parameterName);
}
}
/// <summary>
/// Verifies, that the string method parameter with specified object value and message
/// is not null, not empty and does not contain only blanks and throws an exception
/// if the object is null.
/// Ensures that the target value is not null, empty, or whitespace.
/// </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="message">The error message, if any to add to the exception.</param>
/// <exception cref="ArgumentNullException"><paramref name="target"/> is null.</exception>
/// <exception cref="ArgumentException"><paramref name="target"/> is empty or contains only blanks.</exception>
public static void NotNullOrEmpty(string target, string parameterName, string message = "")
/// <exception cref="ArgumentNullException"><paramref name="value"/> is null.</exception>
/// <exception cref="ArgumentException"><paramref name="value"/> is empty or contains only blanks.</exception>
public static void NotNullOrWhiteSpace(string value, string parameterName)
{
NotNull(target, parameterName, message);
if (string.IsNullOrWhiteSpace(target))
if (value == null)
{
if (!string.IsNullOrWhiteSpace(message))
{
throw new ArgumentException(message, parameterName);
}
throw new ArgumentNullException(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>
/// Verifies, that the enumeration is not null and not empty.
/// Ensures that the enumeration is not null or empty.
/// </summary>
/// <typeparam name="T">The type of objects in the <paramref name="target"/></typeparam>
/// <param name="target">The target enumeration, which should be checked against being null or empty.</param>
/// <typeparam name="T">The type of objects in the <paramref name="value"/></typeparam>
/// <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="message">The error message, if any to add to the exception.</param>
/// <exception cref="ArgumentNullException"><paramref name="target"/> is null.</exception>
/// <exception cref="ArgumentException"><paramref name="target"/> is empty.</exception>
public static void NotNullOrEmpty<T>(IEnumerable<T> target, string parameterName, string message = "")
/// <exception cref="ArgumentNullException"><paramref name="value"/> is null.</exception>
/// <exception cref="ArgumentException"><paramref name="value"/> is empty.</exception>
public static void NotNullOrEmpty<T>(ICollection<T> value, string parameterName)
{
NotNull(target, parameterName, message);
if (!target.Any())
if (value == null)
{
if (!string.IsNullOrWhiteSpace(message))
{
throw new ArgumentException(message, parameterName);
}
throw new ArgumentNullException(parameterName);
}
throw new ArgumentException("Value cannot be empty.", parameterName);
if (value.Count == 0)
{
throw new ArgumentException("Must not be empty.", parameterName);
}
}
/// <summary>
/// Verifies that the specified value is less than a maximum value
/// and throws an exception if it is not.
/// Ensures that the specified value is less than a maximum value.
/// </summary>
/// <param name="value">The target value, which should be validated.</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
/// and throws an exception if it is found to be so.
/// </summary>
/// <param name="target">
/// The target value, which cannot be false.
/// </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>
/// <param name="target">The target value, which cannot be false.</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">
/// <paramref name="target"/> is false
/// </exception>
@ -230,14 +207,14 @@ namespace SixLabors.ImageSharp
}
/// <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>
/// <typeparam name="T">The element type of the spans</typeparam>
/// <param name="source">The source span.</param>
/// <param name="minLength">The minimum length.</param>
/// <param name="parameterName">The name of the parameter that is to be checked.</param>
/// <exception cref="ArgumentException">
/// <paramref name="source"/> is true
/// <paramref name="source"/> has less than <paramref name="minLength"/> items
/// </exception>
public static void MustBeSizedAtLeast<T>(ReadOnlySpan<T> source, int minLength, string parameterName)
{
@ -248,14 +225,14 @@ namespace SixLabors.ImageSharp
}
/// <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>
/// <typeparam name="T">The element type of the spans</typeparam>
/// <param name="source">The target span.</param>
/// <param name="minLength">The minimum length.</param>
/// <param name="parameterName">The name of the parameter that is to be checked.</param>
/// <exception cref="ArgumentException">
/// <paramref name="source"/> is true
/// <paramref name="source"/> has less than <paramref name="minLength"/> items
/// </exception>
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
{
/// <summary>
/// Provides initialization code which allows extending the library.
/// Provides configuration code which allows altering default behaviour or extending the library.
/// </summary>
public sealed class Configuration
{

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

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

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

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

@ -4,7 +4,7 @@
using System;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
/// <summary>
/// Provides information about the Adobe marker segment.
@ -78,7 +78,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
return true;
}
marker = default(AdobeMarker);
marker = default;
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.Numerics;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal abstract partial class JpegColorConverter
{
internal class FromCmyk : ColorConverters.JpegColorConverter
internal class FromCmyk : JpegColorConverter
{
public FromCmyk()
: 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()!
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.Numerics;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal abstract partial class JpegColorConverter
{
internal class FromGrayscale : ColorConverters.JpegColorConverter
internal class FromGrayscale : JpegColorConverter
{
public FromGrayscale()
: 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()!
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.Numerics;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal abstract partial class JpegColorConverter
{
internal class FromRgb : ColorConverters.JpegColorConverter
internal class FromRgb : JpegColorConverter
{
public FromRgb()
: 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()!
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.Numerics;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal abstract partial class JpegColorConverter
{
internal class FromYCbCrBasic : ColorConverters.JpegColorConverter
internal class FromYCbCrBasic : JpegColorConverter
{
public FromYCbCrBasic()
: base(JpegColorSpace.YCbCr)
{
}
public override void ConvertToRGBA(ComponentValues values, Span<Vector4> result)
public override void ConvertToRgba(ComponentValues values, Span<Vector4> 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.Runtime.CompilerServices;
using System.Runtime.InteropServices;
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 class FromYCbCrSimd : ColorConverters.JpegColorConverter
internal class FromYCbCrSimd : JpegColorConverter
{
public FromYCbCrSimd()
: 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 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.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Common.Tuples;
// 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 class FromYCbCrSimdAvx2 : ColorConverters.JpegColorConverter
internal class FromYCbCrSimdAvx2 : JpegColorConverter
{
public FromYCbCrSimdAvx2()
: base(JpegColorSpace.YCbCr)
@ -21,7 +22,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters
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 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.Numerics;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
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()!
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.Linq;
using System.Numerics;
using SixLabors.ImageSharp.Common.Tuples;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
/// <summary>
/// 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>
/// <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>
public abstract void ConvertToRGBA(ComponentValues values, Span<Vector4> result);
public abstract void ConvertToRgba(ComponentValues values, Span<Vector4> result);
/// <summary>
/// Returns the <see cref="JpegColorConverter"/> for the YCbCr colorspace that matches the current CPU architecture.
/// </summary>
private static JpegColorConverter GetYCbCrConverter() =>
FromYCbCrSimdAvx2.IsAvailable ? (JpegColorConverter)new FromYCbCrSimdAvx2() : new FromYCbCrSimd();
JpegColorConverter.FromYCbCrSimdAvx2.IsAvailable ? (JpegColorConverter)new JpegColorConverter.FromYCbCrSimdAvx2() : new JpegColorConverter.FromYCbCrSimd();
/// <summary>
/// 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.Primitives;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
/// <summary>
/// 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.Collections.Generic;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
/// <inheritdoc />
/// <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>
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;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
/// <summary>
/// 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.
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Memory;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
/// <summary>
/// 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>
/// 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 SixLabors.ImageSharp.Memory;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
/// <summary>
/// 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.Linq;
using System.Numerics;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
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>
/// 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;
/// <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>
private ColorConverters.JpegColorConverter colorConverter;
private readonly JpegColorConverter colorConverter;
/// <summary>
/// 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.rgbaBuffer = memoryManager.Allocate<Vector4>(rawJpeg.ImageSizeInPixels.Width);
this.colorConverter = ColorConverters.JpegColorConverter.GetConverter(rawJpeg.ColorSpace);
this.colorConverter = JpegColorConverter.GetConverter(rawJpeg.ColorSpace);
}
/// <summary>
@ -148,8 +154,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
{
int y = yy - this.PixelRowCounter;
var values = new ColorConverters.JpegColorConverter.ComponentValues(buffers, y);
this.colorConverter.ConvertToRGBA(values, this.rgbaBuffer.Span);
var values = new JpegColorConverter.ComponentValues(buffers, y);
this.colorConverter.ConvertToRgba(values, this.rgbaBuffer.Span);
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.Text;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
/// <summary>
/// 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.
// Licensed under the Apache License, Version 2.0.
using Block8x8F = SixLabors.ImageSharp.Formats.Jpeg.Common.Block8x8F;
namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
{
/// <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.
/// </summary>
internal unsafe struct BlockQuad
{
/// <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>
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.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
{
/// <summary>
/// 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.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
{
/// <summary>
/// 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.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
{
/// <summary>
/// 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.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
{
/// <summary>
/// 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 SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
{
/// <summary>
/// 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>
public static RgbToYCbCrTables Create()
{
RgbToYCbCrTables tables = default(RgbToYCbCrTables);
RgbToYCbCrTables tables = default;
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);
}
[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.Runtime.CompilerServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats.Jpeg.Common;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
{
/// <summary>
/// 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;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Formats.Jpeg.Common
namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{
/// <summary>
/// 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.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using System.Runtime.CompilerServices;
// <auto-generated />
namespace SixLabors.ImageSharp.Formats.Jpeg.Common
namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{
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.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using System.Runtime.CompilerServices;
// <auto-generated />
namespace SixLabors.ImageSharp.Formats.Jpeg.Common
namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{
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.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Formats.Jpeg.Common
namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{
/// <summary>
/// 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 SizeInBytes = Size * 3;
/// <summary>
/// FOR TESTING ONLY!
/// 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.Numerics;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common
namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{
/// <summary>
/// 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.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common
namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{
/// <summary>
/// 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)]
public void EnsureNBits(int n, ref InputProcessor inputProcessor)
{
OrigDecoderErrorCode errorCode = this.EnsureNBitsUnsafe(n, ref inputProcessor);
GolangDecoderErrorCode errorCode = this.EnsureNBitsUnsafe(n, ref inputProcessor);
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
/// 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.
/// This method does not throw. Returns <see cref="OrigDecoderErrorCode"/> instead.
/// This method does not throw. Returns <see cref="GolangDecoderErrorCode"/> instead.
/// </summary>
/// <param name="n">The number of bits to ensure.</param>
/// <param name="inputProcessor">The <see cref="InputProcessor"/></param>
/// <returns>Error code</returns>
public OrigDecoderErrorCode EnsureNBitsUnsafe(int n, ref InputProcessor inputProcessor)
public GolangDecoderErrorCode EnsureNBitsUnsafe(int n, ref InputProcessor inputProcessor)
{
while (true)
{
OrigDecoderErrorCode errorCode = this.EnsureBitsStepImpl(ref inputProcessor);
if (errorCode != OrigDecoderErrorCode.NoError || this.UnreadBits >= n)
GolangDecoderErrorCode errorCode = this.EnsureBitsStepImpl(ref inputProcessor);
if (errorCode != GolangDecoderErrorCode.NoError || this.UnreadBits >= n)
{
return errorCode;
}
@ -67,8 +67,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// Unrolled version of <see cref="EnsureNBitsUnsafe"/> for n==8
/// </summary>
/// <param name="inputProcessor">The <see cref="InputProcessor"/></param>
/// <returns>A <see cref="OrigDecoderErrorCode"/></returns>
public OrigDecoderErrorCode Ensure8BitsUnsafe(ref InputProcessor inputProcessor)
/// <returns>A <see cref="GolangDecoderErrorCode"/></returns>
public GolangDecoderErrorCode Ensure8BitsUnsafe(ref InputProcessor 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
/// </summary>
/// <param name="inputProcessor">The <see cref="InputProcessor"/></param>
/// <returns>A <see cref="OrigDecoderErrorCode"/></returns>
public OrigDecoderErrorCode Ensure1BitUnsafe(ref InputProcessor inputProcessor)
/// <returns>A <see cref="GolangDecoderErrorCode"/></returns>
public GolangDecoderErrorCode Ensure1BitUnsafe(ref InputProcessor inputProcessor)
{
return this.EnsureBitsStepImpl(ref inputProcessor);
}
@ -92,7 +92,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
[MethodImpl(MethodImplOptions.AggressiveInlining)]
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();
return x;
}
@ -103,13 +103,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// <param name="t">Byte</param>
/// <param name="inputProcessor">The <see cref="InputProcessor"/></param>
/// <param name="x">Read bits value</param>
/// <returns>The <see cref="OrigDecoderErrorCode"/></returns>
public OrigDecoderErrorCode ReceiveExtendUnsafe(int t, ref InputProcessor inputProcessor, out int x)
/// <returns>The <see cref="GolangDecoderErrorCode"/></returns>
public GolangDecoderErrorCode ReceiveExtendUnsafe(int t, ref InputProcessor inputProcessor, out int x)
{
if (this.UnreadBits < t)
{
OrigDecoderErrorCode errorCode = this.EnsureNBitsUnsafe(t, ref inputProcessor);
if (errorCode != OrigDecoderErrorCode.NoError)
GolangDecoderErrorCode errorCode = this.EnsureNBitsUnsafe(t, ref inputProcessor);
if (errorCode != GolangDecoderErrorCode.NoError)
{
x = int.MaxValue;
return errorCode;
@ -126,14 +126,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
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;
}

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

@ -77,8 +77,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// </summary>
/// <param name="inputStream">Input stream</param>
/// <param name="x">The result byte as <see cref="int"/></param>
/// <returns>The <see cref="OrigDecoderErrorCode"/></returns>
public OrigDecoderErrorCode ReadByteStuffedByteUnsafe(Stream inputStream, out int x)
/// <returns>The <see cref="GolangDecoderErrorCode"/></returns>
public GolangDecoderErrorCode ReadByteStuffedByteUnsafe(Stream inputStream, out int x)
{
// Take the fast path if bytes.buf contains at least two bytes.
if (this.I + 2 <= this.J)
@ -86,50 +86,50 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
x = this.BufferAsInt[this.I];
this.I++;
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)
{
return OrigDecoderErrorCode.MissingFF00;
return GolangDecoderErrorCode.MissingFF00;
}
this.I++;
this.UnreadableBytes = 2;
x = OrigJpegConstants.Markers.XFF;
return OrigDecoderErrorCode.NoError;
x = JpegConstants.Markers.XFF;
return GolangDecoderErrorCode.NoError;
}
this.UnreadableBytes = 0;
OrigDecoderErrorCode errorCode = this.ReadByteAsIntUnsafe(inputStream, out x);
GolangDecoderErrorCode errorCode = this.ReadByteAsIntUnsafe(inputStream, out x);
this.UnreadableBytes = 1;
if (errorCode != OrigDecoderErrorCode.NoError)
if (errorCode != GolangDecoderErrorCode.NoError)
{
return errorCode;
}
if (x != OrigJpegConstants.Markers.XFF)
if (x != JpegConstants.Markers.XFF)
{
return OrigDecoderErrorCode.NoError;
return GolangDecoderErrorCode.NoError;
}
errorCode = this.ReadByteAsIntUnsafe(inputStream, out x);
this.UnreadableBytes = 2;
if (errorCode != OrigDecoderErrorCode.NoError)
if (errorCode != GolangDecoderErrorCode.NoError)
{
return errorCode;
}
if (x != 0x00)
{
return OrigDecoderErrorCode.MissingFF00;
return GolangDecoderErrorCode.MissingFF00;
}
x = OrigJpegConstants.Markers.XFF;
return OrigDecoderErrorCode.NoError;
x = JpegConstants.Markers.XFF;
return GolangDecoderErrorCode.NoError;
}
/// <summary>
@ -140,25 +140,25 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public byte ReadByte(Stream inputStream)
{
OrigDecoderErrorCode errorCode = this.ReadByteUnsafe(inputStream, out byte result);
GolangDecoderErrorCode errorCode = this.ReadByteUnsafe(inputStream, out byte result);
errorCode.EnsureNoError();
return result;
}
/// <summary>
/// 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>
/// <param name="inputStream">Input stream</param>
/// <param name="result">The result <see cref="byte"/> as out parameter</param>
/// <returns>The <see cref="OrigDecoderErrorCode"/></returns>
public OrigDecoderErrorCode ReadByteUnsafe(Stream inputStream, out byte result)
/// <returns>The <see cref="GolangDecoderErrorCode"/></returns>
public GolangDecoderErrorCode ReadByteUnsafe(Stream inputStream, out byte result)
{
OrigDecoderErrorCode errorCode = OrigDecoderErrorCode.NoError;
GolangDecoderErrorCode errorCode = GolangDecoderErrorCode.NoError;
while (this.I == this.J)
{
errorCode = this.FillUnsafe(inputStream);
if (errorCode != OrigDecoderErrorCode.NoError)
if (errorCode != GolangDecoderErrorCode.NoError)
{
result = 0;
return errorCode;
@ -176,15 +176,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// </summary>
/// <param name="inputStream">The input stream</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)]
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)
{
errorCode = this.FillUnsafe(inputStream);
if (errorCode != OrigDecoderErrorCode.NoError)
if (errorCode != GolangDecoderErrorCode.NoError)
{
result = 0;
return errorCode;
@ -206,18 +206,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Fill(Stream inputStream)
{
OrigDecoderErrorCode errorCode = this.FillUnsafe(inputStream);
GolangDecoderErrorCode errorCode = this.FillUnsafe(inputStream);
errorCode.EnsureNoError();
}
/// <summary>
/// Fills up the bytes buffer from the underlying stream.
/// 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>
/// <param name="inputStream">Input stream</param>
/// <returns>The <see cref="OrigDecoderErrorCode"/></returns>
public OrigDecoderErrorCode FillUnsafe(Stream inputStream)
/// <returns>The <see cref="GolangDecoderErrorCode"/></returns>
public GolangDecoderErrorCode FillUnsafe(Stream inputStream)
{
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);
if (n == 0)
{
return OrigDecoderErrorCode.UnexpectedEndOfStream;
return GolangDecoderErrorCode.UnexpectedEndOfStream;
}
this.J += n;
@ -249,7 +249,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
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
{
/// <summary>
/// Throws an exception that belongs to the given <see cref="OrigDecoderErrorCode"/>
/// Throws an exception that belongs to the given <see cref="GolangDecoderErrorCode"/>
/// </summary>
/// <param name="errorCode">The <see cref="OrigDecoderErrorCode"/></param>
/// <param name="errorCode">The <see cref="GolangDecoderErrorCode"/></param>
[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,
// consider using the ***Unsafe variant of the parsing method that asks for ThrowExceptionForErrorCode()
// then verify the error code + implement fallback logic manually!
switch (errorCode)
{
case OrigDecoderErrorCode.NoError:
case GolangDecoderErrorCode.NoError:
throw new ArgumentException("ThrowExceptionForErrorCode() called with NoError!", nameof(errorCode));
case OrigDecoderErrorCode.MissingFF00:
case GolangDecoderErrorCode.MissingFF00:
throw new MissingFF00Exception();
case OrigDecoderErrorCode.UnexpectedEndOfStream:
case GolangDecoderErrorCode.UnexpectedEndOfStream:
throw new EOFException();
default:
throw new ArgumentOutOfRangeException(nameof(errorCode), errorCode, null);
@ -35,26 +35,26 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
}
/// <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>
/// <param name="errorCode">The <see cref="OrigDecoderErrorCode"/></param>
/// <param name="errorCode">The <see cref="GolangDecoderErrorCode"/></param>
[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);
}
}
/// <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>
/// <param name="errorCode">The <see cref="OrigDecoderErrorCode"/></param>
/// <param name="errorCode">The <see cref="GolangDecoderErrorCode"/></param>
[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();
}

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.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.Primitives;
@ -14,9 +15,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// <summary>
/// Represents a single color component
/// </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.Index = index;
@ -56,8 +57,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// Initializes <see cref="SpectralBlocks"/>
/// </summary>
/// <param name="memoryManager">The <see cref="MemoryManager"/> to use for buffer allocations.</param>
/// <param name="decoder">The <see cref="OrigJpegDecoderCore"/> instance</param>
public void InitializeDerivedData(MemoryManager memoryManager, OrigJpegDecoderCore decoder)
/// <param name="decoder">The <see cref="GolangJpegDecoderCore"/> instance</param>
public void InitializeDerivedData(MemoryManager memoryManager, GolangJpegDecoderCore decoder)
{
// For 4-component images (either CMYK or YCbCrK), we only support two
// hv vectors: [0x11 0x11 0x11 0x11] and [0x22 0x11 0x11 0x22].
@ -76,7 +77,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
}
else
{
OrigComponent c0 = decoder.Components[0];
GolangComponent c0 = decoder.Components[0];
this.SubSamplingDivisors = c0.SamplingFactors.DivideBy(this.SamplingFactors);
}
@ -86,8 +87,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// <summary>
/// Initializes all component data except <see cref="SpectralBlocks"/>.
/// </summary>
/// <param name="decoder">The <see cref="OrigJpegDecoderCore"/> instance</param>
public void InitializeCoreData(OrigJpegDecoderCore decoder)
/// <param name="decoder">The <see cref="GolangJpegDecoderCore"/> instance</param>
public void InitializeCoreData(GolangJpegDecoderCore decoder)
{
// Section B.2.2 states that "the value of C_i shall be different from
// 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)];
if (this.QuantizationTableIndex > OrigJpegDecoderCore.MaxTq)
if (this.QuantizationTableIndex > GolangJpegDecoderCore.MaxTq)
{
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
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal struct OrigComponentScan
internal struct GolangComponentScan
{
/// <summary>
/// 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>
/// Represents "recoverable" decoder errors.
/// </summary>
internal enum OrigDecoderErrorCode
internal enum GolangDecoderErrorCode
{
/// <summary>
/// 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.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@ -12,7 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// Represents a Huffman tree
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal unsafe struct OrigHuffmanTree
internal unsafe struct GolangHuffmanTree
{
/// <summary>
/// The index of the AC table row
@ -95,12 +93,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
public FixedInt32Buffer16 Indices;
/// <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>
/// <returns>An array of <see cref="OrigHuffmanTree" /> instances representing the Huffman tables</returns>
public static OrigHuffmanTree[] CreateHuffmanTrees()
/// <returns>An array of <see cref="GolangHuffmanTree" /> instances representing the Huffman tables</returns>
public static GolangHuffmanTree[] CreateHuffmanTrees()
{
return new OrigHuffmanTree[NumberOfTrees];
return new GolangHuffmanTree[NumberOfTrees];
}
/// <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.
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Formats.Jpeg.Common;
using SixLabors.ImageSharp.Formats.Jpeg.Components;
namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
{
/// <content>
/// Conains the definition of <see cref="ComputationData"/>
/// </content>
internal unsafe partial struct OrigJpegScanDecoder
internal unsafe partial struct GolangJpegScanDecoder
{
/// <summary>
/// Holds the "large" data blocks needed for computations.
@ -28,14 +29,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
public ZigZag Unzig;
/// <summary>
/// The buffer storing the <see cref="OrigComponentScan"/>-s for each component
/// The buffer storing the <see cref="GolangComponentScan"/>-s for each component
/// </summary>
public fixed byte ScanData[3 * OrigJpegDecoderCore.MaxComponents];
public fixed byte ScanData[3 * GolangJpegDecoderCore.MaxComponents];
/// <summary>
/// The DC values for each component
/// </summary>
public fixed int Dc[OrigJpegDecoderCore.MaxComponents];
public fixed int Dc[GolangJpegDecoderCore.MaxComponents];
/// <summary>
/// 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.
// 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
{
/// <content>
/// Conains the definition of <see cref="DataPointers"/>
/// </content>
internal unsafe partial struct OrigJpegScanDecoder
internal unsafe partial struct GolangJpegScanDecoder
{
/// <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"/>
@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// <summary>
/// Pointer to <see cref="ComputationData.ScanData"/> as Scan*
/// </summary>
public OrigComponentScan* ComponentScan;
public GolangComponentScan* ComponentScan;
/// <summary>
/// Pointer to <see cref="ComputationData.Dc"/>
@ -43,7 +43,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
{
this.Block = &basePtr->Block;
this.Unzig = basePtr->Unzig.Data;
this.ComponentScan = (OrigComponentScan*)basePtr->ScanData;
this.ComponentScan = (GolangComponentScan*)basePtr->ScanData;
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.InteropServices;
using SixLabors.ImageSharp.Formats.Jpeg.Common;
using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Formats.Jpeg.Components;
// ReSharper disable InconsistentNaming
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.
/// </summary>
[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.
#pragma warning disable SA1202 // ElementsMustBeOrderedByAccess
@ -110,12 +109,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
private byte expectedRst;
/// <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>
/// <param name="p">Pointer to <see cref="OrigJpegScanDecoder"/> on the stack</param>
/// <param name="decoder">The <see cref="OrigJpegDecoderCore"/> instance</param>
/// <param name="p">Pointer to <see cref="GolangJpegScanDecoder"/> on the stack</param>
/// <param name="decoder">The <see cref="GolangJpegDecoderCore"/> instance</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->pointers = new DataPointers(&p->data);
@ -123,8 +122,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
}
/// <summary>
/// Read Huffman data from Jpeg scans in <see cref="OrigJpegDecoderCore.InputStream"/>,
/// and decode it as <see cref="Block8x8"/> into <see cref="OrigComponent.SpectralBlocks"/>.
/// Read Huffman data from Jpeg scans in <see cref="GolangJpegDecoderCore.InputStream"/>,
/// 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
/// 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
/// 3 4 5
/// </summary>
/// <param name="decoder">The <see cref="OrigJpegDecoderCore"/> instance</param>
public void DecodeBlocks(OrigJpegDecoderCore decoder)
/// <param name="decoder">The <see cref="GolangJpegDecoderCore"/> instance</param>
public void DecodeBlocks(GolangJpegDecoderCore decoder)
{
decoder.InputProcessor.ResetErrorState();
this.blockCounter = 0;
this.mcuCounter = 0;
this.expectedRst = OrigJpegConstants.Markers.RST0;
this.expectedRst = JpegConstants.Markers.RST0;
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++)
{
this.ComponentIndex = this.pointers.ComponentScan[scanIndex].ComponentIndex;
OrigComponent component = decoder.Components[this.ComponentIndex];
GolangComponent component = decoder.Components[this.ComponentIndex];
this.hi = component.HorizontalSamplingFactor;
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.
if (!decoder.InputProcessor.ReachedEOF)
@ -262,15 +261,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
}
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();
@ -285,15 +284,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// </summary>
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>
/// The implementation part of <see cref="InitStreamReading"/> as an instance method.
/// </summary>
/// <param name="decoder">The <see cref="OrigJpegDecoderCore"/></param>
/// <param name="decoder">The <see cref="GolangJpegDecoderCore"/></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)
{
@ -360,10 +359,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// </summary>
/// <param name="decoder">The decoder</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;
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)
{
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++;
// 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(
ref decoder.HuffmanTrees[huffmanIndex],
out int value);
@ -467,7 +466,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
private void DecodeEobRun(int count, ref InputProcessor processor)
{
processor.DecodeBitsUnsafe(count, out int bitsResult);
if (processor.LastErrorCode != OrigDecoderErrorCode.NoError)
if (processor.LastErrorCode != GolangDecoderErrorCode.NoError)
{
return;
}
@ -475,7 +474,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
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.
int cs = decoder.Temp[1 + (2 * i)];
@ -500,11 +499,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
}
private void ProcessComponentImpl(
OrigJpegDecoderCore decoder,
GolangJpegDecoderCore decoder,
int i,
ref OrigComponentScan currentComponentScan,
ref GolangComponentScan currentComponentScan,
ref int totalHv,
OrigComponent currentComponent)
GolangComponent currentComponent)
{
// 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
@ -522,13 +521,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
totalHv += currentComponent.HorizontalSamplingFactor * currentComponent.VerticalSamplingFactor;
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");
}
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");
}
@ -540,7 +539,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// <param name="bp">The <see cref="InputProcessor"/> instance</param>
/// <param name="h">The Huffman tree</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;
@ -560,7 +559,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
if (bit)
{
int stuff = (int)Block8x8.GetScalarAt(b, 0);
int stuff = Block8x8.GetScalarAt(b, 0);
// int stuff = (int)b[0];
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
{
/// <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
/// </summary>
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.
/// </summary>
/// <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)
{
this.Bits = default(Bits);
this.Bits = default;
this.Bytes = Bytes.Create();
this.InputStream = inputStream;
this.Temp = temp;
this.LastErrorCode = OrigDecoderErrorCode.NoError;
this.LastErrorCode = GolangDecoderErrorCode.NoError;
}
/// <summary>
@ -43,20 +43,20 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
public Stream InputStream { get; }
/// <summary>
/// Gets the temporary buffer, same instance as <see cref="OrigJpegDecoderCore.Temp"/>
/// Gets the temporary buffer, same instance as <see cref="GolangJpegDecoderCore.Temp"/>
/// </summary>
public byte[] Temp { get; }
/// <summary>
/// Gets a value indicating whether an unexpected EOF reached in <see cref="InputStream"/>.
/// </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>
/// 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>
public bool CheckEOFEnsureNoError()
{
if (this.LastErrorCode == OrigDecoderErrorCode.UnexpectedEndOfStream)
if (this.LastErrorCode == GolangDecoderErrorCode.UnexpectedEndOfStream)
{
return false;
}
@ -81,7 +81,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// <returns>A <see cref="bool"/> indicating whether EOF reached</returns>
public bool CheckEOF()
{
if (this.LastErrorCode == OrigDecoderErrorCode.UnexpectedEndOfStream)
if (this.LastErrorCode == GolangDecoderErrorCode.UnexpectedEndOfStream)
{
return false;
}
@ -106,7 +106,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public OrigDecoderErrorCode ReadByteUnsafe(out byte result)
public GolangDecoderErrorCode ReadByteUnsafe(out byte result)
{
this.LastErrorCode = this.Bytes.ReadByteUnsafe(this.InputStream, out result);
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!
/// </summary>
/// <param name="result">The decoded bit as a <see cref="bool"/></param>
/// <returns>The <see cref="OrigDecoderErrorCode" /></returns>
public OrigDecoderErrorCode DecodeBitUnsafe(out bool result)
/// <returns>The <see cref="GolangDecoderErrorCode" /></returns>
public GolangDecoderErrorCode DecodeBitUnsafe(out bool result)
{
if (this.Bits.UnreadBits == 0)
{
this.LastErrorCode = this.Bits.Ensure1BitUnsafe(ref this);
if (this.LastErrorCode != OrigDecoderErrorCode.NoError)
if (this.LastErrorCode != GolangDecoderErrorCode.NoError)
{
result = false;
return this.LastErrorCode;
@ -133,18 +133,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
result = (this.Bits.Accumulator & this.Bits.Mask) != 0;
this.Bits.UnreadBits--;
this.Bits.Mask >>= 1;
return this.LastErrorCode = OrigDecoderErrorCode.NoError;
return this.LastErrorCode = GolangDecoderErrorCode.NoError;
}
/// <summary>
/// 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>
/// <param name="data">The data to write to.</param>
/// <param name="offset">The offset in the source buffer</param>
/// <param name="length">The number of bytes to read</param>
/// <returns>The <see cref="OrigDecoderErrorCode"/></returns>
public OrigDecoderErrorCode ReadFullUnsafe(byte[] data, int offset, int length)
/// <returns>The <see cref="GolangDecoderErrorCode"/></returns>
public GolangDecoderErrorCode ReadFullUnsafe(byte[] data, int offset, int length)
{
// Unread the overshot bytes, if any.
if (this.Bytes.UnreadableBytes != 0)
@ -157,8 +157,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
this.Bytes.UnreadableBytes = 0;
}
this.LastErrorCode = OrigDecoderErrorCode.NoError;
while (length > 0 && this.LastErrorCode == OrigDecoderErrorCode.NoError)
this.LastErrorCode = GolangDecoderErrorCode.NoError;
while (length > 0 && this.LastErrorCode == GolangDecoderErrorCode.NoError)
{
if (this.Bytes.J - this.Bytes.I >= length)
{
@ -185,13 +185,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// </summary>
/// <param name="count">The number of bits to decode.</param>
/// <param name="result">The <see cref="uint" /> result</param>
/// <returns>The <see cref="OrigDecoderErrorCode"/></returns>
public OrigDecoderErrorCode DecodeBitsUnsafe(int count, out int result)
/// <returns>The <see cref="GolangDecoderErrorCode"/></returns>
public GolangDecoderErrorCode DecodeBitsUnsafe(int count, out int result)
{
if (this.Bits.UnreadBits < count)
{
this.LastErrorCode = this.Bits.EnsureNBitsUnsafe(count, ref this);
if (this.LastErrorCode != OrigDecoderErrorCode.NoError)
if (this.LastErrorCode != GolangDecoderErrorCode.NoError)
{
result = 0;
return this.LastErrorCode;
@ -202,7 +202,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
result = result & ((1 << count) - 1);
this.Bits.UnreadBits -= count;
this.Bits.Mask >>= count;
return this.LastErrorCode = OrigDecoderErrorCode.NoError;
return this.LastErrorCode = GolangDecoderErrorCode.NoError;
}
/// <summary>
@ -210,8 +210,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// </summary>
/// <param name="huffmanTree">The huffman value</param>
/// <param name="result">The decoded <see cref="byte" /></param>
/// <returns>The <see cref="OrigDecoderErrorCode"/></returns>
public OrigDecoderErrorCode DecodeHuffmanUnsafe(ref OrigHuffmanTree huffmanTree, out int result)
/// <returns>The <see cref="GolangDecoderErrorCode"/></returns>
public GolangDecoderErrorCode DecodeHuffmanUnsafe(ref GolangHuffmanTree huffmanTree, out int result)
{
result = 0;
@ -224,9 +224,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
{
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];
if (v != 0)
@ -246,7 +246,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
}
int code = 0;
for (int i = 0; i < OrigHuffmanTree.MaxCodeLength; i++)
for (int i = 0; i < GolangHuffmanTree.MaxCodeLength; i++)
{
if (this.Bits.UnreadBits == 0)
{
@ -269,7 +269,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
if (code <= huffmanTree.MaxCodes[i])
{
result = huffmanTree.GetValue(code, i);
return this.LastErrorCode = OrigDecoderErrorCode.NoError;
return this.LastErrorCode = GolangDecoderErrorCode.NoError;
}
code <<= 1;
@ -279,7 +279,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
DecoderThrowHelper.ThrowImageFormatException.BadHuffmanCode();
// DUMMY RETURN! C# doesn't know we have thrown an exception!
return OrigDecoderErrorCode.NoError;
return GolangDecoderErrorCode.NoError;
}
/// <summary>
@ -295,11 +295,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// <summary>
/// Skips the next n bytes.
/// Does not throw, returns <see cref="OrigDecoderErrorCode"/> instead!
/// Does not throw, returns <see cref="GolangDecoderErrorCode"/> instead!
/// </summary>
/// <param name="count">The number of bytes to ignore.</param>
/// <returns>The <see cref="OrigDecoderErrorCode"/></returns>
public OrigDecoderErrorCode SkipUnsafe(int count)
/// <returns>The <see cref="GolangDecoderErrorCode"/></returns>
public GolangDecoderErrorCode SkipUnsafe(int count)
{
// Unread the overshot bytes, if any.
if (this.Bytes.UnreadableBytes != 0)
@ -328,13 +328,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
}
this.LastErrorCode = this.Bytes.FillUnsafe(this.InputStream);
if (this.LastErrorCode != OrigDecoderErrorCode.NoError)
if (this.LastErrorCode != GolangDecoderErrorCode.NoError)
{
return this.LastErrorCode;
}
}
return this.LastErrorCode = OrigDecoderErrorCode.NoError;
return this.LastErrorCode = GolangDecoderErrorCode.NoError;
}
/// <summary>
@ -374,8 +374,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// </summary>
/// <param name="t">Byte</param>
/// <param name="x">Read bits value</param>
/// <returns>The <see cref="OrigDecoderErrorCode"/></returns>
public OrigDecoderErrorCode ReceiveExtendUnsafe(int t, out int x)
/// <returns>The <see cref="GolangDecoderErrorCode"/></returns>
public GolangDecoderErrorCode ReceiveExtendUnsafe(int t, out int x)
{
this.LastErrorCode = this.Bits.ReceiveExtendUnsafe(t, ref this, out x);
return this.LastErrorCode;
@ -386,7 +386,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// </summary>
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>
/// Image decoder for generating an image out of a jpg stream.
/// </summary>
internal sealed class OrigJpegDecoder : IImageDecoder, IJpegDecoderOptions, IImageInfoDetector
internal sealed class GolangJpegDecoder : IImageDecoder, IJpegDecoderOptions, IImageInfoDetector
{
/// <summary>
/// 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));
using (var decoder = new OrigJpegDecoderCore(configuration, this))
using (var decoder = new GolangJpegDecoderCore(configuration, this))
{
return decoder.Decode<TPixel>(stream);
}
@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
{
Guard.NotNull(stream, nameof(stream));
using (var decoder = new OrigJpegDecoderCore(configuration, this))
using (var decoder = new GolangJpegDecoderCore(configuration, this))
{
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.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.MetaData;
using SixLabors.ImageSharp.MetaData.Profiles.Exif;
@ -19,7 +20,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
/// <summary>
/// Performs the jpeg decoding operation.
/// </summary>
internal sealed unsafe class OrigJpegDecoderCore : IRawJpegData
internal sealed unsafe class GolangJpegDecoderCore : IRawJpegData
{
/// <summary>
/// The maximum number of color components
@ -40,7 +41,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
#pragma warning disable SA1401 // FieldsMustBePrivate
/// <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
/// </summary>
public InputProcessor InputProcessor;
@ -79,11 +80,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
private AdobeMarker adobe;
/// <summary>
/// Initializes a new instance of the <see cref="OrigJpegDecoderCore" /> class.
/// Initializes a new instance of the <see cref="GolangJpegDecoderCore" /> class.
/// </summary>
/// <param name="configuration">The configuration.</param>
/// <param name="options">The options.</param>
public OrigJpegDecoderCore(Configuration configuration, IJpegDecoderOptions options)
public GolangJpegDecoderCore(Configuration configuration, IJpegDecoderOptions options)
{
this.IgnoreMetadata = options.IgnoreMetadata;
this.configuration = configuration ?? Configuration.Default;
@ -96,12 +97,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
/// <summary>
/// Gets the component array
/// </summary>
public OrigComponent[] Components { get; private set; }
public GolangComponent[] Components { get; private set; }
/// <summary>
/// Gets the huffman trees
/// </summary>
public OrigHuffmanTree[] HuffmanTrees { get; private set; }
public GolangHuffmanTree[] HuffmanTrees { get; private set; }
/// <inheritdoc />
public Block8x8F[] QuantizationTables { get; private set; }
@ -209,7 +210,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
{
if (this.Components != null)
{
foreach (OrigComponent component in this.Components)
foreach (GolangComponent component in this.Components)
{
component?.Dispose();
}
@ -219,7 +220,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
}
/// <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>
/// <param name="stream">The stream</param>
/// <param name="metadataOnly">Whether to decode metadata only.</param>
@ -231,14 +232,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
if (!metadataOnly)
{
this.HuffmanTrees = OrigHuffmanTree.CreateHuffmanTrees();
this.HuffmanTrees = GolangHuffmanTree.CreateHuffmanTrees();
this.QuantizationTables = new Block8x8F[MaxTq + 1];
}
// Check for the Start Of Image marker.
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.");
}
@ -302,12 +303,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
}
// End Of Image.
if (marker == OrigJpegConstants.Markers.EOI)
if (marker == JpegConstants.Markers.EOI)
{
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
// only occur between Entropy Coded Segments and not after the final ECS.
@ -329,14 +330,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
switch (marker)
{
case OrigJpegConstants.Markers.SOF0:
case OrigJpegConstants.Markers.SOF1:
case OrigJpegConstants.Markers.SOF2:
this.IsProgressive = marker == OrigJpegConstants.Markers.SOF2;
case JpegConstants.Markers.SOF0:
case JpegConstants.Markers.SOF1:
case JpegConstants.Markers.SOF2:
this.IsProgressive = marker == JpegConstants.Markers.SOF2;
this.ProcessStartOfFrameMarker(remaining, metadataOnly);
break;
case OrigJpegConstants.Markers.DHT:
case JpegConstants.Markers.DHT:
if (metadataOnly)
{
this.InputProcessor.Skip(remaining);
@ -347,7 +348,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
}
break;
case OrigJpegConstants.Markers.DQT:
case JpegConstants.Markers.DQT:
if (metadataOnly)
{
this.InputProcessor.Skip(remaining);
@ -358,7 +359,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
}
break;
case OrigJpegConstants.Markers.SOS:
case JpegConstants.Markers.SOS:
if (!metadataOnly)
{
this.ProcessStartOfScanMarker(remaining);
@ -377,7 +378,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
break;
case OrigJpegConstants.Markers.DRI:
case JpegConstants.Markers.DRI:
if (metadataOnly)
{
this.InputProcessor.Skip(remaining);
@ -388,21 +389,21 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
}
break;
case OrigJpegConstants.Markers.APP0:
case JpegConstants.Markers.APP0:
this.ProcessApplicationHeaderMarker(remaining);
break;
case OrigJpegConstants.Markers.APP1:
case JpegConstants.Markers.APP1:
this.ProcessApp1Marker(remaining);
break;
case OrigJpegConstants.Markers.APP2:
case JpegConstants.Markers.APP2:
this.ProcessApp2Marker(remaining);
break;
case OrigJpegConstants.Markers.APP14:
case JpegConstants.Markers.APP14:
this.ProcessApp14Marker(remaining);
break;
default:
if ((marker >= OrigJpegConstants.Markers.APP0 && marker <= OrigJpegConstants.Markers.APP15)
|| marker == OrigJpegConstants.Markers.COM)
if ((marker >= JpegConstants.Markers.APP0 && marker <= JpegConstants.Markers.APP15)
|| marker == JpegConstants.Markers.COM)
{
this.InputProcessor.Skip(remaining);
}
@ -449,6 +450,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
this.MetaData.VerticalResolution = verticalValue;
}
}
if (this.MetaData.IccProfile?.CheckIsValid() == false)
{
this.MetaData.IccProfile = null;
}
}
/// <summary>
@ -680,12 +686,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
if (!metadataOnly)
{
this.Components = new OrigComponent[this.ComponentCount];
this.Components = new GolangComponent[this.ComponentCount];
for (int i = 0; i < this.ComponentCount; i++)
{
byte componentIdentifier = this.Temp[6 + (3 * i)];
var component = new OrigComponent(componentIdentifier, i);
var component = new GolangComponent(componentIdentifier, i);
component.InitializeCoreData(this);
this.Components[i] = component;
}
@ -697,7 +703,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
this.ColorSpace = this.DeduceJpegColorSpace();
foreach (OrigComponent component in this.Components)
foreach (GolangComponent component in this.Components)
{
component.InitializeDerivedData(this.configuration.MemoryManager, this);
}
@ -721,18 +727,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
this.InputProcessor.ReadFull(this.Temp, 0, 17);
int tc = this.Temp[0] >> 4;
if (tc > OrigHuffmanTree.MaxTc)
if (tc > GolangHuffmanTree.MaxTc)
{
throw new ImageFormatException("Bad Tc value");
}
int th = this.Temp[0] & 0x0f;
if (th > OrigHuffmanTree.MaxTh)
if (th > GolangHuffmanTree.MaxTh)
{
throw new ImageFormatException("Bad Th value");
}
int huffTreeIndex = (tc * OrigHuffmanTree.ThRowSize) + th;
int huffTreeIndex = (tc * GolangHuffmanTree.ThRowSize) + th;
this.HuffmanTrees[huffTreeIndex].ProcessDefineHuffmanTablesMarkerLoop(
ref this.InputProcessor,
this.Temp,
@ -766,9 +772,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
/// </exception>
private void ProcessStartOfScanMarker(int remaining)
{
var scan = default(OrigJpegScanDecoder);
OrigJpegScanDecoder.InitStreamReading(&scan, this, remaining);
this.InputProcessor.Bits = default(Bits);
var scan = default(GolangJpegScanDecoder);
GolangJpegScanDecoder.InitStreamReading(&scan, this, remaining);
this.InputProcessor.Bits = default;
scan.DecodeBlocks(this);
}
@ -779,19 +785,19 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
case 1:
return JpegColorSpace.Grayscale;
case 3:
if (!this.isAdobe || this.adobe.ColorTransform == OrigJpegConstants.Adobe.ColorTransformYCbCr)
if (!this.isAdobe || this.adobe.ColorTransform == JpegConstants.Adobe.ColorTransformYCbCr)
{
return JpegColorSpace.YCbCr;
}
if (this.adobe.ColorTransform == OrigJpegConstants.Adobe.ColorTransformUnknown)
if (this.adobe.ColorTransform == JpegConstants.Adobe.ColorTransformUnknown)
{
return JpegColorSpace.RGB;
}
break;
case 4:
if (this.adobe.ColorTransform == OrigJpegConstants.Adobe.ColorTransformYcck)
if (this.adobe.ColorTransform == JpegConstants.Adobe.ColorTransformYcck)
{
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.
// Licensed under the Apache License, Version 2.0.
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
using System.Collections.Generic;
namespace SixLabors.ImageSharp.Formats.Jpeg
{
/// <summary>
/// Contains jpeg constant values
/// Contains jpeg constant values defined in the specification.
/// </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>
/// Contains marker specific constants
/// </summary>
public static class Markers
// ReSharper disable InconsistentNaming
internal static class Markers
{
/// <summary>
/// The prefix used for all markers.
/// </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>
/// The Start of Image marker
@ -161,7 +183,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
/// <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.
/// 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;
@ -193,27 +216,27 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
/// </remarks>
/// </summary>
public const byte RST7 = 0xD7;
}
/// <summary>
/// Contains Adobe specific constants
/// </summary>
internal static class Adobe
{
/// <summary>
/// Contains Adobe specific markers
/// The color transform is unknown.(RGB or CMYK)
/// </summary>
public static class Adobe
{
/// <summary>
/// The color transform is unknown.(RGB or CMYK)
/// </summary>
public const byte ColorTransformUnknown = 0;
public const byte ColorTransformUnknown = 0;
/// <summary>
/// The color transform is YCbCr (luminance, red chroma, blue chroma)
/// </summary>
public const byte ColorTransformYCbCr = 1;
/// <summary>
/// The color transform is YCbCr (luminance, red chroma, blue chroma)
/// </summary>
public const byte ColorTransformYCbCr = 1;
/// <summary>
/// The color transform is YCCK (luminance, red chroma, blue chroma, keyline)
/// </summary>
public const byte ColorTransformYcck = 2;
}
/// <summary>
/// The color transform is YCCK (luminance, red chroma, blue chroma, keyline)
/// </summary>
public const byte ColorTransformYcck = 2;
}
}
}

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

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

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

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0.
using System.IO;
using SixLabors.ImageSharp.Formats.Jpeg.GolangPort;
using SixLabors.ImageSharp.PixelFormats;
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.
// Licensed under the Apache License, Version 2.0.
using System.Buffers;
using System.IO;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Formats.Jpeg.Common;
using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components;
using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder;
using SixLabors.ImageSharp.MetaData.Profiles.Exif;
using SixLabors.ImageSharp.MetaData.Profiles.Icc;
using SixLabors.ImageSharp.PixelFormats;
using Block8x8F = SixLabors.ImageSharp.Formats.Jpeg.Common.Block8x8F;
namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
namespace SixLabors.ImageSharp.Formats.Jpeg
{
/// <summary>
/// Image encoder for writing an image to a stream as a jpeg.
@ -58,7 +55,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
/// </summary>
private static readonly byte[] SosHeaderYCbCr =
{
OrigJpegConstants.Markers.XFF, OrigJpegConstants.Markers.SOS,
JpegConstants.Markers.XFF, JpegConstants.Markers.SOS,
// Marker
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>
/// A scratch buffer to reduce allocations.
/// </summary>
@ -190,7 +182,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
Guard.NotNull(image, nameof(image));
Guard.NotNull(stream, nameof(stream));
ushort max = OrigJpegConstants.MaxLength;
ushort max = JpegConstants.MaxLength;
if (image.Width >= max || image.Height >= max)
{
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);
// Write the End Of Image marker.
this.buffer[0] = OrigJpegConstants.Markers.XFF;
this.buffer[1] = OrigJpegConstants.Markers.EOI;
this.buffer[0] = JpegConstants.Markers.XFF;
this.buffer[1] = JpegConstants.Markers.EOI;
stream.Write(this.buffer, 0, 2);
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.)
// (Partially done with YCbCrForwardConverter<TPixel>)
Block8x8F temp1 = default(Block8x8F);
Block8x8F temp2 = default(Block8x8F);
Block8x8F temp1 = default;
Block8x8F temp2 = default;
Block8x8F onStackLuminanceQuantTable = this.luminanceQuantTable;
Block8x8F onStackChrominanceQuantTable = this.chrominanceQuantTable;
ZigZag unzig = ZigZag.CreateUnzigTable();
var unzig = ZigZag.CreateUnzigTable();
// ReSharper disable once InconsistentNaming
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)
{
@ -437,12 +429,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
private void WriteApplicationHeader(short horizontalResolution, short verticalResolution)
{
// Write the start of image marker. Markers are always prefixed with with 0xff.
this.buffer[0] = OrigJpegConstants.Markers.XFF;
this.buffer[1] = OrigJpegConstants.Markers.SOI;
this.buffer[0] = JpegConstants.Markers.XFF;
this.buffer[1] = JpegConstants.Markers.SOI;
// Write the JFIF headers
this.buffer[2] = OrigJpegConstants.Markers.XFF;
this.buffer[3] = OrigJpegConstants.Markers.APP0; // Application Marker
this.buffer[2] = JpegConstants.Markers.XFF;
this.buffer[3] = JpegConstants.Markers.APP0; // Application Marker
this.buffer[4] = 0x00;
this.buffer[5] = 0x10;
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);
// Emit the AC components.
HuffIndex h = (HuffIndex)((2 * (int)index) + 1);
var h = (HuffIndex)((2 * (int)index) + 1);
int runLength = 0;
for (int zig = 1; zig < Block8x8F.Size; zig++)
@ -556,7 +548,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
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++)
{
HuffmanSpec spec = specs[i];
@ -590,7 +582,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
{
// Marker + quantization table lengths
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.
// 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;
this.buffer[0] = OrigJpegConstants.Markers.XFF;
this.buffer[1] = OrigJpegConstants.Markers.APP1; // Application Marker
this.buffer[0] = JpegConstants.Markers.XFF;
this.buffer[1] = JpegConstants.Markers.APP1; // Application Marker
this.buffer[2] = (byte)((length >> 8) & 0xFF);
this.buffer[3] = (byte)(length & 0xFF);
@ -686,8 +678,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
dataLength -= length;
this.buffer[0] = OrigJpegConstants.Markers.XFF;
this.buffer[1] = OrigJpegConstants.Markers.APP2; // Application Marker
this.buffer[0] = JpegConstants.Markers.XFF;
this.buffer[1] = JpegConstants.Markers.APP2; // Application Marker
int markerLength = length + 16;
this.buffer[2] = (byte)((markerLength >> 8) & 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.
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[1] = (byte)(height >> 8);
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>
{
// 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 cr = default(BlockQuad);
Block8x8F* cbPtr = (Block8x8F*)cb.Data;
Block8x8F* crPtr = (Block8x8F*)cr.Data;
BlockQuad cb = default;
BlockQuad cr = default;
var cbPtr = (Block8x8F*)cb.Data;
var crPtr = (Block8x8F*)cr.Data;
Block8x8F temp1 = default(Block8x8F);
Block8x8F temp2 = default(Block8x8F);
Block8x8F temp1 = default;
Block8x8F temp2 = default;
Block8x8F onStackLuminanceQuantTable = this.luminanceQuantTable;
Block8x8F onStackChrominanceQuantTable = this.chrominanceQuantTable;
ZigZag unzig = ZigZag.CreateUnzigTable();
var unzig = ZigZag.CreateUnzigTable();
var pixelConverter = YCbCrForwardConverter<TPixel>.Create();
@ -902,7 +894,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
private void WriteMarkerHeader(byte marker, int length)
{
// 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[2] = (byte)(length >> 8);
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.
using System.Collections.Generic;
using SixLabors.ImageSharp.Formats.Jpeg.GolangPort;
namespace SixLabors.ImageSharp.Formats.Jpeg
{
@ -18,9 +17,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
public string DefaultMimeType => "image/jpeg";
/// <inheritdoc/>
public IEnumerable<string> MimeTypes => OrigJpegConstants.MimeTypes;
public IEnumerable<string> MimeTypes => JpegConstants.MimeTypes;
/// <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.Runtime.CompilerServices;
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.Primitives;

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

@ -7,7 +7,8 @@ using System.Diagnostics;
#endif
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Formats.Jpeg.Common;
using SixLabors.ImageSharp.Formats.Jpeg.Components;
namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
{
@ -136,7 +137,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
byte marker = fileMarker.Marker;
// 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;
}
@ -452,7 +453,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
this.endOfStreamReached = true;
return false;
case PdfJsJpegConstants.Markers.Prefix:
case JpegConstants.Markers.XFF:
int nextByte = stream.ReadByte();
if (nextByte == -0x1)

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

@ -7,8 +7,9 @@ using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
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.Memory;
using SixLabors.ImageSharp.MetaData;
@ -98,15 +99,23 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
/// </summary>
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>
/// Gets the image width
/// </summary>
public int ImageWidth { get; private set; }
public int ImageWidth => this.ImageSizeInPixels.Width;
/// <summary>
/// Gets the image height
/// </summary>
public int ImageHeight { get; private set; }
public int ImageHeight => this.ImageSizeInPixels.Height;
/// <summary>
/// Gets the color depth, in number of bits per pixel.
@ -128,17 +137,19 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
/// </summary>
public ImageMetaData MetaData { get; private set; }
/// <inheritdoc/>
public Size ImageSizeInPixels => new Size(this.ImageWidth, this.ImageHeight);
/// <inheritdoc/>
public int ComponentCount { get; private set; }
/// <inheritdoc/>
public JpegColorSpace ColorSpace { get; private set; }
/// <summary>
/// Gets the components.
/// </summary>
public PdfJsFrameComponent[] Components => this.Frame.Components;
/// <inheritdoc/>
public IEnumerable<IJpegComponent> Components => this.Frame.Components;
IEnumerable<IJpegComponent> IRawJpegData.Components => this.Components;
/// <inheritdoc/>
public Block8x8F[] QuantizationTables { get; private set; }
@ -155,20 +166,20 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
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:
// "Any marker may optionally be preceded by any number of fill bytes, which are bytes assigned code 0xFF."
int m = marker[1];
while (m == PdfJsJpegConstants.Markers.Prefix)
while (m == JpegConstants.Markers.XFF)
{
int suffix = stream.ReadByte();
if (suffix == -1)
{
return new PdfJsFileMarker(PdfJsJpegConstants.Markers.EOI, stream.Length - 2);
return new PdfJsFileMarker(JpegConstants.Markers.EOI, stream.Length - 2);
}
m = suffix;
@ -190,7 +201,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
where TPixel : struct, IPixel<TPixel>
{
this.ParseStream(stream);
this.AssignResolution();
this.InitDerivedMetaDataProperties();
return this.PostProcessIntoImage<TPixel>();
}
@ -201,7 +212,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
public IImageInfo Identify(Stream stream)
{
this.ParseStream(stream, true);
this.AssignResolution();
this.InitDerivedMetaDataProperties();
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.
this.InputStream.Read(this.markerBuffer, 0, 2);
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.");
}
@ -236,7 +247,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
this.fastACTables = new FastACTables(this.configuration.MemoryManager);
}
while (fileMarker.Marker != PdfJsJpegConstants.Markers.EOI)
while (fileMarker.Marker != JpegConstants.Markers.EOI)
{
if (!fileMarker.Invalid)
{
@ -245,13 +256,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
switch (fileMarker.Marker)
{
case PdfJsJpegConstants.Markers.SOF0:
case PdfJsJpegConstants.Markers.SOF1:
case PdfJsJpegConstants.Markers.SOF2:
case JpegConstants.Markers.SOF0:
case JpegConstants.Markers.SOF1:
case JpegConstants.Markers.SOF2:
this.ProcessStartOfFrameMarker(remaining, fileMarker, metadataOnly);
break;
case PdfJsJpegConstants.Markers.SOS:
case JpegConstants.Markers.SOS:
if (!metadataOnly)
{
this.ProcessStartOfScanMarker();
@ -264,7 +275,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
return;
}
case PdfJsJpegConstants.Markers.DHT:
case JpegConstants.Markers.DHT:
if (metadataOnly)
{
this.InputStream.Skip(remaining);
@ -276,7 +287,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
break;
case PdfJsJpegConstants.Markers.DQT:
case JpegConstants.Markers.DQT:
if (metadataOnly)
{
this.InputStream.Skip(remaining);
@ -288,7 +299,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
break;
case PdfJsJpegConstants.Markers.DRI:
case JpegConstants.Markers.DRI:
if (metadataOnly)
{
this.InputStream.Skip(remaining);
@ -300,38 +311,38 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
break;
case PdfJsJpegConstants.Markers.APP0:
case JpegConstants.Markers.APP0:
this.ProcessApplicationHeaderMarker(remaining);
break;
case PdfJsJpegConstants.Markers.APP1:
case JpegConstants.Markers.APP1:
this.ProcessApp1Marker(remaining);
break;
case PdfJsJpegConstants.Markers.APP2:
case JpegConstants.Markers.APP2:
this.ProcessApp2Marker(remaining);
break;
case PdfJsJpegConstants.Markers.APP3:
case PdfJsJpegConstants.Markers.APP4:
case PdfJsJpegConstants.Markers.APP5:
case PdfJsJpegConstants.Markers.APP6:
case PdfJsJpegConstants.Markers.APP7:
case PdfJsJpegConstants.Markers.APP8:
case PdfJsJpegConstants.Markers.APP9:
case PdfJsJpegConstants.Markers.APP10:
case PdfJsJpegConstants.Markers.APP11:
case PdfJsJpegConstants.Markers.APP12:
case PdfJsJpegConstants.Markers.APP13:
case JpegConstants.Markers.APP3:
case JpegConstants.Markers.APP4:
case JpegConstants.Markers.APP5:
case JpegConstants.Markers.APP6:
case JpegConstants.Markers.APP7:
case JpegConstants.Markers.APP8:
case JpegConstants.Markers.APP9:
case JpegConstants.Markers.APP10:
case JpegConstants.Markers.APP11:
case JpegConstants.Markers.APP12:
case JpegConstants.Markers.APP13:
this.InputStream.Skip(remaining);
break;
case PdfJsJpegConstants.Markers.APP14:
case JpegConstants.Markers.APP14:
this.ProcessApp14Marker(remaining);
break;
case PdfJsJpegConstants.Markers.APP15:
case PdfJsJpegConstants.Markers.COM:
case JpegConstants.Markers.APP15:
case JpegConstants.Markers.COM:
this.InputStream.Skip(remaining);
break;
}
@ -370,11 +381,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
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;
}
else if (this.adobe.ColorTransform == PdfJsJpegConstants.Markers.Adobe.ColorTransformUnknown)
if (this.adobe.ColorTransform == JpegConstants.Adobe.ColorTransformUnknown)
{
return JpegColorSpace.RGB;
}
@ -382,7 +394,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
if (this.ComponentCount == 4)
{
return this.adobe.ColorTransform == PdfJsJpegConstants.Markers.Adobe.ColorTransformYcck
return this.adobe.ColorTransform == JpegConstants.Adobe.ColorTransformYcck
? JpegColorSpace.Ycck
: JpegColorSpace.Cmyk;
}
@ -391,13 +403,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
}
/// <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>
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)
{
this.MetaData.HorizontalResolution = this.jFif.XDensity;
@ -419,6 +428,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
this.MetaData.VerticalResolution = verticalValue;
}
}
if (this.MetaData.IccProfile?.CheckIsValid() == false)
{
this.MetaData.IccProfile = null;
}
}
/// <summary>
@ -630,59 +644,58 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
this.Frame = new PdfJsFrame
{
Extended = frameMarker.Marker == PdfJsJpegConstants.Markers.SOF1,
Progressive = frameMarker.Marker == PdfJsJpegConstants.Markers.SOF2,
Extended = frameMarker.Marker == JpegConstants.Markers.SOF1,
Progressive = frameMarker.Marker == JpegConstants.Markers.SOF2,
Precision = this.temp[0],
Scanlines = (short)((this.temp[1] << 8) | this.temp[2]),
SamplesPerLine = (short)((this.temp[3] << 8) | this.temp[4]),
ComponentCount = this.temp[5]
};
this.ImageSizeInPixels = new Size(this.Frame.SamplesPerLine, this.Frame.Scanlines);
int maxH = 0;
int maxV = 0;
int index = 6;
this.ComponentCount = this.Frame.ComponentCount;
if (!metadataOnly)
{
// No need to pool this. They max out at 4
this.Frame.ComponentIds = new byte[this.Frame.ComponentCount];
this.Frame.Components = new PdfJsFrameComponent[this.Frame.ComponentCount];
}
for (int i = 0; i < this.Frame.ComponentCount; i++)
{
byte hv = this.temp[index + 1];
int h = hv >> 4;
int v = hv & 15;
this.ColorSpace = this.DeduceJpegColorSpace();
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)
{
maxV = v;
}
if (maxH < h)
{
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);
this.Frame.Components[i] = component;
this.Frame.ComponentIds[i] = component.Id;
}
index += 3;
}
this.Frame.MaxHorizontalFactor = maxH;
this.Frame.MaxVerticalFactor = maxV;
index += 3;
}
if (!metadataOnly)
{
this.Frame.MaxHorizontalFactor = maxH;
this.Frame.MaxVerticalFactor = maxV;
this.ColorSpace = this.DeduceJpegColorSpace();
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>()
where TPixel : struct, IPixel<TPixel>
{
this.ColorSpace = this.DeduceJpegColorSpace();
using (var postProcessor = new JpegImagePostProcessor(this.configuration.MemoryManager, this))
{
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.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory;
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="result">The filtered scanline result.</param>
[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.
result[0] = 0;
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>
{
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 rgba = default(Rgba32);
@ -865,10 +865,9 @@ namespace SixLabors.ImageSharp.Formats.Png
for (int x = 0; x < this.header.Width; x++)
{
int index = newScanline[x];
int pixelOffset = index * 3;
rgba.A = this.paletteAlpha.Length > index ? this.paletteAlpha[index] : (byte)255;
rgba.Rgb = pal.GetRgb24(pixelOffset);
rgba.Rgb = pal[index];
color.PackFromRgba32(rgba);
row[x] = color;
@ -881,9 +880,8 @@ namespace SixLabors.ImageSharp.Formats.Png
for (int x = 0; x < this.header.Width; x++)
{
int index = newScanline[x];
int pixelOffset = index * 3;
rgba.Rgb = pal.GetRgb24(pixelOffset);
rgba.Rgb = pal[index];
color.PackFromRgba32(rgba);
row[x] = color;
@ -946,6 +944,7 @@ namespace SixLabors.ImageSharp.Formats.Png
ReadOnlySpan<byte> newScanline = ToArrayByBitsLength(scanlineBuffer, this.bytesPerScanline, this.header.BitDepth);
var rgba = default(Rgba32);
Span<Rgb24> pal = MemoryMarshal.Cast<byte, Rgb24>(this.palette);
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++)
{
int index = newScanline[o];
int offset = index * 3;
rgba.A = this.paletteAlpha.Length > index ? this.paletteAlpha[index] : (byte)255;
rgba.Rgb = this.palette.GetRgb24(offset);
rgba.Rgb = pal[index];
color.PackFromRgba32(rgba);
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++)
{
int index = newScanline[o];
int offset = index * 3;
rgba.Rgb = this.palette.GetRgb24(offset);
rgba.Rgb = pal[index];
color.PackFromRgba32(rgba);
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>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <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>
=> LoadPixelData(Configuration.Default, data, width, height);
@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp
/// <param name="height">The height of the final image.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <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>
=> LoadPixelData<TPixel>(Configuration.Default, data, width, height);
@ -72,7 +72,7 @@ namespace SixLabors.ImageSharp
/// <returns>A new <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> LoadPixelData<TPixel>(Configuration config, byte[] data, int width, int height)
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>
/// 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>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <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>
=> 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)
where TPixel : struct, IPixel<TPixel>
{
return LoadPixelData(config, data.AsSpan(), width, height);
return LoadPixelData(config, new ReadOnlySpan<TPixel>(data), width, height);
}
/// <summary>
@ -111,7 +111,7 @@ namespace SixLabors.ImageSharp
/// <param name="height">The height of the final image.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <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>
{
int count = width * height;

90
src/ImageSharp/ImageExtensions.cs

@ -4,13 +4,11 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp
{
@ -21,18 +19,18 @@ namespace SixLabors.ImageSharp
{
#if !NETSTANDARD1_1
/// <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>
/// <typeparam name="TPixel">The Pixel format.</typeparam>
/// <param name="source">The source image</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The source image.</param>
/// <param name="filePath">The file path to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static void Save<TPixel>(this Image<TPixel> source, string filePath)
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);
if (format == null)
{
@ -64,13 +62,13 @@ namespace SixLabors.ImageSharp
}
/// <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>
/// <typeparam name="TPixel">The Pixel format.</typeparam>
/// <param name="source">The source image</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The source image.</param>
/// <param name="filePath">The file path to save the image to.</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)
where TPixel : struct, IPixel<TPixel>
{
@ -83,13 +81,13 @@ namespace SixLabors.ImageSharp
#endif
/// <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>
/// <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="format">The format to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
/// <param name="format">The format to save the image in.</param>
/// <exception cref="ArgumentNullException">Thrown if the stream is null.</exception>
public static void Save<TPixel>(this Image<TPixel> source, Stream stream, IImageFormat format)
where TPixel : struct, IPixel<TPixel>
{
@ -113,67 +111,67 @@ namespace SixLabors.ImageSharp
}
/// <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>
/// <typeparam name="TPixel">The Pixel format.</typeparam>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The source image</param>
/// <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)
where TPixel : struct, IPixel<TPixel>
=> MemoryMarshal.AsBytes(source.GetPixelSpan()).ToArray();
/// <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>
/// <typeparam name="TPixel">The Pixel format.</typeparam>
/// <param name="source">The source image</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The source image.</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)
where TPixel : struct, IPixel<TPixel>
=> SavePixelData(source, MemoryMarshal.Cast<byte, TPixel>(buffer.AsSpan()));
/// <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>
/// <typeparam name="TPixel">The Pixel format.</typeparam>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The source image</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)
where TPixel : struct, IPixel<TPixel>
=> SavePixelData(source, buffer.AsSpan());
/// <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>
/// <typeparam name="TPixel">The Pixel format.</typeparam>
/// <param name="source">The source image</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The source image.</param>
/// <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)
where TPixel : struct, IPixel<TPixel>
=> source.Frames.RootFrame.SavePixelData();
/// <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>
/// <typeparam name="TPixel">The Pixel format.</typeparam>
/// <param name="source">The source image</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The source image.</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)
where TPixel : struct, IPixel<TPixel>
=> source.Frames.RootFrame.SavePixelData(buffer);
/// <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>
/// <typeparam name="TPixel">The Pixel format.</typeparam>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The source image</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)
where TPixel : struct, IPixel<TPixel>
=> source.Frames.RootFrame.SavePixelData(buffer);
@ -182,7 +180,7 @@ namespace SixLabors.ImageSharp
/// Returns a Base64 encoded string from the given image.
/// </summary>
/// <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="format">The format.</param>
/// <returns>The <see cref="string"/></returns>
@ -198,24 +196,24 @@ namespace SixLabors.ImageSharp
}
/// <summary>
/// Saves the raw image to the given bytes.
/// Writes the raw image bytes to the given byte span.
/// </summary>
/// <typeparam name="TPixel">The Pixel format.</typeparam>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <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>
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>
=> source.Frames.RootFrame.SavePixelData(MemoryMarshal.Cast<byte, TPixel>(buffer));
/// <summary>
/// Saves the raw image to the given bytes.
/// Writes the raw image pixels to the given TPixel span.
/// </summary>
/// <typeparam name="TPixel">The Pixel format.</typeparam>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <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>
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>
{
Span<TPixel> sourceBuffer = source.GetPixelSpan();
@ -224,4 +222,4 @@ namespace SixLabors.ImageSharp
sourceBuffer.CopyTo(buffer);
}
}
}
}

15
src/ImageSharp/ImageFrame.LoadPixelData.cs

@ -4,7 +4,6 @@
using System;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp
@ -12,37 +11,37 @@ namespace SixLabors.ImageSharp
/// <content>
/// Adds static methods allowing the creation of new image from raw pixel data.
/// </content>
internal static partial class ImageFrame
internal static class ImageFrame
{
/// <summary>
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the given byte array in <typeparamref name="TPixel"/> format.
/// </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="width">The width of the final image.</param>
/// <param name="height">The height of the final image.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <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>
=> LoadPixelData(memoryManager, MemoryMarshal.Cast<byte, TPixel>(data), width, height);
=> LoadPixelData(configuration, MemoryMarshal.Cast<byte, TPixel>(data), width, height);
/// <summary>
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the raw <typeparamref name="TPixel"/> data.
/// </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="width">The width of the final image.</param>
/// <param name="height">The height of the final image.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <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>
{
int count = width * height;
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());

12
src/ImageSharp/ImageFrameCollection.cs

@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp
internal ImageFrameCollection(Image<TPixel> parent, IEnumerable<ImageFrame<TPixel>> frames)
{
Guard.NotNull(parent, nameof(parent));
Guard.NotNullOrEmpty(frames, nameof(frames));
Guard.NotNull(frames, nameof(frames));
this.parent = parent;
@ -42,6 +42,12 @@ namespace SixLabors.ImageSharp
this.ValidateFrame(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>
@ -110,8 +116,8 @@ namespace SixLabors.ImageSharp
Guard.NotNull(source, nameof(source));
var frame = ImageFrame.LoadPixelData(
this.parent.GetMemoryManager(),
new Span<TPixel>(source),
this.parent.GetConfiguration(),
new ReadOnlySpan<TPixel>(source),
this.RootFrame.Width,
this.RootFrame.Height);
this.frames.Add(frame);

80
src/ImageSharp/ImageFrame{TPixel}.cs

@ -4,7 +4,6 @@
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
@ -19,53 +18,61 @@ namespace SixLabors.ImageSharp
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
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;
/// <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="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="height">The height of the image in pixels.</param>
internal ImageFrame(MemoryManager memoryManager, int width, int height)
: this(memoryManager, width, height, new ImageFrameMetaData()) {
internal ImageFrame(Configuration configuration, int width, int height)
: 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>
/// 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="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="height">The height of the image in pixels.</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>
/// Initializes a new instance of the <see cref="ImageFrame{TPixel}" /> class.
/// </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="height">The height of the image in pixels.</param>
/// <param name="backgroundColor">The color to clear the image with.</param>
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>
/// Initializes a new instance of the <see cref="ImageFrame{TPixel}" /> class.
/// </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="height">The height of the image in pixels.</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.NotNull(metaData, nameof(metaData));
this.configuration = configuration;
this.MemoryManager = configuration.MemoryManager;
this.PixelBuffer = this.MemoryManager.Allocate2D<TPixel>(width, height, false);
this.Clear(configuration.ParallelOptions, backgroundColor);
this.MetaData = metaData;
this.Clear(configuration.ParallelOptions, backgroundColor);
}
/// <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="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="configuration">The configuration which allows altering default behaviour or extending the library.</param>
/// <param name="source">The source.</param>
internal ImageFrame(MemoryManager memoryManager, ImageFrame<TPixel> source)
internal ImageFrame(Configuration configuration, ImageFrame<TPixel> source)
{
this.MemoryManager = memoryManager;
this.PixelBuffer = memoryManager.Allocate2D<TPixel>(source.PixelBuffer.Width, source.PixelBuffer.Height);
Guard.NotNull(configuration, nameof(configuration));
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);
this.MetaData = source.MetaData.Clone();
}
@ -276,13 +278,12 @@ namespace SixLabors.ImageSharp
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(
0,
this.Height,
Configuration.Default,
this.configuration,
this.Width,
(int y, IBuffer<Vector4> tempRowBuffer) =>
{
@ -302,12 +303,13 @@ namespace SixLabors.ImageSharp
/// </summary>
/// <param name="parallelOptions">The parallel options.</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(
0,
this.Height,
parallelOptions,
(int y) =>
y =>
{
Span<TPixel> targetRow = this.GetPixelRowSpan(y);
targetRow.Fill(value);
@ -320,7 +322,7 @@ namespace SixLabors.ImageSharp
/// <returns>The <see cref="ImageFrame{TPixel}"/></returns>
internal ImageFrame<TPixel> Clone()
{
return new ImageFrame<TPixel>(this.MemoryManager, this);
return new ImageFrame<TPixel>(this.configuration, this);
}
/// <inheritdoc/>

21
src/ImageSharp/ImageSharp.csproj

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

2
src/ImageSharp/Image{TPixel}.cs

@ -78,7 +78,7 @@ namespace SixLabors.ImageSharp
this.configuration = configuration ?? Configuration.Default;
this.PixelType = new PixelTypeInfo(Unsafe.SizeOf<TPixel>() * 8);
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>

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>
public ImageProperty(string name, string value)
{
Guard.NotNullOrEmpty(name, nameof(name));
Guard.NotNullOrWhiteSpace(name, nameof(name));
this.Name = name;
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);
}
exifValue = new ExifValue(tag, dataType, value, isArray: value != null && numberOfComponents > 1);
exifValue = new ExifValue(tag, dataType, value, isArray: value != null && numberOfComponents != 1);
return true;
}

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

@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Text;
namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
@ -11,7 +10,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// </summary>
internal sealed partial class IccDataReader
{
private static readonly bool IsLittleEndian = BitConverter.IsLittleEndian;
private static readonly Encoding AsciiEncoding = Encoding.GetEncoding("ASCII");
/// <summary>
@ -34,6 +32,14 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
this.data = data;
}
/// <summary>
/// Gets the length in bytes of the raw data
/// </summary>
public int DataLength
{
get { return this.data.Length; }
}
/// <summary>
/// Sets the reading position to the given value
/// </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.
/// </summary>
/// <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)
{
Guard.NotNull(other, nameof(other));
// TODO: Do we need to copy anything else?
if (other.data != null)
{
this.data = new byte[other.data.Length];
Buffer.BlockCopy(other.data, 0, this.data, 0, other.data.Length);
}
this.data = other.ToByteArray();
}
/// <summary>
@ -108,7 +103,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
#if !NETSTANDARD1_1
/// <summary>
/// Calculates the MD5 hash value of an ICC profile header
/// Calculates the MD5 hash value of an ICC profile
/// </summary>
/// <param name="data">The data of which to calculate the hash value</param>
/// <returns>The calculated hash</returns>
@ -117,22 +112,38 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
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");
byte[] header = new byte[128];
Buffer.BlockCopy(data, 0, header, 0, 128);
const int profileFlagPos = 44;
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())
{
// Zero out some values
Array.Clear(header, 44, 4); // Profile flags
Array.Clear(header, 64, 4); // Rendering Intent
Array.Clear(header, 84, 16); // Profile ID
// Calculate hash
byte[] hash = md5.ComputeHash(data);
// Read values from hash
var reader = new IccDataReader(hash);
return reader.ReadProfileId();
try
{
// Zero out some values
Array.Clear(data, profileFlagPos, 4);
Array.Clear(data, renderingIntentPos, 4);
Array.Clear(data, profileIdPos, 16);
// Calculate hash
byte[] hash = md5.ComputeHash(data);
// 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);
}
/// <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>
/// Converts this instance to a byte array.
/// </summary>
/// <returns>The <see cref="T:byte[]"/></returns>
public byte[] ToByteArray()
{
var writer = new IccWriter();
return writer.Write(this);
if (this.data != null)
{
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()

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

@ -84,27 +84,36 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
private IccTagDataEntry[] ReadTagData(IccDataReader 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>();
for (int i = 0; i < tagTable.Length; i++)
foreach (IccTagTableEntry tag in tagTable)
{
IccTagDataEntry entry;
uint offset = tagTable[i].Offset;
if (store.ContainsKey(offset))
if (store.ContainsKey(tag.Offset))
{
entry = store[offset];
entry = store[tag.Offset];
}
else
{
entry = reader.ReadTagDataEntry(tagTable[i]);
store.Add(offset, entry);
try
{
entry = reader.ReadTagDataEntry(tag);
}
catch
{
// Ignore tags that could not be read
continue;
}
store.Add(tag.Offset, entry);
}
entry.TagSignature = tagTable[i].Signature;
entries[i] = entry;
entry.TagSignature = tag.Signature;
entries.Add(entry);
}
return entries;
return entries.ToArray();
}
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
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++)
{
uint tagSignature = reader.ReadUInt32();
uint tagOffset = 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.
/// </para>
/// </summary>
[StructLayout(LayoutKind.Sequential)]
[StructLayout(LayoutKind.Explicit)]
public struct Bgr24 : IPixel<Bgr24>
{
/// <summary>
/// The blue component.
/// </summary>
[FieldOffset(0)]
public byte B;
/// <summary>
/// The green component.
/// </summary>
[FieldOffset(1)]
public byte G;
/// <summary>
/// The red component.
/// </summary>
[FieldOffset(2)]
public byte R;
/// <summary>

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

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers.Binary;
using System.Globalization;
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>
public static TPixel FromHex(string hex)
{
Guard.NotNullOrEmpty(hex, nameof(hex));
Guard.NotNullOrWhiteSpace(hex, nameof(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));
}
TPixel result = default;
var rgba = new Rgba32(
(byte)(packedValue >> 24),
(byte)(packedValue >> 16),
(byte)(packedValue >> 8),
(byte)(packedValue >> 0));
var rgba = new Rgba32(BinaryPrimitives.ReverseEndianness(packedValue));
result.PackFromRgba32(rgba);
return result;
@ -76,7 +73,10 @@ namespace SixLabors.ImageSharp.PixelFormats
/// </returns>
private static string ToRgbaHex(string hex)
{
hex = hex.StartsWith("#") ? hex.Substring(1) : hex;
if (hex[0] == '#')
{
hex = hex.Substring(1);
}
if (hex.Length == 8)
{
@ -93,12 +93,12 @@ namespace SixLabors.ImageSharp.PixelFormats
return null;
}
string red = char.ToString(hex[0]);
string green = char.ToString(hex[1]);
string blue = char.ToString(hex[2]);
string alpha = hex.Length == 3 ? "F" : char.ToString(hex[3]);
char r = hex[0];
char g = hex[1];
char b = hex[2];
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.
/// </para>
/// </summary>
[StructLayout(LayoutKind.Sequential)]
[StructLayout(LayoutKind.Explicit)]
public struct Rgb24 : IPixel<Rgb24>
{
/// <summary>
/// The red component.
/// </summary>
[FieldOffset(0)]
public byte R;
/// <summary>
/// The green component.
/// </summary>
[FieldOffset(1)]
public byte G;
/// <summary>
/// The blue component.
/// </summary>
[FieldOffset(2)]
public byte B;
/// <summary>

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

@ -157,13 +157,13 @@ namespace SixLabors.ImageSharp.Processing.Quantization.FrameQuantizers
}
finally
{
this.vwt.Dispose();
this.vmr.Dispose();
this.vmg.Dispose();
this.vmb.Dispose();
this.vma.Dispose();
this.m2.Dispose();
this.tag.Dispose();
this.vwt?.Dispose();
this.vmr?.Dispose();
this.vmg?.Dispose();
this.vmb?.Dispose();
this.vma?.Dispose();
this.m2?.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
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
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)
{
// 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
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
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
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.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors;
using SixLabors.ImageSharp.Processing.Transforms.Resamplers;
using SixLabors.Primitives;
@ -56,12 +56,12 @@ namespace SixLabors.ImageSharp.Processing.Transforms.Processors
Guard.MustBeGreaterThan(tempWidth, 0, nameof(tempWidth));
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.Width = locationBounds.size.Width;
this.Height = locationBounds.size.Height;
this.ResizeRectangle = locationBounds.rectangle;
this.Width = size.Width;
this.Height = size.Height;
this.ResizeRectangle = rectangle;
this.Compand = options.Compand;
}
@ -167,13 +167,13 @@ namespace SixLabors.ImageSharp.Processing.Transforms.Processors
float center = ((i + .5F) * ratio) - .5F;
// Keep inside bounds.
int left = (int)Math.Ceiling(center - radius);
int left = (int)MathF.Ceiling(center - radius);
if (left < 0)
{
left = 0;
}
int right = (int)Math.Floor(center + radius);
int right = (int)MathF.Floor(center + radius);
if (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)
{
// 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
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
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());
return;
}
@ -306,7 +306,7 @@ namespace SixLabors.ImageSharp.Processing.Transforms.Processors
source.Width,
(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<Vector4> tempRowSpan = tempRowBuffer.Span;
@ -317,7 +317,7 @@ namespace SixLabors.ImageSharp.Processing.Transforms.Processors
for (int x = minX; x < maxX; x++)
{
WeightsWindow window = this.horizontalWeights.Weights[x - startX];
firstPassRow[x] = window.ComputeExpandedWeightedRowSum(tempRowSpan, sourceX);
Unsafe.Add(ref firstPassRow, x) = window.ComputeExpandedWeightedRowSum(tempRowSpan, sourceX);
}
}
else
@ -325,7 +325,7 @@ namespace SixLabors.ImageSharp.Processing.Transforms.Processors
for (int x = minX; x < maxX; x++)
{
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.
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)
{
@ -349,7 +349,7 @@ namespace SixLabors.ImageSharp.Processing.Transforms.Processors
Vector4 destinationVector = window.ComputeWeightedColumnSum(firstPassPixels, x, sourceY);
destinationVector = destinationVector.Compress();
ref TPixel pixel = ref targetRow[x];
ref TPixel pixel = ref Unsafe.Add(ref targetRow, x);
pixel.PackFromVector4(destinationVector);
}
}
@ -360,7 +360,7 @@ namespace SixLabors.ImageSharp.Processing.Transforms.Processors
// Destination color components
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);
}
}

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

Loading…
Cancel
Save