Browse Source

Merge branch 'master' into colorspace-transforms

af/merge-core
James Jackson-South 8 years ago
parent
commit
1c7cb179af
  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. 5
      src/ImageSharp.Drawing/Primitives/ShapePath.cs
  6. 21
      src/ImageSharp.Drawing/Primitives/ShapeRegion.cs
  7. 16
      src/ImageSharp.Drawing/Processing/BrushApplicator.cs
  8. 2
      src/ImageSharp.Drawing/Processing/Brushes.cs
  9. 7
      src/ImageSharp.Drawing/Processing/ColorStop{TPixel}.cs
  10. 4
      src/ImageSharp.Drawing/Processing/DrawBezierExtensions.cs
  11. 4
      src/ImageSharp.Drawing/Processing/DrawImageExtensions.cs
  12. 4
      src/ImageSharp.Drawing/Processing/DrawLineExtensions.cs
  13. 4
      src/ImageSharp.Drawing/Processing/DrawPathCollectionExtensions.cs
  14. 4
      src/ImageSharp.Drawing/Processing/DrawPathExtensions.cs
  15. 4
      src/ImageSharp.Drawing/Processing/DrawPolygonExtensions.cs
  16. 4
      src/ImageSharp.Drawing/Processing/DrawRectangleExtensions.cs
  17. 40
      src/ImageSharp.Drawing/Processing/DrawTextExtensions.cs
  18. 7
      src/ImageSharp.Drawing/Processing/EllipticGradientBrush{TPixel}.cs
  19. 3
      src/ImageSharp.Drawing/Processing/FillPathBuilderExtensions.cs
  20. 3
      src/ImageSharp.Drawing/Processing/FillPathCollectionExtensions.cs
  21. 3
      src/ImageSharp.Drawing/Processing/FillPathExtensions.cs
  22. 3
      src/ImageSharp.Drawing/Processing/FillPolygonExtensions.cs
  23. 3
      src/ImageSharp.Drawing/Processing/FillRectangleExtensions.cs
  24. 5
      src/ImageSharp.Drawing/Processing/FillRegionExtensions.cs
  25. 7
      src/ImageSharp.Drawing/Processing/GradientBrushBase{TPixel}.cs
  26. 5
      src/ImageSharp.Drawing/Processing/GradientRepetitionMode.cs
  27. 2
      src/ImageSharp.Drawing/Processing/IBrush.cs
  28. 3
      src/ImageSharp.Drawing/Processing/IPen.cs
  29. 14
      src/ImageSharp.Drawing/Processing/ImageBrush{TPixel}.cs
  30. 7
      src/ImageSharp.Drawing/Processing/LinearGradientBrush{TPixel}.cs
  31. 16
      src/ImageSharp.Drawing/Processing/PatternBrush{TPixel}.cs
  32. 3
      src/ImageSharp.Drawing/Processing/Pens.cs
  33. 11
      src/ImageSharp.Drawing/Processing/Pen{TPixel}.cs
  34. 13
      src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs
  35. 13
      src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs
  36. 80
      src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs
  37. 468
      src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs
  38. 7
      src/ImageSharp.Drawing/Processing/RadialGradientBrush{TPixel}.cs
  39. 18
      src/ImageSharp.Drawing/Processing/RecolorBrush{TPixel}.cs
  40. 20
      src/ImageSharp.Drawing/Processing/SolidBrush{TPixel}.cs
  41. 179
      src/ImageSharp.Drawing/Processing/Text/DrawTextExtensions.Path.cs
  42. 21
      src/ImageSharp.Drawing/Processing/TextGraphicsOptions.cs
  43. 84
      src/ImageSharp.Drawing/Utils/QuickSort.cs
  44. 114
      src/ImageSharp/Advanced/AdvancedImageExtensions.cs
  45. 2
      src/ImageSharp/Advanced/IPixelSource.cs
  46. 7
      src/ImageSharp/ColorSpaces/CieLab.cs
  47. 11
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Adapt.cs
  48. 10
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs
  49. 2
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs
  50. 26
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs
  51. 28
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs
  52. 26
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs
  53. 2
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs
  54. 2
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs
  55. 4
      src/ImageSharp/ColorSpaces/Conversion/Implementation/CieXyzAndCieXyyConverter.cs
  56. 2
      src/ImageSharp/ColorSpaces/Conversion/Implementation/CieXyzAndHunterLabConverterBase.cs
  57. 2
      src/ImageSharp/ColorSpaces/Conversion/Implementation/CieXyzToCieLuvConverter.cs
  58. 8
      src/ImageSharp/ColorSpaces/Conversion/Implementation/CieXyzToHunterLabConverter.cs
  59. 2
      src/ImageSharp/ColorSpaces/Conversion/Implementation/CieXyzToLinearRgbConverter.cs
  60. 4
      src/ImageSharp/ColorSpaces/Conversion/Implementation/CmykAndRgbConverter.cs
  61. 4
      src/ImageSharp/ColorSpaces/Conversion/Implementation/HslAndRgbConverter.cs
  62. 4
      src/ImageSharp/ColorSpaces/Conversion/Implementation/HsvAndRgbConverter.cs
  63. 1
      src/ImageSharp/ColorSpaces/Conversion/Implementation/LinearRgbToCieXyzConverter.cs
  64. 2
      src/ImageSharp/ColorSpaces/Conversion/Implementation/LinearRgbToRgbConverter.cs
  65. 4
      src/ImageSharp/ColorSpaces/Conversion/Implementation/YCbCrAndRgbConverter.cs
  66. 9
      src/ImageSharp/Common/Constants.cs
  67. 46
      src/ImageSharp/Common/Extensions/ListExtensions.cs
  68. 29
      src/ImageSharp/Common/Extensions/StreamExtensions.cs
  69. 9
      src/ImageSharp/Common/Helpers/DebugGuard.cs
  70. 3
      src/ImageSharp/Common/Helpers/Guard.cs
  71. 6
      src/ImageSharp/Common/Helpers/ImageMaths.cs
  72. 22
      src/ImageSharp/Common/Helpers/InliningOptions.cs
  73. 8
      src/ImageSharp/Common/Helpers/ParallelFor.cs
  74. 24
      src/ImageSharp/Common/Helpers/TestHelpers.cs
  75. 8
      src/ImageSharp/Configuration.cs
  76. 137
      src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
  77. 2
      src/ImageSharp/Formats/Bmp/BmpEncoder.cs
  78. 49
      src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
  79. 7
      src/ImageSharp/Formats/Bmp/BmpImageFormatDetector.cs
  80. 17
      src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs
  81. 21
      src/ImageSharp/Formats/Gif/GifColorTableMode.cs
  82. 2
      src/ImageSharp/Formats/Gif/GifDecoder.cs
  83. 20
      src/ImageSharp/Formats/Gif/GifDecoderCore.cs
  84. 9
      src/ImageSharp/Formats/Gif/GifEncoder.cs
  85. 129
      src/ImageSharp/Formats/Gif/GifEncoderCore.cs
  86. 7
      src/ImageSharp/Formats/Gif/IGifEncoderOptions.cs
  87. 29
      src/ImageSharp/Formats/Gif/LzwDecoder.cs
  88. 53
      src/ImageSharp/Formats/Gif/LzwEncoder.cs
  89. 2
      src/ImageSharp/Formats/IImageEncoder.cs
  90. 2
      src/ImageSharp/Formats/IImageFormatDetector.cs
  91. 23
      src/ImageSharp/Formats/ImageFormatManager.cs
  92. 2
      src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs
  93. 2
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.CopyTo.cs
  94. 6
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
  95. 6
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs
  96. 6
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs
  97. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/IJpegComponent.cs
  98. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs
  99. 6
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs
  100. 16
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.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);
}
}

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

