Browse Source

Merge branch 'master' into tiff-codec

pull/1570/head
Andrew Wilkinson 9 years ago
parent
commit
db10ba4055
  1. 2
      .vscode/tasks.json
  2. 51
      src/ImageSharp.Drawing/Drawable.cs
  3. 3
      src/ImageSharp.Drawing/ImageSharp.Drawing.csproj
  4. 2
      src/ImageSharp.Drawing/Paths/DrawPath.cs
  5. 45
      src/ImageSharp.Drawing/Paths/DrawPathCollection.cs
  6. 81
      src/ImageSharp.Drawing/Paths/FillPathCollection.cs
  7. 90
      src/ImageSharp.Drawing/Paths/ShapePath.cs
  8. 8
      src/ImageSharp.Drawing/Paths/ShapeRegion.cs
  9. 31
      src/ImageSharp.Drawing/Pens/IPen.cs
  10. 199
      src/ImageSharp.Drawing/Pens/Pen{TPixel}.cs
  11. 27
      src/ImageSharp.Drawing/Pens/Processors/ColoredPointInfo.cs
  12. 40
      src/ImageSharp.Drawing/Pens/Processors/PenApplicator.cs
  13. 141
      src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs
  14. 25
      src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs
  15. 8
      src/ImageSharp.Drawing/Region.cs
  16. 200
      src/ImageSharp.Drawing/Text/DrawText.Path.cs
  17. 26
      src/ImageSharp.Drawing/Text/DrawText.cs
  18. 127
      src/ImageSharp.Drawing/Text/GlyphBuilder.cs
  19. 13
      src/ImageSharp.Drawing/Text/TextGraphicsOptions.cs
  20. 45
      src/ImageSharp/Common/Extensions/ByteExtensions.cs
  21. 1
      src/ImageSharp/Common/Helpers/Guard.cs
  22. 29
      src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
  23. 6
      src/ImageSharp/Formats/Gif/GifDecoderCore.cs
  24. 125
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs
  25. 38
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt
  26. 10
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
  27. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/YCbCrToRgbTables.cs
  28. 77
      src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
  29. BIN
      src/ImageSharp/Formats/Jpeg/itu-t81.pdf
  30. 145
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  31. 44
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  32. 2
      src/ImageSharp/Image/ImageBase{TPixel}.cs
  33. 2
      src/ImageSharp/Image/ImageProcessingExtensions.cs
  34. 2
      src/ImageSharp/Image/Image{TPixel}.cs
  35. 16
      src/ImageSharp/Image/PixelAccessor{TPixel}.cs
  36. 29
      src/ImageSharp/ImageSharp.csproj
  37. 72
      src/ImageSharp/Numerics/Matrix3x2Extensions.cs
  38. 259
      src/ImageSharp/Numerics/Point.cs
  39. 233
      src/ImageSharp/Numerics/PointF.cs
  40. 440
      src/ImageSharp/Numerics/Rectangle.cs
  41. 414
      src/ImageSharp/Numerics/RectangleF.cs
  42. 137
      src/ImageSharp/Numerics/Size.cs
  43. 179
      src/ImageSharp/Numerics/SizeF.cs
  44. 38
      src/ImageSharp/PixelFormats/Alpha8.cs
  45. 57
      src/ImageSharp/PixelFormats/Argb32.cs
  46. 135
      src/ImageSharp/PixelFormats/Bgr24.cs
  47. 42
      src/ImageSharp/PixelFormats/Bgr565.cs
  48. 187
      src/ImageSharp/PixelFormats/Bgra32.cs
  49. 56
      src/ImageSharp/PixelFormats/Bgra4444.cs
  50. 53
      src/ImageSharp/PixelFormats/Bgra5551.cs
  51. 42
      src/ImageSharp/PixelFormats/Byte4.cs
  52. 14
      src/ImageSharp/PixelFormats/ColorBuilder{TPixel}.cs
  53. 302
      src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.cs
  54. 130
      src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.tt
  55. 130
      src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.cs
  56. 99
      src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.tt
  57. 76
      src/ImageSharp/PixelFormats/HalfSingle.cs
  58. 76
      src/ImageSharp/PixelFormats/HalfVector2.cs
  59. 76
      src/ImageSharp/PixelFormats/HalfVector4.cs
  60. 43
      src/ImageSharp/PixelFormats/IPixel.cs
  61. 86
      src/ImageSharp/PixelFormats/NormalizedByte2.cs
  62. 86
      src/ImageSharp/PixelFormats/NormalizedByte4.cs
  63. 86
      src/ImageSharp/PixelFormats/NormalizedShort2.cs
  64. 86
      src/ImageSharp/PixelFormats/NormalizedShort4.cs
  65. 76
      src/ImageSharp/PixelFormats/PixelConversionExtensions.cs
  66. 204
      src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs
  67. 57
      src/ImageSharp/PixelFormats/Rg32.cs
  68. 132
      src/ImageSharp/PixelFormats/Rgb24.cs
  69. 46
      src/ImageSharp/PixelFormats/Rgba1010102.cs
  70. 159
      src/ImageSharp/PixelFormats/Rgba32.PixelOperations.cs
  71. 169
      src/ImageSharp/PixelFormats/Rgba32.cs
  72. 46
      src/ImageSharp/PixelFormats/Rgba64.cs
  73. 5
      src/ImageSharp/PixelFormats/RgbaVector.PixelOperations.cs
  74. 65
      src/ImageSharp/PixelFormats/RgbaVector.cs
  75. 86
      src/ImageSharp/PixelFormats/Short2.cs
  76. 86
      src/ImageSharp/PixelFormats/Short4.cs
  77. 2
      src/ImageSharp/Processing/ColorMatrix/BlackWhite.cs
  78. 2
      src/ImageSharp/Processing/ColorMatrix/ColorBlindness.cs
  79. 42
      src/ImageSharp/Processing/ColorMatrix/Grayscale.cs
  80. 2
      src/ImageSharp/Processing/ColorMatrix/Hue.cs
  81. 2
      src/ImageSharp/Processing/ColorMatrix/Kodachrome.cs
  82. 2
      src/ImageSharp/Processing/ColorMatrix/Lomograph.cs
  83. 2
      src/ImageSharp/Processing/ColorMatrix/Polaroid.cs
  84. 2
      src/ImageSharp/Processing/ColorMatrix/Saturation.cs
  85. 2
      src/ImageSharp/Processing/ColorMatrix/Sepia.cs
  86. 2
      src/ImageSharp/Processing/Convolution/DetectEdges.cs
  87. 2
      src/ImageSharp/Processing/Convolution/GaussianBlur.cs
  88. 2
      src/ImageSharp/Processing/Convolution/GaussianSharpen.cs
  89. 33
      src/ImageSharp/Processing/Effects/OilPainting.cs
  90. 4
      src/ImageSharp/Processing/Overlays/Glow.cs
  91. 4
      src/ImageSharp/Processing/Overlays/Vignette.cs
  92. 49
      src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs
  93. 2
      src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs
  94. 2
      src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs
  95. 50
      src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs
  96. 6
      src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs
  97. 2
      src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs
  98. 2
      src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs
  99. 7
      src/ImageSharp/Processing/Transforms/AutoOrient.cs
  100. 2
      src/ImageSharp/Processing/Transforms/Flip.cs

2
.vscode/tasks.json

@ -16,7 +16,7 @@
{
"taskName": "build benchmark",
"suppressTaskName": true,
"args": [ "build", "tests/ImageSharp.Benchmarks/project.json", "-f", "netcoreapp1.1", "-c", "Release" ],
"args": [ "build", "tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj", "-f", "netcoreapp1.1", "-c", "Release" ],
"showOutput": "always",
"problemMatcher": "$msCompile"
},

51
src/ImageSharp.Drawing/Drawable.cs

@ -1,51 +0,0 @@
// <copyright file="Drawable.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Drawing
{
/// <summary>
/// Represents a path or set of paths that can be drawn as an outline.
/// </summary>
public abstract class Drawable
{
/// <summary>
/// Gets the maximum number of intersections to could be returned.
/// </summary>
public abstract int MaxIntersections { get; }
/// <summary>
/// Gets the bounds.
/// </summary>
public abstract Rectangle Bounds { get; }
/// <summary>
/// Gets the point information for the specified x and y location.
/// </summary>
/// <param name="x">The x.</param>
/// <param name="y">The y.</param>
/// <returns>Information about the point in relation to a drawable edge</returns>
public abstract PointInfo GetPointInfo(int x, int y);
/// <summary>
/// Scans the X axis for intersections.
/// </summary>
/// <param name="x">The x.</param>
/// <param name="buffer">The buffer.</param>
/// <param name="length">The length.</param>
/// <param name="offset">The offset.</param>
/// <returns>The number of intersections found.</returns>
public abstract int ScanX(int x, float[] buffer, int length, int offset);
/// <summary>
/// Scans the Y axis for intersections.
/// </summary>
/// <param name="y">The position along the y axis to find intersections.</param>
/// <param name="buffer">The buffer.</param>
/// <param name="length">The length.</param>
/// <param name="offset">The offset.</param>
/// <returns>The number of intersections found.</returns>
public abstract int ScanY(int y, float[] buffer, int length, int offset);
}
}

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

@ -36,11 +36,10 @@
<ProjectReference Include="..\ImageSharp\ImageSharp.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="SixLabors.Shapes.Text" Version="0.1.0-alpha0017" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.0-beta001">
<PrivateAssets>All</PrivateAssets>
</PackageReference>
<PackageReference Include="SixLabors.Fonts" Version="0.1.0-alpha0010" />
<PackageReference Include="SixLabors.Shapes" Version="0.1.0-alpha0012" />
</ItemGroup>
<PropertyGroup>
<CodeAnalysisRuleSet>..\..\ImageSharp.ruleset</CodeAnalysisRuleSet>

2
src/ImageSharp.Drawing/Paths/DrawPath.cs

@ -28,7 +28,7 @@ namespace ImageSharp
public static Image<TPixel> Draw<TPixel>(this Image<TPixel> source, IPen<TPixel> pen, IPath path, GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
return source.Draw(pen, new ShapePath(path), options);
return source.Fill(pen.StrokeFill, new ShapePath(path, pen), options);
}
/// <summary>

45
src/ImageSharp.Drawing/DrawPath.cs → src/ImageSharp.Drawing/Paths/DrawPathCollection.cs

@ -8,8 +8,8 @@ namespace ImageSharp
using Drawing;
using Drawing.Brushes;
using Drawing.Pens;
using Drawing.Processors;
using ImageSharp.PixelFormats;
using SixLabors.Shapes;
/// <summary>
/// Extension methods for the <see cref="Image{TPixel}"/> type.
@ -17,18 +17,23 @@ namespace ImageSharp
public static partial class ImageExtensions
{
/// <summary>
/// Draws the outline of the region with the provided pen.
/// Draws the outline of the polygon with the provided pen.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="pen">The pen.</param>
/// <param name="path">The path.</param>
/// <param name="paths">The paths.</param>
/// <param name="options">The options.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Draw<TPixel>(this Image<TPixel> source, IPen<TPixel> pen, Drawable path, GraphicsOptions options)
public static Image<TPixel> Draw<TPixel>(this Image<TPixel> source, IPen<TPixel> pen, IPathCollection paths, GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
return source.Apply(new DrawPathProcessor<TPixel>(pen, path, options));
foreach (IPath path in paths)
{
source.Draw(pen, path, options);
}
return source;
}
/// <summary>
@ -37,12 +42,12 @@ namespace ImageSharp
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="pen">The pen.</param>
/// <param name="path">The path.</param>
/// <param name="paths">The paths.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Draw<TPixel>(this Image<TPixel> source, IPen<TPixel> pen, Drawable path)
public static Image<TPixel> Draw<TPixel>(this Image<TPixel> source, IPen<TPixel> pen, IPathCollection paths)
where TPixel : struct, IPixel<TPixel>
{
return source.Draw(pen, path, GraphicsOptions.Default);
return source.Draw(pen, paths, GraphicsOptions.Default);
}
/// <summary>
@ -52,13 +57,13 @@ namespace ImageSharp
/// <param name="source">The image this method extends.</param>
/// <param name="brush">The brush.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="path">The path.</param>
/// <param name="paths">The shapes.</param>
/// <param name="options">The options.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Draw<TPixel>(this Image<TPixel> source, IBrush<TPixel> brush, float thickness, Drawable path, GraphicsOptions options)
public static Image<TPixel> Draw<TPixel>(this Image<TPixel> source, IBrush<TPixel> brush, float thickness, IPathCollection paths, GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
return source.Draw(new Pen<TPixel>(brush, thickness), path, options);
return source.Draw(new Pen<TPixel>(brush, thickness), paths, options);
}
/// <summary>
@ -68,12 +73,12 @@ namespace ImageSharp
/// <param name="source">The image this method extends.</param>
/// <param name="brush">The brush.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="path">The path.</param>
/// <param name="paths">The paths.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Draw<TPixel>(this Image<TPixel> source, IBrush<TPixel> brush, float thickness, Drawable path)
public static Image<TPixel> Draw<TPixel>(this Image<TPixel> source, IBrush<TPixel> brush, float thickness, IPathCollection paths)
where TPixel : struct, IPixel<TPixel>
{
return source.Draw(new Pen<TPixel>(brush, thickness), path);
return source.Draw(new Pen<TPixel>(brush, thickness), paths);
}
/// <summary>
@ -83,13 +88,13 @@ namespace ImageSharp
/// <param name="source">The image this method extends.</param>
/// <param name="color">The color.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="path">The path.</param>
/// <param name="paths">The paths.</param>
/// <param name="options">The options.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Draw<TPixel>(this Image<TPixel> source, TPixel color, float thickness, Drawable path, GraphicsOptions options)
public static Image<TPixel> Draw<TPixel>(this Image<TPixel> source, TPixel color, float thickness, IPathCollection paths, GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
return source.Draw(new SolidBrush<TPixel>(color), thickness, path, options);
return source.Draw(new SolidBrush<TPixel>(color), thickness, paths, options);
}
/// <summary>
@ -99,12 +104,12 @@ namespace ImageSharp
/// <param name="source">The image this method extends.</param>
/// <param name="color">The color.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="path">The path.</param>
/// <param name="paths">The paths.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Draw<TPixel>(this Image<TPixel> source, TPixel color, float thickness, Drawable path)
public static Image<TPixel> Draw<TPixel>(this Image<TPixel> source, TPixel color, float thickness, IPathCollection paths)
where TPixel : struct, IPixel<TPixel>
{
return source.Draw(new SolidBrush<TPixel>(color), thickness, path);
return source.Draw(new SolidBrush<TPixel>(color), thickness, paths);
}
}
}

81
src/ImageSharp.Drawing/Paths/FillPathCollection.cs

@ -0,0 +1,81 @@
// <copyright file="FillPaths.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using Drawing;
using Drawing.Brushes;
using ImageSharp.PixelFormats;
using SixLabors.Shapes;
/// <summary>
/// Extension methods for the <see cref="Image{TPixel}"/> type.
/// </summary>
public static partial class ImageExtensions
{
/// <summary>
/// Flood fills the image in the shape of the provided polygon with the specified brush..
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="brush">The brush.</param>
/// <param name="paths">The shapes.</param>
/// <param name="options">The graphics options.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Fill<TPixel>(this Image<TPixel> source, IBrush<TPixel> brush, IPathCollection paths, GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
foreach (IPath s in paths)
{
source.Fill(brush, s, options);
}
return source;
}
/// <summary>
/// Flood fills the image in the shape of the provided polygon with the specified brush.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="brush">The brush.</param>
/// <param name="paths">The paths.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Fill<TPixel>(this Image<TPixel> source, IBrush<TPixel> brush, IPathCollection paths)
where TPixel : struct, IPixel<TPixel>
{
return source.Fill(brush, paths, GraphicsOptions.Default);
}
/// <summary>
/// Flood fills the image in the shape of the provided polygon with the specified brush..
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="color">The color.</param>
/// <param name="paths">The paths.</param>
/// <param name="options">The options.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Fill<TPixel>(this Image<TPixel> source, TPixel color, IPathCollection paths, GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
return source.Fill(new SolidBrush<TPixel>(color), paths, options);
}
/// <summary>
/// Flood fills the image in the shape of the provided polygon with the specified brush..
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="color">The color.</param>
/// <param name="paths">The paths.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Fill<TPixel>(this Image<TPixel> source, TPixel color, IPathCollection paths)
where TPixel : struct, IPixel<TPixel>
{
return source.Fill(new SolidBrush<TPixel>(color), paths);
}
}
}

90
src/ImageSharp.Drawing/Paths/ShapePath.cs

@ -1,12 +1,12 @@
// <copyright file="ShapePath.cs" company="James Jackson-South">
// <copyright file="ShapeRegion.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Drawing
{
using System;
using System.Buffers;
using System.Collections.Immutable;
using System.Numerics;
using SixLabors.Shapes;
@ -14,91 +14,19 @@ namespace ImageSharp.Drawing
using Rectangle = ImageSharp.Rectangle;
/// <summary>
/// A drawable mapping between a <see cref="IPath"/> and a drawable region.
/// A mapping between a <see cref="IPath"/> and a region.
/// </summary>
internal class ShapePath : Drawable
internal class ShapePath : ShapeRegion
{
/// <summary>
/// Initializes a new instance of the <see cref="ShapePath"/> class.
/// </summary>
/// <param name="path">The path.</param>
public ShapePath(IPath path)
/// <param name="shape">The shape.</param>
/// <param name="pen">The pen to apply to the shape.</param>
// SixLabors.shape willbe moving to a Span/ReadOnlySpan based API shortly use ToArray for now.
public ShapePath(IPath shape, Pens.IPen pen)
: base(shape.GenerateOutline(pen.StrokeWidth, pen.StrokePattern.ToArray()))
{
this.Path = path;
this.Bounds = path.Bounds.Convert();
}
/// <summary>
/// Gets the fillable shape
/// </summary>
public IPath Path { get; }
/// <inheritdoc/>
public override int MaxIntersections => this.Path.MaxIntersections;
/// <inheritdoc/>
public override Rectangle Bounds { get; }
/// <inheritdoc/>
public override int ScanX(int x, float[] buffer, int length, int offset)
{
Vector2 start = new Vector2(x, this.Bounds.Top - 1);
Vector2 end = new Vector2(x, this.Bounds.Bottom + 1);
Vector2[] innerbuffer = ArrayPool<Vector2>.Shared.Rent(length);
try
{
int count = this.Path.FindIntersections(start, end, innerbuffer, length, 0);
for (int i = 0; i < count; i++)
{
buffer[i + offset] = innerbuffer[i].Y;
}
return count;
}
finally
{
ArrayPool<Vector2>.Shared.Return(innerbuffer);
}
}
/// <inheritdoc/>
public override int ScanY(int y, float[] buffer, int length, int offset)
{
Vector2 start = new Vector2(this.Bounds.Left - 1, y);
Vector2 end = new Vector2(this.Bounds.Right + 1, y);
Vector2[] innerbuffer = ArrayPool<Vector2>.Shared.Rent(length);
try
{
int count = this.Path.FindIntersections(start, end, innerbuffer, length, 0);
for (int i = 0; i < count; i++)
{
buffer[i + offset] = innerbuffer[i].X;
}
return count;
}
finally
{
ArrayPool<Vector2>.Shared.Return(innerbuffer);
}
}
/// <inheritdoc/>
public override PointInfo GetPointInfo(int x, int y)
{
Vector2 point = new Vector2(x, y);
SixLabors.Shapes.PointInfo dist = this.Path.Distance(point);
return new PointInfo
{
DistanceAlongPath = dist.DistanceAlongPath,
DistanceFromPath =
dist.DistanceFromPath < 0
? -dist.DistanceFromPath
: dist.DistanceFromPath
};
}
}
}

8
src/ImageSharp.Drawing/Paths/ShapeRegion.cs

@ -40,18 +40,18 @@ namespace ImageSharp.Drawing
public override Rectangle Bounds { get; }
/// <inheritdoc/>
public override int Scan(float y, float[] buffer, int length, int offset)
public override int Scan(float y, Span<float> buffer)
{
Vector2 start = new Vector2(this.Bounds.Left - 1, y);
Vector2 end = new Vector2(this.Bounds.Right + 1, y);
Vector2[] innerbuffer = ArrayPool<Vector2>.Shared.Rent(length);
Vector2[] innerbuffer = ArrayPool<Vector2>.Shared.Rent(buffer.Length);
try
{
int count = this.Shape.FindIntersections(start, end, innerbuffer, length, 0);
int count = this.Shape.FindIntersections(start, end, innerbuffer, buffer.Length, 0);
for (int i = 0; i < count; i++)
{
buffer[i + offset] = innerbuffer[i].X;
buffer[i] = innerbuffer[i].X;
}
return count;

31
src/ImageSharp.Drawing/Pens/IPen.cs

@ -12,21 +12,28 @@ namespace ImageSharp.Drawing.Pens
/// Interface representing a Pen
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
public interface IPen<TPixel>
public interface IPen<TPixel> : IPen
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Creates the applicator for applying this pen to an Image
/// Gets the stroke fill.
/// </summary>
/// <param name="source">The source image.</param>
/// <param name="region">The region the pen will be applied to.</param>
/// <param name="options">The currently active graphic options.</param>
/// <returns>
/// Returns a the applicator for the pen.
/// </returns>
/// <remarks>
/// The <paramref name="region" /> when being applied to things like shapes would usually be the bounding box of the shape not necessarily the shape of the whole image.
/// </remarks>
PenApplicator<TPixel> CreateApplicator(ImageBase<TPixel> source, RectangleF region, GraphicsOptions options);
IBrush<TPixel> StrokeFill { get; }
}
/// <summary>
/// Iterface represting the pattern and size of the stroke to apply with a Pen.
/// </summary>
public interface IPen
{
/// <summary>
/// Gets the width to apply to the stroke
/// </summary>
float StrokeWidth { get; }
/// <summary>
/// Gets the stoke pattern.
/// </summary>
System.ReadOnlySpan<float> StrokePattern { get; }
}
}

199
src/ImageSharp.Drawing/Pens/Pen{TPixel}.cs

@ -5,6 +5,7 @@
namespace ImageSharp.Drawing.Pens
{
using System;
using System.Numerics;
using ImageSharp.Drawing.Brushes;
@ -48,8 +49,8 @@ namespace ImageSharp.Drawing.Pens
/// <param name="pattern">The pattern.</param>
public Pen(IBrush<TPixel> brush, float width, float[] pattern)
{
this.Brush = brush;
this.Width = width;
this.StrokeFill = brush;
this.StrokeWidth = width;
this.pattern = pattern;
}
@ -73,195 +74,13 @@ namespace ImageSharp.Drawing.Pens
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ImageSharp.Drawing.Pens.Pen{TPixel}"/> class.
/// </summary>
/// <param name="pen">The pen.</param>
internal Pen(Pen<TPixel> pen)
: this(pen.Brush, pen.Width, pen.pattern)
{
}
/// <summary>
/// Gets the brush.
/// </summary>
/// <value>
/// The brush.
/// </value>
public IBrush<TPixel> Brush { get; }
/// <summary>
/// Gets the width.
/// </summary>
/// <value>
/// The width.
/// </value>
public float Width { get; }
/// <summary>
/// Creates the applicator for applying this pen to an Image
/// </summary>
/// <param name="source">The source image.</param>
/// <param name="region">The region the pen will be applied to.</param>
/// <param name="options">The Graphics options</param>
/// <returns>
/// Returns a the applicator for the pen.
/// </returns>
/// <remarks>
/// The <paramref name="region" /> when being applied to things like shapes would ussually be the
/// bounding box of the shape not necorserrally the shape of the whole image
/// </remarks>
public PenApplicator<TPixel> CreateApplicator(ImageBase<TPixel> source, RectangleF region, GraphicsOptions options)
{
if (this.pattern == null || this.pattern.Length < 2)
{
// if there is only one item in the pattern then 100% of it will
// be solid so use the quicker applicator
return new SolidPenApplicator(source, this.Brush, region, this.Width, options);
}
return new PatternPenApplicator(source, this.Brush, region, this.Width, this.pattern, options);
}
private class SolidPenApplicator : PenApplicator<TPixel>
{
private readonly BrushApplicator<TPixel> brush;
private readonly float halfWidth;
public SolidPenApplicator(ImageBase<TPixel> sourcePixels, IBrush<TPixel> brush, RectangleF region, float width, GraphicsOptions options)
{
this.brush = brush.CreateApplicator(sourcePixels, region, options);
this.halfWidth = width / 2;
this.RequiredRegion = RectangleF.Outset(region, width);
}
public override RectangleF RequiredRegion
{
get;
}
public override void Dispose()
{
this.brush.Dispose();
}
/// <inheritdoc/>
public IBrush<TPixel> StrokeFill { get; }
public override ColoredPointInfo<TPixel> GetColor(int x, int y, PointInfo info)
{
var result = default(ColoredPointInfo<TPixel>);
result.Color = this.brush[x, y];
/// <inheritdoc/>
public float StrokeWidth { get; }
if (info.DistanceFromPath < this.halfWidth)
{
// inside strip
result.DistanceFromElement = 0;
}
else
{
result.DistanceFromElement = info.DistanceFromPath - this.halfWidth;
}
return result;
}
}
private class PatternPenApplicator : PenApplicator<TPixel>
{
private readonly BrushApplicator<TPixel> brush;
private readonly float halfWidth;
private readonly float[] pattern;
private readonly float totalLength;
public PatternPenApplicator(ImageBase<TPixel> source, IBrush<TPixel> brush, RectangleF region, float width, float[] pattern, GraphicsOptions options)
{
this.brush = brush.CreateApplicator(source, region, options);
this.halfWidth = width / 2;
this.totalLength = 0;
this.pattern = new float[pattern.Length + 1];
this.pattern[0] = 0;
for (int i = 0; i < pattern.Length; i++)
{
this.totalLength += pattern[i] * width;
this.pattern[i + 1] = this.totalLength;
}
this.RequiredRegion = RectangleF.Outset(region, width);
}
public override RectangleF RequiredRegion
{
get;
}
public override void Dispose()
{
this.brush.Dispose();
}
public override ColoredPointInfo<TPixel> GetColor(int x, int y, PointInfo info)
{
var infoResult = default(ColoredPointInfo<TPixel>);
infoResult.DistanceFromElement = float.MaxValue; // is really outside the element
float length = info.DistanceAlongPath % this.totalLength;
// we can treat the DistanceAlongPath and DistanceFromPath as x,y coords for the pattern
// we need to calcualte the distance from the outside edge of the pattern
// and set them on the ColoredPointInfo<TPixel> along with the color.
infoResult.Color = this.brush[x, y];
float distanceWAway = 0;
if (info.DistanceFromPath < this.halfWidth)
{
// inside strip
distanceWAway = 0;
}
else
{
distanceWAway = info.DistanceFromPath - this.halfWidth;
}
for (int i = 0; i < this.pattern.Length - 1; i++)
{
float start = this.pattern[i];
float end = this.pattern[i + 1];
if (length >= start && length < end)
{
// in section
if (i % 2 == 0)
{
// solid part return the maxDistance
infoResult.DistanceFromElement = distanceWAway;
return infoResult;
}
else
{
// this is a none solid part
float distanceFromStart = length - start;
float distanceFromEnd = end - length;
float closestEdge = MathF.Min(distanceFromStart, distanceFromEnd);
float distanceAcross = closestEdge;
if (distanceWAway > 0)
{
infoResult.DistanceFromElement = new Vector2(distanceAcross, distanceWAway).Length();
}
else
{
infoResult.DistanceFromElement = closestEdge;
}
return infoResult;
}
}
}
return infoResult;
}
}
/// <inheritdoc/>
public ReadOnlySpan<float> StrokePattern => this.pattern;
}
}

27
src/ImageSharp.Drawing/Pens/Processors/ColoredPointInfo.cs

@ -1,27 +0,0 @@
// <copyright file="ColoredPointInfo.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Drawing.Processors
{
using ImageSharp.PixelFormats;
/// <summary>
/// Returns details about how far away from the inside of a shape and the color the pixel could be.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
public struct ColoredPointInfo<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// The color
/// </summary>
public TPixel Color;
/// <summary>
/// The distance from element
/// </summary>
public float DistanceFromElement;
}
}

40
src/ImageSharp.Drawing/Pens/Processors/PenApplicator.cs

@ -1,40 +0,0 @@
// <copyright file="PenApplicator.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Drawing.Processors
{
using System;
using ImageSharp.PixelFormats;
/// <summary>
/// primitive that converts a <see cref="PointInfo"/> into a color and a distance away from the drawable part of the path.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
public abstract class PenApplicator<TPixel> : IDisposable
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Gets the required region.
/// </summary>
/// <value>
/// The required region.
/// </value>
public abstract RectangleF RequiredRegion { get; }
/// <inheritdoc/>
public abstract void Dispose();
/// <summary>
/// Gets a <see cref="ColoredPointInfo{TPixel}" /> from a point represented by a <see cref="PointInfo" />.
/// </summary>
/// <param name="x">The x.</param>
/// <param name="y">The y.</param>
/// <param name="info">The information to extract color details about.</param>
/// <returns>
/// Returns the color details and distance from a solid bit of the line.
/// </returns>
public abstract ColoredPointInfo<TPixel> GetColor(int x, int y, PointInfo info);
}
}

141
src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs

@ -1,141 +0,0 @@
// <copyright file="DrawPathProcessor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Drawing.Processors
{
using System;
using System.Numerics;
using System.Threading.Tasks;
using ImageSharp.Memory;
using ImageSharp.PixelFormats;
using ImageSharp.Processing;
using Pens;
/// <summary>
/// Draws a path using the processor pipeline
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <seealso cref="ImageSharp.Processing.ImageProcessor{TPixel}" />
internal class DrawPathProcessor<TPixel> : ImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
private const float AntialiasFactor = 1f;
private const int PaddingFactor = 1; // needs to been the same or greater than AntialiasFactor
/// <summary>
/// Initializes a new instance of the <see cref="DrawPathProcessor{TPixel}" /> class.
/// </summary>
/// <param name="pen">The details how to draw the outline/path.</param>
/// <param name="drawable">The details of the paths and outlines to draw.</param>
/// <param name="options">The drawing configuration options.</param>
public DrawPathProcessor(IPen<TPixel> pen, Drawable drawable, GraphicsOptions options)
{
this.Path = drawable;
this.Pen = pen;
this.Options = options;
}
/// <summary>
/// Gets the graphics options.
/// </summary>
public GraphicsOptions Options { get; }
/// <summary>
/// Gets the pen.
/// </summary>
public IPen<TPixel> Pen { get; }
/// <summary>
/// Gets the path.
/// </summary>
public Drawable Path { get; }
/// <inheritdoc/>
protected override void OnApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
{
using (PenApplicator<TPixel> applicator = this.Pen.CreateApplicator(source, this.Path.Bounds, this.Options))
{
Rectangle rect = RectangleF.Ceiling(applicator.RequiredRegion);
int polyStartY = rect.Y - PaddingFactor;
int polyEndY = rect.Bottom + PaddingFactor;
int startX = rect.X - PaddingFactor;
int endX = rect.Right + PaddingFactor;
int minX = Math.Max(sourceRectangle.Left, startX);
int maxX = Math.Min(sourceRectangle.Right, endX);
int minY = Math.Max(sourceRectangle.Top, polyStartY);
int maxY = Math.Min(sourceRectangle.Bottom, polyEndY);
// Align start/end positions.
minX = Math.Max(0, minX);
maxX = Math.Min(source.Width, maxX);
minY = Math.Max(0, minY);
maxY = Math.Min(source.Height, maxY);
// Reset offset if necessary.
if (minX > 0)
{
startX = 0;
}
if (minY > 0)
{
polyStartY = 0;
}
int width = maxX - minX;
PixelBlender<TPixel> blender = PixelOperations<TPixel>.Instance.GetPixelBlender(this.Options.BlenderMode);
Parallel.For(
minY,
maxY,
this.ParallelOptions,
y =>
{
int offsetY = y - polyStartY;
using (var amount = new Buffer<float>(width))
using (var colors = new Buffer<TPixel>(width))
{
for (int i = 0; i < width; i++)
{
int x = i + minX;
int offsetX = x - startX;
PointInfo info = this.Path.GetPointInfo(offsetX, offsetY);
ColoredPointInfo<TPixel> color = applicator.GetColor(offsetX, offsetY, info);
amount[i] = (this.Opacity(color.DistanceFromElement) * this.Options.BlendPercentage).Clamp(0, 1);
colors[i] = color.Color;
}
Span<TPixel> destination = source.GetRowSpan(offsetY).Slice(minX - startX, width);
blender.Blend(destination, destination, colors, amount);
}
});
}
}
/// <summary>
/// Returns the correct opacity for the given distance.
/// </summary>
/// <param name="distance">Thw distance from the central point.</param>
/// <returns>The <see cref="float"/></returns>
private float Opacity(float distance)
{
if (distance <= 0)
{
return 1;
}
if (this.Options.Antialias && distance < AntialiasFactor)
{
return 1 - (distance / AntialiasFactor);
}
return 0;
}
}
}

25
src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs

