Программирование графического режима

23



МОСКОВСКИЙ ОРДЕНА ЛЕНИНА, ОРДЕНА ОКТЯБРЬСКОЙ РЕВОЛЮЦИИИ ОРДЕНА ТРУДОВОГО КРАСНОГО ЗНАМЕНИ

ГОСУДАРСТВЕННЫЙ ТЕХНИЧЕСКИЙ УНИВЕРСИТЕТ 

ИМ. Н.Э. БАУМАНА

Калужский филиал



Факультет ″Фундаментальных Наук″

Кафедра ″Программного Обеспечения ЭВМ, Информационных Технологий и Прикладной Математики″







РАСЧЕТНО-ПОЯСНИТЕЛЬНАЯ ЗАПИСКА 

К КУРСОВОЙ РАБОТЕ 

ПО СИСТЕМНОМУ ПРОГРАММИРОВАНИЮ



Тема:

“Программирование графического режима”





Студент



Группа 



Руководитель







Калуга 2006г.


АННОТАЦИЯ



Целью написания данной курсовой работы было написание графического редактора. Для реализации данной задачи была создана программа, которая позволяла работать с основными графическими примитивами и инструментами в стандартном GUI Win32: карандаш, линия, прямоугольник, эллипс, стирка, заливка и пипетка. Данная программа написана на языке Assembler. 


СОДЕРЖАНИЕ



1. ИССЛЕДОВАТЕЛЬСКАЯ ЧАСТЬ

	1.1. Постановка задачи

	1.2. Общие сведения

	1.3. Теоретические сведения

2. КОНСТРУКТОРСКАЯ ЧАСТЬ

	2.1. Общие сведения

	2.2. Функциональное назначение

	2.3. Описание логической структуры

	2.4. Вызов и загрузка программы

	2.5. Связь программы с другими технологиями

3. ТЕХНОЛОГИЧЕСКАЯ ЧАСТЬ

	3.1. Руководство программиста

	3.2. Руководство оператора

ЛИТЕРАТУРА

ПРИЛОЖЕНИЕ


1. ИССЛЕДОВАТЕЛЬСКАЯ ЧАСТЬ





Постановка задачи



Создать графический редактор для работы с графическими примитивами: точка, линия, прямоугольник, окружность.



Общие сведения



Ассемблерные программы могут быть очень эффективными. Из программистов, с равными навыками и способностями, работающий на языке ассемблера создаст программу более компактную и быстродействующую, чем такая же программа, написанная на языке высокого уровня. Это так практически для всех небольших или средних программ. К сожалению, по мере возрастания размеров, программы на языке ассемблера теряют часть своих преимуществ. Это происходит из-за необходимого в ассемблерной программе внимания к деталям. Язык ассемблера требует от программиста планирования каждого действия компьютера. В небольших программах это позволяет оптимизировать работу программы с аппаратными средствами. В больших же программах огромное количество деталей может помешать эффективно работать над самой программой, даже если отдельные компоненты программы окажутся очень неплохими. Безусловно, программирование на языке ассемблера отвечает потребностям не каждой программы.

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

Ясно, что эффективность и точность языка ассемблера дают определенные преимущества. Но его детализированность создает и некоторые проблемы. Когда же стоит избирать для программирования язык ассемблера? 

Конечно, следует пользоваться программами на языке ассемблера, когда нет другого способа написать программу. Например, программисты фирмы IBM писали с использованием процедур ассемблера все программы управления устройствами ввода-вывода для IBM PC. Для управления устройствами ввода-вывода и системой прерываний, потребовалась та точность языка ассемблера которую не может обеспечить ни один другой язык программирования. Аналогично, на языке ассемблера в фирме IBM писались процедуры диагностики, которые должны проверять каждую деталь аппаратуры. 

Язык ассемблера необходим также и в тех случаях, когда главными являются рабочие характеристики программы. Это может быть время исполнения или конечный размер программы. Какая программа не подходит для языка ассемблера? Конечно, на нем можно написать любую программу, однако с большой программой лучше работать в языке высокого уровня. Эти языки позволяют сосредоточиться на конкретной задаче и не приходится непосредственно иметь дело с тонкостями аппаратного оборудования и процессора. Языки высокого уровня «позволяют отступить назад и за деревьями увидеть лес».

И последняя причина для изучения программирования на языке ассемблера. Только через написание программ на этом уровне детализации можно понять как работает машина на самом низком уровне. Если вы хотите узнать о компьютере все, вы должны быть знакомы с его языком ассемблера. Единственный способ добиться этого - писать программы на этом языке.

