Browse Source

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

pull/643/head
James Jackson-South 8 years ago
parent
commit
5716dbeb63
  1. 12
      appveyor.yml
  2. 5
      run-tests.ps1
  3. 3
      src/ImageSharp.Drawing/ImageSharp.Drawing.csproj
  4. 7
      src/ImageSharp.Drawing/Primitives/Region.cs
  5. 3
      src/ImageSharp.Drawing/Primitives/ShapePath.cs
  6. 21
      src/ImageSharp.Drawing/Primitives/ShapeRegion.cs
  7. 14
      src/ImageSharp.Drawing/Processing/Drawing/Brushes/BrushApplicator.cs
  8. 12
      src/ImageSharp.Drawing/Processing/Drawing/Brushes/ImageBrush{TPixel}.cs
  9. 14
      src/ImageSharp.Drawing/Processing/Drawing/Brushes/PatternBrush{TPixel}.cs
  10. 14
      src/ImageSharp.Drawing/Processing/Drawing/Brushes/RecolorBrush{TPixel}.cs
  11. 18
      src/ImageSharp.Drawing/Processing/Drawing/Brushes/SolidBrush{TPixel}.cs
  12. 10
      src/ImageSharp.Drawing/Processing/Drawing/Processors/DrawImageProcessor.cs
  13. 8
      src/ImageSharp.Drawing/Processing/Drawing/Processors/FillProcessor.cs
  14. 76
      src/ImageSharp.Drawing/Processing/Drawing/Processors/FillRegionProcessor.cs
  15. 179
      src/ImageSharp.Drawing/Processing/Text/DrawTextExtensions.Path.cs
  16. 34
      src/ImageSharp.Drawing/Processing/Text/DrawTextExtensions.cs
  17. 471
      src/ImageSharp.Drawing/Processing/Text/Processors/DrawTextProcessor.cs
  18. 19
      src/ImageSharp.Drawing/Processing/Text/TextGraphicsOptions.cs
  19. 84
      src/ImageSharp.Drawing/Utils/QuickSort.cs
  20. 114
      src/ImageSharp/Advanced/AdvancedImageExtensions.cs
  21. 2
      src/ImageSharp/Advanced/IPixelSource.cs
  22. 7
      src/ImageSharp/ColorSpaces/CieLab.cs
  23. 18
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Adapt.cs
  24. 8
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs
  25. 2
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs
  26. 26
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs
  27. 27
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs
  28. 27
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs
  29. 2
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs
  30. 2
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs
  31. 2
      src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLuv/CieXyzToCieLuvConverter.cs
  32. 4
      src/ImageSharp/ColorSpaces/Conversion/Implementation/CieXyy/CieXyzAndCieXyyConverter.cs
  33. 4
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Hsl/HslAndRgbConverter.cs
  34. 4
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Hsv/HsvAndRgbConverter.cs
  35. 2
      src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/CieXyzAndHunterLabConverterBase.cs
  36. 8
      src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/CieXyzToHunterLabConverter.cs
  37. 2
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/CieXyzToLinearRgbConverter.cs
  38. 1
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToCieXyzConverter.cs
  39. 2
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToRgbConverter.cs
  40. 4
      src/ImageSharp/ColorSpaces/Conversion/Implementation/YCbCr/YCbCrAndRgbConverter.cs
  41. 9
      src/ImageSharp/Common/Constants.cs
  42. 46
      src/ImageSharp/Common/Extensions/ListExtensions.cs
  43. 29
      src/ImageSharp/Common/Extensions/StreamExtensions.cs
  44. 9
      src/ImageSharp/Common/Helpers/DebugGuard.cs
  45. 3
      src/ImageSharp/Common/Helpers/Guard.cs
  46. 6
      src/ImageSharp/Common/Helpers/ImageMaths.cs
  47. 8
      src/ImageSharp/Common/Helpers/ParallelFor.cs
  48. 24
      src/ImageSharp/Common/Helpers/TestHelpers.cs
  49. 8
      src/ImageSharp/Configuration.cs
  50. 137
      src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
  51. 2
      src/ImageSharp/Formats/Bmp/BmpEncoder.cs
  52. 49
      src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
  53. 7
      src/ImageSharp/Formats/Bmp/BmpImageFormatDetector.cs
  54. 17
      src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs
  55. 21
      src/ImageSharp/Formats/Gif/GifColorTableMode.cs
  56. 2
      src/ImageSharp/Formats/Gif/GifDecoder.cs
  57. 20
      src/ImageSharp/Formats/Gif/GifDecoderCore.cs
  58. 7
      src/ImageSharp/Formats/Gif/GifEncoder.cs
  59. 127
      src/ImageSharp/Formats/Gif/GifEncoderCore.cs
  60. 5
      src/ImageSharp/Formats/Gif/IGifEncoderOptions.cs
  61. 29
      src/ImageSharp/Formats/Gif/LzwDecoder.cs
  62. 53
      src/ImageSharp/Formats/Gif/LzwEncoder.cs
  63. 2
      src/ImageSharp/Formats/IImageEncoder.cs
  64. 2
      src/ImageSharp/Formats/IImageFormatDetector.cs
  65. 23
      src/ImageSharp/Formats/ImageFormatManager.cs
  66. 2
      src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs
  67. 2
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.CopyTo.cs
  68. 6
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
  69. 6
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs
  70. 6
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs
  71. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/IJpegComponent.cs
  72. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs
  73. 6
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs
  74. 16
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs
  75. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ProfileResolver.cs
  76. 3
      src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs
  77. 8
      src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangComponent.cs
  78. 2
      src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangJpegScanDecoder.ComputationData.cs
  79. 9
      src/ImageSharp/Formats/Jpeg/GolangPort/GolangJpegDecoderCore.cs
  80. 2
      src/ImageSharp/Formats/Jpeg/JpegDecoder.cs
  81. 14
      src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
  82. 8
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/DoubleBufferedStreamReader.cs
  83. 8
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FastACTables.cs
  84. 12
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrameComponent.cs
  85. 15
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs
  86. 11
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsScanDecoder.cs
  87. 2
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/ScanDecoder.cs
  88. 34
      src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs
  89. 14
      src/ImageSharp/Formats/Png/IPngEncoderOptions.cs
  90. 22
      src/ImageSharp/Formats/Png/PngBitDepth.cs
  91. 2
      src/ImageSharp/Formats/Png/PngChunk.cs
  92. 8
      src/ImageSharp/Formats/Png/PngConfigurationModule.cs
  93. 756
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  94. 16
      src/ImageSharp/Formats/Png/PngEncoder.cs
  95. 274
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  96. 7
      src/ImageSharp/Formats/Png/PngImageFormatDetector.cs
  97. 14
      src/ImageSharp/Image.Decode.cs
  98. 167
      src/ImageSharp/Image.FromBytes.cs
  99. 59
      src/ImageSharp/Image.WrapMemory.cs
  100. 31
      src/ImageSharp/ImageExtensions.Internal.cs

12
appveyor.yml

@ -1,18 +1,16 @@
version: 1.0.0.{build}
image: Visual Studio 2017 Preview
image: Visual Studio 2017
# prevent the double build when a branch has an active PR
skip_branch_with_pr: true
environment:
matrix:
### TODO: Enable the netcoreapp2.1 target when RC2 has been released!
#- target_framework: netcoreapp2.1
# is_32bit: False
- target_framework: netcoreapp2.1
is_32bit: False
#- target_framework: netcoreapp2.1
# is_32bit: True
- target_framework: netcoreapp2.1
is_32bit: True
- target_framework: netcoreapp2.0
is_32bit: False

5
run-tests.ps1

@ -75,6 +75,11 @@ else {
$xunitArgs += " --fx-version 2.0.0"
}
if ($targetFramework -eq "netcoreapp2.1") {
# There were issues matching the correct installed runtime if we do not specify it explicitly:
$xunitArgs += " --fx-version 2.1.0"
}
if ($is32Bit -eq "True") {
$xunitArgs += " -x86"
}

3
src/ImageSharp.Drawing/ImageSharp.Drawing.csproj

@ -39,7 +39,8 @@
<ItemGroup>
<PackageReference Include="SixLabors.Core" Version="1.0.0-beta0005" />
<AdditionalFiles Include="..\..\stylecop.json" />
<PackageReference Include="SixLabors.Shapes.Text" Version="1.0.0-beta0005" />
<PackageReference Include="SixLabors.Fonts" Version="1.0.0-dev000087" />
<PackageReference Include="SixLabors.Shapes" Version="1.0.0-dev000079" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.0-beta007">
<PrivateAssets>All</PrivateAssets>
</PackageReference>

7
src/ImageSharp.Drawing/Primitives/Region.cs

@ -1,6 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Primitives
@ -19,7 +20,7 @@ namespace SixLabors.ImageSharp.Primitives
/// Gets the bounding box that entirely surrounds this region.
/// </summary>
/// <remarks>
/// This should always contains all possible points returned from <see cref="Scan(float, float[], int)"/>.
/// This should always contains all possible points returned from <see cref="Scan"/>.
/// </remarks>
public abstract Rectangle Bounds { get; }
@ -28,8 +29,8 @@ namespace SixLabors.ImageSharp.Primitives
/// </summary>
/// <param name="y">The position along the y axis to find intersections.</param>
/// <param name="buffer">The buffer.</param>
/// <param name="offset">The point in the buffer to start setting offset.</param>
/// <param name="configuration">A <see cref="Configuration"/> instance in the context of the caller.</param>
/// <returns>The number of intersections found.</returns>
public abstract int Scan(float y, float[] buffer, int offset);
public abstract int Scan(float y, Span<float> buffer, Configuration configuration);
}
}

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

@ -16,9 +16,8 @@ namespace SixLabors.ImageSharp.Primitives
/// </summary>
/// <param name="shape">The shape.</param>
/// <param name="pen">The pen to apply to the shape.</param>
// TODO: SixLabors.shape will be moving to a Span/ReadOnlySpan based API shortly use ToArray for now.
public ShapePath(IPath shape, IPen pen)
: base(shape.GenerateOutline(pen.StrokeWidth, pen.StrokePattern.ToArray()))
: base(shape.GenerateOutline(pen.StrokeWidth, pen.StrokePattern))
{
}
}

21
src/ImageSharp.Drawing/Primitives/ShapeRegion.cs

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.Memory;
using SixLabors.Primitives;
using SixLabors.Shapes;
@ -39,21 +40,23 @@ namespace SixLabors.ImageSharp.Primitives
public override Rectangle Bounds { get; }
/// <inheritdoc/>
public override int Scan(float y, float[] buffer, int offset)
public override int Scan(float y, Span<float> buffer, Configuration configuration)
{
var start = new PointF(this.Bounds.Left - 1, y);
var end = new PointF(this.Bounds.Right + 1, y);
// TODO: This is a temporary workaround because of the lack of Span<T> API-s on IPath. We should use MemoryManager.Allocate() here!
var innerBuffer = new PointF[buffer.Length];
int count = this.Shape.FindIntersections(start, end, innerBuffer, 0);
for (int i = 0; i < count; i++)
using (IBuffer<PointF> tempBuffer = configuration.MemoryAllocator.Allocate<PointF>(buffer.Length))
{
buffer[i + offset] = innerBuffer[i].X;
}
Span<PointF> innerBuffer = tempBuffer.GetSpan();
int count = this.Shape.FindIntersections(start, end, innerBuffer);
return count;
for (int i = 0; i < count; i++)
{
buffer[i] = innerBuffer[i].X;
}
return count;
}
}
}
}

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

@ -3,8 +3,8 @@
using System;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
namespace SixLabors.ImageSharp.Processing.Drawing.Brushes
{
@ -65,13 +65,13 @@ namespace SixLabors.ImageSharp.Processing.Drawing.Brushes
/// <remarks>scanlineBuffer will be > scanlineWidth but provide and offset in case we want to share a larger buffer across runs.</remarks>
internal virtual void Apply(Span<float> scanline, int x, int y)
{
MemoryManager memoryManager = this.Target.MemoryManager;
MemoryAllocator memoryAllocator = this.Target.MemoryAllocator;
using (IBuffer<float> amountBuffer = memoryManager.Allocate<float>(scanline.Length))
using (IBuffer<TPixel> overlay = memoryManager.Allocate<TPixel>(scanline.Length))
using (IBuffer<float> amountBuffer = memoryAllocator.Allocate<float>(scanline.Length))
using (IBuffer<TPixel> overlay = memoryAllocator.Allocate<TPixel>(scanline.Length))
{
Span<float> amountSpan = amountBuffer.Span;
Span<TPixel> overlaySpan = overlay.Span;
Span<float> amountSpan = amountBuffer.GetSpan();
Span<TPixel> overlaySpan = overlay.GetSpan();
for (int i = 0; i < scanline.Length; i++)
{
@ -88,7 +88,7 @@ namespace SixLabors.ImageSharp.Processing.Drawing.Brushes
}
Span<TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length);
this.Blender.Blend(memoryManager, destinationRow, destinationRow, overlaySpan, amountSpan);
this.Blender.Blend(memoryAllocator, destinationRow, destinationRow, overlaySpan, amountSpan);
}
}
}

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

@ -3,8 +3,8 @@
using System;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Drawing.Brushes
@ -118,11 +118,11 @@ namespace SixLabors.ImageSharp.Processing.Drawing.Brushes
internal override void Apply(Span<float> scanline, int x, int y)
{
// Create a span for colors
using (IBuffer<float> amountBuffer = this.Target.MemoryManager.Allocate<float>(scanline.Length))
using (IBuffer<TPixel> overlay = this.Target.MemoryManager.Allocate<TPixel>(scanline.Length))
using (IBuffer<float> amountBuffer = this.Target.MemoryAllocator.Allocate<float>(scanline.Length))
using (IBuffer<TPixel> overlay = this.Target.MemoryAllocator.Allocate<TPixel>(scanline.Length))
{
Span<float> amountSpan = amountBuffer.Span;
Span<TPixel> overlaySpan = overlay.Span;
Span<float> amountSpan = amountBuffer.GetSpan();
Span<TPixel> overlaySpan = overlay.GetSpan();
int sourceY = (y - this.offsetY) % this.yLength;
int offsetX = x - this.offsetX;
@ -138,7 +138,7 @@ namespace SixLabors.ImageSharp.Processing.Drawing.Brushes
}
Span<TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length);
this.Blender.Blend(this.source.MemoryManager, destinationRow, destinationRow, overlaySpan, amountSpan);
this.Blender.Blend(this.source.MemoryAllocator, destinationRow, destinationRow, overlaySpan, amountSpan);
}
}
}

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

@ -4,9 +4,9 @@
using System;
using System.Numerics;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Primitives;
using SixLabors.Memory;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Drawing.Brushes
@ -151,13 +151,13 @@ namespace SixLabors.ImageSharp.Processing.Drawing.Brushes
internal override void Apply(Span<float> scanline, int x, int y)
{
int patternY = y % this.pattern.Rows;
MemoryManager memoryManager = this.Target.MemoryManager;
MemoryAllocator memoryAllocator = this.Target.MemoryAllocator;
using (IBuffer<float> amountBuffer = memoryManager.Allocate<float>(scanline.Length))
using (IBuffer<TPixel> overlay = memoryManager.Allocate<TPixel>(scanline.Length))
using (IBuffer<float> amountBuffer = memoryAllocator.Allocate<float>(scanline.Length))
using (IBuffer<TPixel> overlay = memoryAllocator.Allocate<TPixel>(scanline.Length))
{
Span<float> amountSpan = amountBuffer.Span;
Span<TPixel> overlaySpan = overlay.Span;
Span<float> amountSpan = amountBuffer.GetSpan();
Span<TPixel> overlaySpan = overlay.GetSpan();
for (int i = 0; i < scanline.Length; i++)
{
@ -168,7 +168,7 @@ namespace SixLabors.ImageSharp.Processing.Drawing.Brushes
}
Span<TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length);
this.Blender.Blend(memoryManager, destinationRow, destinationRow, overlaySpan, amountSpan);
this.Blender.Blend(memoryAllocator, destinationRow, destinationRow, overlaySpan, amountSpan);
}
}
}

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

@ -4,8 +4,8 @@
using System;
using System.Numerics;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Drawing.Brushes
@ -136,13 +136,13 @@ namespace SixLabors.ImageSharp.Processing.Drawing.Brushes
/// <inheritdoc />
internal override void Apply(Span<float> scanline, int x, int y)
{
MemoryManager memoryManager = this.Target.MemoryManager;
MemoryAllocator memoryAllocator = this.Target.MemoryAllocator;
using (IBuffer<float> amountBuffer = memoryManager.Allocate<float>(scanline.Length))
using (IBuffer<TPixel> overlay = memoryManager.Allocate<TPixel>(scanline.Length))
using (IBuffer<float> amountBuffer = memoryAllocator.Allocate<float>(scanline.Length))
using (IBuffer<TPixel> overlay = memoryAllocator.Allocate<TPixel>(scanline.Length))
{
Span<float> amountSpan = amountBuffer.Span;
Span<TPixel> overlaySpan = overlay.Span;
Span<float> amountSpan = amountBuffer.GetSpan();
Span<TPixel> overlaySpan = overlay.GetSpan();
for (int i = 0; i < scanline.Length; i++)
{
@ -156,7 +156,7 @@ namespace SixLabors.ImageSharp.Processing.Drawing.Brushes
}
Span<TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length);
this.Blender.Blend(memoryManager, destinationRow, destinationRow, overlaySpan, amountSpan);
this.Blender.Blend(memoryAllocator, destinationRow, destinationRow, overlaySpan, amountSpan);
}
}
}

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

@ -3,8 +3,8 @@
using System;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Drawing.Brushes
@ -58,8 +58,8 @@ namespace SixLabors.ImageSharp.Processing.Drawing.Brushes
public SolidBrushApplicator(ImageFrame<TPixel> source, TPixel color, GraphicsOptions options)
: base(source, options)
{
this.Colors = source.MemoryManager.Allocate<TPixel>(source.Width);
this.Colors.Span.Fill(color);
this.Colors = source.MemoryAllocator.Allocate<TPixel>(source.Width);
this.Colors.GetSpan().Fill(color);
}
/// <summary>
@ -75,7 +75,7 @@ namespace SixLabors.ImageSharp.Processing.Drawing.Brushes
/// <returns>
/// The color
/// </returns>
internal override TPixel this[int x, int y] => this.Colors.Span[x];
internal override TPixel this[int x, int y] => this.Colors.GetSpan()[x];
/// <inheritdoc />
public override void Dispose()
@ -88,24 +88,24 @@ namespace SixLabors.ImageSharp.Processing.Drawing.Brushes
{
Span<TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length);
MemoryManager memoryManager = this.Target.MemoryManager;
MemoryAllocator memoryAllocator = this.Target.MemoryAllocator;
if (this.Options.BlendPercentage == 1f)
{
this.Blender.Blend(memoryManager, destinationRow, destinationRow, this.Colors.Span, scanline);
this.Blender.Blend(memoryAllocator, destinationRow, destinationRow, this.Colors.GetSpan(), scanline);
}
else
{
using (IBuffer<float> amountBuffer = memoryManager.Allocate<float>(scanline.Length))
using (IBuffer<float> amountBuffer = memoryAllocator.Allocate<float>(scanline.Length))
{
Span<float> amountSpan = amountBuffer.Span;
Span<float> amountSpan = amountBuffer.GetSpan();
for (int i = 0; i < scanline.Length; i++)
{
amountSpan[i] = scanline[i] * this.Options.BlendPercentage;
}
this.Blender.Blend(memoryManager, destinationRow, destinationRow, this.Colors.Span, amountSpan);
this.Blender.Blend(memoryAllocator, destinationRow, destinationRow, this.Colors.GetSpan(), amountSpan);
}
}
}

10
src/ImageSharp.Drawing/Processing/Drawing/Processors/DrawImageProcessor.cs

@ -4,9 +4,9 @@
using System;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors;
using SixLabors.Memory;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Drawing.Processors
@ -133,11 +133,11 @@ namespace SixLabors.ImageSharp.Processing.Drawing.Processors
int width = maxX - minX;
MemoryManager memoryManager = this.Image.GetConfiguration().MemoryManager;
MemoryAllocator memoryAllocator = this.Image.GetConfiguration().MemoryAllocator;
using (IBuffer<float> amount = memoryManager.Allocate<float>(width))
using (IBuffer<float> amount = memoryAllocator.Allocate<float>(width))
{
amount.Span.Fill(this.Opacity);
amount.GetSpan().Fill(this.Opacity);
Parallel.For(
minY,
@ -147,7 +147,7 @@ namespace SixLabors.ImageSharp.Processing.Drawing.Processors
{
Span<TPixel> background = source.GetPixelRowSpan(y).Slice(minX, width);
Span<TPixel> foreground = targetImage.GetPixelRowSpan(y - locationY).Slice(targetX, width);
blender.Blend(memoryManager, background, background, foreground, amount.Span);
blender.Blend(memoryAllocator, background, background, foreground, amount.GetSpan());
});
}
}

