@ -2,9 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System ;
using System.Linq ;
using System.Numerics ;
using SixLabors.Primitives ;
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
@ -30,17 +28,34 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// </summary>
/// <param name="sourceSize">The source image size.</param>
/// <param name="options">The resize options.</param>
/// <param name="width">The target width</param>
/// <param name="height">The target height</param>
/// <returns>
/// 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 = options . Size . Width ;
int height = options . Size . Height ;
if ( width < = 0 & & height < = 0 )
{
ThrowInvalid ( $"Target width {width} and height {height} must be greater than zero." ) ;
}
// Ensure target size is populated across both dimensions.
// These dimensions are used to calculate the final dimensions determined by the mode algorithm.
// If only one of the incoming dimensions is 0, it will be modified here to maintain aspect ratio.
// If it is not possible to keep aspect ratio, make sure at least the minimum is is kept.
const int Min = 1 ;
if ( width = = 0 & & height > 0 )
{
width = ( int ) MathF . Max ( Min , MathF . Round ( sourceSize . Width * height / ( float ) sourceSize . Height ) ) ;
}
if ( height = = 0 & & width > 0 )
{
height = ( int ) MathF . Max ( Min , MathF . Round ( sourceSize . Height * width / ( float ) sourceSize . Width ) ) ;
}
switch ( options . Mode )
{
case ResizeMode . Crop :
@ -50,11 +65,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
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 , options , width , height ) ;
return CalculateMinRectangle ( sourceSize , width , height ) ;
case ResizeMode . Manual :
return CalculateManualRectangle ( options , width , height ) ;
// Last case ResizeMode.Stretch:
// case ResizeMode.Stretch:
default :
return ( new Size ( width , height ) , new Rectangle ( 0 , 0 , width , height ) ) ;
}
@ -66,11 +83,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
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 ;
@ -84,55 +96,55 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
// Only calculate if upscaling.
if ( sourceWidth < boxPadWidth & & sourceHeight < boxPadHeight )
{
int des tin ation X;
int des tin ation Y;
int des tin ation Width = sourceWidth ;
int des tin ation Height = sourceHeight ;
int targe tX ;
int targe tY ;
int targe tWidth = sourceWidth ;
int targe tHeight = sourceHeight ;
width = boxPadWidth ;
height = boxPadHeight ;
switch ( options . Position )
{
case AnchorPositionMode . Left :
des tin ation Y = ( height - sourceHeight ) / 2 ;
des tin ation X = 0 ;
targe tY = ( height - sourceHeight ) / 2 ;
targe tX = 0 ;
break ;
case AnchorPositionMode . Right :
des tin ation Y = ( height - sourceHeight ) / 2 ;
des tin ation X = width - sourceWidth ;
targe tY = ( height - sourceHeight ) / 2 ;
targe tX = width - sourceWidth ;
break ;
case AnchorPositionMode . TopRight :
des tin ation Y = 0 ;
des tin ation X = width - sourceWidth ;
targe tY = 0 ;
targe tX = width - sourceWidth ;
break ;
case AnchorPositionMode . Top :
des tin ation Y = 0 ;
des tin ation X = ( width - sourceWidth ) / 2 ;
targe tY = 0 ;
targe tX = ( width - sourceWidth ) / 2 ;
break ;
case AnchorPositionMode . TopLeft :
des tin ation Y = 0 ;
des tin ation X = 0 ;
targe tY = 0 ;
targe tX = 0 ;
break ;
case AnchorPositionMode . BottomRight :
des tin ation Y = height - sourceHeight ;
des tin ation X = width - sourceWidth ;
targe tY = height - sourceHeight ;
targe tX = width - sourceWidth ;
break ;
case AnchorPositionMode . Bottom :
des tin ation Y = height - sourceHeight ;
des tin ation X = ( width - sourceWidth ) / 2 ;
targe tY = height - sourceHeight ;
targe tX = ( width - sourceWidth ) / 2 ;
break ;
case AnchorPositionMode . BottomLeft :
des tin ation Y = height - sourceHeight ;
des tin ation X = 0 ;
targe tY = height - sourceHeight ;
targe tX = 0 ;
break ;
default :
des tin ation Y = ( height - sourceHeight ) / 2 ;
des tin ation X = ( width - sourceWidth ) / 2 ;
targe tY = ( height - sourceHeight ) / 2 ;
targe tX = ( width - sourceWidth ) / 2 ;
break ;
}
return ( new Size ( width , height ) ,
new Rectangle ( des tin ation X, des tin ation Y, des tin ation Width, des tin ation Height) ) ;
// Target image width and height can be different to the rectangle width and height.
return ( new Size ( width , height ) , new Rectangle ( targe tX , targe tY , targe tWidth , targe tHeight ) ) ;
}
// Switch to pad mode to downscale and calculate from there.
@ -145,19 +157,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
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 des tin ation X = 0 ;
int des tin ation Y = 0 ;
int des tin ation Width = width ;
int des tin ation Height = height ;
int targetX = 0 ;
int targe tY = 0 ;
int targe tWidth = width ;
int targe tHeight = height ;
// Fractional variants for preserving aspect ratio.
float percentHeight = MathF . Abs ( height / ( float ) sourceHeight ) ;
@ -167,19 +174,19 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
ratio = percentWidth ;
if ( options . CenterCoordinates . Any ( ) )
if ( options . CenterCoordinates . HasValue )
{
float center = - ( ratio * sourceHeight ) * options . CenterCoordinates . ToArray ( ) [ 1 ] ;
des tin ation Y = ( int ) MathF . Round ( center + ( height / 2F ) ) ;
float center = - ( ratio * sourceHeight ) * options . CenterCoordinates . Value . Y ;
targe tY = ( int ) MathF . Round ( center + ( height / 2F ) ) ;
if ( des tin ation Y > 0 )
if ( targe tY > 0 )
{
des tin ation Y = 0 ;
targe tY = 0 ;
}
if ( des tin ation Y < ( int ) MathF . Round ( height - ( sourceHeight * ratio ) ) )
if ( targe tY < ( int ) MathF . Round ( height - ( sourceHeight * ratio ) ) )
{
des tin ation Y = ( int ) MathF . Round ( height - ( sourceHeight * ratio ) ) ;
targe tY = ( int ) MathF . Round ( height - ( sourceHeight * ratio ) ) ;
}
}
else
@ -189,38 +196,38 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
case AnchorPositionMode . Top :
case AnchorPositionMode . TopLeft :
case AnchorPositionMode . TopRight :
des tin ation Y = 0 ;
targe tY = 0 ;
break ;
case AnchorPositionMode . Bottom :
case AnchorPositionMode . BottomLeft :
case AnchorPositionMode . BottomRight :
des tin ation Y = ( int ) MathF . Round ( height - ( sourceHeight * ratio ) ) ;
targe tY = ( int ) MathF . Round ( height - ( sourceHeight * ratio ) ) ;
break ;
default :
des tin ation Y = ( int ) MathF . Round ( ( height - ( sourceHeight * ratio ) ) / 2F ) ;
targe tY = ( int ) MathF . Round ( ( height - ( sourceHeight * ratio ) ) / 2F ) ;
break ;
}
}
des tin ation Height = ( int ) MathF . Ceiling ( sourceHeight * percentWidth ) ;
targe tHeight = ( int ) MathF . Ceiling ( sourceHeight * percentWidth ) ;
}
else
{
ratio = percentHeight ;
if ( options . CenterCoordinates . Any ( ) )
if ( options . CenterCoordinates . HasValue )
{
float center = - ( ratio * sourceWidth ) * options . CenterCoordinates . First ( ) ;
des tin ation X = ( int ) MathF . Round ( center + ( width / 2F ) ) ;
float center = - ( ratio * sourceWidth ) * options . CenterCoordinates . Value . X ;
targe tX = ( int ) MathF . Round ( center + ( width / 2F ) ) ;
if ( des tin ation X > 0 )
if ( targe tX > 0 )
{
des tin ation X = 0 ;
targe tX = 0 ;
}
if ( des tin ation X < ( int ) MathF . Round ( width - ( sourceWidth * ratio ) ) )
if ( targe tX < ( int ) MathF . Round ( width - ( sourceWidth * ratio ) ) )
{
des tin ation X = ( int ) MathF . Round ( width - ( sourceWidth * ratio ) ) ;
targe tX = ( int ) MathF . Round ( width - ( sourceWidth * ratio ) ) ;
}
}
else
@ -230,68 +237,64 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
case AnchorPositionMode . Left :
case AnchorPositionMode . TopLeft :
case AnchorPositionMode . BottomLeft :
des tin ation X = 0 ;
targe tX = 0 ;
break ;
case AnchorPositionMode . Right :
case AnchorPositionMode . TopRight :
case AnchorPositionMode . BottomRight :
des tin ation X = ( int ) MathF . Round ( width - ( sourceWidth * ratio ) ) ;
targe tX = ( int ) MathF . Round ( width - ( sourceWidth * ratio ) ) ;
break ;
default :
des tin ation X = ( int ) MathF . Round ( ( width - ( sourceWidth * ratio ) ) / 2F ) ;
targe tX = ( int ) MathF . Round ( ( width - ( sourceWidth * ratio ) ) / 2F ) ;
break ;
}
}
des tin ation Width = ( int ) MathF . Ceiling ( sourceWidth * percentHeight ) ;
targe tWidth = ( int ) MathF . Ceiling ( sourceWidth * percentHeight ) ;
}
return ( new Size ( width , height ) ,
new Rectangle ( des tin ation X, des tin ation Y, des tin ation Width, des tin ation Height) ) ;
// Target image width and height can be different to the rectangle width and height.
return ( new Size ( width , height ) , new Rectangle ( targe tX , targe tY , targe tWidth , targe tHeight ) ) ;
}
private static ( Size , Rectangle ) CalculateMaxRectangle (
Size source ,
ResizeOptions options ,
int width ,
int height )
{
int des tin ation Width = width ;
int des tin ation Height = height ;
int targe tWidth = width ;
int targe tHeight = height ;
// Fractional variants for preserving aspect ratio.
float percentHeight = MathF . Abs ( height / ( float ) source . Height ) ;
float percentWidth = MathF . Abs ( width / ( float ) source . Width ) ;
// Integers must be cast to floats to get needed precision
float ratio = options . Size . H eight / ( float ) options . Size . W idth;
float ratio = h eight / ( float ) w idth;
float sourceRatio = source . Height / ( float ) source . Width ;
if ( sourceRatio < ratio )
{
destinationHeight = ( int ) MathF . Round ( source . Height * percentWidth ) ;
height = destinationHeight ;
targetHeight = ( int ) MathF . Round ( source . Height * percentWidth ) ;
}
else
{
destinationWidth = ( int ) MathF . Round ( source . Width * percentHeight ) ;
width = destinationWidth ;
targetWidth = ( int ) MathF . Round ( source . Width * percentHeight ) ;
}
// Replace the size to match the rectangle.
return ( new Size ( width , h eight) , new Rectangle ( 0 , 0 , des tin ation Width, des tin ation Height) ) ;
return ( new Size ( targetWidth , targetH eight) , new Rectangle ( 0 , 0 , targe tWidth , targe tHeight ) ) ;
}
private static ( Size , Rectangle ) CalculateMinRectangle (
Size source ,
ResizeOptions options ,
int width ,
int height )
{
int sourceWidth = source . Width ;
int sourceHeight = source . Height ;
int destinationW idth;
int destinationH eight;
int targetWidth = w idth;
int targetHeight = h eight;
// Don't upscale
if ( width > sourceWidth | | height > sourceHeight )
@ -306,58 +309,45 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
if ( widthDiff < heightDiff )
{
float sourceRatio = ( float ) sourceHeight / sourceWidth ;
destinationHeight = ( int ) MathF . Round ( width * sourceRatio ) ;
height = destinationHeight ;
destinationWidth = width ;
targetHeight = ( int ) MathF . Round ( width * sourceRatio ) ;
}
else if ( widthDiff > heightDiff )
{
float sourceRatioInverse = ( float ) sourceWidth / sourceHeight ;
destinationWidth = ( int ) MathF . Round ( height * sourceRatioInverse ) ;
destinationHeight = height ;
width = destinationWidth ;
targetWidth = ( int ) MathF . Round ( height * sourceRatioInverse ) ;
}
else
{
if ( height > width )
{
destinationWidth = width ;
float percentWidth = MathF . Abs ( width / ( float ) sourceWidth ) ;
destinationHeight = ( int ) MathF . Round ( sourceHeight * percentWidth ) ;
height = destinationHeight ;
targetHeight = ( int ) MathF . Round ( sourceHeight * percentWidth ) ;
}
else
{
destinationHeight = height ;
float percentHeight = MathF . Abs ( height / ( float ) sourceHeight ) ;
destinationWidth = ( int ) MathF . Round ( sourceWidth * percentHeight ) ;
width = destinationWidth ;
targetWidth = ( int ) MathF . Round ( sourceWidth * percentHeight ) ;
}
}
// Replace the size to match the rectangle.
return ( new Size ( width , h eight) , new Rectangle ( 0 , 0 , des tin ation Width, des tin ation Height) ) ;
return ( new Size ( targetWidth , targetH eight) , new Rectangle ( 0 , 0 , targe tWidth , targe tHeight ) ) ;
}
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 des tin ation X = 0 ;
int des tin ation Y = 0 ;
int des tin ation Width = width ;
int des tin ation Height = height ;
int targetX = 0 ;
int targe tY = 0 ;
int targe tWidth = width ;
int targe tHeight = height ;
// Fractional variants for preserving aspect ratio.
float percentHeight = MathF . Abs ( height / ( float ) sourceHeight ) ;
@ -366,50 +356,73 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
if ( percentHeight < percentWidth )
{
ratio = percentHeight ;
des tin ation Width = ( int ) MathF . Round ( sourceWidth * percentHeight ) ;
targe tWidth = ( int ) MathF . Round ( sourceWidth * percentHeight ) ;
switch ( options . Position )
{
case AnchorPositionMode . Left :
case AnchorPositionMode . TopLeft :
case AnchorPositionMode . BottomLeft :
des tin ation X = 0 ;
targe tX = 0 ;
break ;
case AnchorPositionMode . Right :
case AnchorPositionMode . TopRight :
case AnchorPositionMode . BottomRight :
des tin ation X = ( int ) MathF . Round ( width - ( sourceWidth * ratio ) ) ;
targe tX = ( int ) MathF . Round ( width - ( sourceWidth * ratio ) ) ;
break ;
default :
des tin ation X = ( int ) MathF . Round ( ( width - ( sourceWidth * ratio ) ) / 2F ) ;
targe tX = ( int ) MathF . Round ( ( width - ( sourceWidth * ratio ) ) / 2F ) ;
break ;
}
}
else
{
ratio = percentWidth ;
des tin ation Height = ( int ) MathF . Round ( sourceHeight * percentWidth ) ;
targe tHeight = ( int ) MathF . Round ( sourceHeight * percentWidth ) ;
switch ( options . Position )
{
case AnchorPositionMode . Top :
case AnchorPositionMode . TopLeft :
case AnchorPositionMode . TopRight :
des tin ation Y = 0 ;
targe tY = 0 ;
break ;
case AnchorPositionMode . Bottom :
case AnchorPositionMode . BottomLeft :
case AnchorPositionMode . BottomRight :
des tin ation Y = ( int ) MathF . Round ( height - ( sourceHeight * ratio ) ) ;
targe tY = ( int ) MathF . Round ( height - ( sourceHeight * ratio ) ) ;
break ;
default :
des tin ation Y = ( int ) MathF . Round ( ( height - ( sourceHeight * ratio ) ) / 2F ) ;
targe tY = ( int ) MathF . Round ( ( height - ( sourceHeight * ratio ) ) / 2F ) ;
break ;
}
}
return ( new Size ( width , height ) ,
new Rectangle ( des tin ation X, des tin ation Y, des tin ation Width, des tin ation Height) ) ;
// Target image width and height can be different to the rectangle width and height.
return ( new Size ( width , height ) , new Rectangle ( targe tX , targe tY , targe tWidth , targe tHeight ) ) ;
}
private static ( Size , Rectangle ) CalculateManualRectangle (
ResizeOptions options ,
int width ,
int height )
{
if ( ! options . TargetRectangle . HasValue )
{
ThrowInvalid ( "Manual resizing requires a target location and size." ) ;
}
Rectangle targetRectangle = options . TargetRectangle . Value ;
int targetX = targetRectangle . X ;
int targetY = targetRectangle . Y ;
int targetWidth = targetRectangle . Width > 0 ? targetRectangle . Width : width ;
int targetHeight = targetRectangle . Height > 0 ? targetRectangle . Height : height ;
// 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 ) ;
}
}
}