@ -93,6 +93,7 @@ namespace ImageSharp.Drawing.Processors
using (BrushApplicator<TPixel> applicator = this.Brush.CreateApplicator(source, rect, this.Options))
{
float[] buffer = arrayPool.Rent(maxIntersections);
Span<float> bufferSpan = buffer.AsSpan().Slice(0, maxIntersections);
int scanlineWidth = maxX - minX;
using (var scanline = new Buffer<float>(scanlineWidth))
{
@ -116,14 +117,14 @@ namespace ImageSharp.Drawing.Processors
float subpixelFractionPoint = subpixelFraction / subpixelCount;
for (float subPixel = (float)y; subPixel < y + 1; subPixel += subpixelFraction)
{
int pointsFound = region.Scan(subPixel, buffer, maxIntersections, 0);
int pointsFound = region.Scan(subPixel, bufferSpan);
if (pointsFound == 0)
{
// nothing on this line skip
continue;
}
QuickSort(buffer, pointsFound);
QuickSort(bufferSpan.Slice(0, pointsFound));
for (int point = 0; point < pointsFound; point += 2)
{
@ -153,13 +154,11 @@ namespace ImageSharp.Drawing.Processors
int nextX = startX + 1;
endX = Math.Min(endX, scanline.Length); // reduce to end to the right edge
if (nextX >= 0)
nextX = Math.Max(nextX, 0);
for (int x = nextX; x < endX; x++)
{
for (int x = nextX; x < endX; x++)
{
scanline[x] += subpixelFraction;
scanlineDirty = true;
}
scanline[x] += subpixelFraction;
scanlineDirty = true;
}
}
}
@ -194,20 +193,20 @@ namespace ImageSharp.Drawing.Processors
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void Swap(float[] data, int left, int right)
private static void Swap(Span<float> data, int left, int right)
{
float tmp = data[left];
data[left] = data[right];
data[right] = tmp;
}
private static void QuickSort(float[] data, int size)
private static void QuickSort(Span<float> data)
{
int hi = Math.Min(data.Length - 1, size - 1);
int hi = Math.Min(data.Length - 1, data.Length - 1);
QuickSort(data, 0, hi);
}
private static void QuickSort(float[] data, int lo, int hi)
private static void QuickSort(Span<float> data, int lo, int hi)
{
if (lo < hi)
{
@ -217,7 +216,7 @@ namespace ImageSharp.Drawing.Processors
}
}
private static int Partition(float[] data, int lo, int hi)
private static int Partition(Span<float> data, int lo, int hi)
{
float pivot = data[lo];
int i = lo - 1;

8
src/ImageSharp.Drawing/Region.cs

@ -5,6 +5,8 @@
namespace ImageSharp.Drawing
{
using System;
/// <summary>
/// Represents a region of an image.
/// </summary>
@ -19,7 +21,7 @@ namespace ImageSharp.Drawing
/// Gets the bounding box that entirely surrounds this region.
/// </summary>
/// <remarks>
/// This should always contains all possible points returned from <see cref="Scan(float, float[], int, int)"/>.
/// This should always contains all possible points returned from <see cref="Scan(float, Span{float})"/>.
/// </remarks>
public abstract Rectangle Bounds { get; }
@ -28,9 +30,7 @@ namespace ImageSharp.Drawing
/// </summary>
/// <param name="y">The position along the y axis to find intersections.</param>
/// <param name="buffer">The buffer.</param>
/// <param name="length">The length.</param>
/// <param name="offset">The offset.</param>
/// <returns>The number of intersections found.</returns>
public abstract int Scan(float y, float[] buffer, int length, int offset);
public abstract int Scan(float y, Span<float> buffer);
}
}

200
src/ImageSharp.Drawing/Text/DrawText.Path.cs

@ -0,0 +1,200 @@
// <copyright file="DrawText.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System.Numerics;
using Drawing;
using Drawing.Brushes;
using Drawing.Pens;
using ImageSharp.PixelFormats;
using SixLabors.Fonts;
using SixLabors.Shapes;
/// <summary>
/// Extension methods for the <see cref="Image{TPixel}"/> type.
/// </summary>
public static partial class ImageExtensions
{
/// <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 Image<TPixel> DrawText<TPixel>(this Image<TPixel> source, string text, Font font, TPixel color, IPath path)
where TPixel : struct, IPixel<TPixel>
{
return source.DrawText(text, font, color, path, TextGraphicsOptions.Default);
}
/// <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>
/// <param name="options">The options.</param>
/// <returns>
/// The <see cref="Image{TPixel}" />.
/// </returns>
public static Image<TPixel> DrawText<TPixel>(this Image<TPixel> source, string text, Font font, TPixel color, IPath path, TextGraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
return source.DrawText(text, font, Brushes.Solid(color), null, path, options);
}
/// <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 Image<TPixel> DrawText<TPixel>(this Image<TPixel> source, string text, Font font, IBrush<TPixel> brush, IPath path)
where TPixel : struct, IPixel<TPixel>
{
return source.DrawText(text, font, brush, path, TextGraphicsOptions.Default);
}
/// <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 path.</param>
/// <param name="options">The options.</param>
/// <returns>
/// The <see cref="Image{TPixel}" />.
/// </returns>
public static Image<TPixel> DrawText<TPixel>(this Image<TPixel> source, string text, Font font, IBrush<TPixel> brush, IPath path, TextGraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
return source.DrawText(text, font, brush, null, path, options);
}
/// <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 Image<TPixel> DrawText<TPixel>(this Image<TPixel> source, string text, Font font, IPen<TPixel> pen, IPath path)
where TPixel : struct, IPixel<TPixel>
{
return source.DrawText(text, font, pen, path, TextGraphicsOptions.Default);
}
/// <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>
/// <param name="options">The options.</param>
/// <returns>
/// The <see cref="Image{TPixel}" />.
/// </returns>
public static Image<TPixel> DrawText<TPixel>(this Image<TPixel> source, string text, Font font, IPen<TPixel> pen, IPath path, TextGraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
return source.DrawText(text, font, null, pen, path, options);
}
/// <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 Image<TPixel> DrawText<TPixel>(this Image<TPixel> source, string text, Font font, IBrush<TPixel> brush, IPen<TPixel> pen, IPath path)
where TPixel : struct, IPixel<TPixel>
{
return source.DrawText(text, font, brush, pen, path, TextGraphicsOptions.Default);
}
/// <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>
/// <param name="options">The options.</param>
/// <returns>
/// The <see cref="Image{TPixel}" />.
/// </returns>
public static Image<TPixel> DrawText<TPixel>(this Image<TPixel> source, string text, Font font, IBrush<TPixel> brush, IPen<TPixel> pen, IPath path, TextGraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
Vector2 dpi = DefaultTextDpi;
if (options.UseImageResolution)
{
dpi = new Vector2((float)source.MetaData.HorizontalResolution, (float)source.MetaData.VerticalResolution);
}
var style = new FontSpan(font, dpi)
{
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(brush, glyphs, pathOptions);
}
if (pen != null)
{
source.Draw(pen, glyphs, pathOptions);
}
return source;
}
}
}

26
src/ImageSharp.Drawing/Text/DrawText.cs

@ -12,6 +12,7 @@ namespace ImageSharp
using Drawing.Pens;
using ImageSharp.PixelFormats;
using SixLabors.Fonts;
using SixLabors.Shapes;
/// <summary>
/// Extension methods for the <see cref="Image{TPixel}"/> type.
@ -167,43 +168,32 @@ namespace ImageSharp
public static Image<TPixel> DrawText<TPixel>(this Image<TPixel> source, string text, Font font, IBrush<TPixel> brush, IPen<TPixel> pen, Vector2 location, TextGraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
GlyphBuilder glyphBuilder = new GlyphBuilder(location);
TextRenderer renderer = new TextRenderer(glyphBuilder);
Vector2 dpi = DefaultTextDpi;
if (options.UseImageResolution)
{
dpi = new Vector2((float)source.MetaData.HorizontalResolution, (float)source.MetaData.VerticalResolution);
}
FontSpan style = new FontSpan(font, dpi)
var style = new FontSpan(font, dpi)
{
ApplyKerning = options.ApplyKerning,
TabWidth = options.TabWidth,
WrappingWidth = options.WrapTextWidth,
Alignment = options.TextAlignment
HorizontalAlignment = options.HorizontalAlignment,
VerticalAlignment = options.VerticalAlignment
};
renderer.RenderText(text, style);
System.Collections.Generic.IEnumerable<SixLabors.Shapes.IPath> shapesToDraw = glyphBuilder.Paths;
IPathCollection glyphs = TextBuilder.GenerateGlyphs(text, location, style);
GraphicsOptions pathOptions = (GraphicsOptions)options;
var pathOptions = (GraphicsOptions)options;
if (brush != null)
{
foreach (SixLabors.Shapes.IPath s in shapesToDraw)
{
source.Fill(brush, s, pathOptions);
}
source.Fill(brush, glyphs, pathOptions);
}
if (pen != null)
{
foreach (SixLabors.Shapes.IPath s in shapesToDraw)
{
source.Draw(pen, s, pathOptions);
}
source.Draw(pen, glyphs, pathOptions);
}
return source;

127
src/ImageSharp.Drawing/Text/GlyphBuilder.cs

@ -1,127 +0,0 @@
// <copyright file="GlyphBuilder.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Drawing
{
using System.Collections.Generic;
using System.Numerics;
using SixLabors.Fonts;
using SixLabors.Shapes;
/// <summary>
/// rendering surface that Fonts can use to generate Shapes.
/// </summary>
internal class GlyphBuilder : IGlyphRenderer
{
private readonly PathBuilder builder = new PathBuilder();
private readonly List<IPath> paths = new List<IPath>();
private Vector2 currentPoint = default(Vector2);
/// <summary>
/// Initializes a new instance of the <see cref="GlyphBuilder"/> class.
/// </summary>
public GlyphBuilder()
: this(Vector2.Zero)
{
// glyphs are renderd realative to bottom left so invert the Y axis to allow it to render on top left origin surface
this.builder = new PathBuilder();
}
/// <summary>
/// Initializes a new instance of the <see cref="GlyphBuilder"/> class.
/// </summary>
/// <param name="origin">The origin.</param>
public GlyphBuilder(Vector2 origin)
{
this.builder = new PathBuilder();
this.builder.SetOrigin(origin);
}
/// <summary>
/// Gets the paths that have been rendered by this.
/// </summary>
public IEnumerable<IPath> Paths => this.paths;
/// <summary>
/// Begins the glyph.
/// </summary>
/// <param name="location">The offset that the glyph will be rendered at.</param>
void IGlyphRenderer.BeginGlyph(Vector2 location)
{
this.builder.Clear();
}
/// <summary>
/// Begins the figure.
/// </summary>
void IGlyphRenderer.BeginFigure()
{
this.builder.StartFigure();
}
/// <summary>
/// Draws a cubic bezier from the current point to the <paramref name="point"/>
/// </summary>
/// <param name="secondControlPoint">The second control point.</param>
/// <param name="thirdControlPoint">The third control point.</param>
/// <param name="point">The point.</param>
void IGlyphRenderer.CubicBezierTo(Vector2 secondControlPoint, Vector2 thirdControlPoint, Vector2 point)
{
this.builder.AddBezier(this.currentPoint, secondControlPoint, thirdControlPoint, point);
this.currentPoint = point;
}
/// <summary>
/// Ends the glyph.
/// </summary>
void IGlyphRenderer.EndGlyph()
{
this.paths.Add(this.builder.Build());
}
/// <summary>
/// Ends the figure.
/// </summary>
void IGlyphRenderer.EndFigure()
{
this.builder.CloseFigure();
}
/// <summary>
/// Draws a line from the current point to the <paramref name="point"/>.
/// </summary>
/// <param name="point">The point.</param>
void IGlyphRenderer.LineTo(Vector2 point)
{
this.builder.AddLine(this.currentPoint, point);
this.currentPoint = point;
}
/// <summary>
/// Moves to current point to the supplied vector.
/// </summary>
/// <param name="point">The point.</param>
void IGlyphRenderer.MoveTo(Vector2 point)
{
this.builder.StartFigure();
this.currentPoint = point;
}
/// <summary>
/// Draws a quadratics bezier from the current point to the <paramref name="point"/>
/// </summary>
/// <param name="secondControlPoint">The second control point.</param>
/// <param name="point">The point.</param>
void IGlyphRenderer.QuadraticBezierTo(Vector2 secondControlPoint, Vector2 point)
{
Vector2 c1 = (((secondControlPoint - this.currentPoint) * 2) / 3) + this.currentPoint;
Vector2 c2 = (((secondControlPoint - point) * 2) / 3) + point;
this.builder.AddBezier(this.currentPoint, c1, c2, point);
this.currentPoint = point;
}
}
}

13
src/ImageSharp.Drawing/Text/TextGraphicsOptions.cs

@ -33,7 +33,8 @@ namespace ImageSharp.Drawing
private float wrapTextWidth;
private SixLabors.Fonts.TextAlignment? textAlignment;
private SixLabors.Fonts.HorizontalAlignment? horizontalAlignment;
private SixLabors.Fonts.VerticalAlignment? verticalAlignment;
/// <summary>
/// Initializes a new instance of the <see cref="TextGraphicsOptions" /> struct.
@ -45,7 +46,8 @@ namespace ImageSharp.Drawing
this.tabWidth = 4;
this.useImageResolution = false;
this.wrapTextWidth = 0;
this.textAlignment = SixLabors.Fonts.TextAlignment.Left;
this.horizontalAlignment = HorizontalAlignment.Left;
this.verticalAlignment = VerticalAlignment.Top;
this.antialiasSubpixelDepth = 16;
this.blenderMode = PixelBlenderMode.Normal;
@ -104,7 +106,12 @@ namespace ImageSharp.Drawing
/// defined by the location and width, if <see cref="WrapTextWidth"/> equals zero, and thus
/// wrapping disabled, then the alignment is relative to the drawing location.
/// </summary>
public TextAlignment TextAlignment { get => this.textAlignment ?? TextAlignment.Left; set => this.textAlignment = value; }
public HorizontalAlignment HorizontalAlignment { get => this.horizontalAlignment ?? HorizontalAlignment.Left; set => this.horizontalAlignment = value; }
/// <summary>
/// Gets or sets a value indicating how to align the text relative to the rendering space.
/// </summary>
public VerticalAlignment VerticalAlignment { get => this.verticalAlignment ?? VerticalAlignment.Top; set => this.verticalAlignment = value; }
/// <summary>
/// Performs an implicit conversion from <see cref="GraphicsOptions"/> to <see cref="TextGraphicsOptions"/>.

45
src/ImageSharp/Common/Extensions/ByteExtensions.cs

@ -5,10 +5,13 @@
namespace ImageSharp
{
using System;
using System.Runtime.CompilerServices;
using ImageSharp.PixelFormats;
/// <summary>
/// Extension methods for the <see cref="byte"/> struct.
/// Extension methods for the <see cref="byte"/> struct buffers.
/// </summary>
internal static class ByteExtensions
{
@ -44,5 +47,45 @@ namespace ImageSharp
j--;
}
}
/// <summary>
/// Returns a reference to the given position of the array unsafe casted to <see cref="ImageSharp.PixelFormats.Rgb24"/>.
/// </summary>
/// <param name="bytes">The byte array.</param>
/// <param name="offset">The offset in bytes.</param>
/// <returns>The <see cref="ImageSharp.PixelFormats.Rgb24"/> reference at the given offset.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref Rgb24 GetRgb24(this byte[] bytes, int offset)
{
DebugGuard.MustBeLessThan(offset + 2, bytes.Length, nameof(offset));
return ref Unsafe.As<byte, Rgb24>(ref bytes[offset]);
}
/// <summary>
/// Returns a reference to the given position of the span unsafe casted to <see cref="ImageSharp.PixelFormats.Rgb24"/>.
/// </summary>
/// <param name="bytes">The byte span.</param>
/// <param name="offset">The offset in bytes.</param>
/// <returns>The <see cref="ImageSharp.PixelFormats.Rgb24"/> reference at the given offset.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref Rgb24 GetRgb24(this Span<byte> bytes, int offset)
{
DebugGuard.MustBeLessThan(offset + 2, bytes.Length, nameof(offset));
return ref Unsafe.As<byte, Rgb24>(ref bytes[offset]);
}
/// <summary>
/// Returns a reference to the given position of the buffer pointed by `baseRef` unsafe casted to <see cref="ImageSharp.PixelFormats.Rgb24"/>.
/// </summary>
/// <param name="baseRef">A reference to the beginning of the buffer</param>
/// <param name="offset">The offset in bytes.</param>
/// <returns>The <see cref="ImageSharp.PixelFormats.Rgb24"/> reference at the given offset.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref Rgb24 GetRgb24(ref byte baseRef, int offset)
{
return ref Unsafe.As<byte, Rgb24>(ref Unsafe.Add(ref baseRef, offset));
}
}
}

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

@ -242,7 +242,6 @@ namespace ImageSharp
/// <paramref name="target"/> is true
/// </exception>
public static void MustBeSizedAtLeast<T>(Span<T> target, int minLength, string parameterName)
where T : struct
{
if (target.Length < minLength)
{

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

@ -6,7 +6,8 @@ namespace ImageSharp.Formats
{
using System;
using System.IO;
using System.Runtime.CompilerServices;
using ImageSharp.Memory;
using ImageSharp.PixelFormats;
/// <summary>
@ -243,13 +244,16 @@ namespace ImageSharp.Formats
byte[] row = new byte[arrayWidth + padding];
TPixel color = default(TPixel);
Rgba32 rgba = default(Rgba32);
for (int y = 0; y < height; y++)
{
int newY = Invert(y, height, inverted);
this.currentStream.Read(row, 0, row.Length);
int offset = 0;
Span<TPixel> pixelRow = pixels.GetRowSpan(y);
// TODO: Could use PixelOperations here!
for (int x = 0; x < arrayWidth; x++)
{
int colOffset = x * ppb;
@ -260,8 +264,9 @@ namespace ImageSharp.Formats
int newX = colOffset + shift;
// Stored in b-> g-> r order.
color.PackFromBytes(colors[colorIndex + 2], colors[colorIndex + 1], colors[colorIndex], 255);
pixels[newX, newY] = color;
rgba.Bgr = Unsafe.As<byte, Bgr24>(ref colors[colorIndex]);
color.PackFromRgba32(rgba);
pixelRow[newX] = color;
}
offset++;
@ -286,6 +291,8 @@ namespace ImageSharp.Formats
const int ComponentCount = 2;
TPixel color = default(TPixel);
Rgba32 rgba = new Rgba32(0, 0, 0, 255);
using (PixelArea<TPixel> row = new PixelArea<TPixel>(width, ComponentOrder.Xyz))
{
for (int y = 0; y < height; y++)
@ -294,17 +301,19 @@ namespace ImageSharp.Formats
int newY = Invert(y, height, inverted);
Span<TPixel> pixelRow = pixels.GetRowSpan(newY);
int offset = 0;
for (int x = 0; x < width; x++)
{
short temp = BitConverter.ToInt16(row.Bytes, offset);
byte r = (byte)(((temp & Rgb16RMask) >> 11) * ScaleR);
byte g = (byte)(((temp & Rgb16GMask) >> 5) * ScaleG);
byte b = (byte)((temp & Rgb16BMask) * ScaleR);
rgba.R = (byte)(((temp & Rgb16RMask) >> 11) * ScaleR);
rgba.G = (byte)(((temp & Rgb16GMask) >> 5) * ScaleG);
rgba.B = (byte)((temp & Rgb16BMask) * ScaleR);
color.PackFromBytes(r, g, b, 255);
pixels[x, newY] = color;
color.PackFromRgba32(rgba);
pixelRow[x] = color;
offset += ComponentCount;
}
}

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

@ -435,6 +435,8 @@ namespace ImageSharp.Formats
Span<TPixel> rowSpan = image.GetRowSpan(writeY);
Rgba32 rgba = new Rgba32(0, 0, 0, 255);
for (int x = descriptor.Left; x < descriptor.Left + descriptor.Width; x++)
{
int index = indices[i];
@ -446,7 +448,9 @@ namespace ImageSharp.Formats
int indexOffset = index * 3;
ref TPixel pixel = ref rowSpan[x];
pixel.PackFromBytes(colorTable[indexOffset], colorTable[indexOffset + 1], colorTable[indexOffset + 2], 255);
rgba.Rgb = colorTable.GetRgb24(indexOffset);
pixel.PackFromRgba32(rgba);
}
i++;

125
src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs

@ -5,53 +5,120 @@
// ReSharper disable InconsistentNaming
// <auto-generated />
#pragma warning disable
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
namespace ImageSharp.Formats.Jpg
{
using System.Numerics;
using System.Runtime.CompilerServices;
internal partial struct Block8x8F
{
private static readonly Vector4 CMin4 = new Vector4(-128f);
private static readonly Vector4 CMax4 = new Vector4(127f);
private static readonly Vector4 COff4 = new Vector4(128f);
private static readonly Vector4 CMin4 = new Vector4(0F);
private static readonly Vector4 CMax4 = new Vector4(255F);
private static readonly Vector4 COff4 = new Vector4(128F);
/// <summary>
/// Transpose the block into d
/// Transpose the block into the destination block.
/// </summary>
/// <param name="d">Destination</param>
/// <param name="d">The destination block</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void TransposeInto(ref Block8x8F d)
{
d.V0L.X = V0L.X; d.V1L.X = V0L.Y; d.V2L.X = V0L.Z; d.V3L.X = V0L.W; d.V4L.X = V0R.X; d.V5L.X = V0R.Y; d.V6L.X = V0R.Z; d.V7L.X = V0R.W;
d.V0L.Y = V1L.X; d.V1L.Y = V1L.Y; d.V2L.Y = V1L.Z; d.V3L.Y = V1L.W; d.V4L.Y = V1R.X; d.V5L.Y = V1R.Y; d.V6L.Y = V1R.Z; d.V7L.Y = V1R.W;
d.V0L.Z = V2L.X; d.V1L.Z = V2L.Y; d.V2L.Z = V2L.Z; d.V3L.Z = V2L.W; d.V4L.Z = V2R.X; d.V5L.Z = V2R.Y; d.V6L.Z = V2R.Z; d.V7L.Z = V2R.W;
d.V0L.W = V3L.X; d.V1L.W = V3L.Y; d.V2L.W = V3L.Z; d.V3L.W = V3L.W; d.V4L.W = V3R.X; d.V5L.W = V3R.Y; d.V6L.W = V3R.Z; d.V7L.W = V3R.W;
d.V0R.X = V4L.X; d.V1R.X = V4L.Y; d.V2R.X = V4L.Z; d.V3R.X = V4L.W; d.V4R.X = V4R.X; d.V5R.X = V4R.Y; d.V6R.X = V4R.Z; d.V7R.X = V4R.W;
d.V0R.Y = V5L.X; d.V1R.Y = V5L.Y; d.V2R.Y = V5L.Z; d.V3R.Y = V5L.W; d.V4R.Y = V5R.X; d.V5R.Y = V5R.Y; d.V6R.Y = V5R.Z; d.V7R.Y = V5R.W;
d.V0R.Z = V6L.X; d.V1R.Z = V6L.Y; d.V2R.Z = V6L.Z; d.V3R.Z = V6L.W; d.V4R.Z = V6R.X; d.V5R.Z = V6R.Y; d.V6R.Z = V6R.Z; d.V7R.Z = V6R.W;
d.V0R.W = V7L.X; d.V1R.W = V7L.Y; d.V2R.W = V7L.Z; d.V3R.W = V7L.W; d.V4R.W = V7R.X; d.V5R.W = V7R.Y; d.V6R.W = V7R.Z; d.V7R.W = V7R.W;
d.V0L.X = V0L.X;
d.V1L.X = V0L.Y;
d.V2L.X = V0L.Z;
d.V3L.X = V0L.W;
d.V4L.X = V0R.X;
d.V5L.X = V0R.Y;
d.V6L.X = V0R.Z;
d.V7L.X = V0R.W;
d.V0L.Y = V1L.X;
d.V1L.Y = V1L.Y;
d.V2L.Y = V1L.Z;
d.V3L.Y = V1L.W;
d.V4L.Y = V1R.X;
d.V5L.Y = V1R.Y;
d.V6L.Y = V1R.Z;
d.V7L.Y = V1R.W;
d.V0L.Z = V2L.X;
d.V1L.Z = V2L.Y;
d.V2L.Z = V2L.Z;
d.V3L.Z = V2L.W;
d.V4L.Z = V2R.X;
d.V5L.Z = V2R.Y;
d.V6L.Z = V2R.Z;
d.V7L.Z = V2R.W;
d.V0L.W = V3L.X;
d.V1L.W = V3L.Y;
d.V2L.W = V3L.Z;
d.V3L.W = V3L.W;
d.V4L.W = V3R.X;
d.V5L.W = V3R.Y;
d.V6L.W = V3R.Z;
d.V7L.W = V3R.W;
d.V0R.X = V4L.X;
d.V1R.X = V4L.Y;
d.V2R.X = V4L.Z;
d.V3R.X = V4L.W;
d.V4R.X = V4R.X;
d.V5R.X = V4R.Y;
d.V6R.X = V4R.Z;
d.V7R.X = V4R.W;
d.V0R.Y = V5L.X;
d.V1R.Y = V5L.Y;
d.V2R.Y = V5L.Z;
d.V3R.Y = V5L.W;
d.V4R.Y = V5R.X;
d.V5R.Y = V5R.Y;
d.V6R.Y = V5R.Z;
d.V7R.Y = V5R.W;
d.V0R.Z = V6L.X;
d.V1R.Z = V6L.Y;
d.V2R.Z = V6L.Z;
d.V3R.Z = V6L.W;
d.V4R.Z = V6R.X;
d.V5R.Z = V6R.Y;
d.V6R.Z = V6R.Z;
d.V7R.Z = V6R.W;
d.V0R.W = V7L.X;
d.V1R.W = V7L.Y;
d.V2R.W = V7L.Z;
d.V3R.W = V7L.W;
d.V4R.W = V7R.X;
d.V5R.W = V7R.Y;
d.V6R.W = V7R.Z;
d.V7R.W = V7R.W;
}
/// <summary>
/// Level shift by +128, clip to [0, 255]
/// </summary>
/// <param name="d">Destination</param>
/// <param name="d">The destination block</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void TransformByteConvetibleColorValuesInto(ref Block8x8F d)
{
d.V0L = Vector4.Max(Vector4.Min(V0L, CMax4), CMin4) + COff4;d.V0R = Vector4.Max(Vector4.Min(V0R, CMax4), CMin4) + COff4;
d.V1L = Vector4.Max(Vector4.Min(V1L, CMax4), CMin4) + COff4;d.V1R = Vector4.Max(Vector4.Min(V1R, CMax4), CMin4) + COff4;
d.V2L = Vector4.Max(Vector4.Min(V2L, CMax4), CMin4) + COff4;d.V2R = Vector4.Max(Vector4.Min(V2R, CMax4), CMin4) + COff4;
d.V3L = Vector4.Max(Vector4.Min(V3L, CMax4), CMin4) + COff4;d.V3R = Vector4.Max(Vector4.Min(V3R, CMax4), CMin4) + COff4;
d.V4L = Vector4.Max(Vector4.Min(V4L, CMax4), CMin4) + COff4;d.V4R = Vector4.Max(Vector4.Min(V4R, CMax4), CMin4) + COff4;
d.V5L = Vector4.Max(Vector4.Min(V5L, CMax4), CMin4) + COff4;d.V5R = Vector4.Max(Vector4.Min(V5R, CMax4), CMin4) + COff4;
d.V6L = Vector4.Max(Vector4.Min(V6L, CMax4), CMin4) + COff4;d.V6R = Vector4.Max(Vector4.Min(V6R, CMax4), CMin4) + COff4;
d.V7L = Vector4.Max(Vector4.Min(V7L, CMax4), CMin4) + COff4;d.V7R = Vector4.Max(Vector4.Min(V7R, CMax4), CMin4) + COff4;
d.V0L = Vector4.Clamp(V0L + COff4, CMin4, CMax4);
d.V0R = Vector4.Clamp(V0R + COff4, CMin4, CMax4);
d.V1L = Vector4.Clamp(V1L + COff4, CMin4, CMax4);
d.V1R = Vector4.Clamp(V1R + COff4, CMin4, CMax4);
d.V2L = Vector4.Clamp(V2L + COff4, CMin4, CMax4);
d.V2R = Vector4.Clamp(V2R + COff4, CMin4, CMax4);
d.V3L = Vector4.Clamp(V3L + COff4, CMin4, CMax4);
d.V3R = Vector4.Clamp(V3R + COff4, CMin4, CMax4);
d.V4L = Vector4.Clamp(V4L + COff4, CMin4, CMax4);
d.V4R = Vector4.Clamp(V4R + COff4, CMin4, CMax4);
d.V5L = Vector4.Clamp(V5L + COff4, CMin4, CMax4);
d.V5R = Vector4.Clamp(V5R + COff4, CMin4, CMax4);
d.V6L = Vector4.Clamp(V6L + COff4, CMin4, CMax4);
d.V6R = Vector4.Clamp(V6R + COff4, CMin4, CMax4);
d.V7L = Vector4.Clamp(V7L + COff4, CMin4, CMax4);
d.V7R = Vector4.Clamp(V7R + COff4, CMin4, CMax4);
}
}
}

38
src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt

@ -11,31 +11,29 @@
<#@ output extension=".cs" #>
// <auto-generated />
#pragma warning disable
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
<#
char[] coordz = {'X', 'Y', 'Z', 'W'};
#>
namespace ImageSharp.Formats.Jpg
{
using System.Numerics;
using System.Runtime.CompilerServices;
internal partial struct Block8x8F
{
private static readonly Vector4 CMin4 = new Vector4(-128f);
private static readonly Vector4 CMax4 = new Vector4(127f);
private static readonly Vector4 COff4 = new Vector4(128f);
private static readonly Vector4 CMin4 = new Vector4(0F);
private static readonly Vector4 CMax4 = new Vector4(255F);
private static readonly Vector4 COff4 = new Vector4(128F);
/// <summary>
/// Transpose the block into d
/// Transpose the block into the destination block.
/// </summary>
/// <param name="d">Destination</param>
/// <param name="d">The destination block</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void TransposeInto(ref Block8x8F d)
{
<#
PushIndent(" ");
PushIndent(" ");
for (int i = 0; i < 8; i++)
{
@ -44,13 +42,16 @@ namespace ImageSharp.Formats.Jpg
for (int j = 0; j < 8; j++)
{
if(i > 0 && j == 0){
WriteLine("");
}
char srcCoord = coordz[j % 4];
char srcSide = (j / 4) % 2 == 0 ? 'L' : 'R';
string expression = $"d.V{j}{destSide}.{destCoord} = V{i}{srcSide}.{srcCoord}; ";
string expression = $"d.V{j}{destSide}.{destCoord} = V{i}{srcSide}.{srcCoord};\r\n";
Write(expression);
}
WriteLine("");
}
PopIndent();
#>
@ -59,27 +60,24 @@ namespace ImageSharp.Formats.Jpg
/// <summary>
/// Level shift by +128, clip to [0, 255]
/// </summary>
/// <param name="d">Destination</param>
/// <param name="d">The destination block</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void TransformByteConvetibleColorValuesInto(ref Block8x8F d)
{
<#
PushIndent(" ");
PushIndent(" ");
for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 2; j++)
{
char side = j == 0 ? 'L' : 'R';
Write($"d.V{i}{side} = Vector4.Max(Vector4.Min(V{i}{side}, CMax4), CMin4) + COff4;");
Write($"d.V{i}{side} = Vector4.Clamp(V{i}{side} + COff4, CMin4, CMax4);\r\n");
}
WriteLine("");
}
PopIndent();
#>
}
}
}

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

@ -57,6 +57,9 @@ namespace ImageSharp.Formats.Jpg
public Vector4 V7R;
#pragma warning restore SA1600 // ElementsMustBeDocumented
private static readonly Vector4 NegativeOne = new Vector4(-1);
private static readonly Vector4 Offset = new Vector4(.5F);
/// <summary>
/// Get/Set scalar elements at a given index
/// </summary>
@ -402,12 +405,11 @@ namespace ImageSharp.Formats.Jpg
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector4 DivideRound(Vector4 dividend, Vector4 divisor)
{
// sign(v) = max(min(v, 1), -1)
Vector4 sign = Vector4.Min(dividend, Vector4.One);
sign = Vector4.Max(sign, new Vector4(-1));
// sign(dividend) = max(min(dividend, 1), -1)
var sign = Vector4.Clamp(dividend, NegativeOne, Vector4.One);
// AlmostRound(dividend/divisor) = dividend/divisior + 0.5*sign(dividend)
return (dividend / divisor) + (sign * new Vector4(0.5f));
return (dividend / divisor) + (sign * Offset);
}
}
}

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

@ -91,7 +91,7 @@ namespace ImageSharp.Formats.Jpg
// float b = MathF.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero);
byte b = (byte)(y + tables->CbBTable[cb]).Clamp(0, 255);
packed.PackFromBytes(r, g, b, byte.MaxValue);
packed.PackFromRgba32(new Rgba32(r, g, b, 255));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]

77
src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs

@ -613,25 +613,23 @@ namespace ImageSharp.Formats
private void ConvertFromGrayScale<TPixel>(Image<TPixel> image)
where TPixel : struct, IPixel<TPixel>
{
using (PixelAccessor<TPixel> pixels = image.Lock())
{
Parallel.For(
0,
image.Height,
image.Configuration.ParallelOptions,
y =>
Parallel.For(
0,
image.Height,
image.Configuration.ParallelOptions,
y =>
{
ref TPixel pixelRowBaseRef = ref image.GetPixelReference(0, y);
int yoff = this.grayImage.GetRowOffset(y);
for (int x = 0; x < image.Width; x++)
{
byte rgb = this.grayImage.Pixels[yoff + x];
TPixel packed = default(TPixel);
packed.PackFromBytes(rgb, rgb, rgb, 255);
pixels[x, y] = packed;
ref TPixel pixel = ref Unsafe.Add(ref pixelRowBaseRef, x);
pixel.PackFromRgba32(new Rgba32(rgb, rgb, rgb, 255));
}
});
}
this.AssignResolution(image);
}
@ -646,30 +644,29 @@ namespace ImageSharp.Formats
{
int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor;
using (PixelAccessor<TPixel> pixels = image.Lock())
{
Parallel.For(
0,
image.Height,
image.Configuration.ParallelOptions,
y =>
Parallel.For(
0,
image.Height,
image.Configuration.ParallelOptions,
y =>
{
// TODO: Simplify + optimize + share duplicate code across converter methods
int yo = this.ycbcrImage.GetRowYOffset(y);
int co = this.ycbcrImage.GetRowCOffset(y);
ref TPixel pixelRowBaseRef = ref image.GetPixelReference(0, y);
Rgba32 rgba = new Rgba32(0, 0, 0, 255);
for (int x = 0; x < image.Width; x++)
{
byte red = this.ycbcrImage.YChannel[yo + x];
byte green = this.ycbcrImage.CbChannel[co + (x / scale)];
byte blue = this.ycbcrImage.CrChannel[co + (x / scale)];
rgba.R = this.ycbcrImage.YChannel[yo + x];
rgba.G = this.ycbcrImage.CbChannel[co + (x / scale)];
rgba.B = this.ycbcrImage.CrChannel[co + (x / scale)];
TPixel packed = default(TPixel);
packed.PackFromBytes(red, green, blue, 255);
pixels[x, y] = packed;
ref TPixel pixel = ref Unsafe.Add(ref pixelRowBaseRef, x);
pixel.PackFromRgba32(rgba);
}
});
}
this.AssignResolution(image);
}
@ -729,16 +726,15 @@ namespace ImageSharp.Formats
{
int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor;
using (PixelAccessor<TPixel> pixels = image.Lock())
{
Parallel.For(
0,
image.Height,
y =>
Parallel.For(
0,
image.Height,
y =>
{
// TODO: Simplify + optimize + share duplicate code across converter methods
int yo = this.ycbcrImage.GetRowYOffset(y);
int co = this.ycbcrImage.GetRowCOffset(y);
ref TPixel pixelRowBaseRef = ref image.GetPixelReference(0, y);
for (int x = 0; x < image.Width; x++)
{
@ -746,12 +742,10 @@ namespace ImageSharp.Formats
byte cb = this.ycbcrImage.CbChannel[co + (x / scale)];
byte cr = this.ycbcrImage.CrChannel[co + (x / scale)];
TPixel packed = default(TPixel);
this.PackYcck(ref packed, yy, cb, cr, x, y);
pixels[x, y] = packed;
ref TPixel pixel = ref Unsafe.Add(ref pixelRowBaseRef, x);
this.PackYcck(ref pixel, yy, cb, cr, x, y);
}
});
}
this.AssignResolution(image);
}
@ -860,7 +854,7 @@ namespace ImageSharp.Formats
byte g = (byte)(((m / 255F) * (1F - keyline)).Clamp(0, 1) * 255);
byte b = (byte)(((y / 255F) * (1F - keyline)).Clamp(0, 1) * 255);
packed.PackFromBytes(r, g, b, 255);
packed.PackFromRgba32(new Rgba32(r, g, b));
}
/// <summary>
@ -904,7 +898,7 @@ namespace ImageSharp.Formats
byte g = (byte)(((1 - magenta) * (1 - keyline)).Clamp(0, 1) * 255);
byte b = (byte)(((1 - yellow) * (1 - keyline)).Clamp(0, 1) * 255);
packed.PackFromBytes(r, g, b, 255);
packed.PackFromRgba32(new Rgba32(r, g, b));
}
/// <summary>
@ -982,6 +976,7 @@ namespace ImageSharp.Formats
byte[] identifier = new byte[Icclength];
this.InputProcessor.ReadFull(identifier, 0, Icclength);
remaining -= Icclength; // we have read it by this point
if (identifier[0] == 'I' &&
identifier[1] == 'C' &&
@ -996,7 +991,6 @@ namespace ImageSharp.Formats
identifier[10] == 'E' &&
identifier[11] == '\0')
{
remaining -= Icclength;
byte[] profile = new byte[remaining];
this.InputProcessor.ReadFull(profile, 0, remaining);
@ -1009,6 +1003,11 @@ namespace ImageSharp.Formats
metadata.IccProfile.Extend(profile);
}
}
else
{
// not an ICC profile we can handle read the remaining so we can carry on and ignore this.
this.InputProcessor.Skip(remaining);
}
}
/// <summary>

BIN
src/ImageSharp/Formats/Jpeg/itu-t81.pdf

Binary file not shown.

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

