КОМПЬЮТЕРНЫЕ КУРСЫ "ПОИСК"
Как отобразить BMP файл на форме
ТЕОРИЯ
Этот контекст используется для хранения изображений, которые затем будут скопированы на устройство вывода. Сам по себе контекст в памяти не создается. Он обязательно создается как совместимый с тем устройством или окном, на которое предполагается копировать информацию (вот он - совместимый контекст - переходник между программой и драйвером устройства!). Алгоритм работы с контекстом в памяти состоит из нескольких шагов:
При необходимости шаги 6 и 7 можно поменять местами. Когда и как удалять замещенный bitmap, зависит от программиста и поставленной перед ним задачи.
Именно этот способ и используется в большинстве программ для копирования изображения.
Но, как известно, лучше один раз увидеть, чем сто раз услышать (по-английски это звучит еще более категорично - seeing is believing - увидеть, значит поверить). Поэтому давайте напишем небольшую программу, в которой продемонстрируем возможности Windows по манипулированию изображениями.
SEEING IS BELIEVING
Перед тем, как начинать писать программу, мы должны прояснить для себя еще одну тему. В каких единицах измеряются размеры окна и, соответственно, все смещения в окне? Для того чтобы ответить на эта вопрос, мы должны рассмотреть
Режимы отображения
Подавляющее большинство функций, работающих с оконными координатами, определяют координаты относительно начала рабочей области окна, т. е. от левого верхнего угла.
Таким образом, даже при перемещении окна координаты объектов внутри окна остаются неизменными. При этом единицы, в которых измеряются координаты, зависят от режима отображения (mapping mode), установленного для данного окна. Единицы измерения, зависящие от режима отображения, называют логическими единицами, а координаты в этом случае называют логическими координатами.
При выводе информации на конкретное устройство единицы логических координат преобразуются в физические единицы, которыми являются пиксели.
Для установки текущего режима отображения используется функция SetMappingMode(), которая описана следующим образом:
function SetMapMode(
DC: HDC;
p2: Integer
): Integer; stdcall;
ППервый аргумент этой функции - хэндл контекста устройства, для которого устанавливается данный режим. Второй аргумент определяет задаваемый режим отображения. При создании окна по умолчанию устанавливается режим ММ_ТЕХТ, т. е. все координаты исчисляются в пикселах.
СОДЕРЖИМОЕ
Наша программа будет отображать bitmap в окне и при необходимости производить его масштабирование:
program Project1; uses Windows, Messages; var wc: TWndClassEx; MainWnd: THandle; Mes: TMsg; const ClassName = 'DCDemo'; AppName = 'Program 1'; function WndProc(hWnd, Msg, WParam, LParam: Integer): Integer; stdcall; var hwndDC, hCompatibleDC: HDC; PaintStruct: TPaintStruct; hBitmap, hOldBitmap: THandle; Rect: TRect; Bitmap: TBitmap; begin Result := 0; case Msg of WM_DESTROY: PostQuitMessage(0); WM_PAINT: begin // получаем контекст устройства hwndDC := BeginPaint(hWnd, PaintStruct); // Загружаем bitmap, который будет отображаться в окне, из файла hBitmap := LoadImage(0, 'chemical.bmp', IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); // Получаем размерность загруженного bitmap GetObject(hBitmap, SizeOf(TBitmap), @Bitmap); // Создаем совместимый с контекстом окна контекст в памяти hCompatibleDC := CreateCompatibleDC(hwndDC); // Делаем загруженный из файла bitmap текущим в совместимом формает hOldBitmap := SelectObject(hCompatibleDC, hBitmap); // Определяем размер рабочей области окна. GetClientRect(hWnd, Rect); // Копируем bitmap с совместимого на основной контекст с масштабированием StretchBlt(hwndDC, 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); end else Result := DefWindowProc(hWnd, Msg, WParam, LParam); end; end; begin wc.cbSize := SizeOf(wc); wc.style := CS_VREDRAW or CS_HREDRAW; wc.lpfnWndProc := @WndProc; wc.cbClsExtra := 0; wc.cbWndExtra := 0; wc.hInstance := HInstance; wc.hbrBackground := COLOR_WINDOW + 1; wc.hCursor := LoadCursor(0, IDC_ARROW); wc.hIcon := LoadIcon(0, IDI_APPLICATION); wc.hIconSm := wc.hIcon; wc.lpszMenuName := nil; wc.lpszClassName := ClassName; if RegisterClassEx(wc) = 0 then Exit; MainWnd := CreateWindowEx(WS_EX_CLIENTEDGE, ClassName, AppName, WS_OVERLAPPEDWINDOW, Integer(CW_USEDEFAULT), Integer(CW_USEDEFAULT), 300, 200, 0, 0, HInstance, nil); ShowWindow(MainWnd, SW_SHOWNORMAL); UpdateWindow(MainWnd); while GetMessage(Mes, 0,0,0) do begin TranslateMessage(Mes); DispatchMessage(Mes); end; end.
АНАЛИЗ
Первый шаг алгортима - получить хэндл контекста устройства - мы выполянем посредством вызова функции BeginPaint(hWnd, PaintStruct). Аргумент hWnd очевиден - мы получаем контекст данного окна. Что же касается PaintStruct ...
Понятно, что окно далеко не всегда должно перерисовываться полностью. К примеру, только часть окна перекрывается другим окном. Естественно, что и перерисоваться должна только часть окна. В этом случае полная перерисовка всего окна будет только лишней тратой времени и ресурсов компьютера. Windows «знает» о том, какая часть окна перекрыта. При необходимости перерисовки (при вызове BeginPaint) система заполняет структуру типа TPaintStruct, адрес которой передается системе как второй аргумент BeginPaint. Среди всех полей структуры типа TPaintStruct основным (на мой взгляд) является поле, содержащее координаты той области (clipping region), которая должна быть перерисована. В нашем примере информация, хранящаяся в этой структуре, не используется, но я прошу читателя обратить внимание на эту структуру и в дальнейшем использовать ее. Получив от функции BeginPaint хэндл контекста устройства (hwndDC), будем считать первый шаг выполненным.
Второй шаг - получение хэндла bitmap'a, который будет отображаться в окне - мы делаем, вызывая функцию Loadlmage. Я не случайно воспользовался именно этой функцией. Во-первых, возможности этой функции достаточно широки. Она позволяет загружать графические образы как из ресурсов, записанных в исполняемом файле, так и из файлов, содержащих только изображения. Графическим образом может быть bitmap, курсор и иконка. Кроме этого, функция позволяет управлять параметрами отображения и загрузки образа. Во-вторых, подавляющее большинство функций работают с ресурсами, сохраненными в исполняемом файле, и у программистов, начинающих осваивать Win32, попытки загрузить что-либо из файла сопровождаются некоторыми трудностями. Эта функция описана следующим образом:
function LoadImage(
hInst: HINST;
ImageName: PWideChar;
ImageType: UINT;
X, Y: Integer;
Flags: UINT
): THandle; stdcall;
Первый, второй и последний аргументы этой функции работают в связке. Первый apryмeнт(hlnst) - хэндл программы. Второй аргумент -ImageName - определяет загружаемый объект. Последний аргумент - Flags - содержит флаги, определяющие режим загрузки объекта. Среди этих флагов есть флаг LR_LOADFROMFILE. Его название определяет его назначение - если этот флаг установлен, загрузка происходит из внешнего файла. От значения первого и последнего аргументов зависит, как будет интерпретирован второй аргумент.
Третий аргумент - тип образа, он может принимать значения IMAGE_BITMAP, IMAGE_CURSOR, IMAGE_ICON и IMAGE__ENHMETAFILE. Четвертый и пятый аргументы указывают ширину и высоту иконки или курсора и в нашем примере не используются.
Функция Loadlmage возвращает нам хэндл загруженного bitmap'a (hBitmap) (или nil, если где-то что-то не так), после чего мы можем считать второй шаг нашего алгоритма пройденным.
Третий шаг - получение совместимого контекста в памяти - выполняется посредством вызова функции CreateCompatibleDC. Единственный аргумент этой функции - хэндл контекста (hDC), для которого создается совместимый контекст.
Четвертый шаг мы реализуем вызовом функции SelectObject. Первым аргументом указываем хэндл контекста, в котором замещается текущий элемент (в данном случае это хэндл только что созданного контекста в памяти hCompatibleDC), а вторым - хэндл элемента, которым замещается текущий элемент (хэндл загруженного bitmap'a hBitmap). Немаловажно, что эта функция возвращает хэндл ЗАМЕЩЕННОГО элемента (hOldBitmap), т. е, впоследствии с этим элементом могут также производиться манипуляции.
А вот на пятом шаге происходит то, ради чего мы заварили всю эту кашу с загрузкой bitmap'ов, совместимыми контекстами и прочим. Для копирования bitmap'a (с масштабированием) с одного контекста на другой, мы используем функцию StretchBlt, одну из «могучих bit», по меткому выражению Чарльза Петцольда. К их числу, помимо StretchBlt, относятся BitBlt и PatBlt.
Наверное, StretchBlt является самой «могучей» из них. И наверное, ее мощь и обусловила наличие у этой функции «всего-навсего» одиннадцати аргументов.
Источник: П.В. Румянцев. Азбука программирования в WIN32 API.