@ -1,7 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Processing.Drawing.Pens;
using SixLabors.ImageSharp.Processing;
using SixLabors.Shapes;
namespace SixLabors.ImageSharp.Primitives
@ -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;
}
}
}
}

16
src/ImageSharp.Drawing/Processing/Drawing/Brushes/BrushApplicator.cs → src/ImageSharp.Drawing/Processing/BrushApplicator.cs

@ -3,10 +3,10 @@
using System;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
namespace SixLabors.ImageSharp.Processing.Drawing.Brushes
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// primitive that converts a point in to a color for discovering the fill color based on an implementation
@ -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);
}
}
}

2
src/ImageSharp.Drawing/Processing/Drawing/Brushes/Brushes.cs → src/ImageSharp.Drawing/Processing/Brushes.cs

@ -3,7 +3,7 @@
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Drawing.Brushes
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// A collection of methods for creating generic brushes.

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

@ -1,8 +1,11 @@
using System.Diagnostics;
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Diagnostics;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Drawing.Brushes.GradientBrushes
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// A struct that defines a single color stop.

4
src/ImageSharp.Drawing/Processing/Drawing/DrawBezierExtensions.cs → src/ImageSharp.Drawing/Processing/DrawBezierExtensions.cs

@ -2,12 +2,10 @@
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Drawing.Brushes;
using SixLabors.ImageSharp.Processing.Drawing.Pens;
using SixLabors.Primitives;
using SixLabors.Shapes;
namespace SixLabors.ImageSharp.Processing.Drawing
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Adds extensions that allow the drawing of Bezier paths to the <see cref="Image{TPixel}"/> type.

