在 .Net 中清除位图

Clear A Bitmap in .Net

我在用PictureBox控件绘制复杂的图表,为了优化性能我对每层绘图都使用了一个缓存Bitmap,并绘制在控件图像上,上层有透明背景,我需要清除它们并在每次更改时重新绘制。

假设 Bitmap 的 Graphics class 的 g 实例,使用 g.Clear(Color.White) 在所有内容上绘制一个白色矩形,从而隐藏较低层,并且 g.Clear(Color.Transparent) 在其上绘制透明矩形,这意味着什么什么都不做。

没有办法清除 Bitmap 使其恢复到原来的状态吗?

这可能不是您正在寻找的答案,但我认为它是您现有答案的有趣替代品。

我不必每次更改都向上绘制所有层,而是通过 嵌套 将多个 PictureBoxes 层叠成一个层底部 PictureBox pBox0:

List<PictureBox> Layers = new List<PictureBox>();

private void Form1_Load(object sender, EventArgs e)
{
    Layers.Add(pBox0);
    setUpLayers(pBox0 , 20);  // stacking 20 layers onto the botton one
    timer1.Start();           // simulate changes
}

堆叠是这样设置的:

void setUpLayers(Control parent, int count)
{
    for (int i = 0; i < count; i++)
    {
        PictureBox pb = new PictureBox();
        pb.BorderStyle = BorderStyle.None;
        pb.Size = parent.ClientSize;
        Bitmap bmp = new Bitmap(pb.Size.Width,pb.Size.Height,PixelFormat.Format32bppPArgb);
        pb.Image = bmp;
        pb.Parent = i == 0 ? pBox0 : Layers[i - 1];
        Layers.Add(pb);
    }
}

为了获得最佳性能,我使用 Format32bppPArgb 作为像素格式。

为了测试我 运行 随机绘制到图层上的 Tick 事件:

Random R = new Random(9);
private void timer1_Tick(object sender, EventArgs e)
{
    int l = R.Next(Layers.Count-1) + 1;

    Bitmap bmp = (Bitmap) Layers[l].Image;
    using (Graphics G = Graphics.FromImage(Layers[l].Image))
    {
        G.Clear(Color.Transparent);
        using (Font font = new Font("Consolas", 33f))
        G.DrawString(l + " " + DateTime.Now.Second , font, Brushes.Gold, 
            R.Next(bmp.Size.Width),  R.Next(bmp.Size.Height));
    }
    Layers[l].Image = bmp;
}

要将所有图层收集到一张位图中,您可以使用 DrawToBitmap 方法:

Bitmap GetComposite(Control ctl)
{
    Bitmap bmp = new Bitmap(ctl.ClientSize.Width, ctl.ClientSize.Height,
                            PixelFormat.Format32bppArgb);
    ctl.DrawToBitmap(bmp, ctl.ClientRectangle);
    return bmp;
}

结果可以保存或以任何其他方式使用..

请注意,以这种方式创建太多图层会达到 window 个句柄的限制;我在大约 90 层时达到了这个限制。如果您需要的层数超过几十层,则需要更复杂的缓存策略..

private void btn_CancelImage_Click(object sender, EventArgs e)
    {
        DialogResult dialogResult = MessageBox.Show("Cancel and delete this Image?", "Cancel", MessageBoxButtons.YesNo);
        if (dialogResult == DialogResult.Yes)
        {
            ClearImage();
            pictureBox1.Refresh();
        }
        else if (dialogResult == DialogResult.No)
        {
            return;
        }
    }
    public void ClearImage()
    {
        Graphics g = Graphics.FromImage(ImageBitmap);
        g.Clear(Color.White);
    }