none
Сохранение изображения в файл RRS feed

  • Вопрос

  • Здравствуйте!Прошу помочь в решении проблемы...На форме есть PictureBox1,в нём рисунок(PictureBox1.Image) В рисунке есть вырезанные области сквозь которые проглядывается фоновое изображение(PictureBox1.BackgroundImage)
    Мне нужно сохранить этот двойной рисунок в файл.Но у меня всё время сохраняется только PictureBox1.Image а фоновый рисунок нет.Visual Basic2008 экспресс выпуск(Windows Form)
    • Перемещено Tagore Bandlamudi 2 октября 2010 г. 21:57 MSDN Forums consolidation (От:Разработка Windows-приложений)
    28 февраля 2010 г. 8:34

Ответы

  • По сути у вас имеются два изображения, которые вы пытаетесь сохранить через одно PicrureBox1.Image. На самом деле в таких ситуациях нужно использовать более ниузкоуровневые возможности gdi для создания экранной копии клиентской области PictureBox, тогда в файл будет сохранятся всё что нарисовано в PictureBox. Для этого нужно воспользоваться функцией GDI32 BitBlt для создания копии PictureBox. Синтаксис Visual Basic я знаю не очень хорошо, поэтому привожу реализацию на C#, я думаю, переделать на VB будет не очень сложно.
    Имеется форма с PictureBox и Button, при нажатии кнопки всё, что отображено в PictureBox вместе с фоном сохраняется в файл.
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.Runtime.InteropServices;
    
    namespace PictureBoxCopy
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
    
            private void button1_Click(object sender, EventArgs e)
            {
                //Создаем Graphics для PictureBox
                Graphics g1 = pictureBox1.CreateGraphics();
                
                //Создаем объект Image с теми же параметрами, что и PictureBox
                Image img = new Bitmap(pictureBox1.ClientRectangle.Width, pictureBox1.ClientRectangle.Height, g1);
                
                //Создаем новый Graphics для рисования на изображении
                Graphics g2 = Graphics.FromImage(img);
    
                //Получаем контексты рисования для PictureBox и Image из объектов Graphics
                IntPtr dc1 = g1.GetHdc();
                IntPtr dc2 = g2.GetHdc();
    
                //Копируем изображение с PictureBox на Image
                BitBlt(dc2, 0, 0, pictureBox1.ClientRectangle.Width, pictureBox1.ClientRectangle.Height, dc1, 0, 0, 13369376);
    
                //Освобождаем ресурсы контекстов рисования
                g1.ReleaseHdc(dc1);
                g2.ReleaseHdc(dc2);
    
                //Сохраняем изображение в файл
                img.Save("out.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
            }
    
            [DllImport("gdi32.dll")]
            private static extern bool BitBlt(IntPtr hdcDest, int nxDest, int nyDest, int nWidth, int nHeight, IntPtr hdcSrc, int nxSrc, int nySrc, int dwRop);
    
    
        }
    }
    
    • Предложено в качестве ответа PashaPash 2 марта 2010 г. 10:05
    • Помечено в качестве ответа I.Vorontsov 4 марта 2010 г. 7:43
    2 марта 2010 г. 8:42

