Опубликован: 17.08.2010 | Доступ: свободный | Студентов: 999 / 59 | Оценка: 4.11 / 3.89 | Длительность: 29:38:00
Самостоятельная работа 8:

Работа MFC с GDI (GDI+)

Загрузка растровых ресурсов программно в процессе выполнения приложения

Здесь мы будем загружать растровые ресурсы, которые должны создать на этапе проектирования и которые при компиляции будут храниться в исполнимом файле. Фактически мы создадим код, который будет только отображать эти ресурсы для пользователя на холсте, а не подкачивать в работающее приложение откуда-то со стороны.

В этом смысле растровые ресурсы можно загружать программно двумя почти идентичными способами:

  1. Используя функции API-Windows напрямую
  2. Используя MFC -класс CBitmap

Далее мы увидим, что можно отображать рисунки не только как заранее (на этапе проектирования) загруженные ресурсы. Но можно отображать рисунки (и не только растровые формата .bmp) из файлов на диске через стандартное диалоговое окно File Open. Для программной загрузки рисунков из файлов можно использовать два класса:

  1. CBitmap - только для загрузки файлов формата .bmp ( MFC -библиотека)
  2. CImage - для загрузки файлов других популярных форматов ( ATL -библиотека)

Для всех перечисленных способов желательно проверить два варианта отображения рисунка:

  1. Без искажения истинных размеров рисунка (применяется функция BitBlt )
  2. С подстройкой размеров отображаемого рисунка под размеры окна отображения (применяется функция StretchBlt )

Воспользуемся только вторым способом и примем следующий порядок использования инструментов главной панели:

  • Кнопка Bitmap должна срабатывать только при включенном режиме Tool=Bitmap.Проверку этого условия мы разместим и в обработчике кнопки, и в обработчике OnPaint() перед вызовом функции вывода рисунка
  • Когда режим Tool=Bitmap включен, у нас остаются не у дел инструменты Shape и Color. Задействуем их так...
  • Режим Shape=Line приспособим для вывода ресурсов растровых рисунков формата .bmp с помощью API -функций Windows. При этом радиокнопками Color будем выбирать различные растровые рисунки, которые загрузим в ресурсы приложения на этапе проектирования
  • Режим Shape=Circle установим для загрузки растровых рисунков формата .bmp с помощью функций MFC -класса CBitmap через стандартное диалоговое окно File Open
  • Режим Shape=Square установим для загрузки растровых рисунков формата .bmp с помощью функций ATL -класса CImage через стандартное диалоговое окно File Open. При этом сделаем возможным загрузку и отображение не только .bmp -файлов, но и файлов других популярных форматов (например, JPEG и PNG )
  • При загрузке файлов через диалоговое окно кнопки Color использоваться не будут
Создание ресурсов в режиме проектирования
  • Загрузите ресурсы Bitmap из каталога Pictures\Photo лабораторной работы. При этом импортируйте файлы
    • Photo1.bmp
    • Photo2.bmp
    • Photo3.bmp
    • Photo4.bmp
    • Photo5.bmp
    • Photo6.bmp
    • Photo7.bmp
    • Photo8.bmp

    Имена дескрипторов оставьте как есть


  • Установите размеры окна холста 281x199
Создание обработчика кнопки Bitmap для открытия диалогового окна File Open

