Graphics.DrawImage() 在使用 RotateTransform() 时剪辑图像
Graphics.DrawImage() clips image when using RotateTransform()
我有以下代码可以在 4 个方向上绘制我的 border2.bmp
private void Form1_Paint(object sender, PaintEventArgs e)
{
Bitmap border = new Bitmap("border2.bmp");
int borderThick = border.Height;
Graphics g = e.Graphics;
Size region = g.VisibleClipBounds.Size.ToSize();
Rectangle desRectW = new Rectangle(0, 0, region.Width - borderThick, borderThick);
// 1. LEFT - RIGHT border
g.TranslateTransform(30, 30);
g.DrawImage(border, desRectW, desRectW, GraphicsUnit.Pixel);
// 2. UP - BUTTOM border
g.ResetTransform();
g.TranslateTransform(50, 50);
g.RotateTransform(90);
g.DrawImage(border, desRectW, desRectW, GraphicsUnit.Pixel);
// 3. RIGHT-LEFT border
g.ResetTransform();
g.TranslateTransform(100, 100);
g.RotateTransform(180);
g.DrawImage(border, desRectW, desRectW, GraphicsUnit.Pixel);
// 4. BOTTOM - UP border
g.ResetTransform();
g.TranslateTransform(150, 150);
g.RotateTransform(270);
g.DrawImage(border, desRectW, desRectW, GraphicsUnit.Pixel);
}
我的原图是:
但是旋转的结果和我想象的不太一样。 90 度缺少第一条红线,270 度缺少第一条黑柱,180 度缺少两者。
喜欢我附上的图片:
PS:您可以在 http://i.imgur.com/pzonx3i.png
获得 border2.bmp
编辑:
我试过 g.PixelOffsetMode = PixelOffsetMode.HighQuality;
作为@Peter Duniho 的评论,但我发现它也没有正确绘制。
示例:第 4 行的起始位置与我们预期的不同。
g.TranslateTransform(50, 50);
// LEFT - RIGHT border
g.DrawLine(Pens.Red, 0, 0, 100, 0);
// UP - BOTTOM border
g.RotateTransform(90);
g.DrawLine(new Pen(Color.FromArgb(128, Color.Blue)), 0, 0, 100, 0);
// RIGHT-LEFT border
g.RotateTransform(90);
g.DrawLine(new Pen(Color.FromArgb(128, Color.Green)), 0, 0, 100, 0);
// BOTTOM - UP border
g.RotateTransform(90);
g.DrawLine(new Pen(Color.FromArgb(128, Color.Gray)), 0, 0, 100, 0);
我真的无法解释为什么会发生这种情况,除了任何图形 API 都必然会包含优化,这有时可能会导致不精确的行为,而且您似乎 运行就是这种情况。
在您的特定示例中,可以通过在绘制图像之前向代码中添加以下语句来更正问题:
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
将其设置为 Half
也可以。它等同于 HighQuality
(或者从技术上讲,HighQuality
等同于它……但我发现 HighQuality
在代码中更具描述性 :))。
这会稍微减慢位图的渲染速度,但可能不会以您的用户可以察觉的方式。
虽然 .NET 文档在描述此设置方面不是很有帮助,但同一功能的本机 Win32 文档 稍微 更详细:
PixelOffsetMode enumeration。从描述中可以推断,如果像素的逻辑中心位于 (0,0),则旋转时可能会在一个边缘上丢失一个像素(and/or 在另一条边缘上获得一个像素)。切换到 Half
可以解决此问题。
你需要考虑一些事情。
(1) RotateTransforms
在当前 origin 处应用旋转(如 Matrix.Rotate
documentation
中所述
(2) 默认 MatrixOrder(当未通过使用带有附加参数的覆盖指定时)是 Prepend
。这意味着在您的情况下,生成的转换是旋转,然后翻译。
例如,如果您将此代码放入绘画事件中:
var g = e.Graphics;
var loc = new Point(128, 128);
var rect = new Rectangle(0, 0, 64, 16);
g.ResetTransform();
g.TranslateTransform(loc.X, loc.Y);
g.FillRectangle(Brushes.Blue, rect);
g.ResetTransform();
g.TranslateTransform(loc.X, loc.Y);
g.RotateTransform(90);
g.FillRectangle(Brushes.Red, rect);
g.ResetTransform();
g.TranslateTransform(loc.X, loc.Y);
g.RotateTransform(180);
g.FillRectangle(Brushes.Green, rect);
g.ResetTransform();
g.TranslateTransform(loc.X, loc.Y);
g.RotateTransform(270);
g.FillRectangle(Brushes.Magenta, rect);
你会得到这个
蓝色矩形没有旋转。现在固定原点(左上角)并开始顺时针旋转。您会看到它将与红色 (90)、绿色 (180) 和洋红色 (270) 矩形完全匹配。
这意味着如果你想形成一个矩形,你需要对旋转的矩形应用额外的偏移(平移)。这取决于你想如何处理重叠区域,但对于示例,如果我们想将红色矩形连接到蓝色矩形,我们需要在 X 方向上添加蓝色矩形宽度 + 原始矩形高度。对于其他旋转的矩形,您可以对 X、Y 或两者应用类似的额外偏移。
要完成示例,如果我们像这样修改代码
var g = e.Graphics;
var loc = new Point(128, 128);
var rect = new Rectangle(0, 0, 64, 16);
g.ResetTransform();
g.TranslateTransform(loc.X, loc.Y);
g.FillRectangle(Brushes.Blue, rect);
g.ResetTransform();
g.TranslateTransform(loc.X + rect.Width + rect.Height, loc.Y);
g.RotateTransform(90);
g.FillRectangle(Brushes.Red, rect);
g.ResetTransform();
g.TranslateTransform(loc.X + rect.Width + rect.Height, loc.Y + rect.Width + rect.Height);
g.RotateTransform(180);
g.FillRectangle(Brushes.Green, rect);
g.ResetTransform();
g.TranslateTransform(loc.X, loc.Y + rect.Width + rect.Height);
g.RotateTransform(270);
g.FillRectangle(Brushes.Magenta, rect);
新结果将是
一旦你理解了所有这些,希望你可以对你的具体代码应用所需的更正。
我有以下代码可以在 4 个方向上绘制我的 border2.bmp
private void Form1_Paint(object sender, PaintEventArgs e)
{
Bitmap border = new Bitmap("border2.bmp");
int borderThick = border.Height;
Graphics g = e.Graphics;
Size region = g.VisibleClipBounds.Size.ToSize();
Rectangle desRectW = new Rectangle(0, 0, region.Width - borderThick, borderThick);
// 1. LEFT - RIGHT border
g.TranslateTransform(30, 30);
g.DrawImage(border, desRectW, desRectW, GraphicsUnit.Pixel);
// 2. UP - BUTTOM border
g.ResetTransform();
g.TranslateTransform(50, 50);
g.RotateTransform(90);
g.DrawImage(border, desRectW, desRectW, GraphicsUnit.Pixel);
// 3. RIGHT-LEFT border
g.ResetTransform();
g.TranslateTransform(100, 100);
g.RotateTransform(180);
g.DrawImage(border, desRectW, desRectW, GraphicsUnit.Pixel);
// 4. BOTTOM - UP border
g.ResetTransform();
g.TranslateTransform(150, 150);
g.RotateTransform(270);
g.DrawImage(border, desRectW, desRectW, GraphicsUnit.Pixel);
}
我的原图是:
但是旋转的结果和我想象的不太一样。 90 度缺少第一条红线,270 度缺少第一条黑柱,180 度缺少两者。
喜欢我附上的图片:
PS:您可以在 http://i.imgur.com/pzonx3i.png
获得border2.bmp
编辑:
我试过 g.PixelOffsetMode = PixelOffsetMode.HighQuality;
作为@Peter Duniho 的评论,但我发现它也没有正确绘制。
示例:第 4 行的起始位置与我们预期的不同。
g.TranslateTransform(50, 50);
// LEFT - RIGHT border
g.DrawLine(Pens.Red, 0, 0, 100, 0);
// UP - BOTTOM border
g.RotateTransform(90);
g.DrawLine(new Pen(Color.FromArgb(128, Color.Blue)), 0, 0, 100, 0);
// RIGHT-LEFT border
g.RotateTransform(90);
g.DrawLine(new Pen(Color.FromArgb(128, Color.Green)), 0, 0, 100, 0);
// BOTTOM - UP border
g.RotateTransform(90);
g.DrawLine(new Pen(Color.FromArgb(128, Color.Gray)), 0, 0, 100, 0);
我真的无法解释为什么会发生这种情况,除了任何图形 API 都必然会包含优化,这有时可能会导致不精确的行为,而且您似乎 运行就是这种情况。
在您的特定示例中,可以通过在绘制图像之前向代码中添加以下语句来更正问题:
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
将其设置为 Half
也可以。它等同于 HighQuality
(或者从技术上讲,HighQuality
等同于它……但我发现 HighQuality
在代码中更具描述性 :))。
这会稍微减慢位图的渲染速度,但可能不会以您的用户可以察觉的方式。
虽然 .NET 文档在描述此设置方面不是很有帮助,但同一功能的本机 Win32 文档 稍微 更详细:
PixelOffsetMode enumeration。从描述中可以推断,如果像素的逻辑中心位于 (0,0),则旋转时可能会在一个边缘上丢失一个像素(and/or 在另一条边缘上获得一个像素)。切换到 Half
可以解决此问题。
你需要考虑一些事情。
(1) RotateTransforms
在当前 origin 处应用旋转(如 Matrix.Rotate
documentation
(2) 默认 MatrixOrder(当未通过使用带有附加参数的覆盖指定时)是 Prepend
。这意味着在您的情况下,生成的转换是旋转,然后翻译。
例如,如果您将此代码放入绘画事件中:
var g = e.Graphics;
var loc = new Point(128, 128);
var rect = new Rectangle(0, 0, 64, 16);
g.ResetTransform();
g.TranslateTransform(loc.X, loc.Y);
g.FillRectangle(Brushes.Blue, rect);
g.ResetTransform();
g.TranslateTransform(loc.X, loc.Y);
g.RotateTransform(90);
g.FillRectangle(Brushes.Red, rect);
g.ResetTransform();
g.TranslateTransform(loc.X, loc.Y);
g.RotateTransform(180);
g.FillRectangle(Brushes.Green, rect);
g.ResetTransform();
g.TranslateTransform(loc.X, loc.Y);
g.RotateTransform(270);
g.FillRectangle(Brushes.Magenta, rect);
你会得到这个
蓝色矩形没有旋转。现在固定原点(左上角)并开始顺时针旋转。您会看到它将与红色 (90)、绿色 (180) 和洋红色 (270) 矩形完全匹配。
这意味着如果你想形成一个矩形,你需要对旋转的矩形应用额外的偏移(平移)。这取决于你想如何处理重叠区域,但对于示例,如果我们想将红色矩形连接到蓝色矩形,我们需要在 X 方向上添加蓝色矩形宽度 + 原始矩形高度。对于其他旋转的矩形,您可以对 X、Y 或两者应用类似的额外偏移。
要完成示例,如果我们像这样修改代码
var g = e.Graphics;
var loc = new Point(128, 128);
var rect = new Rectangle(0, 0, 64, 16);
g.ResetTransform();
g.TranslateTransform(loc.X, loc.Y);
g.FillRectangle(Brushes.Blue, rect);
g.ResetTransform();
g.TranslateTransform(loc.X + rect.Width + rect.Height, loc.Y);
g.RotateTransform(90);
g.FillRectangle(Brushes.Red, rect);
g.ResetTransform();
g.TranslateTransform(loc.X + rect.Width + rect.Height, loc.Y + rect.Width + rect.Height);
g.RotateTransform(180);
g.FillRectangle(Brushes.Green, rect);
g.ResetTransform();
g.TranslateTransform(loc.X, loc.Y + rect.Width + rect.Height);
g.RotateTransform(270);
g.FillRectangle(Brushes.Magenta, rect);
新结果将是
一旦你理解了所有这些,希望你可以对你的具体代码应用所需的更正。