4
src/ImageSharp.Drawing/Processing/Drawing/DrawImageExtensions.cs → src/ImageSharp.Drawing/Processing/DrawImageExtensions.cs

@ -2,10 +2,10 @@
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Drawing.Processors;
using SixLabors.ImageSharp.Processing.Processors.Drawing;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Drawing
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Adds extensions that allow the drawing of images to the <see cref="Image{TPixel}"/> type.

4
src/ImageSharp.Drawing/Processing/Drawing/DrawLineExtensions.cs → src/ImageSharp.Drawing/Processing/DrawLineExtensions.cs

@ -2,12 +2,10 @@
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Drawing.Brushes;
using SixLabors.ImageSharp.Processing.Drawing.Pens;
using SixLabors.Primitives;
using SixLabors.Shapes;
namespace SixLabors.ImageSharp.Processing.Drawing
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Adds extensions that allow the drawing of lines to the <see cref="Image{TPixel}"/> type.

4
src/ImageSharp.Drawing/Processing/Drawing/DrawPathCollectionExtensions.cs → src/ImageSharp.Drawing/Processing/DrawPathCollectionExtensions.cs

@ -2,11 +2,9 @@
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Drawing.Brushes;
using SixLabors.ImageSharp.Processing.Drawing.Pens;
using SixLabors.Shapes;
namespace SixLabors.ImageSharp.Processing.Drawing
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Adds extensions that allow the drawing of collections of polygon outlines to the <see cref="Image{TPixel}"/> type.

4
src/ImageSharp.Drawing/Processing/Drawing/DrawPathExtensions.cs → src/ImageSharp.Drawing/Processing/DrawPathExtensions.cs

@ -3,11 +3,9 @@
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Primitives;
using SixLabors.ImageSharp.Processing.Drawing.Brushes;
using SixLabors.ImageSharp.Processing.Drawing.Pens;
using SixLabors.Shapes;
namespace SixLabors.ImageSharp.Processing.Drawing
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Adds extensions that allow the drawing of polygon outlines to the <see cref="Image{TPixel}"/> type.

4
src/ImageSharp.Drawing/Processing/Drawing/DrawPolygonExtensions.cs → src/ImageSharp.Drawing/Processing/DrawPolygonExtensions.cs

@ -2,12 +2,10 @@
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Drawing.Brushes;
using SixLabors.ImageSharp.Processing.Drawing.Pens;
using SixLabors.Primitives;
using SixLabors.Shapes;
namespace SixLabors.ImageSharp.Processing.Drawing
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Adds extensions that allow the drawing of closed linear polygons to the <see cref="Image{TPixel}"/> type.

4
src/ImageSharp.Drawing/Processing/Drawing/DrawRectangleExtensions.cs → src/ImageSharp.Drawing/Processing/DrawRectangleExtensions.cs

@ -2,12 +2,10 @@
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Drawing.Brushes;
using SixLabors.ImageSharp.Processing.Drawing.Pens;
using SixLabors.Primitives;
using SixLabors.Shapes;
namespace SixLabors.ImageSharp.Processing.Drawing
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Adds extensions that allow the drawing of rectangles to the <see cref="Image{TPixel}"/> type.

40
src/ImageSharp.Drawing/Processing/Text/DrawTextExtensions.cs → src/ImageSharp.Drawing/Processing/DrawTextExtensions.cs