@ -178,7 +178,7 @@ namespace ImageSharp.Formats
/// <exception cref="ImageFormatException">
/// Thrown if the stream does not contain and end chunk.
/// </exception>
/// <exception cref="System.ArgumentOutOfRangeException">
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown if the image is larger than the maximum allowable size.
/// </exception>
/// <returns>The decoded image</returns>
@ -189,7 +189,6 @@ namespace ImageSharp.Formats
this.currentStream = stream;
this.currentStream.Skip(8);
Image<TPixel> image = null;
PixelAccessor<TPixel> pixels = null;
try
{
using (var deframeStream = new ZlibInflateStream(this.currentStream))
@ -211,11 +210,11 @@ namespace ImageSharp.Formats
case PngChunkTypes.Data:
if (image == null)
{
this.InitializeImage(metadata, out image, out pixels);
this.InitializeImage(metadata, out image);
}
deframeStream.AllocateNewBytes(currentChunk.Length);
this.ReadScanlines(deframeStream.CompressedStream, pixels);
this.ReadScanlines(deframeStream.CompressedStream, image);
stream.Read(this.crcBuffer, 0, 4);
break;
case PngChunkTypes.Palette:
@ -252,7 +251,6 @@ namespace ImageSharp.Formats
}
finally
{
pixels?.Dispose();
this.scanline?.Dispose();
this.previousScanline?.Dispose();
}
@ -324,8 +322,7 @@ namespace ImageSharp.Formats
/// <typeparam name="TPixel">The type the pixels will be</typeparam>
/// <param name="metadata">The metadata information for the image</param>
/// <param name="image">The image that we will populate</param>
/// <param name="pixels">The pixel accessor</param>
private void InitializeImage<TPixel>(ImageMetaData metadata, out Image<TPixel> image, out PixelAccessor<TPixel> pixels)
private void InitializeImage<TPixel>(ImageMetaData metadata, out Image<TPixel> image)
where TPixel : struct, IPixel<TPixel>
{
if (this.header.Width > Image<TPixel>.MaxWidth || this.header.Height > Image<TPixel>.MaxHeight)
@ -334,7 +331,6 @@ namespace ImageSharp.Formats
}
image = new Image<TPixel>(this.configuration, this.header.Width, this.header.Height, metadata);
pixels = image.Lock();
this.bytesPerPixel = this.CalculateBytesPerPixel();
this.bytesPerScanline = this.CalculateScanlineLength(this.header.Width) + 1;
this.bytesPerSample = 1;
@ -398,17 +394,17 @@ namespace ImageSharp.Formats
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="dataStream">The <see cref="MemoryStream"/> containing data.</param>
/// <param name="pixels"> The pixel data.</param>
private void ReadScanlines<TPixel>(Stream dataStream, PixelAccessor<TPixel> pixels)
/// <param name="image"> The pixel data.</param>
private void ReadScanlines<TPixel>(Stream dataStream, Image<TPixel> image)
where TPixel : struct, IPixel<TPixel>
{
if (this.header.InterlaceMethod == PngInterlaceMode.Adam7)
{
this.DecodeInterlacedPixelData(dataStream, pixels);
this.DecodeInterlacedPixelData(dataStream, image);
}
else
{
this.DecodePixelData(dataStream, pixels);
this.DecodePixelData(dataStream, image);
}
}
@ -417,8 +413,8 @@ namespace ImageSharp.Formats
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="compressedStream">The compressed pixel data stream.</param>
/// <param name="pixels">The image pixel accessor.</param>
private void DecodePixelData<TPixel>(Stream compressedStream, PixelAccessor<TPixel> pixels)
/// <param name="image">The image to decode to.</param>
private void DecodePixelData<TPixel>(Stream compressedStream, Image<TPixel> image)
where TPixel : struct, IPixel<TPixel>
{
while (this.currentRow < this.header.Height)
@ -462,7 +458,7 @@ namespace ImageSharp.Formats
throw new ImageFormatException("Unknown filter type.");
}
this.ProcessDefilteredScanline(this.scanline.Array, pixels);
this.ProcessDefilteredScanline(this.scanline.Array, image);
Swap(ref this.scanline, ref this.previousScanline);
this.currentRow++;
@ -475,8 +471,8 @@ namespace ImageSharp.Formats
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="compressedStream">The compressed pixel data stream.</param>
/// <param name="pixels">The image pixel accessor.</param>
private void DecodeInterlacedPixelData<TPixel>(Stream compressedStream, PixelAccessor<TPixel> pixels)
/// <param name="image">The current image.</param>
private void DecodeInterlacedPixelData<TPixel>(Stream compressedStream, Image<TPixel> image)
where TPixel : struct, IPixel<TPixel>
{
while (true)
@ -537,7 +533,8 @@ namespace ImageSharp.Formats
throw new ImageFormatException("Unknown filter type.");
}
this.ProcessInterlacedDefilteredScanline(this.scanline.Array, this.currentRow, pixels, Adam7FirstColumn[this.pass], Adam7ColumnIncrement[this.pass]);
Span<TPixel> rowSpan = image.GetRowSpan(this.currentRow);
this.ProcessInterlacedDefilteredScanline(this.scanline.Array, rowSpan, Adam7FirstColumn[this.pass], Adam7ColumnIncrement[this.pass]);
Swap(ref this.scanline, ref this.previousScanline);
@ -561,12 +558,12 @@ namespace ImageSharp.Formats
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="defilteredScanline">The de-filtered scanline</param>
/// <param name="pixels">The image pixels</param>
private void ProcessDefilteredScanline<TPixel>(byte[] defilteredScanline, PixelAccessor<TPixel> pixels)
/// <param name="pixels">The image</param>
private void ProcessDefilteredScanline<TPixel>(byte[] defilteredScanline, Image<TPixel> pixels)
where TPixel : struct, IPixel<TPixel>
{
var color = default(TPixel);
Span<TPixel> pixelBuffer = pixels.GetRowSpan(this.currentRow);
Span<TPixel> rowSpan = pixels.GetRowSpan(this.currentRow);
var scanlineBuffer = new Span<byte>(defilteredScanline, 1);
switch (this.PngColorType)
@ -577,8 +574,8 @@ namespace ImageSharp.Formats
for (int x = 0; x < this.header.Width; x++)
{
byte intensity = (byte)(newScanline1[x] * factor);
color.PackFromBytes(intensity, intensity, intensity, 255);
pixels[x, this.currentRow] = color;
color.PackFromRgba32(new Rgba32(intensity, intensity, intensity));
rowSpan[x] = color;
}
break;
@ -592,27 +589,27 @@ namespace ImageSharp.Formats
byte intensity = defilteredScanline[offset];
byte alpha = defilteredScanline[offset + this.bytesPerSample];
color.PackFromBytes(intensity, intensity, intensity, alpha);
pixels[x, this.currentRow] = color;
color.PackFromRgba32(new Rgba32(intensity, intensity, intensity));
rowSpan[x] = color;
}
break;
case PngColorType.Palette:
this.ProcessScanlineFromPalette(defilteredScanline, pixels);
this.ProcessScanlineFromPalette(defilteredScanline, rowSpan);
break;
case PngColorType.Rgb:
PixelOperations<TPixel>.Instance.PackFromXyzBytes(scanlineBuffer, pixelBuffer, this.header.Width);
PixelOperations<TPixel>.Instance.PackFromRgb24Bytes(scanlineBuffer, rowSpan, this.header.Width);
break;
case PngColorType.RgbWithAlpha:
PixelOperations<TPixel>.Instance.PackFromXyzwBytes(scanlineBuffer, pixelBuffer, this.header.Width);
PixelOperations<TPixel>.Instance.PackFromRgba32Bytes(scanlineBuffer, rowSpan, this.header.Width);
break;
}
@ -623,14 +620,16 @@ namespace ImageSharp.Formats
/// </summary>
/// <typeparam name="TPixel">The type of pixel we are expanding to</typeparam>
/// <param name="defilteredScanline">The scanline</param>
/// <param name="pixels">The output pixels</param>
private void ProcessScanlineFromPalette<TPixel>(byte[] defilteredScanline, PixelAccessor<TPixel> pixels)
/// <param name="row">Thecurrent output image row</param>
private void ProcessScanlineFromPalette<TPixel>(byte[] defilteredScanline, Span<TPixel> row)
where TPixel : struct, IPixel<TPixel>
{
byte[] newScanline = ToArrayByBitsLength(defilteredScanline, this.bytesPerScanline, this.header.BitDepth);
byte[] palette = this.palette;
var color = default(TPixel);
Rgba32 rgba = default(Rgba32);
if (this.paletteAlpha != null && this.paletteAlpha.Length > 0)
{
// If the alpha palette is not null and has one or more entries, this means, that the image contains an alpha
@ -640,36 +639,34 @@ namespace ImageSharp.Formats
int index = newScanline[x + 1];
int pixelOffset = index * 3;
byte a = this.paletteAlpha.Length > index ? this.paletteAlpha[index] : (byte)255;
rgba.A = this.paletteAlpha.Length > index ? this.paletteAlpha[index] : (byte)255;
if (a > 0)
if (rgba.A > 0)
{
byte r = palette[pixelOffset];
byte g = palette[pixelOffset + 1];
byte b = palette[pixelOffset + 2];
color.PackFromBytes(r, g, b, a);
rgba.Rgb = palette.GetRgb24(pixelOffset);
}
else
{
color.PackFromBytes(0, 0, 0, 0);
rgba = default(Rgba32);
}
pixels[x, this.currentRow] = color;
color.PackFromRgba32(rgba);
row[x] = color;
}
}
else
{
rgba.A = 255;
for (int x = 0; x < this.header.Width; x++)
{
int index = newScanline[x + 1];
int pixelOffset = index * 3;
byte r = palette[pixelOffset];
byte g = palette[pixelOffset + 1];
byte b = palette[pixelOffset + 2];
rgba.Rgb = palette.GetRgb24(pixelOffset);
color.PackFromBytes(r, g, b, 255);
pixels[x, this.currentRow] = color;
color.PackFromRgba32(rgba);
row[x] = color;
}
}
}
@ -679,11 +676,10 @@ namespace ImageSharp.Formats
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="defilteredScanline">The de-filtered scanline</param>
/// <param name="row">The current image row.</param>
/// <param name="pixels">The image pixels</param>
/// <param name="rowSpan">The current image row.</param>
/// <param name="pixelOffset">The column start index. Always 0 for none interlaced images.</param>
/// <param name="increment">The column increment. Always 1 for none interlaced images.</param>
private void ProcessInterlacedDefilteredScanline<TPixel>(byte[] defilteredScanline, int row, PixelAccessor<TPixel> pixels, int pixelOffset = 0, int increment = 1)
private void ProcessInterlacedDefilteredScanline<TPixel>(byte[] defilteredScanline, Span<TPixel> rowSpan, int pixelOffset = 0, int increment = 1)
where TPixel : struct, IPixel<TPixel>
{
var color = default(TPixel);
@ -696,8 +692,8 @@ namespace ImageSharp.Formats
for (int x = pixelOffset, o = 1; x < this.header.Width; x += increment, o++)
{
byte intensity = (byte)(newScanline1[o] * factor);
color.PackFromBytes(intensity, intensity, intensity, 255);
pixels[x, row] = color;
color.PackFromRgba32(new Rgba32(intensity, intensity, intensity));
rowSpan[x] = color;
}
break;
@ -708,9 +704,8 @@ namespace ImageSharp.Formats
{
byte intensity = defilteredScanline[o];
byte alpha = defilteredScanline[o + this.bytesPerSample];
color.PackFromBytes(intensity, intensity, intensity, alpha);
pixels[x, row] = color;
color.PackFromRgba32(new Rgba32(intensity, intensity, intensity, alpha));
rowSpan[x] = color;
}
break;
@ -718,6 +713,7 @@ namespace ImageSharp.Formats
case PngColorType.Palette:
byte[] newScanline = ToArrayByBitsLength(defilteredScanline, this.bytesPerScanline, this.header.BitDepth);
Rgba32 rgba = default(Rgba32);
if (this.paletteAlpha != null && this.paletteAlpha.Length > 0)
{
@ -728,36 +724,34 @@ namespace ImageSharp.Formats
int index = newScanline[o];
int offset = index * 3;
byte a = this.paletteAlpha.Length > index ? this.paletteAlpha[index] : (byte)255;
rgba.A = this.paletteAlpha.Length > index ? this.paletteAlpha[index] : (byte)255;
if (a > 0)
if (rgba.A > 0)
{
byte r = this.palette[offset];
byte g = this.palette[offset + 1];
byte b = this.palette[offset + 2];
color.PackFromBytes(r, g, b, a);
rgba.Rgb = this.palette.GetRgb24(offset);
}
else
{
color.PackFromBytes(0, 0, 0, 0);
rgba = default(Rgba32);
}
pixels[x, row] = color;
color.PackFromRgba32(rgba);
rowSpan[x] = color;
}
}
else
{
rgba.A = 255;
for (int x = pixelOffset, o = 1; x < this.header.Width; x += increment, o++)
{
int index = newScanline[o];
int offset = index * 3;
byte r = this.palette[offset];
byte g = this.palette[offset + 1];
byte b = this.palette[offset + 2];
rgba.Rgb = this.palette.GetRgb24(offset);
color.PackFromBytes(r, g, b, 255);
pixels[x, row] = color;
color.PackFromRgba32(rgba);
rowSpan[x] = color;
}
}
@ -765,14 +759,15 @@ namespace ImageSharp.Formats
case PngColorType.Rgb:
rgba.A = 255;
for (int x = pixelOffset, o = 1; x < this.header.Width; x += increment, o += this.bytesPerPixel)
{
byte r = defilteredScanline[o];
byte g = defilteredScanline[o + this.bytesPerSample];
byte b = defilteredScanline[o + (2 * this.bytesPerSample)];
rgba.R = defilteredScanline[o];
rgba.G = defilteredScanline[o + this.bytesPerSample];
rgba.B = defilteredScanline[o + (2 * this.bytesPerSample)];
color.PackFromBytes(r, g, b, 255);
pixels[x, row] = color;
color.PackFromRgba32(rgba);
rowSpan[x] = color;
}
break;
@ -781,13 +776,13 @@ namespace ImageSharp.Formats
for (int x = pixelOffset, o = 1; x < this.header.Width; x += increment, o += this.bytesPerPixel)
{
byte r = defilteredScanline[o];
byte g = defilteredScanline[o + this.bytesPerSample];
byte b = defilteredScanline[o + (2 * this.bytesPerSample)];
byte a = defilteredScanline[o + (3 * this.bytesPerSample)];
rgba.R = defilteredScanline[o];
rgba.G = defilteredScanline[o + this.bytesPerSample];
rgba.B = defilteredScanline[o + (2 * this.bytesPerSample)];
rgba.A = defilteredScanline[o + (3 * this.bytesPerSample)];
color.PackFromBytes(r, g, b, a);
pixels[x, row] = color;
color.PackFromRgba32(rgba);
rowSpan[x] = color;
}
break;

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

@ -220,11 +220,7 @@ namespace ImageSharp.Formats
this.WritePhysicalChunk(stream, image);
this.WriteGammaChunk(stream);
using (PixelAccessor<TPixel> pixels = image.Lock())
{
this.WriteDataChunks(pixels, stream);
}
this.WriteDataChunks(image, stream);
this.WriteEndChunk(stream);
stream.Flush();
}
@ -302,9 +298,8 @@ namespace ImageSharp.Formats
/// Collects a row of grayscale pixels.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="pixels">The image pixels accessor.</param>
/// <param name="row">The row index.</param>
private void CollectGrayscaleBytes<TPixel>(PixelAccessor<TPixel> pixels, int row)
/// <param name="rowSpan">The image row span.</param>
private void CollectGrayscaleBytes<TPixel>(Span<TPixel> rowSpan)
where TPixel : struct, IPixel<TPixel>
{
byte[] rawScanlineArray = this.rawScanline.Array;
@ -316,7 +311,7 @@ namespace ImageSharp.Formats
// Convert the color to YCbCr and store the luminance
// Optionally store the original color alpha.
int offset = x * this.bytesPerPixel;
pixels[x, row].ToXyzwBytes(this.chunkTypeBuffer, 0);
rowSpan[x].ToXyzwBytes(this.chunkTypeBuffer, 0);
byte luminance = (byte)((0.299F * this.chunkTypeBuffer[0]) + (0.587F * this.chunkTypeBuffer[1]) + (0.114F * this.chunkTypeBuffer[2]));
for (int i = 0; i < this.bytesPerPixel; i++)
@ -337,20 +332,17 @@ namespace ImageSharp.Formats
/// Collects a row of true color pixel data.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="pixels">The image pixel accessor.</param>
/// <param name="row">The row index.</param>
private void CollecTPixelBytes<TPixel>(PixelAccessor<TPixel> pixels, int row)
/// <param name="rowSpan">The row span.</param>
private void CollecTPixelBytes<TPixel>(Span<TPixel> rowSpan)
where TPixel : struct, IPixel<TPixel>
{
Span<TPixel> rowSpan = pixels.GetRowSpan(row);
if (this.bytesPerPixel == 4)
{
PixelOperations<TPixel>.Instance.ToXyzwBytes(rowSpan, this.rawScanline, this.width);
PixelOperations<TPixel>.Instance.ToRgba32Bytes(rowSpan, this.rawScanline, this.width);
}
else
{
PixelOperations<TPixel>.Instance.ToXyzBytes(rowSpan, this.rawScanline, this.width);
PixelOperations<TPixel>.Instance.ToRgb24Bytes(rowSpan, this.rawScanline, this.width);
}
}
@ -359,10 +351,10 @@ namespace ImageSharp.Formats
/// Each scanline is encoded in the most optimal manner to improve compression.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="pixels">The image pixel accessor.</param>
/// <param name="rowSpan">The row span.</param>
/// <param name="row">The row.</param>
/// <returns>The <see cref="T:byte[]"/></returns>
private Buffer<byte> EncodePixelRow<TPixel>(PixelAccessor<TPixel> pixels, int row)
private Buffer<byte> EncodePixelRow<TPixel>(Span<TPixel> rowSpan, int row)
where TPixel : struct, IPixel<TPixel>
{
switch (this.pngColorType)
@ -372,10 +364,10 @@ namespace ImageSharp.Formats
break;
case PngColorType.Grayscale:
case PngColorType.GrayscaleWithAlpha:
this.CollectGrayscaleBytes(pixels, row);
this.CollectGrayscaleBytes(rowSpan);
break;
default:
this.CollecTPixelBytes(pixels, row);
this.CollecTPixelBytes(rowSpan);
break;
}
@ -637,17 +629,17 @@ namespace ImageSharp.Formats
/// Writes the pixel information to the stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="pixels">The pixel accessor.</param>
/// <param name="pixels">The image.</param>
/// <param name="stream">The stream.</param>
private void WriteDataChunks<TPixel>(PixelAccessor<TPixel> pixels, Stream stream)
private void WriteDataChunks<TPixel>(Image<TPixel> pixels, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
this.bytesPerScanline = this.width * this.bytesPerPixel;
int resultLength = this.bytesPerScanline + 1;
this.previousScanline = new Buffer<byte>(this.bytesPerScanline);
this.rawScanline = new Buffer<byte>(this.bytesPerScanline);
this.result = new Buffer<byte>(resultLength);
this.previousScanline = Buffer<byte>.CreateClean(this.bytesPerScanline);
this.rawScanline = Buffer<byte>.CreateClean(this.bytesPerScanline);
this.result = Buffer<byte>.CreateClean(resultLength);
if (this.pngColorType != PngColorType.Palette)
{
@ -667,7 +659,7 @@ namespace ImageSharp.Formats
{
for (int y = 0; y < this.height; y++)
{
Buffer<byte> r = this.EncodePixelRow(pixels, y);
Buffer<byte> r = this.EncodePixelRow(pixels.GetRowSpan(y), y);
deflateStream.Write(r.Array, 0, resultLength);
Swap(ref this.rawScanline, ref this.previousScanline);

2
src/ImageSharp/Image/ImageBase{TPixel}.cs

@ -11,7 +11,7 @@ namespace ImageSharp
using ImageSharp.Memory;
using ImageSharp.PixelFormats;
using Processing;
using ImageSharp.Processing;
/// <summary>
/// The base class of all images. Encapsulates the basic properties and methods required to manipulate

2
src/ImageSharp/Image/ImageProcessingExtensions.cs

@ -7,7 +7,7 @@ namespace ImageSharp
{
using ImageSharp.PixelFormats;
using Processing;
using ImageSharp.Processing;
/// <summary>
/// Extension methods for the <see cref="Image{TPixel}"/> type.

2
src/ImageSharp/Image/Image{TPixel}.cs

@ -15,7 +15,7 @@ namespace ImageSharp
using Formats;
using ImageSharp.PixelFormats;
using Processing;
using ImageSharp.Processing;
/// <summary>
/// Encapsulates an image, which consists of the pixel data for a graphics image and its attributes.

16
src/ImageSharp/Image/PixelAccessor{TPixel}.cs

@ -275,7 +275,7 @@ namespace ImageSharp
Span<byte> source = area.GetRowSpan(y);
Span<TPixel> destination = this.GetRowSpan(targetX, targetY + y);
Operations.PackFromZyxBytes(source, destination, width);
Operations.PackFromBgr24Bytes(source, destination, width);
}
}
@ -295,7 +295,7 @@ namespace ImageSharp
Span<byte> source = area.GetRowSpan(y);
Span<TPixel> destination = this.GetRowSpan(targetX, targetY + y);
Operations.PackFromZyxwBytes(source, destination, width);
Operations.PackFromBgra32Bytes(source, destination, width);
}
}
@ -315,7 +315,7 @@ namespace ImageSharp
Span<byte> source = area.GetRowSpan(y);
Span<TPixel> destination = this.GetRowSpan(targetX, targetY + y);
Operations.PackFromXyzBytes(source, destination, width);
Operations.PackFromRgb24Bytes(source, destination, width);
}
}
@ -334,7 +334,7 @@ namespace ImageSharp
{
Span<byte> source = area.GetRowSpan(y);
Span<TPixel> destination = this.GetRowSpan(targetX, targetY + y);
Operations.PackFromXyzwBytes(source, destination, width);
Operations.PackFromRgba32Bytes(source, destination, width);
}
}
@ -353,7 +353,7 @@ namespace ImageSharp
{
Span<TPixel> source = this.GetRowSpan(sourceX, sourceY + y);
Span<byte> destination = area.GetRowSpan(y);
Operations.ToZyxBytes(source, destination, width);
Operations.ToBgr24Bytes(source, destination, width);
}
}
@ -372,7 +372,7 @@ namespace ImageSharp
{
Span<TPixel> source = this.GetRowSpan(sourceX, sourceY + y);
Span<byte> destination = area.GetRowSpan(y);
Operations.ToZyxwBytes(source, destination, width);
Operations.ToBgra32Bytes(source, destination, width);
}
}
@ -391,7 +391,7 @@ namespace ImageSharp
{
Span<TPixel> source = this.GetRowSpan(sourceX, sourceY + y);
Span<byte> destination = area.GetRowSpan(y);
Operations.ToXyzBytes(source, destination, width);
Operations.ToRgb24Bytes(source, destination, width);
}
}
@ -410,7 +410,7 @@ namespace ImageSharp
{
Span<TPixel> source = this.GetRowSpan(sourceX, sourceY + y);
Span<byte> destination = area.GetRowSpan(y);
Operations.ToXyzwBytes(source, destination, width);
Operations.ToRgba32Bytes(source, destination, width);
}
}

29
src/ImageSharp/ImageSharp.csproj

@ -53,6 +53,35 @@
<ItemGroup>
<None Update="Formats\Jpeg\Components\Block8x8F.Generated.tt">
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>Block8x8F.Generated.cs</LastGenOutput>
</None>
<None Update="PixelFormats\Generated\PixelOperations{TPixel}.Generated.tt">
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>PixelOperations{TPixel}.Generated.cs</LastGenOutput>
</None>
<None Update="PixelFormats\Generated\Rgba32.PixelOperations.Generated.tt">
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>Rgba32.PixelOperations.Generated.cs</LastGenOutput>
</None>
</ItemGroup>
<ItemGroup>
<Service Include="{508349b6-6b84-4df5-91f0-309beebad82d}" />
</ItemGroup>
<ItemGroup>
<Compile Update="Formats\Jpeg\Components\Block8x8F.Generated.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Block8x8F.Generated.tt</DependentUpon>
</Compile>
<Compile Update="PixelFormats\Generated\PixelOperations{TPixel}.Generated.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>PixelOperations{TPixel}.Generated.tt</DependentUpon>
</Compile>
<Compile Update="PixelFormats\Generated\Rgba32.PixelOperations.Generated.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Rgba32.PixelOperations.Generated.tt</DependentUpon>
</Compile>
</ItemGroup>
</Project>

72
src/ImageSharp/Numerics/Matrix3x2Extensions.cs

@ -0,0 +1,72 @@
// <copyright file="Matrix3x2Extensions.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System.Numerics;
using System.Runtime.CompilerServices;
/// <summary>
/// Extension methods for the <see cref="Matrix3x2"/> struct
/// </summary>
public static class Matrix3x2Extensions
{
/// <summary>
/// Creates a rotation matrix for the given rotation in degrees and a center point.
/// </summary>
/// <param name="degree">The angle in degrees</param>
/// <param name="centerPoint">The center point</param>
/// <returns>The rotation <see cref="Matrix3x2"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Matrix3x2 CreateRotation(float degree, Point centerPoint)
{
float radian = MathF.DegreeToRadian(degree);
return Matrix3x2.CreateRotation(radian, new Vector2(centerPoint.X, centerPoint.Y));
}
/// <summary>
/// Creates a rotation matrix for the given rotation in degrees and a center point.
/// </summary>
/// <param name="degree">The angle in degrees</param>
/// <param name="centerPoint">The center point</param>
/// <returns>The rotation <see cref="Matrix3x2"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Matrix3x2 CreateRotation(float degree, PointF centerPoint)
{
float radian = MathF.DegreeToRadian(degree);
return Matrix3x2.CreateRotation(radian, centerPoint);
}
/// <summary>
/// Creates a skew matrix for the given angle in degrees and a center point.
/// </summary>
/// <param name="degreesX">The x-angle in degrees</param>
/// <param name="degreesY">The y-angle in degrees</param>
/// <param name="centerPoint">The center point</param>
/// <returns>The rotation <see cref="Matrix3x2"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Matrix3x2 CreateSkew(float degreesX, float degreesY, Point centerPoint)
{
float radiansX = MathF.DegreeToRadian(degreesX);
float radiansY = MathF.DegreeToRadian(degreesY);
return Matrix3x2.CreateSkew(radiansX, radiansY, new Vector2(centerPoint.X, centerPoint.Y));
}
/// <summary>
/// Creates a skew matrix for the given angle in degrees and a center point.
/// </summary>
/// <param name="degreesX">The x-angle in degrees</param>
/// <param name="degreesY">The y-angle in degrees</param>
/// <returns>The rotation <see cref="Matrix3x2"/></returns>
/// <param name="centerPoint">The center point</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Matrix3x2 CreateSkew(float degreesX, float degreesY, PointF centerPoint)
{
float radiansX = MathF.DegreeToRadian(degreesX);
float radiansY = MathF.DegreeToRadian(degreesY);
return Matrix3x2.CreateSkew(radiansX, radiansY, new Vector2(centerPoint.X, centerPoint.Y));
}
}
}

259
src/ImageSharp/Numerics/Point.cs

@ -25,6 +25,17 @@ namespace ImageSharp
/// </summary>
public static readonly Point Empty = default(Point);
/// <summary>
/// Initializes a new instance of the <see cref="Point"/> struct.
/// </summary>
/// <param name="value">The horizontal and vertical position of the point.</param>
public Point(int value)
: this()
{
this.X = LowInt16(value);
this.Y = HighInt16(value);
}
/// <summary>
/// Initializes a new instance of the <see cref="Point"/> struct.
/// </summary>
@ -38,15 +49,13 @@ namespace ImageSharp
}
/// <summary>
/// Initializes a new instance of the <see cref="Point"/> struct.
/// Initializes a new instance of the <see cref="Point"/> struct from the given <see cref="Size"/>.
/// </summary>
/// <param name="vector">
/// The vector representing the width and height.
/// </param>
public Point(Vector2 vector)
/// <param name="size">The size</param>
public Point(Size size)
{
this.X = (int)Math.Round(vector.X);
this.Y = (int)Math.Round(vector.Y);
this.X = size.Width;
this.Y = size.Height;
}
/// <summary>
@ -66,176 +75,160 @@ namespace ImageSharp
public bool IsEmpty => this.Equals(Empty);
/// <summary>
/// Computes the sum of adding two points.
/// Creates a <see cref="PointF"/> with the coordinates of the specified <see cref="Point"/>.
/// </summary>
/// <param name="left">The point on the left hand of the operand.</param>
/// <param name="right">The point on the right hand of the operand.</param>
/// <returns>
/// The <see cref="Point"/>
/// </returns>
/// <param name="point">The point</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Point operator +(Point left, Point right)
{
return new Point(left.X + right.X, left.Y + right.Y);
}
public static implicit operator PointF(Point point) => new PointF(point.X, point.Y);
/// <summary>
/// Creates a <see cref="Vector2"/> with the coordinates of the specified <see cref="Point"/>.
/// </summary>
/// <param name="point">The point</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator Vector2(Point point) => new Vector2(point.X, point.Y);
/// <summary>
/// Creates a <see cref="Size"/> with the coordinates of the specified <see cref="Point"/>.
/// </summary>
/// <param name="point">The point</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator Size(Point point) => new Size(point.X, point.Y);
/// <summary>
/// Computes the difference left by subtracting one point from another.
/// Translates a <see cref="Point"/> by a given <see cref="Size"/>.
/// </summary>
/// <param name="left">The point on the left hand of the operand.</param>
/// <param name="right">The point on the right hand of the operand.</param>
/// <param name="point">The point on the left hand of the operand.</param>
/// <param name="size">The size on the right hand of the operand.</param>
/// <returns>
/// The <see cref="Point"/>
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Point operator -(Point left, Point right)
{
return new Point(left.X - right.X, left.Y - right.Y);
}
public static Point operator +(Point point, Size size) => Add(point, size);
/// <summary>
/// Translates a <see cref="Point"/> by the negative of a given <see cref="Size"/>.
/// </summary>
/// <param name="point">The point on the left hand of the operand.</param>
/// <param name="size">The size on the right hand of the operand.</param>
/// <returns>The <see cref="Point"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Point operator -(Point point, Size size) => Subtract(point, size);
/// <summary>
/// Compares two <see cref="Point"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="Point"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Point"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="Point"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="Point"/> on the right side of the operand.</param>
/// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(Point left, Point right)
{
return left.Equals(right);
}
public static bool operator ==(Point left, Point right) => left.Equals(right);
/// <summary>
/// Compares two <see cref="Point"/> objects for inequality.
/// </summary>
/// <param name="left">
/// The <see cref="Point"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Point"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="Point"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="Point"/> on the right side of the operand.</param>
/// <returns>
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(Point left, Point right)
{
return !left.Equals(right);
}
public static bool operator !=(Point left, Point right) => !left.Equals(right);
/// <summary>
/// Creates a rotation matrix for the given point and angle.
/// Translates a <see cref="Point"/> by the negative of a given <see cref="Size"/>.
/// </summary>
/// <param name="origin">The origin point to rotate around</param>
/// <param name="degrees">Rotation in degrees</param>
/// <returns>The rotation <see cref="Matrix3x2"/></returns>
public static Matrix3x2 CreateRotation(Point origin, float degrees)
{
float radians = MathF.DegreeToRadian(degrees);
return Matrix3x2.CreateRotation(radians, new Vector2(origin.X, origin.Y));
}
/// <param name="point">The point on the left hand of the operand.</param>
/// <param name="size">The size on the right hand of the operand.</param>
/// <returns>The <see cref="Point"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Point Add(Point point, Size size) => new Point(unchecked(point.X + size.Width), unchecked(point.Y + size.Height));
/// <summary>
/// Rotates a point around a given a rotation matrix.
/// Translates a <see cref="Point"/> by the negative of a given <see cref="Size"/>.
/// </summary>
/// <param name="point">The point to rotate</param>
/// <param name="rotation">Rotation matrix used</param>
/// <returns>The rotated <see cref="Point"/></returns>
public static Point Rotate(Point point, Matrix3x2 rotation)
{
return new Point(Vector2.Transform(new Vector2(point.X, point.Y), rotation));
}
/// <param name="point">The point on the left hand of the operand.</param>
/// <param name="size">The size on the right hand of the operand.</param>
/// <returns>The <see cref="Point"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Point Subtract(Point point, Size size) => new Point(unchecked(point.X - size.Width), unchecked(point.Y - size.Height));
/// <summary>
/// Rotates a point around a given origin by the specified angle in degrees.
/// Converts a <see cref="PointF"/> to a <see cref="Point"/> by performing a ceiling operation on all the coordinates.
/// </summary>
/// <param name="point">The point to rotate</param>
/// <param name="origin">The center point to rotate around.</param>
/// <param name="degrees">The angle in degrees.</param>
/// <returns>The rotated <see cref="Point"/></returns>
public static Point Rotate(Point point, Point origin, float degrees)
{
return new Point(Vector2.Transform(new Vector2(point.X, point.Y), CreateRotation(origin, degrees)));
}
/// <param name="point">The point</param>
/// <returns>The <see cref="Point"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Point Ceiling(PointF point) => new Point(unchecked((int)MathF.Ceiling(point.X)), unchecked((int)MathF.Ceiling(point.Y)));
/// <summary>
/// Creates a skew matrix for the given point and angle.
/// Converts a <see cref="PointF"/> to a <see cref="Point"/> by performing a round operation on all the coordinates.
/// </summary>
/// <param name="origin">The origin point to rotate around</param>
/// <param name="degreesX">The x-angle in degrees.</param>
/// <param name="degreesY">The y-angle in degrees.</param>
/// <returns>The rotation <see cref="Matrix3x2"/></returns>
public static Matrix3x2 CreateSkew(Point origin, float degreesX, float degreesY)
{
float radiansX = MathF.DegreeToRadian(degreesX);
float radiansY = MathF.DegreeToRadian(degreesY);
return Matrix3x2.CreateSkew(radiansX, radiansY, new Vector2(origin.X, origin.Y));
}
/// <param name="point">The point</param>
/// <returns>The <see cref="Point"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Point Round(PointF point) => new Point(unchecked((int)MathF.Round(point.X)), unchecked((int)MathF.Round(point.Y)));
/// <summary>
/// Skews a point using a given a skew matrix.
/// Converts a <see cref="PointF"/> to a <see cref="Point"/> by performing a truncate operation on all the coordinates.
/// </summary>
/// <param name="point">The point to rotate</param>
/// <param name="skew">Rotation matrix used</param>
/// <returns>The rotated <see cref="Point"/></returns>
public static Point Skew(Point point, Matrix3x2 skew)
{
return new Point(Vector2.Transform(new Vector2(point.X, point.Y), skew));
}
/// <param name="point">The point</param>
/// <returns>The <see cref="Point"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Point Truncate(PointF point) => new Point(unchecked((int)point.X), unchecked((int)point.Y));
/// <summary>
/// Skews a point around a given origin by the specified angles in degrees.
/// Converts a <see cref="Vector2"/> to a <see cref="Point"/> by performing a round operation on all the coordinates.
/// </summary>
/// <param name="point">The point to skew.</param>
/// <param name="origin">The center point to rotate around.</param>
/// <param name="degreesX">The x-angle in degrees.</param>
/// <param name="degreesY">The y-angle in degrees.</param>
/// <returns>The skewed <see cref="Point"/></returns>
public static Point Skew(Point point, Point origin, float degreesX, float degreesY)
{
return new Point(Vector2.Transform(new Vector2(point.X, point.Y), CreateSkew(origin, degreesX, degreesY)));
}
/// <param name="vector">The vector</param>
/// <returns>The <see cref="Point"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Point Round(Vector2 vector) => new Point(unchecked((int)MathF.Round(vector.X)), unchecked((int)MathF.Round(vector.Y)));
/// <summary>
/// Gets a <see cref="Vector2"/> representation for this <see cref="Point"/>.
/// Rotates a point around the given rotation matrix.
/// </summary>
/// <returns>A <see cref="Vector2"/> representation for this object.</returns>
public Vector2 ToVector2()
{
return new Vector2(this.X, this.Y);
}
/// <param name="point">The point to rotate</param>
/// <param name="rotation">Rotation matrix used</param>
/// <returns>The rotated <see cref="Point"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Point Rotate(Point point, Matrix3x2 rotation) => Round(Vector2.Transform(new Vector2(point.X, point.Y), rotation));
/// <summary>
/// Skews a point using the given skew matrix.
/// </summary>
/// <param name="point">The point to rotate</param>
/// <param name="skew">Rotation matrix used</param>
/// <returns>The rotated <see cref="Point"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Point Skew(Point point, Matrix3x2 skew) => Round(Vector2.Transform(new Vector2(point.X, point.Y), skew));
/// <summary>
/// Translates this <see cref="Point"/> by the specified amount.
/// </summary>
/// <param name="dx">The amount to offset the x-coordinate.</param>
/// <param name="dy">The amount to offset the y-coordinate.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Offset(int dx, int dy)
{
this.X += dx;
this.Y += dy;
unchecked
{
this.X += dx;
this.Y += dy;
}
}
/// <summary>
/// Translates this <see cref="Point"/> by the specified amount.
/// </summary>
/// <param name="p">The <see cref="Point"/> used offset this <see cref="Point"/>.</param>
public void Offset(Point p)
{
this.Offset(p.X, p.Y);
}
/// <param name="point">The <see cref="Point"/> used offset this <see cref="Point"/>.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Offset(Point point) => this.Offset(point.X, point.Y);
/// <inheritdoc/>
public override int GetHashCode()
{
return this.GetHashCode(this);
}
public override int GetHashCode() => this.GetHashCode(this);
/// <inheritdoc/>
public override string ToString()
@ -249,34 +242,16 @@ namespace ImageSharp
}
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (obj is Point)
{
return this.Equals((Point)obj);
}
return false;
}
public override bool Equals(object obj) => obj is Point && this.Equals((Point)obj);
/// <inheritdoc/>
public bool Equals(Point other)
{
return this.X == other.X && this.Y == other.Y;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(Point other) => this.X == other.X && this.Y == other.Y;
/// <summary>
/// Returns the hash code for this instance.
/// </summary>
/// <param name="point">
/// The instance of <see cref="Point"/> to return the hash code for.
/// </param>
/// <returns>
/// A 32-bit signed integer that is the hash code for this instance.
/// </returns>
private int GetHashCode(Point point)
{
return point.X ^ point.Y;
}
private static short HighInt16(int n) => unchecked((short)((n >> 16) & 0xffff));
private static short LowInt16(int n) => unchecked((short)(n & 0xffff));
private int GetHashCode(Point point) => point.X ^ point.Y;
}
}

233
src/ImageSharp/Numerics/PointF.cs

@ -0,0 +1,233 @@
// <copyright file="PointF.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.ComponentModel;
using System.Numerics;
using System.Runtime.CompilerServices;
/// <summary>
/// Represents an ordered pair of single precision floating point x- and y-coordinates that defines a point in
/// a two-dimensional plane.
/// </summary>
/// <remarks>
/// This struct is fully mutable. This is done (against the guidelines) for the sake of performance,
/// as it avoids the need to create new values for modification operations.
/// </remarks>
public struct PointF : IEquatable<PointF>
{
/// <summary>
/// Represents a <see cref="PointF"/> that has X and Y values set to zero.
/// </summary>
public static readonly PointF Empty = default(PointF);
/// <summary>
/// Initializes a new instance of the <see cref="PointF"/> struct.
/// </summary>
/// <param name="x">The horizontal position of the point.</param>
/// <param name="y">The vertical position of the point.</param>
public PointF(float x, float y)
: this()
{
this.X = x;
this.Y = y;
}
/// <summary>
/// Initializes a new instance of the <see cref="PointF"/> struct from the given <see cref="SizeF"/>.
/// </summary>
/// <param name="size">The size</param>
public PointF(SizeF size)
{
this.X = size.Width;
this.Y = size.Height;
}
/// <summary>
/// Gets or sets the x-coordinate of this <see cref="PointF"/>.
/// </summary>
public float X { get; set; }
/// <summary>
/// Gets or sets the y-coordinate of this <see cref="PointF"/>.
/// </summary>
public float Y { get; set; }
/// <summary>
/// Gets a value indicating whether this <see cref="PointF"/> is empty.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public bool IsEmpty => this.Equals(Empty);
/// <summary>
/// Creates a <see cref="Vector2"/> with the coordinates of the specified <see cref="PointF"/>.
/// </summary>
/// <param name="vector">The vector.</param>
/// <returns>
/// The <see cref="Vector2"/>.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator PointF(Vector2 vector) => new PointF(vector.X, vector.Y);
/// <summary>
/// Creates a <see cref="Vector2"/> with the coordinates of the specified <see cref="PointF"/>.
/// </summary>
/// <param name="point">The point.</param>
/// <returns>
/// The <see cref="Vector2"/>.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator Vector2(PointF point) => new Vector2(point.X, point.Y);
/// <summary>
/// Creates a <see cref="Point"/> with the coordinates of the specified <see cref="PointF"/> by truncating each of the coordinates.
/// </summary>
/// <param name="point">The point.</param>
/// <returns>
/// The <see cref="Point"/>.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator Point(PointF point) => Point.Truncate(point);
/// <summary>
/// Translates a <see cref="PointF"/> by a given <see cref="SizeF"/>.
/// </summary>
/// <param name="point">The point on the left hand of the operand.</param>
/// <param name="size">The size on the right hand of the operand.</param>
/// <returns>
/// The <see cref="PointF"/>
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static PointF operator +(PointF point, SizeF size) => Add(point, size);
/// <summary>
/// Translates a <see cref="PointF"/> by the negative of a given <see cref="SizeF"/>.
/// </summary>
/// <param name="point">The point on the left hand of the operand.</param>
/// <param name="size">The size on the right hand of the operand.</param>
/// <returns>The <see cref="PointF"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static PointF operator -(PointF point, SizeF size) => Subtract(point, size);
/// <summary>
/// Compares two <see cref="PointF"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="PointF"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="PointF"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(PointF left, PointF right) => left.Equals(right);
/// <summary>
/// Compares two <see cref="PointF"/> objects for inequality.
/// </summary>
/// <param name="left">
/// The <see cref="PointF"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="PointF"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(PointF left, PointF right) => !left.Equals(right);
/// <summary>
/// Translates a <see cref="PointF"/> by the negative of a given <see cref="SizeF"/>.
/// </summary>
/// <param name="point">The point on the left hand of the operand.</param>
/// <param name="size">The size on the right hand of the operand.</param>
/// <returns>The <see cref="PointF"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static PointF Add(PointF point, SizeF size) => new PointF(point.X + size.Width, point.Y + size.Height);
/// <summary>
/// Translates a <see cref="PointF"/> by the negative of a given <see cref="SizeF"/>.
/// </summary>
/// <param name="point">The point on the left hand of the operand.</param>
/// <param name="size">The size on the right hand of the operand.</param>
/// <returns>The <see cref="PointF"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static PointF Subtract(PointF point, SizeF size) => new PointF(point.X - size.Width, point.Y - size.Height);
/// <summary>
/// Rotates a point around the given rotation matrix.
/// </summary>
/// <param name="point">The point to rotate</param>
/// <param name="rotation">Rotation matrix used</param>
/// <returns>The rotated <see cref="PointF"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static PointF Rotate(PointF point, Matrix3x2 rotation) => Vector2.Transform(new Vector2(point.X, point.Y), rotation);
/// <summary>
/// Skews a point using the given skew matrix.
/// </summary>
/// <param name="point">The point to rotate</param>
/// <param name="skew">Rotation matrix used</param>
/// <returns>The rotated <see cref="PointF"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static PointF Skew(PointF point, Matrix3x2 skew) => Vector2.Transform(new Vector2(point.X, point.Y), skew);
/// <summary>
/// Translates this <see cref="PointF"/> by the specified amount.
/// </summary>
/// <param name="dx">The amount to offset the x-coordinate.</param>
/// <param name="dy">The amount to offset the y-coordinate.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Offset(float dx, float dy)
{
this.X += dx;
this.Y += dy;
}
/// <summary>
/// Translates this <see cref="PointF"/> by the specified amount.
/// </summary>
/// <param name="point">The <see cref="PointF"/> used offset this <see cref="PointF"/>.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Offset(PointF point) => this.Offset(point.X, point.Y);
/// <inheritdoc/>
public override int GetHashCode() => this.GetHashCode(this);
/// <inheritdoc/>
public override string ToString()
{
if (this.IsEmpty)
{
return "PointF [ Empty ]";
}
return $"PointF [ X={this.X}, Y={this.Y} ]";
}
/// <inheritdoc/>
public override bool Equals(object obj) => obj is PointF && this.Equals((PointF)obj);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(PointF other) => this.X.Equals(other.X) && this.Y.Equals(other.Y);
/// <summary>
/// Returns the hash code for this instance.
/// </summary>
/// <param name="point">
/// The instance of <see cref="PointF"/> to return the hash code for.
/// </param>
/// <returns>
/// A 32-bit signed integer that is the hash code for this instance.
/// </returns>
private int GetHashCode(PointF point) => point.X.GetHashCode() ^ point.Y.GetHashCode();
}
}

