mirror of https://github.com/SixLabors/ImageSharp
committed by
GitHub
468 changed files with 11208 additions and 8843 deletions
@ -1,14 +0,0 @@ |
|||
<SolutionConfiguration> |
|||
<FileVersion>1</FileVersion> |
|||
<InferProjectReferencesUsingAssemblyNames>false</InferProjectReferencesUsingAssemblyNames> |
|||
<AllowParallelTestExecution>false</AllowParallelTestExecution> |
|||
<AllowTestsToRunInParallelWithThemselves>true</AllowTestsToRunInParallelWithThemselves> |
|||
<FrameworkUtilisationTypeForNUnit>UseDynamicAnalysis</FrameworkUtilisationTypeForNUnit> |
|||
<FrameworkUtilisationTypeForGallio>UseStaticAnalysis</FrameworkUtilisationTypeForGallio> |
|||
<FrameworkUtilisationTypeForMSpec>UseStaticAnalysis</FrameworkUtilisationTypeForMSpec> |
|||
<FrameworkUtilisationTypeForMSTest>UseStaticAnalysis</FrameworkUtilisationTypeForMSTest> |
|||
<FrameworkUtilisationTypeForXUnit2>UseDynamicAnalysis</FrameworkUtilisationTypeForXUnit2> |
|||
<NCrunchCacheStoragePath /> |
|||
<MetricsExclusionList> |
|||
</MetricsExclusionList> |
|||
</SolutionConfiguration> |
|||
@ -1,6 +0,0 @@ |
|||
<SolutionConfiguration> |
|||
<Settings> |
|||
<AllowParallelTestExecution>True</AllowParallelTestExecution> |
|||
<SolutionConfigured>True</SolutionConfigured> |
|||
</Settings> |
|||
</SolutionConfiguration> |
|||
@ -1,4 +0,0 @@ |
|||
#recipe Docs |
|||
Settings[Keys.Host] = "imagesharp.org"; |
|||
Settings[Keys.Title] = "Image Sharp"; |
|||
FileSystem.OutputPath = "./docs"; |
|||
@ -1,153 +0,0 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<packages> |
|||
<package id="Wyam.Docs" version="0.18.6" targetFramework="net462"> |
|||
<package id="AngleSharp" version="0.9.9" targetFramework="net462" /> |
|||
<package id="CommonServiceLocator" version="1.3" targetFramework="net462" /> |
|||
<package id="DotlessClientOnly" version="1.5.2" targetFramework="net462" /> |
|||
<package id="ManagedEsent" version="1.9.4" targetFramework="net462" /> |
|||
<package id="Markdig" version="0.10.6" targetFramework="net462" /> |
|||
<package id="Microsoft.CodeAnalysis.Analyzers" version="1.1.0" targetFramework="net462" /> |
|||
<package id="Microsoft.CodeAnalysis.Elfie" version="0.10.6" targetFramework="net462" /> |
|||
<package id="Microsoft.Composition" version="1.0.30" targetFramework="net462" /> |
|||
<package id="Microsoft.CSharp" version="4.3.0" targetFramework="net462" /> |
|||
<package id="Microsoft.DotNet.InternalAbstractions" version="1.0.0" targetFramework="net462" /> |
|||
<package id="Microsoft.DotNet.PlatformAbstractions" version="1.1.1" targetFramework="net462" /> |
|||
<package id="Microsoft.NETCore.Platforms" version="1.1.0" targetFramework="net462" /> |
|||
<package id="Microsoft.Win32.Primitives" version="4.3.0" targetFramework="net462" /> |
|||
<package id="Newtonsoft.Json" version="10.0.2" targetFramework="net462" /> |
|||
<package id="Microsoft.Extensions.DependencyModel" version="1.1.1" targetFramework="net462" /> |
|||
<package id="SharpScss" version="1.3.4" targetFramework="net462" /> |
|||
<package id="System.AppContext" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.Buffers" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.Collections" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.Collections.Concurrent" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.Collections.Immutable" version="1.3.1" targetFramework="net462" /> |
|||
<package id="System.ComponentModel" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.Console" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.Diagnostics.Contracts" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.Diagnostics.Debug" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.Diagnostics.DiagnosticSource" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.Diagnostics.FileVersionInfo" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.Diagnostics.StackTrace" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.Diagnostics.Tools" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.Diagnostics.Tracing" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.Dynamic.Runtime" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.Globalization" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.Globalization.Calendars" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.IO" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.IO.Compression" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.IO.Compression.ZipFile" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.IO.FileSystem.Primitives" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.IO.FileSystem" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.Linq" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.Linq.Expressions" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.Net.Primitives" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.Net.Sockets" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.ObjectModel" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.Reflection" version="4.3.0" targetFramework="net462" /> |
|||
<package id="ReflectionMagic" version="3.0.0" targetFramework="net462" /> |
|||
<package id="System.Reflection.Extensions" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.Reflection.Metadata" version="1.4.2" targetFramework="net462" /> |
|||
<package id="System.Reflection.Primitives" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.Resources.ResourceManager" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.Runtime" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.Runtime.CompilerServices.Unsafe" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.Runtime.Extensions" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.Runtime.Handles" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.Runtime.InteropServices" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.Runtime.InteropServices.RuntimeInformation" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.Runtime.Numerics" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.Security.Cryptography.Encoding" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.Security.Cryptography.Primitives" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.Security.Cryptography.Algorithms" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.Security.Cryptography.X509Certificates" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.Net.Http" version="4.3.1" targetFramework="net462" /> |
|||
<package id="System.Text.Encoding" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.Text.Encoding.CodePages" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.Text.Encoding.Extensions" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.Text.Encodings.Web" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.Text.RegularExpressions" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.Threading" version="4.3.0" targetFramework="net462" /> |
|||
<package id="ConcurrentHashSet" version="0.3.1" targetFramework="net462" /> |
|||
<package id="System.Threading.Tasks" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.Threading.Tasks.Parallel" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.Threading.Thread" version="4.3.0" targetFramework="net462" /> |
|||
<package id="Microsoft.Build.Framework" version="15.1.1012" targetFramework="net462" /> |
|||
<package id="Microsoft.Build" version="15.1.1012" targetFramework="net462" /> |
|||
<package id="Microsoft.Build.Utilities.Core" version="15.1.1012" targetFramework="net462" /> |
|||
<package id="Microsoft.Build.Tasks.Core" version="15.1.1012" targetFramework="net462" /> |
|||
<package id="System.Threading.Timer" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.ValueTuple" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.Xml.ReaderWriter" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.Xml.XDocument" version="4.3.0" targetFramework="net462" /> |
|||
<package id="NETStandard.Library" version="1.6.1" targetFramework="net462" /> |
|||
<package id="Microsoft.AspNetCore.Cryptography.Internal" version="1.1.1" targetFramework="net462" /> |
|||
<package id="Microsoft.AspNetCore.DataProtection.Abstractions" version="1.1.1" targetFramework="net462" /> |
|||
<package id="Microsoft.AspNetCore.Diagnostics.Abstractions" version="1.1.1" targetFramework="net462" /> |
|||
<package id="Microsoft.AspNetCore.Html.Abstractions" version="1.1.1" targetFramework="net462" /> |
|||
<package id="Microsoft.AspNetCore.JsonPatch" version="1.1.1" targetFramework="net462" /> |
|||
<package id="Microsoft.AspNetCore.Razor" version="1.1.1" targetFramework="net462" /> |
|||
<package id="Microsoft.AspNetCore.Razor.Runtime" version="1.1.1" targetFramework="net462" /> |
|||
<package id="Microsoft.Extensions.DependencyInjection.Abstractions" version="1.1.0" targetFramework="net462" /> |
|||
<package id="Microsoft.Extensions.DependencyInjection" version="1.1.0" targetFramework="net462" /> |
|||
<package id="Microsoft.Extensions.FileSystemGlobbing" version="1.1.0" targetFramework="net462" /> |
|||
<package id="Microsoft.Extensions.Localization.Abstractions" version="1.1.1" targetFramework="net462" /> |
|||
<package id="Microsoft.Extensions.Logging.Abstractions" version="1.1.1" targetFramework="net462" /> |
|||
<package id="Microsoft.Extensions.ObjectPool" version="1.1.0" targetFramework="net462" /> |
|||
<package id="Microsoft.Extensions.PlatformAbstractions" version="1.1.0" targetFramework="net462" /> |
|||
<package id="Microsoft.Extensions.Primitives" version="1.1.0" targetFramework="net462" /> |
|||
<package id="Microsoft.AspNetCore.Http.Features" version="1.1.1" targetFramework="net462" /> |
|||
<package id="Microsoft.AspNetCore.Http.Abstractions" version="1.1.1" targetFramework="net462" /> |
|||
<package id="Microsoft.AspNetCore.ResponseCaching.Abstractions" version="1.1.1" targetFramework="net462" /> |
|||
<package id="Microsoft.AspNetCore.Routing.Abstractions" version="1.1.1" targetFramework="net462" /> |
|||
<package id="Microsoft.Extensions.Caching.Abstractions" version="1.1.1" targetFramework="net462" /> |
|||
<package id="Microsoft.Extensions.Configuration.Abstractions" version="1.1.1" targetFramework="net462" /> |
|||
<package id="Microsoft.AspNetCore.Hosting.Server.Abstractions" version="1.1.1" targetFramework="net462" /> |
|||
<package id="Microsoft.Extensions.FileProviders.Abstractions" version="1.1.0" targetFramework="net462" /> |
|||
<package id="Microsoft.AspNetCore.Hosting.Abstractions" version="1.1.1" targetFramework="net462" /> |
|||
<package id="Microsoft.Extensions.FileProviders.Composite" version="1.1.0" targetFramework="net462" /> |
|||
<package id="Microsoft.Extensions.FileProviders.Physical" version="1.1.0" targetFramework="net462" /> |
|||
<package id="Microsoft.Extensions.Options" version="1.1.1" targetFramework="net462" /> |
|||
<package id="Microsoft.AspNetCore.Authorization" version="1.1.1" targetFramework="net462" /> |
|||
<package id="Microsoft.AspNetCore.DataProtection" version="1.1.1" targetFramework="net462" /> |
|||
<package id="Microsoft.Extensions.Caching.Memory" version="1.1.1" targetFramework="net462" /> |
|||
<package id="Microsoft.AspNetCore.Mvc.Razor.Host" version="1.1.2" targetFramework="net462" /> |
|||
<package id="Microsoft.Extensions.Localization" version="1.1.1" targetFramework="net462" /> |
|||
<package id="Microsoft.Extensions.WebEncoders" version="1.1.1" targetFramework="net462" /> |
|||
<package id="Microsoft.Net.Http.Headers" version="1.1.1" targetFramework="net462" /> |
|||
<package id="Microsoft.AspNetCore.Http.Extensions" version="1.1.1" targetFramework="net462" /> |
|||
<package id="Microsoft.AspNetCore.Mvc.Abstractions" version="1.1.2" targetFramework="net462" /> |
|||
<package id="Microsoft.AspNetCore.Routing" version="1.1.1" targetFramework="net462" /> |
|||
<package id="Microsoft.AspNetCore.WebUtilities" version="1.1.1" targetFramework="net462" /> |
|||
<package id="Microsoft.AspNetCore.Antiforgery" version="1.1.1" targetFramework="net462" /> |
|||
<package id="Microsoft.AspNetCore.Http" version="1.1.1" targetFramework="net462" /> |
|||
<package id="Microsoft.AspNetCore.Mvc.Core" version="1.1.2" targetFramework="net462" /> |
|||
<package id="Microsoft.AspNetCore.Mvc.DataAnnotations" version="1.1.2" targetFramework="net462" /> |
|||
<package id="Microsoft.AspNetCore.Mvc.Formatters.Json" version="1.1.2" targetFramework="net462" /> |
|||
<package id="Microsoft.AspNetCore.Mvc.ViewFeatures" version="1.1.2" targetFramework="net462" /> |
|||
<package id="System.Xml.XmlDocument" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.Xml.XPath" version="4.3.0" targetFramework="net462" /> |
|||
<package id="System.Xml.XPath.XDocument" version="4.3.0" targetFramework="net462" /> |
|||
<package id="Microsoft.CodeAnalysis.Common" version="2.0.0" targetFramework="net462" /> |
|||
<package id="Microsoft.CodeAnalysis.CSharp" version="2.0.0" targetFramework="net462" /> |
|||
<package id="Microsoft.AspNetCore.Mvc.Razor" version="1.1.2" targetFramework="net462" /> |
|||
<package id="Microsoft.CodeAnalysis.VisualBasic" version="2.0.0" targetFramework="net462" /> |
|||
<package id="Microsoft.CodeAnalysis.Workspaces.Common" version="2.0.0" targetFramework="net462" /> |
|||
<package id="Microsoft.CodeAnalysis.CSharp.Workspaces" version="2.0.0" targetFramework="net462" /> |
|||
<package id="Microsoft.CodeAnalysis.VisualBasic.Workspaces" version="2.0.0" targetFramework="net462" /> |
|||
<package id="Microsoft.CodeAnalysis" version="2.0.0" targetFramework="net462" /> |
|||
<package id="Wyam.CodeAnalysis" version="0.18.6" targetFramework="net462" /> |
|||
<package id="Wyam.Feeds" version="0.18.6" targetFramework="net462" /> |
|||
<package id="Wyam.Html" version="0.18.6" targetFramework="net462" /> |
|||
<package id="Wyam.Less" version="0.18.6" targetFramework="net462" /> |
|||
<package id="Wyam.Markdown" version="0.18.6" targetFramework="net462" /> |
|||
<package id="Wyam.Razor" version="0.18.6" targetFramework="net462" /> |
|||
<package id="Wyam.Sass" version="0.18.6" targetFramework="net462" /> |
|||
<package id="Wyam.SearchIndex" version="0.18.6" targetFramework="net462" /> |
|||
<package id="YamlDotNet" version="3.9.0" targetFramework="net462" /> |
|||
<package id="YamlDotNet.Dynamic" version="3.2.3" targetFramework="net462" /> |
|||
<package id="Wyam.Yaml" version="0.18.6" targetFramework="net462" /> |
|||
<package id="Wyam.Web" version="0.18.6" targetFramework="net462" /> |
|||
</package> |
|||
<package id="Wyam.Docs.Samson" version="0.18.6" targetFramework="net462" /> |
|||
</packages> |
|||
@ -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); |
|||
} |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue