Browse Source

Merge remote-tracking branch 'upstream/master' into feature/pngExif

# Conflicts:
#	src/ImageSharp/MetaData/Profiles/Exif/ExifWriter.cs
af/merge-core
popow 8 years ago
parent
commit
ac34d907e5
  1. 12
      appveyor.yml
  2. 5
      run-tests.ps1
  3. 3
      src/ImageSharp.Drawing/ImageSharp.Drawing.csproj
  4. 3
      src/ImageSharp.Drawing/Primitives/ShapePath.cs
  5. 69
      src/ImageSharp.Drawing/Processing/Drawing/Processors/FillRegionProcessor.cs
  6. 179
      src/ImageSharp.Drawing/Processing/Text/DrawTextExtensions.Path.cs
  7. 34
      src/ImageSharp.Drawing/Processing/Text/DrawTextExtensions.cs
  8. 471
      src/ImageSharp.Drawing/Processing/Text/Processors/DrawTextProcessor.cs
  9. 19
      src/ImageSharp.Drawing/Processing/Text/TextGraphicsOptions.cs
  10. 84
      src/ImageSharp.Drawing/Utils/QuickSort.cs
  11. 18
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Adapt.cs
  12. 8
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs
  13. 2
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs
  14. 26
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs
  15. 27
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs
  16. 27
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs
  17. 2
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs
  18. 2
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs
  19. 2
      src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLuv/CieXyzToCieLuvConverter.cs
  20. 4
      src/ImageSharp/ColorSpaces/Conversion/Implementation/CieXyy/CieXyzAndCieXyyConverter.cs
  21. 4
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Hsl/HslAndRgbConverter.cs
  22. 4
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Hsv/HsvAndRgbConverter.cs
  23. 2
      src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/CieXyzAndHunterLabConverterBase.cs
  24. 8
      src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/CieXyzToHunterLabConverter.cs
  25. 2
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/CieXyzToLinearRgbConverter.cs
  26. 1
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToCieXyzConverter.cs
  27. 2
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToRgbConverter.cs
  28. 4
      src/ImageSharp/ColorSpaces/Conversion/Implementation/YCbCr/YCbCrAndRgbConverter.cs
  29. 29
      src/ImageSharp/Common/Extensions/StreamExtensions.cs
  30. 4
      src/ImageSharp/Common/Helpers/ImageMaths.cs
  31. 24
      src/ImageSharp/Common/Helpers/TestHelpers.cs
  32. 26
      src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
  33. 7
      src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
  34. 7
      src/ImageSharp/Formats/Bmp/BmpImageFormatDetector.cs
  35. 5
      src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs
  36. 2
      src/ImageSharp/Formats/Gif/GifDecoder.cs
  37. 11
      src/ImageSharp/Formats/Gif/LzwDecoder.cs
  38. 2
      src/ImageSharp/Formats/IImageEncoder.cs
  39. 2
      src/ImageSharp/Formats/IImageFormatDetector.cs
  40. 23
      src/ImageSharp/Formats/ImageFormatManager.cs
  41. 2
      src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs
  42. 6
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
  43. 6
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs
  44. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs
  45. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ProfileResolver.cs
  46. 2
      src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangJpegScanDecoder.ComputationData.cs
  47. 5
      src/ImageSharp/Formats/Jpeg/GolangPort/GolangJpegDecoderCore.cs
  48. 2
      src/ImageSharp/Formats/Jpeg/JpegDecoder.cs
  49. 14
      src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
  50. 8
      src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs
  51. 19
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  52. 7
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  53. 7
      src/ImageSharp/Formats/Png/PngImageFormatDetector.cs
  54. 8
      src/ImageSharp/Image.Decode.cs
  55. 16
      src/ImageSharp/ImageSharp.csproj
  56. 1
      src/ImageSharp/MetaData/ImageFrameMetaData.cs
  57. 31
      src/ImageSharp/MetaData/Profiles/Exif/ExifConstants.cs
  58. 25
      src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs
  59. 22
      src/ImageSharp/MetaData/Profiles/Exif/ExifWriter.cs
  60. 2
      src/ImageSharp/PixelFormats/Bgr24.cs
  61. 42
      src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs
  62. 2
      src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt
  63. 2
      src/ImageSharp/PixelFormats/Rgb24.cs
  64. 2
      src/ImageSharp/PixelFormats/Rgba1010102.cs
  65. 2
      src/ImageSharp/PixelFormats/Rgba32.cs
  66. 2
      src/ImageSharp/PixelFormats/Rgba64.cs
  67. 41
      src/ImageSharp/Primitives/DenseMatrix{T}.cs
  68. 2
      src/ImageSharp/Processing/Binarization/Processors/BinaryErrorDiffusionProcessor.cs
  69. 2
      src/ImageSharp/Processing/Binarization/Processors/BinaryOrderedDitherProcessor.cs
  70. 2
      src/ImageSharp/Processing/Binarization/Processors/BinaryThresholdProcessor.cs
  71. 2
      src/ImageSharp/Processing/Convolution/Processors/Convolution2PassProcessor.cs
  72. 2
      src/ImageSharp/Processing/Dithering/Processors/ErrorDiffusionPaletteProcessor.cs
  73. 2
      src/ImageSharp/Processing/Dithering/Processors/OrderedDitherPaletteProcessor.cs
  74. 4
      src/ImageSharp/Processing/Dithering/Processors/PaletteDitherProcessorBase.cs
  75. 8
      src/ImageSharp/Processing/Quantization/FrameQuantizers/OctreeFrameQuantizer{TPixel}.cs
  76. 4
      src/ImageSharp/Processing/Quantization/FrameQuantizers/WuFrameQuantizer{TPixel}.cs
  77. 2
      src/ImageSharp/Processing/Transforms/TransformHelpers.cs
  78. 101
      tests/ImageSharp.Benchmarks/Drawing/DrawText.cs
  79. 99
      tests/ImageSharp.Benchmarks/Drawing/DrawTextOutline.cs
  80. 1
      tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj
  81. 6
      tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj
  82. 3
      tests/ImageSharp.Tests/Drawing/BeziersTests.cs
  83. 117
      tests/ImageSharp.Tests/Drawing/Paths/DrawPathCollection.cs
  84. 210
      tests/ImageSharp.Tests/Drawing/Text/DrawText.Path.cs
  85. 101
      tests/ImageSharp.Tests/Drawing/Text/DrawText.cs
  86. 106
      tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs
  87. 51
      tests/ImageSharp.Tests/Drawing/Utils/QuickSortTests.cs
  88. 1
      tests/ImageSharp.Tests/Formats/Png/PngChunkTypeTests.cs
  89. 9
      tests/ImageSharp.Tests/ImageSharp.Tests.csproj
  90. 21
      tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs
  91. 26
      tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.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>

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

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

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

@ -2,11 +2,11 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
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;
@ -124,7 +124,7 @@ namespace SixLabors.ImageSharp.Processing.Drawing.Processors
continue;
}
QuickSort(buffer.Slice(0, pointsFound));
QuickSort.Sort(buffer.Slice(0, pointsFound));
for (int point = 0; point < pointsFound; point += 2)
{
@ -186,70 +186,5 @@ namespace SixLabors.ImageSharp.Processing.Drawing.Processors
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void Swap(ref float left, ref float right)
{
float tmp = left;
left = right;
right = tmp;
}
private static void QuickSort(Span<float> data)
{
if (data.Length < 2)
{
return;
}
else if (data.Length == 2)
{
if (data[0] > data[1])
{
Swap(ref data[0], ref data[1]);
}
return;
}
QuickSort(ref data[0], 0, data.Length - 1);
}
private static void QuickSort(ref float data0, int lo, int hi)
{
if (lo < hi)
{
int p = Partition(ref data0, lo, hi);
QuickSort(ref data0, lo, p);
QuickSort(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));
}
}
}
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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>

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

@ -153,8 +153,8 @@ namespace SixLabors.ImageSharp
{
int width = bitmap.Width;
int height = bitmap.Height;
var topLeft = default(Point);
var bottomRight = default(Point);
Point topLeft = default;
Point bottomRight = default;
Func<ImageFrame<TPixel>, int, int, float, bool> delegateFunc;

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

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

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers.Binary;
using System.IO;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.MetaData;
@ -216,7 +217,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
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.memoryAllocator.AllocateClean2D<byte>(width, height))
@ -251,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)
@ -392,7 +397,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
{
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.memoryAllocator.AllocateManagedByteBuffer(stride))
@ -475,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}.");
@ -504,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
{
@ -520,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);

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

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

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)

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

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

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

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

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

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

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

@ -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.

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

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

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

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

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