Данная курсовая работа написана с использованием только функций Win32 API. Т.о. данная программа могла быть реализована и на языке высокого уровня. Однако, по той причине, что в программе не использовались очень сложные и объемные алгоритмы, а сама программа хорошо структурирована, сложность написания данной программы сравнима с разработкой аналогичной программы на языке C/C++. Кроме того, ассемблер позволил написать многие участки программы максимально коротко, без использования шаблонов реализации структурных элементов языков высокого уровня, чем грешат многие компиляторы высокоуровневых языков. Также, при написании данной программы было четко известно, что будет в исполняемом файле после компиляции, в то время как многие компиляторы добавляют в исполняемые программы множество не необходимой и даже ненужной информации и кода. Поэтому готовая программа имеет минимальный размер и максимальное быстродействие. Из всего этого следует, что ассемблер является весьма подходящим для написания данной курсовой работы, являясь не то, что б не хуже, а даже лучше языков более высокого уровня.



Теоретические сведения



Программирование в среде Windows

Win32 программы выполняются в защищенном режиме, который доступен начиная с 80286. Но 80286 теперь история. Как известно, каждую Win32-программу Windows запускает в отдельном виртуальном пространстве. Это означает, что каждая Win32-программа будет иметь 4-х гигабайтовое адресное пространство, но вовсе не означает, что каждая программа имеет 4 гигабайта физической памяти, а только то, что программа может обращаться по любому адресу в этих пределах. А Windows сделает все необходимое, чтобы сделать память, к которой программа обращается, "существующей". Конечно, программа должна придерживаться правил, установленных Windows, или это вызовет General Protection Fault.

Каждая программа одна в своем адресном пространстве, в то время как в Win16 дело обстоит не так. Все Win16-программы могут «видеть» друг друга, что невозможно в Win32. Эта особенность помогает снизить шанс того, что одна программа запишет что-нибудь поверх данных или кода другой программы.

Модель памяти также коренным образом отличается от 16-битных программ. Под Win32 мы больше не должны беспокоиться о моделях памяти или сегментах. Теперь только одна модель память: плоская, без 64-ти килобайтных сегментов. Теперь память - это большое последовательное 4-х гигабайтовое пространство. Это также означает, что не нужно работать с сегментными регистрами, зато можно использовать любой сегментный регистр для адресации к любой точке памяти. Это ОГРОМНОЕ подспорье для программистов. Это то, что делает программирование на ассемблере под Win32 таким же простым, как на C.



Использование Win32 API

Windows предоставляет огромное количество ресурсов Windows-программам через Windows API (Application Programming Interface). Windows API - это большая коллекция очень полезных функций, располагающихся непосредственно в операционной системе и готовых для использования программами. Эти функции находятся в нескольких динамически подгружаемых библиотек, таких как kernel32.dll, user32.dll, gdi32.dll и т.д. Kernel32.dll содержит API функции, взаимодействующие с памятью и управляющие процессами. User32.dll контролирует пользовательский интерфейс. Gdi32.dll ответственен за графические операции. Кроме этих трех "основных", существуют также другие динамические библиотеки, которые можно использовать при условии, что обладаете достаточным количеством информации о нужных API функциях. Windows-программы динамически подсоединяются к этим библиотекам, то есть код API функций не включается в исполняемый файл. Информация находится в библиотеках импорта. Нужно слинковать программы с правильными библиотеками импорта, иначе они не смогут найти эти функции. Когда Windows-программа загружается в память, Windows читает информацию, сохраненную в программе. Эта информация включает имена функций, которые программа использует и DLL-ок, в которых эти функции располагаются. Когда Windows находит подобную информацию в программе, она вызывает библиотеки и исправляет в программе вызовы этих функций, так что контроль всегда будет передаваться по правильному адресу.

Существует две категории API функций: одна для ANSI и другая для Unicode. На конце имен API функций для ANSI стоит "A", например, MessageBoxA. В конце имен функций для Unicode находится "W”. Мы обычно имеем дело с ANSI строками (массивы символов, оканчивающиеся NULL-ом). Размер ANSI-символа - 1 байт. В то время как ANSI достаточна для европейских языков, она не поддерживает некоторые восточные языки, в которых есть несколько тысяч уникальных символов. Вот в этих случаях в дело вступает Unicode. Размер символа UNICODE - 2 байта, и поэтому может поддерживать 65536 уникальных символов. Но по большей части, используются include-файлы, которые могут определить и выбрать подходящую для платформы функцию. Тогда достаточно обращаться к именам API функций без постфикса.