8
src/ImageSharp.Drawing/Processing/Drawing/Processors/FillProcessor.cs

@ -4,10 +4,10 @@
using System;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Drawing.Brushes;
using SixLabors.ImageSharp.Processing.Processors;
using SixLabors.Memory;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Drawing.Processors
@ -77,13 +77,13 @@ namespace SixLabors.ImageSharp.Processing.Drawing.Processors
startY = 0;
}
using (IBuffer<float> amount = source.MemoryManager.Allocate<float>(width))
using (IBuffer<float> amount = source.MemoryAllocator.Allocate<float>(width))
using (BrushApplicator<TPixel> applicator = this.brush.CreateApplicator(
source,
sourceRectangle,
this.options))
{
amount.Span.Fill(1f);
amount.GetSpan().Fill(1f);
Parallel.For(
minY,
@ -94,7 +94,7 @@ namespace SixLabors.ImageSharp.Processing.Drawing.Processors
int offsetY = y - startY;
int offsetX = minX - startX;
applicator.Apply(amount.Span, offsetX, offsetY);
applicator.Apply(amount.GetSpan(), offsetX, offsetY);
});
}
}

76
src/ImageSharp.Drawing/Processing/Drawing/Processors/FillRegionProcessor.cs

@ -2,12 +2,12 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Primitives;
using SixLabors.ImageSharp.Processing.Drawing.Brushes;
using SixLabors.ImageSharp.Processing.Processors;
using SixLabors.ImageSharp.Utils;
using SixLabors.Memory;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Drawing.Processors
@ -96,36 +96,35 @@ namespace SixLabors.ImageSharp.Processing.Drawing.Processors
using (BrushApplicator<TPixel> applicator = this.Brush.CreateApplicator(source, rect, this.Options))
{
int scanlineWidth = maxX - minX;
using (BasicArrayBuffer<float> buffer = source.MemoryManager.AllocateFake<float>(maxIntersections))
using (BasicArrayBuffer<float> scanline = source.MemoryManager.AllocateFake<float>(scanlineWidth))
using (IBuffer<float> bBuffer = source.MemoryAllocator.Allocate<float>(maxIntersections))
using (IBuffer<float> bScanline = source.MemoryAllocator.Allocate<float>(scanlineWidth))
{
bool scanlineDirty = true;
float subpixelFraction = 1f / subpixelCount;
float subpixelFractionPoint = subpixelFraction / subpixelCount;
Span<float> buffer = bBuffer.GetSpan();
Span<float> scanline = bScanline.GetSpan();
for (int y = minY; y < maxY; y++)
{
if (scanlineDirty)
{
// clear the buffer
for (int x = 0; x < scanlineWidth; x++)
{
scanline[x] = 0;
}
scanline.Clear();
scanlineDirty = false;
}
float yPlusOne = y + 1;
for (float subPixel = (float)y; subPixel < yPlusOne; subPixel += subpixelFraction)
{
int pointsFound = region.Scan(subPixel + offset, buffer.Array, 0);
int pointsFound = region.Scan(subPixel + offset, buffer, configuration);
if (pointsFound == 0)
{
// nothing on this line skip
continue;
}
QuickSort(new Span<float>(buffer.Array, 0, pointsFound));
QuickSort.Sort(buffer.Slice(0, pointsFound));
for (int point = 0; point < pointsFound; point += 2)
{
@ -181,62 +180,11 @@ namespace SixLabors.ImageSharp.Processing.Drawing.Processors
}
}
applicator.Apply(scanline.Span, minX, y);
applicator.Apply(scanline, minX, y);
}
}
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void Swap(Span<float> data, int left, int right)
{
float tmp = data[left];
data[left] = data[right];
data[right] = tmp;
}
private static void QuickSort(Span<float> data)
{
QuickSort(data, 0, data.Length - 1);
}
private static void QuickSort(Span<float> data, int lo, int hi)
{
if (lo < hi)
{
int p = Partition(data, lo, hi);
QuickSort(data, lo, p);
QuickSort(data, p + 1, hi);
}
}
private static int Partition(Span<float> data, int lo, int hi)
{
float pivot = data[lo];
int i = lo - 1;
int j = hi + 1;
while (true)
{
do
{
i = i + 1;
}
while (data[i] < pivot && i < hi);
do
{
j = j - 1;
}
while (data[j] > pivot && j > lo);
if (i >= j)
{
return j;
}
Swap(data, i, j);
}
}
}
}

179
src/ImageSharp.Drawing/Processing/Text/DrawTextExtensions.Path.cs

@ -1,179 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.Fonts;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Drawing;
using SixLabors.ImageSharp.Processing.Drawing.Brushes;
using SixLabors.ImageSharp.Processing.Drawing.Pens;
using SixLabors.Shapes;
namespace SixLabors.ImageSharp.Processing.Text
{
/// <summary>
/// Adds extensions that allow the drawing of text along given paths to the <see cref="Image{TPixel}"/> type.
/// </summary>
public static partial class DrawTextExtensions
{
/// <summary>
/// Draws the text onto the the image filled via the brush.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="text">The text.</param>
/// <param name="font">The font.</param>
/// <param name="color">The color.</param>
/// <param name="path">The path.</param>
/// <returns>
/// The <see cref="Image{TPixel}" />.
/// </returns>
public static IImageProcessingContext<TPixel> DrawText<TPixel>(this IImageProcessingContext<TPixel> source, string text, Font font, TPixel color, IPath path)
where TPixel : struct, IPixel<TPixel>
=> source.DrawText(TextGraphicsOptions.Default, text, font, color, path);
/// <summary>
/// Draws the text onto the the image filled via the brush.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="text">The text.</param>
/// <param name="font">The font.</param>
/// <param name="color">The color.</param>
/// <param name="path">The path.</param>
/// <returns>
/// The <see cref="Image{TPixel}" />.
/// </returns>
public static IImageProcessingContext<TPixel> DrawText<TPixel>(this IImageProcessingContext<TPixel> source, TextGraphicsOptions options, string text, Font font, TPixel color, IPath path)
where TPixel : struct, IPixel<TPixel>
=> source.DrawText(options, text, font, Brushes.Solid(color), null, path);
/// <summary>
/// Draws the text onto the the image filled via the brush.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="text">The text.</param>
/// <param name="font">The font.</param>
/// <param name="brush">The brush.</param>
/// <param name="path">The location.</param>
/// <returns>
/// The <see cref="Image{TPixel}" />.
/// </returns>
public static IImageProcessingContext<TPixel> DrawText<TPixel>(this IImageProcessingContext<TPixel> source, string text, Font font, IBrush<TPixel> brush, IPath path)
where TPixel : struct, IPixel<TPixel>
=> source.DrawText(TextGraphicsOptions.Default, text, font, brush, path);
/// <summary>
/// Draws the text onto the the image filled via the brush.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="text">The text.</param>
/// <param name="font">The font.</param>
/// <param name="brush">The brush.</param>
/// <param name="path">The path.</param>
/// <returns>
/// The <see cref="Image{TPixel}" />.
/// </returns>
public static IImageProcessingContext<TPixel> DrawText<TPixel>(this IImageProcessingContext<TPixel> source, TextGraphicsOptions options, string text, Font font, IBrush<TPixel> brush, IPath path)
where TPixel : struct, IPixel<TPixel>
=> source.DrawText(options, text, font, brush, null, path);
/// <summary>
/// Draws the text onto the the image outlined via the pen.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="text">The text.</param>
/// <param name="font">The font.</param>
/// <param name="pen">The pen.</param>
/// <param name="path">The path.</param>
/// <returns>
/// The <see cref="Image{TPixel}" />.
/// </returns>
public static IImageProcessingContext<TPixel> DrawText<TPixel>(this IImageProcessingContext<TPixel> source, string text, Font font, IPen<TPixel> pen, IPath path)
where TPixel : struct, IPixel<TPixel>
=> source.DrawText(TextGraphicsOptions.Default, text, font, pen, path);
/// <summary>
/// Draws the text onto the the image outlined via the pen.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="text">The text.</param>
/// <param name="font">The font.</param>
/// <param name="pen">The pen.</param>
/// <param name="path">The path.</param>
/// <returns>
/// The <see cref="Image{TPixel}" />.
/// </returns>
public static IImageProcessingContext<TPixel> DrawText<TPixel>(this IImageProcessingContext<TPixel> source, TextGraphicsOptions options, string text, Font font, IPen<TPixel> pen, IPath path)
where TPixel : struct, IPixel<TPixel>
=> source.DrawText(options, text, font, null, pen, path);
/// <summary>
/// Draws the text onto the the image filled via the brush then outlined via the pen.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="text">The text.</param>
/// <param name="font">The font.</param>
/// <param name="brush">The brush.</param>
/// <param name="pen">The pen.</param>
/// <param name="path">The path.</param>
/// <returns>
/// The <see cref="Image{TPixel}" />.
/// </returns>
public static IImageProcessingContext<TPixel> DrawText<TPixel>(this IImageProcessingContext<TPixel> source, string text, Font font, IBrush<TPixel> brush, IPen<TPixel> pen, IPath path)
where TPixel : struct, IPixel<TPixel>
=> source.DrawText(TextGraphicsOptions.Default, text, font, brush, pen, path);
/// <summary>
/// Draws the text onto the the image filled via the brush then outlined via the pen.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="text">The text.</param>
/// <param name="font">The font.</param>
/// <param name="brush">The brush.</param>
/// <param name="pen">The pen.</param>
/// <param name="path">The path.</param>
/// <returns>
/// The <see cref="Image{TPixel}" />.
/// </returns>
public static IImageProcessingContext<TPixel> DrawText<TPixel>(this IImageProcessingContext<TPixel> source, TextGraphicsOptions options, string text, Font font, IBrush<TPixel> brush, IPen<TPixel> pen, IPath path)
where TPixel : struct, IPixel<TPixel>
{
float dpiX = DefaultTextDpi;
float dpiY = DefaultTextDpi;
var style = new RendererOptions(font, dpiX, dpiY)
{
ApplyKerning = options.ApplyKerning,
TabWidth = options.TabWidth,
WrappingWidth = options.WrapTextWidth,
HorizontalAlignment = options.HorizontalAlignment,
VerticalAlignment = options.VerticalAlignment
};
IPathCollection glyphs = TextBuilder.GenerateGlyphs(text, path, style);
var pathOptions = (GraphicsOptions)options;
if (brush != null)
{
source.Fill(pathOptions, brush, glyphs);
}
if (pen != null)
{
source.Draw(pathOptions, pen, glyphs);
}
return source;
}
}
}

34
src/ImageSharp.Drawing/Processing/Text/DrawTextExtensions.cs

@ -3,11 +3,10 @@
using SixLabors.Fonts;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Drawing;
using SixLabors.ImageSharp.Processing.Drawing.Brushes;
using SixLabors.ImageSharp.Processing.Drawing.Pens;
using SixLabors.ImageSharp.Processing.Text.Processors;
using SixLabors.Primitives;
using SixLabors.Shapes;
namespace SixLabors.ImageSharp.Processing.Text
{
@ -16,8 +15,6 @@ namespace SixLabors.ImageSharp.Processing.Text
/// </summary>
public static partial class DrawTextExtensions
{
private static readonly int DefaultTextDpi = 72;
/// <summary>
/// Draws the text onto the the image filled via the brush.
/// </summary>
@ -150,33 +147,6 @@ namespace SixLabors.ImageSharp.Processing.Text
/// </returns>
public static IImageProcessingContext<TPixel> DrawText<TPixel>(this IImageProcessingContext<TPixel> source, TextGraphicsOptions options, string text, Font font, IBrush<TPixel> brush, IPen<TPixel> pen, PointF location)
where TPixel : struct, IPixel<TPixel>
{
float dpiX = DefaultTextDpi;
float dpiY = DefaultTextDpi;
var style = new RendererOptions(font, dpiX, dpiY, location)
{
ApplyKerning = options.ApplyKerning,
TabWidth = options.TabWidth,
WrappingWidth = options.WrapTextWidth,
HorizontalAlignment = options.HorizontalAlignment,
VerticalAlignment = options.VerticalAlignment
};
IPathCollection glyphs = TextBuilder.GenerateGlyphs(text, style);
var pathOptions = (GraphicsOptions)options;
if (brush != null)
{
source.Fill(pathOptions, brush, glyphs);
}
if (pen != null)
{
source.Draw(pathOptions, pen, glyphs);
}
return source;
}
=> source.ApplyProcessor(new DrawTextProcessor<TPixel>(options, text, font, brush, pen, location));
}
}

471
src/ImageSharp.Drawing/Processing/Text/Processors/DrawTextProcessor.cs

@ -0,0 +1,471 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Generic;
using SixLabors.Fonts;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Drawing.Brushes;
using SixLabors.ImageSharp.Processing.Drawing.Pens;
using SixLabors.ImageSharp.Processing.Processors;
using SixLabors.ImageSharp.Utils;
using SixLabors.Memory;
using SixLabors.Primitives;
using SixLabors.Shapes;
namespace SixLabors.ImageSharp.Processing.Text.Processors
{
/// <summary>
/// Using the brush as a source of pixels colors blends the brush color with source.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class DrawTextProcessor<TPixel> : ImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
private CachingGlyphRenderer textRenderer;
/// <summary>
/// Initializes a new instance of the <see cref="DrawTextProcessor{TPixel}"/> class.
/// </summary>
/// <param name="options">The options</param>
/// <param name="text">The text we want to render</param>
/// <param name="font">The font we want to render with</param>
/// <param name="brush">The brush to source pixel colors from.</param>
/// <param name="pen">The pen to outline text with.</param>
/// <param name="location">The location on the image to start drawign the text from.</param>
public DrawTextProcessor(TextGraphicsOptions options, string text, Font font, IBrush<TPixel> brush, IPen<TPixel> pen, PointF location)
{
Guard.NotNull(text, nameof(text));
Guard.NotNull(font, nameof(font));
if (brush == null && pen == null)
{
throw new ArgumentNullException($"at least one of {nameof(brush)} or {nameof(pen)} must not be null");
}
this.Options = options;
this.Text = text;
this.Font = font;
this.Location = location;
this.Brush = brush;
this.Pen = pen;
}
/// <summary>
/// Gets the brush.
/// </summary>
public IBrush<TPixel> Brush { get; }
/// <summary>
/// Gets the options
/// </summary>
public TextGraphicsOptions Options { get; }
/// <summary>
/// Gets the text
/// </summary>
public string Text { get; }
/// <summary>
/// Gets the pen used for outlining the text, if Null then we will not outline
/// </summary>
public IPen<TPixel> Pen { get; }
/// <summary>
/// Gets the font used to render the text.
/// </summary>
public Font Font { get; }
/// <summary>
/// Gets the location to draw the text at.
/// </summary>
public PointF Location { get; }
protected override void BeforeImageApply(Image<TPixel> source, Rectangle sourceRectangle)
{
base.BeforeImageApply(source, sourceRectangle);
// do everythign at the image level as we are deligating the processing down to other processors
var style = new RendererOptions(this.Font, this.Options.DpiX, this.Options.DpiY, this.Location)
{
ApplyKerning = this.Options.ApplyKerning,
TabWidth = this.Options.TabWidth,
WrappingWidth = this.Options.WrapTextWidth,
HorizontalAlignment = this.Options.HorizontalAlignment,
VerticalAlignment = this.Options.VerticalAlignment
};
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);
}
protected override void AfterImageApply(Image<TPixel> source, Rectangle sourceRectangle)
{
base.AfterImageApply(source, sourceRectangle);
this.textRenderer?.Dispose();
this.textRenderer = null;
}
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
{
// this is a no-op as we have processes all as an image, we should be able to pass out of before email apply a skip frames outcome
Draw(this.textRenderer.FillOperations, this.Brush);
Draw(this.textRenderer.OutlineOperations, this.Pen?.StrokeFill);
void Draw(List<DrawingOperation> operations, IBrush<TPixel> brush)
{
if (operations?.Count > 0)
{
using (BrushApplicator<TPixel> app = brush.CreateApplicator(source, sourceRectangle, this.textRenderer.Options))
{
foreach (DrawingOperation operation in operations)
{
Buffer2D<float> buffer = operation.Map;
int startY = operation.Location.Y;
int startX = operation.Location.X;
int offSetSpan = 0;
if (startX < 0)
{
offSetSpan = -startX;
startX = 0;
}
int fistRow = 0;
if (startY < 0)
{
fistRow = -startY;
}
int end = operation.Map.Height;
int maxHeight = source.Height - startY;
end = Math.Min(end, maxHeight);
for (int row = fistRow; row < end; row++)
{
int y = startY + row;
Span<float> span = buffer.GetRowSpan(row).Slice(offSetSpan);
app.Apply(span, startX, y);
}
}
}
}
}
}
private struct DrawingOperation
{
public Buffer2D<float> Map { get; set; }
public Point Location { get; set; }
}
private class CachingGlyphRenderer : IGlyphRenderer, IDisposable
{
private PathBuilder builder;
private Point currentRenderPosition = default;
private GlyphRendererParameters currentGlyphRenderParams = default;
private int offset = 0;
private PointF currentPoint = default(PointF);
private readonly Dictionary<GlyphRendererParameters, GlyphRenderData> glyphData = new Dictionary<GlyphRendererParameters, GlyphRenderData>();
private bool renderOutline = false;
private bool renderFill = false;
private bool raterizationRequired = false;
public CachingGlyphRenderer(MemoryAllocator memoryAllocator, int size, IPen pen, bool renderFill)
{
this.MemoryAllocator = memoryAllocator;
this.Pen = pen;
this.renderFill = renderFill;
this.renderOutline = pen != null;
this.offset = 2;
if (this.renderFill)
{
this.FillOperations = new List<DrawingOperation>(size);
}
if (this.renderOutline)
{
this.offset = (int)MathF.Ceiling((pen.StrokeWidth * 2) + 2);
this.OutlineOperations = new List<DrawingOperation>(size);
}
this.builder = new PathBuilder();
}
public List<DrawingOperation> FillOperations { get; }
public List<DrawingOperation> OutlineOperations { get; }
public MemoryAllocator MemoryAllocator { get; internal set; }
public IPen Pen { get; internal set; }
public GraphicsOptions Options { get; internal set; }
public void BeginFigure()
{
this.builder.StartFigure();
}
public bool BeginGlyph(RectangleF bounds, GlyphRendererParameters paramters)
{
this.currentRenderPosition = Point.Truncate(bounds.Location);
// 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))
{
// we have already drawn the glyph vectors skip trying again
this.raterizationRequired = false;
return false;
}
// we check to see if we have a render cache and if we do then we render else
this.builder.Clear();
// 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;
return true;
}
public void BeginText(RectangleF bounds)
{
// not concerned about this one
this.OutlineOperations?.Clear();
this.FillOperations?.Clear();
}
public void CubicBezierTo(PointF secondControlPoint, PointF thirdControlPoint, PointF point)
{
this.builder.AddBezier(this.currentPoint, secondControlPoint, thirdControlPoint, point);
this.currentPoint = point;
}
public void Dispose()
{
foreach (KeyValuePair<GlyphRendererParameters, GlyphRenderData> kv in this.glyphData)
{
kv.Value.Dispose();
}
this.glyphData.Clear();
}
public void EndFigure()
{
this.builder.CloseFigure();
}
public void EndGlyph()
{
GlyphRenderData renderData = default;
// has the glyoh been rendedered already????
if (this.raterizationRequired)
{
IPath path = this.builder.Build();
if (this.renderFill)
{
renderData.FillMap = this.Render(path);
}
if (this.renderOutline)
{
if (this.Pen.StrokePattern.Length == 0)
{
path = path.GenerateOutline(this.Pen.StrokeWidth);
}
else
{
path = path.GenerateOutline(this.Pen.StrokeWidth, this.Pen.StrokePattern);
}
renderData.OutlineMap = this.Render(path);
}
this.glyphData[this.currentGlyphRenderParams] = renderData;
}
else
{
renderData = this.glyphData[this.currentGlyphRenderParams];
}
if (this.renderFill)
{
this.FillOperations.Add(new DrawingOperation
{
Location = this.currentRenderPosition,
Map = renderData.FillMap
});
}
if (this.renderOutline)
{
this.OutlineOperations.Add(new DrawingOperation
{
Location = this.currentRenderPosition,
Map = renderData.OutlineMap
});
}
}
private Buffer2D<float> Render(IPath path)
{
Size size = Rectangle.Ceiling(path.Bounds).Size;
size = new Size(size.Width + (this.offset * 2), size.Height + (this.offset * 2));
float subpixelCount = 4;
float offset = 0.5f;
if (this.Options.Antialias)
{
offset = 0f; // we are antialising skip offsetting as real antalising should take care of offset.
subpixelCount = this.Options.AntialiasSubpixelDepth;
if (subpixelCount < 4)
{
subpixelCount = 4;
}
}
// take the path inside the path builder, scan thing and generate a Buffer2d representing the glyph and cache it.
Buffer2D<float> fullBuffer = this.MemoryAllocator.Allocate2D<float>(size.Width + 1, size.Height + 1, true);
using (IBuffer<float> bufferBacking = this.MemoryAllocator.Allocate<float>(path.MaxIntersections))
using (IBuffer<PointF> rowIntersectionBuffer = this.MemoryAllocator.Allocate<PointF>(size.Width))
{
float subpixelFraction = 1f / subpixelCount;
float subpixelFractionPoint = subpixelFraction / subpixelCount;
for (int y = 0; y <= size.Height; y++)
{
Span<float> scanline = fullBuffer.GetRowSpan(y);
bool scanlineDirty = false;
float yPlusOne = y + 1;
for (float subPixel = (float)y; subPixel < yPlusOne; subPixel += subpixelFraction)
{
var start = new PointF(path.Bounds.Left - 1, subPixel);
var end = new PointF(path.Bounds.Right + 1, subPixel);
Span<PointF> intersectionSpan = rowIntersectionBuffer.GetSpan();
Span<float> buffer = bufferBacking.GetSpan();
int pointsFound = path.FindIntersections(start, end, intersectionSpan);
if (pointsFound == 0)
{
// nothing on this line skip
continue;
}
for (int i = 0; i < pointsFound && i < intersectionSpan.Length; i++)
{
buffer[i] = intersectionSpan[i].X;
}
QuickSort.Sort(buffer.Slice(0, pointsFound));
for (int point = 0; point < pointsFound; point += 2)
{
// points will be paired up
float scanStart = buffer[point];
float scanEnd = buffer[point + 1];
int startX = (int)MathF.Floor(scanStart + offset);
int endX = (int)MathF.Floor(scanEnd + offset);
if (startX >= 0 && startX < scanline.Length)
{
for (float x = scanStart; x < startX + 1; x += subpixelFraction)
{
scanline[startX] += subpixelFractionPoint;
scanlineDirty = true;
}
}
if (endX >= 0 && endX < scanline.Length)
{
for (float x = endX; x < scanEnd; x += subpixelFraction)
{
scanline[endX] += subpixelFractionPoint;
scanlineDirty = true;
}
}
int nextX = startX + 1;
endX = Math.Min(endX, scanline.Length); // reduce to end to the right edge
nextX = Math.Max(nextX, 0);
for (int x = nextX; x < endX; x++)
{
scanline[x] += subpixelFraction;
scanlineDirty = true;
}
}
}
if (scanlineDirty)
{
if (!this.Options.Antialias)
{
for (int x = 0; x < size.Width; x++)
{
if (scanline[x] >= 0.5)
{
scanline[x] = 1;
}
else
{
scanline[x] = 0;
}
}
}
}
}
}
return fullBuffer;
}
public void EndText()
{
}
public void LineTo(PointF point)
{
this.builder.AddLine(this.currentPoint, point);
this.currentPoint = point;
}
public void MoveTo(PointF point)
{
this.builder.StartFigure();
this.currentPoint = point;
}
public void QuadraticBezierTo(PointF secondControlPoint, PointF point)
{
this.builder.AddBezier(this.currentPoint, secondControlPoint, point);
this.currentPoint = point;
}
private struct GlyphRenderData : IDisposable
{
public Buffer2D<float> FillMap;
public Buffer2D<float> OutlineMap;
public void Dispose()
{
this.FillMap?.Dispose();
this.OutlineMap?.Dispose();
}
}
}
}
}