@ -3,21 +3,16 @@
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.Processors.Text;
using SixLabors.Primitives;
using SixLabors.Shapes;
namespace SixLabors.ImageSharp.Processing.Text
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Adds extensions that allow the drawing of text to the <see cref="Image{TPixel}"/> type.
/// </summary>
public static partial class DrawTextExtensions
public static class DrawTextExtensions
{
private static readonly int DefaultTextDpi = 72;
/// <summary>
/// Draws the text onto the the image filled via the brush.
/// </summary>
@ -150,33 +145,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));
}
}

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

@ -1,9 +1,12 @@
using System;
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Drawing.Brushes.GradientBrushes
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Gradient Brush with elliptic shape.

3
src/ImageSharp.Drawing/Processing/Drawing/FillPathBuilderExtensions.cs → src/ImageSharp.Drawing/Processing/FillPathBuilderExtensions.cs

@ -3,10 +3,9 @@
using System;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Drawing.Brushes;
using SixLabors.Shapes;
namespace SixLabors.ImageSharp.Processing.Drawing
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Adds extensions that allow the filling of polygons with various brushes to the <see cref="Image{TPixel}"/> type.

3
src/ImageSharp.Drawing/Processing/Drawing/FillPathCollectionExtensions.cs → src/ImageSharp.Drawing/Processing/FillPathCollectionExtensions.cs

@ -2,10 +2,9 @@
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Drawing.Brushes;
using SixLabors.Shapes;
namespace SixLabors.ImageSharp.Processing.Drawing
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Adds extensions that allow the filling of collections of polygon outlines to the <see cref="Image{TPixel}"/> type.

3
src/ImageSharp.Drawing/Processing/Drawing/FillPathExtensions.cs → src/ImageSharp.Drawing/Processing/FillPathExtensions.cs

@ -3,10 +3,9 @@
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Primitives;
using SixLabors.ImageSharp.Processing.Drawing.Brushes;
using SixLabors.Shapes;
namespace SixLabors.ImageSharp.Processing.Drawing
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Adds extensions that allow the filling of polygon outlines to the <see cref="Image{TPixel}"/> type.

3
src/ImageSharp.Drawing/Processing/Drawing/FillPolygonExtensions.cs → src/ImageSharp.Drawing/Processing/FillPolygonExtensions.cs

@ -2,11 +2,10 @@
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Drawing.Brushes;
using SixLabors.Primitives;
using SixLabors.Shapes;
namespace SixLabors.ImageSharp.Processing.Drawing
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Adds extensions that allow the filling of closed linear polygons to the <see cref="Image{TPixel}"/> type.

3
src/ImageSharp.Drawing/Processing/Drawing/FillRectangleExtensions.cs → src/ImageSharp.Drawing/Processing/FillRectangleExtensions.cs

@ -2,11 +2,10 @@
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Drawing.Brushes;
using SixLabors.Primitives;
using SixLabors.Shapes;
namespace SixLabors.ImageSharp.Processing.Drawing
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Adds extensions that allow the filling of rectangles to the <see cref="Image{TPixel}"/> type.

5
src/ImageSharp.Drawing/Processing/Drawing/FillRegionExtensions.cs → src/ImageSharp.Drawing/Processing/FillRegionExtensions.cs

@ -3,10 +3,9 @@
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Primitives;
using SixLabors.ImageSharp.Processing.Drawing.Brushes;
using SixLabors.ImageSharp.Processing.Drawing.Processors;
using SixLabors.ImageSharp.Processing.Processors.Drawing;
namespace SixLabors.ImageSharp.Processing.Drawing
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Adds extensions that allow the filling of regions with various brushes to the <see cref="Image{TPixel}"/> type.

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

@ -1,11 +1,14 @@
using System;
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.PixelFormats.PixelBlenders;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Drawing.Brushes.GradientBrushes
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Base class for Gradient brushes

5
src/ImageSharp.Drawing/Processing/Drawing/Brushes/GradientBrushes/GradientRepetitionMode.cs → src/ImageSharp.Drawing/Processing/GradientRepetitionMode.cs

@ -1,4 +1,7 @@
namespace SixLabors.ImageSharp.Processing.Drawing.Brushes.GradientBrushes
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Modes to repeat a gradient.

2
src/ImageSharp.Drawing/Processing/Drawing/Brushes/IBrush.cs → src/ImageSharp.Drawing/Processing/IBrush.cs

