mirror of https://github.com/SixLabors/ImageSharp
Browse Source
# Conflicts: # tests/ImageSharp.Tests/Processing/Binarization/BinaryThresholdTest.cs # tests/ImageSharp.Tests/Processing/Binarization/DitherTest.cs # tests/ImageSharp.Tests/Processing/ColorMatrix/BlackWhiteTest.cs # tests/ImageSharp.Tests/Processing/ColorMatrix/ColorBlindnessTest.cs # tests/ImageSharp.Tests/Processing/ColorMatrix/HueTest.cs # tests/ImageSharp.Tests/Processing/ColorMatrix/KodachromeTest.cs # tests/ImageSharp.Tests/Processing/ColorMatrix/LomographTest.cs # tests/ImageSharp.Tests/Processing/ColorMatrix/PolaroidTest.cs # tests/ImageSharp.Tests/Processing/ColorMatrix/SaturationTest.cs # tests/ImageSharp.Tests/Processing/ColorMatrix/SepiaTest.cs # tests/ImageSharp.Tests/Processing/Convolution/BoxBlurTest.cs # tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs # tests/ImageSharp.Tests/Processing/Convolution/GaussianBlurTest.cs # tests/ImageSharp.Tests/Processing/Convolution/GaussianSharpenTest.cs # tests/ImageSharp.Tests/Processing/Effects/AlphaTest.cs # tests/ImageSharp.Tests/Processing/Effects/BackgroundColorTest.cs # tests/ImageSharp.Tests/Processing/Effects/BrightnessTest.cs # tests/ImageSharp.Tests/Processing/Effects/ContrastTest.cs # tests/ImageSharp.Tests/Processing/Effects/InvertTest.cs # tests/ImageSharp.Tests/Processing/Effects/OilPaintTest.cs # tests/ImageSharp.Tests/Processing/Effects/PixelateTest.cs # tests/ImageSharp.Tests/Processing/Overlays/GlowTest.cs # tests/ImageSharp.Tests/Processing/Overlays/VignetteTest.cs # tests/ImageSharp.Tests/Processing/Transforms/AutoOrientTests.cs # tests/ImageSharp.Tests/Processing/Transforms/CropTest.cs # tests/ImageSharp.Tests/Processing/Transforms/EntropyCropTest.cs # tests/ImageSharp.Tests/Processing/Transforms/FlipTests.cs # tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs # tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs # tests/ImageSharp.Tests/Processing/Transforms/RotateFlipTests.cs # tests/ImageSharp.Tests/Processing/Transforms/RotateTests.cs # tests/ImageSharp.Tests/Processing/Transforms/SkewTest.cs # tests/ImageSharp.Tests/TestFile.cspull/298/head
218 changed files with 5243 additions and 2856 deletions
@ -0,0 +1,107 @@ |
|||
// <copyright file="ApplyProcessors.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp |
|||
{ |
|||
using System; |
|||
|
|||
using ImageSharp.PixelFormats; |
|||
|
|||
using ImageSharp.Processing; |
|||
|
|||
/// <summary>
|
|||
/// Extension methods for the <see cref="Image{TPixel}"/> type.
|
|||
/// </summary>
|
|||
public static partial class ImageExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// Mutates the image by applying the image operation to it.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|||
/// <param name="source">The image to rotate, flip, or both.</param>
|
|||
/// <param name="operation">The operations to perform on the source.</param>
|
|||
public static void Mutate<TPixel>(this Image<TPixel> source, Action<IImageProcessingContext<TPixel>> operation) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
Guard.NotNull(operation, nameof(operation)); |
|||
Guard.NotNull(source, nameof(source)); |
|||
|
|||
IInternalImageProcessingContext<TPixel> operationsRunner = source.Configuration.ImageOperationsProvider.CreateImageProcessingContext(source, true); |
|||
operation(operationsRunner); |
|||
operationsRunner.Apply(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Mutates the image by applying the operations to it.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|||
/// <param name="source">The image to rotate, flip, or both.</param>
|
|||
/// <param name="operations">The operations to perform on the source.</param>
|
|||
public static void Mutate<TPixel>(this Image<TPixel> source, params IImageProcessor<TPixel>[] operations) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
Guard.NotNull(operations, nameof(operations)); |
|||
Guard.NotNull(source, nameof(source)); |
|||
|
|||
IInternalImageProcessingContext<TPixel> operationsRunner = source.Configuration.ImageOperationsProvider.CreateImageProcessingContext(source, true); |
|||
operationsRunner.ApplyProcessors(operations); |
|||
operationsRunner.Apply(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Clones the current image mutating the clone by applying the operation to it.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|||
/// <param name="source">The image to rotate, flip, or both.</param>
|
|||
/// <param name="operation">The operations to perform on the source.</param>
|
|||
/// <returns>Anew Image which has teh data from the <paramref name="source"/> but with the <paramref name="operation"/> applied.</returns>
|
|||
public static Image<TPixel> Clone<TPixel>(this Image<TPixel> source, Action<IImageProcessingContext<TPixel>> operation) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
Guard.NotNull(operation, nameof(operation)); |
|||
Guard.NotNull(source, nameof(source)); |
|||
|
|||
IInternalImageProcessingContext<TPixel> operationsRunner = source.Configuration.ImageOperationsProvider.CreateImageProcessingContext(source, false); |
|||
operation(operationsRunner); |
|||
return operationsRunner.Apply(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Clones the current image mutating the clone by applying the operations to it.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|||
/// <param name="source">The image to rotate, flip, or both.</param>
|
|||
/// <param name="operations">The operations to perform on the source.</param>
|
|||
/// <returns>Anew Image which has teh data from the <paramref name="source"/> but with the <paramref name="operations"/> applied.</returns>
|
|||
public static Image<TPixel> Clone<TPixel>(this Image<TPixel> source, params IImageProcessor<TPixel>[] operations) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
Guard.NotNull(operations, nameof(operations)); |
|||
Guard.NotNull(source, nameof(source)); |
|||
|
|||
IInternalImageProcessingContext<TPixel> operationsRunner = source.Configuration.ImageOperationsProvider.CreateImageProcessingContext(source, false); |
|||
operationsRunner.ApplyProcessors(operations); |
|||
return operationsRunner.Apply(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Applies all the ImageProcessors agains the operation
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|||
/// <param name="source">The image to rotate, flip, or both.</param>
|
|||
/// <param name="operations">The operations to perform on the source.</param>
|
|||
/// <returns>returns the current operations class to allow chaining of operations.</returns>
|
|||
public static IImageProcessingContext<TPixel> ApplyProcessors<TPixel>(this IImageProcessingContext<TPixel> source, params IImageProcessor<TPixel>[] operations) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
foreach (IImageProcessor<TPixel> p in operations) |
|||
{ |
|||
source = source.ApplyProcessor(p); |
|||
} |
|||
|
|||
return source; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,78 @@ |
|||
// <copyright file="DefaultInternalImageProcessorApplicator.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp |
|||
{ |
|||
using ImageSharp.PixelFormats; |
|||
using ImageSharp.Processing; |
|||
using SixLabors.Primitives; |
|||
|
|||
/// <summary>
|
|||
/// Performs processor application operations on the source image
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel format</typeparam>
|
|||
internal class DefaultInternalImageProcessorContext<TPixel> : IInternalImageProcessingContext<TPixel> |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
private readonly bool mutate; |
|||
private readonly Image<TPixel> source; |
|||
private Image<TPixel> destination; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="DefaultInternalImageProcessorContext{TPixel}"/> class.
|
|||
/// </summary>
|
|||
/// <param name="source">The image.</param>
|
|||
/// <param name="mutate">The mutate.</param>
|
|||
public DefaultInternalImageProcessorContext(Image<TPixel> source, bool mutate) |
|||
{ |
|||
this.mutate = mutate; |
|||
this.source = source; |
|||
if (this.mutate) |
|||
{ |
|||
this.destination = source; |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public Image<TPixel> Apply() |
|||
{ |
|||
if (!this.mutate && this.destination == null) |
|||
{ |
|||
// Ensure we have cloned it if we are not mutating as we might have failed to register any Processors
|
|||
this.destination = this.source.Clone(); |
|||
} |
|||
|
|||
return this.destination; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public IImageProcessingContext<TPixel> ApplyProcessor(IImageProcessor<TPixel> processor, Rectangle rectangle) |
|||
{ |
|||
if (!this.mutate && this.destination == null) |
|||
{ |
|||
// This will only work if the first processor applied is the cloning one thus
|
|||
// realistically for this optermissation to work the resize must the first processor
|
|||
// applied any only up processors will take the douple data path.
|
|||
var cloningImageProcessor = processor as ICloningImageProcessor<TPixel>; |
|||
if (cloningImageProcessor != null) |
|||
{ |
|||
this.destination = cloningImageProcessor.CloneAndApply(this.source, rectangle); |
|||
return this; |
|||
} |
|||
|
|||
this.destination = this.source.Clone(); |
|||
} |
|||
|
|||
processor.Apply(this.destination, rectangle); |
|||
return this; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public IImageProcessingContext<TPixel> ApplyProcessor(IImageProcessor<TPixel> processor) |
|||
{ |
|||
return this.ApplyProcessor(processor, this.source.Bounds()); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,38 @@ |
|||
// <copyright file="IImageProcessorApplicatorFactory.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp |
|||
{ |
|||
using ImageSharp.PixelFormats; |
|||
|
|||
/// <summary>
|
|||
/// Represents an interface that will create IInternalImageProcessingContext instances
|
|||
/// </summary>
|
|||
internal interface IImageProcessingContextFactory |
|||
{ |
|||
/// <summary>
|
|||
/// Called during Mutate operations to generate the image operations provider.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel format</typeparam>
|
|||
/// <param name="source">The source image.</param>
|
|||
/// <param name="mutate">A flag to determin with the image operations is allowed to mutate the source image or not.</param>
|
|||
/// <returns>A new IImageOPeration</returns>
|
|||
IInternalImageProcessingContext<TPixel> CreateImageProcessingContext<TPixel>(Image<TPixel> source, bool mutate) |
|||
where TPixel : struct, IPixel<TPixel>; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// The default implmentation of IImageOperationsProvider
|
|||
/// </summary>
|
|||
internal class DefaultImageOperationsProvider : IImageProcessingContextFactory |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public IInternalImageProcessingContext<TPixel> CreateImageProcessingContext<TPixel>(Image<TPixel> source, bool mutate) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
return new DefaultInternalImageProcessorContext<TPixel>(source, mutate); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,48 @@ |
|||
// <copyright file="IImageProcessorApplicator.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp |
|||
{ |
|||
using ImageSharp.PixelFormats; |
|||
using ImageSharp.Processing; |
|||
using SixLabors.Primitives; |
|||
|
|||
/// <summary>
|
|||
/// An interface to queue up image operations.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel format</typeparam>
|
|||
public interface IImageProcessingContext<TPixel> |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
/// <summary>
|
|||
/// Adds the processor to the current set of image operations to be applied.
|
|||
/// </summary>
|
|||
/// <param name="processor">The processor to apply</param>
|
|||
/// <param name="rectangle">The area to apply it to</param>
|
|||
/// <returns>The current operations class to allow chaining of operations.</returns>
|
|||
IImageProcessingContext<TPixel> ApplyProcessor(IImageProcessor<TPixel> processor, Rectangle rectangle); |
|||
|
|||
/// <summary>
|
|||
/// Adds the processor to the current set of image operations to be applied.
|
|||
/// </summary>
|
|||
/// <param name="processor">The processor to apply</param>
|
|||
/// <returns>The current operations class to allow chaining of operations.</returns>
|
|||
IImageProcessingContext<TPixel> ApplyProcessor(IImageProcessor<TPixel> processor); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// An internal interface to queue up image operations and have a method to apply them to and return a result
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel format</typeparam>
|
|||
internal interface IInternalImageProcessingContext<TPixel> : IImageProcessingContext<TPixel> |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
/// <summary>
|
|||
/// Adds the processors to the current image
|
|||
/// </summary>
|
|||
/// <returns>The current image or a new image depending on withere this is alloed to mutate the source image.</returns>
|
|||
Image<TPixel> Apply(); |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
// <copyright file="ICloningImageProcessor.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Processing |
|||
{ |
|||
using ImageSharp.PixelFormats; |
|||
using SixLabors.Primitives; |
|||
|
|||
/// <summary>
|
|||
/// Encapsulates methods to alter the pixels of a new image, cloned from the original image.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|||
internal interface ICloningImageProcessor<TPixel> : IImageProcessor<TPixel> |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
/// <summary>
|
|||
/// Applies the process to the specified portion of the specified <see cref="ImageBase{TPixel}"/>.
|
|||
/// </summary>
|
|||
/// <param name="source">The source image. Cannot be null.</param>
|
|||
/// <param name="sourceRectangle">
|
|||
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
|
|||
/// </param>
|
|||
/// <exception cref="System.ArgumentNullException">
|
|||
/// <paramref name="source"/> is null.
|
|||
/// </exception>
|
|||
/// <exception cref="System.ArgumentException">
|
|||
/// <paramref name="sourceRectangle"/> doesnt fit the dimension of the image.
|
|||
/// </exception>
|
|||
/// <returns>Returns the cloned image after thre processor has been applied to it.</returns>
|
|||
Image<TPixel> CloneAndApply(Image<TPixel> source, Rectangle sourceRectangle); |
|||
} |
|||
} |
|||
@ -0,0 +1,153 @@ |
|||
// <copyright file="ImageExtensions.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp |
|||
{ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.IO; |
|||
using System.Text; |
|||
using ImageSharp.Formats; |
|||
using ImageSharp.PixelFormats; |
|||
using SixLabors.Primitives; |
|||
|
|||
/// <summary>
|
|||
/// Extension methods over Image{TPixel}
|
|||
/// </summary>
|
|||
public static partial class ImageExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the bounds of the image.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The Pixel format.</typeparam>
|
|||
/// <param name="source">The source image</param>
|
|||
/// <returns>Returns the bounds of the image</returns>
|
|||
public static Rectangle Bounds<TPixel>(this ImageBase<TPixel> source) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
=> new Rectangle(0, 0, source.Width, source.Height); |
|||
|
|||
/// <summary>
|
|||
/// Gets the size of the image.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The Pixel format.</typeparam>
|
|||
/// <param name="source">The source image</param>
|
|||
/// <returns>Returns the bounds of the image</returns>
|
|||
public static Size Size<TPixel>(this ImageBase<TPixel> source) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
=> new Size(source.Width, source.Height); |
|||
|
|||
#if !NETSTANDARD1_1
|
|||
/// <summary>
|
|||
/// Saves the image to the given stream using the currently loaded image format.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The Pixel format.</typeparam>
|
|||
/// <param name="source">The source image</param>
|
|||
/// <param name="filePath">The file path to save the image to.</param>
|
|||
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
|
|||
public static void Save<TPixel>(this Image<TPixel> source, string filePath) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
Guard.NotNullOrEmpty(filePath, nameof(filePath)); |
|||
|
|||
string ext = Path.GetExtension(filePath).Trim('.'); |
|||
IImageFormat format = source.Configuration.FindFormatByFileExtensions(ext); |
|||
if (format == null) |
|||
{ |
|||
var stringBuilder = new StringBuilder(); |
|||
stringBuilder.AppendLine($"Can't find a format that is associated with the file extention '{ext}'. Registered formats with there extensions include:"); |
|||
foreach (IImageFormat fmt in source.Configuration.ImageFormats) |
|||
{ |
|||
stringBuilder.AppendLine($" - {fmt.Name} : {string.Join(", ", fmt.FileExtensions)}"); |
|||
} |
|||
|
|||
throw new NotSupportedException(stringBuilder.ToString()); |
|||
} |
|||
|
|||
IImageEncoder encoder = source.Configuration.FindEncoder(format); |
|||
|
|||
if (encoder == null) |
|||
{ |
|||
var stringBuilder = new StringBuilder(); |
|||
stringBuilder.AppendLine($"Can't find encoder for file extention '{ext}' using image format '{format.Name}'. Registered encoders include:"); |
|||
foreach (KeyValuePair<IImageFormat, IImageEncoder> enc in source.Configuration.ImageEncoders) |
|||
{ |
|||
stringBuilder.AppendLine($" - {enc.Key} : {enc.Value.GetType().Name}"); |
|||
} |
|||
|
|||
throw new NotSupportedException(stringBuilder.ToString()); |
|||
} |
|||
|
|||
source.Save(filePath, encoder); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Saves the image to the given stream using the currently loaded image format.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The Pixel format.</typeparam>
|
|||
/// <param name="source">The source image</param>
|
|||
/// <param name="filePath">The file path to save the image to.</param>
|
|||
/// <param name="encoder">The encoder to save the image with.</param>
|
|||
/// <exception cref="System.ArgumentNullException">Thrown if the encoder is null.</exception>
|
|||
public static void Save<TPixel>(this Image<TPixel> source, string filePath, IImageEncoder encoder) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
Guard.NotNull(encoder, nameof(encoder)); |
|||
using (Stream fs = source.Configuration.FileSystem.Create(filePath)) |
|||
{ |
|||
source.Save(fs, encoder); |
|||
} |
|||
} |
|||
#endif
|
|||
|
|||
/// <summary>
|
|||
/// Saves the image to the given stream using the currently loaded image format.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The Pixel format.</typeparam>
|
|||
/// <param name="source">The source image</param>
|
|||
/// <param name="stream">The stream to save the image to.</param>
|
|||
/// <param name="format">The format to save the image to.</param>
|
|||
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
|
|||
public static void Save<TPixel>(this Image<TPixel> source, Stream stream, IImageFormat format) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
Guard.NotNull(format, nameof(format)); |
|||
IImageEncoder encoder = source.Configuration.FindEncoder(format); |
|||
|
|||
if (encoder == null) |
|||
{ |
|||
var stringBuilder = new StringBuilder(); |
|||
stringBuilder.AppendLine("Can't find encoder for provided mime type. Available encoded:"); |
|||
|
|||
foreach (KeyValuePair<IImageFormat, IImageEncoder> val in source.Configuration.ImageEncoders) |
|||
{ |
|||
stringBuilder.AppendLine($" - {val.Key.Name} : {val.Value.GetType().Name}"); |
|||
} |
|||
|
|||
throw new NotSupportedException(stringBuilder.ToString()); |
|||
} |
|||
|
|||
source.Save(stream, encoder); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns a Base64 encoded string from the given image.
|
|||
/// </summary>
|
|||
/// <example><see href="data:image/gif;base64,R0lGODlhAQABAIABAEdJRgAAACwAAAAAAQABAAACAkQBAA=="/></example>
|
|||
/// <typeparam name="TPixel">The Pixel format.</typeparam>
|
|||
/// <param name="source">The source image</param>
|
|||
/// <param name="format">The format.</param>
|
|||
/// <returns>The <see cref="string"/></returns>
|
|||
public static string ToBase64String<TPixel>(this Image<TPixel> source, IImageFormat format) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
using (var stream = new MemoryStream()) |
|||
{ |
|||
source.Save(stream, format); |
|||
stream.Flush(); |
|||
return $"data:{format.DefaultMimeType};base64,{Convert.ToBase64String(stream.ToArray())}"; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,32 +0,0 @@ |
|||
// <copyright file="ImageProcessingExtensions.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp |
|||
{ |
|||
using ImageSharp.PixelFormats; |
|||
|
|||
using ImageSharp.Processing; |
|||
|
|||
/// <summary>
|
|||
/// Extension methods for the <see cref="Image{TPixel}"/> type.
|
|||
/// </summary>
|
|||
public static partial class ImageExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// Applies the processor to the image.
|
|||
/// <remarks>This method does not resize the target image.</remarks>
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|||
/// <param name="source">The image this method extends.</param>
|
|||
/// <param name="processor">The processor to apply to the image.</param>
|
|||
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
|
|||
public static Image<TPixel> Apply<TPixel>(this Image<TPixel> source, IImageProcessor<TPixel> processor) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
source.ApplyProcessor(processor, source.Bounds); |
|||
return source; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,131 @@ |
|||
// <copyright file="LongRational.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp |
|||
{ |
|||
using System; |
|||
using SixLabors.Primitives; |
|||
|
|||
/// <summary>
|
|||
/// Represents a value in relation to a value on the image
|
|||
/// </summary>
|
|||
internal struct ValueSize : IEquatable<ValueSize> |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="ValueSize"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="value">The value.</param>
|
|||
/// <param name="type">The type.</param>
|
|||
public ValueSize(float value, ValueSizeType type) |
|||
{ |
|||
if (type != ValueSizeType.Absolute) |
|||
{ |
|||
Guard.MustBeBetweenOrEqualTo(value, 0, 1, nameof(value)); |
|||
} |
|||
|
|||
this.Value = value; |
|||
this.Type = type; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// The different vlaue types
|
|||
/// </summary>
|
|||
public enum ValueSizeType |
|||
{ |
|||
/// <summary>
|
|||
/// The value is the final return value
|
|||
/// </summary>
|
|||
Absolute, |
|||
|
|||
/// <summary>
|
|||
/// The value is a percentage of the Images Width
|
|||
/// </summary>
|
|||
PercentageOfWidth, |
|||
|
|||
/// <summary>
|
|||
/// The value is a percentage of the Images height
|
|||
/// </summary>
|
|||
PercentageOfHeight |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the value.
|
|||
/// </summary>
|
|||
public float Value { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the type.
|
|||
/// </summary>
|
|||
public ValueSizeType Type { get; } |
|||
|
|||
/// <summary>
|
|||
/// Implicitly converts a float into an absolute value
|
|||
/// </summary>
|
|||
/// <param name="d">the vlaue to use as the absolute figure.</param>
|
|||
public static implicit operator ValueSize(float d) |
|||
=> Absolute(d); |
|||
|
|||
/// <summary>
|
|||
/// Create a new ValueSize with as a PercentageOfWidth type with value set to percentage.
|
|||
/// </summary>
|
|||
/// <param name="percentage">The percentage.</param>
|
|||
/// <returns>a Values size with type PercentageOfWidth</returns>
|
|||
public static ValueSize PercentageOfWidth(float percentage) |
|||
{ |
|||
return new ValueSize(percentage, ValueSizeType.PercentageOfWidth); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Create a new ValueSize with as a PercentageOfHeight type with value set to percentage.
|
|||
/// </summary>
|
|||
/// <param name="percentage">The percentage.</param>
|
|||
/// <returns>a Values size with type PercentageOfHeight</returns>
|
|||
public static ValueSize PercentageOfHeight(float percentage) |
|||
{ |
|||
return new ValueSize(percentage, ValueSizeType.PercentageOfHeight); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Create a new ValueSize with as a Absolute type with value set to value.
|
|||
/// </summary>
|
|||
/// <param name="value">The value.</param>
|
|||
/// <returns>a Values size with type Absolute(</returns>
|
|||
public static ValueSize Absolute(float value) |
|||
{ |
|||
return new ValueSize(value, ValueSizeType.Absolute); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Calculates the specified size.
|
|||
/// </summary>
|
|||
/// <param name="size">The size.</param>
|
|||
/// <returns>The calucalted value</returns>
|
|||
public float Calculate(Size size) |
|||
{ |
|||
switch (this.Type) |
|||
{ |
|||
case ValueSizeType.PercentageOfWidth: |
|||
return this.Value * size.Width; |
|||
case ValueSizeType.PercentageOfHeight: |
|||
return this.Value * size.Height; |
|||
case ValueSizeType.Absolute: |
|||
default: |
|||
return this.Value; |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override string ToString() |
|||
{ |
|||
return $"{this.Value} - {this.Type}"; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public bool Equals(ValueSize other) |
|||
{ |
|||
return this.Type == other.Type && this.Value == other.Value; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
// <copyright file="RotateFlip.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp |
|||
{ |
|||
using System; |
|||
|
|||
using ImageSharp.PixelFormats; |
|||
|
|||
using ImageSharp.Processing; |
|||
|
|||
/// <summary>
|
|||
/// Extension methods for the <see cref="Image{TPixel}"/> type.
|
|||
/// </summary>
|
|||
public static partial class ImageExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// Queues up an operation that provides access to the mutatable image.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|||
/// <param name="source">The image to rotate, flip, or both.</param>
|
|||
/// <param name="operation">The operations to perform on the source.</param>
|
|||
/// <returns>returns the current operations class to allow chaining of operations.</returns>
|
|||
public static IImageProcessingContext<TPixel> Apply<TPixel>(this IImageProcessingContext<TPixel> source, Action<Image<TPixel>> operation) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
=> source.ApplyProcessor(new DelegateProcessor<TPixel>(operation)); |
|||
} |
|||
} |
|||
@ -0,0 +1,166 @@ |
|||
// <copyright file="CloneingImageProcessor.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Processing |
|||
{ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
|
|||
using ImageSharp.PixelFormats; |
|||
using SixLabors.Primitives; |
|||
|
|||
/// <summary>
|
|||
/// Allows the application of processors to images.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|||
internal abstract class CloningImageProcessor<TPixel> : IImageProcessor<TPixel>, ICloningImageProcessor<TPixel> |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public virtual ParallelOptions ParallelOptions { get; set; } |
|||
|
|||
/// <inheritdoc/>
|
|||
public virtual bool Compand { get; set; } = false; |
|||
|
|||
/// <inheritdoc/>
|
|||
public Image<TPixel> CloneAndApply(Image<TPixel> source, Rectangle sourceRectangle) |
|||
{ |
|||
if (this.ParallelOptions == null) |
|||
{ |
|||
this.ParallelOptions = source.Configuration.ParallelOptions; |
|||
} |
|||
|
|||
try |
|||
{ |
|||
Image<TPixel> clone = this.CreateDestination(source, sourceRectangle); |
|||
|
|||
if (clone.Frames.Count != source.Frames.Count) |
|||
{ |
|||
throw new ImageProcessingException($"An error occured when processing the image using {this.GetType().Name}. The processor changed the number of frames."); |
|||
} |
|||
|
|||
this.BeforeImageApply(source, clone, sourceRectangle); |
|||
|
|||
this.BeforeApply(source, clone, sourceRectangle); |
|||
this.OnApply(source, clone, sourceRectangle); |
|||
this.AfterApply(source, clone, sourceRectangle); |
|||
|
|||
for (int i = 0; i < source.Frames.Count; i++) |
|||
{ |
|||
ImageFrame<TPixel> sourceFrame = source.Frames[i]; |
|||
ImageFrame<TPixel> clonedFrame = clone.Frames[i]; |
|||
|
|||
this.BeforeApply(sourceFrame, clonedFrame, sourceRectangle); |
|||
|
|||
this.OnApply(sourceFrame, clonedFrame, sourceRectangle); |
|||
this.AfterApply(sourceFrame, clonedFrame, sourceRectangle); |
|||
} |
|||
|
|||
this.AfterImageApply(source, clone, sourceRectangle); |
|||
|
|||
return clone; |
|||
} |
|||
#if DEBUG
|
|||
catch (Exception) |
|||
{ |
|||
throw; |
|||
#else
|
|||
catch (Exception ex) |
|||
{ |
|||
throw new ImageProcessingException($"An error occured when processing the image using {this.GetType().Name}. See the inner exception for more detail.", ex); |
|||
#endif
|
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public void Apply(Image<TPixel> source, Rectangle sourceRectangle) |
|||
{ |
|||
using (Image<TPixel> cloned = this.CloneAndApply(source, sourceRectangle)) |
|||
{ |
|||
// we now need to move the pixel data/size data from one image base to another
|
|||
if (cloned.Frames.Count != source.Frames.Count) |
|||
{ |
|||
throw new ImageProcessingException($"An error occured when processing the image using {this.GetType().Name}. The processor changed the number of frames."); |
|||
} |
|||
|
|||
source.SwapPixelsData(cloned); |
|||
for (int i = 0; i < source.Frames.Count; i++) |
|||
{ |
|||
source.Frames[i].SwapPixelsData(cloned.Frames[i]); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Generates the clone of the source image that operatinos should be applied to.
|
|||
/// </summary>
|
|||
/// <param name="source">The source image. Cannot be null.</param>
|
|||
/// <param name="sourceRectangle">The source rectangle.</param>
|
|||
/// <returns>The cloned image.</returns>
|
|||
protected virtual Image<TPixel> CreateDestination(Image<TPixel> source, Rectangle sourceRectangle) |
|||
{ |
|||
return source.Clone(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// This method is called before the process is applied to prepare the processor.
|
|||
/// </summary>
|
|||
/// <param name="source">The source image. Cannot be null.</param>
|
|||
/// <param name="destination">The cloned/destination image. Cannot be null.</param>
|
|||
/// <param name="sourceRectangle">
|
|||
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
|
|||
/// </param>
|
|||
protected virtual void BeforeImageApply(Image<TPixel> source, Image<TPixel> destination, Rectangle sourceRectangle) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// This method is called before the process is applied to prepare the processor.
|
|||
/// </summary>
|
|||
/// <param name="source">The source image. Cannot be null.</param>
|
|||
/// <param name="destination">The cloned/destination image. Cannot be null.</param>
|
|||
/// <param name="sourceRectangle">
|
|||
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
|
|||
/// </param>
|
|||
protected virtual void BeforeApply(ImageBase<TPixel> source, ImageBase<TPixel> destination, Rectangle sourceRectangle) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Applies the process to the specified portion of the specified <see cref="ImageBase{TPixel}"/> at the specified location
|
|||
/// and with the specified size.
|
|||
/// </summary>
|
|||
/// <param name="source">The source image. Cannot be null.</param>
|
|||
/// <param name="destination">The cloned/destination image. Cannot be null.</param>
|
|||
/// <param name="sourceRectangle">
|
|||
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
|
|||
/// </param>
|
|||
protected abstract void OnApply(ImageBase<TPixel> source, ImageBase<TPixel> destination, Rectangle sourceRectangle); |
|||
|
|||
/// <summary>
|
|||
/// This method is called after the process is applied to prepare the processor.
|
|||
/// </summary>
|
|||
/// <param name="source">The source image. Cannot be null.</param>
|
|||
/// <param name="destination">The cloned/destination image. Cannot be null.</param>
|
|||
/// <param name="sourceRectangle">
|
|||
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
|
|||
/// </param>
|
|||
protected virtual void AfterApply(ImageBase<TPixel> source, ImageBase<TPixel> destination, Rectangle sourceRectangle) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// This method is called after the process is applied to prepare the processor.
|
|||
/// </summary>
|
|||
/// <param name="source">The source image. Cannot be null.</param>
|
|||
/// <param name="destination">The cloned/destination image. Cannot be null.</param>
|
|||
/// <param name="sourceRectangle">
|
|||
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
|
|||
/// </param>
|
|||
protected virtual void AfterImageApply(Image<TPixel> source, Image<TPixel> destination, Rectangle sourceRectangle) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,48 @@ |
|||
// <copyright file="DelegateProcessor.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Processing |
|||
{ |
|||
using System; |
|||
|
|||
using ImageSharp.PixelFormats; |
|||
using SixLabors.Primitives; |
|||
|
|||
/// <summary>
|
|||
/// Allows the application of processors to images.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|||
internal class DelegateProcessor<TPixel> : ImageProcessor<TPixel> |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
private readonly Action<Image<TPixel>> action; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="DelegateProcessor{TPixel}"/> class.
|
|||
/// </summary>
|
|||
/// <param name="action">The action.</param>
|
|||
public DelegateProcessor(Action<Image<TPixel>> action) |
|||
{ |
|||
this.action = action; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the action that will be applied to the image.
|
|||
/// </summary>
|
|||
internal Action<Image<TPixel>> Action => this.action; |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void BeforeImageApply(Image<TPixel> source, Rectangle sourceRectangle) |
|||
{ |
|||
this.action?.Invoke(source); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void OnApply(ImageBase<TPixel> source, Rectangle sourceRectangle) |
|||
{ |
|||
// NOP, we did all we wanted to do inside BeforeImageApply
|
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,112 @@ |
|||
// <copyright file="FlipProcessor.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Processing.Processors |
|||
{ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
|
|||
using ImageSharp.Memory; |
|||
using ImageSharp.PixelFormats; |
|||
using SixLabors.Primitives; |
|||
|
|||
/// <summary>
|
|||
/// Adjusts an image so that its orientation is suitable for viewing. Adjustments are based on EXIF metadata embedded in the image.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|||
internal class AutoRotateProcessor<TPixel> : ImageProcessor<TPixel> |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="AutoRotateProcessor{TPixel}"/> class.
|
|||
/// </summary>
|
|||
public AutoRotateProcessor() |
|||
{ |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void OnApply(ImageBase<TPixel> sourceBase, Rectangle sourceRectangle) |
|||
{ |
|||
// can only apply to the origional image
|
|||
var source = sourceBase as Image<TPixel>; |
|||
if (source != null) |
|||
{ |
|||
Orientation orientation = GetExifOrientation(source); |
|||
|
|||
switch (orientation) |
|||
{ |
|||
case Orientation.TopRight: |
|||
new FlipProcessor<TPixel>(FlipType.Horizontal).Apply(source, sourceRectangle); |
|||
break; |
|||
|
|||
case Orientation.BottomRight: |
|||
new RotateProcessor<TPixel>() { Angle = (int)RotateType.Rotate180, Expand = false }.Apply(source, sourceRectangle); |
|||
break; |
|||
|
|||
case Orientation.BottomLeft: |
|||
new FlipProcessor<TPixel>(FlipType.Vertical).Apply(source, sourceRectangle); |
|||
break; |
|||
|
|||
case Orientation.LeftTop: |
|||
new RotateProcessor<TPixel>() { Angle = (int)RotateType.Rotate90, Expand = false }.Apply(source, sourceRectangle); |
|||
new FlipProcessor<TPixel>(FlipType.Horizontal).Apply(source, sourceRectangle); |
|||
break; |
|||
|
|||
case Orientation.RightTop: |
|||
new RotateProcessor<TPixel>() { Angle = (int)RotateType.Rotate90, Expand = false }.Apply(source, sourceRectangle); |
|||
break; |
|||
|
|||
case Orientation.RightBottom: |
|||
new FlipProcessor<TPixel>(FlipType.Vertical).Apply(source, sourceRectangle); |
|||
new RotateProcessor<TPixel>() { Angle = (int)RotateType.Rotate270, Expand = false }.Apply(source, sourceRectangle); |
|||
break; |
|||
|
|||
case Orientation.LeftBottom: |
|||
new RotateProcessor<TPixel>() { Angle = (int)RotateType.Rotate270, Expand = false }.Apply(source, sourceRectangle); |
|||
break; |
|||
|
|||
case Orientation.Unknown: |
|||
case Orientation.TopLeft: |
|||
default: |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the current EXIF orientation
|
|||
/// </summary>
|
|||
/// <param name="source">The image to auto rotate.</param>
|
|||
/// <returns>The <see cref="Orientation"/></returns>
|
|||
private static Orientation GetExifOrientation(Image<TPixel> source) |
|||
{ |
|||
if (source.MetaData.ExifProfile == null) |
|||
{ |
|||
return Orientation.Unknown; |
|||
} |
|||
|
|||
ExifValue value = source.MetaData.ExifProfile.GetValue(ExifTag.Orientation); |
|||
if (value == null) |
|||
{ |
|||
return Orientation.Unknown; |
|||
} |
|||
|
|||
Orientation orientation; |
|||
if (value.DataType == ExifDataType.Short) |
|||
{ |
|||
orientation = (Orientation)value.Value; |
|||
} |
|||
else |
|||
{ |
|||
orientation = (Orientation)Convert.ToUInt16(value.Value); |
|||
source.MetaData.ExifProfile.RemoveValue(ExifTag.Orientation); |
|||
} |
|||
|
|||
source.MetaData.ExifProfile.SetValue(ExifTag.Orientation, (ushort)Orientation.TopLeft); |
|||
|
|||
return orientation; |
|||
} |
|||
} |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue