Browse Source

Added new percentile based cropping system

Former-commit-id: c50657927da59f8acc381780e18e50750f511a3b
pull/17/head
James South 12 years ago
parent
commit
c2db727945
  1. 31
      src/ImageProcessor/Extensions/StringExtensions.cs
  2. 25
      src/ImageProcessor/ImageFactory.cs
  3. 2
      src/ImageProcessor/ImageProcessor.csproj
  4. 81
      src/ImageProcessor/Imaging/CropLayer.cs
  5. 28
      src/ImageProcessor/Imaging/CropMode.cs
  6. 111
      src/ImageProcessor/Processors/Crop.cs
  7. 1
      src/ImageProcessor/Processors/Resize.cs
  8. 28
      src/TestWebsites/NET45/Test_Website_NET45/Views/Home/Index.cshtml

31
src/ImageProcessor/Extensions/StringExtensions.cs

@ -123,7 +123,7 @@ namespace ImageProcessor.Extensions
throw new ArgumentNullException("expression");
}
Regex regex = new Regex(@"\d+", RegexOptions.Compiled);
Regex regex = new Regex(@"[\d+]+(?=[,|])|[\d+]+(?![,|])", RegexOptions.Compiled);
MatchCollection matchCollection = regex.Matches(expression);
@ -139,6 +139,35 @@ namespace ImageProcessor.Extensions
return matches;
}
/// <summary>
/// Creates an array of floats scraped from the String.
/// </summary>
/// <param name="expression">The <see cref="T:System.String">String</see> instance that this method extends.</param>
/// <returns>An array of floats scraped from the String.</returns>
public static float[] ToPositiveFloatArray(this string expression)
{
if (string.IsNullOrWhiteSpace(expression))
{
throw new ArgumentNullException("expression");
}
Regex regex = new Regex(@"[\d+\.]+(?=[,|])|[\d+\.]+(?![,|])", RegexOptions.Compiled);
MatchCollection matchCollection = regex.Matches(expression);
// Get the collections.
int count = matchCollection.Count;
float[] matches = new float[count];
// Loop and parse the int values.
for (int i = 0; i < count; i++)
{
matches[i] = float.Parse(matchCollection[i].Value);
}
return matches;
}
#endregion
#region Files and Paths

25
src/ImageProcessor/ImageFactory.cs

