Skip to content

Commit

Permalink
Optimize resize helper
Browse files Browse the repository at this point in the history
  • Loading branch information
JimBobSquarePants committed Oct 22, 2019
1 parent b7c4bd2 commit 7d78769
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 84 deletions.
4 changes: 1 addition & 3 deletions src/ImageProcessor/Processing/Resize.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@
// Licensed under the Apache License, Version 2.0.

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Linq;

namespace ImageProcessor.Processing
{
Expand Down Expand Up @@ -188,7 +186,7 @@ public class ResizeOptions
/// <summary>
/// Gets or sets the center coordinates.
/// </summary>
public IEnumerable<float> CenterCoordinates { get; set; } = Enumerable.Empty<float>();
public PointF? CenterCoordinates { get; set; }

/// <summary>
/// Gets or sets the target size.
Expand Down
147 changes: 66 additions & 81 deletions src/ImageProcessor/Processing/ResizeHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

using System;
using System.Drawing;
using System.Linq;

namespace ImageProcessor.Processing
{
Expand All @@ -29,6 +28,11 @@ public static (Size, Rectangle) CalculateTargetLocationAndBounds(
int width,
int height)
{
if (width <= 0 && height <= 0)
{
ThrowInvalid($"Target width {width} and height {height} must be greater than zero.");
}

switch (options.Mode)
{
case ResizeMode.Crop:
Expand All @@ -38,7 +42,7 @@ public static (Size, Rectangle) CalculateTargetLocationAndBounds(
case ResizeMode.BoxPad:
return CalculateBoxPadRectangle(sourceSize, options, width, height);
case ResizeMode.Max:
return CalculateMaxRectangle(sourceSize, options, width, height);
return CalculateMaxRectangle(sourceSize, width, height);
case ResizeMode.Min:
return CalculateMinRectangle(sourceSize, width, height);

Expand Down Expand Up @@ -133,19 +137,14 @@ private static (Size, Rectangle) CalculateCropRectangle(
int width,
int height)
{
if (width <= 0 || height <= 0)
{
return (new Size(source.Width, source.Height), new Rectangle(0, 0, source.Width, source.Height));
}

float ratio;
int sourceWidth = source.Width;
int sourceHeight = source.Height;

int destinationX = 0;
int destinationY = 0;
int destinationWidth = width;
int destinationHeight = height;
int targetX = 0;
int targetY = 0;
int targetWidth = width;
int targetHeight = height;

// Fractional variants for preserving aspect ratio.
float percentHeight = Math.Abs(height / (float)sourceHeight);
Expand All @@ -155,19 +154,19 @@ private static (Size, Rectangle) CalculateCropRectangle(
{
ratio = percentWidth;

if (options.CenterCoordinates.Any())
if (options.CenterCoordinates.HasValue)
{
float center = -(ratio * sourceHeight) * options.CenterCoordinates.ToArray()[1];
destinationY = (int)Math.Round(center + (height / 2F));
float center = -(ratio * sourceHeight) * options.CenterCoordinates.Value.Y;
targetY = (int)Math.Round(center + (height / 2F));

if (destinationY > 0)
if (targetY > 0)
{
destinationY = 0;
targetY = 0;
}

if (destinationY < (int)Math.Round(height - (sourceHeight * ratio)))
if (targetY < (int)Math.Round(height - (sourceHeight * ratio)))
{
destinationY = (int)Math.Round(height - (sourceHeight * ratio));
targetY = (int)Math.Round(height - (sourceHeight * ratio));
}
}
else
Expand All @@ -177,38 +176,38 @@ private static (Size, Rectangle) CalculateCropRectangle(
case AnchorPositionMode.Top:
case AnchorPositionMode.TopLeft:
case AnchorPositionMode.TopRight:
destinationY = 0;
targetY = 0;
break;
case AnchorPositionMode.Bottom:
case AnchorPositionMode.BottomLeft:
case AnchorPositionMode.BottomRight:
destinationY = (int)Math.Round(height - (sourceHeight * ratio));
targetY = (int)Math.Round(height - (sourceHeight * ratio));
break;
default:
destinationY = (int)Math.Round((height - (sourceHeight * ratio)) / 2F);
targetY = (int)Math.Round((height - (sourceHeight * ratio)) / 2F);
break;
}
}

destinationHeight = (int)Math.Ceiling(sourceHeight * percentWidth);
targetHeight = (int)Math.Ceiling(sourceHeight * percentWidth);
}
else
{
ratio = percentHeight;

if (options.CenterCoordinates.Any())
if (options.CenterCoordinates.HasValue)
{
float center = -(ratio * sourceWidth) * options.CenterCoordinates.First();
destinationX = (int)Math.Round(center + (width / 2F));
float center = -(ratio * sourceWidth) * options.CenterCoordinates.Value.X;
targetX = (int)Math.Round(center + (width / 2F));

if (destinationX > 0)
if (targetX > 0)
{
destinationX = 0;
targetX = 0;
}

if (destinationX < (int)Math.Round(width - (sourceWidth * ratio)))
if (targetX < (int)Math.Round(width - (sourceWidth * ratio)))
{
destinationX = (int)Math.Round(width - (sourceWidth * ratio));
targetX = (int)Math.Round(width - (sourceWidth * ratio));
}
}
else
Expand All @@ -218,56 +217,53 @@ private static (Size, Rectangle) CalculateCropRectangle(
case AnchorPositionMode.Left:
case AnchorPositionMode.TopLeft:
case AnchorPositionMode.BottomLeft:
destinationX = 0;
targetX = 0;
break;
case AnchorPositionMode.Right:
case AnchorPositionMode.TopRight:
case AnchorPositionMode.BottomRight:
destinationX = (int)Math.Round(width - (sourceWidth * ratio));
targetX = (int)Math.Round(width - (sourceWidth * ratio));
break;
default:
destinationX = (int)Math.Round((width - (sourceWidth * ratio)) / 2F);
targetX = (int)Math.Round((width - (sourceWidth * ratio)) / 2F);
break;
}
}

destinationWidth = (int)Math.Ceiling(sourceWidth * percentHeight);
targetWidth = (int)Math.Ceiling(sourceWidth * percentHeight);
}

return (new Size(width, height),
new Rectangle(destinationX, destinationY, destinationWidth, destinationHeight));
// Target image width and height can be different to the rectangle width and height.
return (new Size(width, height), new Rectangle(targetX, targetY, targetWidth, targetHeight));
}

private static (Size, Rectangle) CalculateMaxRectangle(
Size source,
ResizeOptions options,
int width,
int height)
{
int destinationWidth = width;
int destinationHeight = height;
int targetWidth = width;
int targetHeight = height;

// Fractional variants for preserving aspect ratio.
float percentHeight = Math.Abs(height / (float)source.Height);
float percentWidth = Math.Abs(width / (float)source.Width);

// Integers must be cast to floats to get needed precision
float ratio = options.Size.Height / (float)options.Size.Width;
float ratio = height / (float)width;
float sourceRatio = source.Height / (float)source.Width;

if (sourceRatio < ratio)
{
destinationHeight = (int)Math.Round(source.Height * percentWidth);
height = destinationHeight;
targetHeight = (int)Math.Round(source.Height * percentWidth);
}
else
{
destinationWidth = (int)Math.Round(source.Width * percentHeight);
width = destinationWidth;
targetWidth = (int)Math.Round(source.Width * percentHeight);
}

// Replace the size to match the rectangle.
return (new Size(width, height), new Rectangle(0, 0, destinationWidth, destinationHeight));
return (new Size(targetWidth, targetHeight), new Rectangle(0, 0, targetWidth, targetHeight));
}

private static (Size, Rectangle) CalculateMinRectangle(
Expand All @@ -277,8 +273,8 @@ private static (Size, Rectangle) CalculateMinRectangle(
{
int sourceWidth = source.Width;
int sourceHeight = source.Height;
int destinationWidth;
int destinationHeight;
int targetWidth = width;
int targetHeight = height;

// Don't upscale
if (width > sourceWidth || height > sourceHeight)
Expand All @@ -293,58 +289,45 @@ private static (Size, Rectangle) CalculateMinRectangle(
if (widthDiff < heightDiff)
{
float sourceRatio = (float)sourceHeight / sourceWidth;
destinationHeight = (int)Math.Round(width * sourceRatio);
height = destinationHeight;
destinationWidth = width;
targetHeight = (int)Math.Round(width * sourceRatio);
}
else if (widthDiff > heightDiff)
{
float sourceRatioInverse = (float)sourceWidth / sourceHeight;
destinationWidth = (int)Math.Round(height * sourceRatioInverse);
destinationHeight = height;
width = destinationWidth;
targetWidth = (int)Math.Round(height * sourceRatioInverse);
}
else
{
if (height > width)
{
destinationWidth = width;
float percentWidth = Math.Abs(width / (float)sourceWidth);
destinationHeight = (int)Math.Round(sourceHeight * percentWidth);
height = destinationHeight;
targetHeight = (int)Math.Round(sourceHeight * percentWidth);
}
else
{
destinationHeight = height;
float percentHeight = Math.Abs(height / (float)sourceHeight);
destinationWidth = (int)Math.Round(sourceWidth * percentHeight);
width = destinationWidth;
targetWidth = (int)Math.Round(sourceWidth * percentHeight);
}
}

// Replace the size to match the rectangle.
return (new Size(width, height), new Rectangle(0, 0, destinationWidth, destinationHeight));
return (new Size(targetWidth, targetHeight), new Rectangle(0, 0, targetWidth, targetHeight));
}

private static (Size, Rectangle) CalculatePadRectangle(
Size source,
Size sourceSize,
ResizeOptions options,
int width,
int height)
{
if (width <= 0 || height <= 0)
{
return (new Size(source.Width, source.Height), new Rectangle(0, 0, source.Width, source.Height));
}

float ratio;
int sourceWidth = source.Width;
int sourceHeight = source.Height;
int sourceWidth = sourceSize.Width;
int sourceHeight = sourceSize.Height;

int destinationX = 0;
int destinationY = 0;
int destinationWidth = width;
int destinationHeight = height;
int targetX = 0;
int targetY = 0;
int targetWidth = width;
int targetHeight = height;

// Fractional variants for preserving aspect ratio.
float percentHeight = Math.Abs(height / (float)sourceHeight);
Expand All @@ -353,50 +336,52 @@ private static (Size, Rectangle) CalculatePadRectangle(
if (percentHeight < percentWidth)
{
ratio = percentHeight;
destinationWidth = (int)Math.Round(sourceWidth * percentHeight);
targetWidth = (int)Math.Round(sourceWidth * percentHeight);

switch (options.Position)
{
case AnchorPositionMode.Left:
case AnchorPositionMode.TopLeft:
case AnchorPositionMode.BottomLeft:
destinationX = 0;
targetX = 0;
break;
case AnchorPositionMode.Right:
case AnchorPositionMode.TopRight:
case AnchorPositionMode.BottomRight:
destinationX = (int)Math.Round(width - (sourceWidth * ratio));
targetX = (int)Math.Round(width - (sourceWidth * ratio));
break;
default:
destinationX = (int)Math.Round((width - (sourceWidth * ratio)) / 2F);
targetX = (int)Math.Round((width - (sourceWidth * ratio)) / 2F);
break;
}
}
else
{
ratio = percentWidth;
destinationHeight = (int)Math.Round(sourceHeight * percentWidth);
targetHeight = (int)Math.Round(sourceHeight * percentWidth);

switch (options.Position)
{
case AnchorPositionMode.Top:
case AnchorPositionMode.TopLeft:
case AnchorPositionMode.TopRight:
destinationY = 0;
targetY = 0;
break;
case AnchorPositionMode.Bottom:
case AnchorPositionMode.BottomLeft:
case AnchorPositionMode.BottomRight:
destinationY = (int)Math.Round(height - (sourceHeight * ratio));
targetY = (int)Math.Round(height - (sourceHeight * ratio));
break;
default:
destinationY = (int)Math.Round((height - (sourceHeight * ratio)) / 2F);
targetY = (int)Math.Round((height - (sourceHeight * ratio)) / 2F);
break;
}
}

return (new Size(width, height),
new Rectangle(destinationX, destinationY, destinationWidth, destinationHeight));
// Target image width and height can be different to the rectangle width and height.
return (new Size(width, height), new Rectangle(targetX, targetY, targetWidth, targetHeight));
}

private static void ThrowInvalid(string message) => throw new InvalidOperationException(message);
}
}

0 comments on commit 7d78769

Please sign in to comment.