440
src/ImageSharp/Numerics/Rectangle.cs

@ -8,6 +8,7 @@ namespace ImageSharp
using System;
using System.ComponentModel;
using System.Numerics;
using System.Runtime.CompilerServices;
/// <summary>
/// Stores a set of four integers that represent the location and size of a rectangle.
@ -23,11 +24,6 @@ namespace ImageSharp
/// </summary>
public static readonly Rectangle Empty = default(Rectangle);
/// <summary>
/// The backing vector for SIMD support.
/// </summary>
private Vector4 backingVector;
/// <summary>
/// Initializes a new instance of the <see cref="Rectangle"/> struct.
/// </summary>
@ -37,7 +33,10 @@ namespace ImageSharp
/// <param name="height">The height of the rectangle.</param>
public Rectangle(int x, int y, int width, int height)
{
this.backingVector = new Vector4(x, y, width, height);
this.X = x;
this.Y = y;
this.Width = width;
this.Height = height;
}
/// <summary>
@ -51,197 +50,325 @@ namespace ImageSharp
/// </param>
public Rectangle(Point point, Size size)
{
this.backingVector = new Vector4(point.X, point.Y, size.Width, size.Height);
this.X = point.X;
this.Y = point.Y;
this.Width = size.Width;
this.Height = size.Height;
}
/// <summary>
/// Initializes a new instance of the <see cref="Rectangle"/> struct.
/// Gets or sets the x-coordinate of this <see cref="Rectangle"/>.
/// </summary>
/// <param name="topLeft">
/// The <see cref="Point"/> which specifies the rectangles top left point in a two-dimensional plane.
/// </param>
/// <param name="bottomRight">
/// The <see cref="Point"/>which specifies the rectangles bottom right point in a two-dimensional plane.
/// </param>
public Rectangle(Point topLeft, Point bottomRight)
{
this.backingVector = new Vector4(topLeft.X, topLeft.Y, bottomRight.X - topLeft.X, bottomRight.Y - topLeft.Y);
}
public int X { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="Rectangle"/> struct.
/// Gets or sets the y-coordinate of this <see cref="Rectangle"/>.
/// </summary>
/// <param name="vector">The vector.</param>
public Rectangle(Vector4 vector)
{
this.backingVector = vector;
}
public int Y { get; set; }
/// <summary>
/// Gets or sets the x-coordinate of this <see cref="Rectangle"/>.
/// Gets or sets the width of this <see cref="Rectangle"/>.
/// </summary>
public int Width { get; set; }
/// <summary>
/// Gets or sets the height of this <see cref="Rectangle"/>.
/// </summary>
public int X
public int Height { get; set; }
/// <summary>
/// Gets or sets the coordinates of the upper-left corner of the rectangular region represented by this <see cref="Rectangle"/>.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public Point Location
{
get
{
return (int)this.backingVector.X;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new Point(this.X, this.Y);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set
{
this.backingVector.X = value;
this.X = value.X;
this.Y = value.Y;
}
}
/// <summary>
/// Gets or sets the y-coordinate of this <see cref="Rectangle"/>.
/// Gets or sets the size of this <see cref="Rectangle"/>.
/// </summary>
public int Y
[EditorBrowsable(EditorBrowsableState.Never)]
public Size Size
{
get
{
return (int)this.backingVector.Y;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new Size(this.Width, this.Height);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set
{
this.backingVector.Y = value;
this.Width = value.Width;
this.Height = value.Height;
}
}
/// <summary>
/// Gets or sets the width of this <see cref="Rectangle"/>.
/// Gets a value indicating whether this <see cref="Rectangle"/> is empty.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public bool IsEmpty => this.Equals(Empty);
/// <summary>
/// Gets the y-coordinate of the top edge of this <see cref="Rectangle"/>.
/// </summary>
public int Width
public int Top
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return (int)this.backingVector.Z;
return this.Y;
}
}
set
/// <summary>
/// Gets the x-coordinate of the right edge of this <see cref="Rectangle"/>.
/// </summary>
public int Right
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
this.backingVector.Z = value;
return unchecked(this.X + this.Width);
}
}
/// <summary>
/// Gets or sets the height of this <see cref="Rectangle"/>.
/// Gets the y-coordinate of the bottom edge of this <see cref="Rectangle"/>.
/// </summary>
public int Height
public int Bottom
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return (int)this.backingVector.W;
return unchecked(this.Y + this.Height);
}
}
set
/// <summary>
/// Gets the x-coordinate of the left edge of this <see cref="Rectangle"/>.
/// </summary>
public int Left
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
this.backingVector.W = value;
return this.X;
}
}
/// <summary>
/// Gets the size of this <see cref="Rectangle"/>.
/// Creates a <see cref="RectangleF"/> with the coordinates of the specified <see cref="Rectangle"/>.
/// </summary>
public Size Size => new Size(this.Width, this.Height);
/// <param name="rectangle">The rectangle</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator RectangleF(Rectangle rectangle) => new RectangleF(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height);
/// <summary>
/// Gets a value indicating whether this <see cref="Rectangle"/> is empty.
/// Creates a <see cref="Vector4"/> with the coordinates of the specified <see cref="Rectangle"/>.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public bool IsEmpty => this.Equals(Empty);
/// <param name="rectangle">The rectangle</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator Vector4(Rectangle rectangle) => new Vector4(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height);
/// <summary>
/// Gets the y-coordinate of the top edge of this <see cref="Rectangle"/>.
/// Compares two <see cref="Rectangle"/> objects for equality.
/// </summary>
public int Top => this.Y;
/// <param name="left">The <see cref="Rectangle"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="Rectangle"/> on the right side of the operand.</param>
/// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(Rectangle left, Rectangle right) => left.Equals(right);
/// <summary>
/// Gets the x-coordinate of the right edge of this <see cref="Rectangle"/>.
/// Compares two <see cref="Rectangle"/> objects for inequality.
/// </summary>
public int Right => this.X + this.Width;
/// <param name="left">The <see cref="Rectangle"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="Rectangle"/> on the right side of the operand.</param>
/// <returns>
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(Rectangle left, Rectangle right) => !left.Equals(right);
/// <summary>
/// Gets the y-coordinate of the bottom edge of this <see cref="Rectangle"/>.
/// Creates a new <see cref="Rectangle"/> with the specified location and size. </summary>
/// <param name="left">The left coordinate of the rectangle</param>
/// <param name="top">The top coordinate of the rectangle</param>
/// <param name="right">The right coordinate of the rectangle</param>
/// <param name="bottom">The bottom coordinate of the rectangle</param>
/// <returns>The <see cref="Rectangle"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
// ReSharper disable once InconsistentNaming
public static Rectangle FromLTRB(int left, int top, int right, int bottom) => new Rectangle(left, top, unchecked(right - left), unchecked(bottom - top));
/// <summary>
/// Returns the center point of the given <see cref="Rectangle"/>
/// </summary>
public int Bottom => this.Y + this.Height;
/// <param name="rectangle">The rectangle</param>
/// <returns>The <see cref="Point"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Point Center(Rectangle rectangle) => new Point(rectangle.Left + (rectangle.Width / 2), rectangle.Top + (rectangle.Height / 2));
/// <summary>
/// Gets the x-coordinate of the left edge of this <see cref="Rectangle"/>.
/// Creates a rectangle that represents the intersection between <paramref name="a"/> and
/// <paramref name="b"/>. If there is no intersection, an empty rectangle is returned.
/// </summary>
public int Left => this.X;
/// <param name="a">The first rectangle</param>
/// <param name="b">The second rectangle</param>
/// <returns>The <see cref="Rectangle"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Rectangle Intersect(Rectangle a, Rectangle b)
{
int x1 = Math.Max(a.X, b.X);
int x2 = Math.Min(a.Right, b.Right);
int y1 = Math.Max(a.Y, b.Y);
int y2 = Math.Min(a.Bottom, b.Bottom);
if (x2 >= x1 && y2 >= y1)
{
return new Rectangle(x1, y1, x2 - x1, y2 - y1);
}
return Empty;
}
/// <summary>
/// Computes the sum of adding two rectangles.
/// Creates a <see cref="Rectangle"/> that is inflated by the specified amount.
/// </summary>
/// <param name="left">The rectangle on the left hand of the operand.</param>
/// <param name="right">The rectangle on the right hand of the operand.</param>
/// <returns>
/// The <see cref="Rectangle"/>
/// </returns>
public static Rectangle operator +(Rectangle left, Rectangle right)
/// <param name="rectangle">The rectangle</param>
/// <param name="x">The amount to inflate the width by</param>
/// <param name="y">The amount to inflate the height by</param>
/// <returns>A new <see cref="Rectangle"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Rectangle Inflate(Rectangle rectangle, int x, int y)
{
return new Rectangle(left.backingVector + right.backingVector);
Rectangle r = rectangle;
r.Inflate(x, y);
return r;
}
/// <summary>
/// Computes the difference left by subtracting one rectangle from another.
/// Converts a <see cref="RectangleF"/> to a <see cref="Rectangle"/> by performing a ceiling operation on all the coordinates.
/// </summary>
/// <param name="left">The rectangle on the left hand of the operand.</param>
/// <param name="right">The rectangle on the right hand of the operand.</param>
/// <returns>
/// The <see cref="Rectangle"/>
/// </returns>
public static Rectangle operator -(Rectangle left, Rectangle right)
/// <param name="rectangle">The rectangle</param>
/// <returns>The <see cref="Rectangle"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Rectangle Ceiling(RectangleF rectangle)
{
return new Rectangle(left.backingVector - right.backingVector);
unchecked
{
return new Rectangle(
(int)MathF.Ceiling(rectangle.X),
(int)MathF.Ceiling(rectangle.Y),
(int)MathF.Ceiling(rectangle.Width),
(int)MathF.Ceiling(rectangle.Height));
}
}
/// <summary>
/// Compares two <see cref="Rectangle"/> objects for equality.
/// Converts a <see cref="RectangleF"/> to a <see cref="Rectangle"/> by performing a truncate operation on all the coordinates.
/// </summary>
/// <param name="left">
/// The <see cref="Rectangle"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Rectangle"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(Rectangle left, Rectangle right)
/// <param name="rectangle">The rectangle</param>
/// <returns>The <see cref="Rectangle"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Rectangle Truncate(RectangleF rectangle)
{
return left.Equals(right);
unchecked
{
return new Rectangle(
(int)rectangle.X,
(int)rectangle.Y,
(int)rectangle.Width,
(int)rectangle.Height);
}
}
/// <summary>
/// Compares two <see cref="Rectangle"/> objects for inequality.
/// Converts a <see cref="RectangleF"/> to a <see cref="Rectangle"/> by performing a round operation on all the coordinates.
/// </summary>
/// <param name="left">
/// The <see cref="Rectangle"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Rectangle"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(Rectangle left, Rectangle right)
/// <param name="rectangle">The rectangle</param>
/// <returns>The <see cref="Rectangle"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Rectangle Round(RectangleF rectangle)
{
return !left.Equals(right);
unchecked
{
return new Rectangle(
(int)MathF.Round(rectangle.X),
(int)MathF.Round(rectangle.Y),
(int)MathF.Round(rectangle.Width),
(int)MathF.Round(rectangle.Height));
}
}
/// <summary>
/// Returns the center point of the given <see cref="Rectangle"/>
/// Creates a rectangle that represents the union between <paramref name="a"/> and <paramref name="b"/>.
/// </summary>
/// <param name="a">The first rectangle</param>
/// <param name="b">The second rectangle</param>
/// <returns>The <see cref="Rectangle"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Rectangle Union(Rectangle a, Rectangle b)
{
int x1 = Math.Min(a.X, b.X);
int x2 = Math.Max(a.Right, b.Right);
int y1 = Math.Min(a.Y, b.Y);
int y2 = Math.Max(a.Bottom, b.Bottom);
return new Rectangle(x1, y1, x2 - x1, y2 - y1);
}
/// <summary>
/// Creates a Rectangle that represents the intersection between this Rectangle and the <paramref name="rectangle"/>.
/// </summary>
/// <param name="rectangle">The rectangle</param>
/// <returns><see cref="Point"/></returns>
public static Point Center(Rectangle rectangle)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Intersect(Rectangle rectangle)
{
return new Point(rectangle.Left + (rectangle.Width / 2), rectangle.Top + (rectangle.Height / 2));
Rectangle result = Intersect(rectangle, this);
this.X = result.X;
this.Y = result.Y;
this.Width = result.Width;
this.Height = result.Height;
}
/// <summary>
/// Inflates this <see cref="Rectangle"/> by the specified amount.
/// </summary>
/// <param name="width">The width</param>
/// <param name="height">The height</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Inflate(int width, int height)
{
unchecked
{
this.X -= width;
this.Y -= height;
this.Width += 2 * width;
this.Height += 2 * height;
}
}
/// <summary>
/// Inflates this <see cref="Rectangle"/> by the specified amount.
/// </summary>
/// <param name="size">The size</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Inflate(Size size) => this.Inflate(size.Width, size.Height);
/// <summary>
/// Determines if the specfied point is contained within the rectangular region defined by
/// this <see cref="Rectangle"/>.
@ -249,33 +376,63 @@ namespace ImageSharp
/// <param name="x">The x-coordinate of the given point.</param>
/// <param name="y">The y-coordinate of the given point.</param>
/// <returns>The <see cref="bool"/></returns>
public bool Contains(int x, int y)
{
// TODO: SIMD?
return this.X <= x
&& x < this.Right
&& this.Y <= y
&& y < this.Bottom;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Contains(int x, int y) => this.X <= x && x < this.Right && this.Y <= y && y < this.Bottom;
/// <summary>
/// Determines if the specified point is contained within the rectangular region defined by this <see cref="Rectangle"/> .
/// </summary>
/// <param name="point">The point</param>
/// <returns>The <see cref="bool"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Contains(Point point) => this.Contains(point.X, point.Y);
/// <summary>
/// Determines if the rectangular region represented by <paramref name="rectangle"/> is entirely contained
/// within the rectangular region represented by this <see cref="Rectangle"/> .
/// </summary>
/// <param name="rectangle">The rectangle</param>
/// <returns>The <see cref="bool"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Contains(Rectangle rectangle) =>
(this.X <= rectangle.X) && (rectangle.Right <= this.Right) &&
(this.Y <= rectangle.Y) && (rectangle.Bottom <= this.Bottom);
/// <summary>
/// Determines if the specfied <see cref="Rectangle"/> intersects the rectangular region defined by
/// this <see cref="Rectangle"/>.
/// </summary>
/// <param name="rect">The other Rectange </param>
/// <param name="rectangle">The other Rectange </param>
/// <returns>The <see cref="bool"/></returns>
public bool Intersects(Rectangle rect)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IntersectsWith(Rectangle rectangle) =>
(rectangle.X < this.Right) && (this.X < rectangle.Right) &&
(rectangle.Y < this.Bottom) && (this.Y < rectangle.Bottom);
/// <summary>
/// Adjusts the location of this rectangle by the specified amount.
/// </summary>
/// <param name="point">The point</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Offset(Point point) => this.Offset(point.X, point.Y);
/// <summary>
/// Adjusts the location of this rectangle by the specified amount.
/// </summary>
/// <param name="dx">The amount to offset the x-coordinate.</param>
/// <param name="dy">The amount to offset the y-coordinate.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Offset(int dx, int dy)
{
return rect.Left <= this.Right && rect.Right >= this.Left
&&
rect.Top <= this.Bottom && rect.Bottom >= this.Top;
unchecked
{
this.X += dx;
this.Y += dy;
}
}
/// <inheritdoc/>
public override int GetHashCode()
{
return this.GetHashCode(this);
}
public override int GetHashCode() => this.GetHashCode(this);
/// <inheritdoc/>
public override string ToString()
@ -285,39 +442,26 @@ namespace ImageSharp
return "Rectangle [ Empty ]";
}
return
$"Rectangle [ X={this.X}, Y={this.Y}, Width={this.Width}, Height={this.Height} ]";
return $"Rectangle [ X={this.X}, Y={this.Y}, Width={this.Width}, Height={this.Height} ]";
}
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (obj is Rectangle)
{
return this.Equals((Rectangle)obj);
}
return false;
}
public override bool Equals(object obj) => obj is Rectangle && this.Equals((Rectangle)obj);
/// <inheritdoc/>
public bool Equals(Rectangle other)
{
return this.backingVector.Equals(other.backingVector);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(Rectangle other) => this.X == other.X && this.Y == other.Y && this.Width == other.Width && this.Height == other.Height;
/// <summary>
/// Returns the hash code for this instance.
/// </summary>
/// <param name="rectangle">
/// The instance of <see cref="Rectangle"/> to return the hash code for.
/// </param>
/// <returns>
/// A 32-bit signed integer that is the hash code for this instance.
/// </returns>
private int GetHashCode(Rectangle rectangle)
{
return rectangle.backingVector.GetHashCode();
unchecked
{
int hashCode = rectangle.X;
hashCode = (hashCode * 397) ^ rectangle.Y;
hashCode = (hashCode * 397) ^ rectangle.Width;
hashCode = (hashCode * 397) ^ rectangle.Height;
return hashCode;
}
}
}
}
}

414
src/ImageSharp/Numerics/RectangleF.cs

@ -8,9 +8,10 @@ namespace ImageSharp
using System;
using System.ComponentModel;
using System.Numerics;
using System.Runtime.CompilerServices;
/// <summary>
/// Stores a set of four integers that represent the location and size of a rectangle.
/// Stores a set of four single precision floating points that represent the location and size of a rectangle.
/// </summary>
/// <remarks>
/// This struct is fully mutable. This is done (against the guidelines) for the sake of performance,
@ -19,15 +20,10 @@ namespace ImageSharp
public struct RectangleF : IEquatable<RectangleF>
{
/// <summary>
/// Represents a <see cref="Rectangle"/> that has X, Y, Width, and Height values set to zero.
/// Represents a <see cref="RectangleF"/> that has X, Y, Width, and Height values set to zero.
/// </summary>
public static readonly RectangleF Empty = default(RectangleF);
/// <summary>
/// The backing vector for SIMD support.
/// </summary>
private Vector4 backingVector;
/// <summary>
/// Initializes a new instance of the <see cref="RectangleF"/> struct.
/// </summary>
@ -37,79 +33,80 @@ namespace ImageSharp
/// <param name="height">The height of the rectangle.</param>
public RectangleF(float x, float y, float width, float height)
{
this.backingVector = new Vector4(x, y, width, height);
this.X = x;
this.Y = y;
this.Width = width;
this.Height = height;
}
/// <summary>
/// Initializes a new instance of the <see cref="RectangleF"/> struct.
/// </summary>
/// <param name="vector">The vector.</param>
public RectangleF(Vector4 vector)
/// <param name="point">
/// The <see cref="Point"/> which specifies the rectangles point in a two-dimensional plane.
/// </param>
/// <param name="size">
/// The <see cref="Size"/> which specifies the rectangles height and width.
/// </param>
public RectangleF(PointF point, SizeF size)
{
this.backingVector = vector;
this.X = point.X;
this.Y = point.Y;
this.Width = size.Width;
this.Height = size.Height;
}
/// <summary>
/// Gets or sets the x-coordinate of this <see cref="RectangleF"/>.
/// </summary>
public float X
{
get
{
return this.backingVector.X;
}
set
{
this.backingVector.X = value;
}
}
public float X { get; set; }
/// <summary>
/// Gets or sets the y-coordinate of this <see cref="RectangleF"/>.
/// </summary>
public float Y
{
get
{
return this.backingVector.Y;
}
set
{
this.backingVector.Y = value;
}
}
public float Y { get; set; }
/// <summary>
/// Gets or sets the width of this <see cref="RectangleF"/>.
/// </summary>
public float Width
public float Width { get; set; }
/// <summary>
/// Gets or sets the height of this <see cref="RectangleF"/>.
/// </summary>
public float Height { get; set; }
/// <summary>
/// Gets or sets the coordinates of the upper-left corner of the rectangular region represented by this <see cref="RectangleF"/>.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public PointF Location
{
get
{
return this.backingVector.Z;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new PointF(this.X, this.Y);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set
{
this.backingVector.Z = value;
this.X = value.X;
this.Y = value.Y;
}
}
/// <summary>
/// Gets or sets the height of this <see cref="RectangleF"/>.
/// Gets or sets the size of this <see cref="RectangleF"/>.
/// </summary>
public float Height
[EditorBrowsable(EditorBrowsableState.Never)]
public SizeF Size
{
get
{
return this.backingVector.W;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new SizeF(this.Width, this.Height);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set
{
this.backingVector.W = value;
this.Width = value.Width;
this.Height = value.Height;
}
}
@ -117,141 +114,196 @@ namespace ImageSharp
/// Gets a value indicating whether this <see cref="RectangleF"/> is empty.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public bool IsEmpty => this.Equals(Empty);
public bool IsEmpty => (this.Width <= 0) || (this.Height <= 0);
/// <summary>
/// Gets the y-coordinate of the top edge of this <see cref="RectangleF"/>.
/// </summary>
public float Top => this.Y;
public float Top
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return this.Y;
}
}
/// <summary>
/// Gets the x-coordinate of the right edge of this <see cref="RectangleF"/>.
/// </summary>
public float Right => this.X + this.Width;
public float Right
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return this.X + this.Width;
}
}
/// <summary>
/// Gets the y-coordinate of the bottom edge of this <see cref="RectangleF"/>.
/// </summary>
public float Bottom => this.Y + this.Height;
public float Bottom
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return this.Y + this.Height;
}
}
/// <summary>
/// Gets the x-coordinate of the left edge of this <see cref="RectangleF"/>.
/// </summary>
public float Left => this.X;
public float Left
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return this.X;
}
}
/// <summary>
/// Performs an implicit conversion from <see cref="Rectangle"/> to <see cref="RectangleF"/>.
/// Creates a <see cref="Rectangle"/> with the coordinates of the specified <see cref="RectangleF"/> by truncating each coordinate.
/// </summary>
/// <param name="d">The d.</param>
/// <returns>
/// The result of the conversion.
/// </returns>
public static implicit operator RectangleF(Rectangle d)
{
return new RectangleF(d.Left, d.Top, d.Width, d.Height);
}
/// <param name="rectangle">The rectangle</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator Rectangle(RectangleF rectangle) => Rectangle.Truncate(rectangle);
/// <summary>
/// Computes the sum of adding two rectangles.
/// Compares two <see cref="RectangleF"/> objects for equality.
/// </summary>
/// <param name="left">The rectangle on the left hand of the operand.</param>
/// <param name="right">The rectangle on the right hand of the operand.</param>
/// <param name="left">The <see cref="RectangleF"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="RectangleF"/> on the right side of the operand.</param>
/// <returns>
/// The <see cref="RectangleF"/>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static RectangleF operator +(RectangleF left, RectangleF right)
{
return new RectangleF(left.backingVector + right.backingVector);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(RectangleF left, RectangleF right) => left.Equals(right);
/// <summary>
/// Computes the difference left by subtracting one rectangle from another.
/// Compares two <see cref="RectangleF"/> objects for inequality.
/// </summary>
/// <param name="left">The rectangle on the left hand of the operand.</param>
/// <param name="right">The rectangle on the right hand of the operand.</param>
/// <param name="left">The <see cref="RectangleF"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="RectangleF"/> on the right side of the operand.</param>
/// <returns>
/// The <see cref="RectangleF"/>
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static RectangleF operator -(RectangleF left, RectangleF right)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(RectangleF left, RectangleF right) => !left.Equals(right);
/// <summary>
/// Creates a new <see cref="RectangleF"/> with the specified location and size. </summary>
/// <param name="left">The left coordinate of the rectangle</param>
/// <param name="top">The top coordinate of the rectangle</param>
/// <param name="right">The right coordinate of the rectangle</param>
/// <param name="bottom">The bottom coordinate of the rectangle</param>
/// <returns>The <see cref="RectangleF"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
// ReSharper disable once InconsistentNaming
public static RectangleF FromLTRB(float left, float top, float right, float bottom) => new RectangleF(left, top, right - left, bottom - top);
/// <summary>
/// Returns the center point of the given <see cref="RectangleF"/>
/// </summary>
/// <param name="rectangle">The rectangle</param>
/// <returns>The <see cref="Point"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static PointF Center(RectangleF rectangle) => new PointF(rectangle.Left + (rectangle.Width / 2), rectangle.Top + (rectangle.Height / 2));
/// <summary>
/// Creates a rectangle that represents the intersection between <paramref name="a"/> and
/// <paramref name="b"/>. If there is no intersection, an empty rectangle is returned.
/// </summary>
/// <param name="a">The first rectangle</param>
/// <param name="b">The second rectangle</param>
/// <returns>The <see cref="RectangleF"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static RectangleF Intersect(RectangleF a, RectangleF b)
{
return new RectangleF(left.backingVector - right.backingVector);
float x1 = MathF.Max(a.X, b.X);
float x2 = MathF.Min(a.Right, b.Right);
float y1 = MathF.Max(a.Y, b.Y);
float y2 = MathF.Min(a.Bottom, b.Bottom);
if (x2 >= x1 && y2 >= y1)
{
return new RectangleF(x1, y1, x2 - x1, y2 - y1);
}
return Empty;
}
/// <summary>
/// Compares two <see cref="RectangleF"/> objects for equality.
/// Creates a <see cref="RectangleF"/> that is inflated by the specified amount.
/// </summary>
/// <param name="left">
/// The <see cref="RectangleF"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="RectangleF"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(RectangleF left, RectangleF right)
/// <param name="rectangle">The rectangle</param>
/// <param name="x">The amount to inflate the width by</param>
/// <param name="y">The amount to inflate the height by</param>
/// <returns>A new <see cref="RectangleF"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static RectangleF Inflate(RectangleF rectangle, float x, float y)
{
return left.Equals(right);
RectangleF r = rectangle;
r.Inflate(x, y);
return r;
}
/// <summary>
/// Compares two <see cref="RectangleF"/> objects for inequality.
/// Creates a rectangle that represents the union between <paramref name="a"/> and <paramref name="b"/>.
/// </summary>
/// <param name="left">
/// The <see cref="RectangleF"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="RectangleF"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(RectangleF left, RectangleF right)
/// <param name="a">The first rectangle</param>
/// <param name="b">The second rectangle</param>
/// <returns>The <see cref="RectangleF"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static RectangleF Union(RectangleF a, RectangleF b)
{
return !left.Equals(right);
float x1 = MathF.Min(a.X, b.X);
float x2 = MathF.Max(a.Right, b.Right);
float y1 = MathF.Min(a.Y, b.Y);
float y2 = MathF.Max(a.Bottom, b.Bottom);
return new RectangleF(x1, y1, x2 - x1, y2 - y1);
}
/// <summary>
/// Returns the center point of the given <see cref="RectangleF"/>
/// Creates a RectangleF that represents the intersection between this RectangleF and the <paramref name="rectangle"/>.
/// </summary>
/// <param name="rectangle">The rectangle</param>
/// <returns><see cref="Point"/></returns>
public static Vector2 Center(RectangleF rectangle)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Intersect(RectangleF rectangle)
{
return new Vector2(rectangle.Left + (rectangle.Width / 2), rectangle.Top + (rectangle.Height / 2));
RectangleF result = Intersect(rectangle, this);
this.X = result.X;
this.Y = result.Y;
this.Width = result.Width;
this.Height = result.Height;
}
/// <summary>
/// Rounds the points away from the center this into a <see cref="Rectangle"/>
/// by rounding the dimensions to the nerent integer ensuring that the new rectangle is
/// never smaller than the source <see cref="RectangleF"/>
/// Inflates this <see cref="RectangleF"/> by the specified amount.
/// </summary>
/// <param name="source">The source area to round out</param>
/// <returns>
/// The smallest <see cref="Rectangle"/> that the <see cref="RectangleF"/> will fit inside.
/// </returns>
public static Rectangle Ceiling(RectangleF source)
/// <param name="width">The width</param>
/// <param name="height">The height</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Inflate(float width, float height)
{
int y = (int)Math.Floor(source.Y);
int width = (int)Math.Ceiling(source.Width);
int x = (int)Math.Floor(source.X);
int height = (int)Math.Ceiling(source.Height);
return new Rectangle(x, y, width, height);
this.X -= width;
this.Y -= height;
this.Width += 2 * width;
this.Height += 2 * height;
}
/// <summary>
/// Outsets the specified region.
/// Inflates this <see cref="RectangleF"/> by the specified amount.
/// </summary>
/// <param name="region">The region.</param>
/// <param name="width">The width.</param>
/// <returns>
/// The <see cref="RectangleF"/> with all dimensions move away from the center by the offset.
/// </returns>
public static RectangleF Outset(RectangleF region, float width)
{
float dblWidth = width * 2;
return new RectangleF(region.X - width, region.Y - width, region.Width + dblWidth, region.Height + dblWidth);
}
/// <param name="size">The size</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Inflate(SizeF size) => this.Inflate(size.Width, size.Height);
/// <summary>
/// Determines if the specfied point is contained within the rectangular region defined by
@ -260,75 +312,89 @@ namespace ImageSharp
/// <param name="x">The x-coordinate of the given point.</param>
/// <param name="y">The y-coordinate of the given point.</param>
/// <returns>The <see cref="bool"/></returns>
public bool Contains(float x, float y)
{
// TODO: SIMD?
return this.X <= x
&& x < this.Right
&& this.Y <= y
&& y < this.Bottom;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Contains(float x, float y) => this.X <= x && x < this.Right && this.Y <= y && y < this.Bottom;
/// <summary>
/// Determines if the specified point is contained within the rectangular region defined by this <see cref="RectangleF"/> .
/// </summary>
/// <param name="point">The point</param>
/// <returns>The <see cref="bool"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Contains(PointF point) => this.Contains(point.X, point.Y);
/// <summary>
/// Determines if the rectangular region represented by <paramref name="rectangle"/> is entirely contained
/// within the rectangular region represented by this <see cref="RectangleF"/> .
/// </summary>
/// <param name="rectangle">The rectangle</param>
/// <returns>The <see cref="bool"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Contains(RectangleF rectangle) =>
(this.X <= rectangle.X) && (rectangle.Right <= this.Right) &&
(this.Y <= rectangle.Y) && (rectangle.Bottom <= this.Bottom);
/// <summary>
/// Determines if the specfied <see cref="Rectangle"/> intersects the rectangular region defined by
/// this <see cref="Rectangle"/>.
/// Determines if the specfied <see cref="RectangleF"/> intersects the rectangular region defined by
/// this <see cref="RectangleF"/>.
/// </summary>
/// <param name="rect">The other Rectange </param>
/// <param name="rectangle">The other Rectange </param>
/// <returns>The <see cref="bool"/></returns>
public bool Intersects(RectangleF rect)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IntersectsWith(RectangleF rectangle) =>
(rectangle.X < this.Right) && (this.X < rectangle.Right) &&
(rectangle.Y < this.Bottom) && (this.Y < rectangle.Bottom);
/// <summary>
/// Adjusts the location of this rectangle by the specified amount.
/// </summary>
/// <param name="point">The point</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Offset(PointF point) => this.Offset(point.X, point.Y);
/// <summary>
/// Adjusts the location of this rectangle by the specified amount.
/// </summary>
/// <param name="dx">The amount to offset the x-coordinate.</param>
/// <param name="dy">The amount to offset the y-coordinate.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Offset(float dx, float dy)
{
return rect.Left <= this.Right && rect.Right >= this.Left
&&
rect.Top <= this.Bottom && rect.Bottom >= this.Top;
this.X += dx;
this.Y += dy;
}
/// <inheritdoc/>
public override int GetHashCode()
{
return this.GetHashCode(this);
}
public override int GetHashCode() => this.GetHashCode(this);
/// <inheritdoc/>
public override string ToString()
{
if (this.IsEmpty)
{
return "Rectangle [ Empty ]";
return "RectangleF [ Empty ]";
}
return
$"Rectangle [ X={this.X}, Y={this.Y}, Width={this.Width}, Height={this.Height} ]";
return $"RectangleF [ X={this.X}, Y={this.Y}, Width={this.Width}, Height={this.Height} ]";
}
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (obj is RectangleF)
{
return this.Equals((RectangleF)obj);
}
return false;
}
public override bool Equals(object obj) => obj is RectangleF && this.Equals((RectangleF)obj);
/// <inheritdoc/>
public bool Equals(RectangleF other)
{
return this.backingVector.Equals(other.backingVector);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(RectangleF other) => this.X.Equals(other.X) && this.Y.Equals(other.Y) && this.Width.Equals(other.Width) && this.Height.Equals(other.Height);
/// <summary>
/// Returns the hash code for this instance.
/// </summary>
/// <param name="rectangle">
/// The instance of <see cref="RectangleF"/> to return the hash code for.
/// </param>
/// <returns>
/// A 32-bit signed integer that is the hash code for this instance.
/// </returns>
private int GetHashCode(RectangleF rectangle)
{
return rectangle.backingVector.GetHashCode();
unchecked
{
int hashCode = rectangle.X.GetHashCode();
hashCode = (hashCode * 397) ^ rectangle.Y.GetHashCode();
hashCode = (hashCode * 397) ^ rectangle.Width.GetHashCode();
hashCode = (hashCode * 397) ^ rectangle.Height.GetHashCode();
return hashCode;
}
}
}
}
}

137
src/ImageSharp/Numerics/Size.cs