@ -395,7 +395,30 @@ namespace ImageProcessor
{
if (this.ShouldProcess)
{
Crop crop = new Crop { DynamicParameter = rectangle };
CropLayer cropLayer = new CropLayer(rectangle.Left, rectangle.Top, rectangle.Right, rectangle.Bottom, CropMode.Pixels);
Crop crop = new Crop { DynamicParameter = cropLayer };
this.Image = crop.ProcessImage(this);
}
return this;
}
/// <summary>
/// Crops the current image to the given location and size.
/// </summary>
/// <param name="cropLayer">
/// The <see cref="T:CropLayer"/> containing the coordinates and mode to crop the image with.
/// </param>
/// <returns>
/// The current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class.
/// </returns>
public ImageFactory Crop(CropLayer cropLayer)
{
if (this.ShouldProcess)
{
Crop crop = new Crop { DynamicParameter = cropLayer };
this.Image = crop.ProcessImage(this);
}

2
src/ImageProcessor/ImageProcessor.csproj

@ -66,6 +66,8 @@
<Compile Include="ImageFactory.cs" />
<Compile Include="Imaging\AnchorPosition.cs" />
<Compile Include="Imaging\Convolution.cs" />
<Compile Include="Imaging\CropLayer.cs" />
<Compile Include="Imaging\CropMode.cs" />
<Compile Include="Imaging\GaussianLayer.cs" />
<Compile Include="Imaging\ColorQuantizer.cs" />
<Compile Include="Imaging\ResizeLayer.cs" />

81
src/ImageProcessor/Imaging/CropLayer.cs

@ -0,0 +1,81 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="CropLayer.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Encapsulates the properties required to crop an image.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Imaging
{
using System;
/// <summary>
/// Encapsulates the properties required to crop an image.
/// </summary>
public class CropLayer
{
/// <summary>
/// Initializes a new instance of the <see cref="CropLayer"/> class.
/// </summary>
/// <param name="left">
/// The left coordinate of the crop layer.
/// </param>
/// <param name="top">
/// The top coordinate of the crop layer.
/// </param>
/// <param name="right">
/// The right coordinate of the crop layer.
/// </param>
/// <param name="bottom">
/// The bottom coordinate of the crop layer.
/// </param>
/// <param name="cropMode">
/// The <see cref="CropMode"/>.
/// </param>
/// <remarks>
/// If the <see cref="CropMode"/> is set to <value>CropMode.Percentage</value> then the four coordinates
/// become percentages to reduce from each edge.
/// </remarks>
public CropLayer(float left, float top, float right, float bottom, CropMode cropMode = CropMode.Percentage)
{
if (left < 0 || top < 0 || right < 0 || bottom < 0)
{
throw new ArgumentOutOfRangeException();
}
this.Left = left;
this.Top = top;
this.Right = right;
this.Bottom = bottom;
this.CropMode = cropMode;
}
/// <summary>
/// Gets or sets the left coordinate of the crop layer.
/// </summary>
public float Left { get; set; }
/// <summary>
/// Gets or sets the top coordinate of the crop layer.
/// </summary>
public float Top { get; set; }
/// <summary>
/// Gets or sets the right coordinate of the crop layer.
/// </summary>
public float Right { get; set; }
/// <summary>
/// Gets or sets the bottom coordinate of the crop layer.
/// </summary>
public float Bottom { get; set; }
/// <summary>
/// Gets or sets the <see cref="CropMode"/>.
/// </summary>
public CropMode CropMode { get; set; }
}
}

28
src/ImageProcessor/Imaging/CropMode.cs

@ -0,0 +1,28 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="CropMode.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Enumerated cop modes to apply to cropped images.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Imaging
{
/// <summary>
/// Enumerated cop modes to apply to cropped images.
/// </summary>
public enum CropMode
{
/// <summary>
/// Crops the image using the standard rectangle model of x, y, width, height.
/// </summary>
Pixels,
/// <summary>
/// Crops the image using percentages model left, top, right, bottom.
/// </summary>
Percentage
}
}

111
src/ImageProcessor/Processors/Crop.cs

@ -15,9 +15,11 @@ namespace ImageProcessor.Processors
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Text;
using System.Text.RegularExpressions;
using ImageProcessor.Extensions;
using ImageProcessor.Imaging;
#endregion
@ -30,7 +32,17 @@ namespace ImageProcessor.Processors
/// The regular expression to search strings for.
/// <see cref="http://stackoverflow.com/a/6400969/427899"/>
/// </summary>
private static readonly Regex QueryRegex = new Regex(@"crop=\d+[,-]\d+[,-]\d+[,-]\d+", RegexOptions.Compiled);
private static readonly Regex QueryRegex = new Regex(@"crop=\d+(.\d+)?[,-]\d+(.\d+)?[,-]\d+(.\d+)?[,-]\d+(.\d+)?|cropmode=(pixels|percent)", RegexOptions.Compiled);
/// <summary>
/// The coordinate regex.
/// </summary>
private static readonly Regex CoordinateRegex = new Regex(@"crop=\d+(.\d+)?[,-]\d+(.\d+)?[,-]\d+(.\d+)?[,-]\d+(.\d+)?", RegexOptions.Compiled);
/// <summary>
/// The mode regex.
/// </summary>
private static readonly Regex ModeRegex = new Regex(@"cropmode=(pixels|percent)", RegexOptions.Compiled);
#region IGraphicsProcessor Members
/// <summary>
@ -87,6 +99,9 @@ namespace ImageProcessor.Processors
// 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)
@ -95,21 +110,26 @@ namespace ImageProcessor.Processors
{
// Set the index on the first instance only.
this.SortOrder = match.Index;
int[] coordinates = match.Value.ToPositiveIntegerArray();
int x = coordinates[0];
int y = coordinates[1];
int width = coordinates[2];
int height = coordinates[3];
Rectangle rectangle = new Rectangle(x, y, width, height);
this.DynamicParameter = rectangle;
}
stringBuilder.Append(match.Value);
index += 1;
}
}
if (this.SortOrder < int.MaxValue)
{
// Match syntax
string toParse = stringBuilder.ToString();
float[] coordinates = this.ParseCoordinates(toParse);
CropMode cropMode = this.ParseMode(toParse);
CropLayer cropLayer = new CropLayer(coordinates[0], coordinates[1], coordinates[2], coordinates[3], cropMode);
this.DynamicParameter = cropLayer;
}
return this.SortOrder;
}
@ -129,10 +149,27 @@ namespace ImageProcessor.Processors
Image image = factory.Image;
try
{
Rectangle rectangle = this.DynamicParameter;
int sourceWidth = image.Width;
int sourceHeight = image.Height;
RectangleF rectangleF;
CropLayer cropLayer = this.DynamicParameter;
if (cropLayer.CropMode == CropMode.Percentage)
{
// Work out the percentages.
float left = cropLayer.Left * sourceWidth;
float top = cropLayer.Top * sourceWidth;
float right = (sourceWidth - (cropLayer.Right * sourceWidth)) - left;
float bottom = (sourceHeight - (cropLayer.Bottom * sourceHeight)) - top;
rectangleF = new RectangleF(left, top, right, bottom);
}
else
{
rectangleF = new RectangleF(cropLayer.Left, cropLayer.Top, cropLayer.Right, cropLayer.Bottom);
}
Rectangle rectangle = Rectangle.Round(rectangleF);
if (rectangle.X < sourceWidth && rectangle.Y < sourceHeight)
{
@ -190,5 +227,55 @@ namespace ImageProcessor.Processors
return image;
}
#endregion
/// <summary>
/// Returns the correct <see cref="CropMode"/> for the given string.
/// </summary>
/// <param name="input">
/// The input string containing the value to parse.
/// </param>
/// <returns>
/// The correct <see cref="CropMode"/>.
/// </returns>
private CropMode ParseMode(string input)
{
foreach (Match match in ModeRegex.Matches(input))
{
// Split on =
string mode = match.Value.Split('=')[1];
switch (mode)
{
case "percent":
return CropMode.Percentage;
case "pixels":
return CropMode.Pixels;
}
}
return CropMode.Pixels;
}
/// <summary>
/// Returns the correct <see cref="CropMode"/> for the given string.
/// </summary>
/// <param name="input">
/// The input string containing the value to parse.
/// </param>
/// <returns>
/// The correct <see cref="CropMode"/>.
/// </returns>
private float[] ParseCoordinates(string input)
{
float[] floats = { };
foreach (Match match in CoordinateRegex.Matches(input))
{
floats = match.Value.ToPositiveFloatArray();
}
return floats;
}
}
}