Основы рисования

С точки зрения программиста Windows является системой, не зависящей от устройств (device independent). Эту независимость со стороны Windows обеспечивает библиотека GDI32.dll, а со стороны устройства - драйвер этого устройства. С точки зрения программы связующим звеном между программой и устройством является контекст устройства (Device Context - DC). Если программе нужно осуществить обмен с внешним устройством, программа должна оповестить GDI о необходимости подготовить устройство для операции ввода-вывода. После того, как устройство подготовлено, программа получает хэндл контекста устройства, т. е. хэндл структуры, содержащей набор характеристик этого устройства. В этот набор входят:

		bitmap (битовая карта, изображение), отображаемый в окне

		перо для прорисовки линий

		кисть

		палитра

		шрифт

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

Когда программа требует контекст устройства, она получает его уже заполненным значениями по умолчанию. Объект в составе контекста называется текущим объектом. Само слово – текущий – говорит о том, что контекст устройства можно изменить. Программа может создать новый объект, скажем, bitmap или шрифт, и сделать его текущим. Замещенный объект автоматически из памяти не удаляется и его необходимо позже удалить отдельно. Само собой разумеется, что программа может получить характеристики текущего устройства. А вот изменить эти характеристики, увы, можно только через замену объекта (впрочем, это и так понятно).

В Windows поддерживаются следующие типы контекстов устройств:

		контекст дисплея (обеспечивает работу с дисплеем)

		контекст принтера (обеспечивает работу с принтером)

		контекст в памяти (моделирует в памяти устройство вывода)

		информационный контекст (служит для получения данных от устройства)

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

Приложения, которые не очень интенсивно работают с экраном, используют общий контекст. Контекст класса является устаревшим и поддерживается только для обеспечения совместимости с предыдущими версиями Windows. Microsoft не рекомендует использовать его при разработке новых приложении и рекомендует использовать только приватный контекст.

Контексты устройств хранятся в кэше, управляемом системой. Хэндл общего контекста программа получает с помощью функций GetDC, GetDCEx или BeginPaint. После того, как программа отработает с дисплеем, она должна освободить контекст, вызвав функцию ReleaseDC или EndPaint (в случаи, если контекст получался с помощью BeginPaint). После того, как контекст дисплея освобожден, все изменения, внесенные в него программой, теряются и при повторном получении контекста все действия по изменению контекста необходимо повторять заново.

Приватный контекст отличается от общего тем, что сохраняет изменения даже после того, как прикладная программа освободила его. Приватный контекст не хранится в кэше, поэтому прикладная программа может не освобождать его. Естественно, что в этом случае за счет использования большого объема памяти достигается более высокая скорость работы с дисплеем.

Для работы с приватным контекстом необходимо при регистрации класса окна указать стиль CS_OWNDC. После этого программа может получать хэндл контекста устройства точно так же, как и в случае общего контекста. Система удаляет приватный контекст в том случае, когда удаляется окно.

При работе с контекстами необходимо запомнить, что хэндлы контекста устройства с помощью функции BeginPaint необходимо получать только в случае обработки сообщения WM_PAINT. Во всех остальных случаях необходимо использовать функции GetDC или GetDCEx.

Контекст в памяти используется для хранения изображений, которые затем будут скопированы на устройство вывода. Сам по себе контекст в памяти не создается. Он обязательно создан как совместимый с тем устройством или окном, на которое предполагается копировать информацию (вот он - совместимый контекст - переходник между программой и драйвером устройства!). Алгоритм работы с контекстом в памяти состоит из нескольких шагов:

		Получения хэндла контекста устройства (hDC - handle of Device Context) для окна, в которое будет осуществляться вывод изображения

		Получения хэндла bitmap'а, который будет отображаться в окне

		Получения совместимого с hDC контекста в памяти (для хранения изображения) с помощью функции CreateCompatibleDC

		Выбора изображения (hBitmap) как текущего для контекста в памяти (hCompalibleDC)

		Копирования изображения контекста в памяти (hCompatibleDC) на контекст устройства (hDC)

		Удаления совместимого контекста (hCompatibleDC)

		Принятия мер для того, чтобы замещенный bitmap из контекста в памяти не остался в памяти

		Освобождения контекста устройства (hDC)

