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