19
src/ImageSharp.Drawing/Processing/Text/TextGraphicsOptions.cs

@ -11,6 +11,8 @@ namespace SixLabors.ImageSharp.Processing.Text
/// </summary>
public struct TextGraphicsOptions
{
private const int DefaultTextDpi = 72;
/// <summary>
/// Represents the default <see cref="TextGraphicsOptions"/>.
/// </summary>
@ -26,11 +28,16 @@ namespace SixLabors.ImageSharp.Processing.Text
private float? tabWidth;
private float? dpiX;
private float? dpiY;
private PixelBlenderMode blenderMode;
private float wrapTextWidth;
private HorizontalAlignment? horizontalAlignment;
private VerticalAlignment? verticalAlignment;
/// <summary>
@ -49,6 +56,8 @@ namespace SixLabors.ImageSharp.Processing.Text
this.blenderMode = PixelBlenderMode.Normal;
this.blendPercentage = 1;
this.antialias = enableAntialiasing;
this.dpiX = DefaultTextDpi;
this.dpiY = DefaultTextDpi;
}
/// <summary>
@ -90,6 +99,16 @@ namespace SixLabors.ImageSharp.Processing.Text
/// </summary>
public float WrapTextWidth { get => this.wrapTextWidth; set => this.wrapTextWidth = value; }
/// <summary>
/// Gets or sets a value indicating the DPI to render text along the X axis.
/// </summary>
public float DpiX { get => this.dpiX ?? DefaultTextDpi; set => this.dpiX = value; }
/// <summary>
/// Gets or sets a value indicating the DPI to render text along the Y axis.
/// </summary>
public float DpiY { get => this.dpiY ?? DefaultTextDpi; set => this.dpiY = value; }
/// <summary>
/// Gets or sets a value indicating how to align the text relative to the rendering space.
/// If <see cref="WrapTextWidth"/> is greater than zero it will align relative to the space

84
src/ImageSharp.Drawing/Utils/QuickSort.cs

@ -0,0 +1,84 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Utils
{
/// <summary>
/// Optimized quick sort implementation for Span{float} input
/// </summary>
internal class QuickSort
{
/// <summary>
/// Sorts the elements of <paramref name="data"/> in ascending order
/// </summary>
/// <param name="data">The items to sort</param>
public static void Sort(Span<float> data)
{
if (data.Length < 2)
{
return;
}
if (data.Length == 2)
{
if (data[0] > data[1])
{
Swap(ref data[0], ref data[1]);
}
return;
}
Sort(ref data[0], 0, data.Length - 1);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void Swap(ref float left, ref float right)
{
float tmp = left;
left = right;
right = tmp;
}
private static void Sort(ref float data0, int lo, int hi)
{
if (lo < hi)
{
int p = Partition(ref data0, lo, hi);
Sort(ref data0, lo, p);
Sort(ref data0, p + 1, hi);
}
}
private static int Partition(ref float data0, int lo, int hi)
{
float pivot = Unsafe.Add(ref data0, lo);
int i = lo - 1;
int j = hi + 1;
while (true)
{
do
{
i = i + 1;
}
while (Unsafe.Add(ref data0, i) < pivot && i < hi);
do
{
j = j - 1;
}
while (Unsafe.Add(ref data0, j) > pivot && j > lo);
if (i >= j)
{
return j;
}
Swap(ref Unsafe.Add(ref data0, i), ref Unsafe.Add(ref data0, j));
}
}
}
}

114
src/ImageSharp/Advanced/AdvancedImageExtensions.cs

@ -3,8 +3,8 @@
using System;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
namespace SixLabors.ImageSharp.Advanced
{
@ -23,6 +23,52 @@ namespace SixLabors.ImageSharp.Advanced
where TPixel : struct, IPixel<TPixel>
=> GetConfiguration((IConfigurable)source);
/// <summary>
/// Gets the representation of the pixels as a <see cref="Span{T}"/> of contiguous memory in the source image's pixel format
/// stored in row major order.
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="source">The source.</param>
/// <returns>The <see cref="Span{TPixel}"/></returns>
public static Span<TPixel> GetPixelSpan<TPixel>(this ImageFrame<TPixel> source)
where TPixel : struct, IPixel<TPixel>
=> source.GetPixelMemory().Span;
/// <summary>
/// Gets the representation of the pixels as a <see cref="Span{T}"/> of contiguous memory in the source image's pixel format
/// stored in row major order.
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="source">The source.</param>
/// <returns>The <see cref="Span{TPixel}"/></returns>
public static Span<TPixel> GetPixelSpan<TPixel>(this Image<TPixel> source)
where TPixel : struct, IPixel<TPixel>
=> source.Frames.RootFrame.GetPixelSpan();
/// <summary>
/// Gets the representation of the pixels as a <see cref="Span{T}"/> of contiguous memory
/// at row <paramref name="rowIndex"/> beginning from the the first pixel on that row.
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="source">The source.</param>
/// <param name="rowIndex">The row.</param>
/// <returns>The <see cref="Span{TPixel}"/></returns>
public static Span<TPixel> GetPixelRowSpan<TPixel>(this ImageFrame<TPixel> source, int rowIndex)
where TPixel : struct, IPixel<TPixel>
=> source.PixelBuffer.GetRowSpan(rowIndex);
/// <summary>
/// Gets the representation of the pixels as <see cref="Span{T}"/> of of contiguous memory
/// at row <paramref name="rowIndex"/> beginning from the the first pixel on that row.
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="source">The source.</param>
/// <param name="rowIndex">The row.</param>
/// <returns>The <see cref="Span{TPixel}"/></returns>
public static Span<TPixel> GetPixelRowSpan<TPixel>(this Image<TPixel> source, int rowIndex)
where TPixel : struct, IPixel<TPixel>
=> source.Frames.RootFrame.GetPixelRowSpan(rowIndex);
/// <summary>
/// Returns a reference to the 0th element of the Pixel buffer,
/// allowing direct manipulation of pixel data through unsafe operations.
@ -31,9 +77,10 @@ namespace SixLabors.ImageSharp.Advanced
/// <typeparam name="TPixel">The Pixel format.</typeparam>
/// <param name="source">The source image frame</param>
/// <returns>A pinnable reference the first root of the pixel buffer.</returns>
[Obsolete("This method will be removed in our next release! Please use MemoryMarshal.GetReference(source.GetPixelSpan())!")]
public static ref TPixel DangerousGetPinnableReferenceToPixelBuffer<TPixel>(this ImageFrame<TPixel> source)
where TPixel : struct, IPixel<TPixel>
=> ref DangerousGetPinnableReferenceToPixelBuffer((IPixelSource<TPixel>)source);
=> ref DangerousGetPinnableReferenceToPixelBuffer((IPixelSource<TPixel>)source);
/// <summary>
/// Returns a reference to the 0th element of the Pixel buffer,
@ -43,59 +90,68 @@ namespace SixLabors.ImageSharp.Advanced
/// <typeparam name="TPixel">The Pixel format.</typeparam>
/// <param name="source">The source image</param>
/// <returns>A pinnable reference the first root of the pixel buffer.</returns>
[Obsolete("This method will be removed in our next release! Please use MemoryMarshal.GetReference(source.GetPixelSpan())!")]
public static ref TPixel DangerousGetPinnableReferenceToPixelBuffer<TPixel>(this Image<TPixel> source)
where TPixel : struct, IPixel<TPixel>
=> ref source.Frames.RootFrame.DangerousGetPinnableReferenceToPixelBuffer();
=> ref source.Frames.RootFrame.DangerousGetPinnableReferenceToPixelBuffer();
/// <summary>
/// Gets the representation of the pixels as an area of contiguous memory in the given pixel format.
/// Gets the representation of the pixels as a <see cref="Memory{T}"/> of contiguous memory in the source image's pixel format
/// stored in row major order.
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="source">The source.</param>
/// <returns>The <see cref="Span{TPixel}"/></returns>
internal static Span<TPixel> GetPixelSpan<TPixel>(this ImageFrame<TPixel> source)
/// <typeparam name="TPixel">The Pixel format.</typeparam>
/// <param name="source">The source <see cref="ImageFrame{TPixel}"/></param>
/// <returns>The <see cref="Memory{T}"/></returns>
internal static Memory<TPixel> GetPixelMemory<TPixel>(this ImageFrame<TPixel> source)
where TPixel : struct, IPixel<TPixel>
=> GetSpan(source);
{
return source.PixelBuffer.Buffer.Memory;
}
/// <summary>
/// Gets the representation of the pixels as an area of contiguous memory at row 'y' beginning from the the first pixel on that row.
/// Gets the representation of the pixels as a <see cref="Memory{T}"/> of contiguous memory in the source image's pixel format
/// stored in row major order.
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="source">The source.</param>
/// <param name="row">The row.</param>
/// <returns>The <see cref="Span{TPixel}"/></returns>
internal static Span<TPixel> GetPixelRowSpan<TPixel>(this ImageFrame<TPixel> source, int row)
/// <typeparam name="TPixel">The Pixel format.</typeparam>
/// <param name="source">The source <see cref="Image{TPixel}"/></param>
/// <returns>The <see cref="Memory{T}"/></returns>
internal static Memory<TPixel> GetPixelMemory<TPixel>(this Image<TPixel> source)
where TPixel : struct, IPixel<TPixel>
=> GetSpan(source, row);
{
return source.Frames.RootFrame.GetPixelMemory();
}
/// <summary>
/// Gets the representation of the pixels as an area of contiguous memory in the given pixel format.
/// Gets the representation of the pixels as a <see cref="Span{T}"/> of contiguous memory
/// at row <paramref name="rowIndex"/> beginning from the the first pixel on that row.
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="source">The source.</param>
/// <param name="rowIndex">The row.</param>
/// <returns>The <see cref="Span{TPixel}"/></returns>
internal static Span<TPixel> GetPixelSpan<TPixel>(this Image<TPixel> source)
internal static Memory<TPixel> GetPixelRowMemory<TPixel>(this ImageFrame<TPixel> source, int rowIndex)
where TPixel : struct, IPixel<TPixel>
=> source.Frames.RootFrame.GetPixelSpan();
=> source.PixelBuffer.GetRowMemory(rowIndex);
/// <summary>
/// Gets the representation of the pixels as an area of contiguous memory at row 'y' beginning from the the first pixel on that row.
/// Gets the representation of the pixels as <see cref="Span{T}"/> of of contiguous memory
/// at row <paramref name="rowIndex"/> beginning from the the first pixel on that row.
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="source">The source.</param>
/// <param name="row">The row.</param>
/// <param name="rowIndex">The row.</param>
/// <returns>The <see cref="Span{TPixel}"/></returns>
internal static Span<TPixel> GetPixelRowSpan<TPixel>(this Image<TPixel> source, int row)
internal static Memory<TPixel> GetPixelRowMemory<TPixel>(this Image<TPixel> source, int rowIndex)
where TPixel : struct, IPixel<TPixel>
=> source.Frames.RootFrame.GetPixelRowSpan(row);
=> source.Frames.RootFrame.GetPixelRowMemory(rowIndex);
/// <summary>
/// Gets the <see cref="MemoryManager"/> assigned to 'source'.
/// Gets the <see cref="MemoryAllocator"/> assigned to 'source'.
/// </summary>
/// <param name="source">The source image</param>
/// <returns>Returns the configuration.</returns>
internal static MemoryManager GetMemoryManager(this IConfigurable source)
=> GetConfiguration(source).MemoryManager;
internal static MemoryAllocator GetMemoryAllocator(this IConfigurable source)
=> GetConfiguration(source).MemoryAllocator;
/// <summary>
/// Gets the span to the backing buffer.
@ -105,7 +161,7 @@ namespace SixLabors.ImageSharp.Advanced
/// <returns>The span retuned from Pixel source</returns>
private static Span<TPixel> GetSpan<TPixel>(IPixelSource<TPixel> source)
where TPixel : struct, IPixel<TPixel>
=> source.PixelBuffer.Span;
=> source.PixelBuffer.GetSpan();
/// <summary>
/// Gets the span to the backing buffer at the given row.
@ -131,7 +187,7 @@ namespace SixLabors.ImageSharp.Advanced
/// </returns>
private static Span<TPixel> GetSpan<TPixel>(Buffer2D<TPixel> source, int row)
where TPixel : struct, IPixel<TPixel>
=> source.Span.Slice(row * source.Width, source.Width);
=> source.GetSpan().Slice(row * source.Width, source.Width);
/// <summary>
/// Gets the configuration.
@ -149,6 +205,6 @@ namespace SixLabors.ImageSharp.Advanced
/// <returns>A reference to the element.</returns>
private static ref TPixel DangerousGetPinnableReferenceToPixelBuffer<TPixel>(IPixelSource<TPixel> source)
where TPixel : struct, IPixel<TPixel>
=> ref MemoryMarshal.GetReference(source.PixelBuffer.Span);
=> ref MemoryMarshal.GetReference(source.PixelBuffer.GetSpan());
}
}

2
src/ImageSharp/Advanced/IPixelSource.cs

