mirror of https://github.com/SixLabors/ImageSharp
8 changed files with 343 additions and 363 deletions
@ -0,0 +1,324 @@ |
|||
// --------------------------------------------------------------------------------------------------------------------
|
|||
// <copyright file="Resize.cs" company="James South">
|
|||
// Copyright (c) James South.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
// <summary>
|
|||
// Resizes an image to the given dimensions.
|
|||
// </summary>
|
|||
// --------------------------------------------------------------------------------------------------------------------
|
|||
|
|||
namespace ImageProcessor.Web.Processors |
|||
{ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Drawing; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Text.RegularExpressions; |
|||
using ImageProcessor.Core.Common.Extensions; |
|||
using ImageProcessor.Imaging; |
|||
using ImageProcessor.Processors; |
|||
|
|||
/// <summary>
|
|||
/// Resizes an image to the given dimensions.
|
|||
/// </summary>
|
|||
public class Resize : IWebGraphicsProcessor |
|||
{ |
|||
/// <summary>
|
|||
/// The regular expression to search strings for.
|
|||
/// </summary>
|
|||
private static readonly Regex QueryRegex = new Regex(@"(width|height)=|(width|height)ratio=|mode=|anchor=|center=|upscale=", RegexOptions.Compiled); |
|||
|
|||
/// <summary>
|
|||
/// The regular expression to search strings for the size attribute.
|
|||
/// </summary>
|
|||
private static readonly Regex SizeRegex = new Regex(@"(width|height)=\d+", RegexOptions.Compiled); |
|||
|
|||
/// <summary>
|
|||
/// The regular expression to search strings for the ratio attribute.
|
|||
/// </summary>
|
|||
private static readonly Regex RatioRegex = new Regex(@"(width|height)ratio=\d+(.\d+)?", RegexOptions.Compiled); |
|||
|
|||
/// <summary>
|
|||
/// The regular expression to search strings for the mode attribute.
|
|||
/// </summary>
|
|||
private static readonly Regex ModeRegex = new Regex(@"mode=(pad|stretch|crop|max)", RegexOptions.Compiled); |
|||
|
|||
/// <summary>
|
|||
/// The regular expression to search strings for the anchor attribute.
|
|||
/// </summary>
|
|||
private static readonly Regex AnchorRegex = new Regex(@"anchor=(top|bottom|left|right|center)", RegexOptions.Compiled); |
|||
|
|||
/// <summary>
|
|||
/// The regular expression to search strings for the center attribute.
|
|||
/// </summary>
|
|||
private static readonly Regex CenterRegex = new Regex(@"center=\d+(.\d+)?[,-]\d+(.\d+)", RegexOptions.Compiled); |
|||
|
|||
/// <summary>
|
|||
/// The regular expression to search strings for the upscale attribute.
|
|||
/// </summary>
|
|||
private static readonly Regex UpscaleRegex = new Regex(@"upscale=false", RegexOptions.Compiled); |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Resize"/> class.
|
|||
/// </summary>
|
|||
public Resize() |
|||
{ |
|||
this.Processor = new ImageProcessor.Processors.Resize(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the regular expression to search strings for.
|
|||
/// </summary>
|
|||
public Regex RegexPattern |
|||
{ |
|||
get |
|||
{ |
|||
return QueryRegex; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the order in which this processor is to be used in a chain.
|
|||
/// </summary>
|
|||
public int SortOrder { get; private set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the associated graphics processor.
|
|||
/// </summary>
|
|||
public IGraphicsProcessor Processor { get; private set; } |
|||
|
|||
/// <summary>
|
|||
/// The position in the original string where the first character of the captured substring was found.
|
|||
/// </summary>
|
|||
/// <param name="queryString">The query string to search.</param>
|
|||
/// <returns>
|
|||
/// The zero-based starting position in the original string where the captured substring was found.
|
|||
/// </returns>
|
|||
public int MatchRegexIndex(string queryString) |
|||
{ |
|||
int index = 0; |
|||
|
|||
// Set the sort order to max to allow filtering.
|
|||
this.SortOrder = int.MaxValue; |
|||
|
|||
// First merge the matches so we can parse .
|
|||
StringBuilder stringBuilder = new StringBuilder(); |
|||
|
|||
foreach (Match match in this.RegexPattern.Matches(queryString)) |
|||
{ |
|||
if (match.Success) |
|||
{ |
|||
if (index == 0) |
|||
{ |
|||
// Set the index on the first instance only.
|
|||
this.SortOrder = match.Index; |
|||
stringBuilder.Append(queryString); |
|||
} |
|||
|
|||
index += 1; |
|||
} |
|||
} |
|||
|
|||
// Match syntax
|
|||
string toParse = stringBuilder.ToString(); |
|||
|
|||
Size size = this.ParseSize(toParse); |
|||
ResizeLayer resizeLayer = new ResizeLayer(size) |
|||
{ |
|||
ResizeMode = this.ParseMode(toParse), |
|||
AnchorPosition = this.ParsePosition(toParse), |
|||
Upscale = !UpscaleRegex.IsMatch(toParse), |
|||
CenterCoordinates = this.ParseCoordinates(toParse), |
|||
}; |
|||
|
|||
this.Processor.DynamicParameter = resizeLayer; |
|||
|
|||
// Correctly parse any restrictions.
|
|||
string restrictions; |
|||
this.Processor.Settings.TryGetValue("RestrictTo", out restrictions); |
|||
((ImageProcessor.Processors.Resize)this.Processor).RestrictedSizes = this.ParseRestrictions(restrictions); |
|||
return this.SortOrder; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the correct <see cref="Size"/> for the given string.
|
|||
/// </summary>
|
|||
/// <param name="input">
|
|||
/// The input string containing the value to parse.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// The <see cref="Size"/>.
|
|||
/// </returns>
|
|||
private Size ParseSize(string input) |
|||
{ |
|||
const string Width = "width="; |
|||
const string Height = "height="; |
|||
const string WidthRatio = "widthratio="; |
|||
const string HeightRatio = "heightratio="; |
|||
Size size = new Size(); |
|||
|
|||
// First merge the matches so we can parse .
|
|||
StringBuilder stringBuilder = new StringBuilder(); |
|||
foreach (Match match in SizeRegex.Matches(input)) |
|||
{ |
|||
stringBuilder.Append(match.Value); |
|||
} |
|||
|
|||
// First cater for single dimensions.
|
|||
string value = stringBuilder.ToString(); |
|||
|
|||
if (input.Contains(Width) && !input.Contains(Height)) |
|||
{ |
|||
size = new Size(value.ToPositiveIntegerArray()[0], 0); |
|||
} |
|||
|
|||
if (input.Contains(Height) && !input.Contains(Width)) |
|||
{ |
|||
size = new Size(0, value.ToPositiveIntegerArray()[0]); |
|||
} |
|||
|
|||
// Both dimensions supplied.
|
|||
if (input.Contains(Height) && input.Contains(Width)) |
|||
{ |
|||
int[] dimensions = value.ToPositiveIntegerArray(); |
|||
|
|||
// Check the order in which they have been supplied.
|
|||
size = input.IndexOf(Width, StringComparison.Ordinal) < input.IndexOf(Height, StringComparison.Ordinal) |
|||
? new Size(dimensions[0], dimensions[1]) |
|||
: new Size(dimensions[1], dimensions[0]); |
|||
} |
|||
|
|||
// Calculate any ratio driven sizes.
|
|||
if (size.Width == 0 || size.Height == 0) |
|||
{ |
|||
stringBuilder.Clear(); |
|||
foreach (Match match in RatioRegex.Matches(input)) |
|||
{ |
|||
stringBuilder.Append(match.Value); |
|||
} |
|||
|
|||
value = stringBuilder.ToString(); |
|||
|
|||
// Replace 0 width
|
|||
if (size.Width == 0 && size.Height > 0 && input.Contains(WidthRatio) && !input.Contains(HeightRatio)) |
|||
{ |
|||
size.Width = (int)(value.ToPositiveFloatArray()[0] * size.Height); |
|||
} |
|||
|
|||
// Replace 0 height
|
|||
if (size.Height == 0 && size.Width > 0 && input.Contains(HeightRatio) && !input.Contains(WidthRatio)) |
|||
{ |
|||
size.Height = (int)(value.ToPositiveFloatArray()[0] * size.Width); |
|||
} |
|||
} |
|||
|
|||
return size; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the correct <see cref="ResizeMode"/> for the given string.
|
|||
/// </summary>
|
|||
/// <param name="input">
|
|||
/// The input string containing the value to parse.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// The correct <see cref="ResizeMode"/>.
|
|||
/// </returns>
|
|||
private ResizeMode ParseMode(string input) |
|||
{ |
|||
foreach (Match match in ModeRegex.Matches(input)) |
|||
{ |
|||
// Split on =
|
|||
string mode = match.Value.Split('=')[1]; |
|||
|
|||
switch (mode) |
|||
{ |
|||
case "stretch": |
|||
return ResizeMode.Stretch; |
|||
case "crop": |
|||
return ResizeMode.Crop; |
|||
case "max": |
|||
return ResizeMode.Max; |
|||
default: |
|||
return ResizeMode.Pad; |
|||
} |
|||
} |
|||
|
|||
return ResizeMode.Pad; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the correct <see cref="AnchorPosition"/> for the given string.
|
|||
/// </summary>
|
|||
/// <param name="input">
|
|||
/// The input string containing the value to parse.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// The correct <see cref="AnchorPosition"/>.
|
|||
/// </returns>
|
|||
private AnchorPosition ParsePosition(string input) |
|||
{ |
|||
foreach (Match match in AnchorRegex.Matches(input)) |
|||
{ |
|||
// Split on =
|
|||
string anchor = match.Value.Split('=')[1]; |
|||
|
|||
switch (anchor) |
|||
{ |
|||
case "top": |
|||
return AnchorPosition.Top; |
|||
case "bottom": |
|||
return AnchorPosition.Bottom; |
|||
case "left": |
|||
return AnchorPosition.Left; |
|||
case "right": |
|||
return AnchorPosition.Right; |
|||
default: |
|||
return AnchorPosition.Center; |
|||
} |
|||
} |
|||
|
|||
return AnchorPosition.Center; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Parses the coordinates.
|
|||
/// </summary>
|
|||
/// <param name="input">The input.</param>
|
|||
/// <returns>The <see cref="float"/> array containing the coordinates</returns>
|
|||
private float[] ParseCoordinates(string input) |
|||
{ |
|||
float[] floats = { }; |
|||
|
|||
foreach (Match match in CenterRegex.Matches(input)) |
|||
{ |
|||
floats = match.Value.ToPositiveFloatArray(); |
|||
} |
|||
|
|||
return floats; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns a <see cref="List{T}"/> of sizes to restrict resizing to.
|
|||
/// </summary>
|
|||
/// <param name="input">
|
|||
/// The input.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// The <see cref="List{Size}"/> to restrict resizing to.
|
|||
/// </returns>
|
|||
private List<Size> ParseRestrictions(string input) |
|||
{ |
|||
List<Size> sizes = new List<Size>(); |
|||
|
|||
if (!string.IsNullOrWhiteSpace(input)) |
|||
{ |
|||
sizes.AddRange(input.Split(',').Select(this.ParseSize)); |
|||
} |
|||
|
|||
return sizes; |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue