Копировать графическое содержимое в bitmap

#c# #graphics #bitmap #gdi #bitblt

#c# #графика #растровое изображение #gdi #bitblt

Вопрос:

Я пытаюсь скопировать содержимое графического объекта в bitmap. Я использую этот код

 public static class GraphicsBitmapConverter
{
    [DllImport("gdi32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, TernaryRasterOperations dwRop);

    public static Bitmap GraphicsToBitmap(Graphics g, Rectangle bounds)
    {
        Bitmap bmp = new Bitmap(bounds.Width, bounds.Height);

        using (Graphics bmpGrf = Graphics.FromImage(bmp))
        {
            IntPtr hdc1 = g.GetHdc();
            IntPtr hdc2 = bmpGrf.GetHdc();

            BitBlt(hdc2, 0, 0, bmp.Width, bmp.Height, hdc1, 0, 0, TernaryRasterOperations.SRCCOPY);

            g.ReleaseHdc(hdc1);
            bmpGrf.ReleaseHdc(hdc2);
        }

        return bmp;
    }
}
  

Если я использую метод, подобный этому

 Graphics g = button1.CreateGraphics();
var bmp = GraphicsBitmapConverter.GraphicsToBitmap(g, Rectangle.Truncate(g.VisibleClipBounds));
  

растровое изображение содержит содержимое. Но если я рисую графический объект перед вызовом методов, bitmap будет пустым:

 using (Bitmap bmp = new Bitmap(100, 100))
{
    using (Graphics g = Graphics.FromImage(bmp))
    {
        g.FillRectangle(Brushes.Red, 10, 10, 50, 50);
        g.FillRectangle(Brushes.Blue, 20, 20, 50, 50);
        g.FillRectangle(Brushes.Green, 0, 0, bmp.Width, bmp.Height);

        var bmp2 = GraphicsBitmapConverter.GraphicsToBitmap(g, Rectangle.Truncate(g.VisibleClipBounds));
    }
}
  

Почему это работает в первом случае, а не во втором?

Комментарии:

1. Вы проверяете возвращаемое значение BitBlt()? Возможно, это сбой и может привести к подсказкам.

2. код неполный

Ответ №1:

Насколько я понимаю

     Graphics g = Graphics.FromImage(bmp)
  

создает графический контекст для рисования в изображении, тогда как

     Graphics g = button1.CreateGraphics();
  

создает изображение для элемента управления, который уже был выведен на экран. Вы могли бы нарисовать динамически созданное растровое изображение в PictureBox, а затем сделать то же самое, что и для вашей кнопки, что-то вроде

         using (Bitmap bmp = new Bitmap(100, 100)) 
            {
            using (Graphics g = Graphics.FromImage(bmp)) 
                {
                g.FillRectangle(Brushes.Green, 0, 0, bmp.Width, bmp.Height);
                g.FillRectangle(Brushes.Red, 10, 10, 50, 50);
                g.FillRectangle(Brushes.Blue, 20, 20, 50, 50);

                pictureBox1.Image = bmp;
                pictureBox1.Update(); // force an update before doing anything with it
                Graphics g2 = pictureBox1.CreateGraphics();
                var bmp2 = GraphicsBitmapConverter.GraphicsToBitmap(g2, Rectangle.Truncate(g.VisibleClipBounds)); 
                // bmp2 now has the same contents as bmp1
                pictureBox1.Image = null; // bmp is about to be Disposed so remove the reference to it
                }
            }
  

Конечно, это просто воссоздает Bitmap, который у вас уже есть, поэтому я предполагаю, что вы имеете в виду какое-то другое использование.

Ответ №2:

Вы пытались удалить свою графику?

 using (Bitmap bmp = new Bitmap(100, 100))
{
    using (Graphics g = Graphics.FromImage(bmp))
    {
        g.FillRectangle(Brushes.Red, 10, 10, 50, 50);
        g.FillRectangle(Brushes.Blue, 20, 20, 50, 50);
        g.FillRectangle(Brushes.Green, 0, 0, bmp.Width, bmp.Height);

        g.Flush(); // !!

        var bmp2 = GraphicsBitmapConverter.GraphicsToBitmap(g, Rectangle.Truncate(g.VisibleClipBounds));
    }
}
  

Ответ №3:

Вы делаете это сложным способом. Очевидно, что, поскольку один и тот же код выполняется успешно и завершается неудачей при разных вызовах, графика использует разное временное пространство для хранения информации до тех пор, пока не будет запрошено завершение с помощью create graphics — иначе оно должно быть прочитано в обоих случаях.

Попробуйте использовать

 gr.Save(); 
  

для графики, возможно, это сработает перед созданием.

Однако, когда я хочу приступить к рисованию, я просто клонирую графическое изображение, используя:

 public void CopyBackgroundLayer(ref Bitmap bitmap)
{
    Rectangle rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
    bitmap = new Bitmap(buffer.Clone(rect, Primary.BackGround.PixelFormat));
}
  

Итак, тем временем я добавляю материал в графический объект, который я называю этим, где клонирует изображение, которое рисует графический объект (буфер в примере), чтобы увеличить растровое изображение до любой точки, прежде чем добавлять больше материала.