Как и когда удалять замещенный bitmap, зависит от программиста и поставленной перед ним задачи.

Именно этот способ и используется в большинстве программ для копирования изображения.


КОНСТРУКТОРСКАЯ ЧАСТЬ



		Общие сведения



Программа называется EasyPaint. Программа написана на языке программирования ассемблер в среде RadAsm и скомпилирована компилятором MASM32. Кроме того, при разработке использовались следующие программы:

Bred

ICAConverter

Irfan View

Microangelo



		Функциональное назначение



Программа предназначена для предоставления простой работы с изображениями в формате BMP/DIB, для чего были реализованы необходимые инструменты, а также загрузка и сохранение изображений.



		Описание логической структуры



Алгоритм работы программы

После запуска программы создается основное окно программы, загружаются ресурсы и создаются элементы управления, а также в памяти создается совместимый контекст, на котором будет производится все рисование. После этого запускается цикл обработки сообщений, в котором происходит обработка поступающих сообщений:

Обработка клавиатуры

Обработка «горячих клавиш» главного меню

Обработка сообщений к диалоговому окну

А также всех остальных сообщений как оконной функцией, так и стандартным обработчиком

При обработке перерисовки содержимое совместимого контекста копируется на основной контекст - в окно. Все рисование осуществляется при обработке событий от мыши, а также при изменении размеров окна.

При завершении работы программы совместимый контекст удаляется из памяти.



Структура программы

После запуска вначале происходит инициализация общих элементов управления (Common Controls), затем вызывается процедура WinMain (procWinMain.inc), являющаяся основной частью программы, своеобразным каркасом, на который навешаны остальные функции.

В этой процедуре вначале регистрируется класс окна и создается экземпляр, причем в качестве иконки и курсора выступают загруженные ресурсы. После этого окно центрируется на экране и в него добавляются элементы управления Панель управления и Строка состояния, описанные в файлах ToolBarRealization.inc и StatusBarRealization.inc соответственно. Элементы управления создаются и настраиваются, после этого они полностью готовы к функционированию как составные части программы.

Затем в файле InitApp.inc производятся действия по инициализации программы:

производится вызов процедуры CreateBackBuffer (DoubleBuff.inc), в которой создается совместимый контекст – для двойной буферизации вывода графики (задний буфер)

вызов процедур SetPenParam и SetBrushColor (Draw.inc) настраивает свойства объектов карандаш и кисть совместного контекста: устанавливаются цвет и толщина

В конце инициализации программы загружается список акселераторов из ресурсов, делается видимым главное окно и запускается цикл обработки сообщений. После завершения данного цикла вызов процедуры DeleteBackBuffer освобождает ресурсы, выделенные на задний буфер.



Работа с задним буфером (DoubleBuff.inc)

Процедуры этого модуля предназначены для создания плавного вывода без мерцания и любых других визуальных артефактов.

Задний буфер создается в процедуре CreateBackBuffer, которая не только создает буфер, но и кроме того позволяет менять его размер и даже копировать в новый буфер старое содержимое.

Для освобождения ресурсов буфера предназначена процедура DeleteBackBuffer.

Для очистки буфера (он заполняется белым цветом) предназначена процедура Clear.

Процедура Flip позволяет скопировать содержимое буфера на контекст окна.

Т.к. при вызове процедуры Flip отображается только заранее видимая часть изображения, то необходимо эту видимую часть вычислять. Это и делает процедура Resize.

Кроме того, в файле содержатся процедуры LoadFromFile и SaveToFile, позволяющие загружать и сохранять изображения соответственно.



Параметры заднего буфера (Draw.inc)

В данном файле содержатся процедуры SetPenParam, SetBrushColor и PutPixel. Две первые предоставляют удобное средство для изменения свойств карандаша и кисти заднего буфера, а процедура PutPixel предназначена для вывода точки с определенной толщиной (в зависимости от толщины она выводит или один пиксель, или окружность в заданной точке).



Обработка главного меню (Menu.inc)

Первой процедурой данного файла является SelectColor – процедура, реализующая работу со стандартным диалогом выбора цвета.

Следующая процедура – MenuChecked – выполняет обработку выбора инструмента. Она меняет позицию «галочки» в главном меню, а также вдавленную кнопку на панели инструментов. Также процедура меняет значение переменной OutMode, отвечающей за текущий инструмент.

