/// <summary> /// Resizes an image in accordance with the given <see cref="ResizeOptions"/>. /// </summary> /// <typeparam name="TPixel">The pixel format.</typeparam> /// <param name="source">The image to resize.</param> /// <param name="options">The resize options.</param> /// <returns>The <see cref="Image{TPixel}"/></returns> /// <remarks>Passing zero for one of height or width within the resize options will automatically preserve the aspect ratio of the original image</remarks> public static IImageProcessingContext <TPixel> Resize <TPixel>(this IImageProcessingContext <TPixel> source, ResizeOptions options) where TPixel : struct, IPixel <TPixel> => source.ApplyProcessor(new ResizeProcessor <TPixel>(options, source.GetCurrentSize()));
private static (Size, Rectangle) CalculateCropRectangle(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 = percentWidth; if (options.CenterCoordinates.Any()) { float center = -(ratio * sourceHeight) * options.CenterCoordinates.ToArray()[1]; destinationY = (int)MathF.Round(center + (height / 2F)); if (destinationY > 0) { destinationY = 0; } if (destinationY < (int)MathF.Round(height - (sourceHeight * ratio))) { destinationY = (int)MathF.Round(height - (sourceHeight * ratio)); } } else { switch (options.Position) { case AnchorPosition.Top: case AnchorPosition.TopLeft: case AnchorPosition.TopRight: destinationY = 0; break; case AnchorPosition.Bottom: case AnchorPosition.BottomLeft: case AnchorPosition.BottomRight: destinationY = (int)MathF.Round(height - (sourceHeight * ratio)); break; default: destinationY = (int)MathF.Round((height - (sourceHeight * ratio)) / 2F); break; } } destinationHeight = (int)MathF.Ceiling(sourceHeight * percentWidth); } else { ratio = percentHeight; if (options.CenterCoordinates.Any()) { float center = -(ratio * sourceWidth) * options.CenterCoordinates.First(); destinationX = (int)MathF.Round(center + (width / 2F)); if (destinationX > 0) { destinationX = 0; } if (destinationX < (int)MathF.Round(width - (sourceWidth * ratio))) { destinationX = (int)MathF.Round(width - (sourceWidth * ratio)); } } else { switch (options.Position) { case AnchorPosition.Left: case AnchorPosition.TopLeft: case AnchorPosition.BottomLeft: destinationX = 0; break; case AnchorPosition.Right: case AnchorPosition.TopRight: case AnchorPosition.BottomRight: destinationX = (int)MathF.Round(width - (sourceWidth * ratio)); break; default: destinationX = (int)MathF.Round((width - (sourceWidth * ratio)) / 2F); break; } } destinationWidth = (int)MathF.Ceiling(sourceWidth * percentHeight); } 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 AnchorPosition.Left: destinationY = (height - sourceHeight) / 2; destinationX = 0; break; case AnchorPosition.Right: destinationY = (height - sourceHeight) / 2; destinationX = width - sourceWidth; break; case AnchorPosition.TopRight: destinationY = 0; destinationX = width - sourceWidth; break; case AnchorPosition.Top: destinationY = 0; destinationX = (width - sourceWidth) / 2; break; case AnchorPosition.TopLeft: destinationY = 0; destinationX = 0; break; case AnchorPosition.BottomRight: destinationY = height - sourceHeight; destinationX = width - sourceWidth; break; case AnchorPosition.Bottom: destinationY = height - sourceHeight; destinationX = (width - sourceWidth) / 2; break; case AnchorPosition.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)); }
/// <summary> /// Calculates the target location and bounds to perform the resize operation against. /// </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 <see cref="ValueTuple{Size,Rectangle}"/>. /// </returns> public static (Size, Rectangle) CalculateTargetLocationAndBounds(Size sourceSize, ResizeOptions options, int width, int height) { switch (options.Mode) { case ResizeMode.Crop: return(CalculateCropRectangle(sourceSize, options, width, height)); case ResizeMode.Pad: return(CalculatePadRectangle(sourceSize, options, width, height)); case ResizeMode.BoxPad: return(CalculateBoxPadRectangle(sourceSize, options, width, height)); case ResizeMode.Max: return(CalculateMaxRectangle(sourceSize, options, width, height)); case ResizeMode.Min: return(CalculateMinRectangle(sourceSize, options, width, height)); // Last case ResizeMode.Stretch: default: return(new Size(width, height), new Rectangle(0, 0, width, height)); } }
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 AnchorPosition.Left: case AnchorPosition.TopLeft: case AnchorPosition.BottomLeft: destinationX = 0; break; case AnchorPosition.Right: case AnchorPosition.TopRight: case AnchorPosition.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 AnchorPosition.Top: case AnchorPosition.TopLeft: case AnchorPosition.TopRight: destinationY = 0; break; case AnchorPosition.Bottom: case AnchorPosition.BottomLeft: case AnchorPosition.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)); }