diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index 188c11ff31..01c09d2231 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -20,6 +20,11 @@
* Do not open an issue on GitHub until you have collected positive feedback about the change. GitHub issues are primarily intended for bug reports and fixes.
+#### **Running tests and Debugging**
+
+* Expected test output is pulled in as a submodule from the [ImageSharp.Tests.Images repository](https://github.com/SixLabors/Imagesharp.Tests.Images/tree/master/ReferenceOutput). To succesfully run tests, make sure that you have updated the submodules!
+* Debugging (running tests in Debug mode) is only supported on .NET Core 2.1, because of JIT Code Generation bugs like [dotnet/coreclr#16443](https://github.com/dotnet/coreclr/issues/16443) or [dotnet/coreclr#20657](https://github.com/dotnet/coreclr/issues/20657)
+
#### **Do you have questions about consuming the library or the source code?**
* Ask any question about how to use ImageSharp in the [ImageSharp Gitter Chat Room](https://gitter.im/ImageSharp/General).
diff --git a/.gitmodules b/.gitmodules
index e7972649f4..37ef701cdf 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -2,3 +2,6 @@
path = tests/Images/External
url = https://github.com/SixLabors/Imagesharp.Tests.Images.git
branch = master
+[submodule "standards"]
+ path = standards
+ url = https://github.com/SixLabors/Standards
diff --git a/ImageSharp.sln b/ImageSharp.sln
index 8f3bc68602..3c3817bf63 100644
--- a/ImageSharp.sln
+++ b/ImageSharp.sln
@@ -5,7 +5,7 @@ VisualStudioVersion = 15.0.26730.12
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionItems", "{C317F1B1-D75E-4C6D-83EB-80367343E0D7}"
ProjectSection(SolutionItems) = preProject
- .editorconfig = .editorconfig
+ standards\.editorconfig = standards\.editorconfig
.travis.yml = .travis.yml
appveyor.yml = appveyor.yml
.github\ISSUE_TEMPLATE\ask-question.md = .github\ISSUE_TEMPLATE\ask-question.md
@@ -15,12 +15,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionIt
.github\CONTRIBUTING.md = .github\CONTRIBUTING.md
.github\ISSUE_TEMPLATE\feature-request.md = .github\ISSUE_TEMPLATE\feature-request.md
features.md = features.md
- ImageSharp.ruleset = ImageSharp.ruleset
ImageSharp.sln.DotSettings = ImageSharp.sln.DotSettings
NuGet.config = NuGet.config
.github\PULL_REQUEST_TEMPLATE.md = .github\PULL_REQUEST_TEMPLATE.md
README.md = README.md
run-tests.ps1 = run-tests.ps1
+ standards\SixLabors.ruleset = standards\SixLabors.ruleset
+ standards\stylecop.json = standards\stylecop.json
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Source", "Source", "{815C0625-CD3D-440F-9F80-2D83856AB7AE}"
diff --git a/ImageSharp.sln.DotSettings b/ImageSharp.sln.DotSettings
index 432f4524a0..8e7b5dd488 100644
--- a/ImageSharp.sln.DotSettings
+++ b/ImageSharp.sln.DotSettings
@@ -382,6 +382,7 @@
True
True
True
+ True
True
True
True
diff --git a/README.md b/README.md
index a72074f8f3..ceb28564b9 100644
--- a/README.md
+++ b/README.md
@@ -123,6 +123,14 @@ To clone ImageSharp locally click the "Clone in Windows" button above or run the
git clone https://github.com/SixLabors/ImageSharp
```
+### Submodules
+
+This repository contains [git submodules](https://blog.github.com/2016-02-01-working-with-submodules/). To add the submodules to the project, navigate to the repository root and type:
+
+``` bash
+git submodule update --init --recursive
+```
+
### How can you help?
Please... Spread the word, contribute algorithms, submit performance improvements, unit tests, no input is too little. Make sure to read our [Contribution Guide](https://github.com/SixLabors/ImageSharp/blob/master/.github/CONTRIBUTING.md) before opening a PR.
diff --git a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj
index 1cb3f444f0..638687ae55 100644
--- a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj
+++ b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj
@@ -1,54 +1,53 @@
-
- An extension to ImageSharp that allows the drawing of images, paths, and text.
- SixLabors.ImageSharp.Drawing
- $(packageversion)
- 0.0.1
- SixLabors and contributors
- netstandard1.3;netstandard2.0
- 7.3
- true
- true
- SixLabors.ImageSharp.Drawing
- SixLabors.ImageSharp.Drawing
- Image Draw Shape Path Font
- https://raw.githubusercontent.com/SixLabors/Branding/master/icons/imagesharp/sixlabors.imagesharp.128.png
- https://github.com/SixLabors/ImageSharp
- http://www.apache.org/licenses/LICENSE-2.0
- git
- https://github.com/SixLabors/ImageSharp
- false
- false
- false
- false
- false
- false
- false
- false
- false
- full
- portable
- True
-
-
-
-
-
-
-
-
-
-
-
-
- All
-
-
-
- ..\..\ImageSharp.ruleset
- SixLabors.ImageSharp
-
-
- true
-
+
+ SixLabors.ImageSharp.Drawing
+ SixLabors and contributors
+ Six Labors
+ Copyright (c) Six Labors and contributors.
+ SixLabors.ImageSharp
+ An extension to ImageSharp that allows the drawing of images, paths, and text.
+ en
+
+ $(packageversion)
+ 0.0.1
+ netcoreapp2.1;netstandard1.3;netstandard2.0
+ 7.3
+ true
+ true
+ SixLabors.ImageSharp.Drawing
+ SixLabors.ImageSharp.Drawing
+ Image Draw Shape Path Font
+ https://raw.githubusercontent.com/SixLabors/Branding/master/icons/imagesharp/sixlabors.imagesharp.128.png
+ https://github.com/SixLabors/ImageSharp
+ http://www.apache.org/licenses/LICENSE-2.0
+ git
+ https://github.com/SixLabors/ImageSharp
+ full
+ portable
+ True
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ..\..\standards\SixLabors.ruleset
+ SixLabors.ImageSharp
+
+
+
+ true
+
\ No newline at end of file
diff --git a/src/ImageSharp.Drawing/Processing/BrushApplicator.cs b/src/ImageSharp.Drawing/Processing/BrushApplicator.cs
index 770d440656..0c6e0d3b40 100644
--- a/src/ImageSharp.Drawing/Processing/BrushApplicator.cs
+++ b/src/ImageSharp.Drawing/Processing/BrushApplicator.cs
@@ -89,7 +89,7 @@ namespace SixLabors.ImageSharp.Processing
}
Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length);
- this.Blender.Blend(memoryAllocator, destinationRow, destinationRow, overlaySpan, amountSpan);
+ this.Blender.Blend(this.Target.Configuration, destinationRow, destinationRow, overlaySpan, amountSpan);
}
}
}
diff --git a/src/ImageSharp.Drawing/Processing/EllipticGradientBrush{TPixel}.cs b/src/ImageSharp.Drawing/Processing/EllipticGradientBrush{TPixel}.cs
index 8af01564c4..64901c8e80 100644
--- a/src/ImageSharp.Drawing/Processing/EllipticGradientBrush{TPixel}.cs
+++ b/src/ImageSharp.Drawing/Processing/EllipticGradientBrush{TPixel}.cs
@@ -18,9 +18,9 @@ namespace SixLabors.ImageSharp.Processing
public sealed class EllipticGradientBrush : GradientBrushBase
where TPixel : struct, IPixel
{
- private readonly Point center;
+ private readonly PointF center;
- private readonly Point referenceAxisEnd;
+ private readonly PointF referenceAxisEnd;
private readonly float axisRatio;
@@ -35,8 +35,8 @@ namespace SixLabors.ImageSharp.Processing
/// Defines how the colors of the gradients are repeated.
/// the color stops as defined in base class.
public EllipticGradientBrush(
- Point center,
- Point referenceAxisEnd,
+ PointF center,
+ PointF referenceAxisEnd,
float axisRatio,
GradientRepetitionMode repetitionMode,
params ColorStop[] colorStops)
@@ -64,9 +64,9 @@ namespace SixLabors.ImageSharp.Processing
///
private sealed class RadialGradientBrushApplicator : GradientBrushApplicatorBase
{
- private readonly Point center;
+ private readonly PointF center;
- private readonly Point referenceAxisEnd;
+ private readonly PointF referenceAxisEnd;
private readonly float axisRatio;
@@ -99,8 +99,8 @@ namespace SixLabors.ImageSharp.Processing
public RadialGradientBrushApplicator(
ImageFrame target,
GraphicsOptions options,
- Point center,
- Point referenceAxisEnd,
+ PointF center,
+ PointF referenceAxisEnd,
float axisRatio,
ColorStop[] colorStops,
GradientRepetitionMode repetitionMode)
@@ -129,7 +129,7 @@ namespace SixLabors.ImageSharp.Processing
}
///
- protected override float PositionOnGradient(int xt, int yt)
+ protected override float PositionOnGradient(float xt, float yt)
{
float x0 = xt - this.center.X;
float y0 = yt - this.center.Y;
diff --git a/src/ImageSharp.Drawing/Processing/GradientBrushBase{TPixel}.cs b/src/ImageSharp.Drawing/Processing/GradientBrushBase{TPixel}.cs
index 00141a8d8d..2dfae9e8f1 100644
--- a/src/ImageSharp.Drawing/Processing/GradientBrushBase{TPixel}.cs
+++ b/src/ImageSharp.Drawing/Processing/GradientBrushBase{TPixel}.cs
@@ -81,7 +81,7 @@ namespace SixLabors.ImageSharp.Processing
{
get
{
- float positionOnCompleteGradient = this.PositionOnGradient(x, y);
+ float positionOnCompleteGradient = this.PositionOnGradient(x + 0.5f, y + 0.5f);
switch (this.repetitionMode)
{
@@ -121,7 +121,7 @@ namespace SixLabors.ImageSharp.Processing
{
var fromAsVector = from.Color.ToVector4();
var toAsVector = to.Color.ToVector4();
- float onLocalGradient = (positionOnCompleteGradient - from.Ratio) / to.Ratio;
+ float onLocalGradient = (positionOnCompleteGradient - from.Ratio) / (to.Ratio - from.Ratio);
// TODO: this should be changeble for different gradienting functions
Vector4 result = PorterDuffFunctions.NormalSrcOver(
@@ -137,18 +137,18 @@ namespace SixLabors.ImageSharp.Processing
}
///
- /// calculates the position on the gradient for a given pixel.
+ /// calculates the position on the gradient for a given point.
/// This method is abstract as it's content depends on the shape of the gradient.
///
- /// The x coordinate of the pixel
- /// The y coordinate of the pixel
+ /// The x coordinate of the point
+ /// The y coordinate of the point
///
- /// The position the given pixel has on the gradient.
+ /// The position the given point 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 enum.
///
- protected abstract float PositionOnGradient(int x, int y);
+ protected abstract float PositionOnGradient(float x, float y);
private (ColorStop from, ColorStop to) GetGradientSegment(
float positionOnCompleteGradient)
diff --git a/src/ImageSharp.Drawing/Processing/ImageBrush{TPixel}.cs b/src/ImageSharp.Drawing/Processing/ImageBrush{TPixel}.cs
index c3f81868be..1ef4bb9ec9 100644
--- a/src/ImageSharp.Drawing/Processing/ImageBrush{TPixel}.cs
+++ b/src/ImageSharp.Drawing/Processing/ImageBrush{TPixel}.cs
@@ -140,7 +140,12 @@ namespace SixLabors.ImageSharp.Processing
}
Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length);
- this.Blender.Blend(this.source.MemoryAllocator, destinationRow, destinationRow, overlaySpan, amountSpan);
+ this.Blender.Blend(
+ this.source.Configuration,
+ destinationRow,
+ destinationRow,
+ overlaySpan,
+ amountSpan);
}
}
}
diff --git a/src/ImageSharp.Drawing/Processing/LinearGradientBrush{TPixel}.cs b/src/ImageSharp.Drawing/Processing/LinearGradientBrush{TPixel}.cs
index 765bf5499d..efdc852f28 100644
--- a/src/ImageSharp.Drawing/Processing/LinearGradientBrush{TPixel}.cs
+++ b/src/ImageSharp.Drawing/Processing/LinearGradientBrush{TPixel}.cs
@@ -17,9 +17,9 @@ namespace SixLabors.ImageSharp.Processing
public sealed class LinearGradientBrush : GradientBrushBase
where TPixel : struct, IPixel
{
- private readonly Point p1;
+ private readonly PointF p1;
- private readonly Point p2;
+ private readonly PointF p2;
///
/// Initializes a new instance of the class.
@@ -29,8 +29,8 @@ namespace SixLabors.ImageSharp.Processing
/// defines how colors are repeated.
///
public LinearGradientBrush(
- Point p1,
- Point p2,
+ PointF p1,
+ PointF p2,
GradientRepetitionMode repetitionMode,
params ColorStop[] colorStops)
: base(repetitionMode, colorStops)
@@ -48,9 +48,9 @@ namespace SixLabors.ImageSharp.Processing
///
private sealed class LinearGradientBrushApplicator : GradientBrushApplicatorBase
{
- private readonly Point start;
+ private readonly PointF start;
- private readonly Point end;
+ private readonly PointF end;
///
/// the vector along the gradient, x component
@@ -93,8 +93,8 @@ namespace SixLabors.ImageSharp.Processing
/// the graphics options
public LinearGradientBrushApplicator(
ImageFrame source,
- Point start,
- Point end,
+ PointF start,
+ PointF end,
ColorStop[] colorStops,
GradientRepetitionMode repetitionMode,
GraphicsOptions options)
@@ -116,15 +116,15 @@ namespace SixLabors.ImageSharp.Processing
this.length = (float)Math.Sqrt(this.alongsSquared);
}
- protected override float PositionOnGradient(int x, int y)
+ protected override float PositionOnGradient(float x, float y)
{
if (this.acrossX == 0)
{
- return (x - this.start.X) / (float)(this.end.X - this.start.X);
+ return (x - this.start.X) / (this.end.X - this.start.X);
}
else if (this.acrossY == 0)
{
- return (y - this.start.Y) / (float)(this.end.Y - this.start.Y);
+ return (y - this.start.Y) / (this.end.Y - this.start.Y);
}
else
{
diff --git a/src/ImageSharp.Drawing/Processing/PatternBrush{TPixel}.cs b/src/ImageSharp.Drawing/Processing/PatternBrush{TPixel}.cs
index 2ce9a7ce57..46ed36f687 100644
--- a/src/ImageSharp.Drawing/Processing/PatternBrush{TPixel}.cs
+++ b/src/ImageSharp.Drawing/Processing/PatternBrush{TPixel}.cs
@@ -170,7 +170,12 @@ namespace SixLabors.ImageSharp.Processing
}
Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length);
- this.Blender.Blend(memoryAllocator, destinationRow, destinationRow, overlaySpan, amountSpan);
+ this.Blender.Blend(
+ this.Target.Configuration,
+ destinationRow,
+ destinationRow,
+ overlaySpan,
+ amountSpan);
}
}
}
diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs
index dc73420f30..0957904c62 100644
--- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs
+++ b/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs
@@ -79,8 +79,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
int width = maxX - minX;
- MemoryAllocator memoryAllocator = this.Image.GetConfiguration().MemoryAllocator;
-
var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY);
ParallelHelper.IterateRows(
@@ -93,7 +91,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
Span background = source.GetPixelRowSpan(y).Slice(minX, width);
Span foreground =
targetImage.GetPixelRowSpan(y - locationY).Slice(targetX, width);
- blender.Blend(memoryAllocator, background, background, foreground, this.Opacity);
+ blender.Blend(configuration, background, background, foreground, this.Opacity);
}
});
}
diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs
index 514249a2d4..550c021caa 100644
--- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs
+++ b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs
@@ -3,7 +3,7 @@
using System;
using System.Buffers;
-
+using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Primitives;
@@ -82,11 +82,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
// we need to offset the pixel grid to account for when we outline a path.
// basically if the line is [1,2] => [3,2] then when outlining at 1 we end up with a region of [0.5,1.5],[1.5, 1.5],[3.5,2.5],[2.5,2.5]
// and this can cause missed fills when not using antialiasing.so we offset the pixel grid by 0.5 in the x & y direction thus causing the#
- // region to alline with the pixel grid.
+ // region to align with the pixel grid.
float offset = 0.5f;
if (this.Options.Antialias)
{
- offset = 0f; // we are antialising skip offsetting as real antalising should take care of offset.
+ offset = 0f; // we are antialiasing skip offsetting as real antialiasing should take care of offset.
subpixelCount = this.Options.AntialiasSubpixelDepth;
if (subpixelCount < 4)
{
@@ -107,6 +107,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
Span buffer = bBuffer.GetSpan();
Span scanline = bScanline.GetSpan();
+ bool isSolidBrushWithoutBlending = this.IsSolidBrushWithoutBlending(out SolidBrush solidBrush);
+
for (int y = minY; y < maxY; y++)
{
if (scanlineDirty)
@@ -121,7 +123,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
int pointsFound = region.Scan(subPixel + offset, buffer, configuration);
if (pointsFound == 0)
{
- // nothing on this line skip
+ // nothing on this line, skip
continue;
}
@@ -168,16 +170,30 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
{
if (!this.Options.Antialias)
{
+ bool hasOnes = false;
+ bool hasZeros = false;
for (int x = 0; x < scanlineWidth; x++)
{
if (scanline[x] >= 0.5)
{
scanline[x] = 1;
+ hasOnes = true;
}
else
{
scanline[x] = 0;
+ hasZeros = true;
+ }
+ }
+
+ if (isSolidBrushWithoutBlending && hasOnes != hasZeros)
+ {
+ if (hasOnes)
+ {
+ source.GetPixelRowSpan(y).Slice(minX, scanlineWidth).Fill(solidBrush.Color);
}
+
+ continue;
}
}
@@ -187,5 +203,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
}
}
}
+
+ private bool IsSolidBrushWithoutBlending(out SolidBrush solidBrush)
+ {
+ solidBrush = this.Brush as SolidBrush;
+
+ if (solidBrush == null)
+ {
+ return false;
+ }
+
+ return this.Options.IsOpaqueColorWithoutBlending(solidBrush.Color);
+ }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs b/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs
index 1095de325f..266d842bfa 100644
--- a/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs
+++ b/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs
@@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
/// The font we want to render with
/// The brush to source pixel colors from.
/// The pen to outline text with.
- /// The location on the image to start drawign the text from.
+ /// The location on the image to start drawing the text from.
public DrawTextProcessor(TextGraphicsOptions options, string text, Font font, IBrush brush, IPen pen, PointF location)
{
Guard.NotNull(text, nameof(text));
@@ -85,7 +85,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
{
base.BeforeImageApply(source, sourceRectangle);
- // do everythign at the image level as we are deligating the processing down to other processors
+ // do everything at the image level as we are delegating the processing down to other processors
var style = new RendererOptions(this.Font, this.Options.DpiX, this.Options.DpiY, this.Location)
{
ApplyKerning = this.Options.ApplyKerning,
@@ -97,7 +97,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
this.textRenderer = new CachingGlyphRenderer(source.GetMemoryAllocator(), this.Text.Length, this.Pen, this.Brush != null);
this.textRenderer.Options = (GraphicsOptions)this.Options;
- TextRenderer.RenderTextTo(this.textRenderer, this.Text, style);
+ var renderer = new TextRenderer(this.textRenderer);
+ renderer.RenderText(this.Text, style);
}
protected override void AfterImageApply(Image source, Rectangle sourceRectangle)
@@ -138,10 +139,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
fistRow = -startY;
}
- int end = operation.Map.Height;
-
int maxHeight = source.Height - startY;
- end = Math.Min(end, maxHeight);
+ int end = Math.Min(operation.Map.Height, maxHeight);
for (int row = fistRow; row < end; row++)
{
@@ -164,18 +163,26 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
private class CachingGlyphRenderer : IGlyphRenderer, IDisposable
{
- private PathBuilder builder;
+ // just enough accuracy to allow for 1/8 pixel differences which
+ // later are accumulated while rendering, but do not grow into full pixel offsets
+ // The value 8 is benchmarked to:
+ // - Provide a good accuracy (smaller than 0.2% image difference compared to the non-caching variant)
+ // - Cache hit ratio above 60%
+ private const float AccuracyMultiple = 8;
+
+ private readonly PathBuilder builder;
private Point currentRenderPosition = default;
- private GlyphRendererParameters currentGlyphRenderParams = default;
- private int offset = 0;
+ private (GlyphRendererParameters glyph, PointF subPixelOffset) currentGlyphRenderParams = default;
+ private readonly int offset = 0;
private PointF currentPoint = default(PointF);
- private readonly Dictionary glyphData = new Dictionary();
+ private readonly Dictionary<(GlyphRendererParameters glyph, PointF subPixelOffset), GlyphRenderData>
+ glyphData = new Dictionary<(GlyphRendererParameters glyph, PointF subPixelOffset), GlyphRenderData>();
- private bool renderOutline = false;
- private bool renderFill = false;
- private bool raterizationRequired = false;
+ private readonly bool renderOutline = false;
+ private readonly bool renderFill = false;
+ private bool rasterizationRequired = false;
public CachingGlyphRenderer(MemoryAllocator memoryAllocator, int size, IPen pen, bool renderFill)
{
@@ -213,17 +220,22 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
this.builder.StartFigure();
}
- public bool BeginGlyph(RectangleF bounds, GlyphRendererParameters paramters)
+ public bool BeginGlyph(RectangleF bounds, GlyphRendererParameters parameters)
{
this.currentRenderPosition = Point.Truncate(bounds.Location);
+ PointF subPixelOffset = bounds.Location - this.currentRenderPosition;
+
+ subPixelOffset.X = MathF.Round(subPixelOffset.X * AccuracyMultiple) / AccuracyMultiple;
+ subPixelOffset.Y = MathF.Round(subPixelOffset.Y * AccuracyMultiple) / AccuracyMultiple;
// we have offset our rendering origion a little bit down to prevent edge cropping, move the draw origin up to compensate
this.currentRenderPosition = new Point(this.currentRenderPosition.X - this.offset, this.currentRenderPosition.Y - this.offset);
- this.currentGlyphRenderParams = paramters;
- if (this.glyphData.ContainsKey(paramters))
+ this.currentGlyphRenderParams = (parameters, subPixelOffset);
+
+ if (this.glyphData.ContainsKey(this.currentGlyphRenderParams))
{
// we have already drawn the glyph vectors skip trying again
- this.raterizationRequired = false;
+ this.rasterizationRequired = false;
return false;
}
@@ -233,7 +245,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
// ensure all glyphs render around [zero, zero] so offset negative root positions so when we draw the glyph we can offet it back
this.builder.SetOrigin(new PointF(-(int)bounds.X + this.offset, -(int)bounds.Y + this.offset));
- this.raterizationRequired = true;
+ this.rasterizationRequired = true;
return true;
}
@@ -252,7 +264,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
public void Dispose()
{
- foreach (KeyValuePair kv in this.glyphData)
+ foreach (KeyValuePair<(GlyphRendererParameters glyph, PointF subPixelOffset), GlyphRenderData> kv in this.glyphData)
{
kv.Value.Dispose();
}
@@ -270,7 +282,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
GlyphRenderData renderData = default;
// has the glyoh been rendedered already????
- if (this.raterizationRequired)
+ if (this.rasterizationRequired)
{
IPath path = this.builder.Build();
diff --git a/src/ImageSharp.Drawing/Processing/RadialGradientBrush{TPixel}.cs b/src/ImageSharp.Drawing/Processing/RadialGradientBrush{TPixel}.cs
index 16380fc34b..d33d099931 100644
--- a/src/ImageSharp.Drawing/Processing/RadialGradientBrush{TPixel}.cs
+++ b/src/ImageSharp.Drawing/Processing/RadialGradientBrush{TPixel}.cs
@@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Processing
public sealed class RadialGradientBrush : GradientBrushBase
where TPixel : struct, IPixel
{
- private readonly Point center;
+ private readonly PointF center;
private readonly float radius;
@@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Processing
/// Defines how the colors in the gradient are repeated.
/// the color stops as defined in base class.
public RadialGradientBrush(
- Point center,
+ PointF center,
float radius,
GradientRepetitionMode repetitionMode,
params ColorStop[] colorStops)
@@ -51,7 +51,7 @@ namespace SixLabors.ImageSharp.Processing
///
private sealed class RadialGradientBrushApplicator : GradientBrushApplicatorBase
{
- private readonly Point center;
+ private readonly PointF center;
private readonly float radius;
@@ -67,7 +67,7 @@ namespace SixLabors.ImageSharp.Processing
public RadialGradientBrushApplicator(
ImageFrame target,
GraphicsOptions options,
- Point center,
+ PointF center,
float radius,
ColorStop[] colorStops,
GradientRepetitionMode repetitionMode)
@@ -89,7 +89,7 @@ namespace SixLabors.ImageSharp.Processing
/// The X coordinate of the target pixel.
/// The Y coordinate of the target pixel.
/// the position on the color gradient.
- protected override float PositionOnGradient(int x, int y)
+ protected override float PositionOnGradient(float x, float y)
{
float distance = (float)Math.Sqrt(Math.Pow(this.center.X - x, 2) + Math.Pow(this.center.Y - y, 2));
return distance / this.radius;
diff --git a/src/ImageSharp.Drawing/Processing/RecolorBrush{TPixel}.cs b/src/ImageSharp.Drawing/Processing/RecolorBrush{TPixel}.cs
index 2968b68b55..09a1ff71fb 100644
--- a/src/ImageSharp.Drawing/Processing/RecolorBrush{TPixel}.cs
+++ b/src/ImageSharp.Drawing/Processing/RecolorBrush{TPixel}.cs
@@ -158,7 +158,12 @@ namespace SixLabors.ImageSharp.Processing
}
Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length);
- this.Blender.Blend(memoryAllocator, destinationRow, destinationRow, overlaySpan, amountSpan);
+ this.Blender.Blend(
+ this.Target.Configuration,
+ destinationRow,
+ destinationRow,
+ overlaySpan,
+ amountSpan);
}
}
}
diff --git a/src/ImageSharp.Drawing/Processing/SolidBrush{TPixel}.cs b/src/ImageSharp.Drawing/Processing/SolidBrush{TPixel}.cs
index 6b69c33f07..20a6833c40 100644
--- a/src/ImageSharp.Drawing/Processing/SolidBrush{TPixel}.cs
+++ b/src/ImageSharp.Drawing/Processing/SolidBrush{TPixel}.cs
@@ -89,13 +89,24 @@ namespace SixLabors.ImageSharp.Processing
///
internal override void Apply(Span scanline, int x, int y)
{
- Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length);
+ Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x);
+
+ // constrain the spans to each other
+ if (destinationRow.Length > scanline.Length)
+ {
+ destinationRow = destinationRow.Slice(0, scanline.Length);
+ }
+ else
+ {
+ scanline = scanline.Slice(0, destinationRow.Length);
+ }
MemoryAllocator memoryAllocator = this.Target.MemoryAllocator;
+ Configuration configuration = this.Target.Configuration;
if (this.Options.BlendPercentage == 1f)
{
- this.Blender.Blend(memoryAllocator, destinationRow, destinationRow, this.Colors.GetSpan(), scanline);
+ this.Blender.Blend(configuration, destinationRow, destinationRow, this.Colors.GetSpan(), scanline);
}
else
{
@@ -108,7 +119,12 @@ namespace SixLabors.ImageSharp.Processing
amountSpan[i] = scanline[i] * this.Options.BlendPercentage;
}
- this.Blender.Blend(memoryAllocator, destinationRow, destinationRow, this.Colors.GetSpan(), amountSpan);
+ this.Blender.Blend(
+ configuration,
+ destinationRow,
+ destinationRow,
+ this.Colors.GetSpan(),
+ amountSpan);
}
}
}
diff --git a/src/ImageSharp/Advanced/AdvancedImageExtensions.cs b/src/ImageSharp/Advanced/AdvancedImageExtensions.cs
index 328d575969..bdcb4c10f1 100644
--- a/src/ImageSharp/Advanced/AdvancedImageExtensions.cs
+++ b/src/ImageSharp/Advanced/AdvancedImageExtensions.cs
@@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Advanced
/// Gets the configuration for the image.
///
/// The Pixel format.
- /// The source image
+ /// The source image.
/// Returns the configuration.
public static Configuration GetConfiguration(this Image source)
where TPixel : struct, IPixel
@@ -150,7 +150,7 @@ namespace SixLabors.ImageSharp.Advanced
///
/// Gets the assigned to 'source'.
///
- /// The source image
+ /// The source image.
/// Returns the configuration.
internal static MemoryAllocator GetMemoryAllocator(this IConfigurable source)
=> GetConfiguration(source).MemoryAllocator;
@@ -160,7 +160,7 @@ namespace SixLabors.ImageSharp.Advanced
///
/// The type of the pixel.
/// The source.
- /// The span retuned from Pixel source
+ /// The span returned from Pixel source
private static Span GetSpan(IPixelSource source)
where TPixel : struct, IPixel
=> source.PixelBuffer.GetSpan();
@@ -172,7 +172,7 @@ namespace SixLabors.ImageSharp.Advanced
/// The source.
/// The row.
///
- /// The span retuned from Pixel source
+ /// The span returned from Pixel source
///
private static Span GetSpan(IPixelSource source, int row)
where TPixel : struct, IPixel
@@ -185,7 +185,7 @@ namespace SixLabors.ImageSharp.Advanced
/// The source.
/// The row.
///
- /// The span retuned from Pixel source
+ /// The span returned from Pixel source.
///
private static Span GetSpan(Buffer2D source, int row)
where TPixel : struct, IPixel
diff --git a/src/ImageSharp/Advanced/AotCompilerTools.cs b/src/ImageSharp/Advanced/AotCompilerTools.cs
new file mode 100644
index 0000000000..eb6991e6a1
--- /dev/null
+++ b/src/ImageSharp/Advanced/AotCompilerTools.cs
@@ -0,0 +1,150 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System.Numerics;
+using SixLabors.ImageSharp.Formats;
+using SixLabors.ImageSharp.Formats.Jpeg.Components;
+using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.ImageSharp.Processing.Processors.Dithering;
+using SixLabors.ImageSharp.Processing.Processors.Quantization;
+
+namespace SixLabors.ImageSharp.Advanced
+{
+ ///
+ /// Unlike traditional Mono/.NET, code on the iPhone is statically compiled ahead of time instead of being
+ /// compiled on demand by a JIT compiler. This means there are a few limitations with respect to generics,
+ /// these are caused because not every possible generic instantiation can be determined up front at compile time.
+ /// The Aot Compiler is designed to overcome the limitations of this compiler.
+ ///
+ public static class AotCompilerTools
+ {
+ static AotCompilerTools()
+ {
+ System.Runtime.CompilerServices.Unsafe.SizeOf();
+ System.Runtime.CompilerServices.Unsafe.SizeOf();
+ System.Runtime.CompilerServices.Unsafe.SizeOf();
+ System.Runtime.CompilerServices.Unsafe.SizeOf();
+ System.Runtime.CompilerServices.Unsafe.SizeOf();
+ System.Runtime.CompilerServices.Unsafe.SizeOf();
+ System.Runtime.CompilerServices.Unsafe.SizeOf();
+ }
+
+ ///
+ /// Seeds the compiler using the given pixel format.
+ ///
+ /// The pixel format.
+ public static void Seed()
+ where TPixel : struct, IPixel
+ {
+ // This is we actually call all the individual methods you need to seed.
+ AotCompileOctreeQuantizer();
+ AotCompileWuQuantizer();
+ AotCompileDithering();
+
+ System.Runtime.CompilerServices.Unsafe.SizeOf();
+
+ AotCodec(new Formats.Png.PngDecoder(), new Formats.Png.PngEncoder());
+ AotCodec(new Formats.Bmp.BmpDecoder(), new Formats.Bmp.BmpEncoder());
+ AotCodec(new Formats.Gif.GifDecoder(), new Formats.Gif.GifEncoder());
+ AotCodec(new Formats.Jpeg.JpegDecoder(), new Formats.Jpeg.JpegEncoder());
+
+ // TODO: Do the discovery work to figure out what works and what doesn't.
+ }
+
+ ///
+ /// Seeds the compiler using the given pixel formats.
+ ///
+ /// The first pixel format.
+ /// The second pixel format.
+ public static void Seed()
+ where TPixel : struct, IPixel
+ where TPixel2 : struct, IPixel
+ {
+ Seed();
+ Seed();
+ }
+
+ ///
+ /// Seeds the compiler using the given pixel formats.
+ ///
+ /// The first pixel format.
+ /// The second pixel format.
+ /// The third pixel format.
+ public static void Seed()
+ where TPixel : struct, IPixel
+ where TPixel2 : struct, IPixel
+ where TPixel3 : struct, IPixel
+ {
+ Seed();
+ Seed();
+ }
+
+ ///
+ /// This method doesn't actually do anything but serves an important purpose...
+ /// If you are running ImageSharp on iOS and try to call SaveAsGif, it will throw an excepion:
+ /// "Attempting to JIT compile method... OctreeFrameQuantizer.ConstructPalette... while running in aot-only mode."
+ /// The reason this happens is the SaveAsGif method makes haevy use of generics, which are too confusing for the AoT
+ /// compiler used on Xamarin.iOS. It spins up the JIT compiler to try and figure it out, but that is an illegal op on
+ /// iOS so it bombs out.
+ /// If you are getting the above error, you need to call this method, which will pre-seed the AoT compiler with the
+ /// necessary methods to complete the SaveAsGif call. That's it, otherwise you should NEVER need this method!!!
+ ///
+ /// The pixel format.
+ private static void AotCompileOctreeQuantizer()
+ where TPixel : struct, IPixel
+ {
+ var test = new OctreeFrameQuantizer(new OctreeQuantizer(false));
+ test.AotGetPalette();
+ }
+
+ ///
+ /// This method pre-seeds the WuQuantizer in the AoT compiler for iOS.
+ ///
+ /// The pixel format.
+ private static void AotCompileWuQuantizer()
+ where TPixel : struct, IPixel
+ {
+ var test = new WuFrameQuantizer(new WuQuantizer(false));
+ test.QuantizeFrame(new ImageFrame(Configuration.Default, 1, 1));
+ test.AotGetPalette();
+ }
+
+ ///
+ /// This method pre-seeds the default dithering engine (FloydSteinbergDiffuser) in the AoT compiler for iOS.
+ ///
+ /// The pixel format.
+ private static void AotCompileDithering()
+ where TPixel : struct, IPixel
+ {
+ var test = new FloydSteinbergDiffuser();
+ TPixel pixel = default;
+ test.Dither(new ImageFrame(Configuration.Default, 1, 1), pixel, pixel, 0, 0, 0, 0, 0, 0);
+ }
+
+ ///
+ /// This method pre-seeds the decoder and encoder for a given pixel format in the AoT compiler for iOS.
+ ///
+ /// The image decoder to seed.
+ /// The image encoder to seed.
+ /// The pixel format.
+ private static void AotCodec(IImageDecoder decoder, IImageEncoder encoder)
+ where TPixel : struct, IPixel
+ {
+ try
+ {
+ decoder.Decode(Configuration.Default, null);
+ }
+ catch
+ {
+ }
+
+ try
+ {
+ encoder.Encode(null, null);
+ }
+ catch
+ {
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Advanced/IPixelSource.cs b/src/ImageSharp/Advanced/IPixelSource.cs
index 19616d7427..a321e877ba 100644
--- a/src/ImageSharp/Advanced/IPixelSource.cs
+++ b/src/ImageSharp/Advanced/IPixelSource.cs
@@ -3,7 +3,6 @@
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
-using SixLabors.Memory;
namespace SixLabors.ImageSharp.Advanced
{
diff --git a/src/ImageSharp/ColorSpaces/CieLab.cs b/src/ImageSharp/ColorSpaces/CieLab.cs
index ea6df86e27..146acf12ee 100644
--- a/src/ImageSharp/ColorSpaces/CieLab.cs
+++ b/src/ImageSharp/ColorSpaces/CieLab.cs
@@ -118,13 +118,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
public static bool operator !=(CieLab left, CieLab right) => !left.Equals(right);
///
- public override int GetHashCode()
- {
- int hash = this.L.GetHashCode();
- hash = HashHelpers.Combine(hash, this.A.GetHashCode());
- hash = HashHelpers.Combine(hash, this.B.GetHashCode());
- return HashHelpers.Combine(hash, this.WhitePoint.GetHashCode());
- }
+ public override int GetHashCode() => HashCode.Combine(this.L, this.A, this.B, this.WhitePoint);
///
public override string ToString() => FormattableString.Invariant($"CieLab({this.L:#0.##}, {this.A:#0.##}, {this.B:#0.##})");
diff --git a/src/ImageSharp/ColorSpaces/CieLch.cs b/src/ImageSharp/ColorSpaces/CieLch.cs
index f1a7425e9e..99d4c09e97 100644
--- a/src/ImageSharp/ColorSpaces/CieLch.cs
+++ b/src/ImageSharp/ColorSpaces/CieLch.cs
@@ -13,15 +13,15 @@ namespace SixLabors.ImageSharp.ColorSpaces
///
public readonly struct CieLch : IEquatable
{
- private static readonly Vector3 Min = new Vector3(0, -200, 0);
- private static readonly Vector3 Max = new Vector3(100, 200, 360);
-
///
/// D50 standard illuminant.
/// Used when reference white is not specified explicitly.
///
public static readonly CieXyz DefaultWhitePoint = Illuminants.D50;
+ private static readonly Vector3 Min = new Vector3(0, -200, 0);
+ private static readonly Vector3 Max = new Vector3(100, 200, 360);
+
///
/// Gets the lightness dimension.
/// A value ranging between 0 (black), 100 (diffuse white) or higher (specular white).
@@ -122,10 +122,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
///
public override int GetHashCode()
{
- int hash = this.L.GetHashCode();
- hash = HashHelpers.Combine(hash, this.C.GetHashCode());
- hash = HashHelpers.Combine(hash, this.H.GetHashCode());
- return HashHelpers.Combine(hash, this.WhitePoint.GetHashCode());
+ return HashCode.Combine(this.L, this.C, this.H, this.WhitePoint);
}
///
diff --git a/src/ImageSharp/ColorSpaces/CieLchuv.cs b/src/ImageSharp/ColorSpaces/CieLchuv.cs
index 256b5dc0fd..ab6f639a2b 100644
--- a/src/ImageSharp/ColorSpaces/CieLchuv.cs
+++ b/src/ImageSharp/ColorSpaces/CieLchuv.cs
@@ -119,13 +119,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
public static bool operator !=(CieLchuv left, CieLchuv right) => !left.Equals(right);
///
- public override int GetHashCode()
- {
- int hash = this.L.GetHashCode();
- hash = HashHelpers.Combine(hash, this.C.GetHashCode());
- hash = HashHelpers.Combine(hash, this.H.GetHashCode());
- return HashHelpers.Combine(hash, this.WhitePoint.GetHashCode());
- }
+ public override int GetHashCode() => HashCode.Combine(this.L, this.C, this.H, this.WhitePoint);
///
public override string ToString() => FormattableString.Invariant($"CieLchuv({this.L:#0.##}, {this.C:#0.##}, {this.H:#0.##})");
diff --git a/src/ImageSharp/ColorSpaces/CieLuv.cs b/src/ImageSharp/ColorSpaces/CieLuv.cs
index 8fe073d6bf..d54d92b62a 100644
--- a/src/ImageSharp/ColorSpaces/CieLuv.cs
+++ b/src/ImageSharp/ColorSpaces/CieLuv.cs
@@ -119,13 +119,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
public static bool operator !=(CieLuv left, CieLuv right) => !left.Equals(right);
///
- public override int GetHashCode()
- {
- int hash = this.L.GetHashCode();
- hash = HashHelpers.Combine(hash, this.U.GetHashCode());
- hash = HashHelpers.Combine(hash, this.V.GetHashCode());
- return HashHelpers.Combine(hash, this.WhitePoint.GetHashCode());
- }
+ public override int GetHashCode() => HashCode.Combine(this.L, this.U, this.V, this.WhitePoint);
///
public override string ToString() => FormattableString.Invariant($"CieLuv({this.L:#0.##}, {this.U:#0.##}, {this.V:#0.##})");
diff --git a/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs b/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs
index f625bb7616..8e14274f66 100644
--- a/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs
+++ b/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs
@@ -8,7 +8,7 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces
{
///
- /// Represents the coordinates of CIEXY chromaticity space
+ /// Represents the coordinates of CIEXY chromaticity space.
///
public readonly struct CieXyChromaticityCoordinates : IEquatable
{
@@ -64,7 +64,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
///
[MethodImpl(InliningOptions.ShortMethod)]
- public override int GetHashCode() => HashHelpers.Combine(this.X.GetHashCode(), this.Y.GetHashCode());
+ public override int GetHashCode() => HashCode.Combine(this.X, this.Y);
///
public override string ToString() => FormattableString.Invariant($"CieXyChromaticityCoordinates({this.X:#0.##}, {this.Y:#0.##})");
diff --git a/src/ImageSharp/ColorSpaces/CieXyy.cs b/src/ImageSharp/ColorSpaces/CieXyy.cs
index 7137360e94..fff296945e 100644
--- a/src/ImageSharp/ColorSpaces/CieXyy.cs
+++ b/src/ImageSharp/ColorSpaces/CieXyy.cs
@@ -83,12 +83,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
public static bool operator !=(CieXyy left, CieXyy right) => !left.Equals(right);
///
- public override int GetHashCode()
- {
- int hash = this.X.GetHashCode();
- hash = HashHelpers.Combine(hash, this.Y.GetHashCode());
- return HashHelpers.Combine(hash, this.Yl.GetHashCode());
- }
+ public override int GetHashCode() => HashCode.Combine(this.X, this.Y, this.Yl);
///
public override string ToString() => FormattableString.Invariant($"CieXyy({this.X:#0.##}, {this.Y:#0.##}, {this.Yl:#0.##})");
diff --git a/src/ImageSharp/ColorSpaces/CieXyz.cs b/src/ImageSharp/ColorSpaces/CieXyz.cs
index c0ed356601..6c5adcb219 100644
--- a/src/ImageSharp/ColorSpaces/CieXyz.cs
+++ b/src/ImageSharp/ColorSpaces/CieXyz.cs
@@ -26,7 +26,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
public readonly float Y;
///
- /// Gets the Z component. Quasi-equal to blue stimulation, or the S cone response
+ /// Gets the Z component. Quasi-equal to blue stimulation, or the S cone response.
/// A value usually ranging between 0 and 1.
///
public readonly float Z;
@@ -86,12 +86,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
public Vector3 ToVector3() => new Vector3(this.X, this.Y, this.Z);
///
- public override int GetHashCode()
- {
- int hash = this.X.GetHashCode();
- hash = HashHelpers.Combine(hash, this.Y.GetHashCode());
- return HashHelpers.Combine(hash, this.Z.GetHashCode());
- }
+ public override int GetHashCode() => HashCode.Combine(this.X, this.Y, this.Z);
///
public override string ToString() => FormattableString.Invariant($"CieXyz({this.X:#0.##}, {this.Y:#0.##}, {this.Z:#0.##})");
diff --git a/src/ImageSharp/ColorSpaces/Cmyk.cs b/src/ImageSharp/ColorSpaces/Cmyk.cs
index 634667c0c6..c2331c3798 100644
--- a/src/ImageSharp/ColorSpaces/Cmyk.cs
+++ b/src/ImageSharp/ColorSpaces/Cmyk.cs
@@ -78,7 +78,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
public static bool operator ==(Cmyk left, Cmyk right) => left.Equals(right);
///
- /// Compares two objects for inequality
+ /// Compares two objects for inequality.
///
/// The on the left side of the operand.
/// The on the right side of the operand.
@@ -90,13 +90,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
///
[MethodImpl(InliningOptions.ShortMethod)]
- public override int GetHashCode()
- {
- int hash = this.C.GetHashCode();
- hash = HashHelpers.Combine(hash, this.M.GetHashCode());
- hash = HashHelpers.Combine(hash, this.Y.GetHashCode());
- return HashHelpers.Combine(hash, this.K.GetHashCode());
- }
+ public override int GetHashCode() => HashCode.Combine(this.C, this.M, this.Y, this.K);
///
public override string ToString() => FormattableString.Invariant($"Cmyk({this.C:#0.##}, {this.M:#0.##}, {this.Y:#0.##}, {this.K:#0.##})");
diff --git a/src/ImageSharp/ColorSpaces/Companding/GammaCompanding.cs b/src/ImageSharp/ColorSpaces/Companding/GammaCompanding.cs
index 13cca1582d..09b324b00f 100644
--- a/src/ImageSharp/ColorSpaces/Companding/GammaCompanding.cs
+++ b/src/ImageSharp/ColorSpaces/Companding/GammaCompanding.cs
@@ -7,7 +7,7 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Companding
{
///
- /// Implements gamma companding
+ /// Implements gamma companding.
///
///
///
diff --git a/src/ImageSharp/ColorSpaces/Companding/LCompanding.cs b/src/ImageSharp/ColorSpaces/Companding/LCompanding.cs
index 9e2cf8ad86..80b8aee7e7 100644
--- a/src/ImageSharp/ColorSpaces/Companding/LCompanding.cs
+++ b/src/ImageSharp/ColorSpaces/Companding/LCompanding.cs
@@ -8,7 +8,7 @@ using SixLabors.ImageSharp.ColorSpaces.Conversion;
namespace SixLabors.ImageSharp.ColorSpaces.Companding
{
///
- /// Implements L* companding
+ /// Implements L* companding.
///
///
/// For more info see:
@@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding
/// The representing the linear channel value.
[MethodImpl(InliningOptions.ShortMethod)]
public static float Expand(float channel)
- => channel <= 0.08 ? 100 * channel / CieConstants.Kappa : ImageMaths.Pow3((channel + 0.16F) / 1.16F);
+ => channel <= 0.08F ? (100F * channel) / CieConstants.Kappa : ImageMaths.Pow3((channel + 0.16F) / 1.16F);
///
/// Compresses an uncompanded channel (linear) to its nonlinear equivalent.
@@ -33,6 +33,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding
/// The representing the nonlinear channel value.
[MethodImpl(InliningOptions.ShortMethod)]
public static float Compress(float channel)
- => channel <= CieConstants.Epsilon ? channel * CieConstants.Kappa / 100F : MathF.Pow(1.16F * channel, 0.3333333F) - 0.16F;
+ => channel <= CieConstants.Epsilon ? (channel * CieConstants.Kappa) / 100F : (1.16F * MathF.Pow(channel, 0.3333333F)) - 0.16F;
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/ColorSpaces/Companding/Rec2020Companding.cs b/src/ImageSharp/ColorSpaces/Companding/Rec2020Companding.cs
index a3a9121727..773fe81647 100644
--- a/src/ImageSharp/ColorSpaces/Companding/Rec2020Companding.cs
+++ b/src/ImageSharp/ColorSpaces/Companding/Rec2020Companding.cs
@@ -7,14 +7,19 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Companding
{
///
- /// Implements Rec. 2020 companding function (for 12-bits).
+ /// Implements Rec. 2020 companding function.
///
///
///
- /// For 10-bits, companding is identical to
///
public static class Rec2020Companding
{
+ private const float Alpha = 1.09929682680944F;
+ private const float AlphaMinusOne = Alpha - 1F;
+ private const float Beta = 0.018053968510807F;
+ private const float InverseBeta = Beta * 4.5F;
+ private const float Epsilon = 1 / 0.45F;
+
///
/// Expands a companded channel to its linear equivalent with respect to the energy.
///
@@ -22,7 +27,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding
/// The representing the linear channel value.
[MethodImpl(InliningOptions.ShortMethod)]
public static float Expand(float channel)
- => channel < 0.08145F ? channel / 4.5F : MathF.Pow((channel + 0.0993F) / 1.0993F, 2.222222F);
+ => channel < InverseBeta ? channel / 4.5F : MathF.Pow((channel + AlphaMinusOne) / Alpha, Epsilon);
///
/// Compresses an uncompanded channel (linear) to its nonlinear equivalent.
@@ -31,6 +36,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding
/// The representing the nonlinear channel value.
[MethodImpl(InliningOptions.ShortMethod)]
public static float Compress(float channel)
- => channel < 0.0181F ? 4500F * channel : (1.0993F * channel) - 0.0993F;
+ => channel < Beta ? 4.5F * channel : (Alpha * MathF.Pow(channel, 0.45F)) - AlphaMinusOne;
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/ColorSpaces/Companding/Rec709Companding.cs b/src/ImageSharp/ColorSpaces/Companding/Rec709Companding.cs
index e2e802d08a..edc0b9763f 100644
--- a/src/ImageSharp/ColorSpaces/Companding/Rec709Companding.cs
+++ b/src/ImageSharp/ColorSpaces/Companding/Rec709Companding.cs
@@ -14,6 +14,8 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding
///
public static class Rec709Companding
{
+ private const float Epsilon = 1 / 0.45F;
+
///
/// Expands a companded channel to its linear equivalent with respect to the energy.
///
@@ -21,7 +23,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding
/// The representing the linear channel value.
[MethodImpl(InliningOptions.ShortMethod)]
public static float Expand(float channel)
- => channel < 0.081F ? channel / 4.5F : MathF.Pow((channel + 0.099F) / 1.099F, 2.222222F);
+ => channel < 0.081F ? channel / 4.5F : MathF.Pow((channel + 0.099F) / 1.099F, Epsilon);
///
/// Compresses an uncompanded channel (linear) to its nonlinear equivalent.
@@ -30,6 +32,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding
/// The representing the nonlinear channel value.
[MethodImpl(InliningOptions.ShortMethod)]
public static float Compress(float channel)
- => channel < 0.018F ? 4500F * channel : (1.099F * channel) - 0.099F;
+ => channel < 0.018F ? 4.5F * channel : (1.099F * MathF.Pow(channel, 0.45F)) - 0.099F;
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/ColorSpaces/Companding/SRgbCompanding.cs b/src/ImageSharp/ColorSpaces/Companding/SRgbCompanding.cs
index 5ae4629137..cc30c3632d 100644
--- a/src/ImageSharp/ColorSpaces/Companding/SRgbCompanding.cs
+++ b/src/ImageSharp/ColorSpaces/Companding/SRgbCompanding.cs
@@ -9,7 +9,7 @@ using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.ColorSpaces.Companding
{
///
- /// Implements sRGB companding
+ /// Implements sRGB companding.
///
///
/// For more info see:
@@ -30,9 +30,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding
for (int i = 0; i < vectors.Length; i++)
{
ref Vector4 v = ref Unsafe.Add(ref baseRef, i);
- v.X = Expand(v.X);
- v.Y = Expand(v.Y);
- v.Z = Expand(v.Z);
+ Expand(ref v);
}
}
@@ -48,9 +46,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding
for (int i = 0; i < vectors.Length; i++)
{
ref Vector4 v = ref Unsafe.Add(ref baseRef, i);
- v.X = Compress(v.X);
- v.Y = Compress(v.Y);
- v.Z = Compress(v.Z);
+ Compress(ref v);
}
}
@@ -58,17 +54,25 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding
/// Expands a companded vector to its linear equivalent with respect to the energy.
///
/// The vector.
- /// The representing the linear channel values.
[MethodImpl(InliningOptions.ShortMethod)]
- public static Vector4 Expand(Vector4 vector) => new Vector4(Expand(vector.X), Expand(vector.Y), Expand(vector.Z), vector.W);
+ public static void Expand(ref Vector4 vector)
+ {
+ vector.X = Expand(vector.X);
+ vector.Y = Expand(vector.Y);
+ vector.Z = Expand(vector.Z);
+ }
///
/// Compresses an uncompanded vector (linear) to its nonlinear equivalent.
///
/// The vector.
- /// The representing the nonlinear channel values.
[MethodImpl(InliningOptions.ShortMethod)]
- public static Vector4 Compress(Vector4 vector) => new Vector4(Compress(vector.X), Compress(vector.Y), Compress(vector.Z), vector.W);
+ public static void Compress(ref Vector4 vector)
+ {
+ vector.X = Compress(vector.X);
+ vector.Y = Compress(vector.Y);
+ vector.Z = Compress(vector.Z);
+ }
///
/// Expands a companded channel to its linear equivalent with respect to the energy.
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs
index 3c197673d3..bfabbb21d5 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs
@@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
private static readonly CieLchToCieLabConverter CieLchToCieLabConverter = new CieLchToCieLabConverter();
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -28,12 +28,11 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
// Conversion (perserving white point)
CieLab unadapted = CieLchToCieLabConverter.Convert(color);
- // Adaptation
return this.Adapt(unadapted);
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -54,7 +53,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -66,7 +65,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -87,7 +86,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -99,7 +98,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -120,7 +119,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -132,7 +131,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -159,10 +158,8 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// The
public CieLab ToCieLab(in CieXyz color)
{
- // Adaptation
CieXyz adapted = this.Adapt(color, this.whitePoint, this.targetLabWhitePoint);
- // Conversion
return this.cieXyzToCieLabConverter.Convert(adapted);
}
@@ -199,7 +196,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -220,7 +217,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -232,7 +229,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -253,7 +250,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -264,7 +261,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -285,7 +282,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -297,7 +294,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -318,7 +315,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -330,7 +327,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -351,7 +348,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -363,7 +360,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -384,7 +381,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -396,7 +393,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -429,7 +426,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs
index 0a8607e3bc..7f4abfa7bb 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs
@@ -26,15 +26,13 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// The
public CieLch ToCieLch(in CieLab color)
{
- // Adaptation
CieLab adapted = this.Adapt(color);
- // Conversion
return CieLabToCieLchConverter.Convert(adapted);
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -55,7 +53,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -67,7 +65,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -88,7 +86,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -100,7 +98,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -121,7 +119,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -133,7 +131,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -166,7 +164,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -187,7 +185,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -198,7 +196,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -219,7 +217,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -231,7 +229,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -252,7 +250,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -264,7 +262,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -285,7 +283,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -297,7 +295,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -318,7 +316,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -330,7 +328,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -351,7 +349,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -363,7 +361,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -384,7 +382,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -396,7 +394,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -417,7 +415,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -429,7 +427,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs
index 3a779ee722..6ca40af035 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs
@@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -53,7 +53,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -86,21 +86,19 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
public CieLchuv ToCieLchuv(in CieLuv color)
{
- // Adaptation
CieLuv adapted = this.Adapt(color);
- // Conversion
return CieLuvToCieLchuvConverter.Convert(adapted);
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -121,7 +119,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -133,7 +131,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -154,7 +152,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -166,7 +164,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -187,7 +185,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -199,7 +197,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -220,7 +218,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -253,7 +251,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -265,7 +263,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -286,7 +284,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -298,7 +296,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -319,7 +317,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -331,7 +329,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -352,7 +350,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -364,7 +362,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -385,7 +383,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -397,7 +395,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -418,7 +416,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -429,7 +427,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs
index 90eb8e34d7..316b35f37a 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs
@@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
private static readonly CieLchuvToCieLuvConverter CieLchuvToCieLuvConverter = new CieLchuvToCieLuvConverter();
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -60,7 +60,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -81,7 +81,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -95,7 +95,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -116,7 +116,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -127,7 +127,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -148,7 +148,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -162,7 +162,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -183,7 +183,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -194,7 +194,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -226,7 +226,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -247,7 +247,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -258,7 +258,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -279,7 +279,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -290,7 +290,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -311,7 +311,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -322,7 +322,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -343,7 +343,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -354,7 +354,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -375,7 +375,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -386,7 +386,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -407,7 +407,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -418,10 +418,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
- /// The span to the source colors
- /// The span to the destination colors
+ /// The span to the source colors.
+ /// The span to the destination colors.
public void Convert(ReadOnlySpan source, Span destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs
index fada6d9c59..2cc8437bba 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs
@@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
private LinearRgbToCieXyzConverter linearRgbToCieXyzConverter;
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -38,7 +38,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -73,7 +73,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -94,7 +94,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -108,7 +108,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -143,7 +143,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -164,7 +164,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -175,7 +175,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -196,20 +196,19 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
public CieXyz ToCieXyz(in Cmyk color)
{
- // Conversion
var rgb = this.ToRgb(color);
return this.ToCieXyz(rgb);
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -230,23 +229,22 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
public CieXyz ToCieXyz(in Hsl color)
{
- // Conversion
var rgb = this.ToRgb(color);
return this.ToCieXyz(rgb);
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
- /// The span to the source colors
- /// The span to the destination colors
+ /// The span to the source colors.
+ /// The span to the destination colors.
public void Convert(ReadOnlySpan source, Span destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
@@ -264,7 +262,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -277,7 +275,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -298,21 +296,19 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
public CieXyz ToCieXyz(in HunterLab color)
{
- // Conversion
CieXyz unadapted = HunterLabToCieXyzConverter.Convert(color);
- // Adaptation
return this.Adapt(unadapted, color.WhitePoint);
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -333,7 +329,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -343,15 +339,14 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
LinearRgbToCieXyzConverter converter = this.GetLinearRgbToCieXyzConverter(color.WorkingSpace);
CieXyz unadapted = converter.Convert(color);
- // Adaptation
return this.Adapt(unadapted, color.WorkingSpace.WhitePoint);
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
- /// The span to the source colors
- /// The span to the destination colors
+ /// The span to the source colors.
+ /// The span to the destination colors.
public void Convert(ReadOnlySpan source, Span destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
@@ -369,18 +364,17 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
public CieXyz ToCieXyz(in Lms color)
{
- // Conversion
return this.cieXyzAndLmsConverter.Convert(color);
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -440,7 +434,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// The
public CieXyz ToCieXyz(in YCbCr color)
{
- // Conversion
var rgb = this.ToRgb(color);
return this.ToCieXyz(rgb);
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs
index b798516359..00e20e25b4 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs
@@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
private static readonly CmykAndRgbConverter CmykAndRgbConverter = new CmykAndRgbConverter();
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -62,7 +62,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -149,7 +149,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -161,7 +161,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -182,7 +182,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -215,7 +215,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -227,7 +227,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -248,7 +248,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -260,7 +260,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -281,7 +281,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -293,7 +293,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -314,7 +314,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -326,7 +326,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -347,7 +347,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -359,9 +359,9 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
- /// The span to the source colors
+ /// The span to the source colors,
/// The span to the destination colors
public void Convert(ReadOnlySpan source, Span destination)
{
@@ -380,7 +380,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -408,7 +408,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -420,7 +420,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs
index a7080b9749..76175f1cba 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs
@@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
private static readonly HslAndRgbConverter HslAndRgbConverter = new HslAndRgbConverter();
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -62,7 +62,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -83,7 +83,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -95,7 +95,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -116,7 +116,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -128,7 +128,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -149,7 +149,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -281,7 +281,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -293,7 +293,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -314,7 +314,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -326,7 +326,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -347,7 +347,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -359,7 +359,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -387,7 +387,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
public Hsl ToHsl(in Rgb color) => HslAndRgbConverter.Convert(color);
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -408,7 +408,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -420,7 +420,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs
index a2121203c1..e64beb3e49 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs
@@ -263,7 +263,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// Performs the bulk conversion from into
///
/// The span to the source colors
- /// The span to the destination colors
+ /// The span to the destination colors.
public void Convert(ReadOnlySpan source, Span destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs
index e5996c238e..91e5549ac8 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs
@@ -13,10 +13,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
public partial class ColorSpaceConverter
{
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
- /// The span to the source colors
- /// The span to the destination colors
+ /// The span to the source colors.
+ /// The span to the destination colors.
public void Convert(ReadOnlySpan source, Span destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
@@ -34,10 +34,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
- /// The span to the source colors
- /// The span to the destination colors
+ /// The span to the source colors.
+ /// The span to the destination colors.
public void Convert(ReadOnlySpan source, Span destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
@@ -55,7 +55,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -76,7 +76,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -97,10 +97,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
- /// The span to the source colors
- /// The span to the destination colors
+ /// The span to the source colors.
+ /// The span to the destination colors.
public void Convert(ReadOnlySpan source, Span destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
@@ -118,10 +118,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
- /// The span to the source colors
- /// The span to the destination colors
+ /// The span to the source colors.
+ /// The span to the destination colors.
public void Convert(ReadOnlySpan source, Span destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
@@ -139,7 +139,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -160,10 +160,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
- /// The span to the destination colors
+ /// The span to the destination colors.
public void Convert(ReadOnlySpan source, Span destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
@@ -181,7 +181,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -223,7 +223,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -244,7 +244,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -265,7 +265,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -286,7 +286,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -297,7 +297,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -308,7 +308,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -319,7 +319,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -330,7 +330,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -341,21 +341,19 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
public HunterLab ToHunterLab(in CieXyz color)
{
- // Adaptation
CieXyz adapted = this.Adapt(color, this.whitePoint, this.targetHunterLabWhitePoint);
- // Conversion
return this.cieXyzToHunterLabConverter.Convert(adapted);
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -366,7 +364,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -377,7 +375,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -388,7 +386,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -399,7 +397,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -421,7 +419,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs
index eef626be2f..4be3f0079d 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs
@@ -17,10 +17,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
private static readonly RgbToLinearRgbConverter RgbToLinearRgbConverter = new RgbToLinearRgbConverter();
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
- /// The span to the source colors
- /// The span to the destination colors
+ /// The span to the source colors.
+ /// The span to the destination colors.
public void Convert(ReadOnlySpan source, Span destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
@@ -38,10 +38,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
- /// The span to the source colors
- /// The span to the destination colors
+ /// The span to the source colors.
+ /// The span to the destination colors.
public void Convert(ReadOnlySpan source, Span destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
@@ -59,10 +59,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
- /// The span to the source colors
- /// The span to the destination colors
+ /// The span to the source colors.
+ /// The span to the destination colors.
public void Convert(ReadOnlySpan source, Span destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
@@ -80,10 +80,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
- /// The span to the source colors
- /// The span to the destination colors
+ /// The span to the source colors.
+ /// The span to the destination colors.
public void Convert(ReadOnlySpan source, Span destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
@@ -101,10 +101,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
- /// The span to the source colors
- /// The span to the destination colors
+ /// The span to the source colors.
+ /// The span to the destination colors.
public void Convert(ReadOnlySpan source, Span destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
@@ -122,10 +122,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
- /// The span to the source colors
- /// The span to the destination colors
+ /// The span to the source colors.
+ /// The span to the destination colors.
public void Convert(ReadOnlySpan source, Span destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
@@ -143,10 +143,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
- /// The span to the source colors
- /// The span to the destination colors
+ /// The span to the source colors.
+ /// The span to the destination colors.
public void Convert(ReadOnlySpan source, Span destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
@@ -164,10 +164,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
- /// The span to the source colors
- /// The span to the destination colors
+ /// The span to the source colors.
+ /// The span to the destination colors.
public void Convert(ReadOnlySpan source, Span destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
@@ -185,10 +185,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
- /// The span to the source colors
- /// The span to the destination colors
+ /// The span to the source colors.
+ /// The span to the destination colors.
public void Convert(ReadOnlySpan source, Span destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
@@ -206,10 +206,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
- /// The span to the source colors
- /// The span to the destination colors
+ /// The span to the source colors.
+ /// The span to the destination colors.
public void Convert(ReadOnlySpan source, Span destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
@@ -227,10 +227,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
- /// The span to the source colors
- /// The span to the destination colors
+ /// The span to the source colors.
+ /// The span to the destination colors.
public void Convert(ReadOnlySpan source, Span destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
@@ -248,7 +248,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -269,7 +269,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -290,7 +290,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -301,7 +301,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -312,7 +312,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -323,7 +323,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -345,7 +345,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -359,7 +359,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -370,7 +370,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -381,7 +381,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -392,7 +392,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -403,7 +403,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -425,7 +425,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs
index 5780f4f545..68cd34bf2e 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs
@@ -17,10 +17,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
private static readonly YCbCrAndRgbConverter YCbCrAndRgbConverter = new YCbCrAndRgbConverter();
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
- /// The span to the source colors
- /// The span to the destination colors
+ /// The span to the source colors.
+ /// The span to the destination colors.
public void Convert(ReadOnlySpan source, Span destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
@@ -38,10 +38,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
- /// The span to the source colors
- /// The span to the destination colors
+ /// The span to the source colors.
+ /// The span to the destination colors.
public void Convert(ReadOnlySpan source, Span destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
@@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -101,7 +101,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -122,7 +122,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -143,7 +143,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -164,7 +164,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -185,7 +185,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -206,7 +206,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -227,7 +227,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -248,7 +248,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Performs the bulk conversion from into
+ /// Performs the bulk conversion from into .
///
/// The span to the source colors
/// The span to the destination colors
@@ -269,7 +269,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -281,7 +281,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -293,7 +293,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -305,7 +305,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -317,7 +317,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -329,7 +329,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -341,7 +341,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -353,7 +353,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -365,7 +365,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -377,7 +377,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -389,7 +389,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
@@ -401,7 +401,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
///
- /// Converts a into a
+ /// Converts a into a .
///
/// The color to convert.
/// The
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CIeLchToCieLabConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CIeLchToCieLabConverter.cs
index dd352db809..40d8c5bc69 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CIeLchToCieLabConverter.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CIeLchToCieLabConverter.cs
@@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
// Conversion algorithm described here:
// https://en.wikipedia.org/wiki/Lab_color_space#Cylindrical_representation:_CIELCh_or_CIEHLC
float l = input.L, c = input.C, hDegrees = input.H;
- float hRadians = MathFExtensions.DegreeToRadian(hDegrees);
+ float hRadians = GeometryUtilities.DegreeToRadian(hDegrees);
float a = c * MathF.Cos(hRadians);
float b = c * MathF.Sin(hRadians);
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieLchConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieLchConverter.cs
index 81196604e5..2b859205a0 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieLchConverter.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieLchConverter.cs
@@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
float l = input.L, a = input.A, b = input.B;
float c = MathF.Sqrt((a * a) + (b * b));
float hRadians = MathF.Atan2(b, a);
- float hDegrees = MathFExtensions.RadianToDegree(hRadians);
+ float hDegrees = GeometryUtilities.RadianToDegree(hRadians);
// Wrap the angle round at 360.
hDegrees %= 360;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLchuvToCieLuvConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLchuvToCieLuvConverter.cs
index 4f5a20bec7..ba5b8bfb79 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLchuvToCieLuvConverter.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLchuvToCieLuvConverter.cs
@@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
// Conversion algorithm described here:
// https://en.wikipedia.org/wiki/CIELUV#Cylindrical_representation_.28CIELCH.29
float l = input.L, c = input.C, hDegrees = input.H;
- float hRadians = MathFExtensions.DegreeToRadian(hDegrees);
+ float hRadians = GeometryUtilities.DegreeToRadian(hDegrees);
float u = c * MathF.Cos(hRadians);
float v = c * MathF.Sin(hRadians);
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieLchuvConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieLchuvConverter.cs
index 297c18c5c3..3c7d356a5e 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieLchuvConverter.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieLchuvConverter.cs
@@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
float l = input.L, a = input.U, b = input.V;
float c = MathF.Sqrt((a * a) + (b * b));
float hRadians = MathF.Atan2(b, a);
- float hDegrees = MathFExtensions.RadianToDegree(hRadians);
+ float hDegrees = GeometryUtilities.RadianToDegree(hRadians);
// Wrap the angle round at 360.
hDegrees %= 360;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndCieXyyConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndCieXyyConverter.cs
index f33d1ddcc9..7767b7b448 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndCieXyyConverter.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndCieXyyConverter.cs
@@ -7,7 +7,7 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
///
- /// Color converter between CIE XYZ and CIE xyY
+ /// Color converter between CIE XYZ and CIE xyY.
/// for formulas.
///
internal sealed class CieXyzAndCieXyyConverter
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToHunterLabConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToHunterLabConverter.cs
index c27c61608d..f21235d06c 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToHunterLabConverter.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToHunterLabConverter.cs
@@ -45,9 +45,11 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
float ka = ComputeKa(this.HunterLabWhitePoint);
float kb = ComputeKb(this.HunterLabWhitePoint);
- float l = 100 * MathF.Sqrt(y / yn);
- float a = ka * (((x / xn) - (y / yn)) / MathF.Sqrt(y / yn));
- float b = kb * (((y / yn) - (z / zn)) / MathF.Sqrt(y / yn));
+ float yByYn = y / yn;
+ float sqrtYbyYn = MathF.Sqrt(yByYn);
+ float l = 100 * sqrtYbyYn;
+ float a = ka * (((x / xn) - yByYn) / sqrtYbyYn);
+ float b = kb * ((yByYn - (z / zn)) / sqrtYbyYn);
if (float.IsNaN(a))
{
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToLinearRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToLinearRgbConverter.cs
index 3812cdbdd8..f166e4c14a 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToLinearRgbConverter.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToLinearRgbConverter.cs
@@ -28,11 +28,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
public CieXyzToLinearRgbConverter(RgbWorkingSpaceBase workingSpace)
{
this.TargetWorkingSpace = workingSpace;
- this.conversionMatrix = GetRgbToCieXyzMatrix(workingSpace);
+
+ // Gets the inverted Rgb -> Xyz matrix
+ Matrix4x4.Invert(GetRgbToCieXyzMatrix(workingSpace), out Matrix4x4 inverted);
+
+ this.conversionMatrix = inverted;
}
///
- /// Gets the target working space
+ /// Gets the target working space.
///
public RgbWorkingSpaceBase TargetWorkingSpace { get; }
@@ -40,12 +44,12 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
/// Performs the conversion from the input to an instance of type.
///
/// The input color instance.
- /// The converted result
+ /// The converted result.
[MethodImpl(InliningOptions.ShortMethod)]
public LinearRgb Convert(in CieXyz input)
{
- Matrix4x4.Invert(this.conversionMatrix, out Matrix4x4 inverted);
- var vector = Vector3.Transform(input.ToVector3(), inverted);
+ var vector = Vector3.Transform(input.ToVector3(), this.conversionMatrix);
+
return new LinearRgb(vector, this.TargetWorkingSpace);
}
}
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CmykAndRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CmykAndRgbConverter.cs
index 29fd32905b..0700dab43a 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CmykAndRgbConverter.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CmykAndRgbConverter.cs
@@ -8,7 +8,7 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
///
- /// Color converter between and
+ /// Color converter between and .
///
internal sealed class CmykAndRgbConverter
{
@@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
/// Performs the conversion from the input to an instance of type.
///
/// The input color instance.
- /// The converted result
+ /// The converted result.
[MethodImpl(InliningOptions.ShortMethod)]
public Cmyk Convert(in Rgb input)
{
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HunterLabToCieXyzConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HunterLabToCieXyzConverter.cs
index 783d29a557..4d6808e6c0 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HunterLabToCieXyzConverter.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HunterLabToCieXyzConverter.cs
@@ -26,9 +26,12 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
float ka = ComputeKa(input.WhitePoint);
float kb = ComputeKb(input.WhitePoint);
- float y = ImageMaths.Pow2(l / 100F) * yn;
- float x = (((a / ka) * MathF.Sqrt(y / yn)) + (y / yn)) * xn;
- float z = (((b / kb) * MathF.Sqrt(y / yn)) - (y / yn)) * (-zn);
+ float pow = ImageMaths.Pow2(l / 100F);
+ float sqrtPow = MathF.Sqrt(pow);
+ float y = pow * yn;
+
+ float x = (((a / ka) * sqrtPow) + pow) * xn;
+ float z = (((b / kb) * sqrtPow) - pow) * (-zn);
return new CieXyz(x, y, z);
}
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToRgbConverter.cs
index 1cc055bee2..8454430935 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToRgbConverter.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToRgbConverter.cs
@@ -6,7 +6,7 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
///
- /// Color converter between and
+ /// Color converter between and .
///
internal sealed class LinearRgbToRgbConverter
{
@@ -14,16 +14,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
/// Performs the conversion from the input to an instance of type.
///
/// The input color instance.
- /// The converted result
+ /// The converted result.
[MethodImpl(InliningOptions.ShortMethod)]
public Rgb Convert(in LinearRgb input)
{
- var vector = input.ToVector3();
- vector.X = input.WorkingSpace.Compress(vector.X);
- vector.Y = input.WorkingSpace.Compress(vector.Y);
- vector.Z = input.WorkingSpace.Compress(vector.Z);
-
- return new Rgb(vector, input.WorkingSpace);
+ return new Rgb(
+ r: input.WorkingSpace.Compress(input.R),
+ g: input.WorkingSpace.Compress(input.G),
+ b: input.WorkingSpace.Compress(input.B),
+ workingSpace: input.WorkingSpace);
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/RgbToLinearRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/RgbToLinearRgbConverter.cs
index 03912a421e..4ddbe42e54 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/RgbToLinearRgbConverter.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/RgbToLinearRgbConverter.cs
@@ -6,7 +6,7 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
///
- /// Color converter between Rgb and LinearRgb
+ /// Color converter between Rgb and LinearRgb.
///
internal class RgbToLinearRgbConverter
{
@@ -14,16 +14,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
/// Performs the conversion from the input to an instance of type.
///
/// The input color instance.
- /// The converted result
+ /// The converted result.
[MethodImpl(InliningOptions.ShortMethod)]
public LinearRgb Convert(in Rgb input)
{
- var vector = input.ToVector3();
- vector.X = input.WorkingSpace.Expand(vector.X);
- vector.Y = input.WorkingSpace.Expand(vector.Y);
- vector.Z = input.WorkingSpace.Expand(vector.Z);
-
- return new LinearRgb(vector, input.WorkingSpace);
+ return new LinearRgb(
+ r: input.WorkingSpace.Expand(input.R),
+ g: input.WorkingSpace.Expand(input.G),
+ b: input.WorkingSpace.Expand(input.B),
+ workingSpace: input.WorkingSpace);
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/YCbCrAndRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/YCbCrAndRgbConverter.cs
index 4ac3ad3cf9..ee15ffa508 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/YCbCrAndRgbConverter.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/YCbCrAndRgbConverter.cs
@@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
/// Performs the conversion from the input to an instance of type.
///
/// The input color instance.
- /// The converted result
+ /// The converted result.
[MethodImpl(InliningOptions.ShortMethod)]
public Rgb Convert(in YCbCr input)
{
@@ -38,7 +38,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
/// Performs the conversion from the input to an instance of type.
///
/// The input color instance.
- /// The converted result
+ /// The converted result.
[MethodImpl(InliningOptions.ShortMethod)]
public YCbCr Convert(in Rgb input)
{
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/RGBPrimariesChromaticityCoordinates.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/RGBPrimariesChromaticityCoordinates.cs
index 68b4d95fc6..4c69133e0f 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/RGBPrimariesChromaticityCoordinates.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/RGBPrimariesChromaticityCoordinates.cs
@@ -86,14 +86,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
}
///
- public override int GetHashCode()
- {
- unchecked
- {
- int hashCode = this.R.GetHashCode();
- hashCode = (hashCode * 397) ^ this.G.GetHashCode();
- return (hashCode * 397) ^ this.B.GetHashCode();
- }
- }
+ public override int GetHashCode() => HashCode.Combine(this.R, this.G, this.B);
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/GammaWorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/GammaWorkingSpace.cs
index 73aa60b6cb..c45dea3179 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/GammaWorkingSpace.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/GammaWorkingSpace.cs
@@ -1,6 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
+using System;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces.Companding;
@@ -9,7 +10,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
///
/// The gamma working space.
///
- public class GammaWorkingSpace : RgbWorkingSpaceBase
+ public sealed class GammaWorkingSpace : RgbWorkingSpaceBase
{
///
/// Initializes a new instance of the class.
@@ -57,10 +58,9 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
}
///
- public override int GetHashCode()
- {
- int hash = base.GetHashCode();
- return HashHelpers.Combine(hash, this.Gamma.GetHashCode());
- }
+ public override int GetHashCode() => HashCode.Combine(
+ this.WhitePoint,
+ this.ChromaticityCoordinates,
+ this.Gamma);
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/RgbWorkingSpaceBase.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/RgbWorkingSpaceBase.cs
index 5a89321c8f..6051f52865 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/RgbWorkingSpaceBase.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/RgbWorkingSpaceBase.cs
@@ -1,6 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
+using System;
+
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
///
@@ -76,8 +78,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
///
public override int GetHashCode()
{
- int hash = this.WhitePoint.GetHashCode();
- return HashHelpers.Combine(hash, this.ChromaticityCoordinates.GetHashCode());
+ return HashCode.Combine(this.WhitePoint, this.ChromaticityCoordinates);
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/ColorSpaces/Conversion/VonKriesChromaticAdaptation.cs b/src/ImageSharp/ColorSpaces/Conversion/VonKriesChromaticAdaptation.cs
index 85a36331b2..a4d96d19e7 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/VonKriesChromaticAdaptation.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/VonKriesChromaticAdaptation.cs
@@ -10,13 +10,13 @@ using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
///
- /// Basic implementation of the von Kries chromatic adaptation model
+ /// Implementation of the von Kries chromatic adaptation model.
///
///
/// Transformation described here:
/// http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html
///
- public class VonKriesChromaticAdaptation : IChromaticAdaptation
+ public sealed class VonKriesChromaticAdaptation : IChromaticAdaptation
{
private readonly CieXyzAndLmsConverter converter;
diff --git a/src/ImageSharp/ColorSpaces/Hsl.cs b/src/ImageSharp/ColorSpaces/Hsl.cs
index f6e531df35..04b3bea41f 100644
--- a/src/ImageSharp/ColorSpaces/Hsl.cs
+++ b/src/ImageSharp/ColorSpaces/Hsl.cs
@@ -84,12 +84,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
///
[MethodImpl(InliningOptions.ShortMethod)]
- public override int GetHashCode()
- {
- int hash = this.H.GetHashCode();
- hash = HashHelpers.Combine(hash, this.S.GetHashCode());
- return HashHelpers.Combine(hash, this.L.GetHashCode());
- }
+ public override int GetHashCode() => HashCode.Combine(this.H, this.S, this.L);
///
public override string ToString() => FormattableString.Invariant($"Hsl({this.H:#0.##}, {this.S:#0.##}, {this.L:#0.##})");
diff --git a/src/ImageSharp/ColorSpaces/Hsv.cs b/src/ImageSharp/ColorSpaces/Hsv.cs
index 631f03d09f..8ccc74ae09 100644
--- a/src/ImageSharp/ColorSpaces/Hsv.cs
+++ b/src/ImageSharp/ColorSpaces/Hsv.cs
@@ -82,12 +82,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
///
[MethodImpl(InliningOptions.ShortMethod)]
- public override int GetHashCode()
- {
- int hash = this.H.GetHashCode();
- hash = HashHelpers.Combine(hash, this.S.GetHashCode());
- return HashHelpers.Combine(hash, this.V.GetHashCode());
- }
+ public override int GetHashCode() => HashCode.Combine(this.H, this.S, this.V);
///
public override string ToString() => FormattableString.Invariant($"Hsv({this.H:#0.##}, {this.S:#0.##}, {this.V:#0.##})");
diff --git a/src/ImageSharp/ColorSpaces/HunterLab.cs b/src/ImageSharp/ColorSpaces/HunterLab.cs
index f4fa29d314..402761d8c1 100644
--- a/src/ImageSharp/ColorSpaces/HunterLab.cs
+++ b/src/ImageSharp/ColorSpaces/HunterLab.cs
@@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
{
///
/// Represents an Hunter LAB color.
- ///
+ /// .
///
public readonly struct HunterLab : IEquatable
{
@@ -38,7 +38,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
public readonly float B;
///
- /// Gets the reference white point of this color
+ /// Gets the reference white point of this color.
///
public readonly CieXyz WhitePoint;
@@ -117,13 +117,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
///
[MethodImpl(InliningOptions.ShortMethod)]
- public override int GetHashCode()
- {
- int hash = this.L.GetHashCode();
- hash = HashHelpers.Combine(hash, this.A.GetHashCode());
- hash = HashHelpers.Combine(hash, this.B.GetHashCode());
- return HashHelpers.Combine(hash, this.WhitePoint.GetHashCode());
- }
+ public override int GetHashCode() => HashCode.Combine(this.L, this.A, this.B, this.WhitePoint);
///
public override string ToString() => FormattableString.Invariant($"HunterLab({this.L:#0.##}, {this.A:#0.##}, {this.B:#0.##})");
diff --git a/src/ImageSharp/ColorSpaces/Illuminants.cs b/src/ImageSharp/ColorSpaces/Illuminants.cs
index ed385e02cd..d5c1b3eed5 100644
--- a/src/ImageSharp/ColorSpaces/Illuminants.cs
+++ b/src/ImageSharp/ColorSpaces/Illuminants.cs
@@ -8,11 +8,9 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// Standard illuminants provide a basis for comparing images or colors recorded under different lighting
///
///
- /// Coefficients taken from:
- /// http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html
+ /// Coefficients taken from: http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html
///
- /// Descriptions taken from:
- /// http://en.wikipedia.org/wiki/Standard_illuminant
+ /// Descriptions taken from: http://en.wikipedia.org/wiki/Standard_illuminant
///
public static class Illuminants
{
diff --git a/src/ImageSharp/ColorSpaces/LinearRgb.cs b/src/ImageSharp/ColorSpaces/LinearRgb.cs
index ec6d18be2b..46b2275968 100644
--- a/src/ImageSharp/ColorSpaces/LinearRgb.cs
+++ b/src/ImageSharp/ColorSpaces/LinearRgb.cs
@@ -126,12 +126,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
///
[MethodImpl(InliningOptions.ShortMethod)]
- public override int GetHashCode()
- {
- int hash = this.R.GetHashCode();
- hash = HashHelpers.Combine(hash, this.G.GetHashCode());
- return HashHelpers.Combine(hash, this.B.GetHashCode());
- }
+ public override int GetHashCode() => HashCode.Combine(this.R, this.G, this.B);
///
public override string ToString() => FormattableString.Invariant($"LinearRgb({this.R:#0.##}, {this.G:#0.##}, {this.B:#0.##})");
diff --git a/src/ImageSharp/ColorSpaces/Lms.cs b/src/ImageSharp/ColorSpaces/Lms.cs
index 0a8b7aa7b9..0ee56abbc2 100644
--- a/src/ImageSharp/ColorSpaces/Lms.cs
+++ b/src/ImageSharp/ColorSpaces/Lms.cs
@@ -87,12 +87,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
public Vector3 ToVector3() => new Vector3(this.L, this.M, this.S);
///
- public override int GetHashCode()
- {
- int hash = this.L.GetHashCode();
- hash = HashHelpers.Combine(hash, this.M.GetHashCode());
- return HashHelpers.Combine(hash, this.S.GetHashCode());
- }
+ public override int GetHashCode() => HashCode.Combine(this.L, this.M, this.S);
///
public override string ToString() => FormattableString.Invariant($"Lms({this.L:#0.##}, {this.M:#0.##}, {this.S:#0.##})");
diff --git a/src/ImageSharp/ColorSpaces/Rgb.cs b/src/ImageSharp/ColorSpaces/Rgb.cs
index 97fafbaf37..5cb89a2931 100644
--- a/src/ImageSharp/ColorSpaces/Rgb.cs
+++ b/src/ImageSharp/ColorSpaces/Rgb.cs
@@ -14,14 +14,14 @@ namespace SixLabors.ImageSharp.ColorSpaces
///
public readonly struct Rgb : IEquatable
{
- private static readonly Vector3 Min = Vector3.Zero;
- private static readonly Vector3 Max = Vector3.One;
-
///
- /// The default rgb working space
+ /// The default rgb working space.
///
public static readonly RgbWorkingSpaceBase DefaultWorkingSpace = RgbWorkingSpaces.SRgb;
+ private static readonly Vector3 Min = Vector3.Zero;
+ private static readonly Vector3 Max = Vector3.One;
+
///
/// Gets the red component.
/// A value usually ranging between 0 and 1.
@@ -147,12 +147,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
public Vector3 ToVector3() => new Vector3(this.R, this.G, this.B);
///
- public override int GetHashCode()
- {
- int hash = this.R.GetHashCode();
- hash = HashHelpers.Combine(hash, this.G.GetHashCode());
- return HashHelpers.Combine(hash, this.B.GetHashCode());
- }
+ public override int GetHashCode() => HashCode.Combine(this.R, this.G, this.B);
///
public override string ToString() => FormattableString.Invariant($"Rgb({this.R:#0.##}, {this.G:#0.##}, {this.B:#0.##})");
diff --git a/src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs b/src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs
index ee3822c152..7ddcae7cea 100644
--- a/src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs
+++ b/src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs
@@ -8,8 +8,7 @@ using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces
{
///
- /// Chromaticity coordinates taken from:
- ///
+ /// Chromaticity coordinates based on:
///
public static class RgbWorkingSpaces
{
diff --git a/src/ImageSharp/ColorSpaces/YCbCr.cs b/src/ImageSharp/ColorSpaces/YCbCr.cs
index 6aa191c2de..b0563bb899 100644
--- a/src/ImageSharp/ColorSpaces/YCbCr.cs
+++ b/src/ImageSharp/ColorSpaces/YCbCr.cs
@@ -83,12 +83,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
///
[MethodImpl(InliningOptions.ShortMethod)]
- public override int GetHashCode()
- {
- int hash = this.Y.GetHashCode();
- hash = HashHelpers.Combine(hash, this.Cb.GetHashCode());
- return HashHelpers.Combine(hash, this.Cr.GetHashCode());
- }
+ public override int GetHashCode() => HashCode.Combine(this.Y, this.Cb, this.Cr);
///
public override string ToString() => FormattableString.Invariant($"YCbCr({this.Y}, {this.Cb}, {this.Cr})");
diff --git a/src/ImageSharp/Common/Extensions/EncoderExtensions.cs b/src/ImageSharp/Common/Extensions/EncoderExtensions.cs
index 82899863c9..59c878485d 100644
--- a/src/ImageSharp/Common/Extensions/EncoderExtensions.cs
+++ b/src/ImageSharp/Common/Extensions/EncoderExtensions.cs
@@ -20,6 +20,11 @@ namespace SixLabors.ImageSharp
/// The string.
public static string GetString(this Encoding encoding, ReadOnlySpan buffer)
{
+ if (buffer.Length == 0)
+ {
+ return string.Empty;
+ }
+
fixed (byte* bytes = buffer)
{
return encoding.GetString(bytes, buffer.Length);
diff --git a/src/ImageSharp/Common/Extensions/EnumerableExtensions.cs b/src/ImageSharp/Common/Extensions/EnumerableExtensions.cs
index e97a2eaed0..e28db7cff1 100644
--- a/src/ImageSharp/Common/Extensions/EnumerableExtensions.cs
+++ b/src/ImageSharp/Common/Extensions/EnumerableExtensions.cs
@@ -45,7 +45,7 @@ namespace SixLabors.ImageSharp.Common
/// The start index, inclusive.
///
///
- /// A method that has one parameter and returns a calculating the end index
+ /// A method that has one parameter and returns a calculating the end index.
///
///
/// The incremental step.
@@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp.Common
/// The start index, inclusive.
///
///
- /// A method that has one parameter and returns a calculating the end index
+ /// A method that has one parameter and returns a calculating the end index.
///
///
/// The incremental step.
diff --git a/src/ImageSharp/Common/Extensions/StreamExtensions.cs b/src/ImageSharp/Common/Extensions/StreamExtensions.cs
index a200ffebcc..505ecccdda 100644
--- a/src/ImageSharp/Common/Extensions/StreamExtensions.cs
+++ b/src/ImageSharp/Common/Extensions/StreamExtensions.cs
@@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System;
+using System.Buffers;
using System.IO;
using SixLabors.ImageSharp.Memory;
@@ -82,5 +83,25 @@ namespace SixLabors.ImageSharp
{
stream.Write(buffer.Array, 0, buffer.Length());
}
+
+#if NET472 || NETSTANDARD1_3 || NETSTANDARD2_0
+ // This is a port of the CoreFX implementation and is MIT Licensed: https://github.com/dotnet/coreclr/blob/c4dca1072d15bdda64c754ad1ea474b1580fa554/src/System.Private.CoreLib/shared/System/IO/Stream.cs#L770
+ public static void Write(this Stream stream, ReadOnlySpan buffer)
+ {
+ // This uses ArrayPool.Shared, rather than taking a MemoryAllocator,
+ // in order to match the signature of the framework method that exists in
+ // .NET Core.
+ byte[] sharedBuffer = ArrayPool.Shared.Rent(buffer.Length);
+ try
+ {
+ buffer.CopyTo(sharedBuffer);
+ stream.Write(sharedBuffer, 0, buffer.Length);
+ }
+ finally
+ {
+ ArrayPool.Shared.Return(sharedBuffer);
+ }
+ }
+#endif
}
}
diff --git a/src/ImageSharp/Common/Helpers/DebugGuard.cs b/src/ImageSharp/Common/Helpers/DebugGuard.cs
index 2cf18b2456..43eebeac87 100644
--- a/src/ImageSharp/Common/Helpers/DebugGuard.cs
+++ b/src/ImageSharp/Common/Helpers/DebugGuard.cs
@@ -163,6 +163,20 @@ namespace SixLabors.ImageSharp
}
}
+ ///
+ /// Verifies whether a specific condition is met, throwing an exception if it's false.
+ ///
+ /// The condition
+ /// The error message
+ [Conditional("DEBUG")]
+ public static void IsTrue(bool target, string message)
+ {
+ if (!target)
+ {
+ throw new InvalidOperationException(message);
+ }
+ }
+
///
/// Verifies, that the method parameter with specified target value is false
/// and throws an exception if it is found to be so.
diff --git a/src/ImageSharp/Common/Helpers/Guard.cs b/src/ImageSharp/Common/Helpers/Guard.cs
index d8cf69a52e..e86da78e30 100644
--- a/src/ImageSharp/Common/Helpers/Guard.cs
+++ b/src/ImageSharp/Common/Helpers/Guard.cs
@@ -257,6 +257,26 @@ namespace SixLabors.ImageSharp
}
}
+ ///
+ /// Verifies that the 'destination' span is not shorter than 'source'.
+ ///
+ /// The source element type
+ /// The destination element type
+ /// The source span
+ /// The destination span
+ /// The name of the argument for 'destination'
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public static void DestinationShouldNotBeTooShort(
+ Span source,
+ Span destination,
+ string destinationParamName)
+ {
+ if (destination.Length < source.Length)
+ {
+ ThrowArgumentException($"Destination span is too short!", destinationParamName);
+ }
+ }
+
///
/// Verifies, that the `source` span has the length of 'minLength', or longer.
///
diff --git a/src/ImageSharp/Common/Helpers/ImageMaths.cs b/src/ImageSharp/Common/Helpers/ImageMaths.cs
index 6c52eded5f..0c5b051809 100644
--- a/src/ImageSharp/Common/Helpers/ImageMaths.cs
+++ b/src/ImageSharp/Common/Helpers/ImageMaths.cs
@@ -5,6 +5,7 @@ using System;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.ImageSharp.Processing.Processors.Transforms;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp
@@ -22,7 +23,8 @@ namespace SixLabors.ImageSharp
/// The blue component.
/// The .
[MethodImpl(InliningOptions.ShortMethod)]
- public static byte Get8BitBT709Luminance(byte r, byte g, byte b) => (byte)((r * .2126F) + (g * .7152F) + (b * .0722F));
+ public static byte Get8BitBT709Luminance(byte r, byte g, byte b) =>
+ (byte)((r * .2126F) + (g * .7152F) + (b * .0722F) + 0.5f);
///
/// Gets the luminance from the rgb components using the formula as specified by ITU-R Recommendation BT.709.
@@ -32,7 +34,8 @@ namespace SixLabors.ImageSharp
/// The blue component.
/// The .
[MethodImpl(InliningOptions.ShortMethod)]
- public static ushort Get16BitBT709Luminance(ushort r, ushort g, ushort b) => (ushort)((r * .2126F) + (g * .7152F) + (b * .0722F));
+ public static ushort Get16BitBT709Luminance(ushort r, ushort g, ushort b) =>
+ (ushort)((r * .2126F) + (g * .7152F) + (b * .0722F));
///
/// Scales a value from a 16 bit to it's 8 bit equivalent.
@@ -76,7 +79,7 @@ namespace SixLabors.ImageSharp
///
/// Scales a value from an 8 bit to it's 16 bit equivalent.
///
- /// The 8 bit compoonent value.
+ /// The 8 bit component value.
/// The
[MethodImpl(InliningOptions.ShortMethod)]
public static ushort UpscaleFrom8BitTo16Bit(byte component) => (ushort)(component * 257);
@@ -98,7 +101,6 @@ namespace SixLabors.ImageSharp
///
/// Determine the Least Common Multiple (LCM) of two numbers.
- /// TODO: This method might be useful for building a more compact
///
public static int LeastCommonMultiple(int a, int b)
{
@@ -123,10 +125,7 @@ namespace SixLabors.ImageSharp
/// should be power of 2.
///
[MethodImpl(InliningOptions.ShortMethod)]
- public static int ModuloP2(int x, int m)
- {
- return x & (m - 1);
- }
+ public static int ModuloP2(int x, int m) => x & (m - 1);
///
/// Returns the absolute value of a 32-bit signed integer. Uses bit shifting to speed up the operation.
diff --git a/src/ImageSharp/Common/Helpers/InliningOptions.cs b/src/ImageSharp/Common/Helpers/InliningOptions.cs
index ad85c4fc81..069a426d75 100644
--- a/src/ImageSharp/Common/Helpers/InliningOptions.cs
+++ b/src/ImageSharp/Common/Helpers/InliningOptions.cs
@@ -1,7 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
-// Uncomment this for verbose profiler results:
+// Uncomment this for verbose profiler results. DO NOT PUSH TO MAIN!
// #define PROFILING
using System.Runtime.CompilerServices;
diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.BasicIntrinsics256.cs b/src/ImageSharp/Common/Helpers/SimdUtils.BasicIntrinsics256.cs
index 0f1ce2ab6a..85c9f00748 100644
--- a/src/ImageSharp/Common/Helpers/SimdUtils.BasicIntrinsics256.cs
+++ b/src/ImageSharp/Common/Helpers/SimdUtils.BasicIntrinsics256.cs
@@ -90,7 +90,7 @@ namespace SixLabors.ImageSharp
var bVec = new Vector(256.0f / 255.0f);
var magicFloat = new Vector(32768.0f);
- var magicInt = new Vector(1191182336); // reinterpreded value of 32768.0f
+ var magicInt = new Vector(1191182336); // reinterpreted value of 32768.0f
var mask = new Vector(255);
ref Octet.OfByte sourceBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source));
diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs b/src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs
index 2ac577264c..83216aaa72 100644
--- a/src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs
+++ b/src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs
@@ -1,4 +1,7 @@
-using System;
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@@ -25,6 +28,20 @@ namespace SixLabors.ImageSharp
false;
#endif
+ ///
+ /// Widen and convert a vector of values into 2 vectors of -s.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static void ConvertToSingle(
+ Vector source,
+ out Vector dest1,
+ out Vector dest2)
+ {
+ Vector.Widen(source, out Vector i1, out Vector i2);
+ dest1 = Vector.ConvertToSingle(i1);
+ dest2 = Vector.ConvertToSingle(i2);
+ }
+
///
/// as many elements as possible, slicing them down (keeping the remainder).
///
diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.cs b/src/ImageSharp/Common/Helpers/SimdUtils.cs
index a989cc8752..867e7b9de1 100644
--- a/src/ImageSharp/Common/Helpers/SimdUtils.cs
+++ b/src/ImageSharp/Common/Helpers/SimdUtils.cs
@@ -169,7 +169,7 @@ namespace SixLabors.ImageSharp
DebugGuard.IsTrue(
ImageMaths.ModuloP2(dest.Length, shouldBeDivisibleBy) == 0,
nameof(source),
- $"length should be divisable by {shouldBeDivisibleBy}!");
+ $"length should be divisible by {shouldBeDivisibleBy}!");
}
[Conditional("DEBUG")]
@@ -179,7 +179,7 @@ namespace SixLabors.ImageSharp
DebugGuard.IsTrue(
ImageMaths.ModuloP2(dest.Length, shouldBeDivisibleBy) == 0,
nameof(source),
- $"length should be divisable by {shouldBeDivisibleBy}!");
+ $"length should be divisible by {shouldBeDivisibleBy}!");
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Common/Helpers/TestHelpers.cs b/src/ImageSharp/Common/Helpers/TestHelpers.cs
index fd16a577ab..d330233c4c 100644
--- a/src/ImageSharp/Common/Helpers/TestHelpers.cs
+++ b/src/ImageSharp/Common/Helpers/TestHelpers.cs
@@ -1,4 +1,4 @@
-// Copyright(c) Six Labors and contributors.
+// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Common.Helpers
diff --git a/src/ImageSharp/Common/Helpers/TolerantMath.cs b/src/ImageSharp/Common/Helpers/TolerantMath.cs
new file mode 100644
index 0000000000..bef7eb1610
--- /dev/null
+++ b/src/ImageSharp/Common/Helpers/TolerantMath.cs
@@ -0,0 +1,106 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Runtime.CompilerServices;
+
+namespace SixLabors.ImageSharp
+{
+ ///
+ /// Implements basic math operations using tolerant comparison
+ /// whenever an equality check is needed.
+ ///
+ internal readonly struct TolerantMath
+ {
+ private readonly double epsilon;
+
+ private readonly double negEpsilon;
+
+ ///
+ /// A read-only default instance for using 1e-8 as epsilon.
+ /// It is a field so it can be passed as an 'in' parameter.
+ /// Does not necessarily fit all use cases!
+ ///
+ public static readonly TolerantMath Default = new TolerantMath(1e-8);
+
+ public TolerantMath(double epsilon)
+ {
+ DebugGuard.MustBeGreaterThan(epsilon, 0, nameof(epsilon));
+
+ this.epsilon = epsilon;
+ this.negEpsilon = -epsilon;
+ }
+
+ ///
+ /// == 0
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public bool IsZero(double a) => a > this.negEpsilon && a < this.epsilon;
+
+ ///
+ /// > 0
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public bool IsPositive(double a) => a > this.epsilon;
+
+ ///
+ /// < 0
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public bool IsNegative(double a) => a < this.negEpsilon;
+
+ ///
+ /// ==
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public bool AreEqual(double a, double b) => this.IsZero(a - b);
+
+ ///
+ /// >
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public bool IsGreater(double a, double b) => a > b + this.epsilon;
+
+ ///
+ /// <
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public bool IsLess(double a, double b) => a < b - this.epsilon;
+
+ ///
+ /// >=
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public bool IsGreaterOrEqual(double a, double b) => a >= b - this.epsilon;
+
+ ///
+ /// <=
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public bool IsLessOrEqual(double a, double b) => b >= a - this.epsilon;
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public double Ceiling(double a)
+ {
+ double rem = Math.IEEERemainder(a, 1);
+ if (this.IsZero(rem))
+ {
+ return Math.Round(a);
+ }
+
+ return Math.Ceiling(a);
+ }
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public double Floor(double a)
+ {
+ double rem = Math.IEEERemainder(a, 1);
+ if (this.IsZero(rem))
+ {
+ return Math.Round(a);
+ }
+
+ return Math.Floor(a);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Common/Helpers/UnitConverter.cs b/src/ImageSharp/Common/Helpers/UnitConverter.cs
index c8b25bf564..75dbb032c5 100644
--- a/src/ImageSharp/Common/Helpers/UnitConverter.cs
+++ b/src/ImageSharp/Common/Helpers/UnitConverter.cs
@@ -2,8 +2,8 @@
// Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices;
-using SixLabors.ImageSharp.MetaData;
-using SixLabors.ImageSharp.MetaData.Profiles.Exif;
+using SixLabors.ImageSharp.Metadata;
+using SixLabors.ImageSharp.Metadata.Profiles.Exif;
namespace SixLabors.ImageSharp.Common.Helpers
{
diff --git a/src/ImageSharp/Common/Helpers/Vector4Utils.cs b/src/ImageSharp/Common/Helpers/Vector4Utils.cs
index 75bb00b6a5..5c122217d1 100644
--- a/src/ImageSharp/Common/Helpers/Vector4Utils.cs
+++ b/src/ImageSharp/Common/Helpers/Vector4Utils.cs
@@ -5,6 +5,7 @@ using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+using SixLabors.ImageSharp.Primitives;
namespace SixLabors.ImageSharp
{
@@ -70,5 +71,41 @@ namespace SixLabors.ImageSharp
UnPremultiply(ref v);
}
}
+
+ ///
+ /// Transforms a vector by the given matrix.
+ ///
+ /// The source vector.
+ /// The transformation matrix.
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public static void Transform(ref Vector4 vector, ref ColorMatrix matrix)
+ {
+ float x = vector.X;
+ float y = vector.Y;
+ float z = vector.Z;
+ float w = vector.W;
+
+ vector.X = (x * matrix.M11) + (y * matrix.M21) + (z * matrix.M31) + (w * matrix.M41) + matrix.M51;
+ vector.Y = (x * matrix.M12) + (y * matrix.M22) + (z * matrix.M32) + (w * matrix.M42) + matrix.M52;
+ vector.Z = (x * matrix.M13) + (y * matrix.M23) + (z * matrix.M33) + (w * matrix.M43) + matrix.M53;
+ vector.W = (x * matrix.M14) + (y * matrix.M24) + (z * matrix.M34) + (w * matrix.M44) + matrix.M54;
+ }
+
+ ///
+ /// Bulk variant of
+ ///
+ /// The span of vectors
+ /// The transformation matrix.
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public static void Transform(Span vectors, ref ColorMatrix matrix)
+ {
+ ref Vector4 baseRef = ref MemoryMarshal.GetReference(vectors);
+
+ for (int i = 0; i < vectors.Length; i++)
+ {
+ ref Vector4 v = ref Unsafe.Add(ref baseRef, i);
+ Transform(ref v, ref matrix);
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Common/ParallelUtils/ParallelExecutionSettings.cs b/src/ImageSharp/Common/ParallelUtils/ParallelExecutionSettings.cs
index 0b45719c38..40163bc789 100644
--- a/src/ImageSharp/Common/ParallelUtils/ParallelExecutionSettings.cs
+++ b/src/ImageSharp/Common/ParallelUtils/ParallelExecutionSettings.cs
@@ -1,4 +1,4 @@
-// Copyright(c) Six Labors and contributors.
+// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Threading.Tasks;
diff --git a/src/ImageSharp/Common/ParallelUtils/ParallelHelper.cs b/src/ImageSharp/Common/ParallelUtils/ParallelHelper.cs
index 1d1734a863..a930b8390f 100644
--- a/src/ImageSharp/Common/ParallelUtils/ParallelHelper.cs
+++ b/src/ImageSharp/Common/ParallelUtils/ParallelHelper.cs
@@ -1,4 +1,4 @@
-// Copyright(c) Six Labors and contributors.
+// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@@ -85,7 +85,7 @@ namespace SixLabors.ImageSharp.ParallelUtils
Rectangle rectangle,
in ParallelExecutionSettings parallelSettings,
Action> body)
- where T : struct
+ where T : unmanaged
{
int maxSteps = DivideCeil(rectangle.Width * rectangle.Height, parallelSettings.MinimumPixelsProcessedPerTask);
@@ -135,7 +135,7 @@ namespace SixLabors.ImageSharp.ParallelUtils
Rectangle rectangle,
Configuration configuration,
Action> body)
- where T : struct
+ where T : unmanaged
{
IterateRowsWithTempBuffer(rectangle, configuration.GetParallelSettings(), body);
}
diff --git a/src/ImageSharp/Common/Tuples/Octet.cs b/src/ImageSharp/Common/Tuples/Octet.cs
index 539b74e324..7ece2ed562 100644
--- a/src/ImageSharp/Common/Tuples/Octet.cs
+++ b/src/ImageSharp/Common/Tuples/Octet.cs
@@ -1,4 +1,7 @@
-using System.Runtime.CompilerServices;
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.Tuples
@@ -9,7 +12,7 @@ namespace SixLabors.ImageSharp.Tuples
internal static class Octet
{
///
- /// Value tuple of -s
+ /// Value tuple of -s.
///
[StructLayout(LayoutKind.Explicit, Size = 8 * sizeof(uint))]
public struct OfUInt32
diff --git a/src/ImageSharp/Common/Tuples/Vector4Pair.cs b/src/ImageSharp/Common/Tuples/Vector4Pair.cs
index cae283d628..b3a32deeef 100644
--- a/src/ImageSharp/Common/Tuples/Vector4Pair.cs
+++ b/src/ImageSharp/Common/Tuples/Vector4Pair.cs
@@ -1,4 +1,7 @@
-using System.Numerics;
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@@ -37,12 +40,11 @@ namespace SixLabors.ImageSharp.Tuples
this.B += other.B;
}
- ///
- /// Downscale method, specific to Jpeg color conversion. Works only if Vector{float}.Count == 4!
- /// TODO: Move it somewhere else.
+ /// .
+ /// Downscale method, specific to Jpeg color conversion. Works only if Vector{float}.Count == 4! /// TODO: Move it somewhere else.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal void RoundAndDownscalePreAvx2()
+ internal void RoundAndDownscalePreAvx2(float downscaleFactor)
{
ref Vector a = ref Unsafe.As>(ref this.A);
a = a.FastRound();
@@ -50,8 +52,8 @@ namespace SixLabors.ImageSharp.Tuples
ref Vector b = ref Unsafe.As>(ref this.B);
b = b.FastRound();
- // Downscale by 1/255
- var scale = new Vector4(1 / 255f);
+ // Downscale by 1/factor
+ var scale = new Vector4(1 / downscaleFactor);
this.A *= scale;
this.B *= scale;
}
@@ -61,14 +63,14 @@ namespace SixLabors.ImageSharp.Tuples
/// TODO: Move it somewhere else.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal void RoundAndDownscaleAvx2()
+ internal void RoundAndDownscaleAvx2(float downscaleFactor)
{
ref Vector self = ref Unsafe.As>(ref this);
Vector v = self;
v = v.FastRound();
- // Downscale by 1/255
- v *= new Vector(1 / 255f);
+ // Downscale by 1/factor
+ v *= new Vector(1 / downscaleFactor);
self = v;
}
diff --git a/src/ImageSharp/Formats/Bmp/BmpCompression.cs b/src/ImageSharp/Formats/Bmp/BmpCompression.cs
index ef063f0106..be275019e6 100644
--- a/src/ImageSharp/Formats/Bmp/BmpCompression.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpCompression.cs
@@ -25,7 +25,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// If the first byte is zero, the record has different meanings, depending
/// on the second byte. If the second byte is zero, it is the end of the row,
/// if it is one, it is the end of the image.
- /// Not supported at the moment.
///
RLE8 = 1,
@@ -35,14 +34,12 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// If the first byte is zero, the record has different meanings, depending
/// on the second byte. If the second byte is zero, it is the end of the row,
/// if it is one, it is the end of the image.
- /// Not supported at the moment.
///
RLE4 = 2,
///
/// Each image row has a multiple of four elements. If the
/// row has less elements, zeros will be added at the right side.
- /// Not supported at the moment.
///
BitFields = 3,
@@ -56,6 +53,13 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// The bitmap contains a PNG image.
/// Not supported at the moment.
///
- PNG = 5
+ PNG = 5,
+
+ ///
+ /// Introduced with Windows CE.
+ /// Specifies that the bitmap is not compressed and that the color table consists of four DWORD color
+ /// masks that specify the red, green, blue, and alpha components of each pixel.
+ ///
+ BI_ALPHABITFIELDS = 6
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Bmp/BmpConstants.cs b/src/ImageSharp/Formats/Bmp/BmpConstants.cs
index 99799b619c..5cbed4af2b 100644
--- a/src/ImageSharp/Formats/Bmp/BmpConstants.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpConstants.cs
@@ -19,5 +19,41 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// The list of file extensions that equate to a bmp.
///
public static readonly IEnumerable FileExtensions = new[] { "bm", "bmp", "dip" };
+
+ ///
+ /// Valid magic bytes markers identifying a Bitmap file.
+ ///
+ internal static class TypeMarkers
+ {
+ ///
+ /// Single-image BMP file that may have been created under Windows or OS/2.
+ ///
+ public const int Bitmap = 0x4D42;
+
+ ///
+ /// OS/2 Bitmap Array.
+ ///
+ public const int BitmapArray = 0x4142;
+
+ ///
+ /// OS/2 Color Icon.
+ ///
+ public const int ColorIcon = 0x4943;
+
+ ///
+ /// OS/2 Color Pointer.
+ ///
+ public const int ColorPointer = 0x5043;
+
+ ///
+ /// OS/2 Icon.
+ ///
+ public const int Icon = 0x4349;
+
+ ///
+ /// OS/2 Pointer.
+ ///
+ public const int Pointer = 0x5450;
+ }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs
index 3d079cf619..82af2a671e 100644
--- a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs
@@ -15,8 +15,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// - JPG
/// - PNG
/// - RLE4
- /// - RLE8
- /// - BitFields
///
/// Formats will be supported in a later releases. We advise always
/// to use only 24 Bit Windows bitmaps.
diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
index 77cd322221..b52c3754e2 100644
--- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
@@ -2,19 +2,21 @@
// Licensed under the Apache License, Version 2.0.
using System;
+using System.Buffers;
using System.Buffers.Binary;
using System.IO;
+using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Common.Helpers;
using SixLabors.ImageSharp.Memory;
-using SixLabors.ImageSharp.MetaData;
+using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
namespace SixLabors.ImageSharp.Formats.Bmp
{
///
- /// Performs the bmp decoding operation.
+ /// Performs the bitmap decoding operation.
///
///
/// A useful decoding source example can be found at
@@ -22,19 +24,19 @@ namespace SixLabors.ImageSharp.Formats.Bmp
internal sealed class BmpDecoderCore
{
///
- /// The mask for the red part of the color for 16 bit rgb bitmaps.
+ /// The default mask for the red part of the color for 16 bit rgb bitmaps.
///
- private const int Rgb16RMask = 0x7C00;
+ private const int DefaultRgb16RMask = 0x7C00;
///
- /// The mask for the green part of the color for 16 bit rgb bitmaps.
+ /// The default mask for the green part of the color for 16 bit rgb bitmaps.
///
- private const int Rgb16GMask = 0x3E0;
+ private const int DefaultRgb16GMask = 0x3E0;
///
- /// The mask for the blue part of the color for 16 bit rgb bitmaps.
+ /// The default mask for the blue part of the color for 16 bit rgb bitmaps.
///
- private const int Rgb16BMask = 0x1F;
+ private const int DefaultRgb16BMask = 0x1F;
///
/// RLE8 flag value that indicates following byte has special meaning.
@@ -62,13 +64,17 @@ namespace SixLabors.ImageSharp.Formats.Bmp
private Stream stream;
///
- /// The metadata
+ /// The metadata.
///
- private ImageMetaData metaData;
+ private ImageMetadata metadata;
+
+ ///
+ /// The bmp specific metadata.
+ ///
+ private BmpMetadata bmpMetadata;
///
/// The file header containing general information.
- /// TODO: Why is this not used? We advance the stream but do not use the values parsed.
///
private BmpFileHeader fileHeader;
@@ -85,7 +91,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// Initializes a new instance of the class.
///
/// The configuration.
- /// The options
+ /// The options.
public BmpDecoderCore(Configuration configuration, IBmpDecoderOptions options)
{
this.configuration = configuration;
@@ -108,9 +114,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp
{
try
{
- this.ReadImageHeaders(stream, out bool inverted, out byte[] palette);
+ int bytesPerColorMapEntry = this.ReadImageHeaders(stream, out bool inverted, out byte[] palette);
- var image = new Image(this.configuration, this.infoHeader.Width, this.infoHeader.Height, this.metaData);
+ var image = new Image(this.configuration, this.infoHeader.Width, this.infoHeader.Height, this.metadata);
Buffer2D pixels = image.GetRootFramePixelBuffer();
@@ -119,7 +125,14 @@ namespace SixLabors.ImageSharp.Formats.Bmp
case BmpCompression.RGB:
if (this.infoHeader.BitsPerPixel == 32)
{
- this.ReadRgb32(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted);
+ if (this.bmpMetadata.InfoHeaderType == BmpInfoHeaderType.WinVersion3)
+ {
+ this.ReadRgb32Slow(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted);
+ }
+ else
+ {
+ this.ReadRgb32Fast(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted);
+ }
}
else if (this.infoHeader.BitsPerPixel == 24)
{
@@ -137,16 +150,27 @@ namespace SixLabors.ImageSharp.Formats.Bmp
this.infoHeader.Width,
this.infoHeader.Height,
this.infoHeader.BitsPerPixel,
+ bytesPerColorMapEntry,
inverted);
}
break;
case BmpCompression.RLE8:
- this.ReadRle8(pixels, palette, this.infoHeader.Width, this.infoHeader.Height, inverted);
+ case BmpCompression.RLE4:
+ this.ReadRle(this.infoHeader.Compression, pixels, palette, this.infoHeader.Width, this.infoHeader.Height, inverted);
break;
+
+ case BmpCompression.BitFields:
+ case BmpCompression.BI_ALPHABITFIELDS:
+ this.ReadBitFields(pixels, inverted);
+
+ break;
+
default:
- throw new NotSupportedException("Does not support this kind of bitmap files.");
+ BmpThrowHelper.ThrowNotSupportedException("Does not support this kind of bitmap files.");
+
+ break;
}
return image;
@@ -164,7 +188,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
public IImageInfo Identify(Stream stream)
{
this.ReadImageHeaders(stream, out _, out _);
- return new ImageInfo(new PixelTypeInfo(this.infoHeader.BitsPerPixel), this.infoHeader.Width, this.infoHeader.Height, this.metaData);
+ return new ImageInfo(new PixelTypeInfo(this.infoHeader.BitsPerPixel), this.infoHeader.Width, this.infoHeader.Height, this.metadata);
}
///
@@ -198,30 +222,66 @@ namespace SixLabors.ImageSharp.Formats.Bmp
}
///
- /// Performs final shifting from a 5bit value to an 8bit one.
+ /// Decodes a bitmap containing BITFIELDS Compression type. For each color channel, there will be bitmask
+ /// which will be used to determine which bits belong to that channel.
///
- /// The masked and shifted value
- /// The
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static byte GetBytesFrom5BitValue(int value) => (byte)((value << 3) | (value >> 2));
+ /// The pixel format.
+ /// The output pixel buffer containing the decoded image.
+ /// Whether the bitmap is inverted.
+ private void ReadBitFields(Buffer2D pixels, bool inverted)
+ where TPixel : struct, IPixel
+ {
+ if (this.infoHeader.BitsPerPixel == 16)
+ {
+ this.ReadRgb16(
+ pixels,
+ this.infoHeader.Width,
+ this.infoHeader.Height,
+ inverted,
+ this.infoHeader.RedMask,
+ this.infoHeader.GreenMask,
+ this.infoHeader.BlueMask);
+ }
+ else
+ {
+ this.ReadRgb32BitFields(
+ pixels,
+ this.infoHeader.Width,
+ this.infoHeader.Height,
+ inverted,
+ this.infoHeader.RedMask,
+ this.infoHeader.GreenMask,
+ this.infoHeader.BlueMask,
+ this.infoHeader.AlphaMask);
+ }
+ }
///
- /// Looks up color values and builds the image from de-compressed RLE8 data.
- /// Compresssed RLE8 stream is uncompressed by
+ /// Looks up color values and builds the image from de-compressed RLE8 or RLE4 data.
+ /// Compressed RLE8 stream is uncompressed by
+ /// Compressed RLE4 stream is uncompressed by
///
/// The pixel format.
+ /// The compression type. Either RLE4 or RLE8.
/// The to assign the palette to.
/// The containing the colors.
/// The width of the bitmap.
/// The height of the bitmap.
/// Whether the bitmap is inverted.
- private void ReadRle8(Buffer2D pixels, byte[] colors, int width, int height, bool inverted)
+ private void ReadRle(BmpCompression compression, Buffer2D pixels, byte[] colors, int width, int height, bool inverted)
where TPixel : struct, IPixel
{
TPixel color = default;
using (Buffer2D buffer = this.memoryAllocator.Allocate2D(width, height, AllocationOptions.Clean))
{
- this.UncompressRle8(width, buffer.GetSpan());
+ if (compression == BmpCompression.RLE8)
+ {
+ this.UncompressRle8(width, buffer.GetSpan());
+ }
+ else
+ {
+ this.UncompressRle4(width, buffer.GetSpan());
+ }
for (int y = 0; y < height; y++)
{
@@ -239,12 +299,122 @@ namespace SixLabors.ImageSharp.Formats.Bmp
}
///
- /// Produce uncompressed bitmap data from RLE8 stream
+ /// Produce uncompressed bitmap data from a RLE4 stream.
///
///
- /// RLE8 is a 2-byte run-length encoding
- ///
If first byte is 0, the second byte may have special meaning
- ///
Otherwise, first byte is the length of the run and second byte is the color for the run
+ /// RLE4 is a 2-byte run-length encoding.
+ ///
If first byte is 0, the second byte may have special meaning.
+ ///
Otherwise, the first byte is the length of the run and second byte contains two color indexes.
+ ///
+ /// The width of the bitmap.
+ /// Buffer for uncompressed data.
+ private void UncompressRle4(int w, Span buffer)
+ {
+#if NETCOREAPP2_1
+ Span cmd = stackalloc byte[2];
+#else
+ byte[] cmd = new byte[2];
+#endif
+ int count = 0;
+
+ while (count < buffer.Length)
+ {
+ if (this.stream.Read(cmd, 0, cmd.Length) != 2)
+ {
+ BmpThrowHelper.ThrowImageFormatException("Failed to read 2 bytes from the stream while uncompressing RLE4 bitmap.");
+ }
+
+ if (cmd[0] == RleCommand)
+ {
+ switch (cmd[1])
+ {
+ case RleEndOfBitmap:
+ return;
+
+ case RleEndOfLine:
+ int extra = count % w;
+ if (extra > 0)
+ {
+ count += w - extra;
+ }
+
+ break;
+
+ case RleDelta:
+ int dx = this.stream.ReadByte();
+ int dy = this.stream.ReadByte();
+ count += (w * dy) + dx;
+
+ break;
+
+ default:
+ // If the second byte > 2, we are in 'absolute mode'.
+ // The second byte contains the number of color indexes that follow.
+ int max = cmd[1];
+ int bytesToRead = (max + 1) / 2;
+
+ byte[] run = new byte[bytesToRead];
+
+ this.stream.Read(run, 0, run.Length);
+
+ int idx = 0;
+ for (int i = 0; i < max; i++)
+ {
+ byte twoPixels = run[idx];
+ if (i % 2 == 0)
+ {
+ byte leftPixel = (byte)((twoPixels >> 4) & 0xF);
+ buffer[count++] = leftPixel;
+ }
+ else
+ {
+ byte rightPixel = (byte)(twoPixels & 0xF);
+ buffer[count++] = rightPixel;
+ idx++;
+ }
+ }
+
+ // Absolute mode data is aligned to two-byte word-boundary
+ int padding = bytesToRead & 1;
+
+ this.stream.Skip(padding);
+
+ break;
+ }
+ }
+ else
+ {
+ int max = cmd[0];
+
+ // The second byte contains two color indexes, one in its high-order 4 bits and one in its low-order 4 bits.
+ byte twoPixels = cmd[1];
+ byte rightPixel = (byte)(twoPixels & 0xF);
+ byte leftPixel = (byte)((twoPixels >> 4) & 0xF);
+
+ for (int idx = 0; idx < max; idx++)
+ {
+ if (idx % 2 == 0)
+ {
+ buffer[count] = leftPixel;
+ }
+ else
+ {
+ buffer[count] = rightPixel;
+ }
+
+ count++;
+ }
+ }
+ }
+ }
+
+ ///
+ /// Produce uncompressed bitmap data from a RLE8 stream.
+ ///
+ ///
+ /// RLE8 is a 2-byte run-length encoding.
+ ///
If first byte is 0, the second byte may have special meaning.
+ ///
Otherwise, the first byte is the length of the run and second byte is the color for the run.
///
/// The width of the bitmap.
/// Buffer for uncompressed data.
@@ -261,7 +431,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
{
if (this.stream.Read(cmd, 0, cmd.Length) != 2)
{
- throw new Exception("Failed to read 2 bytes from stream");
+ BmpThrowHelper.ThrowImageFormatException("Failed to read 2 bytes from stream while uncompressing RLE8 bitmap.");
}
if (cmd[0] == RleCommand)
@@ -310,9 +480,12 @@ namespace SixLabors.ImageSharp.Formats.Bmp
}
else
{
- for (int i = 0; i < cmd[0]; i++)
+ int max = count + cmd[0]; // as we start at the current count in the following loop, max is count + cmd[0]
+ byte cmd1 = cmd[1]; // store the value to avoid the repeated indexer access inside the loop
+
+ for (; count < max; count++)
{
- buffer[count++] = cmd[1];
+ buffer[count] = cmd1;
}
}
}
@@ -326,18 +499,20 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// The containing the colors.
/// The width of the bitmap.
/// The height of the bitmap.
- /// The number of bits per pixel.
+ /// The number of bits per pixel.
+ /// Usually 4 bytes, but in case of Windows 2.x bitmaps or OS/2 1.x bitmaps
+ /// the bytes per color palette entry's can be 3 bytes instead of 4.
/// Whether the bitmap is inverted.
- private void ReadRgbPalette(Buffer2D pixels, byte[] colors, int width, int height, int bits, bool inverted)
+ private void ReadRgbPalette(Buffer2D pixels, byte[] colors, int width, int height, int bitsPerPixel, int bytesPerColorMapEntry, bool inverted)
where TPixel : struct, IPixel
{
// Pixels per byte (bits per pixel)
- int ppb = 8 / bits;
+ int ppb = 8 / bitsPerPixel;
int arrayWidth = (width + ppb - 1) / ppb;
// Bit mask
- int mask = 0xFF >> (8 - bits);
+ int mask = 0xFF >> (8 - bitsPerPixel);
// Rows are aligned on 4 byte boundaries
int padding = arrayWidth % 4;
@@ -363,7 +538,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
int colOffset = x * ppb;
for (int shift = 0, newX = colOffset; shift < ppb && newX < width; shift++, newX++)
{
- int colorIndex = ((rowSpan[offset] >> (8 - bits - (shift * bits))) & mask) * 4;
+ int colorIndex = ((rowSpan[offset] >> (8 - bitsPerPixel - (shift * bitsPerPixel))) & mask) * bytesPerColorMapEntry;
color.FromBgr24(Unsafe.As(ref colors[colorIndex]));
pixelRow[newX] = color;
@@ -376,20 +551,32 @@ namespace SixLabors.ImageSharp.Formats.Bmp
}
///
- /// Reads the 16 bit color palette from the stream
+ /// Reads the 16 bit color palette from the stream.
///
/// The pixel format.
/// The to assign the palette to.
/// The width of the bitmap.
/// The height of the bitmap.
/// Whether the bitmap is inverted.
- private void ReadRgb16(Buffer2D pixels, int width, int height, bool inverted)
+ /// The bitmask for the red channel.
+ /// The bitmask for the green channel.
+ /// The bitmask for the blue channel.
+ private void ReadRgb16(Buffer2D pixels, int width, int height, bool inverted, int redMask = DefaultRgb16RMask, int greenMask = DefaultRgb16GMask, int blueMask = DefaultRgb16BMask)
where TPixel : struct, IPixel
{
int padding = CalculatePadding(width, 2);
int stride = (width * 2) + padding;
TPixel color = default;
+ int rightShiftRedMask = CalculateRightShift((uint)redMask);
+ int rightShiftGreenMask = CalculateRightShift((uint)greenMask);
+ int rightShiftBlueMask = CalculateRightShift((uint)blueMask);
+
+ // Each color channel contains either 5 or 6 Bits values.
+ int redMaskBits = CountBits((uint)redMask);
+ int greenMaskBits = CountBits((uint)greenMask);
+ int blueMaskBits = CountBits((uint)blueMask);
+
using (IManagedByteBuffer buffer = this.memoryAllocator.AllocateManagedByteBuffer(stride))
{
for (int y = 0; y < height; y++)
@@ -403,10 +590,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp
{
short temp = BitConverter.ToInt16(buffer.Array, offset);
- var rgb = new Rgb24(
- GetBytesFrom5BitValue((temp & Rgb16RMask) >> 10),
- GetBytesFrom5BitValue((temp & Rgb16GMask) >> 5),
- GetBytesFrom5BitValue(temp & Rgb16BMask));
+ // Rescale values, so the values range from 0 to 255.
+ int r = (redMaskBits == 5) ? GetBytesFrom5BitValue((temp & redMask) >> rightShiftRedMask) : GetBytesFrom6BitValue((temp & redMask) >> rightShiftRedMask);
+ int g = (greenMaskBits == 5) ? GetBytesFrom5BitValue((temp & greenMask) >> rightShiftGreenMask) : GetBytesFrom6BitValue((temp & greenMask) >> rightShiftGreenMask);
+ int b = (blueMaskBits == 5) ? GetBytesFrom5BitValue((temp & blueMask) >> rightShiftBlueMask) : GetBytesFrom6BitValue((temp & blueMask) >> rightShiftBlueMask);
+ var rgb = new Rgb24((byte)r, (byte)g, (byte)b);
color.FromRgb24(rgb);
pixelRow[x] = color;
@@ -417,7 +605,23 @@ namespace SixLabors.ImageSharp.Formats.Bmp
}
///
- /// Reads the 24 bit color palette from the stream
+ /// Performs final shifting from a 5bit value to an 8bit one.
+ ///
+ /// The masked and shifted value.
+ /// The
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static byte GetBytesFrom5BitValue(int value) => (byte)((value << 3) | (value >> 2));
+
+ ///
+ /// Performs final shifting from a 6bit value to an 8bit one.
+ ///
+ /// The masked and shifted value.
+ /// The
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static byte GetBytesFrom6BitValue(int value) => (byte)((value << 2) | (value >> 4));
+
+ ///
+ /// Reads the 24 bit color palette from the stream.
///
/// The pixel format.
/// The to assign the palette to.
@@ -436,36 +640,266 @@ namespace SixLabors.ImageSharp.Formats.Bmp
this.stream.Read(row);
int newY = Invert(y, height, inverted);
Span pixelSpan = pixels.GetRowSpan(newY);
- PixelOperations.Instance.FromBgr24Bytes(row.GetSpan(), pixelSpan, width);
+ PixelOperations.Instance.FromBgr24Bytes(
+ this.configuration,
+ row.GetSpan(),
+ pixelSpan,
+ width);
+ }
+ }
+ }
+
+ ///
+ /// Reads the 32 bit color palette from the stream.
+ ///
+ /// The pixel format.
+ /// The to assign the palette to.
+ /// The width of the bitmap.
+ /// The height of the bitmap.
+ /// Whether the bitmap is inverted.
+ private void ReadRgb32Fast(Buffer2D pixels, int width, int height, bool inverted)
+ where TPixel : struct, IPixel
+ {
+ int padding = CalculatePadding(width, 4);
+
+ using (IManagedByteBuffer row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 4, padding))
+ {
+ for (int y = 0; y < height; y++)
+ {
+ this.stream.Read(row);
+ int newY = Invert(y, height, inverted);
+ Span pixelSpan = pixels.GetRowSpan(newY);
+ PixelOperations.Instance.FromBgra32Bytes(
+ this.configuration,
+ row.GetSpan(),
+ pixelSpan,
+ width);
}
}
}
///
- /// Reads the 32 bit color palette from the stream
+ /// Reads the 32 bit color palette from the stream, checking the alpha component of each pixel.
+ /// This is a special case only used for 32bpp WinBMPv3 files, which could be in either BGR0 or BGRA format.
///
/// The pixel format.
/// The to assign the palette to.
/// The width of the bitmap.
/// The height of the bitmap.
/// Whether the bitmap is inverted.
- private void ReadRgb32(Buffer2D pixels, int width, int height, bool inverted)
+ private void ReadRgb32Slow(Buffer2D pixels, int width, int height, bool inverted)
where TPixel : struct, IPixel
{
int padding = CalculatePadding(width, 4);
using (IManagedByteBuffer row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 4, padding))
+ using (IMemoryOwner bgraRow = this.memoryAllocator.Allocate(width))
{
+ Span bgraRowSpan = bgraRow.GetSpan();
+ long currentPosition = this.stream.Position;
+ bool hasAlpha = false;
+
+ // Loop though the rows checking each pixel. We start by assuming it's
+ // an BGR0 image. If we hit a non-zero alpha value, then we know it's
+ // actually a BGRA image, and change tactics accordingly.
for (int y = 0; y < height; y++)
{
this.stream.Read(row);
+
+ PixelOperations.Instance.FromBgra32Bytes(
+ this.configuration,
+ row.GetSpan(),
+ bgraRowSpan,
+ width);
+
+ // Check each pixel in the row to see if it has an alpha value.
+ for (int x = 0; x < width; x++)
+ {
+ Bgra32 bgra = bgraRowSpan[x];
+ if (bgra.A > 0)
+ {
+ hasAlpha = true;
+ break;
+ }
+ }
+
+ if (hasAlpha)
+ {
+ break;
+ }
+ }
+
+ // Reset our stream for a second pass.
+ this.stream.Position = currentPosition;
+
+ // Process the pixels in bulk taking the raw alpha component value.
+ if (hasAlpha)
+ {
+ for (int y = 0; y < height; y++)
+ {
+ this.stream.Read(row);
+
+ int newY = Invert(y, height, inverted);
+ Span pixelSpan = pixels.GetRowSpan(newY);
+
+ PixelOperations.Instance.FromBgra32Bytes(
+ this.configuration,
+ row.GetSpan(),
+ pixelSpan,
+ width);
+ }
+
+ return;
+ }
+
+ // Slow path. We need to set each alpha component value to fully opaque.
+ for (int y = 0; y < height; y++)
+ {
+ this.stream.Read(row);
+ PixelOperations.Instance.FromBgra32Bytes(
+ this.configuration,
+ row.GetSpan(),
+ bgraRowSpan,
+ width);
+
int newY = Invert(y, height, inverted);
Span pixelSpan = pixels.GetRowSpan(newY);
- PixelOperations.Instance.FromBgra32Bytes(row.GetSpan(), pixelSpan, width);
+
+ for (int x = 0; x < width; x++)
+ {
+ Bgra32 bgra = bgraRowSpan[x];
+ bgra.A = byte.MaxValue;
+ ref TPixel pixel = ref pixelSpan[x];
+ pixel.FromBgra32(bgra);
+ }
}
}
}
+ ///
+ /// Decode an 32 Bit Bitmap containing a bitmask for each color channel.
+ ///
+ /// The pixel format.
+ /// The output pixel buffer containing the decoded image.
+ /// The width of the image.
+ /// The height of the image.
+ /// Whether the bitmap is inverted.
+ /// The bitmask for the red channel.
+ /// The bitmask for the green channel.
+ /// The bitmask for the blue channel.
+ /// The bitmask for the alpha channel.
+ private void ReadRgb32BitFields(Buffer2D pixels, int width, int height, bool inverted, int redMask, int greenMask, int blueMask, int alphaMask)
+ where TPixel : struct, IPixel
+ {
+ TPixel color = default;
+ int padding = CalculatePadding(width, 4);
+ int stride = (width * 4) + padding;
+
+ int rightShiftRedMask = CalculateRightShift((uint)redMask);
+ int rightShiftGreenMask = CalculateRightShift((uint)greenMask);
+ int rightShiftBlueMask = CalculateRightShift((uint)blueMask);
+ int rightShiftAlphaMask = CalculateRightShift((uint)alphaMask);
+
+ int bitsRedMask = CountBits((uint)redMask);
+ int bitsGreenMask = CountBits((uint)greenMask);
+ int bitsBlueMask = CountBits((uint)blueMask);
+ int bitsAlphaMask = CountBits((uint)alphaMask);
+ float invMaxValueRed = 1.0f / (0xFFFFFFFF >> (32 - bitsRedMask));
+ float invMaxValueGreen = 1.0f / (0xFFFFFFFF >> (32 - bitsGreenMask));
+ float invMaxValueBlue = 1.0f / (0xFFFFFFFF >> (32 - bitsBlueMask));
+ uint maxValueAlpha = 0xFFFFFFFF >> (32 - bitsAlphaMask);
+ float invMaxValueAlpha = 1.0f / maxValueAlpha;
+
+ bool unusualBitMask = false;
+ if (bitsRedMask > 8 || bitsGreenMask > 8 || bitsBlueMask > 8 || invMaxValueAlpha > 8)
+ {
+ unusualBitMask = true;
+ }
+
+ using (IManagedByteBuffer buffer = this.memoryAllocator.AllocateManagedByteBuffer(stride))
+ {
+ for (int y = 0; y < height; y++)
+ {
+ this.stream.Read(buffer.Array, 0, stride);
+ int newY = Invert(y, height, inverted);
+ Span pixelRow = pixels.GetRowSpan(newY);
+
+ int offset = 0;
+ for (int x = 0; x < width; x++)
+ {
+ uint temp = BitConverter.ToUInt32(buffer.Array, offset);
+
+ if (unusualBitMask)
+ {
+ uint r = (uint)(temp & redMask) >> rightShiftRedMask;
+ uint g = (uint)(temp & greenMask) >> rightShiftGreenMask;
+ uint b = (uint)(temp & blueMask) >> rightShiftBlueMask;
+ float alpha = alphaMask != 0 ? invMaxValueAlpha * ((uint)(temp & alphaMask) >> rightShiftAlphaMask) : 1.0f;
+ var vector4 = new Vector4(
+ r * invMaxValueRed,
+ g * invMaxValueGreen,
+ b * invMaxValueBlue,
+ alpha);
+ color.FromVector4(vector4);
+ }
+ else
+ {
+ byte r = (byte)((temp & redMask) >> rightShiftRedMask);
+ byte g = (byte)((temp & greenMask) >> rightShiftGreenMask);
+ byte b = (byte)((temp & blueMask) >> rightShiftBlueMask);
+ byte a = alphaMask != 0 ? (byte)((temp & alphaMask) >> rightShiftAlphaMask) : (byte)255;
+ color.FromRgba32(new Rgba32(r, g, b, a));
+ }
+
+ pixelRow[x] = color;
+ offset += 4;
+ }
+ }
+ }
+ }
+
+ ///
+ /// Calculates the necessary right shifts for a given color bitmask (the 0 bits to the right).
+ ///
+ /// The color bit mask.
+ /// Number of bits to shift right.
+ private static int CalculateRightShift(uint n)
+ {
+ int count = 0;
+ while (n > 0)
+ {
+ if ((1 & n) == 0)
+ {
+ count++;
+ }
+ else
+ {
+ break;
+ }
+
+ n >>= 1;
+ }
+
+ return count;
+ }
+
+ ///
+ /// Counts none zero bits.
+ ///
+ /// A color mask.
+ /// The none zero bits.
+ private static int CountBits(uint n)
+ {
+ int count = 0;
+ while (n != 0)
+ {
+ count++;
+ n &= n - 1;
+ }
+
+ return count;
+ }
+
///
/// Reads the from the stream.
///
@@ -481,7 +915,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
int headerSize = BinaryPrimitives.ReadInt32LittleEndian(buffer);
if (headerSize < BmpInfoHeader.CoreSize)
{
- throw new NotSupportedException($"ImageSharp does not support this BMP file. HeaderSize: {headerSize}.");
+ BmpThrowHelper.ThrowNotSupportedException($"ImageSharp does not support this BMP file. HeaderSize is '{headerSize}'.");
}
int skipAmount = 0;
@@ -494,23 +928,78 @@ namespace SixLabors.ImageSharp.Formats.Bmp
// read the rest of the header
this.stream.Read(buffer, BmpInfoHeader.HeaderSizeSize, headerSize - BmpInfoHeader.HeaderSizeSize);
+ BmpInfoHeaderType infoHeaderType = BmpInfoHeaderType.WinVersion2;
if (headerSize == BmpInfoHeader.CoreSize)
{
// 12 bytes
+ infoHeaderType = BmpInfoHeaderType.WinVersion2;
this.infoHeader = BmpInfoHeader.ParseCore(buffer);
}
- else if (headerSize >= BmpInfoHeader.Size)
+ else if (headerSize == BmpInfoHeader.Os22ShortSize)
+ {
+ // 16 bytes
+ infoHeaderType = BmpInfoHeaderType.Os2Version2Short;
+ this.infoHeader = BmpInfoHeader.ParseOs22Short(buffer);
+ }
+ else if (headerSize == BmpInfoHeader.SizeV3)
+ {
+ // == 40 bytes
+ infoHeaderType = BmpInfoHeaderType.WinVersion3;
+ this.infoHeader = BmpInfoHeader.ParseV3(buffer);
+
+ // If the info header is BMP version 3 and the compression type is BITFIELDS,
+ // color masks for each color channel follow the info header.
+ if (this.infoHeader.Compression == BmpCompression.BitFields)
+ {
+ byte[] bitfieldsBuffer = new byte[12];
+ this.stream.Read(bitfieldsBuffer, 0, 12);
+ Span data = bitfieldsBuffer.AsSpan();
+ this.infoHeader.RedMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(0, 4));
+ this.infoHeader.GreenMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4));
+ this.infoHeader.BlueMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4));
+ }
+ else if (this.infoHeader.Compression == BmpCompression.BI_ALPHABITFIELDS)
+ {
+ byte[] bitfieldsBuffer = new byte[16];
+ this.stream.Read(bitfieldsBuffer, 0, 16);
+ Span data = bitfieldsBuffer.AsSpan();
+ this.infoHeader.RedMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(0, 4));
+ this.infoHeader.GreenMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4));
+ this.infoHeader.BlueMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4));
+ this.infoHeader.AlphaMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(12, 4));
+ }
+ }
+ else if (headerSize == BmpInfoHeader.AdobeV3Size)
+ {
+ // == 52 bytes
+ infoHeaderType = BmpInfoHeaderType.AdobeVersion3;
+ this.infoHeader = BmpInfoHeader.ParseAdobeV3(buffer, withAlpha: false);
+ }
+ else if (headerSize == BmpInfoHeader.AdobeV3WithAlphaSize)
+ {
+ // == 56 bytes
+ infoHeaderType = BmpInfoHeaderType.AdobeVersion3WithAlpha;
+ this.infoHeader = BmpInfoHeader.ParseAdobeV3(buffer, withAlpha: true);
+ }
+ else if (headerSize == BmpInfoHeader.Os2v2Size)
+ {
+ // == 64 bytes
+ infoHeaderType = BmpInfoHeaderType.Os2Version2;
+ this.infoHeader = BmpInfoHeader.ParseOs2Version2(buffer);
+ }
+ else if (headerSize >= BmpInfoHeader.SizeV4)
{
- // >= 40 bytes
- this.infoHeader = BmpInfoHeader.Parse(buffer);
+ // >= 108 bytes
+ infoHeaderType = headerSize == BmpInfoHeader.SizeV4 ? BmpInfoHeaderType.WinVersion4 : BmpInfoHeaderType.WinVersion5;
+ this.infoHeader = BmpInfoHeader.ParseV4(buffer);
}
else
{
- throw new NotSupportedException($"ImageSharp does not support this BMP file. HeaderSize: {headerSize}.");
+ BmpThrowHelper.ThrowNotSupportedException($"ImageSharp does not support this BMP file. HeaderSize '{headerSize}'.");
}
// Resolution is stored in PPM.
- var meta = new ImageMetaData
+ var meta = new ImageMetadata
{
ResolutionUnits = PixelResolutionUnit.PixelsPerMeter
};
@@ -522,20 +1011,21 @@ namespace SixLabors.ImageSharp.Formats.Bmp
else
{
// Convert default metadata values to PPM.
- meta.HorizontalResolution = Math.Round(UnitConverter.InchToMeter(ImageMetaData.DefaultHorizontalResolution));
- meta.VerticalResolution = Math.Round(UnitConverter.InchToMeter(ImageMetaData.DefaultVerticalResolution));
+ meta.HorizontalResolution = Math.Round(UnitConverter.InchToMeter(ImageMetadata.DefaultHorizontalResolution));
+ meta.VerticalResolution = Math.Round(UnitConverter.InchToMeter(ImageMetadata.DefaultVerticalResolution));
}
- this.metaData = meta;
+ this.metadata = meta;
short bitsPerPixel = this.infoHeader.BitsPerPixel;
- BmpMetaData bmpMetaData = this.metaData.GetFormatMetaData(BmpFormat.Instance);
+ this.bmpMetadata = this.metadata.GetFormatMetadata(BmpFormat.Instance);
+ this.bmpMetadata.InfoHeaderType = infoHeaderType;
// We can only encode at these bit rates so far.
if (bitsPerPixel.Equals((short)BmpBitsPerPixel.Pixel24)
|| bitsPerPixel.Equals((short)BmpBitsPerPixel.Pixel32))
{
- bmpMetaData.BitsPerPixel = (BmpBitsPerPixel)bitsPerPixel;
+ this.bmpMetadata.BitsPerPixel = (BmpBitsPerPixel)bitsPerPixel;
}
// skip the remaining header because we can't read those parts
@@ -555,12 +1045,19 @@ namespace SixLabors.ImageSharp.Formats.Bmp
this.stream.Read(buffer, 0, BmpFileHeader.Size);
this.fileHeader = BmpFileHeader.Parse(buffer);
+
+ if (this.fileHeader.Type != BmpConstants.TypeMarkers.Bitmap)
+ {
+ BmpThrowHelper.ThrowNotSupportedException($"ImageSharp does not support this BMP file. File header bitmap type marker '{this.fileHeader.Type}'.");
+ }
}
///
/// Reads the and from the stream and sets the corresponding fields.
///
- private void ReadImageHeaders(Stream stream, out bool inverted, out byte[] palette)
+ /// Bytes per color palette entry. Usually 4 bytes, but in case of Windows 2.x bitmaps or OS/2 1.x bitmaps
+ /// the bytes per color palette entry's can be 3 bytes instead of 4.
+ private int ReadImageHeaders(Stream stream, out bool inverted, out byte[] palette)
{
this.stream = stream;
@@ -580,6 +1077,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
}
int colorMapSize = -1;
+ int bytesPerColorMapEntry = 4;
if (this.infoHeader.ClrUsed == 0)
{
@@ -587,12 +1085,18 @@ namespace SixLabors.ImageSharp.Formats.Bmp
|| this.infoHeader.BitsPerPixel == 4
|| this.infoHeader.BitsPerPixel == 8)
{
- colorMapSize = ImageMaths.GetColorCountForBitDepth(this.infoHeader.BitsPerPixel) * 4;
+ int colorMapSizeBytes = this.fileHeader.Offset - BmpFileHeader.Size - this.infoHeader.HeaderSize;
+ int colorCountForBitDepth = ImageMaths.GetColorCountForBitDepth(this.infoHeader.BitsPerPixel);
+ bytesPerColorMapEntry = colorMapSizeBytes / colorCountForBitDepth;
+
+ // Edge case for less-than-full-sized palette: bytesPerColorMapEntry should be at least 3.
+ bytesPerColorMapEntry = Math.Max(bytesPerColorMapEntry, 3);
+ colorMapSize = colorMapSizeBytes;
}
}
else
{
- colorMapSize = this.infoHeader.ClrUsed * 4;
+ colorMapSize = this.infoHeader.ClrUsed * bytesPerColorMapEntry;
}
palette = null;
@@ -602,7 +1106,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
// 256 * 4
if (colorMapSize > 1024)
{
- throw new ImageFormatException($"Invalid bmp colormap size '{colorMapSize}'");
+ BmpThrowHelper.ThrowImageFormatException($"Invalid bmp colormap size '{colorMapSize}'");
}
palette = new byte[colorMapSize];
@@ -611,6 +1115,19 @@ namespace SixLabors.ImageSharp.Formats.Bmp
}
this.infoHeader.VerifyDimensions();
+
+ int skipAmount = this.fileHeader.Offset - (int)this.stream.Position;
+ if ((skipAmount + (int)this.stream.Position) > this.stream.Length)
+ {
+ BmpThrowHelper.ThrowImageFormatException($"Invalid fileheader offset found. Offset is greater than the stream length.");
+ }
+
+ if (skipAmount > 0)
+ {
+ this.stream.Skip(skipAmount);
+ }
+
+ return bytesPerColorMapEntry;
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
index 186ff812f7..ad526f6689 100644
--- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
@@ -3,9 +3,11 @@
using System;
using System.IO;
+
+using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Common.Helpers;
using SixLabors.ImageSharp.Memory;
-using SixLabors.ImageSharp.MetaData;
+using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
@@ -21,8 +23,30 @@ namespace SixLabors.ImageSharp.Formats.Bmp
///
private int padding;
+ ///
+ /// The mask for the alpha channel of the color for a 32 bit rgba bitmaps.
+ ///
+ private const int Rgba32AlphaMask = 0xFF << 24;
+
+ ///
+ /// The mask for the red part of the color for a 32 bit rgba bitmaps.
+ ///
+ private const int Rgba32RedMask = 0xFF << 16;
+
+ ///
+ /// The mask for the green part of the color for a 32 bit rgba bitmaps.
+ ///
+ private const int Rgba32GreenMask = 0xFF << 8;
+
+ ///
+ /// The mask for the blue part of the color for a 32 bit rgba bitmaps.
+ ///
+ private const int Rgba32BlueMask = 0xFF;
+
private readonly MemoryAllocator memoryAllocator;
+ private Configuration configuration;
+
private BmpBitsPerPixel? bitsPerPixel;
///
@@ -48,9 +72,10 @@ namespace SixLabors.ImageSharp.Formats.Bmp
Guard.NotNull(image, nameof(image));
Guard.NotNull(stream, nameof(stream));
- ImageMetaData metaData = image.MetaData;
- BmpMetaData bmpMetaData = metaData.GetFormatMetaData(BmpFormat.Instance);
- this.bitsPerPixel = this.bitsPerPixel ?? bmpMetaData.BitsPerPixel;
+ this.configuration = image.GetConfiguration();
+ ImageMetadata metadata = image.Metadata;
+ BmpMetadata bmpMetadata = metadata.GetFormatMetadata(BmpFormat.Instance);
+ this.bitsPerPixel = this.bitsPerPixel ?? bmpMetadata.BitsPerPixel;
short bpp = (short)this.bitsPerPixel;
int bytesPerLine = 4 * (((image.Width * bpp) + 31) / 32);
@@ -60,35 +85,36 @@ namespace SixLabors.ImageSharp.Formats.Bmp
int hResolution = 0;
int vResolution = 0;
- if (metaData.ResolutionUnits != PixelResolutionUnit.AspectRatio)
+ if (metadata.ResolutionUnits != PixelResolutionUnit.AspectRatio)
{
- if (metaData.HorizontalResolution > 0 && metaData.VerticalResolution > 0)
+ if (metadata.HorizontalResolution > 0 && metadata.VerticalResolution > 0)
{
- switch (metaData.ResolutionUnits)
+ switch (metadata.ResolutionUnits)
{
case PixelResolutionUnit.PixelsPerInch:
- hResolution = (int)Math.Round(UnitConverter.InchToMeter(metaData.HorizontalResolution));
- vResolution = (int)Math.Round(UnitConverter.InchToMeter(metaData.VerticalResolution));
+ hResolution = (int)Math.Round(UnitConverter.InchToMeter(metadata.HorizontalResolution));
+ vResolution = (int)Math.Round(UnitConverter.InchToMeter(metadata.VerticalResolution));
break;
case PixelResolutionUnit.PixelsPerCentimeter:
- hResolution = (int)Math.Round(UnitConverter.CmToMeter(metaData.HorizontalResolution));
- vResolution = (int)Math.Round(UnitConverter.CmToMeter(metaData.VerticalResolution));
+ hResolution = (int)Math.Round(UnitConverter.CmToMeter(metadata.HorizontalResolution));
+ vResolution = (int)Math.Round(UnitConverter.CmToMeter(metadata.VerticalResolution));
break;
case PixelResolutionUnit.PixelsPerMeter:
- hResolution = (int)Math.Round(metaData.HorizontalResolution);
- vResolution = (int)Math.Round(metaData.VerticalResolution);
+ hResolution = (int)Math.Round(metadata.HorizontalResolution);
+ vResolution = (int)Math.Round(metadata.VerticalResolution);
break;
}
}
}
+ int infoHeaderSize = BmpInfoHeader.SizeV4;
var infoHeader = new BmpInfoHeader(
- headerSize: BmpInfoHeader.Size,
+ headerSize: infoHeaderSize,
height: image.Height,
width: image.Width,
bitsPerPixel: bpp,
@@ -97,26 +123,37 @@ namespace SixLabors.ImageSharp.Formats.Bmp
clrUsed: 0,
clrImportant: 0,
xPelsPerMeter: hResolution,
- yPelsPerMeter: vResolution);
+ yPelsPerMeter: vResolution)
+ {
+ RedMask = Rgba32RedMask,
+ GreenMask = Rgba32GreenMask,
+ BlueMask = Rgba32BlueMask,
+ Compression = BmpCompression.BitFields
+ };
+
+ if (this.bitsPerPixel == BmpBitsPerPixel.Pixel32)
+ {
+ infoHeader.AlphaMask = Rgba32AlphaMask;
+ }
var fileHeader = new BmpFileHeader(
- type: 19778, // BM
- fileSize: 54 + infoHeader.ImageSize,
+ type: BmpConstants.TypeMarkers.Bitmap,
+ fileSize: BmpFileHeader.Size + infoHeaderSize + infoHeader.ImageSize,
reserved: 0,
- offset: 54);
+ offset: BmpFileHeader.Size + infoHeaderSize);
#if NETCOREAPP2_1
- Span buffer = stackalloc byte[40];
+ Span buffer = stackalloc byte[infoHeaderSize];
#else
- byte[] buffer = new byte[40];
+ byte[] buffer = new byte[infoHeaderSize];
#endif
fileHeader.WriteTo(buffer);
stream.Write(buffer, 0, BmpFileHeader.Size);
- infoHeader.WriteTo(buffer);
+ infoHeader.WriteV4Header(buffer);
- stream.Write(buffer, 0, 40);
+ stream.Write(buffer, 0, infoHeaderSize);
this.WriteImage(stream, image.Frames.RootFrame);
@@ -163,7 +200,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp
for (int y = pixels.Height - 1; y >= 0; y--)
{
Span pixelSpan = pixels.GetRowSpan(y);
- PixelOperations.Instance.ToBgra32Bytes(pixelSpan, row.GetSpan(), pixelSpan.Length);
+ PixelOperations.Instance.ToBgra32Bytes(
+ this.configuration,
+ pixelSpan,
+ row.GetSpan(),
+ pixelSpan.Length);
stream.Write(row.Array, 0, row.Length());
}
}
@@ -183,7 +224,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp
for (int y = pixels.Height - 1; y >= 0; y--)
{
Span pixelSpan = pixels.GetRowSpan(y);
- PixelOperations.Instance.ToBgr24Bytes(pixelSpan, row.GetSpan(), pixelSpan.Length);
+ PixelOperations.Instance.ToBgr24Bytes(
+ this.configuration,
+ pixelSpan,
+ row.GetSpan(),
+ pixelSpan.Length);
stream.Write(row.Array, 0, row.Length());
}
}
diff --git a/src/ImageSharp/Formats/Bmp/BmpFormat.cs b/src/ImageSharp/Formats/Bmp/BmpFormat.cs
index a5eaab8ebf..056fbe8406 100644
--- a/src/ImageSharp/Formats/Bmp/BmpFormat.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpFormat.cs
@@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
///
/// Registers the image encoders, decoders and mime type detectors for the bmp format.
///
- public sealed class BmpFormat : IImageFormat
+ public sealed class BmpFormat : IImageFormat
{
private BmpFormat()
{
@@ -32,6 +32,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp
public IEnumerable FileExtensions => BmpConstants.FileExtensions;
///
- public BmpMetaData CreateDefaultFormatMetaData() => new BmpMetaData();
+ public BmpMetadata CreateDefaultFormatMetadata() => new BmpMetadata();
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Bmp/BmpImageFormatDetector.cs b/src/ImageSharp/Formats/Bmp/BmpImageFormatDetector.cs
index 6a740d47d1..c0814b1dfc 100644
--- a/src/ImageSharp/Formats/Bmp/BmpImageFormatDetector.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpImageFormatDetector.cs
@@ -6,7 +6,7 @@ using System;
namespace SixLabors.ImageSharp.Formats.Bmp
{
///
- /// Detects bmp file headers
+ /// Detects bmp file headers.
///
public sealed class BmpImageFormatDetector : IImageFormatDetector
{
@@ -22,9 +22,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp
private bool IsSupportedFileFormat(ReadOnlySpan header)
{
// TODO: This should be in constants
- return header.Length >= this.HeaderSize &&
- header[0] == 0x42 && // B
- header[1] == 0x4D; // M
+ return header.Length >= this.HeaderSize
+ && header[0] == 0x42 // B
+ && header[1] == 0x4D; // M
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs b/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs
index 4dd63a9626..6da5f73e3f 100644
--- a/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs
@@ -17,19 +17,44 @@ namespace SixLabors.ImageSharp.Formats.Bmp
internal struct BmpInfoHeader
{
///
- /// Defines the size of the BITMAPINFOHEADER data structure in the bitmap file.
+ /// Defines the size of the BITMAPCOREHEADER data structure in the bitmap file.
///
- public const int Size = 40;
+ public const int CoreSize = 12;
///
- /// Defines the size of the BITMAPCOREHEADER data structure in the bitmap file.
+ /// Defines the size of the short variant of the OS22XBITMAPHEADER data structure in the bitmap file.
///
- public const int CoreSize = 12;
+ public const int Os22ShortSize = 16;
+
+ ///
+ /// Defines the size of the BITMAPINFOHEADER (BMP Version 3) data structure in the bitmap file.
+ ///
+ public const int SizeV3 = 40;
+
+ ///
+ /// Special case of the BITMAPINFOHEADER V3 used by adobe where the color bitmasks are part of the info header instead of following it.
+ ///
+ public const int AdobeV3Size = 52;
+
+ ///
+ /// Special case of the BITMAPINFOHEADER V3 used by adobe where the color bitmasks (including the alpha channel) are part of the info header instead of following it.
+ ///
+ public const int AdobeV3WithAlphaSize = 56;
+
+ ///
+ /// Size of a IBM OS/2 2.x bitmap header.
+ ///
+ public const int Os2v2Size = 64;
+
+ ///
+ /// Defines the size of the BITMAPINFOHEADER (BMP Version 4) data structure in the bitmap file.
+ ///
+ public const int SizeV4 = 108;
///
/// Defines the size of the biggest supported header data structure in the bitmap file.
///
- public const int MaxHeaderSize = Size;
+ public const int MaxHeaderSize = SizeV4;
///
/// Defines the size of the field.
@@ -47,7 +72,24 @@ namespace SixLabors.ImageSharp.Formats.Bmp
int xPelsPerMeter = 0,
int yPelsPerMeter = 0,
int clrUsed = 0,
- int clrImportant = 0)
+ int clrImportant = 0,
+ int redMask = 0,
+ int greenMask = 0,
+ int blueMask = 0,
+ int alphaMask = 0,
+ int csType = 0,
+ int redX = 0,
+ int redY = 0,
+ int redZ = 0,
+ int greenX = 0,
+ int greenY = 0,
+ int greenZ = 0,
+ int blueX = 0,
+ int blueY = 0,
+ int blueZ = 0,
+ int gammeRed = 0,
+ int gammeGreen = 0,
+ int gammeBlue = 0)
{
this.HeaderSize = headerSize;
this.Width = width;
@@ -60,10 +102,27 @@ namespace SixLabors.ImageSharp.Formats.Bmp
this.YPelsPerMeter = yPelsPerMeter;
this.ClrUsed = clrUsed;
this.ClrImportant = clrImportant;
+ this.RedMask = redMask;
+ this.GreenMask = greenMask;
+ this.BlueMask = blueMask;
+ this.AlphaMask = alphaMask;
+ this.CsType = csType;
+ this.RedX = redX;
+ this.RedY = redY;
+ this.RedZ = redZ;
+ this.GreenX = greenX;
+ this.GreenY = greenY;
+ this.GreenZ = greenZ;
+ this.BlueX = blueX;
+ this.BlueY = blueY;
+ this.BlueZ = blueZ;
+ this.GammaRed = gammeRed;
+ this.GammaGreen = gammeGreen;
+ this.GammaBlue = gammeBlue;
}
///
- /// Gets or sets the size of this header
+ /// Gets or sets the size of this header.
///
public int HeaderSize { get; set; }
@@ -125,25 +184,94 @@ namespace SixLabors.ImageSharp.Formats.Bmp
public int ClrImportant { get; set; }
///
- /// Parses the full BITMAPINFOHEADER header (40 bytes).
+ /// Gets or sets red color mask. This is used with the BITFIELDS decoding.
///
- /// The data to parse.
- /// Parsed header
- ///
- public static BmpInfoHeader Parse(ReadOnlySpan data)
- {
- if (data.Length != Size)
- {
- throw new ArgumentException(nameof(data), $"Must be {Size} bytes. Was {data.Length} bytes.");
- }
+ public int RedMask { get; set; }
- return MemoryMarshal.Cast(data)[0];
- }
+ ///
+ /// Gets or sets green color mask. This is used with the BITFIELDS decoding.
+ ///
+ public int GreenMask { get; set; }
+
+ ///
+ /// Gets or sets blue color mask. This is used with the BITFIELDS decoding.
+ ///
+ public int BlueMask { get; set; }
///
- /// Parses the BITMAPCOREHEADER consisting of the headerSize, width, height, planes, and bitsPerPixel fields (12 bytes).
+ /// Gets or sets alpha color mask. This is not used yet.
///
- /// The data to parse,
+ public int AlphaMask { get; set; }
+
+ ///
+ /// Gets or sets the Color space type. Not used yet.
+ ///
+ public int CsType { get; set; }
+
+ ///
+ /// Gets or sets the X coordinate of red endpoint. Not used yet.
+ ///
+ public int RedX { get; set; }
+
+ ///
+ /// Gets or sets the Y coordinate of red endpoint. Not used yet.
+ ///
+ public int RedY { get; set; }
+
+ ///
+ /// Gets or sets the Z coordinate of red endpoint. Not used yet.
+ ///
+ public int RedZ { get; set; }
+
+ ///
+ /// Gets or sets the X coordinate of green endpoint. Not used yet.
+ ///
+ public int GreenX { get; set; }
+
+ ///
+ /// Gets or sets the Y coordinate of green endpoint. Not used yet.
+ ///
+ public int GreenY { get; set; }
+
+ ///
+ /// Gets or sets the Z coordinate of green endpoint. Not used yet.
+ ///
+ public int GreenZ { get; set; }
+
+ ///
+ /// Gets or sets the X coordinate of blue endpoint. Not used yet.
+ ///
+ public int BlueX { get; set; }
+
+ ///
+ /// Gets or sets the Y coordinate of blue endpoint. Not used yet.
+ ///
+ public int BlueY { get; set; }
+
+ ///
+ /// Gets or sets the Z coordinate of blue endpoint. Not used yet.
+ ///
+ public int BlueZ { get; set; }
+
+ ///
+ /// Gets or sets the Gamma red coordinate scale value. Not used yet.
+ ///
+ public int GammaRed { get; set; }
+
+ ///
+ /// Gets or sets the Gamma green coordinate scale value. Not used yet.
+ ///
+ public int GammaGreen { get; set; }
+
+ ///
+ /// Gets or sets the Gamma blue coordinate scale value. Not used yet.
+ ///
+ public int GammaBlue { get; set; }
+
+ ///
+ /// Parses the BITMAPCOREHEADER (BMP Version 2) consisting of the headerSize, width, height, planes, and bitsPerPixel fields (12 bytes).
+ ///
+ /// The data to parse.
/// Parsed header
///
public static BmpInfoHeader ParseCore(ReadOnlySpan data)
@@ -156,7 +284,161 @@ namespace SixLabors.ImageSharp.Formats.Bmp
bitsPerPixel: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(10, 2)));
}
- public unsafe void WriteTo(Span buffer)
+ ///
+ /// Parses a short variant of the OS22XBITMAPHEADER. It is identical to the BITMAPCOREHEADER, except that the width and height
+ /// are 4 bytes instead of 2, resulting in 16 bytes total.
+ ///
+ /// The data to parse.
+ /// Parsed header
+ ///
+ public static BmpInfoHeader ParseOs22Short(ReadOnlySpan data)
+ {
+ return new BmpInfoHeader(
+ headerSize: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(0, 4)),
+ width: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4)),
+ height: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4)),
+ planes: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(12, 2)),
+ bitsPerPixel: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(14, 2)));
+ }
+
+ ///
+ /// Parses the full BMP Version 3 BITMAPINFOHEADER header (40 bytes).
+ ///
+ /// The data to parse.
+ /// The parsed header.
+ ///
+ public static BmpInfoHeader ParseV3(ReadOnlySpan data)
+ {
+ return new BmpInfoHeader(
+ headerSize: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(0, 4)),
+ width: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4)),
+ height: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4)),
+ planes: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(12, 2)),
+ bitsPerPixel: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(14, 2)),
+ compression: (BmpCompression)BinaryPrimitives.ReadInt32LittleEndian(data.Slice(16, 4)),
+ imageSize: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(20, 4)),
+ xPelsPerMeter: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(24, 4)),
+ yPelsPerMeter: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(28, 4)),
+ clrUsed: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(32, 4)),
+ clrImportant: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(36, 4)));
+ }
+
+ ///
+ /// Special case of the BITMAPINFOHEADER V3 used by adobe where the color bitmasks are part of the info header instead of following it.
+ /// 52 bytes without the alpha mask, 56 bytes with the alpha mask.
+ ///
+ /// The data to parse.
+ /// Indicates, if the alpha bitmask is present.
+ /// The parsed header.
+ ///
+ public static BmpInfoHeader ParseAdobeV3(ReadOnlySpan data, bool withAlpha = true)
+ {
+ return new BmpInfoHeader(
+ headerSize: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(0, 4)),
+ width: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4)),
+ height: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4)),
+ planes: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(12, 2)),
+ bitsPerPixel: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(14, 2)),
+ compression: (BmpCompression)BinaryPrimitives.ReadInt32LittleEndian(data.Slice(16, 4)),
+ imageSize: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(20, 4)),
+ xPelsPerMeter: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(24, 4)),
+ yPelsPerMeter: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(28, 4)),
+ clrUsed: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(32, 4)),
+ clrImportant: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(36, 4)),
+ redMask: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(40, 4)),
+ greenMask: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(44, 4)),
+ blueMask: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(48, 4)),
+ alphaMask: withAlpha ? BinaryPrimitives.ReadInt32LittleEndian(data.Slice(52, 4)) : 0);
+ }
+
+ ///
+ /// Parses a OS/2 version 2 bitmap header (64 bytes). Only the first 40 bytes are parsed which are
+ /// very similar to the Bitmap v3 header. The other 24 bytes are ignored, but they do not hold any
+ /// useful information for decoding the image.
+ ///
+ /// The data to parse.
+ /// The parsed header.
+ ///
+ public static BmpInfoHeader ParseOs2Version2(ReadOnlySpan data)
+ {
+ var infoHeader = new BmpInfoHeader(
+ headerSize: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(0, 4)),
+ width: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4)),
+ height: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4)),
+ planes: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(12, 2)),
+ bitsPerPixel: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(14, 2)));
+
+ int compression = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(16, 4));
+
+ // The compression value in OS/2 bitmap has a different meaning than in windows bitmaps.
+ // Map the OS/2 value to the windows values.
+ switch (compression)
+ {
+ case 0:
+ infoHeader.Compression = BmpCompression.RGB;
+ break;
+ case 1:
+ infoHeader.Compression = BmpCompression.RLE8;
+ break;
+ case 2:
+ infoHeader.Compression = BmpCompression.RLE4;
+ break;
+ default:
+ BmpThrowHelper.ThrowImageFormatException($"Compression type is not supported. ImageSharp only supports uncompressed, RLE4 and RLE8.");
+ break;
+ }
+
+ infoHeader.ImageSize = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(20, 4));
+ infoHeader.XPelsPerMeter = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(24, 4));
+ infoHeader.YPelsPerMeter = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(28, 4));
+ infoHeader.ClrUsed = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(32, 4));
+ infoHeader.ClrImportant = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(36, 4));
+
+ // The following 24 bytes of the header are omitted.
+ return infoHeader;
+ }
+
+ ///
+ /// Parses the full BMP Version 4 BITMAPINFOHEADER header (108 bytes).
+ ///
+ /// The data to parse.
+ /// The parsed header.
+ ///
+ public static BmpInfoHeader ParseV4(ReadOnlySpan data)
+ {
+ if (data.Length != SizeV4)
+ {
+ throw new ArgumentException(nameof(data), $"Must be {SizeV4} bytes. Was {data.Length} bytes.");
+ }
+
+ return MemoryMarshal.Cast(data)[0];
+ }
+
+ ///
+ /// Writes a bitmap version 3 (Microsoft Windows NT) header to a buffer (40 bytes).
+ ///
+ /// The buffer to write to.
+ public void WriteV3Header(Span buffer)
+ {
+ buffer.Clear();
+ BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(0, 4), SizeV3);
+ BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(4, 4), this.Width);
+ BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(8, 4), this.Height);
+ BinaryPrimitives.WriteInt16LittleEndian(buffer.Slice(12, 2), this.Planes);
+ BinaryPrimitives.WriteInt16LittleEndian(buffer.Slice(14, 2), this.BitsPerPixel);
+ BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(16, 4), (int)this.Compression);
+ BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(20, 4), this.ImageSize);
+ BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(24, 4), this.XPelsPerMeter);
+ BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(28, 4), this.YPelsPerMeter);
+ BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(32, 4), this.ClrUsed);
+ BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(36, 4), this.ClrImportant);
+ }
+
+ ///
+ /// Writes a complete Bitmap V4 header to a buffer.
+ ///
+ /// The buffer to write to.
+ public unsafe void WriteV4Header(Span buffer)
{
ref BmpInfoHeader dest = ref Unsafe.As(ref MemoryMarshal.GetReference(buffer));
diff --git a/src/ImageSharp/Formats/Bmp/BmpInfoHeaderType.cs b/src/ImageSharp/Formats/Bmp/BmpInfoHeaderType.cs
new file mode 100644
index 0000000000..a92a19d9ba
--- /dev/null
+++ b/src/ImageSharp/Formats/Bmp/BmpInfoHeaderType.cs
@@ -0,0 +1,51 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+namespace SixLabors.ImageSharp.Formats.Bmp
+{
+ ///
+ /// Enum value for the different bitmap info header types. The enum value is the number of bytes for the specific bitmap header.
+ ///
+ public enum BmpInfoHeaderType
+ {
+ ///
+ /// Bitmap Core or BMP Version 2 header (Microsoft Windows 2.x).
+ ///
+ WinVersion2 = 12,
+
+ ///
+ /// Short variant of the OS/2 Version 2 bitmap header.
+ ///
+ Os2Version2Short = 16,
+
+ ///
+ /// BMP Version 3 header (Microsoft Windows 3.x or Microsoft Windows NT).
+ ///
+ WinVersion3 = 40,
+
+ ///
+ /// Adobe variant of the BMP Version 3 header.
+ ///
+ AdobeVersion3 = 52,
+
+ ///
+ /// Adobe variant of the BMP Version 3 header with an alpha mask.
+ ///
+ AdobeVersion3WithAlpha = 56,
+
+ ///
+ /// BMP Version 2.x header (IBM OS/2 2.x).
+ ///
+ Os2Version2 = 64,
+
+ ///
+ /// BMP Version 4 header (Microsoft Windows 95).
+ ///
+ WinVersion4 = 108,
+
+ ///
+ /// BMP Version 5 header (Windows NT 5.0, 98 or later).
+ ///
+ WinVersion5 = 124,
+ }
+}
diff --git a/src/ImageSharp/Formats/Bmp/BmpMetaData.cs b/src/ImageSharp/Formats/Bmp/BmpMetaData.cs
index 8b33e30fa6..83d4eefe40 100644
--- a/src/ImageSharp/Formats/Bmp/BmpMetaData.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpMetaData.cs
@@ -6,20 +6,29 @@ namespace SixLabors.ImageSharp.Formats.Bmp
///
/// Provides Bmp specific metadata information for the image.
///
- public class BmpMetaData : IDeepCloneable
+ public class BmpMetadata : IDeepCloneable
{
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
- public BmpMetaData()
+ public BmpMetadata()
{
}
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
/// The metadata to create an instance from.
- private BmpMetaData(BmpMetaData other) => this.BitsPerPixel = other.BitsPerPixel;
+ private BmpMetadata(BmpMetadata other)
+ {
+ this.BitsPerPixel = other.BitsPerPixel;
+ this.InfoHeaderType = other.InfoHeaderType;
+ }
+
+ ///
+ /// Gets or sets the bitmap info header type.
+ ///
+ public BmpInfoHeaderType InfoHeaderType { get; set; }
///
/// Gets or sets the number of bits per pixel.
@@ -27,7 +36,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
public BmpBitsPerPixel BitsPerPixel { get; set; } = BmpBitsPerPixel.Pixel24;
///
- public IDeepCloneable DeepClone() => new BmpMetaData(this);
+ public IDeepCloneable DeepClone() => new BmpMetadata(this);
// TODO: Colors used once we support encoding palette bmps.
}
diff --git a/src/ImageSharp/Formats/Bmp/BmpThrowHelper.cs b/src/ImageSharp/Formats/Bmp/BmpThrowHelper.cs
new file mode 100644
index 0000000000..443471404e
--- /dev/null
+++ b/src/ImageSharp/Formats/Bmp/BmpThrowHelper.cs
@@ -0,0 +1,31 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Runtime.CompilerServices;
+
+namespace SixLabors.ImageSharp.Formats.Bmp
+{
+ internal static class BmpThrowHelper
+ {
+ ///
+ /// Cold path optimization for throwing -s
+ ///
+ /// The error message for the exception.
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static void ThrowImageFormatException(string errorMessage)
+ {
+ throw new ImageFormatException(errorMessage);
+ }
+
+ ///
+ /// Cold path optimization for throwing -s
+ ///
+ /// The error message for the exception.
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static void ThrowNotSupportedException(string errorMessage)
+ {
+ throw new NotSupportedException(errorMessage);
+ }
+ }
+}
diff --git a/src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs b/src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs
index c44ca73f2e..219d37ca62 100644
--- a/src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs
+++ b/src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs
@@ -8,6 +8,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp
///
internal interface IBmpDecoderOptions
{
- // added this for consistancy so we can add stuff as required, no options currently availible
+ // added this for consistency so we can add stuff as required, no options currently available
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Gif/GifDecoder.cs b/src/ImageSharp/Formats/Gif/GifDecoder.cs
index 42c76d6400..6af75f2d0f 100644
--- a/src/ImageSharp/Formats/Gif/GifDecoder.cs
+++ b/src/ImageSharp/Formats/Gif/GifDecoder.cs
@@ -3,7 +3,7 @@
using System.IO;
using System.Text;
-using SixLabors.ImageSharp.MetaData;
+using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Gif
diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
index db512a0781..e16ecb42e3 100644
--- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
+++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
@@ -8,7 +8,7 @@ using System.Runtime.InteropServices;
using System.Text;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
-using SixLabors.ImageSharp.MetaData;
+using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
using SixLabors.Primitives;
@@ -56,19 +56,19 @@ namespace SixLabors.ImageSharp.Formats.Gif
private GifGraphicControlExtension graphicsControlExtension;
///
- /// The image desciptor.
+ /// The image descriptor.
///
private GifImageDescriptor imageDescriptor;
///
/// The abstract metadata.
///
- private ImageMetaData metaData;
+ private ImageMetadata metadata;
///
/// The gif specific metadata.
///
- private GifMetaData gifMetaData;
+ private GifMetadata gifMetadata;
///
/// Initializes a new instance of the class.
@@ -142,8 +142,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.ReadApplicationExtension();
break;
case GifConstants.PlainTextLabel:
- int plainLength = stream.ReadByte();
- this.Skip(plainLength); // Not supported by any known decoder.
+ this.SkipBlock(); // Not supported by any known decoder.
break;
}
}
@@ -190,9 +189,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
switch (stream.ReadByte())
{
case GifConstants.GraphicControlLabel:
-
- // Skip graphic control extension block
- this.Skip(0);
+ this.SkipBlock(); // Skip graphic control extension block
break;
case GifConstants.CommentLabel:
this.ReadComments();
@@ -201,8 +198,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.ReadApplicationExtension();
break;
case GifConstants.PlainTextLabel:
- int plainLength = stream.ReadByte();
- this.Skip(plainLength); // Not supported by any known decoder.
+ this.SkipBlock(); // Not supported by any known decoder.
break;
}
}
@@ -227,7 +223,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
new PixelTypeInfo(this.logicalScreenDescriptor.BitsPerPixel),
this.logicalScreenDescriptor.Width,
this.logicalScreenDescriptor.Height,
- this.metaData);
+ this.metadata);
}
///
@@ -280,7 +276,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
if (subBlockSize == GifConstants.NetscapeLoopingSubBlockSize)
{
this.stream.Read(this.buffer, 0, GifConstants.NetscapeLoopingSubBlockSize);
- this.gifMetaData.RepeatCount = GifNetscapeLoopingApplicationExtension.Parse(this.buffer.AsSpan(1)).RepeatCount;
+ this.gifMetadata.RepeatCount = GifNetscapeLoopingApplicationExtension.Parse(this.buffer.AsSpan(1)).RepeatCount;
this.stream.Skip(1); // Skip the terminator.
return;
}
@@ -288,24 +284,27 @@ namespace SixLabors.ImageSharp.Formats.Gif
// Could be XMP or something else not supported yet.
// Back up and skip.
this.stream.Position -= appLength + 1;
- this.Skip(appLength);
+ this.SkipBlock(appLength);
return;
}
- this.Skip(appLength); // Not supported by any known decoder.
+ this.SkipBlock(appLength); // Not supported by any known decoder.
}
///
- /// Skips the designated number of bytes in the stream.
+ /// Skips over a block or reads its terminator.
+ /// The length of the block to skip.
///
- /// The number of bytes to skip.
- private void Skip(int length)
+ private void SkipBlock(int blockSize = 0)
{
- this.stream.Skip(length);
+ if (blockSize > 0)
+ {
+ this.stream.Skip(blockSize);
+ }
int flag;
- while ((flag = this.stream.ReadByte()) != 0)
+ while ((flag = this.stream.ReadByte()) > 0)
{
this.stream.Skip(flag);
}
@@ -335,7 +334,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
{
this.stream.Read(commentsBuffer.Array, 0, length);
string comments = this.TextEncoding.GetString(commentsBuffer.Array, 0, length);
- this.metaData.Properties.Add(new ImageProperty(GifConstants.Comments, comments));
+ this.metadata.Properties.Add(new ImageProperty(GifConstants.Comments, comments));
}
}
}
@@ -370,7 +369,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.ReadFrameColors(ref image, ref previousFrame, indices.GetSpan(), colorTable, this.imageDescriptor);
// Skip any remaining blocks
- this.Skip(0);
+ this.SkipBlock();
}
finally
{
@@ -417,9 +416,9 @@ namespace SixLabors.ImageSharp.Formats.Gif
if (previousFrame is null)
{
// This initializes the image to become fully transparent because the alpha channel is zero.
- image = new Image(this.configuration, imageWidth, imageHeight, this.metaData);
+ image = new Image(this.configuration, imageWidth, imageHeight, this.metadata);
- this.SetFrameMetaData(image.Frames.RootFrame.MetaData);
+ this.SetFrameMetadata(image.Frames.RootFrame.Metadata);
imageFrame = image.Frames.RootFrame;
}
@@ -432,7 +431,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
currentFrame = image.Frames.AddFrame(previousFrame); // This clones the frame and adds it the collection
- this.SetFrameMetaData(currentFrame.MetaData);
+ this.SetFrameMetadata(currentFrame.Metadata);
imageFrame = currentFrame;
@@ -550,11 +549,11 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
/// Sets the frames metadata.
///
- /// The meta data.
+ /// The metadata.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void SetFrameMetaData(ImageFrameMetaData meta)
+ private void SetFrameMetadata(ImageFrameMetadata meta)
{
- GifFrameMetaData gifMeta = meta.GetFormatMetaData(GifFormat.Instance);
+ GifFrameMetadata gifMeta = meta.GetFormatMetadata(GifFormat.Instance);
if (this.graphicsControlExtension.DelayTime > 0)
{
gifMeta.FrameDelay = this.graphicsControlExtension.DelayTime;
@@ -587,7 +586,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.stream.Skip(6);
this.ReadLogicalScreenDescriptor();
- var meta = new ImageMetaData();
+ var meta = new ImageMetadata();
// The Pixel Aspect Ratio is defined to be the quotient of the pixel's
// width over its height. The value range in this field allows
@@ -615,16 +614,16 @@ namespace SixLabors.ImageSharp.Formats.Gif
}
}
- this.metaData = meta;
- this.gifMetaData = meta.GetFormatMetaData(GifFormat.Instance);
- this.gifMetaData.ColorTableMode = this.logicalScreenDescriptor.GlobalColorTableFlag
+ this.metadata = meta;
+ this.gifMetadata = meta.GetFormatMetadata(GifFormat.Instance);
+ this.gifMetadata.ColorTableMode = this.logicalScreenDescriptor.GlobalColorTableFlag
? GifColorTableMode.Global
: GifColorTableMode.Local;
if (this.logicalScreenDescriptor.GlobalColorTableFlag)
{
int globalColorTableLength = this.logicalScreenDescriptor.GlobalColorTableSize * 3;
- this.gifMetaData.GlobalColorTableLength = globalColorTableLength;
+ this.gifMetadata.GlobalColorTableLength = globalColorTableLength;
this.globalColorTable = this.MemoryAllocator.AllocateManagedByteBuffer(globalColorTableLength, AllocationOptions.Clean);
diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
index 7db347aa65..12a515cca7 100644
--- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
+++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
@@ -8,8 +8,9 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
+using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
-using SixLabors.ImageSharp.MetaData;
+using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
using SixLabors.Memory;
@@ -22,10 +23,15 @@ namespace SixLabors.ImageSharp.Formats.Gif
internal sealed class GifEncoderCore
{
///
- /// Used for allocating memory during procesing operations.
+ /// Used for allocating memory during processing operations.
///
private readonly MemoryAllocator memoryAllocator;
+ ///
+ /// Configuration bound to the encoding operation.
+ ///
+ private Configuration configuration;
+
///
/// A reusable buffer used to reduce allocations.
///
@@ -52,9 +58,9 @@ namespace SixLabors.ImageSharp.Formats.Gif
private int bitDepth;
///
- /// Gif specific meta data.
+ /// Gif specific metadata.
///
- private GifMetaData gifMetaData;
+ private GifMetadata gifMetadata;
///
/// Initializes a new instance of the class.
@@ -81,14 +87,19 @@ namespace SixLabors.ImageSharp.Formats.Gif
Guard.NotNull(image, nameof(image));
Guard.NotNull(stream, nameof(stream));
- ImageMetaData metaData = image.MetaData;
- this.gifMetaData = metaData.GetFormatMetaData(GifFormat.Instance);
- this.colorTableMode = this.colorTableMode ?? this.gifMetaData.ColorTableMode;
- bool useGlobalTable = this.colorTableMode.Equals(GifColorTableMode.Global);
+ this.configuration = image.GetConfiguration();
+
+ ImageMetadata metadata = image.Metadata;
+ this.gifMetadata = metadata.GetFormatMetadata(GifFormat.Instance);
+ this.colorTableMode = this.colorTableMode ?? this.gifMetadata.ColorTableMode;
+ bool useGlobalTable = this.colorTableMode == GifColorTableMode.Global;
// Quantize the image returning a palette.
- QuantizedFrame quantized =
- this.quantizer.CreateFrameQuantizer().QuantizeFrame(image.Frames.RootFrame);
+ QuantizedFrame quantized = null;
+ using (IFrameQuantizer frameQuantizer = this.quantizer.CreateFrameQuantizer(image.GetConfiguration()))
+ {
+ quantized = frameQuantizer.QuantizeFrame(image.Frames.RootFrame);
+ }
// Get the number of bits.
this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(quantized.Palette.Length).Clamp(1, 8);
@@ -98,7 +109,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
// Write the LSD.
int index = this.GetTransparentIndex(quantized);
- this.WriteLogicalScreenDescriptor(metaData, image.Width, image.Height, index, useGlobalTable, stream);
+ this.WriteLogicalScreenDescriptor(metadata, image.Width, image.Height, index, useGlobalTable, stream);
if (useGlobalTable)
{
@@ -106,12 +117,12 @@ namespace SixLabors.ImageSharp.Formats.Gif
}
// Write the comments.
- this.WriteComments(metaData, stream);
+ this.WriteComments(metadata, stream);
// Write application extension to allow additional frames.
if (image.Frames.Count > 1)
{
- this.WriteApplicationExtension(stream, this.gifMetaData.RepeatCount);
+ this.WriteApplicationExtension(stream, this.gifMetadata.RepeatCount);
}
if (useGlobalTable)
@@ -125,7 +136,6 @@ namespace SixLabors.ImageSharp.Formats.Gif
// Clean up.
quantized?.Dispose();
- quantized = null;
// TODO: Write extension etc
stream.WriteByte(GifConstants.EndIntroducer);
@@ -134,14 +144,14 @@ namespace SixLabors.ImageSharp.Formats.Gif
private void EncodeGlobal(Image image, QuantizedFrame quantized, int transparencyIndex, Stream stream)
where TPixel : struct, IPixel
{
- var palleteQuantizer = new PaletteQuantizer(this.quantizer.Diffuser);
+ var palleteQuantizer = new PaletteQuantizer(quantized.Palette, this.quantizer.Diffuser);
for (int i = 0; i < image.Frames.Count; i++)
{
ImageFrame frame = image.Frames[i];
- ImageFrameMetaData metaData = frame.MetaData;
- GifFrameMetaData frameMetaData = metaData.GetFormatMetaData(GifFormat.Instance);
- this.WriteGraphicalControlExtension(frameMetaData, transparencyIndex, stream);
+ ImageFrameMetadata metadata = frame.Metadata;
+ GifFrameMetadata frameMetadata = metadata.GetFormatMetadata(GifFormat.Instance);
+ this.WriteGraphicalControlExtension(frameMetadata, transparencyIndex, stream);
this.WriteImageDescriptor(frame, false, stream);
if (i == 0)
@@ -150,8 +160,8 @@ namespace SixLabors.ImageSharp.Formats.Gif
}
else
{
- using (QuantizedFrame paletteQuantized
- = palleteQuantizer.CreateFrameQuantizer(() => quantized.Palette).QuantizeFrame(frame))
+ using (IFrameQuantizer palleteFrameQuantizer = palleteQuantizer.CreateFrameQuantizer(image.GetConfiguration()))
+ using (QuantizedFrame paletteQuantized = palleteFrameQuantizer.QuantizeFrame(frame))
{
this.WriteImageData(paletteQuantized, stream);
}
@@ -163,28 +173,33 @@ namespace SixLabors.ImageSharp.Formats.Gif
where TPixel : struct, IPixel
{
ImageFrame previousFrame = null;
- GifFrameMetaData previousMeta = null;
+ GifFrameMetadata previousMeta = null;
foreach (ImageFrame frame in image.Frames)
{
- ImageFrameMetaData metaData = frame.MetaData;
- GifFrameMetaData frameMetaData = metaData.GetFormatMetaData(GifFormat.Instance);
+ ImageFrameMetadata metadata = frame.Metadata;
+ GifFrameMetadata frameMetadata = metadata.GetFormatMetadata(GifFormat.Instance);
if (quantized is null)
{
// Allow each frame to be encoded at whatever color depth the frame designates if set.
- if (previousFrame != null
- && previousMeta.ColorTableLength != frameMetaData.ColorTableLength
- && frameMetaData.ColorTableLength > 0)
+ if (previousFrame != null && previousMeta.ColorTableLength != frameMetadata.ColorTableLength
+ && frameMetadata.ColorTableLength > 0)
{
- quantized = this.quantizer.CreateFrameQuantizer(frameMetaData.ColorTableLength).QuantizeFrame(frame);
+ using (IFrameQuantizer frameQuantizer = this.quantizer.CreateFrameQuantizer(image.GetConfiguration(), frameMetadata.ColorTableLength))
+ {
+ quantized = frameQuantizer.QuantizeFrame(frame);
+ }
}
else
{
- quantized = this.quantizer.CreateFrameQuantizer().QuantizeFrame(frame);
+ using (IFrameQuantizer frameQuantizer = this.quantizer.CreateFrameQuantizer(image.GetConfiguration()))
+ {
+ quantized = frameQuantizer.QuantizeFrame(frame);
+ }
}
}
this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(quantized.Palette.Length).Clamp(1, 8);
- this.WriteGraphicalControlExtension(frameMetaData, this.GetTransparentIndex(quantized), stream);
+ this.WriteGraphicalControlExtension(frameMetadata, this.GetTransparentIndex(quantized), stream);
this.WriteImageDescriptor(frame, true, stream);
this.WriteColorTable(quantized, stream);
this.WriteImageData(quantized, stream);
@@ -192,7 +207,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
quantized?.Dispose();
quantized = null; // So next frame can regenerate it
previousFrame = frame;
- previousMeta = frameMetaData;
+ previousMeta = frameMetadata;
}
}
@@ -217,7 +232,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
{
Span rgbaSpan = rgbaBuffer.GetSpan();
ref Rgba32 paletteRef = ref MemoryMarshal.GetReference(rgbaSpan);
- PixelOperations.Instance.ToRgba32(quantized.Palette, rgbaSpan);
+ PixelOperations.Instance.ToRgba32(this.configuration, quantized.Palette, rgbaSpan);
for (int i = quantized.Palette.Length - 1; i >= 0; i--)
{
@@ -241,14 +256,14 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
/// Writes the logical screen descriptor to the stream.
///
- /// The image metadata.
+ /// The image metadata.
/// The image width.
/// The image height.
/// The transparency index to set the default background index to.
/// Whether to use a global or local color table.
/// The stream to write to.
private void WriteLogicalScreenDescriptor(
- ImageMetaData metaData,
+ ImageMetadata metadata,
int width,
int height,
int transparencyIndex,
@@ -268,10 +283,10 @@ namespace SixLabors.ImageSharp.Formats.Gif
// Aspect Ratio = (Pixel Aspect Ratio + 15) / 64
byte ratio = 0;
- if (metaData.ResolutionUnits == PixelResolutionUnit.AspectRatio)
+ if (metadata.ResolutionUnits == PixelResolutionUnit.AspectRatio)
{
- double hr = metaData.HorizontalResolution;
- double vr = metaData.VerticalResolution;
+ double hr = metadata.HorizontalResolution;
+ double vr = metadata.VerticalResolution;
if (hr != vr)
{
if (hr > vr)
@@ -317,9 +332,10 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
/// The metadata to be extract the comment data.
/// The stream to write to.
- private void WriteComments(ImageMetaData metadata, Stream stream)
+ private void WriteComments(ImageMetadata metadata, Stream stream)
{
- if (!metadata.TryGetProperty(GifConstants.Comments, out ImageProperty property) || string.IsNullOrEmpty(property.Value))
+ if (!metadata.TryGetProperty(GifConstants.Comments, out ImageProperty property)
+ || string.IsNullOrEmpty(property.Value))
{
return;
}
@@ -340,18 +356,18 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
/// Writes the graphics control extension to the stream.
///
- /// The metadata of the image or frame.
+ /// The metadata of the image or frame.
/// The index of the color in the color palette to make transparent.
/// The stream to write to.
- private void WriteGraphicalControlExtension(GifFrameMetaData metaData, int transparencyIndex, Stream stream)
+ private void WriteGraphicalControlExtension(GifFrameMetadata metadata, int transparencyIndex, Stream stream)
{
byte packedValue = GifGraphicControlExtension.GetPackedValue(
- disposalMethod: metaData.DisposalMethod,
+ disposalMethod: metadata.DisposalMethod,
transparencyFlag: transparencyIndex > -1);
var extension = new GifGraphicControlExtension(
packed: packedValue,
- delayTime: (ushort)metaData.FrameDelay,
+ delayTime: (ushort)metadata.FrameDelay,
transparencyIndex: unchecked((byte)transparencyIndex));
this.WriteExtension(extension, stream);
@@ -411,13 +427,17 @@ namespace SixLabors.ImageSharp.Formats.Gif
private void WriteColorTable(QuantizedFrame image, Stream stream)
where TPixel : struct, IPixel
{
- // The maximium number of colors for the bit depth
+ // The maximum number of colors for the bit depth
int colorTableLength = ImageMaths.GetColorCountForBitDepth(this.bitDepth) * 3;
int pixelCount = image.Palette.Length;
using (IManagedByteBuffer colorTable = this.memoryAllocator.AllocateManagedByteBuffer(colorTableLength))
{
- PixelOperations.Instance.ToRgb24Bytes(image.Palette.AsSpan(), colorTable.GetSpan(), pixelCount);
+ PixelOperations.Instance.ToRgb24Bytes(
+ this.configuration,
+ image.Palette.AsSpan(),
+ colorTable.GetSpan(),
+ pixelCount);
stream.Write(colorTable.Array, 0, colorTableLength);
}
}
diff --git a/src/ImageSharp/Formats/Gif/GifFormat.cs b/src/ImageSharp/Formats/Gif/GifFormat.cs
index 07d4a54547..abe87819c6 100644
--- a/src/ImageSharp/Formats/Gif/GifFormat.cs
+++ b/src/ImageSharp/Formats/Gif/GifFormat.cs
@@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
/// Registers the image encoders, decoders and mime type detectors for the gif format.
///
- public sealed class GifFormat : IImageFormat
+ public sealed class GifFormat : IImageFormat
{
private GifFormat()
{
@@ -32,9 +32,9 @@ namespace SixLabors.ImageSharp.Formats.Gif
public IEnumerable FileExtensions => GifConstants.FileExtensions;
///
- public GifMetaData CreateDefaultFormatMetaData() => new GifMetaData();
+ public GifMetadata CreateDefaultFormatMetadata() => new GifMetadata();
///
- public GifFrameMetaData CreateDefaultFormatFrameMetaData() => new GifFrameMetaData();
+ public GifFrameMetadata CreateDefaultFormatFrameMetadata() => new GifFrameMetadata();
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Gif/GifFrameMetaData.cs b/src/ImageSharp/Formats/Gif/GifFrameMetaData.cs
index 0042c6a108..613825ad63 100644
--- a/src/ImageSharp/Formats/Gif/GifFrameMetaData.cs
+++ b/src/ImageSharp/Formats/Gif/GifFrameMetaData.cs
@@ -6,20 +6,20 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
/// Provides Gif specific metadata information for the image frame.
///
- public class GifFrameMetaData : IDeepCloneable
+ public class GifFrameMetadata : IDeepCloneable
{
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
- public GifFrameMetaData()
+ public GifFrameMetadata()
{
}
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
/// The metadata to create an instance from.
- private GifFrameMetaData(GifFrameMetaData other)
+ private GifFrameMetadata(GifFrameMetadata other)
{
this.ColorTableLength = other.ColorTableLength;
this.FrameDelay = other.FrameDelay;
@@ -49,6 +49,6 @@ namespace SixLabors.ImageSharp.Formats.Gif
public GifDisposalMethod DisposalMethod { get; set; }
///
- public IDeepCloneable DeepClone() => new GifFrameMetaData(this);
+ public IDeepCloneable DeepClone() => new GifFrameMetadata(this);
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Gif/GifMetaData.cs b/src/ImageSharp/Formats/Gif/GifMetaData.cs
index bb7fb50518..0b6566fbfe 100644
--- a/src/ImageSharp/Formats/Gif/GifMetaData.cs
+++ b/src/ImageSharp/Formats/Gif/GifMetaData.cs
@@ -6,20 +6,20 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
/// Provides Gif specific metadata information for the image.
///
- public class GifMetaData : IDeepCloneable
+ public class GifMetadata : IDeepCloneable
{
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
- public GifMetaData()
+ public GifMetadata()
{
}
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
/// The metadata to create an instance from.
- private GifMetaData(GifMetaData other)
+ private GifMetadata(GifMetadata other)
{
this.RepeatCount = other.RepeatCount;
this.ColorTableMode = other.ColorTableMode;
@@ -45,6 +45,6 @@ namespace SixLabors.ImageSharp.Formats.Gif
public int GlobalColorTableLength { get; set; }
///
- public IDeepCloneable DeepClone() => new GifMetaData(this);
+ public IDeepCloneable DeepClone() => new GifMetadata(this);
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Gif/IGifDecoderOptions.cs b/src/ImageSharp/Formats/Gif/IGifDecoderOptions.cs
index 42c202a3d9..871b511a0d 100644
--- a/src/ImageSharp/Formats/Gif/IGifDecoderOptions.cs
+++ b/src/ImageSharp/Formats/Gif/IGifDecoderOptions.cs
@@ -2,7 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System.Text;
-using SixLabors.ImageSharp.MetaData;
+using SixLabors.ImageSharp.Metadata;
namespace SixLabors.ImageSharp.Formats.Gif
{
@@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
Encoding TextEncoding { get; }
///
- /// Gets the decoding mode for multi-frame images
+ /// Gets the decoding mode for multi-frame images.
///
FrameDecodingMode DecodingMode { get; }
}
diff --git a/src/ImageSharp/Formats/Gif/LzwEncoder.cs b/src/ImageSharp/Formats/Gif/LzwEncoder.cs
index e390dfd54c..2d32fd23aa 100644
--- a/src/ImageSharp/Formats/Gif/LzwEncoder.cs
+++ b/src/ImageSharp/Formats/Gif/LzwEncoder.cs
@@ -52,7 +52,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
};
///
- /// The maximium number of bits/code.
+ /// The maximum number of bits/code.
///
private const int MaxBits = 12;
@@ -210,7 +210,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// flush the packet to disk.
///
/// The character to add.
- /// The reference to the storage for packat accumulators
+ /// The reference to the storage for packet accumulators
/// The stream to write to.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void AddCharacter(byte c, ref byte accumulatorsRef, Stream stream)
@@ -309,10 +309,10 @@ namespace SixLabors.ImageSharp.Formats.Gif
// Non-empty slot
if (Unsafe.Add(ref hashTableRef, i) >= 0)
{
- int disp = hsizeReg - i;
- if (i == 0)
+ int disp = 1;
+ if (i != 0)
{
- disp = 1;
+ disp = hsizeReg - i;
}
do
diff --git a/src/ImageSharp/Formats/Gif/Sections/IGifExtension.cs b/src/ImageSharp/Formats/Gif/Sections/IGifExtension.cs
index 2fdc233b0c..c8bd286746 100644
--- a/src/ImageSharp/Formats/Gif/Sections/IGifExtension.cs
+++ b/src/ImageSharp/Formats/Gif/Sections/IGifExtension.cs
@@ -1,4 +1,7 @@
-using System;
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
namespace SixLabors.ImageSharp.Formats.Gif
{
diff --git a/src/ImageSharp/Formats/IImageFormat.cs b/src/ImageSharp/Formats/IImageFormat.cs
index 94191c1493..bd0d6357cb 100644
--- a/src/ImageSharp/Formats/IImageFormat.cs
+++ b/src/ImageSharp/Formats/IImageFormat.cs
@@ -16,12 +16,12 @@ namespace SixLabors.ImageSharp.Formats
string Name { get; }
///
- /// Gets the default mimetype that the image foramt uses
+ /// Gets the default mimetype that the image format uses
///
string DefaultMimeType { get; }
///
- /// Gets all the mimetypes that have been used by this image foramt.
+ /// Gets all the mimetypes that have been used by this image format.
///
IEnumerable MimeTypes { get; }
@@ -34,30 +34,30 @@ namespace SixLabors.ImageSharp.Formats
///
/// Defines the contract for an image format containing metadata.
///
- /// The type of format metadata.
- public interface IImageFormat : IImageFormat
- where TFormatMetaData : class
+ /// The type of format metadata.
+ public interface IImageFormat : IImageFormat
+ where TFormatMetadata : class
{
///
/// Creates a default instance of the format metadata.
///
- /// The .
- TFormatMetaData CreateDefaultFormatMetaData();
+ /// The .
+ TFormatMetadata CreateDefaultFormatMetadata();
}
///
/// Defines the contract for an image format containing metadata with multiple frames.
///
- /// The type of format metadata.
- /// The type of format frame metadata.
- public interface IImageFormat : IImageFormat
- where TFormatMetaData : class
- where TFormatFrameMetaData : class
+ /// The type of format metadata.
+ /// The type of format frame metadata.
+ public interface IImageFormat : IImageFormat
+ where TFormatMetadata : class
+ where TFormatFrameMetadata : class
{
///
/// Creates a default instance of the format frame metadata.
///
- /// The .
- TFormatFrameMetaData CreateDefaultFormatFrameMetaData();
+ /// The .
+ TFormatFrameMetadata CreateDefaultFormatFrameMetadata();
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs
index 5601a94366..60fec25d29 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs
@@ -10,7 +10,7 @@ using System.Text;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{
///
- /// Represents a Jpeg block with coefficiens.
+ /// Represents a Jpeg block with coefficients.
///
// ReSharper disable once InconsistentNaming
internal unsafe struct Block8x8 : IEquatable
@@ -64,7 +64,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
}
///
- /// Gets or sets a value in a row+coulumn of the 8x8 block
+ /// Gets or sets a value in a row+column of the 8x8 block
///
/// The x position index in the row
/// The column index
@@ -283,7 +283,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
}
///
- /// Calculate the total sum of absoulute differences of elements in 'a' and 'b'.
+ /// Calculate the total sum of absolute differences of elements in 'a' and 'b'.
///
public static long TotalDifference(ref Block8x8 a, ref Block8x8 b)
{
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.CopyTo.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.CopyTo.cs
index b7dd125a88..6bf9c8483a 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.CopyTo.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.CopyTo.cs
@@ -3,64 +3,41 @@
using System.Numerics;
using System.Runtime.CompilerServices;
-
using SixLabors.ImageSharp.Memory;
+// ReSharper disable UseObjectOrCollectionInitializer
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{
internal partial struct Block8x8F
{
///
- /// Copy block data into the destination color buffer pixel area with the provided horizontal and vertical.
+ /// Copy block data into the destination color buffer pixel area with the provided horizontal and vertical scale factors.
///
+ [MethodImpl(InliningOptions.ShortMethod)]
public void CopyTo(in BufferArea area, int horizontalScale, int verticalScale)
{
if (horizontalScale == 1 && verticalScale == 1)
{
- this.CopyTo(area);
+ this.Copy1x1Scale(area);
return;
}
- else if (horizontalScale == 2 && verticalScale == 2)
+
+ if (horizontalScale == 2 && verticalScale == 2)
{
- this.CopyTo2x2(area);
+ this.Copy2x2Scale(area);
return;
}
- ref float destBase = ref area.GetReferenceToOrigin();
-
- // TODO: Optimize: implement all the cases with loopless special code! (T4?)
- for (int y = 0; y < 8; y++)
- {
- int yy = y * verticalScale;
- int y8 = y * 8;
-
- for (int x = 0; x < 8; x++)
- {
- int xx = x * horizontalScale;
-
- float value = this[y8 + x];
-
- for (int i = 0; i < verticalScale; i++)
- {
- int baseIdx = ((yy + i) * area.Stride) + xx;
-
- for (int j = 0; j < horizontalScale; j++)
- {
- // area[xx + j, yy + i] = value;
- Unsafe.Add(ref destBase, baseIdx + j) = value;
- }
- }
- }
- }
+ // TODO: Optimize: implement all cases with scale-specific, loopless code!
+ this.CopyArbitraryScale(area, horizontalScale, verticalScale);
}
- // [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void CopyTo(in BufferArea area)
+ public void Copy1x1Scale(in BufferArea destination)
{
ref byte selfBase = ref Unsafe.As(ref this);
- ref byte destBase = ref Unsafe.As(ref area.GetReferenceToOrigin());
- int destStride = area.Stride * sizeof(float);
+ ref byte destBase = ref Unsafe.As(ref destination.GetReferenceToOrigin());
+ int destStride = destination.Stride * sizeof(float);
CopyRowImpl(ref selfBase, ref destBase, destStride, 0);
CopyRowImpl(ref selfBase, ref destBase, destStride, 1);
@@ -80,76 +57,86 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
Unsafe.CopyBlock(ref d, ref s, 8 * sizeof(float));
}
- private void CopyTo2x2(in BufferArea area)
+ private void Copy2x2Scale(in BufferArea area)
{
- ref float destBase = ref area.GetReferenceToOrigin();
- int destStride = area.Stride;
-
- this.WidenCopyImpl2x2(ref destBase, 0, destStride);
- this.WidenCopyImpl2x2(ref destBase, 1, destStride);
- this.WidenCopyImpl2x2(ref destBase, 2, destStride);
- this.WidenCopyImpl2x2(ref destBase, 3, destStride);
- this.WidenCopyImpl2x2(ref destBase, 4, destStride);
- this.WidenCopyImpl2x2(ref destBase, 5, destStride);
- this.WidenCopyImpl2x2(ref destBase, 6, destStride);
- this.WidenCopyImpl2x2(ref destBase, 7, destStride);
+ ref Vector2 destBase = ref Unsafe.As(ref area.GetReferenceToOrigin());
+ int destStride = area.Stride / 2;
+
+ this.WidenCopyRowImpl2x2(ref destBase, 0, destStride);
+ this.WidenCopyRowImpl2x2(ref destBase, 1, destStride);
+ this.WidenCopyRowImpl2x2(ref destBase, 2, destStride);
+ this.WidenCopyRowImpl2x2(ref destBase, 3, destStride);
+ this.WidenCopyRowImpl2x2(ref destBase, 4, destStride);
+ this.WidenCopyRowImpl2x2(ref destBase, 5, destStride);
+ this.WidenCopyRowImpl2x2(ref destBase, 6, destStride);
+ this.WidenCopyRowImpl2x2(ref destBase, 7, destStride);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void WidenCopyImpl2x2(ref float destBase, int row, int destStride)
+ private void WidenCopyRowImpl2x2(ref Vector2 destBase, int row, int destStride)
{
- ref Vector4 selfLeft = ref Unsafe.Add(ref this.V0L, 2 * row);
- ref Vector4 selfRight = ref Unsafe.Add(ref selfLeft, 1);
- ref float destLocalOrigo = ref Unsafe.Add(ref destBase, row * 2 * destStride);
-
- Unsafe.Add(ref destLocalOrigo, 0) = selfLeft.X;
- Unsafe.Add(ref destLocalOrigo, 1) = selfLeft.X;
- Unsafe.Add(ref destLocalOrigo, 2) = selfLeft.Y;
- Unsafe.Add(ref destLocalOrigo, 3) = selfLeft.Y;
- Unsafe.Add(ref destLocalOrigo, 4) = selfLeft.Z;
- Unsafe.Add(ref destLocalOrigo, 5) = selfLeft.Z;
- Unsafe.Add(ref destLocalOrigo, 6) = selfLeft.W;
- Unsafe.Add(ref destLocalOrigo, 7) = selfLeft.W;
-
- Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 0) = selfRight.X;
- Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 1) = selfRight.X;
- Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 2) = selfRight.Y;
- Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 3) = selfRight.Y;
- Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 4) = selfRight.Z;
- Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 5) = selfRight.Z;
- Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 6) = selfRight.W;
- Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 7) = selfRight.W;
-
- Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 0) = selfLeft.X;
- Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 1) = selfLeft.X;
- Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 2) = selfLeft.Y;
- Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 3) = selfLeft.Y;
- Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 4) = selfLeft.Z;
- Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 5) = selfLeft.Z;
- Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 6) = selfLeft.W;
- Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 7) = selfLeft.W;
-
- Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 0) = selfRight.X;
- Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 1) = selfRight.X;
- Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 2) = selfRight.Y;
- Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 3) = selfRight.Y;
- Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 4) = selfRight.Z;
- Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 5) = selfRight.Z;
- Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 6) = selfRight.W;
- Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 7) = selfRight.W;
+ ref Vector4 sLeft = ref Unsafe.Add(ref this.V0L, 2 * row);
+ ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1);
+
+ int offset = 2 * row * destStride;
+ ref Vector4 dTopLeft = ref Unsafe.As(ref Unsafe.Add(ref destBase, offset));
+ ref Vector4 dBottomLeft = ref Unsafe.As(ref Unsafe.Add(ref destBase, offset + destStride));
+
+ var xyLeft = new Vector4(sLeft.X);
+ xyLeft.Z = sLeft.Y;
+ xyLeft.W = sLeft.Y;
+
+ var zwLeft = new Vector4(sLeft.Z);
+ zwLeft.Z = sLeft.W;
+ zwLeft.W = sLeft.W;
+
+ var xyRight = new Vector4(sRight.X);
+ xyRight.Z = sRight.Y;
+ xyRight.W = sRight.Y;
+
+ var zwRight = new Vector4(sRight.Z);
+ zwRight.Z = sRight.W;
+ zwRight.W = sRight.W;
+
+ dTopLeft = xyLeft;
+ Unsafe.Add(ref dTopLeft, 1) = zwLeft;
+ Unsafe.Add(ref dTopLeft, 2) = xyRight;
+ Unsafe.Add(ref dTopLeft, 3) = zwRight;
+
+ dBottomLeft = xyLeft;
+ Unsafe.Add(ref dBottomLeft, 1) = zwLeft;
+ Unsafe.Add(ref dBottomLeft, 2) = xyRight;
+ Unsafe.Add(ref dBottomLeft, 3) = zwRight;
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static void WidenCopyImpl(ref Vector4 s, ref float destBase)
+ [MethodImpl(InliningOptions.ColdPath)]
+ private void CopyArbitraryScale(BufferArea area, int horizontalScale, int verticalScale)
{
- Unsafe.Add(ref destBase, 0) = s.X;
- Unsafe.Add(ref destBase, 1) = s.X;
- Unsafe.Add(ref destBase, 2) = s.Y;
- Unsafe.Add(ref destBase, 3) = s.Y;
- Unsafe.Add(ref destBase, 4) = s.Z;
- Unsafe.Add(ref destBase, 5) = s.Z;
- Unsafe.Add(ref destBase, 6) = s.W;
- Unsafe.Add(ref destBase, 7) = s.W;
+ ref float destBase = ref area.GetReferenceToOrigin();
+
+ for (int y = 0; y < 8; y++)
+ {
+ int yy = y * verticalScale;
+ int y8 = y * 8;
+
+ for (int x = 0; x < 8; x++)
+ {
+ int xx = x * horizontalScale;
+
+ float value = this[y8 + x];
+
+ for (int i = 0; i < verticalScale; i++)
+ {
+ int baseIdx = ((yy + i) * area.Stride) + xx;
+
+ for (int j = 0; j < horizontalScale; j++)
+ {
+ // area[xx + j, yy + i] = value;
+ Unsafe.Add(ref destBase, baseIdx + j) = value;
+ }
+ }
+ }
+ }
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs
index e83896f587..a9e9903a9d 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs
@@ -1,6 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
+using System;
using System.Numerics;
using System.Runtime.CompilerServices;
@@ -9,15 +10,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{
internal partial struct Block8x8F
{
- private static readonly Vector4 CMin4 = new Vector4(0F);
- private static readonly Vector4 CMax4 = new Vector4(255F);
- private static readonly Vector4 COff4 = new Vector4(128F);
-
///
/// Transpose the block into the destination block.
///
/// The destination block
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
public void TransposeInto(ref Block8x8F d)
{
d.V0L.X = V0L.X;
@@ -94,10 +91,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
}
///
- /// Level shift by +128, clip to [0, 255]
+ /// Level shift by +maximum/2, clip to [0, maximum]
///
- public void NormalizeColorsInplace()
+ public void NormalizeColorsInplace(float maximum)
{
+ Vector4 CMin4 = new Vector4(0F);
+ Vector4 CMax4 = new Vector4(maximum);
+ Vector4 COff4 = new Vector4(MathF.Ceiling(maximum / 2));
+
this.V0L = Vector4.Clamp(this.V0L + COff4, CMin4, CMax4);
this.V0R = Vector4.Clamp(this.V0R + COff4, CMin4, CMax4);
this.V1L = Vector4.Clamp(this.V1L + COff4, CMin4, CMax4);
@@ -119,11 +120,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
///
/// AVX2-only variant for executing and in one step.
///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void NormalizeColorsAndRoundInplaceAvx2()
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public void NormalizeColorsAndRoundInplaceAvx2(float maximum)
{
- Vector off = new Vector(128f);
- Vector max = new Vector(255F);
+ Vector off = new Vector(MathF.Ceiling(maximum / 2));
+ Vector max = new Vector(maximum);
ref Vector row0 = ref Unsafe.As>(ref this.V0L);
row0 = NormalizeAndRound(row0, off, max);
@@ -154,7 +155,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
///
/// Fill the block from 'source' doing short -> float conversion.
///
- public void LoadFrom(ref Block8x8 source)
+ public void LoadFromInt16Scalar(ref Block8x8 source)
{
ref short selfRef = ref Unsafe.As(ref source);
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt
index 82d82ef0c2..d6c42a802a 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt
+++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt
@@ -11,6 +11,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
+using System;
using System.Numerics;
using System.Runtime.CompilerServices;
@@ -22,15 +23,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{
internal partial struct Block8x8F
{
- private static readonly Vector4 CMin4 = new Vector4(0F);
- private static readonly Vector4 CMax4 = new Vector4(255F);
- private static readonly Vector4 COff4 = new Vector4(128F);
-
///
/// Transpose the block into the destination block.
///
/// The destination block
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
public void TransposeInto(ref Block8x8F d)
{
<#
@@ -59,10 +56,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
}
///
- /// Level shift by +128, clip to [0, 255]
+ /// Level shift by +maximum/2, clip to [0, maximum]
///
- public void NormalizeColorsInplace()
+ public void NormalizeColorsInplace(float maximum)
{
+ Vector4 CMin4 = new Vector4(0F);
+ Vector4 CMax4 = new Vector4(maximum);
+ Vector4 COff4 = new Vector4(MathF.Ceiling(maximum / 2));
+
<#
PushIndent(" ");
@@ -82,11 +83,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
///
/// AVX2-only variant for executing and in one step.
///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void NormalizeColorsAndRoundInplaceAvx2()
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public void NormalizeColorsAndRoundInplaceAvx2(float maximum)
{
- Vector off = new Vector(128f);
- Vector max = new Vector(255F);
+ Vector off = new Vector(MathF.Ceiling(maximum / 2));
+ Vector max = new Vector(maximum);
<#
for (int i = 0; i < 8; i++)
@@ -104,7 +105,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
///
/// Fill the block from 'source' doing short -> float conversion.
///
- public void LoadFrom(ref Block8x8 source)
+ public void LoadFromInt16Scalar(ref Block8x8 source)
{
ref short selfRef = ref Unsafe.As(ref source);
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
index 59fc234c42..c85bd725c9 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
@@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
///
/// Represents a Jpeg block with coefficients.
///
- internal partial struct Block8x8F
+ internal partial struct Block8x8F : IEquatable
{
///
/// A number of scalar coefficients in a
@@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// The float value at the specified index
public float this[int idx]
{
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
get
{
GuardBlockIndex(idx);
@@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
return Unsafe.Add(ref selfRef, idx);
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
set
{
GuardBlockIndex(idx);
@@ -147,9 +147,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
}
///
- /// Fill the block with defaults (zeroes)
+ /// Fill the block with defaults (zeroes).
///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
public void Clear()
{
// The cheapest way to do this in C#:
@@ -157,10 +157,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
}
///
- /// Load raw 32bit floating point data from source
+ /// Load raw 32bit floating point data from source.
///
/// Source
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
public void LoadFrom(Span source)
{
ref byte s = ref Unsafe.As(ref MemoryMarshal.GetReference(source));
@@ -170,11 +170,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
}
///
- /// Load raw 32bit floating point data from source
+ /// Load raw 32bit floating point data from source.
///
/// Block pointer
/// Source
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
public static unsafe void LoadFrom(Block8x8F* blockPtr, Span source)
{
blockPtr->LoadFrom(source);
@@ -197,10 +197,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
}
///
- /// Copy raw 32bit floating point data to dest
+ /// Copy raw 32bit floating point data to dest,
///
/// Destination
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
public void CopyTo(Span dest)
{
ref byte d = ref Unsafe.As(ref MemoryMarshal.GetReference(dest));
@@ -210,11 +210,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
}
///
- /// Convert salars to byte-s and copy to dest
+ /// Convert salars to byte-s and copy to dest,
///
/// Pointer to block
/// Destination
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
public static unsafe void CopyTo(Block8x8F* blockPtr, Span dest)
{
float* fPtr = (float*)blockPtr;
@@ -226,11 +226,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
}
///
- /// Copy raw 32bit floating point data to dest
+ /// Copy raw 32bit floating point data to dest.
///
- /// Block pointer
- /// Destination
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ /// The block pointer.
+ /// The destination.
+ [MethodImpl(InliningOptions.ShortMethod)]
public static unsafe void CopyTo(Block8x8F* blockPtr, Span dest)
{
blockPtr->CopyTo(dest);
@@ -240,7 +240,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// Copy raw 32bit floating point data to dest
///
/// Destination
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
public unsafe void CopyTo(float[] dest)
{
fixed (void* ptr = &this.V0L)
@@ -275,8 +275,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
///
/// Multiply all elements of the block.
///
- /// The value to multiply by
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ /// The value to multiply by.
+ [MethodImpl(InliningOptions.ShortMethod)]
public void MultiplyInplace(float value)
{
this.V0L *= value;
@@ -298,9 +298,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
}
///
- /// Multiply all elements of the block by the corresponding elements of 'other'
+ /// Multiply all elements of the block by the corresponding elements of 'other'.
///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
public void MultiplyInplace(ref Block8x8F other)
{
this.V0L *= other.V0L;
@@ -325,7 +325,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// Adds a vector to all elements of the block.
///
/// The added vector
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
public void AddToAllInplace(Vector4 diff)
{
this.V0L += diff;
@@ -349,8 +349,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
///
/// Quantize the block.
///
- /// Block pointer
- /// Qt pointer
+ /// The block pointer.
+ /// The qt pointer.
/// Unzig pointer
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void DequantizeBlock(Block8x8F* blockPtr, Block8x8F* qtPtr, byte* unzigPtr)
@@ -395,7 +395,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
}
///
- /// Scales the 16x16 region represented by the 4 source blocks to the 8x8 DST block.
+ /// Scales the 16x16 region represented by the 4 source blocks to the 8x8 DST block.
///
/// The destination block.
/// The source block.
@@ -420,7 +420,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
}
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
private static void DivideRoundAll(ref Block8x8F a, ref Block8x8F b)
{
a.V0L = DivideRound(a.V0L, b.V0L);
@@ -467,17 +467,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
}
///
- /// Level shift by +128, clip to [0..255], and round all the values in the block.
+ /// Level shift by +maximum/2, clip to [0..maximum], and round all the values in the block.
///
- public void NormalizeColorsAndRoundInplace()
+ public void NormalizeColorsAndRoundInplace(float maximum)
{
if (SimdUtils.IsAvx2CompatibleArchitecture)
{
- this.NormalizeColorsAndRoundInplaceAvx2();
+ this.NormalizeColorsAndRoundInplaceAvx2(maximum);
}
else
{
- this.NormalizeColorsInplace();
+ this.NormalizeColorsInplace(maximum);
this.RoundInplace();
}
}
@@ -493,25 +493,90 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
}
}
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public void LoadFrom(ref Block8x8 source)
+ {
+#if SUPPORTS_EXTENDED_INTRINSICS
+ if (SimdUtils.IsAvx2CompatibleArchitecture)
+ {
+ this.LoadFromInt16ExtendedAvx2(ref source);
+ return;
+ }
+#endif
+ this.LoadFromInt16Scalar(ref source);
+ }
+
+ ///
+ /// Loads values from using extended AVX2 intrinsics.
+ ///
+ /// The source
+ public void LoadFromInt16ExtendedAvx2(ref Block8x8 source)
+ {
+ DebugGuard.IsTrue(
+ SimdUtils.IsAvx2CompatibleArchitecture,
+ "LoadFromUInt16ExtendedAvx2 only works on AVX2 compatible architecture!");
+
+ ref Vector sRef = ref Unsafe.As>(ref source);
+ ref Vector dRef = ref Unsafe.As>(ref this);
+
+ // Vector.Count == 16 on AVX2
+ // We can process 2 block rows in a single step
+ SimdUtils.ExtendedIntrinsics.ConvertToSingle(sRef, out Vector top, out Vector bottom);
+ dRef = top;
+ Unsafe.Add(ref dRef, 1) = bottom;
+
+ SimdUtils.ExtendedIntrinsics.ConvertToSingle(Unsafe.Add(ref sRef, 1), out top, out bottom);
+ Unsafe.Add(ref dRef, 2) = top;
+ Unsafe.Add(ref dRef, 3) = bottom;
+
+ SimdUtils.ExtendedIntrinsics.ConvertToSingle(Unsafe.Add(ref sRef, 2), out top, out bottom);
+ Unsafe.Add(ref dRef, 4) = top;
+ Unsafe.Add(ref dRef, 5) = bottom;
+
+ SimdUtils.ExtendedIntrinsics.ConvertToSingle(Unsafe.Add(ref sRef, 3), out top, out bottom);
+ Unsafe.Add(ref dRef, 6) = top;
+ Unsafe.Add(ref dRef, 7) = bottom;
+ }
+
+ ///
+ public bool Equals(Block8x8F other)
+ {
+ return this.V0L == other.V0L
+ && this.V0R == other.V0R
+ && this.V1L == other.V1L
+ && this.V1R == other.V1R
+ && this.V2L == other.V2L
+ && this.V2R == other.V2R
+ && this.V3L == other.V3L
+ && this.V3R == other.V3R
+ && this.V4L == other.V4L
+ && this.V4R == other.V4R
+ && this.V5L == other.V5L
+ && this.V5R == other.V5R
+ && this.V6L == other.V6L
+ && this.V6R == other.V6R
+ && this.V7L == other.V7L
+ && this.V7R == other.V7R;
+ }
+
///
public override string ToString()
{
var sb = new StringBuilder();
sb.Append('[');
- for (int i = 0; i < Size; i++)
+ for (int i = 0; i < Size - 1; i++)
{
sb.Append(this[i]);
- if (i < Size - 1)
- {
- sb.Append(',');
- }
+ sb.Append(',');
}
+ sb.Append(this[Size - 1]);
+
sb.Append(']');
return sb.ToString();
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
private static Vector NormalizeAndRound(Vector row, Vector off, Vector max)
{
row += off;
@@ -520,13 +585,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
return row.FastRound();
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
private static Vector4 DivideRound(Vector4 dividend, Vector4 divisor)
{
// sign(dividend) = max(min(dividend, 1), -1)
var sign = Vector4.Clamp(dividend, NegativeOne, Vector4.One);
- // AlmostRound(dividend/divisor) = dividend/divisior + 0.5*sign(dividend)
+ // AlmostRound(dividend/divisor) = dividend/divisor + 0.5*sign(dividend)
return (dividend / divisor) + (sign * Offset);
}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/AdobeMarker.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/AdobeMarker.cs
index af0938d302..e34af98251 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/AdobeMarker.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/AdobeMarker.cs
@@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
///
/// Gets the horizontal downsampling hint used for DCT encoding
/// 0x0 : (none - Chop)
- /// Bit 15 : Encoded with Blend=1 downsampling
+ /// Bit 15 : Encoded with Blend=1 downsampling.
///
public short APP14Flags0 { get; }
@@ -63,7 +63,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// Converts the specified byte array representation of an Adobe marker to its equivalent and
/// returns a value that indicates whether the conversion succeeded.
///
- /// The byte array containing metadata to parse
+ /// The byte array containing metadata to parse.
/// The marker to return.
public static bool TryParse(byte[] bytes, out AdobeMarker marker)
{
@@ -100,13 +100,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
///
public override int GetHashCode()
{
- return HashHelpers.Combine(
- this.DCTEncodeVersion.GetHashCode(),
- HashHelpers.Combine(
- this.APP14Flags0.GetHashCode(),
- HashHelpers.Combine(
- this.APP14Flags1.GetHashCode(),
- this.ColorTransform.GetHashCode())));
+ return HashCode.Combine(
+ this.DCTEncodeVersion,
+ this.APP14Flags0,
+ this.APP14Flags1,
+ this.ColorTransform);
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmyk.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmyk.cs
index 7a14d072e6..a57535b71c 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmyk.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmyk.cs
@@ -8,10 +8,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal abstract partial class JpegColorConverter
{
- internal class FromCmyk : JpegColorConverter
+ internal sealed class FromCmyk : JpegColorConverter
{
- public FromCmyk()
- : base(JpegColorSpace.Cmyk)
+ public FromCmyk(int precision)
+ : base(JpegColorSpace.Cmyk, precision)
{
}
@@ -25,14 +25,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
var v = new Vector4(0, 0, 0, 1F);
- var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F);
+ var scale = new Vector4(
+ 1 / this.MaximumValue,
+ 1 / this.MaximumValue,
+ 1 / this.MaximumValue,
+ 1F);
for (int i = 0; i < result.Length; i++)
{
float c = cVals[i];
float m = mVals[i];
float y = yVals[i];
- float k = kVals[i] / 255F;
+ float k = kVals[i] / this.MaximumValue;
v.X = c * k;
v.Y = m * k;
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs
index 5d7a31a12b..15bb2cf4b4 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs
@@ -3,38 +3,37 @@
using System;
using System.Numerics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal abstract partial class JpegColorConverter
{
- internal class FromGrayscale : JpegColorConverter
+ internal sealed class FromGrayscale : JpegColorConverter
{
- public FromGrayscale()
- : base(JpegColorSpace.Grayscale)
+ public FromGrayscale(int precision)
+ : base(JpegColorSpace.Grayscale, precision)
{
}
public override void ConvertToRgba(in ComponentValues values, Span result)
{
- // TODO: We can optimize a lot here with Vector and SRCS.Unsafe()!
- ReadOnlySpan yVals = values.Component0;
+ var scale = new Vector4(
+ 1 / this.MaximumValue,
+ 1 / this.MaximumValue,
+ 1 / this.MaximumValue,
+ 1F);
- var v = new Vector4(0, 0, 0, 1);
-
- var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F);
+ ref float sBase = ref MemoryMarshal.GetReference(values.Component0);
+ ref Vector4 dBase = ref MemoryMarshal.GetReference(result);
for (int i = 0; i < result.Length; i++)
{
- float y = yVals[i];
-
- v.X = y;
- v.Y = y;
- v.Z = y;
-
+ var v = new Vector4(Unsafe.Add(ref sBase, i));
+ v.W = 1f;
v *= scale;
-
- result[i] = v;
+ Unsafe.Add(ref dBase, i) = v;
}
}
}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgb.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgb.cs
index 7cd97c4140..2f68e312dc 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgb.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgb.cs
@@ -8,10 +8,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal abstract partial class JpegColorConverter
{
- internal class FromRgb : JpegColorConverter
+ internal sealed class FromRgb : JpegColorConverter
{
- public FromRgb()
- : base(JpegColorSpace.RGB)
+ public FromRgb(int precision)
+ : base(JpegColorSpace.RGB, precision)
{
}
@@ -24,7 +24,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
var v = new Vector4(0, 0, 0, 1);
- var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F);
+ var scale = new Vector4(
+ 1 / this.MaximumValue,
+ 1 / this.MaximumValue,
+ 1 / this.MaximumValue,
+ 1F);
for (int i = 0; i < result.Length; i++)
{
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrBasic.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrBasic.cs
index cb71889bc5..a646cd6cfe 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrBasic.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrBasic.cs
@@ -8,19 +8,19 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal abstract partial class JpegColorConverter
{
- internal class FromYCbCrBasic : JpegColorConverter
+ internal sealed class FromYCbCrBasic : JpegColorConverter
{
- public FromYCbCrBasic()
- : base(JpegColorSpace.YCbCr)
+ public FromYCbCrBasic(int precision)
+ : base(JpegColorSpace.YCbCr, precision)
{
}
public override void ConvertToRgba(in ComponentValues values, Span result)
{
- ConvertCore(values, result);
+ ConvertCore(values, result, this.MaximumValue, this.HalfValue);
}
- internal static void ConvertCore(in ComponentValues values, Span result)
+ internal static void ConvertCore(in ComponentValues values, Span result, float maxValue, float halfValue)
{
// TODO: We can optimize a lot here with Vector and SRCS.Unsafe()!
ReadOnlySpan yVals = values.Component0;
@@ -29,13 +29,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
var v = new Vector4(0, 0, 0, 1);
- var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F);
+ var scale = new Vector4(1 / maxValue, 1 / maxValue, 1 / maxValue, 1F);
for (int i = 0; i < result.Length; i++)
{
float y = yVals[i];
- float cb = cbVals[i] - 128F;
- float cr = crVals[i] - 128F;
+ float cb = cbVals[i] - halfValue;
+ float cr = crVals[i] - halfValue;
v.X = MathF.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero);
v.Y = MathF.Round(y - (0.344136F * cb) - (0.714136F * cr), MidpointRounding.AwayFromZero);
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs
index 1dc72aaf5b..1706b4c1bc 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs
@@ -12,10 +12,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal abstract partial class JpegColorConverter
{
- internal class FromYCbCrSimd : JpegColorConverter
+ internal sealed class FromYCbCrSimd : JpegColorConverter
{
- public FromYCbCrSimd()
- : base(JpegColorSpace.YCbCr)
+ public FromYCbCrSimd(int precision)
+ : base(JpegColorSpace.YCbCr, precision)
{
}
@@ -25,18 +25,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
int simdCount = result.Length - remainder;
if (simdCount > 0)
{
- ConvertCore(values.Slice(0, simdCount), result.Slice(0, simdCount));
+ ConvertCore(values.Slice(0, simdCount), result.Slice(0, simdCount), this.MaximumValue, this.HalfValue);
}
- FromYCbCrBasic.ConvertCore(values.Slice(simdCount, remainder), result.Slice(simdCount, remainder));
+ FromYCbCrBasic.ConvertCore(values.Slice(simdCount, remainder), result.Slice(simdCount, remainder), this.MaximumValue, this.HalfValue);
}
///
- /// SIMD convert using buffers of sizes divisable by 8.
+ /// SIMD convert using buffers of sizes divisible by 8.
///
- internal static void ConvertCore(in ComponentValues values, Span result)
+ internal static void ConvertCore(in ComponentValues values, Span result, float maxValue, float halfValue)
{
- DebugGuard.IsTrue(result.Length % 8 == 0, nameof(result), "result.Length should be divisable by 8!");
+ DebugGuard.IsTrue(result.Length % 8 == 0, nameof(result), "result.Length should be divisible by 8!");
ref Vector4Pair yBase =
ref Unsafe.As(ref MemoryMarshal.GetReference(values.Component0));
@@ -48,7 +48,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
ref Vector4Octet resultBase =
ref Unsafe.As(ref MemoryMarshal.GetReference(result));
- var chromaOffset = new Vector4(-128f);
+ var chromaOffset = new Vector4(-halfValue);
// Walking 8 elements at one step:
int n = result.Length / 8;
@@ -58,11 +58,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
// y = yVals[i];
Vector4Pair y = Unsafe.Add(ref yBase, i);
- // cb = cbVals[i] - 128F;
+ // cb = cbVals[i] - halfValue);
Vector4Pair cb = Unsafe.Add(ref cbBase, i);
cb.AddInplace(chromaOffset);
- // cr = crVals[i] - 128F;
+ // cr = crVals[i] - halfValue;
Vector4Pair cr = Unsafe.Add(ref crBase, i);
cr.AddInplace(chromaOffset);
@@ -90,15 +90,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
if (Vector.Count == 4)
{
// TODO: Find a way to properly run & test this path on AVX2 PC-s! (Have I already mentioned that Vector is terrible?)
- r.RoundAndDownscalePreAvx2();
- g.RoundAndDownscalePreAvx2();
- b.RoundAndDownscalePreAvx2();
+ r.RoundAndDownscalePreAvx2(maxValue);
+ g.RoundAndDownscalePreAvx2(maxValue);
+ b.RoundAndDownscalePreAvx2(maxValue);
}
else if (SimdUtils.IsAvx2CompatibleArchitecture)
{
- r.RoundAndDownscaleAvx2();
- g.RoundAndDownscaleAvx2();
- b.RoundAndDownscaleAvx2();
+ r.RoundAndDownscaleAvx2(maxValue);
+ g.RoundAndDownscaleAvx2(maxValue);
+ b.RoundAndDownscaleAvx2(maxValue);
}
else
{
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs
index 46644258b1..093ea2f9a2 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs
@@ -13,10 +13,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal abstract partial class JpegColorConverter
{
- internal class FromYCbCrSimdAvx2 : JpegColorConverter
+ internal sealed class FromYCbCrSimdAvx2 : JpegColorConverter
{
- public FromYCbCrSimdAvx2()
- : base(JpegColorSpace.YCbCr)
+ public FromYCbCrSimdAvx2(int precision)
+ : base(JpegColorSpace.YCbCr, precision)
{
}
@@ -28,16 +28,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
int simdCount = result.Length - remainder;
if (simdCount > 0)
{
- ConvertCore(values.Slice(0, simdCount), result.Slice(0, simdCount));
+ ConvertCore(values.Slice(0, simdCount), result.Slice(0, simdCount), this.MaximumValue, this.HalfValue);
}
- FromYCbCrBasic.ConvertCore(values.Slice(simdCount, remainder), result.Slice(simdCount, remainder));
+ FromYCbCrBasic.ConvertCore(values.Slice(simdCount, remainder), result.Slice(simdCount, remainder), this.MaximumValue, this.HalfValue);
}
///
- /// SIMD convert using buffers of sizes divisable by 8.
+ /// SIMD convert using buffers of sizes divisible by 8.
///
- internal static void ConvertCore(in ComponentValues values, Span result)
+ internal static void ConvertCore(in ComponentValues values, Span result, float maxValue, float halfValue)
{
// This implementation is actually AVX specific.
// An AVX register is capable of storing 8 float-s.
@@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
ref Vector4Octet resultBase =
ref Unsafe.As(ref MemoryMarshal.GetReference(result));
- var chromaOffset = new Vector(-128f);
+ var chromaOffset = new Vector(-halfValue);
// Walking 8 elements at one step:
int n = result.Length / 8;
@@ -70,7 +70,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
ref Vector ggRefAsVector = ref Unsafe.As>(ref gg);
ref Vector bbRefAsVector = ref Unsafe.As>(ref bb);
- var scale = new Vector(1 / 255f);
+ var scale = new Vector(1 / maxValue);
for (int i = 0; i < n; i++)
{
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccK.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccK.cs
index 6f940f62f9..cd8a3bb06c 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccK.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccK.cs
@@ -8,10 +8,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal abstract partial class JpegColorConverter
{
- internal class FromYccK : JpegColorConverter
+ internal sealed class FromYccK : JpegColorConverter
{
- public FromYccK()
- : base(JpegColorSpace.Ycck)
+ public FromYccK(int precision)
+ : base(JpegColorSpace.Ycck, precision)
{
}
@@ -25,18 +25,22 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
var v = new Vector4(0, 0, 0, 1F);
- var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F);
+ var scale = new Vector4(
+ 1 / this.MaximumValue,
+ 1 / this.MaximumValue,
+ 1 / this.MaximumValue,
+ 1F);
for (int i = 0; i < result.Length; i++)
{
float y = yVals[i];
- float cb = cbVals[i] - 128F;
- float cr = crVals[i] - 128F;
- float k = kVals[i] / 255F;
+ float cb = cbVals[i] - this.HalfValue;
+ float cr = crVals[i] - this.HalfValue;
+ float k = kVals[i] / this.MaximumValue;
- v.X = (255F - MathF.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero)) * k;
- v.Y = (255F - MathF.Round(y - (0.344136F * cb) - (0.714136F * cr), MidpointRounding.AwayFromZero)) * k;
- v.Z = (255F - MathF.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero)) * k;
+ v.X = (this.MaximumValue - MathF.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero)) * k;
+ v.Y = (this.MaximumValue - MathF.Round(y - (0.344136F * cb) - (0.714136F * cr), MidpointRounding.AwayFromZero)) * k;
+ v.Z = (this.MaximumValue - MathF.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero)) * k;
v.W = 1F;
v *= scale;
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs
index 456636dc39..61e3598696 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs
@@ -3,12 +3,10 @@
using System;
using System.Collections.Generic;
-using System.Linq;
using System.Numerics;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Tuples;
-using SixLabors.Memory;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
@@ -18,19 +16,34 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
internal abstract partial class JpegColorConverter
{
///
- /// The avalilable converters
+ /// The available converters
///
private static readonly JpegColorConverter[] Converters =
{
- GetYCbCrConverter(), new FromYccK(), new FromCmyk(), new FromGrayscale(), new FromRgb()
+ // 8-bit converters
+ GetYCbCrConverter(8),
+ new FromYccK(8),
+ new FromCmyk(8),
+ new FromGrayscale(8),
+ new FromRgb(8),
+
+ // 12-bit converters
+ GetYCbCrConverter(12),
+ new FromYccK(12),
+ new FromCmyk(12),
+ new FromGrayscale(12),
+ new FromRgb(12),
};
///
/// Initializes a new instance of the class.
///
- protected JpegColorConverter(JpegColorSpace colorSpace)
+ protected JpegColorConverter(JpegColorSpace colorSpace, int precision)
{
this.ColorSpace = colorSpace;
+ this.Precision = precision;
+ this.MaximumValue = MathF.Pow(2, precision) - 1;
+ this.HalfValue = MathF.Ceiling(this.MaximumValue / 2);
}
///
@@ -38,12 +51,28 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
///
public JpegColorSpace ColorSpace { get; }
+ ///
+ /// Gets the Precision of this converter in bits.
+ ///
+ public int Precision { get; }
+
+ ///
+ /// Gets the maximum value of a sample
+ ///
+ private float MaximumValue { get; }
+
+ ///
+ /// Gets the half of the maximum value of a sample
+ ///
+ private float HalfValue { get; }
+
///
/// Returns the corresponding to the given
///
- public static JpegColorConverter GetConverter(JpegColorSpace colorSpace)
+ public static JpegColorConverter GetConverter(JpegColorSpace colorSpace, float precision)
{
- JpegColorConverter converter = Converters.FirstOrDefault(c => c.ColorSpace == colorSpace);
+ JpegColorConverter converter = Array.Find(Converters, c => c.ColorSpace == colorSpace
+ && c.Precision == precision);
if (converter is null)
{
@@ -63,8 +92,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
///
/// Returns the for the YCbCr colorspace that matches the current CPU architecture.
///
- private static JpegColorConverter GetYCbCrConverter() =>
- FromYCbCrSimdAvx2.IsAvailable ? (JpegColorConverter)new FromYCbCrSimdAvx2() : new FromYCbCrSimd();
+ private static JpegColorConverter GetYCbCrConverter(int precision) =>
+ FromYCbCrSimdAvx2.IsAvailable ? (JpegColorConverter)new FromYCbCrSimdAvx2(precision) : new FromYCbCrSimd(precision);
///
/// A stack-only struct to reference the input buffers using -s.
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/FastACTable.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/FastACTable.cs
new file mode 100644
index 0000000000..dc78a89ddb
--- /dev/null
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/FastACTable.cs
@@ -0,0 +1,61 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System.Runtime.CompilerServices;
+
+namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
+{
+ internal unsafe struct FastACTable
+ {
+ ///
+ /// Gets the lookahead array.
+ ///
+ public fixed short Lookahead[512];
+
+ ///
+ /// Derives a lookup table for fast AC entropy scan decoding.
+ /// This can happen multiple times during progressive decoding but always outside mcu loops.
+ ///
+ /// The AC Huffman table.
+ public void Derive(ref HuffmanTable huffmanTable)
+ {
+ const int FastBits = ScanDecoder.FastBits;
+ ref short fastACRef = ref this.Lookahead[0];
+ ref byte huffmanLookaheadRef = ref huffmanTable.Lookahead[0];
+ ref byte huffmanValuesRef = ref huffmanTable.Values[0];
+ ref short huffmanSizesRef = ref huffmanTable.Sizes[0];
+
+ int i;
+ for (i = 0; i < (1 << FastBits); i++)
+ {
+ byte fast = Unsafe.Add(ref huffmanLookaheadRef, i);
+ Unsafe.Add(ref fastACRef, i) = 0;
+
+ if (fast < byte.MaxValue)
+ {
+ int rs = Unsafe.Add(ref huffmanValuesRef, fast);
+ int run = (rs >> 4) & 15;
+ int magbits = rs & 15;
+ int len = Unsafe.Add(ref huffmanSizesRef, fast);
+
+ if (magbits != 0 && len + magbits <= FastBits)
+ {
+ // Magnitude code followed by receive_extend code
+ int k = ((i << len) & ((1 << FastBits) - 1)) >> (FastBits - magbits);
+ int m = 1 << (magbits - 1);
+ if (k < m)
+ {
+ k += (int)((~0U << magbits) + 1);
+ }
+
+ // If the result is small enough, we can fit it in fastAC table
+ if (k >= -128 && k <= 127)
+ {
+ Unsafe.Add(ref fastACRef, i) = (short)((k << 8) + (run << 4) + (len + magbits));
+ }
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/FastACTables.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/FastACTables.cs
deleted file mode 100644
index 06b46746a6..0000000000
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/FastACTables.cs
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright (c) Six Labors and contributors.
-// Licensed under the Apache License, Version 2.0.
-
-using System;
-using System.Runtime.CompilerServices;
-
-using SixLabors.ImageSharp.Memory;
-using SixLabors.Memory;
-
-namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
-{
- ///
- /// The collection of lookup tables used for fast AC entropy scan decoding.
- ///
- internal sealed class FastACTables : IDisposable
- {
- private Buffer2D tables;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The memory allocator used to allocate memory for image processing operations.
- public FastACTables(MemoryAllocator memoryAllocator) => this.tables = memoryAllocator.Allocate2D(512, 4, AllocationOptions.Clean);
-
- ///
- /// Gets the representing the table at the index in the collection.
- ///
- /// The table index.
- ///
- [MethodImpl(InliningOptions.ShortMethod)]
- public ReadOnlySpan GetTableSpan(int index) => this.tables.GetRowSpan(index);
-
- ///
- /// Gets a reference to the first element of the AC table indexed by
- ///
- /// The frame component.
- [MethodImpl(InliningOptions.ShortMethod)]
- public ref short GetAcTableReference(JpegComponent component) => ref this.tables.GetRowSpan(component.ACHuffmanTableId)[0];
-
- ///
- /// Builds a lookup table for fast AC entropy scan decoding.
- ///
- /// The table index.
- /// The collection of AC Huffman tables.
- public unsafe void BuildACTableLut(int index, HuffmanTables acHuffmanTables)
- {
- const int FastBits = ScanDecoder.FastBits;
- Span fastAC = this.tables.GetRowSpan(index);
- ref HuffmanTable huffmanTable = ref acHuffmanTables[index];
-
- int i;
- for (i = 0; i < (1 << FastBits); i++)
- {
- byte fast = huffmanTable.Lookahead[i];
- fastAC[i] = 0;
- if (fast < byte.MaxValue)
- {
- int rs = huffmanTable.Values[fast];
- int run = (rs >> 4) & 15;
- int magbits = rs & 15;
- int len = huffmanTable.Sizes[fast];
-
- if (magbits != 0 && len + magbits <= FastBits)
- {
- // Magnitude code followed by receive_extend code
- int k = ((i << len) & ((1 << FastBits) - 1)) >> (FastBits - magbits);
- int m = 1 << (magbits - 1);
- if (k < m)
- {
- k += (int)((~0U << magbits) + 1);
- }
-
- // if the result is small enough, we can fit it in fastAC table
- if (k >= -128 && k <= 127)
- {
- fastAC[i] = (short)((k << 8) + (run << 4) + (len + magbits));
- }
- }
- }
- }
- }
-
- ///
- public void Dispose()
- {
- this.tables?.Dispose();
- this.tables = null;
- }
- }
-}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs
index 24d570bf1c..7e025e271e 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs
@@ -17,23 +17,31 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
[StructLayout(LayoutKind.Sequential)]
internal unsafe struct HuffmanTable
{
+ private bool isDerived;
+
+ private readonly MemoryAllocator memoryAllocator;
+
+#pragma warning disable IDE0044 // Add readonly modifier
+ private fixed byte codeLengths[17];
+#pragma warning restore IDE0044 // Add readonly modifier
+
///
- /// Gets the max code array
+ /// Gets the max code array.
///
public fixed uint MaxCode[18];
///
- /// Gets the value offset array
+ /// Gets the value offset array.
///
public fixed int ValOffset[18];
///
- /// Gets the huffman value array
+ /// Gets the huffman value array.
///
public fixed byte Values[256];
///
- /// Gets the lookahead array
+ /// Gets the lookahead array.
///
public fixed byte Lookahead[512];
@@ -50,16 +58,31 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// The huffman values
public HuffmanTable(MemoryAllocator memoryAllocator, ReadOnlySpan codeLengths, ReadOnlySpan values)
{
+ this.isDerived = false;
+ this.memoryAllocator = memoryAllocator;
+ Unsafe.CopyBlockUnaligned(ref this.codeLengths[0], ref MemoryMarshal.GetReference(codeLengths), (uint)codeLengths.Length);
+ Unsafe.CopyBlockUnaligned(ref this.Values[0], ref MemoryMarshal.GetReference(values), (uint)values.Length);
+ }
+
+ ///
+ /// Expands the HuffmanTable into its derived form.
+ ///
+ public void Derive()
+ {
+ if (this.isDerived)
+ {
+ return;
+ }
+
const int Length = 257;
- using (IMemoryOwner huffcode = memoryAllocator.Allocate(Length))
+ using (IMemoryOwner huffcode = this.memoryAllocator.Allocate(Length))
{
ref short huffcodeRef = ref MemoryMarshal.GetReference(huffcode.GetSpan());
- ref byte codeLengthsRef = ref MemoryMarshal.GetReference(codeLengths);
+ ref byte codeLengthsRef = ref this.codeLengths[0];
// Figure C.1: make table of Huffman code length for each symbol
ref short sizesRef = ref this.Sizes[0];
short x = 0;
-
for (short i = 1; i < 17; i++)
{
byte length = Unsafe.Add(ref codeLengthsRef, i);
@@ -91,7 +114,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
}
// Figure F.15: generate decoding tables for bit-sequential decoding.
- // Compute largest code + 1 for this size. preshifted as we needit later.
+ // Compute largest code + 1 for this size. preshifted as we need it later.
Unsafe.Add(ref maxcodeRef, k) = code << (16 - k);
code <<= 1;
}
@@ -100,25 +123,26 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
// Generate non-spec lookup tables to speed up decoding.
const int FastBits = ScanDecoder.FastBits;
- ref byte fastRef = ref this.Lookahead[0];
- Unsafe.InitBlockUnaligned(ref fastRef, 0xFF, 1 << FastBits); // Flag for non-accelerated
+ ref byte lookaheadRef = ref this.Lookahead[0];
+ Unsafe.InitBlockUnaligned(ref lookaheadRef, 0xFF, 1 << FastBits); // Flag for non-accelerated
for (int i = 0; i < si; i++)
{
int size = Unsafe.Add(ref sizesRef, i);
if (size <= FastBits)
{
- int c = Unsafe.Add(ref huffcodeRef, i) << (FastBits - size);
- int m = 1 << (FastBits - size);
- for (int l = 0; l < m; l++)
+ int fastOffset = FastBits - size;
+ int fastCode = Unsafe.Add(ref huffcodeRef, i) << fastOffset;
+ int fastMax = 1 << fastOffset;
+ for (int left = 0; left < fastMax; left++)
{
- Unsafe.Add(ref fastRef, c + l) = (byte)i;
+ Unsafe.Add(ref lookaheadRef, fastCode + left) = (byte)i;
}
}
}
}
- Unsafe.CopyBlockUnaligned(ref this.Values[0], ref MemoryMarshal.GetReference(values), 256);
+ this.isDerived = true;
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTables.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTables.cs
deleted file mode 100644
index dc066aa0ac..0000000000
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTables.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (c) Six Labors and contributors.
-// Licensed under the Apache License, Version 2.0.
-
-using System.Runtime.CompilerServices;
-
-namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
-{
- ///
- /// Defines a 2 pairs of huffman tables.
- ///
- internal sealed class HuffmanTables
- {
- private readonly HuffmanTable[] tables = new HuffmanTable[4];
-
- ///
- /// Gets or sets the table at the given index.
- ///
- /// The index
- /// The
- public ref HuffmanTable this[int index]
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => ref this.tables[index];
- }
- }
-}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/IJpegComponent.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/IJpegComponent.cs
index c033980336..2492a985a8 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/IJpegComponent.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/IJpegComponent.cs
@@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Memory;
-using SixLabors.Memory;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
@@ -43,16 +42,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
///
/// Gets the storing the "raw" frequency-domain decoded + unzigged blocks.
- /// We need to apply IDCT and dequantiazition to transform them into color-space blocks.
+ /// We need to apply IDCT and dequantization to transform them into color-space blocks.
///
Buffer2D SpectralBlocks { get; }
-
- ///
- /// Gets a reference to the at the given row and column index from
- ///
- /// The column
- /// The row
- /// The
- ref Block8x8 GetBlockReference(int column, int row);
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs
index dace78b337..ace8d7215b 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs
@@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
Size ImageSizeInPixels { get; }
///
- /// Gets the number of coponents.
+ /// Gets the number of components.
///
int ComponentCount { get; }
@@ -29,10 +29,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
///
JpegColorSpace ColorSpace { get; }
+ ///
+ /// Gets the number of bits used for precision.
+ ///
+ int Precision { get; }
+
///
/// Gets the components.
///
- IEnumerable Components { get; }
+ IJpegComponent[] Components { get; }
///
/// Gets the quantization tables, in zigzag order.
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs
index f153ce062a..7497c8a409 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs
@@ -2,12 +2,12 @@
// Licensed under the Apache License, Version 2.0.
using System;
-using SixLabors.ImageSharp.MetaData;
+using SixLabors.ImageSharp.Metadata;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
///
- /// Provides information about the JFIF marker segment
+ /// Provides information about the JFIF marker segment.
/// TODO: Thumbnail?
///
internal readonly struct JFifMarker : IEquatable
@@ -20,31 +20,39 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
///
/// Initializes a new instance of the struct.
///
- /// The major version
- /// The minor version
- /// The units for the density values
- /// The horizontal pixel density
- /// The veritcal pixel density
+ /// The major version.
+ /// The minor version.
+ /// The units for the density values.
+ /// The horizontal pixel density.
+ /// The vertical pixel density.
private JFifMarker(byte majorVersion, byte minorVersion, byte densityUnits, short xDensity, short yDensity)
{
- Guard.MustBeGreaterThan(xDensity, 0, nameof(xDensity));
- Guard.MustBeGreaterThan(yDensity, 0, nameof(yDensity));
- Guard.MustBeBetweenOrEqualTo(densityUnits, 0, 2, nameof(densityUnits));
+ if (xDensity <= 0)
+ {
+ JpegThrowHelper.ThrowImageFormatException($"X-Density {xDensity} must be greater than 0.");
+ }
+
+ if (yDensity <= 0)
+ {
+ JpegThrowHelper.ThrowImageFormatException($"Y-Density {yDensity} must be greater than 0.");
+ }
this.MajorVersion = majorVersion;
this.MinorVersion = minorVersion;
+
+ // LibJpeg and co will simply cast and not try to enforce a range.
this.DensityUnits = (PixelResolutionUnit)densityUnits;
this.XDensity = xDensity;
this.YDensity = yDensity;
}
///
- /// Gets the major version
+ /// Gets the major version.
///
public byte MajorVersion { get; }
///
- /// Gets the minor version
+ /// Gets the minor version.
///
public byte MinorVersion { get; }
@@ -70,7 +78,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// Converts the specified byte array representation of an JFIF marker to its equivalent and
/// returns a value that indicates whether the conversion succeeded.
///
- /// The byte array containing metadata to parse
+ /// The byte array containing metadata to parse.
/// The marker to return.
public static bool TryParse(byte[] bytes, out JFifMarker marker)
{
@@ -104,21 +112,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
}
///
- public override bool Equals(object obj)
- {
- return obj is JFifMarker other && this.Equals(other);
- }
+ public override bool Equals(object obj) => obj is JFifMarker other && this.Equals(other);
///
public override int GetHashCode()
{
- return HashHelpers.Combine(
- this.MajorVersion.GetHashCode(),
- HashHelpers.Combine(
- this.MinorVersion.GetHashCode(),
- HashHelpers.Combine(
- this.DensityUnits.GetHashCode(),
- HashHelpers.Combine(this.XDensity, this.YDensity))));
+ return HashCode.Combine(
+ this.MajorVersion,
+ this.MinorVersion,
+ this.DensityUnits,
+ this.XDensity,
+ this.YDensity);
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs
index 0108e30815..c5efb812e0 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs
@@ -1,6 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
+using System;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Memory;
using SixLabors.Primitives;
@@ -19,17 +20,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
public Block8x8F SourceBlock;
///
- /// Temporal block 1 to store intermediate and/or final computation results
+ /// Temporal block 1 to store intermediate and/or final computation results.
///
public Block8x8F WorkspaceBlock1;
///
- /// Temporal block 2 to store intermediate and/or final computation results
+ /// Temporal block 2 to store intermediate and/or final computation results.
///
public Block8x8F WorkspaceBlock2;
///
- /// The quantization table as
+ /// The quantization table as .
///
public Block8x8F DequantiazationTable;
@@ -38,6 +39,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
///
private Size subSamplingDivisors;
+ ///
+ /// Defines the maximum value derived from the bitdepth.
+ ///
+ private readonly int maximumValue;
+
///
/// Initializes a new instance of the struct.
///
@@ -48,6 +54,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
int qtIndex = component.QuantizationTableIndex;
this.DequantiazationTable = ZigZag.CreateDequantizationTable(ref decoder.QuantizationTables[qtIndex]);
this.subSamplingDivisors = component.SubSamplingDivisors;
+ this.maximumValue = (int)MathF.Pow(2, decoder.Precision) - 1;
this.SourceBlock = default;
this.WorkspaceBlock1 = default;
@@ -58,14 +65,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// Processes 'sourceBlock' producing Jpeg color channel values from spectral values:
/// - Dequantize
/// - Applying IDCT
- /// - Level shift by +128, clip to [0, 255]
- /// - Copy the resultin color values into 'destArea' scaling up the block by amount defined in
+ /// - Level shift by +maximumValue/2, clip to [0, maximumValue]
+ /// - Copy the resulting color values into 'destArea' scaling up the block by amount defined in .
///
/// The source block.
/// The destination buffer area.
+ /// The maximum value derived from the bitdepth.
public void ProcessBlockColorsInto(
ref Block8x8 sourceBlock,
- in BufferArea destArea)
+ in BufferArea destArea,
+ float maximumValue)
{
ref Block8x8F b = ref this.SourceBlock;
b.LoadFrom(ref sourceBlock);
@@ -78,7 +87,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
// To conform better to libjpeg we actually NEED TO loose precision here.
// This is because they store blocks as Int16 between all the operations.
// To be "more accurate", we need to emulate this by rounding!
- this.WorkspaceBlock1.NormalizeColorsAndRoundInplace();
+ this.WorkspaceBlock1.NormalizeColorsAndRoundInplace(maximumValue);
this.WorkspaceBlock1.CopyTo(destArea, this.subSamplingDivisors.Width, this.subSamplingDivisors.Height);
}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegColorSpace.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegColorSpace.cs
index 2861a2c2e9..aa33744adf 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegColorSpace.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegColorSpace.cs
@@ -4,7 +4,7 @@
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
///
- /// Identifies the colorspace of a Jpeg image
+ /// Identifies the colorspace of a Jpeg image.
///
internal enum JpegColorSpace
{
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs
index 65a584c4f2..5353303947 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs
@@ -2,8 +2,6 @@
// Licensed under the Apache License, Version 2.0.
using System;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Memory;
using SixLabors.Memory;
@@ -12,9 +10,9 @@ using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
///
- /// Represents a single frame component
+ /// Represents a single frame component.
///
- internal class JpegComponent : IDisposable, IJpegComponent
+ internal sealed class JpegComponent : IDisposable, IJpegComponent
{
private readonly MemoryAllocator memoryAllocator;
@@ -23,20 +21,36 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
this.memoryAllocator = memoryAllocator;
this.Frame = frame;
this.Id = id;
+
+ // Valid sampling factors are 1..2
+ if (horizontalFactor == 0
+ || verticalFactor == 0
+ || horizontalFactor > 2
+ || verticalFactor > 2)
+ {
+ JpegThrowHelper.ThrowBadSampling();
+ }
+
this.HorizontalSamplingFactor = horizontalFactor;
this.VerticalSamplingFactor = verticalFactor;
this.SamplingFactors = new Size(this.HorizontalSamplingFactor, this.VerticalSamplingFactor);
+
+ if (quantizationTableIndex > 3)
+ {
+ JpegThrowHelper.ThrowBadQuantizationTable();
+ }
+
this.QuantizationTableIndex = quantizationTableIndex;
this.Index = index;
}
///
- /// Gets the component Id
+ /// Gets the component id.
///
public byte Id { get; }
///
- /// Gets or sets DC coefficient predictor
+ /// Gets or sets DC coefficient predictor.
///
public int DcPredictor { get; set; }
@@ -69,22 +83,22 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
public Size SamplingFactors { get; set; }
///
- /// Gets the number of blocks per line
+ /// Gets the number of blocks per line.
///
public int WidthInBlocks { get; private set; }
///
- /// Gets the number of blocks per column
+ /// Gets the number of blocks per column.
///
public int HeightInBlocks { get; private set; }
///
- /// Gets or sets the index for the DC Huffman table
+ /// Gets or sets the index for the DC Huffman table.
///
public int DCHuffmanTableId { get; set; }
///
- /// Gets or sets the index for the AC Huffman table
+ /// Gets or sets the index for the AC Huffman table.
///
public int ACHuffmanTableId { get; set; }
@@ -109,40 +123,19 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
int blocksPerColumnForMcu = this.Frame.McusPerColumn * this.VerticalSamplingFactor;
this.SizeInBlocks = new Size(blocksPerLineForMcu, blocksPerColumnForMcu);
- // For 4-component images (either CMYK or YCbCrK), we only support two
- // hv vectors: [0x11 0x11 0x11 0x11] and [0x22 0x11 0x11 0x22].
- // Theoretically, 4-component JPEG images could mix and match hv values
- // but in practice, those two combinations are the only ones in use,
- // and it simplifies the applyBlack code below if we can assume that:
- // - for CMYK, the C and K channels have full samples, and if the M
- // and Y channels subsample, they subsample both horizontally and
- // vertically.
- // - for YCbCrK, the Y and K channels have full samples.
- if (this.Index == 0 || this.Index == 3)
- {
- this.SubSamplingDivisors = new Size(1, 1);
- }
- else
+ JpegComponent c0 = this.Frame.Components[0];
+ this.SubSamplingDivisors = c0.SamplingFactors.DivideBy(this.SamplingFactors);
+
+ if (this.SubSamplingDivisors.Width == 0 || this.SubSamplingDivisors.Height == 0)
{
- JpegComponent c0 = this.Frame.Components[0];
- this.SubSamplingDivisors = c0.SamplingFactors.DivideBy(this.SamplingFactors);
+ JpegThrowHelper.ThrowBadSampling();
}
- this.SpectralBlocks = this.memoryAllocator.Allocate2D(blocksPerColumnForMcu, blocksPerLineForMcu + 1, AllocationOptions.Clean);
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public ref Block8x8 GetBlockReference(int column, int row)
- {
- int offset = ((this.WidthInBlocks + 1) * row) + column;
- return ref Unsafe.Add(ref MemoryMarshal.GetReference(this.SpectralBlocks.GetSpan()), offset);
- }
+ int totalNumberOfBlocks = blocksPerColumnForMcu * (blocksPerLineForMcu + 1);
+ int width = this.WidthInBlocks + 1;
+ int height = totalNumberOfBlocks / width;
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public ref short GetBlockDataReference(int column, int row)
- {
- ref Block8x8 blockRef = ref this.GetBlockReference(column, row);
- return ref Unsafe.As(ref blockRef);
+ this.SpectralBlocks = this.memoryAllocator.Allocate2D(width, height, AllocationOptions.Clean);
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs
index 890f402595..e1a9380a03 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs
@@ -2,6 +2,8 @@
// Licensed under the Apache License, Version 2.0.
using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Memory;
using SixLabors.Memory;
@@ -20,7 +22,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
private int currentComponentRowInBlocks;
///
- /// The size of the area in corrsponding to one 8x8 Jpeg block
+ /// The size of the area in corresponding to one 8x8 Jpeg block
///
private readonly Size blockAreaSize;
@@ -76,6 +78,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
public void CopyBlocksToColorBuffer()
{
var blockPp = new JpegBlockPostProcessor(this.ImagePostProcessor.RawJpeg, this.Component);
+ float maximumValue = MathF.Pow(2, this.ImagePostProcessor.RawJpeg.Precision) - 1;
for (int y = 0; y < this.BlockRowsPerStep; y++)
{
@@ -88,12 +91,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
int yBuffer = y * this.blockAreaSize.Height;
- for (int x = 0; x < this.SizeInBlocks.Width; x++)
- {
- int xBlock = x;
- int xBuffer = x * this.blockAreaSize.Width;
+ Span blockRow = this.Component.SpectralBlocks.GetRowSpan(yBlock);
+
+ ref Block8x8 blockRowBase = ref MemoryMarshal.GetReference(blockRow);
- ref Block8x8 block = ref this.Component.GetBlockReference(xBlock, yBlock);
+ for (int xBlock = 0; xBlock < this.SizeInBlocks.Width; xBlock++)
+ {
+ ref Block8x8 block = ref Unsafe.Add(ref blockRowBase, xBlock);
+ int xBuffer = xBlock * this.blockAreaSize.Width;
BufferArea destArea = this.ColorBuffer.GetArea(
xBuffer,
@@ -101,7 +106,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
this.blockAreaSize.Width,
this.blockAreaSize.Height);
- blockPp.ProcessBlockColorsInto(ref block, destArea);
+ blockPp.ProcessBlockColorsInto(ref block, destArea, maximumValue);
}
}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs
index 36a3dc2d26..f426eb1b15 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs
@@ -11,68 +11,68 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
internal sealed class JpegFrame : IDisposable
{
///
- /// Gets or sets a value indicating whether the frame uses the extended specification
+ /// Gets or sets a value indicating whether the frame uses the extended specification.
///
public bool Extended { get; set; }
///
- /// Gets or sets a value indicating whether the frame uses the progressive specification
+ /// Gets or sets a value indicating whether the frame uses the progressive specification.
///
public bool Progressive { get; set; }
///
- /// Gets or sets the precision
+ /// Gets or sets the precision.
///
public byte Precision { get; set; }
///
- /// Gets or sets the number of scanlines within the frame
+ /// Gets or sets the number of scanlines within the frame.
///
public short Scanlines { get; set; }
///
- /// Gets or sets the number of samples per scanline
+ /// Gets or sets the number of samples per scanline.
///
public short SamplesPerLine { get; set; }
///
- /// Gets or sets the number of components within a frame. In progressive frames this value can range from only 1 to 4
+ /// Gets or sets the number of components within a frame. In progressive frames this value can range from only 1 to 4.
///
public byte ComponentCount { get; set; }
///
- /// Gets or sets the component id collection
+ /// Gets or sets the component id collection.
///
public byte[] ComponentIds { get; set; }
///
- /// Gets or sets the order in which to process the components
+ /// Gets or sets the order in which to process the components.
/// in interleaved mode.
///
public byte[] ComponentOrder { get; set; }
///
- /// Gets or sets the frame component collection
+ /// Gets or sets the frame component collection.
///
public JpegComponent[] Components { get; set; }
///
- /// Gets or sets the maximum horizontal sampling factor
+ /// Gets or sets the maximum horizontal sampling factor.
///
public int MaxHorizontalFactor { get; set; }
///
- /// Gets or sets the maximum vertical sampling factor
+ /// Gets or sets the maximum vertical sampling factor.
///
public int MaxVerticalFactor { get; set; }
///
- /// Gets or sets the number of MCU's per line
+ /// Gets or sets the number of MCU's per line.
///
public int McusPerLine { get; set; }
///
- /// Gets or sets the number of MCU's per column
+ /// Gets or sets the number of MCU's per column.
///
public int McusPerColumn { get; set; }
@@ -83,7 +83,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
for (int i = 0; i < this.Components.Length; i++)
{
- this.Components[i].Dispose();
+ this.Components[i]?.Dispose();
}
this.Components = null;
@@ -91,7 +91,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
}
///
- /// Allocates the frame component blocks
+ /// Allocates the frame component blocks.
///
public void InitComponents()
{
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs
index 6bd287732d..2c81908843 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs
@@ -3,7 +3,6 @@
using System;
using System.Buffers;
-using System.Linq;
using System.Numerics;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
@@ -26,6 +25,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
///
internal class JpegImagePostProcessor : IDisposable
{
+ private readonly Configuration configuration;
+
///
/// The number of block rows to be processed in one Step.
///
@@ -49,18 +50,26 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
///
/// Initializes a new instance of the class.
///
- /// The to use for buffer allocations.
+ /// The to configure internal operations.
/// The representing the uncompressed spectral Jpeg data
- public JpegImagePostProcessor(MemoryAllocator memoryAllocator, IRawJpegData rawJpeg)
+ public JpegImagePostProcessor(Configuration configuration, IRawJpegData rawJpeg)
{
+ this.configuration = configuration;
this.RawJpeg = rawJpeg;
- IJpegComponent c0 = rawJpeg.Components.First();
+ IJpegComponent c0 = rawJpeg.Components[0];
this.NumberOfPostProcessorSteps = c0.SizeInBlocks.Height / BlockRowsPerStep;
this.PostProcessorBufferSize = new Size(c0.SizeInBlocks.Width * 8, PixelRowsPerStep);
- this.ComponentProcessors = rawJpeg.Components.Select(c => new JpegComponentPostProcessor(memoryAllocator, this, c)).ToArray();
+ MemoryAllocator memoryAllocator = configuration.MemoryAllocator;
+
+ this.ComponentProcessors = new JpegComponentPostProcessor[rawJpeg.Components.Length];
+ for (int i = 0; i < rawJpeg.Components.Length; i++)
+ {
+ this.ComponentProcessors[i] = new JpegComponentPostProcessor(memoryAllocator, this, rawJpeg.Components[i]);
+ }
+
this.rgbaBuffer = memoryAllocator.Allocate(rawJpeg.ImageSizeInPixels.Width);
- this.colorConverter = JpegColorConverter.GetConverter(rawJpeg.ColorSpace);
+ this.colorConverter = JpegColorConverter.GetConverter(rawJpeg.ColorSpace, rawJpeg.Precision);
}
///
@@ -148,7 +157,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
int maxY = Math.Min(destination.Height, this.PixelRowCounter + PixelRowsPerStep);
- Buffer2D[] buffers = this.ComponentProcessors.Select(cp => cp.ColorBuffer).ToArray();
+ var buffers = new Buffer2D[this.ComponentProcessors.Length];
+ for (int i = 0; i < this.ComponentProcessors.Length; i++)
+ {
+ buffers[i] = this.ComponentProcessors[i].ColorBuffer;
+ }
for (int yy = this.PixelRowCounter; yy < maxY; yy++)
{
@@ -160,7 +173,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
Span destRow = destination.GetPixelRowSpan(yy);
// TODO: Investigate if slicing is actually necessary
- PixelOperations.Instance.FromVector4(this.rgbaBuffer.GetSpan().Slice(0, destRow.Length), destRow);
+ PixelOperations.Instance.FromVector4Destructive(this.configuration, this.rgbaBuffer.GetSpan().Slice(0, destRow.Length), destRow);
}
}
}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ProfileResolver.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ProfileResolver.cs
index 3e7108b151..abfd6f556c 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ProfileResolver.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ProfileResolver.cs
@@ -12,30 +12,30 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
internal static class ProfileResolver
{
///
- /// Describes the EXIF specific markers
+ /// Describes the EXIF specific markers.
///
public static readonly byte[] JFifMarker = Encoding.ASCII.GetBytes("JFIF\0");
///
- /// Describes the EXIF specific markers
+ /// Describes the EXIF specific markers.
///
public static readonly byte[] IccMarker = Encoding.ASCII.GetBytes("ICC_PROFILE\0");
///
- /// Describes the ICC specific markers
+ /// Describes the ICC specific markers.
///
public static readonly byte[] ExifMarker = Encoding.ASCII.GetBytes("Exif\0\0");
///
- /// Describes Adobe specific markers
+ /// Describes Adobe specific markers .
///
public static readonly byte[] AdobeMarker = Encoding.ASCII.GetBytes("Adobe");
///
- /// Returns a value indicating whether the passed bytes are a match to the profile identifier
+ /// Returns a value indicating whether the passed bytes are a match to the profile identifier.
///
- /// The bytes to check
- /// The profile identifier
+ /// The bytes to check.
+ /// The profile identifier.
/// The
public static bool IsProfile(ReadOnlySpan bytesToCheck, ReadOnlySpan profileIdentifier)
{
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/QualityEvaluator.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/QualityEvaluator.cs
index 4e11568c8d..a7d2a0fde9 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/QualityEvaluator.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/QualityEvaluator.cs
@@ -84,9 +84,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
for (int i = 0; i < quantizationTables.Length; i++)
{
ref Block8x8F qTable = ref quantizationTables[i];
- for (int j = 0; j < Block8x8F.Size; j++)
+
+ if (!qTable.Equals(default))
{
- sum += qTable[j];
+ for (int j = 0; j < Block8x8F.Size; j++)
+ {
+ sum += qTable[j];
+ }
}
}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs
index 351e453484..ef4b359ff2 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs
@@ -1,8 +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;
using SixLabors.ImageSharp.IO;
+using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
@@ -23,9 +26,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
private static readonly int[] Bias = { 0, -1, -3, -7, -15, -31, -63, -127, -255, -511, -1023, -2047, -4095, -8191, -16383, -32767 };
private readonly JpegFrame frame;
- private readonly HuffmanTables dcHuffmanTables;
- private readonly HuffmanTables acHuffmanTables;
- private readonly FastACTables fastACTables;
+ private readonly HuffmanTable[] dcHuffmanTables;
+ private readonly HuffmanTable[] acHuffmanTables;
+ private readonly FastACTable[] fastACTables;
private readonly DoubleBufferedStreamReader stream;
private readonly JpegComponent[] components;
@@ -93,9 +96,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
public ScanDecoder(
DoubleBufferedStreamReader stream,
JpegFrame frame,
- HuffmanTables dcHuffmanTables,
- HuffmanTables acHuffmanTables,
- FastACTables fastACTables,
+ HuffmanTable[] dcHuffmanTables,
+ HuffmanTable[] acHuffmanTables,
+ FastACTable[] fastACTables,
int componentsLength,
int restartInterval,
int spectralStart,
@@ -142,7 +145,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
}
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
private static uint LRot(uint x, int y) => (x << y) | (x >> (32 - y));
private void ParseBaselineData()
@@ -157,17 +160,35 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
}
}
- private void ParseBaselineDataInterleaved()
+ private unsafe void ParseBaselineDataInterleaved()
{
// Interleaved
int mcu = 0;
int mcusPerColumn = this.frame.McusPerColumn;
int mcusPerLine = this.frame.McusPerLine;
+
+ // Pre-derive the huffman table to avoid in-loop checks.
+ for (int i = 0; i < this.componentsLength; i++)
+ {
+ int order = this.frame.ComponentOrder[i];
+ JpegComponent component = this.components[order];
+
+ ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId];
+ ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId];
+ dcHuffmanTable.Derive();
+ acHuffmanTable.Derive();
+
+ ref FastACTable fastAcTable = ref this.fastACTables[component.ACHuffmanTableId];
+ fastAcTable.Derive(ref acHuffmanTable);
+ }
+
for (int j = 0; j < mcusPerColumn; j++)
{
for (int i = 0; i < mcusPerLine; i++)
{
// Scan an interleaved mcu... process components in order
+ int mcuRow = mcu / mcusPerLine;
+ int mcuCol = mcu % mcusPerLine;
for (int k = 0; k < this.componentsLength; k++)
{
int order = this.frame.ComponentOrder[k];
@@ -175,7 +196,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId];
ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId];
- ref short fastACRef = ref this.fastACTables.GetAcTableReference(component);
+ ref FastACTable fastAcTable = ref this.fastACTables[component.ACHuffmanTableId];
+ ref short fastACRef = ref fastAcTable.Lookahead[0];
+
int h = component.HorizontalSamplingFactor;
int v = component.VerticalSamplingFactor;
@@ -183,6 +206,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
// by the basic H and V specified for the component
for (int y = 0; y < v; y++)
{
+ int blockRow = (mcuRow * v) + y;
+ Span blockSpan = component.SpectralBlocks.GetRowSpan(blockRow);
+ ref Block8x8 blockRef = ref MemoryMarshal.GetReference(blockSpan);
+
for (int x = 0; x < h; x++)
{
if (this.eof)
@@ -190,15 +217,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
return;
}
- int mcuRow = mcu / mcusPerLine;
- int mcuCol = mcu % mcusPerLine;
- int blockRow = (mcuRow * v) + y;
int blockCol = (mcuCol * h) + x;
this.DecodeBlockBaseline(
component,
- blockRow,
- blockCol,
+ ref Unsafe.Add(ref blockRef, blockCol),
ref dcHuffmanTable,
ref acHuffmanTable,
ref fastACRef);
@@ -222,7 +245,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// number of blocks to do just depends on how many actual "pixels" each component has,
/// independent of interleaved MCU blocking and such.
///
- private void ParseBaselineDataNonInterleaved()
+ private unsafe void ParseBaselineDataNonInterleaved()
{
JpegComponent component = this.components[this.frame.ComponentOrder[0]];
@@ -231,11 +254,20 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId];
ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId];
- ref short fastACRef = ref this.fastACTables.GetAcTableReference(component);
+ dcHuffmanTable.Derive();
+ acHuffmanTable.Derive();
+
+ ref FastACTable fastAcTable = ref this.fastACTables[component.ACHuffmanTableId];
+ fastAcTable.Derive(ref acHuffmanTable);
+ ref short fastACRef = ref fastAcTable.Lookahead[0];
int mcu = 0;
for (int j = 0; j < h; j++)
{
+ int blockRow = j;
+ Span blockSpan = component.SpectralBlocks.GetRowSpan(j);
+ ref Block8x8 blockRef = ref MemoryMarshal.GetReference(blockSpan);
+
for (int i = 0; i < w; i++)
{
if (this.eof)
@@ -243,13 +275,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
return;
}
- int blockRow = mcu / w;
- int blockCol = mcu % w;
-
this.DecodeBlockBaseline(
component,
- blockRow,
- blockCol,
+ ref Unsafe.Add(ref blockRef, i),
ref dcHuffmanTable,
ref acHuffmanTable,
ref fastACRef);
@@ -264,8 +292,60 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
}
}
+ private void CheckProgressiveData()
+ {
+ // Validate successive scan parameters.
+ // Logic has been adapted from libjpeg.
+ // See Table B.3 – Scan header parameter size and values. itu-t81.pdf
+ bool invalid = false;
+ if (this.spectralStart == 0)
+ {
+ if (this.spectralEnd != 0)
+ {
+ invalid = true;
+ }
+ }
+ else
+ {
+ // Need not check Ss/Se < 0 since they came from unsigned bytes.
+ if (this.spectralEnd < this.spectralStart || this.spectralEnd > 63)
+ {
+ invalid = true;
+ }
+
+ // AC scans may have only one component.
+ if (this.componentsLength != 1)
+ {
+ invalid = true;
+ }
+ }
+
+ if (this.successiveHigh != 0)
+ {
+ // Successive approximation refinement scan: must have Al = Ah-1.
+ if (this.successiveHigh - 1 != this.successiveLow)
+ {
+ invalid = true;
+ }
+ }
+
+ // TODO: How does this affect 12bit jpegs.
+ // According to libjpeg the range covers 8bit only?
+ if (this.successiveLow > 13)
+ {
+ invalid = true;
+ }
+
+ if (invalid)
+ {
+ JpegThrowHelper.ThrowBadProgressiveScan(this.spectralStart, this.spectralEnd, this.successiveHigh, this.successiveLow);
+ }
+ }
+
private void ParseProgressiveData()
{
+ this.CheckProgressiveData();
+
if (this.componentsLength == 1)
{
this.ParseProgressiveDataNonInterleaved();
@@ -282,16 +362,29 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
int mcu = 0;
int mcusPerColumn = this.frame.McusPerColumn;
int mcusPerLine = this.frame.McusPerLine;
+
+ // Pre-derive the huffman table to avoid in-loop checks.
+ for (int k = 0; k < this.componentsLength; k++)
+ {
+ int order = this.frame.ComponentOrder[k];
+ JpegComponent component = this.components[order];
+ ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId];
+ dcHuffmanTable.Derive();
+ }
+
for (int j = 0; j < mcusPerColumn; j++)
{
for (int i = 0; i < mcusPerLine; i++)
{
// Scan an interleaved mcu... process components in order
+ int mcuRow = mcu / mcusPerLine;
+ int mcuCol = mcu % mcusPerLine;
for (int k = 0; k < this.componentsLength; k++)
{
int order = this.frame.ComponentOrder[k];
JpegComponent component = this.components[order];
ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId];
+
int h = component.HorizontalSamplingFactor;
int v = component.VerticalSamplingFactor;
@@ -299,6 +392,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
// by the basic H and V specified for the component
for (int y = 0; y < v; y++)
{
+ int blockRow = (mcuRow * v) + y;
+ Span blockSpan = component.SpectralBlocks.GetRowSpan(blockRow);
+ ref Block8x8 blockRef = ref MemoryMarshal.GetReference(blockSpan);
+
for (int x = 0; x < h; x++)
{
if (this.eof)
@@ -306,15 +403,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
return;
}
- int mcuRow = mcu / mcusPerLine;
- int mcuCol = mcu % mcusPerLine;
- int blockRow = (mcuRow * v) + y;
int blockCol = (mcuCol * h) + x;
this.DecodeBlockProgressiveDC(
component,
- blockRow,
- blockCol,
+ ref Unsafe.Add(ref blockRef, blockCol),
ref dcHuffmanTable);
}
}
@@ -337,53 +430,78 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// number of blocks to do just depends on how many actual "pixels" this
/// component has, independent of interleaved MCU blocking and such
///
- private void ParseProgressiveDataNonInterleaved()
+ private unsafe void ParseProgressiveDataNonInterleaved()
{
JpegComponent component = this.components[this.frame.ComponentOrder[0]];
int w = component.WidthInBlocks;
int h = component.HeightInBlocks;
- ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId];
- ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId];
- ref short fastACRef = ref this.fastACTables.GetAcTableReference(component);
-
- int mcu = 0;
- for (int j = 0; j < h; j++)
+ if (this.spectralStart == 0)
{
- for (int i = 0; i < w; i++)
- {
- if (this.eof)
- {
- return;
- }
+ ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId];
+ dcHuffmanTable.Derive();
- int blockRow = mcu / w;
- int blockCol = mcu % w;
+ int mcu = 0;
+ for (int j = 0; j < h; j++)
+ {
+ Span blockSpan = component.SpectralBlocks.GetRowSpan(j);
+ ref Block8x8 blockRef = ref MemoryMarshal.GetReference(blockSpan);
- if (this.spectralStart == 0)
+ for (int i = 0; i < w; i++)
{
+ if (this.eof)
+ {
+ return;
+ }
+
this.DecodeBlockProgressiveDC(
component,
- blockRow,
- blockCol,
+ ref Unsafe.Add(ref blockRef, i),
ref dcHuffmanTable);
+
+ // Every data block is an MCU, so countdown the restart interval
+ mcu++;
+ if (!this.ContinueOnMcuComplete())
+ {
+ return;
+ }
}
- else
+ }
+ }
+ else
+ {
+ ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId];
+ acHuffmanTable.Derive();
+
+ ref FastACTable fastAcTable = ref this.fastACTables[component.ACHuffmanTableId];
+ fastAcTable.Derive(ref acHuffmanTable);
+ ref short fastACRef = ref fastAcTable.Lookahead[0];
+
+ int mcu = 0;
+ for (int j = 0; j < h; j++)
+ {
+ Span blockSpan = component.SpectralBlocks.GetRowSpan(j);
+ ref Block8x8 blockRef = ref MemoryMarshal.GetReference(blockSpan);
+
+ for (int i = 0; i < w; i++)
{
+ if (this.eof)
+ {
+ return;
+ }
+
this.DecodeBlockProgressiveAC(
- component,
- blockRow,
- blockCol,
+ ref Unsafe.Add(ref blockRef, i),
ref acHuffmanTable,
ref fastACRef);
- }
- // Every data block is an MCU, so countdown the restart interval
- mcu++;
- if (!this.ContinueOnMcuComplete())
- {
- return;
+ // Every data block is an MCU, so countdown the restart interval
+ mcu++;
+ if (!this.ContinueOnMcuComplete())
+ {
+ return;
+ }
}
}
}
@@ -391,8 +509,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
private void DecodeBlockBaseline(
JpegComponent component,
- int row,
- int col,
+ ref Block8x8 block,
ref HuffmanTable dcTable,
ref HuffmanTable acTable,
ref short fastACRef)
@@ -405,7 +522,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
JpegThrowHelper.ThrowBadHuffmanCode();
}
- ref short blockDataRef = ref component.GetBlockDataReference(col, row);
+ ref short blockDataRef = ref Unsafe.As(ref block);
int diff = t != 0 ? this.ExtendReceive(t) : 0;
int dc = component.DcPredictor + diff;
@@ -465,23 +582,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
Unsafe.Add(ref blockDataRef, zig) = (short)this.ExtendReceive(s);
}
}
- } while (k < 64);
+ }
+ while (k < 64);
}
private void DecodeBlockProgressiveDC(
JpegComponent component,
- int row,
- int col,
+ ref Block8x8 block,
ref HuffmanTable dcTable)
{
- if (this.spectralEnd != 0)
- {
- JpegThrowHelper.ThrowImageFormatException("Can't merge DC and AC.");
- }
-
this.CheckBits();
- ref short blockDataRef = ref component.GetBlockDataReference(col, row);
+ ref short blockDataRef = ref Unsafe.As(ref block);
if (this.successiveHigh == 0)
{
@@ -505,18 +617,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
}
private void DecodeBlockProgressiveAC(
- JpegComponent component,
- int row,
- int col,
+ ref Block8x8 block,
ref HuffmanTable acTable,
ref short fastACRef)
{
- if (this.spectralStart == 0)
- {
- JpegThrowHelper.ThrowImageFormatException("Can't merge DC and AC.");
- }
-
- ref short blockDataRef = ref component.GetBlockDataReference(col, row);
+ ref short blockDataRef = ref Unsafe.As(ref block);
if (this.successiveHigh == 0)
{
@@ -749,7 +854,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
[MethodImpl(InliningOptions.ColdPath)]
private void FillBuffer()
{
- // Attempt to load at least the minimum nbumber of required bits into the buffer.
+ // Attempt to load at least the minimum number of required bits into the buffer.
// We fail to do so only if we hit a marker or reach the end of the input stream.
do
{
@@ -906,7 +1011,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
}
// If it's NOT a restart, then just bail, so we get corrupt data rather than no data.
- // Reset the stream to before any bad markers to ensure we can read sucessive segments.
+ // Reset the stream to before any bad markers to ensure we can read successive segments.
if (this.badMarker)
{
this.stream.Position = this.markerPosition;
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrTables.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrTables.cs
index a0cc9ee8e5..cb0810985e 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrTables.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrTables.cs
@@ -63,7 +63,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
///
/// Initializes the YCbCr tables
///
- /// The intialized
+ /// The initialized
public static RgbToYCbCrTables Create()
{
RgbToYCbCrTables tables = default;
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs
index b2a8fccf4a..301079b6ae 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs
@@ -1,4 +1,7 @@
-using System;
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Advanced;
@@ -53,12 +56,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
///
/// Converts a 8x8 image area inside 'pixels' at position (x,y) placing the result members of the structure (, , )
///
- public void Convert(IPixelSource pixels, int x, int y)
+ public void Convert(ImageFrame frame, int x, int y)
{
- this.pixelBlock.LoadAndStretchEdges(pixels, x, y);
+ this.pixelBlock.LoadAndStretchEdges(frame, x, y);
Span rgbSpan = this.rgbBlock.AsSpanUnsafe();
- PixelOperations.Instance.ToRgb24(this.pixelBlock.AsSpanUnsafe(), rgbSpan);
+ PixelOperations.Instance.ToRgb24(frame.Configuration, this.pixelBlock.AsSpanUnsafe(), rgbSpan);
ref float yBlockStart = ref Unsafe.As(ref this.Y);
ref float cbBlockStart = ref Unsafe.As(ref this.Cb);
diff --git a/src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs b/src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs
index 2e20da266b..c795ccc8b5 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs
@@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
///
/// FOR TESTING ONLY!
- /// Gets or sets a value in a row+coulumn of the 8x8 block
+ /// Gets or sets a value in a row+column of the 8x8 block
///
/// The x position index in the row
/// The column index
diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
index 22d9cbdee4..6d6983f191 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
@@ -3,7 +3,6 @@
using System;
using System.Buffers.Binary;
-using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
@@ -13,9 +12,9 @@ using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
-using SixLabors.ImageSharp.MetaData;
-using SixLabors.ImageSharp.MetaData.Profiles.Exif;
-using SixLabors.ImageSharp.MetaData.Profiles.Icc;
+using SixLabors.ImageSharp.Metadata;
+using SixLabors.ImageSharp.Metadata.Profiles.Exif;
+using SixLabors.ImageSharp.Metadata.Profiles.Icc;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Primitives;
using SixLabors.Memory;
@@ -33,7 +32,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
///
/// The only supported precision
///
- public const int SupportedPrecision = 8;
+ private readonly int[] supportedPrecisions = { 8, 12 };
///
/// The global configuration
@@ -51,19 +50,19 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
private readonly byte[] markerBuffer = new byte[2];
///
- /// The DC HUffman tables
+ /// The DC Huffman tables
///
- private HuffmanTables dcHuffmanTables;
+ private HuffmanTable[] dcHuffmanTables;
///
- /// The AC HUffman tables
+ /// The AC Huffman tables
///
- private HuffmanTables acHuffmanTables;
+ private HuffmanTable[] acHuffmanTables;
///
/// The fast AC tables used for entropy decoding
///
- private FastACTables fastACTables;
+ private FastACTable[] fastACTables;
///
/// The reset interval determined by RST markers
@@ -137,7 +136,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
///
/// Gets the color depth, in number of bits per pixel.
///
- public int BitsPerPixel => this.ComponentCount * SupportedPrecision;
+ public int BitsPerPixel => this.ComponentCount * this.Frame.Precision;
///
/// Gets the input stream.
@@ -150,9 +149,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
public bool IgnoreMetadata { get; }
///
- /// Gets the decoded by this decoder instance.
+ /// Gets the decoded by this decoder instance.
///
- public ImageMetaData MetaData { get; private set; }
+ public ImageMetadata Metadata { get; private set; }
///
public int ComponentCount { get; private set; }
@@ -160,13 +159,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
///
public JpegColorSpace ColorSpace { get; private set; }
+ ///
+ public int Precision { get; private set; }
+
///
/// Gets the components.
///
public JpegComponent[] Components => this.Frame.Components;
///
- IEnumerable IRawJpegData.Components => this.Components;
+ IJpegComponent[] IRawJpegData.Components => this.Components;
///
public Block8x8F[] QuantizationTables { get; private set; }
@@ -220,7 +222,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
this.ParseStream(stream);
this.InitExifProfile();
this.InitIccProfile();
- this.InitDerivedMetaDataProperties();
+ this.InitDerivedMetadataProperties();
return this.PostProcessIntoImage();
}
@@ -233,9 +235,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
this.ParseStream(stream, true);
this.InitExifProfile();
this.InitIccProfile();
- this.InitDerivedMetaDataProperties();
+ this.InitDerivedMetadataProperties();
- return new ImageInfo(new PixelTypeInfo(this.BitsPerPixel), this.ImageWidth, this.ImageHeight, this.MetaData);
+ return new ImageInfo(new PixelTypeInfo(this.BitsPerPixel), this.ImageWidth, this.ImageHeight, this.Metadata);
}
///
@@ -245,7 +247,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// Whether to decode metadata only.
public void ParseStream(Stream stream, bool metadataOnly = false)
{
- this.MetaData = new ImageMetaData();
+ this.Metadata = new ImageMetadata();
this.InputStream = new DoubleBufferedStreamReader(this.configuration.MemoryAllocator, stream);
// Check for the Start Of Image marker.
@@ -253,7 +255,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
var fileMarker = new JpegFileMarker(this.markerBuffer[1], 0);
if (fileMarker.Marker != JpegConstants.Markers.SOI)
{
- throw new ImageFormatException("Missing SOI marker.");
+ JpegThrowHelper.ThrowImageFormatException("Missing SOI marker.");
}
this.InputStream.Read(this.markerBuffer, 0, 2);
@@ -264,9 +266,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
// Only assign what we need
if (!metadataOnly)
{
- this.dcHuffmanTables = new HuffmanTables();
- this.acHuffmanTables = new HuffmanTables();
- this.fastACTables = new FastACTables(this.configuration.MemoryAllocator);
+ const int maxTables = 4;
+ this.dcHuffmanTables = new HuffmanTable[maxTables];
+ this.acHuffmanTables = new HuffmanTable[maxTables];
+ this.fastACTables = new FastACTable[maxTables];
}
// Break only when we discover a valid EOI marker.
@@ -376,7 +379,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
{
this.InputStream?.Dispose();
this.Frame?.Dispose();
- this.fastACTables?.Dispose();
// Set large fields to null.
this.InputStream = null;
@@ -399,15 +401,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
if (this.ComponentCount == 3)
{
- if (this.adobe.Equals(default) || this.adobe.ColorTransform == JpegConstants.Adobe.ColorTransformYCbCr)
- {
- return JpegColorSpace.YCbCr;
- }
-
- if (this.adobe.ColorTransform == JpegConstants.Adobe.ColorTransformUnknown)
+ if (!this.adobe.Equals(default) && this.adobe.ColorTransform == JpegConstants.Adobe.ColorTransformUnknown)
{
return JpegColorSpace.RGB;
}
+
+ // Some images are poorly encoded and contain incorrect colorspace transform metadata.
+ // We ignore that and always fall back to the default colorspace.
+ return JpegColorSpace.YCbCr;
}
if (this.ComponentCount == 4)
@@ -417,7 +418,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
: JpegColorSpace.Cmyk;
}
- throw new ImageFormatException($"Unsupported color mode. Max components 4; found {this.ComponentCount}");
+ JpegThrowHelper.ThrowImageFormatException($"Unsupported color mode. Supported component counts 1, 3, and 4; found {this.ComponentCount}");
+ return default;
}
///
@@ -427,7 +429,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
{
if (this.isExif)
{
- this.MetaData.ExifProfile = new ExifProfile(this.exifData);
+ this.Metadata.ExifProfile = new ExifProfile(this.exifData);
}
}
@@ -441,21 +443,21 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
var profile = new IccProfile(this.iccData);
if (profile.CheckIsValid())
{
- this.MetaData.IccProfile = profile;
+ this.Metadata.IccProfile = profile;
}
}
}
///
- /// Assigns derived metadata properties to , eg. horizontal and vertical resolution if it has a JFIF header.
+ /// Assigns derived metadata properties to , eg. horizontal and vertical resolution if it has a JFIF header.
///
- private void InitDerivedMetaDataProperties()
+ private void InitDerivedMetadataProperties()
{
if (this.jFif.XDensity > 0 && this.jFif.YDensity > 0)
{
- this.MetaData.HorizontalResolution = this.jFif.XDensity;
- this.MetaData.VerticalResolution = this.jFif.YDensity;
- this.MetaData.ResolutionUnits = this.jFif.DensityUnits;
+ this.Metadata.HorizontalResolution = this.jFif.XDensity;
+ this.Metadata.VerticalResolution = this.jFif.YDensity;
+ this.Metadata.ResolutionUnits = this.jFif.DensityUnits;
}
else if (this.isExif)
{
@@ -464,16 +466,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
if (horizontalValue > 0 && verticalValue > 0)
{
- this.MetaData.HorizontalResolution = horizontalValue;
- this.MetaData.VerticalResolution = verticalValue;
- this.MetaData.ResolutionUnits = UnitConverter.ExifProfileToResolutionUnit(this.MetaData.ExifProfile);
+ this.Metadata.HorizontalResolution = horizontalValue;
+ this.Metadata.VerticalResolution = verticalValue;
+ this.Metadata.ResolutionUnits = UnitConverter.ExifProfileToResolutionUnit(this.Metadata.ExifProfile);
}
}
}
private double GetExifResolutionValue(ExifTag tag)
{
- if (!this.MetaData.ExifProfile.TryGetValue(tag, out ExifValue exifValue))
+ if (!this.Metadata.ExifProfile.TryGetValue(tag, out ExifValue exifValue))
{
return 0;
}
@@ -553,12 +555,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
if (this.exifData is null)
{
// The first 6 bytes (Exif00) will be skipped, because this is Jpeg specific
- this.exifData = profile.Skip(Exif00).ToArray();
+ this.exifData = profile.AsSpan(Exif00).ToArray();
}
else
{
// If the EXIF information exceeds 64K, it will be split over multiple APP1 markers
- this.ExtendProfile(ref this.exifData, profile.Skip(Exif00).ToArray());
+ this.ExtendProfile(ref this.exifData, profile.AsSpan(Exif00).ToArray());
}
}
}
@@ -644,6 +646,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
bool done = false;
remaining--;
int quantizationTableSpec = this.InputStream.ReadByte();
+ int tableIndex = quantizationTableSpec & 15;
+
+ // Max index. 4 Tables max.
+ if (tableIndex > 3)
+ {
+ JpegThrowHelper.ThrowBadQuantizationTable();
+ }
switch (quantizationTableSpec >> 4)
{
@@ -659,7 +668,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
this.InputStream.Read(this.temp, 0, 64);
remaining -= 64;
- ref Block8x8F table = ref this.QuantizationTables[quantizationTableSpec & 15];
+ ref Block8x8F table = ref this.QuantizationTables[tableIndex];
for (int j = 0; j < 64; j++)
{
table[j] = this.temp[j];
@@ -679,7 +688,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
this.InputStream.Read(this.temp, 0, 128);
remaining -= 128;
- ref Block8x8F table = ref this.QuantizationTables[quantizationTableSpec & 15];
+ ref Block8x8F table = ref this.QuantizationTables[tableIndex];
for (int j = 0; j < 64; j++)
{
table[j] = (this.temp[2 * j] << 8) | this.temp[(2 * j) + 1];
@@ -688,7 +697,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
break;
default:
- throw new ImageFormatException("Bad Tq index value");
+ JpegThrowHelper.ThrowBadQuantizationTable();
+ break;
}
if (done)
@@ -699,10 +709,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
if (remaining != 0)
{
- throw new ImageFormatException("DQT has wrong length");
+ JpegThrowHelper.ThrowBadMarker(nameof(JpegConstants.Markers.DQT), remaining);
}
- this.MetaData.GetFormatMetaData(JpegFormat.Instance).Quality = QualityEvaluator.EstimateQuality(this.QuantizationTables);
+ this.Metadata.GetFormatMetadata(JpegFormat.Instance).Quality = QualityEvaluator.EstimateQuality(this.QuantizationTables);
}
///
@@ -715,17 +725,21 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
{
if (this.Frame != null)
{
- throw new ImageFormatException("Multiple SOF markers. Only single frame jpegs supported.");
+ JpegThrowHelper.ThrowImageFormatException("Multiple SOF markers. Only single frame jpegs supported.");
}
- this.InputStream.Read(this.temp, 0, remaining);
+ // Read initial marker definitions.
+ const int length = 6;
+ this.InputStream.Read(this.temp, 0, length);
- // We only support 8-bit precision.
- if (this.temp[0] != SupportedPrecision)
+ // We only support 8-bit and 12-bit precision.
+ if (!this.supportedPrecisions.Contains(this.temp[0]))
{
- throw new ImageFormatException("Only 8-Bit precision supported.");
+ JpegThrowHelper.ThrowImageFormatException("Only 8-Bit and 12-Bit precision supported.");
}
+ this.Precision = this.temp[0];
+
this.Frame = new JpegFrame
{
Extended = frameMarker.Marker == JpegConstants.Markers.SOF1,
@@ -736,22 +750,35 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
ComponentCount = this.temp[5]
};
- this.ImageSizeInPixels = new Size(this.Frame.SamplesPerLine, this.Frame.Scanlines);
-
- int maxH = 0;
- int maxV = 0;
- int index = 6;
+ if (this.Frame.SamplesPerLine == 0 || this.Frame.Scanlines == 0)
+ {
+ JpegThrowHelper.ThrowInvalidImageDimensions(this.Frame.SamplesPerLine, this.Frame.Scanlines);
+ }
+ this.ImageSizeInPixels = new Size(this.Frame.SamplesPerLine, this.Frame.Scanlines);
this.ComponentCount = this.Frame.ComponentCount;
if (!metadataOnly)
{
+ remaining -= length;
+
+ const int componentBytes = 3;
+ if (remaining > this.ComponentCount * componentBytes)
+ {
+ JpegThrowHelper.ThrowBadMarker("SOFn", remaining);
+ }
+
+ this.InputStream.Read(this.temp, 0, remaining);
+
// No need to pool this. They max out at 4
this.Frame.ComponentIds = new byte[this.ComponentCount];
this.Frame.ComponentOrder = new byte[this.ComponentCount];
this.Frame.Components = new JpegComponent[this.ComponentCount];
this.ColorSpace = this.DeduceJpegColorSpace();
+ int maxH = 0;
+ int maxV = 0;
+ int index = 0;
for (int i = 0; i < this.ComponentCount; i++)
{
byte hv = this.temp[index + 1];
@@ -773,7 +800,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
this.Frame.Components[i] = component;
this.Frame.ComponentIds[i] = component.Id;
- index += 3;
+ index += componentBytes;
}
this.Frame.MaxHorizontalFactor = maxH;
@@ -791,12 +818,29 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// The remaining bytes in the segment block.
private void ProcessDefineHuffmanTablesMarker(int remaining)
{
+ int length = remaining;
+
using (IManagedByteBuffer huffmanData = this.configuration.MemoryAllocator.AllocateManagedByteBuffer(256, AllocationOptions.Clean))
{
ref byte huffmanDataRef = ref MemoryMarshal.GetReference(huffmanData.GetSpan());
for (int i = 2; i < remaining;)
{
byte huffmanTableSpec = (byte)this.InputStream.ReadByte();
+ int tableType = huffmanTableSpec >> 4;
+ int tableIndex = huffmanTableSpec & 15;
+
+ // Types 0..1 DC..AC
+ if (tableType > 1)
+ {
+ JpegThrowHelper.ThrowImageFormatException("Bad Huffman Table type.");
+ }
+
+ // Max tables of each type
+ if (tableIndex > 3)
+ {
+ JpegThrowHelper.ThrowImageFormatException("Bad Huffman Table index.");
+ }
+
this.InputStream.Read(huffmanData.Array, 0, 16);
using (IManagedByteBuffer codeLengths = this.configuration.MemoryAllocator.AllocateManagedByteBuffer(17, AllocationOptions.Clean))
@@ -809,26 +853,24 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
codeLengthSum += Unsafe.Add(ref codeLengthsRef, j) = Unsafe.Add(ref huffmanDataRef, j - 1);
}
+ length -= 17;
+
+ if (codeLengthSum > 256 || codeLengthSum > length)
+ {
+ JpegThrowHelper.ThrowImageFormatException("Huffman table has excessive length.");
+ }
+
using (IManagedByteBuffer huffmanValues = this.configuration.MemoryAllocator.AllocateManagedByteBuffer(256, AllocationOptions.Clean))
{
this.InputStream.Read(huffmanValues.Array, 0, codeLengthSum);
i += 17 + codeLengthSum;
- int tableType = huffmanTableSpec >> 4;
- int tableIndex = huffmanTableSpec & 15;
-
this.BuildHuffmanTable(
tableType == 0 ? this.dcHuffmanTables : this.acHuffmanTables,
tableIndex,
codeLengths.GetSpan(),
huffmanValues.GetSpan());
-
- if (tableType != 0)
- {
- // Build a table that decodes both magnitude and value of small ACs in one go.
- this.fastACTables.BuildACTableLut(tableIndex, this.acHuffmanTables);
- }
}
}
}
@@ -844,7 +886,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
{
if (remaining != 2)
{
- throw new ImageFormatException($"DRI has wrong length: {remaining}");
+ JpegThrowHelper.ThrowBadMarker(nameof(JpegConstants.Markers.DRI), remaining);
}
this.resetInterval = this.ReadUint16();
@@ -855,11 +897,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
///
private void ProcessStartOfScanMarker()
{
+ if (this.Frame is null)
+ {
+ JpegThrowHelper.ThrowImageFormatException("No readable SOFn (Start Of Frame) marker found.");
+ }
+
int selectorsCount = this.InputStream.ReadByte();
- int componentIndex = -1;
for (int i = 0; i < selectorsCount; i++)
{
- componentIndex = -1;
+ int componentIndex = -1;
int selector = this.InputStream.ReadByte();
for (int j = 0; j < this.Frame.ComponentIds.Length; j++)
@@ -874,7 +920,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
if (componentIndex < 0)
{
- throw new ImageFormatException("Unknown component selector");
+ JpegThrowHelper.ThrowImageFormatException($"Unknown component selector {componentIndex}.");
}
ref JpegComponent component = ref this.Frame.Components[componentIndex];
@@ -913,15 +959,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// The table index
/// The codelengths
/// The values
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void BuildHuffmanTable(HuffmanTables tables, int index, ReadOnlySpan codeLengths, ReadOnlySpan values)
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private void BuildHuffmanTable(HuffmanTable[] tables, int index, ReadOnlySpan codeLengths, ReadOnlySpan values)
=> tables[index] = new HuffmanTable(this.configuration.MemoryAllocator, codeLengths, values);
///
/// Reads a from the stream advancing it by two bytes
///
/// The
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
private ushort ReadUint16()
{
this.InputStream.Read(this.markerBuffer, 0, 2);
@@ -936,12 +982,23 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
private Image PostProcessIntoImage()
where TPixel : struct, IPixel
{
- using (var postProcessor = new JpegImagePostProcessor(this.configuration.MemoryAllocator, this))
+ if (this.ImageWidth == 0 || this.ImageHeight == 0)
+ {
+ JpegThrowHelper.ThrowInvalidImageDimensions(this.ImageWidth, this.ImageHeight);
+ }
+
+ var image = Image.CreateUninitialized(
+ this.configuration,
+ this.ImageWidth,
+ this.ImageHeight,
+ this.Metadata);
+
+ using (var postProcessor = new JpegImagePostProcessor(this.configuration, this))
{
- var image = new Image(this.configuration, this.ImageWidth, this.ImageHeight, this.MetaData);
postProcessor.PostProcess(image.Frames.RootFrame);
- return image;
}
+
+ return image;
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
index a4677ba2b7..d4ce28107f 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
@@ -10,9 +10,9 @@ using SixLabors.ImageSharp.Common.Helpers;
using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder;
using SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder;
-using SixLabors.ImageSharp.MetaData;
-using SixLabors.ImageSharp.MetaData.Profiles.Exif;
-using SixLabors.ImageSharp.MetaData.Profiles.Icc;
+using SixLabors.ImageSharp.Metadata;
+using SixLabors.ImageSharp.Metadata.Profiles.Exif;
+using SixLabors.ImageSharp.Metadata.Profiles.Icc;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Jpeg
@@ -27,85 +27,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
///
private const int QuantizationTableCount = 2;
- ///
- /// Counts the number of bits needed to hold an integer.
- ///
- private static readonly uint[] BitCountLut =
- {
- 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5,
- 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
- 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
- 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
- 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
- 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
- 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
- 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
- 8, 8, 8,
- };
-
- ///
- /// The SOS (Start Of Scan) marker "\xff\xda" followed by 12 bytes:
- /// - the marker length "\x00\x0c",
- /// - the number of components "\x03",
- /// - component 1 uses DC table 0 and AC table 0 "\x01\x00",
- /// - component 2 uses DC table 1 and AC table 1 "\x02\x11",
- /// - component 3 uses DC table 1 and AC table 1 "\x03\x11",
- /// - the bytes "\x00\x3f\x00". Section B.2.3 of the spec says that for
- /// sequential DCTs, those bytes (8-bit Ss, 8-bit Se, 4-bit Ah, 4-bit Al)
- /// should be 0x00, 0x3f, 0x00<<4 | 0x00.
- ///
- private static readonly byte[] SosHeaderYCbCr =
- {
- JpegConstants.Markers.XFF, JpegConstants.Markers.SOS,
-
- // Marker
- 0x00, 0x0c,
-
- // Length (high byte, low byte), must be 6 + 2 * (number of components in scan)
- 0x03, // Number of components in a scan, 3
- 0x01, // Component Id Y
- 0x00, // DC/AC Huffman table
- 0x02, // Component Id Cb
- 0x11, // DC/AC Huffman table
- 0x03, // Component Id Cr
- 0x11, // DC/AC Huffman table
- 0x00, // Ss - Start of spectral selection.
- 0x3f, // Se - End of spectral selection.
- 0x00
-
- // Ah + Ah (Successive approximation bit position high + low)
- };
-
- ///
- /// The unscaled quantization tables in zig-zag order. Each
- /// encoder copies and scales the tables according to its quality parameter.
- /// The values are derived from section K.1 after converting from natural to
- /// zig-zag order.
- ///
- private static readonly byte[,] UnscaledQuant =
- {
- {
- // Luminance.
- 16, 11, 12, 14, 12, 10, 16, 14, 13, 14, 18, 17, 16, 19, 24,
- 40, 26, 24, 22, 22, 24, 49, 35, 37, 29, 40, 58, 51, 61, 60,
- 57, 51, 56, 55, 64, 72, 92, 78, 64, 68, 87, 69, 55, 56, 80,
- 109, 81, 87, 95, 98, 103, 104, 103, 62, 77, 113, 121, 112,
- 100, 120, 92, 101, 103, 99,
- },
- {
- // Chrominance.
- 17, 18, 18, 24, 21, 24, 47, 26, 26, 47, 99, 66, 56, 66,
- 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
- 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
- 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
- 99, 99, 99, 99, 99, 99, 99, 99,
- }
- };
-
///
/// A scratch buffer to reduce allocations.
///
@@ -167,6 +88,103 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
this.subsample = options.Subsample;
}
+ ///
+ /// Gets the counts the number of bits needed to hold an integer.
+ ///
+ // The C# compiler emits this as a compile-time constant embedded in the PE file.
+ // This is effectively compiled down to: return new ReadOnlySpan(&data, length)
+ // More details can be found: https://github.com/dotnet/roslyn/pull/24621
+ private static ReadOnlySpan BitCountLut => new byte[]
+ {
+ 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8,
+ };
+
+ ///
+ /// Gets the SOS (Start Of Scan) marker "\xff\xda" followed by 12 bytes:
+ /// - the marker length "\x00\x0c",
+ /// - the number of components "\x03",
+ /// - component 1 uses DC table 0 and AC table 0 "\x01\x00",
+ /// - component 2 uses DC table 1 and AC table 1 "\x02\x11",
+ /// - component 3 uses DC table 1 and AC table 1 "\x03\x11",
+ /// - the bytes "\x00\x3f\x00". Section B.2.3 of the spec says that for
+ /// sequential DCTs, those bytes (8-bit Ss, 8-bit Se, 4-bit Ah, 4-bit Al)
+ /// should be 0x00, 0x3f, 0x00<<4 | 0x00.
+ ///
+ // The C# compiler emits this as a compile-time constant embedded in the PE file.
+ // This is effectively compiled down to: return new ReadOnlySpan(&data, length)
+ // More details can be found: https://github.com/dotnet/roslyn/pull/24621
+ private static ReadOnlySpan SosHeaderYCbCr => new byte[]
+ {
+ JpegConstants.Markers.XFF, JpegConstants.Markers.SOS,
+
+ // Marker
+ 0x00, 0x0c,
+
+ // Length (high byte, low byte), must be 6 + 2 * (number of components in scan)
+ 0x03, // Number of components in a scan, 3
+ 0x01, // Component Id Y
+ 0x00, // DC/AC Huffman table
+ 0x02, // Component Id Cb
+ 0x11, // DC/AC Huffman table
+ 0x03, // Component Id Cr
+ 0x11, // DC/AC Huffman table
+ 0x00, // Ss - Start of spectral selection.
+ 0x3f, // Se - End of spectral selection.
+ 0x00
+
+ // Ah + Ah (Successive approximation bit position high + low)
+ };
+
+ ///
+ /// Gets the unscaled quantization tables in zig-zag order. Each
+ /// encoder copies and scales the tables according to its quality parameter.
+ /// The values are derived from section K.1 after converting from natural to
+ /// zig-zag order.
+ ///
+ // The C# compiler emits this as a compile-time constant embedded in the PE file.
+ // This is effectively compiled down to: return new ReadOnlySpan(&data, length)
+ // More details can be found: https://github.com/dotnet/roslyn/pull/24621
+ private static ReadOnlySpan UnscaledQuant_Luminance => new byte[]
+ {
+ // Luminance.
+ 16, 11, 12, 14, 12, 10, 16, 14, 13, 14, 18, 17, 16, 19, 24,
+ 40, 26, 24, 22, 22, 24, 49, 35, 37, 29, 40, 58, 51, 61, 60,
+ 57, 51, 56, 55, 64, 72, 92, 78, 64, 68, 87, 69, 55, 56, 80,
+ 109, 81, 87, 95, 98, 103, 104, 103, 62, 77, 113, 121, 112,
+ 100, 120, 92, 101, 103, 99,
+ };
+
+ ///
+ /// Gets the unscaled quantization tables in zig-zag order. Each
+ /// encoder copies and scales the tables according to its quality parameter.
+ /// The values are derived from section K.1 after converting from natural to
+ /// zig-zag order.
+ ///
+ // The C# compiler emits this as a compile-time constant embedded in the PE file.
+ // This is effectively compiled down to: return new ReadOnlySpan(&data, length)
+ // More details can be found: https://github.com/dotnet/roslyn/pull/24621
+ private static ReadOnlySpan UnscaledQuant_Chrominance => new byte[]
+ {
+ // Chrominance.
+ 17, 18, 18, 24, 21, 24, 47, 26, 26, 47, 99, 66, 56, 66,
+ 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ };
+
///
/// Encode writes the image to the jpeg baseline format with the given options.
///
@@ -186,10 +204,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
}
this.outputStream = stream;
- ImageMetaData metaData = image.MetaData;
+ ImageMetadata metadata = image.Metadata;
// System.Drawing produces identical output for jpegs with a quality parameter of 0 and 1.
- int qlty = (this.quality ?? metaData.GetFormatMetaData(JpegFormat.Instance).Quality).Clamp(1, 100);
+ int qlty = (this.quality ?? metadata.GetFormatMetadata(JpegFormat.Instance).Quality).Clamp(1, 100);
this.subsample = this.subsample ?? (qlty >= 91 ? JpegSubsample.Ratio444 : JpegSubsample.Ratio420);
// Convert from a quality rating to a scaling factor.
@@ -211,10 +229,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
int componentCount = 3;
// Write the Start Of Image marker.
- this.WriteApplicationHeader(metaData);
+ this.WriteApplicationHeader(metadata);
// Write Exif and ICC profiles
- this.WriteProfiles(metaData);
+ this.WriteProfiles(metadata);
// Write the quantization tables.
this.WriteDefineQuantizationTables();
@@ -259,9 +277,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// The quantization table.
private static void InitQuantizationTable(int i, int scale, ref Block8x8F quant)
{
+ DebugGuard.MustBeBetweenOrEqualTo(i, 0, 1, nameof(i));
+ var unscaledQuant = (i == 0) ? UnscaledQuant_Luminance : UnscaledQuant_Chrominance;
+
for (int j = 0; j < Block8x8F.Size; j++)
{
- int x = UnscaledQuant[i, j];
+ int x = unscaledQuant[j];
x = ((x * scale) + 50) / 100;
if (x < 1)
{
@@ -357,7 +378,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
}
else
{
- bt = 8 + BitCountLut[a >> 8];
+ bt = 8 + (uint)BitCountLut[a >> 8];
}
this.EmitHuff(index, (int)((uint)(runLength << 4) | bt));
@@ -427,8 +448,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
///
/// Writes the application header containing the JFIF identifier plus extra data.
///
- /// The image meta data.
- private void WriteApplicationHeader(ImageMetaData meta)
+ /// The image metadata.
+ private void WriteApplicationHeader(ImageMetadata meta)
{
// Write the start of image marker. Markers are always prefixed with 0xff.
this.buffer[0] = JpegConstants.Markers.XFF;
@@ -547,7 +568,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
private void WriteDefineHuffmanTables(int componentCount)
{
// Table identifiers.
- Span headers = stackalloc byte[] { 0x00, 0x10, 0x01, 0x11 };
+ Span headers = stackalloc byte[]
+ {
+ 0x00,
+ 0x10,
+ 0x01,
+ 0x11
+ };
int markerlen = 2;
HuffmanSpec[] specs = HuffmanSpec.TheHuffmanSpecs;
@@ -764,17 +791,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
///
/// Writes the metadata profiles to the image.
///
- /// The image meta data.
- private void WriteProfiles(ImageMetaData metaData)
+ /// The image metadata.
+ private void WriteProfiles(ImageMetadata metadata)
{
- if (metaData is null)
+ if (metadata is null)
{
return;
}
- metaData.SyncProfiles();
- this.WriteExifProfile(metaData.ExifProfile);
- this.WriteIccProfile(metaData.IccProfile);
+ metadata.SyncProfiles();
+ this.WriteExifProfile(metadata.ExifProfile);
+ this.WriteIccProfile(metadata.IccProfile);
}
///
@@ -786,16 +813,37 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
private void WriteStartOfFrame(int width, int height, int componentCount)
{
// "default" to 4:2:0
- Span subsamples = stackalloc byte[] { 0x22, 0x11, 0x11 };
- Span chroma = stackalloc byte[] { 0x00, 0x01, 0x01 };
+ Span subsamples = stackalloc byte[]
+ {
+ 0x22,
+ 0x11,
+ 0x11
+ };
+
+ Span chroma = stackalloc byte[]
+ {
+ 0x00,
+ 0x01,
+ 0x01
+ };
switch (this.subsample)
{
case JpegSubsample.Ratio444:
- subsamples = stackalloc byte[] { 0x11, 0x11, 0x11 };
+ subsamples = stackalloc byte[]
+ {
+ 0x11,
+ 0x11,
+ 0x11
+ };
break;
case JpegSubsample.Ratio420:
- subsamples = stackalloc byte[] { 0x22, 0x11, 0x11 };
+ subsamples = stackalloc byte[]
+ {
+ 0x22,
+ 0x11,
+ 0x11
+ };
break;
}
@@ -822,11 +870,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
{
for (int i = 0; i < componentCount; i++)
{
- this.buffer[(3 * i) + 6] = (byte)(i + 1);
+ int i3 = 3 * i;
+ this.buffer[i3 + 6] = (byte)(i + 1);
// We use 4:2:0 chroma subsampling by default.
- this.buffer[(3 * i) + 7] = subsamples[i];
- this.buffer[(3 * i) + 8] = chroma[i];
+ this.buffer[i3 + 7] = subsamples[i];
+ this.buffer[i3 + 8] = chroma[i];
}
}
@@ -843,7 +892,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
{
// TODO: Need a JpegScanEncoder class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.)
// TODO: We should allow grayscale writing.
- this.outputStream.Write(SosHeaderYCbCr, 0, SosHeaderYCbCr.Length);
+ this.outputStream.Write(SosHeaderYCbCr);
switch (this.subsample)
{
diff --git a/src/ImageSharp/Formats/Jpeg/JpegFormat.cs b/src/ImageSharp/Formats/Jpeg/JpegFormat.cs
index 6b23ceac7a..f56072a4b5 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegFormat.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegFormat.cs
@@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
///
/// Registers the image encoders, decoders and mime type detectors for the jpeg format.
///
- public sealed class JpegFormat : IImageFormat
+ public sealed class JpegFormat : IImageFormat
{
private JpegFormat()
{
@@ -32,6 +32,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
public IEnumerable FileExtensions => JpegConstants.FileExtensions;
///
- public JpegMetaData CreateDefaultFormatMetaData() => new JpegMetaData();
+ public JpegMetadata CreateDefaultFormatMetadata() => new JpegMetadata();
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpeg/JpegMetaData.cs b/src/ImageSharp/Formats/Jpeg/JpegMetaData.cs
index fcad29e5d0..9f0deae02c 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegMetaData.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegMetaData.cs
@@ -6,20 +6,20 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
///
/// Provides Jpeg specific metadata information for the image.
///
- public class JpegMetaData : IDeepCloneable
+ public class JpegMetadata : IDeepCloneable
{
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
- public JpegMetaData()
+ public JpegMetadata()
{
}
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
/// The metadata to create an instance from.
- private JpegMetaData(JpegMetaData other) => this.Quality = other.Quality;
+ private JpegMetadata(JpegMetadata other) => this.Quality = other.Quality;
///
/// Gets or sets the encoded quality.
@@ -27,6 +27,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
public int Quality { get; set; } = 75;
///
- public IDeepCloneable DeepClone() => new JpegMetaData(this);
+ public IDeepCloneable DeepClone() => new JpegMetadata(this);
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpeg/JpegThrowHelper.cs b/src/ImageSharp/Formats/Jpeg/JpegThrowHelper.cs
index c7f3666604..b30da28c45 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegThrowHelper.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegThrowHelper.cs
@@ -8,19 +8,28 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
internal static class JpegThrowHelper
{
///
- /// Cold path optimization for throwing -s
+ /// Cold path optimization for throwing 's.
///
- /// The error message for the exception
- [MethodImpl(MethodImplOptions.NoInlining)]
- public static void ThrowImageFormatException(string errorMessage)
- {
- throw new ImageFormatException(errorMessage);
- }
-
- [MethodImpl(MethodImplOptions.NoInlining)]
- public static void ThrowBadHuffmanCode()
- {
- throw new ImageFormatException("Bad Huffman code.");
- }
+ /// The error message for the exception.
+ [MethodImpl(InliningOptions.ColdPath)]
+ public static void ThrowImageFormatException(string errorMessage) => throw new ImageFormatException(errorMessage);
+
+ [MethodImpl(InliningOptions.ColdPath)]
+ public static void ThrowBadMarker(string marker, int length) => throw new ImageFormatException($"Marker {marker} has bad length {length}.");
+
+ [MethodImpl(InliningOptions.ColdPath)]
+ public static void ThrowBadQuantizationTable() => throw new ImageFormatException("Bad Quantization Table index.");
+
+ [MethodImpl(InliningOptions.ColdPath)]
+ public static void ThrowBadHuffmanCode() => throw new ImageFormatException("Bad Huffman code.");
+
+ [MethodImpl(InliningOptions.ColdPath)]
+ public static void ThrowBadSampling() => throw new ImageFormatException("Bad sampling factor.");
+
+ [MethodImpl(InliningOptions.ColdPath)]
+ public static void ThrowBadProgressiveScan(int ss, int se, int ah, int al) => throw new ImageFormatException($"Invalid progressive parameters Ss={ss} Se={se} Ah={ah} Al={al}.");
+
+ [MethodImpl(InliningOptions.ColdPath)]
+ public static void ThrowInvalidImageDimensions(int width, int height) => throw new ImageFormatException($"Invalid image dimensions: {width}x{height}.");
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Png/Chunks/PhysicalChunkData.cs b/src/ImageSharp/Formats/Png/Chunks/PhysicalChunkData.cs
index 07fc688d50..8b3c3e9aad 100644
--- a/src/ImageSharp/Formats/Png/Chunks/PhysicalChunkData.cs
+++ b/src/ImageSharp/Formats/Png/Chunks/PhysicalChunkData.cs
@@ -1,7 +1,10 @@
-using System;
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
using System.Buffers.Binary;
using SixLabors.ImageSharp.Common.Helpers;
-using SixLabors.ImageSharp.MetaData;
+using SixLabors.ImageSharp.Metadata;
namespace SixLabors.ImageSharp.Formats.Png.Chunks
{
@@ -53,11 +56,11 @@ namespace SixLabors.ImageSharp.Formats.Png.Chunks
///
/// Constructs the PngPhysicalChunkData from the provided metadata.
- /// If the resolution units are not in meters, they are automatically convereted.
+ /// If the resolution units are not in meters, they are automatically converted.
///
/// The metadata.
/// The constructed PngPhysicalChunkData instance.
- public static PhysicalChunkData FromMetadata(ImageMetaData meta)
+ public static PhysicalChunkData FromMetadata(ImageMetadata meta)
{
byte unitSpecifier = 0;
uint x;
diff --git a/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs b/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs
index 4ffc39bdbd..4cd61e043d 100644
--- a/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs
+++ b/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs
@@ -30,7 +30,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
ref byte prevBaseRef = ref MemoryMarshal.GetReference(previousScanline);
// Paeth(x) + PaethPredictor(Raw(x-bpp), Prior(x), Prior(x-bpp))
- int offset = bytesPerPixel + 1; // Add one bcause x starts at one.
+ int offset = bytesPerPixel + 1; // Add one because x starts at one.
int x = 1;
for (; x < offset; x++)
{
diff --git a/src/ImageSharp/Formats/Png/Filters/UpFilter.cs b/src/ImageSharp/Formats/Png/Filters/UpFilter.cs
index c6a297e33a..5d9dc6a890 100644
--- a/src/ImageSharp/Formats/Png/Filters/UpFilter.cs
+++ b/src/ImageSharp/Formats/Png/Filters/UpFilter.cs
@@ -57,7 +57,8 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
// Up(x) = Raw(x) - Prior(x)
resultBaseRef = 2;
- for (int x = 0; x < scanline.Length; /* Note: ++x happens in the body to avoid one add operation */) {
+ for (int x = 0; x < scanline.Length; /* Note: ++x happens in the body to avoid one add operation */)
+ {
byte scan = Unsafe.Add(ref scanBaseRef, x);
byte above = Unsafe.Add(ref prevBaseRef, x);
++x;
diff --git a/src/ImageSharp/Formats/Png/IPngDecoderOptions.cs b/src/ImageSharp/Formats/Png/IPngDecoderOptions.cs
index bd0b932056..5b650ac2a0 100644
--- a/src/ImageSharp/Formats/Png/IPngDecoderOptions.cs
+++ b/src/ImageSharp/Formats/Png/IPngDecoderOptions.cs
@@ -6,7 +6,7 @@ using System.Text;
namespace SixLabors.ImageSharp.Formats.Png
{
///
- /// The optioas for decoding png images
+ /// The options for decoding png images
///
internal interface IPngDecoderOptions
{
diff --git a/src/ImageSharp/Formats/Png/PngChunkType.cs b/src/ImageSharp/Formats/Png/PngChunkType.cs
index 7654c17014..1b251a5748 100644
--- a/src/ImageSharp/Formats/Png/PngChunkType.cs
+++ b/src/ImageSharp/Formats/Png/PngChunkType.cs
@@ -56,10 +56,10 @@ namespace SixLabors.ImageSharp.Formats.Png
Text = 0x74455874U,
///
- /// This chunk specifies that the image uses simple transparency:
+ /// The tRNS chunk specifies that the image uses simple transparency:
/// either alpha values associated with palette entries (for indexed-color images)
/// or a single transparent color (for grayscale and true color images).
///
- PaletteAlpha = 0x74524E53U
+ Transparency = 0x74524E53U
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Png/PngConstants.cs b/src/ImageSharp/Formats/Png/PngConstants.cs
index 62a7b74aba..e1f978e1ac 100644
--- a/src/ImageSharp/Formats/Png/PngConstants.cs
+++ b/src/ImageSharp/Formats/Png/PngConstants.cs
@@ -26,7 +26,8 @@ namespace SixLabors.ImageSharp.Formats.Png
///
public static readonly IEnumerable FileExtensions = new[] { "png" };
- public static readonly byte[] HeaderBytes = {
+ public static readonly byte[] HeaderBytes =
+ {
0x89, // Set the high bit.
0x50, // P
0x4E, // N
diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs
index d66ac6c0d2..5e9d1440ac 100644
--- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs
+++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs
@@ -12,8 +12,8 @@ using SixLabors.ImageSharp.Formats.Png.Chunks;
using SixLabors.ImageSharp.Formats.Png.Filters;
using SixLabors.ImageSharp.Formats.Png.Zlib;
using SixLabors.ImageSharp.Memory;
-using SixLabors.ImageSharp.MetaData;
-using SixLabors.ImageSharp.MetaData.Profiles.Exif;
+using SixLabors.ImageSharp.Metadata;
+using SixLabors.ImageSharp.Metadata.Profiles.Exif;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
@@ -124,31 +124,6 @@ namespace SixLabors.ImageSharp.Formats.Png
///
private PngColorType pngColorType;
- ///
- /// Represents any color in an 8 bit Rgb24 encoded png that should be transparent
- ///
- private Rgb24 rgb24Trans;
-
- ///
- /// Represents any color in a 16 bit Rgb24 encoded png that should be transparent
- ///
- private Rgb48 rgb48Trans;
-
- ///
- /// Represents any color in an 8 bit grayscale encoded png that should be transparent
- ///
- private byte luminanceTrans;
-
- ///
- /// Represents any color in a 16 bit grayscale encoded png that should be transparent
- ///
- private ushort luminance16Trans;
-
- ///
- /// Whether the image has transparency chunk and markers were decoded
- ///
- private bool hasTrans;
-
///
/// The next chunk of data to return
///
@@ -182,8 +157,8 @@ namespace SixLabors.ImageSharp.Formats.Png
public Image Decode(Stream stream)
where TPixel : struct, IPixel
{
- var metaData = new ImageMetaData();
- PngMetaData pngMetaData = metaData.GetFormatMetaData(PngFormat.Instance);
+ var metadata = new ImageMetadata();
+ PngMetadata pngMetadata = metadata.GetFormatMetadata(PngFormat.Instance);
this.currentStream = stream;
this.currentStream.Skip(8);
Image image = null;
@@ -196,24 +171,24 @@ namespace SixLabors.ImageSharp.Formats.Png
switch (chunk.Type)
{
case PngChunkType.Header:
- this.ReadHeaderChunk(pngMetaData, chunk.Data.Array);
+ this.ReadHeaderChunk(pngMetadata, chunk.Data.Array);
break;
case PngChunkType.Physical:
- this.ReadPhysicalChunk(metaData, chunk.Data.GetSpan());
+ this.ReadPhysicalChunk(metadata, chunk.Data.GetSpan());
break;
case PngChunkType.Gamma:
- this.ReadGammaChunk(pngMetaData, chunk.Data.GetSpan());
+ this.ReadGammaChunk(pngMetadata, chunk.Data.GetSpan());
break;
case PngChunkType.Data:
if (image is null)
{
- this.InitializeImage(metaData, out image);
+ this.InitializeImage(metadata, out image);
}
using (var deframeStream = new ZlibInflateStream(this.currentStream, this.ReadNextDataChunk))
{
deframeStream.AllocateNewBytes(chunk.Length);
- this.ReadScanlines(deframeStream.CompressedStream, image.Frames.RootFrame);
+ this.ReadScanlines(deframeStream.CompressedStream, image.Frames.RootFrame, pngMetadata);
}
break;
@@ -222,21 +197,21 @@ namespace SixLabors.ImageSharp.Formats.Png
Buffer.BlockCopy(chunk.Data.Array, 0, pal, 0, chunk.Length);
this.palette = pal;
break;
- case PngChunkType.PaletteAlpha:
+ case PngChunkType.Transparency:
byte[] alpha = new byte[chunk.Length];
Buffer.BlockCopy(chunk.Data.Array, 0, alpha, 0, chunk.Length);
this.paletteAlpha = alpha;
- this.AssignTransparentMarkers(alpha);
+ this.AssignTransparentMarkers(alpha, pngMetadata);
break;
case PngChunkType.Text:
- this.ReadTextChunk(metaData, chunk.Data.Array.AsSpan(0, chunk.Length));
+ this.ReadTextChunk(metadata, chunk.Data.Array.AsSpan(0, chunk.Length));
break;
case PngChunkType.Exif:
if (!this.ignoreMetadata)
{
byte[] exifData = new byte[chunk.Length];
Buffer.BlockCopy(chunk.Data.Array, 0, exifData, 0, chunk.Length);
- metaData.ExifProfile = new ExifProfile(exifData);
+ metadata.ExifProfile = new ExifProfile(exifData);
}
break;
@@ -271,8 +246,8 @@ namespace SixLabors.ImageSharp.Formats.Png
/// The containing image data.
public IImageInfo Identify(Stream stream)
{
- var metaData = new ImageMetaData();
- PngMetaData pngMetaData = metaData.GetFormatMetaData(PngFormat.Instance);
+ var metadata = new ImageMetadata();
+ PngMetadata pngMetadata = metadata.GetFormatMetadata(PngFormat.Instance);
this.currentStream = stream;
this.currentStream.Skip(8);
try
@@ -284,19 +259,19 @@ namespace SixLabors.ImageSharp.Formats.Png
switch (chunk.Type)
{
case PngChunkType.Header:
- this.ReadHeaderChunk(pngMetaData, chunk.Data.Array);
+ this.ReadHeaderChunk(pngMetadata, chunk.Data.Array);
break;
case PngChunkType.Physical:
- this.ReadPhysicalChunk(metaData, chunk.Data.GetSpan());
+ this.ReadPhysicalChunk(metadata, chunk.Data.GetSpan());
break;
case PngChunkType.Gamma:
- this.ReadGammaChunk(pngMetaData, chunk.Data.GetSpan());
+ this.ReadGammaChunk(pngMetadata, chunk.Data.GetSpan());
break;
case PngChunkType.Data:
this.SkipChunkDataAndCrc(chunk);
break;
case PngChunkType.Text:
- this.ReadTextChunk(metaData, chunk.Data.Array.AsSpan(0, chunk.Length));
+ this.ReadTextChunk(metadata, chunk.Data.Array.AsSpan(0, chunk.Length));
break;
case PngChunkType.End:
this.isEndChunkReached = true;
@@ -320,7 +295,7 @@ namespace SixLabors.ImageSharp.Formats.Png
throw new ImageFormatException("PNG Image does not contain a header chunk");
}
- return new ImageInfo(new PixelTypeInfo(this.CalculateBitsPerPixel()), this.header.Width, this.header.Height, metaData);
+ return new ImageInfo(new PixelTypeInfo(this.CalculateBitsPerPixel()), this.header.Width, this.header.Height, metadata);
}
///
@@ -375,7 +350,7 @@ namespace SixLabors.ImageSharp.Formats.Png
///
/// The metadata to read to.
/// The data containing physical data.
- private void ReadPhysicalChunk(ImageMetaData metadata, ReadOnlySpan data)
+ private void ReadPhysicalChunk(ImageMetadata metadata, ReadOnlySpan data)
{
var physicalChunk = PhysicalChunkData.Parse(data);
@@ -392,7 +367,7 @@ namespace SixLabors.ImageSharp.Formats.Png
///
/// The metadata to read to.
/// The data containing physical data.
- private void ReadGammaChunk(PngMetaData pngMetadata, ReadOnlySpan data)
+ private void ReadGammaChunk(PngMetadata pngMetadata, ReadOnlySpan data)
{
// The value is encoded as a 4-byte unsigned integer, representing gamma times 100000.
// For example, a gamma of 1/2.2 would be stored as 45455.
@@ -405,7 +380,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// The type the pixels will be
/// The metadata information for the image
/// The image that we will populate
- private void InitializeImage(ImageMetaData metadata, out Image image)
+ private void InitializeImage(ImageMetadata metadata, out Image image)
where TPixel : struct, IPixel
{
image = new Image(this.configuration, this.header.Width, this.header.Height, metadata);
@@ -496,16 +471,17 @@ namespace SixLabors.ImageSharp.Formats.Png
/// The pixel format.
/// The containing data.
/// The pixel data.
- private void ReadScanlines(Stream dataStream, ImageFrame image)
+ /// The png metadata
+ private void ReadScanlines(Stream dataStream, ImageFrame image, PngMetadata pngMetadata)
where TPixel : struct, IPixel
{
if (this.header.InterlaceMethod == PngInterlaceMode.Adam7)
{
- this.DecodeInterlacedPixelData(dataStream, image);
+ this.DecodeInterlacedPixelData(dataStream, image, pngMetadata);
}
else
{
- this.DecodePixelData(dataStream, image);
+ this.DecodePixelData(dataStream, image, pngMetadata);
}
}
@@ -515,7 +491,8 @@ namespace SixLabors.ImageSharp.Formats.Png
/// The pixel format.
/// The compressed pixel data stream.
/// The image to decode to.
- private void DecodePixelData(Stream compressedStream, ImageFrame image)
+ /// The png metadata
+ private void DecodePixelData(Stream compressedStream, ImageFrame image, PngMetadata pngMetadata)
where TPixel : struct, IPixel
{
while (this.currentRow < this.header.Height)
@@ -555,7 +532,7 @@ namespace SixLabors.ImageSharp.Formats.Png
throw new ImageFormatException("Unknown filter type.");
}
- this.ProcessDefilteredScanline(scanlineSpan, image);
+ this.ProcessDefilteredScanline(scanlineSpan, image, pngMetadata);
this.SwapBuffers();
this.currentRow++;
@@ -569,7 +546,8 @@ namespace SixLabors.ImageSharp.Formats.Png
/// The pixel format.
/// The compressed pixel data stream.
/// The current image.
- private void DecodeInterlacedPixelData(Stream compressedStream, ImageFrame image)
+ /// The png metadata.
+ private void DecodeInterlacedPixelData(Stream compressedStream, ImageFrame image, PngMetadata pngMetadata)
where TPixel : struct, IPixel
{
while (true)
@@ -626,7 +604,7 @@ namespace SixLabors.ImageSharp.Formats.Png
}
Span rowSpan = image.GetPixelRowSpan(this.currentRow);
- this.ProcessInterlacedDefilteredScanline(this.scanline.GetSpan(), rowSpan, Adam7.FirstColumn[this.pass], Adam7.ColumnIncrement[this.pass]);
+ this.ProcessInterlacedDefilteredScanline(this.scanline.GetSpan(), rowSpan, pngMetadata, Adam7.FirstColumn[this.pass], Adam7.ColumnIncrement[this.pass]);
this.SwapBuffers();
@@ -654,7 +632,8 @@ namespace SixLabors.ImageSharp.Formats.Png
/// The pixel format.
/// The de-filtered scanline
/// The image
- private void ProcessDefilteredScanline(ReadOnlySpan defilteredScanline, ImageFrame pixels)
+ /// The png metadata.
+ private void ProcessDefilteredScanline(ReadOnlySpan defilteredScanline, ImageFrame pixels, PngMetadata pngMetadata)
where TPixel : struct, IPixel
{
Span rowSpan = pixels.GetPixelRowSpan(this.currentRow);
@@ -674,9 +653,9 @@ namespace SixLabors.ImageSharp.Formats.Png
this.header,
scanlineSpan,
rowSpan,
- this.hasTrans,
- this.luminance16Trans,
- this.luminanceTrans);
+ pngMetadata.HasTrans,
+ pngMetadata.TransparentGray16.GetValueOrDefault(),
+ pngMetadata.TransparentGray8.GetValueOrDefault());
break;
@@ -702,19 +681,21 @@ namespace SixLabors.ImageSharp.Formats.Png
case PngColorType.Rgb:
PngScanlineProcessor.ProcessRgbScanline(
+ this.configuration,
this.header,
scanlineSpan,
rowSpan,
this.bytesPerPixel,
this.bytesPerSample,
- this.hasTrans,
- this.rgb48Trans,
- this.rgb24Trans);
+ pngMetadata.HasTrans,
+ pngMetadata.TransparentRgb48.GetValueOrDefault(),
+ pngMetadata.TransparentRgb24.GetValueOrDefault());
break;
case PngColorType.RgbWithAlpha:
PngScanlineProcessor.ProcessRgbaScanline(
+ this.configuration,
this.header,
scanlineSpan,
rowSpan,
@@ -733,9 +714,10 @@ namespace SixLabors.ImageSharp.Formats.Png
/// The pixel format.
/// The de-filtered scanline
/// The current image row.
+ /// The png metadata.
/// The column start index. Always 0 for none interlaced images.
/// The column increment. Always 1 for none interlaced images.
- private void ProcessInterlacedDefilteredScanline(ReadOnlySpan defilteredScanline, Span rowSpan, int pixelOffset = 0, int increment = 1)
+ private void ProcessInterlacedDefilteredScanline(ReadOnlySpan defilteredScanline, Span rowSpan, PngMetadata pngMetadata, int pixelOffset = 0, int increment = 1)
where TPixel : struct, IPixel
{
// Trim the first marker byte from the buffer
@@ -755,9 +737,9 @@ namespace SixLabors.ImageSharp.Formats.Png
rowSpan,
pixelOffset,
increment,
- this.hasTrans,
- this.luminance16Trans,
- this.luminanceTrans);
+ pngMetadata.HasTrans,
+ pngMetadata.TransparentGray16.GetValueOrDefault(),
+ pngMetadata.TransparentGray8.GetValueOrDefault());
break;
@@ -794,9 +776,9 @@ namespace SixLabors.ImageSharp.Formats.Png
increment,
this.bytesPerPixel,
this.bytesPerSample,
- this.hasTrans,
- this.rgb48Trans,
- this.rgb24Trans);
+ pngMetadata.HasTrans,
+ pngMetadata.TransparentRgb48.GetValueOrDefault(),
+ pngMetadata.TransparentRgb24.GetValueOrDefault());
break;
@@ -817,10 +799,11 @@ namespace SixLabors.ImageSharp.Formats.Png
}
///
- /// Decodes and assigns marker colors that identify transparent pixels in non indexed images
+ /// Decodes and assigns marker colors that identify transparent pixels in non indexed images.
///
- /// The alpha tRNS array
- private void AssignTransparentMarkers(ReadOnlySpan alpha)
+ /// The alpha tRNS array.
+ /// The png metadata.
+ private void AssignTransparentMarkers(ReadOnlySpan alpha, PngMetadata pngMetadata)
{
if (this.pngColorType == PngColorType.Rgb)
{
@@ -832,16 +815,16 @@ namespace SixLabors.ImageSharp.Formats.Png
ushort gc = BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(2, 2));
ushort bc = BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(4, 2));
- this.rgb48Trans = new Rgb48(rc, gc, bc);
- this.hasTrans = true;
+ pngMetadata.TransparentRgb48 = new Rgb48(rc, gc, bc);
+ pngMetadata.HasTrans = true;
return;
}
byte r = ReadByteLittleEndian(alpha, 0);
byte g = ReadByteLittleEndian(alpha, 2);
byte b = ReadByteLittleEndian(alpha, 4);
- this.rgb24Trans = new Rgb24(r, g, b);
- this.hasTrans = true;
+ pngMetadata.TransparentRgb24 = new Rgb24(r, g, b);
+ pngMetadata.HasTrans = true;
}
}
else if (this.pngColorType == PngColorType.Grayscale)
@@ -850,14 +833,14 @@ namespace SixLabors.ImageSharp.Formats.Png
{
if (this.header.BitDepth == 16)
{
- this.luminance16Trans = BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(0, 2));
+ pngMetadata.TransparentGray16 = new Gray16(BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(0, 2)));
}
else
{
- this.luminanceTrans = ReadByteLittleEndian(alpha, 0);
+ pngMetadata.TransparentGray8 = new Gray8(ReadByteLittleEndian(alpha, 0));
}
- this.hasTrans = true;
+ pngMetadata.HasTrans = true;
}
}
}
@@ -865,16 +848,16 @@ namespace SixLabors.ImageSharp.Formats.Png
///
/// Reads a header chunk from the data.
///
- /// The png metadata.
+ /// The png metadata.
/// The containing data.
- private void ReadHeaderChunk(PngMetaData pngMetaData, ReadOnlySpan data)
+ private void ReadHeaderChunk(PngMetadata pngMetadata, ReadOnlySpan data)
{
this.header = PngHeader.Parse(data);
this.header.Validate();
- pngMetaData.BitDepth = (PngBitDepth)this.header.BitDepth;
- pngMetaData.ColorType = this.header.ColorType;
+ pngMetadata.BitDepth = (PngBitDepth)this.header.BitDepth;
+ pngMetadata.ColorType = this.header.ColorType;
this.pngColorType = this.header.ColorType;
}
@@ -884,7 +867,7 @@ namespace SixLabors.ImageSharp.Formats.Png
///
/// The metadata to decode to.
/// The containing the data.
- private void ReadTextChunk(ImageMetaData metadata, ReadOnlySpan data)
+ private void ReadTextChunk(ImageMetadata metadata, ReadOnlySpan data)
{
if (this.ignoreMetadata)
{
diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs
index 84f7cb6cc4..7415b07532 100644
--- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs
+++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs
@@ -14,7 +14,7 @@ using SixLabors.ImageSharp.Formats.Png.Chunks;
using SixLabors.ImageSharp.Formats.Png.Filters;
using SixLabors.ImageSharp.Formats.Png.Zlib;
using SixLabors.ImageSharp.Memory;
-using SixLabors.ImageSharp.MetaData;
+using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
using SixLabors.Memory;
@@ -43,6 +43,11 @@ namespace SixLabors.ImageSharp.Formats.Png
///
private readonly MemoryAllocator memoryAllocator;
+ ///
+ /// The configuration instance for the decoding operation
+ ///
+ private Configuration configuration;
+
///
/// The maximum block size, defaults at 64k for uncompressed blocks.
///
@@ -180,7 +185,7 @@ namespace SixLabors.ImageSharp.Formats.Png
this.pngColorType = options.ColorType;
// Specification recommends default filter method None for paletted images and Paeth for others.
- this.pngFilterMethod = options.FilterMethod ?? (options.ColorType.Equals(PngColorType.Palette)
+ this.pngFilterMethod = options.FilterMethod ?? (options.ColorType == PngColorType.Palette
? PngFilterMethod.None
: PngFilterMethod.Paeth);
this.compressionLevel = options.CompressionLevel;
@@ -201,17 +206,18 @@ namespace SixLabors.ImageSharp.Formats.Png
Guard.NotNull(image, nameof(image));
Guard.NotNull(stream, nameof(stream));
+ this.configuration = image.GetConfiguration();
this.width = image.Width;
this.height = image.Height;
// Always take the encoder options over the metadata values.
- ImageMetaData metaData = image.MetaData;
- PngMetaData pngMetaData = metaData.GetFormatMetaData(PngFormat.Instance);
- this.gamma = this.gamma ?? pngMetaData.Gamma;
+ ImageMetadata metadata = image.Metadata;
+ PngMetadata pngMetadata = metadata.GetFormatMetadata(PngFormat.Instance);
+ this.gamma = this.gamma ?? pngMetadata.Gamma;
this.writeGamma = this.gamma > 0;
- this.pngColorType = this.pngColorType ?? pngMetaData.ColorType;
- this.pngBitDepth = this.pngBitDepth ?? pngMetaData.BitDepth;
- this.use16Bit = this.pngBitDepth.Equals(PngBitDepth.Bit16);
+ this.pngColorType = this.pngColorType ?? pngMetadata.ColorType;
+ this.pngBitDepth = this.pngBitDepth ?? pngMetadata.BitDepth;
+ this.use16Bit = this.pngBitDepth == PngBitDepth.Bit16;
// Ensure we are not allowing impossible combinations.
if (!ColorTypes.ContainsKey(this.pngColorType.Value))
@@ -231,13 +237,17 @@ namespace SixLabors.ImageSharp.Formats.Png
}
// Use the metadata to determine what quantization depth to use if no quantizer has been set.
- if (this.quantizer == null)
+ if (this.quantizer is null)
{
this.quantizer = new WuQuantizer(ImageMaths.GetColorCountForBitDepth(bits));
}
// Create quantized frame returning the palette and set the bit depth.
- quantized = this.quantizer.CreateFrameQuantizer().QuantizeFrame(image.Frames.RootFrame);
+ using (IFrameQuantizer frameQuantizer = this.quantizer.CreateFrameQuantizer(image.GetConfiguration()))
+ {
+ quantized = frameQuantizer.QuantizeFrame(image.Frames.RootFrame);
+ }
+
byte quantizedBits = (byte)ImageMaths.GetBitsNeededForColorDepth(quantized.Palette.Length).Clamp(1, 8);
bits = Math.Max(bits, quantizedBits);
@@ -283,9 +293,14 @@ namespace SixLabors.ImageSharp.Formats.Png
this.WritePaletteChunk(stream, quantized);
}
- this.WritePhysicalChunk(stream, metaData);
+ if (pngMetadata.HasTrans)
+ {
+ this.WriteTransparencyChunk(stream, pngMetadata);
+ }
+
+ this.WritePhysicalChunk(stream, metadata);
this.WriteGammaChunk(stream);
- this.WriteExifChunk(stream, metaData);
+ this.WriteExifChunk(stream, metadata);
this.WriteDataChunks(image.Frames.RootFrame, quantized, stream);
this.WriteEndChunk(stream);
stream.Flush();
@@ -317,9 +332,8 @@ namespace SixLabors.ImageSharp.Formats.Png
Span rawScanlineSpan = this.rawScanline.GetSpan();
ref byte rawScanlineSpanRef = ref MemoryMarshal.GetReference(rawScanlineSpan);
- if (this.pngColorType.Equals(PngColorType.Grayscale))
+ if (this.pngColorType == PngColorType.Grayscale)
{
- // TODO: Research and add support for grayscale plus tRNS
if (this.use16Bit)
{
// 16 bit grayscale
@@ -327,7 +341,7 @@ namespace SixLabors.ImageSharp.Formats.Png
{
Span luminanceSpan = luminanceBuffer.GetSpan();
ref Gray16 luminanceRef = ref MemoryMarshal.GetReference(luminanceSpan);
- PixelOperations.Instance.ToGray16(rowSpan, luminanceSpan);
+ PixelOperations.Instance.ToGray16(this.configuration, rowSpan, luminanceSpan);
// Can't map directly to byte array as it's big endian.
for (int x = 0, o = 0; x < luminanceSpan.Length; x++, o += 2)
@@ -342,19 +356,28 @@ namespace SixLabors.ImageSharp.Formats.Png
if (this.bitDepth == 8)
{
// 8 bit grayscale
- PixelOperations.Instance.ToGray8Bytes(rowSpan, rawScanlineSpan, rowSpan.Length);
+ PixelOperations.Instance.ToGray8Bytes(
+ this.configuration,
+ rowSpan,
+ rawScanlineSpan,
+ rowSpan.Length);
}
else
{
// 1, 2, and 4 bit grayscale
- using (IManagedByteBuffer temp = this.memoryAllocator.AllocateManagedByteBuffer(rowSpan.Length, AllocationOptions.Clean))
+ using (IManagedByteBuffer temp = this.memoryAllocator.AllocateManagedByteBuffer(
+ rowSpan.Length,
+ AllocationOptions.Clean))
{
int scaleFactor = 255 / (ImageMaths.GetColorCountForBitDepth(this.bitDepth) - 1);
Span tempSpan = temp.GetSpan();
- ref byte tempSpanRef = ref MemoryMarshal.GetReference(tempSpan);
// We need to first create an array of luminance bytes then scale them down to the correct bit depth.
- PixelOperations.Instance.ToGray8Bytes(rowSpan, tempSpan, rowSpan.Length);
+ PixelOperations.Instance.ToGray8Bytes(
+ this.configuration,
+ rowSpan,
+ tempSpan,
+ rowSpan.Length);
this.ScaleDownFrom8BitArray(tempSpan, rawScanlineSpan, this.bitDepth, scaleFactor);
}
}
@@ -370,7 +393,7 @@ namespace SixLabors.ImageSharp.Formats.Png
{
Span rgbaSpan = rgbaBuffer.GetSpan();
ref Rgba64 rgbaRef = ref MemoryMarshal.GetReference(rgbaSpan);
- PixelOperations.Instance.ToRgba64(rowSpan, rgbaSpan);
+ PixelOperations.Instance.ToRgba64(this.configuration, rowSpan, rgbaSpan);
// Can't map directly to byte array as it's big endian.
for (int x = 0, o = 0; x < rgbaSpan.Length; x++, o += 4)
@@ -390,7 +413,8 @@ namespace SixLabors.ImageSharp.Formats.Png
for (int x = 0, o = 0; x < rowSpan.Length; x++, o += 2)
{
Unsafe.Add(ref rowSpanRef, x).ToRgba32(ref rgba);
- Unsafe.Add(ref rawScanlineSpanRef, o) = ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B);
+ Unsafe.Add(ref rawScanlineSpanRef, o) =
+ ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B);
Unsafe.Add(ref rawScanlineSpanRef, o + 1) = rgba.A;
}
}
@@ -412,14 +436,22 @@ namespace SixLabors.ImageSharp.Formats.Png
case 4:
{
// 8 bit Rgba
- PixelOperations.Instance.ToRgba32Bytes(rowSpan, rawScanlineSpan, this.width);
+ PixelOperations.Instance.ToRgba32Bytes(
+ this.configuration,
+ rowSpan,
+ rawScanlineSpan,
+ this.width);
break;
}
case 3:
{
// 8 bit Rgb
- PixelOperations.Instance.ToRgb24Bytes(rowSpan, rawScanlineSpan, this.width);
+ PixelOperations.Instance.ToRgb24Bytes(
+ this.configuration,
+ rowSpan,
+ rawScanlineSpan,
+ this.width);
break;
}
@@ -430,7 +462,7 @@ namespace SixLabors.ImageSharp.Formats.Png
{
Span rgbaSpan = rgbaBuffer.GetSpan();
ref Rgba64 rgbaRef = ref MemoryMarshal.GetReference(rgbaSpan);
- PixelOperations.Instance.ToRgba64(rowSpan, rgbaSpan);
+ PixelOperations.Instance.ToRgba64(this.configuration, rowSpan, rgbaSpan);
// Can't map directly to byte array as it's big endian.
for (int x = 0, o = 0; x < rowSpan.Length; x++, o += 8)
@@ -453,7 +485,7 @@ namespace SixLabors.ImageSharp.Formats.Png
{
Span rgbSpan = rgbBuffer.GetSpan();
ref Rgb48 rgbRef = ref MemoryMarshal.GetReference(rgbSpan);
- PixelOperations.Instance.ToRgb48(rowSpan, rgbSpan);
+ PixelOperations.Instance.ToRgb48(this.configuration, rowSpan, rgbSpan);
// Can't map directly to byte array as it's big endian.
for (int x = 0, o = 0; x < rowSpan.Length; x++, o += 6)
@@ -676,7 +708,7 @@ namespace SixLabors.ImageSharp.Formats.Png
// Write the transparency data
if (anyAlpha)
{
- this.WriteChunk(stream, PngChunkType.PaletteAlpha, alphaTable.Array, 0, paletteLength);
+ this.WriteChunk(stream, PngChunkType.Transparency, alphaTable.Array, 0, paletteLength);
}
}
}
@@ -685,8 +717,8 @@ namespace SixLabors.ImageSharp.Formats.Png
/// Writes the physical dimension information to the stream.
///
/// The containing image data.
- /// The image meta data.
- private void WritePhysicalChunk(Stream stream, ImageMetaData meta)
+ /// The image metadata.
+ private void WritePhysicalChunk(Stream stream, ImageMetadata meta)
{
PhysicalChunkData.FromMetadata(meta).WriteTo(this.chunkDataBuffer);
@@ -694,11 +726,11 @@ namespace SixLabors.ImageSharp.Formats.Png
}
///
- /// Writes the eXIf chunk to the stream, if any EXIF Profile values are present in the meta data.
+ /// Writes the eXIf chunk to the stream, if any EXIF Profile values are present in the metadata.
///
/// The containing image data.
- /// The image meta data.
- private void WriteExifChunk(Stream stream, ImageMetaData meta)
+ /// The image metadata.
+ private void WriteExifChunk(Stream stream, ImageMetadata meta)
{
if (meta.ExifProfile?.Values.Count > 0)
{
@@ -724,6 +756,51 @@ namespace SixLabors.ImageSharp.Formats.Png
}
}
+ ///
+ /// Writes the transparency chunk to the stream
+ ///
+ /// The containing image data.
+ /// The image metadata.
+ private void WriteTransparencyChunk(Stream stream, PngMetadata pngMetadata)
+ {
+ Span alpha = this.chunkDataBuffer.AsSpan();
+ if (pngMetadata.ColorType == PngColorType.Rgb)
+ {
+ if (pngMetadata.TransparentRgb48.HasValue && this.use16Bit)
+ {
+ Rgb48 rgb = pngMetadata.TransparentRgb48.Value;
+ BinaryPrimitives.WriteUInt16LittleEndian(alpha, rgb.R);
+ BinaryPrimitives.WriteUInt16LittleEndian(alpha.Slice(2, 2), rgb.G);
+ BinaryPrimitives.WriteUInt16LittleEndian(alpha.Slice(4, 2), rgb.B);
+
+ this.WriteChunk(stream, PngChunkType.Transparency, this.chunkDataBuffer, 0, 6);
+ }
+ else if (pngMetadata.TransparentRgb24.HasValue)
+ {
+ alpha.Clear();
+ Rgb24 rgb = pngMetadata.TransparentRgb24.Value;
+ alpha[1] = rgb.R;
+ alpha[3] = rgb.G;
+ alpha[5] = rgb.B;
+ this.WriteChunk(stream, PngChunkType.Transparency, this.chunkDataBuffer, 0, 6);
+ }
+ }
+ else if (pngMetadata.ColorType == PngColorType.Grayscale)
+ {
+ if (pngMetadata.TransparentGray16.HasValue && this.use16Bit)
+ {
+ BinaryPrimitives.WriteUInt16LittleEndian(alpha, pngMetadata.TransparentGray16.Value.PackedValue);
+ this.WriteChunk(stream, PngChunkType.Transparency, this.chunkDataBuffer, 0, 2);
+ }
+ else if (pngMetadata.TransparentGray8.HasValue)
+ {
+ alpha.Clear();
+ alpha[1] = pngMetadata.TransparentGray8.Value.PackedValue;
+ this.WriteChunk(stream, PngChunkType.Transparency, this.chunkDataBuffer, 0, 2);
+ }
+ }
+ }
+
///
/// Writes the pixel information to the stream.
///
@@ -871,9 +948,9 @@ namespace SixLabors.ImageSharp.Formats.Png
ref byte sourceRef = ref MemoryMarshal.GetReference(source);
ref byte resultRef = ref MemoryMarshal.GetReference(result);
- byte mask = (byte)(0xFF >> (8 - bits));
- byte shift0 = (byte)(8 - bits);
int shift = 8 - bits;
+ byte mask = (byte)(0xFF >> shift);
+ byte shift0 = (byte)shift;
int v = 0;
int resultOffset = 0;
@@ -922,4 +999,4 @@ namespace SixLabors.ImageSharp.Formats.Png
return scanlineLength / mod;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Formats/Png/PngFormat.cs b/src/ImageSharp/Formats/Png/PngFormat.cs
index 210e2a837d..408e37802f 100644
--- a/src/ImageSharp/Formats/Png/PngFormat.cs
+++ b/src/ImageSharp/Formats/Png/PngFormat.cs
@@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Formats.Png
///
/// Registers the image encoders, decoders and mime type detectors for the png format.
///
- public sealed class PngFormat : IImageFormat
+ public sealed class PngFormat : IImageFormat
{
private PngFormat()
{
@@ -32,6 +32,6 @@ namespace SixLabors.ImageSharp.Formats.Png
public IEnumerable FileExtensions => PngConstants.FileExtensions;
///
- public PngMetaData CreateDefaultFormatMetaData() => new PngMetaData();
+ public PngMetadata CreateDefaultFormatMetadata() => new PngMetadata();
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Png/PngMetaData.cs b/src/ImageSharp/Formats/Png/PngMetaData.cs
index 9c76765146..dd951763f7 100644
--- a/src/ImageSharp/Formats/Png/PngMetaData.cs
+++ b/src/ImageSharp/Formats/Png/PngMetaData.cs
@@ -1,29 +1,36 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
+using SixLabors.ImageSharp.PixelFormats;
+
namespace SixLabors.ImageSharp.Formats.Png
{
///
/// Provides Png specific metadata information for the image.
///
- public class PngMetaData : IDeepCloneable
+ public class PngMetadata : IDeepCloneable
{
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
- public PngMetaData()
+ public PngMetadata()
{
}
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
/// The metadata to create an instance from.
- private PngMetaData(PngMetaData other)
+ private PngMetadata(PngMetadata other)
{
this.BitDepth = other.BitDepth;
this.ColorType = other.ColorType;
this.Gamma = other.Gamma;
+ this.HasTrans = other.HasTrans;
+ this.TransparentGray8 = other.TransparentGray8;
+ this.TransparentGray16 = other.TransparentGray16;
+ this.TransparentRgb24 = other.TransparentRgb24;
+ this.TransparentRgb48 = other.TransparentRgb48;
}
///
@@ -42,7 +49,32 @@ namespace SixLabors.ImageSharp.Formats.Png
///
public float Gamma { get; set; }
+ ///
+ /// Gets or sets the Rgb 24 transparent color. This represents any color in an 8 bit Rgb24 encoded png that should be transparent
+ ///
+ public Rgb24? TransparentRgb24 { get; set; }
+
+ ///
+ /// Gets or sets the Rgb 48 transparent color. This represents any color in a 16 bit Rgb24 encoded png that should be transparent
+ ///
+ public Rgb48? TransparentRgb48 { get; set; }
+
+ ///
+ /// Gets or sets the 8 bit grayscale transparent color. This represents any color in an 8 bit grayscale encoded png that should be transparent
+ ///
+ public Gray8? TransparentGray8 { get; set; }
+
+ ///
+ /// Gets or sets the 16 bit grayscale transparent color. This represents any color in a 16 bit grayscale encoded png that should be transparent
+ ///
+ public Gray16? TransparentGray16 { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether the image has transparency chunk and markers were decoded
+ ///
+ public bool HasTrans { get; set; }
+
///
- public IDeepCloneable DeepClone() => new PngMetaData(this);
+ public IDeepCloneable DeepClone() => new PngMetadata(this);
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs b/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs
index e4a2562e65..c23694951d 100644
--- a/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs
+++ b/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs
@@ -11,6 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Png
{
///
/// Provides methods to allow the decoding of raw scanlines to image rows of different pixel formats.
+ /// TODO: We should make this a stateful class or struct to reduce the number of arguments on methods (most are invariant).
///
internal static class PngScanlineProcessor
{
@@ -19,8 +20,8 @@ namespace SixLabors.ImageSharp.Formats.Png
ReadOnlySpan scanlineSpan,
Span rowSpan,
bool hasTrans,
- ushort luminance16Trans,
- byte luminanceTrans)
+ Gray16 luminance16Trans,
+ Gray8 luminanceTrans)
where TPixel : struct, IPixel
{
TPixel pixel = default;
@@ -61,7 +62,7 @@ namespace SixLabors.ImageSharp.Formats.Png
rgba64.R = luminance;
rgba64.G = luminance;
rgba64.B = luminance;
- rgba64.A = luminance.Equals(luminance16Trans) ? ushort.MinValue : ushort.MaxValue;
+ rgba64.A = luminance.Equals(luminance16Trans.PackedValue) ? ushort.MinValue : ushort.MaxValue;
pixel.FromRgba64(rgba64);
Unsafe.Add(ref rowSpanRef, x) = pixel;
@@ -69,7 +70,7 @@ namespace SixLabors.ImageSharp.Formats.Png
}
else
{
- byte scaledLuminanceTrans = (byte)(luminanceTrans * scaleFactor);
+ byte scaledLuminanceTrans = (byte)(luminanceTrans.PackedValue * scaleFactor);
Rgba32 rgba32 = default;
for (int x = 0; x < header.Width; x++)
{
@@ -92,8 +93,8 @@ namespace SixLabors.ImageSharp.Formats.Png
int pixelOffset,
int increment,
bool hasTrans,
- ushort luminance16Trans,
- byte luminanceTrans)
+ Gray16 luminance16Trans,
+ Gray8 luminanceTrans)
where TPixel : struct, IPixel
{
TPixel pixel = default;
@@ -134,7 +135,7 @@ namespace SixLabors.ImageSharp.Formats.Png
rgba64.R = luminance;
rgba64.G = luminance;
rgba64.B = luminance;
- rgba64.A = luminance.Equals(luminance16Trans) ? ushort.MinValue : ushort.MaxValue;
+ rgba64.A = luminance.Equals(luminance16Trans.PackedValue) ? ushort.MinValue : ushort.MaxValue;
pixel.FromRgba64(rgba64);
Unsafe.Add(ref rowSpanRef, x) = pixel;
@@ -142,11 +143,11 @@ namespace SixLabors.ImageSharp.Formats.Png
}
else
{
- byte scaledLuminanceTrans = (byte)(luminanceTrans * scaleFactor);
+ byte scaledLuminanceTrans = (byte)(luminanceTrans.PackedValue * scaleFactor);
Rgba32 rgba32 = default;
- for (int x = pixelOffset; x < header.Width; x += increment)
+ for (int x = pixelOffset, o = 0; x < header.Width; x += increment, o++)
{
- byte luminance = (byte)(Unsafe.Add(ref scanlineSpanRef, x) * scaleFactor);
+ byte luminance = (byte)(Unsafe.Add(ref scanlineSpanRef, o) * scaleFactor);
rgba32.R = luminance;
rgba32.G = luminance;
rgba32.B = luminance;
@@ -189,12 +190,11 @@ namespace SixLabors.ImageSharp.Formats.Png
else
{
Rgba32 rgba32 = default;
- int bps = bytesPerSample;
for (int x = 0; x < header.Width; x++)
{
int offset = x * bytesPerPixel;
byte luminance = Unsafe.Add(ref scanlineSpanRef, offset);
- byte alpha = Unsafe.Add(ref scanlineSpanRef, offset + bps);
+ byte alpha = Unsafe.Add(ref scanlineSpanRef, offset + bytesPerSample);
rgba32.R = luminance;
rgba32.G = luminance;
@@ -240,9 +240,9 @@ namespace SixLabors.ImageSharp.Formats.Png
else
{
Rgba32 rgba32 = default;
+ int offset = 0;
for (int x = pixelOffset; x < header.Width; x += increment)
{
- int offset = x * bytesPerPixel;
byte luminance = Unsafe.Add(ref scanlineSpanRef, offset);
byte alpha = Unsafe.Add(ref scanlineSpanRef, offset + bytesPerSample);
rgba32.R = luminance;
@@ -252,6 +252,7 @@ namespace SixLabors.ImageSharp.Formats.Png
pixel.FromRgba32(rgba32);
Unsafe.Add(ref rowSpanRef, x) = pixel;
+ offset += bytesPerPixel;
}
}
}
@@ -346,6 +347,7 @@ namespace SixLabors.ImageSharp.Formats.Png
}
public static void ProcessRgbScanline(
+ Configuration configuration,
in PngHeader header,
ReadOnlySpan scanlineSpan,
Span rowSpan,
@@ -357,7 +359,6 @@ namespace SixLabors.ImageSharp.Formats.Png
where TPixel : struct, IPixel
{
TPixel pixel = default;
- ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan);
ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan);
if (!hasTrans)
@@ -377,7 +378,7 @@ namespace SixLabors.ImageSharp.Formats.Png
}
else
{
- PixelOperations.Instance.FromRgb24Bytes(scanlineSpan, rowSpan, header.Width);
+ PixelOperations.Instance.FromRgb24Bytes(configuration, scanlineSpan, rowSpan, header.Width);
}
return;
@@ -500,6 +501,7 @@ namespace SixLabors.ImageSharp.Formats.Png
}
public static void ProcessRgbaScanline(
+ Configuration configuration,
in PngHeader header,
ReadOnlySpan scanlineSpan,
Span rowSpan,
@@ -526,7 +528,7 @@ namespace SixLabors.ImageSharp.Formats.Png
}
else
{
- PixelOperations.Instance.FromRgba32Bytes(scanlineSpan, rowSpan, header.Width);
+ PixelOperations.Instance.FromRgba32Bytes(configuration, scanlineSpan, rowSpan, header.Width);
}
}
diff --git a/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs b/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs
index 583175b566..71141a8939 100644
--- a/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs
+++ b/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs
@@ -126,8 +126,9 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
int bytesToRead = Math.Min(count, this.currentDataRemaining);
this.currentDataRemaining -= bytesToRead;
int bytesRead = this.innerStream.Read(buffer, offset, bytesToRead);
+ long length = this.innerStream.Length;
- // keep reading data until we've reached the end of the stream or filled the buffer
+ // Keep reading data until we've reached the end of the stream or filled the buffer
while (this.currentDataRemaining == 0 && bytesRead < count)
{
this.currentDataRemaining = this.getData();
@@ -138,6 +139,12 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
}
offset += bytesRead;
+
+ if (offset >= length)
+ {
+ return bytesRead;
+ }
+
bytesToRead = Math.Min(count - bytesRead, this.currentDataRemaining);
this.currentDataRemaining -= bytesToRead;
bytesRead += this.innerStream.Read(buffer, offset, bytesToRead);
diff --git a/src/ImageSharp/IImageInfo.cs b/src/ImageSharp/IImageInfo.cs
index 25d5ec7cab..b270c2c4de 100644
--- a/src/ImageSharp/IImageInfo.cs
+++ b/src/ImageSharp/IImageInfo.cs
@@ -2,13 +2,13 @@
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Formats;
-using SixLabors.ImageSharp.MetaData;
+using SixLabors.ImageSharp.Metadata;
namespace SixLabors.ImageSharp
{
///
- /// Encapsulates properties that descibe basic image information including dimensions, pixel type information
- /// and additional metadata
+ /// Encapsulates properties that describe basic image information including dimensions, pixel type information
+ /// and additional metadata.
///
public interface IImageInfo
{
@@ -30,6 +30,6 @@ namespace SixLabors.ImageSharp
///
/// Gets the metadata of the image.
///
- ImageMetaData MetaData { get; }
+ ImageMetadata Metadata { get; }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/IO/DoubleBufferedStreamReader.cs b/src/ImageSharp/IO/DoubleBufferedStreamReader.cs
index 94a2f2cbfc..6d5673d5a4 100644
--- a/src/ImageSharp/IO/DoubleBufferedStreamReader.cs
+++ b/src/ImageSharp/IO/DoubleBufferedStreamReader.cs
@@ -13,10 +13,10 @@ namespace SixLabors.ImageSharp.IO
/// A stream reader that add a secondary level buffer in addition to native stream buffered reading
/// to reduce the overhead of small incremental reads.
///
- internal class DoubleBufferedStreamReader : IDisposable
+ internal sealed class DoubleBufferedStreamReader : IDisposable
{
///
- /// The length, in bytes, of the buffering chunk
+ /// The length, in bytes, of the buffering chunk.
///
public const int ChunkLength = 4096;
@@ -42,18 +42,19 @@ namespace SixLabors.ImageSharp.IO
public DoubleBufferedStreamReader(MemoryAllocator memoryAllocator, Stream stream)
{
this.stream = stream;
+ this.Position = (int)stream.Position;
this.length = (int)stream.Length;
this.managedBuffer = memoryAllocator.AllocateManagedByteBuffer(ChunkLength, AllocationOptions.Clean);
this.bufferChunk = this.managedBuffer.Array;
}
///
- /// Gets the length, in bytes, of the stream
+ /// Gets the length, in bytes, of the stream.
///
public long Length => this.length;
///
- /// Gets or sets the current position within the stream
+ /// Gets or sets the current position within the stream.
///
public long Position
{
diff --git a/src/ImageSharp/IO/Endianness.cs b/src/ImageSharp/IO/Endianness.cs
deleted file mode 100644
index 59b2ae77c1..0000000000
--- a/src/ImageSharp/IO/Endianness.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright (c) Six Labors and contributors.
-// Licensed under the Apache License, Version 2.0.
-
-namespace SixLabors.ImageSharp.IO
-{
- ///
- /// Endianness of a converter
- ///
- internal enum Endianness
- {
- ///
- /// Little endian - least significant byte first
- ///
- LittleEndian,
-
- ///
- /// Big endian - most significant byte first
- ///
- BigEndian
- }
-}
diff --git a/src/ImageSharp/IO/LocalFileSystem.cs b/src/ImageSharp/IO/LocalFileSystem.cs
index dc5901ff92..11f3d79723 100644
--- a/src/ImageSharp/IO/LocalFileSystem.cs
+++ b/src/ImageSharp/IO/LocalFileSystem.cs
@@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.IO
///
/// A wrapper around the local File apis.
///
- internal class LocalFileSystem : IFileSystem
+ internal sealed class LocalFileSystem : IFileSystem
{
///
public Stream OpenRead(string path) => File.OpenRead(path);
diff --git a/src/ImageSharp/Image.Decode.cs b/src/ImageSharp/Image.Decode.cs
index 8b9f3fdb5b..9e83d173f2 100644
--- a/src/ImageSharp/Image.Decode.cs
+++ b/src/ImageSharp/Image.Decode.cs
@@ -5,6 +5,7 @@ using System.IO;
using System.Linq;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Memory;
+using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
@@ -15,6 +16,29 @@ namespace SixLabors.ImageSharp
///
public static partial class Image
{
+ ///
+ /// Creates an instance backed by an uninitialized memory buffer.
+ /// This is an optimized creation method intended to be used by decoders.
+ /// The image might be filled with memory garbage.
+ ///
+ /// The pixel type
+ /// The
+ /// The width of the image
+ /// The height of the image
+ /// The
+ /// The result
+ internal static Image CreateUninitialized(
+ Configuration configuration,
+ int width,
+ int height,
+ ImageMetadata metadata)
+ where TPixel : struct, IPixel
+ {
+ Buffer2D uninitializedMemoryBuffer =
+ configuration.MemoryAllocator.Allocate2D(width, height);
+ return new Image(configuration, uninitializedMemoryBuffer.MemorySource, width, height, metadata);
+ }
+
///
/// By reading the header on the provided stream this calculates the images format.
///
diff --git a/src/ImageSharp/Image.FromBytes.cs b/src/ImageSharp/Image.FromBytes.cs
index 07adc03ff6..7ceeea9498 100644
--- a/src/ImageSharp/Image.FromBytes.cs
+++ b/src/ImageSharp/Image.FromBytes.cs
@@ -3,7 +3,6 @@
using System;
using System.IO;
-using System.Linq;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.PixelFormats;
@@ -32,7 +31,7 @@ namespace SixLabors.ImageSharp
/// The mime type or null if none found.
public static IImageFormat DetectFormat(Configuration config, byte[] data)
{
- using (Stream stream = new MemoryStream(data))
+ using (var stream = new MemoryStream(data))
{
return DetectFormat(config, stream);
}
@@ -118,9 +117,9 @@ namespace SixLabors.ImageSharp
public static Image Load(Configuration config, byte[] data)
where TPixel : struct, IPixel
{
- using (var memoryStream = new MemoryStream(data))
+ using (var steram = new MemoryStream(data))
{
- return Load(config, memoryStream);
+ return Load(config, steram);
}
}
@@ -135,9 +134,9 @@ namespace SixLabors.ImageSharp
public static Image Load(Configuration config, byte[] data, out IImageFormat format)
where TPixel : struct, IPixel
{
- using (var memoryStream = new MemoryStream(data))
+ using (var stream = new MemoryStream(data))
{
- return Load(config, memoryStream, out format);
+ return Load(config, stream, out format);
}
}
@@ -151,9 +150,9 @@ namespace SixLabors.ImageSharp
public static Image Load(byte[] data, IImageDecoder decoder)
where TPixel : struct, IPixel
{
- using (var memoryStream = new MemoryStream(data))
+ using (var stream = new MemoryStream(data))
{
- return Load(memoryStream, decoder);
+ return Load(stream, decoder);
}
}
@@ -198,18 +197,17 @@ namespace SixLabors.ImageSharp
return null;
}
- IImageFormat format = default;
foreach (IImageFormatDetector detector in config.ImageFormatsManager.FormatDetectors)
{
IImageFormat f = detector.DetectFormat(data);
if (f != null)
{
- format = f;
+ return f;
}
}
- return format;
+ return default;
}
///
diff --git a/src/ImageSharp/Image.WrapMemory.cs b/src/ImageSharp/Image.WrapMemory.cs
index e8d9ab7547..3d788bb73a 100644
--- a/src/ImageSharp/Image.WrapMemory.cs
+++ b/src/ImageSharp/Image.WrapMemory.cs
@@ -5,9 +5,8 @@ using System;
using System.Buffers;
using SixLabors.ImageSharp.Memory;
-using SixLabors.ImageSharp.MetaData;
+using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats;
-using SixLabors.Memory;
namespace SixLabors.ImageSharp
{
@@ -17,38 +16,38 @@ namespace SixLabors.ImageSharp
public static partial class Image
{
///
- /// Wraps an existing contigous memory area of 'width'x'height' pixels,
+ /// Wraps an existing contiguous memory area of 'width' x 'height' pixels,
/// allowing to view/manipulate it as an ImageSharp instance.
///
/// The pixel type
/// The
- /// The pixel memory
- /// The width of the memory image
- /// The height of the memory image
- /// The
+ /// The pixel memory.
+ /// The width of the memory image.
+ /// The height of the memory image.
+ /// The .
/// An instance
public static Image WrapMemory(
Configuration config,
Memory pixelMemory,
int width,
int height,
- ImageMetaData metaData)
+ ImageMetadata metadata)
where TPixel : struct, IPixel
{
var memorySource = new MemorySource(pixelMemory);
- return new Image(config, memorySource, width, height, metaData);
+ return new Image(config, memorySource, width, height, metadata);
}
///
- /// Wraps an existing contigous memory area of 'width'x'height' pixels,
+ /// Wraps an existing contiguous memory area of 'width' x 'height' pixels,
/// allowing to view/manipulate it as an ImageSharp instance.
///
/// The pixel type
/// The
- /// The pixel memory
- /// The width of the memory image
- /// The height of the memory image
- /// An instance
+ /// The pixel memory.
+ /// The width of the memory image.
+ /// The height of the memory image.
+ /// An instance.
public static Image WrapMemory(
Configuration config,
Memory pixelMemory,
@@ -56,19 +55,19 @@ namespace SixLabors.ImageSharp
int height)
where TPixel : struct, IPixel
{
- return WrapMemory(config, pixelMemory, width, height, new ImageMetaData());
+ return WrapMemory(config, pixelMemory, width, height, new ImageMetadata());
}
///
- /// Wraps an existing contigous memory area of 'width'x'height' pixels,
+ /// Wraps an existing contiguous memory area of 'width' x 'height' pixels,
/// allowing to view/manipulate it as an ImageSharp instance.
/// The memory is being observed, the caller remains responsible for managing it's lifecycle.
///
- /// The pixel type
- /// The pixel memory
- /// The width of the memory image
- /// The height of the memory image
- /// An instance
+ /// The pixel type.
+ /// The pixel memory.
+ /// The width of the memory image.
+ /// The height of the memory image.
+ /// An instance.
public static Image WrapMemory(
Memory pixelMemory,
int width,
@@ -79,43 +78,43 @@ namespace SixLabors.ImageSharp
}
///
- /// Wraps an existing contigous memory area of 'width'x'height' pixels,
+ /// Wraps an existing contiguous memory area of 'width' x 'height' pixels,
/// allowing to view/manipulate it as an ImageSharp instance.
- /// The ownership of the is being transfered to the new instance,
+ /// The ownership of the is being transferred to the new instance,
/// meaning that the caller is not allowed to dispose .
/// It will be disposed together with the result image.
///
/// The pixel type
/// The
- /// The that is being transfered to the image
- /// The width of the memory image
- /// The height of the memory image
- /// The
+ /// The that is being transferred to the image
+ /// The width of the memory image.
+ /// The height of the memory image.
+ /// The
/// An instance
public static Image WrapMemory(
Configuration config,
IMemoryOwner pixelMemoryOwner,
int width,
int height,
- ImageMetaData metaData)
+ ImageMetadata metadata)
where TPixel : struct, IPixel
{
var memorySource = new MemorySource(pixelMemoryOwner, false);
- return new Image(config, memorySource, width, height, metaData);
+ return new Image(config, memorySource, width, height, metadata);
}
///
- /// Wraps an existing contigous memory area of 'width'x'height' pixels,
+ /// Wraps an existing contiguous memory area of 'width' x 'height' pixels,
/// allowing to view/manipulate it as an ImageSharp instance.
- /// The ownership of the is being transfered to the new instance,
+ /// The ownership of the is being transferred to the new instance,
/// meaning that the caller is not allowed to dispose .
/// It will be disposed together with the result image.
///
- /// The pixel type
+ /// The pixel type.
/// The
- /// The that is being transfered to the image
- /// The width of the memory image
- /// The height of the memory image
+ /// The that is being transferred to the image.
+ /// The width of the memory image.
+ /// The height of the memory image.
/// An instance
public static Image WrapMemory(
Configuration config,
@@ -124,21 +123,21 @@ namespace SixLabors.ImageSharp
int height)
where TPixel : struct, IPixel
{
- return WrapMemory(config, pixelMemoryOwner, width, height, new ImageMetaData());
+ return WrapMemory(config, pixelMemoryOwner, width, height, new ImageMetadata());
}
///
- /// Wraps an existing contigous memory area of 'width'x'height' pixels,
+ /// Wraps an existing contiguous memory area of 'width' x 'height' pixels,
/// allowing to view/manipulate it as an ImageSharp instance.
- /// The ownership of the is being transfered to the new instance,
+ /// The ownership of the is being transferred to the new instance,
/// meaning that the caller is not allowed to dispose .
/// It will be disposed together with the result image.
///
/// The pixel type
- /// The that is being transfered to the image
- /// The width of the memory image
- /// The height of the memory image
- /// An instance
+ /// The that is being transferred to the image.
+ /// The width of the memory image.
+ /// The height of the memory image.
+ /// An instance.
public static Image WrapMemory(
IMemoryOwner pixelMemoryOwner,
int width,
diff --git a/src/ImageSharp/ImageExtensions.Internal.cs b/src/ImageSharp/ImageExtensions.Internal.cs
index dfdbbd89b8..5b5e566659 100644
--- a/src/ImageSharp/ImageExtensions.Internal.cs
+++ b/src/ImageSharp/ImageExtensions.Internal.cs
@@ -3,7 +3,6 @@
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
-using SixLabors.Memory;
namespace SixLabors.ImageSharp
{
diff --git a/src/ImageSharp/ImageExtensions.cs b/src/ImageSharp/ImageExtensions.cs
index e579bec1a6..5010451b8e 100644
--- a/src/ImageSharp/ImageExtensions.cs
+++ b/src/ImageSharp/ImageExtensions.cs
@@ -12,7 +12,7 @@ using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp
{
///
- /// Extension methods over Image{TPixel}
+ /// Extension methods over Image{TPixel}.
///
public static partial class ImageExtensions
{
@@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp
if (format is null)
{
var sb = new StringBuilder();
- sb.AppendLine($"Can't find a format that is associated with the file extention '{ext}'. Registered formats with there extensions include:");
+ sb.AppendLine($"No encoder was found for extension '{ext}'. Registered encoders include:");
foreach (IImageFormat fmt in source.GetConfiguration().ImageFormats)
{
sb.AppendLine($" - {fmt.Name} : {string.Join(", ", fmt.FileExtensions)}");
@@ -47,7 +47,7 @@ namespace SixLabors.ImageSharp
if (encoder is null)
{
var sb = new StringBuilder();
- sb.AppendLine($"Can't find encoder for file extention '{ext}' using image format '{format.Name}'. Registered encoders include:");
+ sb.AppendLine($"No encoder was found for extension '{ext}' using image format '{format.Name}'. Registered encoders include:");
foreach (KeyValuePair enc in source.GetConfiguration().ImageFormatsManager.ImageEncoders)
{
sb.AppendLine($" - {enc.Key} : {enc.Value.GetType().Name}");
@@ -94,7 +94,7 @@ namespace SixLabors.ImageSharp
if (encoder is null)
{
var sb = new StringBuilder();
- sb.AppendLine("Can't find encoder for provided mime type. Available encoded:");
+ sb.AppendLine("No encoder was found for the provided mime type. Registered encoders include:");
foreach (KeyValuePair val in source.GetConfiguration().ImageFormatsManager.ImageEncoders)
{
diff --git a/src/ImageSharp/ImageFrameCollection.cs b/src/ImageSharp/ImageFrameCollection.cs
index bbe05ce435..7e516434cb 100644
--- a/src/ImageSharp/ImageFrameCollection.cs
+++ b/src/ImageSharp/ImageFrameCollection.cs
@@ -192,7 +192,7 @@ namespace SixLabors.ImageSharp
this.frames.Remove(frame);
- return new Image(this.parent.GetConfiguration(), this.parent.MetaData.DeepClone(), new[] { frame });
+ return new Image(this.parent.GetConfiguration(), this.parent.Metadata.DeepClone(), new[] { frame });
}
///
@@ -205,7 +205,7 @@ namespace SixLabors.ImageSharp
{
ImageFrame frame = this[index];
ImageFrame clonedFrame = frame.Clone();
- return new Image(this.parent.GetConfiguration(), this.parent.MetaData.DeepClone(), new[] { clonedFrame });
+ return new Image(this.parent.GetConfiguration(), this.parent.Metadata.DeepClone(), new[] { clonedFrame });
}
///
diff --git a/src/ImageSharp/ImageFrame{TPixel}.cs b/src/ImageSharp/ImageFrame{TPixel}.cs
index 25c517d442..4f0be59713 100644
--- a/src/ImageSharp/ImageFrame{TPixel}.cs
+++ b/src/ImageSharp/ImageFrame{TPixel}.cs
@@ -6,7 +6,7 @@ using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
-using SixLabors.ImageSharp.MetaData;
+using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.ParallelUtils;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
@@ -21,7 +21,6 @@ namespace SixLabors.ImageSharp
public sealed class ImageFrame : IPixelSource, IDisposable
where TPixel : struct, IPixel
{
- private readonly Configuration configuration;
private bool isDisposed;
///
@@ -31,7 +30,7 @@ namespace SixLabors.ImageSharp
/// The width of the image in pixels.
/// The height of the image in pixels.
internal ImageFrame(Configuration configuration, int width, int height)
- : this(configuration, width, height, new ImageFrameMetaData())
+ : this(configuration, width, height, new ImageFrameMetadata())
{
}
@@ -40,9 +39,9 @@ namespace SixLabors.ImageSharp
///
/// The configuration which allows altering default behaviour or extending the library.
/// The of the frame.
- /// The meta data.
- internal ImageFrame(Configuration configuration, Size size, ImageFrameMetaData metaData)
- : this(configuration, size.Width, size.Height, metaData)
+ /// The metadata.
+ internal ImageFrame(Configuration configuration, Size size, ImageFrameMetadata metadata)
+ : this(configuration, size.Width, size.Height, metadata)
{
}
@@ -52,9 +51,9 @@ namespace SixLabors.ImageSharp
/// The configuration which allows altering default behaviour or extending the library.
/// The width of the image in pixels.
/// The height of the image in pixels.
- /// The meta data.
- internal ImageFrame(Configuration configuration, int width, int height, ImageFrameMetaData metaData)
- : this(configuration, width, height, default(TPixel), metaData)
+ /// The metadata.
+ internal ImageFrame(Configuration configuration, int width, int height, ImageFrameMetadata metadata)
+ : this(configuration, width, height, default(TPixel), metadata)
{
}
@@ -66,7 +65,7 @@ namespace SixLabors.ImageSharp
/// The height of the image in pixels.
/// The color to clear the image with.
internal ImageFrame(Configuration configuration, int width, int height, TPixel backgroundColor)
- : this(configuration, width, height, backgroundColor, new ImageFrameMetaData())
+ : this(configuration, width, height, backgroundColor, new ImageFrameMetadata())
{
}
@@ -77,17 +76,17 @@ namespace SixLabors.ImageSharp
/// The width of the image in pixels.
/// The height of the image in pixels.
/// The color to clear the image with.
- /// The meta data.
- internal ImageFrame(Configuration configuration, int width, int height, TPixel backgroundColor, ImageFrameMetaData metaData)
+ /// The metadata.
+ internal ImageFrame(Configuration configuration, int width, int height, TPixel backgroundColor, ImageFrameMetadata metadata)
{
Guard.NotNull(configuration, nameof(configuration));
Guard.MustBeGreaterThan(width, 0, nameof(width));
Guard.MustBeGreaterThan(height, 0, nameof(height));
- this.configuration = configuration;
+ this.Configuration = configuration;
this.MemoryAllocator = configuration.MemoryAllocator;
this.PixelBuffer = this.MemoryAllocator.Allocate2D(width, height);
- this.MetaData = metaData ?? new ImageFrameMetaData();
+ this.Metadata = metadata ?? new ImageFrameMetadata();
this.Clear(configuration.GetParallelOptions(), backgroundColor);
}
@@ -99,7 +98,7 @@ namespace SixLabors.ImageSharp
/// The height of the image in pixels.
/// The memory source.
internal ImageFrame(Configuration configuration, int width, int height, MemorySource memorySource)
- : this(configuration, width, height, memorySource, new ImageFrameMetaData())
+ : this(configuration, width, height, memorySource, new ImageFrameMetadata())
{
}
@@ -110,18 +109,18 @@ namespace SixLabors.ImageSharp
/// The width of the image in pixels.
/// The height of the image in pixels.
/// The memory source.
- /// The meta data.
- internal ImageFrame(Configuration configuration, int width, int height, MemorySource memorySource, ImageFrameMetaData metaData)
+ /// The metadata.
+ internal ImageFrame(Configuration configuration, int width, int height, MemorySource memorySource, ImageFrameMetadata metadata)
{
Guard.NotNull(configuration, nameof(configuration));
Guard.MustBeGreaterThan(width, 0, nameof(width));
Guard.MustBeGreaterThan(height, 0, nameof(height));
- Guard.NotNull(metaData, nameof(metaData));
+ Guard.NotNull(metadata, nameof(metadata));
- this.configuration = configuration;
+ this.Configuration = configuration;
this.MemoryAllocator = configuration.MemoryAllocator;
this.PixelBuffer = new Buffer2D(memorySource, width, height);
- this.MetaData = metaData;
+ this.Metadata = metadata;
}
///
@@ -134,11 +133,11 @@ namespace SixLabors.ImageSharp
Guard.NotNull(configuration, nameof(configuration));
Guard.NotNull(source, nameof(source));
- this.configuration = configuration;
+ this.Configuration = configuration;
this.MemoryAllocator = configuration.MemoryAllocator;
this.PixelBuffer = this.MemoryAllocator.Allocate2D(source.PixelBuffer.Width, source.PixelBuffer.Height);
source.PixelBuffer.GetSpan().CopyTo(this.PixelBuffer.GetSpan());
- this.MetaData = source.MetaData.DeepClone();
+ this.Metadata = source.Metadata.DeepClone();
}
///
@@ -146,6 +145,11 @@ namespace SixLabors.ImageSharp
///
public MemoryAllocator MemoryAllocator { get; }
+ ///
+ /// Gets the instance associated with this .
+ ///
+ internal Configuration Configuration { get; }
+
///
/// Gets the image pixels. Not private as Buffer2D requires an array in its constructor.
///
@@ -165,9 +169,9 @@ namespace SixLabors.ImageSharp
public int Height => this.PixelBuffer.Height;
///
- /// Gets the meta data of the frame.
+ /// Gets the metadata of the frame.
///
- public ImageFrameMetaData MetaData { get; }
+ public ImageFrameMetadata Metadata { get; }
///
/// Gets or sets the pixel at the specified position.
@@ -248,13 +252,13 @@ namespace SixLabors.ImageSharp
}
///
- public override string ToString() => $"ImageFrame<{typeof(TPixel).Name}>: {this.Width}x{this.Height}";
+ public override string ToString() => $"ImageFrame<{typeof(TPixel).Name}>({this.Width}x{this.Height})";
///
/// Clones the current instance.
///
/// The
- internal ImageFrame Clone() => this.Clone(this.configuration);
+ internal ImageFrame Clone() => this.Clone(this.Configuration);
///
/// Clones the current instance.
@@ -269,7 +273,7 @@ namespace SixLabors.ImageSharp
/// The pixel format.
/// The
internal ImageFrame CloneAs()
- where TPixel2 : struct, IPixel => this.CloneAs(this.configuration);
+ where TPixel2 : struct, IPixel => this.CloneAs(this.Configuration);
///
/// Returns a copy of the image frame in the given pixel format.
@@ -285,7 +289,7 @@ namespace SixLabors.ImageSharp
return this.Clone(configuration) as ImageFrame;
}
- var target = new ImageFrame(configuration, this.Width, this.Height, this.MetaData.DeepClone());
+ var target = new ImageFrame(configuration, this.Width, this.Height, this.Metadata.DeepClone());
ParallelHelper.IterateRows(
this.Bounds(),
@@ -296,7 +300,7 @@ namespace SixLabors.ImageSharp
{
Span sourceRow = this.GetPixelRowSpan(y);
Span targetRow = target.GetPixelRowSpan(y);
- PixelOperations.Instance.To(sourceRow, targetRow);
+ PixelOperations.Instance.To(configuration, sourceRow, targetRow);
}
});
diff --git a/src/ImageSharp/ImageInfo.cs b/src/ImageSharp/ImageInfo.cs
index 6f894cb599..12dcf1ed74 100644
--- a/src/ImageSharp/ImageInfo.cs
+++ b/src/ImageSharp/ImageInfo.cs
@@ -2,7 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Formats;
-using SixLabors.ImageSharp.MetaData;
+using SixLabors.ImageSharp.Metadata;
namespace SixLabors.ImageSharp
{
@@ -17,13 +17,13 @@ namespace SixLabors.ImageSharp
/// The image pixel type information.
/// The width of the image in pixels.
/// The height of the image in pixels.
- /// The images metadata.
- public ImageInfo(PixelTypeInfo pixelType, int width, int height, ImageMetaData metaData)
+ /// The images metadata.
+ public ImageInfo(PixelTypeInfo pixelType, int width, int height, ImageMetadata metadata)
{
this.PixelType = pixelType;
this.Width = width;
this.Height = height;
- this.MetaData = metaData;
+ this.Metadata = metadata;
}
///
@@ -36,6 +36,6 @@ namespace SixLabors.ImageSharp
public int Height { get; }
///
- public ImageMetaData MetaData { get; }
+ public ImageMetadata Metadata { get; }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj
index 29d29d50d8..f7f9e1d5f4 100644
--- a/src/ImageSharp/ImageSharp.csproj
+++ b/src/ImageSharp/ImageSharp.csproj
@@ -1,11 +1,16 @@
- A cross-platform library for the processing of image files; written in C#
SixLabors.ImageSharp
+ Six Labors and contributors
+ Six Labors
+ Copyright (c) Six Labors and contributors.
+ SixLabors.ImageSharp
+ A cross-platform library for the processing of image files; written in C#
+ en
+
$(packageversion)
0.0.1
- Six Labors and contributors
- netstandard1.3;netstandard2.0;netcoreapp2.1;net472
+ netcoreapp2.1;netstandard1.3;netstandard2.0;net472
true
true
SixLabors.ImageSharp
@@ -16,39 +21,27 @@
http://www.apache.org/licenses/LICENSE-2.0
git
https://github.com/SixLabors/ImageSharp
- false
- false
- false
- false
- false
- false
- false
- false
- false
full
portable
True
IOperation
- Latest
+ 7.3
$(DefineConstants);SUPPORTS_EXTENDED_INTRINSICS
-
+
-
+
+
+
+
-
-
-
- All
-
-
@@ -56,14 +49,16 @@
-
+
- ..\..\ImageSharp.ruleset
+ ..\..\standards\SixLabors.ruleset
SixLabors.ImageSharp
+
true
+
TextTemplatingFileGenerator
@@ -126,6 +121,7 @@
TextTemplatingFileGenerator
+
True
@@ -203,6 +199,7 @@
PorterDuffFunctions.Generated.tt
+
diff --git a/src/ImageSharp/ImageSharp.csproj.DotSettings b/src/ImageSharp/ImageSharp.csproj.DotSettings
index cd75f91b7b..a7337240a5 100644
--- a/src/ImageSharp/ImageSharp.csproj.DotSettings
+++ b/src/ImageSharp/ImageSharp.csproj.DotSettings
@@ -5,4 +5,6 @@
True
True
True
- True
\ No newline at end of file
+ True
+ True
+ True
\ No newline at end of file
diff --git a/src/ImageSharp/Image{TPixel}.cs b/src/ImageSharp/Image{TPixel}.cs
index 9d4c1ef0b3..f2bef78e1a 100644
--- a/src/ImageSharp/Image{TPixel}.cs
+++ b/src/ImageSharp/Image{TPixel}.cs
@@ -9,7 +9,7 @@ using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Memory;
-using SixLabors.ImageSharp.MetaData;
+using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp
@@ -31,7 +31,7 @@ namespace SixLabors.ImageSharp
/// The width of the image in pixels.
/// The height of the image in pixels.
public Image(Configuration configuration, int width, int height)
- : this(configuration, width, height, new ImageMetaData())
+ : this(configuration, width, height, new ImageMetadata())
{
}
@@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp
/// The height of the image in pixels.
/// The color to initialize the pixels with.
public Image(Configuration configuration, int width, int height, TPixel backgroundColor)
- : this(configuration, width, height, backgroundColor, new ImageMetaData())
+ : this(configuration, width, height, backgroundColor, new ImageMetadata())
{
}
@@ -67,11 +67,11 @@ namespace SixLabors.ImageSharp
/// The width of the image in pixels.
/// The height of the image in pixels.
/// The images metadata.
- internal Image(Configuration configuration, int width, int height, ImageMetaData metadata)
+ internal Image(Configuration configuration, int width, int height, ImageMetadata metadata)
{
this.configuration = configuration ?? Configuration.Default;
this.PixelType = new PixelTypeInfo(Unsafe.SizeOf() * 8);
- this.MetaData = metadata ?? new ImageMetaData();
+ this.Metadata = metadata ?? new ImageMetadata();
this.Frames = new ImageFrameCollection(this, width, height, default(TPixel));
}
@@ -84,11 +84,11 @@ namespace SixLabors.ImageSharp
/// The width of the image in pixels.
/// The height of the image in pixels.
/// The images metadata.
- internal Image(Configuration configuration, MemorySource memorySource, int width, int height, ImageMetaData metadata)
+ internal Image(Configuration configuration, MemorySource memorySource, int width, int height, ImageMetadata metadata)
{
this.configuration = configuration;
this.PixelType = new PixelTypeInfo(Unsafe.SizeOf() * 8);
- this.MetaData = metadata;
+ this.Metadata = metadata;
this.Frames = new ImageFrameCollection(this, width, height, memorySource);
}
@@ -101,11 +101,11 @@ namespace SixLabors.ImageSharp
/// The height of the image in pixels.
/// The color to initialize the pixels with.
/// The images metadata.
- internal Image(Configuration configuration, int width, int height, TPixel backgroundColor, ImageMetaData metadata)
+ internal Image(Configuration configuration, int width, int height, TPixel backgroundColor, ImageMetadata metadata)
{
this.configuration = configuration ?? Configuration.Default;
this.PixelType = new PixelTypeInfo(Unsafe.SizeOf() * 8);
- this.MetaData = metadata ?? new ImageMetaData();
+ this.Metadata = metadata ?? new ImageMetadata();
this.Frames = new ImageFrameCollection(this, width, height, backgroundColor);
}
@@ -116,11 +116,11 @@ namespace SixLabors.ImageSharp
/// The configuration providing initialization code which allows extending the library.
/// The images metadata.
/// The frames that will be owned by this image instance.
- internal Image(Configuration configuration, ImageMetaData metadata, IEnumerable> frames)
+ internal Image(Configuration configuration, ImageMetadata metadata, IEnumerable> frames)
{
this.configuration = configuration ?? Configuration.Default;
this.PixelType = new PixelTypeInfo(Unsafe.SizeOf() * 8);
- this.MetaData = metadata ?? new ImageMetaData();
+ this.Metadata = metadata ?? new ImageMetadata();
this.Frames = new ImageFrameCollection(this, frames);
}
@@ -140,7 +140,7 @@ namespace SixLabors.ImageSharp
public int Height => this.Frames.RootFrame.Height;
///
- public ImageMetaData MetaData { get; }
+ public ImageMetadata Metadata { get; }
///
/// Gets the frames.
@@ -186,14 +186,14 @@ namespace SixLabors.ImageSharp
public Image Clone() => this.Clone(this.configuration);
///
- /// Clones the current image with the given configueation.
+ /// Clones the current image with the given configuration.
///
/// The configuration providing initialization code which allows extending the library.
/// Returns a new with all the same pixel data as the original.
public Image Clone(Configuration configuration)
{
IEnumerable> clonedFrames = this.Frames.Select(x => x.Clone(configuration));
- return new Image(configuration, this.MetaData.DeepClone(), clonedFrames);
+ return new Image(configuration, this.Metadata.DeepClone(), clonedFrames);
}
///
@@ -214,7 +214,7 @@ namespace SixLabors.ImageSharp
where TPixel2 : struct, IPixel
{
IEnumerable> clonedFrames = this.Frames.Select(x => x.CloneAs(configuration));
- return new Image(configuration, this.MetaData.DeepClone(), clonedFrames);
+ return new Image(configuration, this.Metadata.DeepClone(), clonedFrames);
}
///
diff --git a/src/ImageSharp/Memory/RowInterval.cs b/src/ImageSharp/Memory/RowInterval.cs
index 0750e0368c..835e880e94 100644
--- a/src/ImageSharp/Memory/RowInterval.cs
+++ b/src/ImageSharp/Memory/RowInterval.cs
@@ -1,4 +1,4 @@
-// Copyright(c) Six Labors and contributors.
+// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.Primitives;
@@ -22,24 +22,21 @@ namespace SixLabors.ImageSharp.Memory
}
///
- /// Gets the INCLUSIVE minimum
+ /// Gets the INCLUSIVE minimum.
///
public int Min { get; }
///
- /// Gets the EXCLUSIVE maximum
+ /// Gets the EXCLUSIVE maximum.
///
public int Max { get; }
///
- /// Gets the difference ( - )
+ /// Gets the difference ( - ).
///
public int Height => this.Max - this.Min;
///
- public override string ToString()
- {
- return $"RowInterval [{this.Min}->{this.Max}[";
- }
+ public override string ToString() => $"RowInterval [{this.Min}->{this.Max}]";
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/MetaData/FrameDecodingMode.cs b/src/ImageSharp/MetaData/FrameDecodingMode.cs
index 2863fbf8f9..835e43354a 100644
--- a/src/ImageSharp/MetaData/FrameDecodingMode.cs
+++ b/src/ImageSharp/MetaData/FrameDecodingMode.cs
@@ -1,7 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
-namespace SixLabors.ImageSharp.MetaData
+namespace SixLabors.ImageSharp.Metadata
{
///
/// Enumerated frame process modes to apply to multi-frame images.
diff --git a/src/ImageSharp/MetaData/ImageFrameMetaData.cs b/src/ImageSharp/MetaData/ImageFrameMetaData.cs
index f1f884be68..3858a7d0a3 100644
--- a/src/ImageSharp/MetaData/ImageFrameMetaData.cs
+++ b/src/ImageSharp/MetaData/ImageFrameMetaData.cs
@@ -4,62 +4,62 @@
using System.Collections.Generic;
using SixLabors.ImageSharp.Formats;
-namespace SixLabors.ImageSharp.MetaData
+namespace SixLabors.ImageSharp.Metadata
{
///
/// Encapsulates the metadata of an image frame.
///
- public sealed class ImageFrameMetaData : IDeepCloneable
+ public sealed class ImageFrameMetadata : IDeepCloneable
{
- private readonly Dictionary formatMetaData = new Dictionary();
+ private readonly Dictionary formatMetadata = new Dictionary();
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
- internal ImageFrameMetaData()
+ internal ImageFrameMetadata()
{
}
///
- /// Initializes a new instance of the class
+ /// Initializes a new instance of the class
/// by making a copy from other metadata.
///
///
- /// The other to create this instance from.
+ /// The other to create this instance from.
///
- internal ImageFrameMetaData(ImageFrameMetaData other)
+ internal ImageFrameMetadata(ImageFrameMetadata other)
{
DebugGuard.NotNull(other, nameof(other));
- foreach (KeyValuePair meta in other.formatMetaData)
+ foreach (KeyValuePair meta in other.formatMetadata)
{
- this.formatMetaData.Add(meta.Key, meta.Value.DeepClone());
+ this.formatMetadata.Add(meta.Key, meta.Value.DeepClone());
}
}
///
- public ImageFrameMetaData DeepClone() => new ImageFrameMetaData(this);
+ public ImageFrameMetadata DeepClone() => new ImageFrameMetadata(this);
///
/// Gets the metadata value associated with the specified key.
///
- /// The type of format metadata.
- /// The type of format frame metadata.
+ /// The type of format metadata.
+ /// The type of format frame metadata.
/// The key of the value to get.
///
- /// The .
+ /// The .
///
- public TFormatFrameMetaData GetFormatMetaData(IImageFormat key)
- where TFormatMetaData : class
- where TFormatFrameMetaData : class, IDeepCloneable
+ public TFormatFrameMetadata GetFormatMetadata(IImageFormat key)
+ where TFormatMetadata : class
+ where TFormatFrameMetadata : class, IDeepCloneable
{
- if (this.formatMetaData.TryGetValue(key, out IDeepCloneable meta))
+ if (this.formatMetadata.TryGetValue(key, out IDeepCloneable meta))
{
- return (TFormatFrameMetaData)meta;
+ return (TFormatFrameMetadata)meta;
}
- TFormatFrameMetaData newMeta = key.CreateDefaultFormatFrameMetaData();
- this.formatMetaData[key] = newMeta;
+ TFormatFrameMetadata newMeta = key.CreateDefaultFormatFrameMetadata();
+ this.formatMetadata[key] = newMeta;
return newMeta;
}
}
diff --git a/src/ImageSharp/MetaData/ImageMetaData.cs b/src/ImageSharp/MetaData/ImageMetaData.cs
index 73549d98aa..b9efca4fee 100644
--- a/src/ImageSharp/MetaData/ImageMetaData.cs
+++ b/src/ImageSharp/MetaData/ImageMetaData.cs
@@ -3,15 +3,15 @@
using System.Collections.Generic;
using SixLabors.ImageSharp.Formats;
-using SixLabors.ImageSharp.MetaData.Profiles.Exif;
-using SixLabors.ImageSharp.MetaData.Profiles.Icc;
+using SixLabors.ImageSharp.Metadata.Profiles.Exif;
+using SixLabors.ImageSharp.Metadata.Profiles.Icc;
-namespace SixLabors.ImageSharp.MetaData
+namespace SixLabors.ImageSharp.Metadata
{
///
/// Encapsulates the metadata of an image.
///
- public sealed class ImageMetaData : IDeepCloneable
+ public sealed class ImageMetadata : IDeepCloneable
{
///
/// The default horizontal resolution value (dots per inch) in x direction.
@@ -31,14 +31,14 @@ namespace SixLabors.ImageSharp.MetaData
///
public const PixelResolutionUnit DefaultPixelResolutionUnits = PixelResolutionUnit.PixelsPerInch;
- private readonly Dictionary formatMetaData = new Dictionary();
+ private readonly Dictionary formatMetadata = new Dictionary();
private double horizontalResolution;
private double verticalResolution;
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
- internal ImageMetaData()
+ internal ImageMetadata()
{
this.horizontalResolution = DefaultHorizontalResolution;
this.verticalResolution = DefaultVerticalResolution;
@@ -46,21 +46,21 @@ namespace SixLabors.ImageSharp.MetaData
}
///
- /// Initializes a new instance of the class
+ /// Initializes a new instance of the class
/// by making a copy from other metadata.
///
///
- /// The other to create this instance from.
+ /// The other to create this instance from.
///
- private ImageMetaData(ImageMetaData other)
+ private ImageMetadata(ImageMetadata other)
{
this.HorizontalResolution = other.HorizontalResolution;
this.VerticalResolution = other.VerticalResolution;
this.ResolutionUnits = other.ResolutionUnits;
- foreach (KeyValuePair meta in other.formatMetaData)
+ foreach (KeyValuePair meta in other.formatMetadata)
{
- this.formatMetaData.Add(meta.Key, meta.Value.DeepClone());
+ this.formatMetadata.Add(meta.Key, meta.Value.DeepClone());
}
foreach (ImageProperty property in other.Properties)
@@ -135,26 +135,26 @@ namespace SixLabors.ImageSharp.MetaData
///
/// Gets the metadata value associated with the specified key.
///
- /// The type of metadata.
+ /// The type of metadata.
/// The key of the value to get.
///
- /// The .
+ /// The .
///
- public TFormatMetaData GetFormatMetaData(IImageFormat key)
- where TFormatMetaData : class, IDeepCloneable
+ public TFormatMetadata GetFormatMetadata