将图像裁剪成 "perfect" 正方形,就像本机照片应用一样
Cropping an image to a "perfect" square like native Photos app
在开发用于查看从我们的一台服务器获取的嵌入式图像的功能时,其中一个要求是制作一个显示所有图像的概览页面,类似于 iOS本机照片应用程序(方形图块,以 4/5 行排列,点击时会在全屏显示图像。
这已经成功实现,除了图像被变形(图像方面从未被考虑在内)。因此,他们让我设计一种裁剪图像的方法,使原始方面保持不变。
为了做到这一点,我想先调整图像的大小,直到宽度达到我想要的最大宽度 ((screenwidth - 10) / 4 - 5)。
- 如果图片的高度小于宽度,我设置高度等于宽度,并相应裁剪宽度(纵横比不变)。
- 如果图片的高度大于宽度,我只想裁剪高度。
这是我目前使用的方法:
//Images is an array/List with base64 strings
foreach (var image in Images)
{
UIImage imageToAdd = UIImage.LoadFromData(NSData.FromArray(Convert.FromBase64String(image.Content)));
var width = imageToAdd.Size.Width;
var height = imageToAdd.Size.Height;
//GlobalSupport.ScreenWidth is a static variable that contains the actual screensize
var newWidth = (GlobalSupport.ScreenWidth - 10) / 4 - 5;
var newHeight = height * newWidth / width;
var widthToCrop = 0.0f;
var heightToCrop = 0.0f;
//If the new height is smaller than the new width, make the new height equal to the new width, and modify the new width accordingly.
if (newHeight < newWidth)
{
newHeight = newWidth;
newWidth = width * newHeight / height;
widthToCrop = newWidth - newHeight;
}
//Or, if the new height is greater than the new width, just crop the height.
else if (newHeight > newWidth)
{
heightToCrop = newHeight - newWidth;
}
UIGraphics.BeginImageContext(new SizeF(newWidth, newHeight));
var cropRectangle = new RectangleF(-widthToCrop, -heightToCrop, newWidth, newHeight);
imageToAdd.Draw(cropRectangle);
imageToAdd = UIGraphics.GetImageFromCurrentImageContext();
UIGraphics.EndImageContext();
UIButton btnToAdd = new UIButton(new RectangleF(-widthToCrop, -heightToCrop, newWidth, newHeight));
btnToAdd.SetBackgroundImage(imageToAdd, UIControlState.Normal);
btnToAdd.TouchUpInside += (sender, e) =>
{
Task.Factory.StartNew(() =>
{
//Displays a loading spinner
GlobalSupport.EnableLoadingOverlay(true, NSBundle.MainBundle.LocalizedString("txtOneMoment", "", ""));
}).ContinueWith(task => InvokeOnMainThread(() =>
{
//Navigate to the full-screen image viewer
this.NavigationController.PushViewController(
new VCDCMPhotoDetails(
UIImage.LoadFromData(
NSData.FromArray(
Convert.FromBase64String(image.Content)))), true);
GlobalSupport.EnableLoadingOverlay(false, "");
}));
};
//Add btn to the scrollable view's children
scrollView.AddSubview(btnToAdd);
}
这个方法很好用,除了一件事:如果 new height
小于 new width
,new height
和 new width
成功修改,但是之后ImageToAdd.Draw(RectangleF)
ImageToAdd
的 Size
仍然具有 new width
值,而不是裁剪后的宽度(也就是说,如果我可以假设,如果我用-20 作为它的 x 值,宽度也被修改为 -20。
我不知道这样做是否正确。如果不是,那么欢迎所有帮助!如果这是正确的方法,但我遗漏了什么,请告诉我!提前致谢!
裁剪图片时,x坐标等于图片的宽度减去缩略图的最大宽度:http://i.stack.imgur.com/uNm7k.png
裁剪后,图像仍然具有原始的新宽度,而不是裁剪后的宽度:http://i.stack.imgur.com/rabeY.png
通过创建具有目标图像大小的图像视图,最容易实现您正在做的事情。然后将您的图像设置为它,并将内容模式设置为缩放纵横比填充并简单地创建视图的屏幕截图(您可以在网络上找到非常简单的解决方案)。
从你的问题中我看不出你是否有问题或者它是什么。但是有一件事困扰着我。行
RectangleF(-widthToCrop, -heightToCrop, newWidth, newHeight)
将按照您始终获得图像右下部分的方式裁剪它。您很可能应该将其居中:
RectangleF(-widthToCrop*0.5, -heightToCrop*0.5, newWidth, newHeight)
好的,我想我发现了你的问题。您计算的框架是您应该在其上绘制图像以将其放在正方形上的框架。但是当你开始图像上下文时,你应该将大小设置为正方形。因此,如果宽度较大,则尺寸应为 (newHeight, newHeight)
,否则为 (newWidth, newWidth)
。按钮框架对我来说也毫无意义。为什么要使用本应仅用于重绘图像的矩形?
让我向您展示如何使用 FILL 操作裁剪图像的正确步骤。代码在 Swift 中,但移植它应该没有问题:
public static func resampleImageToSize(image: UIImage!, size: CGSize) -> UIImage {
let originalWidth = image.size.width
let originalHeight = image.size.height
let originalRatio = originalWidth/originalHeight
let targetRatio = size.width/size.height
var targetFrame = CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height)
if originalRatio>targetRatio {
// crop width
let targetHeight = size.height
let targetWidth = targetHeight * originalRatio
targetFrame = CGRect(x: (size.width-targetWidth)*0.5, y: (size.height-targetHeight)*0.5, width: targetWidth, height: targetHeight)
} else if originalRatio<targetRatio {
// crop height
let targetWidth = size.width
let targetHeight = targetWidth / originalRatio
targetFrame = CGRect(x: (size.width-targetWidth)*0.5, y: (size.height-targetHeight)*0.5, width: targetWidth, height: targetHeight)
}
UIGraphicsBeginImageContext(size)
image.drawInRect(targetFrame)
let outputImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return outputImage
}
所以这是一种通用方法,它接受原始图像和 returns 具有您选择的大小的图像。在您的情况下,这是您要显示的图标的大小。这里的操作是fill,如果你需要fit图像你只需要交换<
和>
在两个 if
语句中。
在开发用于查看从我们的一台服务器获取的嵌入式图像的功能时,其中一个要求是制作一个显示所有图像的概览页面,类似于 iOS本机照片应用程序(方形图块,以 4/5 行排列,点击时会在全屏显示图像。
这已经成功实现,除了图像被变形(图像方面从未被考虑在内)。因此,他们让我设计一种裁剪图像的方法,使原始方面保持不变。
为了做到这一点,我想先调整图像的大小,直到宽度达到我想要的最大宽度 ((screenwidth - 10) / 4 - 5)。
- 如果图片的高度小于宽度,我设置高度等于宽度,并相应裁剪宽度(纵横比不变)。
- 如果图片的高度大于宽度,我只想裁剪高度。
这是我目前使用的方法:
//Images is an array/List with base64 strings
foreach (var image in Images)
{
UIImage imageToAdd = UIImage.LoadFromData(NSData.FromArray(Convert.FromBase64String(image.Content)));
var width = imageToAdd.Size.Width;
var height = imageToAdd.Size.Height;
//GlobalSupport.ScreenWidth is a static variable that contains the actual screensize
var newWidth = (GlobalSupport.ScreenWidth - 10) / 4 - 5;
var newHeight = height * newWidth / width;
var widthToCrop = 0.0f;
var heightToCrop = 0.0f;
//If the new height is smaller than the new width, make the new height equal to the new width, and modify the new width accordingly.
if (newHeight < newWidth)
{
newHeight = newWidth;
newWidth = width * newHeight / height;
widthToCrop = newWidth - newHeight;
}
//Or, if the new height is greater than the new width, just crop the height.
else if (newHeight > newWidth)
{
heightToCrop = newHeight - newWidth;
}
UIGraphics.BeginImageContext(new SizeF(newWidth, newHeight));
var cropRectangle = new RectangleF(-widthToCrop, -heightToCrop, newWidth, newHeight);
imageToAdd.Draw(cropRectangle);
imageToAdd = UIGraphics.GetImageFromCurrentImageContext();
UIGraphics.EndImageContext();
UIButton btnToAdd = new UIButton(new RectangleF(-widthToCrop, -heightToCrop, newWidth, newHeight));
btnToAdd.SetBackgroundImage(imageToAdd, UIControlState.Normal);
btnToAdd.TouchUpInside += (sender, e) =>
{
Task.Factory.StartNew(() =>
{
//Displays a loading spinner
GlobalSupport.EnableLoadingOverlay(true, NSBundle.MainBundle.LocalizedString("txtOneMoment", "", ""));
}).ContinueWith(task => InvokeOnMainThread(() =>
{
//Navigate to the full-screen image viewer
this.NavigationController.PushViewController(
new VCDCMPhotoDetails(
UIImage.LoadFromData(
NSData.FromArray(
Convert.FromBase64String(image.Content)))), true);
GlobalSupport.EnableLoadingOverlay(false, "");
}));
};
//Add btn to the scrollable view's children
scrollView.AddSubview(btnToAdd);
}
这个方法很好用,除了一件事:如果 new height
小于 new width
,new height
和 new width
成功修改,但是之后ImageToAdd.Draw(RectangleF)
ImageToAdd
的 Size
仍然具有 new width
值,而不是裁剪后的宽度(也就是说,如果我可以假设,如果我用-20 作为它的 x 值,宽度也被修改为 -20。
我不知道这样做是否正确。如果不是,那么欢迎所有帮助!如果这是正确的方法,但我遗漏了什么,请告诉我!提前致谢!
裁剪图片时,x坐标等于图片的宽度减去缩略图的最大宽度:http://i.stack.imgur.com/uNm7k.png
裁剪后,图像仍然具有原始的新宽度,而不是裁剪后的宽度:http://i.stack.imgur.com/rabeY.png
通过创建具有目标图像大小的图像视图,最容易实现您正在做的事情。然后将您的图像设置为它,并将内容模式设置为缩放纵横比填充并简单地创建视图的屏幕截图(您可以在网络上找到非常简单的解决方案)。
从你的问题中我看不出你是否有问题或者它是什么。但是有一件事困扰着我。行
RectangleF(-widthToCrop, -heightToCrop, newWidth, newHeight)
将按照您始终获得图像右下部分的方式裁剪它。您很可能应该将其居中:
RectangleF(-widthToCrop*0.5, -heightToCrop*0.5, newWidth, newHeight)
好的,我想我发现了你的问题。您计算的框架是您应该在其上绘制图像以将其放在正方形上的框架。但是当你开始图像上下文时,你应该将大小设置为正方形。因此,如果宽度较大,则尺寸应为 (newHeight, newHeight)
,否则为 (newWidth, newWidth)
。按钮框架对我来说也毫无意义。为什么要使用本应仅用于重绘图像的矩形?
让我向您展示如何使用 FILL 操作裁剪图像的正确步骤。代码在 Swift 中,但移植它应该没有问题:
public static func resampleImageToSize(image: UIImage!, size: CGSize) -> UIImage {
let originalWidth = image.size.width
let originalHeight = image.size.height
let originalRatio = originalWidth/originalHeight
let targetRatio = size.width/size.height
var targetFrame = CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height)
if originalRatio>targetRatio {
// crop width
let targetHeight = size.height
let targetWidth = targetHeight * originalRatio
targetFrame = CGRect(x: (size.width-targetWidth)*0.5, y: (size.height-targetHeight)*0.5, width: targetWidth, height: targetHeight)
} else if originalRatio<targetRatio {
// crop height
let targetWidth = size.width
let targetHeight = targetWidth / originalRatio
targetFrame = CGRect(x: (size.width-targetWidth)*0.5, y: (size.height-targetHeight)*0.5, width: targetWidth, height: targetHeight)
}
UIGraphicsBeginImageContext(size)
image.drawInRect(targetFrame)
let outputImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return outputImage
}
所以这是一种通用方法,它接受原始图像和 returns 具有您选择的大小的图像。在您的情况下,这是您要显示的图标的大小。这里的操作是fill,如果你需要fit图像你只需要交换<
和>
在两个 if
语句中。