@ -23,6 +23,17 @@ namespace ImageSharp
/// </summary>
public static readonly Size Empty = default(Size);
/// <summary>
/// Initializes a new instance of the <see cref="Size"/> struct.
/// </summary>
/// <param name="value">The width and height of the size</param>
public Size(int value)
: this()
{
this.Width = value;
this.Height = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="Size"/> struct.
/// </summary>
@ -34,6 +45,27 @@ namespace ImageSharp
this.Height = height;
}
/// <summary>
/// Initializes a new instance of the <see cref="Size"/> struct.
/// </summary>
/// <param name="size">The size</param>
public Size(Size size)
: this()
{
this.Width = size.Width;
this.Height = size.Height;
}
/// <summary>
/// Initializes a new instance of the <see cref="Size"/> struct from the given <see cref="Point"/>.
/// </summary>
/// <param name="point">The point</param>
public Size(Point point)
{
this.Width = point.X;
this.Height = point.Y;
}
/// <summary>
/// Gets or sets the width of this <see cref="Size"/>.
/// </summary>
@ -50,6 +82,20 @@ namespace ImageSharp
[EditorBrowsable(EditorBrowsableState.Never)]
public bool IsEmpty => this.Equals(Empty);
/// <summary>
/// Creates a <see cref="SizeF"/> with the dimensions of the specified <see cref="Size"/>.
/// </summary>
/// <param name="size">The point</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator SizeF(Size size) => new SizeF(size.Width, size.Height);
/// <summary>
/// Converts the given <see cref="Size"/> into a <see cref="Point"/>.
/// </summary>
/// <param name="size">The size</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator Point(Size size) => new Point(size.Width, size.Height);
/// <summary>
/// Computes the sum of adding two sizes.
/// </summary>
@ -59,10 +105,7 @@ namespace ImageSharp
/// The <see cref="Size"/>
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Size operator +(Size left, Size right)
{
return new Size(left.Width + right.Width, left.Height + right.Height);
}
public static Size operator +(Size left, Size right) => Add(left, right);
/// <summary>
/// Computes the difference left by subtracting one size from another.
@ -73,10 +116,7 @@ namespace ImageSharp
/// The <see cref="Size"/>
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Size operator -(Size left, Size right)
{
return new Size(left.Width - right.Width, left.Height - right.Height);
}
public static Size operator -(Size left, Size right) => Subtract(left, right);
/// <summary>
/// Compares two <see cref="Size"/> objects for equality.
@ -91,10 +131,7 @@ namespace ImageSharp
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(Size left, Size right)
{
return left.Equals(right);
}
public static bool operator ==(Size left, Size right) => left.Equals(right);
/// <summary>
/// Compares two <see cref="Size"/> objects for inequality.
@ -109,16 +146,52 @@ namespace ImageSharp
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(Size left, Size right)
{
return !left.Equals(right);
}
public static bool operator !=(Size left, Size right) => !left.Equals(right);
/// <summary>
/// Performs vector addition of two <see cref="Size"/> objects.
/// </summary>
/// <param name="left">The size on the left hand of the operand.</param>
/// <param name="right">The size on the right hand of the operand.</param>
/// <returns>The <see cref="Size"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Size Add(Size left, Size right) => new Size(unchecked(left.Width + right.Width), unchecked(left.Height + right.Height));
/// <summary>
/// Contracts a <see cref="Size"/> by another <see cref="Size"/>
/// </summary>
/// <param name="left">The size on the left hand of the operand.</param>
/// <param name="right">The size on the right hand of the operand.</param>
/// <returns>The <see cref="Size"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Size Subtract(Size left, Size right) => new Size(unchecked(left.Width - right.Width), unchecked(left.Height - right.Height));
/// <summary>
/// Converts a <see cref="SizeF"/> to a <see cref="Size"/> by performing a ceiling operation on all the dimensions.
/// </summary>
/// <param name="size">The size</param>
/// <returns>The <see cref="Size"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Size Ceiling(SizeF size) => new Size(unchecked((int)MathF.Ceiling(size.Width)), unchecked((int)MathF.Ceiling(size.Height)));
/// <summary>
/// Converts a <see cref="SizeF"/> to a <see cref="Size"/> by performing a round operation on all the dimensions.
/// </summary>
/// <param name="size">The size</param>
/// <returns>The <see cref="Size"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Size Round(SizeF size) => new Size(unchecked((int)MathF.Round(size.Width)), unchecked((int)MathF.Round(size.Height)));
/// <summary>
/// Converts a <see cref="SizeF"/> to a <see cref="Size"/> by performing a round operation on all the dimensions.
/// </summary>
/// <param name="size">The size</param>
/// <returns>The <see cref="Size"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Size Truncate(SizeF size) => new Size(unchecked((int)size.Width), unchecked((int)size.Height));
/// <inheritdoc/>
public override int GetHashCode()
{
return this.GetHashCode(this);
}
public override int GetHashCode() => this.GetHashCode(this);
/// <inheritdoc/>
public override string ToString()
@ -132,22 +205,11 @@ namespace ImageSharp
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj)
{
if (obj is Size)
{
return this.Equals((Size)obj);
}
return false;
}
public override bool Equals(object obj) => obj is Size && this.Equals((Size)obj);
/// <inheritdoc/>
public bool Equals(Size other)
{
return this.Width == other.Width && this.Height == other.Height;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(Size other) => this.Width == other.Width && this.Height == other.Height;
/// <summary>
/// Returns the hash code for this instance.
@ -158,9 +220,6 @@ namespace ImageSharp
/// <returns>
/// A 32-bit signed integer that is the hash code for this instance.
/// </returns>
private int GetHashCode(Size size)
{
return size.Width ^ size.Height;
}
private int GetHashCode(Size size) => size.Width ^ size.Height;
}
}
}

179
src/ImageSharp/Numerics/SizeF.cs

@ -0,0 +1,179 @@
// <copyright file="SizeF.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
/// <summary>
/// Stores an ordered pair of single precision floating points, which specify a height and width.
/// </summary>
/// <remarks>
/// This struct is fully mutable. This is done (against the guidelines) for the sake of performance,
/// as it avoids the need to create new values for modification operations.
/// </remarks>
public struct SizeF : IEquatable<SizeF>
{
/// <summary>
/// Represents a <see cref="SizeF"/> that has Width and Height values set to zero.
/// </summary>
public static readonly SizeF Empty = default(SizeF);
/// <summary>
/// Initializes a new instance of the <see cref="SizeF"/> struct.
/// </summary>
/// <param name="width">The width of the size.</param>
/// <param name="height">The height of the size.</param>
public SizeF(float width, float height)
{
this.Width = width;
this.Height = height;
}
/// <summary>
/// Initializes a new instance of the <see cref="SizeF"/> struct.
/// </summary>
/// <param name="size">The size</param>
public SizeF(SizeF size)
: this()
{
this.Width = size.Width;
this.Height = size.Height;
}
/// <summary>
/// Initializes a new instance of the <see cref="SizeF"/> struct from the given <see cref="PointF"/>.
/// </summary>
/// <param name="point">The point</param>
public SizeF(PointF point)
{
this.Width = point.X;
this.Height = point.Y;
}
/// <summary>
/// Gets or sets the width of this <see cref="SizeF"/>.
/// </summary>
public float Width { get; set; }
/// <summary>
/// Gets or sets the height of this <see cref="SizeF"/>.
/// </summary>
public float Height { get; set; }
/// <summary>
/// Gets a value indicating whether this <see cref="SizeF"/> is empty.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public bool IsEmpty => this.Equals(Empty);
/// <summary>
/// Creates a <see cref="Size"/> with the dimensions of the specified <see cref="SizeF"/> by truncating each of the dimensions.
/// </summary>
/// <param name="size">The size.</param>
/// <returns>
/// The <see cref="Size"/>.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator Size(SizeF size) => new Size(unchecked((int)size.Width), unchecked((int)size.Height));
/// <summary>
/// Converts the given <see cref="SizeF"/> into a <see cref="PointF"/>.
/// </summary>
/// <param name="size">The size</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator PointF(SizeF size) => new PointF(size.Width, size.Height);
/// <summary>
/// Computes the sum of adding two sizes.
/// </summary>
/// <param name="left">The size on the left hand of the operand.</param>
/// <param name="right">The size on the right hand of the operand.</param>
/// <returns>
/// The <see cref="SizeF"/>
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static SizeF operator +(SizeF left, SizeF right) => Add(left, right);
/// <summary>
/// Computes the difference left by subtracting one size from another.
/// </summary>
/// <param name="left">The size on the left hand of the operand.</param>
/// <param name="right">The size on the right hand of the operand.</param>
/// <returns>
/// The <see cref="SizeF"/>
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static SizeF operator -(SizeF left, SizeF right) => Subtract(left, right);
/// <summary>
/// Compares two <see cref="SizeF"/> objects for equality.
/// </summary>
/// <param name="left">The size on the left hand of the operand.</param>
/// <param name="right">The size on the right hand of the operand.</param>
/// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(SizeF left, SizeF right) => left.Equals(right);
/// <summary>
/// Compares two <see cref="SizeF"/> objects for inequality.
/// </summary>
/// <param name="left">The size on the left hand of the operand.</param>
/// <param name="right">The size on the right hand of the operand.</param>
/// <returns>
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(SizeF left, SizeF right) => !left.Equals(right);
/// <summary>
/// Performs vector addition of two <see cref="SizeF"/> objects.
/// </summary>
/// <param name="left">The size on the left hand of the operand.</param>
/// <param name="right">The size on the right hand of the operand.</param>
/// <returns>The <see cref="SizeF"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static SizeF Add(SizeF left, SizeF right) => new SizeF(left.Width + right.Width, left.Height + right.Height);
/// <summary>
/// Contracts a <see cref="SizeF"/> by another <see cref="SizeF"/>
/// </summary>
/// <param name="left">The size on the left hand of the operand.</param>
/// <param name="right">The size on the right hand of the operand.</param>
/// <returns>The <see cref="SizeF"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static SizeF Subtract(SizeF left, SizeF right) => new SizeF(left.Width - right.Width, left.Height - right.Height);
/// <inheritdoc/>
public override int GetHashCode()
{
return this.GetHashCode(this);
}
/// <inheritdoc/>
public override string ToString()
{
if (this.IsEmpty)
{
return "SizeF [ Empty ]";
}
return $"SizeF [ Width={this.Width}, Height={this.Height} ]";
}
/// <inheritdoc/>
public override bool Equals(object obj) => obj is SizeF && this.Equals((SizeF)obj);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(SizeF other) => this.Width.Equals(other.Width) && this.Height.Equals(other.Height);
private int GetHashCode(SizeF size) => size.Width.GetHashCode() ^ size.Height.GetHashCode();
}
}

38
src/ImageSharp/PixelFormats/Alpha8.cs

@ -62,7 +62,7 @@ namespace ImageSharp.PixelFormats
}
/// <inheritdoc />
public PixelOperations<Alpha8> CreateBulkOperations() => new PixelOperations<Alpha8>();
public PixelOperations<Alpha8> CreatePixelOperations() => new PixelOperations<Alpha8>();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -80,47 +80,43 @@ namespace ImageSharp.PixelFormats
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromBytes(byte x, byte y, byte z, byte w)
public void PackFromRgba32(Rgba32 source)
{
this.PackedValue = w;
this.PackedValue = source.A;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToXyzBytes(Span<byte> bytes, int startIndex)
public void ToRgb24(ref Rgb24 dest)
{
bytes[startIndex] = 0;
bytes[startIndex + 1] = 0;
bytes[startIndex + 2] = 0;
dest = default(Rgb24);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToXyzwBytes(Span<byte> bytes, int startIndex)
public void ToRgba32(ref Rgba32 dest)
{
bytes[startIndex] = 0;
bytes[startIndex + 1] = 0;
bytes[startIndex + 2] = 0;
bytes[startIndex + 3] = this.PackedValue;
dest.R = 0;
dest.G = 0;
dest.B = 0;
dest.A = this.PackedValue;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToZyxBytes(Span<byte> bytes, int startIndex)
public void ToBgr24(ref Bgr24 dest)
{
bytes[startIndex] = 0;
bytes[startIndex + 1] = 0;
bytes[startIndex + 2] = 0;
dest = default(Bgr24);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToZyxwBytes(Span<byte> bytes, int startIndex)
public void ToBgra32(ref Bgra32 dest)
{
bytes[startIndex] = 0;
bytes[startIndex + 1] = 0;
bytes[startIndex + 2] = 0;
bytes[startIndex + 3] = this.PackedValue;
dest.R = 0;
dest.G = 0;
dest.B = 0;
dest.A = this.PackedValue;
}
/// <summary>

57
src/ImageSharp/PixelFormats/Argb32.cs

@ -59,11 +59,24 @@ namespace ImageSharp.PixelFormats
/// <param name="g">The green component.</param>
/// <param name="b">The blue component.</param>
/// <param name="a">The alpha component.</param>
public Argb32(byte r, byte g, byte b, byte a = 255)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Argb32(byte r, byte g, byte b, byte a)
{
this.PackedValue = Pack(r, g, b, a);
}
/// <summary>
/// Initializes a new instance of the <see cref="Argb32"/> struct.
/// </summary>
/// <param name="r">The red component.</param>
/// <param name="g">The green component.</param>
/// <param name="b">The blue component.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Argb32(byte r, byte g, byte b)
{
this.PackedValue = Pack(r, g, b, 255);
}
/// <summary>
/// Initializes a new instance of the <see cref="Argb32"/> struct.
/// </summary>
@ -224,7 +237,7 @@ namespace ImageSharp.PixelFormats
}
/// <inheritdoc />
public PixelOperations<Argb32> CreateBulkOperations() => new PixelOperations<Argb32>();
public PixelOperations<Argb32> CreatePixelOperations() => new PixelOperations<Argb32>();
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -235,47 +248,47 @@ namespace ImageSharp.PixelFormats
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromBytes(byte x, byte y, byte z, byte w)
public void PackFromRgba32(Rgba32 source)
{
this.PackedValue = Pack(x, y, z, w);
this.PackedValue = Pack(source.R, source.G, source.B, source.A);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToXyzBytes(Span<byte> bytes, int startIndex)
public void ToRgb24(ref Rgb24 dest)
{
bytes[startIndex] = this.R;
bytes[startIndex + 1] = this.G;
bytes[startIndex + 2] = this.B;
dest.R = this.R;
dest.G = this.G;
dest.B = this.B;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToXyzwBytes(Span<byte> bytes, int startIndex)
public void ToRgba32(ref Rgba32 dest)
{
bytes[startIndex] = this.R;
bytes[startIndex + 1] = this.G;
bytes[startIndex + 2] = this.B;
bytes[startIndex + 3] = this.A;
dest.R = this.R;
dest.G = this.G;
dest.B = this.B;
dest.A = this.A;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToZyxBytes(Span<byte> bytes, int startIndex)
public void ToBgr24(ref Bgr24 dest)
{
bytes[startIndex] = this.B;
bytes[startIndex + 1] = this.G;
bytes[startIndex + 2] = this.R;
dest.R = this.R;
dest.G = this.G;
dest.B = this.B;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToZyxwBytes(Span<byte> bytes, int startIndex)
public void ToBgra32(ref Bgra32 dest)
{
bytes[startIndex] = this.B;
bytes[startIndex + 1] = this.G;
bytes[startIndex + 2] = this.R;
bytes[startIndex + 3] = this.A;
dest.R = this.R;
dest.G = this.G;
dest.B = this.B;
dest.A = this.A;
}
/// <inheritdoc/>

135
src/ImageSharp/PixelFormats/Bgr24.cs

@ -0,0 +1,135 @@
// <copyright file="Bgr24.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.PixelFormats
{
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
/// <summary>
/// Pixel type containing three 8-bit unsigned normalized values ranging from 0 to 255.
/// The color components are stored in blue, green, red order.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct Bgr24 : IPixel<Bgr24>
{
/// <summary>
/// The blue component.
/// </summary>
public byte B;
/// <summary>
/// The green component.
/// </summary>
public byte G;
/// <summary>
/// The red component.
/// </summary>
public byte R;
/// <summary>
/// Initializes a new instance of the <see cref="Bgr24"/> struct.
/// </summary>
/// <param name="r">The red component.</param>
/// <param name="g">The green component.</param>
/// <param name="b">The blue component.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Bgr24(byte r, byte g, byte b)
{
this.R = r;
this.G = g;
this.B = b;
}
/// <inheritdoc/>
public PixelOperations<Bgr24> CreatePixelOperations() => new PixelOperations<Bgr24>();
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(Bgr24 other)
{
return this.R == other.R && this.G == other.G && this.B == other.B;
}
/// <inheritdoc/>
public override bool Equals(object obj)
{
return obj?.GetType() == typeof(Bgr24) && this.Equals((Bgr24)obj);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode()
{
unchecked
{
int hashCode = this.B;
hashCode = (hashCode * 397) ^ this.G;
hashCode = (hashCode * 397) ^ this.R;
return hashCode;
}
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgba32(Rgba32 source)
{
this = source.Bgr;
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromVector4(Vector4 vector)
{
var rgba = default(Rgba32);
rgba.PackFromVector4(vector);
this.PackFromRgba32(rgba);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToVector4()
{
return new Rgba32(this.R, this.G, this.B, 255).ToVector4();
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgb24(ref Rgb24 dest)
{
dest.R = this.R;
dest.G = this.G;
dest.B = this.B;
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgba32(ref Rgba32 dest)
{
dest.R = this.R;
dest.G = this.G;
dest.B = this.B;
dest.A = 255;
}
/// <inheritdoc/>
public void ToBgr24(ref Bgr24 dest)
{
dest = this;
}
/// <inheritdoc/>
public void ToBgra32(ref Bgra32 dest)
{
dest.R = this.R;
dest.G = this.G;
dest.B = this.B;
dest.A = 255;
}
}
}

42
src/ImageSharp/PixelFormats/Bgr565.cs

@ -71,7 +71,7 @@ namespace ImageSharp.PixelFormats
}
/// <inheritdoc />
public PixelOperations<Bgr565> CreateBulkOperations() => new PixelOperations<Bgr565>();
public PixelOperations<Bgr565> CreatePixelOperations() => new PixelOperations<Bgr565>();
/// <summary>
/// Expands the packed representation into a <see cref="Vector3"/>.
@ -103,51 +103,51 @@ namespace ImageSharp.PixelFormats
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromBytes(byte x, byte y, byte z, byte w)
public void PackFromRgba32(Rgba32 source)
{
this.PackFromVector4(new Vector4(x, y, z, w) / 255F);
this.PackFromVector4(source.ToVector4());
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToXyzBytes(Span<byte> bytes, int startIndex)
public void ToRgb24(ref Rgb24 dest)
{
Vector4 vector = this.ToVector4() * 255F;
bytes[startIndex] = (byte)MathF.Round(vector.X);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = (byte)MathF.Round(vector.Z);
dest.R = (byte)MathF.Round(vector.X);
dest.G = (byte)MathF.Round(vector.Y);
dest.B = (byte)MathF.Round(vector.Z);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToXyzwBytes(Span<byte> bytes, int startIndex)
public void ToRgba32(ref Rgba32 dest)
{
Vector4 vector = this.ToVector4() * 255F;
bytes[startIndex] = (byte)MathF.Round(vector.X);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = (byte)MathF.Round(vector.Z);
bytes[startIndex + 3] = (byte)MathF.Round(vector.W);
dest.R = (byte)MathF.Round(vector.X);
dest.G = (byte)MathF.Round(vector.Y);
dest.B = (byte)MathF.Round(vector.Z);
dest.A = (byte)MathF.Round(vector.W);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToZyxBytes(Span<byte> bytes, int startIndex)
public void ToBgr24(ref Bgr24 dest)
{
Vector4 vector = this.ToVector4() * 255F;
bytes[startIndex] = (byte)MathF.Round(vector.Z);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = (byte)MathF.Round(vector.X);
dest.R = (byte)MathF.Round(vector.X);
dest.G = (byte)MathF.Round(vector.Y);
dest.B = (byte)MathF.Round(vector.Z);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToZyxwBytes(Span<byte> bytes, int startIndex)
public void ToBgra32(ref Bgra32 dest)
{
Vector4 vector = this.ToVector4() * 255F;
bytes[startIndex] = (byte)MathF.Round(vector.Z);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = (byte)MathF.Round(vector.X);
bytes[startIndex + 3] = (byte)MathF.Round(vector.W);
dest.R = (byte)MathF.Round(vector.X);
dest.G = (byte)MathF.Round(vector.Y);
dest.B = (byte)MathF.Round(vector.Z);
dest.A = (byte)MathF.Round(vector.W);
}
/// <inheritdoc />

187
src/ImageSharp/PixelFormats/Bgra32.cs

@ -0,0 +1,187 @@
// <copyright file="Bgra32.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.PixelFormats
{
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
/// <summary>
/// Packed pixel type containing four 8-bit unsigned normalized values ranging from 0 to 255.
/// The color components are stored in blue, green, red, and alpha order.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct Bgra32 : IPixel<Bgra32>, IPackedVector<uint>
{
/// <summary>
/// Gets or sets the blue component.
/// </summary>
public byte B;
/// <summary>
/// Gets or sets the green component.
/// </summary>
public byte G;
/// <summary>
/// Gets or sets the red component.
/// </summary>
public byte R;
/// <summary>
/// Gets or sets the alpha component.
/// </summary>
public byte A;
/// <summary>
/// Initializes a new instance of the <see cref="Bgra32"/> struct.
/// </summary>
/// <param name="r">The red component.</param>
/// <param name="g">The green component.</param>
/// <param name="b">The blue component.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Bgra32(byte r, byte g, byte b)
{
this.R = r;
this.G = g;
this.B = b;
this.A = 255;
}
/// <summary>
/// Initializes a new instance of the <see cref="Bgra32"/> struct.
/// </summary>
/// <param name="r">The red component.</param>
/// <param name="g">The green component.</param>
/// <param name="b">The blue component.</param>
/// <param name="a">The alpha component.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Bgra32(byte r, byte g, byte b, byte a)
{
this.R = r;
this.G = g;
this.B = b;
this.A = a;
}
/// <summary>
/// Gets or sets the packed representation of the Bgra32 struct.
/// </summary>
public uint Bgra
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return Unsafe.As<Bgra32, uint>(ref this);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set
{
Unsafe.As<Bgra32, uint>(ref this) = value;
}
}
/// <inheritdoc/>
public uint PackedValue
{
get => this.Bgra;
set => this.Bgra = value;
}
/// <inheritdoc/>
public PixelOperations<Bgra32> CreatePixelOperations() => new PixelOperations<Bgra32>();
/// <inheritdoc/>
public bool Equals(Bgra32 other)
{
return this.R == other.R && this.G == other.G && this.B == other.B && this.A == other.A;
}
/// <inheritdoc/>
public override bool Equals(object obj) => obj?.GetType() == typeof(Bgra32) && this.Equals((Bgra32)obj);
/// <inheritdoc/>
public override int GetHashCode()
{
unchecked
{
int hashCode = this.B;
hashCode = (hashCode * 397) ^ this.G;
hashCode = (hashCode * 397) ^ this.R;
hashCode = (hashCode * 397) ^ this.A;
return hashCode;
}
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromVector4(Vector4 vector)
{
var rgba = default(Rgba32);
rgba.PackFromVector4(vector);
this.PackFromRgba32(rgba);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToVector4()
{
return this.ToRgba32().ToVector4();
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgba32(Rgba32 source)
{
this.R = source.R;
this.G = source.G;
this.B = source.B;
this.A = source.A;
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgb24(ref Rgb24 dest)
{
dest.R = this.R;
dest.G = this.G;
dest.B = this.B;
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgba32(ref Rgba32 dest)
{
dest.R = this.R;
dest.G = this.G;
dest.B = this.B;
dest.A = this.A;
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToBgr24(ref Bgr24 dest)
{
dest = Unsafe.As<Bgra32, Bgr24>(ref this);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToBgra32(ref Bgra32 dest)
{
dest = this;
}
/// <summary>
/// Converts the pixel to <see cref="Rgba32"/> format.
/// </summary>
/// <returns>The RGBA value</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Rgba32 ToRgba32() => new Rgba32(this.R, this.G, this.B, this.A);
}
}

56
src/ImageSharp/PixelFormats/Bgra4444.cs

@ -70,7 +70,7 @@ namespace ImageSharp.PixelFormats
}
/// <inheritdoc />
public PixelOperations<Bgra4444> CreateBulkOperations() => new PixelOperations<Bgra4444>();
public PixelOperations<Bgra4444> CreatePixelOperations() => new PixelOperations<Bgra4444>();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -79,10 +79,10 @@ namespace ImageSharp.PixelFormats
const float Max = 1 / 15F;
return new Vector4(
((this.PackedValue >> 8) & 0x0F) * Max,
((this.PackedValue >> 4) & 0x0F) * Max,
(this.PackedValue & 0x0F) * Max,
((this.PackedValue >> 12) & 0x0F) * Max);
((this.PackedValue >> 8) & 0x0F) * Max,
((this.PackedValue >> 4) & 0x0F) * Max,
(this.PackedValue & 0x0F) * Max,
((this.PackedValue >> 12) & 0x0F) * Max);
}
/// <inheritdoc />
@ -94,51 +94,51 @@ namespace ImageSharp.PixelFormats
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromBytes(byte x, byte y, byte z, byte w)
public void PackFromRgba32(Rgba32 source)
{
this.PackFromVector4(new Vector4(x, y, z, w) / 255F);
this.PackFromVector4(source.ToVector4());
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToXyzBytes(Span<byte> bytes, int startIndex)
public void ToRgb24(ref Rgb24 dest)
{
Vector4 vector = this.ToVector4() * 255F;
bytes[startIndex] = (byte)vector.X;
bytes[startIndex + 1] = (byte)vector.Y;
bytes[startIndex + 2] = (byte)vector.Z;
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToXyzwBytes(Span<byte> bytes, int startIndex)
public void ToRgba32(ref Rgba32 dest)
{
Vector4 vector = this.ToVector4() * 255F;
bytes[startIndex] = (byte)vector.X;
bytes[startIndex + 1] = (byte)vector.Y;
bytes[startIndex + 2] = (byte)vector.Z;
bytes[startIndex + 3] = (byte)vector.W;
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
dest.A = (byte)vector.W;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToZyxBytes(Span<byte> bytes, int startIndex)
public void ToBgr24(ref Bgr24 dest)
{
Vector4 vector = this.ToVector4() * 255F;
bytes[startIndex] = (byte)vector.Z;
bytes[startIndex + 1] = (byte)vector.Y;
bytes[startIndex + 2] = (byte)vector.X;
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToZyxwBytes(Span<byte> bytes, int startIndex)
public void ToBgra32(ref Bgra32 dest)
{
Vector4 vector = this.ToVector4() * 255F;
bytes[startIndex] = (byte)vector.Z;
bytes[startIndex + 1] = (byte)vector.Y;
bytes[startIndex + 2] = (byte)vector.X;
bytes[startIndex + 3] = (byte)vector.W;
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
dest.A = (byte)vector.W;
}
/// <inheritdoc />
@ -179,9 +179,9 @@ namespace ImageSharp.PixelFormats
private static ushort Pack(float x, float y, float z, float w)
{
return (ushort)((((int)Math.Round(w.Clamp(0, 1) * 15F) & 0x0F) << 12) |
(((int)Math.Round(x.Clamp(0, 1) * 15F) & 0x0F) << 8) |
(((int)Math.Round(y.Clamp(0, 1) * 15F) & 0x0F) << 4) |
((int)Math.Round(z.Clamp(0, 1) * 15F) & 0x0F));
(((int)Math.Round(x.Clamp(0, 1) * 15F) & 0x0F) << 8) |
(((int)Math.Round(y.Clamp(0, 1) * 15F) & 0x0F) << 4) |
((int)Math.Round(z.Clamp(0, 1) * 15F) & 0x0F));
}
}
}

53
src/ImageSharp/PixelFormats/Bgra5551.cs

@ -72,7 +72,7 @@ namespace ImageSharp.PixelFormats
}
/// <inheritdoc />
public PixelOperations<Bgra5551> CreateBulkOperations() => new PixelOperations<Bgra5551>();
public PixelOperations<Bgra5551> CreatePixelOperations() => new PixelOperations<Bgra5551>();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -94,51 +94,51 @@ namespace ImageSharp.PixelFormats
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromBytes(byte x, byte y, byte z, byte w)
public void PackFromRgba32(Rgba32 source)
{
this.PackFromVector4(new Vector4(x, y, z, w) / 255F);
this.PackFromVector4(source.ToVector4());
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToXyzBytes(Span<byte> bytes, int startIndex)
public void ToRgb24(ref Rgb24 dest)
{
Vector4 vector = this.ToVector4() * 255F;
bytes[startIndex] = (byte)vector.X;
bytes[startIndex + 1] = (byte)vector.Y;
bytes[startIndex + 2] = (byte)vector.Z;
Vector4 vector = this.ToScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToXyzwBytes(Span<byte> bytes, int startIndex)
public void ToRgba32(ref Rgba32 dest)
{
Vector4 vector = this.ToVector4() * 255F;
bytes[startIndex] = (byte)vector.X;
bytes[startIndex + 1] = (byte)vector.Y;
bytes[startIndex + 2] = (byte)vector.Z;
bytes[startIndex + 3] = (byte)vector.W;
Vector4 vector = this.ToScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
dest.A = (byte)vector.W;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToZyxBytes(Span<byte> bytes, int startIndex)
public void ToBgr24(ref Bgr24 dest)
{
Vector4 vector = this.ToVector4() * 255F;
bytes[startIndex] = (byte)vector.Z;
bytes[startIndex + 1] = (byte)vector.Y;
bytes[startIndex + 2] = (byte)vector.X;
Vector4 vector = this.ToScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToZyxwBytes(Span<byte> bytes, int startIndex)
public void ToBgra32(ref Bgra32 dest)
{
Vector4 vector = this.ToVector4() * 255F;
bytes[startIndex] = (byte)vector.Z;
bytes[startIndex + 1] = (byte)vector.Y;
bytes[startIndex + 2] = (byte)vector.X;
bytes[startIndex + 3] = (byte)vector.W;
Vector4 vector = this.ToScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
dest.A = (byte)vector.W;
}
/// <inheritdoc />
@ -190,5 +190,8 @@ namespace ImageSharp.PixelFormats
(((int)Math.Round(z.Clamp(0, 1) * 31F) & 0x1F) << 0) |
(((int)Math.Round(w.Clamp(0, 1)) & 0x1) << 15));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private Vector4 ToScaledVector4() => this.ToVector4() * 255f;
}
}

42
src/ImageSharp/PixelFormats/Byte4.cs

@ -73,7 +73,7 @@ namespace ImageSharp.PixelFormats
}
/// <inheritdoc />
public PixelOperations<Byte4> CreateBulkOperations() => new PixelOperations<Byte4>();
public PixelOperations<Byte4> CreatePixelOperations() => new PixelOperations<Byte4>();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -95,51 +95,51 @@ namespace ImageSharp.PixelFormats
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromBytes(byte x, byte y, byte z, byte w)
public void PackFromRgba32(Rgba32 source)
{
this.PackFromVector4(new Vector4(x, y, z, w));
this.PackFromVector4(source.ToUnscaledVector4());
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToXyzBytes(Span<byte> bytes, int startIndex)
public void ToRgb24(ref Rgb24 dest)
{
Vector4 vector = this.ToVector4();
bytes[startIndex] = (byte)vector.X;
bytes[startIndex + 1] = (byte)vector.Y;
bytes[startIndex + 2] = (byte)vector.Z;
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToXyzwBytes(Span<byte> bytes, int startIndex)
public void ToRgba32(ref Rgba32 dest)
{
Vector4 vector = this.ToVector4();
bytes[startIndex] = (byte)vector.X;
bytes[startIndex + 1] = (byte)vector.Y;
bytes[startIndex + 2] = (byte)vector.Z;
bytes[startIndex + 3] = (byte)vector.W;
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
dest.A = (byte)vector.W;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToZyxBytes(Span<byte> bytes, int startIndex)
public void ToBgr24(ref Bgr24 dest)
{
Vector4 vector = this.ToVector4();
bytes[startIndex] = (byte)vector.Z;
bytes[startIndex + 1] = (byte)vector.Y;
bytes[startIndex + 2] = (byte)vector.X;
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToZyxwBytes(Span<byte> bytes, int startIndex)
public void ToBgra32(ref Bgra32 dest)
{
Vector4 vector = this.ToVector4();
bytes[startIndex] = (byte)vector.Z;
bytes[startIndex + 1] = (byte)vector.Y;
bytes[startIndex + 2] = (byte)vector.X;
bytes[startIndex + 3] = (byte)vector.W;
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
dest.A = (byte)vector.W;
}
/// <inheritdoc />

14
src/ImageSharp/PixelFormats/ColorBuilder{TPixel}.cs

@ -35,12 +35,13 @@ namespace ImageSharp.PixelFormats
}
TPixel result = default(TPixel);
result.PackFromBytes(
Rgba32 rgba = new Rgba32(
(byte)(packedValue >> 24),
(byte)(packedValue >> 16),
(byte)(packedValue >> 8),
(byte)(packedValue >> 0));
result.PackFromRgba32(rgba);
return result;
}
@ -51,12 +52,7 @@ namespace ImageSharp.PixelFormats
/// <param name="green">The green intensity.</param>
/// <param name="blue">The blue intensity.</param>
/// <returns>Returns a <typeparamref name="TPixel"/> that represents the color defined by the provided RGB values with 100% opacity.</returns>
public static TPixel FromRGB(byte red, byte green, byte blue)
{
TPixel color = default(TPixel);
color.PackFromBytes(red, green, blue, 255);
return color;
}
public static TPixel FromRGB(byte red, byte green, byte blue) => FromRGBA(red, green, blue, 255);
/// <summary>
/// Creates a new <typeparamref name="TPixel"/> representation from standard RGBA bytes.
@ -69,7 +65,7 @@ namespace ImageSharp.PixelFormats
public static TPixel FromRGBA(byte red, byte green, byte blue, byte alpha)
{
TPixel color = default(TPixel);
color.PackFromBytes(red, green, blue, alpha);
color.PackFromRgba32(new Rgba32(red, green, blue, alpha));
return color;
}

302
src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.cs

@ -0,0 +1,302 @@
// <auto-generated />
// <copyright file="PixelOperations{TPixel}.Generated.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.PixelFormats
{
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
public partial class PixelOperations<TPixel>
{
/// <summary>
/// Converts 'count' elements in 'source` span of <see cref="Rgba32"/> data to a span of <typeparamref name="TPixel"/>-s.
/// </summary>
/// <param name="source">The source <see cref="Span{T}"/> of <see cref="Rgba32"/> data.</param>
/// <param name="destPixels">The <see cref="Span{T}"/> to the destination pixels.</param>
/// <param name="count">The number of pixels to convert.</param>
internal virtual void PackFromRgba32(Span<Rgba32> source, Span<TPixel> destPixels, int count)
{
GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count);
ref Rgba32 sourceRef = ref source.DangerousGetPinnableReference();
ref TPixel destRef = ref destPixels.DangerousGetPinnableReference();
Rgba32 rgba = new Rgba32(0, 0, 0, 255);
for (int i = 0; i < count; i++)
{
ref TPixel dp = ref Unsafe.Add(ref destRef, i);
rgba = Unsafe.Add(ref sourceRef, i);
dp.PackFromRgba32(rgba);
}
}
/// <summary>
/// A helper for <see cref="PackFromRgba32(Span{Rgba32}, Span{TPixel}, int)"/> that expects a byte span.
/// The layout of the data in 'sourceBytes' must be compatible with <see cref="Rgba32"/> layout.
/// </summary>
/// <param name="sourceBytes">The <see cref="Span{T}"/> to the source bytes.</param>
/// <param name="destPixels">The <see cref="Span{T}"/> to the destination pixels.</param>
/// <param name="count">The number of pixels to convert.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void PackFromRgba32Bytes(Span<byte> sourceBytes, Span<TPixel> destPixels, int count)
{
this.PackFromRgba32(sourceBytes.NonPortableCast<byte, Rgba32>(), destPixels, count);
}
/// <summary>
/// Converts 'count' pixels in 'sourcePixels` span to a span of <see cref="Rgba32"/>-s.
/// Bulk version of <see cref="IPixel.ToRgba32(ref Rgba32)"/>.
/// </summary>
/// <param name="sourcePixels">The span of source pixels</param>
/// <param name="dest">The destination span of <see cref="Rgba32"/> data.</param>
/// <param name="count">The number of pixels to convert.</param>
internal virtual void ToRgba32(Span<TPixel> sourcePixels, Span<Rgba32> dest, int count)
{
GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count);
ref TPixel sourceBaseRef = ref sourcePixels.DangerousGetPinnableReference();
ref Rgba32 destBaseRef = ref dest.DangerousGetPinnableReference();
for (int i = 0; i < count; i++)
{
ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i);
ref Rgba32 dp = ref Unsafe.Add(ref destBaseRef, i);
sp.ToRgba32(ref dp);
}
}
/// <summary>
/// A helper for <see cref="ToRgba32(Span{TPixel}, Span{Rgba32}, int)"/> that expects a byte span as destination.
/// The layout of the data in 'destBytes' must be compatible with <see cref="Rgba32"/> layout.
/// </summary>
/// <param name="sourceColors">The <see cref="Span{T}"/> to the source colors.</param>
/// <param name="destBytes">The <see cref="Span{T}"/> to the destination bytes.</param>
/// <param name="count">The number of pixels to convert.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void ToRgba32Bytes(Span<TPixel> sourceColors, Span<byte> destBytes, int count)
{
this.ToRgba32(sourceColors, destBytes.NonPortableCast<byte, Rgba32>(), count);
}
/// <summary>
/// Converts 'count' elements in 'source` span of <see cref="Bgra32"/> data to a span of <typeparamref name="TPixel"/>-s.
/// </summary>
/// <param name="source">The source <see cref="Span{T}"/> of <see cref="Bgra32"/> data.</param>
/// <param name="destPixels">The <see cref="Span{T}"/> to the destination pixels.</param>
/// <param name="count">The number of pixels to convert.</param>
internal virtual void PackFromBgra32(Span<Bgra32> source, Span<TPixel> destPixels, int count)
{
GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count);
ref Bgra32 sourceRef = ref source.DangerousGetPinnableReference();
ref TPixel destRef = ref destPixels.DangerousGetPinnableReference();
Rgba32 rgba = new Rgba32(0, 0, 0, 255);
for (int i = 0; i < count; i++)
{
ref TPixel dp = ref Unsafe.Add(ref destRef, i);
rgba = Unsafe.Add(ref sourceRef, i).ToRgba32();
dp.PackFromRgba32(rgba);
}
}
/// <summary>
/// A helper for <see cref="PackFromBgra32(Span{Bgra32}, Span{TPixel}, int)"/> that expects a byte span.
/// The layout of the data in 'sourceBytes' must be compatible with <see cref="Bgra32"/> layout.
/// </summary>
/// <param name="sourceBytes">The <see cref="Span{T}"/> to the source bytes.</param>
/// <param name="destPixels">The <see cref="Span{T}"/> to the destination pixels.</param>
/// <param name="count">The number of pixels to convert.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void PackFromBgra32Bytes(Span<byte> sourceBytes, Span<TPixel> destPixels, int count)
{
this.PackFromBgra32(sourceBytes.NonPortableCast<byte, Bgra32>(), destPixels, count);
}
/// <summary>
/// Converts 'count' pixels in 'sourcePixels` span to a span of <see cref="Bgra32"/>-s.
/// Bulk version of <see cref="IPixel.ToBgra32(ref Bgra32)"/>.
/// </summary>
/// <param name="sourcePixels">The span of source pixels</param>
/// <param name="dest">The destination span of <see cref="Bgra32"/> data.</param>
/// <param name="count">The number of pixels to convert.</param>
internal virtual void ToBgra32(Span<TPixel> sourcePixels, Span<Bgra32> dest, int count)
{
GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count);
ref TPixel sourceBaseRef = ref sourcePixels.DangerousGetPinnableReference();
ref Bgra32 destBaseRef = ref dest.DangerousGetPinnableReference();
for (int i = 0; i < count; i++)
{
ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i);
ref Bgra32 dp = ref Unsafe.Add(ref destBaseRef, i);
sp.ToBgra32(ref dp);
}
}
/// <summary>
/// A helper for <see cref="ToBgra32(Span{TPixel}, Span{Bgra32}, int)"/> that expects a byte span as destination.
/// The layout of the data in 'destBytes' must be compatible with <see cref="Bgra32"/> layout.
/// </summary>
/// <param name="sourceColors">The <see cref="Span{T}"/> to the source colors.</param>
/// <param name="destBytes">The <see cref="Span{T}"/> to the destination bytes.</param>
/// <param name="count">The number of pixels to convert.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void ToBgra32Bytes(Span<TPixel> sourceColors, Span<byte> destBytes, int count)
{
this.ToBgra32(sourceColors, destBytes.NonPortableCast<byte, Bgra32>(), count);
}
/// <summary>
/// Converts 'count' elements in 'source` span of <see cref="Rgb24"/> data to a span of <typeparamref name="TPixel"/>-s.
/// </summary>
/// <param name="source">The source <see cref="Span{T}"/> of <see cref="Rgb24"/> data.</param>
/// <param name="destPixels">The <see cref="Span{T}"/> to the destination pixels.</param>
/// <param name="count">The number of pixels to convert.</param>
internal virtual void PackFromRgb24(Span<Rgb24> source, Span<TPixel> destPixels, int count)
{
GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count);
ref Rgb24 sourceRef = ref source.DangerousGetPinnableReference();
ref TPixel destRef = ref destPixels.DangerousGetPinnableReference();
Rgba32 rgba = new Rgba32(0, 0, 0, 255);
for (int i = 0; i < count; i++)
{
ref TPixel dp = ref Unsafe.Add(ref destRef, i);
rgba.Rgb = Unsafe.Add(ref sourceRef, i);
dp.PackFromRgba32(rgba);
}
}
/// <summary>
/// A helper for <see cref="PackFromRgb24(Span{Rgb24}, Span{TPixel}, int)"/> that expects a byte span.
/// The layout of the data in 'sourceBytes' must be compatible with <see cref="Rgb24"/> layout.
/// </summary>
/// <param name="sourceBytes">The <see cref="Span{T}"/> to the source bytes.</param>
/// <param name="destPixels">The <see cref="Span{T}"/> to the destination pixels.</param>
/// <param name="count">The number of pixels to convert.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void PackFromRgb24Bytes(Span<byte> sourceBytes, Span<TPixel> destPixels, int count)
{
this.PackFromRgb24(sourceBytes.NonPortableCast<byte, Rgb24>(), destPixels, count);
}
/// <summary>
/// Converts 'count' pixels in 'sourcePixels` span to a span of <see cref="Rgb24"/>-s.
/// Bulk version of <see cref="IPixel.ToRgb24(ref Rgb24)"/>.
/// </summary>
/// <param name="sourcePixels">The span of source pixels</param>
/// <param name="dest">The destination span of <see cref="Rgb24"/> data.</param>
/// <param name="count">The number of pixels to convert.</param>
internal virtual void ToRgb24(Span<TPixel> sourcePixels, Span<Rgb24> dest, int count)
{
GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count);
ref TPixel sourceBaseRef = ref sourcePixels.DangerousGetPinnableReference();
ref Rgb24 destBaseRef = ref dest.DangerousGetPinnableReference();
for (int i = 0; i < count; i++)
{
ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i);
ref Rgb24 dp = ref Unsafe.Add(ref destBaseRef, i);
sp.ToRgb24(ref dp);
}
}
/// <summary>
/// A helper for <see cref="ToRgb24(Span{TPixel}, Span{Rgb24}, int)"/> that expects a byte span as destination.
/// The layout of the data in 'destBytes' must be compatible with <see cref="Rgb24"/> layout.
/// </summary>
/// <param name="sourceColors">The <see cref="Span{T}"/> to the source colors.</param>
/// <param name="destBytes">The <see cref="Span{T}"/> to the destination bytes.</param>
/// <param name="count">The number of pixels to convert.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void ToRgb24Bytes(Span<TPixel> sourceColors, Span<byte> destBytes, int count)
{
this.ToRgb24(sourceColors, destBytes.NonPortableCast<byte, Rgb24>(), count);
}
/// <summary>
/// Converts 'count' elements in 'source` span of <see cref="Bgr24"/> data to a span of <typeparamref name="TPixel"/>-s.
/// </summary>
/// <param name="source">The source <see cref="Span{T}"/> of <see cref="Bgr24"/> data.</param>
/// <param name="destPixels">The <see cref="Span{T}"/> to the destination pixels.</param>
/// <param name="count">The number of pixels to convert.</param>
internal virtual void PackFromBgr24(Span<Bgr24> source, Span<TPixel> destPixels, int count)
{
GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count);
ref Bgr24 sourceRef = ref source.DangerousGetPinnableReference();
ref TPixel destRef = ref destPixels.DangerousGetPinnableReference();
Rgba32 rgba = new Rgba32(0, 0, 0, 255);
for (int i = 0; i < count; i++)
{
ref TPixel dp = ref Unsafe.Add(ref destRef, i);
rgba.Bgr = Unsafe.Add(ref sourceRef, i);
dp.PackFromRgba32(rgba);
}
}
/// <summary>
/// A helper for <see cref="PackFromBgr24(Span{Bgr24}, Span{TPixel}, int)"/> that expects a byte span.
/// The layout of the data in 'sourceBytes' must be compatible with <see cref="Bgr24"/> layout.
/// </summary>
/// <param name="sourceBytes">The <see cref="Span{T}"/> to the source bytes.</param>
/// <param name="destPixels">The <see cref="Span{T}"/> to the destination pixels.</param>
/// <param name="count">The number of pixels to convert.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void PackFromBgr24Bytes(Span<byte> sourceBytes, Span<TPixel> destPixels, int count)
{
this.PackFromBgr24(sourceBytes.NonPortableCast<byte, Bgr24>(), destPixels, count);
}
/// <summary>
/// Converts 'count' pixels in 'sourcePixels` span to a span of <see cref="Bgr24"/>-s.
/// Bulk version of <see cref="IPixel.ToBgr24(ref Bgr24)"/>.
/// </summary>
/// <param name="sourcePixels">The span of source pixels</param>
/// <param name="dest">The destination span of <see cref="Bgr24"/> data.</param>
/// <param name="count">The number of pixels to convert.</param>
internal virtual void ToBgr24(Span<TPixel> sourcePixels, Span<Bgr24> dest, int count)
{
GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count);
ref TPixel sourceBaseRef = ref sourcePixels.DangerousGetPinnableReference();
ref Bgr24 destBaseRef = ref dest.DangerousGetPinnableReference();
for (int i = 0; i < count; i++)
{
ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i);
ref Bgr24 dp = ref Unsafe.Add(ref destBaseRef, i);
sp.ToBgr24(ref dp);
}
}
/// <summary>
/// A helper for <see cref="ToBgr24(Span{TPixel}, Span{Bgr24}, int)"/> that expects a byte span as destination.
/// The layout of the data in 'destBytes' must be compatible with <see cref="Bgr24"/> layout.
/// </summary>
/// <param name="sourceColors">The <see cref="Span{T}"/> to the source colors.</param>
/// <param name="destBytes">The <see cref="Span{T}"/> to the destination bytes.</param>
/// <param name="count">The number of pixels to convert.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void ToBgr24Bytes(Span<TPixel> sourceColors, Span<byte> destBytes, int count)
{
this.ToBgr24(sourceColors, destBytes.NonPortableCast<byte, Bgr24>(), count);
}
}
}

130
src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.tt

@ -0,0 +1,130 @@
<#
// <copyright file="PixelOperations{TPixel}.Generated.tt" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
#>
<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>
<#
void GenerateToDestFormatMethods(string pixelType)
{
#>
/// <summary>
/// Converts 'count' pixels in 'sourcePixels` span to a span of <see cref="<#=pixelType#>"/>-s.
/// Bulk version of <see cref="IPixel.To<#=pixelType#>(ref <#=pixelType#>)"/>.
/// </summary>
/// <param name="sourcePixels">The span of source pixels</param>
/// <param name="dest">The destination span of <see cref="<#=pixelType#>"/> data.</param>
/// <param name="count">The number of pixels to convert.</param>
internal virtual void To<#=pixelType#>(Span<TPixel> sourcePixels, Span<<#=pixelType#>> dest, int count)
{
GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count);
ref TPixel sourceBaseRef = ref sourcePixels.DangerousGetPinnableReference();
ref <#=pixelType#> destBaseRef = ref dest.DangerousGetPinnableReference();
for (int i = 0; i < count; i++)
{
ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i);
ref <#=pixelType#> dp = ref Unsafe.Add(ref destBaseRef, i);
sp.To<#=pixelType#>(ref dp);
}
}
/// <summary>
/// A helper for <see cref="To<#=pixelType#>(Span{TPixel}, Span{<#=pixelType#>}, int)"/> that expects a byte span as destination.
/// The layout of the data in 'destBytes' must be compatible with <see cref="<#=pixelType#>"/> layout.
/// </summary>
/// <param name="sourceColors">The <see cref="Span{T}"/> to the source colors.</param>
/// <param name="destBytes">The <see cref="Span{T}"/> to the destination bytes.</param>
/// <param name="count">The number of pixels to convert.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void To<#=pixelType#>Bytes(Span<TPixel> sourceColors, Span<byte> destBytes, int count)
{
this.To<#=pixelType#>(sourceColors, destBytes.NonPortableCast<byte, <#=pixelType#>>(), count);
}
<#
}
void GeneratePackFromMethodUsingPackFromRgba32(string pixelType, string rgbaOperationCode)
{
#>
/// <summary>
/// Converts 'count' elements in 'source` span of <see cref="<#=pixelType#>"/> data to a span of <typeparamref name="TPixel"/>-s.
/// </summary>
/// <param name="source">The source <see cref="Span{T}"/> of <see cref="<#=pixelType#>"/> data.</param>
/// <param name="destPixels">The <see cref="Span{T}"/> to the destination pixels.</param>
/// <param name="count">The number of pixels to convert.</param>
internal virtual void PackFrom<#=pixelType#>(Span<<#=pixelType#>> source, Span<TPixel> destPixels, int count)
{
GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count);
ref <#=pixelType#> sourceRef = ref source.DangerousGetPinnableReference();
ref TPixel destRef = ref destPixels.DangerousGetPinnableReference();
Rgba32 rgba = new Rgba32(0, 0, 0, 255);
for (int i = 0; i < count; i++)
{
ref TPixel dp = ref Unsafe.Add(ref destRef, i);
<#=rgbaOperationCode#>
dp.PackFromRgba32(rgba);
}
}
/// <summary>
/// A helper for <see cref="PackFrom<#=pixelType#>(Span{<#=pixelType#>}, Span{TPixel}, int)"/> that expects a byte span.
/// The layout of the data in 'sourceBytes' must be compatible with <see cref="<#=pixelType#>"/> layout.
/// </summary>
/// <param name="sourceBytes">The <see cref="Span{T}"/> to the source bytes.</param>
/// <param name="destPixels">The <see cref="Span{T}"/> to the destination pixels.</param>
/// <param name="count">The number of pixels to convert.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void PackFrom<#=pixelType#>Bytes(Span<byte> sourceBytes, Span<TPixel> destPixels, int count)
{
this.PackFrom<#=pixelType#>(sourceBytes.NonPortableCast<byte, <#=pixelType#>>(), destPixels, count);
}
<#
}
#>
// <auto-generated />
// <copyright file="PixelOperations{TPixel}.Generated.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.PixelFormats
{
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
public partial class PixelOperations<TPixel>
{
<#
GeneratePackFromMethodUsingPackFromRgba32("Rgba32", "rgba = Unsafe.Add(ref sourceRef, i);");
GenerateToDestFormatMethods("Rgba32");
GeneratePackFromMethodUsingPackFromRgba32("Bgra32", "rgba = Unsafe.Add(ref sourceRef, i).ToRgba32();");
GenerateToDestFormatMethods("Bgra32");
GeneratePackFromMethodUsingPackFromRgba32("Rgb24", "rgba.Rgb = Unsafe.Add(ref sourceRef, i);");
GenerateToDestFormatMethods("Rgb24");
GeneratePackFromMethodUsingPackFromRgba32("Bgr24", "rgba.Bgr = Unsafe.Add(ref sourceRef, i);");
GenerateToDestFormatMethods("Bgr24");
#>
}
}

130
src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.cs

@ -0,0 +1,130 @@
// <auto-generated />
// <copyright file="PixelOperations{TPixel}.Generated.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using ImageSharp.Memory;
using ImageSharp.PixelFormats;
/// <content>
/// Provides optimized overrides for bulk operations.
/// </content>
public partial struct Rgba32
{
internal partial class PixelOperations : PixelOperations<Rgba32>
{
/// <inheritdoc />
internal override void PackFromRgb24(Span<Rgb24> source, Span<Rgba32> destPixels, int count)
{
GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count);
ref Rgb24 sourceRef = ref source.DangerousGetPinnableReference();
ref Rgba32 destRef = ref destPixels.DangerousGetPinnableReference();
for (int i = 0; i < count; i++)
{
ref Rgb24 sp = ref Unsafe.Add(ref sourceRef, i);
ref Rgba32 dp = ref Unsafe.Add(ref destRef, i);
Unsafe.As<Rgba32, Rgb24>(ref dp) = sp; dp.A = 255;
}
}
/// <inheritdoc />
internal override void ToRgb24(Span<Rgba32> sourcePixels, Span<Rgb24> dest, int count)
{
GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count);
ref Rgba32 sourceRef = ref sourcePixels.DangerousGetPinnableReference();
ref Rgb24 destRef = ref dest.DangerousGetPinnableReference();
for (int i = 0; i < count; i++)
{
ref Rgba32 sp = ref Unsafe.Add(ref sourceRef, i);
ref Rgb24 dp = ref Unsafe.Add(ref destRef, i);
dp = Unsafe.As<Rgba32, Rgb24>(ref sp);
}
}
/// <inheritdoc />
internal override void PackFromBgr24(Span<Bgr24> source, Span<Rgba32> destPixels, int count)
{
GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count);
ref Bgr24 sourceRef = ref source.DangerousGetPinnableReference();
ref Rgba32 destRef = ref destPixels.DangerousGetPinnableReference();
for (int i = 0; i < count; i++)
{
ref Bgr24 sp = ref Unsafe.Add(ref sourceRef, i);
ref Rgba32 dp = ref Unsafe.Add(ref destRef, i);
dp.Bgr = sp; dp.A = 255;
}
}
/// <inheritdoc />
internal override void ToBgr24(Span<Rgba32> sourcePixels, Span<Bgr24> dest, int count)
{
GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count);
ref Rgba32 sourceRef = ref sourcePixels.DangerousGetPinnableReference();
ref Bgr24 destRef = ref dest.DangerousGetPinnableReference();
for (int i = 0; i < count; i++)
{
ref Rgba32 sp = ref Unsafe.Add(ref sourceRef, i);
ref Bgr24 dp = ref Unsafe.Add(ref destRef, i);
dp = sp.Bgr;
}
}
/// <inheritdoc />
internal override void PackFromBgra32(Span<Bgra32> source, Span<Rgba32> destPixels, int count)
{
GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count);
ref Bgra32 sourceRef = ref source.DangerousGetPinnableReference();
ref Rgba32 destRef = ref destPixels.DangerousGetPinnableReference();
for (int i = 0; i < count; i++)
{
ref Bgra32 sp = ref Unsafe.Add(ref sourceRef, i);
ref Rgba32 dp = ref Unsafe.Add(ref destRef, i);
dp = sp.ToRgba32();
}
}
/// <inheritdoc />
internal override void ToBgra32(Span<Rgba32> sourcePixels, Span<Bgra32> dest, int count)
{
GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count);
ref Rgba32 sourceRef = ref sourcePixels.DangerousGetPinnableReference();
ref Bgra32 destRef = ref dest.DangerousGetPinnableReference();
for (int i = 0; i < count; i++)
{
ref Rgba32 sp = ref Unsafe.Add(ref sourceRef, i);
ref Bgra32 dp = ref Unsafe.Add(ref destRef, i);
dp = sp.ToBgra32();
}
}
}
}
}