@ -4,7 +4,7 @@
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Drawing.Brushes
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Brush represents a logical configuration of a brush which can be used to source pixel colors

3
src/ImageSharp.Drawing/Processing/Drawing/Pens/IPen.cs → src/ImageSharp.Drawing/Processing/IPen.cs

@ -3,9 +3,8 @@
using System;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Drawing.Brushes;
namespace SixLabors.ImageSharp.Processing.Drawing.Pens
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Interface representing a Pen

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

@ -3,11 +3,11 @@
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
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Provides an implementation of an image brush for painting images within areas.
@ -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);
}
}
}

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

@ -1,9 +1,12 @@
using System;
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Drawing.Brushes.GradientBrushes
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Provides an implementation of a brush for painting linear gradients within areas.

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

@ -4,12 +4,12 @@
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
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Provides an implementation of a pattern brush for painting patterns.
@ -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);
}
}
}

3
src/ImageSharp.Drawing/Processing/Drawing/Pens/Pens.cs → src/ImageSharp.Drawing/Processing/Pens.cs

@ -2,9 +2,8 @@
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Drawing.Brushes;
namespace SixLabors.ImageSharp.Processing.Drawing.Pens
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Contains a collection of common Pen styles

11
src/ImageSharp.Drawing/Processing/Drawing/Pens/Pen{TPixel}.cs → src/ImageSharp.Drawing/Processing/Pen{TPixel}.cs

@ -3,9 +3,8 @@
using System;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Drawing.Brushes;
namespace SixLabors.ImageSharp.Processing.Drawing.Pens
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Provides a pen that can apply a pattern to a line with a set brush and thickness
@ -25,7 +24,7 @@ namespace SixLabors.ImageSharp.Processing.Drawing.Pens
private readonly float[] pattern;
/// <summary>
/// Initializes a new instance of the <see cref="Drawing.Pens.Pen{TPixel}"/> class.
/// Initializes a new instance of the <see cref="Pen{TPixel}"/> class.
/// </summary>
/// <param name="color">The color.</param>
/// <param name="width">The width.</param>
@ -36,7 +35,7 @@ namespace SixLabors.ImageSharp.Processing.Drawing.Pens
}
/// <summary>
/// Initializes a new instance of the <see cref="Drawing.Pens.Pen{TPixel}"/> class.
/// Initializes a new instance of the <see cref="Pen{TPixel}"/> class.
/// </summary>
/// <param name="brush">The brush.</param>
/// <param name="width">The width.</param>
@ -49,7 +48,7 @@ namespace SixLabors.ImageSharp.Processing.Drawing.Pens
}
/// <summary>
/// Initializes a new instance of the <see cref="Drawing.Pens.Pen{TPixel}"/> class.
/// Initializes a new instance of the <see cref="Pen{TPixel}"/> class.
/// </summary>
/// <param name="color">The color.</param>
/// <param name="width">The width.</param>
@ -59,7 +58,7 @@ namespace SixLabors.ImageSharp.Processing.Drawing.Pens
}
/// <summary>
/// Initializes a new instance of the <see cref="Drawing.Pens.Pen{TPixel}"/> class.
/// Initializes a new instance of the <see cref="Pen{TPixel}"/> class.
/// </summary>
/// <param name="brush">The brush.</param>
/// <param name="width">The width.</param>

13
src/ImageSharp.Drawing/Processing/Drawing/Processors/DrawImageProcessor.cs → src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs

@ -4,12 +4,11 @@
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
namespace SixLabors.ImageSharp.Processing.Processors.Drawing
{
/// <summary>
/// Combines two images together by blending the pixels.
@ -133,11 +132,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 +146,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());
});
}
}

13
src/ImageSharp.Drawing/Processing/Drawing/Processors/FillProcessor.cs → src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs

@ -4,13 +4,12 @@
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.ImageSharp.Processing;
using SixLabors.Memory;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Drawing.Processors
namespace SixLabors.ImageSharp.Processing.Processors.Drawing
{
/// <summary>
/// Using the brush as a source of pixels colors blends the brush color with source.
@ -77,13 +76,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 +93,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);
});
}
}

80
src/ImageSharp.Drawing/Processing/Drawing/Processors/FillRegionProcessor.cs → src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs

