C# 查找C图元文件最后绘制的像素

C# 查找C图元文件最后绘制的像素,c#,graphics,metafile,C#,Graphics,Metafile,我有一个图元文件对象。由于我无法控制的原因,它的尺寸比在其内部绘制的图像所需的尺寸大数千倍 例如,它可以是4000x40000,但在2000x1600的区域中只包含真正的不透明像素 最初,该图元文件只是绘制到控件,控件边界将该区域限制为合理大小 现在,我正试图根据用户的输入,将其分为不同的动态大小的块。我想做的是计算在x和y中会有多少个块,甚至分割成一个二维的块网格 我知道,从技术上讲,我可以继续下去,一个接一个地检查像素,以找到绘制图像的真实边界 但这将是痛苦的缓慢 我正在寻找一种方法来获取整

我有一个图元文件对象。由于我无法控制的原因,它的尺寸比在其内部绘制的图像所需的尺寸大数千倍

例如,它可以是4000x40000,但在2000x1600的区域中只包含真正的不透明像素

最初,该图元文件只是绘制到控件,控件边界将该区域限制为合理大小

现在,我正试图根据用户的输入,将其分为不同的动态大小的块。我想做的是计算在x和y中会有多少个块,甚至分割成一个二维的块网格

我知道,从技术上讲,我可以继续下去,一个接一个地检查像素,以找到绘制图像的真实边界

但这将是痛苦的缓慢

我正在寻找一种方法来获取整个图元文件中最后绘制的像素的位置x,y,而不必遍历它们中的每一个

由于DrawImage方法并不是非常慢,至少不是N²慢,所以我假设图元文件对象的内部有一些优化,可以实现类似的效果。就像列表对象有一个.Count属性,这个属性比实际计算对象快得多,有什么方法可以得到图元文件的实际边界吗

在此场景中,绘制的内容将始终为矩形。我可以安全地假设最后一个像素是相同的,不管我是在x然后y循环,还是在y然后x循环


如何找到最后一个像素的坐标?

为如此大的图像找到不透明像素的边界矩形确实是一个有趣的挑战

最直接的方法是处理WMF内容,但这也是迄今为止最难做到的

让我们将图像渲染为位图并查看位图

首先是基本方法,然后是一些优化

要获得边界,需要找到左、上、右和下边界

下面是一个简单的函数:

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);
}
请注意,要正确绘制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);
}
我本可以通过不超过整个高度,而只是部分加上我们已经找到的delte来优化


如果可以使用有关数据的知识,则可以实现进一步的优化。如果我们知道图像数据是连接在一起的,我们可以在循环中使用更大的步长,直到找到一个像素,然后追溯到一个步长。

那么“last”是指“最右底部”吗?不是上一个WMF绘图命令/形状所绘制的形状?如果形状未知,可能很难分辨哪一个是最后一个,但由于我被保证为矩形形状,所以我所说的最后一个确实是指右下角。如果在宽度上循环,在高度或相反方向上循环,则将是最后一个检查的不透明像素。所以,是的,最后一个坐标是明智的,而不是绘制顺序。我想到了三件事:a不要从左上角看,而是从右下角看。不管怎样,你都可以这样做。如果你能画出缩小1/10倍或1/100倍的图像,那么你就可以少看很多像素。如果你能估计出数字,你可以画一幅移到左上角的图像,只看剩下的部分。这三个方案可能都应该结合起来。@TaW这很容易就能得到答案
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);
}