使用 pictureBox sizeMode 裁剪位图克隆已缩放
Crop Bitmap clone with pictureBox sizeMode is zoomed
我在网上找到这个裁剪图片的功能,如果pictureBox源sizeMode正常就可以了。但是当 pictureBox sizeMode 缩放时,它仍然像正常 sizeMode 一样被克隆。
如何复制和放大后的pictureBox一样的?不是原始普通位图的大小?
public static Bitmap CropBitmap(Bitmap bitmap, int x, int y, int w, int h)
{
Rectangle rect = new Rectangle(x, y, w, h);
Bitmap cropped = bitmap.Clone(rect, bitmap.PixelFormat);
return cropped;
}
并像这样使用
pictureBox2.Image = CropBitmap((Bitmap)pictureBox1.Image.Clone(), 35, 0, 110, 150);
最简单的方法是使用 DrawToBitmap 方法从图片框获取输出而不考虑 SizeMode
,然后像这样裁剪输出:
public static Bitmap Crop(PictureBox pb, int x, int y, int w, int h)
{
var rect = pb.ClientRectangle;
using (var output = new Bitmap(rect.Width, rect.Height, pb.Image.PixelFormat))
{
pb.DrawToBitmap(output, rect);
return output.Clone(new Rectangle(x, y, w, h), output.PixelFormat);
}
}
但是,上述方法的缺点是它确实会裁剪潜在缩放的图像。
如果您确实想要裁剪原始图像,您需要将传递的矩形(我假设它在图片框客户端坐标中)映射到原始图像坐标。
如果图片框提供方法ClientToImage
就好了(类似于ClientToScreen
),但是没有,所以我们需要从中提取SizeMode
逻辑Reference Source。
新方法是这样的:
public static class ImageUtils
{
public static Bitmap CropImage(this PictureBox pb, int x, int y, int w, int h)
{
var imageRect = pb.GetImageRectangle();
var image = pb.Image;
float scaleX = (float)image.Width / imageRect.Width;
float scaleY = (float)image.Height / imageRect.Height;
var cropRect = new Rectangle();
cropRect.X = Scale(x - imageRect.X, scaleX, image.Width);
cropRect.Y = Scale(y - imageRect.Y, scaleY, image.Height);
cropRect.Width = Scale(w, scaleX, image.Width - cropRect.X);
cropRect.Height = Scale(h, scaleY, image.Height - cropRect.Y);
var result = new Bitmap(cropRect.Width, cropRect.Height, image.PixelFormat);
using (var g = Graphics.FromImage(result))
g.DrawImage(image, new Rectangle(new Point(0, 0), cropRect.Size), cropRect, GraphicsUnit.Pixel);
return result;
}
static int Scale(int value, float scale, int maxValue)
{
int result = (int)(value * scale);
return result < 0 ? 0 : result > maxValue ? maxValue : result;
}
public static Rectangle GetImageRectangle(this PictureBox pb)
{
var rect = pb.ClientRectangle;
var padding = pb.Padding;
rect.X += padding.Left;
rect.Y += padding.Top;
rect.Width -= padding.Horizontal;
rect.Height -= padding.Vertical;
var image = pb.Image;
var sizeMode = pb.SizeMode;
if (sizeMode == PictureBoxSizeMode.Normal || sizeMode == PictureBoxSizeMode.AutoSize)
rect.Size = image.Size;
else if (sizeMode == PictureBoxSizeMode.CenterImage)
{
rect.X += (rect.Width - image.Width) / 2;
rect.Y += (rect.Height - image.Height) / 2;
rect.Size = image.Size;
}
else if (sizeMode == PictureBoxSizeMode.Zoom)
{
var imageSize = image.Size;
var zoomSize = pb.ClientSize;
float ratio = Math.Min((float)zoomSize.Width / imageSize.Width, (float)zoomSize.Height / imageSize.Height);
rect.Width = (int)(imageSize.Width * ratio);
rect.Height = (int)(imageSize.Height * ratio);
rect.X = (pb.ClientRectangle.Width - rect.Width) / 2;
rect.Y = (pb.ClientRectangle.Height - rect.Height) / 2;
}
return rect;
}
}
我在网上找到这个裁剪图片的功能,如果pictureBox源sizeMode正常就可以了。但是当 pictureBox sizeMode 缩放时,它仍然像正常 sizeMode 一样被克隆。
如何复制和放大后的pictureBox一样的?不是原始普通位图的大小?
public static Bitmap CropBitmap(Bitmap bitmap, int x, int y, int w, int h)
{
Rectangle rect = new Rectangle(x, y, w, h);
Bitmap cropped = bitmap.Clone(rect, bitmap.PixelFormat);
return cropped;
}
并像这样使用
pictureBox2.Image = CropBitmap((Bitmap)pictureBox1.Image.Clone(), 35, 0, 110, 150);
最简单的方法是使用 DrawToBitmap 方法从图片框获取输出而不考虑 SizeMode
,然后像这样裁剪输出:
public static Bitmap Crop(PictureBox pb, int x, int y, int w, int h)
{
var rect = pb.ClientRectangle;
using (var output = new Bitmap(rect.Width, rect.Height, pb.Image.PixelFormat))
{
pb.DrawToBitmap(output, rect);
return output.Clone(new Rectangle(x, y, w, h), output.PixelFormat);
}
}
但是,上述方法的缺点是它确实会裁剪潜在缩放的图像。
如果您确实想要裁剪原始图像,您需要将传递的矩形(我假设它在图片框客户端坐标中)映射到原始图像坐标。
如果图片框提供方法ClientToImage
就好了(类似于ClientToScreen
),但是没有,所以我们需要从中提取SizeMode
逻辑Reference Source。
新方法是这样的:
public static class ImageUtils
{
public static Bitmap CropImage(this PictureBox pb, int x, int y, int w, int h)
{
var imageRect = pb.GetImageRectangle();
var image = pb.Image;
float scaleX = (float)image.Width / imageRect.Width;
float scaleY = (float)image.Height / imageRect.Height;
var cropRect = new Rectangle();
cropRect.X = Scale(x - imageRect.X, scaleX, image.Width);
cropRect.Y = Scale(y - imageRect.Y, scaleY, image.Height);
cropRect.Width = Scale(w, scaleX, image.Width - cropRect.X);
cropRect.Height = Scale(h, scaleY, image.Height - cropRect.Y);
var result = new Bitmap(cropRect.Width, cropRect.Height, image.PixelFormat);
using (var g = Graphics.FromImage(result))
g.DrawImage(image, new Rectangle(new Point(0, 0), cropRect.Size), cropRect, GraphicsUnit.Pixel);
return result;
}
static int Scale(int value, float scale, int maxValue)
{
int result = (int)(value * scale);
return result < 0 ? 0 : result > maxValue ? maxValue : result;
}
public static Rectangle GetImageRectangle(this PictureBox pb)
{
var rect = pb.ClientRectangle;
var padding = pb.Padding;
rect.X += padding.Left;
rect.Y += padding.Top;
rect.Width -= padding.Horizontal;
rect.Height -= padding.Vertical;
var image = pb.Image;
var sizeMode = pb.SizeMode;
if (sizeMode == PictureBoxSizeMode.Normal || sizeMode == PictureBoxSizeMode.AutoSize)
rect.Size = image.Size;
else if (sizeMode == PictureBoxSizeMode.CenterImage)
{
rect.X += (rect.Width - image.Width) / 2;
rect.Y += (rect.Height - image.Height) / 2;
rect.Size = image.Size;
}
else if (sizeMode == PictureBoxSizeMode.Zoom)
{
var imageSize = image.Size;
var zoomSize = pb.ClientSize;
float ratio = Math.Min((float)zoomSize.Width / imageSize.Width, (float)zoomSize.Height / imageSize.Height);
rect.Width = (int)(imageSize.Width * ratio);
rect.Height = (int)(imageSize.Height * ratio);
rect.X = (pb.ClientRectangle.Width - rect.Width) / 2;
rect.Y = (pb.ClientRectangle.Height - rect.Height) / 2;
}
return rect;
}
}