@ -2,15 +2,13 @@
// 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
namespace SixLabors.ImageSharp.Processing.Processors.Drawing
{
/// <summary>
/// Using a brush and a shape fills shape with contents of brush the
@ -96,36 +94,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 +178,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);
}
}
}
}

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

@ -0,0 +1,468 @@
// 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.Utils;
using SixLabors.Memory;
using SixLabors.Primitives;
using SixLabors.Shapes;
namespace SixLabors.ImageSharp.Processing.Processors.Text
{
/// <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();
}
}
}
}
}

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

@ -1,9 +1,12 @@
using System;
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Drawing.Brushes.GradientBrushes
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// A Circular Gradient Brush, defined by center point and radius.

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

@ -4,18 +4,18 @@
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
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Provides an implementation of a brush that can recolor an image
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
public class RecolorBrush<TPixel> : IBrush<TPixel>
where TPixel : struct, IPixel<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="RecolorBrush{TPixel}" /> class.
@ -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);
}
}
}

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

@ -3,11 +3,11 @@
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
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Provides an implementation of a solid brush for painting solid color areas.
@ -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);
}
}
}

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;
}
}
}

21
src/ImageSharp.Drawing/Processing/Text/TextGraphicsOptions.cs → src/ImageSharp.Drawing/Processing/TextGraphicsOptions.cs

@ -4,13 +4,15 @@
using SixLabors.Fonts;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Text
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Options for influencing the drawing functions.
/// </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/>

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

@ -21,10 +21,7 @@ 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));
this.CheckChromaticAdaptation();
return this.ChromaticAdaptation.Transform(color, sourceWhitePoint, this.WhitePoint);
}
@ -35,7 +32,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The adapted color</returns>
public CieLab Adapt(CieLab color)
{
Guard.NotNull(color, nameof(color));
this.CheckChromaticAdaptation();
if (color.WhitePoint.Equals(this.TargetLabWhitePoint))
@ -54,7 +50,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The adapted color</returns>
public CieLch Adapt(CieLch color)
{
Guard.NotNull(color, nameof(color));
this.CheckChromaticAdaptation();
if (color.WhitePoint.Equals(this.TargetLabWhitePoint))
@ -73,7 +68,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The adapted color</returns>
public CieLchuv Adapt(CieLchuv color)
{
Guard.NotNull(color, nameof(color));
this.CheckChromaticAdaptation();
if (color.WhitePoint.Equals(this.TargetLabWhitePoint))
@ -92,7 +86,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The adapted color</returns>
public CieLuv Adapt(CieLuv color)
{
Guard.NotNull(color, nameof(color));
this.CheckChromaticAdaptation();
if (color.WhitePoint.Equals(this.TargetLuvWhitePoint))
@ -111,7 +104,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The adapted color</returns>
public HunterLab Adapt(HunterLab color)
{
Guard.NotNull(color, nameof(color));
this.CheckChromaticAdaptation();
if (color.WhitePoint.Equals(this.TargetHunterLabWhitePoint))
@ -130,7 +122,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The adapted color</returns>
public LinearRgb Adapt(LinearRgb color)
{
Guard.NotNull(color, nameof(color));
this.CheckChromaticAdaptation();
if (color.WorkingSpace.Equals(this.TargetRgbWorkingSpace))
@ -157,8 +148,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The adapted color</returns>
public Rgb Adapt(Rgb color)
{
Guard.NotNull(color, nameof(color));
var linearInput = this.ToLinearRgb(color);
LinearRgb linearOutput = this.Adapt(linearInput);
return this.ToRgb(linearOutput);

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

@ -25,9 +25,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(CieLch color)
{
Guard.NotNull(color, nameof(color));
// Conversion (preserving white point)
// Conversion (perserving white point)
CieLab unadapted = CieLchToCieLabConverter.Convert(color);
if (!this.IsChromaticAdaptationPerformed)
@ -166,8 +164,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)
@ -206,8 +202,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(Cmyk color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor);
}
@ -273,8 +267,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(Hsv color)
{
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor);
}

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

@ -192,8 +192,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLch"/></returns>
public CieLch ToCieLch(Cmyk color)
{
Guard.NotNull(color, nameof(color));
var 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);

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

@ -25,8 +25,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);
@ -45,8 +43,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);
@ -61,8 +57,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);
@ -77,8 +71,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);
@ -97,8 +89,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);
}
@ -110,8 +100,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);
@ -125,8 +113,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);
@ -140,8 +126,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);
@ -155,8 +139,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);
@ -175,8 +157,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);
@ -194,8 +174,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);
}
@ -207,8 +185,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);
@ -221,8 +197,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);
@ -236,7 +210,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="LinearRgbToCieXyzConverter"/></returns>
private LinearRgbToCieXyzConverter GetLinearRgbToCieXyzConverter(IRgbWorkingSpace workingSpace)
{
if (this.linearRgbToCieXyzConverter != null && this.linearRgbToCieXyzConverter.SourceWorkingSpace.Equals(workingSpace))
if (this.linearRgbToCieXyzConverter?.SourceWorkingSpace.Equals(workingSpace) == true)
{
return this.linearRgbToCieXyzConverter;
}

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

@ -19,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);
@ -33,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);
@ -47,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);
@ -61,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);
@ -75,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);
@ -89,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);
@ -103,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);
@ -117,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);
@ -131,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);
@ -145,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);
@ -159,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);
@ -173,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);
}
@ -185,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);
}

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

