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

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

Подписываем
Если у вас возникли сложности с курсовой, контрольной, дипломной, рефератом, отчетом по практике, научно-исследовательской и любой другой работой - мы готовы помочь.
Предоплата всего
Подписываем
Лекция №4. GDI. Контекст устройства.
Другие функции поддержки окон
Часто используемые сообщения
GDI графический интерфейс устройства
GDI (графический интерфейс устройства) обеспечивает графический вывод на устройства отображения информации.
Соотношение между приложениями, GDI и драйверами устройств отображено на рис. 1.
Рис. 1.
Взаимодействие приложения с GDI осуществляется при участии контекста устройства. Дескриптор контекста отображения служит первым аргументом вызова всех функций, связанных с выводом в окно.
GDI (Graphics Device Interface, Graphical Device Interface) один из трёх основных компонентов или «подсистем», вместе с ядром и Windows API составляет пользовательский интерфейс (оконный менеджер GDI) Microsoft Windows.
GDI это интерфейс Windows для представления графических объектов и передачи их на устройства отображения, такие как мониторы и принтеры.
GDI отвечает за отрисовку линий и кривых, отображение шрифтов и обработку палитры. Он не отвечает за отрисовку окон, меню и т. п., эта задача закреплена за пользовательской подсистемой, располагающейся в user32.dll и основывающейся на GDI.
Одно из преимуществ использования GDI вместо прямого доступа к оборудованию это унификация работы с различными устройствами. Используя GDI, можно одними и теми же функциями рисовать на разных устройствах, таких как экран или принтер, получая на них практически одинаковые изображения.
Простые игры, которые не требуют быстрой графики, могут использовать GDI. Однако GDI не обеспечивает качественной анимации, поскольку в нём нет возможности синхронизации с кадровым буфером. Также, в GDI нет растеризации для отрисовки 3D-графики. Современные игры используют DirectX или OpenGL, что даёт программистам доступ к большему количеству аппаратных возможностей.
GDI+
С выходом Windows XP появился потомок подсистемы GDI - 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, который является почти аналогом GDI+, но реализован «сверху донизу» вплоть до драйвера видеокарты (использует некоторые возможности Direct3D в этом драйвере), и может использовать аппаратное ускорение т. е. трехмерный видеопроцессор для рисования некоторых двухмерных объектов (antialiasing и т. д.)
Контекст устройства
Для определения атрибутов текста и изображения, которые выводятся на экран или принтер, используется программный объект под названием «контекст устройства» (Device Context, DC). DC, как и большинство объектов GDI, инкапсулирует подробности реализации и данные в себе и к ним нельзя получить прямой доступ.
Windows-приложения никогда не выводят элементы изображения непосредственно на экран или на принтер, а записывают их в логическую сущность, называемую контекстом устройства.
Контекст устройства - это виртуальная поверхность с присущими ей атрибутами, такими как перо, кисть, шрифт, цвет фона, цвет текста и текущая позиция. Для приложения, независимо от того, какое это на самом деле устройство, все контексты устройства выглядят аналогично.
Для любого рисования нужен объект HDC (хэндл DC). При выводе на принтер HDC получается вызовом CreateDC. При выводе на экран также можно использовать CreateDC, но это приведет к рисованию поверх всех окон вне их границ, потому обычно для рисования на экране используются вызовы GetDC и BeginPaint, принадлежащие уже не GDI, а USER, и возвращающие контекст, ссылающийся на регион отсечения окна.
Контекст устройства это внутренняя структура данных, которая определяет набор графических объектов и ассоциированных с ними атрибутов, а также графических режимов, влияющих на вывод. С точки зрения программы контекст устройства является связующим звеном между программой и устройством вывода. Когда программе нужно осуществить обмен с внешним устройством, то программа должна оповестить GDI о необходимости подготовить устройство для операции ввода-вывода. После того, как устройство подготовлено программа получает хендл контекста устройства, то есть хэндл структуры, содержащей набор характеристик этого устройства. Контекст устройства содержит много атрибутов, определяющих поведение функций GDI.
В набор характеристик контекста устройства входят следующие графические объекты:
1) Перо (pen) для рисования линий;
2) Кисть (brush) для заполнения фона или заливки фигур;
3) Растровое изображение (bitmap) для отображения в указанной области окна;
4) Палитра (palette) для определения набора доступных цветов;
5) Шрифт (font) для вывода текста;
6) Регион (region) для отсечения области вывода.
Windows приложение напрямую не обращается к контексту устройства, она обращается к нему через определённые функции. После того, как все действия произведены и необходимость в использовании устройства отпала, программа должна освободить контекст устройства, чтобы не занимать память. Если контекст устройства не будет освобождаться после операций вывода, то через несколько перерисовок окна система может зависнуть.
Типы контекстов устройств (контекстов отображения)
Можно выделить следующие типы контекста отображения:
(common display context);
контекст устройства для класса окна
(class display context);
(private display context);
При работе с графическими изображениями Windows-приложению нужно придерживаться следующей последовательности действий:
Последнее действие (освобождение либо удаление контекста отображения) должно быть обязательно выполнено.
Система поддерживает кэш для контекстов устройства (DC), таких как общий, родительский и оконный контекст. Система извлекает контекст устройства из кэша всякий раз, когда приложение вызывает функцию GetDC или BeginPaint; система возвращает контекст устройства (DC) в кэш, когда приложение впоследствии вызывает функцию ReleaseDC или EndPaint.
Плюсы: этот контекст используется чаще всего и поэтому для ускорения доступа к нему Windows использует кеширование (размер кеша достаточен для хранения только пяти контекстов отображения).
Минусы: каждый раз, когда приложение получает общий контекст отображения, его атрибуты принимают значения по умолчанию. Если перед выполнением рисования приложение изменит атрибуты контекста отображения, вызвав соответствующие функции GDI, в следующий раз при получении общего контекста отображения эти атрибуты снова примут значения по умолчанию. Поэтому установка атрибутов должна выполняться каждый раз после получения общего контекста отображения.
Для получения общего контекста отображения приложение должно вызвать функцию BeginPaint (при обработке сообщения WM_PAINT) или GetDC (при обработке других сообщений). При этом перед регистрацией класса окна в поле стиля класса окна в структуре WNDCLASS не должны использоваться значения CS_OWNDC , CS_PARENTDC или CS_CLASSDC:
wc.style = 0;
Функция BeginPaint возвращает контекст отображения для окна hwnd:
HDC WINAPI BeginPaint(HWND hwnd,
PAINTSTRUCT FAR* lpps);
Заполняя структуру типа PAINTSTRUCT информацией, которую можно использовать в процессе рисования функция, таким образом, подготавливает указанное окно для рисования.
В случае успешного завершения функция возвращает дескриптор контекста дисплея для клиентской области окна. Кроме этого функция заполняет поля структуры PAINTSTRUCT.
Структура PAINTSTRUCT описана в файле windows.h:
typedef struct tagPAINTSTRUCT
{
HDC hdc; // контекст устройства;
BOOL fErase; // признак стирания фона клиентской области;
RECT rcPaint; // границы недействительного прямоугольника;
BOOL fRestore;
BOOL fIncUpdate;
BYTE rgbReserved[32];
} PAINTSTRUCT;
hdc идентификатор полученного контекста отображения, который должен передаваться в качестве параметра функциям интерфейса GDI, выполняющим рисование. Можно использовать также значение идентификатора контекста, возвращенное функцией BeginPaint, так как эти значения одинаковые.
Поле fErase указывает на то, нужно ли перерисовывать фон окна. Если в этом поле находится значение TRUE, фон окна должен быть перерисован. Такая необходимость может возникнуть в том случае, если в классе, на базе которого создано окно, при регистрации не была выбрана кисть для закрашивания фона (поле hbrBackground структуры WNDCLASS).
Поле rcPaint (представляет собой структуру типа RECT) содержит координаты верхнего левого и правого нижнего угла прямоугольника, внутри которого нужно рисовать.
Формат структуры RECT, описанной в файле windows.h имеет вид:
typedef struct tagRECT
{
int left;
int top;
int right;
int bottom;
} RECT;
При обработке сообщения WM_PAINT приложение должно суметь перерисовать все окно или любую его часть. Сообщение WM_PAINT может попасть в функцию окна в том случае, если все окно или его часть требуют перерисовки. Поле rcPaint в структуре PAINTSTRUCT содержит координаты прямоугольной области, расположенной в окне и требующей перерисовки.
Остальные поля зарезервированы для Windows и не используются приложениями.
Контекст устройства, полученный при помощи функции BeginPaint, необходимо освободить перед завершением обработки сообщения WM_PAINT, вызвав функцию EndPaint :
void WINAPI EndPaint(HWND hwnd,
const PAINTSTRUCT FAR* lpps);
Функции EndPaint передаются те же параметры, что и функции BeginPaint.
Обычно обработчик сообщения WM_PAINT выглядит следующим образом:
PAINTSTRUCT ps;
HDC hdc;
........
case WM_PAINT:
{
// Получаем контекст отображения
hdc = BeginPaint(hwnd, &ps);
// После получения контекста отображения
// можно вызывать функции GDI
TextOut(hdc, 0, 0, (LPSTR)"String", 6);
.
.
// Освобождаем контекст отображения
EndPaint(hwnd, &ps);
break;
}
Функции BeginPaint и EndPaint можно использовать только внутри обработчика сообщения WM_PAINT.
Если же необходимо в программе рисовать не во время обработки сообщения WM_PAINT, то приложение должно получить контекст отображения с помощью функции GetDC. Перед выходом из обработчика сообщения следует освободить полученный контекст отображения, вызвав функцию ReleaseDC.
Функция GetDC возвращает контекст отображения для окна с идентификатором hwnd:
HDC WINAPI GetDC(HWND hwnd);
Функция ReleaseDC освобождает контекст отображения hdc, полученный для окна hwnd:
int WINAPI ReleaseDC(HWND hwnd, HDC hdc);
Общий контекст отображения, кешируется операционной системой Windows для ускорения доступа к нему. Контекст устройства для класса окна хранится отдельно в единственном экземпляре и используется всеми окнами, созданными на базе класса окна. При регистрации такого класса окна необходимо указать стиль CS_CLASSDC:
wc.style = CS_CLASSDC;
Все окна, созданные на базе класса, имеющего стиль CS_CLASSDC, будут использовать один общий контекст отображения.
В отличие от общего контекста устройства, приложение, однажды получив контекст устройства для класса окна, может не освобождать его. То есть после функций BeginPaint и GetDC можно не вызывать функции EndPaint и ReleaseDC. Если же приложение вызовет функцию EndPaint или ReleaseDC, они не будут ничего делать и сразу вернут управление.
Для уменьшения вероятности ошибки рекомендуется всегда освобождать контекст отображения.
Контекст устройства для класса окна нужно использовать в тех случаях, когда нежелательно выполнять настройку всех атрибутов контекста отображения после каждого вызова функций BeginPaint и GetDC. Эту настройку можно выполнить только один раз. Каждый раз, когда функция окна получает контекст устройства для класса, ей остается выполнить настройку только двух атрибутов области ограничения и начала системы физических координат устройства вывода. Остальные атрибуты остаются без изменений и не требуют повторной настройки.
При получении контекста устройства для класса окна в нем устанавливаются атрибуты области ограничения и начало системы физических координат устройства отображения.
Если указать в стиле класса окна значение CS_OWNDC, то для каждого окна, созданного на базе такого класса, Windows создаст отдельную структуру контекста устройства:
wc.style = CS_OWNDC;
Личный контекст отображения можно, получив один раз, никогда не освобождать. Можно один раз настроить атрибуты контекста отображения сразу после получения самого контекста и использовать полученный контекст без изменений до завершения работы приложения.
Не будет ошибкой, если приложение будет использовать личный контекст отображения как общий.
Личный контекст проще в использовании, так как его можно получать и настраивать только один раз для каждого окна, однако оперативная память будет расходоваться менее экономно.
Родительский контекст отображения используется для дочерних окон. Он позволяет дочерним окнам "унаследовать" атрибуты контекста отображения у родительского окна. Например, дочернее окно может использовать для вывода текста тот же шрифт и цвет, что и родительское окно.
Для использования родительского контекста отображения в классе, на базе которого создается дочернее окно, перед регистрацией необходимо указать стиль CS_PARENTDC :
wc.style = CS_PARENTDC;
Все описанные выше контексты отображения позволяют рисовать только во внутренней области окна (client region).
С помощью функции GetWindowDC приложение может получить контекст отображения для окна, позволяющий рисовать в любом месте окна (в области заголовка окна, в области системного меню, рамки окна, кнопок изменения размера и т. п.).
Контекст отображения, полученный для окна, используется аналогично общему контексту отображения. После получения контекста его атрибуты принимают значения по умолчанию. Приложение обязано освобождать этот контекст сразу после использования, вызывая функцию ReleaseDC.
Например, необходимо получить контекст отображения, позволяющий рисовать во всем окне приложения:
hdc = GetWindowDC(hwnd);
Освободить указанный контекст отображения можно следующим образом:
ReleaseDC(hwnd, hdc);
Особенностью данного контекста является то, что в нем выбрана система координат, начало которой находится в левом верхнем углу окна (всего окна, а не его внутренней области).
Это контекст обычно используется при необходимости заменить стандартные элементы окна (заголовок, полосы просмотра и т.п.) на собственные элементы оформления.
Вывод изображений на такое устройство, как принтер, может выполняться с использованием тех же приемов, что и вывод в окно приложения.
Контекст физического устройства не получается, а создается, для чего используется функция CreateDC:
HDC WINAPI CreateDC(
LPCSTR lpszDriver, // имя драйвера
LPCSTR lpszDevice, // имя устройства
LPCSTR lpszOutput, // имя файла или порта вывода
const void FAR* lpvInitData); // данные для инициализации
Параметр lpszDriver имя драйвера совпадает с именем файла *.drv, содержащего сам драйвер и расположенного в системном каталоге Windows.
Имя устройства lpszDevice - название устройства, описанное, например, в файле win.ini в разделе [devices].
Параметр lpszOutput - имя файла или порта вывода;
Параметр lpvInitData указывает на структуру данных типа DEVMODE, используемую при инициализации устройства вывода. Если настройки наследуются из Control Panel (панели управления Windows), то данный параметр нужно устанавливать в NULL.
Пример1: Создать контекст устройства для лазерного принтера HP Laserjet III, подключенного к порту LPT1:, для этого принтера установлен драйвер hppcl5a.drv:
hdc = CreateDC("hppcl5a", "HP LaserJet III", "LPT1:", NULL);
Пример2: Создать контекст устройства для принтера Epson FX-850, подключенного к порту LPT2:, и работающему через драйвер epson9.drv:
hdc = CreateDC("epson9", "Epson FX-850", "LPT2:", NULL);
Созданный при помощи функции CreateDC контекст устройства следует удалить (но не освободить), вызвав функцию DeleteDC:
BOOL WINAPI DeleteDC(HDC hdc);
Эта функция возвращает TRUE при нормальном завершении и FALSE при возникновении ошибки.
1 способ: Если требуется получить контекст дисплея, позволяющий приложению рисовать в любом месте экрана, то такой контекст создаётся при помощи функции CreateDC:
hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
Начало системы координат, выбранной в данный контекст, располагается в верхнем левом углу экрана видеомонитора.
После использования созданный таким образом контекст отображения следует удалить, вызвав функцию DeleteDC.
2 способ: Используя функцию GetDC, указав в качестве параметра значение NULL:
hdc = GetDC(NULL);
Этот контекст следует освободить при помощи функции ReleaseDC, передав ей вместо идентификатора окна значение NULL:
ReleaseDC(NULL, hdc);
3 способ: Используя функцию GetDCEx.
Если приложению необходимо получить информацию об устройстве вывода (например, с помощью функции GetDeviceCaps), оно может создать вместо обычного информационный контекст. Информационный контекст нельзя использовать для рисования, однако он занимает меньше места в памяти.
Информационный контекст создается при помощи функции CreateIC , аналогичной по своим параметрам функции CreateDC:
HDC WINAPI CreateIC(
LPCSTR lpszDriver, // имя драйвера
LPCSTR lpszDevice, // имя устройства
LPCSTR lpszOutput, // имя файла или порта вывода
const void FAR* lpvInitData); // данные для инициализации
После использования информационный контекст следует удалить, вызвав функцию DeleteDC.
Функция GetDeviceCaps извлекает зависимую от устройства информацию для заданного устройства.
Синтаксис
int GetDeviceCaps(
HDC hdc, // дескриптор DC
int nIndex // индекс действия
);
Параметры
hdc - Дескриптор DC.
nIndex - Устанавливает элемент для возврата. Этот параметр может быть одним из ниже перечисленных значений.
DRIVERVERSION Версия драйвера устройства.
TECHNOLOGY Технология устройства. Это может быть любое из ниже перечисленных значений.
HORZSIZE Ширина, в миллиметрах, физического экрана.
VERTSIZE Высота, в миллиметрах, физического экрана.
HORZRES Ширина, в пикселях, экрана.
VERTRES Высота, в растровых строках, экрана.
NUMBRUSHES Число зависимых от устройства кистей.
NUMPENS Число зависимых от устройства перьев.
NUMFONTS Число зависимых от устройства шрифтов.
NUMCOLORS Число записей в таблице цветов устройства, если устройство имеет разрядность (глубину) цвета не больше, чем 8 битов на пиксель. Для устройств с большими разрядностями цвета, возвращается - (минус) 1.
ASPECTX Относительная ширина пикселя устройства, которое использовалась для рисования линии.
ASPECTY Относительная высота пикселя устройства, которое использовалась для рисования линии.
ASPECTXY Диагональная ширина пикселя устройства, которое использовалась для рисования линии.
В работе с битовыми изображениями bitmap часто используется такое "устройство вывода", как оперативная память. Приложение может полностью подготовить изображение в оперативной памяти, получив контекст для памяти, и затем быстро вывести готовое изображение на экран.
Контекст для памяти создается совместимым с тем контекстом отображения, в котором будет выполняться вывод на физическое устройство.
Для создания совместимого контекста используется функция CreateCompatibleDC :
HDC WINAPI CreateCompatibleDC(HDC hdc);
Созданный таким образом контекст памяти удаляется при помощи функции DeleteDC.
Алгоритм работы с контекстом в памяти состоит из следующих шагов:
При необходимости шаги 6 и 7 можно поменять местами. Когда и как удалять замещенный bitmap, зависит от программиста и поставленной перед ним задачи.
Притмер 1. Вывод графического изображения из файла в окно на Си:
#include <windows.h>
LRESULT CALLBACK PaintWndProc(HWND,UINT,UINT,LONG);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
HWND hMainWnd;
char szClassName[] = "MyClass";
MSG msg;
WNDCLASSEX wc;
// Заполняем структуру класса окна
wc.cbSize = sizeof(wc);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = PaintWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);
wc.lpszMenuName = NULL;
wc.lpszClassName = szClassName;
wc.hIconSm = LoadIcon(NULL, IDI_INFORMATION);
// Регистрируем класс окна
if (!RegisterClassEx(&wc)) {
MessageBox(NULL, "Cannot register class", "Error", MB_OK);
return 0;
}
hMainWnd=CreateWindow(szClassName,"Должно быть фото...",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,CW_USEDEFAULT,
CW_USEDEFAULT,CW_USEDEFAULT,
NULL,NULL,
hInstance,NULL);
if (!hMainWnd)
{
MessageBox(NULL,"Не могу создать окно","Error",MB_OK);
return 0;
}
// Показываем наше окно
ShowWindow(hMainWnd,nCmdShow);
UpdateWindow(hMainWnd);
// Создаём цикл сообщений
while (GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
// Создаём оконную процедуру
LRESULT CALLBACK PaintWndProc(HWND hWnd,UINT Message,
UINT wParam,LONG lParam)
{
HDC hDC,hCompatibleDC;
PAINTSTRUCT PaintStruct;
HANDLE hBitmap,hOldBitmap;
RECT Rect;
BITMAP Bitmap;
switch(Message)
{
case WM_PAINT:
// Получаем контекст устройства
hDC=BeginPaint(hWnd,&PaintStruct);
// Загружаем bitmap, который будет отображаться в окне
hBitmap=LoadImage(NULL, "Foto118.bmp",IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
// Получаем размерность загруженного bitmapа
GetObject(hBitmap,sizeof(BITMAP),&Bitmap);
// Создаём совместимый с контекстом окна контекст в памяти
hCompatibleDC=CreateCompatibleDC(hDC);
// Делаем загруженный из файла bitmap текущим в совместимом контексте
hOldBitmap=SelectObject(hCompatibleDC,hBitmap);
// Определяем размер рабочей области окна
GetClientRect(hWnd,&Rect);
// Копируем bitmap с совместимого на основной контекст с масштабированием
StretchBlt(hDC,0,0,Rect.right,Rect.bottom,hCompatibleDC,0,0,Bitmap.bmWidth,Bitmap.bmHeight,SRCCOPY);
// Делаем старый bitmap текущим
SelectObject(hCompatibleDC,hOldBitmap);
// Удаляем загруженный с диска bitmap
DeleteObject(hBitmap);
// Удаляем совместимый контекст
DeleteDC(hCompatibleDC);
// Освобождаем основной контекст, завершая перерисовку рабочей области окна
EndPaint(hWnd,&PaintStruct);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd,Message,wParam,lParam);
}
Для отображения рисунка Foto125.bmp необходимо, чтобы этот файл находился в текущей директории Debug. Можно также указать адрес файла с изображением, если он находится не в текущей директории:
D:\\Задания\\WinAPI\\Graf\\Debug
Пример 2. Вывод графического изображения из файла в окно на ассемблере.
;выводит изображения графического файла в окно
.386
.model flat, stdcall
WM_DESTROY equ 2
WM_CREATE equ 1
WM_PAINT equ 0Fh
CS_VREDRAW equ 1h
CS_HREDRAW equ 2h
CS_GLOBALCLASS equ 4000h
WS_OVERLAPPEDWINDOW equ 000CF0000H
style equ CS_HREDRAW+CS_VREDRAW+CS_GLOBALCLASS
IDI_APPLICATION equ 32512
IDC_ARROW equ 32512
SW_SHOWNORMAL equ 1
WHITE_BRUSH equ 0
CW_USEDEFAULT equ 80000000h
extrn BeginPaint:PROC
extrn CreateCompatibleDC:PROC
extrn CreateWindowExA:PROC
extrn DefWindowProcA:PROC
extrn DeleteDC:PROC
extrn DeleteObject:PROC
extrn DispatchMessageA:PROC
extrn EndPaint:PROC
extrn ExitProcess:PROC
extrn GetClientRect:PROC
extrn GetMessageA:PROC
extrn GetModuleHandleA:PROC
extrn GetObjectA:PROC
extrn GetStockObject:PROC
extrn LoadCursorA:PROC
extrn LoadIconA:PROC
extrn LoadImageA:PROC
extrn PostQuitMessage:PROC
extrn RegisterClassA:PROC
extrn SelectObject:PROC
extrn ShowWindow:PROC
extrn StretchBlt:PROC
extrn TranslateMessage:PROC
extrn UpdateWindow:PROC
MSGSTRUCT STRUC
MSHWND DD ? ; идентификатор окна, получающего сообщение
MSMESSAGE DD ? ; идентификатор сообщения
MSWPARAM DD ? ; доп. информация о сообщении
MSLPARAM DD ? ; доп. информация о сообщении
MSTIME DD ? ; время посылки сообщения
MSPT DD ? ; положение курсора, во время посылки сообщения
MSGSTRUCT ENDS
WNDCLASS STRUC
clsStyle DD ? ; стиль окна
clsLpfnWndProc DD ? ; указатель на процедуру окна
clsCbClsExtra DD ? ; информация о доп. байтах для данной структуры
clsCbWndExtra DD ? ; информация о доп. байтах для окна
clsHInstance DD ? ; дескриптор приложения
clsHIcon DD ? ; идентификатор иконы окна
clsHCursor DD ? ; идентификатор курсора окна
clsHbrBackground DD ? ; идентификатор кисти окна
clsLpszMenuName DD ? ; имя-идентификатор меню
clsLpszClassName DD ? ; специфицирует имя класса окон
WNDCLASS ENDS
BITMAP struct
bmType dd ?
bmWidth dd ?
bmHeight dd ?
bmWidthBytes dd ?
bmPlanes dw ?
bmBitsPixel dw ?
bmBits dd ?
BITMAP ends
PAINTSTRUCT STRUC
hdc DWORD 0
fErase DWORD 0
left DWORD 0
top DWORD 0
right DWORD 0
bottom DWORD 0
fRes DWORD 0
fIncUp DWORD 0
Reserv DB 32 dup(0)
PAINTSTRUCT ENDS
RECT STRUC
L DWORD ?
T DWORD ?
R DWORD ?
B DWORD ?
RECT ENDS
LR_LOADFROMFILE = 010h
SRCCOPY = 00CC0020h
IMAGE_BITMAP = 0
.data
newhwnd dd 0
MSG MSGSTRUCT <?>
wc WNDCLASS <?>
hDC dd ?
hCompatibleDC dd ?
Ps PAINTSTRUCT <?>
hBitmap dd ?
hOldBitmap dd ?
Rect RECT <?>
Bitmap BITMAP <?>
hInstance dd 0
szTitleName db 'DCDemo', 0
szClassName db 'ASMCLASS32',0
szImg db 'IMG2.bmp',0
.code
;-----------------------------------------------------------------------------
start:
call GetModuleHandleA,0
mov [hInstance],eax
; initialize the WndClass structure
mov [wc.clsStyle], CS_HREDRAW + CS_VREDRAW
mov [wc.clsLpfnWndProc], offset DCDemoWndProc
mov [wc.clsCbClsExtra], 0
mov [wc.clsCbWndExtra], 0
mov eax, [hInstance]
mov [wc.clsHInstance], eax
call LoadIconA, 0, IDI_APPLICATION
mov [wc.clsHIcon], eax
call LoadCursorA, 0 ,IDC_ARROW
mov [wc.clsHCursor], eax
call GetStockObject, WHITE_BRUSH
mov [wc.clsHbrBackground], eax
mov [wc.clsLpszMenuName], 0
mov [wc.clsLpszClassName], offset szClassName
call RegisterClassA, offset wc
call CreateWindowExA, 0,offset szClassName,offset szTitleName, \
WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, \
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,0,0, \
[hInstance], 0
mov [newhwnd],eax
call ShowWindow, [newhwnd], SW_SHOWNORMAL
call UpdateWindow, [newhwnd]
msg_loop:
PUSH 0
PUSH 0
PUSH 0
PUSH OFFSET MSG
CALL GetMessageA
CMP EAX, 0
JE END_LOOP
PUSH OFFSET MSG
CALL TranslateMessage
PUSH OFFSET MSG
CALL DispatchMessageA
JMP msg_loop
END_LOOP:
; выход из программы (закрыть процесс)
PUSH [MSG.MSWPARAM]
CALL ExitProcess
;-----------------------------------------------------------------------------
DCDemoWndProc proc uses ebx edi esi, hwnd:DWORD, wmsg:DWORD,\
wparam:DWORD, lparam:DWORD
cmp [wmsg], WM_PAINT
je wmpaint
cmp [wmsg], WM_DESTROY
je wmdestroy
call DefWindowProcA, [hwnd],[wmsg],[wparam],[lparam]
jmp finish
wmpaint:
;==================================================
; Получаем контекст устройства
call BeginPaint, [hwnd], offset Ps
mov [hDC], eax
; Загружаем bitmap, который будет отображаться в окне, из файла
call LoadImageA, 0,offset szImg,IMAGE_BITMAP, \
0,0,LR_LOADFROMFILE
mov [hBitmap], eax
; Получаем размерность загруженного bitmap'а
call GetObjectA, [hBitmap], size BITMAP, offset Bitmap
; Создаем совместимый с контекстом окна контекст в памяти
call CreateCompatibleDC, [hDC]
mov [hCompatibleDC], eax
; Делаем загруженный из файла bitmap текущим в совместимом контексте
call SelectObject, [hCompatibleDC], [hBitmap]
mov [hOldBitmap], eax
; Определяем размер рабочей области окна
call GetClientRect, [hwnd], offset Rect
; Копируем bitmap с совместимого на основной контекст
; с маштабированием
call StretchBlt, [hDC],0,0,[Rect.R],[Rect.B], \
[hCompatibleDC],0,0,[Bitmap.bmWidth],[Bitmap.bmHeight], \
SRCCOPY
; Вновь делаем старый bitmap текущим
call SelectObject, [hCompatibleDC], [hOldBitmap]
; Удаляем загруженный с диска bitmap
call DeleteObject, [hBitmap]
; Удаляем совместимый контекст
call DeleteDC, [hCompatibleDC]
; Освобождаем основной контекст, завершая перерисовку
; рабочей области окна
call EndPaint, [hwnd], offset Ps
mov eax, 0
jmp finish
;====================================================
wmdestroy:
push 0
call PostQuitMessage
mov eax, 0
finish:
ret
DCDemoWndProc endp
ends
end start
Самостоятельно на практике: Ввести программы из примера 1 и 2. Довести их до рабочего состояния. Разработать программу на ассемблере, которая бы выводила в окно из четырёх различных графических файла четыре различных изображения. Пример:
Контекст для метафайла
Контекст для метафайла позволяет записывать команды GDI в файл, а затем проиграть такой файл на физическом устройстве вывода. Файл может находиться в памяти или на диске.
1) Для создания контекста метафайла используется функция CreateMetaFile:
HDC WINAPI CreateMetaFile(LPCSTR lpszFileName);
Параметр lpszFileName указывает на строку, содержащую путь к имени файла, в который будут записаны команды GDI, или NULL. Если параметр равен NULL, то создается метафайл в оперативной памяти.
2) После выполнения рисования в контексте метафайла следует закрыть метафайл, вызвав функцию CloseMetaFile :
HMETAFILE WINAPI CloseMetaFile(HDC hdc);
Функция закрывает метафайл для контекста hdc и возвращает идентификатор метафайла.
3) Если имеется идентификатор метафайла, то метафайл можно скопировать в обычный дисковый файл, с помощью функции CopyMetaFile :
HMETAFILE WINAPI CopyMetaFile(HMETAFILE hmf,
LPCSTR lpszFileName);
Параметр hmf определяет метафайл, параметр lpszFileName содержит путь к имени файла, в который будет записан метафайл .
4) Можно проиграть метафайл в контексте отображения или контексте устройства, вызвав функцию PlayMetaFile:
BOOL WINAPI PlayMetaFile(HDC hdc, HMETAFILE hmf);
5) При помощи функции DeleteMetaFile можно удалить метафайл:
BOOL WINAPI DeleteMetaFile(HMETAFILE hmf);
При удалении метафайла освобождается оперативная память, занятая метафайлом. Если метафайл был создан как дисковый файл, функция DeleteMetaFile не удаляет его с диска.
6) Для того чтобы воспользоваться метафайлом, хранящимся в виде дискового файла, его нужно загрузить функцией GetMetaFile, указав в качестве параметра путь к соответствующему файлу:
HMETAFILE WINAPI GetMetaFile(LPCSTR lpszFileName);
Использование сообщения WM_PAINT
Сообщение WM_PAINT система посылает окну во всех случаях, требующих перерисовки клиентской области окна, например при наступлении следующих событий:
Кроме того, приложение может само инициировать посылку сообщения WM_PAINT посредством вызова одной из функций InvalidateRect, InvalidateRgn или UpdateWindow.
Функция UpdateWindow посылает сообщение WM_PAINT непосредственно в оконную процедуру, минуя очередь сообщений приложения.
Использование функций InvalidateRect и InvalidateRgn
Если необходимо сгенерировать сообщение WM_PAINT, то можно воспользоваться функциями InvalidateRect и InvalidateRgn. Функция InvalidateRect имеет следующий прототип:
BOOL InvalidateRect(HWND hWnd, CONST RECT* lpRect, BOOL bErase);
hWnd дескриптор окна, у которого изменился обновляемый регион. Если этот параметр равен NULL, то система обновляет и перерисовывает все окна приложения, а также посылает сообщения WM_ERASEBKGNB и WM_NCPAINT оконной процедуре до возврата из функции.
lpRect указатель на строку типа RECT, содержащую клиентские координаты прямоугольника, который добавляется к обновляемому региону. Если этот параметр имеет значение NULL, то к обновляемому региону добавляется вся клиентская область.
bErase флаг, определяющий будет ли стираться фон обновляемого региона. Если этот параметр равен TRUE, то фон стирается, когда вызывается функция BeginPaint. Если указано значение FALSE, то фон остаётся без изменения. То есть, функция InvalidateRect добавляет прямоугольник в область перерисовки окна hwnd.
Функция InvalidateRgn имеет следующий прототип:
BOOL InvalidateRgn(HWND hWnd, HRGN hRgn, BOOL bErase);
Параметр hRgn содержит дескриптор региона, добавляемого к обновляемому региону. Этот дескриптор можно получить при помощи функций типа CreateRectRgn, CreatePolygonRgn и др. функций.
Функция ValidateRect
Добавляемые для перерисовки прямоугольники накапливаются до обработки сообщения WM_PAINT. В качестве единой области перерисовки Windows вычисляет один прямоугольник, который охватывает все добавленные прямоугольники.
Функция ValidateRect удаляет прямоугольную область из списка прямоугольников перерисовки. Прототип этой функции:
BOOL ValidateRect(HWND hwnd, CONST RECT *lpRect);
Параметр hwnd указывает на окно, из области перерисовки которого исключается прямоугольник. Если этот параметр равен NULL, Windows перерисовывает все окна посылает сообщения WM_ERASEBKGND и WM_NCPAINT функциям всех окон.
Параметр lpRect указывает на прямоугольник, который будет удалён из области перерисовки. Если lpRect = NULL, то из области обновления удаляются все прямоугольники.
В случае успешного выполнения функция возвращает ненулевое значение.
Операции по обмену блоками бит (bit block transfer, BLT) или тернарные растровые операции (ternary raster operation) механизм, осуществляющий передачу растровых изображений между различными контекстами устройств.
Основная идея растровых операций заключается в организации обмена данными между двумя контекстами устройств. В Windows-приложении можно осуществить передачу изображения:
2) между двумя битмапами;
GDI содержит 3 функции, осуществляющих такую передачу изображений PatBlt, BitBlt и StretchBlt:
BOOL PatBlt(hDC, nX, nY, nWidth, nHeight, dwROP);
BOOL BitBlt(hDestDC, nDestX, nDestY, nDestWidth, nDestHeight, hSrcDC, nSrcX, nSrcY, dwROP);
BOOL StretchBlt(hDestDC, nDestX, nDestY, nDestWidth, nDestHeight, hSrcDC, nSrcX, nSrcY, nSrcWidth, nSrcHeight, dwROP);
Все три функции строят результирующее изображение на контекстеприемнике, используя в качестве исходных данных:
1. изображение, создаваемое на приемнике при закраске фона текущей кистью, выбранной в контекстприемник (это называется образцом, pattern).
2. изображение, существующее на контекстеисточнике (исходное изображение, source).
3. изображение, существующее в данный момент на контекстеприемнике (имеющееся изображение, destination).
В процессе выполнения растровой операции три исходных изображения (битовых последовательности p, d, s) комбинируются и получается результирующее изображение. Код выполняемой операции задается параметром dwROP индексом тернарной растровой операции.
Предположим, что биты, определяющие битмап совместимого контекста обозначают буквой S (source источник, ресурс), биты заливки буквой Р (pattern - образец), а биты, на которых будет прорисовываться изображение буквой D (destination назначение, место назначения). Операции, которые выполняются с битами S, P, D: а побитовое И, n побитовое НЕТ, о побитовое ИЛИ, х побитовое исключающее ИЛИ.
Х1 Х2 И Х1 НЕТ Х1 Х2 ИЛИ Х1 Х2 Исключающее ИЛИ
0 0 0 0 1 0 0 0 0 0 0
0 1 0 1 0 0 1 1 0 1 1
1 0 0 1 0 1 1 0 1
1 1 1 1 1 1 1 1 0
Обозначив знак операции как Ор, то действия с битами можно записать таким образом: PSОр. То есть необходимо взять пиксель паттерны и прорисовываемого битмапа и произвести над ними операцию (бинарная растровая операция). Если в операции участвует три операнда, то получим DPSОр1Ор2 (тернарная растровая операция).
Определить индекс операции PSx и DPSxx.
P 1 1 1 1 0 0 0 0
S 1 1 0 0 1 1 0 0
D 1 0 1 0 1 0 1 0
PSx 0 0 1 1 1 1 0 0 =0x3c ????
DPSxx 1 0 0 1 0 1 1 0 = 0x96 ????
DPx 0 1 0 1 1 0 1 0 = 5A ????
В документации по SDK имеется таблица, перечисляющая индексы 256 возможных растровых операций, их имена и короткое пояснение к каждой операции. Имена присвоены только 15 наиболее употребляемым операциям. Таблица, представленная в документации имеет следующий вид:
Number |
Hex ROP |
Boolean function |
Common Name |
0 |
00000042 |
0 |
BLACKNESS |
... |
|||
0D |
000D0B25 |
PDSnaon |
|
... |
|||
Поле «Hex ROP» индекс тернарной растровой операции (используется в качестве параметра dwROP).
Поле «Boolean function» содержит пояснение к выполняемой операции;
Поле «Common name» имя растровой операции, если оно назначено.
Основные коды растровых операций приведены в таблице.
Функция StretchBlt
Скопировать bitmap с совместимого на основной контекст с масштабированием можно с помощью функции StretchBlt(). Эта функция описана следующим образом:
StretchBlt(HDC, int, int, int, int, HDC, int, int, int, int, DWORD);
Устройством приёмником должно быть окно, в которое необходимо вывести изображение и которое представляется своим контекстом. В качестве устройства источника (псевдоустройства) необходимо создать в памяти область, которая по своим возможностям должна быть совместима с экраном и в которую сначала необходимо поместить изображение, а затем скопировать это изображение в окно приёмник. Поэтому функция StretchBlt() должна получать через свои параметры дескрипторы обоих контекстов.
Первый и шестой аргументы функции хэндлы окна (hDC) и совместимого контекста (hCompatibleDC) соответственно. Одиннадцатый аргумент функции StretchBlt() это код растровой операции.
Первые пять аргументов описывают тот прямоугольник на экране, в который будет вписан битмап. Ту часть битмапа, которая будет вписана в прямоугольник на экране, описывают следующие пят аргументов.
При масштабировании, используя функцию StretchBlt возможно два случая:
изображение увеличивается, то некоторые строки (столбцы) будут дублироваться;
изображение уменьшается, то некоторые строки (столбцы) будут комбинироваться в одну строку (столбец).
Объединение строк (столбцов) при сжатии может осуществляться различными способами, которые выбираются с помощью функции:
UINT SetStretchBltMode(hDC, nMode);
параметр nMode задает режим объединения строк:
BLACKONWHITE |
выполняется операция И (AND). В результате получается, что черный цвет имеет "приоритет" над белым сочетание черного с белым рассматривается как черный |
WHITEONBLACK |
выполняется операция ИЛИ (OR). При этом "приоритет" принадлежит белому над черным сочетание черного с белым дает белый |
COLORONCOLOR |
при этом происходит простое исключение строк (столбцов). |
HALFTONE 1 |
только в Win32 API; происходит усреднение цвета объединяемых точек. |
Функция BitBlt
BOOL BitBlt(hDestDC, nDestX, nDestY, nDestWidth, nDestHeight, hSrcDC, nSrcX, nSrcY, dwROP);
Функция осуществляет передачу изображений между двумя контекстами устройств, при этом передается прямоугольный фрагмент, который на контексте-приемнике и на контексте-источнике имеет одинаковые размеры. Для задания координат и размеров используется логическая система координат, и логический размер изображения в обеих системах может быть различным.
Отдельно надо рассмотреть случай, когда один из контекстов является цветным, а другой чернобелым при этом особым образом осуществляется преобразование цветов:
при переходе от монохромного к цветному цвет, закодированный 1, соответствует цвету фона (задаваемому функцией SetBkColor), а цвет 0 цвету текста (функция SetTextColor).
при переходе от цветного к монохромному считается, что если цвет точки совпадает с цветом фона, то эта точка кодируется цветом 1, иначе 0.
Функция PatBlt не может рисовать битовые изображения, но используется для закраски прямоугольных областей экрана. Эта функция имеет имя PatBlt :
BOOL WINAPI PatBlt(
HDC hdc, // контекст для рисования
int nX, // x-координата верхнего левого угла закрашиваемой области
int nY, // y-координата верхнего левого угла закрашиваемой области
int nWidth, // ширина области
int nHeight, // высота области
DWORD dwRop); // код растровой операции
При использовании этой функции можно закрашивать области экрана с использованием следующих кодов растровых операций: PATCOPY, PATINVERT, PATPAINT, DSTINVERT, BLACKNESS, WHITENESS.
Возвращаемое функцией PatBlt значение равно TRUE при успешном завершении или FALSE при ошибке.
PAGE 36