@ -338,6 +338,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
break;
case JpegConstants.Markers.DHT:
if (metadataOnly)
{
this.InputProcessor.Skip(remaining);
@ -721,7 +722,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
{
if (remaining < 17)
{
throw new ImageFormatException("DHT has wrong length");
throw new ImageFormatException($"DHT has wrong length. {remaining}");
}
this.InputProcessor.ReadFull(this.Temp, 0, 17);
@ -772,7 +773,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
/// </exception>
private void ProcessStartOfScanMarker(int remaining)
{
var scan = default(GolangJpegScanDecoder);
GolangJpegScanDecoder scan = default;
GolangJpegScanDecoder.InitStreamReading(&scan, this, remaining);
this.InputProcessor.Bits = default;
scan.DecodeBlocks(this);

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

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

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

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

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

@ -270,6 +270,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
}
case JpegConstants.Markers.DHT:
if (metadataOnly)
{
this.InputStream.Skip(remaining);
@ -698,11 +699,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
/// <param name="remaining">The remaining bytes in the segment block.</param>
private void ProcessDefineHuffmanTablesMarker(int remaining)
{
if (remaining < 17)
{
throw new ImageFormatException($"DHT has wrong length: {remaining}");
}
using (IManagedByteBuffer huffmanData = this.configuration.MemoryAllocator.AllocateCleanManagedByteBuffer(256))
{
ref byte huffmanDataRef = ref MemoryMarshal.GetReference(huffmanData.GetSpan());
@ -790,7 +786,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
int spectralStart = this.temp[0];
int spectralEnd = this.temp[1];
int successiveApproximation = this.temp[2];
var scanDecoder = default(PdfJsScanDecoder);
PdfJsScanDecoder scanDecoder = default;
scanDecoder.DecodeScan(
this.Frame,

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

@ -680,7 +680,7 @@ namespace SixLabors.ImageSharp.Formats.Png
private void ProcessDefilteredScanline<TPixel>(ReadOnlySpan<byte> defilteredScanline, ImageFrame<TPixel> pixels)
where TPixel : struct, IPixel<TPixel>
{
var color = default(TPixel);
TPixel color = default;
Span<TPixel> rowSpan = pixels.GetPixelRowSpan(this.currentRow);
// Trim the first marker byte from the buffer
@ -763,7 +763,7 @@ namespace SixLabors.ImageSharp.Formats.Png
for (int x = 0; x < this.header.Width; x++)
{
ref Rgb24 rgb24 = ref rgb24Span[x];
var rgba32 = default(Rgba32);
Rgba32 rgba32 = default;
rgba32.Rgb = rgb24;
rgba32.A = (byte)(rgb24.Equals(this.rgb24Trans) ? 0 : 255);
@ -778,7 +778,7 @@ namespace SixLabors.ImageSharp.Formats.Png
for (int x = 0; x < this.header.Width; x++)
{
ref readonly Rgb24 rgb24 = ref rgb24Span[x];
var rgba32 = default(Rgba32);
Rgba32 rgba32 = default;
rgba32.Rgb = rgb24;
rgba32.A = (byte)(rgb24.Equals(this.rgb24Trans) ? 0 : 255);
@ -829,7 +829,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <summary>
/// Decodes and assigns marker colors that identify transparent pixels in non indexed images
/// </summary>
/// <param name="alpha">The aplha tRNS array</param>
/// <param name="alpha">The alpha tRNS array</param>
private void AssignTransparentMarkers(byte[] alpha)
{
if (this.pngColorType == PngColorType.Rgb)
@ -858,15 +858,14 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary>
/// <typeparam name="TPixel">The type of pixel we are expanding to</typeparam>
/// <param name="defilteredScanline">The scanline</param>
/// <param name="row">Thecurrent output image row</param>
/// <param name="row">The current output image row</param>
private void ProcessScanlineFromPalette<TPixel>(ReadOnlySpan<byte> defilteredScanline, Span<TPixel> row)
where TPixel : struct, IPixel<TPixel>
{
ReadOnlySpan<byte> newScanline = ToArrayByBitsLength(defilteredScanline, this.bytesPerScanline, this.header.BitDepth);
ReadOnlySpan<Rgb24> pal = MemoryMarshal.Cast<byte, Rgb24>(this.palette);
var color = default(TPixel);
var rgba = default(Rgba32);
TPixel color = default;
Rgba32 rgba = default;
if (this.paletteAlpha != null && this.paletteAlpha.Length > 0)
{
@ -910,7 +909,7 @@ namespace SixLabors.ImageSharp.Formats.Png
private void ProcessInterlacedDefilteredScanline<TPixel>(ReadOnlySpan<byte> defilteredScanline, Span<TPixel> rowSpan, int pixelOffset = 0, int increment = 1)
where TPixel : struct, IPixel<TPixel>
{
var color = default(TPixel);
TPixel color = default;
// Trim the first marker byte from the buffer
ReadOnlySpan<byte> scanlineBuffer = defilteredScanline.Slice(1, defilteredScanline.Length - 1);
@ -953,7 +952,7 @@ namespace SixLabors.ImageSharp.Formats.Png
case PngColorType.Palette:
ReadOnlySpan<byte> newScanline = ToArrayByBitsLength(scanlineBuffer, this.bytesPerScanline, this.header.BitDepth);
var rgba = default(Rgba32);
Rgba32 rgba = default;
Span<Rgb24> pal = MemoryMarshal.Cast<byte, Rgb24>(this.palette);
if (this.paletteAlpha != null && this.paletteAlpha.Length > 0)

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

@ -248,7 +248,7 @@ namespace SixLabors.ImageSharp.Formats.Png
where TPixel : struct, IPixel<TPixel>
{
byte[] rawScanlineArray = this.rawScanline.Array;
var rgba = default(Rgba32);
Rgba32 rgba = default;
// Copy the pixels across from the image.
// Reuse the chunk type buffer.
@ -306,8 +306,9 @@ namespace SixLabors.ImageSharp.Formats.Png
switch (this.pngColorType)
{
case PngColorType.Palette:
// TODO: Use Span copy!
Buffer.BlockCopy(this.palettePixelData, row * this.rawScanline.Length(), this.rawScanline.Array, 0, this.rawScanline.Length());
int stride = this.rawScanline.Length();
this.palettePixelData.AsSpan(row * stride, stride).CopyTo(this.rawScanline.GetSpan());
break;
case PngColorType.Grayscale:
case PngColorType.GrayscaleWithAlpha:

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

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

8
src/ImageSharp/Image.Decode.cs

@ -48,12 +48,10 @@ namespace SixLabors.ImageSharp
private static IImageDecoder DiscoverDecoder(Stream stream, Configuration config, out IImageFormat format)
{
format = InternalDetectFormat(stream, config);
if (format != null)
{
return config.ImageFormatsManager.FindDecoder(format);
}
return null;
return format != null
? config.ImageFormatsManager.FindDecoder(format)
: null;
}
#pragma warning disable SA1008 // Opening parenthesis must be spaced correctly

16
src/ImageSharp/ImageSharp.csproj

@ -5,7 +5,7 @@
<VersionPrefix Condition="$(packageversion) != ''">$(packageversion)</VersionPrefix>
<VersionPrefix Condition="$(packageversion) == ''">0.0.1</VersionPrefix>
<Authors>Six Labors and contributors</Authors>
<TargetFrameworks>netstandard1.1;netstandard1.3;netstandard2.0</TargetFrameworks>
<TargetFrameworks>netstandard1.1;netstandard1.3;netstandard2.0;netcoreapp2.1</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<AssemblyName>SixLabors.ImageSharp</AssemblyName>
@ -29,7 +29,7 @@
<DebugType Condition="$(codecov) == ''">portable</DebugType>
<DebugSymbols>True</DebugSymbols>
<Features>IOperation</Features>
<LangVersion>7.2</LangVersion>
<LangVersion>7.3</LangVersion>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\Shared\*.cs" Exclude="bin\**;obj\**;**\*.xproj;packages\**" />
@ -40,13 +40,21 @@
<PackageReference Include="StyleCop.Analyzers" Version="1.1.0-beta007">
<PrivateAssets>All</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.5.1" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' != 'netcoreapp2.1'">
<PackageReference Include="System.Buffers" Version="4.5.0" />
<PackageReference Include="System.Memory" Version="4.5.0" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.5.0" />
<PackageReference Include="System.Memory" Version="4.5.1" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' ">
<PackageReference Include="System.IO.UnmanagedMemoryStream" Version="4.3.0" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.1' OR '$(TargetFramework)' == 'netstandard1.3'">
<PackageReference Include="System.IO.Compression" Version="4.3.0" />
<PackageReference Include="System.Threading.Tasks.Parallel" Version="4.3.0" />

1
src/ImageSharp/MetaData/ImageFrameMetaData.cs

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

31
src/ImageSharp/MetaData/Profiles/Exif/ExifConstants.cs

@ -0,0 +1,31 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
{
internal static class ExifConstants
{
public static readonly byte[] ExifIdCode = {
(byte)'E',
(byte)'x',
(byte)'i',
(byte)'f',
0x00,
0x00
};
public static readonly byte[] LittleEndianByteOrderMarker = {
(byte)'I',
(byte)'I',
0x2A,
0x00,
};
public static readonly byte[] BigEndianByteOrderMarker = {
(byte)'M',
(byte)'M',
0x2A,
0x00,
};
}
}

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

@ -141,26 +141,25 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
private unsafe string ConvertToString(ReadOnlySpan<byte> buffer)
{
#if NETSTANDARD1_1
byte[] bytes = buffer.ToArray();
Span<byte> nullChar = stackalloc byte[1] { 0 };
string result = Encoding.UTF8.GetString(bytes, 0, buffer.Length);
int nullCharIndex = buffer.IndexOf(nullChar);
#else
string result;
if (nullCharIndex > -1)
{
buffer = buffer.Slice(0, nullCharIndex);
}
#if NETSTANDARD1_1
return Encoding.UTF8.GetString(buffer.ToArray(), 0, buffer.Length);
#elif NETCOREAPP2_1
return Encoding.UTF8.GetString(buffer);
#else
fixed (byte* pointer = &MemoryMarshal.GetReference(buffer))
{
result = Encoding.UTF8.GetString(pointer, buffer.Length);
return Encoding.UTF8.GetString(pointer, buffer.Length);
}
#endif
int nullCharIndex = result.IndexOf('\0');
if (nullCharIndex != -1)
{
result = result.Substring(0, nullCharIndex);
}
return result;
}
/// <summary>

22
src/ImageSharp/MetaData/Profiles/Exif/ExifWriter.cs

@ -106,19 +106,13 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
int i = 0;
if (includeExifIdCode)
{
result[i++] = (byte)'E';
result[i++] = (byte)'x';
result[i++] = (byte)'i';
result[i++] = (byte)'f';
result[i++] = 0x00;
result[i++] = 0x00;
ExifConstants.ExifIdCode.AsSpan().CopyTo(result); // 0-5
i += ExifConstants.ExifIdCode.Length;
}
// the byte order marker for little-endian, followed by the number 42 and a 0
result[i++] = (byte)'I';
result[i++] = (byte)'I';
result[i++] = 0x2A;
result[i++] = 0x00;
ExifConstants.LittleEndianByteOrderMarker.AsSpan().CopyTo(result.AsSpan(start: i));
i += ExifConstants.LittleEndianByteOrderMarker.Length;
uint ifdOffset = ((uint)i - startIndex) + 4;
uint thumbnailOffset = ifdOffset + ifdLength + exifLength + gpsLength;
@ -268,7 +262,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
return length;
}
private int WriteArray(ExifValue value, byte[] destination, int offset)
private int WriteArray(ExifValue value, Span<byte> destination, int offset)
{
if (value.DataType == ExifDataType.Ascii)
{
@ -284,7 +278,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
return newOffset;
}
private int WriteData(uint startIndex, List<int> indexes, byte[] destination, int offset)
private int WriteData(uint startIndex, List<int> indexes, Span<byte> destination, int offset)
{
if (this.dataOffsets.Count == 0)
{
@ -307,7 +301,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
return newOffset;
}
private int WriteHeaders(List<int> indexes, byte[] destination, int offset)
private int WriteHeaders(List<int> indexes, Span<byte> destination, int offset)
{
this.dataOffsets = new List<int>();
@ -388,7 +382,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
}
}
private int WriteValue(ExifValue value, byte[] destination, int offset)
private int WriteValue(ExifValue value, Span<byte> destination, int offset)
{
if (value.IsArray && value.DataType != ExifDataType.Ascii)
{

2
src/ImageSharp/PixelFormats/Bgr24.cs

@ -121,7 +121,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromVector4(Vector4 vector)
{
var rgba = default(Rgba32);
Rgba32 rgba = default;
rgba.PackFromVector4(vector);
this.PackFromRgba32(rgba);
}

42
src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs

@ -261,7 +261,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
public static TPixel Normal<TPixel>(TPixel backdrop, TPixel source, float amount)
where TPixel : struct, IPixel<TPixel>
{
TPixel dest = default(TPixel);
TPixel dest = default;
dest.PackFromVector4(Normal(backdrop.ToVector4(), source.ToVector4(), amount));
return dest;
}
@ -270,7 +270,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
public static TPixel Multiply<TPixel>(TPixel backdrop, TPixel source, float amount)
where TPixel : struct, IPixel<TPixel>
{
TPixel dest = default(TPixel);
TPixel dest = default;
dest.PackFromVector4(Multiply(backdrop.ToVector4(), source.ToVector4(), amount));
return dest;
}
@ -279,7 +279,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
public static TPixel Add<TPixel>(TPixel backdrop, TPixel source, float amount)
where TPixel : struct, IPixel<TPixel>
{
TPixel dest = default(TPixel);
TPixel dest = default;
dest.PackFromVector4(Add(backdrop.ToVector4(), source.ToVector4(), amount));
return dest;
}
@ -288,7 +288,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
public static TPixel Subtract<TPixel>(TPixel backdrop, TPixel source, float amount)
where TPixel : struct, IPixel<TPixel>
{
TPixel dest = default(TPixel);
TPixel dest = default;
dest.PackFromVector4(Subtract(backdrop.ToVector4(), source.ToVector4(), amount));
return dest;
}
@ -297,7 +297,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
public static TPixel Screen<TPixel>(TPixel backdrop, TPixel source, float amount)
where TPixel : struct, IPixel<TPixel>
{
TPixel dest = default(TPixel);
TPixel dest = default;
dest.PackFromVector4(Screen(backdrop.ToVector4(), source.ToVector4(), amount));
return dest;
}
@ -306,7 +306,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
public static TPixel Darken<TPixel>(TPixel backdrop, TPixel source, float amount)
where TPixel : struct, IPixel<TPixel>
{
TPixel dest = default(TPixel);
TPixel dest = default;
dest.PackFromVector4(Darken(backdrop.ToVector4(), source.ToVector4(), amount));
return dest;
}
@ -315,7 +315,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
public static TPixel Lighten<TPixel>(TPixel backdrop, TPixel source, float amount)
where TPixel : struct, IPixel<TPixel>
{
TPixel dest = default(TPixel);
TPixel dest = default;
dest.PackFromVector4(Lighten(backdrop.ToVector4(), source.ToVector4(), amount));
return dest;
}
@ -324,7 +324,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
public static TPixel Overlay<TPixel>(TPixel backdrop, TPixel source, float amount)
where TPixel : struct, IPixel<TPixel>
{
TPixel dest = default(TPixel);
TPixel dest = default;
dest.PackFromVector4(Overlay(backdrop.ToVector4(), source.ToVector4(), amount));
return dest;
}
@ -333,7 +333,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
public static TPixel HardLight<TPixel>(TPixel backdrop, TPixel source, float amount)
where TPixel : struct, IPixel<TPixel>
{
TPixel dest = default(TPixel);
TPixel dest = default;
dest.PackFromVector4(HardLight(backdrop.ToVector4(), source.ToVector4(), amount));
return dest;
}
@ -342,7 +342,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
public static TPixel Src<TPixel>(TPixel backdrop, TPixel source, float amount)
where TPixel : struct, IPixel<TPixel>
{
TPixel dest = default(TPixel);
TPixel dest = default;
dest.PackFromVector4(Src(backdrop.ToVector4(), source.ToVector4(), amount));
return dest;
}
@ -351,7 +351,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
public static TPixel Atop<TPixel>(TPixel backdrop, TPixel source, float amount)
where TPixel : struct, IPixel<TPixel>
{
TPixel dest = default(TPixel);
TPixel dest = default;
dest.PackFromVector4(Atop(backdrop.ToVector4(), source.ToVector4(), amount));
return dest;
}
@ -360,7 +360,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
public static TPixel Over<TPixel>(TPixel backdrop, TPixel source, float amount)
where TPixel : struct, IPixel<TPixel>
{
TPixel dest = default(TPixel);
TPixel dest = default;
dest.PackFromVector4(Over(backdrop.ToVector4(), source.ToVector4(), amount));
return dest;
}
@ -369,7 +369,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
public static TPixel In<TPixel>(TPixel backdrop, TPixel source, float amount)
where TPixel : struct, IPixel<TPixel>
{
TPixel dest = default(TPixel);
TPixel dest = default;
dest.PackFromVector4(In(backdrop.ToVector4(), source.ToVector4(), amount));
return dest;
}
@ -378,7 +378,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
public static TPixel Out<TPixel>(TPixel backdrop, TPixel source, float amount)
where TPixel : struct, IPixel<TPixel>
{
TPixel dest = default(TPixel);
TPixel dest = default;
dest.PackFromVector4(Out(backdrop.ToVector4(), source.ToVector4(), amount));
return dest;
}
@ -387,7 +387,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
public static TPixel Dest<TPixel>(TPixel backdrop, TPixel source, float amount)
where TPixel : struct, IPixel<TPixel>
{
TPixel dest = default(TPixel);
TPixel dest = default;
dest.PackFromVector4(Dest(backdrop.ToVector4(), source.ToVector4(), amount));
return dest;
}
@ -396,7 +396,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
public static TPixel DestAtop<TPixel>(TPixel backdrop, TPixel source, float amount)
where TPixel : struct, IPixel<TPixel>
{
TPixel dest = default(TPixel);
TPixel dest = default;
dest.PackFromVector4(DestAtop(backdrop.ToVector4(), source.ToVector4(), amount));
return dest;
}
@ -405,7 +405,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
public static TPixel DestOver<TPixel>(TPixel backdrop, TPixel source, float amount)
where TPixel : struct, IPixel<TPixel>
{
TPixel dest = default(TPixel);
TPixel dest = default;
dest.PackFromVector4(DestOver(backdrop.ToVector4(), source.ToVector4(), amount));
return dest;
}
@ -414,7 +414,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
public static TPixel DestIn<TPixel>(TPixel backdrop, TPixel source, float amount)
where TPixel : struct, IPixel<TPixel>
{
TPixel dest = default(TPixel);
TPixel dest = default;
dest.PackFromVector4(DestIn(backdrop.ToVector4(), source.ToVector4(), amount));
return dest;
}
@ -423,7 +423,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
public static TPixel DestOut<TPixel>(TPixel backdrop, TPixel source, float amount)
where TPixel : struct, IPixel<TPixel>
{
TPixel dest = default(TPixel);
TPixel dest = default;
dest.PackFromVector4(DestOut(backdrop.ToVector4(), source.ToVector4(), amount));
return dest;
}
@ -432,7 +432,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
public static TPixel Clear<TPixel>(TPixel backdrop, TPixel source, float amount)
where TPixel : struct, IPixel<TPixel>
{
TPixel dest = default(TPixel);
TPixel dest = default;
dest.PackFromVector4(Clear(backdrop.ToVector4(), source.ToVector4(), amount));
return dest;
}
@ -441,7 +441,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
public static TPixel Xor<TPixel>(TPixel backdrop, TPixel source, float amount)
where TPixel : struct, IPixel<TPixel>
{
TPixel dest = default(TPixel);
TPixel dest = default;
dest.PackFromVector4(Xor(backdrop.ToVector4(), source.ToVector4(), amount));
return dest;
}

2
src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt

@ -30,7 +30,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
public static TPixel <#=blender#><TPixel>(TPixel backdrop, TPixel source, float amount)
where TPixel : struct, IPixel<TPixel>
{
TPixel dest = default(TPixel);
TPixel dest = default;
dest.PackFromVector4(<#=blender#>(backdrop.ToVector4(), source.ToVector4(), amount));
return dest;
}

2
src/ImageSharp/PixelFormats/Rgb24.cs

@ -122,7 +122,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromVector4(Vector4 vector)
{
var rgba = default(Rgba32);
Rgba32 rgba = default;
rgba.PackFromVector4(vector);
this.PackFromRgba32(rgba);
}

2
src/ImageSharp/PixelFormats/Rgba1010102.cs

@ -188,7 +188,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc />
public override bool Equals(object obj)
{
return (obj is Rgba1010102) && this.Equals((Rgba1010102)obj);
return obj is Rgba1010102 other && this.Equals(other);
}
/// <inheritdoc />

2
src/ImageSharp/PixelFormats/Rgba32.cs

@ -387,7 +387,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc/>
public override bool Equals(object obj)
{
return (obj is Rgba32) && this.Equals((Rgba32)obj);
return obj is Rgba32 other && this.Equals(other);
}
/// <inheritdoc/>

2
src/ImageSharp/PixelFormats/Rgba64.cs

@ -187,7 +187,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc />
public override bool Equals(object obj)
{
return (obj is Rgba64) && this.Equals((Rgba64)obj);
return obj is Rgba64 other && this.Equals(other);
}
/// <inheritdoc />

41
src/ImageSharp/Primitives/DenseMatrix{T}.cs

@ -89,6 +89,11 @@ namespace SixLabors.ImageSharp.Primitives
}
}
/// <summary>
/// Gets a Span wrapping the Data.
/// </summary>
internal Span<T> Span => new Span<T>(this.Data);
/// <summary>
/// Gets or sets the item at the specified position.
/// </summary>
@ -146,19 +151,13 @@ namespace SixLabors.ImageSharp.Primitives
/// </summary>
/// <param name="value">The value to fill each item with</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Fill(T value)
{
for (int i = 0; i < this.Data.Length; i++)
{
this.Data[i] = value;
}
}
public void Fill(T value) => this.Span.Fill(value);
/// <summary>
/// Clears the matrix setting each value to the default value for the element type
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear() => Array.Clear(this.Data, 0, this.Data.Length);
public void Clear() => this.Span.Clear();
/// <summary>
/// Checks the coordinates to ensure they are within bounds.
@ -183,28 +182,10 @@ namespace SixLabors.ImageSharp.Primitives
}
/// <inheritdoc/>
public bool Equals(DenseMatrix<T> other)
{
if (this.Columns != other.Columns)
{
return false;
}
if (this.Rows != other.Rows)
{
return false;
}
for (int i = 0; i < this.Data.Length; i++)
{
if (!this.Data[i].Equals(other.Data[i]))
{
return false;
}
}
return true;
}
public bool Equals(DenseMatrix<T> other) =>
this.Columns == other.Columns &&
this.Rows == other.Rows &&
this.Span.SequenceEqual(other.Span);
/// <inheritdoc/>
public override bool Equals(object obj) => obj is DenseMatrix<T> other && this.Equals(other);

2
src/ImageSharp/Processing/Binarization/Processors/BinaryErrorDiffusionProcessor.cs

@ -78,7 +78,7 @@ namespace SixLabors.ImageSharp.Processing.Binarization.Processors
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
{
float threshold = this.Threshold * 255F;
var rgba = default(Rgba32);
Rgba32 rgba = default;
bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8);
var interest = Rectangle.Intersect(sourceRectangle, source.Bounds());

2
src/ImageSharp/Processing/Binarization/Processors/BinaryOrderedDitherProcessor.cs

@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp.Processing.Binarization.Processors
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
{
var rgba = default(Rgba32);
Rgba32 rgba = default;
bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8);
var interest = Rectangle.Intersect(sourceRectangle, source.Bounds());

2
src/ImageSharp/Processing/Binarization/Processors/BinaryThresholdProcessor.cs

@ -77,7 +77,7 @@ namespace SixLabors.ImageSharp.Processing.Binarization.Processors
y =>
{
Span<TPixel> row = source.GetPixelRowSpan(y);
var rgba = default(Rgba32);
Rgba32 rgba = default;
for (int x = startX; x < endX; x++)
{

2
src/ImageSharp/Processing/Convolution/Processors/Convolution2PassProcessor.cs

@ -92,7 +92,7 @@ namespace SixLabors.ImageSharp.Processing.Convolution.Processors
for (int x = startX; x < endX; x++)
{
var destination = default(Vector4);
Vector4 destination = default;
// Apply each matrix multiplier to the color components for each pixel.
for (int fy = 0; fy < kernelHeight; fy++)

2
src/ImageSharp/Processing/Dithering/Processors/ErrorDiffusionPaletteProcessor.cs

@ -66,7 +66,7 @@ namespace SixLabors.ImageSharp.Processing.Dithering.Processors
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
{
float threshold = this.Threshold * 255F;
var rgba = default(Rgba32);
Rgba32 rgba = default;
bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8);
var interest = Rectangle.Intersect(sourceRectangle, source.Bounds());

2
src/ImageSharp/Processing/Dithering/Processors/OrderedDitherPaletteProcessor.cs

@ -47,7 +47,7 @@ namespace SixLabors.ImageSharp.Processing.Dithering.Processors
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
{
var rgba = default(Rgba32);
Rgba32 rgba = default;
bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8);
var interest = Rectangle.Intersect(sourceRectangle, source.Bounds());

4
src/ImageSharp/Processing/Dithering/Processors/PaletteDitherProcessorBase.cs

@ -46,8 +46,8 @@ namespace SixLabors.ImageSharp.Processing.Dithering.Processors
float secondLeastDistance = int.MaxValue;
var vector = pixel.ToVector4();
var closest = default(TPixel);
var secondClosest = default(TPixel);
TPixel closest = default;
TPixel secondClosest = default;
for (int index = 0; index < colorPalette.Length; index++)
{
TPixel temp = colorPalette[index];

8
src/ImageSharp/Processing/Quantization/FrameQuantizers/OctreeFrameQuantizer{TPixel}.cs

@ -68,7 +68,7 @@ namespace SixLabors.ImageSharp.Processing.Quantization.FrameQuantizers
ref TPixel scanBaseRef = ref MemoryMarshal.GetReference(row);
// And loop through each column
var rgba = default(Rgba32);
Rgba32 rgba = default;
for (int x = 0; x < width; x++)
{
ref TPixel pixel = ref Unsafe.Add(ref scanBaseRef, x);
@ -87,7 +87,7 @@ namespace SixLabors.ImageSharp.Processing.Quantization.FrameQuantizers
// pass of the algorithm by avoiding transforming rows of identical color.
TPixel sourcePixel = source[0, 0];
TPixel previousPixel = sourcePixel;
var rgba = default(Rgba32);
Rgba32 rgba = default;
byte pixelValue = this.QuantizePixel(sourcePixel, ref rgba);
TPixel[] colorPalette = this.GetPalette();
TPixel transformedPixel = colorPalette[pixelValue];
@ -152,7 +152,7 @@ namespace SixLabors.ImageSharp.Processing.Quantization.FrameQuantizers
{
// Transparent pixels are much more likely to be found at the end of a palette
int index = this.colors;
var trans = default(Rgba32);
Rgba32 trans = default;
for (int i = this.palette.Length - 1; i >= 0; i--)
{
this.palette[i].ToRgba32(ref trans);
@ -539,7 +539,7 @@ namespace SixLabors.ImageSharp.Processing.Quantization.FrameQuantizers
byte b = (this.blue / this.pixelCount).ToByte();
// And set the color of the palette entry
var pixel = default(TPixel);
TPixel pixel = default;
pixel.PackFromRgba32(new Rgba32(r, g, b, 255));
palette[index] = pixel;

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

@ -237,7 +237,7 @@ namespace SixLabors.ImageSharp.Processing.Quantization.FrameQuantizers
ref TPixel scanBaseRef = ref MemoryMarshal.GetReference(row);
// And loop through each column
var rgba = default(Rgba32);
Rgba32 rgba = default;
for (int x = 0; x < width; x++)
{
ref TPixel pixel = ref Unsafe.Add(ref scanBaseRef, x);
@ -858,7 +858,7 @@ namespace SixLabors.ImageSharp.Processing.Quantization.FrameQuantizers
}
// Expected order r->g->b->a
var rgba = default(Rgba32);
Rgba32 rgba = default;
pixel.ToRgba32(ref rgba);
int r = rgba.R >> (8 - IndexBits);

2
src/ImageSharp/Processing/Transforms/TransformHelpers.cs

@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Processing.Transforms
/// <summary>
/// Contains helper methods for working with affine and non-affine transforms
/// </summary>
internal class TransformHelpers
internal static class TransformHelpers
{
/// <summary>
/// Updates the dimensional metadata of a transformed image

101
tests/ImageSharp.Benchmarks/Drawing/DrawText.cs

@ -0,0 +1,101 @@
// <copyright file="Crop.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
using System.Drawing;
using System.Drawing.Drawing2D;
using BenchmarkDotNet.Attributes;
using System.IO;
using System.Numerics;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Text;
using SixLabors.ImageSharp.Processing.Overlays;
using SixLabors.ImageSharp.Processing.Drawing;
using System.Linq;
namespace SixLabors.ImageSharp.Benchmarks
{
[MemoryDiagnoser]
public class DrawText : BenchmarkBase
{
[Params(10, 100)]
public int TextIterations{ get; set; }
public string TextPhrase { get; set; } = "Hello World";
public string TextToRender => string.Join(" ", Enumerable.Repeat(TextPhrase, TextIterations));
[Benchmark(Baseline = true, Description = "System.Drawing Draw Text")]
public void DrawTextSystemDrawing()
{
using (Bitmap destination = new Bitmap(800, 800))
{
using (Graphics graphics = Graphics.FromImage(destination))
{
graphics.InterpolationMode = InterpolationMode.Default;
graphics.SmoothingMode = SmoothingMode.AntiAlias;
Pen pen = new Pen(System.Drawing.Color.HotPink, 10);
var font = new Font("Arial", 12, GraphicsUnit.Point);
graphics.DrawString(TextToRender, font, Brushes.HotPink, new RectangleF(10, 10, 780, 780));
}
}
}
[Benchmark(Description = "ImageSharp Draw Text - Cached Glyphs")]
public void DrawTextCore()
{
using (Image<Rgba32> image = new Image<Rgba32>(800, 800))
{
var font = SixLabors.Fonts.SystemFonts.CreateFont("Arial", 12);
image.Mutate(x => x.ApplyProcessor(new SixLabors.ImageSharp.Processing.Text.Processors.DrawTextProcessor<Rgba32>(new TextGraphicsOptions(true) { WrapTextWidth = 780 }, TextToRender, font, SixLabors.ImageSharp.Processing.Drawing.Brushes.Brushes.Solid(Rgba32.HotPink), null, new SixLabors.Primitives.PointF(10, 10))));
}
}
[Benchmark(Description = "ImageSharp Draw Text - Nieve")]
public void DrawTextCoreOld()
{
using (Image<Rgba32> image = new Image<Rgba32>(800, 800))
{
var font = SixLabors.Fonts.SystemFonts.CreateFont("Arial", 12);
image.Mutate(x => DrawTextOldVersion(x, new TextGraphicsOptions(true) { WrapTextWidth = 780 }, TextToRender, font, SixLabors.ImageSharp.Processing.Drawing.Brushes.Brushes.Solid(Rgba32.HotPink), null, new SixLabors.Primitives.PointF(10, 10)));
}
IImageProcessingContext<TPixel> DrawTextOldVersion<TPixel>(IImageProcessingContext<TPixel> source, TextGraphicsOptions options, string text, SixLabors.Fonts.Font font, SixLabors.ImageSharp.Processing.Drawing.Brushes.IBrush<TPixel> brush, SixLabors.ImageSharp.Processing.Drawing.Pens.IPen<TPixel> pen, SixLabors.Primitives.PointF location)
where TPixel : struct, IPixel<TPixel>
{
float dpiX = 72;
float dpiY = 72;
var style = new SixLabors.Fonts.RendererOptions(font, dpiX, dpiY, location)
{
ApplyKerning = options.ApplyKerning,
TabWidth = options.TabWidth,
WrappingWidth = options.WrapTextWidth,
HorizontalAlignment = options.HorizontalAlignment,
VerticalAlignment = options.VerticalAlignment
};
Shapes.IPathCollection glyphs = Shapes.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;
}
}
}
}

99
tests/ImageSharp.Benchmarks/Drawing/DrawTextOutline.cs

@ -0,0 +1,99 @@
// <copyright file="Crop.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
using System.Drawing;
using System.Drawing.Drawing2D;
using BenchmarkDotNet.Attributes;
using System.IO;
using System.Numerics;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Text;
using SixLabors.ImageSharp.Processing.Overlays;
using SixLabors.ImageSharp.Processing.Drawing;
using System.Linq;
namespace SixLabors.ImageSharp.Benchmarks
{
[MemoryDiagnoser]
public class DrawTextOutline : BenchmarkBase
{
[Params(10, 100)]
public int TextIterations{ get; set; }
public string TextPhrase { get; set; } = "Hello World";
public string TextToRender => string.Join(" ", Enumerable.Repeat(TextPhrase, TextIterations));
[Benchmark(Baseline = true, Description = "System.Drawing Draw Text Outline")]
public void DrawTextSystemDrawing()
{
using (Bitmap destination = new Bitmap(800, 800))
{
using (Graphics graphics = Graphics.FromImage(destination))
{
graphics.InterpolationMode = InterpolationMode.Default;
graphics.SmoothingMode = SmoothingMode.AntiAlias;
Pen pen = new Pen(System.Drawing.Color.HotPink, 10);
var font = new Font("Arial", 12, GraphicsUnit.Point);
var gp = new GraphicsPath();
gp.AddString(TextToRender, font.FontFamily, (int)font.Style, font.Size, new RectangleF(10, 10, 780, 780), new StringFormat());
graphics.DrawPath(pen, gp);
}
}
}
[Benchmark(Description = "ImageSharp Draw Text Outline - Cached Glyphs")]
public void DrawTextCore()
{
using (Image<Rgba32> image = new Image<Rgba32>(800, 800))
{
var font = SixLabors.Fonts.SystemFonts.CreateFont("Arial", 12);
image.Mutate(x => x.ApplyProcessor(new SixLabors.ImageSharp.Processing.Text.Processors.DrawTextProcessor<Rgba32>(new TextGraphicsOptions(true) { WrapTextWidth = 780 }, TextToRender, font, null, SixLabors.ImageSharp.Processing.Drawing.Pens.Pens.Solid(Rgba32.HotPink, 10), new SixLabors.Primitives.PointF(10, 10))));
}
}
[Benchmark(Description = "ImageSharp Draw Text Outline - Nieve")]
public void DrawTextCoreOld()
{
using (Image<Rgba32> image = new Image<Rgba32>(800, 800))
{
var font = SixLabors.Fonts.SystemFonts.CreateFont("Arial", 12);
image.Mutate(x => DrawTextOldVersion(x, new TextGraphicsOptions(true) { WrapTextWidth = 780 }, TextToRender, font, null, SixLabors.ImageSharp.Processing.Drawing.Pens.Pens.Solid(Rgba32.HotPink, 10), new SixLabors.Primitives.PointF(10, 10)));
}
IImageProcessingContext<TPixel> DrawTextOldVersion<TPixel>(IImageProcessingContext<TPixel> source, TextGraphicsOptions options, string text, SixLabors.Fonts.Font font, SixLabors.ImageSharp.Processing.Drawing.Brushes.IBrush<TPixel> brush, SixLabors.ImageSharp.Processing.Drawing.Pens.IPen<TPixel> pen, SixLabors.Primitives.PointF location)
where TPixel : struct, IPixel<TPixel>
{
var style = new SixLabors.Fonts.RendererOptions(font, options.DpiX, options.DpiY, location)
{
ApplyKerning = options.ApplyKerning,
TabWidth = options.TabWidth,
WrappingWidth = options.WrapTextWidth,
HorizontalAlignment = options.HorizontalAlignment,
VerticalAlignment = options.VerticalAlignment
};
Shapes.IPathCollection glyphs = Shapes.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;
}
}
}
}

1
tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj

@ -18,6 +18,7 @@
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.10.14" />
<PackageReference Include="Colourful" Version="1.2.2" />
<PackageReference Include="SixLabors.Shapes.Text" Version="1.0.0-dev000081" />
<PackageReference Include="System.Drawing.Common" Version="4.5.0" />
</ItemGroup>

6
tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj

@ -20,9 +20,9 @@
<ItemGroup>
<PackageReference Include="BitMiracle.LibJpeg.NET" Version="1.4.280" />
<PackageReference Include="xunit" Version="2.3.1" />
<PackageReference Include="Moq" Version="4.8.2" />
<!--<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0" />-->
<!--<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />-->
<PackageReference Include="Moq" Version="4.8.3" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\ImageSharp.Tests\**\*.cs" Exclude="bin\**;obj\**" Link="Tests\%(RecursiveDir)%(Filename)%(Extension)" />

3
tests/ImageSharp.Tests/Drawing/BeziersTests.cs

@ -7,12 +7,11 @@ using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Drawing;
using SixLabors.ImageSharp.Processing.Overlays;
using SixLabors.Memory;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Drawing
{
using SixLabors.Memory;
public class Beziers : FileTestBase
{
[Fact]

117
tests/ImageSharp.Tests/Drawing/Paths/DrawPathCollection.cs

@ -0,0 +1,117 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Primitives;
using SixLabors.ImageSharp.Processing.Drawing;
using SixLabors.ImageSharp.Processing.Drawing.Brushes;
using SixLabors.ImageSharp.Processing.Drawing.Pens;
using SixLabors.ImageSharp.Processing.Drawing.Processors;
using SixLabors.Shapes;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Drawing.Paths
{
public class DrawPathCollection : BaseImageOperationsExtensionTest
{
GraphicsOptions noneDefault = new GraphicsOptions();
Rgba32 color = Rgba32.HotPink;
Pen<Rgba32> pen = Pens.Solid(Rgba32.HotPink, 1);
IPath path1 = new Path(new LinearLineSegment(new SixLabors.Primitives.PointF[] {
new Vector2(10,10),
new Vector2(20,10),
new Vector2(20,10),
new Vector2(30,10),
}));
IPath path2 = new Path(new LinearLineSegment(new SixLabors.Primitives.PointF[] {
new Vector2(10,10),
new Vector2(20,10),
new Vector2(20,10),
new Vector2(30,10),
}));
IPathCollection pathCollection;
public DrawPathCollection()
{
this.pathCollection = new PathCollection(this.path1, this.path2);
}
[Fact]
public void CorrectlySetsBrushAndPath()
{
this.operations.Draw(this.pen, this.pathCollection);
for (int i = 0; i < 2; i++)
{
FillRegionProcessor<Rgba32> processor = this.Verify<FillRegionProcessor<Rgba32>>(i);
Assert.Equal(GraphicsOptions.Default, processor.Options);
ShapePath region = Assert.IsType<ShapePath>(processor.Region);
// path is converted to a polygon before filling
ComplexPolygon polygon = Assert.IsType<ComplexPolygon>(region.Shape);
Assert.Equal(this.pen.StrokeFill, processor.Brush);
}
}
[Fact]
public void CorrectlySetsBrushPathOptions()
{
this.operations.Draw(this.noneDefault, this.pen, this.pathCollection);
for (int i = 0; i < 2; i++)
{
FillRegionProcessor<Rgba32> processor = this.Verify<FillRegionProcessor<Rgba32>>(i);
Assert.Equal(this.noneDefault, processor.Options);
ShapePath region = Assert.IsType<ShapePath>(processor.Region);
ComplexPolygon polygon = Assert.IsType<ComplexPolygon>(region.Shape);
Assert.Equal(this.pen.StrokeFill, processor.Brush);
}
}
[Fact]
public void CorrectlySetsColorAndPath()
{
this.operations.Draw(this.color, 1, this.pathCollection);
for (int i = 0; i < 2; i++)
{
FillRegionProcessor<Rgba32> processor = this.Verify<FillRegionProcessor<Rgba32>>(i);
Assert.Equal(GraphicsOptions.Default, processor.Options);
ShapePath region = Assert.IsType<ShapePath>(processor.Region);
ComplexPolygon polygon = Assert.IsType<ComplexPolygon>(region.Shape);
SolidBrush<Rgba32> brush = Assert.IsType<SolidBrush<Rgba32>>(processor.Brush);
Assert.Equal(this.color, brush.Color);
}
}
[Fact]
public void CorrectlySetsColorPathAndOptions()
{
this.operations.Draw(this.noneDefault, this.color, 1, this.pathCollection);
for (int i = 0; i < 2; i++)
{
FillRegionProcessor<Rgba32> processor = this.Verify<FillRegionProcessor<Rgba32>>(i);
Assert.Equal(this.noneDefault, processor.Options);
ShapePath region = Assert.IsType<ShapePath>(processor.Region);
ComplexPolygon polygon = Assert.IsType<ComplexPolygon>(region.Shape);
SolidBrush<Rgba32> brush = Assert.IsType<SolidBrush<Rgba32>>(processor.Brush);
Assert.Equal(this.color, brush.Color);
}
}
}
}

210
tests/ImageSharp.Tests/Drawing/Text/DrawText.Path.cs

@ -1,210 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.Fonts;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Drawing.Brushes;
using SixLabors.ImageSharp.Processing.Drawing.Pens;
using SixLabors.ImageSharp.Processing.Drawing.Processors;
using SixLabors.ImageSharp.Processing.Text;
using SixLabors.Shapes;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Drawing.Text
{
public class DrawText_Path : BaseImageOperationsExtensionTest
{
Rgba32 color = Rgba32.HotPink;
SolidBrush<Rgba32> brush = Brushes.Solid(Rgba32.HotPink);
IPath path = new SixLabors.Shapes.Path(
new LinearLineSegment(
new SixLabors.Primitives.PointF[] { new Vector2(10, 10), new Vector2(20, 10), new Vector2(20, 10), new Vector2(30, 10), }));
private readonly FontCollection FontCollection;
private readonly Font Font;
public DrawText_Path()
{
this.FontCollection = new FontCollection();
this.Font = this.FontCollection.Install(TestFontUtilities.GetPath("SixLaborsSampleAB.woff")).CreateFont(12);
}
[Fact]
public void FillsForEachACharachterWhenBrushSetAndNotPen()
{
this.operations.DrawText(
new TextGraphicsOptions(true),
"123",
this.Font,
Brushes.Solid(Rgba32.Red),
null,
this.path);
this.Verify<FillRegionProcessor<Rgba32>>(0);
this.Verify<FillRegionProcessor<Rgba32>>(1);
this.Verify<FillRegionProcessor<Rgba32>>(2);
}
[Fact]
public void FillsForEachACharachterWhenBrushSetAndNotPenDefaultOptions()
{
this.operations.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), null, this.path);
this.Verify<FillRegionProcessor<Rgba32>>(0);
this.Verify<FillRegionProcessor<Rgba32>>(1);
this.Verify<FillRegionProcessor<Rgba32>>(2);
}
[Fact]
public void FillsForEachACharachterWhenBrushSet()
{
this.operations.DrawText(new TextGraphicsOptions(true), "123", this.Font, Brushes.Solid(Rgba32.Red), this.path);
this.Verify<FillRegionProcessor<Rgba32>>(0);
this.Verify<FillRegionProcessor<Rgba32>>(1);
this.Verify<FillRegionProcessor<Rgba32>>(2);
}
[Fact]
public void FillsForEachACharachterWhenBrushSetDefaultOptions()
{
this.operations.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), this.path);
this.Verify<FillRegionProcessor<Rgba32>>(0);
this.Verify<FillRegionProcessor<Rgba32>>(1);
this.Verify<FillRegionProcessor<Rgba32>>(2);
}
[Fact]
public void FillsForEachACharachterWhenColorSet()
{
this.operations.DrawText(new TextGraphicsOptions(true), "123", this.Font, Rgba32.Red, this.path);
var processor = this.Verify<FillRegionProcessor<Rgba32>>(0);
this.Verify<FillRegionProcessor<Rgba32>>(1);
this.Verify<FillRegionProcessor<Rgba32>>(2);
SolidBrush<Rgba32> brush = Assert.IsType<SolidBrush<Rgba32>>(processor.Brush);
Assert.Equal(Rgba32.Red, brush.Color);
}
[Fact]
public void FillsForEachACharachterWhenColorSetDefaultOptions()
{
this.operations.DrawText("123", this.Font, Rgba32.Red, this.path);
FillRegionProcessor<Rgba32> processor = this.Verify<FillRegionProcessor<Rgba32>>(0);
this.Verify<FillRegionProcessor<Rgba32>>(1);
this.Verify<FillRegionProcessor<Rgba32>>(2);
SolidBrush<Rgba32> brush = Assert.IsType<SolidBrush<Rgba32>>(processor.Brush);
Assert.Equal(Rgba32.Red, brush.Color);
}
[Fact]
public void DrawForEachACharachterWhenPenSetAndNotBrush()
{
this.operations.DrawText(
new TextGraphicsOptions(true),
"123",
this.Font,
null,
Pens.Dash(Rgba32.Red, 1),
this.path);
var processor = this.Verify<FillRegionProcessor<Rgba32>>(0);
this.Verify<FillRegionProcessor<Rgba32>>(1);
this.Verify<FillRegionProcessor<Rgba32>>(2);
}
[Fact]
public void DrawForEachACharachterWhenPenSetAndNotBrushDefaultOptions()
{
this.operations.DrawText("123", this.Font, null, Pens.Dash(Rgba32.Red, 1), this.path);
var processor = this.Verify<FillRegionProcessor<Rgba32>>(0);
this.Verify<FillRegionProcessor<Rgba32>>(1);
this.Verify<FillRegionProcessor<Rgba32>>(2);
}
[Fact]
public void DrawForEachACharachterWhenPenSet()
{
this.operations.DrawText(new TextGraphicsOptions(true), "123", this.Font, Pens.Dash(Rgba32.Red, 1), this.path);
var processor = this.Verify<FillRegionProcessor<Rgba32>>(0);
this.Verify<FillRegionProcessor<Rgba32>>(1);
this.Verify<FillRegionProcessor<Rgba32>>(2);
}
[Fact]
public void DrawForEachACharachterWhenPenSetDefaultOptions()
{
this.operations.DrawText("123", this.Font, Pens.Dash(Rgba32.Red, 1), this.path);
FillRegionProcessor<Rgba32> processor = this.Verify<FillRegionProcessor<Rgba32>>(0);
this.Verify<FillRegionProcessor<Rgba32>>(1);
this.Verify<FillRegionProcessor<Rgba32>>(2);
}
[Fact]
public void DrawForEachACharachterWhenPenSetAndFillFroEachWhenBrushSet()
{
this.operations.DrawText(
new TextGraphicsOptions(true),
"123",
this.Font,
Brushes.Solid(Rgba32.Red),
Pens.Dash(Rgba32.Red, 1),
this.path);
var processor = this.Verify<FillRegionProcessor<Rgba32>>(0);
this.Verify<FillRegionProcessor<Rgba32>>(1);
this.Verify<FillRegionProcessor<Rgba32>>(2);
this.Verify<FillRegionProcessor<Rgba32>>(3);
this.Verify<FillRegionProcessor<Rgba32>>(4);
this.Verify<FillRegionProcessor<Rgba32>>(5);
}
[Fact]
public void DrawForEachACharachterWhenPenSetAndFillFroEachWhenBrushSetDefaultOptions()
{
this.operations.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), Pens.Dash(Rgba32.Red, 1), this.path);
var processor = this.Verify<FillRegionProcessor<Rgba32>>(0);
this.Verify<FillRegionProcessor<Rgba32>>(1);
this.Verify<FillRegionProcessor<Rgba32>>(2);
this.Verify<FillRegionProcessor<Rgba32>>(3);
this.Verify<FillRegionProcessor<Rgba32>>(4);
this.Verify<FillRegionProcessor<Rgba32>>(5);
}
[Fact]
public void BrushAppliesBeforPen()
{
this.operations.DrawText(
new TextGraphicsOptions(true),
"1",
this.Font,
Brushes.Solid(Rgba32.Red),
Pens.Dash(Rgba32.Red, 1),
this.path);
var processor = this.Verify<FillRegionProcessor<Rgba32>>(0);
this.Verify<FillRegionProcessor<Rgba32>>(1);
}
[Fact]
public void BrushAppliesBeforPenDefaultOptions()
{
this.operations.DrawText("1", this.Font, Brushes.Solid(Rgba32.Red), Pens.Dash(Rgba32.Red, 1), this.path);
var processor = this.Verify<FillRegionProcessor<Rgba32>>(0);
this.Verify<FillRegionProcessor<Rgba32>>(1);
}
}
}

101
tests/ImageSharp.Tests/Drawing/Text/DrawText.cs

@ -8,6 +8,8 @@ using SixLabors.ImageSharp.Processing.Drawing.Brushes;
using SixLabors.ImageSharp.Processing.Drawing.Pens;
using SixLabors.ImageSharp.Processing.Drawing.Processors;
using SixLabors.ImageSharp.Processing.Text;
using SixLabors.ImageSharp.Processing.Text.Processors;
using SixLabors.Primitives;
using SixLabors.Shapes;
using Xunit;
@ -44,9 +46,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
null,
Vector2.Zero);
this.Verify<FillRegionProcessor<Rgba32>>(0);
this.Verify<FillRegionProcessor<Rgba32>>(1);
this.Verify<FillRegionProcessor<Rgba32>>(2);
this.Verify<DrawTextProcessor<Rgba32>>(0);
}
[Fact]
@ -54,9 +54,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
{
this.operations.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), null, Vector2.Zero);
this.Verify<FillRegionProcessor<Rgba32>>(0);
this.Verify<FillRegionProcessor<Rgba32>>(1);
this.Verify<FillRegionProcessor<Rgba32>>(2);
this.Verify<DrawTextProcessor<Rgba32>>(0);
}
[Fact]
@ -64,9 +62,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
{
this.operations.DrawText(new TextGraphicsOptions(true), "123", this.Font, Brushes.Solid(Rgba32.Red), Vector2.Zero);
this.Verify<FillRegionProcessor<Rgba32>>(0);
this.Verify<FillRegionProcessor<Rgba32>>(1);
this.Verify<FillRegionProcessor<Rgba32>>(2);
this.Verify<DrawTextProcessor<Rgba32>>(0);
}
[Fact]
@ -74,9 +70,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
{
this.operations.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), Vector2.Zero);
this.Verify<FillRegionProcessor<Rgba32>>(0);
this.Verify<FillRegionProcessor<Rgba32>>(1);
this.Verify<FillRegionProcessor<Rgba32>>(2);
this.Verify<DrawTextProcessor<Rgba32>>(0);
}
[Fact]
@ -84,9 +78,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
{
this.operations.DrawText(new TextGraphicsOptions(true), "123", this.Font, Rgba32.Red, Vector2.Zero);
var processor = this.Verify<FillRegionProcessor<Rgba32>>(0);
this.Verify<FillRegionProcessor<Rgba32>>(1);
this.Verify<FillRegionProcessor<Rgba32>>(2);
var processor = this.Verify<DrawTextProcessor<Rgba32>>(0);
SolidBrush<Rgba32> brush = Assert.IsType<SolidBrush<Rgba32>>(processor.Brush);
Assert.Equal(Rgba32.Red, brush.Color);
@ -97,9 +89,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
{
this.operations.DrawText("123", this.Font, Rgba32.Red, Vector2.Zero);
var processor = this.Verify<FillRegionProcessor<Rgba32>>(0);
this.Verify<FillRegionProcessor<Rgba32>>(1);
this.Verify<FillRegionProcessor<Rgba32>>(2);
var processor = this.Verify<DrawTextProcessor<Rgba32>>(0);
SolidBrush<Rgba32> brush = Assert.IsType<SolidBrush<Rgba32>>(processor.Brush);
Assert.Equal(Rgba32.Red, brush.Color);
@ -116,9 +106,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
Pens.Dash(Rgba32.Red, 1),
Vector2.Zero);
var processor = this.Verify<FillRegionProcessor<Rgba32>>(0);
this.Verify<FillRegionProcessor<Rgba32>>(1);
this.Verify<FillRegionProcessor<Rgba32>>(2);
var processor = this.Verify<DrawTextProcessor<Rgba32>>(0);
}
[Fact]
@ -126,9 +114,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
{
this.operations.DrawText("123", this.Font, null, Pens.Dash(Rgba32.Red, 1), Vector2.Zero);
var processor = this.Verify<FillRegionProcessor<Rgba32>>(0);
this.Verify<FillRegionProcessor<Rgba32>>(1);
this.Verify<FillRegionProcessor<Rgba32>>(2);
var processor = this.Verify<DrawTextProcessor<Rgba32>>(0);
}
[Fact]
@ -136,9 +122,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
{
this.operations.DrawText(new TextGraphicsOptions(true), "123", this.Font, Pens.Dash(Rgba32.Red, 1), Vector2.Zero);
var processor = this.Verify<FillRegionProcessor<Rgba32>>(0);
this.Verify<FillRegionProcessor<Rgba32>>(1);
this.Verify<FillRegionProcessor<Rgba32>>(2);
var processor = this.Verify<DrawTextProcessor<Rgba32>>(0);
}
[Fact]
@ -146,9 +130,14 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
{
this.operations.DrawText("123", this.Font, Pens.Dash(Rgba32.Red, 1), Vector2.Zero);
var processor = this.Verify<FillRegionProcessor<Rgba32>>(0);
this.Verify<FillRegionProcessor<Rgba32>>(1);
this.Verify<FillRegionProcessor<Rgba32>>(2);
var processor = this.Verify<DrawTextProcessor<Rgba32>>(0);
Assert.Equal("123", processor.Text);
Assert.Equal(this.Font, processor.Font);
var penBrush = Assert.IsType<SolidBrush<Rgba32>>(processor.Pen.StrokeFill);
Assert.Equal(Rgba32.Red, penBrush.Color);
Assert.Equal(1, processor.Pen.StrokeWidth);
Assert.Equal(PointF.Empty, processor.Location);
}
[Fact]
@ -162,50 +151,16 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
Pens.Dash(Rgba32.Red, 1),
Vector2.Zero);
var processor = this.Verify<FillRegionProcessor<Rgba32>>(0);
this.Verify<FillRegionProcessor<Rgba32>>(1);
this.Verify<FillRegionProcessor<Rgba32>>(2);
this.Verify<FillRegionProcessor<Rgba32>>(3);
this.Verify<FillRegionProcessor<Rgba32>>(4);
this.Verify<FillRegionProcessor<Rgba32>>(5);
}
[Fact]
public void DrawForEachACharachterWhenPenSetAndFillFroEachWhenBrushSetDefaultOptions()
{
this.operations.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), Pens.Dash(Rgba32.Red, 1), Vector2.Zero);
var processor = this.Verify<FillRegionProcessor<Rgba32>>(0);
this.Verify<FillRegionProcessor<Rgba32>>(1);
this.Verify<FillRegionProcessor<Rgba32>>(2);
this.Verify<FillRegionProcessor<Rgba32>>(3);
this.Verify<FillRegionProcessor<Rgba32>>(4);
this.Verify<FillRegionProcessor<Rgba32>>(5);
}
[Fact]
public void BrushAppliesBeforePen()
{
this.operations.DrawText(
new TextGraphicsOptions(true),
"1",
this.Font,
Brushes.Solid(Rgba32.Red),
Pens.Dash(Rgba32.Red, 1),
Vector2.Zero);
var processor = this.Verify<FillRegionProcessor<Rgba32>>(0);
this.Verify<FillRegionProcessor<Rgba32>>(1);
}
var processor = this.Verify<DrawTextProcessor<Rgba32>>(0);
[Fact]
public void BrushAppliesBeforPenDefaultOptions()
{
this.operations.DrawText("1", this.Font, Brushes.Solid(Rgba32.Red), Pens.Dash(Rgba32.Red, 1), Vector2.Zero);
var processor = this.Verify<FillRegionProcessor<Rgba32>>(0);
this.Verify<FillRegionProcessor<Rgba32>>(1);
Assert.Equal("123", processor.Text);
Assert.Equal(this.Font, processor.Font);
var brush = Assert.IsType<SolidBrush<Rgba32>>(processor.Brush);
Assert.Equal(Rgba32.Red, brush.Color);
Assert.Equal(PointF.Empty, processor.Location);
var penBrush = Assert.IsType<SolidBrush<Rgba32>>(processor.Pen.StrokeFill);
Assert.Equal(Rgba32.Red, penBrush.Color);
Assert.Equal(1, processor.Pen.StrokeWidth);
}
}
}

106
tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs

@ -1,25 +1,22 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using System;
using System.Linq;
using System.Text;
using SixLabors.Fonts;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Drawing;
using SixLabors.ImageSharp.Processing.Drawing.Pens;
using SixLabors.ImageSharp.Processing.Text;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using SixLabors.Primitives;
using Xunit;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests.Drawing.Text
{
using System;
using System.Linq;
using System.Text;
using SixLabors.Primitives;
[GroupOutput("Drawing/Text")]
public class DrawTextOnImageTests
{
@ -27,9 +24,9 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
private const string TestText = "Sphinx of black quartz, judge my vow\n0123456789";
private const string TestText2 =
"THISISTESTWORDS ";
public static ImageComparer TextDrawingComparer = ImageComparer.TolerantPercentage(0.01f);
public static ImageComparer OutlinedTextDrawingComparer = ImageComparer.TolerantPercentage(0.5f, 3);
[Theory]
[WithSolidFilledImages(200, 100, "White", PixelTypes.Rgba32, 50, 0, 0, "SixLaborsSampleAB.woff", AB)]
[WithSolidFilledImages(900, 100, "White", PixelTypes.Rgba32, 50, 0, 0, "OpenSans-Regular.ttf", TestText)]
@ -45,16 +42,15 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
where TPixel : struct, IPixel<TPixel>
{
Font font = CreateFont(fontName, fontSize);
string fnDisplayText = text.Replace("\n", "");
fnDisplayText = fnDisplayText.Substring(0, Math.Min(fnDisplayText.Length, 4));
TPixel color = NamedColors<TPixel>.Black;
provider.VerifyOperation(
TextDrawingComparer,
img =>
{
img.Mutate(c => c.DrawText(text, new Font(font, fontSize), color, new PointF(x, y)));
},
$"{fontName}-{fontSize}-{fnDisplayText}-({x},{y})",
{
img.Mutate(c => c.DrawText(text, new Font(font, fontSize), color, new PointF(x, y)));
},
$"{fontName}-{fontSize}-{ToTestOutputDisplayText(text)}-({x},{y})",
appendPixelTypeToFileName: false,
appendSourceFileOrDescription: true);
}
@ -84,15 +80,16 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
}
var textOptions = new TextGraphicsOptions
{
Antialias = true,
ApplyKerning = true,
VerticalAlignment = VerticalAlignment.Top,
HorizontalAlignment = HorizontalAlignment.Left,
};
{
Antialias = true,
ApplyKerning = true,
VerticalAlignment = VerticalAlignment.Top,
HorizontalAlignment = HorizontalAlignment.Left,
};
TPixel color = NamedColors<TPixel>.Black;
provider.VerifyOperation(
TextDrawingComparer,
img =>
{
img.Mutate(c => c.DrawText(textOptions, sb.ToString(), font, color, new PointF(10, 5)));
@ -101,8 +98,69 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
false);
}
[Theory]
[WithSolidFilledImages(200, 100, "White", PixelTypes.Rgba32, 50, 0, 0, "SixLaborsSampleAB.woff", AB)]
[WithSolidFilledImages(900, 100, "White", PixelTypes.Rgba32, 50, 0, 0, "OpenSans-Regular.ttf", TestText)]
[WithSolidFilledImages(1100, 200, "White", PixelTypes.Rgba32, 50, 150, 100, "OpenSans-Regular.ttf", TestText)]
public void FontShapesAreRenderedCorrectlyWithAPen<TPixel>(
TestImageProvider<TPixel> provider,
int fontSize,
int x,
int y,
string fontName,
string text)
where TPixel : struct, IPixel<TPixel>
{
Font font = CreateFont(fontName, fontSize);
TPixel color = NamedColors<TPixel>.Black;
provider.VerifyOperation(
OutlinedTextDrawingComparer,
img =>
{
img.Mutate(c => c.DrawText(text, new Font(font, fontSize), null, Pens.Solid(color, 1), new PointF(x, y)));
},
$"pen_{fontName}-{fontSize}-{ToTestOutputDisplayText(text)}-({x},{y})",
appendPixelTypeToFileName: false,
appendSourceFileOrDescription: true);
}
[Theory]
[WithSolidFilledImages(200, 100, "White", PixelTypes.Rgba32, 50, 0, 0, "SixLaborsSampleAB.woff", AB)]
[WithSolidFilledImages(900, 100, "White", PixelTypes.Rgba32, 50, 0, 0, "OpenSans-Regular.ttf", TestText)]
[WithSolidFilledImages(1100, 200, "White", PixelTypes.Rgba32, 50, 150, 100, "OpenSans-Regular.ttf", TestText)]
public void FontShapesAreRenderedCorrectlyWithAPenPatterned<TPixel>(
TestImageProvider<TPixel> provider,
int fontSize,
int x,
int y,
string fontName,
string text)
where TPixel : struct, IPixel<TPixel>
{
Font font = CreateFont(fontName, fontSize);
TPixel color = NamedColors<TPixel>.Black;
provider.VerifyOperation(
OutlinedTextDrawingComparer,
img =>
{
img.Mutate(c => c.DrawText(text, new Font(font, fontSize), null, Pens.DashDot(color, 3), new PointF(x, y)));
},
$"pen_{fontName}-{fontSize}-{ToTestOutputDisplayText(text)}-({x},{y})",
appendPixelTypeToFileName: false,
appendSourceFileOrDescription: true);
}
private static string Repeat(string str, int times) => string.Concat(Enumerable.Repeat(str, times));
private static string ToTestOutputDisplayText(string text)
{
string fnDisplayText = text.Replace("\n", "");
fnDisplayText = fnDisplayText.Substring(0, Math.Min(fnDisplayText.Length, 4));
return fnDisplayText;
}
private static Font CreateFont(string fontName, int size)
{
var fontCollection = new FontCollection();

51
tests/ImageSharp.Tests/Drawing/Utils/QuickSortTests.cs

@ -0,0 +1,51 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Tests.Drawing.Utils
{
using System;
using System.Linq;
using SixLabors.ImageSharp.Utils;
using Xunit;
public class QuickSortTests
{
public static readonly TheoryData<float[]> Data = new TheoryData<float[]>()
{
new float[]{ 3, 2, 1 },
new float[0],
new float[] { 42},
new float[] { 1, 2},
new float[] { 2, 1},
new float[] { 5, 1, 2, 3, 0}
};
[Theory]
[MemberData(nameof(Data))]
public void Sort(float[] data)
{
float[] expected = data.ToArray();
Array.Sort(expected);
QuickSort.Sort(data);
Assert.Equal(expected, data);
}
[Fact]
public void SortSlice()
{
float[] data = { 3, 2, 1, 0, -1 };
Span<float> slice = data.AsSpan(1, 3);
QuickSort.Sort(slice);
float[] actual = slice.ToArray();
float[] expected = { 0, 1, 2 };
Assert.Equal(actual, expected);
}
}
}

1
tests/ImageSharp.Tests/Formats/Png/PngChunkTypeTests.cs

@ -19,6 +19,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
Assert.Equal(PngChunkType.Text, GetType("tEXt"));
Assert.Equal(PngChunkType.Gamma, GetType("gAMA"));
Assert.Equal(PngChunkType.Physical, GetType("pHYs"));
Assert.Equal(PngChunkType.Exif, GetType("eXIf"));
}
private static PngChunkType GetType(string text)

9
tests/ImageSharp.Tests/ImageSharp.Tests.csproj

@ -25,6 +25,7 @@
<ItemGroup>
<None Include="PixelFormats\PixelOperationsTests.Blender.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CSharp" Version="4.5.0" />
<PackageReference Include="System.Drawing.Common" Version="4.5.0" />
@ -34,7 +35,7 @@
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.2" />
<PackageReference Include="xunit" Version="2.3.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
<PackageReference Include="Moq" Version="4.8.2" />
<PackageReference Include="Moq" Version="4.8.3" />
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="..\..\stylecop.json" />
@ -45,6 +46,12 @@
<DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
</ItemGroup>
<ItemGroup>
<None Update="TestFonts\OpenSans-Regular.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="TestFonts\SixLaborsSampleAB.woff">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="xunit.runner.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>

21
tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs

@ -30,6 +30,13 @@ namespace SixLabors.ImageSharp.Tests
return Boolean.TryParse(Environment.GetEnvironmentVariable("CI"), out isCi) && isCi;
});
private static readonly Lazy<string> NetCoreVersionLazy = new Lazy<string>(GetNetCoreVersion);
/// <summary>
/// Gets the .NET Core version, if running on .NET Core, otherwise returns an empty string.
/// </summary>
internal static string NetCoreVersion => NetCoreVersionLazy.Value;
// ReSharper disable once InconsistentNaming
/// <summary>
/// Gets a value indicating whether test execution runs on CI.
@ -123,5 +130,19 @@ namespace SixLabors.ImageSharp.Tests
return path;
}
/// <summary>
/// Solution borrowed from:
/// https://github.com/dotnet/BenchmarkDotNet/issues/448#issuecomment-308424100
/// </summary>
private static string GetNetCoreVersion()
{
Assembly assembly = typeof(System.Runtime.GCSettings).GetTypeInfo().Assembly;
string[] assemblyPath = assembly.CodeBase.Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
int netCoreAppIndex = Array.IndexOf(assemblyPath, "Microsoft.NETCore.App");
if (netCoreAppIndex > 0 && netCoreAppIndex < assemblyPath.Length - 2)
return assemblyPath[netCoreAppIndex + 1];
return "";
}
}
}

26
tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs

@ -4,6 +4,7 @@
using System;
using System.IO;
using SixLabors.ImageSharp.Common.Helpers;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.Formats.Gif;
@ -13,9 +14,11 @@ using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs;
using Xunit;
using Xunit.Abstractions;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests
{
public class TestEnvironmentTests
{
public TestEnvironmentTests(ITestOutputHelper output)
@ -31,6 +34,29 @@ namespace SixLabors.ImageSharp.Tests
Assert.True(Directory.Exists(path));
}
/// <summary>
/// We need this test to make sure that the netcoreapp2.1 test execution actually covers the netcoreapp2.1 build configuration of ImageSharp.
/// </summary>
[Fact]
public void ImageSharpAssemblyUnderTest_MatchesExpectedTargetFramework()
{
this.Output.WriteLine("NetCoreVersion: " + TestEnvironment.NetCoreVersion);
this.Output.WriteLine("ImageSharpBuiltAgainst: " + TestHelpers.ImageSharpBuiltAgainst);
if (string.IsNullOrEmpty(TestEnvironment.NetCoreVersion))
{
this.Output.WriteLine("Not running under .NET Core!");
}
else if (TestEnvironment.NetCoreVersion.StartsWith("2.1"))
{
Assert.Equal("netcoreapp2.1", TestHelpers.ImageSharpBuiltAgainst);
}
else
{
Assert.Equal("netstandard2.0", TestHelpers.ImageSharpBuiltAgainst);
}
}
[Fact]
public void SolutionDirectoryFullPath()
{

Loading…
Cancel
Save