为如此大的图像找到不透明像素的边界矩形确实是一个有趣的挑战。
最直接的方法是处理 WMF 内容,但这也是迄今为止最难做到的。
让我们将图像渲染为位图并查看位图。
首先是基本方法,然后是一些优化。
要获得边界,需要找到 left, top, right
和bottom
边界。
这是一个简单的功能:
Rectangle getBounds(Bitmap bmp)
{
int l, r, t, b; l = t = r = b = 0;
for (int x = 0; x < bmp.Width - 1; x++)
for (int y = 0; y < bmp.Height - 1; y++)
if (bmp.GetPixel(x,y).A > 0) { l = x; goto l1; }
l1:
for (int x = bmp.Width - 1; x > l ; x--)
for (int y = 0; y < bmp.Height - 1; y++)
if (bmp.GetPixel(x,y).A > 0) { r = x; goto l2; }
l2:
for (int y = 0; y < bmp.Height - 1; y++)
for (int x = l; x < r; x++)
if (bmp.GetPixel(x,y).A > 0) { t = y; goto l3; }
l3:
for (int y = bmp.Height - 1; y > t; y--)
for (int x = l; x < r; x++)
if (bmp.GetPixel(x,y).A > 0) { b = y; goto l4; }
l4:
return Rectangle.FromLTRB(l,t,r,b);
}
请注意,它稍微优化了最后一个垂直循环,以仅查看尚未由水平循环测试的部分。
它使用GetPixel
,这是非常缓慢的;但甚至Lockbits
仅获得“仅”约 10 倍左右。所以我们需要减少绝对数量;无论如何我们都需要这样做,因为 40k x 40k 像素对于Bitmap
.
由于 WMF 通常充满矢量数据,因此我们可能可以将其缩小很多。这是一个例子:
string fn = "D:\\_test18b.emf";
Image img = Image.FromFile(fn);
int w = img.Width;
int h = img.Height;
float scale = 100;
Rectangle rScaled = Rectangle.Empty;
using (Bitmap bmp = new Bitmap((int)(w / scale), (int)(h / scale)))
using (Graphics g = Graphics.FromImage(bmp))
{
g.ScaleTransform(1f/scale, 1f/scale);
g.Clear(Color.Transparent);
g.DrawImage(img, 0, 0);
rScaled = getBounds(bmp);
Rectangle rUnscaled = Rectangle.Round(
new RectangleF(rScaled.Left * scale, rScaled.Top * scale,
rScaled.Width * scale, rScaled.Height * scale ));
}
请注意,要正确绘制 wmf 文件,可能需要调整分辨率。这是我用于测试的示例:
using (Graphics g2 = pictureBox.CreateGraphics())
{
float scaleX = g2.DpiX / img.HorizontalResolution / scale;
float scaleY = g2.DpiY / img.VerticalResolution / scale;
g2.ScaleTransform(scaleX, scaleY);
g2.DrawImage(img, 0, 0); // draw the original emf image.. (*)
g2.ResetTransform();
// g2.DrawImage(bmp, 0, 0); // .. it will look the same as (*)
g2.DrawRectangle(Pens.Black, rScaled);
}
我把它省略了,但为了完全控制渲染,它也应该包含在上面的代码片段中。
这可能不够好,也可能不够好,具体取决于所需的准确性。
要完美地测量边界,可以做到这一点:使用缩小测试的边界并测量未缩放但仅围绕四个边界数字的微小条纹。创建渲染位图时,我们相应地移动原点。
右边界示例:
Rectangle rScaled2 = Rectangle.Empty;
int delta = 80;
int right = (int)(rScaled.Right * scale);
using (Bitmap bmp = new Bitmap((int)(delta * 2 ), (int)(h )))
using (Graphics g = Graphics.FromImage(bmp))
{
g.Clear(Color.Transparent);
g.DrawImage(img, - right - delta, 0);
rScaled2 = getBounds(bmp);
}
我可以通过不超过整个高度而只优化我们已经找到的部分(加上增量)来优化..
如果可以使用有关数据的知识,则可以实现进一步的优化。如果我们知道图像数据是连接的,我们可以在循环中使用更大的步骤,直到找到一个像素,然后回溯一个步骤。