Создадим в классе CGraphicsDlg общедоступную переменную strFileName для хранения имени выбранного файла. Для этого:

  • В панели Class View выделите класс CGraphicsDlg и через контекстное меню вызовите мастер, который заполните так


    Объявление переменной strFileName файла GraphicsDlg.h
    class CGraphicsDlg : public CDialog
    {
    ..................................................
    public:
      // Переменная для хранения имени файла
      CString strFileName;
    };
    Инициализация переменной strFileName файла GraphicsDlg.cpp
    CGraphicsDlg::CGraphicsDlg(CWnd* pParent /*=NULL*/)
      : CDialog(CGraphicsDlg::IDD, pParent)
      , m_iTool(0)
      , m_iShape(0)
      , m_iColor(7)
      , strFileName(_T(""))
    {
      m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
    }
  • Поместите в файл PaintDlg.cpp инструкцию #include <atlimage.h> с объявлением класса CImage ATL -библиотеки ( именно в конец группы инструкций include, как показано ниже, иначе компилятор заблудится и будет выдавать ошибку!)

    Начало файла PaintDlg.cpp
    // PaintDlg.cpp : implementation file
    //
      
    #include "stdafx.h"
    #include "Graphics.h"
    #include "PaintDlg.h"
    #include ".\paintdlg.h"
      
    #include "GraphicsDlg.h"
      
    #include <atlimage.h>  
      
    // CPaintDlg dialog
      
    IMPLEMENT_DYNAMIC(CPaintDlg, CDialog)
    CPaintDlg::CPaintDlg(CWnd* pParent /*=NULL*/)
      : CDialog(CPaintDlg::IDD, pParent)
    {
    }
    ........................................
  • Откройте ресурс IDD_GRAPHICS_DIALOG главного диалогового окна в режиме редактирования
  • Двойным щелчком по кнопке Bitmap создайте обработчик, который заполните так

    Функция-обработчик кнопки Bitmap файла GraphicsDlg.cpp
    void CGraphicsDlg::OnBnClickedBbitmap()
    {
      if(m_iTool != 2 || m_iShape == 0) return; 
      
      // Готовим фильтры
        static char szFilterBmp[] = "Bitmap Files (*.bmp)|*.bmp||";
        static char szFilterDefault[] = 
          "JPEG Files (*.jpg)|*.jpg|"
          "PNG Files (*.png)|*.png|"
          "Bitmap Files (*.bmp)|*.bmp||";
        char  *filter;// Вспомогательный указатель
      
      // Определить фильтр
      if(m_iShape == 1) // Circle для использования класса CBitmap
        filter = szFilterBmp;
      else // Оставшийся режим Shape==Square
        filter = szFilterDefault;
      
      // Открываем диалог для выбора файла
      
      // Создать стандартное диалоговое окно File Open
      CFileDialog dlgOpenFileDialog(TRUE, "", strFileName,
          OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, filter);
      
      // Отобразить диалоговое окно
      if(dlgOpenFileDialog.DoModal() == IDOK){
        // Заменить имя файла на выбранное для последующего отображения
        strFileName = dlgOpenFileDialog.GetPathName();
        m_dlgPaint.Invalidate(); // Перерисовать второе окно
      }
    }

