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.
|
// Copyright (c) Six Labors and contributors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
using System; |
using System; |
||||
using System.Collections.Concurrent; |
using System.Collections.Concurrent; |
||||
using System.Collections.Generic; |
using System.Collections.Generic; |
||||
using System.Linq; |
using System.Linq; |
||||
using System.Threading.Tasks; |
using System.Threading.Tasks; |
||||
using SixLabors.ImageSharp.Formats; |
using SixLabors.ImageSharp.Formats; |
||||
using SixLabors.ImageSharp.Formats.Bmp; |
using SixLabors.ImageSharp.Formats.Bmp; |
||||
using SixLabors.ImageSharp.Formats.Gif; |
using SixLabors.ImageSharp.Formats.Gif; |
||||
using SixLabors.ImageSharp.Formats.Jpeg; |
using SixLabors.ImageSharp.Formats.Jpeg; |
||||
using SixLabors.ImageSharp.Formats.Png; |
using SixLabors.ImageSharp.Formats.Png; |
||||
using SixLabors.ImageSharp.IO; |
using SixLabors.ImageSharp.IO; |
||||
|
using SixLabors.ImageSharp.Memory; |
||||
namespace SixLabors.ImageSharp |
|
||||
{ |
namespace SixLabors.ImageSharp |
||||
/// <summary>
|
{ |
||||
/// Provides initialization code which allows extending the library.
|
/// <summary>
|
||||
/// </summary>
|
/// Provides initialization code which allows extending the library.
|
||||
public sealed class Configuration |
/// </summary>
|
||||
{ |
public sealed class Configuration |
||||
/// <summary>
|
{ |
||||
/// A lazily initialized configuration default instance.
|
/// <summary>
|
||||
/// </summary>
|
/// A lazily initialized configuration default instance.
|
||||
private static readonly Lazy<Configuration> Lazy = new Lazy<Configuration>(CreateDefaultInstance); |
/// </summary>
|
||||
|
private static readonly Lazy<Configuration> Lazy = new Lazy<Configuration>(CreateDefaultInstance); |
||||
/// <summary>
|
|
||||
/// The list of supported <see cref="IImageEncoder"/> keyed to mime types.
|
/// <summary>
|
||||
/// </summary>
|
/// Initializes a new instance of the <see cref="Configuration" /> class.
|
||||
private readonly ConcurrentDictionary<IImageFormat, IImageEncoder> mimeTypeEncoders = new ConcurrentDictionary<IImageFormat, IImageEncoder>(); |
/// </summary>
|
||||
|
public Configuration() |
||||
/// <summary>
|
{ |
||||
/// The list of supported <see cref="IImageEncoder"/> keyed to mime types.
|
} |
||||
/// </summary>
|
|
||||
private readonly ConcurrentDictionary<IImageFormat, IImageDecoder> mimeTypeDecoders = new ConcurrentDictionary<IImageFormat, IImageDecoder>(); |
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="Configuration" /> class.
|
||||
/// <summary>
|
/// </summary>
|
||||
/// The list of supported <see cref="IImageFormat"/>s.
|
/// <param name="configurationModules">A collection of configuration modules to register</param>
|
||||
/// </summary>
|
public Configuration(params IConfigurationModule[] configurationModules) |
||||
private readonly ConcurrentBag<IImageFormat> imageFormats = new ConcurrentBag<IImageFormat>(); |
{ |
||||
|
if (configurationModules != null) |
||||
/// <summary>
|
{ |
||||
/// The list of supported <see cref="IImageFormatDetector"/>s.
|
foreach (IConfigurationModule p in configurationModules) |
||||
/// </summary>
|
{ |
||||
private ConcurrentBag<IImageFormatDetector> imageFormatDetectors = new ConcurrentBag<IImageFormatDetector>(); |
p.Configure(this); |
||||
|
} |
||||
/// <summary>
|
} |
||||
/// Initializes a new instance of the <see cref="Configuration" /> class.
|
} |
||||
/// </summary>
|
|
||||
public Configuration() |
/// <summary>
|
||||
{ |
/// Gets the default <see cref="Configuration"/> instance.
|
||||
} |
/// </summary>
|
||||
|
public static Configuration Default { get; } = Lazy.Value; |
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the <see cref="Configuration" /> class.
|
/// <summary>
|
||||
/// </summary>
|
/// Gets the global parallel options for processing tasks in parallel.
|
||||
/// <param name="configurationModules">A collection of configuration modules to register</param>
|
/// </summary>
|
||||
public Configuration(params IConfigurationModule[] configurationModules) |
public ParallelOptions ParallelOptions { get; private set; } = new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount }; |
||||
{ |
|
||||
if (configurationModules != null) |
/// <summary>
|
||||
{ |
/// Gets the currently registered <see cref="IImageFormat"/>s.
|
||||
foreach (IConfigurationModule p in configurationModules) |
/// </summary>
|
||||
{ |
public IEnumerable<IImageFormat> ImageFormats => this.ImageFormatsManager.ImageFormats; |
||||
p.Configure(this); |
|
||||
} |
/// <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 the default <see cref="Configuration"/> instance.
|
/// <summary>
|
||||
/// </summary>
|
/// Gets or sets the <see cref="ImageFormatManager"/> that is currently in use.
|
||||
public static Configuration Default { get; } = Lazy.Value; |
/// </summary>
|
||||
|
public ImageFormatManager ImageFormatsManager { get; set; } = new ImageFormatManager(); |
||||
/// <summary>
|
|
||||
/// Gets the global parallel options for processing tasks in parallel.
|
/// <summary>
|
||||
/// </summary>
|
/// Gets or sets the <see cref="MemoryManager"/> that is currently in use.
|
||||
public ParallelOptions ParallelOptions { get; } = new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount }; |
/// </summary>
|
||||
|
public MemoryManager MemoryManager { get; set; } = ArrayPoolMemoryManager.CreateDefault(); |
||||
/// <summary>
|
|
||||
/// Gets the currently registered <see cref="IImageFormat"/>s.
|
/// <summary>
|
||||
/// </summary>
|
/// Gets the maximum header size of all the formats.
|
||||
public IEnumerable<IImageFormat> ImageFormats => this.imageFormats; |
/// </summary>
|
||||
|
internal int MaxHeaderSize => this.ImageFormatsManager.MaxHeaderSize; |
||||
/// <summary>
|
|
||||
/// Gets the maximum header size of all the formats.
|
#if !NETSTANDARD1_1
|
||||
/// </summary>
|
/// <summary>
|
||||
internal int MaxHeaderSize { get; private set; } |
/// Gets or sets the filesystem helper for accessing the local file system.
|
||||
|
/// </summary>
|
||||
/// <summary>
|
internal IFileSystem FileSystem { get; set; } = new LocalFileSystem(); |
||||
/// Gets the currently registered <see cref="IImageFormatDetector"/>s.
|
#endif
|
||||
/// </summary>
|
|
||||
internal IEnumerable<IImageFormatDetector> FormatDetectors => this.imageFormatDetectors; |
/// <summary>
|
||||
|
/// Gets or sets the image operations provider factory.
|
||||
/// <summary>
|
/// </summary>
|
||||
/// Gets the currently registered <see cref="IImageDecoder"/>s.
|
internal IImageProcessingContextFactory ImageOperationsProvider { get; set; } = new DefaultImageOperationsProviderFactory(); |
||||
/// </summary>
|
|
||||
internal IEnumerable<KeyValuePair<IImageFormat, IImageDecoder>> ImageDecoders => this.mimeTypeDecoders; |
/// <summary>
|
||||
|
/// Registers a new format provider.
|
||||
/// <summary>
|
/// </summary>
|
||||
/// Gets the currently registered <see cref="IImageEncoder"/>s.
|
/// <param name="configuration">The configuration provider to call configure on.</param>
|
||||
/// </summary>
|
public void Configure(IConfigurationModule configuration) |
||||
internal IEnumerable<KeyValuePair<IImageFormat, IImageEncoder>> ImageEncoders => this.mimeTypeEncoders; |
{ |
||||
|
Guard.NotNull(configuration, nameof(configuration)); |
||||
#if !NETSTANDARD1_1
|
configuration.Configure(this); |
||||
/// <summary>
|
} |
||||
/// Gets or sets the filesystem helper for accessing the local file system.
|
|
||||
/// </summary>
|
/// <summary>
|
||||
internal IFileSystem FileSystem { get; set; } = new LocalFileSystem(); |
/// Creates a shallow copy of the <see cref="Configuration"/>
|
||||
#endif
|
/// </summary>
|
||||
|
/// <returns>A new configuration instance</returns>
|
||||
/// <summary>
|
public Configuration ShallowCopy() |
||||
/// Gets or sets the image operations provider factory.
|
{ |
||||
/// </summary>
|
return new Configuration |
||||
internal IImageProcessingContextFactory ImageOperationsProvider { get; set; } = new DefaultImageOperationsProviderFactory(); |
{ |
||||
|
ParallelOptions = this.ParallelOptions, |
||||
/// <summary>
|
ImageFormatsManager = this.ImageFormatsManager, |
||||
/// Registers a new format provider.
|
MemoryManager = this.MemoryManager, |
||||
/// </summary>
|
ImageOperationsProvider = this.ImageOperationsProvider, |
||||
/// <param name="configuration">The configuration provider to call configure on.</param>
|
ReadOrigin = this.ReadOrigin, |
||||
public void Configure(IConfigurationModule configuration) |
|
||||
{ |
#if !NETSTANDARD1_1
|
||||
Guard.NotNull(configuration, nameof(configuration)); |
FileSystem = this.FileSystem |
||||
configuration.Configure(this); |
#endif
|
||||
} |
}; |
||||
|
} |
||||
/// <summary>
|
|
||||
/// Registers a new format provider.
|
/// <summary>
|
||||
/// </summary>
|
/// Creates the default instance with the following <see cref="IConfigurationModule"/>s preregistered:
|
||||
/// <param name="format">The format to register as a known format.</param>
|
/// <para><see cref="PngConfigurationModule"/></para>
|
||||
public void AddImageFormat(IImageFormat format) |
/// <para><see cref="JpegConfigurationModule"/></para>
|
||||
{ |
/// <para><see cref="GifConfigurationModule"/></para>
|
||||
Guard.NotNull(format, nameof(format)); |
/// <para><see cref="BmpConfigurationModule"/></para>
|
||||
Guard.NotNull(format.MimeTypes, nameof(format.MimeTypes)); |
/// </summary>
|
||||
Guard.NotNull(format.FileExtensions, nameof(format.FileExtensions)); |
/// <returns>The default configuration of <see cref="Configuration"/></returns>
|
||||
this.imageFormats.Add(format); |
internal static Configuration CreateDefaultInstance() |
||||
} |
{ |
||||
|
return new Configuration( |
||||
/// <summary>
|
new PngConfigurationModule(), |
||||
/// For the specified file extensions type find the e <see cref="IImageFormat"/>.
|
new JpegConfigurationModule(), |
||||
/// </summary>
|
new GifConfigurationModule(), |
||||
/// <param name="extension">The extension to discover</param>
|
new BmpConfigurationModule()); |
||||
/// <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); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -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.
|
// Copyright (c) Six Labors and contributors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
using SixLabors.ImageSharp.Dithering.Base; |
|
||||
using SixLabors.ImageSharp.Memory; |
using SixLabors.ImageSharp.Memory; |
||||
|
using SixLabors.ImageSharp.PixelFormats; |
||||
|
|
||||
namespace SixLabors.ImageSharp.Dithering |
namespace SixLabors.ImageSharp.Dithering |
||||
{ |
{ |
||||
/// <summary>
|
/// <summary>
|
||||
/// Applies error diffusion based dithering using the 4x4 ordered dithering matrix.
|
/// An ordered dithering matrix with equal sides of arbitrary length
|
||||
/// <see href="https://en.wikipedia.org/wiki/Ordered_dithering"/>
|
|
||||
/// </summary>
|
/// </summary>
|
||||
public sealed class OrderedDither : OrderedDitherBase |
public class OrderedDither : IOrderedDither |
||||
{ |
{ |
||||
/// <summary>
|
private readonly Fast2DArray<uint> thresholdMatrix; |
||||
/// The threshold matrix.
|
private readonly int modulusX; |
||||
/// This is calculated by multiplying each value in the original matrix by 16
|
private readonly int modulusY; |
||||
/// </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 } |
|
||||
}; |
|
||||
|
|
||||
/// <summary>
|
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="OrderedDither"/> class.
|
/// Initializes a new instance of the <see cref="OrderedDither"/> class.
|
||||
/// </summary>
|
/// </summary>
|
||||
public OrderedDither() |
/// <param name="length">The length of the matrix sides</param>
|
||||
: base(ThresholdMatrix) |
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