mirror of https://github.com/SixLabors/ImageSharp
336 changed files with 7522 additions and 6433 deletions
@ -1,12 +0,0 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<OutputType>Exe</OutputType> |
|||
<TargetFramework>netcoreapp1.1</TargetFramework> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\src\ImageSharp.Drawing\ImageSharp.Drawing.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -1,111 +0,0 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Numerics; |
|||
using SixLabors.ImageSharp; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
using SixLabors.ImageSharp.Processing; |
|||
using SixLabors.Primitives; |
|||
using SixLabors.Shapes; |
|||
|
|||
namespace AvatarWithRoundedCorner |
|||
{ |
|||
static class Program |
|||
{ |
|||
static void Main(string[] args) |
|||
{ |
|||
System.IO.Directory.CreateDirectory("output"); |
|||
using (var img = Image.Load("fb.jpg")) |
|||
{ |
|||
// as generate returns a new IImage make sure we dispose of it
|
|||
using (Image<Rgba32> destRound = img.Clone(x => x.ConvertToAvatar(new Size(200, 200), 20))) |
|||
{ |
|||
destRound.Save("output/fb.png"); |
|||
} |
|||
|
|||
using (Image<Rgba32> destRound = img.Clone(x => x.ConvertToAvatar(new Size(200, 200), 100))) |
|||
{ |
|||
destRound.Save("output/fb-round.png"); |
|||
} |
|||
|
|||
using (Image<Rgba32> destRound = img.Clone(x => x.ConvertToAvatar(new Size(200, 200), 150))) |
|||
{ |
|||
destRound.Save("output/fb-rounder.png"); |
|||
} |
|||
|
|||
using (Image<Rgba32> destRound = img.CloneAndConvertToAvatarWithoutApply(new Size(200, 200), 150)) |
|||
{ |
|||
destRound.Save("output/fb-rounder-without-apply.png"); |
|||
} |
|||
|
|||
// the original `img` object has not been altered at all.
|
|||
} |
|||
} |
|||
|
|||
// 1. The short way:
|
|||
// Implements a full image mutating pipeline operating on IImageProcessingContext<Rgba32>
|
|||
// We need the dimensions of the resized image to deduce 'IPathCollection' needed to build the corners,
|
|||
// so we implement an "inline" image processor by utilizing 'ImageExtensions.Apply()'
|
|||
private static IImageProcessingContext<Rgba32> ConvertToAvatar(this IImageProcessingContext<Rgba32> processingContext, Size size, float cornerRadius) |
|||
{ |
|||
return processingContext.Resize(new ResizeOptions |
|||
{ |
|||
Size = size, |
|||
Mode = ResizeMode.Crop |
|||
}).Apply(i => ApplyRoundedCorners(i, cornerRadius)); |
|||
} |
|||
|
|||
// 2. A more verbose way, avoiding 'Apply()':
|
|||
// First we create a resized clone of the image, then we draw the corners on that instance with Mutate().
|
|||
private static Image<Rgba32> CloneAndConvertToAvatarWithoutApply(this Image<Rgba32> image, Size size, float cornerRadius) |
|||
{ |
|||
Image<Rgba32> result = image.Clone( |
|||
ctx => ctx.Resize( |
|||
new ResizeOptions |
|||
{ |
|||
Size = size, |
|||
Mode = ResizeMode.Crop |
|||
})); |
|||
|
|||
ApplyRoundedCorners(result, cornerRadius); |
|||
return result; |
|||
} |
|||
|
|||
// This method can be seen as an inline implementation of an `IImageProcessor`:
|
|||
// (The combination of `IImageOperations.Apply()` + this could be replaced with an `IImageProcessor`)
|
|||
public static void ApplyRoundedCorners(Image<Rgba32> img, float cornerRadius) |
|||
{ |
|||
IPathCollection corners = BuildCorners(img.Width, img.Height, cornerRadius); |
|||
|
|||
// mutating in here as we already have a cloned original
|
|||
img.Mutate(x => x.Fill(Rgba32.Transparent, corners, new GraphicsOptions(true) |
|||
{ |
|||
BlenderMode = PixelBlenderMode.Src // enforces that any part of this shape that has color is punched out of the background
|
|||
})); |
|||
} |
|||
|
|||
public static IPathCollection BuildCorners(int imageWidth, int imageHeight, float cornerRadius) |
|||
{ |
|||
// first create a square
|
|||
var rect = new RectangularePolygon(-0.5f, -0.5f, cornerRadius, cornerRadius); |
|||
|
|||
// then cut out of the square a circle so we are left with a corner
|
|||
IPath cornerToptLeft = rect.Clip(new EllipsePolygon(cornerRadius - 0.5f, cornerRadius - 0.5f, cornerRadius)); |
|||
|
|||
// corner is now a corner shape positions top left
|
|||
//lets make 3 more positioned correctly, we can do that by translating the orgional artound the center of the image
|
|||
var center = new Vector2(imageWidth / 2F, imageHeight / 2F); |
|||
|
|||
float rightPos = imageWidth - cornerToptLeft.Bounds.Width + 1; |
|||
float bottomPos = imageHeight - cornerToptLeft.Bounds.Height + 1; |
|||
|
|||
// move it across the widthof the image - the width of the shape
|
|||
IPath cornerTopRight = cornerToptLeft.RotateDegree(90).Translate(rightPos, 0); |
|||
IPath cornerBottomLeft = cornerToptLeft.RotateDegree(-90).Translate(0, bottomPos); |
|||
IPath cornerBottomRight = cornerToptLeft.RotateDegree(180).Translate(rightPos, bottomPos); |
|||
|
|||
return new PathCollection(cornerToptLeft, cornerBottomLeft, cornerTopRight, cornerBottomRight); |
|||
} |
|||
} |
|||
} |
|||
@ -1,3 +0,0 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:93bb4d6281dc1e845db57e836e0dca30b7a4062e81044efb27ad4d8b1a33130c |
|||
size 15787 |
|||
@ -1,12 +0,0 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<OutputType>Exe</OutputType> |
|||
<TargetFramework>netcoreapp1.1</TargetFramework> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\src\ImageSharp\ImageSharp.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -1,23 +0,0 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using SixLabors.ImageSharp; |
|||
using SixLabors.ImageSharp.Formats.Jpeg; |
|||
|
|||
namespace ChangeDefaultEncoderOptions |
|||
{ |
|||
class Program |
|||
{ |
|||
static void Main(string[] args) |
|||
{ |
|||
// lets switch out the default encoder for jpeg to one
|
|||
// that saves at 90 quality and ignores the matadata
|
|||
Configuration.Default.SetEncoder(ImageFormats.Jpeg, new JpegEncoder() |
|||
{ |
|||
Quality = 90, |
|||
IgnoreMetadata = true |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,60 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using SixLabors.ImageSharp.Memory; |
|||
|
|||
namespace SixLabors.ImageSharp |
|||
{ |
|||
/// <summary>
|
|||
/// Utility methods for Parallel.For() execution. Use this instead of raw <see cref="Parallel"/> calls!
|
|||
/// </summary>
|
|||
internal static class ParallelFor |
|||
{ |
|||
/// <summary>
|
|||
/// Helper method to execute Parallel.For using the settings in <see cref="Configuration.ParallelOptions"/>
|
|||
/// </summary>
|
|||
public static void WithConfiguration(int fromInclusive, int toExclusive, Configuration configuration, Action<int> body) |
|||
{ |
|||
Parallel.For(fromInclusive, toExclusive, configuration.ParallelOptions, body); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Helper method to execute Parallel.For with temporary worker buffer shared between executing tasks.
|
|||
/// The buffer is not guaranteed to be clean!
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The value type of the buffer</typeparam>
|
|||
/// <param name="fromInclusive">The start index, inclusive.</param>
|
|||
/// <param name="toExclusive">The end index, exclusive.</param>
|
|||
/// <param name="configuration">The <see cref="Configuration"/> used for getting the <see cref="MemoryManager"/> and <see cref="ParallelOptions"/></param>
|
|||
/// <param name="bufferLength">The length of the requested parallel buffer</param>
|
|||
/// <param name="body">The delegate that is invoked once per iteration.</param>
|
|||
public static void WithTemporaryBuffer<T>( |
|||
int fromInclusive, |
|||
int toExclusive, |
|||
Configuration configuration, |
|||
int bufferLength, |
|||
Action<int, IBuffer<T>> body) |
|||
where T : struct |
|||
{ |
|||
MemoryManager memoryManager = configuration.MemoryManager; |
|||
ParallelOptions parallelOptions = configuration.ParallelOptions; |
|||
|
|||
IBuffer<T> InitBuffer() |
|||
{ |
|||
return memoryManager.Allocate<T>(bufferLength); |
|||
} |
|||
|
|||
void CleanUpBuffer(IBuffer<T> buffer) |
|||
{ |
|||
buffer.Dispose(); |
|||
} |
|||
|
|||
IBuffer<T> BodyFunc(int i, ParallelLoopState state, IBuffer<T> buffer) |
|||
{ |
|||
body(i, buffer); |
|||
return buffer; |
|||
} |
|||
|
|||
Parallel.For(fromInclusive, toExclusive, parallelOptions, InitBuffer, BodyFunc, CleanUpBuffer); |
|||
} |
|||
} |
|||
} |
|||
@ -1,261 +1,145 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Collections.Concurrent; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using SixLabors.ImageSharp.Formats; |
|||
using SixLabors.ImageSharp.Formats.Bmp; |
|||
using SixLabors.ImageSharp.Formats.Gif; |
|||
using SixLabors.ImageSharp.Formats.Jpeg; |
|||
using SixLabors.ImageSharp.Formats.Png; |
|||
using SixLabors.ImageSharp.IO; |
|||
|
|||
namespace SixLabors.ImageSharp |
|||
{ |
|||
/// <summary>
|
|||
/// Provides initialization code which allows extending the library.
|
|||
/// </summary>
|
|||
public sealed class Configuration |
|||
{ |
|||
/// <summary>
|
|||
/// A lazily initialized configuration default instance.
|
|||
/// </summary>
|
|||
private static readonly Lazy<Configuration> Lazy = new Lazy<Configuration>(CreateDefaultInstance); |
|||
|
|||
/// <summary>
|
|||
/// The list of supported <see cref="IImageEncoder"/> keyed to mime types.
|
|||
/// </summary>
|
|||
private readonly ConcurrentDictionary<IImageFormat, IImageEncoder> mimeTypeEncoders = new ConcurrentDictionary<IImageFormat, IImageEncoder>(); |
|||
|
|||
/// <summary>
|
|||
/// The list of supported <see cref="IImageEncoder"/> keyed to mime types.
|
|||
/// </summary>
|
|||
private readonly ConcurrentDictionary<IImageFormat, IImageDecoder> mimeTypeDecoders = new ConcurrentDictionary<IImageFormat, IImageDecoder>(); |
|||
|
|||
/// <summary>
|
|||
/// The list of supported <see cref="IImageFormat"/>s.
|
|||
/// </summary>
|
|||
private readonly ConcurrentBag<IImageFormat> imageFormats = new ConcurrentBag<IImageFormat>(); |
|||
|
|||
/// <summary>
|
|||
/// The list of supported <see cref="IImageFormatDetector"/>s.
|
|||
/// </summary>
|
|||
private ConcurrentBag<IImageFormatDetector> imageFormatDetectors = new ConcurrentBag<IImageFormatDetector>(); |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Configuration" /> class.
|
|||
/// </summary>
|
|||
public Configuration() |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Configuration" /> class.
|
|||
/// </summary>
|
|||
/// <param name="configurationModules">A collection of configuration modules to register</param>
|
|||
public Configuration(params IConfigurationModule[] configurationModules) |
|||
{ |
|||
if (configurationModules != null) |
|||
{ |
|||
foreach (IConfigurationModule p in configurationModules) |
|||
{ |
|||
p.Configure(this); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the default <see cref="Configuration"/> instance.
|
|||
/// </summary>
|
|||
public static Configuration Default { get; } = Lazy.Value; |
|||
|
|||
/// <summary>
|
|||
/// Gets the global parallel options for processing tasks in parallel.
|
|||
/// </summary>
|
|||
public ParallelOptions ParallelOptions { get; } = new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount }; |
|||
|
|||
/// <summary>
|
|||
/// Gets the currently registered <see cref="IImageFormat"/>s.
|
|||
/// </summary>
|
|||
public IEnumerable<IImageFormat> ImageFormats => this.imageFormats; |
|||
|
|||
/// <summary>
|
|||
/// Gets the maximum header size of all the formats.
|
|||
/// </summary>
|
|||
internal int MaxHeaderSize { get; private set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the currently registered <see cref="IImageFormatDetector"/>s.
|
|||
/// </summary>
|
|||
internal IEnumerable<IImageFormatDetector> FormatDetectors => this.imageFormatDetectors; |
|||
|
|||
/// <summary>
|
|||
/// Gets the currently registered <see cref="IImageDecoder"/>s.
|
|||
/// </summary>
|
|||
internal IEnumerable<KeyValuePair<IImageFormat, IImageDecoder>> ImageDecoders => this.mimeTypeDecoders; |
|||
|
|||
/// <summary>
|
|||
/// Gets the currently registered <see cref="IImageEncoder"/>s.
|
|||
/// </summary>
|
|||
internal IEnumerable<KeyValuePair<IImageFormat, IImageEncoder>> ImageEncoders => this.mimeTypeEncoders; |
|||
|
|||
#if !NETSTANDARD1_1
|
|||
/// <summary>
|
|||
/// Gets or sets the filesystem helper for accessing the local file system.
|
|||
/// </summary>
|
|||
internal IFileSystem FileSystem { get; set; } = new LocalFileSystem(); |
|||
#endif
|
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the image operations provider factory.
|
|||
/// </summary>
|
|||
internal IImageProcessingContextFactory ImageOperationsProvider { get; set; } = new DefaultImageOperationsProviderFactory(); |
|||
|
|||
/// <summary>
|
|||
/// Registers a new format provider.
|
|||
/// </summary>
|
|||
/// <param name="configuration">The configuration provider to call configure on.</param>
|
|||
public void Configure(IConfigurationModule configuration) |
|||
{ |
|||
Guard.NotNull(configuration, nameof(configuration)); |
|||
configuration.Configure(this); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Registers a new format provider.
|
|||
/// </summary>
|
|||
/// <param name="format">The format to register as a known format.</param>
|
|||
public void AddImageFormat(IImageFormat format) |
|||
{ |
|||
Guard.NotNull(format, nameof(format)); |
|||
Guard.NotNull(format.MimeTypes, nameof(format.MimeTypes)); |
|||
Guard.NotNull(format.FileExtensions, nameof(format.FileExtensions)); |
|||
this.imageFormats.Add(format); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// For the specified file extensions type find the e <see cref="IImageFormat"/>.
|
|||
/// </summary>
|
|||
/// <param name="extension">The extension to discover</param>
|
|||
/// <returns>The <see cref="IImageFormat"/> if found otherwise null</returns>
|
|||
public IImageFormat FindFormatByFileExtension(string extension) |
|||
{ |
|||
return this.imageFormats.FirstOrDefault(x => x.FileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// For the specified mime type find the <see cref="IImageFormat"/>.
|
|||
/// </summary>
|
|||
/// <param name="mimeType">The mime-type to discover</param>
|
|||
/// <returns>The <see cref="IImageFormat"/> if found; otherwise null</returns>
|
|||
public IImageFormat FindFormatByMimeType(string mimeType) |
|||
{ |
|||
return this.imageFormats.FirstOrDefault(x => x.MimeTypes.Contains(mimeType, StringComparer.OrdinalIgnoreCase)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets a specific image encoder as the encoder for a specific image format.
|
|||
/// </summary>
|
|||
/// <param name="imageFormat">The image format to register the encoder for.</param>
|
|||
/// <param name="encoder">The encoder to use,</param>
|
|||
public void SetEncoder(IImageFormat imageFormat, IImageEncoder encoder) |
|||
{ |
|||
Guard.NotNull(imageFormat, nameof(imageFormat)); |
|||
Guard.NotNull(encoder, nameof(encoder)); |
|||
this.AddImageFormat(imageFormat); |
|||
this.mimeTypeEncoders.AddOrUpdate(imageFormat, encoder, (s, e) => encoder); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets a specific image decoder as the decoder for a specific image format.
|
|||
/// </summary>
|
|||
/// <param name="imageFormat">The image format to register the encoder for.</param>
|
|||
/// <param name="decoder">The decoder to use,</param>
|
|||
public void SetDecoder(IImageFormat imageFormat, IImageDecoder decoder) |
|||
{ |
|||
Guard.NotNull(imageFormat, nameof(imageFormat)); |
|||
Guard.NotNull(decoder, nameof(decoder)); |
|||
this.AddImageFormat(imageFormat); |
|||
this.mimeTypeDecoders.AddOrUpdate(imageFormat, decoder, (s, e) => decoder); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Removes all the registered image format detectors.
|
|||
/// </summary>
|
|||
public void ClearImageFormatDetectors() |
|||
{ |
|||
this.imageFormatDetectors = new ConcurrentBag<IImageFormatDetector>(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Adds a new detector for detecting mime types.
|
|||
/// </summary>
|
|||
/// <param name="detector">The detector to add</param>
|
|||
public void AddImageFormatDetector(IImageFormatDetector detector) |
|||
{ |
|||
Guard.NotNull(detector, nameof(detector)); |
|||
this.imageFormatDetectors.Add(detector); |
|||
this.SetMaxHeaderSize(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// For the specified mime type find the decoder.
|
|||
/// </summary>
|
|||
/// <param name="format">The format to discover</param>
|
|||
/// <returns>The <see cref="IImageDecoder"/> if found otherwise null</returns>
|
|||
public IImageDecoder FindDecoder(IImageFormat format) |
|||
{ |
|||
Guard.NotNull(format, nameof(format)); |
|||
if (this.mimeTypeDecoders.TryGetValue(format, out IImageDecoder decoder)) |
|||
{ |
|||
return decoder; |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// For the specified mime type find the encoder.
|
|||
/// </summary>
|
|||
/// <param name="format">The format to discover</param>
|
|||
/// <returns>The <see cref="IImageEncoder"/> if found otherwise null</returns>
|
|||
public IImageEncoder FindEncoder(IImageFormat format) |
|||
{ |
|||
Guard.NotNull(format, nameof(format)); |
|||
if (this.mimeTypeEncoders.TryGetValue(format, out IImageEncoder encoder)) |
|||
{ |
|||
return encoder; |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Creates the default instance with the following <see cref="IConfigurationModule"/>s preregistered:
|
|||
/// <para><see cref="PngConfigurationModule"/></para>
|
|||
/// <para><see cref="JpegConfigurationModule"/></para>
|
|||
/// <para><see cref="GifConfigurationModule"/></para>
|
|||
/// <para><see cref="BmpConfigurationModule"/></para>
|
|||
/// </summary>
|
|||
/// <returns>The default configuration of <see cref="Configuration"/></returns>
|
|||
internal static Configuration CreateDefaultInstance() |
|||
{ |
|||
return new Configuration( |
|||
new PngConfigurationModule(), |
|||
new JpegConfigurationModule(), |
|||
new GifConfigurationModule(), |
|||
new BmpConfigurationModule()); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the max header size.
|
|||
/// </summary>
|
|||
private void SetMaxHeaderSize() |
|||
{ |
|||
this.MaxHeaderSize = this.imageFormatDetectors.Max(x => x.HeaderSize); |
|||
} |
|||
} |
|||
} |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Collections.Concurrent; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using SixLabors.ImageSharp.Formats; |
|||
using SixLabors.ImageSharp.Formats.Bmp; |
|||
using SixLabors.ImageSharp.Formats.Gif; |
|||
using SixLabors.ImageSharp.Formats.Jpeg; |
|||
using SixLabors.ImageSharp.Formats.Png; |
|||
using SixLabors.ImageSharp.IO; |
|||
using SixLabors.ImageSharp.Memory; |
|||
|
|||
namespace SixLabors.ImageSharp |
|||
{ |
|||
/// <summary>
|
|||
/// Provides initialization code which allows extending the library.
|
|||
/// </summary>
|
|||
public sealed class Configuration |
|||
{ |
|||
/// <summary>
|
|||
/// A lazily initialized configuration default instance.
|
|||
/// </summary>
|
|||
private static readonly Lazy<Configuration> Lazy = new Lazy<Configuration>(CreateDefaultInstance); |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Configuration" /> class.
|
|||
/// </summary>
|
|||
public Configuration() |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Configuration" /> class.
|
|||
/// </summary>
|
|||
/// <param name="configurationModules">A collection of configuration modules to register</param>
|
|||
public Configuration(params IConfigurationModule[] configurationModules) |
|||
{ |
|||
if (configurationModules != null) |
|||
{ |
|||
foreach (IConfigurationModule p in configurationModules) |
|||
{ |
|||
p.Configure(this); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the default <see cref="Configuration"/> instance.
|
|||
/// </summary>
|
|||
public static Configuration Default { get; } = Lazy.Value; |
|||
|
|||
/// <summary>
|
|||
/// Gets the global parallel options for processing tasks in parallel.
|
|||
/// </summary>
|
|||
public ParallelOptions ParallelOptions { get; private set; } = new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount }; |
|||
|
|||
/// <summary>
|
|||
/// Gets the currently registered <see cref="IImageFormat"/>s.
|
|||
/// </summary>
|
|||
public IEnumerable<IImageFormat> ImageFormats => this.ImageFormatsManager.ImageFormats; |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the position in a stream to use for reading when using a seekable stream as an image data source.
|
|||
/// </summary>
|
|||
public ReadOrigin ReadOrigin { get; set; } = ReadOrigin.Current; |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the <see cref="ImageFormatManager"/> that is currently in use.
|
|||
/// </summary>
|
|||
public ImageFormatManager ImageFormatsManager { get; set; } = new ImageFormatManager(); |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the <see cref="MemoryManager"/> that is currently in use.
|
|||
/// </summary>
|
|||
public MemoryManager MemoryManager { get; set; } = ArrayPoolMemoryManager.CreateDefault(); |
|||
|
|||
/// <summary>
|
|||
/// Gets the maximum header size of all the formats.
|
|||
/// </summary>
|
|||
internal int MaxHeaderSize => this.ImageFormatsManager.MaxHeaderSize; |
|||
|
|||
#if !NETSTANDARD1_1
|
|||
/// <summary>
|
|||
/// Gets or sets the filesystem helper for accessing the local file system.
|
|||
/// </summary>
|
|||
internal IFileSystem FileSystem { get; set; } = new LocalFileSystem(); |
|||
#endif
|
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the image operations provider factory.
|
|||
/// </summary>
|
|||
internal IImageProcessingContextFactory ImageOperationsProvider { get; set; } = new DefaultImageOperationsProviderFactory(); |
|||
|
|||
/// <summary>
|
|||
/// Registers a new format provider.
|
|||
/// </summary>
|
|||
/// <param name="configuration">The configuration provider to call configure on.</param>
|
|||
public void Configure(IConfigurationModule configuration) |
|||
{ |
|||
Guard.NotNull(configuration, nameof(configuration)); |
|||
configuration.Configure(this); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Creates a shallow copy of the <see cref="Configuration"/>
|
|||
/// </summary>
|
|||
/// <returns>A new configuration instance</returns>
|
|||
public Configuration ShallowCopy() |
|||
{ |
|||
return new Configuration |
|||
{ |
|||
ParallelOptions = this.ParallelOptions, |
|||
ImageFormatsManager = this.ImageFormatsManager, |
|||
MemoryManager = this.MemoryManager, |
|||
ImageOperationsProvider = this.ImageOperationsProvider, |
|||
ReadOrigin = this.ReadOrigin, |
|||
|
|||
#if !NETSTANDARD1_1
|
|||
FileSystem = this.FileSystem |
|||
#endif
|
|||
}; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Creates the default instance with the following <see cref="IConfigurationModule"/>s preregistered:
|
|||
/// <para><see cref="PngConfigurationModule"/></para>
|
|||
/// <para><see cref="JpegConfigurationModule"/></para>
|
|||
/// <para><see cref="GifConfigurationModule"/></para>
|
|||
/// <para><see cref="BmpConfigurationModule"/></para>
|
|||
/// </summary>
|
|||
/// <returns>The default configuration of <see cref="Configuration"/></returns>
|
|||
internal static Configuration CreateDefaultInstance() |
|||
{ |
|||
return new Configuration( |
|||
new PngConfigurationModule(), |
|||
new JpegConfigurationModule(), |
|||
new GifConfigurationModule(), |
|||
new BmpConfigurationModule()); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,56 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
namespace SixLabors.ImageSharp.Dithering |
|||
{ |
|||
/// <summary>
|
|||
/// Contains reusable static instances of known error diffusion algorithms
|
|||
/// </summary>
|
|||
public static class KnownDiffusers |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the error diffuser that implements the Atkinson algorithm.
|
|||
/// </summary>
|
|||
public static IErrorDiffuser Atkinson { get; } = new AtkinsonDiffuser(); |
|||
|
|||
/// <summary>
|
|||
/// Gets the error diffuser that implements the Burks algorithm.
|
|||
/// </summary>
|
|||
public static IErrorDiffuser Burks { get; } = new BurksDiffuser(); |
|||
|
|||
/// <summary>
|
|||
/// Gets the error diffuser that implements the Floyd-Steinberg algorithm.
|
|||
/// </summary>
|
|||
public static IErrorDiffuser FloydSteinberg { get; } = new FloydSteinbergDiffuser(); |
|||
|
|||
/// <summary>
|
|||
/// Gets the error diffuser that implements the Jarvis-Judice-Ninke algorithm.
|
|||
/// </summary>
|
|||
public static IErrorDiffuser JarvisJudiceNinke { get; } = new JarvisJudiceNinkeDiffuser(); |
|||
|
|||
/// <summary>
|
|||
/// Gets the error diffuser that implements the Sierra-2 algorithm.
|
|||
/// </summary>
|
|||
public static IErrorDiffuser Sierra2 { get; } = new Sierra2Diffuser(); |
|||
|
|||
/// <summary>
|
|||
/// Gets the error diffuser that implements the Sierra-3 algorithm.
|
|||
/// </summary>
|
|||
public static IErrorDiffuser Sierra3 { get; } = new Sierra3Diffuser(); |
|||
|
|||
/// <summary>
|
|||
/// Gets the error diffuser that implements the Sierra-Lite algorithm.
|
|||
/// </summary>
|
|||
public static IErrorDiffuser SierraLite { get; } = new SierraLiteDiffuser(); |
|||
|
|||
/// <summary>
|
|||
/// Gets the error diffuser that implements the Stevenson-Arce algorithm.
|
|||
/// </summary>
|
|||
public static IErrorDiffuser StevensonArce { get; } = new StevensonArceDiffuser(); |
|||
|
|||
/// <summary>
|
|||
/// Gets the error diffuser that implements the Stucki algorithm.
|
|||
/// </summary>
|
|||
public static IErrorDiffuser Stucki { get; } = new StuckiDiffuser(); |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using SixLabors.ImageSharp.Dithering.Base; |
|||
using SixLabors.ImageSharp.Memory; |
|||
|
|||
namespace SixLabors.ImageSharp.Dithering |
|||
{ |
|||
/// <summary>
|
|||
/// Applies error diffusion based dithering using the Stevenson-Arce image dithering algorithm.
|
|||
/// </summary>
|
|||
public sealed class StevensonArceDiffuser : ErrorDiffuserBase |
|||
{ |
|||
/// <summary>
|
|||
/// The diffusion matrix
|
|||
/// </summary>
|
|||
private static readonly Fast2DArray<float> StevensonArceMatrix = |
|||
new float[,] |
|||
{ |
|||
{ 0, 0, 0, 0, 0, 32, 0 }, |
|||
{ 12, 0, 26, 0, 30, 0, 16 }, |
|||
{ 0, 12, 0, 26, 0, 12, 0 }, |
|||
{ 5, 0, 12, 0, 12, 0, 5 } |
|||
}; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="StevensonArceDiffuser"/> class.
|
|||
/// </summary>
|
|||
public StevensonArceDiffuser() |
|||
: base(StevensonArceMatrix, 200) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -1,36 +0,0 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using SixLabors.ImageSharp.Dithering.Base; |
|||
using SixLabors.ImageSharp.Memory; |
|||
|
|||
namespace SixLabors.ImageSharp.Dithering |
|||
{ |
|||
/// <summary>
|
|||
/// Applies error diffusion based dithering using the 4x4 Bayer dithering matrix.
|
|||
/// <see href="http://www.efg2.com/Lab/Library/ImageProcessing/DHALF.TXT"/>
|
|||
/// </summary>
|
|||
public sealed class BayerDither : OrderedDitherBase |
|||
{ |
|||
/// <summary>
|
|||
/// The threshold matrix.
|
|||
/// This is calculated by multiplying each value in the original matrix by 16 and subtracting 1
|
|||
/// </summary>
|
|||
private static readonly Fast2DArray<byte> ThresholdMatrix = |
|||
new byte[,] |
|||
{ |
|||
{ 15, 143, 47, 175 }, |
|||
{ 207, 79, 239, 111 }, |
|||
{ 63, 191, 31, 159 }, |
|||
{ 255, 127, 223, 95 } |
|||
}; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="BayerDither"/> class.
|
|||
/// </summary>
|
|||
public BayerDither() |
|||
: base(ThresholdMatrix) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
namespace SixLabors.ImageSharp.Dithering |
|||
{ |
|||
/// <summary>
|
|||
/// Applies order dithering using the 2x2 Bayer dithering matrix.
|
|||
/// </summary>
|
|||
public sealed class BayerDither2x2 : OrderedDither |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="BayerDither2x2"/> class.
|
|||
/// </summary>
|
|||
public BayerDither2x2() |
|||
: base(2) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
namespace SixLabors.ImageSharp.Dithering |
|||
{ |
|||
/// <summary>
|
|||
/// Applies order dithering using the 4x4 Bayer dithering matrix.
|
|||
/// </summary>
|
|||
public sealed class BayerDither4x4 : OrderedDither |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="BayerDither4x4"/> class.
|
|||
/// </summary>
|
|||
public BayerDither4x4() |
|||
: base(4) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
namespace SixLabors.ImageSharp.Dithering |
|||
{ |
|||
/// <summary>
|
|||
/// Applies order dithering using the 8x8 Bayer dithering matrix.
|
|||
/// </summary>
|
|||
public sealed class BayerDither8x8 : OrderedDither |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="BayerDither8x8"/> class.
|
|||
/// </summary>
|
|||
public BayerDither8x8() |
|||
: base(8) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
namespace SixLabors.ImageSharp.Dithering |
|||
{ |
|||
/// <summary>
|
|||
/// Contains reusable static instances of known ordered dither matrices
|
|||
/// </summary>
|
|||
public class KnownDitherers |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the order ditherer using the 2x2 Bayer dithering matrix
|
|||
/// </summary>
|
|||
public static IOrderedDither BayerDither2x2 { get; } = new BayerDither2x2(); |
|||
|
|||
/// <summary>
|
|||
/// Gets the order ditherer using the 3x3 dithering matrix
|
|||
/// </summary>
|
|||
public static IOrderedDither OrderedDither3x3 { get; } = new OrderedDither3x3(); |
|||
|
|||
/// <summary>
|
|||
/// Gets the order ditherer using the 4x4 Bayer dithering matrix
|
|||
/// </summary>
|
|||
public static IOrderedDither BayerDither4x4 { get; } = new BayerDither4x4(); |
|||
|
|||
/// <summary>
|
|||
/// Gets the order ditherer using the 8x8 Bayer dithering matrix
|
|||
/// </summary>
|
|||
public static IOrderedDither BayerDither8x8 { get; } = new BayerDither8x8(); |
|||
} |
|||
} |
|||
@ -1,36 +1,50 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using SixLabors.ImageSharp.Dithering.Base; |
|||
using SixLabors.ImageSharp.Memory; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
namespace SixLabors.ImageSharp.Dithering |
|||
{ |
|||
/// <summary>
|
|||
/// Applies error diffusion based dithering using the 4x4 ordered dithering matrix.
|
|||
/// <see href="https://en.wikipedia.org/wiki/Ordered_dithering"/>
|
|||
/// An ordered dithering matrix with equal sides of arbitrary length
|
|||
/// </summary>
|
|||
public sealed class OrderedDither : OrderedDitherBase |
|||
public class OrderedDither : IOrderedDither |
|||
{ |
|||
/// <summary>
|
|||
/// The threshold matrix.
|
|||
/// This is calculated by multiplying each value in the original matrix by 16
|
|||
/// </summary>
|
|||
private static readonly Fast2DArray<byte> ThresholdMatrix = |
|||
new byte[,] |
|||
{ |
|||
{ 0, 128, 32, 160 }, |
|||
{ 192, 64, 224, 96 }, |
|||
{ 48, 176, 16, 144 }, |
|||
{ 240, 112, 208, 80 } |
|||
}; |
|||
private readonly Fast2DArray<uint> thresholdMatrix; |
|||
private readonly int modulusX; |
|||
private readonly int modulusY; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="OrderedDither"/> class.
|
|||
/// </summary>
|
|||
public OrderedDither() |
|||
: base(ThresholdMatrix) |
|||
/// <param name="length">The length of the matrix sides</param>
|
|||
public OrderedDither(uint length) |
|||
{ |
|||
Fast2DArray<uint> ditherMatrix = OrderedDitherFactory.CreateDitherMatrix(length); |
|||
this.modulusX = ditherMatrix.Width; |
|||
this.modulusY = ditherMatrix.Height; |
|||
|
|||
// Adjust the matrix range for 0-255
|
|||
// It looks like it's actually possible to dither an image using it's own colors. We should investigate for V2
|
|||
// https://stackoverflow.com/questions/12422407/monochrome-dithering-in-javascript-bayer-atkinson-floyd-steinberg
|
|||
int multiplier = 256 / ditherMatrix.Count; |
|||
for (int y = 0; y < ditherMatrix.Height; y++) |
|||
{ |
|||
for (int x = 0; x < ditherMatrix.Width; x++) |
|||
{ |
|||
ditherMatrix[y, x] = (uint)((ditherMatrix[y, x] + 1) * multiplier) - 1; |
|||
} |
|||
} |
|||
|
|||
this.thresholdMatrix = ditherMatrix; |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
public void Dither<TPixel>(ImageFrame<TPixel> image, TPixel source, TPixel upper, TPixel lower, float threshold, int x, int y) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
image[x, y] = this.thresholdMatrix[y % this.modulusY, x % this.modulusX] >= threshold ? lower : upper; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
namespace SixLabors.ImageSharp.Dithering |
|||
{ |
|||
/// <summary>
|
|||
/// Applies order dithering using the 3x3 dithering matrix.
|
|||
/// </summary>
|
|||
public sealed class OrderedDither3x3 : OrderedDither |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="OrderedDither3x3"/> class.
|
|||
/// </summary>
|
|||
public OrderedDither3x3() |
|||
: base(3) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -1,53 +0,0 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using SixLabors.ImageSharp.Memory; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
namespace SixLabors.ImageSharp.Dithering.Base |
|||
{ |
|||
/// <summary>
|
|||
/// The base class for performing ordered dithering using a 4x4 matrix.
|
|||
/// </summary>
|
|||
public abstract class OrderedDitherBase : IOrderedDither |
|||
{ |
|||
/// <summary>
|
|||
/// The dithering matrix
|
|||
/// </summary>
|
|||
private Fast2DArray<byte> matrix; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="OrderedDitherBase"/> class.
|
|||
/// </summary>
|
|||
/// <param name="matrix">The thresholding matrix. </param>
|
|||
internal OrderedDitherBase(Fast2DArray<byte> matrix) |
|||
{ |
|||
this.matrix = matrix; |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
public void Dither<TPixel>(ImageFrame<TPixel> image, TPixel source, TPixel upper, TPixel lower, ref Rgba32 rgba, int index, int x, int y) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
source.ToRgba32(ref rgba); |
|||
switch (index) |
|||
{ |
|||
case 0: |
|||
image[x, y] = this.matrix[y % 3, x % 3] >= rgba.R ? lower : upper; |
|||
return; |
|||
case 1: |
|||
image[x, y] = this.matrix[y % 3, x % 3] >= rgba.G ? lower : upper; |
|||
return; |
|||
case 2: |
|||
image[x, y] = this.matrix[y % 3, x % 3] >= rgba.B ? lower : upper; |
|||
return; |
|||
case 3: |
|||
image[x, y] = this.matrix[y % 3, x % 3] >= rgba.A ? lower : upper; |
|||
return; |
|||
} |
|||
|
|||
throw new ArgumentOutOfRangeException(nameof(index), "Index should be between 0 and 3 inclusive."); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,94 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System.Runtime.CompilerServices; |
|||
using SixLabors.ImageSharp.Memory; |
|||
|
|||
namespace SixLabors.ImageSharp.Dithering |
|||
{ |
|||
/// <summary>
|
|||
/// A factory for creating ordered dither matrices.
|
|||
/// </summary>
|
|||
internal static class OrderedDitherFactory |
|||
{ |
|||
/// <summary>
|
|||
/// Creates an ordered dithering matrix with equal sides of arbitrary length.
|
|||
/// <see href="https://en.wikipedia.org/wiki/Ordered_dithering"/>
|
|||
/// </summary>
|
|||
/// <param name="length">The length of the matrix sides</param>
|
|||
/// <returns>The <see cref="Fast2DArray{T}"/></returns>
|
|||
public static Fast2DArray<uint> CreateDitherMatrix(uint length) |
|||
{ |
|||
// Calculate the the logarithm of length to the base 2
|
|||
uint exponent = 0; |
|||
uint bayerLength = 0; |
|||
do |
|||
{ |
|||
exponent++; |
|||
bayerLength = (uint)(1 << (int)exponent); |
|||
} |
|||
while (length > bayerLength); |
|||
|
|||
// Create our Bayer matrix that matches the given exponent and dimensions
|
|||
var matrix = new Fast2DArray<uint>((int)length); |
|||
uint i = 0; |
|||
for (int y = 0; y < length; y++) |
|||
{ |
|||
for (int x = 0; x < length; x++) |
|||
{ |
|||
matrix[y, x] = Bayer(i / length, i % length, exponent); |
|||
i++; |
|||
} |
|||
} |
|||
|
|||
// If the user requested a matrix with a non-power-of-2 length e.g. 3x3 and we used 4x4 algorithm,
|
|||
// we need to convert the numbers so that the resulting range is un-gapped.
|
|||
// We generated: We saved: We compress the number range:
|
|||
// 0 8 2 10 0 8 2 0 5 2
|
|||
// 12 4 14 6 12 4 14 7 4 8
|
|||
// 3 11 1 9 3 11 1 3 6 1
|
|||
// 15 7 13 5
|
|||
uint maxValue = bayerLength * bayerLength; |
|||
uint missing = 0; |
|||
for (uint v = 0; v < maxValue; ++v) |
|||
{ |
|||
bool found = false; |
|||
for (int y = 0; y < length; ++y) |
|||
{ |
|||
for (int x = 0; x < length; x++) |
|||
{ |
|||
if (matrix[y, x] == v) |
|||
{ |
|||
matrix[y, x] -= missing; |
|||
found = true; |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (!found) |
|||
{ |
|||
++missing; |
|||
} |
|||
} |
|||
|
|||
return matrix; |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private static uint Bayer(uint x, uint y, uint order) |
|||
{ |
|||
uint result = 0; |
|||
for (uint i = 0; i < order; ++i) |
|||
{ |
|||
uint xOdd_XOR_yOdd = (x & 1) ^ (y & 1); |
|||
uint xOdd = x & 1; |
|||
result = ((result << 1 | xOdd_XOR_yOdd) << 1) | xOdd; |
|||
x >>= 1; |
|||
y >>= 1; |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,58 @@ |
|||
List of error diffusion schemes. |
|||
|
|||
Quantization error of *current* pixel is added to the pixels |
|||
on the right and below according to the formulas below. |
|||
This works nicely for most static pictures, but causes |
|||
an avalanche of jittering artifacts if used in animation. |
|||
|
|||
Floyd-Steinberg: |
|||
|
|||
* 7 |
|||
3 5 1 / 16 |
|||
|
|||
Jarvis-Judice-Ninke: |
|||
|
|||
* 7 5 |
|||
3 5 7 5 3 |
|||
1 3 5 3 1 / 48 |
|||
|
|||
Stucki: |
|||
|
|||
* 8 4 |
|||
2 4 8 4 2 |
|||
1 2 4 2 1 / 42 |
|||
|
|||
Burkes: |
|||
|
|||
* 8 4 |
|||
2 4 8 4 2 / 32 |
|||
|
|||
|
|||
Sierra3: |
|||
|
|||
* 5 3 |
|||
2 4 5 4 2 |
|||
2 3 2 / 32 |
|||
|
|||
Sierra2: |
|||
|
|||
* 4 3 |
|||
1 2 3 2 1 / 16 |
|||
|
|||
Sierra-2-4A: |
|||
|
|||
* 2 |
|||
1 1 / 4 |
|||
|
|||
Stevenson-Arce: |
|||
|
|||
* . 32 |
|||
12 . 26 . 30 . 16 |
|||
. 12 . 26 . 12 . |
|||
5 . 12 . 12 . 5 / 200 |
|||
|
|||
Atkinson: |
|||
|
|||
* 1 1 / 8 |
|||
1 1 1 |
|||
1 |
|||
@ -0,0 +1,186 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Collections.Concurrent; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats |
|||
{ |
|||
/// <summary>
|
|||
/// Collection of Image Formats to be used in <see cref="Configuration" /> class.
|
|||
/// </summary>
|
|||
public class ImageFormatManager |
|||
{ |
|||
/// <summary>
|
|||
/// The list of supported <see cref="IImageEncoder"/> keyed to mime types.
|
|||
/// </summary>
|
|||
private readonly ConcurrentDictionary<IImageFormat, IImageEncoder> mimeTypeEncoders = new ConcurrentDictionary<IImageFormat, IImageEncoder>(); |
|||
|
|||
/// <summary>
|
|||
/// The list of supported <see cref="IImageEncoder"/> keyed to mime types.
|
|||
/// </summary>
|
|||
private readonly ConcurrentDictionary<IImageFormat, IImageDecoder> mimeTypeDecoders = new ConcurrentDictionary<IImageFormat, IImageDecoder>(); |
|||
|
|||
/// <summary>
|
|||
/// The list of supported <see cref="IImageFormat"/>s.
|
|||
/// </summary>
|
|||
private readonly ConcurrentBag<IImageFormat> imageFormats = new ConcurrentBag<IImageFormat>(); |
|||
|
|||
/// <summary>
|
|||
/// The list of supported <see cref="IImageFormatDetector"/>s.
|
|||
/// </summary>
|
|||
private ConcurrentBag<IImageFormatDetector> imageFormatDetectors = new ConcurrentBag<IImageFormatDetector>(); |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="ImageFormatManager" /> class.
|
|||
/// </summary>
|
|||
public ImageFormatManager() |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the maximum header size of all the formats.
|
|||
/// </summary>
|
|||
internal int MaxHeaderSize { get; private set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the currently registered <see cref="IImageFormat"/>s.
|
|||
/// </summary>
|
|||
public IEnumerable<IImageFormat> ImageFormats => this.imageFormats; |
|||
|
|||
/// <summary>
|
|||
/// Gets the currently registered <see cref="IImageFormatDetector"/>s.
|
|||
/// </summary>
|
|||
internal IEnumerable<IImageFormatDetector> FormatDetectors => this.imageFormatDetectors; |
|||
|
|||
/// <summary>
|
|||
/// Gets the currently registered <see cref="IImageDecoder"/>s.
|
|||
/// </summary>
|
|||
internal IEnumerable<KeyValuePair<IImageFormat, IImageDecoder>> ImageDecoders => this.mimeTypeDecoders; |
|||
|
|||
/// <summary>
|
|||
/// Gets the currently registered <see cref="IImageEncoder"/>s.
|
|||
/// </summary>
|
|||
internal IEnumerable<KeyValuePair<IImageFormat, IImageEncoder>> ImageEncoders => this.mimeTypeEncoders; |
|||
|
|||
/// <summary>
|
|||
/// Registers a new format provider.
|
|||
/// </summary>
|
|||
/// <param name="format">The format to register as a known format.</param>
|
|||
public void AddImageFormat(IImageFormat format) |
|||
{ |
|||
Guard.NotNull(format, nameof(format)); |
|||
Guard.NotNull(format.MimeTypes, nameof(format.MimeTypes)); |
|||
Guard.NotNull(format.FileExtensions, nameof(format.FileExtensions)); |
|||
this.imageFormats.Add(format); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// For the specified file extensions type find the e <see cref="IImageFormat"/>.
|
|||
/// </summary>
|
|||
/// <param name="extension">The extension to discover</param>
|
|||
/// <returns>The <see cref="IImageFormat"/> if found otherwise null</returns>
|
|||
public IImageFormat FindFormatByFileExtension(string extension) |
|||
{ |
|||
return this.imageFormats.FirstOrDefault(x => x.FileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// For the specified mime type find the <see cref="IImageFormat"/>.
|
|||
/// </summary>
|
|||
/// <param name="mimeType">The mime-type to discover</param>
|
|||
/// <returns>The <see cref="IImageFormat"/> if found; otherwise null</returns>
|
|||
public IImageFormat FindFormatByMimeType(string mimeType) |
|||
{ |
|||
return this.imageFormats.FirstOrDefault(x => x.MimeTypes.Contains(mimeType, StringComparer.OrdinalIgnoreCase)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets a specific image encoder as the encoder for a specific image format.
|
|||
/// </summary>
|
|||
/// <param name="imageFormat">The image format to register the encoder for.</param>
|
|||
/// <param name="encoder">The encoder to use,</param>
|
|||
public void SetEncoder(IImageFormat imageFormat, IImageEncoder encoder) |
|||
{ |
|||
Guard.NotNull(imageFormat, nameof(imageFormat)); |
|||
Guard.NotNull(encoder, nameof(encoder)); |
|||
this.AddImageFormat(imageFormat); |
|||
this.mimeTypeEncoders.AddOrUpdate(imageFormat, encoder, (s, e) => encoder); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets a specific image decoder as the decoder for a specific image format.
|
|||
/// </summary>
|
|||
/// <param name="imageFormat">The image format to register the encoder for.</param>
|
|||
/// <param name="decoder">The decoder to use,</param>
|
|||
public void SetDecoder(IImageFormat imageFormat, IImageDecoder decoder) |
|||
{ |
|||
Guard.NotNull(imageFormat, nameof(imageFormat)); |
|||
Guard.NotNull(decoder, nameof(decoder)); |
|||
this.AddImageFormat(imageFormat); |
|||
this.mimeTypeDecoders.AddOrUpdate(imageFormat, decoder, (s, e) => decoder); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Removes all the registered image format detectors.
|
|||
/// </summary>
|
|||
public void ClearImageFormatDetectors() |
|||
{ |
|||
this.imageFormatDetectors = new ConcurrentBag<IImageFormatDetector>(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Adds a new detector for detecting mime types.
|
|||
/// </summary>
|
|||
/// <param name="detector">The detector to add</param>
|
|||
public void AddImageFormatDetector(IImageFormatDetector detector) |
|||
{ |
|||
Guard.NotNull(detector, nameof(detector)); |
|||
this.imageFormatDetectors.Add(detector); |
|||
this.SetMaxHeaderSize(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// For the specified mime type find the decoder.
|
|||
/// </summary>
|
|||
/// <param name="format">The format to discover</param>
|
|||
/// <returns>The <see cref="IImageDecoder"/> if found otherwise null</returns>
|
|||
public IImageDecoder FindDecoder(IImageFormat format) |
|||
{ |
|||
Guard.NotNull(format, nameof(format)); |
|||
if (this.mimeTypeDecoders.TryGetValue(format, out IImageDecoder decoder)) |
|||
{ |
|||
return decoder; |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// For the specified mime type find the encoder.
|
|||
/// </summary>
|
|||
/// <param name="format">The format to discover</param>
|
|||
/// <returns>The <see cref="IImageEncoder"/> if found otherwise null</returns>
|
|||
public IImageEncoder FindEncoder(IImageFormat format) |
|||
{ |
|||
Guard.NotNull(format, nameof(format)); |
|||
if (this.mimeTypeEncoders.TryGetValue(format, out IImageEncoder encoder)) |
|||
{ |
|||
return encoder; |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the max header size.
|
|||
/// </summary>
|
|||
private void SetMaxHeaderSize() |
|||
{ |
|||
this.MaxHeaderSize = this.imageFormatDetectors.Max(x => x.HeaderSize); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
|
|||
// <auto-generated />
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Common |
|||
{ |
|||
internal unsafe partial struct GenericBlock8x8<T> |
|||
{ |
|||
#pragma warning disable 169
|
|||
|
|||
// It's not allowed use fix-sized buffers with generics, need to place all the fields manually:
|
|||
private T _y0_x0, _y0_x1, _y0_x2, _y0_x3, _y0_x4, _y0_x5, _y0_x6, _y0_x7; |
|||
private T _y1_x0, _y1_x1, _y1_x2, _y1_x3, _y1_x4, _y1_x5, _y1_x6, _y1_x7; |
|||
private T _y2_x0, _y2_x1, _y2_x2, _y2_x3, _y2_x4, _y2_x5, _y2_x6, _y2_x7; |
|||
private T _y3_x0, _y3_x1, _y3_x2, _y3_x3, _y3_x4, _y3_x5, _y3_x6, _y3_x7; |
|||
private T _y4_x0, _y4_x1, _y4_x2, _y4_x3, _y4_x4, _y4_x5, _y4_x6, _y4_x7; |
|||
private T _y5_x0, _y5_x1, _y5_x2, _y5_x3, _y5_x4, _y5_x5, _y5_x6, _y5_x7; |
|||
private T _y6_x0, _y6_x1, _y6_x2, _y6_x3, _y6_x4, _y6_x5, _y6_x6, _y6_x7; |
|||
private T _y7_x0, _y7_x1, _y7_x2, _y7_x3, _y7_x4, _y7_x5, _y7_x6, _y7_x7; |
|||
|
|||
#pragma warning restore 169
|
|||
} |
|||
} |
|||
@ -0,0 +1,43 @@ |
|||
<# |
|||
// Copyright (c) Six Labors and contributors. |
|||
// Licensed under the Apache License, Version 2.0. |
|||
#> |
|||
<#@ 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" #> |
|||
// Copyright (c) Six Labors and contributors. |
|||
// Licensed under the Apache License, Version 2.0. |
|||
|
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
|
|||
// <auto-generated /> |
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Common |
|||
{ |
|||
internal unsafe partial struct GenericBlock8x8<T> |
|||
{ |
|||
#pragma warning disable 169 |
|||
|
|||
// It's not allowed use fix-sized buffers with generics, need to place all the fields manually: |
|||
<# |
|||
PushIndent(" "); |
|||
Write(" "); |
|||
for (int y = 0; y < 8; y++) |
|||
{ |
|||
Write("private T "); |
|||
for (int x = 0; x < 8; x++) |
|||
{ |
|||
Write($"_y{y}_x{x}"); |
|||
if (x < 7) Write(", "); |
|||
} |
|||
WriteLine(";"); |
|||
} |
|||
PopIndent(); |
|||
#> |
|||
|
|||
#pragma warning restore 169 |
|||
} |
|||
} |
|||
@ -0,0 +1,129 @@ |
|||
using System; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
using SixLabors.ImageSharp.Advanced; |
|||
using SixLabors.ImageSharp.Memory; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
using SixLabors.Primitives; |
|||
|
|||
// ReSharper disable InconsistentNaming
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Common |
|||
{ |
|||
/// <summary>
|
|||
/// A generic 8x8 block implementation, useful for manipulating custom 8x8 pixel data.
|
|||
/// </summary>
|
|||
[StructLayout(LayoutKind.Sequential)] |
|||
internal unsafe partial struct GenericBlock8x8<T> |
|||
where T : struct |
|||
{ |
|||
public const int Size = 64; |
|||
|
|||
public const int SizeInBytes = Size * 3; |
|||
|
|||
/// <summary>
|
|||
/// FOR TESTING ONLY!
|
|||
/// Gets or sets a <see cref="Rgb24"/> value at the given index
|
|||
/// </summary>
|
|||
/// <param name="idx">The index</param>
|
|||
/// <returns>The value</returns>
|
|||
public T this[int idx] |
|||
{ |
|||
get |
|||
{ |
|||
ref T selfRef = ref Unsafe.As<GenericBlock8x8<T>, T>(ref this); |
|||
return Unsafe.Add(ref selfRef, idx); |
|||
} |
|||
|
|||
set |
|||
{ |
|||
ref T selfRef = ref Unsafe.As<GenericBlock8x8<T>, T>(ref this); |
|||
Unsafe.Add(ref selfRef, idx) = value; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// FOR TESTING ONLY!
|
|||
/// Gets or sets a value in a row+coulumn of the 8x8 block
|
|||
/// </summary>
|
|||
/// <param name="x">The x position index in the row</param>
|
|||
/// <param name="y">The column index</param>
|
|||
/// <returns>The value</returns>
|
|||
public T this[int x, int y] |
|||
{ |
|||
get => this[(y * 8) + x]; |
|||
set => this[(y * 8) + x] = value; |
|||
} |
|||
|
|||
public void LoadAndStretchEdges<TPixel>(IPixelSource<TPixel> source, int sourceX, int sourceY) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
var buffer = source.PixelBuffer as Buffer2D<T>; |
|||
if (buffer == null) |
|||
{ |
|||
throw new InvalidOperationException("LoadAndStretchEdges<TPixels>() is only valid for TPixel == T !"); |
|||
} |
|||
|
|||
this.LoadAndStretchEdges(buffer, sourceX, sourceY); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Load a 8x8 region of an image into the block.
|
|||
/// The "outlying" area of the block will be stretched out with pixels on the right and bottom edge of the image.
|
|||
/// </summary>
|
|||
public void LoadAndStretchEdges(Buffer2D<T> source, int sourceX, int sourceY) |
|||
{ |
|||
int width = Math.Min(8, source.Width - sourceX); |
|||
int height = Math.Min(8, source.Height - sourceY); |
|||
|
|||
if (width <= 0 || height <= 0) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
uint byteWidth = (uint)width * (uint)Unsafe.SizeOf<T>(); |
|||
int remainderXCount = 8 - width; |
|||
|
|||
ref byte blockStart = ref Unsafe.As<GenericBlock8x8<T>, byte>(ref this); |
|||
ref byte imageStart = ref Unsafe.As<T, byte>( |
|||
ref Unsafe.Add(ref MemoryMarshal.GetReference(source.GetRowSpan(sourceY)), sourceX)); |
|||
|
|||
int blockRowSizeInBytes = 8 * Unsafe.SizeOf<T>(); |
|||
int imageRowSizeInBytes = source.Width * Unsafe.SizeOf<T>(); |
|||
|
|||
for (int y = 0; y < height; y++) |
|||
{ |
|||
ref byte s = ref Unsafe.Add(ref imageStart, y * imageRowSizeInBytes); |
|||
ref byte d = ref Unsafe.Add(ref blockStart, y * blockRowSizeInBytes); |
|||
|
|||
Unsafe.CopyBlock(ref d, ref s, byteWidth); |
|||
|
|||
ref T last = ref Unsafe.Add(ref Unsafe.As<byte, T>(ref d), width - 1); |
|||
|
|||
for (int x = 1; x <= remainderXCount; x++) |
|||
{ |
|||
Unsafe.Add(ref last, x) = last; |
|||
} |
|||
} |
|||
|
|||
int remainderYCount = 8 - height; |
|||
|
|||
if (remainderYCount == 0) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
ref byte lastRowStart = ref Unsafe.Add(ref blockStart, (height - 1) * blockRowSizeInBytes); |
|||
|
|||
for (int y = 1; y <= remainderYCount; y++) |
|||
{ |
|||
ref byte remStart = ref Unsafe.Add(ref lastRowStart, blockRowSizeInBytes * y); |
|||
Unsafe.CopyBlock(ref remStart, ref lastRowStart, (uint)blockRowSizeInBytes); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Only for on-stack instances!
|
|||
/// </summary>
|
|||
public Span<T> AsSpanUnsafe() => new Span<T>(Unsafe.AsPointer(ref this), Size); |
|||
} |
|||
} |
|||
@ -0,0 +1,82 @@ |
|||
using System; |
|||
using System.Runtime.CompilerServices; |
|||
using SixLabors.ImageSharp.Advanced; |
|||
using SixLabors.ImageSharp.Formats.Jpeg.Common; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder |
|||
{ |
|||
/// <summary>
|
|||
/// On-stack worker struct to efficiently encapsulate the TPixel -> Rgb24 -> YCbCr conversion chain of 8x8 pixel blocks.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel type to work on</typeparam>
|
|||
internal struct YCbCrForwardConverter<TPixel> |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
/// <summary>
|
|||
/// The Y component
|
|||
/// </summary>
|
|||
public Block8x8F Y; |
|||
|
|||
/// <summary>
|
|||
/// The Cb component
|
|||
/// </summary>
|
|||
public Block8x8F Cb; |
|||
|
|||
/// <summary>
|
|||
/// The Cr component
|
|||
/// </summary>
|
|||
public Block8x8F Cr; |
|||
|
|||
/// <summary>
|
|||
/// The color conversion tables
|
|||
/// </summary>
|
|||
private RgbToYCbCrTables colorTables; |
|||
|
|||
/// <summary>
|
|||
/// Temporal 8x8 block to hold TPixel data
|
|||
/// </summary>
|
|||
private GenericBlock8x8<TPixel> pixelBlock; |
|||
|
|||
/// <summary>
|
|||
/// Temporal RGB block
|
|||
/// </summary>
|
|||
private GenericBlock8x8<Rgb24> rgbBlock; |
|||
|
|||
public static YCbCrForwardConverter<TPixel> Create() |
|||
{ |
|||
var result = default(YCbCrForwardConverter<TPixel>); |
|||
result.colorTables = RgbToYCbCrTables.Create(); |
|||
return result; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Converts a 8x8 image area inside 'pixels' at position (x,y) placing the result members of the structure (<see cref="Y"/>, <see cref="Cb"/>, <see cref="Cr"/>)
|
|||
/// </summary>
|
|||
public void Convert(IPixelSource<TPixel> pixels, int x, int y) |
|||
{ |
|||
this.pixelBlock.LoadAndStretchEdges(pixels, x, y); |
|||
|
|||
Span<Rgb24> rgbSpan = this.rgbBlock.AsSpanUnsafe(); |
|||
PixelOperations<TPixel>.Instance.ToRgb24(this.pixelBlock.AsSpanUnsafe(), rgbSpan, 64); |
|||
|
|||
ref float yBlockStart = ref Unsafe.As<Block8x8F, float>(ref this.Y); |
|||
ref float cbBlockStart = ref Unsafe.As<Block8x8F, float>(ref this.Cb); |
|||
ref float crBlockStart = ref Unsafe.As<Block8x8F, float>(ref this.Cr); |
|||
ref Rgb24 rgbStart = ref rgbSpan[0]; |
|||
|
|||
for (int i = 0; i < 64; i++) |
|||
{ |
|||
ref Rgb24 c = ref Unsafe.Add(ref rgbStart, i); |
|||
|
|||
this.colorTables.ConvertPixelInto( |
|||
c.R, |
|||
c.G, |
|||
c.B, |
|||
ref Unsafe.Add(ref yBlockStart, i), |
|||
ref Unsafe.Add(ref cbBlockStart, i), |
|||
ref Unsafe.Add(ref crBlockStart, i)); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue