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} 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 # prevent the double build when a branch has an active PR
skip_branch_with_pr: true skip_branch_with_pr: true
environment: environment:
matrix: 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 - target_framework: netcoreapp2.1
# is_32bit: True is_32bit: True
- target_framework: netcoreapp2.0 - target_framework: netcoreapp2.0
is_32bit: False is_32bit: False

5
run-tests.ps1

@ -75,6 +75,11 @@ else {
$xunitArgs += " --fx-version 2.0.0" $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") { if ($is32Bit -eq "True") {
$xunitArgs += " -x86" $xunitArgs += " -x86"
} }

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

@ -39,7 +39,8 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="SixLabors.Core" Version="1.0.0-beta0005" /> <PackageReference Include="SixLabors.Core" Version="1.0.0-beta0005" />
<AdditionalFiles Include="..\..\stylecop.json" /> <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"> <PackageReference Include="StyleCop.Analyzers" Version="1.1.0-beta007">
<PrivateAssets>All</PrivateAssets> <PrivateAssets>All</PrivateAssets>
</PackageReference> </PackageReference>

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

@ -16,9 +16,8 @@ namespace SixLabors.ImageSharp.Primitives
/// </summary> /// </summary>
/// <param name="shape">The shape.</param> /// <param name="shape">The shape.</param>
/// <param name="pen">The pen to apply to 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) 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. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Primitives;
using SixLabors.ImageSharp.Processing.Drawing.Brushes; using SixLabors.ImageSharp.Processing.Drawing.Brushes;
using SixLabors.ImageSharp.Processing.Processors; using SixLabors.ImageSharp.Processing.Processors;
using SixLabors.ImageSharp.Utils;
using SixLabors.Memory; using SixLabors.Memory;
using SixLabors.Primitives; using SixLabors.Primitives;
@ -124,7 +124,7 @@ namespace SixLabors.ImageSharp.Processing.Drawing.Processors
continue; continue;
} }
QuickSort(buffer.Slice(0, pointsFound)); QuickSort.Sort(buffer.Slice(0, pointsFound));
for (int point = 0; point < pointsFound; point += 2) 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.Fonts;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Drawing;
using SixLabors.ImageSharp.Processing.Drawing.Brushes; using SixLabors.ImageSharp.Processing.Drawing.Brushes;
using SixLabors.ImageSharp.Processing.Drawing.Pens; using SixLabors.ImageSharp.Processing.Drawing.Pens;
using SixLabors.ImageSharp.Processing.Text.Processors;
using SixLabors.Primitives; using SixLabors.Primitives;
using SixLabors.Shapes;
namespace SixLabors.ImageSharp.Processing.Text namespace SixLabors.ImageSharp.Processing.Text
{ {
@ -16,8 +15,6 @@ namespace SixLabors.ImageSharp.Processing.Text
/// </summary> /// </summary>
public static partial class DrawTextExtensions public static partial class DrawTextExtensions
{ {
private static readonly int DefaultTextDpi = 72;
/// <summary> /// <summary>
/// Draws the text onto the the image filled via the brush. /// Draws the text onto the the image filled via the brush.
/// </summary> /// </summary>
@ -150,33 +147,6 @@ namespace SixLabors.ImageSharp.Processing.Text
/// </returns> /// </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) 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> where TPixel : struct, IPixel<TPixel>
{ => source.ApplyProcessor(new DrawTextProcessor<TPixel>(options, text, font, brush, pen, location));
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;
}
} }
} }

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> /// </summary>
public struct TextGraphicsOptions public struct TextGraphicsOptions
{ {
private const int DefaultTextDpi = 72;
/// <summary> /// <summary>
/// Represents the default <see cref="TextGraphicsOptions"/>. /// Represents the default <see cref="TextGraphicsOptions"/>.
/// </summary> /// </summary>
@ -26,11 +28,16 @@ namespace SixLabors.ImageSharp.Processing.Text
private float? tabWidth; private float? tabWidth;
private float? dpiX;
private float? dpiY;
private PixelBlenderMode blenderMode; private PixelBlenderMode blenderMode;
private float wrapTextWidth; private float wrapTextWidth;
private HorizontalAlignment? horizontalAlignment; private HorizontalAlignment? horizontalAlignment;
private VerticalAlignment? verticalAlignment; private VerticalAlignment? verticalAlignment;
/// <summary> /// <summary>
@ -49,6 +56,8 @@ namespace SixLabors.ImageSharp.Processing.Text
this.blenderMode = PixelBlenderMode.Normal; this.blenderMode = PixelBlenderMode.Normal;
this.blendPercentage = 1; this.blendPercentage = 1;
this.antialias = enableAntialiasing; this.antialias = enableAntialiasing;
this.dpiX = DefaultTextDpi;
this.dpiY = DefaultTextDpi;
} }
/// <summary> /// <summary>
@ -90,6 +99,16 @@ namespace SixLabors.ImageSharp.Processing.Text
/// </summary> /// </summary>
public float WrapTextWidth { get => this.wrapTextWidth; set => this.wrapTextWidth = value; } 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> /// <summary>
/// Gets or sets a value indicating how to align the text relative to the rendering space. /// 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 /// 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. // Licensed under the Apache License, Version 2.0.
using System; using System;
using SixLabors.ImageSharp.ColorSpaces;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce; using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion namespace SixLabors.ImageSharp.ColorSpaces.Conversion
@ -21,9 +20,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The adapted color</returns> /// <returns>The adapted color</returns>
public CieXyz Adapt(CieXyz color, CieXyz sourceWhitePoint) public CieXyz Adapt(CieXyz color, CieXyz sourceWhitePoint)
{ {
Guard.NotNull(color, nameof(color));
Guard.NotNull(sourceWhitePoint, nameof(sourceWhitePoint));
if (!this.IsChromaticAdaptationPerformed) if (!this.IsChromaticAdaptationPerformed)
{ {
throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point."); 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> /// <returns>The adapted color</returns>
public CieLab Adapt(CieLab color) public CieLab Adapt(CieLab color)
{ {
Guard.NotNull(color, nameof(color));
if (!this.IsChromaticAdaptationPerformed) if (!this.IsChromaticAdaptationPerformed)
{ {
throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point."); 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> /// <returns>The adapted color</returns>
public CieLch Adapt(CieLch color) public CieLch Adapt(CieLch color)
{ {
Guard.NotNull(color, nameof(color));
if (!this.IsChromaticAdaptationPerformed) if (!this.IsChromaticAdaptationPerformed)
{ {
throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point."); 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> /// <returns>The adapted color</returns>
public CieLchuv Adapt(CieLchuv color) public CieLchuv Adapt(CieLchuv color)
{ {
Guard.NotNull(color, nameof(color));
if (!this.IsChromaticAdaptationPerformed) if (!this.IsChromaticAdaptationPerformed)
{ {
throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point."); 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> /// <returns>The adapted color</returns>
public CieLuv Adapt(CieLuv color) public CieLuv Adapt(CieLuv color)
{ {
Guard.NotNull(color, nameof(color));
if (!this.IsChromaticAdaptationPerformed) if (!this.IsChromaticAdaptationPerformed)
{ {
throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point."); 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> /// <returns>The adapted color</returns>
public HunterLab Adapt(HunterLab color) public HunterLab Adapt(HunterLab color)
{ {
Guard.NotNull(color, nameof(color));
if (!this.IsChromaticAdaptationPerformed) if (!this.IsChromaticAdaptationPerformed)
{ {
throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point."); 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> /// <returns>The adapted color</returns>
public LinearRgb Adapt(LinearRgb color) public LinearRgb Adapt(LinearRgb color)
{ {
Guard.NotNull(color, nameof(color));
if (!this.IsChromaticAdaptationPerformed) if (!this.IsChromaticAdaptationPerformed)
{ {
throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point."); 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> /// <returns>The adapted color</returns>
public Rgb Adapt(Rgb color) public Rgb Adapt(Rgb color)
{ {
Guard.NotNull(color, nameof(color));
LinearRgb linearInput = this.ToLinearRgb(color); LinearRgb linearInput = this.ToLinearRgb(color);
LinearRgb linearOutput = this.Adapt(linearInput); LinearRgb linearOutput = this.Adapt(linearInput);
return this.ToRgb(linearOutput); 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> /// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(CieLch color) public CieLab ToCieLab(CieLch color)
{ {
Guard.NotNull(color, nameof(color));
// Conversion (perserving white point) // Conversion (perserving white point)
CieLab unadapted = CieLchToCieLabConverter.Convert(color); CieLab unadapted = CieLchToCieLabConverter.Convert(color);
@ -77,8 +75,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns> /// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(CieXyz color) public CieLab ToCieLab(CieXyz color)
{ {
Guard.NotNull(color, nameof(color));
// Adaptation // Adaptation
CieXyz adapted = !this.WhitePoint.Equals(this.TargetLabWhitePoint) && this.IsChromaticAdaptationPerformed CieXyz adapted = !this.WhitePoint.Equals(this.TargetLabWhitePoint) && this.IsChromaticAdaptationPerformed
? this.ChromaticAdaptation.Transform(color, this.WhitePoint, this.TargetLabWhitePoint) ? this.ChromaticAdaptation.Transform(color, this.WhitePoint, this.TargetLabWhitePoint)
@ -96,8 +92,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns> /// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(Cmyk color) public CieLab ToCieLab(Cmyk color)
{ {
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color); CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor); return this.ToCieLab(xyzColor);
} }
@ -120,8 +114,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns> /// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(Hsv color) public CieLab ToCieLab(Hsv color)
{ {
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color); CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor); 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> /// <returns>The <see cref="CieLch"/></returns>
public CieLch ToCieLch(Cmyk color) public CieLch ToCieLch(Cmyk color)
{ {
Guard.NotNull(color, nameof(color));
CieXyz xyzColor = this.ToCieXyz(color); CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLch(xyzColor); 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> /// <returns>The <see cref="CieXyy"/></returns>
public CieXyy ToCieXyy(CieLab color) public CieXyy ToCieXyy(CieLab color)
{ {
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToCieXyy(xyzColor); return this.ToCieXyy(xyzColor);
@ -33,8 +31,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyy"/></returns> /// <returns>The <see cref="CieXyy"/></returns>
public CieXyy ToCieXyy(CieLch color) public CieXyy ToCieXyy(CieLch color)
{ {
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToCieXyy(xyzColor); return this.ToCieXyy(xyzColor);
@ -47,8 +43,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyy"/></returns> /// <returns>The <see cref="CieXyy"/></returns>
public CieXyy ToCieXyy(CieLchuv color) public CieXyy ToCieXyy(CieLchuv color)
{ {
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToCieXyy(xyzColor); return this.ToCieXyy(xyzColor);
@ -61,8 +55,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyy"/></returns> /// <returns>The <see cref="CieXyy"/></returns>
public CieXyy ToCieXyy(CieLuv color) public CieXyy ToCieXyy(CieLuv color)
{ {
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToCieXyy(xyzColor); return this.ToCieXyy(xyzColor);
@ -75,8 +67,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyy"/></returns> /// <returns>The <see cref="CieXyy"/></returns>
public CieXyy ToCieXyy(CieXyz color) public CieXyy ToCieXyy(CieXyz color)
{ {
Guard.NotNull(color, nameof(color));
return CieXyzAndCieXyyConverter.Convert(color); return CieXyzAndCieXyyConverter.Convert(color);
} }
@ -87,8 +77,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyy"/></returns> /// <returns>The <see cref="CieXyy"/></returns>
public CieXyy ToCieXyy(Cmyk color) public CieXyy ToCieXyy(Cmyk color)
{ {
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToCieXyy(xyzColor); return this.ToCieXyy(xyzColor);
@ -101,8 +89,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyy"/></returns> /// <returns>The <see cref="CieXyy"/></returns>
public CieXyy ToCieXyy(Hsl color) public CieXyy ToCieXyy(Hsl color)
{ {
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToCieXyy(xyzColor); return this.ToCieXyy(xyzColor);
@ -115,8 +101,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyy"/></returns> /// <returns>The <see cref="CieXyy"/></returns>
public CieXyy ToCieXyy(Hsv color) public CieXyy ToCieXyy(Hsv color)
{ {
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToCieXyy(xyzColor); return this.ToCieXyy(xyzColor);
@ -129,8 +113,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyy"/></returns> /// <returns>The <see cref="CieXyy"/></returns>
public CieXyy ToCieXyy(HunterLab color) public CieXyy ToCieXyy(HunterLab color)
{ {
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToCieXyy(xyzColor); return this.ToCieXyy(xyzColor);
@ -143,8 +125,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyy"/></returns> /// <returns>The <see cref="CieXyy"/></returns>
public CieXyy ToCieXyy(LinearRgb color) public CieXyy ToCieXyy(LinearRgb color)
{ {
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToCieXyy(xyzColor); return this.ToCieXyy(xyzColor);
@ -157,8 +137,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyy"/></returns> /// <returns>The <see cref="CieXyy"/></returns>
public CieXyy ToCieXyy(Lms color) public CieXyy ToCieXyy(Lms color)
{ {
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToCieXyy(xyzColor); return this.ToCieXyy(xyzColor);
@ -171,8 +149,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyy"/></returns> /// <returns>The <see cref="CieXyy"/></returns>
public CieXyy ToCieXyy(Rgb color) public CieXyy ToCieXyy(Rgb color)
{ {
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToCieXyy(xyzColor); return this.ToCieXyy(xyzColor);
@ -185,8 +161,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyy"/></returns> /// <returns>The <see cref="CieXyy"/></returns>
public CieXyy ToCieXyy(YCbCr color) public CieXyy ToCieXyy(YCbCr color)
{ {
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToCieXyy(xyzColor); return this.ToCieXyy(xyzColor);

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

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

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

@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.ColorSpaces;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HsvColorSapce; using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HsvColorSapce;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion namespace SixLabors.ImageSharp.ColorSpaces.Conversion
@ -20,8 +19,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Hsv"/></returns> /// <returns>The <see cref="Hsv"/></returns>
public Hsv ToHsv(CieLab color) public Hsv ToHsv(CieLab color)
{ {
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToHsv(xyzColor); return this.ToHsv(xyzColor);
@ -34,8 +31,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Hsv"/></returns> /// <returns>The <see cref="Hsv"/></returns>
public Hsv ToHsv(CieLch color) public Hsv ToHsv(CieLch color)
{ {
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToHsv(xyzColor); return this.ToHsv(xyzColor);
@ -48,8 +43,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Hsv"/></returns> /// <returns>The <see cref="Hsv"/></returns>
public Hsv ToHsv(CieLchuv color) public Hsv ToHsv(CieLchuv color)
{ {
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToHsv(xyzColor); return this.ToHsv(xyzColor);
@ -62,8 +55,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Hsv"/></returns> /// <returns>The <see cref="Hsv"/></returns>
public Hsv ToHsv(CieLuv color) public Hsv ToHsv(CieLuv color)
{ {
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToHsv(xyzColor); return this.ToHsv(xyzColor);
@ -76,8 +67,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Hsv"/></returns> /// <returns>The <see cref="Hsv"/></returns>
public Hsv ToHsv(CieXyy color) public Hsv ToHsv(CieXyy color)
{ {
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToHsv(xyzColor); return this.ToHsv(xyzColor);
@ -90,8 +79,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Hsv"/></returns> /// <returns>The <see cref="Hsv"/></returns>
public Hsv ToHsv(CieXyz color) public Hsv ToHsv(CieXyz color)
{ {
Guard.NotNull(color, nameof(color));
var rgb = this.ToRgb(color); var rgb = this.ToRgb(color);
return HsvAndRgbConverter.Convert(rgb); return HsvAndRgbConverter.Convert(rgb);
@ -104,8 +91,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Hsv"/></returns> /// <returns>The <see cref="Hsv"/></returns>
public Hsv ToHsv(Cmyk color) public Hsv ToHsv(Cmyk color)
{ {
Guard.NotNull(color, nameof(color));
var rgb = this.ToRgb(color); var rgb = this.ToRgb(color);
return HsvAndRgbConverter.Convert(rgb); return HsvAndRgbConverter.Convert(rgb);
@ -118,8 +103,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Hsv"/></returns> /// <returns>The <see cref="Hsv"/></returns>
public Hsv ToHsv(Hsl color) public Hsv ToHsv(Hsl color)
{ {
Guard.NotNull(color, nameof(color));
var rgb = this.ToRgb(color); var rgb = this.ToRgb(color);
return HsvAndRgbConverter.Convert(rgb); return HsvAndRgbConverter.Convert(rgb);
@ -132,8 +115,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Hsv"/></returns> /// <returns>The <see cref="Hsv"/></returns>
public Hsv ToHsv(HunterLab color) public Hsv ToHsv(HunterLab color)
{ {
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToHsv(xyzColor); return this.ToHsv(xyzColor);
@ -146,8 +127,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Hsv"/></returns> /// <returns>The <see cref="Hsv"/></returns>
public Hsv ToHsv(LinearRgb color) public Hsv ToHsv(LinearRgb color)
{ {
Guard.NotNull(color, nameof(color));
var rgb = this.ToRgb(color); var rgb = this.ToRgb(color);
return HsvAndRgbConverter.Convert(rgb); return HsvAndRgbConverter.Convert(rgb);
@ -160,8 +139,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Hsv"/></returns> /// <returns>The <see cref="Hsv"/></returns>
public Hsv ToHsv(Lms color) public Hsv ToHsv(Lms color)
{ {
Guard.NotNull(color, nameof(color));
var xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToHsv(xyzColor); return this.ToHsv(xyzColor);
@ -174,8 +151,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Hsv"/></returns> /// <returns>The <see cref="Hsv"/></returns>
public Hsv ToHsv(Rgb color) public Hsv ToHsv(Rgb color)
{ {
Guard.NotNull(color, nameof(color));
return HsvAndRgbConverter.Convert(color); return HsvAndRgbConverter.Convert(color);
} }
@ -186,8 +161,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Hsv"/></returns> /// <returns>The <see cref="Hsv"/></returns>
public Hsv ToHsv(YCbCr color) public Hsv ToHsv(YCbCr color)
{ {
Guard.NotNull(color, nameof(color));
var rgb = this.ToRgb(color); var rgb = this.ToRgb(color);
return HsvAndRgbConverter.Convert(rgb); 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> /// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(Hsv color) public LinearRgb ToLinearRgb(Hsv color)
{ {
Guard.NotNull(color, nameof(color));
var rgb = this.ToRgb(color); var rgb = this.ToRgb(color);
return this.ToLinearRgb(rgb); 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> /// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(Hsl color) public Rgb ToRgb(Hsl color)
{ {
Guard.NotNull(color, nameof(color));
// Conversion // Conversion
return HslAndRgbConverter.Convert(color); 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)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieLuv Convert(CieXyz input) 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 // Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_Luv.html
float yr = input.Y / this.LuvWhitePoint.Y; float yr = input.Y / this.LuvWhitePoint.Y;
float up = ComputeUp(input); 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)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieXyy Convert(CieXyz input) public CieXyy Convert(CieXyz input)
{ {
DebugGuard.NotNull(input, nameof(input));
float x = input.X / (input.X + input.Y + input.Z); float x = input.X / (input.X + input.Y + input.Z);
float y = input.Y / (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)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieXyz Convert(CieXyy input) public CieXyz Convert(CieXyy input)
{ {
DebugGuard.NotNull(input, nameof(input));
if (MathF.Abs(input.Y) < Constants.Epsilon) if (MathF.Abs(input.Y) < Constants.Epsilon)
{ {
return new CieXyz(0, 0, input.Yl); 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)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public Rgb Convert(Hsl input) public Rgb Convert(Hsl input)
{ {
DebugGuard.NotNull(input, nameof(input));
float rangedH = input.H / 360F; float rangedH = input.H / 360F;
float r = 0; float r = 0;
float g = 0; float g = 0;
@ -49,8 +47,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HslColorSap
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public Hsl Convert(Rgb input) public Hsl Convert(Rgb input)
{ {
DebugGuard.NotNull(input, nameof(input));
float r = input.R; float r = input.R;
float g = input.G; float g = input.G;
float b = input.B; 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)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public Rgb Convert(Hsv input) public Rgb Convert(Hsv input)
{ {
DebugGuard.NotNull(input, nameof(input));
float s = input.S; float s = input.S;
float v = input.V; float v = input.V;
@ -81,8 +79,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HsvColorSap
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public Hsv Convert(Rgb input) public Hsv Convert(Rgb input)
{ {
DebugGuard.NotNull(input, nameof(input));
float r = input.R; float r = input.R;
float g = input.G; float g = input.G;
float b = input.B; 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)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float ComputeKb(CieXyz whitePoint) public static float ComputeKb(CieXyz whitePoint)
{ {
DebugGuard.NotNull(whitePoint, nameof(whitePoint));
if (whitePoint == Illuminants.C) if (whitePoint == Illuminants.C)
{ {
return 70F; return 70F;

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

@ -33,18 +33,12 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabCo
/// <summary> /// <summary>
/// Gets the target reference white. When not set, <see cref="HunterLab.DefaultWhitePoint"/> is used. /// Gets the target reference white. When not set, <see cref="HunterLab.DefaultWhitePoint"/> is used.
/// </summary> /// </summary>
public CieXyz HunterLabWhitePoint public CieXyz HunterLabWhitePoint { get; }
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get;
}
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public HunterLab Convert(CieXyz input) public HunterLab Convert(CieXyz input)
{ {
DebugGuard.NotNull(input, nameof(input));
// Conversion algorithm described here: http://en.wikipedia.org/wiki/Lab_color_space#Hunter_Lab // 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 x = input.X, y = input.Y, z = input.Z;
float xn = this.HunterLabWhitePoint.X, yn = this.HunterLabWhitePoint.Y, zn = this.HunterLabWhitePoint.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/> /// <inheritdoc/>
public LinearRgb Convert(CieXyz input) public LinearRgb Convert(CieXyz input)
{ {
DebugGuard.NotNull(input, nameof(input));
Matrix4x4.Invert(this.conversionMatrix, out Matrix4x4 inverted); Matrix4x4.Invert(this.conversionMatrix, out Matrix4x4 inverted);
Vector3 vector = Vector3.Transform(input.Vector, inverted); Vector3 vector = Vector3.Transform(input.Vector, inverted);
return new LinearRgb(vector, this.TargetWorkingSpace); 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/> /// <inheritdoc/>
public CieXyz Convert(LinearRgb input) 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."); 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); 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/> /// <inheritdoc/>
public Rgb Convert(LinearRgb input) public Rgb Convert(LinearRgb input)
{ {
DebugGuard.NotNull(input, nameof(input));
Vector3 vector = input.Vector; Vector3 vector = input.Vector;
vector.X = input.WorkingSpace.Companding.Compress(vector.X); vector.X = input.WorkingSpace.Companding.Compress(vector.X);
vector.Y = input.WorkingSpace.Companding.Compress(vector.Y); 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)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public Rgb Convert(YCbCr input) public Rgb Convert(YCbCr input)
{ {
DebugGuard.NotNull(input, nameof(input));
float y = input.Y; float y = input.Y;
float cb = input.Cb - 128F; float cb = input.Cb - 128F;
float cr = input.Cr - 128F; float cr = input.Cr - 128F;
@ -36,8 +34,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.YCbCrColorS
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public YCbCr Convert(Rgb input) public YCbCr Convert(Rgb input)
{ {
DebugGuard.NotNull(input, nameof(input));
Vector3 rgb = input.Vector * MaxBytes; Vector3 rgb = input.Vector * MaxBytes;
float r = rgb.X; float r = rgb.X;
float g = rgb.Y; float g = rgb.Y;

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

@ -1,7 +1,7 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System.Buffers; using System;
using System.IO; using System.IO;
namespace SixLabors.ImageSharp namespace SixLabors.ImageSharp
@ -11,6 +11,33 @@ namespace SixLabors.ImageSharp
/// </summary> /// </summary>
internal static class StreamExtensions 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> /// <summary>
/// Skips the number of bytes in the given stream. /// Skips the number of bytes in the given stream.
/// </summary> /// </summary>

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

@ -153,8 +153,8 @@ namespace SixLabors.ImageSharp
{ {
int width = bitmap.Width; int width = bitmap.Width;
int height = bitmap.Height; int height = bitmap.Height;
var topLeft = default(Point); Point topLeft = default;
var bottomRight = default(Point); Point bottomRight = default;
Func<ImageFrame<TPixel>, int, int, float, bool> delegateFunc; 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. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Buffers.Binary;
using System.IO; using System.IO;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.MetaData; 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) private void ReadRle8<TPixel>(Buffer2D<TPixel> pixels, byte[] colors, int width, int height, bool inverted)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
var color = default(TPixel); TPixel color = default;
var rgba = new Rgba32(0, 0, 0, 255); var rgba = new Rgba32(0, 0, 0, 255);
using (Buffer2D<byte> buffer = this.memoryAllocator.AllocateClean2D<byte>(width, height)) 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> /// <param name="buffer">Buffer for uncompressed data.</param>
private void UncompressRle8(int w, Span<byte> buffer) private void UncompressRle8(int w, Span<byte> buffer)
{ {
#if NETCOREAPP2_1
Span<byte> cmd = stackalloc byte[2];
#else
byte[] cmd = new byte[2]; byte[] cmd = new byte[2];
#endif
int count = 0; int count = 0;
while (count < buffer.Length) while (count < buffer.Length)
@ -392,7 +397,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
{ {
int padding = CalculatePadding(width, 2); int padding = CalculatePadding(width, 2);
int stride = (width * 2) + padding; int stride = (width * 2) + padding;
var color = default(TPixel); TPixel color = default;
var rgba = new Rgba32(0, 0, 0, 255); var rgba = new Rgba32(0, 0, 0, 255);
using (IManagedByteBuffer buffer = this.memoryAllocator.AllocateManagedByteBuffer(stride)) using (IManagedByteBuffer buffer = this.memoryAllocator.AllocateManagedByteBuffer(stride))
@ -475,12 +480,14 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// </summary> /// </summary>
private void ReadInfoHeader() private void ReadInfoHeader()
{ {
#if NETCOREAPP2_1
Span<byte> buffer = stackalloc byte[BmpInfoHeader.MaxHeaderSize];
#else
byte[] buffer = new byte[BmpInfoHeader.MaxHeaderSize]; byte[] buffer = new byte[BmpInfoHeader.MaxHeaderSize];
#endif
this.stream.Read(buffer, 0, BmpInfoHeader.HeaderSizeSize); // read the header size
// read header size int headerSize = BinaryPrimitives.ReadInt32LittleEndian(buffer);
this.stream.Read(buffer, 0, BmpInfoHeader.HeaderSizeSize);
int headerSize = BitConverter.ToInt32(buffer, 0);
if (headerSize < BmpInfoHeader.CoreSize) if (headerSize < BmpInfoHeader.CoreSize)
{ {
throw new NotSupportedException($"ImageSharp does not support this BMP file. HeaderSize: {headerSize}."); 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) else if (headerSize >= BmpInfoHeader.Size)
{ {
// >= 40 bytes // >= 40 bytes
this.infoHeader = BmpInfoHeader.Parse(buffer.AsSpan(0, 40)); this.infoHeader = BmpInfoHeader.Parse(buffer);
} }
else else
{ {
@ -520,8 +527,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// </summary> /// </summary>
private void ReadFileHeader() private void ReadFileHeader()
{ {
#if NETCOREAPP2_1
Span<byte> buffer = stackalloc byte[BmpFileHeader.Size];
#else
byte[] buffer = new byte[BmpFileHeader.Size]; byte[] buffer = new byte[BmpFileHeader.Size];
#endif
this.stream.Read(buffer, 0, BmpFileHeader.Size); this.stream.Read(buffer, 0, BmpFileHeader.Size);
this.fileHeader = BmpFileHeader.Parse(buffer); this.fileHeader = BmpFileHeader.Parse(buffer);

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

@ -66,8 +66,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp
reserved: 0, reserved: 0,
fileSize: 54 + infoHeader.ImageSize); 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); fileHeader.WriteTo(buffer);
stream.Write(buffer, 0, BmpFileHeader.Size); stream.Write(buffer, 0, BmpFileHeader.Size);

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

@ -16,12 +16,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <inheritdoc/> /// <inheritdoc/>
public IImageFormat DetectFormat(ReadOnlySpan<byte> header) public IImageFormat DetectFormat(ReadOnlySpan<byte> header)
{ {
if (this.IsSupportedFileFormat(header)) return this.IsSupportedFileFormat(header) ? ImageFormats.Bmp : null;
{
return ImageFormats.Bmp;
}
return null;
} }
private bool IsSupportedFileFormat(ReadOnlySpan<byte> header) 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"/> /// <seealso href="https://msdn.microsoft.com/en-us/library/windows/desktop/dd183376.aspx"/>
public static BmpInfoHeader Parse(ReadOnlySpan<byte> data) 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]; return MemoryMarshal.Cast<byte, BmpInfoHeader>(data)[0];
} }

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

@ -38,7 +38,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <inheritdoc/> /// <inheritdoc/>
public IImageInfo Identify(Configuration configuration, Stream stream) public IImageInfo Identify(Configuration configuration, Stream stream)
{ {
Guard.NotNull(stream, "stream"); Guard.NotNull(stream, nameof(stream));
var decoder = new GifDecoderCore(configuration, this); var decoder = new GifDecoderCore(configuration, this);
return decoder.Identify(stream); 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; Unsafe.Add(ref suffixRef, code) = (byte)code;
} }
#if NETCOREAPP2_1
Span<byte> buffer = stackalloc byte[255];
#else
byte[] buffer = new byte[255]; byte[] buffer = new byte[255];
#endif
while (xyz < length) while (xyz < length)
{ {
if (top == 0) if (top == 0)
@ -221,15 +226,21 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// The <see cref="int"/>. /// The <see cref="int"/>.
/// </returns> /// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
#if NETCOREAPP2_1
private int ReadBlock(Span<byte> buffer)
#else
private int ReadBlock(byte[] buffer) private int ReadBlock(byte[] buffer)
#endif
{ {
int bufferSize = this.stream.ReadByte(); int bufferSize = this.stream.ReadByte();
if (bufferSize < 1) if (bufferSize < 1)
{ {
return 0; return 0;
} }
int count = this.stream.Read(buffer, 0, bufferSize); int count = this.stream.Read(buffer, 0, bufferSize);
return count != bufferSize ? 0 : bufferSize; return count != bufferSize ? 0 : bufferSize;
} }

2
src/ImageSharp/Formats/IImageEncoder.cs

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

2
src/ImageSharp/Formats/IImageFormatDetector.cs

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

23
src/ImageSharp/Formats/ImageFormatManager.cs

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

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

@ -184,7 +184,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// </summary> /// </summary>
public Block8x8F AsFloatBlock() public Block8x8F AsFloatBlock()
{ {
var result = default(Block8x8F); Block8x8F result = default;
result.LoadFrom(ref this); result.LoadFrom(ref this);
return result; 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) public static Block8x8F Load(Span<float> data)
{ {
var result = default(Block8x8F); Block8x8F result = default;
result.LoadFrom(data); result.LoadFrom(data);
return result; return result;
} }
public static Block8x8F Load(Span<int> data) public static Block8x8F Load(Span<int> data)
{ {
var result = default(Block8x8F); Block8x8F result = default;
result.LoadFrom(data); result.LoadFrom(data);
return result; return result;
} }
@ -461,7 +461,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
public Block8x8 RoundAsInt16Block() public Block8x8 RoundAsInt16Block()
{ {
var result = default(Block8x8); Block8x8 result = default;
this.RoundInto(ref result); this.RoundInto(ref result);
return 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: // Walking 8 elements at one step:
int n = result.Length / 8; int n = result.Length / 8;
var rr = default(Vector4Pair); Vector4Pair rr = default;
var gg = default(Vector4Pair); Vector4Pair gg = default;
var bb = default(Vector4Pair); Vector4Pair bb = default;
ref Vector<float> rrRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref rr); ref Vector<float> rrRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref rr);
ref Vector<float> ggRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref gg); 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. /// Returns the <see cref="JpegColorConverter"/> for the YCbCr colorspace that matches the current CPU architecture.
/// </summary> /// </summary>
private static JpegColorConverter GetYCbCrConverter() => private static JpegColorConverter GetYCbCrConverter() =>
JpegColorConverter.FromYCbCrSimdAvx2.IsAvailable ? (JpegColorConverter)new JpegColorConverter.FromYCbCrSimdAvx2() : new JpegColorConverter.FromYCbCrSimd(); FromYCbCrSimdAvx2.IsAvailable ? (JpegColorConverter)new FromYCbCrSimdAvx2() : new FromYCbCrSimd();
/// <summary> /// <summary>
/// A stack-only struct to reference the input buffers using <see cref="ReadOnlySpan{T}"/>-s. /// 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="bytesToCheck">The bytes to check</param>
/// <param name="profileIdentifier">The profile identifier</param> /// <param name="profileIdentifier">The profile identifier</param>
/// <returns>The <see cref="bool"/></returns> /// <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 return bytesToCheck.Length >= profileIdentifier.Length
&& bytesToCheck.Slice(0, profileIdentifier.Length).SequenceEqual(profileIdentifier); && 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> /// <returns>The <see cref="ComputationData"/></returns>
public static ComputationData Create() public static ComputationData Create()
{ {
var data = default(ComputationData); ComputationData data = default;
data.Unzig = ZigZag.CreateUnzigTable(); data.Unzig = ZigZag.CreateUnzigTable();
return data; return data;
} }

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

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

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

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

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

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

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

@ -270,6 +270,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
} }
case JpegConstants.Markers.DHT: case JpegConstants.Markers.DHT:
if (metadataOnly) if (metadataOnly)
{ {
this.InputStream.Skip(remaining); 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> /// <param name="remaining">The remaining bytes in the segment block.</param>
private void ProcessDefineHuffmanTablesMarker(int remaining) private void ProcessDefineHuffmanTablesMarker(int remaining)
{ {
if (remaining < 17)
{
throw new ImageFormatException($"DHT has wrong length: {remaining}");
}
using (IManagedByteBuffer huffmanData = this.configuration.MemoryAllocator.AllocateCleanManagedByteBuffer(256)) using (IManagedByteBuffer huffmanData = this.configuration.MemoryAllocator.AllocateCleanManagedByteBuffer(256))
{ {
ref byte huffmanDataRef = ref MemoryMarshal.GetReference(huffmanData.GetSpan()); ref byte huffmanDataRef = ref MemoryMarshal.GetReference(huffmanData.GetSpan());
@ -790,7 +786,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
int spectralStart = this.temp[0]; int spectralStart = this.temp[0];
int spectralEnd = this.temp[1]; int spectralEnd = this.temp[1];
int successiveApproximation = this.temp[2]; int successiveApproximation = this.temp[2];
var scanDecoder = default(PdfJsScanDecoder); PdfJsScanDecoder scanDecoder = default;
scanDecoder.DecodeScan( scanDecoder.DecodeScan(
this.Frame, 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) private void ProcessDefilteredScanline<TPixel>(ReadOnlySpan<byte> defilteredScanline, ImageFrame<TPixel> pixels)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
var color = default(TPixel); TPixel color = default;
Span<TPixel> rowSpan = pixels.GetPixelRowSpan(this.currentRow); Span<TPixel> rowSpan = pixels.GetPixelRowSpan(this.currentRow);
// Trim the first marker byte from the buffer // 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++) for (int x = 0; x < this.header.Width; x++)
{ {
ref Rgb24 rgb24 = ref rgb24Span[x]; ref Rgb24 rgb24 = ref rgb24Span[x];
var rgba32 = default(Rgba32); Rgba32 rgba32 = default;
rgba32.Rgb = rgb24; rgba32.Rgb = rgb24;
rgba32.A = (byte)(rgb24.Equals(this.rgb24Trans) ? 0 : 255); 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++) for (int x = 0; x < this.header.Width; x++)
{ {
ref readonly Rgb24 rgb24 = ref rgb24Span[x]; ref readonly Rgb24 rgb24 = ref rgb24Span[x];
var rgba32 = default(Rgba32); Rgba32 rgba32 = default;
rgba32.Rgb = rgb24; rgba32.Rgb = rgb24;
rgba32.A = (byte)(rgb24.Equals(this.rgb24Trans) ? 0 : 255); rgba32.A = (byte)(rgb24.Equals(this.rgb24Trans) ? 0 : 255);
@ -829,7 +829,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <summary> /// <summary>
/// Decodes and assigns marker colors that identify transparent pixels in non indexed images /// Decodes and assigns marker colors that identify transparent pixels in non indexed images
/// </summary> /// </summary>
/// <param name="alpha">The aplha tRNS array</param> /// <param name="alpha">The alpha tRNS array</param>
private void AssignTransparentMarkers(byte[] alpha) private void AssignTransparentMarkers(byte[] alpha)
{ {
if (this.pngColorType == PngColorType.Rgb) if (this.pngColorType == PngColorType.Rgb)
@ -858,15 +858,14 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of pixel we are expanding to</typeparam> /// <typeparam name="TPixel">The type of pixel we are expanding to</typeparam>
/// <param name="defilteredScanline">The scanline</param> /// <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) private void ProcessScanlineFromPalette<TPixel>(ReadOnlySpan<byte> defilteredScanline, Span<TPixel> row)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
ReadOnlySpan<byte> newScanline = ToArrayByBitsLength(defilteredScanline, this.bytesPerScanline, this.header.BitDepth); ReadOnlySpan<byte> newScanline = ToArrayByBitsLength(defilteredScanline, this.bytesPerScanline, this.header.BitDepth);
ReadOnlySpan<Rgb24> pal = MemoryMarshal.Cast<byte, Rgb24>(this.palette); ReadOnlySpan<Rgb24> pal = MemoryMarshal.Cast<byte, Rgb24>(this.palette);
var color = default(TPixel); TPixel color = default;
Rgba32 rgba = default;
var rgba = default(Rgba32);
if (this.paletteAlpha != null && this.paletteAlpha.Length > 0) 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) private void ProcessInterlacedDefilteredScanline<TPixel>(ReadOnlySpan<byte> defilteredScanline, Span<TPixel> rowSpan, int pixelOffset = 0, int increment = 1)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
var color = default(TPixel); TPixel color = default;
// Trim the first marker byte from the buffer // Trim the first marker byte from the buffer
ReadOnlySpan<byte> scanlineBuffer = defilteredScanline.Slice(1, defilteredScanline.Length - 1); ReadOnlySpan<byte> scanlineBuffer = defilteredScanline.Slice(1, defilteredScanline.Length - 1);
@ -953,7 +952,7 @@ namespace SixLabors.ImageSharp.Formats.Png
case PngColorType.Palette: case PngColorType.Palette:
ReadOnlySpan<byte> newScanline = ToArrayByBitsLength(scanlineBuffer, this.bytesPerScanline, this.header.BitDepth); 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); Span<Rgb24> pal = MemoryMarshal.Cast<byte, Rgb24>(this.palette);
if (this.paletteAlpha != null && this.paletteAlpha.Length > 0) 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> where TPixel : struct, IPixel<TPixel>
{ {
byte[] rawScanlineArray = this.rawScanline.Array; byte[] rawScanlineArray = this.rawScanline.Array;
var rgba = default(Rgba32); Rgba32 rgba = default;
// Copy the pixels across from the image. // Copy the pixels across from the image.
// Reuse the chunk type buffer. // Reuse the chunk type buffer.
@ -306,8 +306,9 @@ namespace SixLabors.ImageSharp.Formats.Png
switch (this.pngColorType) switch (this.pngColorType)
{ {
case PngColorType.Palette: case PngColorType.Palette:
// TODO: Use Span copy! int stride = this.rawScanline.Length();
Buffer.BlockCopy(this.palettePixelData, row * this.rawScanline.Length(), this.rawScanline.Array, 0, this.rawScanline.Length());
this.palettePixelData.AsSpan(row * stride, stride).CopyTo(this.rawScanline.GetSpan());
break; break;
case PngColorType.Grayscale: case PngColorType.Grayscale:
case PngColorType.GrayscaleWithAlpha: case PngColorType.GrayscaleWithAlpha:

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

@ -17,12 +17,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <inheritdoc/> /// <inheritdoc/>
public IImageFormat DetectFormat(ReadOnlySpan<byte> header) public IImageFormat DetectFormat(ReadOnlySpan<byte> header)
{ {
if (this.IsSupportedFileFormat(header)) return this.IsSupportedFileFormat(header) ? ImageFormats.Png : null;
{
return ImageFormats.Png;
}
return null;
} }
private bool IsSupportedFileFormat(ReadOnlySpan<byte> header) 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) private static IImageDecoder DiscoverDecoder(Stream stream, Configuration config, out IImageFormat format)
{ {
format = InternalDetectFormat(stream, config); 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 #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) != ''">$(packageversion)</VersionPrefix>
<VersionPrefix Condition="$(packageversion) == ''">0.0.1</VersionPrefix> <VersionPrefix Condition="$(packageversion) == ''">0.0.1</VersionPrefix>
<Authors>Six Labors and contributors</Authors> <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> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<AssemblyName>SixLabors.ImageSharp</AssemblyName> <AssemblyName>SixLabors.ImageSharp</AssemblyName>
@ -29,7 +29,7 @@
<DebugType Condition="$(codecov) == ''">portable</DebugType> <DebugType Condition="$(codecov) == ''">portable</DebugType>
<DebugSymbols>True</DebugSymbols> <DebugSymbols>True</DebugSymbols>
<Features>IOperation</Features> <Features>IOperation</Features>
<LangVersion>7.2</LangVersion> <LangVersion>7.3</LangVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Include="..\Shared\*.cs" Exclude="bin\**;obj\**;**\*.xproj;packages\**" /> <Compile Include="..\Shared\*.cs" Exclude="bin\**;obj\**;**\*.xproj;packages\**" />
@ -40,13 +40,21 @@
<PackageReference Include="StyleCop.Analyzers" Version="1.1.0-beta007"> <PackageReference Include="StyleCop.Analyzers" Version="1.1.0-beta007">
<PrivateAssets>All</PrivateAssets> <PrivateAssets>All</PrivateAssets>
</PackageReference> </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.Buffers" Version="4.5.0" />
<PackageReference Include="System.Memory" Version="4.5.0" /> <PackageReference Include="System.Memory" Version="4.5.1" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.5.0" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' "> <ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' ">
<PackageReference Include="System.IO.UnmanagedMemoryStream" Version="4.3.0" /> <PackageReference Include="System.IO.UnmanagedMemoryStream" Version="4.3.0" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.1' OR '$(TargetFramework)' == 'netstandard1.3'"> <ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.1' OR '$(TargetFramework)' == 'netstandard1.3'">
<PackageReference Include="System.IO.Compression" Version="4.3.0" /> <PackageReference Include="System.IO.Compression" Version="4.3.0" />
<PackageReference Include="System.Threading.Tasks.Parallel" 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. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Gif;
namespace SixLabors.ImageSharp.MetaData 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) private unsafe string ConvertToString(ReadOnlySpan<byte> buffer)
{ {
#if NETSTANDARD1_1 Span<byte> nullChar = stackalloc byte[1] { 0 };
byte[] bytes = buffer.ToArray();
string result = Encoding.UTF8.GetString(bytes, 0, buffer.Length); int nullCharIndex = buffer.IndexOf(nullChar);
#else if (nullCharIndex > -1)
string result; {
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)) fixed (byte* pointer = &MemoryMarshal.GetReference(buffer))
{ {
result = Encoding.UTF8.GetString(pointer, buffer.Length); return Encoding.UTF8.GetString(pointer, buffer.Length);
} }
#endif #endif
int nullCharIndex = result.IndexOf('\0');
if (nullCharIndex != -1)
{
result = result.Substring(0, nullCharIndex);
}
return result;
} }
/// <summary> /// <summary>

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

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

2
src/ImageSharp/PixelFormats/Bgr24.cs

@ -121,7 +121,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromVector4(Vector4 vector) public void PackFromVector4(Vector4 vector)
{ {
var rgba = default(Rgba32); Rgba32 rgba = default;
rgba.PackFromVector4(vector); rgba.PackFromVector4(vector);
this.PackFromRgba32(rgba); 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) public static TPixel Normal<TPixel>(TPixel backdrop, TPixel source, float amount)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel dest = default(TPixel); TPixel dest = default;
dest.PackFromVector4(Normal(backdrop.ToVector4(), source.ToVector4(), amount)); dest.PackFromVector4(Normal(backdrop.ToVector4(), source.ToVector4(), amount));
return dest; return dest;
} }
@ -270,7 +270,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
public static TPixel Multiply<TPixel>(TPixel backdrop, TPixel source, float amount) public static TPixel Multiply<TPixel>(TPixel backdrop, TPixel source, float amount)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel dest = default(TPixel); TPixel dest = default;
dest.PackFromVector4(Multiply(backdrop.ToVector4(), source.ToVector4(), amount)); dest.PackFromVector4(Multiply(backdrop.ToVector4(), source.ToVector4(), amount));
return dest; return dest;
} }
@ -279,7 +279,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
public static TPixel Add<TPixel>(TPixel backdrop, TPixel source, float amount) public static TPixel Add<TPixel>(TPixel backdrop, TPixel source, float amount)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel dest = default(TPixel); TPixel dest = default;
dest.PackFromVector4(Add(backdrop.ToVector4(), source.ToVector4(), amount)); dest.PackFromVector4(Add(backdrop.ToVector4(), source.ToVector4(), amount));
return dest; return dest;
} }
@ -288,7 +288,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
public static TPixel Subtract<TPixel>(TPixel backdrop, TPixel source, float amount) public static TPixel Subtract<TPixel>(TPixel backdrop, TPixel source, float amount)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel dest = default(TPixel); TPixel dest = default;
dest.PackFromVector4(Subtract(backdrop.ToVector4(), source.ToVector4(), amount)); dest.PackFromVector4(Subtract(backdrop.ToVector4(), source.ToVector4(), amount));
return dest; return dest;
} }
@ -297,7 +297,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
public static TPixel Screen<TPixel>(TPixel backdrop, TPixel source, float amount) public static TPixel Screen<TPixel>(TPixel backdrop, TPixel source, float amount)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel dest = default(TPixel); TPixel dest = default;
dest.PackFromVector4(Screen(backdrop.ToVector4(), source.ToVector4(), amount)); dest.PackFromVector4(Screen(backdrop.ToVector4(), source.ToVector4(), amount));
return dest; return dest;
} }
@ -306,7 +306,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
public static TPixel Darken<TPixel>(TPixel backdrop, TPixel source, float amount) public static TPixel Darken<TPixel>(TPixel backdrop, TPixel source, float amount)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel dest = default(TPixel); TPixel dest = default;
dest.PackFromVector4(Darken(backdrop.ToVector4(), source.ToVector4(), amount)); dest.PackFromVector4(Darken(backdrop.ToVector4(), source.ToVector4(), amount));
return dest; return dest;
} }
@ -315,7 +315,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
public static TPixel Lighten<TPixel>(TPixel backdrop, TPixel source, float amount) public static TPixel Lighten<TPixel>(TPixel backdrop, TPixel source, float amount)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel dest = default(TPixel); TPixel dest = default;
dest.PackFromVector4(Lighten(backdrop.ToVector4(), source.ToVector4(), amount)); dest.PackFromVector4(Lighten(backdrop.ToVector4(), source.ToVector4(), amount));
return dest; return dest;
} }
@ -324,7 +324,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
public static TPixel Overlay<TPixel>(TPixel backdrop, TPixel source, float amount) public static TPixel Overlay<TPixel>(TPixel backdrop, TPixel source, float amount)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel dest = default(TPixel); TPixel dest = default;
dest.PackFromVector4(Overlay(backdrop.ToVector4(), source.ToVector4(), amount)); dest.PackFromVector4(Overlay(backdrop.ToVector4(), source.ToVector4(), amount));
return dest; return dest;
} }
@ -333,7 +333,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
public static TPixel HardLight<TPixel>(TPixel backdrop, TPixel source, float amount) public static TPixel HardLight<TPixel>(TPixel backdrop, TPixel source, float amount)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel dest = default(TPixel); TPixel dest = default;
dest.PackFromVector4(HardLight(backdrop.ToVector4(), source.ToVector4(), amount)); dest.PackFromVector4(HardLight(backdrop.ToVector4(), source.ToVector4(), amount));
return dest; return dest;
} }
@ -342,7 +342,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
public static TPixel Src<TPixel>(TPixel backdrop, TPixel source, float amount) public static TPixel Src<TPixel>(TPixel backdrop, TPixel source, float amount)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel dest = default(TPixel); TPixel dest = default;
dest.PackFromVector4(Src(backdrop.ToVector4(), source.ToVector4(), amount)); dest.PackFromVector4(Src(backdrop.ToVector4(), source.ToVector4(), amount));
return dest; return dest;
} }
@ -351,7 +351,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
public static TPixel Atop<TPixel>(TPixel backdrop, TPixel source, float amount) public static TPixel Atop<TPixel>(TPixel backdrop, TPixel source, float amount)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel dest = default(TPixel); TPixel dest = default;
dest.PackFromVector4(Atop(backdrop.ToVector4(), source.ToVector4(), amount)); dest.PackFromVector4(Atop(backdrop.ToVector4(), source.ToVector4(), amount));
return dest; return dest;
} }
@ -360,7 +360,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
public static TPixel Over<TPixel>(TPixel backdrop, TPixel source, float amount) public static TPixel Over<TPixel>(TPixel backdrop, TPixel source, float amount)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel dest = default(TPixel); TPixel dest = default;
dest.PackFromVector4(Over(backdrop.ToVector4(), source.ToVector4(), amount)); dest.PackFromVector4(Over(backdrop.ToVector4(), source.ToVector4(), amount));
return dest; return dest;
} }
@ -369,7 +369,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
public static TPixel In<TPixel>(TPixel backdrop, TPixel source, float amount) public static TPixel In<TPixel>(TPixel backdrop, TPixel source, float amount)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel dest = default(TPixel); TPixel dest = default;
dest.PackFromVector4(In(backdrop.ToVector4(), source.ToVector4(), amount)); dest.PackFromVector4(In(backdrop.ToVector4(), source.ToVector4(), amount));
return dest; return dest;
} }
@ -378,7 +378,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
public static TPixel Out<TPixel>(TPixel backdrop, TPixel source, float amount) public static TPixel Out<TPixel>(TPixel backdrop, TPixel source, float amount)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel dest = default(TPixel); TPixel dest = default;
dest.PackFromVector4(Out(backdrop.ToVector4(), source.ToVector4(), amount)); dest.PackFromVector4(Out(backdrop.ToVector4(), source.ToVector4(), amount));
return dest; return dest;
} }
@ -387,7 +387,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
public static TPixel Dest<TPixel>(TPixel backdrop, TPixel source, float amount) public static TPixel Dest<TPixel>(TPixel backdrop, TPixel source, float amount)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel dest = default(TPixel); TPixel dest = default;
dest.PackFromVector4(Dest(backdrop.ToVector4(), source.ToVector4(), amount)); dest.PackFromVector4(Dest(backdrop.ToVector4(), source.ToVector4(), amount));
return dest; return dest;
} }
@ -396,7 +396,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
public static TPixel DestAtop<TPixel>(TPixel backdrop, TPixel source, float amount) public static TPixel DestAtop<TPixel>(TPixel backdrop, TPixel source, float amount)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel dest = default(TPixel); TPixel dest = default;
dest.PackFromVector4(DestAtop(backdrop.ToVector4(), source.ToVector4(), amount)); dest.PackFromVector4(DestAtop(backdrop.ToVector4(), source.ToVector4(), amount));
return dest; return dest;
} }
@ -405,7 +405,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
public static TPixel DestOver<TPixel>(TPixel backdrop, TPixel source, float amount) public static TPixel DestOver<TPixel>(TPixel backdrop, TPixel source, float amount)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel dest = default(TPixel); TPixel dest = default;
dest.PackFromVector4(DestOver(backdrop.ToVector4(), source.ToVector4(), amount)); dest.PackFromVector4(DestOver(backdrop.ToVector4(), source.ToVector4(), amount));
return dest; return dest;
} }
@ -414,7 +414,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
public static TPixel DestIn<TPixel>(TPixel backdrop, TPixel source, float amount) public static TPixel DestIn<TPixel>(TPixel backdrop, TPixel source, float amount)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel dest = default(TPixel); TPixel dest = default;
dest.PackFromVector4(DestIn(backdrop.ToVector4(), source.ToVector4(), amount)); dest.PackFromVector4(DestIn(backdrop.ToVector4(), source.ToVector4(), amount));
return dest; return dest;
} }
@ -423,7 +423,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
public static TPixel DestOut<TPixel>(TPixel backdrop, TPixel source, float amount) public static TPixel DestOut<TPixel>(TPixel backdrop, TPixel source, float amount)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel dest = default(TPixel); TPixel dest = default;
dest.PackFromVector4(DestOut(backdrop.ToVector4(), source.ToVector4(), amount)); dest.PackFromVector4(DestOut(backdrop.ToVector4(), source.ToVector4(), amount));
return dest; return dest;
} }
@ -432,7 +432,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
public static TPixel Clear<TPixel>(TPixel backdrop, TPixel source, float amount) public static TPixel Clear<TPixel>(TPixel backdrop, TPixel source, float amount)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel dest = default(TPixel); TPixel dest = default;
dest.PackFromVector4(Clear(backdrop.ToVector4(), source.ToVector4(), amount)); dest.PackFromVector4(Clear(backdrop.ToVector4(), source.ToVector4(), amount));
return dest; return dest;
} }
@ -441,7 +441,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
public static TPixel Xor<TPixel>(TPixel backdrop, TPixel source, float amount) public static TPixel Xor<TPixel>(TPixel backdrop, TPixel source, float amount)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel dest = default(TPixel); TPixel dest = default;
dest.PackFromVector4(Xor(backdrop.ToVector4(), source.ToVector4(), amount)); dest.PackFromVector4(Xor(backdrop.ToVector4(), source.ToVector4(), amount));
return dest; 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) public static TPixel <#=blender#><TPixel>(TPixel backdrop, TPixel source, float amount)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel dest = default(TPixel); TPixel dest = default;
dest.PackFromVector4(<#=blender#>(backdrop.ToVector4(), source.ToVector4(), amount)); dest.PackFromVector4(<#=blender#>(backdrop.ToVector4(), source.ToVector4(), amount));
return dest; return dest;
} }

2
src/ImageSharp/PixelFormats/Rgb24.cs

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

2
src/ImageSharp/PixelFormats/Rgba1010102.cs

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

2
src/ImageSharp/PixelFormats/Rgba32.cs

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

2
src/ImageSharp/PixelFormats/Rgba64.cs

@ -187,7 +187,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc /> /// <inheritdoc />
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
return (obj is Rgba64) && this.Equals((Rgba64)obj); return obj is Rgba64 other && this.Equals(other);
} }
/// <inheritdoc /> /// <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> /// <summary>
/// Gets or sets the item at the specified position. /// Gets or sets the item at the specified position.
/// </summary> /// </summary>
@ -146,19 +151,13 @@ namespace SixLabors.ImageSharp.Primitives
/// </summary> /// </summary>
/// <param name="value">The value to fill each item with</param> /// <param name="value">The value to fill each item with</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Fill(T value) public void Fill(T value) => this.Span.Fill(value);
{
for (int i = 0; i < this.Data.Length; i++)
{
this.Data[i] = value;
}
}
/// <summary> /// <summary>
/// Clears the matrix setting each value to the default value for the element type /// Clears the matrix setting each value to the default value for the element type
/// </summary> /// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear() => Array.Clear(this.Data, 0, this.Data.Length); public void Clear() => this.Span.Clear();
/// <summary> /// <summary>
/// Checks the coordinates to ensure they are within bounds. /// Checks the coordinates to ensure they are within bounds.
@ -183,28 +182,10 @@ namespace SixLabors.ImageSharp.Primitives
} }
/// <inheritdoc/> /// <inheritdoc/>
public bool Equals(DenseMatrix<T> other) public bool Equals(DenseMatrix<T> other) =>
{ this.Columns == other.Columns &&
if (this.Columns != other.Columns) this.Rows == other.Rows &&
{ this.Span.SequenceEqual(other.Span);
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;
}
/// <inheritdoc/> /// <inheritdoc/>
public override bool Equals(object obj) => obj is DenseMatrix<T> other && this.Equals(other); 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) protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
{ {
float threshold = this.Threshold * 255F; float threshold = this.Threshold * 255F;
var rgba = default(Rgba32); Rgba32 rgba = default;
bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8); bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8);
var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); 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/> /// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration) protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
{ {
var rgba = default(Rgba32); Rgba32 rgba = default;
bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8); bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8);
var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); 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 => y =>
{ {
Span<TPixel> row = source.GetPixelRowSpan(y); Span<TPixel> row = source.GetPixelRowSpan(y);
var rgba = default(Rgba32); Rgba32 rgba = default;
for (int x = startX; x < endX; x++) 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++) 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. // Apply each matrix multiplier to the color components for each pixel.
for (int fy = 0; fy < kernelHeight; fy++) 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) protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
{ {
float threshold = this.Threshold * 255F; float threshold = this.Threshold * 255F;
var rgba = default(Rgba32); Rgba32 rgba = default;
bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8); bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8);
var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); 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/> /// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration) protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
{ {
var rgba = default(Rgba32); Rgba32 rgba = default;
bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8); bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8);
var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); 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; float secondLeastDistance = int.MaxValue;
var vector = pixel.ToVector4(); var vector = pixel.ToVector4();
var closest = default(TPixel); TPixel closest = default;
var secondClosest = default(TPixel); TPixel secondClosest = default;
for (int index = 0; index < colorPalette.Length; index++) for (int index = 0; index < colorPalette.Length; index++)
{ {
TPixel temp = colorPalette[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); ref TPixel scanBaseRef = ref MemoryMarshal.GetReference(row);
// And loop through each column // And loop through each column
var rgba = default(Rgba32); Rgba32 rgba = default;
for (int x = 0; x < width; x++) for (int x = 0; x < width; x++)
{ {
ref TPixel pixel = ref Unsafe.Add(ref scanBaseRef, 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. // pass of the algorithm by avoiding transforming rows of identical color.
TPixel sourcePixel = source[0, 0]; TPixel sourcePixel = source[0, 0];
TPixel previousPixel = sourcePixel; TPixel previousPixel = sourcePixel;
var rgba = default(Rgba32); Rgba32 rgba = default;
byte pixelValue = this.QuantizePixel(sourcePixel, ref rgba); byte pixelValue = this.QuantizePixel(sourcePixel, ref rgba);
TPixel[] colorPalette = this.GetPalette(); TPixel[] colorPalette = this.GetPalette();
TPixel transformedPixel = colorPalette[pixelValue]; 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 // Transparent pixels are much more likely to be found at the end of a palette
int index = this.colors; int index = this.colors;
var trans = default(Rgba32); Rgba32 trans = default;
for (int i = this.palette.Length - 1; i >= 0; i--) for (int i = this.palette.Length - 1; i >= 0; i--)
{ {
this.palette[i].ToRgba32(ref trans); this.palette[i].ToRgba32(ref trans);
@ -539,7 +539,7 @@ namespace SixLabors.ImageSharp.Processing.Quantization.FrameQuantizers
byte b = (this.blue / this.pixelCount).ToByte(); byte b = (this.blue / this.pixelCount).ToByte();
// And set the color of the palette entry // And set the color of the palette entry
var pixel = default(TPixel); TPixel pixel = default;
pixel.PackFromRgba32(new Rgba32(r, g, b, 255)); pixel.PackFromRgba32(new Rgba32(r, g, b, 255));
palette[index] = pixel; 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); ref TPixel scanBaseRef = ref MemoryMarshal.GetReference(row);
// And loop through each column // And loop through each column
var rgba = default(Rgba32); Rgba32 rgba = default;
for (int x = 0; x < width; x++) for (int x = 0; x < width; x++)
{ {
ref TPixel pixel = ref Unsafe.Add(ref scanBaseRef, 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 // Expected order r->g->b->a
var rgba = default(Rgba32); Rgba32 rgba = default;
pixel.ToRgba32(ref rgba); pixel.ToRgba32(ref rgba);
int r = rgba.R >> (8 - IndexBits); int r = rgba.R >> (8 - IndexBits);

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

@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Processing.Transforms
/// <summary> /// <summary>
/// Contains helper methods for working with affine and non-affine transforms /// Contains helper methods for working with affine and non-affine transforms
/// </summary> /// </summary>
internal class TransformHelpers internal static class TransformHelpers
{ {
/// <summary> /// <summary>
/// Updates the dimensional metadata of a transformed image /// 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> <ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.10.14" /> <PackageReference Include="BenchmarkDotNet" Version="0.10.14" />
<PackageReference Include="Colourful" Version="1.2.2" /> <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" /> <PackageReference Include="System.Drawing.Common" Version="4.5.0" />
</ItemGroup> </ItemGroup>

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

@ -20,9 +20,9 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="BitMiracle.LibJpeg.NET" Version="1.4.280" /> <PackageReference Include="BitMiracle.LibJpeg.NET" Version="1.4.280" />
<PackageReference Include="xunit" Version="2.3.1" /> <PackageReference Include="xunit" Version="2.3.1" />
<PackageReference Include="Moq" Version="4.8.2" /> <PackageReference Include="Moq" Version="4.8.3" />
<!--<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0" />--> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.2" />
<!--<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />--> <PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="..\ImageSharp.Tests\**\*.cs" Exclude="bin\**;obj\**" Link="Tests\%(RecursiveDir)%(Filename)%(Extension)" /> <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;
using SixLabors.ImageSharp.Processing.Drawing; using SixLabors.ImageSharp.Processing.Drawing;
using SixLabors.ImageSharp.Processing.Overlays; using SixLabors.ImageSharp.Processing.Overlays;
using SixLabors.Memory;
using Xunit; using Xunit;
namespace SixLabors.ImageSharp.Tests.Drawing namespace SixLabors.ImageSharp.Tests.Drawing
{ {
using SixLabors.Memory;
public class Beziers : FileTestBase public class Beziers : FileTestBase
{ {
[Fact] [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.Pens;
using SixLabors.ImageSharp.Processing.Drawing.Processors; using SixLabors.ImageSharp.Processing.Drawing.Processors;
using SixLabors.ImageSharp.Processing.Text; using SixLabors.ImageSharp.Processing.Text;
using SixLabors.ImageSharp.Processing.Text.Processors;
using SixLabors.Primitives;
using SixLabors.Shapes; using SixLabors.Shapes;
using Xunit; using Xunit;
@ -44,9 +46,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
null, null,
Vector2.Zero); Vector2.Zero);
this.Verify<FillRegionProcessor<Rgba32>>(0); this.Verify<DrawTextProcessor<Rgba32>>(0);
this.Verify<FillRegionProcessor<Rgba32>>(1);
this.Verify<FillRegionProcessor<Rgba32>>(2);
} }
[Fact] [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.operations.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), null, Vector2.Zero);
this.Verify<FillRegionProcessor<Rgba32>>(0); this.Verify<DrawTextProcessor<Rgba32>>(0);
this.Verify<FillRegionProcessor<Rgba32>>(1);
this.Verify<FillRegionProcessor<Rgba32>>(2);
} }
[Fact] [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.operations.DrawText(new TextGraphicsOptions(true), "123", this.Font, Brushes.Solid(Rgba32.Red), Vector2.Zero);
this.Verify<FillRegionProcessor<Rgba32>>(0); this.Verify<DrawTextProcessor<Rgba32>>(0);
this.Verify<FillRegionProcessor<Rgba32>>(1);
this.Verify<FillRegionProcessor<Rgba32>>(2);
} }
[Fact] [Fact]
@ -74,9 +70,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
{ {
this.operations.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), Vector2.Zero); this.operations.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), Vector2.Zero);
this.Verify<FillRegionProcessor<Rgba32>>(0); this.Verify<DrawTextProcessor<Rgba32>>(0);
this.Verify<FillRegionProcessor<Rgba32>>(1);
this.Verify<FillRegionProcessor<Rgba32>>(2);
} }
[Fact] [Fact]
@ -84,9 +78,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
{ {
this.operations.DrawText(new TextGraphicsOptions(true), "123", this.Font, Rgba32.Red, Vector2.Zero); this.operations.DrawText(new TextGraphicsOptions(true), "123", this.Font, Rgba32.Red, Vector2.Zero);
var processor = this.Verify<FillRegionProcessor<Rgba32>>(0); var processor = this.Verify<DrawTextProcessor<Rgba32>>(0);
this.Verify<FillRegionProcessor<Rgba32>>(1);
this.Verify<FillRegionProcessor<Rgba32>>(2);
SolidBrush<Rgba32> brush = Assert.IsType<SolidBrush<Rgba32>>(processor.Brush); SolidBrush<Rgba32> brush = Assert.IsType<SolidBrush<Rgba32>>(processor.Brush);
Assert.Equal(Rgba32.Red, brush.Color); 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); this.operations.DrawText("123", this.Font, Rgba32.Red, Vector2.Zero);
var processor = this.Verify<FillRegionProcessor<Rgba32>>(0); var processor = this.Verify<DrawTextProcessor<Rgba32>>(0);
this.Verify<FillRegionProcessor<Rgba32>>(1);
this.Verify<FillRegionProcessor<Rgba32>>(2);
SolidBrush<Rgba32> brush = Assert.IsType<SolidBrush<Rgba32>>(processor.Brush); SolidBrush<Rgba32> brush = Assert.IsType<SolidBrush<Rgba32>>(processor.Brush);
Assert.Equal(Rgba32.Red, brush.Color); Assert.Equal(Rgba32.Red, brush.Color);
@ -116,9 +106,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
Pens.Dash(Rgba32.Red, 1), Pens.Dash(Rgba32.Red, 1),
Vector2.Zero); Vector2.Zero);
var processor = this.Verify<FillRegionProcessor<Rgba32>>(0); var processor = this.Verify<DrawTextProcessor<Rgba32>>(0);
this.Verify<FillRegionProcessor<Rgba32>>(1);
this.Verify<FillRegionProcessor<Rgba32>>(2);
} }
[Fact] [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); this.operations.DrawText("123", this.Font, null, Pens.Dash(Rgba32.Red, 1), Vector2.Zero);
var processor = this.Verify<FillRegionProcessor<Rgba32>>(0); var processor = this.Verify<DrawTextProcessor<Rgba32>>(0);
this.Verify<FillRegionProcessor<Rgba32>>(1);
this.Verify<FillRegionProcessor<Rgba32>>(2);
} }
[Fact] [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); this.operations.DrawText(new TextGraphicsOptions(true), "123", this.Font, Pens.Dash(Rgba32.Red, 1), Vector2.Zero);
var processor = this.Verify<FillRegionProcessor<Rgba32>>(0); var processor = this.Verify<DrawTextProcessor<Rgba32>>(0);
this.Verify<FillRegionProcessor<Rgba32>>(1);
this.Verify<FillRegionProcessor<Rgba32>>(2);
} }
[Fact] [Fact]
@ -146,9 +130,14 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
{ {
this.operations.DrawText("123", this.Font, Pens.Dash(Rgba32.Red, 1), Vector2.Zero); this.operations.DrawText("123", this.Font, Pens.Dash(Rgba32.Red, 1), Vector2.Zero);
var processor = this.Verify<FillRegionProcessor<Rgba32>>(0); var processor = this.Verify<DrawTextProcessor<Rgba32>>(0);
this.Verify<FillRegionProcessor<Rgba32>>(1);
this.Verify<FillRegionProcessor<Rgba32>>(2); 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] [Fact]
@ -162,50 +151,16 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
Pens.Dash(Rgba32.Red, 1), Pens.Dash(Rgba32.Red, 1),
Vector2.Zero); Vector2.Zero);
var processor = this.Verify<FillRegionProcessor<Rgba32>>(0); var processor = this.Verify<DrawTextProcessor<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);
}
[Fact] Assert.Equal("123", processor.Text);
public void BrushAppliesBeforPenDefaultOptions() Assert.Equal(this.Font, processor.Font);
{ var brush = Assert.IsType<SolidBrush<Rgba32>>(processor.Brush);
this.operations.DrawText("1", this.Font, Brushes.Solid(Rgba32.Red), Pens.Dash(Rgba32.Red, 1), Vector2.Zero); Assert.Equal(Rgba32.Red, brush.Color);
Assert.Equal(PointF.Empty, processor.Location);
var processor = this.Verify<FillRegionProcessor<Rgba32>>(0); var penBrush = Assert.IsType<SolidBrush<Rgba32>>(processor.Pen.StrokeFill);
this.Verify<FillRegionProcessor<Rgba32>>(1); 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. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System.Numerics; using System;
using System.Linq;
using System.Text;
using SixLabors.Fonts; using SixLabors.Fonts;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Drawing; using SixLabors.ImageSharp.Processing.Drawing.Pens;
using SixLabors.ImageSharp.Processing.Text; using SixLabors.ImageSharp.Processing.Text;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using SixLabors.Primitives;
using Xunit; using Xunit;
// ReSharper disable InconsistentNaming // ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests.Drawing.Text namespace SixLabors.ImageSharp.Tests.Drawing.Text
{ {
using System;
using System.Linq;
using System.Text;
using SixLabors.Primitives;
[GroupOutput("Drawing/Text")] [GroupOutput("Drawing/Text")]
public class DrawTextOnImageTests 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 TestText = "Sphinx of black quartz, judge my vow\n0123456789";
private const string TestText2 = public static ImageComparer TextDrawingComparer = ImageComparer.TolerantPercentage(0.01f);
"THISISTESTWORDS "; public static ImageComparer OutlinedTextDrawingComparer = ImageComparer.TolerantPercentage(0.5f, 3);
[Theory] [Theory]
[WithSolidFilledImages(200, 100, "White", PixelTypes.Rgba32, 50, 0, 0, "SixLaborsSampleAB.woff", AB)] [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(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> where TPixel : struct, IPixel<TPixel>
{ {
Font font = CreateFont(fontName, fontSize); Font font = CreateFont(fontName, fontSize);
string fnDisplayText = text.Replace("\n", "");
fnDisplayText = fnDisplayText.Substring(0, Math.Min(fnDisplayText.Length, 4));
TPixel color = NamedColors<TPixel>.Black; TPixel color = NamedColors<TPixel>.Black;
provider.VerifyOperation( provider.VerifyOperation(
TextDrawingComparer,
img => img =>
{ {
img.Mutate(c => c.DrawText(text, new Font(font, fontSize), color, new PointF(x, y))); img.Mutate(c => c.DrawText(text, new Font(font, fontSize), color, new PointF(x, y)));
}, },
$"{fontName}-{fontSize}-{fnDisplayText}-({x},{y})", $"{fontName}-{fontSize}-{ToTestOutputDisplayText(text)}-({x},{y})",
appendPixelTypeToFileName: false, appendPixelTypeToFileName: false,
appendSourceFileOrDescription: true); appendSourceFileOrDescription: true);
} }
@ -84,15 +80,16 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
} }
var textOptions = new TextGraphicsOptions var textOptions = new TextGraphicsOptions
{ {
Antialias = true, Antialias = true,
ApplyKerning = true, ApplyKerning = true,
VerticalAlignment = VerticalAlignment.Top, VerticalAlignment = VerticalAlignment.Top,
HorizontalAlignment = HorizontalAlignment.Left, HorizontalAlignment = HorizontalAlignment.Left,
}; };
TPixel color = NamedColors<TPixel>.Black; TPixel color = NamedColors<TPixel>.Black;
provider.VerifyOperation( provider.VerifyOperation(
TextDrawingComparer,
img => img =>
{ {
img.Mutate(c => c.DrawText(textOptions, sb.ToString(), font, color, new PointF(10, 5))); 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); 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 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) private static Font CreateFont(string fontName, int size)
{ {
var fontCollection = new FontCollection(); 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.Text, GetType("tEXt"));
Assert.Equal(PngChunkType.Gamma, GetType("gAMA")); Assert.Equal(PngChunkType.Gamma, GetType("gAMA"));
Assert.Equal(PngChunkType.Physical, GetType("pHYs")); Assert.Equal(PngChunkType.Physical, GetType("pHYs"));
Assert.Equal(PngChunkType.Exif, GetType("eXIf"));
} }
private static PngChunkType GetType(string text) private static PngChunkType GetType(string text)

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

@ -25,6 +25,7 @@
<ItemGroup> <ItemGroup>
<None Include="PixelFormats\PixelOperationsTests.Blender.cs" /> <None Include="PixelFormats\PixelOperationsTests.Blender.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.CSharp" Version="4.5.0" /> <PackageReference Include="Microsoft.CSharp" Version="4.5.0" />
<PackageReference Include="System.Drawing.Common" 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="Microsoft.NET.Test.Sdk" Version="15.7.2" />
<PackageReference Include="xunit" Version="2.3.1" /> <PackageReference Include="xunit" Version="2.3.1" />
<PackageReference Include="xunit.runner.visualstudio" 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>
<ItemGroup> <ItemGroup>
<AdditionalFiles Include="..\..\stylecop.json" /> <AdditionalFiles Include="..\..\stylecop.json" />
@ -45,6 +46,12 @@
<DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" /> <DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
</ItemGroup> </ItemGroup>
<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"> <None Update="xunit.runner.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </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; 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 // ReSharper disable once InconsistentNaming
/// <summary> /// <summary>
/// Gets a value indicating whether test execution runs on CI. /// Gets a value indicating whether test execution runs on CI.
@ -123,5 +130,19 @@ namespace SixLabors.ImageSharp.Tests
return path; 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;
using System.IO; using System.IO;
using SixLabors.ImageSharp.Common.Helpers;
using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Gif;
@ -13,9 +14,11 @@ using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs;
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests namespace SixLabors.ImageSharp.Tests
{ {
public class TestEnvironmentTests public class TestEnvironmentTests
{ {
public TestEnvironmentTests(ITestOutputHelper output) public TestEnvironmentTests(ITestOutputHelper output)
@ -31,6 +34,29 @@ namespace SixLabors.ImageSharp.Tests
Assert.True(Directory.Exists(path)); 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] [Fact]
public void SolutionDirectoryFullPath() public void SolutionDirectoryFullPath()
{ {

Loading…
Cancel
Save