99
src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.tt

@ -0,0 +1,99 @@
<#
// <copyright file="Rgba32.PixelOperations.Generated.tt" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
#>
<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>
<#
void GeneratePackFromMethod(string pixelType, string converterCode)
{
#>
/// <inheritdoc />
internal override void PackFrom<#=pixelType#>(Span<<#=pixelType#>> source, Span<Rgba32> destPixels, int count)
{
GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count);
ref <#=pixelType#> sourceRef = ref source.DangerousGetPinnableReference();
ref Rgba32 destRef = ref destPixels.DangerousGetPinnableReference();
for (int i = 0; i < count; i++)
{
ref <#=pixelType#> sp = ref Unsafe.Add(ref sourceRef, i);
ref Rgba32 dp = ref Unsafe.Add(ref destRef, i);
<#=converterCode#>
}
}
<#
}
void GenerateConvertToMethod(string pixelType, string converterCode)
{
#>
/// <inheritdoc />
internal override void To<#=pixelType#>(Span<Rgba32> sourcePixels, Span<<#=pixelType#>> dest, int count)
{
GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count);
ref Rgba32 sourceRef = ref sourcePixels.DangerousGetPinnableReference();
ref <#=pixelType#> destRef = ref dest.DangerousGetPinnableReference();
for (int i = 0; i < count; i++)
{
ref Rgba32 sp = ref Unsafe.Add(ref sourceRef, i);
ref <#=pixelType#> dp = ref Unsafe.Add(ref destRef, i);
<#=converterCode#>
}
}
<#
}
#>
// <auto-generated />
// <copyright file="PixelOperations{TPixel}.Generated.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using ImageSharp.Memory;
using ImageSharp.PixelFormats;
/// <content>
/// Provides optimized overrides for bulk operations.
/// </content>
public partial struct Rgba32
{
internal partial class PixelOperations : PixelOperations<Rgba32>
{
<#
GeneratePackFromMethod("Rgb24", "Unsafe.As<Rgba32, Rgb24>(ref dp) = sp; dp.A = 255;");
GenerateConvertToMethod("Rgb24", "dp = Unsafe.As<Rgba32, Rgb24>(ref sp);");
GeneratePackFromMethod("Bgr24", "dp.Bgr = sp; dp.A = 255;");
GenerateConvertToMethod("Bgr24", "dp = sp.Bgr;");
GeneratePackFromMethod("Bgra32", "dp = sp.ToRgba32();");
GenerateConvertToMethod("Bgra32", "dp = sp.ToBgra32();");
#>
}
}
}

76
src/ImageSharp/PixelFormats/HalfSingle.cs

