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

Предоплата всего

Подписываем
Если у вас возникли сложности с курсовой, контрольной, дипломной, рефератом, отчетом по практике, научно-исследовательской и любой другой работой - мы готовы помочь.
Предоплата всего
Подписываем
Государственное образовательное учреждение
высшего профессионального образования
Уфимский Государственный Авиационный Технический Университет
Кафедра математики
Компьютерная Графика
Отчёт по лабораторной работе №2
Основы создания графических приложений в системе Windows
GDI+
Выполнил: студент 2 курса группы ПМИ-34,
Ибрагимов Ринат Радикович
Проверил: старший преподаватель кафедры ВВТиС,
Мухтаров Айрат Радикович
г. Уфа, УГАТУ, Апрель 25, 2012
Задание
Создать приложение для Windows, в окне которого средствами GDI, с помощью графических примитивов, рисуются разноцветные буквы (инициалы), совершающие равноускоренное движение.
Так же, приложение должно содержать визуализацию картинки на тему «Космическая ракета».
Теоретическая часть
GDI (Graphics Device Interface, Graphical Device Interface) один из трёх основных компонентов или «подсистем», вместе с ядром и Windows API составляющих пользовательский интерфейс (оконный менеджер GDI) Microsoft Windows.
GDI это интерфейс Windows для представления графических объектов и передачи их на устройства отображения, такие как мониторы и принтеры.
С выходом Windows XP появился потомок подсистемы, GDI+, основанный на C++.
GDI+ является улучшенной средой для 2D-графики, в которую добавлены такие возможности, как сглаживание линий (antialiasing), использование координат с плавающей точкой, градиентная заливка, внутренняя поддержка таких графических форматов, как JPEG и PNG, куда лучшая поддержка регионов отсечения с возможностью использовать в них координаты с плавающей точкой (а не 16-битные целые) и применения к ним World Transform, преобразования двумерных матриц и т. п. GDI+ использует ARGB-цвета. Эти возможности используются в пользовательском интерфейсе Windows XP, а их присутствие в базовом графическом слое облегчает использование систем векторной графики, таких как Flash или SVG.
Динамические библиотеки GDI+ могут распространяться вместе с приложениями для использования в предыдущих версиях Windows.
GDI+ схож с подсистемой Quartz 2D у Apple и библиотеками с открытым кодом libart и Cairo.
GDI+ есть не более чем набор оберток над обычной GDI. В Windows 7 появился новый API Direct2D, который есть примерно то же, но реализован «сверху донизу» вплоть до драйвера видеокарты (точнее, использует некие возможности Direct3D в этом драйвере), и может использовать аппаратное ускорение т. е. трехмерный видеопроцессор для рисования некоторых двухмерных объектов (antialiasing и т. д.)
Выполнение работы
Исходный код полученного приложения, компилируемый в системе windows с помощью средств Microsoft Visual Studio 2010, прилагается к отчёту (laba2cg.zip).
Здесь же приведу разбор исходного кода. Кстати, он большей частью состоит из кода первой лабораторной работы.
Подключаем заголовочные файлы
#include <windows.h>
#include <windowsx.h>
#include <stdexcept>
#include <gdiplus.h>
#include <tchar.h>
#include <memory> // нужен для подключения умного указателя std::auto_ptr
#include <locale>
#include <string>
#include<vector>
#include <algorithm>
Так же мы создадим ресурс для приложения а именно, строку меню. Описание этих ресурсов поместим в файл, который так же подключим.
#include "resource.h"
Далее, параметры окна и переменные
HINSTANCE g_hInstance = NULL;
using namespace Gdiplus;
using namespace std;
TCHAR const CLASS_NAME[] = _T("MainWndClass");
TCHAR const WINDOW_TITLE[] = _T("Простейшая Анимация 2");
auto_ptr<Bitmap> g_pBitmap;
int winH = 500, winW = 700;// размер окна по умолчанию
enum{ ANIMATION_TIMER_ID = 1 }; //у нас всего один таймер
DWORD g_lastTick;
UINT_PTR g_timerId = 0;
int glide = 0, polos=103;
WCHAR fio[]=L"Ибрагимов\nРинат\nРадикович";
Функция, которая создаёт окно с меню:
HWND CreateMainWindow(HINSTANCE hInstance){
HMENU hMainMenu = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_MAIN_MENU));
HWND hMainWindow = CreateWindowEx(0, CLASS_NAME, WINDOW_TITLE, WS_OVERLAPPEDWINDOW, 20, 20, winW, winH, NULL, hMainMenu, hInstance, NULL);
return hMainWindow;
}
Для инициализации и отключения GDI+ лучше написать класс.
class CGdiplusInitializer {
public:
CGdiplusInitializer(){
Gdiplus::GdiplusStartupInput input;
Gdiplus::GdiplusStartupOutput output;
if(Gdiplus::GdiplusStartup(&m_token, &input, &output) != Gdiplus::Ok){ // Не удалось throw std::runtime_error("Failed to initialize GDI+");
}
}
~CGdiplusInitializer(){
Gdiplus::GdiplusShutdown(m_token);
}
private:
ULONG_PTR m_token;
};
Класс области из предыдущей лабораторной работы
class Area { //класс, орпеделяющий эээ... место для фигур, и эээ.. жирность
public:
int top; //отступ сверху
int left; //слева
int width; //ширина фигуры
int height; //высота
int bold; //жирность
Area(int l=20, int t=10, int w=50, int h=80, int b=5){ //конструктор
top = t;
left = l;
width = w;
height = h;
bold = b;
}
~Area(){}
};
Функции рисования конуса и капли переписаны с использованием GDI+
void conus(Graphics *g, Pen &pen, Brush &brush, Area A, bool top = true){
Point *pntArray = new Point[3];
if(top == false){
pntArray[0].X = A.left+A.width/2; pntArray[0].Y = A.top+A.height;
pntArray[1].X = A.left+A.width; pntArray[1].Y = A.top;
pntArray[2].X = A.left; pntArray[2].Y = A.top;
} else {
pntArray[0].X = A.left+A.width/2; pntArray[0].Y = A.top;
pntArray[1].X = A.left+A.width; pntArray[1].Y = A.top+A.height;
pntArray[2].X = A.left; pntArray[2].Y = A.top+A.height;
}
g->FillPolygon(&brush, pntArray, 3);
g->DrawPolygon(&pen, pntArray, 3);
}
void drop(Graphics *g, Pen &pen, Brush &brush, Area A){
g->FillPie(&brush, A.left, A.top-A.height, A.width, 2*A.height, 0, 180);
g->DrawPie(&pen, A.left, A.top-A.height,A.width, 2*A.height, 0, 180);
}
Функция, отвечающая за показ стандартного диалогового окна выбора файла. Кстати, там будет фильтр, ограничивающий выбор только изображениями, при желании.
void InitFileNameStructure(HWND hwndOwner, OPENFILENAME *pOpenFileName, TCHAR *pFileName, DWORD maxFileName){
ZeroMemory(pOpenFileName, sizeof(OPENFILENAME));
pOpenFileName->lStructSize = sizeof(OPENFILENAME);
pOpenFileName->hwndOwner = hwndOwner;
pOpenFileName->hInstance = g_hInstance;
pOpenFileName->nMaxFile = maxFileName;
pOpenFileName->lpstrFile = pFileName;
pOpenFileName->lpstrFilter =
_T("Images (BMP, PNG, JPG, TIFF)\0*.bmp;*.png;*.jpg;*.tif\0")
_T("All files\0*.*\0")
_T("\0");
}
Обработка выбора пункта меню «открыть файл».
void OnOpenFile(HWND hwnd, UINT codeNotify){
OPENFILENAME ofn; TCHAR fileName[MAX_PATH + 1] = _T("");
InitFileNameStructure(hwnd, &ofn, fileName, MAX_PATH);
if (GetOpenFileName(&ofn)){
Image img(ofn.lpstrFile);
if (img.GetLastStatus() == Ok){
g_pBitmap = auto_ptr<Bitmap>(new Bitmap(img.GetWidth(), img.GetHeight(), PixelFormat32bppARGB));
Graphics g(g_pBitmap.get());
g.DrawImage(&img, 0, 0);
InvalidateRect(hwnd, NULL, TRUE);
}
}
}
std::wstring WStringToLower(std::wstring const& str){
std::wstring result(str);
std::transform(result.begin(), result.end(), result.begin(), ::tolower);
return result;
}
Теперь займёмся функциями, которые отвечают за запись в файлы.
CLSID GetEncoderCLSID(std::wstring const& fileExtension){ // Приводим разрешение к виду "*.разрешение"
std::wstring extensionMask = L"*." + WStringToLower(fileExtension) + L";"; // Запрашиваем у GDI+ количество кодировщиков изображений и размер блока данных для хранения их описания
UINT numEncoders, encodersSize;
GetImageEncodersSize(&numEncoders, &encodersSize); // Выделяем буфер для хранения информации о кодировщиках
std::vector<BYTE> encodersBuffer(encodersSize); // Запрашиваем у GDI+ информацию обо всех кодировщиков
ImageCodecInfo* pInstalledCodecs = reinterpret_cast<ImageCodecInfo *>(&encodersBuffer[0]);
GetImageEncoders(numEncoders, encodersSize, pInstalledCodecs);
ImageCodecInfo * pMatchedCodec = NULL;
for (unsigned i = 0; i < numEncoders; ++i){ // ищем подходящий кодировщик изображений
ImageCodecInfo & codec = pInstalledCodecs[i]; // получаем расширения файлов, поддерживаемых данным кодировщиком в формате: *.jpg;*.jpe;*.jpeg;
std::wstring extensions = WStringToLower(codec.FilenameExtension) + L";";
if(extensions.find(extensionMask) != wstring::npos){ // Если в списке расширений содержится маска расширения файла то кодек считается найденным
return codec.Clsid;
}
}
return CLSID_NULL; // не нашли подходящий кодировщик, возвращаем нулевой идентификатор
}
Получение типа файла.
std::wstring GetFileExtension(std::wstring const& fileName){
size_t dotPos = fileName.find_last_of(L'.');
if(dotPos != std::wstring::npos){
return fileName.substr(dotPos + 1);
} else {
return std::wstring();
}
}
Ну вот и само «Сохранение картинки в файл».
void SaveBitmap(Bitmap & bitmap, std::wstring const& fileName, int quality = 0){
std::wstring fileExtension = GetFileExtension(fileName); // получаем расширение выходного файла
CLSID codecId = GetEncoderCLSID(fileExtension); // Получаем идентификатор по расширению файла
if (IsEqualCLSID(codecId, CLSID_NULL)){ return; } // Если вернули CLSID_NULL (кодек не найден), то выходим
EncoderParameters params; // заполняем параметры кодировщика
params.Count = 1;// у нас только один параметр (степень компресии 0-100)
EncoderParameter & param0 = params.Parameter[0]; // заполняем характеристики параметра качество сжатия
LONG qualityParam = quality;
param0.Guid = EncoderCompression;// идентификатор параметра "компрессия"
param0.NumberOfValues = 1; // в массиве параметров содержится одно значение
param0.Type = EncoderParameterValueTypeLong; // тип значений LONG
param0.Value = &qualityParam; // адрес массива параметров
bitmap.Save(fileName.c_str(), &codecId, ¶ms); // сохраняем изображение с использованием подобранного кодировщика и параметра Quality (на практике используется только в JPEG-е)
}
Нажатие на кнопку «сохранить»:
void OnSaveFile(HWND hwnd, UINT codeNotify){
if (!g_pBitmap.get()){ return; } // Если изображение не открыто, то его и сохранить нельзя
Graphics g(g_pBitmap.get());
Font font(L"Segoe UI", 48);
RectF bounds(0, 0, winW, winH);
LinearGradientBrush brush4(bounds, Color(255,0,0), Color(255,255,255,255), LinearGradientModeBackwardDiagonal);
g.DrawString(fio, -1, &font, bounds, NULL, &brush4);// Выводим текст приветствия, длина -1 означает, что строка заканчивается нулем
OPENFILENAME ofn;
TCHAR fileName[MAX_PATH + 1] = _T("");
InitFileNameStructure(hwnd, &ofn, fileName, MAX_PATH);
if (GetSaveFileName(&ofn)){
SaveBitmap(*g_pBitmap, fileName, 95);
}
}
Теперь перейдём к рисованию на экране. На этот раз мы не будем анимировать буквы.
Функция контроля параметров анимации, на этот раз только огонь.
void Animate(HWND hwnd){
if(glide<40) glide++; else glide = 0;
InvalidateRect(hwnd, NULL, TRUE);
UpdateWindow(hwnd);
}
События окна.
void OnDestroy(HWND){ PostQuitMessage(0); }
void OnExit(HWND hwnd, UINT codeNotify){ DestroyWindow(hwnd); }
BOOL OnCreate(HWND hwnd, LPCREATESTRUCT){
g_lastTick = GetTickCount();
g_timerId = SetTimer(hwnd, ANIMATION_TIMER_ID, 20, NULL);
return (g_timerId != 0);
}
Событие выбора пункта меню
void OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify){
switch(id){
case ID_FILE_OPEN_IMAGE: OnOpenFile(hwnd, codeNotify); break;
case ID_FILE_SAVE_IMAGE_AS: OnSaveFile(hwnd, codeNotify); break;
case ID_FILE_EXIT: OnExit(hwnd, codeNotify); break;
}
}
«Тик» таймера
void OnTimer(HWND hwnd, UINT id){
switch (id){
case ANIMATION_TIMER_ID:
Animate(hwnd);
break;
}
}
Тоже событие окна, но рисование
void OnPaint(HWND hwnd) {
Насоздаём переменных и «теневой экран»
PAINTSTRUCT ps;
HDC dc = BeginPaint(hwnd, &ps);
HDC mdc = CreateCompatibleDC(dc);
HBITMAP bdc = CreateCompatibleBitmap(dc,winW,winH);
SelectObject(mdc,bdc);
BitBlt(mdc, 0,0, winW,winH, NULL, 0,0, WHITENESS);
Инициализируем объект Graphics из GDI+
Graphics g(mdc);
g.SetPageUnit(UnitPixel);
Font font(L"Segoe UI", 48);
RectF bounds(0, 0, winW, winH);
Если мы не должны показать в окне никакой картинки, просто рисуем ракету с текстом
if(g_pBitmap.get() != NULL){
g.DrawImage(g_pBitmap.get(), 0, 0);
} else {
Graphics *G = &g;
g.SetSmoothingMode(Gdiplus::SmoothingModeHighQuality);
LinearGradientBrush brush1(bounds, Color(0,0,0), Color(200,200,200),LinearGradientModeVertical), brush4(bounds, Color(0,0,0, 0), Color(255,255,255, 255), LinearGradientModeBackwardDiagonal);
SolidBrush brush(Color(25,25,112)), brush2(Color(255,0,0)), brushFlameCold(Color(255,215-2*glide,0)), brushFlameHot(Color(255-2*glide,191+glide,0)), brushIlum(Color(0,127,255));
Pen pen1(Color(255, 0, 0), 2), pen2(Color(0, 255, 0), 2), pen3(Color(0, 0, 255), 2), pen4(Color(255, 255, 255), 2);
g.FillRectangle(&brush, 0, 0, winW, winH); //фон
g.FillRectangle(&brush1,450, 100, 51, 260); //ракета, цилиндр
conus(G, pen1, brushFlameHot, Area(449, 20, 53, 80));//ракета, нос
for(int i=0;i<5;i++){
polos=polos+9;
g.FillRectangle(&brush2, 450, polos, 51, 3);//ракета, полосы верхние
g.FillRectangle(&brush2, 450, polos+110, 51, 3);//ракета, полосы нижние
}
polos=103;
g.FillEllipse(&brushIlum,460,103, 31,31);
g.FillEllipse(&brushIlum,460,140, 31,31);
drop(G, pen4, brushFlameCold, Area(442, 170, 15, 25)); //пламя из направляющих
drop(G, pen4, brushFlameCold, Area(494, 170, 15, 25));
drop(G, pen4, brushFlameCold, Area(426, 355, 100, 150)); //Основное пламя из ускорителей
conus(G, pen4, brushFlameHot, Area(426, 365, 45, 100),false); //Пламя из ускорителей
conus(G, pen4, brushFlameHot, Area(478, 365, 45, 100),false);
conus(G, pen1, brush1, Area(426, 235, 47, 130)); //ракета, ускорители
conus(G, pen1, brush1, Area(478, 235, 47, 130));
conus(G, pen1, brush1, Area(441, 135, 17, 35)); //ракета, направляющие двигатели
conus(G, pen1, brush1, Area(493, 135, 17, 35));
g.DrawString(fio, -1, &font, bounds, NULL, &brush4);
}
BitBlt(dc, 0,0, winW,winH, mdc, 0,0, SRCCOPY);
EndPaint(hwnd, &ps);
}
Хук обработки событий.
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam){
switch (uMsg){
HANDLE_MSG(hwnd, WM_DESTROY, OnDestroy);
HANDLE_MSG(hwnd, WM_TIMER, OnTimer);
HANDLE_MSG(hwnd, WM_PAINT, OnPaint);
HANDLE_MSG(hwnd, WM_CREATE, OnCreate);
HANDLE_MSG(hwnd, WM_COMMAND, OnCommand);
case WM_ERASEBKGND: return 1;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
Создание класса приложения
bool RegisterWndClass(HINSTANCE hInstance){
WNDCLASSEX wndClass = {
sizeof(wndClass), //UINT cbSize;
CS_HREDRAW | CS_VREDRAW, //UINT style;
&WindowProc, //WNDPROC lpfnWndProc;
0, //int cbClsExtra;
0, //int cbWndExtra;
hInstance, //HINSTANCE hInstance;
NULL, //HICON hIcon;
LoadCursor(NULL, IDC_ARROW), //HCURSOR hCursor;
(HBRUSH)(COLOR_BTNFACE + 1), //HBRUSH hbrBackground;
NULL, //LPCTSTR lpszMenuName;
CLASS_NAME, //LPCTSTR lpszClassName;
NULL, //HICON hIconSm;
};
return RegisterClassEx(&wndClass) != FALSE;
}
Основной цикл приложения
int MainLoop(HWND hMainWindow){
HACCEL accel = LoadAccelerators(g_hInstance, MAKEINTRESOURCE(IDR_MAIN_MENU)); MSG msg;
BOOL res;
while((res = GetMessage(&msg, NULL, 0, 0)) != 0){
if (res == -1){ // произошла ошибка - нужно обработать ее и, вероятно, завершить работу приложения
} else { // Пытаемся обработать сообщение как сообщение от нажатия клавиш быстрого доступа
if(!TranslateAccelerator(hMainWindow, accel, &msg)){ // Это не сообщение о нажатии клавиш быстрого доступа обрабатываем сообщение стандартным образом
TranslateMessage(&msg); // Если это сообщение о нажатии виртуальной клавиши, то добавляем в очередь сообщений сообщения, несущие информацию о коде вводимого пользователем символа
DispatchMessage(&msg); // передаем сообщение в соответствующую оконную процедуру
}
}
}
// сюда мы попадем только в том случае извлечения сообщения WM_QUIT. msg.wParam содержит код возврата, помещенный при помощи функции PostQuitMessage()
return msg.wParam;
}
И старт программы. Мы ыыполняем инициализацию GDI+.
При выходе из блока try произойдет автоматическая деинициализация GDI+
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int nCmdShow){
try {
CGdiplusInitializer initializer;
if (!RegisterWndClass(hInstance)) { return 1; } // Регистрируем класс главного окна
HWND hMainWindow = CreateMainWindow(hInstance); // Создаем главное окно приложения
if (hMainWindow == NULL) { return 1; }
ShowWindow(hMainWindow, nCmdShow); // Показываем главное окно приложения
UpdateWindow(hMainWindow);
int result = MainLoop(hMainWindow); // Запускаем цикл выборки сообщений, пока не получим сигнал о завершении приложения
g_pBitmap.release(); // Удаляем растр перед выходом g_pBitmap.release();
return result;
} catch (std::runtime_error &){ // ошибка инициализации gdi+
return 2;
}
}
Скриншот