|
|
|
@ -1,11 +1,13 @@ |
|
|
|
// Copyright (c) Six Labors and contributors.
|
|
|
|
// Copyright (c) Six Labors and contributors.
|
|
|
|
// Licensed under the Apache License, Version 2.0.
|
|
|
|
|
|
|
|
using System; |
|
|
|
using System.Linq; |
|
|
|
using System.Numerics; |
|
|
|
|
|
|
|
using SixLabors.Primitives; |
|
|
|
|
|
|
|
namespace SixLabors.ImageSharp.Processing |
|
|
|
namespace SixLabors.ImageSharp.Processing.Processors.Transforms |
|
|
|
{ |
|
|
|
/// <summary>
|
|
|
|
/// Provides methods to help calculate the target rectangle when resizing using the
|
|
|
|
@ -13,6 +15,16 @@ namespace SixLabors.ImageSharp.Processing |
|
|
|
/// </summary>
|
|
|
|
internal static class ResizeHelper |
|
|
|
{ |
|
|
|
public static unsafe int CalculateResizeWorkerWindowCount( |
|
|
|
int windowDiameter, |
|
|
|
int width, |
|
|
|
int sizeLimitHintInBytes) |
|
|
|
{ |
|
|
|
int sizeLimitHint = sizeLimitHintInBytes / sizeof(Vector4); |
|
|
|
int sizeOfOneWindow = windowDiameter * width; |
|
|
|
return Math.Max(2, sizeLimitHint / sizeOfOneWindow); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Calculates the target location and bounds to perform the resize operation against.
|
|
|
|
/// </summary>
|
|
|
|
@ -21,9 +33,13 @@ namespace SixLabors.ImageSharp.Processing |
|
|
|
/// <param name="width">The target width</param>
|
|
|
|
/// <param name="height">The target height</param>
|
|
|
|
/// <returns>
|
|
|
|
/// The <see cref="ValueTuple{Size,Rectangle}"/>.
|
|
|
|
/// The tuple representing the location and the bounds
|
|
|
|
/// </returns>
|
|
|
|
public static (Size, Rectangle) CalculateTargetLocationAndBounds(Size sourceSize, ResizeOptions options, int width, int height) |
|
|
|
public static (Size, Rectangle) CalculateTargetLocationAndBounds( |
|
|
|
Size sourceSize, |
|
|
|
ResizeOptions options, |
|
|
|
int width, |
|
|
|
int height) |
|
|
|
{ |
|
|
|
switch (options.Mode) |
|
|
|
{ |
|
|
|
@ -44,7 +60,90 @@ namespace SixLabors.ImageSharp.Processing |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private static (Size, Rectangle) CalculateCropRectangle(Size source, ResizeOptions options, int width, int height) |
|
|
|
private static (Size, Rectangle) CalculateBoxPadRectangle( |
|
|
|
Size source, |
|
|
|
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)); |
|
|
|
} |
|
|
|
|
|
|
|
int sourceWidth = source.Width; |
|
|
|
int sourceHeight = source.Height; |
|
|
|
|
|
|
|
// Fractional variants for preserving aspect ratio.
|
|
|
|
float percentHeight = MathF.Abs(height / (float)sourceHeight); |
|
|
|
float percentWidth = MathF.Abs(width / (float)sourceWidth); |
|
|
|
|
|
|
|
int boxPadHeight = height > 0 ? height : (int)MathF.Round(sourceHeight * percentWidth); |
|
|
|
int boxPadWidth = width > 0 ? width : (int)MathF.Round(sourceWidth * percentHeight); |
|
|
|
|
|
|
|
// Only calculate if upscaling.
|
|
|
|
if (sourceWidth < boxPadWidth && sourceHeight < boxPadHeight) |
|
|
|
{ |
|
|
|
int destinationX; |
|
|
|
int destinationY; |
|
|
|
int destinationWidth = sourceWidth; |
|
|
|
int destinationHeight = sourceHeight; |
|
|
|
width = boxPadWidth; |
|
|
|
height = boxPadHeight; |
|
|
|
|
|
|
|
switch (options.Position) |
|
|
|
{ |
|
|
|
case AnchorPositionMode.Left: |
|
|
|
destinationY = (height - sourceHeight) / 2; |
|
|
|
destinationX = 0; |
|
|
|
break; |
|
|
|
case AnchorPositionMode.Right: |
|
|
|
destinationY = (height - sourceHeight) / 2; |
|
|
|
destinationX = width - sourceWidth; |
|
|
|
break; |
|
|
|
case AnchorPositionMode.TopRight: |
|
|
|
destinationY = 0; |
|
|
|
destinationX = width - sourceWidth; |
|
|
|
break; |
|
|
|
case AnchorPositionMode.Top: |
|
|
|
destinationY = 0; |
|
|
|
destinationX = (width - sourceWidth) / 2; |
|
|
|
break; |
|
|
|
case AnchorPositionMode.TopLeft: |
|
|
|
destinationY = 0; |
|
|
|
destinationX = 0; |
|
|
|
break; |
|
|
|
case AnchorPositionMode.BottomRight: |
|
|
|
destinationY = height - sourceHeight; |
|
|
|
destinationX = width - sourceWidth; |
|
|
|
break; |
|
|
|
case AnchorPositionMode.Bottom: |
|
|
|
destinationY = height - sourceHeight; |
|
|
|
destinationX = (width - sourceWidth) / 2; |
|
|
|
break; |
|
|
|
case AnchorPositionMode.BottomLeft: |
|
|
|
destinationY = height - sourceHeight; |
|
|
|
destinationX = 0; |
|
|
|
break; |
|
|
|
default: |
|
|
|
destinationY = (height - sourceHeight) / 2; |
|
|
|
destinationX = (width - sourceWidth) / 2; |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
return (new Size(width, height), |
|
|
|
new Rectangle(destinationX, destinationY, destinationWidth, destinationHeight)); |
|
|
|
} |
|
|
|
|
|
|
|
// Switch to pad mode to downscale and calculate from there.
|
|
|
|
return CalculatePadRectangle(source, options, width, height); |
|
|
|
} |
|
|
|
|
|
|
|
private static (Size, Rectangle) CalculateCropRectangle( |
|
|
|
Size source, |
|
|
|
ResizeOptions options, |
|
|
|
int width, |
|
|
|
int height) |
|
|
|
{ |
|
|
|
if (width <= 0 || height <= 0) |
|
|
|
{ |
|
|
|
@ -147,152 +246,15 @@ namespace SixLabors.ImageSharp.Processing |
|
|
|
destinationWidth = (int)MathF.Ceiling(sourceWidth * percentHeight); |
|
|
|
} |
|
|
|
|
|
|
|
return (new Size(width, height), new Rectangle(destinationX, destinationY, destinationWidth, destinationHeight)); |
|
|
|
} |
|
|
|
|
|
|
|
private static (Size, Rectangle) CalculatePadRectangle(Size source, 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 destinationX = 0; |
|
|
|
int destinationY = 0; |
|
|
|
int destinationWidth = width; |
|
|
|
int destinationHeight = height; |
|
|
|
|
|
|
|
// Fractional variants for preserving aspect ratio.
|
|
|
|
float percentHeight = MathF.Abs(height / (float)sourceHeight); |
|
|
|
float percentWidth = MathF.Abs(width / (float)sourceWidth); |
|
|
|
|
|
|
|
if (percentHeight < percentWidth) |
|
|
|
{ |
|
|
|
ratio = percentHeight; |
|
|
|
destinationWidth = (int)MathF.Round(sourceWidth * percentHeight); |
|
|
|
|
|
|
|
switch (options.Position) |
|
|
|
{ |
|
|
|
case AnchorPositionMode.Left: |
|
|
|
case AnchorPositionMode.TopLeft: |
|
|
|
case AnchorPositionMode.BottomLeft: |
|
|
|
destinationX = 0; |
|
|
|
break; |
|
|
|
case AnchorPositionMode.Right: |
|
|
|
case AnchorPositionMode.TopRight: |
|
|
|
case AnchorPositionMode.BottomRight: |
|
|
|
destinationX = (int)MathF.Round(width - (sourceWidth * ratio)); |
|
|
|
break; |
|
|
|
default: |
|
|
|
destinationX = (int)MathF.Round((width - (sourceWidth * ratio)) / 2F); |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
ratio = percentWidth; |
|
|
|
destinationHeight = (int)MathF.Round(sourceHeight * percentWidth); |
|
|
|
|
|
|
|
switch (options.Position) |
|
|
|
{ |
|
|
|
case AnchorPositionMode.Top: |
|
|
|
case AnchorPositionMode.TopLeft: |
|
|
|
case AnchorPositionMode.TopRight: |
|
|
|
destinationY = 0; |
|
|
|
break; |
|
|
|
case AnchorPositionMode.Bottom: |
|
|
|
case AnchorPositionMode.BottomLeft: |
|
|
|
case AnchorPositionMode.BottomRight: |
|
|
|
destinationY = (int)MathF.Round(height - (sourceHeight * ratio)); |
|
|
|
break; |
|
|
|
default: |
|
|
|
destinationY = (int)MathF.Round((height - (sourceHeight * ratio)) / 2F); |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return (new Size(width, height), new Rectangle(destinationX, destinationY, destinationWidth, destinationHeight)); |
|
|
|
} |
|
|
|
|
|
|
|
private static (Size, Rectangle) CalculateBoxPadRectangle(Size source, 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)); |
|
|
|
} |
|
|
|
|
|
|
|
int sourceWidth = source.Width; |
|
|
|
int sourceHeight = source.Height; |
|
|
|
|
|
|
|
// Fractional variants for preserving aspect ratio.
|
|
|
|
float percentHeight = MathF.Abs(height / (float)sourceHeight); |
|
|
|
float percentWidth = MathF.Abs(width / (float)sourceWidth); |
|
|
|
|
|
|
|
int boxPadHeight = height > 0 ? height : (int)MathF.Round(sourceHeight * percentWidth); |
|
|
|
int boxPadWidth = width > 0 ? width : (int)MathF.Round(sourceWidth * percentHeight); |
|
|
|
|
|
|
|
// Only calculate if upscaling.
|
|
|
|
if (sourceWidth < boxPadWidth && sourceHeight < boxPadHeight) |
|
|
|
{ |
|
|
|
int destinationX; |
|
|
|
int destinationY; |
|
|
|
int destinationWidth = sourceWidth; |
|
|
|
int destinationHeight = sourceHeight; |
|
|
|
width = boxPadWidth; |
|
|
|
height = boxPadHeight; |
|
|
|
|
|
|
|
switch (options.Position) |
|
|
|
{ |
|
|
|
case AnchorPositionMode.Left: |
|
|
|
destinationY = (height - sourceHeight) / 2; |
|
|
|
destinationX = 0; |
|
|
|
break; |
|
|
|
case AnchorPositionMode.Right: |
|
|
|
destinationY = (height - sourceHeight) / 2; |
|
|
|
destinationX = width - sourceWidth; |
|
|
|
break; |
|
|
|
case AnchorPositionMode.TopRight: |
|
|
|
destinationY = 0; |
|
|
|
destinationX = width - sourceWidth; |
|
|
|
break; |
|
|
|
case AnchorPositionMode.Top: |
|
|
|
destinationY = 0; |
|
|
|
destinationX = (width - sourceWidth) / 2; |
|
|
|
break; |
|
|
|
case AnchorPositionMode.TopLeft: |
|
|
|
destinationY = 0; |
|
|
|
destinationX = 0; |
|
|
|
break; |
|
|
|
case AnchorPositionMode.BottomRight: |
|
|
|
destinationY = height - sourceHeight; |
|
|
|
destinationX = width - sourceWidth; |
|
|
|
break; |
|
|
|
case AnchorPositionMode.Bottom: |
|
|
|
destinationY = height - sourceHeight; |
|
|
|
destinationX = (width - sourceWidth) / 2; |
|
|
|
break; |
|
|
|
case AnchorPositionMode.BottomLeft: |
|
|
|
destinationY = height - sourceHeight; |
|
|
|
destinationX = 0; |
|
|
|
break; |
|
|
|
default: |
|
|
|
destinationY = (height - sourceHeight) / 2; |
|
|
|
destinationX = (width - sourceWidth) / 2; |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
return (new Size(width, height), new Rectangle(destinationX, destinationY, destinationWidth, destinationHeight)); |
|
|
|
} |
|
|
|
|
|
|
|
// Switch to pad mode to downscale and calculate from there.
|
|
|
|
return CalculatePadRectangle(source, options, width, height); |
|
|
|
return (new Size(width, height), |
|
|
|
new Rectangle(destinationX, destinationY, destinationWidth, destinationHeight)); |
|
|
|
} |
|
|
|
|
|
|
|
private static (Size, Rectangle) CalculateMaxRectangle(Size source, ResizeOptions options, int width, int height) |
|
|
|
private static (Size, Rectangle) CalculateMaxRectangle( |
|
|
|
Size source, |
|
|
|
ResizeOptions options, |
|
|
|
int width, |
|
|
|
int height) |
|
|
|
{ |
|
|
|
int destinationWidth = width; |
|
|
|
int destinationHeight = height; |
|
|
|
@ -320,7 +282,11 @@ namespace SixLabors.ImageSharp.Processing |
|
|
|
return (new Size(width, height), new Rectangle(0, 0, destinationWidth, destinationHeight)); |
|
|
|
} |
|
|
|
|
|
|
|
private static (Size, Rectangle) CalculateMinRectangle(Size source, ResizeOptions options, int width, int height) |
|
|
|
private static (Size, Rectangle) CalculateMinRectangle( |
|
|
|
Size source, |
|
|
|
ResizeOptions options, |
|
|
|
int width, |
|
|
|
int height) |
|
|
|
{ |
|
|
|
int sourceWidth = source.Width; |
|
|
|
int sourceHeight = source.Height; |
|
|
|
@ -372,5 +338,78 @@ namespace SixLabors.ImageSharp.Processing |
|
|
|
// Replace the size to match the rectangle.
|
|
|
|
return (new Size(width, height), new Rectangle(0, 0, destinationWidth, destinationHeight)); |
|
|
|
} |
|
|
|
|
|
|
|
private static (Size, Rectangle) CalculatePadRectangle( |
|
|
|
Size source, |
|
|
|
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 destinationX = 0; |
|
|
|
int destinationY = 0; |
|
|
|
int destinationWidth = width; |
|
|
|
int destinationHeight = height; |
|
|
|
|
|
|
|
// Fractional variants for preserving aspect ratio.
|
|
|
|
float percentHeight = MathF.Abs(height / (float)sourceHeight); |
|
|
|
float percentWidth = MathF.Abs(width / (float)sourceWidth); |
|
|
|
|
|
|
|
if (percentHeight < percentWidth) |
|
|
|
{ |
|
|
|
ratio = percentHeight; |
|
|
|
destinationWidth = (int)MathF.Round(sourceWidth * percentHeight); |
|
|
|
|
|
|
|
switch (options.Position) |
|
|
|
{ |
|
|
|
case AnchorPositionMode.Left: |
|
|
|
case AnchorPositionMode.TopLeft: |
|
|
|
case AnchorPositionMode.BottomLeft: |
|
|
|
destinationX = 0; |
|
|
|
break; |
|
|
|
case AnchorPositionMode.Right: |
|
|
|
case AnchorPositionMode.TopRight: |
|
|
|
case AnchorPositionMode.BottomRight: |
|
|
|
destinationX = (int)MathF.Round(width - (sourceWidth * ratio)); |
|
|
|
break; |
|
|
|
default: |
|
|
|
destinationX = (int)MathF.Round((width - (sourceWidth * ratio)) / 2F); |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
ratio = percentWidth; |
|
|
|
destinationHeight = (int)MathF.Round(sourceHeight * percentWidth); |
|
|
|
|
|
|
|
switch (options.Position) |
|
|
|
{ |
|
|
|
case AnchorPositionMode.Top: |
|
|
|
case AnchorPositionMode.TopLeft: |
|
|
|
case AnchorPositionMode.TopRight: |
|
|
|
destinationY = 0; |
|
|
|
break; |
|
|
|
case AnchorPositionMode.Bottom: |
|
|
|
case AnchorPositionMode.BottomLeft: |
|
|
|
case AnchorPositionMode.BottomRight: |
|
|
|
destinationY = (int)MathF.Round(height - (sourceHeight * ratio)); |
|
|
|
break; |
|
|
|
default: |
|
|
|
destinationY = (int)MathF.Round((height - (sourceHeight * ratio)) / 2F); |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return (new Size(width, height), |
|
|
|
new Rectangle(destinationX, destinationY, destinationWidth, destinationHeight)); |
|
|
|
} |
|
|
|
} |
|
|
|
} |