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);

新结果将是

一旦你理解了所有这些,希望你可以对你的具体代码应用所需的更正。