@ -1,8 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
namespace SixLabors.ImageSharp.Advanced
{

7
src/ImageSharp/ColorSpaces/CieLab.cs

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

18
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Adapt.cs

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.ColorSpaces;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion
@ -21,9 +20,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The adapted color</returns>
public CieXyz Adapt(CieXyz color, CieXyz sourceWhitePoint)
{
Guard.NotNull(color, nameof(color));
Guard.NotNull(sourceWhitePoint, nameof(sourceWhitePoint));
if (!this.IsChromaticAdaptationPerformed)
{
throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point.");
@ -39,8 +35,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The adapted color</returns>
public CieLab Adapt(CieLab color)
{
Guard.NotNull(color, nameof(color));
if (!this.IsChromaticAdaptationPerformed)
{
throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point.");
@ -62,8 +56,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The adapted color</returns>
public CieLch Adapt(CieLch color)
{
Guard.NotNull(color, nameof(color));
if (!this.IsChromaticAdaptationPerformed)
{
throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point.");
@ -85,8 +77,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The adapted color</returns>
public CieLchuv Adapt(CieLchuv color)
{
Guard.NotNull(color, nameof(color));
if (!this.IsChromaticAdaptationPerformed)
{
throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point.");
@ -108,8 +98,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The adapted color</returns>
public CieLuv Adapt(CieLuv color)
{
Guard.NotNull(color, nameof(color));
if (!this.IsChromaticAdaptationPerformed)
{
throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point.");
@ -131,8 +119,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The adapted color</returns>
public HunterLab Adapt(HunterLab color)
{
Guard.NotNull(color, nameof(color));
if (!this.IsChromaticAdaptationPerformed)
{
throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point.");
@ -154,8 +140,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The adapted color</returns>
public LinearRgb Adapt(LinearRgb color)
{
Guard.NotNull(color, nameof(color));
if (!this.IsChromaticAdaptationPerformed)
{
throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point.");
@ -185,8 +169,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The adapted color</returns>
public Rgb Adapt(Rgb color)
{
Guard.NotNull(color, nameof(color));
LinearRgb linearInput = this.ToLinearRgb(color);
LinearRgb linearOutput = this.Adapt(linearInput);
return this.ToRgb(linearOutput);

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

@ -23,8 +23,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(CieLch color)
{
Guard.NotNull(color, nameof(color));
// Conversion (perserving white point)
CieLab unadapted = CieLchToCieLabConverter.Convert(color);
@ -77,8 +75,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(CieXyz color)
{
Guard.NotNull(color, nameof(color));
// Adaptation
CieXyz adapted = !this.WhitePoint.Equals(this.TargetLabWhitePoint) && this.IsChromaticAdaptationPerformed
? this.ChromaticAdaptation.Transform(color, this.WhitePoint, this.TargetLabWhitePoint)
@ -96,8 +92,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(Cmyk color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor);
}
@ -120,8 +114,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(Hsv color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor);
}

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

@ -80,8 +80,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLch"/></returns>
public CieLch ToCieLch(Cmyk color)
{
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLch(xyzColor);
}

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

@ -19,8 +19,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyy"/></returns>
public CieXyy ToCieXyy(CieLab color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToCieXyy(xyzColor);
@ -33,8 +31,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyy"/></returns>
public CieXyy ToCieXyy(CieLch color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToCieXyy(xyzColor);
@ -47,8 +43,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyy"/></returns>
public CieXyy ToCieXyy(CieLchuv color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToCieXyy(xyzColor);
@ -61,8 +55,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyy"/></returns>
public CieXyy ToCieXyy(CieLuv color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToCieXyy(xyzColor);
@ -75,8 +67,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyy"/></returns>
public CieXyy ToCieXyy(CieXyz color)
{
Guard.NotNull(color, nameof(color));
return CieXyzAndCieXyyConverter.Convert(color);
}
@ -87,8 +77,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyy"/></returns>
public CieXyy ToCieXyy(Cmyk color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToCieXyy(xyzColor);
@ -101,8 +89,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyy"/></returns>
public CieXyy ToCieXyy(Hsl color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToCieXyy(xyzColor);
@ -115,8 +101,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyy"/></returns>
public CieXyy ToCieXyy(Hsv color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToCieXyy(xyzColor);
@ -129,8 +113,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyy"/></returns>
public CieXyy ToCieXyy(HunterLab color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToCieXyy(xyzColor);
@ -143,8 +125,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyy"/></returns>
public CieXyy ToCieXyy(LinearRgb color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToCieXyy(xyzColor);
@ -157,8 +137,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyy"/></returns>
public CieXyy ToCieXyy(Lms color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToCieXyy(xyzColor);
@ -171,8 +149,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyy"/></returns>
public CieXyy ToCieXyy(Rgb color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToCieXyy(xyzColor);
@ -185,8 +161,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyy"/></returns>
public CieXyy ToCieXyy(YCbCr color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToCieXyy(xyzColor);

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

@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.ColorSpaces;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLabColorSapce;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLuvColorSapce;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabColorSapce;
@ -29,8 +28,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyz"/></returns>
public CieXyz ToCieXyz(CieLab color)
{
Guard.NotNull(color, nameof(color));
// Conversion
CieXyz unadapted = CieLabToCieXyzConverter.Convert(color);
@ -49,8 +46,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyz"/></returns>
public CieXyz ToCieXyz(CieLch color)
{
Guard.NotNull(color, nameof(color));
// Conversion to Lab
CieLab labColor = CieLchToCieLabConverter.Convert(color);
@ -65,8 +60,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyz"/></returns>
public CieXyz ToCieXyz(CieLchuv color)
{
Guard.NotNull(color, nameof(color));
// Conversion to Luv
CieLuv luvColor = CieLchuvToCieLuvConverter.Convert(color);
@ -81,8 +74,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyz"/></returns>
public CieXyz ToCieXyz(CieLuv color)
{
Guard.NotNull(color, nameof(color));
// Conversion
CieXyz unadapted = CieLuvToCieXyzConverter.Convert(color);
@ -101,8 +92,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyz"/></returns>
public CieXyz ToCieXyz(CieXyy color)
{
Guard.NotNull(color, nameof(color));
// Conversion
return CieXyzAndCieXyyConverter.Convert(color);
}
@ -114,8 +103,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyz"/></returns>
public CieXyz ToCieXyz(Cmyk color)
{
Guard.NotNull(color, nameof(color));
// Conversion
var rgb = this.ToRgb(color);
@ -129,8 +116,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyz"/></returns>
public CieXyz ToCieXyz(Hsl color)
{
Guard.NotNull(color, nameof(color));
// Conversion
var rgb = this.ToRgb(color);
@ -144,8 +129,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyz"/></returns>
public CieXyz ToCieXyz(Hsv color)
{
Guard.NotNull(color, nameof(color));
// Conversion
var rgb = this.ToRgb(color);
@ -159,8 +142,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyz"/></returns>
public CieXyz ToCieXyz(HunterLab color)
{
Guard.NotNull(color, nameof(color));
// Conversion
CieXyz unadapted = HunterLabToCieXyzConverter.Convert(color);
@ -179,8 +160,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyz"/></returns>
public CieXyz ToCieXyz(LinearRgb color)
{
Guard.NotNull(color, nameof(color));
// Conversion
LinearRgbToCieXyzConverter converter = this.GetLinearRgbToCieXyzConverter(color.WorkingSpace);
CieXyz unadapted = converter.Convert(color);
@ -198,8 +177,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyz"/></returns>
public CieXyz ToCieXyz(Lms color)
{
Guard.NotNull(color, nameof(color));
// Conversion
return this.cachedCieXyzAndLmsConverter.Convert(color);
}
@ -211,8 +188,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyz"/></returns>
public CieXyz ToCieXyz(Rgb color)
{
Guard.NotNull(color, nameof(color));
// Conversion
LinearRgb linear = RgbToLinearRgbConverter.Convert(color);
return this.ToCieXyz(linear);
@ -225,8 +200,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyz"/></returns>
public CieXyz ToCieXyz(YCbCr color)
{
Guard.NotNull(color, nameof(color));
// Conversion
var rgb = this.ToRgb(color);

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

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

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

@ -115,8 +115,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(Hsv color)
{
Guard.NotNull(color, nameof(color));
var rgb = this.ToRgb(color);
return this.ToLinearRgb(rgb);
}

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

@ -110,8 +110,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(Hsl color)
{
Guard.NotNull(color, nameof(color));
// Conversion
return HslAndRgbConverter.Convert(color);
}

2
src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLuv/CieXyzToCieLuvConverter.cs

@ -44,8 +44,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLuvColor
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieLuv Convert(CieXyz input)
{
DebugGuard.NotNull(input, nameof(input));
// Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_Luv.html
float yr = input.Y / this.LuvWhitePoint.Y;
float up = ComputeUp(input);

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

@ -16,8 +16,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieXyyColor
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieXyy Convert(CieXyz input)
{
DebugGuard.NotNull(input, nameof(input));
float x = input.X / (input.X + input.Y + input.Z);
float y = input.Y / (input.X + input.Y + input.Z);
@ -33,8 +31,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieXyyColor
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieXyz Convert(CieXyy input)
{
DebugGuard.NotNull(input, nameof(input));
if (MathF.Abs(input.Y) < Constants.Epsilon)
{
return new CieXyz(0, 0, input.Yl);

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

@ -16,8 +16,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HslColorSap
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Rgb Convert(Hsl input)
{
DebugGuard.NotNull(input, nameof(input));
float rangedH = input.H / 360F;
float r = 0;
float g = 0;
@ -49,8 +47,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HslColorSap
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Hsl Convert(Rgb input)
{
DebugGuard.NotNull(input, nameof(input));
float r = input.R;
float g = input.G;
float b = input.B;

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

@ -16,8 +16,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HsvColorSap
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Rgb Convert(Hsv input)
{
DebugGuard.NotNull(input, nameof(input));
float s = input.S;
float v = input.V;
@ -81,8 +79,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HsvColorSap
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Hsv Convert(Rgb input)
{
DebugGuard.NotNull(input, nameof(input));
float r = input.R;
float g = input.G;
float b = input.B;

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

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

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

@ -33,18 +33,12 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabCo
/// <summary>
/// Gets the target reference white. When not set, <see cref="HunterLab.DefaultWhitePoint"/> is used.
/// </summary>
public CieXyz HunterLabWhitePoint
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get;
}
public CieXyz HunterLabWhitePoint { get; }
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public HunterLab Convert(CieXyz input)
{
DebugGuard.NotNull(input, nameof(input));
// Conversion algorithm described here: http://en.wikipedia.org/wiki/Lab_color_space#Hunter_Lab
float x = input.X, y = input.Y, z = input.Z;
float xn = this.HunterLabWhitePoint.X, yn = this.HunterLabWhitePoint.Y, zn = this.HunterLabWhitePoint.Z;

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

@ -38,8 +38,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap
/// <inheritdoc/>
public LinearRgb Convert(CieXyz input)
{
DebugGuard.NotNull(input, nameof(input));
Matrix4x4.Invert(this.conversionMatrix, out Matrix4x4 inverted);
Vector3 vector = Vector3.Transform(input.Vector, inverted);
return new LinearRgb(vector, this.TargetWorkingSpace);

1
src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToCieXyzConverter.cs

@ -38,7 +38,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap
/// <inheritdoc/>
public CieXyz Convert(LinearRgb input)
{
DebugGuard.NotNull(input, nameof(input));
DebugGuard.IsTrue(input.WorkingSpace.Equals(this.SourceWorkingSpace), nameof(input.WorkingSpace), "Input and source working spaces must be equal.");
Vector3 vector = Vector3.Transform(input.Vector, this.conversionMatrix);

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

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

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

@ -19,8 +19,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.YCbCrColorS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Rgb Convert(YCbCr input)
{
DebugGuard.NotNull(input, nameof(input));
float y = input.Y;
float cb = input.Cb - 128F;
float cr = input.Cr - 128F;
@ -36,8 +34,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.YCbCrColorS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public YCbCr Convert(Rgb input)
{
DebugGuard.NotNull(input, nameof(input));
Vector3 rgb = input.Vector * MaxBytes;
float r = rgb.X;
float g = rgb.Y;

9
src/ImageSharp/Common/Constants.cs

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

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

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

29
src/ImageSharp/Common/Extensions/StreamExtensions.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Buffers;
using System;
using System.IO;
namespace SixLabors.ImageSharp
@ -11,6 +11,33 @@ namespace SixLabors.ImageSharp
/// </summary>
internal static class StreamExtensions
{
#if NETCOREAPP2_1
/// <summary>
/// Writes data from a stream into the provided buffer.
/// </summary>
/// <param name="stream">The stream.</param>
/// <param name="buffer">The buffer.</param>
/// <param name="offset">The offset within the buffer to begin writing.</param>
/// <param name="count">The number of bytes to write to the stream.</param>
public static void Write(this Stream stream, Span<byte> buffer, int offset, int count)
{
stream.Write(buffer.Slice(offset, count));
}
/// <summary>
/// Reads data from a stream into the provided buffer.
/// </summary>
/// <param name="stream">The stream.</param>
/// <param name="buffer">The buffer..</param>
/// <param name="offset">The offset within the buffer where the bytes are read into.</param>
/// <param name="count">The number of bytes, if available, to read.</param>
/// <returns>The actual number of bytes read.</returns>
public static int Read(this Stream stream, Span<byte> buffer, int offset, int count)
{
return stream.Read(buffer.Slice(offset, count));
}
#endif
/// <summary>
/// Skips the number of bytes in the given stream.
/// </summary>

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

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

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

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

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

@ -38,7 +38,7 @@ namespace SixLabors.ImageSharp
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int GetBitsNeededForColorDepth(int colors)
{
return (int)Math.Ceiling(Math.Log(colors, 2));
return Math.Max(1, (int)Math.Ceiling(Math.Log(colors, 2)));
}
/// <summary>
@ -153,8 +153,8 @@ namespace SixLabors.ImageSharp
{
int width = bitmap.Width;
int height = bitmap.Height;
var topLeft = default(Point);
var bottomRight = default(Point);
Point topLeft = default;
Point bottomRight = default;
Func<ImageFrame<TPixel>, int, int, float, bool> delegateFunc;

8
src/ImageSharp/Common/Helpers/ParallelFor.cs

@ -1,6 +1,6 @@
using System;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Memory;
using SixLabors.Memory;
namespace SixLabors.ImageSharp
{
@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp
/// <typeparam name="T">The value type of the buffer</typeparam>
/// <param name="fromInclusive">The start index, inclusive.</param>
/// <param name="toExclusive">The end index, exclusive.</param>
/// <param name="configuration">The <see cref="Configuration"/> used for getting the <see cref="MemoryManager"/> and <see cref="ParallelOptions"/></param>
/// <param name="configuration">The <see cref="Configuration"/> used for getting the <see cref="MemoryAllocator"/> and <see cref="ParallelOptions"/></param>
/// <param name="bufferLength">The length of the requested parallel buffer</param>
/// <param name="body">The delegate that is invoked once per iteration.</param>
public static void WithTemporaryBuffer<T>(
@ -35,12 +35,12 @@ namespace SixLabors.ImageSharp
Action<int, IBuffer<T>> body)
where T : struct
{
MemoryManager memoryManager = configuration.MemoryManager;
MemoryAllocator memoryAllocator = configuration.MemoryAllocator;
ParallelOptions parallelOptions = configuration.ParallelOptions;
IBuffer<T> InitBuffer()
{
return memoryManager.Allocate<T>(bufferLength);
return memoryAllocator.Allocate<T>(bufferLength);
}
void CleanUpBuffer(IBuffer<T> buffer)

24
src/ImageSharp/Common/Helpers/TestHelpers.cs

@ -0,0 +1,24 @@
// Copyright(c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Common.Helpers
{
/// <summary>
/// Internal utilities intended to be only used in tests.
/// </summary>
internal static class TestHelpers
{
/// <summary>
/// This constant is useful to verify the target framework ImageSharp has been built against.
/// Only intended to be used in tests!
/// </summary>
internal const string ImageSharpBuiltAgainst =
#if NETSTANDARD1_1
"netstandard1.1";
#elif NETCOREAPP2_1
"netcoreapp2.1";
#else
"netstandard2.0";
#endif
}
}

8
src/ImageSharp/Configuration.cs

@ -12,8 +12,8 @@ using SixLabors.ImageSharp.Formats.Png;
#if !NETSTANDARD1_1
using SixLabors.ImageSharp.IO;
#endif
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Processing;
using SixLabors.Memory;
namespace SixLabors.ImageSharp
{
@ -75,9 +75,9 @@ namespace SixLabors.ImageSharp
public ImageFormatManager ImageFormatsManager { get; set; } = new ImageFormatManager();
/// <summary>
/// Gets or sets the <see cref="MemoryManager"/> that is currently in use.
/// Gets or sets the <see cref="MemoryAllocator"/> that is currently in use.
/// </summary>
public MemoryManager MemoryManager { get; set; } = ArrayPoolMemoryManager.CreateDefault();
public MemoryAllocator MemoryAllocator { get; set; } = ArrayPoolMemoryAllocator.CreateDefault();
/// <summary>
/// Gets the maximum header size of all the formats.
@ -116,7 +116,7 @@ namespace SixLabors.ImageSharp
{
ParallelOptions = this.ParallelOptions,
ImageFormatsManager = this.ImageFormatsManager,
MemoryManager = this.MemoryManager,
MemoryAllocator = this.MemoryAllocator,
ImageOperationsProvider = this.ImageOperationsProvider,
ReadOrigin = this.ReadOrigin,

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

@ -2,11 +2,12 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers.Binary;
using System.IO;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
namespace SixLabors.ImageSharp.Formats.Bmp
{
@ -71,7 +72,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
private readonly Configuration configuration;
private readonly MemoryManager memoryManager;
private readonly MemoryAllocator memoryAllocator;
/// <summary>
/// Initializes a new instance of the <see cref="BmpDecoderCore"/> class.
@ -81,7 +82,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
public BmpDecoderCore(Configuration configuration, IBmpDecoderOptions options)
{
this.configuration = configuration;
this.memoryManager = configuration.MemoryManager;
this.memoryAllocator = configuration.MemoryAllocator;
}
/// <summary>
@ -103,36 +104,42 @@ namespace SixLabors.ImageSharp.Formats.Bmp
this.ReadImageHeaders(stream, out bool inverted, out byte[] palette);
var image = new Image<TPixel>(this.configuration, this.infoHeader.Width, this.infoHeader.Height);
using (PixelAccessor<TPixel> pixels = image.Lock())
Buffer2D<TPixel> pixels = image.GetRootFramePixelBuffer();
switch (this.infoHeader.Compression)
{
switch (this.infoHeader.Compression)
{
case BmpCompression.RGB:
if (this.infoHeader.BitsPerPixel == 32)
{
this.ReadRgb32(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted);
}
else if (this.infoHeader.BitsPerPixel == 24)
{
this.ReadRgb24(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted);
}
else if (this.infoHeader.BitsPerPixel == 16)
{
this.ReadRgb16(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted);
}
else if (this.infoHeader.BitsPerPixel <= 8)
{
this.ReadRgbPalette(pixels, palette, this.infoHeader.Width, this.infoHeader.Height, this.infoHeader.BitsPerPixel, inverted);
}
case BmpCompression.RGB:
if (this.infoHeader.BitsPerPixel == 32)
{
this.ReadRgb32(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted);
}
else if (this.infoHeader.BitsPerPixel == 24)
{
this.ReadRgb24(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted);
}
else if (this.infoHeader.BitsPerPixel == 16)
{
this.ReadRgb16(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted);
}
else if (this.infoHeader.BitsPerPixel <= 8)
{
this.ReadRgbPalette(
pixels,
palette,
this.infoHeader.Width,
this.infoHeader.Height,
this.infoHeader.BitsPerPixel,
inverted);
}
break;
case BmpCompression.RLE8:
this.ReadRle8(pixels, palette, this.infoHeader.Width, this.infoHeader.Height, inverted);
break;
case BmpCompression.RLE8:
this.ReadRle8(pixels, palette, this.infoHeader.Width, this.infoHeader.Height, inverted);
break;
default:
throw new NotSupportedException("Does not support this kind of bitmap files.");
}
break;
default:
throw new NotSupportedException("Does not support this kind of bitmap files.");
}
return image;
@ -202,20 +209,20 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// Compresssed RLE8 stream is uncompressed by <see cref="UncompressRle8(int, Span{byte})"/>
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="pixels">The <see cref="PixelAccessor{TPixel}"/> to assign the palette to.</param>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
/// <param name="colors">The <see cref="T:byte[]"/> containing the colors.</param>
/// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param>
/// <param name="inverted">Whether the bitmap is inverted.</param>
private void ReadRle8<TPixel>(PixelAccessor<TPixel> pixels, byte[] colors, int width, int height, bool inverted)
private void ReadRle8<TPixel>(Buffer2D<TPixel> pixels, byte[] colors, int width, int height, bool inverted)
where TPixel : struct, IPixel<TPixel>
{
var color = default(TPixel);
TPixel color = default;
var rgba = new Rgba32(0, 0, 0, 255);
using (Buffer2D<byte> buffer = this.memoryManager.AllocateClean2D<byte>(width, height))
using (Buffer2D<byte> buffer = this.memoryAllocator.AllocateClean2D<byte>(width, height))
{
this.UncompressRle8(width, buffer.Span);
this.UncompressRle8(width, buffer.GetSpan());
for (int y = 0; y < height; y++)
{
@ -245,7 +252,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <param name="buffer">Buffer for uncompressed data.</param>
private void UncompressRle8(int w, Span<byte> buffer)
{
#if NETCOREAPP2_1
Span<byte> cmd = stackalloc byte[2];
#else
byte[] cmd = new byte[2];
#endif
int count = 0;
while (count < buffer.Length)
@ -313,13 +324,13 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// Reads the color palette from the stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="pixels">The <see cref="PixelAccessor{TPixel}"/> to assign the palette to.</param>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
/// <param name="colors">The <see cref="T:byte[]"/> containing the colors.</param>
/// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param>
/// <param name="bits">The number of bits per pixel.</param>
/// <param name="inverted">Whether the bitmap is inverted.</param>
private void ReadRgbPalette<TPixel>(PixelAccessor<TPixel> pixels, byte[] colors, int width, int height, int bits, bool inverted)
private void ReadRgbPalette<TPixel>(Buffer2D<TPixel> pixels, byte[] colors, int width, int height, int bits, bool inverted)
where TPixel : struct, IPixel<TPixel>
{
// Pixels per byte (bits per pixel)
@ -337,12 +348,12 @@ namespace SixLabors.ImageSharp.Formats.Bmp
padding = 4 - padding;
}
using (IManagedByteBuffer row = this.memoryManager.AllocateCleanManagedByteBuffer(arrayWidth + padding))
using (IManagedByteBuffer row = this.memoryAllocator.AllocateCleanManagedByteBuffer(arrayWidth + padding))
{
TPixel color = default;
var rgba = new Rgba32(0, 0, 0, 255);
Span<byte> rowSpan = row.Span;
Span<byte> rowSpan = row.GetSpan();
for (int y = 0; y < height; y++)
{
@ -377,19 +388,19 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// Reads the 16 bit color palette from the stream
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="pixels">The <see cref="PixelAccessor{TPixel}"/> to assign the palette to.</param>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
/// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param>
/// <param name="inverted">Whether the bitmap is inverted.</param>
private void ReadRgb16<TPixel>(PixelAccessor<TPixel> pixels, int width, int height, bool inverted)
private void ReadRgb16<TPixel>(Buffer2D<TPixel> pixels, int width, int height, bool inverted)
where TPixel : struct, IPixel<TPixel>
{
int padding = CalculatePadding(width, 2);
int stride = (width * 2) + padding;
var color = default(TPixel);
TPixel color = default;
var rgba = new Rgba32(0, 0, 0, 255);
using (IManagedByteBuffer buffer = this.memoryManager.AllocateManagedByteBuffer(stride))
using (IManagedByteBuffer buffer = this.memoryAllocator.AllocateManagedByteBuffer(stride))
{
for (int y = 0; y < height; y++)
{
@ -418,23 +429,23 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// Reads the 24 bit color palette from the stream
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="pixels">The <see cref="PixelAccessor{TPixel}"/> to assign the palette to.</param>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
/// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param>
/// <param name="inverted">Whether the bitmap is inverted.</param>
private void ReadRgb24<TPixel>(PixelAccessor<TPixel> pixels, int width, int height, bool inverted)
private void ReadRgb24<TPixel>(Buffer2D<TPixel> pixels, int width, int height, bool inverted)
where TPixel : struct, IPixel<TPixel>
{
int padding = CalculatePadding(width, 3);
using (IManagedByteBuffer row = this.memoryManager.AllocatePaddedPixelRowBuffer(width, 3, padding))
using (IManagedByteBuffer row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 3, padding))
{
for (int y = 0; y < height; y++)
{
this.stream.Read(row);
int newY = Invert(y, height, inverted);
Span<TPixel> pixelSpan = pixels.GetRowSpan(newY);
PixelOperations<TPixel>.Instance.PackFromBgr24Bytes(row.Span, pixelSpan, width);
PixelOperations<TPixel>.Instance.PackFromBgr24Bytes(row.GetSpan(), pixelSpan, width);
}
}
}
@ -443,23 +454,23 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// Reads the 32 bit color palette from the stream
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="pixels">The <see cref="PixelAccessor{TPixel}"/> to assign the palette to.</param>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
/// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param>
/// <param name="inverted">Whether the bitmap is inverted.</param>
private void ReadRgb32<TPixel>(PixelAccessor<TPixel> pixels, int width, int height, bool inverted)
private void ReadRgb32<TPixel>(Buffer2D<TPixel> pixels, int width, int height, bool inverted)
where TPixel : struct, IPixel<TPixel>
{
int padding = CalculatePadding(width, 4);
using (IManagedByteBuffer row = this.memoryManager.AllocatePaddedPixelRowBuffer(width, 4, padding))
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<TPixel> pixelSpan = pixels.GetRowSpan(newY);
PixelOperations<TPixel>.Instance.PackFromBgra32Bytes(row.Span, pixelSpan, width);
PixelOperations<TPixel>.Instance.PackFromBgra32Bytes(row.GetSpan(), pixelSpan, width);
}
}
}
@ -469,12 +480,14 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// </summary>
private void ReadInfoHeader()
{
#if NETCOREAPP2_1
Span<byte> buffer = stackalloc byte[BmpInfoHeader.MaxHeaderSize];
#else
byte[] buffer = new byte[BmpInfoHeader.MaxHeaderSize];
#endif
this.stream.Read(buffer, 0, BmpInfoHeader.HeaderSizeSize); // read the header size
// read header size
this.stream.Read(buffer, 0, BmpInfoHeader.HeaderSizeSize);
int headerSize = BitConverter.ToInt32(buffer, 0);
int headerSize = BinaryPrimitives.ReadInt32LittleEndian(buffer);
if (headerSize < BmpInfoHeader.CoreSize)
{
throw new NotSupportedException($"ImageSharp does not support this BMP file. HeaderSize: {headerSize}.");
@ -498,7 +511,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
else if (headerSize >= BmpInfoHeader.Size)
{
// >= 40 bytes
this.infoHeader = BmpInfoHeader.Parse(buffer.AsSpan(0, 40));
this.infoHeader = BmpInfoHeader.Parse(buffer);
}
else
{
@ -514,8 +527,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// </summary>
private void ReadFileHeader()
{
#if NETCOREAPP2_1
Span<byte> buffer = stackalloc byte[BmpFileHeader.Size];
#else
byte[] buffer = new byte[BmpFileHeader.Size];
#endif
this.stream.Read(buffer, 0, BmpFileHeader.Size);
this.fileHeader = BmpFileHeader.Parse(buffer);
@ -574,12 +590,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
this.stream.Read(palette, 0, colorMapSize);
}
if (this.infoHeader.Width > int.MaxValue || this.infoHeader.Height > int.MaxValue)
{
throw new ArgumentOutOfRangeException(
$"The input bmp '{this.infoHeader.Width}x{this.infoHeader.Height}' is "
+ $"bigger then the max allowed size '{int.MaxValue}x{int.MaxValue}'");
}
this.infoHeader.VerifyDimensions();
}
}
}

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

@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
public void Encode<TPixel>(Image<TPixel> image, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
var encoder = new BmpEncoderCore(this, image.GetMemoryManager());
var encoder = new BmpEncoderCore(this, image.GetMemoryAllocator());
encoder.Encode(image, stream);
}
}

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

@ -3,8 +3,8 @@
using System;
using System.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
namespace SixLabors.ImageSharp.Formats.Bmp
{
@ -20,16 +20,16 @@ namespace SixLabors.ImageSharp.Formats.Bmp
private readonly BmpBitsPerPixel bitsPerPixel;
private readonly MemoryManager memoryManager;
private readonly MemoryAllocator memoryAllocator;
/// <summary>
/// Initializes a new instance of the <see cref="BmpEncoderCore"/> class.
/// </summary>
/// <param name="options">The encoder options</param>
/// <param name="memoryManager">The memory manager</param>
public BmpEncoderCore(IBmpEncoderOptions options, MemoryManager memoryManager)
/// <param name="memoryAllocator">The memory manager</param>
public BmpEncoderCore(IBmpEncoderOptions options, MemoryAllocator memoryAllocator)
{
this.memoryManager = memoryManager;
this.memoryAllocator = memoryAllocator;
this.bitsPerPixel = options.BitsPerPixel;
}
@ -66,8 +66,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp
reserved: 0,
fileSize: 54 + infoHeader.ImageSize);
byte[] buffer = new byte[40]; // TODO: stackalloc
#if NETCOREAPP2_1
Span<byte> buffer = stackalloc byte[40];
#else
byte[] buffer = new byte[40];
#endif
fileHeader.WriteTo(buffer);
stream.Write(buffer, 0, BmpFileHeader.Size);
@ -92,24 +95,22 @@ namespace SixLabors.ImageSharp.Formats.Bmp
private void WriteImage<TPixel>(Stream stream, ImageFrame<TPixel> image)
where TPixel : struct, IPixel<TPixel>
{
using (PixelAccessor<TPixel> pixels = image.Lock())
Buffer2D<TPixel> pixels = image.PixelBuffer;
switch (this.bitsPerPixel)
{
switch (this.bitsPerPixel)
{
case BmpBitsPerPixel.Pixel32:
this.Write32Bit(stream, pixels);
break;
case BmpBitsPerPixel.Pixel32:
this.Write32Bit(stream, pixels);
break;
case BmpBitsPerPixel.Pixel24:
this.Write24Bit(stream, pixels);
break;
}
case BmpBitsPerPixel.Pixel24:
this.Write24Bit(stream, pixels);
break;
}
}
private IManagedByteBuffer AllocateRow(int width, int bytesPerPixel)
{
return this.memoryManager.AllocatePaddedPixelRowBuffer(width, bytesPerPixel, this.padding);
return this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, bytesPerPixel, this.padding);
}
/// <summary>
@ -117,8 +118,8 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The <see cref="Stream"/> to write to.</param>
/// <param name="pixels">The <see cref="PixelAccessor{TPixel}"/> containing pixel data.</param>
private void Write32Bit<TPixel>(Stream stream, PixelAccessor<TPixel> pixels)
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> containing pixel data.</param>
private void Write32Bit<TPixel>(Stream stream, Buffer2D<TPixel> pixels)
where TPixel : struct, IPixel<TPixel>
{
using (IManagedByteBuffer row = this.AllocateRow(pixels.Width, 4))
@ -126,7 +127,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
for (int y = pixels.Height - 1; y >= 0; y--)
{
Span<TPixel> pixelSpan = pixels.GetRowSpan(y);
PixelOperations<TPixel>.Instance.ToBgra32Bytes(pixelSpan, row.Span, pixelSpan.Length);
PixelOperations<TPixel>.Instance.ToBgra32Bytes(pixelSpan, row.GetSpan(), pixelSpan.Length);
stream.Write(row.Array, 0, row.Length());
}
}
@ -137,8 +138,8 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The <see cref="Stream"/> to write to.</param>
/// <param name="pixels">The <see cref="PixelAccessor{TPixel}"/> containing pixel data.</param>
private void Write24Bit<TPixel>(Stream stream, PixelAccessor<TPixel> pixels)
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> containing pixel data.</param>
private void Write24Bit<TPixel>(Stream stream, Buffer2D<TPixel> pixels)
where TPixel : struct, IPixel<TPixel>
{
using (IManagedByteBuffer row = this.AllocateRow(pixels.Width, 3))
@ -146,7 +147,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
for (int y = pixels.Height - 1; y >= 0; y--)
{
Span<TPixel> pixelSpan = pixels.GetRowSpan(y);
PixelOperations<TPixel>.Instance.ToBgr24Bytes(pixelSpan, row.Span, pixelSpan.Length);
PixelOperations<TPixel>.Instance.ToBgr24Bytes(pixelSpan, row.GetSpan(), pixelSpan.Length);
stream.Write(row.Array, 0, row.Length());
}
}

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

@ -16,12 +16,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <inheritdoc/>
public IImageFormat DetectFormat(ReadOnlySpan<byte> header)
{
if (this.IsSupportedFileFormat(header))
{
return ImageFormats.Bmp;
}
return null;
return this.IsSupportedFileFormat(header) ? ImageFormats.Bmp : null;
}
private bool IsSupportedFileFormat(ReadOnlySpan<byte> header)

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

@ -132,6 +132,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <seealso href="https://msdn.microsoft.com/en-us/library/windows/desktop/dd183376.aspx"/>
public static BmpInfoHeader Parse(ReadOnlySpan<byte> data)
{
if (data.Length != Size)
{
throw new ArgumentException(nameof(data), $"Must be {Size} bytes. Was {data.Length} bytes.");
}
return MemoryMarshal.Cast<byte, BmpInfoHeader>(data)[0];
}
@ -157,5 +162,17 @@ namespace SixLabors.ImageSharp.Formats.Bmp
dest = this;
}
internal void VerifyDimensions()
{
const int MaximumBmpDimension = 65535;
if (this.Width > MaximumBmpDimension || this.Height > MaximumBmpDimension)
{
throw new InvalidOperationException(
$"The input bmp '{this.Width}x{this.Height}' is "
+ $"bigger then the max allowed size '{MaximumBmpDimension}x{MaximumBmpDimension}'");
}
}
}
}

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

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

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

@ -38,7 +38,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <inheritdoc/>
public IImageInfo Identify(Configuration configuration, Stream stream)
{
Guard.NotNull(stream, "stream");
Guard.NotNull(stream, nameof(stream));
var decoder = new GifDecoderCore(configuration, this);
return decoder.Identify(stream);

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

@ -7,9 +7,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.PixelFormats;
using SixLabors.Memory;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Formats.Gif
@ -87,7 +87,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
public FrameDecodingMode DecodingMode { get; }
private MemoryManager MemoryManager => this.configuration.MemoryManager;
private MemoryAllocator MemoryAllocator => this.configuration.MemoryAllocator;
/// <summary>
/// Decodes the stream to the image.
@ -293,7 +293,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
continue;
}
using (IManagedByteBuffer commentsBuffer = this.MemoryManager.AllocateManagedByteBuffer(length))
using (IManagedByteBuffer commentsBuffer = this.MemoryAllocator.AllocateManagedByteBuffer(length))
{
this.stream.Read(commentsBuffer.Array, 0, length);
string comments = this.TextEncoding.GetString(commentsBuffer.Array, 0, length);
@ -321,15 +321,15 @@ namespace SixLabors.ImageSharp.Formats.Gif
if (imageDescriptor.LocalColorTableFlag)
{
int length = imageDescriptor.LocalColorTableSize * 3;
localColorTable = this.configuration.MemoryManager.AllocateManagedByteBuffer(length, true);
localColorTable = this.configuration.MemoryAllocator.AllocateManagedByteBuffer(length, true);
this.stream.Read(localColorTable.Array, 0, length);
}
indices = this.configuration.MemoryManager.AllocateManagedByteBuffer(imageDescriptor.Width * imageDescriptor.Height, true);
indices = this.configuration.MemoryAllocator.AllocateManagedByteBuffer(imageDescriptor.Width * imageDescriptor.Height, true);
this.ReadFrameIndices(imageDescriptor, indices.Span);
ReadOnlySpan<Rgb24> colorTable = MemoryMarshal.Cast<byte, Rgb24>((localColorTable ?? this.globalColorTable).Span);
this.ReadFrameColors(ref image, ref previousFrame, indices.Span, colorTable, imageDescriptor);
this.ReadFrameIndices(imageDescriptor, indices.GetSpan());
ReadOnlySpan<Rgb24> colorTable = MemoryMarshal.Cast<byte, Rgb24>((localColorTable ?? this.globalColorTable).GetSpan());
this.ReadFrameColors(ref image, ref previousFrame, indices.GetSpan(), colorTable, imageDescriptor);
// Skip any remaining blocks
this.Skip(0);
@ -350,7 +350,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
private void ReadFrameIndices(in GifImageDescriptor imageDescriptor, Span<byte> indices)
{
int dataSize = this.stream.ReadByte();
using (var lzwDecoder = new LzwDecoder(this.configuration.MemoryManager, this.stream))
using (var lzwDecoder = new LzwDecoder(this.configuration.MemoryAllocator, this.stream))
{
lzwDecoder.DecodePixels(imageDescriptor.Width, imageDescriptor.Height, dataSize, indices);
}
@ -528,7 +528,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
{
int globalColorTableLength = this.logicalScreenDescriptor.GlobalColorTableSize * 3;
this.globalColorTable = this.MemoryManager.AllocateManagedByteBuffer(globalColorTableLength, true);
this.globalColorTable = this.MemoryAllocator.AllocateManagedByteBuffer(globalColorTableLength, true);
// Read the global color table data from the stream
stream.Read(this.globalColorTable.Array, 0, globalColorTableLength);

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

@ -30,11 +30,16 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
public IQuantizer Quantizer { get; set; } = new OctreeQuantizer();
/// <summary>
/// Gets or sets the color table mode: Global or local.
/// </summary>
public GifColorTableMode ColorTableMode { get; set; }
/// <inheritdoc/>
public void Encode<TPixel>(Image<TPixel> image, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
var encoder = new GifEncoderCore(image.GetConfiguration().MemoryManager, this);
var encoder = new GifEncoderCore(image.GetConfiguration().MemoryAllocator, this);
encoder.Encode(image, stream);
}
}

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

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

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

@ -25,5 +25,10 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// Gets the quantizer used to generate the color palette.
/// </summary>
IQuantizer Quantizer { get; }
/// <summary>
/// Gets the color table mode: Global or local.
/// </summary>
GifColorTableMode ColorTableMode { get; }
}
}

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

@ -5,7 +5,7 @@ using System;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Memory;
using SixLabors.Memory;
namespace SixLabors.ImageSharp.Formats.Gif
{
@ -48,18 +48,18 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// Initializes a new instance of the <see cref="LzwDecoder"/> class
/// and sets the stream, where the compressed data should be read from.
/// </summary>
/// <param name="memoryManager">The <see cref="MemoryManager"/> to use for buffer allocations.</param>
/// <param name="memoryAllocator">The <see cref="MemoryAllocator"/> to use for buffer allocations.</param>
/// <param name="stream">The stream to read from.</param>
/// <exception cref="System.ArgumentNullException"><paramref name="stream"/> is null.</exception>
public LzwDecoder(MemoryManager memoryManager, Stream stream)
public LzwDecoder(MemoryAllocator memoryAllocator, Stream stream)
{
Guard.NotNull(stream, nameof(stream));
this.stream = stream;
this.prefix = memoryManager.Allocate<int>(MaxStackSize, true);
this.suffix = memoryManager.Allocate<int>(MaxStackSize, true);
this.pixelStack = memoryManager.Allocate<int>(MaxStackSize + 1, true);
this.prefix = memoryAllocator.Allocate<int>(MaxStackSize, true);
this.suffix = memoryAllocator.Allocate<int>(MaxStackSize, true);
this.pixelStack = memoryAllocator.Allocate<int>(MaxStackSize + 1, true);
}
/// <summary>
@ -102,9 +102,9 @@ namespace SixLabors.ImageSharp.Formats.Gif
int data = 0;
int first = 0;
ref int prefixRef = ref MemoryMarshal.GetReference(this.prefix.Span);
ref int suffixRef = ref MemoryMarshal.GetReference(this.suffix.Span);
ref int pixelStackRef = ref MemoryMarshal.GetReference(this.pixelStack.Span);
ref int prefixRef = ref MemoryMarshal.GetReference(this.prefix.GetSpan());
ref int suffixRef = ref MemoryMarshal.GetReference(this.suffix.GetSpan());
ref int pixelStackRef = ref MemoryMarshal.GetReference(this.pixelStack.GetSpan());
ref byte pixelsRef = ref MemoryMarshal.GetReference(pixels);
for (code = 0; code < clearCode; code++)
@ -112,7 +112,12 @@ namespace SixLabors.ImageSharp.Formats.Gif
Unsafe.Add(ref suffixRef, code) = (byte)code;
}
#if NETCOREAPP2_1
Span<byte> buffer = stackalloc byte[255];
#else
byte[] buffer = new byte[255];
#endif
while (xyz < length)
{
if (top == 0)
@ -221,15 +226,21 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// The <see cref="int"/>.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#if NETCOREAPP2_1
private int ReadBlock(Span<byte> buffer)
#else
private int ReadBlock(byte[] buffer)
#endif
{
int bufferSize = this.stream.ReadByte();
if (bufferSize < 1)
{
return 0;
}
int count = this.stream.Read(buffer, 0, bufferSize);
return count != bufferSize ? 0 : bufferSize;
}

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

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

2
src/ImageSharp/Formats/IImageEncoder.cs

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

2
src/ImageSharp/Formats/IImageFormatDetector.cs

@ -2,8 +2,6 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Generic;
using System.Text;
namespace SixLabors.ImageSharp.Formats
{

23
src/ImageSharp/Formats/ImageFormatManager.cs

@ -5,7 +5,6 @@ using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SixLabors.ImageSharp.Formats
{
@ -87,9 +86,9 @@ namespace SixLabors.ImageSharp.Formats
{
Guard.NotNullOrWhiteSpace(extension, nameof(extension));
if (extension[0] == '.')
{
extension = extension.Substring(1);
if (extension[0] == '.')
{
extension = extension.Substring(1);
}
return this.imageFormats.FirstOrDefault(x => x.FileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase));
@ -158,12 +157,10 @@ namespace SixLabors.ImageSharp.Formats
public IImageDecoder FindDecoder(IImageFormat format)
{
Guard.NotNull(format, nameof(format));
if (this.mimeTypeDecoders.TryGetValue(format, out IImageDecoder decoder))
{
return decoder;
}
return null;
return this.mimeTypeDecoders.TryGetValue(format, out IImageDecoder decoder)
? decoder
: null;
}
/// <summary>
@ -174,12 +171,10 @@ namespace SixLabors.ImageSharp.Formats
public IImageEncoder FindEncoder(IImageFormat format)
{
Guard.NotNull(format, nameof(format));
if (this.mimeTypeEncoders.TryGetValue(format, out IImageEncoder encoder))
{
return encoder;
}
return null;
return this.mimeTypeEncoders.TryGetValue(format, out IImageEncoder encoder)
? encoder
: null;
}
/// <summary>

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

@ -184,7 +184,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// </summary>
public Block8x8F AsFloatBlock()
{
var result = default(Block8x8F);
Block8x8F result = default;
result.LoadFrom(ref this);
return result;
}

2
src/ImageSharp/Formats/Jpeg/Components/Block8x8F.CopyTo.cs

@ -4,7 +4,7 @@
using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory;
using SixLabors.Memory;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Formats.Jpeg.Components

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

@ -134,14 +134,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
public static Block8x8F Load(Span<float> data)
{
var result = default(Block8x8F);
Block8x8F result = default;
result.LoadFrom(data);
return result;
}
public static Block8x8F Load(Span<int> data)
{
var result = default(Block8x8F);
Block8x8F result = default;
result.LoadFrom(data);
return result;
}
@ -461,7 +461,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
public Block8x8 RoundAsInt16Block()
{
var result = default(Block8x8);
Block8x8 result = default;
this.RoundInto(ref result);
return result;
}

6
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs

@ -62,9 +62,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
// Walking 8 elements at one step:
int n = result.Length / 8;
var rr = default(Vector4Pair);
var gg = default(Vector4Pair);
var bb = default(Vector4Pair);
Vector4Pair rr = default;
Vector4Pair gg = default;
Vector4Pair bb = default;
ref Vector<float> rrRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref rr);
ref Vector<float> ggRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref gg);

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

@ -7,7 +7,7 @@ using System.Linq;
using System.Numerics;
using SixLabors.ImageSharp.Common.Tuples;
using SixLabors.ImageSharp.Memory;
using SixLabors.Memory;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
@ -62,7 +62,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
/// Returns the <see cref="JpegColorConverter"/> for the YCbCr colorspace that matches the current CPU architecture.
/// </summary>
private static JpegColorConverter GetYCbCrConverter() =>
JpegColorConverter.FromYCbCrSimdAvx2.IsAvailable ? (JpegColorConverter)new JpegColorConverter.FromYCbCrSimdAvx2() : new JpegColorConverter.FromYCbCrSimd();
FromYCbCrSimdAvx2.IsAvailable ? (JpegColorConverter)new FromYCbCrSimdAvx2() : new FromYCbCrSimd();
/// <summary>
/// A stack-only struct to reference the input buffers using <see cref="ReadOnlySpan{T}"/>-s.
@ -101,7 +101,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
/// </summary>
/// <param name="componentBuffers">The 1-4 sized list of component buffers.</param>
/// <param name="row">The row to convert</param>
public ComponentValues(IReadOnlyList<IBuffer2D<float>> componentBuffers, int row)
public ComponentValues(IReadOnlyList<Buffer2D<float>> componentBuffers, int row)
{
this.ComponentCount = componentBuffers.Count;

2
src/ImageSharp/Formats/Jpeg/Components/Decoder/IJpegComponent.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors and contributors.
// 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

2
src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs

@ -3,7 +3,7 @@
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Memory;
using SixLabors.Memory;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder

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

@ -3,7 +3,7 @@
using System;
using SixLabors.ImageSharp.Memory;
using SixLabors.Memory;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
@ -26,11 +26,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// <summary>
/// Initializes a new instance of the <see cref="JpegComponentPostProcessor"/> class.
/// </summary>
public JpegComponentPostProcessor(MemoryManager memoryManager, JpegImagePostProcessor imagePostProcessor, IJpegComponent component)
public JpegComponentPostProcessor(MemoryAllocator memoryAllocator, JpegImagePostProcessor imagePostProcessor, IJpegComponent component)
{
this.Component = component;
this.ImagePostProcessor = imagePostProcessor;
this.ColorBuffer = memoryManager.Allocate2D<float>(
this.ColorBuffer = memoryAllocator.Allocate2D<float>(
imagePostProcessor.PostProcessorBufferSize.Width,
imagePostProcessor.PostProcessorBufferSize.Height);

16
src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs

@ -4,12 +4,10 @@
using System;
using System.Linq;
using System.Numerics;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
using SixLabors.Primitives;
using JpegColorConverter = SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters.JpegColorConverter;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
@ -49,17 +47,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// <summary>
/// Initializes a new instance of the <see cref="JpegImagePostProcessor"/> class.
/// </summary>
/// <param name="memoryManager">The <see cref="MemoryManager"/> to use for buffer allocations.</param>
/// <param name="memoryAllocator">The <see cref="MemoryAllocator"/> to use for buffer allocations.</param>
/// <param name="rawJpeg">The <see cref="IRawJpegData"/> representing the uncompressed spectral Jpeg data</param>
public JpegImagePostProcessor(MemoryManager memoryManager, IRawJpegData rawJpeg)
public JpegImagePostProcessor(MemoryAllocator memoryAllocator, IRawJpegData rawJpeg)
{
this.RawJpeg = rawJpeg;
IJpegComponent c0 = rawJpeg.Components.First();
this.NumberOfPostProcessorSteps = c0.SizeInBlocks.Height / BlockRowsPerStep;
this.PostProcessorBufferSize = new Size(c0.SizeInBlocks.Width * 8, PixelRowsPerStep);
this.ComponentProcessors = rawJpeg.Components.Select(c => new JpegComponentPostProcessor(memoryManager, this, c)).ToArray();
this.rgbaBuffer = memoryManager.Allocate<Vector4>(rawJpeg.ImageSizeInPixels.Width);
this.ComponentProcessors = rawJpeg.Components.Select(c => new JpegComponentPostProcessor(memoryAllocator, this, c)).ToArray();
this.rgbaBuffer = memoryAllocator.Allocate<Vector4>(rawJpeg.ImageSizeInPixels.Width);
this.colorConverter = JpegColorConverter.GetConverter(rawJpeg.ColorSpace);
}
@ -155,11 +153,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
int y = yy - this.PixelRowCounter;
var values = new JpegColorConverter.ComponentValues(buffers, y);
this.colorConverter.ConvertToRgba(values, this.rgbaBuffer.Span);
this.colorConverter.ConvertToRgba(values, this.rgbaBuffer.GetSpan());
Span<TPixel> destRow = destination.GetPixelRowSpan(yy);
PixelOperations<TPixel>.Instance.PackFromVector4(this.rgbaBuffer.Span, destRow, destination.Width);
PixelOperations<TPixel>.Instance.PackFromVector4(this.rgbaBuffer.GetSpan(), destRow, destination.Width);
}
}
}

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

@ -37,7 +37,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// <param name="bytesToCheck">The bytes to check</param>
/// <param name="profileIdentifier">The profile identifier</param>
/// <returns>The <see cref="bool"/></returns>
public static bool IsProfile(Span<byte> bytesToCheck, Span<byte> profileIdentifier)
public static bool IsProfile(ReadOnlySpan<byte> bytesToCheck, ReadOnlySpan<byte> profileIdentifier)
{
return bytesToCheck.Length >= profileIdentifier.Length
&& bytesToCheck.Slice(0, profileIdentifier.Length).SequenceEqual(profileIdentifier);

3
src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs

@ -4,10 +4,9 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Formats.Jpeg.Components

8
src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangComponent.cs

@ -6,7 +6,7 @@ using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder;
using SixLabors.ImageSharp.Memory;
using SixLabors.Memory;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
@ -56,9 +56,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// <summary>
/// Initializes <see cref="SpectralBlocks"/>
/// </summary>
/// <param name="memoryManager">The <see cref="MemoryManager"/> to use for buffer allocations.</param>
/// <param name="memoryAllocator">The <see cref="MemoryAllocator"/> to use for buffer allocations.</param>
/// <param name="decoder">The <see cref="GolangJpegDecoderCore"/> instance</param>
public void InitializeDerivedData(MemoryManager memoryManager, GolangJpegDecoderCore decoder)
public void InitializeDerivedData(MemoryAllocator memoryAllocator, GolangJpegDecoderCore decoder)
{
// For 4-component images (either CMYK or YCbCrK), we only support two
// hv vectors: [0x11 0x11 0x11 0x11] and [0x22 0x11 0x11 0x22].
@ -81,7 +81,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
this.SubSamplingDivisors = c0.SamplingFactors.DivideBy(this.SamplingFactors);
}
this.SpectralBlocks = memoryManager.Allocate2D<Block8x8>(this.SizeInBlocks.Width, this.SizeInBlocks.Height, true);
this.SpectralBlocks = memoryAllocator.Allocate2D<Block8x8>(this.SizeInBlocks.Width, this.SizeInBlocks.Height, true);
}
/// <summary>

2
src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangJpegScanDecoder.ComputationData.cs

@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// <returns>The <see cref="ComputationData"/></returns>
public static ComputationData Create()
{
var data = default(ComputationData);
ComputationData data = default;
data.Unzig = ZigZag.CreateUnzigTable();
return data;
}

9
src/ImageSharp/Formats/Jpeg/GolangPort/GolangJpegDecoderCore.cs

@ -338,6 +338,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
break;
case JpegConstants.Markers.DHT:
if (metadataOnly)
{
this.InputProcessor.Skip(remaining);
@ -705,7 +706,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
foreach (GolangComponent component in this.Components)
{
component.InitializeDerivedData(this.configuration.MemoryManager, this);
component.InitializeDerivedData(this.configuration.MemoryAllocator, this);
}
}
}
@ -721,7 +722,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
{
if (remaining < 17)
{
throw new ImageFormatException("DHT has wrong length");
throw new ImageFormatException($"DHT has wrong length. {remaining}");
}
this.InputProcessor.ReadFull(this.Temp, 0, 17);
@ -772,7 +773,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
/// </exception>
private void ProcessStartOfScanMarker(int remaining)
{
var scan = default(GolangJpegScanDecoder);
GolangJpegScanDecoder scan = default;
GolangJpegScanDecoder.InitStreamReading(&scan, this, remaining);
this.InputProcessor.Bits = default;
scan.DecodeBlocks(this);
@ -812,7 +813,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
private Image<TPixel> PostProcessIntoImage<TPixel>()
where TPixel : struct, IPixel<TPixel>
{
using (var postProcessor = new JpegImagePostProcessor(this.configuration.MemoryManager, this))
using (var postProcessor = new JpegImagePostProcessor(this.configuration.MemoryAllocator, this))
{
var image = new Image<TPixel>(this.configuration, this.ImageWidth, this.ImageHeight, this.MetaData);
postProcessor.PostProcess(image.Frames.RootFrame);

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

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

14
src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs

@ -1,6 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.IO;
using System.Runtime.CompilerServices;
@ -543,15 +544,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
specs = new[] { HuffmanSpec.TheHuffmanSpecs[0], HuffmanSpec.TheHuffmanSpecs[1] };
}
foreach (HuffmanSpec s in specs)
for (int i = 0; i < specs.Length; i++)
{
ref HuffmanSpec s = ref specs[i];
markerlen += 1 + 16 + s.Values.Length;
}
this.WriteMarkerHeader(JpegConstants.Markers.DHT, markerlen);
for (int i = 0; i < specs.Length; i++)
{
HuffmanSpec spec = specs[i];
ref HuffmanSpec spec = ref specs[i];
int len = 0;
fixed (byte* huffman = this.huffmanBuffer)
@ -736,16 +738,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
private void WriteStartOfFrame(int width, int height, int componentCount)
{
// "default" to 4:2:0
byte[] subsamples = { 0x22, 0x11, 0x11 };
byte[] chroma = { 0x00, 0x01, 0x01 };
Span<byte> subsamples = stackalloc byte[] { 0x22, 0x11, 0x11 };
Span<byte> chroma = stackalloc byte[] { 0x00, 0x01, 0x01 };
switch (this.subsample)
{
case JpegSubsample.Ratio444:
subsamples = new byte[] { 0x11, 0x11, 0x11 };
subsamples = stackalloc byte[] { 0x11, 0x11, 0x11 };
break;
case JpegSubsample.Ratio420:
subsamples = new byte[] { 0x22, 0x11, 0x11 };
subsamples = stackalloc byte[] { 0x22, 0x11, 0x11 };
break;
}

8
src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/DoubleBufferedStreamReader.cs

@ -5,7 +5,7 @@ using System;
using System.IO;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory;
using SixLabors.Memory;
// TODO: This could be useful elsewhere.
namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
@ -38,13 +38,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
/// <summary>
/// Initializes a new instance of the <see cref="DoubleBufferedStreamReader"/> class.
/// </summary>
/// <param name="memoryManager">The <see cref="MemoryManager"/> to use for buffer allocations.</param>
/// <param name="memoryAllocator">The <see cref="MemoryAllocator"/> to use for buffer allocations.</param>
/// <param name="stream">The input stream.</param>
public DoubleBufferedStreamReader(MemoryManager memoryManager, Stream stream)
public DoubleBufferedStreamReader(MemoryAllocator memoryAllocator, Stream stream)
{
this.stream = stream;
this.length = (int)stream.Length;
this.managedBuffer = memoryManager.AllocateCleanManagedByteBuffer(ChunkLength);
this.managedBuffer = memoryAllocator.AllocateCleanManagedByteBuffer(ChunkLength);
this.bufferChunk = this.managedBuffer.Array;
}

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

@ -2,7 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.Memory;
using SixLabors.Memory;
namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
{
@ -14,10 +14,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
/// <summary>
/// Initializes a new instance of the <see cref="FastACTables"/> class.
/// </summary>
/// <param name="memoryManager">The memory manager used to allocate memory for image processing operations.</param>
public FastACTables(MemoryManager memoryManager)
/// <param name="memoryAllocator">The memory allocator used to allocate memory for image processing operations.</param>
public FastACTables(MemoryAllocator memoryAllocator)
{
this.Tables = memoryManager.AllocateClean2D<short>(512, 4);
this.Tables = memoryAllocator.AllocateClean2D<short>(512, 4);
}
/// <summary>

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

@ -7,7 +7,7 @@ using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder;
using SixLabors.ImageSharp.Memory;
using SixLabors.Memory;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
@ -17,11 +17,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
/// </summary>
internal class PdfJsFrameComponent : IDisposable, IJpegComponent
{
private readonly MemoryManager memoryManager;
private readonly MemoryAllocator memoryAllocator;
public PdfJsFrameComponent(MemoryManager memoryManager, PdfJsFrame frame, byte id, int horizontalFactor, int verticalFactor, byte quantizationTableIndex, int index)
public PdfJsFrameComponent(MemoryAllocator memoryAllocator, PdfJsFrame frame, byte id, int horizontalFactor, int verticalFactor, byte quantizationTableIndex, int index)
{
this.memoryManager = memoryManager;
this.memoryAllocator = memoryAllocator;
this.Frame = frame;
this.Id = id;
this.HorizontalSamplingFactor = horizontalFactor;
@ -129,14 +129,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
this.SubSamplingDivisors = c0.SamplingFactors.DivideBy(this.SamplingFactors);
}
this.SpectralBlocks = this.memoryManager.Allocate2D<Block8x8>(blocksPerColumnForMcu, blocksPerLineForMcu + 1, true);
this.SpectralBlocks = this.memoryAllocator.Allocate2D<Block8x8>(blocksPerColumnForMcu, blocksPerLineForMcu + 1, true);
}
[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.Span), offset);
return ref Unsafe.Add(ref MemoryMarshal.GetReference(this.SpectralBlocks.GetSpan()), offset);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]

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

@ -4,7 +4,7 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Memory;
using SixLabors.Memory;
namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
{
@ -42,17 +42,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
/// <summary>
/// Initializes a new instance of the <see cref="PdfJsHuffmanTable"/> struct.
/// </summary>
/// <param name="memoryManager">The <see cref="MemoryManager"/> to use for buffer allocations.</param>
/// <param name="memoryAllocator">The <see cref="MemoryAllocator"/> to use for buffer allocations.</param>
/// <param name="count">The code lengths</param>
/// <param name="values">The huffman values</param>
public PdfJsHuffmanTable(MemoryManager memoryManager, ReadOnlySpan<byte> count, ReadOnlySpan<byte> values)
public PdfJsHuffmanTable(MemoryAllocator memoryAllocator, ReadOnlySpan<byte> count, ReadOnlySpan<byte> values)
{
const int Length = 257;
using (IBuffer<short> huffcode = memoryManager.Allocate<short>(Length))
using (IBuffer<short> huffcode = memoryAllocator.Allocate<short>(Length))
{
Span<short> codes = huffcode.Span;
ref short huffcodeRef = ref MemoryMarshal.GetReference(huffcode.Span);
Span<short> codes = huffcode.GetSpan();
ref short huffcodeRef = ref MemoryMarshal.GetReference(huffcode.GetSpan());
this.GenerateSizeTable(count);
int k = 0;
@ -196,7 +195,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
}
else
{
// maxcodeRef[i] = -1; // -1 if no codes of this length
// maxcodeRef[i] = -1; // -1 if no codes of this length
}
}

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

@ -2,13 +2,14 @@
// Licensed under the Apache License, Version 2.0.
using System;
#if DEBUG
using System.Diagnostics;
#endif
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.Memory;
namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
{
@ -166,7 +167,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
if (componentsLength == 1)
{
PdfJsFrameComponent component = components[this.compIndex];
ref short blockDataRef = ref MemoryMarshal.GetReference(MemoryMarshal.Cast<Block8x8, short>(component.SpectralBlocks.Span));
ref short blockDataRef = ref MemoryMarshal.GetReference(MemoryMarshal.Cast<Block8x8, short>(component.SpectralBlocks.GetSpan()));
ref PdfJsHuffmanTable dcHuffmanTable = ref dcHuffmanTables[component.DCHuffmanTableId];
ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId];
@ -188,7 +189,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
for (int i = 0; i < componentsLength; i++)
{
PdfJsFrameComponent component = components[i];
ref short blockDataRef = ref MemoryMarshal.GetReference(MemoryMarshal.Cast<Block8x8, short>(component.SpectralBlocks.Span));
ref short blockDataRef = ref MemoryMarshal.GetReference(MemoryMarshal.Cast<Block8x8, short>(component.SpectralBlocks.GetSpan()));
ref PdfJsHuffmanTable dcHuffmanTable = ref dcHuffmanTables[component.DCHuffmanTableId];
ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId];
int h = component.HorizontalSamplingFactor;
@ -224,7 +225,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
if (componentsLength == 1)
{
PdfJsFrameComponent component = components[this.compIndex];
ref short blockDataRef = ref MemoryMarshal.GetReference(MemoryMarshal.Cast<Block8x8, short>(component.SpectralBlocks.Span));
ref short blockDataRef = ref MemoryMarshal.GetReference(MemoryMarshal.Cast<Block8x8, short>(component.SpectralBlocks.GetSpan()));
ref PdfJsHuffmanTable huffmanTable = ref huffmanTables[isAC ? component.ACHuffmanTableId : component.DCHuffmanTableId];
for (int n = 0; n < this.mcuToRead; n++)
@ -267,7 +268,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
for (int i = 0; i < componentsLength; i++)
{
PdfJsFrameComponent component = components[i];
ref short blockDataRef = ref MemoryMarshal.GetReference(MemoryMarshal.Cast<Block8x8, short>(component.SpectralBlocks.Span));
ref short blockDataRef = ref MemoryMarshal.GetReference(MemoryMarshal.Cast<Block8x8, short>(component.SpectralBlocks.GetSpan()));
ref PdfJsHuffmanTable huffmanTable = ref huffmanTables[isAC ? component.ACHuffmanTableId : component.DCHuffmanTableId];
int h = component.HorizontalSamplingFactor;
int v = component.VerticalSamplingFactor;

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

@ -5,7 +5,7 @@ using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.ImageSharp.Memory;
using SixLabors.Memory;
namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
{

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

@ -11,12 +11,12 @@ using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder;
using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.MetaData.Profiles.Exif;
using SixLabors.ImageSharp.MetaData.Profiles.Icc;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Primitives;
using SixLabors.Memory;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
@ -224,7 +224,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
public void ParseStream(Stream stream, bool metadataOnly = false)
{
this.MetaData = new ImageMetaData();
this.InputStream = new DoubleBufferedStreamReader(this.configuration.MemoryManager, stream);
this.InputStream = new DoubleBufferedStreamReader(this.configuration.MemoryAllocator, stream);
// Check for the Start Of Image marker.
this.InputStream.Read(this.markerBuffer, 0, 2);
@ -244,7 +244,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
this.QuantizationTables = new Block8x8F[4];
this.dcHuffmanTables = new PdfJsHuffmanTables();
this.acHuffmanTables = new PdfJsHuffmanTables();
this.fastACTables = new FastACTables(this.configuration.MemoryManager);
this.fastACTables = new FastACTables(this.configuration.MemoryAllocator);
}
while (fileMarker.Marker != JpegConstants.Markers.EOI)
@ -276,6 +276,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
}
case JpegConstants.Markers.DHT:
if (metadataOnly)
{
this.InputStream.Skip(remaining);
@ -683,7 +684,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
maxV = v;
}
var component = new PdfJsFrameComponent(this.configuration.MemoryManager, this.Frame, this.temp[index], h, v, this.temp[index + 2], i);
var component = new PdfJsFrameComponent(this.configuration.MemoryAllocator, this.Frame, this.temp[index], h, v, this.temp[index + 2], i);
this.Frame.Components[i] = component;
this.Frame.ComponentIds[i] = component.Id;
@ -706,22 +707,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
/// <param name="remaining">The remaining bytes in the segment block.</param>
private void ProcessDefineHuffmanTablesMarker(int remaining)
{
if (remaining < 17)
{
throw new ImageFormatException($"DHT has wrong length: {remaining}");
}
using (IManagedByteBuffer huffmanData = this.configuration.MemoryManager.AllocateCleanManagedByteBuffer(256))
using (IManagedByteBuffer huffmanData = this.configuration.MemoryAllocator.AllocateCleanManagedByteBuffer(256))
{
ref byte huffmanDataRef = ref MemoryMarshal.GetReference(huffmanData.Span);
ref byte huffmanDataRef = ref MemoryMarshal.GetReference(huffmanData.GetSpan());
for (int i = 2; i < remaining;)
{
byte huffmanTableSpec = (byte)this.InputStream.ReadByte();
this.InputStream.Read(huffmanData.Array, 0, 16);
using (IManagedByteBuffer codeLengths = this.configuration.MemoryManager.AllocateCleanManagedByteBuffer(17))
using (IManagedByteBuffer codeLengths = this.configuration.MemoryAllocator.AllocateCleanManagedByteBuffer(17))
{
ref byte codeLengthsRef = ref MemoryMarshal.GetReference(codeLengths.Span);
ref byte codeLengthsRef = ref MemoryMarshal.GetReference(codeLengths.GetSpan());
int codeLengthSum = 0;
for (int j = 1; j < 17; j++)
@ -729,7 +725,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
codeLengthSum += Unsafe.Add(ref codeLengthsRef, j) = Unsafe.Add(ref huffmanDataRef, j - 1);
}
using (IManagedByteBuffer huffmanValues = this.configuration.MemoryManager.AllocateCleanManagedByteBuffer(256))
using (IManagedByteBuffer huffmanValues = this.configuration.MemoryAllocator.AllocateCleanManagedByteBuffer(256))
{
this.InputStream.Read(huffmanValues.Array, 0, codeLengthSum);
@ -741,8 +737,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
this.BuildHuffmanTable(
tableType == 0 ? this.dcHuffmanTables : this.acHuffmanTables,
tableIndex,
codeLengths.Span,
huffmanValues.Span);
codeLengths.GetSpan(),
huffmanValues.GetSpan());
if (huffmanTableSpec >> 4 != 0)
{
@ -821,7 +817,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
sd.ParseEntropyCodedData(this.Frame, this.dcHuffmanTables, this.acHuffmanTables, this.fastACTables);
// var scanDecoder = default(PdfJsScanDecoder);
// PdfJsScanDecoder scanDecoder = default;
//
// scanDecoder.DecodeScan(
// this.Frame,
@ -848,7 +844,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void BuildHuffmanTable(PdfJsHuffmanTables tables, int index, ReadOnlySpan<byte> codeLengths, ReadOnlySpan<byte> values)
{
tables[index] = new PdfJsHuffmanTable(this.configuration.MemoryManager, codeLengths, values);
tables[index] = new PdfJsHuffmanTable(this.configuration.MemoryAllocator, codeLengths, values);
}
/// <summary>
@ -865,7 +861,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
private Image<TPixel> PostProcessIntoImage<TPixel>()
where TPixel : struct, IPixel<TPixel>
{
using (var postProcessor = new JpegImagePostProcessor(this.configuration.MemoryManager, this))
using (var postProcessor = new JpegImagePostProcessor(this.configuration.MemoryAllocator, this))
{
var image = new Image<TPixel>(this.configuration, this.ImageWidth, this.ImageHeight, this.MetaData);
postProcessor.PostProcess(image.Frames.RootFrame);

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

@ -11,14 +11,20 @@ namespace SixLabors.ImageSharp.Formats.Png
internal interface IPngEncoderOptions
{
/// <summary>
/// Gets the png color type
/// Gets the number of bits per sample or per palette index (not per pixel).
/// Not all values are allowed for all <see cref="ColorType"/> values.
/// </summary>
PngColorType PngColorType { get; }
PngBitDepth BitDepth { get; }
/// <summary>
/// Gets the png filter method.
/// Gets the color type
/// </summary>
PngFilterMethod PngFilterMethod { get; }
PngColorType ColorType { get; }
/// <summary>
/// Gets the filter method.
/// </summary>
PngFilterMethod FilterMethod { get; }
/// <summary>
/// Gets the compression level 1-9.

22
src/ImageSharp/Formats/Png/PngBitDepth.cs

@ -0,0 +1,22 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
// Note the value assignment, This will allow us to add 1, 2, and 4 bit encoding when we support it.
namespace SixLabors.ImageSharp.Formats.Png
{
/// <summary>
/// Provides enumeration for the available PNG bit depths.
/// </summary>
public enum PngBitDepth
{
/// <summary>
/// 8 bits per sample or per palette index (not per pixel).
/// </summary>
Bit8 = 8,
/// <summary>
/// 16 bits per sample or per palette index (not per pixel).
/// </summary>
Bit16 = 16
}
}

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

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

8
src/ImageSharp/Formats/Png/PngConfigurationModule.cs

@ -9,11 +9,11 @@ namespace SixLabors.ImageSharp.Formats.Png
public sealed class PngConfigurationModule : IConfigurationModule
{
/// <inheritdoc/>
public void Configure(Configuration config)
public void Configure(Configuration configuration)
{
config.ImageFormatsManager.SetEncoder(ImageFormats.Png, new PngEncoder());
config.ImageFormatsManager.SetDecoder(ImageFormats.Png, new PngDecoder());
config.ImageFormatsManager.AddImageFormatDetector(new PngImageFormatDetector());
configuration.ImageFormatsManager.SetEncoder(ImageFormats.Png, new PngEncoder());
configuration.ImageFormatsManager.SetDecoder(ImageFormats.Png, new PngDecoder());
configuration.ImageFormatsManager.AddImageFormatDetector(new PngImageFormatDetector());
}
}
}

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

File diff suppressed because it is too large

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

@ -14,14 +14,20 @@ namespace SixLabors.ImageSharp.Formats.Png
public sealed class PngEncoder : IImageEncoder, IPngEncoderOptions
{
/// <summary>
/// Gets or sets the png color type.
/// Gets or sets the number of bits per sample or per palette index (not per pixel).
/// Not all values are allowed for all <see cref="ColorType"/> values.
/// </summary>
public PngColorType PngColorType { get; set; } = PngColorType.RgbWithAlpha;
public PngBitDepth BitDepth { get; set; } = PngBitDepth.Bit8;
/// <summary>
/// Gets or sets the png filter method.
/// Gets or sets the color type.
/// </summary>
public PngFilterMethod PngFilterMethod { get; set; } = PngFilterMethod.Adaptive;
public PngColorType ColorType { get; set; } = PngColorType.RgbWithAlpha;
/// <summary>
/// Gets or sets the filter method.
/// </summary>
public PngFilterMethod FilterMethod { get; set; } = PngFilterMethod.Paeth;
/// <summary>
/// Gets or sets the compression level 1-9.
@ -63,7 +69,7 @@ namespace SixLabors.ImageSharp.Formats.Png
public void Encode<TPixel>(Image<TPixel> image, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
using (var encoder = new PngEncoderCore(image.GetMemoryManager(), this))
using (var encoder = new PngEncoderCore(image.GetMemoryAllocator(), this))
{
encoder.Encode(image, stream);
}

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

@ -8,9 +8,9 @@ using System.Linq;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats.Png.Filters;
using SixLabors.ImageSharp.Formats.Png.Zlib;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Quantization;
using SixLabors.Memory;
namespace SixLabors.ImageSharp.Formats.Png
{
@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary>
internal sealed class PngEncoderCore : IDisposable
{
private readonly MemoryManager memoryManager;
private readonly MemoryAllocator memoryAllocator;
/// <summary>
/// The maximum block size, defaults at 64k for uncompressed blocks.
@ -41,6 +41,16 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary>
private readonly Crc32 crc = new Crc32();
/// <summary>
/// The png bit depth
/// </summary>
private readonly PngBitDepth pngBitDepth;
/// <summary>
/// Gets or sets a value indicating whether to use 16 bit encoding for supported color types.
/// </summary>
private readonly bool use16Bit;
/// <summary>
/// The png color type.
/// </summary>
@ -76,11 +86,6 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary>
private readonly bool writeGamma;
/// <summary>
/// Contains the raw pixel data from an indexed image.
/// </summary>
private byte[] palettePixelData;
/// <summary>
/// The image width.
/// </summary>
@ -144,13 +149,15 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <summary>
/// Initializes a new instance of the <see cref="PngEncoderCore"/> class.
/// </summary>
/// <param name="memoryManager">The <see cref="MemoryManager"/> to use for buffer allocations.</param>
/// <param name="memoryAllocator">The <see cref="MemoryAllocator"/> to use for buffer allocations.</param>
/// <param name="options">The options for influencing the encoder</param>
public PngEncoderCore(MemoryManager memoryManager, IPngEncoderOptions options)
public PngEncoderCore(MemoryAllocator memoryAllocator, IPngEncoderOptions options)
{
this.memoryManager = memoryManager;
this.pngColorType = options.PngColorType;
this.pngFilterMethod = options.PngFilterMethod;
this.memoryAllocator = memoryAllocator;
this.pngBitDepth = options.BitDepth;
this.use16Bit = this.pngBitDepth.Equals(PngBitDepth.Bit16);
this.pngColorType = options.ColorType;
this.pngFilterMethod = options.FilterMethod;
this.compressionLevel = options.CompressionLevel;
this.gamma = options.Gamma;
this.quantizer = options.Quantizer;
@ -176,11 +183,12 @@ namespace SixLabors.ImageSharp.Formats.Png
stream.Write(PngConstants.HeaderBytes, 0, PngConstants.HeaderBytes.Length);
QuantizedFrame<TPixel> quantized = null;
ReadOnlySpan<byte> quantizedPixelsSpan = default;
if (this.pngColorType == PngColorType.Palette)
{
// Create quantized frame returning the palette and set the bit depth.
quantized = this.quantizer.CreateFrameQuantizer<TPixel>().QuantizeFrame(image.Frames.RootFrame);
this.palettePixelData = quantized.Pixels;
quantizedPixelsSpan = quantized.GetPixelSpan();
byte bits = (byte)ImageMaths.GetBitsNeededForColorDepth(quantized.Palette.Length).Clamp(1, 8);
// Png only supports in four pixel depths: 1, 2, 4, and 8 bits when using the PLTE chunk
@ -197,7 +205,7 @@ namespace SixLabors.ImageSharp.Formats.Png
}
else
{
this.bitDepth = 8;
this.bitDepth = (byte)(this.use16Bit ? 16 : 8);
}
this.bytesPerPixel = this.CalculateBytesPerPixel();
@ -205,11 +213,11 @@ namespace SixLabors.ImageSharp.Formats.Png
var header = new PngHeader(
width: image.Width,
height: image.Height,
colorType: this.pngColorType,
bitDepth: this.bitDepth,
filterMethod: 0, // None
compressionMethod: 0,
interlaceMethod: 0);
colorType: this.pngColorType,
compressionMethod: 0, // None
filterMethod: 0,
interlaceMethod: 0); // TODO: Can't write interlaced yet.
this.WriteHeaderChunk(stream, header);
@ -221,9 +229,11 @@ namespace SixLabors.ImageSharp.Formats.Png
this.WritePhysicalChunk(stream, image);
this.WriteGammaChunk(stream);
this.WriteDataChunks(image.Frames.RootFrame, stream);
this.WriteDataChunks(image.Frames.RootFrame, quantizedPixelsSpan, stream);
this.WriteEndChunk(stream);
stream.Flush();
quantized?.Dispose();
}
/// <inheritdoc />
@ -246,28 +256,62 @@ namespace SixLabors.ImageSharp.Formats.Png
private void CollectGrayscaleBytes<TPixel>(ReadOnlySpan<TPixel> rowSpan)
where TPixel : struct, IPixel<TPixel>
{
byte[] rawScanlineArray = this.rawScanline.Array;
var rgba = default(Rgba32);
// Use ITU-R recommendation 709 to match libpng.
const float RX = .2126F;
const float GX = .7152F;
const float BX = .0722F;
Span<byte> rawScanlineSpan = this.rawScanline.GetSpan();
// Copy the pixels across from the image.
// Reuse the chunk type buffer.
for (int x = 0; x < this.width; x++)
if (this.pngColorType.Equals(PngColorType.Grayscale))
{
// Convert the color to YCbCr and store the luminance
// Optionally store the original color alpha.
int offset = x * this.bytesPerPixel;
rowSpan[x].ToRgba32(ref rgba);
byte luminance = (byte)((0.299F * rgba.R) + (0.587F * rgba.G) + (0.114F * rgba.B));
for (int i = 0; i < this.bytesPerPixel; i++)
// TODO: Realistically we should support 1, 2, 4, 8, and 16 bit grayscale images.
// we currently do the other types via palette. Maybe RC as I don't understand how the data is packed yet
// for 1, 2, and 4 bit grayscale images.
if (this.use16Bit)
{
// 16 bit grayscale
Rgb48 rgb = default;
for (int x = 0, o = 0; x < rowSpan.Length; x++, o += 2)
{
rowSpan[x].ToRgb48(ref rgb);
ushort luminance = (ushort)((RX * rgb.R) + (GX * rgb.G) + (BX * rgb.B));
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o, 2), luminance);
}
}
else
{
// 8 bit grayscale
Rgb24 rgb = default;
for (int x = 0; x < rowSpan.Length; x++)
{
rowSpan[x].ToRgb24(ref rgb);
rawScanlineSpan[x] = (byte)((RX * rgb.R) + (GX * rgb.G) + (BX * rgb.B));
}
}
}
else
{
if (this.use16Bit)
{
if (i == 0)
// 16 bit grayscale + alpha
Rgba64 rgba = default;
for (int x = 0, o = 0; x < rowSpan.Length; x++, o += 4)
{
rawScanlineArray[offset] = luminance;
rowSpan[x].ToRgba64(ref rgba);
ushort luminance = (ushort)((RX * rgba.R) + (GX * rgba.G) + (BX * rgba.B));
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o, 2), luminance);
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o + 2, 2), rgba.A);
}
else
}
else
{
// 8 bit grayscale + alpha
Rgba32 rgba = default;
for (int x = 0, o = 0; x < rowSpan.Length; x++, o += 2)
{
rawScanlineArray[offset + i] = rgba.A;
rowSpan[x].ToRgba32(ref rgba);
rawScanlineSpan[o] = (byte)((RX * rgba.R) + (GX * rgba.G) + (BX * rgba.B));
rawScanlineSpan[o + 1] = rgba.A;
}
}
}
@ -281,13 +325,54 @@ namespace SixLabors.ImageSharp.Formats.Png
private void CollectTPixelBytes<TPixel>(ReadOnlySpan<TPixel> rowSpan)
where TPixel : struct, IPixel<TPixel>
{
if (this.bytesPerPixel == 4)
{
PixelOperations<TPixel>.Instance.ToRgba32Bytes(rowSpan, this.rawScanline.Span, this.width);
}
else
Span<byte> rawScanlineSpan = this.rawScanline.GetSpan();
switch (this.bytesPerPixel)
{
PixelOperations<TPixel>.Instance.ToRgb24Bytes(rowSpan, this.rawScanline.Span, this.width);
case 4:
{
// 8 bit Rgba
PixelOperations<TPixel>.Instance.ToRgba32Bytes(rowSpan, rawScanlineSpan, this.width);
break;
}
case 3:
{
// 8 bit Rgb
PixelOperations<TPixel>.Instance.ToRgb24Bytes(rowSpan, rawScanlineSpan, this.width);
break;
}
case 8:
{
// 16 bit Rgba
Rgba64 rgba = default;
for (int x = 0, o = 0; x < rowSpan.Length; x++, o += 8)
{
rowSpan[x].ToRgba64(ref rgba);
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o, 2), rgba.R);
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o + 2, 2), rgba.G);
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o + 4, 2), rgba.B);
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o + 6, 2), rgba.A);
}
break;
}
default:
{
// 16 bit Rgb
Rgb48 rgb = default;
for (int x = 0, o = 0; x < rowSpan.Length; x++, o += 6)
{
rowSpan[x].ToRgb48(ref rgb);
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o, 2), rgb.R);
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o + 2, 2), rgb.G);
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o + 4, 2), rgb.B);
}
break;
}
}
}
@ -297,16 +382,19 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="rowSpan">The row span.</param>
/// <param name="quantizedPixelsSpan">The span of quantized pixels. Can be null.</param>
/// <param name="row">The row.</param>
/// <returns>The <see cref="IManagedByteBuffer"/></returns>
private IManagedByteBuffer EncodePixelRow<TPixel>(ReadOnlySpan<TPixel> rowSpan, int row)
private IManagedByteBuffer EncodePixelRow<TPixel>(ReadOnlySpan<TPixel> rowSpan, ReadOnlySpan<byte> quantizedPixelsSpan, int row)
where TPixel : struct, IPixel<TPixel>
{
switch (this.pngColorType)
{
case PngColorType.Palette:
// TODO: Use Span copy!
Buffer.BlockCopy(this.palettePixelData, row * this.rawScanline.Length(), this.rawScanline.Array, 0, this.rawScanline.Length());
int stride = this.rawScanline.Length();
quantizedPixelsSpan.Slice(row * stride, stride).CopyTo(this.rawScanline.GetSpan());
break;
case PngColorType.Grayscale:
case PngColorType.GrayscaleWithAlpha:
@ -320,23 +408,23 @@ namespace SixLabors.ImageSharp.Formats.Png
switch (this.pngFilterMethod)
{
case PngFilterMethod.None:
NoneFilter.Encode(this.rawScanline.Span, this.result.Span);
NoneFilter.Encode(this.rawScanline.GetSpan(), this.result.GetSpan());
return this.result;
case PngFilterMethod.Sub:
SubFilter.Encode(this.rawScanline.Span, this.sub.Span, this.bytesPerPixel, out int _);
SubFilter.Encode(this.rawScanline.GetSpan(), this.sub.GetSpan(), this.bytesPerPixel, out int _);
return this.sub;
case PngFilterMethod.Up:
UpFilter.Encode(this.rawScanline.Span, this.previousScanline.Span, this.up.Span, out int _);
UpFilter.Encode(this.rawScanline.GetSpan(), this.previousScanline.GetSpan(), this.up.GetSpan(), out int _);
return this.up;
case PngFilterMethod.Average:
AverageFilter.Encode(this.rawScanline.Span, this.previousScanline.Span, this.average.Span, this.bytesPerPixel, out int _);
AverageFilter.Encode(this.rawScanline.GetSpan(), this.previousScanline.GetSpan(), this.average.GetSpan(), this.bytesPerPixel, out int _);
return this.average;
case PngFilterMethod.Paeth:
PaethFilter.Encode(this.rawScanline.Span, this.previousScanline.Span, this.paeth.Span, this.bytesPerPixel, out int _);
PaethFilter.Encode(this.rawScanline.GetSpan(), this.previousScanline.GetSpan(), this.paeth.GetSpan(), this.bytesPerPixel, out int _);
return this.paeth;
default:
@ -354,21 +442,24 @@ namespace SixLabors.ImageSharp.Formats.Png
// Palette images don't compress well with adaptive filtering.
if (this.pngColorType == PngColorType.Palette || this.bitDepth < 8)
{
NoneFilter.Encode(this.rawScanline.Span, this.result.Span);
NoneFilter.Encode(this.rawScanline.GetSpan(), this.result.GetSpan());
return this.result;
}
Span<byte> scanSpan = this.rawScanline.Span;
Span<byte> prevSpan = this.previousScanline.Span;
Span<byte> scanSpan = this.rawScanline.GetSpan();
Span<byte> prevSpan = this.previousScanline.GetSpan();
// This order, while different to the enumerated order is more likely to produce a smaller sum
// early on which shaves a couple of milliseconds off the processing time.
UpFilter.Encode(scanSpan, prevSpan, this.up.Span, out int currentSum);
UpFilter.Encode(scanSpan, prevSpan, this.up.GetSpan(), out int currentSum);
// TODO: PERF.. We should be breaking out of the encoding for each line as soon as we hit the sum.
// That way the above comment would actually be true. It used to be anyway...
// If we could use SIMD for none branching filters we could really speed it up.
int lowestSum = currentSum;
IManagedByteBuffer actualResult = this.up;
PaethFilter.Encode(scanSpan, prevSpan, this.paeth.Span, this.bytesPerPixel, out currentSum);
PaethFilter.Encode(scanSpan, prevSpan, this.paeth.GetSpan(), this.bytesPerPixel, out currentSum);
if (currentSum < lowestSum)
{
@ -376,7 +467,7 @@ namespace SixLabors.ImageSharp.Formats.Png
actualResult = this.paeth;
}
SubFilter.Encode(scanSpan, this.sub.Span, this.bytesPerPixel, out currentSum);
SubFilter.Encode(scanSpan, this.sub.GetSpan(), this.bytesPerPixel, out currentSum);
if (currentSum < lowestSum)
{
@ -384,7 +475,7 @@ namespace SixLabors.ImageSharp.Formats.Png
actualResult = this.sub;
}
AverageFilter.Encode(scanSpan, prevSpan, this.average.Span, this.bytesPerPixel, out currentSum);
AverageFilter.Encode(scanSpan, prevSpan, this.average.GetSpan(), this.bytesPerPixel, out currentSum);
if (currentSum < lowestSum)
{
@ -403,22 +494,20 @@ namespace SixLabors.ImageSharp.Formats.Png
switch (this.pngColorType)
{
case PngColorType.Grayscale:
return 1;
return this.use16Bit ? 2 : 1;
case PngColorType.GrayscaleWithAlpha:
return 2;
return this.use16Bit ? 4 : 2;
case PngColorType.Palette:
return 1;
case PngColorType.Rgb:
return 3;
return this.use16Bit ? 6 : 3;
// PngColorType.RgbWithAlpha
// TODO: Maybe figure out a way to detect if there are any transparent
// pixels and encode RGB if none.
default:
return 4;
return this.use16Bit ? 8 : 4;
}
}
@ -460,15 +549,16 @@ namespace SixLabors.ImageSharp.Formats.Png
Rgba32 rgba = default;
bool anyAlpha = false;
using (IManagedByteBuffer colorTable = this.memoryManager.AllocateManagedByteBuffer(colorTableLength))
using (IManagedByteBuffer alphaTable = this.memoryManager.AllocateManagedByteBuffer(pixelCount))
using (IManagedByteBuffer colorTable = this.memoryAllocator.AllocateManagedByteBuffer(colorTableLength))
using (IManagedByteBuffer alphaTable = this.memoryAllocator.AllocateManagedByteBuffer(pixelCount))
{
Span<byte> colorTableSpan = colorTable.Span;
Span<byte> alphaTableSpan = alphaTable.Span;
Span<byte> colorTableSpan = colorTable.GetSpan();
Span<byte> alphaTableSpan = alphaTable.GetSpan();
Span<byte> quantizedSpan = quantized.GetPixelSpan();
for (byte i = 0; i < pixelCount; i++)
{
if (quantized.Pixels.Contains(i))
if (quantizedSpan.IndexOf(i) > -1)
{
int offset = i * 3;
palette[i].ToRgba32(ref rgba);
@ -481,10 +571,10 @@ namespace SixLabors.ImageSharp.Formats.Png
if (alpha > this.threshold)
{
alpha = 255;
alpha = byte.MaxValue;
}
anyAlpha = anyAlpha || alpha < 255;
anyAlpha = anyAlpha || alpha < byte.MaxValue;
alphaTableSpan[i] = alpha;
}
}
@ -545,23 +635,49 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="pixels">The image.</param>
/// <param name="quantizedPixelsSpan">The span of quantized pixel data. Can be null.</param>
/// <param name="stream">The stream.</param>
private void WriteDataChunks<TPixel>(ImageFrame<TPixel> pixels, Stream stream)
private void WriteDataChunks<TPixel>(ImageFrame<TPixel> pixels, ReadOnlySpan<byte> quantizedPixelsSpan, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
this.bytesPerScanline = this.width * this.bytesPerPixel;
int resultLength = this.bytesPerScanline + 1;
this.previousScanline = this.memoryManager.AllocateCleanManagedByteBuffer(this.bytesPerScanline);
this.rawScanline = this.memoryManager.AllocateCleanManagedByteBuffer(this.bytesPerScanline);
this.result = this.memoryManager.AllocateCleanManagedByteBuffer(resultLength);
this.previousScanline = this.memoryAllocator.AllocateCleanManagedByteBuffer(this.bytesPerScanline);
this.rawScanline = this.memoryAllocator.AllocateCleanManagedByteBuffer(this.bytesPerScanline);
this.result = this.memoryAllocator.AllocateCleanManagedByteBuffer(resultLength);
if (this.pngColorType != PngColorType.Palette)
switch (this.pngFilterMethod)
{
this.sub = this.memoryManager.AllocateCleanManagedByteBuffer(resultLength);
this.up = this.memoryManager.AllocateCleanManagedByteBuffer(resultLength);
this.average = this.memoryManager.AllocateCleanManagedByteBuffer(resultLength);
this.paeth = this.memoryManager.AllocateCleanManagedByteBuffer(resultLength);
case PngFilterMethod.None:
break;
case PngFilterMethod.Sub:
this.sub = this.memoryAllocator.AllocateCleanManagedByteBuffer(resultLength);
break;
case PngFilterMethod.Up:
this.up = this.memoryAllocator.AllocateCleanManagedByteBuffer(resultLength);
break;
case PngFilterMethod.Average:
this.average = this.memoryAllocator.AllocateCleanManagedByteBuffer(resultLength);
break;
case PngFilterMethod.Paeth:
this.paeth = this.memoryAllocator.AllocateCleanManagedByteBuffer(resultLength);
break;
case PngFilterMethod.Adaptive:
this.sub = this.memoryAllocator.AllocateCleanManagedByteBuffer(resultLength);
this.up = this.memoryAllocator.AllocateCleanManagedByteBuffer(resultLength);
this.average = this.memoryAllocator.AllocateCleanManagedByteBuffer(resultLength);
this.paeth = this.memoryAllocator.AllocateCleanManagedByteBuffer(resultLength);
break;
}
byte[] buffer;
@ -573,7 +689,7 @@ namespace SixLabors.ImageSharp.Formats.Png
{
for (int y = 0; y < this.height; y++)
{
IManagedByteBuffer r = this.EncodePixelRow((ReadOnlySpan<TPixel>)pixels.GetPixelRowSpan(y), y);
IManagedByteBuffer r = this.EncodePixelRow((ReadOnlySpan<TPixel>)pixels.GetPixelRowSpan(y), quantizedPixelsSpan, y);
deflateStream.Write(r.Array, 0, resultLength);
IManagedByteBuffer temp = this.rawScanline;

7
src/ImageSharp/Formats/Png/PngImageFormatDetector.cs

@ -17,12 +17,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <inheritdoc/>
public IImageFormat DetectFormat(ReadOnlySpan<byte> header)
{
if (this.IsSupportedFileFormat(header))
{
return ImageFormats.Png;
}
return null;
return this.IsSupportedFileFormat(header) ? ImageFormats.Png : null;
}
private bool IsSupportedFileFormat(ReadOnlySpan<byte> header)

14
src/ImageSharp/Image.Decode.cs

@ -4,8 +4,8 @@
using System.IO;
using System.Linq;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
namespace SixLabors.ImageSharp
{
@ -29,12 +29,12 @@ namespace SixLabors.ImageSharp
return null;
}
using (IManagedByteBuffer buffer = config.MemoryManager.AllocateManagedByteBuffer(maxHeaderSize))
using (IManagedByteBuffer buffer = config.MemoryAllocator.AllocateManagedByteBuffer(maxHeaderSize))
{
long startPosition = stream.Position;
stream.Read(buffer.Array, 0, maxHeaderSize);
stream.Position = startPosition;
return config.ImageFormatsManager.FormatDetectors.Select(x => x.DetectFormat(buffer.Span)).LastOrDefault(x => x != null);
return config.ImageFormatsManager.FormatDetectors.Select(x => x.DetectFormat(buffer.GetSpan())).LastOrDefault(x => x != null);
}
}
@ -48,12 +48,10 @@ namespace SixLabors.ImageSharp
private static IImageDecoder DiscoverDecoder(Stream stream, Configuration config, out IImageFormat format)
{
format = InternalDetectFormat(stream, config);
if (format != null)
{
return config.ImageFormatsManager.FindDecoder(format);
}
return null;
return format != null
? config.ImageFormatsManager.FindDecoder(format)
: null;
}
#pragma warning disable SA1008 // Opening parenthesis must be spaced correctly

167
src/ImageSharp/Image.FromBytes.cs

@ -1,6 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.IO;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.PixelFormats;
@ -15,7 +16,7 @@ namespace SixLabors.ImageSharp
/// <summary>
/// By reading the header on the provided byte array this calculates the images format.
/// </summary>
/// <param name="data">The byte array containing image data to read the header from.</param>
/// <param name="data">The byte array containing encoded image data to read the header from.</param>
/// <returns>The format or null if none found.</returns>
public static IImageFormat DetectFormat(byte[] data)
{
@ -26,7 +27,7 @@ namespace SixLabors.ImageSharp
/// By reading the header on the provided byte array this calculates the images format.
/// </summary>
/// <param name="config">The configuration.</param>
/// <param name="data">The byte array containing image data to read the header from.</param>
/// <param name="data">The byte array containing encoded image data to read the header from.</param>
/// <returns>The mime type or null if none found.</returns>
public static IImageFormat DetectFormat(Configuration config, byte[] data)
{
@ -37,30 +38,30 @@ namespace SixLabors.ImageSharp
}
/// <summary>
/// Create a new instance of the <see cref="Image{Rgba32}"/> class from the given byte array.
/// Load a new instance of <see cref="Image{Rgba32}"/> from the given encoded byte array.
/// </summary>
/// <param name="data">The byte array containing image data.</param>
/// <returns>A new <see cref="Image{Rgba32}"/>.</returns>
public static Image<Rgba32> Load(byte[] data) => Load<Rgba32>(Configuration.Default, data);
/// <summary>
/// Create a new instance of the <see cref="Image{Rgba32}"/> class from the given byte array.
/// Load a new instance of <see cref="Image{Rgba32}"/> from the given encoded byte array.
/// </summary>
/// <param name="data">The byte array containing image data.</param>
/// <param name="data">The byte array containing encoded image data.</param>
/// <param name="format">The mime type of the decoded image.</param>
/// <returns>A new <see cref="Image{Rgba32}"/>.</returns>
public static Image<Rgba32> Load(byte[] data, out IImageFormat format) => Load<Rgba32>(Configuration.Default, data, out format);
/// <summary>
/// Create a new instance of the <see cref="Image{Rgba32}"/> class from the given byte array.
/// Load a new instance of <see cref="Image{Rgba32}"/> from the given encoded byte array.
/// </summary>
/// <param name="config">The config for the decoder.</param>
/// <param name="data">The byte array containing image data.</param>
/// <param name="data">The byte array containing encoded image data.</param>
/// <returns>A new <see cref="Image{Rgba32}"/>.</returns>
public static Image<Rgba32> Load(Configuration config, byte[] data) => Load<Rgba32>(config, data);
/// <summary>
/// Create a new instance of the <see cref="Image{Rgba32}"/> class from the given byte array.
/// Load a new instance of <see cref="Image{Rgba32}"/> from the given encoded byte array.
/// </summary>
/// <param name="config">The config for the decoder.</param>
/// <param name="data">The byte array containing image data.</param>
@ -69,15 +70,15 @@ namespace SixLabors.ImageSharp
public static Image<Rgba32> Load(Configuration config, byte[] data, out IImageFormat format) => Load<Rgba32>(config, data, out format);
/// <summary>
/// Create a new instance of the <see cref="Image{Rgba32}"/> class from the given byte array.
/// Load a new instance of <see cref="Image{Rgba32}"/> from the given encoded byte array.
/// </summary>
/// <param name="data">The byte array containing image data.</param>
/// <param name="data">The byte array containing encoded image data.</param>
/// <param name="decoder">The decoder.</param>
/// <returns>A new <see cref="Image{Rgba32}"/>.</returns>
public static Image<Rgba32> Load(byte[] data, IImageDecoder decoder) => Load<Rgba32>(data, decoder);
/// <summary>
/// Create a new instance of the <see cref="Image{Rgba32}"/> class from the given byte array.
/// Load a new instance of <see cref="Image{Rgba32}"/> from the given encoded byte array.
/// </summary>
/// <param name="config">The config for the decoder.</param>
/// <param name="data">The byte array containing image data.</param>
@ -86,9 +87,9 @@ namespace SixLabors.ImageSharp
public static Image<Rgba32> Load(Configuration config, byte[] data, IImageDecoder decoder) => Load<Rgba32>(config, data, decoder);
/// <summary>
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the given byte array.
/// Load a new instance of <see cref="Image{TPixel}"/> from the given encoded byte array.
/// </summary>
/// <param name="data">The byte array containing image data.</param>
/// <param name="data">The byte array containing encoded image data.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A new <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Load<TPixel>(byte[] data)
@ -96,7 +97,7 @@ namespace SixLabors.ImageSharp
=> Load<TPixel>(Configuration.Default, data);
/// <summary>
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the given byte array.
/// Load a new instance of <see cref="Image{TPixel}"/> from the given encoded byte array.
/// </summary>
/// <param name="data">The byte array containing image data.</param>
/// <param name="format">The mime type of the decoded image.</param>
@ -107,10 +108,10 @@ namespace SixLabors.ImageSharp
=> Load<TPixel>(Configuration.Default, data, out format);
/// <summary>
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the given byte array.
/// Load a new instance of <see cref="Image{TPixel}"/> from the given encoded byte array.
/// </summary>
/// <param name="config">The configuration options.</param>
/// <param name="data">The byte array containing image data.</param>
/// <param name="data">The byte array containing encoded image data.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A new <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Load<TPixel>(Configuration config, byte[] data)
@ -123,11 +124,11 @@ namespace SixLabors.ImageSharp
}
/// <summary>
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the given byte array.
/// Load a new instance of <see cref="Image{TPixel}"/> from the given encoded byte array.
/// </summary>
/// <param name="config">The configuration options.</param>
/// <param name="data">The byte array containing image data.</param>
/// <param name="format">The mime type of the decoded image.</param>
/// <param name="data">The byte array containing encoded image data.</param>
/// <param name="format">The <see cref="IImageFormat"/> of the decoded image.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A new <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Load<TPixel>(Configuration config, byte[] data, out IImageFormat format)
@ -140,9 +141,9 @@ namespace SixLabors.ImageSharp
}
/// <summary>
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the given byte array.
/// Load a new instance of <see cref="Image{TPixel}"/> from the given encoded byte array.
/// </summary>
/// <param name="data">The byte array containing image data.</param>
/// <param name="data">The byte array containing encoded image data.</param>
/// <param name="decoder">The decoder.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A new <see cref="Image{TPixel}"/>.</returns>
@ -156,10 +157,10 @@ namespace SixLabors.ImageSharp
}
/// <summary>
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the given byte array.
/// Load a new instance of <see cref="Image{TPixel}"/> from the given encoded byte array.
/// </summary>
/// <param name="config">The Configuration.</param>
/// <param name="data">The byte array containing image data.</param>
/// <param name="data">The byte array containing encoded image data.</param>
/// <param name="decoder">The decoder.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A new <see cref="Image{TPixel}"/>.</returns>
@ -171,5 +172,125 @@ namespace SixLabors.ImageSharp
return Load<TPixel>(config, memoryStream, decoder);
}
}
#if !NETSTANDARD1_1
/// <summary>
/// By reading the header on the provided byte array this calculates the images format.
/// </summary>
/// <param name="data">The byte array containing encoded image data to read the header from.</param>
/// <returns>The format or null if none found.</returns>
public static IImageFormat DetectFormat(ReadOnlySpan<byte> data)
{
return DetectFormat(Configuration.Default, data);
}
/// <summary>
/// By reading the header on the provided byte array this calculates the images format.
/// </summary>
/// <param name="config">The configuration.</param>
/// <param name="data">The byte array containing encoded image data to read the header from.</param>
/// <returns>The mime type or null if none found.</returns>
public static unsafe IImageFormat DetectFormat(Configuration config, ReadOnlySpan<byte> data)
{
fixed (byte* ptr = &data.GetPinnableReference())
{
using (var stream = new UnmanagedMemoryStream(ptr, data.Length))
{
return DetectFormat(config, stream);
}
}
}
/// <summary>
/// Load a new instance of <see cref="Image{Rgba32}"/> from the given encoded byte span.
/// </summary>
/// <param name="data">The byte span containing image data.</param>
/// <returns>A new <see cref="Image{Rgba32}"/>.</returns>
public static Image<Rgba32> Load(ReadOnlySpan<byte> data) => Load<Rgba32>(Configuration.Default, data);
/// <summary>
/// Load a new instance of <see cref="Image{Rgba32}"/> from the given encoded byte span.
/// </summary>
/// <param name="config">The config for the decoder.</param>
/// <param name="data">The byte span containing encoded image data.</param>
/// <returns>A new <see cref="Image{Rgba32}"/>.</returns>
public static Image<Rgba32> Load(Configuration config, ReadOnlySpan<byte> data) => Load<Rgba32>(config, data);
/// <summary>
/// Load a new instance of <see cref="Image{TPixel}"/> from the given encoded byte span.
/// </summary>
/// <param name="data">The byte span containing encoded image data.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A new <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Load<TPixel>(ReadOnlySpan<byte> data)
where TPixel : struct, IPixel<TPixel>
=> Load<TPixel>(Configuration.Default, data);
/// <summary>
/// Load a new instance of <see cref="Image{TPixel}"/> from the given encoded byte span.
/// </summary>
/// <param name="config">The configuration options.</param>
/// <param name="data">The byte span containing encoded image data.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A new <see cref="Image{TPixel}"/>.</returns>
public static unsafe Image<TPixel> Load<TPixel>(Configuration config, ReadOnlySpan<byte> data)
where TPixel : struct, IPixel<TPixel>
{
fixed (byte* ptr = &data.GetPinnableReference())
{
using (var stream = new UnmanagedMemoryStream(ptr, data.Length))
{
return Load<TPixel>(config, stream);
}
}
}
/// <summary>
/// Load a new instance of <see cref="Image{TPixel}"/> from the given encoded byte span.
/// </summary>
/// <param name="config">The Configuration.</param>
/// <param name="data">The byte span containing image data.</param>
/// <param name="decoder">The decoder.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A new <see cref="Image{TPixel}"/>.</returns>
public static unsafe Image<TPixel> Load<TPixel>(
Configuration config,
ReadOnlySpan<byte> data,
IImageDecoder decoder)
where TPixel : struct, IPixel<TPixel>
{
fixed (byte* ptr = &data.GetPinnableReference())
{
using (var stream = new UnmanagedMemoryStream(ptr, data.Length))
{
return Load<TPixel>(config, stream, decoder);
}
}
}
/// <summary>
/// Load a new instance of <see cref="Image{TPixel}"/> from the given encoded byte span.
/// </summary>
/// <param name="config">The configuration options.</param>
/// <param name="data">The byte span containing image data.</param>
/// <param name="format">The <see cref="IImageFormat"/> of the decoded image.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A new <see cref="Image{TPixel}"/>.</returns>
public static unsafe Image<TPixel> Load<TPixel>(
Configuration config,
ReadOnlySpan<byte> data,
out IImageFormat format)
where TPixel : struct, IPixel<TPixel>
{
fixed (byte* ptr = &data.GetPinnableReference())
{
using (var stream = new UnmanagedMemoryStream(ptr, data.Length))
{
return Load<TPixel>(config, stream, out format);
}
}
}
#endif
}
}

59
src/ImageSharp/Image.WrapMemory.cs

@ -0,0 +1,59 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
namespace SixLabors.ImageSharp
{
/// <content>
/// Adds static methods allowing wrapping an existing memory area as an image.
/// </content>
public static partial class Image
{
// TODO: This is a WIP API, should be public when finished.
/// <summary>
/// Wraps an existing contigous memory area of 'width'x'height' pixels,
/// allowing to view/manipulate it as an ImageSharp <see cref="Image{TPixel}"/> instance.
/// </summary>
/// <typeparam name="TPixel">The pixel type</typeparam>
/// <param name="config">The <see cref="Configuration"/></param>
/// <param name="pixelMemory">The pixel memory</param>
/// <param name="width">The width of the memory image</param>
/// <param name="height">The height of the memory image</param>
/// <param name="metaData">The <see cref="ImageMetaData"/></param>
/// <returns>An <see cref="Image{TPixel}"/> instance</returns>
internal static Image<TPixel> WrapMemory<TPixel>(
Configuration config,
Memory<TPixel> pixelMemory,
int width,
int height,
ImageMetaData metaData)
where TPixel : struct, IPixel<TPixel>
{
var buffer = new ConsumedBuffer<TPixel>(pixelMemory);
return new Image<TPixel>(config, buffer, width, height, metaData);
}
/// <summary>
/// Wraps an existing contigous memory area of 'width'x'height' pixels,
/// allowing to view/manipulate it as an ImageSharp <see cref="Image{TPixel}"/> instance.
/// </summary>
/// <typeparam name="TPixel">The pixel type</typeparam>
/// <param name="pixelMemory">The pixel memory</param>
/// <param name="width">The width of the memory image</param>
/// <param name="height">The height of the memory image</param>
/// <returns>An <see cref="Image{TPixel}"/> instance</returns>
internal static Image<TPixel> WrapMemory<TPixel>(
Memory<TPixel> pixelMemory,
int width,
int height)
where TPixel : struct, IPixel<TPixel>
{
return WrapMemory(Configuration.Default, pixelMemory, width, height, new ImageMetaData());
}
}
}

31
src/ImageSharp/ImageExtensions.Internal.cs

@ -0,0 +1,31 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
namespace SixLabors.ImageSharp
{
/// <content>
/// Contains internal extensions for <see cref="Image{TPixel}"/>
/// </content>
public static partial class ImageExtensions
{
/// <summary>
/// Locks the image providing access to the pixels.
/// <remarks>
/// It is imperative that the accessor is correctly disposed off after use.
/// </remarks>
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="image">The image.</param>
/// <returns>
/// The <see cref="Buffer2D{TPixel}" />
/// </returns>
internal static Buffer2D<TPixel> GetRootFramePixelBuffer<TPixel>(this Image<TPixel> image)
where TPixel : struct, IPixel<TPixel>
{
return image.Frames.RootFrame.PixelBuffer;
}
}
}

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

Loading…
Cancel
Save