@ -16,8 +16,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
[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
[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);

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

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

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

@ -43,8 +43,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
[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);

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

@ -33,18 +33,12 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
/// <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/CieXyzToLinearRgbConverter.cs

@ -46,8 +46,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public LinearRgb Convert(CieXyz input)
{
DebugGuard.NotNull(input, nameof(input));
Matrix4x4.Invert(this.conversionMatrix, out Matrix4x4 inverted);
var vector = Vector3.Transform(input.Vector, inverted);
return new LinearRgb(vector, this.TargetWorkingSpace);

4
src/ImageSharp/ColorSpaces/Conversion/Implementation/CmykAndRgbConverter.cs

@ -16,8 +16,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Rgb Convert(Cmyk input)
{
DebugGuard.NotNull(input, nameof(input));
Vector3 rgb = (Vector3.One - new Vector3(input.C, input.M, input.Y)) * (Vector3.One - new Vector3(input.K));
return new Rgb(rgb);
}
@ -26,8 +24,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Cmyk Convert(Rgb input)
{
DebugGuard.NotNull(input, nameof(input));
// To CMYK
Vector3 cmy = Vector3.One - input.Vector;

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

@ -16,8 +16,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
[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
[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/HsvAndRgbConverter.cs

@ -16,8 +16,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
[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
[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;

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

@ -38,7 +38,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
/// <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.");
var vector = Vector3.Transform(input.Vector, this.conversionMatrix);

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

@ -13,8 +13,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
/// <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/YCbCrAndRgbConverter.cs

@ -19,8 +19,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
[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
[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

@ -52,7 +52,7 @@ namespace SixLabors.ImageSharp
/// The <see cref="int"/>
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int GetBitsNeededForColorDepth(int colors) => (int)Math.Ceiling(Math.Log(colors, 2));
public static int GetBitsNeededForColorDepth(int colors) => Math.Max(1, (int)Math.Ceiling(Math.Log(colors, 2)));
/// <summary>
/// Implementation of 1D Gaussian G(x) function
@ -161,8 +161,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;

22
src/ImageSharp/Common/Helpers/InliningOptions.cs

@ -0,0 +1,22 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
// Uncomment this for verbose profiler results:
// #define PROFILING
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp
{
/// <summary>
/// Global inlining options. Helps temporarily disable inling for better profiler output.
/// </summary>
internal static class InliningOptions
{
#if PROFILING
public const MethodImplOptions ShortMethod = 0;
#else
public const MethodImplOptions ShortMethod = MethodImplOptions.AggressiveInlining;
#endif
public const MethodImplOptions ColdPath = MethodImplOptions.NoInlining;
}
}

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);

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

@ -5,7 +5,7 @@ using System.IO;
using System.Text;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Quantization;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
namespace SixLabors.ImageSharp.Formats.Gif
{
@ -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);
}
}

129
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.ImageSharp.Processing.Processors.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);
}
}
}

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

@ -2,7 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System.Text;
using SixLabors.ImageSharp.Processing.Quantization;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
namespace SixLabors.ImageSharp.Formats.Gif
{
@ -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);
}
}
}

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

Loading…
Cancel
Save