mirror of https://github.com/SixLabors/ImageSharp
146 changed files with 1504 additions and 1146 deletions
@ -0,0 +1,100 @@ |
|||||
|
// <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>
|
||||
|
/// 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, Action<IImageOperations<TPixel>> operations) |
||||
|
where TPixel : struct, IPixel<TPixel> |
||||
|
{ |
||||
|
Guard.NotNull(operations, nameof(operations)); |
||||
|
|
||||
|
// TODO: add parameter to Configuration to configure how this is created, create an IImageOperationsFactory that cna be used to switch this out with a fake for testing
|
||||
|
var operationsRunner = new ImageOperations<TPixel>(source); |
||||
|
operations(operationsRunner); |
||||
|
} |
||||
|
|
||||
|
/// <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)); |
||||
|
|
||||
|
// TODO: add parameter to Configuration to configure how this is created, create an IImageOperationsFactory that cna be used to switch this out with a fake for testing
|
||||
|
var operationsRunner = new ImageOperations<TPixel>(source); |
||||
|
operationsRunner.ApplyProcessors(operations); |
||||
|
} |
||||
|
|
||||
|
/// <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>
|
||||
|
/// <returns>Anew Image which has teh data from the <paramref name="source"/> but with the <paramref name="operations"/> applied.</returns>
|
||||
|
public static Image<TPixel> Generate<TPixel>(this Image<TPixel> source, Action<IImageOperations<TPixel>> operations) |
||||
|
where TPixel : struct, IPixel<TPixel> |
||||
|
{ |
||||
|
Guard.NotNull(operations, nameof(operations)); |
||||
|
var generated = new Image<TPixel>(source); |
||||
|
|
||||
|
// TODO: add parameter to Configuration to configure how this is created, create an IImageOperationsFactory that cna be used to switch this out with a fake for testing
|
||||
|
var operationsRunner = new ImageOperations<TPixel>(generated); |
||||
|
operations(operationsRunner); |
||||
|
return generated; |
||||
|
} |
||||
|
|
||||
|
/// <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>
|
||||
|
/// <returns>Anew Image which has teh data from the <paramref name="source"/> but with the <paramref name="operations"/> applied.</returns>
|
||||
|
public static Image<TPixel> Generate<TPixel>(this Image<TPixel> source, params IImageProcessor<TPixel>[] operations) |
||||
|
where TPixel : struct, IPixel<TPixel> |
||||
|
{ |
||||
|
Guard.NotNull(operations, nameof(operations)); |
||||
|
var generated = new Image<TPixel>(source); |
||||
|
|
||||
|
// TODO: add parameter to Configuration to configure how this is created, create an IImageOperationsFactory that cna be used to switch this out with a fake for testing
|
||||
|
var operationsRunner = new ImageOperations<TPixel>(generated); |
||||
|
operationsRunner.ApplyProcessors(operations); |
||||
|
return generated; |
||||
|
} |
||||
|
|
||||
|
/// <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="operation">The operations to perform on the source.</param>
|
||||
|
/// <returns>returns the current optinoatins class to allow chaining of oprations.</returns>
|
||||
|
public static IImageOperations<TPixel> Run<TPixel>(this IImageOperations<TPixel> source, Action<Image<TPixel>> operation) |
||||
|
where TPixel : struct, IPixel<TPixel> |
||||
|
=> source.ApplyProcessor(new DelegateImageProcessor<TPixel>(operation)); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,36 @@ |
|||||
|
// <copyright file="IImageFormat.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.Formats; |
||||
|
using ImageSharp.PixelFormats; |
||||
|
using ImageSharp.Processing; |
||||
|
using SixLabors.Primitives; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The static collection of all the default image formats
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TPixel">The pixel format</typeparam>
|
||||
|
public interface IImageOperations<TPixel> |
||||
|
where TPixel : struct, IPixel<TPixel> |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Adds the processor to the current setr 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>returns the current optinoatins class to allow chaining of oprations.</returns>
|
||||
|
IImageOperations<TPixel> ApplyProcessor(IImageProcessor<TPixel> processor, Rectangle rectangle); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Adds the processor to the current setr of image operations to be applied.
|
||||
|
/// </summary>
|
||||
|
/// <param name="processor">The processor to apply</param>
|
||||
|
/// <returns>returns the current optinoatins class to allow chaining of oprations.</returns>
|
||||
|
IImageOperations<TPixel> ApplyProcessor(IImageProcessor<TPixel> processor); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,62 @@ |
|||||
|
// <copyright file="IImageFormat.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.Collections.Generic; |
||||
|
using ImageSharp.PixelFormats; |
||||
|
using ImageSharp.Processing; |
||||
|
using SixLabors.Primitives; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The static collection of all the default image formats
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TPixel">The pixel format</typeparam>
|
||||
|
internal class ImageOperations<TPixel> : IImageOperations<TPixel> |
||||
|
where TPixel : struct, IPixel<TPixel> |
||||
|
{ |
||||
|
private readonly Image<TPixel> image; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="ImageOperations{TPixel}"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="image">The image.</param>
|
||||
|
public ImageOperations(Image<TPixel> image) |
||||
|
{ |
||||
|
this.image = image; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public IImageOperations<TPixel> ApplyProcessor(IImageProcessor<TPixel> processor, Rectangle rectangle) |
||||
|
{ |
||||
|
// TODO : make this queue, and allow special processors managage the cloing operation for 'generate'
|
||||
|
// to allow things like resize to not need to retain an extra copy of image data in memory, and to
|
||||
|
// prevent an pixel copy operation
|
||||
|
this.image.ApplyProcessor(processor, rectangle); |
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public IImageOperations<TPixel> ApplyProcessor(IImageProcessor<TPixel> processor) |
||||
|
{ |
||||
|
return this.ApplyProcessor(processor, this.image.Bounds); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Applies a bluck colelctino of pressorce at once
|
||||
|
/// </summary>
|
||||
|
/// <param name="processors">Processors to apply</param>
|
||||
|
/// <returns>this </returns>
|
||||
|
public IImageOperations<TPixel> ApplyProcessors(IEnumerable<IImageProcessor<TPixel>> processors) |
||||
|
{ |
||||
|
foreach (var processor in processors) |
||||
|
{ |
||||
|
return this.ApplyProcessor(processor); |
||||
|
} |
||||
|
|
||||
|
return this; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,124 @@ |
|||||
|
// <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 SixLabors.Primitives; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Represents a value in relation to a value on the image
|
||||
|
/// </summary>
|
||||
|
internal struct 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}"; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,44 @@ |
|||||
|
// <copyright file="DelegateImageProcessor.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 class DelegateImageProcessor<TPixel> : ImageProcessor<TPixel> |
||||
|
where TPixel : struct, IPixel<TPixel> |
||||
|
{ |
||||
|
private readonly Action<Image<TPixel>> action; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="DelegateImageProcessor{TPixel}"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="action">The action.</param>
|
||||
|
public DelegateImageProcessor(Action<Image<TPixel>> action) |
||||
|
{ |
||||
|
this.action = action; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
protected override void BeforeImageApply(Image<TPixel> source, Rectangle sourceRectangle) |
||||
|
{ |
||||
|
this.action?.Invoke((Image<TPixel>)source); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
protected override void OnApply(ImageBase<TPixel> source, Rectangle sourceRectangle) |
||||
|
{ |
||||
|
// no op, we did all we wanted to do inside BeforeImageApply
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,103 @@ |
|||||
|
// <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; |
||||
|
} |
||||
|
|
||||
|
var orientation = (Orientation)value.Value; |
||||
|
|
||||
|
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