1
src/ImageProcessor/Processors/Resize.cs

@ -59,6 +59,7 @@ namespace ImageProcessor.Processors
/// The regular expression to search strings for the upscale attribute.
/// </summary>
private static readonly Regex UpscaleRegex = new Regex(@"upscale=false", RegexOptions.Compiled);
#region IGraphicsProcessor Members
/// <summary>
/// Gets the regular expression to search strings for.

28
src/TestWebsites/NET45/Test_Website_NET45/Views/Home/Index.cshtml

@ -14,7 +14,11 @@
</div>
<div class="col-s-6">
<h2>Cropped </h2>
<img src="/images/Penguins.jpg?crop=0-0-300-225" />
<img src="/images/Penguins.jpg?crop=0,0,300,225" />
<h3>Cropped Percent</h3>
<img src="/images/Penguins.jpg?width=300&height300&crop=0.1,0.2,0.1,0.6&cropmode=percent" />
</div>
</div>
</section>
@ -185,17 +189,17 @@
<article>
<h1>Color Profiles</h1>
@* <section>
<div class="row">
<div class="col-s-6">
<h2>CMYK original jpg</h2>
<img src="/images/cmyk.jpg?" width="400" />
</div>
<div class="col-s-6">
<h2>sRGB original jpg</h2>
<img src="/images/srgb.jpg?" width="400" />
</div>
</div>
</section>*@
<div class="row">
<div class="col-s-6">
<h2>CMYK original jpg</h2>
<img src="/images/cmyk.jpg?" width="400" />
</div>
<div class="col-s-6">
<h2>sRGB original jpg</h2>
<img src="/images/srgb.jpg?" width="400" />
</div>
</div>
</section>*@
<section>
<div class="row">
<div class="col-s-6">

Loading…
Cancel
Save