Все ответы

  • По сути у вас имеются два изображения, которые вы пытаетесь сохранить через одно PicrureBox1.Image. На самом деле в таких ситуациях нужно использовать более ниузкоуровневые возможности gdi для создания экранной копии клиентской области PictureBox, тогда в файл будет сохранятся всё что нарисовано в PictureBox. Для этого нужно воспользоваться функцией GDI32 BitBlt для создания копии PictureBox. Синтаксис Visual Basic я знаю не очень хорошо, поэтому привожу реализацию на C#, я думаю, переделать на VB будет не очень сложно.
    Имеется форма с PictureBox и Button, при нажатии кнопки всё, что отображено в PictureBox вместе с фоном сохраняется в файл.
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.Runtime.InteropServices;
    
    namespace PictureBoxCopy
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
    
            private void button1_Click(object sender, EventArgs e)
            {
                //Создаем Graphics для PictureBox
                Graphics g1 = pictureBox1.CreateGraphics();
                
                //Создаем объект Image с теми же параметрами, что и PictureBox
                Image img = new Bitmap(pictureBox1.ClientRectangle.Width, pictureBox1.ClientRectangle.Height, g1);
                
                //Создаем новый Graphics для рисования на изображении
                Graphics g2 = Graphics.FromImage(img);
    
                //Получаем контексты рисования для PictureBox и Image из объектов Graphics
                IntPtr dc1 = g1.GetHdc();
                IntPtr dc2 = g2.GetHdc();
    
                //Копируем изображение с PictureBox на Image
                BitBlt(dc2, 0, 0, pictureBox1.ClientRectangle.Width, pictureBox1.ClientRectangle.Height, dc1, 0, 0, 13369376);
    
                //Освобождаем ресурсы контекстов рисования
                g1.ReleaseHdc(dc1);
                g2.ReleaseHdc(dc2);
    
                //Сохраняем изображение в файл
                img.Save("out.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
            }
    
            [DllImport("gdi32.dll")]
            private static extern bool BitBlt(IntPtr hdcDest, int nxDest, int nyDest, int nWidth, int nHeight, IntPtr hdcSrc, int nxSrc, int nySrc, int dwRop);
    
    
        }
    }
    
    • Предложено в качестве ответа PashaPash 2 марта 2010 г. 10:05
    • Помечено в качестве ответа I.Vorontsov 4 марта 2010 г. 7:43
    2 марта 2010 г. 8:42
  • Переделал вот так

     

    Dim g1 As Graphics = PictureBox1.CreateGraphics()

     

    Dim img As Image

    img =

    New Bitmap(PictureBox1.ClientRectangle.Width, PictureBox1.ClientRectangle.Height, g1)

     

    Dim g2 As Graphics = Graphics.FromImage(img)

    g2.GetHdc()

    g1.GetHdc()

    gr.DrawImage(dc2, 0, 0, PictureBox1.ClientRectangle.Width, PictureBox1.ClientRectangle.Height, dc1, 0, 0, 13369376)

    g1.ReleaseHdc(dc1)

    g2.ReleaseHdc(dc2)

    img.Save(

    "C:\открытка.jpg", System.Drawing.Imaging.ImageFormat.Jpeg)
    Но он у меня на эту строчку

    gr.DrawImage(dc2, 0, 0, PictureBox1.ClientRectangle.Width, PictureBox1.ClientRectangle.Height, dc1, 0, 0, 13369376)
    выдаёт ошибку--Не задана переменная объекта или переменная блока With.--
    вроде бы всё складно или я уже запарился совсем под носом не вижу и копаю не там

    2 марта 2010 г. 12:43
  • А как вы импортируете функцию BitBlt??

    2 марта 2010 г. 12:58
  • может быть ошибка в том, что автор топика не присваивает значения переменным dc1, dc2?
    "
    g2.GetHdc()

    g1.GetHdc()
    "

    3 марта 2010 г. 7:02
  • может быть ошибка в том, что автор топика не присваивает значения переменным dc1, dc2?
    "
    g2.GetHdc()

    g1.GetHdc()
    "


    Скорей всего.
    3 марта 2010 г. 9:30
  • А как вы импортируете функцию BitBlt??

    вот так

    Public

     

     

     

     Declare Ansi Function BitBlt Lib "gdi32.dll" (ByVal hdcDest, ByVal nxDest, ByVal nyDest, ByVal nWidth, ByVal nHeight, ByVal hdcSrc, ByVal nxSrc, ByVal nySrc, ByVal dwRop) As IntPtr
    dc1 = g1.GetHdc()
    dc2 = g2.GetHdc()
     вот так,я там наверху ошибся
    Где то всё равно ошибка потому что вместо рисунка сохраняется прямоугольник без изображения

    5 марта 2010 г. 14:38
  • Кода приводить не буду (пока курить ходил мысь появилась...), а вот общий принцип:

    Исх. данные: Есть PictureBox с фоновой картинкой и "верхней", на которой есть прозрачный прямоугольник, через который видна часть фоновой картинки.

    1) создаем 3 переменные типа bitmap

    первой задаем значение фоновой картинки

    второй - значение "верхней картинки

    у третьей пока просто задаем размеры и разрешение в точках на дюйм, как у картинок в PicturBox

    2) Создаем 2 цикла (по координатам X и Y)

    внутри циклов берем точку (GetPixel)из "верхней" картинки (второй bitmap), проверяем значение альфа-канала, далее:

    если значение а-канала =255 (непрозрачно) выводим эту точку через SetPixel по этим координатам в третий bitmap,

    усли а-канал меньше 255 (0-полностью прозрачен), то берем точку с ЭТИМИ КООРДИНАТАМИ из фоновой картинки (первый bitmap) и через SetPixel выводим в третий bitmap

    3) по окончании циклов в третьем bitmap'е будет картинка, которая тебе нужна и ее и сохраняем на диск.

    Таким способом можно не только прямоугольники  но и любые фигуры делать...

    Плюсы: Никаких API и прочих BitBlt... Минус - если картинка слишком большая (десятки мегабайт), то займет некоторое время (опрос 32 Мб картинки у меня занимает пр. 10 сек, но это с другими алгоритмами, хотя и похожими).

    P.S. Чтоб приложение не выглядело зависшим, между первым и вторым Next в циклах вставить

    Application.DoEvents() и какой-нибуть ProgressBar. Кстати вот одна из функций, которая переводит изображение в 32 битный цвет (это для примера). Извиняюсь за "корявость кода"...

    Private Function BmpConvToARGB(ByVal e As Bitmap) As Bitmap

    Dim x As Single

    Dim y As Single

    Dim c As Color

    Dim Tmp As New Bitmap(e.Width, e.Height)

            Tmp.SetResolution(e.HorizontalResolution, e.VerticalResolution)

             ToolStripProgressBar2.Maximum = e.Width

    For x = 0 To e.Width - 1

              For y = 0 To e.Height - 1

                           c = e.GetPixel(x, y)

                           Tmp.SetPixel(x, y, c)

              Next

             ToolStripProgressBar2.Value = x

             Application.DoEvents()

    Next

    ToolStripProgressBar2.Value = 0

    BmpConvToARGB = Tmp

    End Function

    • Предложено в качестве ответа Alexandr_Smirnoff 30 марта 2010 г. 15:57
    30 марта 2010 г. 15:55