@ -76,7 +76,7 @@ namespace ImageSharp.PixelFormats
}
/// <inheritdoc />
public PixelOperations<HalfSingle> CreateBulkOperations() => new PixelOperations<HalfSingle>();
public PixelOperations<HalfSingle> CreatePixelOperations() => new PixelOperations<HalfSingle>();
/// <summary>
/// Expands the packed representation into a <see cref="float"/>.
@ -104,67 +104,51 @@ namespace ImageSharp.PixelFormats
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromBytes(byte x, byte y, byte z, byte w)
public void PackFromRgba32(Rgba32 source)
{
this.PackFromVector4(new Vector4(x, y, z, w) / MaxBytes);
this.PackFromVector4(source.ToVector4());
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToXyzBytes(Span<byte> bytes, int startIndex)
public void ToRgb24(ref Rgb24 dest)
{
Vector4 vector = this.ToVector4();
vector *= MaxBytes;
vector += Half;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
bytes[startIndex] = (byte)vector.X;
bytes[startIndex + 1] = (byte)vector.Y;
bytes[startIndex + 2] = (byte)vector.Z;
Vector4 vector = this.ToScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToXyzwBytes(Span<byte> bytes, int startIndex)
public void ToRgba32(ref Rgba32 dest)
{
Vector4 vector = this.ToVector4();
vector *= MaxBytes;
vector += Half;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
bytes[startIndex] = (byte)vector.X;
bytes[startIndex + 1] = (byte)vector.Y;
bytes[startIndex + 2] = (byte)vector.Z;
bytes[startIndex + 3] = (byte)vector.W;
Vector4 vector = this.ToScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
dest.A = (byte)vector.W;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToZyxBytes(Span<byte> bytes, int startIndex)
public void ToBgr24(ref Bgr24 dest)
{
Vector4 vector = this.ToVector4();
vector *= MaxBytes;
vector += Half;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
bytes[startIndex] = (byte)vector.Z;
bytes[startIndex + 1] = (byte)vector.Y;
bytes[startIndex + 2] = (byte)vector.X;
Vector4 vector = this.ToScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToZyxwBytes(Span<byte> bytes, int startIndex)
public void ToBgra32(ref Bgra32 dest)
{
Vector4 vector = this.ToVector4();
vector *= MaxBytes;
vector += Half;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
bytes[startIndex] = (byte)vector.Z;
bytes[startIndex + 1] = (byte)vector.Y;
bytes[startIndex + 2] = (byte)vector.X;
bytes[startIndex + 3] = (byte)vector.W;
Vector4 vector = this.ToScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
dest.A = (byte)vector.W;
}
/// <inheritdoc />
@ -192,5 +176,15 @@ namespace ImageSharp.PixelFormats
{
return this.PackedValue.GetHashCode();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private Vector4 ToScaledVector4()
{
Vector4 vector = this.ToVector4();
vector *= MaxBytes;
vector += Half;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
return vector;
}
}
}

76
src/ImageSharp/PixelFormats/HalfVector2.cs

@ -86,7 +86,7 @@ namespace ImageSharp.PixelFormats
}
/// <inheritdoc />
public PixelOperations<HalfVector2> CreateBulkOperations() => new PixelOperations<HalfVector2>();
public PixelOperations<HalfVector2> CreatePixelOperations() => new PixelOperations<HalfVector2>();
/// <summary>
/// Expands the packed representation into a <see cref="Vector2"/>.
@ -118,67 +118,51 @@ namespace ImageSharp.PixelFormats
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromBytes(byte x, byte y, byte z, byte w)
public void PackFromRgba32(Rgba32 source)
{
this.PackFromVector4(new Vector4(x, y, z, w) / MaxBytes);
this.PackFromVector4(source.ToVector4());
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToXyzBytes(Span<byte> bytes, int startIndex)
public void ToRgb24(ref Rgb24 dest)
{
Vector4 vector = this.ToVector4();
vector *= MaxBytes;
vector += Half;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
bytes[startIndex] = (byte)vector.X;
bytes[startIndex + 1] = (byte)vector.Y;
bytes[startIndex + 2] = (byte)vector.Z;
Vector4 vector = this.ToScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = 0;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToXyzwBytes(Span<byte> bytes, int startIndex)
public void ToRgba32(ref Rgba32 dest)
{
Vector4 vector = this.ToVector4();
vector *= MaxBytes;
vector += Half;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
bytes[startIndex] = (byte)vector.X;
bytes[startIndex + 1] = (byte)vector.Y;
bytes[startIndex + 2] = (byte)vector.Z;
bytes[startIndex + 3] = (byte)vector.W;
Vector4 vector = this.ToScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = 0;
dest.A = 255;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToZyxBytes(Span<byte> bytes, int startIndex)
public void ToBgr24(ref Bgr24 dest)
{
Vector4 vector = this.ToVector4();
vector *= MaxBytes;
vector += Half;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
bytes[startIndex] = (byte)vector.Z;
bytes[startIndex + 1] = (byte)vector.Y;
bytes[startIndex + 2] = (byte)vector.X;
Vector4 vector = this.ToScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = 0;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToZyxwBytes(Span<byte> bytes, int startIndex)
public void ToBgra32(ref Bgra32 dest)
{
Vector4 vector = this.ToVector4();
vector *= MaxBytes;
vector += Half;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
bytes[startIndex] = (byte)vector.Z;
bytes[startIndex + 1] = (byte)vector.Y;
bytes[startIndex + 2] = (byte)vector.X;
bytes[startIndex + 3] = (byte)vector.W;
Vector4 vector = this.ToScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = 0;
dest.A = 255;
}
/// <inheritdoc />
@ -220,5 +204,15 @@ namespace ImageSharp.PixelFormats
uint num = (uint)(HalfTypeHelper.Pack(y) << 0x10);
return num2 | num;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private Vector4 ToScaledVector4()
{
Vector4 vector = this.ToVector4();
vector *= MaxBytes;
vector += Half;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
return vector;
}
}
}

76
src/ImageSharp/PixelFormats/HalfVector4.cs

@ -89,7 +89,7 @@ namespace ImageSharp.PixelFormats
}
/// <inheritdoc />
public PixelOperations<HalfVector4> CreateBulkOperations() => new PixelOperations<HalfVector4>();
public PixelOperations<HalfVector4> CreatePixelOperations() => new PixelOperations<HalfVector4>();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -111,67 +111,51 @@ namespace ImageSharp.PixelFormats
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromBytes(byte x, byte y, byte z, byte w)
public void PackFromRgba32(Rgba32 source)
{
this.PackFromVector4(new Vector4(x, y, z, w) / MaxBytes);
this.PackFromVector4(source.ToVector4());
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToXyzBytes(Span<byte> bytes, int startIndex)
public void ToRgb24(ref Rgb24 dest)
{
Vector4 vector = this.ToVector4();
vector *= MaxBytes;
vector += Half;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
bytes[startIndex] = (byte)vector.X;
bytes[startIndex + 1] = (byte)vector.Y;
bytes[startIndex + 2] = (byte)vector.Z;
Vector4 vector = this.ToScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToXyzwBytes(Span<byte> bytes, int startIndex)
public void ToRgba32(ref Rgba32 dest)
{
Vector4 vector = this.ToVector4();
vector *= MaxBytes;
vector += Half;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
bytes[startIndex] = (byte)vector.X;
bytes[startIndex + 1] = (byte)vector.Y;
bytes[startIndex + 2] = (byte)vector.Z;
bytes[startIndex + 3] = (byte)vector.W;
Vector4 vector = this.ToScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
dest.A = (byte)vector.W;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToZyxBytes(Span<byte> bytes, int startIndex)
public void ToBgr24(ref Bgr24 dest)
{
Vector4 vector = this.ToVector4();
vector *= MaxBytes;
vector += Half;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
bytes[startIndex] = (byte)vector.Z;
bytes[startIndex + 1] = (byte)vector.Y;
bytes[startIndex + 2] = (byte)vector.X;
Vector4 vector = this.ToScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToZyxwBytes(Span<byte> bytes, int startIndex)
public void ToBgra32(ref Bgra32 dest)
{
Vector4 vector = this.ToVector4();
vector *= MaxBytes;
vector += Half;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
bytes[startIndex] = (byte)vector.Z;
bytes[startIndex + 1] = (byte)vector.Y;
bytes[startIndex + 2] = (byte)vector.X;
bytes[startIndex + 3] = (byte)vector.W;
Vector4 vector = this.ToScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
dest.A = (byte)vector.W;
}
/// <inheritdoc />
@ -214,5 +198,15 @@ namespace ImageSharp.PixelFormats
ulong num1 = (ulong)HalfTypeHelper.Pack(vector.W) << 0x30;
return num4 | num3 | num2 | num1;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private Vector4 ToScaledVector4()
{
Vector4 vector = this.ToVector4();
vector *= MaxBytes;
vector += Half;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
return vector;
}
}
}

43
src/ImageSharp/PixelFormats/IPixel.cs

@ -20,7 +20,7 @@ namespace ImageSharp.PixelFormats
/// This method is not intended to be consumed directly. Use <see cref="PixelOperations{TPixel}.Instance"/> instead.
/// </summary>
/// <returns>The <see cref="PixelOperations{TPixel}"/> instance.</returns>
PixelOperations<TSelf> CreateBulkOperations();
PixelOperations<TSelf> CreatePixelOperations();
}
/// <summary>
@ -42,44 +42,33 @@ namespace ImageSharp.PixelFormats
Vector4 ToVector4();
/// <summary>
/// Sets the packed representation from the given byte array.
/// Packs the pixel from an <see cref="Rgba32"/> value.
/// </summary>
/// <param name="x">The x-component.</param>
/// <param name="y">The y-component.</param>
/// <param name="z">The z-component.</param>
/// <param name="w">The w-component.</param>
void PackFromBytes(byte x, byte y, byte z, byte w);
/// <param name="source">The <see cref="Rgba32"/> value.</param>
void PackFromRgba32(Rgba32 source);
/// <summary>
/// Expands the packed representation into a given byte array.
/// Output is expanded to X-> Y-> Z order. Equivalent to R-> G-> B in <see cref="Rgba32"/>
/// Converts the pixel to <see cref="Rgb24"/> format.
/// </summary>
/// <param name="bytes">The bytes to set the color in.</param>
/// <param name="startIndex">The starting index of the <paramref name="bytes"/>.</param>
void ToXyzBytes(Span<byte> bytes, int startIndex);
/// <param name="dest">The destination pixel to write to</param>
void ToRgb24(ref Rgb24 dest);
/// <summary>
/// Expands the packed representation into a given byte array.
/// Output is expanded to X-> Y-> Z-> W order. Equivalent to R-> G-> B-> A in <see cref="Rgba32"/>
/// Converts the pixel to <see cref="Rgba32"/> format.
/// </summary>
/// <param name="bytes">The bytes to set the color in.</param>
/// <param name="startIndex">The starting index of the <paramref name="bytes"/>.</param>
void ToXyzwBytes(Span<byte> bytes, int startIndex);
/// <param name="dest">The destination pixel to write to</param>
void ToRgba32(ref Rgba32 dest);
/// <summary>
/// Expands the packed representation into a given byte array.
/// Output is expanded to Z-> Y-> X order. Equivalent to B-> G-> R in <see cref="Rgba32"/>
/// Converts the pixel to <see cref="Bgr24"/> format.
/// </summary>
/// <param name="bytes">The bytes to set the color in.</param>
/// <param name="startIndex">The starting index of the <paramref name="bytes"/>.</param>
void ToZyxBytes(Span<byte> bytes, int startIndex);
/// <param name="dest">The destination pixel to write to</param>
void ToBgr24(ref Bgr24 dest);
/// <summary>
/// Expands the packed representation into a given byte array.
/// Output is expanded to Z-> Y-> X-> W order. Equivalent to B-> G-> R-> A in <see cref="Rgba32"/>
/// Converts the pixel to <see cref="Bgra32"/> format.
/// </summary>
/// <param name="bytes">The bytes to set the color in.</param>
/// <param name="startIndex">The starting index of the <paramref name="bytes"/>.</param>
void ToZyxwBytes(Span<byte> bytes, int startIndex);
/// <param name="dest">The destination pixel to write to</param>
void ToBgra32(ref Bgra32 dest);
}
}

86
src/ImageSharp/PixelFormats/NormalizedByte2.cs

@ -91,7 +91,7 @@ namespace ImageSharp.PixelFormats
}
/// <inheritdoc />
public PixelOperations<NormalizedByte2> CreateBulkOperations() => new PixelOperations<NormalizedByte2>();
public PixelOperations<NormalizedByte2> CreatePixelOperations() => new PixelOperations<NormalizedByte2>();
/// <summary>
/// Expands the packed representation into a <see cref="Vector2"/>.
@ -122,9 +122,9 @@ namespace ImageSharp.PixelFormats
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromBytes(byte x, byte y, byte z, byte w)
public void PackFromRgba32(Rgba32 source)
{
Vector4 vector = new Vector4(x, y, z, w);
Vector4 vector = source.ToUnscaledVector4();
vector -= Round;
vector -= Half;
vector -= Round;
@ -134,68 +134,44 @@ namespace ImageSharp.PixelFormats
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToXyzBytes(Span<byte> bytes, int startIndex)
public void ToRgb24(ref Rgb24 dest)
{
Vector4 vector = this.ToVector4();
vector *= Half;
vector += Round;
vector += Half;
vector += Round;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
bytes[startIndex] = (byte)vector.X;
bytes[startIndex + 1] = (byte)vector.Y;
bytes[startIndex + 2] = 0;
Vector4 vector = this.ToScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = 0;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToXyzwBytes(Span<byte> bytes, int startIndex)
public void ToRgba32(ref Rgba32 dest)
{
Vector4 vector = this.ToVector4();
vector *= Half;
vector += Round;
vector += Half;
vector += Round;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
bytes[startIndex] = (byte)vector.X;
bytes[startIndex + 1] = (byte)vector.Y;
bytes[startIndex + 2] = 0;
bytes[startIndex + 3] = 255;
Vector4 vector = this.ToScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = 0;
dest.A = 255;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToZyxBytes(Span<byte> bytes, int startIndex)
public void ToBgr24(ref Bgr24 dest)
{
Vector4 vector = this.ToVector4();
vector *= Half;
vector += Round;
vector += Half;
vector += Round;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
bytes[startIndex] = 0;
bytes[startIndex + 1] = (byte)vector.Y;
bytes[startIndex + 2] = (byte)vector.X;
Vector4 vector = this.ToScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = 0;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToZyxwBytes(Span<byte> bytes, int startIndex)
public void ToBgra32(ref Bgra32 dest)
{
Vector4 vector = this.ToVector4();
vector *= Half;
vector += Round;
vector += Half;
vector += Round;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
bytes[startIndex] = 0;
bytes[startIndex + 1] = (byte)vector.Y;
bytes[startIndex + 2] = (byte)vector.X;
bytes[startIndex + 3] = 255;
Vector4 vector = this.ToScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = 0;
dest.A = 255;
}
/// <inheritdoc />
@ -238,5 +214,17 @@ namespace ImageSharp.PixelFormats
return (ushort)(byte2 | byte1);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private Vector4 ToScaledVector4()
{
Vector4 vector = this.ToVector4();
vector *= Half;
vector += Round;
vector += Half;
vector += Round;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
return vector;
}
}
}

86
src/ImageSharp/PixelFormats/NormalizedByte4.cs

@ -93,7 +93,7 @@ namespace ImageSharp.PixelFormats
}
/// <inheritdoc />
public PixelOperations<NormalizedByte4> CreateBulkOperations() => new PixelOperations<NormalizedByte4>();
public PixelOperations<NormalizedByte4> CreatePixelOperations() => new PixelOperations<NormalizedByte4>();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -115,9 +115,9 @@ namespace ImageSharp.PixelFormats
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromBytes(byte x, byte y, byte z, byte w)
public void PackFromRgba32(Rgba32 source)
{
Vector4 vector = new Vector4(x, y, z, w);
Vector4 vector = source.ToUnscaledVector4();
vector -= Round;
vector -= Half;
vector -= Round;
@ -127,68 +127,44 @@ namespace ImageSharp.PixelFormats
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToXyzBytes(Span<byte> bytes, int startIndex)
public void ToRgb24(ref Rgb24 dest)
{
Vector4 vector = this.ToVector4();
vector *= Half;
vector += Round;
vector += Half;
vector += Round;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
bytes[startIndex] = (byte)vector.X;
bytes[startIndex + 1] = (byte)vector.Y;
bytes[startIndex + 2] = (byte)vector.Z;
Vector4 vector = this.ToScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToXyzwBytes(Span<byte> bytes, int startIndex)
public void ToRgba32(ref Rgba32 dest)
{
Vector4 vector = this.ToVector4();
vector *= Half;
vector += Round;
vector += Half;
vector += Round;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
bytes[startIndex] = (byte)vector.X;
bytes[startIndex + 1] = (byte)vector.Y;
bytes[startIndex + 2] = (byte)vector.Z;
bytes[startIndex + 3] = (byte)vector.W;
Vector4 vector = this.ToScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
dest.A = (byte)vector.W;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToZyxBytes(Span<byte> bytes, int startIndex)
public void ToBgr24(ref Bgr24 dest)
{
Vector4 vector = this.ToVector4();
vector *= Half;
vector += Round;
vector += Half;
vector += Round;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
bytes[startIndex] = (byte)vector.Z;
bytes[startIndex + 1] = (byte)vector.Y;
bytes[startIndex + 2] = (byte)vector.X;
Vector4 vector = this.ToScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToZyxwBytes(Span<byte> bytes, int startIndex)
public void ToBgra32(ref Bgra32 dest)
{
Vector4 vector = this.ToVector4();
vector *= Half;
vector += Round;
vector += Half;
vector += Round;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
bytes[startIndex] = (byte)vector.Z;
bytes[startIndex + 1] = (byte)vector.Y;
bytes[startIndex + 2] = (byte)vector.X;
bytes[startIndex + 3] = (byte)vector.W;
Vector4 vector = this.ToScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
dest.A = (byte)vector.W;
}
/// <inheritdoc />
@ -235,5 +211,17 @@ namespace ImageSharp.PixelFormats
return byte4 | byte3 | byte2 | byte1;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private Vector4 ToScaledVector4()
{
Vector4 vector = this.ToVector4();
vector *= Half;
vector += Round;
vector += Half;
vector += Round;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
return vector;
}
}
}

86
src/ImageSharp/PixelFormats/NormalizedShort2.cs

@ -91,7 +91,7 @@ namespace ImageSharp.PixelFormats
}
/// <inheritdoc />
public PixelOperations<NormalizedShort2> CreateBulkOperations() => new PixelOperations<NormalizedShort2>();
public PixelOperations<NormalizedShort2> CreatePixelOperations() => new PixelOperations<NormalizedShort2>();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -109,9 +109,9 @@ namespace ImageSharp.PixelFormats
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromBytes(byte x, byte y, byte z, byte w)
public void PackFromRgba32(Rgba32 source)
{
Vector4 vector = new Vector4(x, y, z, w);
Vector4 vector = source.ToUnscaledVector4();
vector -= Round;
vector -= Half;
vector -= Round;
@ -121,68 +121,44 @@ namespace ImageSharp.PixelFormats
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToXyzBytes(Span<byte> bytes, int startIndex)
public void ToRgb24(ref Rgb24 dest)
{
Vector4 vector = this.ToVector4();
vector *= Half;
vector += Round;
vector += Half;
vector += Round;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
bytes[startIndex] = (byte)MathF.Round(vector.X);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = 0;
Vector4 vector = this.ToScaledVector4();
dest.R = (byte)MathF.Round(vector.X);
dest.G = (byte)MathF.Round(vector.Y);
dest.B = 0;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToXyzwBytes(Span<byte> bytes, int startIndex)
public void ToRgba32(ref Rgba32 dest)
{
Vector4 vector = this.ToVector4();
vector *= Half;
vector += Round;
vector += Half;
vector += Round;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
bytes[startIndex] = (byte)MathF.Round(vector.X);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = 0;
bytes[startIndex + 3] = 255;
Vector4 vector = this.ToScaledVector4();
dest.R = (byte)MathF.Round(vector.X);
dest.G = (byte)MathF.Round(vector.Y);
dest.B = 0;
dest.A = 255;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToZyxBytes(Span<byte> bytes, int startIndex)
public void ToBgr24(ref Bgr24 dest)
{
Vector4 vector = this.ToVector4();
vector *= Half;
vector += Round;
vector += Half;
vector += Round;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
bytes[startIndex] = 0;
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = (byte)MathF.Round(vector.X);
Vector4 vector = this.ToScaledVector4();
dest.R = (byte)MathF.Round(vector.X);
dest.G = (byte)MathF.Round(vector.Y);
dest.B = 0;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToZyxwBytes(Span<byte> bytes, int startIndex)
public void ToBgra32(ref Bgra32 dest)
{
Vector4 vector = this.ToVector4();
vector *= Half;
vector += Round;
vector += Half;
vector += Round;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
bytes[startIndex] = 0;
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = (byte)MathF.Round(vector.X);
bytes[startIndex + 3] = 255;
Vector4 vector = this.ToScaledVector4();
dest.R = (byte)MathF.Round(vector.X);
dest.G = (byte)MathF.Round(vector.Y);
dest.B = 0;
dest.A = 255;
}
/// <summary>
@ -245,5 +221,17 @@ namespace ImageSharp.PixelFormats
return word2 | word1;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private Vector4 ToScaledVector4()
{
Vector4 vector = this.ToVector4();
vector *= Half;
vector += Round;
vector += Half;
vector += Round;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
return vector;
}
}
}

86
src/ImageSharp/PixelFormats/NormalizedShort4.cs

@ -93,7 +93,7 @@ namespace ImageSharp.PixelFormats
}
/// <inheritdoc />
public PixelOperations<NormalizedShort4> CreateBulkOperations() => new PixelOperations<NormalizedShort4>();
public PixelOperations<NormalizedShort4> CreatePixelOperations() => new PixelOperations<NormalizedShort4>();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -117,9 +117,9 @@ namespace ImageSharp.PixelFormats
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromBytes(byte x, byte y, byte z, byte w)
public void PackFromRgba32(Rgba32 source)
{
Vector4 vector = new Vector4(x, y, z, w);
Vector4 vector = source.ToUnscaledVector4();
vector -= Round;
vector -= Half;
vector -= Round;
@ -129,68 +129,44 @@ namespace ImageSharp.PixelFormats
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToXyzBytes(Span<byte> bytes, int startIndex)
public void ToRgb24(ref Rgb24 dest)
{
Vector4 vector = this.ToVector4();
vector *= Half;
vector += Round;
vector += Half;
vector += Round;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
bytes[startIndex] = (byte)MathF.Round(vector.X);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = (byte)MathF.Round(vector.Z);
Vector4 vector = this.ToScaledVector4();
dest.R = (byte)MathF.Round(vector.X);
dest.G = (byte)MathF.Round(vector.Y);
dest.B = (byte)MathF.Round(vector.Z);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToXyzwBytes(Span<byte> bytes, int startIndex)
public void ToRgba32(ref Rgba32 dest)
{
Vector4 vector = this.ToVector4();
vector *= Half;
vector += Round;
vector += Half;
vector += Round;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
bytes[startIndex] = (byte)MathF.Round(vector.X);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = (byte)MathF.Round(vector.Z);
bytes[startIndex + 3] = (byte)MathF.Round(vector.W);
Vector4 vector = this.ToScaledVector4();
dest.R = (byte)MathF.Round(vector.X);
dest.G = (byte)MathF.Round(vector.Y);
dest.B = (byte)MathF.Round(vector.Z);
dest.A = (byte)MathF.Round(vector.W);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToZyxBytes(Span<byte> bytes, int startIndex)
public void ToBgr24(ref Bgr24 dest)
{
Vector4 vector = this.ToVector4();
vector *= Half;
vector += Round;
vector += Half;
vector += Round;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
bytes[startIndex] = (byte)MathF.Round(vector.Z);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = (byte)MathF.Round(vector.X);
Vector4 vector = this.ToScaledVector4();
dest.R = (byte)MathF.Round(vector.X);
dest.G = (byte)MathF.Round(vector.Y);
dest.B = (byte)MathF.Round(vector.Z);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToZyxwBytes(Span<byte> bytes, int startIndex)
public void ToBgra32(ref Bgra32 dest)
{
Vector4 vector = this.ToVector4();
vector *= Half;
vector += Round;
vector += Half;
vector += Round;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
bytes[startIndex] = (byte)MathF.Round(vector.Z);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = (byte)MathF.Round(vector.X);
bytes[startIndex + 3] = (byte)MathF.Round(vector.W);
Vector4 vector = this.ToScaledVector4();
dest.R = (byte)MathF.Round(vector.X);
dest.G = (byte)MathF.Round(vector.Y);
dest.B = (byte)MathF.Round(vector.Z);
dest.A = (byte)MathF.Round(vector.W);
}
/// <inheritdoc />
@ -241,5 +217,17 @@ namespace ImageSharp.PixelFormats
return word4 | word3 | word2 | word1;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private Vector4 ToScaledVector4()
{
Vector4 vector = this.ToVector4();
vector *= Half;
vector += Round;
vector += Half;
vector += Round;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
return vector;
}
}
}

76
src/ImageSharp/PixelFormats/PixelConversionExtensions.cs

@ -0,0 +1,76 @@
namespace ImageSharp.PixelFormats
{
using System;
using System.Runtime.CompilerServices;
/// <summary>
/// Extension methods for copying single pixel data into byte Spans.
/// TODO: This utility class exists for legacy reasons. Need to do a lot of chore work to remove it (mostly in test classes).
/// </summary>
internal static class PixelConversionExtensions
{
/// <summary>
/// Expands the packed representation into a given byte array.
/// Output is expanded to X-> Y-> Z order. Equivalent to R-> G-> B in <see cref="Rgb24"/>
/// </summary>
/// <typeparam name="TPixel">The pixel type.</typeparam>
/// <param name="pixel">The pixel to copy the data from.</param>
/// <param name="bytes">The bytes to set the color in.</param>
/// <param name="startIndex">The starting index of the <paramref name="bytes"/>.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void ToXyzBytes<TPixel>(this TPixel pixel, Span<byte> bytes, int startIndex)
where TPixel : struct, IPixel<TPixel>
{
ref Rgb24 dest = ref bytes.GetRgb24(startIndex);
pixel.ToRgb24(ref dest);
}
/// <summary>
/// Expands the packed representation into a given byte array.
/// Output is expanded to X-> Y-> Z-> W order. Equivalent to R-> G-> B-> A in <see cref="Rgba32"/>
/// </summary>
/// <typeparam name="TPixel">The pixel type.</typeparam>
/// <param name="pixel">The pixel to copy the data from.</param>
/// <param name="bytes">The bytes to set the color in.</param>
/// <param name="startIndex">The starting index of the <paramref name="bytes"/>.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void ToXyzwBytes<TPixel>(this TPixel pixel, Span<byte> bytes, int startIndex)
where TPixel : struct, IPixel<TPixel>
{
ref Rgba32 dest = ref Unsafe.As<byte, Rgba32>(ref bytes[startIndex]);
pixel.ToRgba32(ref dest);
}
/// <summary>
/// Expands the packed representation into a given byte array.
/// Output is expanded to Z-> Y-> X order. Equivalent to B-> G-> R in <see cref="Bgr24"/>
/// </summary>
/// <typeparam name="TPixel">The pixel type.</typeparam>
/// <param name="pixel">The pixel to copy the data from.</param>
/// <param name="bytes">The bytes to set the color in.</param>
/// <param name="startIndex">The starting index of the <paramref name="bytes"/>.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void ToZyxBytes<TPixel>(this TPixel pixel, Span<byte> bytes, int startIndex)
where TPixel : struct, IPixel<TPixel>
{
ref Bgr24 dest = ref Unsafe.As<byte, Bgr24>(ref bytes[startIndex]);
pixel.ToBgr24(ref dest);
}
/// <summary>
/// Expands the packed representation into a given byte array.
/// Output is expanded to Z-> Y-> X-> W order. Equivalent to B-> G-> R-> A in <see cref="Bgra32"/>
/// </summary>
/// <typeparam name="TPixel">The pixel type.</typeparam>
/// <param name="pixel">The pixel to copy the data from.</param>
/// <param name="bytes">The bytes to set the color in.</param>
/// <param name="startIndex">The starting index of the <paramref name="bytes"/>.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void ToZyxwBytes<TPixel>(this TPixel pixel, Span<byte> bytes, int startIndex)
where TPixel : struct, IPixel<TPixel>
{
ref Bgra32 dest = ref Unsafe.As<byte, Bgra32>(ref bytes[startIndex]);
pixel.ToBgra32(ref dest);
}
}
}

204
src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs

@ -20,7 +20,7 @@ namespace ImageSharp.PixelFormats
/// <summary>
/// Gets the global <see cref="PixelOperations{TPixel}"/> instance for the pixel type <typeparamref name="TPixel"/>
/// </summary>
public static PixelOperations<TPixel> Instance { get; } = default(TPixel).CreateBulkOperations();
public static PixelOperations<TPixel> Instance { get; } = default(TPixel).CreatePixelOperations();
/// <summary>
/// Bulk version of <see cref="IPixel.PackFromVector4(Vector4)"/>
@ -30,8 +30,7 @@ namespace ImageSharp.PixelFormats
/// <param name="count">The number of pixels to convert.</param>
internal virtual void PackFromVector4(Span<Vector4> sourceVectors, Span<TPixel> destColors, int count)
{
Guard.MustBeSizedAtLeast(sourceVectors, count, nameof(sourceVectors));
Guard.MustBeSizedAtLeast(destColors, count, nameof(destColors));
GuardSpans(sourceVectors, nameof(sourceVectors), destColors, nameof(destColors), count);
ref Vector4 sourceRef = ref sourceVectors.DangerousGetPinnableReference();
ref TPixel destRef = ref destColors.DangerousGetPinnableReference();
@ -52,8 +51,7 @@ namespace ImageSharp.PixelFormats
/// <param name="count">The number of pixels to convert.</param>
internal virtual void ToVector4(Span<TPixel> sourceColors, Span<Vector4> destVectors, int count)
{
Guard.MustBeSizedAtLeast(sourceColors, count, nameof(sourceColors));
Guard.MustBeSizedAtLeast(destVectors, count, nameof(destVectors));
GuardSpans(sourceColors, nameof(sourceColors), destVectors, nameof(destVectors), count);
ref TPixel sourceRef = ref sourceColors.DangerousGetPinnableReference();
ref Vector4 destRef = ref destVectors.DangerousGetPinnableReference();
@ -67,187 +65,25 @@ namespace ImageSharp.PixelFormats
}
/// <summary>
/// Bulk version of <see cref="IPixel.PackFromBytes(byte, byte, byte, byte)"/> that converts data in <see cref="ComponentOrder.Xyz"/>.
/// Verifies that the given 'source' and 'dest' spans are at least of 'minLength' size.
/// Throwing an <see cref="ArgumentException"/> if the condition is not met.
/// </summary>
/// <param name="sourceBytes">The <see cref="Span{T}"/> to the source bytes.</param>
/// <param name="destColors">The <see cref="Span{T}"/> to the destination colors.</param>
/// <param name="count">The number of pixels to convert.</param>
internal virtual void PackFromXyzBytes(Span<byte> sourceBytes, Span<TPixel> destColors, int count)
{
Guard.MustBeSizedAtLeast(sourceBytes, count * 3, nameof(sourceBytes));
Guard.MustBeSizedAtLeast(destColors, count, nameof(destColors));
ref byte sourceRef = ref sourceBytes.DangerousGetPinnableReference();
ref TPixel destRef = ref destColors.DangerousGetPinnableReference();
for (int i = 0; i < count; i++)
{
int i3 = i * 3;
ref TPixel dp = ref Unsafe.Add(ref destRef, i);
dp.PackFromBytes(
Unsafe.Add(ref sourceRef, i3),
Unsafe.Add(ref sourceRef, i3 + 1),
Unsafe.Add(ref sourceRef, i3 + 2),
255);
}
}
/// <summary>
/// Bulk version of <see cref="IPixel.ToXyzBytes(Span{byte}, int)"/>.
/// </summary>
/// <param name="sourceColors">The <see cref="Span{T}"/> to the source colors.</param>
/// <param name="destBytes">The <see cref="Span{T}"/> to the destination bytes.</param>
/// <param name="count">The number of pixels to convert.</param>
internal virtual void ToXyzBytes(Span<TPixel> sourceColors, Span<byte> destBytes, int count)
{
Guard.MustBeSizedAtLeast(sourceColors, count, nameof(sourceColors));
Guard.MustBeSizedAtLeast(destBytes, count * 3, nameof(destBytes));
ref TPixel sourceRef = ref sourceColors.DangerousGetPinnableReference();
for (int i = 0; i < count; i++)
{
ref TPixel sp = ref Unsafe.Add(ref sourceRef, i);
sp.ToXyzBytes(destBytes, i * 3);
}
}
/// <summary>
/// Bulk version of <see cref="IPixel.PackFromBytes(byte, byte, byte, byte)"/> that converts data in <see cref="ComponentOrder.Xyzw"/>.
/// </summary>
/// <param name="sourceBytes">The <see cref="Span{T}"/> to the source bytes.</param>
/// <param name="destColors">The <see cref="Span{T}"/> to the destination colors.</param>
/// <param name="count">The number of pixels to convert.</param>
internal virtual void PackFromXyzwBytes(Span<byte> sourceBytes, Span<TPixel> destColors, int count)
/// <typeparam name="TSource">The source element type</typeparam>
/// <typeparam name="TDest">The destination element type</typeparam>
/// <param name="source">The source span</param>
/// <param name="sourceParamName">The source parameter name</param>
/// <param name="dest">The destination span</param>
/// <param name="destParamName">The destination parameter name</param>
/// <param name="minLength">The minimum length</param>
protected internal static void GuardSpans<TSource, TDest>(
Span<TSource> source,
string sourceParamName,
Span<TDest> dest,
string destParamName,
int minLength)
{
Guard.MustBeSizedAtLeast(sourceBytes, count * 4, nameof(sourceBytes));
Guard.MustBeSizedAtLeast(destColors, count, nameof(destColors));
ref byte sourceRef = ref sourceBytes.DangerousGetPinnableReference();
ref TPixel destRef = ref destColors.DangerousGetPinnableReference();
for (int i = 0; i < count; i++)
{
int i4 = i * 4;
ref TPixel dp = ref Unsafe.Add(ref destRef, i);
dp.PackFromBytes(
Unsafe.Add(ref sourceRef, i4),
Unsafe.Add(ref sourceRef, i4 + 1),
Unsafe.Add(ref sourceRef, i4 + 2),
Unsafe.Add(ref sourceRef, i4 + 3));
}
}
/// <summary>
/// Bulk version of <see cref="IPixel.ToXyzwBytes(Span{byte}, int)"/>
/// </summary>
/// <param name="sourceColors">The <see cref="Span{T}"/> to the source colors.</param>
/// <param name="destBytes">The <see cref="Span{T}"/> to the destination bytes.</param>
/// <param name="count">The number of pixels to convert.</param>
internal virtual void ToXyzwBytes(Span<TPixel> sourceColors, Span<byte> destBytes, int count)
{
Guard.MustBeSizedAtLeast(sourceColors, count, nameof(sourceColors));
Guard.MustBeSizedAtLeast(destBytes, count * 4, nameof(destBytes));
ref TPixel sourceRef = ref sourceColors.DangerousGetPinnableReference();
for (int i = 0; i < count; i++)
{
ref TPixel sp = ref Unsafe.Add(ref sourceRef, i);
sp.ToXyzwBytes(destBytes, i * 4);
}
}
/// <summary>
/// Bulk version of <see cref="IPixel.PackFromBytes(byte, byte, byte, byte)"/> that converts data in <see cref="ComponentOrder.Zyx"/>.
/// </summary>
/// <param name="sourceBytes">The <see cref="Span{T}"/> to the source bytes.</param>
/// <param name="destColors">The <see cref="Span{T}"/> to the destination colors.</param>
/// <param name="count">The number of pixels to convert.</param>
internal virtual void PackFromZyxBytes(Span<byte> sourceBytes, Span<TPixel> destColors, int count)
{
Guard.MustBeSizedAtLeast(sourceBytes, count * 3, nameof(sourceBytes));
Guard.MustBeSizedAtLeast(destColors, count, nameof(destColors));
ref byte sourceRef = ref sourceBytes.DangerousGetPinnableReference();
ref TPixel destRef = ref destColors.DangerousGetPinnableReference();
for (int i = 0; i < count; i++)
{
int i3 = i * 3;
ref TPixel dp = ref Unsafe.Add(ref destRef, i);
dp.PackFromBytes(
Unsafe.Add(ref sourceRef, i3 + 2),
Unsafe.Add(ref sourceRef, i3 + 1),
Unsafe.Add(ref sourceRef, i3),
255);
}
}
/// <summary>
/// Bulk version of <see cref="IPixel.ToZyxBytes(Span{byte}, int)"/>.
/// </summary>
/// <param name="sourceColors">The <see cref="Span{T}"/> to the source colors.</param>
/// <param name="destBytes">The <see cref="Span{T}"/> to the destination bytes.</param>
/// <param name="count">The number of pixels to convert.</param>
internal virtual void ToZyxBytes(Span<TPixel> sourceColors, Span<byte> destBytes, int count)
{
Guard.MustBeSizedAtLeast(sourceColors, count, nameof(sourceColors));
Guard.MustBeSizedAtLeast(destBytes, count * 3, nameof(destBytes));
ref TPixel sourceRef = ref sourceColors.DangerousGetPinnableReference();
for (int i = 0; i < count; i++)
{
ref TPixel sp = ref Unsafe.Add(ref sourceRef, i);
sp.ToZyxBytes(destBytes, i * 3);
}
}
/// <summary>
/// Bulk version of <see cref="IPixel.PackFromBytes(byte, byte, byte, byte)"/> that converts data in <see cref="ComponentOrder.Zyxw"/>.
/// </summary>
/// <param name="sourceBytes">The <see cref="Span{T}"/> to the source bytes.</param>
/// <param name="destColors">The <see cref="Span{T}"/> to the destination colors.</param>
/// <param name="count">The number of pixels to convert.</param>
internal virtual void PackFromZyxwBytes(Span<byte> sourceBytes, Span<TPixel> destColors, int count)
{
Guard.MustBeSizedAtLeast(sourceBytes, count * 4, nameof(sourceBytes));
Guard.MustBeSizedAtLeast(destColors, count, nameof(destColors));
ref byte sourceRef = ref sourceBytes.DangerousGetPinnableReference();
ref TPixel destRef = ref destColors.DangerousGetPinnableReference();
for (int i = 0; i < count; i++)
{
int i4 = i * 4;
ref TPixel dp = ref Unsafe.Add(ref destRef, i);
dp.PackFromBytes(
Unsafe.Add(ref sourceRef, i4 + 2),
Unsafe.Add(ref sourceRef, i4 + 1),
Unsafe.Add(ref sourceRef, i4),
Unsafe.Add(ref sourceRef, i4 + 3));
}
}
/// <summary>
/// Bulk version of <see cref="IPixel.ToZyxwBytes(Span{byte}, int)"/>.
/// </summary>
/// <param name="sourceColors">The <see cref="Span{T}"/> to the source colors.</param>
/// <param name="destBytes">The <see cref="Span{T}"/> to the destination bytes.</param>
/// <param name="count">The number of pixels to convert.</param>
internal virtual void ToZyxwBytes(Span<TPixel> sourceColors, Span<byte> destBytes, int count)
{
Guard.MustBeSizedAtLeast(sourceColors, count, nameof(sourceColors));
Guard.MustBeSizedAtLeast(destBytes, count * 4, nameof(destBytes));
ref TPixel sourceRef = ref sourceColors.DangerousGetPinnableReference();
for (int i = 0; i < count; i++)
{
ref TPixel sp = ref Unsafe.Add(ref sourceRef, i);
sp.ToZyxwBytes(destBytes, i * 4);
}
Guard.MustBeSizedAtLeast(source, minLength, sourceParamName);
Guard.MustBeSizedAtLeast(dest, minLength, destParamName);
}
}
}

57
src/ImageSharp/PixelFormats/Rg32.cs

@ -76,7 +76,7 @@ namespace ImageSharp.PixelFormats
}
/// <inheritdoc />
public PixelOperations<Rg32> CreateBulkOperations() => new PixelOperations<Rg32>();
public PixelOperations<Rg32> CreatePixelOperations() => new PixelOperations<Rg32>();
/// <summary>
/// Expands the packed representation into a <see cref="Vector2"/>.
@ -107,55 +107,51 @@ namespace ImageSharp.PixelFormats
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromBytes(byte x, byte y, byte z, byte w)
public void PackFromRgba32(Rgba32 source)
{
this.PackFromVector4(new Vector4(x, y, z, w) / 255F);
this.PackFromVector4(source.ToVector4());
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToXyzBytes(Span<byte> bytes, int startIndex)
public void ToRgb24(ref Rgb24 dest)
{
Vector4 vector = this.ToVector4() * 255F;
bytes[startIndex] = (byte)vector.X;
bytes[startIndex + 1] = (byte)vector.Y;
bytes[startIndex + 2] = (byte)vector.Z;
Vector4 vector = this.ToScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToXyzwBytes(Span<byte> bytes, int startIndex)
public void ToRgba32(ref Rgba32 dest)
{
Vector4 vector = this.ToVector4() * 255F;
bytes[startIndex] = (byte)vector.X;
bytes[startIndex + 1] = (byte)vector.Y;
bytes[startIndex + 2] = (byte)vector.Z;
bytes[startIndex + 3] = (byte)vector.W;
Vector4 vector = this.ToScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
dest.A = (byte)vector.W;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToZyxBytes(Span<byte> bytes, int startIndex)
public void ToBgr24(ref Bgr24 dest)
{
Vector4 vector = this.ToVector4() * 255F;
bytes[startIndex] = (byte)vector.Z;
bytes[startIndex + 1] = (byte)vector.Y;
bytes[startIndex + 2] = (byte)vector.X;
Vector4 vector = this.ToScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToZyxwBytes(Span<byte> bytes, int startIndex)
public void ToBgra32(ref Bgra32 dest)
{
Vector4 vector = this.ToVector4() * 255F;
bytes[startIndex] = (byte)vector.Z;
bytes[startIndex + 1] = (byte)vector.Y;
bytes[startIndex + 2] = (byte)vector.X;
bytes[startIndex + 3] = (byte)vector.W;
Vector4 vector = this.ToScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
dest.A = (byte)vector.W;
}
/// <inheritdoc />
@ -197,5 +193,8 @@ namespace ImageSharp.PixelFormats
((int)Math.Round(x.Clamp(0, 1) * 65535F) & 0xFFFF) |
(((int)Math.Round(y.Clamp(0, 1) * 65535F) & 0xFFFF) << 16));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private Vector4 ToScaledVector4() => this.ToVector4() * 255f;
}
}

132
src/ImageSharp/PixelFormats/Rgb24.cs

@ -0,0 +1,132 @@
// <copyright file="Rgb24.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.PixelFormats
{
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
/// <summary>
/// Pixel type containing three 8-bit unsigned normalized values ranging from 0 to 255.
/// The color components are stored in red, green, blue order.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct Rgb24 : IPixel<Rgb24>
{
/// <summary>
/// The red component.
/// </summary>
public byte R;
/// <summary>
/// The green component.
/// </summary>
public byte G;
/// <summary>
/// The blue component.
/// </summary>
public byte B;
/// <summary>
/// Initializes a new instance of the <see cref="Rgb24"/> struct.
/// </summary>
/// <param name="r">The red component.</param>
/// <param name="g">The green component.</param>
/// <param name="b">The blue component.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Rgb24(byte r, byte g, byte b)
{
this.R = r;
this.G = g;
this.B = b;
}
/// <inheritdoc/>
public PixelOperations<Rgb24> CreatePixelOperations() => new PixelOperations<Rgb24>();
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(Rgb24 other)
{
return this.R == other.R && this.G == other.G && this.B == other.B;
}
/// <inheritdoc/>
public override bool Equals(object obj)
{
return obj?.GetType() == typeof(Rgb24) && this.Equals((Rgb24)obj);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode()
{
unchecked
{
int hashCode = this.R;
hashCode = (hashCode * 397) ^ this.G;
hashCode = (hashCode * 397) ^ this.B;
return hashCode;
}
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgba32(Rgba32 source)
{
this = Unsafe.As<Rgba32, Rgb24>(ref source);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromVector4(Vector4 vector)
{
var rgba = default(Rgba32);
rgba.PackFromVector4(vector);
this.PackFromRgba32(rgba);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToVector4()
{
return new Rgba32(this.R, this.G, this.B, 255).ToVector4();
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgb24(ref Rgb24 dest)
{
dest = this;
}
/// <inheritdoc/>
public void ToRgba32(ref Rgba32 dest)
{
dest.Rgb = this;
dest.A = 255;
}
/// <inheritdoc/>
public void ToBgr24(ref Bgr24 dest)
{
dest.R = this.R;
dest.G = this.G;
dest.B = this.B;
}
/// <inheritdoc/>
public void ToBgra32(ref Bgra32 dest)
{
dest.R = this.R;
dest.G = this.G;
dest.B = this.B;
dest.A = 255;
}
}
}

46
src/ImageSharp/PixelFormats/Rgba1010102.cs

@ -79,7 +79,7 @@ namespace ImageSharp.PixelFormats
}
/// <inheritdoc />
public PixelOperations<Rgba1010102> CreateBulkOperations() => new PixelOperations<Rgba1010102>();
public PixelOperations<Rgba1010102> CreatePixelOperations() => new PixelOperations<Rgba1010102>();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -101,55 +101,51 @@ namespace ImageSharp.PixelFormats
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromBytes(byte x, byte y, byte z, byte w)
public void PackFromRgba32(Rgba32 source)
{
this.PackFromVector4(new Vector4(x, y, z, w) / 255F);
this.PackFromVector4(source.ToVector4());
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToXyzBytes(Span<byte> bytes, int startIndex)
public void ToRgb24(ref Rgb24 dest)
{
Vector4 vector = this.ToVector4() * 255F;
bytes[startIndex] = (byte)MathF.Round(vector.X);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = (byte)MathF.Round(vector.Z);
dest.R = (byte)MathF.Round(vector.X);
dest.G = (byte)MathF.Round(vector.Y);
dest.B = (byte)MathF.Round(vector.Z);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToXyzwBytes(Span<byte> bytes, int startIndex)
public void ToRgba32(ref Rgba32 dest)
{
Vector4 vector = this.ToVector4() * 255F;
bytes[startIndex] = (byte)MathF.Round(vector.X);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = (byte)MathF.Round(vector.Z);
bytes[startIndex + 3] = (byte)MathF.Round(vector.W);
dest.R = (byte)MathF.Round(vector.X);
dest.G = (byte)MathF.Round(vector.Y);
dest.B = (byte)MathF.Round(vector.Z);
dest.A = (byte)MathF.Round(vector.W);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToZyxBytes(Span<byte> bytes, int startIndex)
public void ToBgr24(ref Bgr24 dest)
{
Vector4 vector = this.ToVector4() * 255F;
bytes[startIndex] = (byte)MathF.Round(vector.Z);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = (byte)MathF.Round(vector.X);
dest.R = (byte)MathF.Round(vector.X);
dest.G = (byte)MathF.Round(vector.Y);
dest.B = (byte)MathF.Round(vector.Z);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToZyxwBytes(Span<byte> bytes, int startIndex)
public void ToBgra32(ref Bgra32 dest)
{
Vector4 vector = this.ToVector4() * 255F;
bytes[startIndex] = (byte)MathF.Round(vector.Z);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = (byte)MathF.Round(vector.X);
bytes[startIndex + 3] = (byte)MathF.Round(vector.W);
dest.R = (byte)MathF.Round(vector.X);
dest.G = (byte)MathF.Round(vector.Y);
dest.B = (byte)MathF.Round(vector.Z);
dest.A = (byte)MathF.Round(vector.W);
}
/// <inheritdoc />

159
src/ImageSharp/PixelFormats/Rgba32.PixelOperations.cs

@ -21,7 +21,7 @@ namespace ImageSharp
/// <summary>
/// <see cref="PixelOperations{TPixel}"/> implementation optimized for <see cref="Rgba32"/>.
/// </summary>
internal class PixelOperations : PixelOperations<Rgba32>
internal partial class PixelOperations : PixelOperations<Rgba32>
{
/// <summary>
/// SIMD optimized bulk implementation of <see cref="IPixel.PackFromVector4(Vector4)"/>
@ -121,164 +121,19 @@ namespace ImageSharp
}
/// <inheritdoc />
internal override void PackFromXyzBytes(Span<byte> sourceBytes, Span<Rgba32> destColors, int count)
internal override void PackFromRgba32(Span<Rgba32> source, Span<Rgba32> destPixels, int count)
{
Guard.MustBeSizedAtLeast(sourceBytes, count * 3, nameof(sourceBytes));
Guard.MustBeSizedAtLeast(destColors, count, nameof(destColors));
GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count);
ref RGB24 sourceRef = ref Unsafe.As<byte, RGB24>(ref sourceBytes.DangerousGetPinnableReference());
ref Rgba32 destRef = ref destColors.DangerousGetPinnableReference();
for (int i = 0; i < count; i++)
{
ref RGB24 sp = ref Unsafe.Add(ref sourceRef, i);
ref Rgba32 dp = ref Unsafe.Add(ref destRef, i);
Unsafe.As<Rgba32, RGB24>(ref dp) = sp;
dp.A = 255;
}
}
/// <inheritdoc />
internal override void ToXyzBytes(Span<Rgba32> sourceColors, Span<byte> destBytes, int count)
{
Guard.MustBeSizedAtLeast(sourceColors, count, nameof(sourceColors));
Guard.MustBeSizedAtLeast(destBytes, count * 3, nameof(destBytes));
ref Rgba32 sourceRef = ref sourceColors.DangerousGetPinnableReference();
ref RGB24 destRef = ref Unsafe.As<byte, RGB24>(ref destBytes.DangerousGetPinnableReference());
for (int i = 0; i < count; i++)
{
ref Rgba32 sp = ref Unsafe.Add(ref sourceRef, i);
ref RGB24 dp = ref Unsafe.Add(ref destRef, i);
dp = Unsafe.As<Rgba32, RGB24>(ref sp);
}
}
/// <inheritdoc />
internal override unsafe void PackFromXyzwBytes(Span<byte> sourceBytes, Span<Rgba32> destColors, int count)
{
Guard.MustBeSizedAtLeast(sourceBytes, count * 4, nameof(sourceBytes));
Guard.MustBeSizedAtLeast(destColors, count, nameof(destColors));
SpanHelper.Copy(sourceBytes, destColors.AsBytes(), count * sizeof(Rgba32));
SpanHelper.Copy(source, destPixels, count);
}
/// <inheritdoc />
internal override unsafe void ToXyzwBytes(Span<Rgba32> sourceColors, Span<byte> destBytes, int count)
internal override void ToRgba32(Span<Rgba32> sourcePixels, Span<Rgba32> dest, int count)
{
Guard.MustBeSizedAtLeast(sourceColors, count, nameof(sourceColors));
Guard.MustBeSizedAtLeast(destBytes, count * 4, nameof(destBytes));
SpanHelper.Copy(sourceColors.AsBytes(), destBytes, count * sizeof(Rgba32));
}
/// <inheritdoc />
internal override void PackFromZyxBytes(Span<byte> sourceBytes, Span<Rgba32> destColors, int count)
{
Guard.MustBeSizedAtLeast(sourceBytes, count * 3, nameof(sourceBytes));
Guard.MustBeSizedAtLeast(destColors, count, nameof(destColors));
ref RGB24 sourceRef = ref Unsafe.As<byte, RGB24>(ref sourceBytes.DangerousGetPinnableReference());
ref Rgba32 destRef = ref destColors.DangerousGetPinnableReference();
for (int i = 0; i < count; i++)
{
ref RGB24 sp = ref Unsafe.Add(ref sourceRef, i);
ref Rgba32 dp = ref Unsafe.Add(ref destRef, i);
Unsafe.As<Rgba32, RGB24>(ref dp) = sp.ToZyx();
dp.A = 255;
}
}
/// <inheritdoc />
internal override void ToZyxBytes(Span<Rgba32> sourceColors, Span<byte> destBytes, int count)
{
Guard.MustBeSizedAtLeast(sourceColors, count, nameof(sourceColors));
Guard.MustBeSizedAtLeast(destBytes, count * 3, nameof(destBytes));
ref Rgba32 sourceRef = ref sourceColors.DangerousGetPinnableReference();
ref RGB24 destRef = ref Unsafe.As<byte, RGB24>(ref destBytes.DangerousGetPinnableReference());
for (int i = 0; i < count; i++)
{
ref Rgba32 sp = ref Unsafe.Add(ref sourceRef, i);
ref RGB24 dp = ref Unsafe.Add(ref destRef, i);
GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count);
dp = Unsafe.As<Rgba32, RGB24>(ref sp).ToZyx();
}
}
/// <inheritdoc />
internal override void PackFromZyxwBytes(Span<byte> sourceBytes, Span<Rgba32> destColors, int count)
{
Guard.MustBeSizedAtLeast(sourceBytes, count * 4, nameof(sourceBytes));
Guard.MustBeSizedAtLeast(destColors, count, nameof(destColors));
ref RGBA32 sourceRef = ref Unsafe.As<byte, RGBA32>(ref sourceBytes.DangerousGetPinnableReference());
ref Rgba32 destRef = ref destColors.DangerousGetPinnableReference();
for (int i = 0; i < count; i++)
{
ref RGBA32 sp = ref Unsafe.Add(ref sourceRef, i);
ref Rgba32 dp = ref Unsafe.Add(ref destRef, i);
RGBA32 zyxw = sp.ToZyxw();
dp = Unsafe.As<RGBA32, Rgba32>(ref zyxw);
}
}
/// <inheritdoc />
internal override void ToZyxwBytes(Span<Rgba32> sourceColors, Span<byte> destBytes, int count)
{
Guard.MustBeSizedAtLeast(sourceColors, count, nameof(sourceColors));
Guard.MustBeSizedAtLeast(destBytes, count * 4, nameof(destBytes));
ref Rgba32 sourceRef = ref sourceColors.DangerousGetPinnableReference();
ref RGBA32 destRef = ref Unsafe.As<byte, RGBA32>(ref destBytes.DangerousGetPinnableReference());
for (int i = 0; i < count; i++)
{
ref RGBA32 sp = ref Unsafe.As<Rgba32, RGBA32>(ref Unsafe.Add(ref sourceRef, i));
ref RGBA32 dp = ref Unsafe.Add(ref destRef, i);
dp = sp.ToZyxw();
}
}
/// <summary>
/// Helper struct to manipulate 3-byte RGB data.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
private struct RGB24
{
private byte x;
private byte y;
private byte z;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public RGB24 ToZyx() => new RGB24 { x = this.z, y = this.y, z = this.x };
}
/// <summary>
/// Helper struct to manipulate 4-byte RGBA data.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
private struct RGBA32
{
private byte x;
private byte y;
private byte z;
private byte w;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public RGBA32 ToZyxw() => new RGBA32 { x = this.z, y = this.y, z = this.x, w = this.w };
SpanHelper.Copy(sourcePixels, dest, count);
}
/// <summary>

169
src/ImageSharp/PixelFormats/Rgba32.cs

@ -23,39 +23,29 @@ namespace ImageSharp
/// This struct is fully mutable. This is done (against the guidelines) for the sake of performance,
/// as it avoids the need to create new values for modification operations.
/// </remarks>
[StructLayout(LayoutKind.Explicit)]
[StructLayout(LayoutKind.Sequential)]
public partial struct Rgba32 : IPixel<Rgba32>, IPackedVector<uint>
{
/// <summary>
/// Gets or sets the red component.
/// </summary>
[FieldOffset(0)]
public byte R;
/// <summary>
/// Gets or sets the green component.
/// </summary>
[FieldOffset(1)]
public byte G;
/// <summary>
/// Gets or sets the blue component.
/// </summary>
[FieldOffset(2)]
public byte B;
/// <summary>
/// Gets or sets the alpha component.
/// </summary>
[FieldOffset(3)]
public byte A;
/// <summary>
/// The packed representation of the value.
/// </summary>
[FieldOffset(0)]
public uint Rgba;
/// <summary>
/// The shift count for the red component
/// </summary>
@ -86,6 +76,21 @@ namespace ImageSharp
/// </summary>
private static readonly Vector4 Half = new Vector4(0.5F);
/// <summary>
/// Initializes a new instance of the <see cref="Rgba32"/> struct.
/// </summary>
/// <param name="r">The red component.</param>
/// <param name="g">The green component.</param>
/// <param name="b">The blue component.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Rgba32(byte r, byte g, byte b)
{
this.R = r;
this.G = g;
this.B = b;
this.A = 255;
}
/// <summary>
/// Initializes a new instance of the <see cref="Rgba32"/> struct.
/// </summary>
@ -94,8 +99,7 @@ namespace ImageSharp
/// <param name="b">The blue component.</param>
/// <param name="a">The alpha component.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Rgba32(byte r, byte g, byte b, byte a = 255)
: this()
public Rgba32(byte r, byte g, byte b, byte a)
{
this.R = r;
this.G = g;
@ -156,8 +160,68 @@ namespace ImageSharp
this.Rgba = packed;
}
/// <summary>
/// Gets or sets the packed representation of the Rgba32 struct.
/// </summary>
public uint Rgba
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return Unsafe.As<Rgba32, uint>(ref this);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set
{
Unsafe.As<Rgba32, uint>(ref this) = value;
}
}
/// <summary>
/// Gets or sets the RGB components of this struct as <see cref="Rgb24"/>
/// </summary>
public Rgb24 Rgb
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return Unsafe.As<Rgba32, Rgb24>(ref this);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set
{
Unsafe.As<Rgba32, Rgb24>(ref this) = value;
}
}
/// <summary>
/// Gets or sets the RGB components of this struct as <see cref="Bgr24"/> reverting the component order.
/// </summary>
public Bgr24 Bgr
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return new Bgr24(this.R, this.G, this.B);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set
{
this.R = value.R;
this.G = value.G;
this.B = value.B;
}
}
/// <inheritdoc/>
public uint PackedValue { get => this.Rgba; set => this.Rgba = value; }
public uint PackedValue
{
get => this.Rgba;
set => this.Rgba = value;
}
/// <summary>
/// Compares two <see cref="Rgba32"/> objects for equality.
@ -207,16 +271,13 @@ namespace ImageSharp
}
/// <inheritdoc />
public PixelOperations<Rgba32> CreateBulkOperations() => new PixelOperations();
public PixelOperations<Rgba32> CreatePixelOperations() => new PixelOperations();
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromBytes(byte x, byte y, byte z, byte w)
public void PackFromRgba32(Rgba32 source)
{
this.R = x;
this.G = y;
this.B = z;
this.A = w;
this = source;
}
/// <summary>
@ -229,42 +290,37 @@ namespace ImageSharp
return hexOrder.ToString("X8");
}
/// <inheritdoc/>
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToXyzBytes(Span<byte> bytes, int startIndex)
public void ToRgb24(ref Rgb24 dest)
{
bytes[startIndex] = this.R;
bytes[startIndex + 1] = this.G;
bytes[startIndex + 2] = this.B;
dest = Unsafe.As<Rgba32, Rgb24>(ref this);
}
/// <inheritdoc/>
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToXyzwBytes(Span<byte> bytes, int startIndex)
public void ToRgba32(ref Rgba32 dest)
{
bytes[startIndex] = this.R;
bytes[startIndex + 1] = this.G;
bytes[startIndex + 2] = this.B;
bytes[startIndex + 3] = this.A;
dest = this;
}
/// <inheritdoc/>
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToZyxBytes(Span<byte> bytes, int startIndex)
public void ToBgr24(ref Bgr24 dest)
{
bytes[startIndex] = this.B;
bytes[startIndex + 1] = this.G;
bytes[startIndex + 2] = this.R;
dest.R = this.R;
dest.G = this.G;
dest.B = this.B;
}
/// <inheritdoc/>
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToZyxwBytes(Span<byte> bytes, int startIndex)
public void ToBgra32(ref Bgra32 dest)
{
bytes[startIndex] = this.B;
bytes[startIndex + 1] = this.G;
bytes[startIndex + 2] = this.R;
bytes[startIndex + 3] = this.A;
dest.R = this.R;
dest.G = this.G;
dest.B = this.B;
dest.A = this.A;
}
/// <inheritdoc/>
@ -281,6 +337,17 @@ namespace ImageSharp
return new Vector4(this.R, this.G, this.B, this.A) / MaxBytes;
}
/// <summary>
/// Gets the value of this struct as <see cref="Bgra32"/>.
/// Useful for changing the component order.
/// </summary>
/// <returns>A <see cref="Bgra32"/> value.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Bgra32 ToBgra32()
{
return new Bgra32(this.R, this.G, this.B, this.A);
}
/// <inheritdoc/>
public override bool Equals(object obj)
{
@ -308,14 +375,24 @@ namespace ImageSharp
{
unchecked
{
int hashCode = this.R.GetHashCode();
hashCode = (hashCode * 397) ^ this.G.GetHashCode();
hashCode = (hashCode * 397) ^ this.B.GetHashCode();
hashCode = (hashCode * 397) ^ this.A.GetHashCode();
int hashCode = this.R;
hashCode = (hashCode * 397) ^ this.G;
hashCode = (hashCode * 397) ^ this.B;
hashCode = (hashCode * 397) ^ this.A;
return hashCode;
}
}
/// <summary>
/// Gets the <see cref="Vector4"/> representation without normalizing to [0, 1]
/// </summary>
/// <returns>A <see cref="Vector4"/> of values in [0, 255] </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal Vector4 ToUnscaledVector4()
{
return new Vector4(this.R, this.G, this.B, this.A);
}
/// <summary>
/// Packs the four floats into a <see cref="uint"/>.
/// </summary>

46
src/ImageSharp/PixelFormats/Rgba64.cs

@ -78,7 +78,7 @@ namespace ImageSharp.PixelFormats
}
/// <inheritdoc />
public PixelOperations<Rgba64> CreateBulkOperations() => new PixelOperations<Rgba64>();
public PixelOperations<Rgba64> CreatePixelOperations() => new PixelOperations<Rgba64>();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -100,55 +100,51 @@ namespace ImageSharp.PixelFormats
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromBytes(byte x, byte y, byte z, byte w)
public void PackFromRgba32(Rgba32 source)
{
this.PackFromVector4(new Vector4(x, y, z, w) / 255F);
this.PackFromVector4(source.ToVector4());
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToXyzBytes(Span<byte> bytes, int startIndex)
public void ToRgb24(ref Rgb24 dest)
{
Vector4 vector = this.ToVector4() * 255F;
bytes[startIndex] = (byte)MathF.Round(vector.X);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = (byte)MathF.Round(vector.Z);
dest.R = (byte)MathF.Round(vector.X);
dest.G = (byte)MathF.Round(vector.Y);
dest.B = (byte)MathF.Round(vector.Z);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToXyzwBytes(Span<byte> bytes, int startIndex)
public void ToRgba32(ref Rgba32 dest)
{
Vector4 vector = this.ToVector4() * 255F;
bytes[startIndex] = (byte)MathF.Round(vector.X);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = (byte)MathF.Round(vector.Z);
bytes[startIndex + 3] = (byte)MathF.Round(vector.W);
dest.R = (byte)MathF.Round(vector.X);
dest.G = (byte)MathF.Round(vector.Y);
dest.B = (byte)MathF.Round(vector.Z);
dest.A = (byte)MathF.Round(vector.W);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToZyxBytes(Span<byte> bytes, int startIndex)
public void ToBgr24(ref Bgr24 dest)
{
Vector4 vector = this.ToVector4() * 255F;
bytes[startIndex] = (byte)MathF.Round(vector.Z);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = (byte)MathF.Round(vector.X);
dest.R = (byte)MathF.Round(vector.X);
dest.G = (byte)MathF.Round(vector.Y);
dest.B = (byte)MathF.Round(vector.Z);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToZyxwBytes(Span<byte> bytes, int startIndex)
public void ToBgra32(ref Bgra32 dest)
{
Vector4 vector = this.ToVector4() * 255F;
bytes[startIndex] = (byte)MathF.Round(vector.Z);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = (byte)MathF.Round(vector.X);
bytes[startIndex + 3] = (byte)MathF.Round(vector.W);
dest.R = (byte)MathF.Round(vector.X);
dest.G = (byte)MathF.Round(vector.Y);
dest.B = (byte)MathF.Round(vector.Z);
dest.A = (byte)MathF.Round(vector.W);
}
/// <inheritdoc />

5
src/ImageSharp/PixelFormats/RgbaVector.PixelOperations.cs

@ -23,10 +23,9 @@ namespace ImageSharp.PixelFormats
/// <inheritdoc />
internal override unsafe void ToVector4(Span<RgbaVector> sourceColors, Span<Vector4> destVectors, int count)
{
Guard.MustBeSizedAtLeast(sourceColors, count, nameof(sourceColors));
Guard.MustBeSizedAtLeast(destVectors, count, nameof(destVectors));
GuardSpans(sourceColors, nameof(sourceColors), destVectors, nameof(destVectors), count);
SpanHelper.Copy(sourceColors.AsBytes(), destVectors.AsBytes(), count * sizeof(Vector4));
SpanHelper.Copy(sourceColors.NonPortableCast<RgbaVector, Vector4>(), destVectors, count);
}
}
}

65
src/ImageSharp/PixelFormats/RgbaVector.cs

@ -211,13 +211,13 @@ namespace ImageSharp.PixelFormats
}
/// <inheritdoc />
public PixelOperations<RgbaVector> CreateBulkOperations() => new RgbaVector.PixelOperations();
public PixelOperations<RgbaVector> CreatePixelOperations() => new RgbaVector.PixelOperations();
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromBytes(byte x, byte y, byte z, byte w)
public void PackFromRgba32(Rgba32 source)
{
this.backingVector = new Vector4(x, y, z, w) / MaxBytes;
this.backingVector = source.ToVector4();
}
/// <summary>
@ -233,50 +233,46 @@ namespace ImageSharp.PixelFormats
return hexOrder.ToString("X8");
}
/// <inheritdoc/>
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToXyzBytes(Span<byte> bytes, int startIndex)
public void ToRgb24(ref Rgb24 dest)
{
Vector4 vector = Vector4.Clamp(this.backingVector, Vector4.Zero, Vector4.One) * MaxBytes;
vector += Half;
bytes[startIndex] = (byte)vector.X;
bytes[startIndex + 1] = (byte)vector.Y;
bytes[startIndex + 2] = (byte)vector.Z;
Vector4 vector = this.ToScaledVector4();
dest.R = (byte)MathF.Round(vector.X);
dest.G = (byte)MathF.Round(vector.Y);
dest.B = (byte)MathF.Round(vector.Z);
}
/// <inheritdoc/>
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToXyzwBytes(Span<byte> bytes, int startIndex)
public void ToRgba32(ref Rgba32 dest)
{
Vector4 vector = Vector4.Clamp(this.backingVector, Vector4.Zero, Vector4.One) * MaxBytes;
vector += Half;
bytes[startIndex] = (byte)vector.X;
bytes[startIndex + 1] = (byte)vector.Y;
bytes[startIndex + 2] = (byte)vector.Z;
bytes[startIndex + 3] = (byte)vector.W;
Vector4 vector = this.ToScaledVector4();
dest.R = (byte)MathF.Round(vector.X);
dest.G = (byte)MathF.Round(vector.Y);
dest.B = (byte)MathF.Round(vector.Z);
dest.A = (byte)MathF.Round(vector.W);
}
/// <inheritdoc/>
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToZyxBytes(Span<byte> bytes, int startIndex)
public void ToBgr24(ref Bgr24 dest)
{
Vector4 vector = Vector4.Clamp(this.backingVector, Vector4.Zero, Vector4.One) * MaxBytes;
vector += Half;
bytes[startIndex] = (byte)vector.Z;
bytes[startIndex + 1] = (byte)vector.Y;
bytes[startIndex + 2] = (byte)vector.X;
Vector4 vector = this.ToScaledVector4();
dest.R = (byte)MathF.Round(vector.X);
dest.G = (byte)MathF.Round(vector.Y);
dest.B = (byte)MathF.Round(vector.Z);
}
/// <inheritdoc/>
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToZyxwBytes(Span<byte> bytes, int startIndex)
public void ToBgra32(ref Bgra32 dest)
{
Vector4 vector = Vector4.Clamp(this.backingVector, Vector4.Zero, Vector4.One) * MaxBytes;
vector += Half;
bytes[startIndex] = (byte)vector.Z;
bytes[startIndex + 1] = (byte)vector.Y;
bytes[startIndex + 2] = (byte)vector.X;
bytes[startIndex + 3] = (byte)vector.W;
Vector4 vector = this.ToScaledVector4();
dest.R = (byte)MathF.Round(vector.X);
dest.G = (byte)MathF.Round(vector.Y);
dest.B = (byte)MathF.Round(vector.Z);
dest.A = (byte)MathF.Round(vector.W);
}
/// <inheritdoc/>
@ -320,5 +316,8 @@ namespace ImageSharp.PixelFormats
{
return this.backingVector.GetHashCode();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private Vector4 ToScaledVector4() => Vector4.Clamp(this.backingVector, Vector4.Zero, Vector4.One) * MaxBytes;
}
}

86
src/ImageSharp/PixelFormats/Short2.cs

@ -91,7 +91,7 @@ namespace ImageSharp.PixelFormats
}
/// <inheritdoc />
public PixelOperations<Short2> CreateBulkOperations() => new PixelOperations<Short2>();
public PixelOperations<Short2> CreatePixelOperations() => new PixelOperations<Short2>();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -109,9 +109,9 @@ namespace ImageSharp.PixelFormats
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromBytes(byte x, byte y, byte z, byte w)
public void PackFromRgba32(Rgba32 source)
{
Vector2 vector = new Vector2(x, y) / 255;
Vector2 vector = new Vector2(source.R, source.G) / 255;
vector *= 65534;
vector -= new Vector2(32767);
this.PackedValue = Pack(vector.X, vector.Y);
@ -119,68 +119,44 @@ namespace ImageSharp.PixelFormats
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToXyzBytes(Span<byte> bytes, int startIndex)
public void ToRgb24(ref Rgb24 dest)
{
Vector2 vector = this.ToVector2();
vector /= 65534;
vector *= 255;
vector += Half;
vector += Round;
vector = Vector2.Clamp(vector, Vector2.Zero, MaxBytes);
bytes[startIndex] = (byte)MathF.Round(vector.X);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = 0;
Vector2 vector = this.ToScaledVector2();
dest.R = (byte)MathF.Round(vector.X);
dest.G = (byte)MathF.Round(vector.Y);
dest.B = 0;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToXyzwBytes(Span<byte> bytes, int startIndex)
public void ToRgba32(ref Rgba32 dest)
{
Vector2 vector = this.ToVector2();
vector /= 65534;
vector *= 255;
vector += Half;
vector += Round;
vector = Vector2.Clamp(vector, Vector2.Zero, MaxBytes);
bytes[startIndex] = (byte)MathF.Round(vector.X);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = 0;
bytes[startIndex + 3] = 255;
Vector2 vector = this.ToScaledVector2();
dest.R = (byte)MathF.Round(vector.X);
dest.G = (byte)MathF.Round(vector.Y);
dest.B = 0;
dest.A = 255;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToZyxBytes(Span<byte> bytes, int startIndex)
public void ToBgr24(ref Bgr24 dest)
{
Vector2 vector = this.ToVector2();
vector /= 65534;
vector *= 255;
vector += Half;
vector += Round;
vector = Vector2.Clamp(vector, Vector2.Zero, MaxBytes);
bytes[startIndex] = 0;
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = (byte)MathF.Round(vector.X);
Vector2 vector = this.ToScaledVector2();
dest.R = (byte)MathF.Round(vector.X);
dest.G = (byte)MathF.Round(vector.Y);
dest.B = 0;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToZyxwBytes(Span<byte> bytes, int startIndex)
public void ToBgra32(ref Bgra32 dest)
{
Vector2 vector = this.ToVector2();
vector /= 65534;
vector *= 255;
vector += Half;
vector += Round;
vector = Vector2.Clamp(vector, Vector2.Zero, MaxBytes);
bytes[startIndex] = 0;
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = (byte)MathF.Round(vector.X);
bytes[startIndex + 3] = 255;
Vector2 vector = this.ToScaledVector2();
dest.R = (byte)MathF.Round(vector.X);
dest.G = (byte)MathF.Round(vector.Y);
dest.B = 0;
dest.A = 255;
}
/// <summary>
@ -239,5 +215,17 @@ namespace ImageSharp.PixelFormats
return word2 | word1;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private Vector2 ToScaledVector2()
{
Vector2 vector = this.ToVector2();
vector /= 65534;
vector *= 255;
vector += Half;
vector += Round;
vector = Vector2.Clamp(vector, Vector2.Zero, MaxBytes);
return vector;
}
}
}

86
src/ImageSharp/PixelFormats/Short4.cs

@ -93,7 +93,7 @@ namespace ImageSharp.PixelFormats
}
/// <inheritdoc />
public PixelOperations<Short4> CreateBulkOperations() => new PixelOperations<Short4>();
public PixelOperations<Short4> CreatePixelOperations() => new PixelOperations<Short4>();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -115,9 +115,9 @@ namespace ImageSharp.PixelFormats
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromBytes(byte x, byte y, byte z, byte w)
public void PackFromRgba32(Rgba32 source)
{
Vector4 vector = new Vector4(x, y, z, w) / 255;
Vector4 vector = source.ToVector4();
vector *= 65534;
vector -= new Vector4(32767);
this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
@ -125,68 +125,44 @@ namespace ImageSharp.PixelFormats
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToXyzBytes(Span<byte> bytes, int startIndex)
public void ToRgb24(ref Rgb24 dest)
{
Vector4 vector = this.ToVector4();
vector /= 65534;
vector *= 255;
vector += Half;
vector += Round;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
bytes[startIndex] = (byte)MathF.Round(vector.X);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = (byte)MathF.Round(vector.Z);
Vector4 vector = this.ToScaledVector4();
dest.R = (byte)MathF.Round(vector.X);
dest.G = (byte)MathF.Round(vector.Y);
dest.B = (byte)MathF.Round(vector.Z);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToXyzwBytes(Span<byte> bytes, int startIndex)
public void ToRgba32(ref Rgba32 dest)
{
Vector4 vector = this.ToVector4();
vector /= 65534;
vector *= 255;
vector += Half;
vector += Round;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
bytes[startIndex] = (byte)MathF.Round(vector.X);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = (byte)MathF.Round(vector.Z);
bytes[startIndex + 3] = (byte)MathF.Round(vector.W);
Vector4 vector = this.ToScaledVector4();
dest.R = (byte)MathF.Round(vector.X);
dest.G = (byte)MathF.Round(vector.Y);
dest.B = (byte)MathF.Round(vector.Z);
dest.A = (byte)MathF.Round(vector.W);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToZyxBytes(Span<byte> bytes, int startIndex)
public void ToBgr24(ref Bgr24 dest)
{
Vector4 vector = this.ToVector4();
vector /= 65534;
vector *= 255;
vector += Half;
vector += Round;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
bytes[startIndex] = (byte)MathF.Round(vector.Z);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = (byte)MathF.Round(vector.X);
Vector4 vector = this.ToScaledVector4();
dest.R = (byte)MathF.Round(vector.X);
dest.G = (byte)MathF.Round(vector.Y);
dest.B = (byte)MathF.Round(vector.Z);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToZyxwBytes(Span<byte> bytes, int startIndex)
public void ToBgra32(ref Bgra32 dest)
{
Vector4 vector = this.ToVector4();
vector /= 65534;
vector *= 255;
vector += Half;
vector += Round;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
bytes[startIndex] = (byte)MathF.Round(vector.Z);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = (byte)MathF.Round(vector.X);
bytes[startIndex + 3] = (byte)MathF.Round(vector.W);
Vector4 vector = this.ToScaledVector4();
dest.R = (byte)MathF.Round(vector.X);
dest.G = (byte)MathF.Round(vector.Y);
dest.B = (byte)MathF.Round(vector.Z);
dest.A = (byte)MathF.Round(vector.W);
}
/// <inheritdoc />
@ -246,5 +222,17 @@ namespace ImageSharp.PixelFormats
return word4 | word3 | word2 | word1;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private Vector4 ToScaledVector4()
{
Vector4 vector = this.ToVector4();
vector /= 65534;
vector *= 255;
vector += Half;
vector += Round;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
return vector;
}
}
}

2
src/ImageSharp/Processing/ColorMatrix/BlackWhite.cs

@ -9,7 +9,7 @@ namespace ImageSharp
using ImageSharp.PixelFormats;
using Processing;
using ImageSharp.Processing;
using Processing.Processors;
/// <summary>

2
src/ImageSharp/Processing/ColorMatrix/ColorBlindness.cs

@ -9,7 +9,7 @@ namespace ImageSharp
using ImageSharp.PixelFormats;
using Processing;
using ImageSharp.Processing;
using Processing.Processors;
/// <summary>

42
src/ImageSharp/Processing/ColorMatrix/Grayscale.cs

@ -5,11 +5,9 @@
namespace ImageSharp
{
using System;
using ImageSharp.PixelFormats;
using Processing;
using ImageSharp.Processing;
using Processing.Processors;
/// <summary>
@ -17,6 +15,18 @@ namespace ImageSharp
/// </summary>
public static partial class ImageExtensions
{
/// <summary>
/// Applies <see cref="GrayscaleMode.Bt709"/> Grayscale toning to the image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Grayscale<TPixel>(this Image<TPixel> source)
where TPixel : struct, IPixel<TPixel>
{
return Grayscale(source, GrayscaleMode.Bt709);
}
/// <summary>
/// Applies Grayscale toning to the image.
/// </summary>
@ -24,23 +34,41 @@ namespace ImageSharp
/// <param name="source">The image this method extends.</param>
/// <param name="mode">The formula to apply to perform the operation.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Grayscale<TPixel>(this Image<TPixel> source, GrayscaleMode mode = GrayscaleMode.Bt709)
public static Image<TPixel> Grayscale<TPixel>(this Image<TPixel> source, GrayscaleMode mode)
where TPixel : struct, IPixel<TPixel>
{
return Grayscale(source, source.Bounds, mode);
return Grayscale(source, mode, source.Bounds);
}
/// <summary>
/// Applies Grayscale toning to the image.
/// Applies <see cref="GrayscaleMode.Bt709"/> Grayscale toning to the image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Grayscale<TPixel>(this Image<TPixel> source, Rectangle rectangle)
where TPixel : struct, IPixel<TPixel>
{
IImageProcessor<TPixel> processor = new GrayscaleBt709Processor<TPixel>();
source.ApplyProcessor(processor, rectangle);
return source;
}
/// <summary>
/// Applies Grayscale toning to the image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="mode">The formula to apply to perform the operation.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Grayscale<TPixel>(this Image<TPixel> source, Rectangle rectangle, GrayscaleMode mode = GrayscaleMode.Bt709)
public static Image<TPixel> Grayscale<TPixel>(this Image<TPixel> source, GrayscaleMode mode, Rectangle rectangle)
where TPixel : struct, IPixel<TPixel>
{
IImageProcessor<TPixel> processor = mode == GrayscaleMode.Bt709

2
src/ImageSharp/Processing/ColorMatrix/Hue.cs

@ -9,7 +9,7 @@ namespace ImageSharp
using ImageSharp.PixelFormats;
using Processing;
using ImageSharp.Processing;
using Processing.Processors;
/// <summary>

2
src/ImageSharp/Processing/ColorMatrix/Kodachrome.cs

@ -9,7 +9,7 @@ namespace ImageSharp
using ImageSharp.PixelFormats;
using Processing;
using ImageSharp.Processing;
using Processing.Processors;
/// <summary>

2
src/ImageSharp/Processing/ColorMatrix/Lomograph.cs

@ -9,7 +9,7 @@ namespace ImageSharp
using ImageSharp.PixelFormats;
using Processing;
using ImageSharp.Processing;
using Processing.Processors;
/// <summary>

2
src/ImageSharp/Processing/ColorMatrix/Polaroid.cs

@ -9,7 +9,7 @@ namespace ImageSharp
using ImageSharp.PixelFormats;
using Processing;
using ImageSharp.Processing;
using Processing.Processors;
/// <summary>

2
src/ImageSharp/Processing/ColorMatrix/Saturation.cs

@ -9,7 +9,7 @@ namespace ImageSharp
using ImageSharp.PixelFormats;
using Processing;
using ImageSharp.Processing;
using Processing.Processors;
/// <summary>

2
src/ImageSharp/Processing/ColorMatrix/Sepia.cs

@ -9,7 +9,7 @@ namespace ImageSharp
using ImageSharp.PixelFormats;
using Processing;
using ImageSharp.Processing;
using Processing.Processors;
/// <summary>

2
src/ImageSharp/Processing/Convolution/DetectEdges.cs

@ -9,7 +9,7 @@ namespace ImageSharp
using ImageSharp.PixelFormats;
using Processing;
using ImageSharp.Processing;
using Processing.Processors;
/// <summary>

2
src/ImageSharp/Processing/Convolution/GaussianBlur.cs

@ -9,7 +9,7 @@ namespace ImageSharp
using ImageSharp.PixelFormats;
using Processing;
using ImageSharp.Processing;
using Processing.Processors;
/// <summary>

2
src/ImageSharp/Processing/Convolution/GaussianSharpen.cs

@ -9,7 +9,7 @@ namespace ImageSharp
using ImageSharp.PixelFormats;
using Processing;
using ImageSharp.Processing;
using Processing.Processors;
/// <summary>

33
src/ImageSharp/Processing/Effects/OilPainting.cs

@ -16,6 +16,35 @@ namespace ImageSharp
/// </summary>
public static partial class ImageExtensions
{
/// <summary>
/// Alters the colors of the image recreating an oil painting effect with levels and brushSize
/// set to <value>10</value> and <value>15</value> respectively.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> OilPaint<TPixel>(this Image<TPixel> source)
where TPixel : struct, IPixel<TPixel>
{
return OilPaint(source, 10, 15);
}
/// <summary>
/// Alters the colors of the image recreating an oil painting effect with levels and brushSize
/// set to <value>10</value> and <value>15</value> respectively.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> OilPaint<TPixel>(this Image<TPixel> source, Rectangle rectangle)
where TPixel : struct, IPixel<TPixel>
{
return OilPaint(source, 10, 15, rectangle);
}
/// <summary>
/// Alters the colors of the image recreating an oil painting effect.
/// </summary>
@ -24,8 +53,8 @@ namespace ImageSharp
/// <param name="levels">The number of intensity levels. Higher values result in a broader range of color intensities forming part of the result image.</param>
/// <param name="brushSize">The number of neighboring pixels used in calculating each individual pixel value.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> OilPaint<TPixel>(this Image<TPixel> source, int levels = 10, int brushSize = 15)
where TPixel : struct, IPixel<TPixel>
public static Image<TPixel> OilPaint<TPixel>(this Image<TPixel> source, int levels, int brushSize)
where TPixel : struct, IPixel<TPixel>
{
return OilPaint(source, levels, brushSize, source.Bounds);
}

4
src/ImageSharp/Processing/Overlays/Glow.cs

@ -5,8 +5,6 @@
namespace ImageSharp
{
using System;
using ImageSharp.PixelFormats;
using Processing.Processors;
@ -158,7 +156,7 @@ namespace ImageSharp
public static Image<TPixel> Glow<TPixel>(this Image<TPixel> source, TPixel color, float radius, Rectangle rectangle, GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
GlowProcessor<TPixel> processor = new GlowProcessor<TPixel>(color, options) { Radius = radius, };
var processor = new GlowProcessor<TPixel>(color, options) { Radius = radius, };
source.ApplyProcessor(processor, rectangle);
return source;
}

4
src/ImageSharp/Processing/Overlays/Vignette.cs

@ -5,8 +5,6 @@
namespace ImageSharp
{
using System;
using ImageSharp.PixelFormats;
using Processing.Processors;
@ -162,7 +160,7 @@ namespace ImageSharp
public static Image<TPixel> Vignette<TPixel>(this Image<TPixel> source, TPixel color, float radiusX, float radiusY, Rectangle rectangle, GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
VignetteProcessor<TPixel> processor = new VignetteProcessor<TPixel>(color, options) { RadiusX = radiusX, RadiusY = radiusY };
var processor = new VignetteProcessor<TPixel>(color, options) { RadiusX = radiusX, RadiusY = radiusY };
source.ApplyProcessor(processor, rectangle);
return source;
}

49
src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs

@ -97,17 +97,7 @@ namespace ImageSharp.Processing.Processors
int fyr = fy - radius;
int offsetY = y + fyr;
// Skip the current row
if (offsetY < minY)
{
continue;
}
// Outwith the current bounds so break.
if (offsetY >= maxY)
{
break;
}
offsetY = offsetY.Clamp(0, maxY);
Span<TPixel> sourceOffsetRow = source.GetRowSpan(offsetY);
@ -115,34 +105,25 @@ namespace ImageSharp.Processing.Processors
{
int fxr = fx - radius;
int offsetX = x + fxr;
offsetX = offsetX.Clamp(0, maxX);
// Skip the column
if (offsetX < 0)
{
continue;
}
var vector = sourceOffsetRow[offsetX].ToVector4();
if (offsetX < maxX)
{
// ReSharper disable once AccessToDisposedClosure
var vector = sourceOffsetRow[offsetX].ToVector4();
float sourceRed = vector.X;
float sourceBlue = vector.Z;
float sourceGreen = vector.Y;
float sourceRed = vector.X;
float sourceBlue = vector.Z;
float sourceGreen = vector.Y;
int currentIntensity = (int)MathF.Round((sourceBlue + sourceGreen + sourceRed) / 3F * (levels - 1));
int currentIntensity = (int)MathF.Round((sourceBlue + sourceGreen + sourceRed) / 3F * (levels - 1));
intensityBin[currentIntensity] += 1;
blueBin[currentIntensity] += sourceBlue;
greenBin[currentIntensity] += sourceGreen;
redBin[currentIntensity] += sourceRed;
intensityBin[currentIntensity] += 1;
blueBin[currentIntensity] += sourceBlue;
greenBin[currentIntensity] += sourceGreen;
redBin[currentIntensity] += sourceRed;
if (intensityBin[currentIntensity] > maxIntensity)
{
maxIntensity = intensityBin[currentIntensity];
maxIndex = currentIntensity;
}
if (intensityBin[currentIntensity] > maxIntensity)
{
maxIntensity = intensityBin[currentIntensity];
maxIndex = currentIntensity;
}
}

2
src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs

@ -52,7 +52,7 @@ namespace ImageSharp.Processing.Processors
int startX = sourceRectangle.X;
int endX = sourceRectangle.Right;
TPixel glowColor = this.GlowColor;
var centre = Rectangle.Center(sourceRectangle).ToVector2();
Vector2 centre = Rectangle.Center(sourceRectangle);
float maxDistance = this.Radius > 0 ? MathF.Min(this.Radius, sourceRectangle.Width * .5F) : sourceRectangle.Width * .5F;
// Align start/end positions.

2
src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs

@ -58,7 +58,7 @@ namespace ImageSharp.Processing.Processors
int startX = sourceRectangle.X;
int endX = sourceRectangle.Right;
TPixel vignetteColor = this.VignetteColor;
var centre = Rectangle.Center(sourceRectangle).ToVector2();
Vector2 centre = Rectangle.Center(sourceRectangle);
float rX = this.RadiusX > 0 ? MathF.Min(this.RadiusX, sourceRectangle.Width * .5F) : sourceRectangle.Width * .5F;
float rY = this.RadiusY > 0 ? MathF.Min(this.RadiusY, sourceRectangle.Height * .5F) : sourceRectangle.Height * .5F;
float maxDistance = MathF.Sqrt((rX * rX) + (rY * rY));

50
src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs

@ -27,31 +27,52 @@ namespace ImageSharp.Processing.Processors
public int Left;
/// <summary>
/// The span of weights pointing to <see cref="WeightsBuffer"/>.
/// The length of the weights window
/// </summary>
public Span<float> Span;
public int Length;
/// <summary>
/// The index in the destination buffer
/// </summary>
private readonly int flatStartIndex;
/// <summary>
/// The buffer containing the weights values.
/// </summary>
private readonly Buffer<float> buffer;
/// <summary>
/// Initializes a new instance of the <see cref="WeightsWindow"/> struct.
/// </summary>
/// <param name="index">The destination index in the buffer</param>
/// <param name="left">The local left index</param>
/// <param name="span">The span</param>
/// <param name="buffer">The span</param>
/// <param name="length">The length of the window</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal WeightsWindow(int left, Span<float> span)
internal WeightsWindow(int index, int left, Buffer2D<float> buffer, int length)
{
this.flatStartIndex = (index * buffer.Width) + left;
this.Left = left;
this.Span = span;
this.buffer = buffer;
this.Length = length;
}
/// <summary>
/// Gets an unsafe float* pointer to the beginning of <see cref="Span"/>.
/// Gets a reference to the first item of the window.
/// </summary>
public ref float Ptr => ref this.Span.DangerousGetPinnableReference();
/// <returns>The reference to the first item of the window</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref float GetStartReference()
{
return ref this.buffer[this.flatStartIndex];
}
/// <summary>
/// Gets the lenghth of the weights window
/// Gets the span representing the portion of the <see cref="WeightsBuffer"/> that this window covers
/// </summary>
public int Length => this.Span.Length;
/// <returns>The <see cref="Span{T}"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span<float> GetWindowSpan() => this.buffer.Slice(this.flatStartIndex, this.Length);
/// <summary>
/// Computes the sum of vectors in 'rowSpan' weighted by weight values, pointed by this <see cref="WeightsWindow"/> instance.
@ -62,7 +83,7 @@ namespace ImageSharp.Processing.Processors
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ComputeWeightedRowSum(Span<Vector4> rowSpan, int sourceX)
{
ref float horizontalValues = ref this.Ptr;
ref float horizontalValues = ref this.GetStartReference();
int left = this.Left;
ref Vector4 vecPtr = ref Unsafe.Add(ref rowSpan.DangerousGetPinnableReference(), left + sourceX);
@ -89,7 +110,7 @@ namespace ImageSharp.Processing.Processors
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ComputeExpandedWeightedRowSum(Span<Vector4> rowSpan, int sourceX)
{
ref float horizontalValues = ref this.Ptr;
ref float horizontalValues = ref this.GetStartReference();
int left = this.Left;
ref Vector4 vecPtr = ref Unsafe.Add(ref rowSpan.DangerousGetPinnableReference(), left + sourceX);
@ -117,7 +138,7 @@ namespace ImageSharp.Processing.Processors
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ComputeWeightedColumnSum(Buffer2D<Vector4> firstPassPixels, int x, int sourceY)
{
ref float verticalValues = ref this.Ptr;
ref float verticalValues = ref this.GetStartReference();
int left = this.Left;
// Destination color components
@ -139,7 +160,7 @@ namespace ImageSharp.Processing.Processors
/// </summary>
internal class WeightsBuffer : IDisposable
{
private Buffer2D<float> dataBuffer;
private readonly Buffer2D<float> dataBuffer;
/// <summary>
/// Initializes a new instance of the <see cref="WeightsBuffer"/> class.
@ -174,8 +195,7 @@ namespace ImageSharp.Processing.Processors
/// <returns>The weights</returns>
public WeightsWindow GetWeightsWindow(int destIdx, int leftIdx, int rightIdx)
{
Span<float> span = this.dataBuffer.GetRowSpan(destIdx).Slice(leftIdx, rightIdx - leftIdx + 1);
return new WeightsWindow(leftIdx, span);
return new WeightsWindow(destIdx, leftIdx, this.dataBuffer, rightIdx - leftIdx + 1);
}
}
}

6
src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs

@ -112,7 +112,7 @@ namespace ImageSharp.Processing.Processors
WeightsWindow ws = result.GetWeightsWindow(i, left, right);
result.Weights[i] = ws;
ref float weights = ref ws.Ptr;
ref float weightsBaseRef = ref ws.GetStartReference();
for (int j = left; j <= right; j++)
{
@ -120,7 +120,7 @@ namespace ImageSharp.Processing.Processors
sum += weight;
// weights[j - left] = weight:
Unsafe.Add(ref weights, j - left) = weight;
Unsafe.Add(ref weightsBaseRef, j - left) = weight;
}
// Normalise, best to do it here rather than in the pixel loop later on.
@ -129,7 +129,7 @@ namespace ImageSharp.Processing.Processors
for (int w = 0; w < ws.Length; w++)
{
// weights[w] = weights[w] / sum:
ref float wRef = ref Unsafe.Add(ref weights, w);
ref float wRef = ref Unsafe.Add(ref weightsBaseRef, w);
wRef = wRef / sum;
}
}

2
src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs

@ -78,7 +78,7 @@ namespace ImageSharp.Processing.Processors
return;
}
this.processMatrix = Point.CreateRotation(new Point(0, 0), -this.Angle);
this.processMatrix = Matrix3x2Extensions.CreateRotation(-this.Angle, new Point(0, 0));
if (this.Expand)
{
this.CreateNewCanvas(sourceRectangle, this.processMatrix);

2
src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs

@ -73,7 +73,7 @@ namespace ImageSharp.Processing.Processors
/// <inheritdoc/>
protected override void BeforeApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
{
this.processMatrix = Point.CreateSkew(new Point(0, 0), -this.AngleX, -this.AngleY);
this.processMatrix = Matrix3x2Extensions.CreateSkew(-this.AngleX, -this.AngleY, new Point(0, 0));
if (this.Expand)
{
this.CreateNewCanvas(sourceRectangle, this.processMatrix);

7
src/ImageSharp/Processing/Transforms/AutoOrient.cs

@ -5,12 +5,9 @@
namespace ImageSharp
{
using System;
using ImageSharp.PixelFormats;
using Processing;
using Processing.Processors;
using ImageSharp.Processing;
/// <summary>
/// Extension methods for the <see cref="Image{TPixel}"/> type.
@ -80,7 +77,7 @@ namespace ImageSharp
return Orientation.Unknown;
}
Orientation orientation = (Orientation)value.Value;
var orientation = (Orientation)value.Value;
source.MetaData.ExifProfile.SetValue(ExifTag.Orientation, (ushort)Orientation.TopLeft);

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

@ -9,7 +9,7 @@ namespace ImageSharp
using ImageSharp.PixelFormats;
using Processing;
using ImageSharp.Processing;
using Processing.Processors;
/// <summary>

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

Loading…
Cancel
Save