Третьей и последней процедурой в файле является процедура MenuProcess, являющаяся чем-то вроде switch’а в программах на C/C++. В ней в зависимости от выбранного пункта меню выполняются определенные действия (в основном вызовы процедур).



Обработка мыши (Mouse.inc)

Данные модуль содержит процедуры по обработки событий от мыши, которые все связаны с рисованием. Процедуры имеют названия, схожие с названиями сообщений: MouseMove для WM_MOUSEMOVE, MouseLUp для WM_LBUTTONUP, MouseLDown для WM_LBUTTONDOWN и т.д.



Обработка диалогового окна (ParamDlg.inc, Приложение рис. 2)

При выборе пункта меню ФайлНовый вызывается диалоговое окно. Оно описано в ресурсах программы (Params.rc). Работа с ним осуществляется через вызов функции DlgShow (и, косвенно, через DlgWndProc), которая осуществляет всю черновую работу. Поэтому в основной программе присутствует только вызов DlgShow.



		Вызов и загрузка программы



Для запуска программы необходимо запустить EasyPaint.exe.

Для выхода из программы выберите ФайлВыход либо нажмите Escape.

Программа может быть запущена с любого носителя данных, будь то: жесткий диск (HDD), дискета (FDD), CD-диск (CD- и DVD- ROM), различных внешних устройств (Flash, ZIP) и т.д.



		Связь программы с другими технологиями

Данная программа написана полностью на функциях Win32 API и не использует каких-либо сторонних технологий, модулей и программ.


ТЕХНОЛОГИЧЕСКАЯ ЧАСТЬ



		Руководство программиста



Назначение и условия выполнения программы

Данная курсовая работа выполнена на низкоуровневом языке Assembler. Программа предназначена для обработки изображений (BITMAP) набором инструментов, стандартного для программ данного класса (графические редакторы). Важно помнить, что программа сохраняет изображения с форматом пиксела рабочего стола, а загружает с любым корректным форматом.



Системные требования

		Операционная система семейства Win32

		Процессор и оперативная память, достаточные для выполнения первого пункта

		Мышь (не менее 1 кнопки), желательно колесико (или аналог)

		Клавиатура

		16 Кб свободного места на носителе

		Монитор, поддерживающий режим не менее 800x600x8



Тестирование программы

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

Запустить программу EasyPaint.exe. В главном окне программы у кнопок панели инструментов должны быть изображения, как на рис. 1 из приложения. А также, курсор должен принимать над клиентской областью окна форму перекрестья. Если что-то не так, это значит, что были изменены ресурсы программы и корректная работа программы не гарантируется.

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

Сохранить полученное изображение, после чего открыть его из системы каким-либо сторонним графическим редактором или просмотрщиком. Соответственно, изображение в программе и сохраненное должны совпадать.

Создать новое изображение, обратив внимание на соответствие указанных параметров и результата. Сделать это с сохранением предыдущего изображения и без него.

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



Входные и выходные данные

Данная программа – это программа управляемая данными, т.к. вся работа основана на обработке событий (действий пользователя и системы). Это, а также загружаемые изображения – входные данные

Выходными данными является изображение в клиентской области окна, текущие параметры карандаша и кисти, отображаемые в строке состояния, а также изображения, сохраняемые в файл.



		Руководство оператора



Назначение программы

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

		Карандаш

		Линия

		Прямоугольник

		Эллипс

		Стирка

		Заливка

		Пипетка



Выполнение программы

После запуска программы появляется главное окно (Приложение рис.1), в котором происходит основная работа. Все операции реализованы в меню, также часть из них доступна через «горячие клавиши», приведенные в меню. Большинство операций доступны через панель инструментов.

Свойства карандаша и кисти выводятся в строке состояния:

Цвет карандаша

Цвет кисти

Толщина карандаша в пикселях

При выборе создания нового изображения открывается новое окно (Приложение рис.2), в котором можно задать размеры нового изображения, а также указать необходимость сохранения части изображения, помещающейся в границах нового изображения.


ЛИТЕРАТУРА



Румянцев П.В. Азбука программирования в Win32 API. – 3-е издание., дополн. – М.: Горячая линия–Телеком, 2001. – 312 с.: ил.

MASM32 Library Reference

Microsoft Win32 Programmer's Reference

www.wasm.ru Уроки Iczelion’а по Win32 API в переводе Aquila


ПРИЛОЖЕНИЕ





Рисунок 1. Основное окно программы





Рисунок 2. Окно параметров нового изображения