Функция загрузки и отображения рисунков

  • Создайте функцию DrawImage(), прототип которой разместите в описании класса CPaintDlg файла PaintDlg.cpp

    Прототип функции DrawImage() файла PaintDlg.h
    class CPaintDlg : public CDialog
    {
    .............................................
    public:
      afx_msg void OnSize(UINT nType, int cx, int cy);
      afx_msg void OnMove(int x, int y);
      void DrawImage(CPaintDC * pDC, int iTool, int iShape, int iColor);
    };
  • Определение функции разместите в фале PaintDlg.cpp и наполните кодом так

    Определение функции DrawImage() в файле PaintDlg.cpp
    void CPaintDlg::DrawImage(CPaintDC* dc, int iTool, int iShape, int iColor)
    {
      // Получить указатель на родительское окно
      // для доступа к имени файла, полученного из File Open
      CGraphicsDlg* pWnd = (CGraphicsDlg*) GetParent();
      if(!pWnd) return; // Если родитель не существует - выйти
      
      // Получить область рисования
      CRect rClientRect;
      GetClientRect(rClientRect);
      rClientRect.NormalizeRect();
      
      // Параметры рисования
      const int iDisp = 7;// Отступы от краев клиентской области
      int iWidth = rClientRect.Width() - 2 * iDisp; // Ширина области рисования
      int iHeight = rClientRect.Height() - 2 * iDisp; // Высота области рисования
      
      // Создадим ананимные перечисления локально, 
      // для удобства работы, чтобы не запутаться
      enum{Pen, Brush, Bitmap};
      enum{Line, Circle, Square};
      enum{Black, Blue, Green, Cyan
        ,Red, Magenta, Yellow
        ,White}; // Режим для CImage отдельно, чтобы не забыть
      
      
    /**************************************************************
        Shape==Line  - выводим ресурсы растровых
                      рисунков формата *.bmp 
                      с помощью API-функций Windows.
      
        Радиокнопки Color используем для выбора различных 
        растровых рисунков, которые загрузим
        в ресурсы приложения на этапе проектирования.
    **************************************************************/
      if(iShape == Line){
        HBITMAP hbitmap; // Создать объект для хранения изображения
        int id;
        switch(iColor){
          case Black:   id = IDB_BITMAP1; break;
          case Blue:     id = IDB_BITMAP2; break;
          case Green:   id = IDB_BITMAP3; break;
          case Cyan:    id = IDB_BITMAP4; break;
          case Red:     id = IDB_BITMAP5; break;
          case Magenta: id = IDB_BITMAP6; break;
          case Yellow:  id = IDB_BITMAP7; break;
          case White:   id = IDB_BITMAP8;
        }
        
        // Поместить растровое изображение в память
        hbitmap = ::LoadBitmap(::AfxGetInstanceHandle()
              ,MAKEINTRESOURCE(id));
      
        // Создать контекст устройства в памяти
        HDC hMemDC = ::CreateCompatibleDC(NULL);
      
        // Поместить растровое изображение в dc памяти
        SelectObject(hMemDC, hbitmap);
      
        // Копировать содержимое dc памяти в dc экрана
        ::StretchBlt(dc->m_hDC, iDisp, iDisp, iWidth, iHeight,// Приемник: dc экрана
          hMemDC, 0, 0, 400, 300, // Источник: dc памяти
          SRCCOPY);
      
        // Уничтожить dc памяти и растровое изображение
        ::DeleteDC(hMemDC);
        ::DeleteObject(hbitmap);
      } // Конец if
      
      
    /**************************************************************
        Shape==Circle - отображаем файл формата *.bmp, 
                      имя которого получено из File Open,
                        с помощью функций MFC-класса CBitmap.
      Состояние радиокнопок Color значения не имеет.
    **************************************************************/
      if(iShape == Circle){
      
        // Создать объект для хранения и загрузить в него изображение
        HBITMAP hbitmap =
          (HBITMAP) ::LoadImage(AfxGetInstanceHandle(),
                         pWnd->strFileName, IMAGE_BITMAP, 0, 0,
                     LR_LOADFROMFILE | LR_CREATEDIBSECTION);
      
        if(hbitmap){// Файл успешно загружен
          // Создаем экземпляр класса CBitmap
          CBitmap m_Bitmap;
          // Прикрепляем загруженный рисунок к объекту класса
          m_Bitmap.Attach(hbitmap);
          // Создать родную структуру для объекта класса CBitmap
          BITMAP bm;
          // Перекачать загруженное в структуру HBITMAP изображение
          // в родную для объекта m_Bitmap структуру BITMAP
          m_Bitmap.GetBitmap(&bm);
          // Создать совместимый контекст устройства в памяти
          // по переданному в функцию параметру CPaintDC* dc
          CDC dcMem;
          dcMem.CreateCompatibleDC(dc);
          // Поместить растровое изображение в dc памяти
          dcMem.SelectObject(m_Bitmap); // Может адрес?
          // Копировать и изменить размеры точечного
          // рисунка по диалоговому окну холста
          dc->StretchBlt(iDisp, iDisp, iWidth, iHeight,// Приемник: dc экрана
            &dcMem, 0, 0, bm.bmWidth, bm.bmHeight,// Источник: dc памяти
            SRCCOPY);
        }
      }
      
      
    /**************************************************************
         Shape==Square- выводим стандартное диалоговое окно
                        File Open для загрузки и
                        отображения .bmp-файлов или файлов других 
                        популярных форматов (например, JPEG и PNG).
      Состояние радиокнопок Color значения не имеет.
    **************************************************************/
      if(iShape == Square){
        // Создать экземпляр класса CImage
        CImage m_Image;
        // Загрузить в него рисунок из файла
        if(pWnd->strFileName.GetLength() > 0){ // Если выбрано имя файла
          m_Image.Load(pWnd->strFileName);
          // Задать координаты рисования
          CRect rRect(iDisp, iDisp, iDisp + iWidth, iDisp + iHeight); 
          // Отобразить на контексте экрана
          m_Image.Draw(dc->m_hDC, rRect);
        }
      }
    }
  • Разместите вызов функции DrawImage() в обработчике OnPaint() файла PaintDlg.cpp, дополнив его так

    Вызов функции DrawImage() в файле PaintDlg.cpp
    void CPaintDlg::OnPaint()
    {
      CPaintDC dc(this); // Локальный контекст устройства 
      
      // Получить указатель на родительское окно
      CGraphicsDlg* pWnd = (CGraphicsDlg*) GetParent();
      
      if(!pWnd) return; // Если родитель не существует - выйти
      
      if(pWnd->m_iTool == 0 // Если выбран режим Pen
         && pWnd->m_iShape == 0) // и если выбран режим Line
        DrawLine(&dc, pWnd->m_iColor); // Рисуем линии
      
      if(pWnd->m_iTool != 2 // Если не выбран растр Bitmap
         && pWnd->m_iShape != 0) // Если не выбран режим Line
        // Рисуем прямоугольник, эллипс или их контуры текущим цветом
        DrawRegion(&dc, pWnd->m_iTool, pWnd->m_iShape, pWnd->m_iColor); 
      
      if(pWnd->m_iTool == 2) // Если выбран рисунок
        // Выдаем рисунки
        DrawImage(&dc, pWnd->m_iTool, pWnd->m_iShape, pWnd->m_iColor); 
    }
  • Запустите приложение и проверьте работу на файлах разных форматов

    Обратите внимание на размер исполнимого файла - он недопустимо большой. Это потому, что растровые рисунки мы включили в ресурсы.


Александр Даниленко
Александр Даниленко
Стоит Windows 8 Pro, Visual Studio 2010 Express Edition .