pzx

advertisement
МИНИСТЕРСТВО ОБРАЗОВАНИЯ И НАУКИ УКРАИНЫ
Донецкий национальный технический университет
ПОЯСНИТЕЛЬНАЯ ЗАПИСКА
к курсовому проекту по курсу
«Методы и способы компьютерных информационных технологий»
на тему «Разработка программных способов для просмотра графических
файлов, создание графических эффектов»
Руководители ______________________ Н.Е.Губенко
(подпись)
(дата)
______________________ О.В.Киселева
(подпись)
(дата)
Разработала:
ст. гр. КЭМ-08 ______________________ Денисенко М.И.
(подпись)
Донецк – 2012
(дата)
РЕФЕРАТ
Пояснительная записка к курсовой работе: 53 стр., 4 приложения, 34
рисунка.
Цель данной курсовой работы – разработка приложения для работы с
графическим форматами BMP и GIF, реализация заданного множества
графических эффектов.
Главной задачей курсовой работы является приобретение навыков
работы с графическими форматами данных и практического применения
знаний, полученных в процессе обучения.
Результатом курсовой работы является программа, реализующая
работу (открытие и сохранение) с графическими форматами данных и
выполняющая преобразование изображения.
ГРАФИЧЕСКИЙ ФОРМАТ ФАЙЛА, GIF, ВМР, ИЗОБРАЖЕНИЕ,
ГРАФИЧЕСКИЕ ЭФФЕКТЫ, ЗАГОЛОВОК ФАЙЛА, ЦВЕТ, ПАЛИТРА,
ФИЛЬТР,
ЦВЕТОВАЯ
МОДЕЛЬ,
НАСЫЩЕННОСТЬ, ЗАШУМЛЕНИЕ.
RGB,
HSV,
СИНИЙ
ЦВЕТ,
Введение
1 Описание графического формата файла
5
6
1.1 Описание графического формата GIF
6
1.1.1 Общие сведения
6
1.1.2 Общая структура файла GIF
7
1.1.3 Сжатие по методу LZW
9
1.2 Описание графического формата ВМР
11
1.2.1 Общие сведения
12
1.2.2 Общая структура файла ВМР
12
2 Алгоритм отображения файлов
15
2.1 Алгоритм отображения файла BMP
15
2.2 Алгоритм сохранения изображения в GIF-формате
16
3 Алгоритмы реализации эффектов
17
3.1 Цветовая модель CMY
17
3.2 Цветовая модель HSV
18
3.3 Преобразование цветовых моделей
18
3.4 Эффект шума
20
3.5 Фильтрация изображений
20
4 Структура приложения
22
5 Выбор инструментальных средств для реализации программы, 23
обоснование выбора
6 Описание основных модулей программы
24
7 Тестирование, результаты работы программы
36
Выводы
37
Список используемой литературы
38
Приложение А. Техническое задание
39
Приложение Б. Экранные формы
40
Приложение В. Листинг программы
43
Приложение Д. Руководство пользователя
69
1 ОПИСАНИЕ ГРАФИЧЕСКОГО ФОРМАТА ФАЙЛА
1.1 Описание графического формата GIF
1.1.1 Общие сведения
GIF – формат хранения графических изображений (от англ. Graphics
Interchange Format). Формат GIF способен хранить сжатые без потерь
изображения в формате до 256 цветов с палитрой, и предназначен, в
основном, для чертежей, графиков и т.д. [3].
Независящий от аппаратного обеспечения формат GIF был разработан
в 1987 году (GIF87a) фирмой CompuServe для передачи растровых
изображений по сетям. В 1989-м формат был модифицирован (GIF89a), были
добавлены
поддержка
прозрачности
для
создания
изображений
непрямоугольной формы и анимации. Файл GIF может содержать не одно, а
несколько растровых изображений, которые браузеры могут загружать друг
за другом с указанной в файле частотой [1].
GIF широко используется в Internet. Формат GIF обладает рядом
свойств, определивших его широкое применение при разработке Webстраниц:
– Прозрачность. Изображения в формате GIF могут иметь прозрачные
области, что позволяет отойти от стандартных прямоугольных форм и
создавать элементы страницы произвольной формы.
–
Чередование строк. Графический файл может быть записан таким
образом, что при отображении сначала будет загружаться каждая
четвертая строка, растянутая на три соседних. Естественно, качество
отображения будет достаточно низкое. Затем, по мере загрузки
последующих строк, изображение примет окончательный вид. Такая
схема позволяет пользователю еще до окончательной загрузки
графического файла понять, что именно представлено на изображении.
– Анимация. Форматом GIF предусмотрена возможность создания
анимированных изображений. Фактически это несколько изображений,
записанных в один файл и чередующихся с заданным интервалом
времени [4].
Основное ограничение формата GIF состоит в том, что цветное
изображение может быть записано только в режиме 256 цветов.
Область применения GIF-файлов ограничивается и применяемым
алгоритмом сжатия изображений (LZW-компрессия), чья эффективность
зависит от разнообразия цветов в изображении, наличия одноцветных
областей. Наибольшую экономию объема файла дают изображения с
небольшим количеством цветов и большими одноцветными областями.
Поэтому формат GIF широко используется для отображения графических
меню и кнопок, а также для создания технических иллюстраций (графики,
схемы, диаграммы и т.п.). В то же время использование формата GIF для
отображения полноцветных фотографий практически не находит применения
по причине малого количества передаваемых цветов и низкой эффективности
алгоритма сжатия для таких изображений [1].
1.1.2 Общая структура файла GIF
Общая структура файла представлена на рисунке (см рис. 1.1).
Заголовок файла
Палитра
Изображение1
…
ИзображениеN
Признак конца = 3В
Рисунок 1.1 – Общая структура файлов GIF
Теперь рассмотрим каждый элемент структуры отдельно.
Начнем с заголовка файла. Заголовок файла GIF представлен на
рисунке (см рис.1.2.). В первых трех байтах стоит метка заголовка (‘GIF’). За
ней следуют 3 байта с уточнением версии GIF. Далее расположен блок
размером 7 байтов с описанием логического экрана.
Название поля
Размер (в байтах)
Примечание
Идентификатор GIF
3
47 49 46 («GIF»)
Номер версии
3
87а или 89а
Ширина
2
Высота
2
Флаг разрешения
1
Цвет фона
1
Специальная информация
1
Количество цветов и прочее
Рисунок 1.2 – Заголовок файла GIF
Специальная информация – это информация о том, есть ли глобальная
палитра, сколько битов используется для RGB-представления одного цвета в
палитре, сколько битов приходится на одну точку изображения (пиксель),
определения последовательности высвечивания пикселей.
После заголовка идет палитра, которая используется для всех
изображений в файле GIF, если у изображения нет собственной палитры.
Палитры изображений и глобальная палитра состоят из троек байтов RGB.
Обе палитры – локальные и глобальная – не обязательны.
После заголовка файла и необязательной глобальной палитры следует
изображение – одно или несколько (если есть анимация). Для каждого
изображения сначала записывается собственный заголовок (см. рис. 1.3).
Название поля
Размер
Примечание
(в байтах)
Признак изображения
1
2Сh или “,” (ASCII) для
изобра-жения
Координаты левого края 2
изображения
Координаты
верхнего 2
края изображения
Ширина
Высота
Специальная
информация
2
2
1
Наличие локальных палитр,
прочие локальные параметры
изображения
Рисунок 1.3 – Заголовок изображения
Каждый дескриптор
изображения
начинается
с
символа-
разделителя изображений. Роль разделителя изображений состоит просто
в синхронизации при входе в дескриптор изображения. Это желательно, если
GIF-файл
состоит
более,
чем
из
одного изображения. Этот символ
определен как шестнадцатеричное 0x2C или ',' (запятая). Как только этот
символ встречается между изображениями, непосредственно за ним следует
дескриптор изображения.
Специальная информация, в этом случае, - это информация о наличие
локальной палитры, в последовательном порядке или порядке переплетения
отформатировано изображение, число бит на пиксель в данном изображении
Далее идет необязательная локальная палитра изображения. Потом
записываются данные. Сжатые данные хранятся в виде потока блоков
размером меньше 255 байтов. Каждый блок состоит из байта длины блока и,
собственно, данных. Когда данные изображения заканчиваются, идет блок
нулевой длины.
Поток графических данных может создаваться локально (например,
при чтении данных из некого файла), либо передаваться от удаленного
источника (например, по коммуникационным линиям). Данный формат
строится на предположении, что на транспортном уровне для передачи
данных используется такой протокол, который самостоятельно производит
коррекцию ошибок, возникающих при работе с коммуникационными
линиями. Поэтому в формате не предусмотрено никаких проверок с целью
обнаружения и коррекции ошибок [4].
1.1.3 Сжатие по методу LZW
Данные файлов GIF хранятся в упакованном виде сжатым алгоритмом
LZW (Lempel-Ziv Welch). LZW - это способ сжатия данных, который
извлекает преимущества при повторяющихся цепочках данных. Поскольку
растровые данные обычно содержат довольно много таких повторений, LZW
является хорошим методом для их сжатия и раскрытия.
Основные понятия:
– "Символ": фундаментальный элемент данных - индекс, который
указывает цвет данного пиксела (K).
– "Цепочка": несколько последовательных символов. Длина цепочки
может изменяться от 1 до очень большого числа символов ([...]K).
– "Префикс": префикс непосредственно предшествует символу, может
иметь нулевую длину ([...]).
– "Корень": односимвольная цепочка ( [...]K, где [...] - пуста).
– "Код": число, определяемое известным количеством бит, которое
кодирует цепочку.
– "Поток
кодов": выходной поток кодов, таких как "растровые
данные".
–
"Элемент": код и его цепочка.
– "Таблица цепочек": список элементов обычно, но не обязательно,
уникальных.
При сжатии и раскрытии LZW манипулирует тремя объектами:
потоком символов, потоком кодов и таблицей цепочек. При сжатии поток
символов является входным и поток кодов - выходным. При раскрытии
входным является поток кодов, а поток символов - выходным. Таблица
цепочек порождается и при сжатии и при раскрытии, однако она никогда не
передается от сжатия к раскрытию и наоборот.
Первым,
что
необходимо
сделать
при
LZW-сжатии,
является
инициализация цепочки символов. Для этого необходимо выбрать код
размера (количество бит), и знать, сколько возможных значений могут
принимать символы. Положим код размера равным 12 битам, что означает
возможность запоминания 0FFF, или 4096, элементов в таблице цепочек.
Чтобы инициализировать таблицу, установим соответствие кода #0
символу #0, кода #1 to символу #1, и т.д., до кода #31 и символа #31. Т. е.
каждый код от 0 до 31 является корневым (больше в таблице не будет других
кодов, обладающих этим свойством).
Начнем сжатие данных: определим "текущий префикс" и "текущую
цепочку". Префикс изначально ничего не содержит. "Текущая цепочка"
("[.c.]K) образуется текущим префиксом и следующим символом в потоке
символов.
Рассмотрим первый символ в потоке символов, назовем его P. Сделаем
[.c.]P текущей цепочкой, выполним поиск в таблице цепочек, чтобы
определить входит ли в нее [.c.]P. Если входит, то ничего не делаем. Затем
делаем текущим префикс [.c.]P.
Берем следующий символ из потока символов - Q, добавим текущий
префикс, чтобы сформировать текущую цепочку[.c.]Q. Выполняем поиск в
таблице цепочек, чтобы определить входит ли в нее [.c.]Q. В данном случае
не входит. Добавим [.c.]Q в таблицу цепочек под кодом #32,и выведем код
для
[.c.] в поток кодов. Теперь начнем опять с текущего префикса.
Продолжаем добавление символов к [.c.], чтобы сформировать [.c.]K, до тех
пор, пока не сможем найти [.c.]K в таблице цепочек. Затем выводим код для
[.c.] и добавляем [.c.]K в таблицу цепочек.
Алгоритм компрессии LZW выглядит следующим образом (см. рис.
1.4):
[1]
[2]
[3]
[4]
Инициализация таблицы цепочек;
[.c.] <- пусто;
K <- следующий символ в потоке символов;
Входит ли [.c.]K в таблицу цепочек?
(да: [.c.] <- [.c.]K;
go to [3];
)
(нет: добавить [.c.]K в таблицу цепочек;
вывести код для [.c.] в поток кодов;
[.c.] <- K;
go to [3];
)
Рисунок 1.4 - Алгоритм компрессии LZW
1.2 Описание графического формата ВМР
1.2.1 Общие сведения
Формат файлов BMP – родной формат Windows. Он поддерживается
всеми графическими редакторами, работающими под управлением этой
операционной системы. Применяется для хранения растровых изображений,
предназначенных для использования в Windows и, по сути, больше ни на что
не пригоден. Способен хранить как индексированный (до 256 цветов), так и
RGB-цвет (16.700.000 оттенков). Возможно применение сжатия по принципу
RLE, но делать это не рекомендуется, так как очень многие программы таких
файлов (они могут иметь расширение .rle) «не понимают». Существует
разновидность формата ВМР для операционной системы OS/2 [2].
1.2.2 Общая структура файла ВМР
BMP-файл состоит из четырёх частей (см. рис. 1.5).
Название поля
Размер (в байтах)
Заголовок файла
14
Заголовок
40
изображения
Палитра
Зависит от количества цветов.
Само изображение
Количество
байтов
определяется
размерами
растра и количеством битов
на пиксель.
Рисунок 1.5 – Общая структура ВМР-файла
Теперь рассмотрим каждую часть подробно.
Заголовок файла (BITMAPFILEHEADER). В нем помещается общее
описание файла. Его структура представлена на рисунке (см. рис. 1.6).
Название поля
Размер
Примечание
(в байтах)
bfType
2
Код
формата,
соответствует
«ВМ».
bfSize
4
Размер файла в байтах.
bfReserved1
2
Зарезервировано. Равно 0.
bfReserved2
2
Зарезервировано. Равно 0.
bfOffBits
4
Адрес
битового
массива
в
данном файле.
Рисунок 1.6 – Структура заголовка ВМР-файла
Далее в файле идет еще один заголовок – заголовок изображения
(BITMAPINFOHEADER, может отсутствовать), в котором хранится описание
размеров растра и цветового формата пикселов (см. рис. 1.7).
Название поля
Размер
Примечание
(в байтах)
biSize
4
Размер заголовка, равен 40 байт.
biWidth
4
Ширина изображения.
biHeight
4
Высота изображения.
biplanes
2
Содержит 1.
biBitCount
2
Бит на пиксел. Может быть 1, 4, 8, 16, 24 или
32.
biCompression
4
Компрессия:
0 – без компрессии;
1 – компрессия RLE8 (8 бит на пиксел);
2 – компрессия RLE4 (4 бита на пиксел);
3 – без компрессии для 16 и 32 бита на пиксел.
biSizeImage
4
biXPelsPerMeter 4
Указывает размер изображения в байтах.
Горизонтальное разрешение в пикселях на
метр.
biYPelsPerMeter 4
Вертикальное разрешение в пикселях на метр.
biClrUsed
Количество используемых цветовых индексов
4
в палитре.
biCrlImportant
4
Указывает количество индексов, необходимых
для отображения изображения.
Рисунок 1.7 – Структура заголовка изображения
Далее в файле помещается палитра (может и отсутствовать) в виде
записей RGBQUAD из четырех полей, длина каждого из которых разна 1
байту (см. рис. 1.8).
Название поля
Примечание
rgbBlue
Компонент Синий, от 0 до 255
rgbGreen
Компонент Зеленый, от 0 до
255
Компонент Красный, от 0 до
rgbRed
255
rgbReserved
Не используется и равно 0.
Рисунок 1.8 – Структура записи RGBQUAD
Количество записей RGBQUAD равняется количеству используемых
цветов.
После палитры (если она есть) в файле ВМР записывается растр в виде
битового массива [5].
Изображение
записано
построчно.
Каждая
строка
изображения
дополняется нулями до длины, кратной четырем. Надо еще помнить, что
изображение идет как бы перевернутое «вверх ногами», т.е. сначала записана
нижняя строка, потом предпоследняя и так далее до самого верха.
2 ОПИСАНИЕ АЛГОРИТМА ОТОБРАЖЕНИЯ ФАЙЛА
2.1 Открытие файла BMP
Открытие BMP файла осуществляется методом LoadFromFile, который
загружает BMP-файл с заданным именем, считывая его с диска в
оперативную память. В процессе загрузки сначала читается заголовок
растрового файла, затем вычисляются размер заголовка информационного
блока в совокупности с цветовой таблицей, который указывается в поле
infoHeadSize.
Помимо этого в поле imageSize указывается вычисленный размер
изображения в байтах. Полученные величины используются для указания
размеров динамически выделяемой памяти под заголовок информационного
блока plnfoHead и под массив пикселов aBitmapBits. После выделения
указанной памяти соответсвующие компоненты растра читаются из файла.
На рисунке 2.1 показан алгоритм реализации чтения BMP файла.
ОткрытьФайл (Файл, ИмяФайла.bmp);
Если(ОшибкаПриОткрытииФайла) то
начало
Сообщение(Ошибка при открытии файла);
Выход;
2.3 Отображение
изображения
конец;
Прочитать (Файл, Заголовок);
Если (тип_рисунка≠поддерживаемый_системой) то
начало
Алгоритм
отображения изображения представлен на рисунке
Сообщение(Данный формат изображения не поддерживается);
Выход;
конец
Если (Палитра) то
Рисунок 2.4 – Алгоритм отображение файла
начало
Прочитать(Файл, Палитра);
ПрочитатьДанныеИзображенияСПалитрой;
конец
иначе
ПрочитатьДанныеИзображения;
Рисунок 2.3 – Алгоритм чтения BMP файла
ЗакрытьФайл(Файл);
Рисунок 2.1 – Алгоритм чтения BMP файла
2.2 Алгоритм сохранения изображения в GIF-формате
Алгоритм сохранения изображения в GIF-файл представлен на рисунке
(см. рис. 2.2).
нач
создать файл имя_файла.gif;
открыть файл имя_файла.gif;
запись Идентификатор GIF;
запись Дескриптор экрана;
запись Глобальная таблица цветов;
запись Дескриптор изображения;
запись Локальная таблица цветов
применения сжатия LZW к данным
запись Растровых данных
запись Терминатор GIF
закрыть файл имя_файла.gif;
кон
Рисунок 2.2 – Алгоритм сохранения изображения в формат GIF
3 ОПИСАНИЕ АЛГОРИТМОВ РЕАЛИЗАЦИИ ЭФФЕКТОВ
3.1 Преобразование цветов между базисами RGB и CMY
CMY (Cyan, Magenta, Yellow - голубой, пурпурный, желтый) аппаратно-ориентированная модель, используемая в полиграфии для
субтрактивного формирования оттенков, основанного на вычитании слоем
краски части падающего светового потока. Цвета модели CMY являются
дополнительными к цветам модели RGB, т.е. дополняющими их до белого.
Рисунок 3.1 – Цветовой куб модели CMY
Таким образом, система координат CMY - тот же куб, что и для RGB, но с
началом отсчета в точке с RGB координатами (1,1,1), соответствующей
белому цвету. Цветовой куб модели CMY показан на рисунке 2.4.
Конвертация RGB в CMY: C := 255 - R; M := 255 - G; Y := 255 - B;
Конвертация CMY в RGB: R := 255 - C; G := 255 - M; B := 255 - Y;
for(int row = 0;row < y;row++)
for(int col = 0;col < x;col++){
R = aBitmapBits[row*bytesPerLine + col*3 + 2];
G = aBitmapBits[row*bytesPerLine + (col*3)+ 1];
B = aBitmapBits[row*bytesPerLine + (col*3)];
C = 255-R;
M = 255-G;
Y = 255-B;
aBitmapCMY[row*bytesPerLine + (col*3)]
= Y;
aBitmapCMY[row*bytesPerLine + (col*3)+ 1] = M;
aBitmapCMY[row*bytesPerLine + col*3 + 2] = C
Рисунок 3.2
-
Конвертация RGB в CMY
for(int row = 0;row < y;row++)
for(int col = 0;col < x;col++){
C = aBitmapCMY[row*bytesPerLine + col*3 + 2];
M = aBitmapCMY[row*bytesPerLine + (col*3)+ 1];
Y = aBitmapCMY[row*bytesPerLine + (col*3)];
R = 255-C;
G = 255-M;
B = 255-Y;
aBitmapBits[row*bytesPerLine + (col*3)]
= B;
aBitmapBits[row*bytesPerLine + (col*3)+ 1] = G;
aBitmapBits[row*bytesPerLine + col*3 + 2] = R;
Рисунок 3.3
-
Конвертация CMY в RGB
3.2 Преобразование цветов между базисами RGB и HSV
HSV (Hue, Saturation, Value - цветовой тон, насыщенность, количество
света)
-
модель,
возможность
ориентированная
явного
Подпространство,
задания
определяемое
на
человека
требуемого
данной
оттенка
моделью
и
обеспечивающая
цвета
-
(рис.
3).
перевернутый
шестигранный конус.
По вертикальной оси конуса задается V - светлота, меняющаяся от 0 до
1. Значению V = 0 соответствует вершина конуса, значению V = 1 основание конуса; цвета при этом наиболее интенсивны.
Цветовой тон H задается углом, отсчитываемым вокруг вертикальной
оси. В частности, 0° - красный, 60° - желтый, 120° - зеленый, 180° - голубой,
240° - синий, 300° - пурпурный, т.е. дополнительные цвета расположены друг
против друга (отличаются на 180°).
Насыщенность S определяет насколько близок цвет к "чистому"
пигменту и меняется от 0 на вертикальной оси V до 1 на боковых гранях
шестигранного конуса.
Рисунок 3.4 – Разложение HSV
Точка V = 0, в которой находится вершина конуса, соответствует
черному цвету. Значение S при этом может быть любым в диапазоне 0-1.
Точка с координатами V = 1, S = 0 - центр основания конуса соответствует
белому цвету. Промежуточные значения координаты V при S=0, т.е. на оси
конуса, соответствуют серым цветам. Если S = 0, то значение оттенка H
считается неопределенным.
for(int row
for(int
{
R
G
B
= 0;row < y;row++)
col = 0;col < x;col++)
= aBitmapBits[row*bytesPerLine + col*3 + 2];
= aBitmapBits[row*bytesPerLine + (col*3)+ 1];
= aBitmapBits[row*bytesPerLine + (col*3)];
rgbmin = min(R,min(G,B));
rgbmax = max(R,max(G,B));
if (rgbmax > 0.0)
st = (rgbmax - rgbmin) / rgbmax;
if (st > 0.0) {
rc = (rgbmax - R) / (rgbmax - rgbmin);
gc = (rgbmax - G) / (rgbmax - rgbmin);
bc = (rgbmax - B) / (rgbmax - rgbmin);
if (R == rgbmax) ht = bc - gc;
else if (G == rgbmax) ht = 2 + rc - bc;
else if (B == rgbmax) ht = 4 + gc - rc;
ht = ht * 60.0;
if (ht < 0.0) ht += 360.0;
}
H = ht / 360.0;
V = rgbmax;
S = st;
aBitmapHSV[row*bytesPerLine + (col*3)]
= V;
aBitmapHSV[row*bytesPerLine + (col*3)+ 1] = S;
aBitmapHSV[row*bytesPerLine + col*3 + 2] = H;
Рисунок 3.5 – Конвертация RGB в HSV
for(int row = 0;row < y;row++)
for(int col = 0;col < x;col++){
i_H = aBitmapHSV[row*bytesPerLine + col*3 + 2];
S = aBitmapHSV[row*bytesPerLine + (col*3)+ 1];
V = aBitmapHSV[row*bytesPerLine + (col*3)];
if (!S)
R = G = B = V;
else
{
while (i_H >= 360)
i_H -= 360;
H = i_H;
ii = (int)(H /= 60.0);
fr = H - ii;
c1 = (V * (255 - S)) / 255;
c2 = (V * (255 - S * fr)) / 255;
c3 = (V * (255 - S * (1.0 - fr)))
switch (ii)
{
case 0: R = V; G = c3; B = c1;
case 1: R = c2; G = V; B = c1;
case 2: R = c1; G = V; B = c3;
case 3: R = c1; G = c2; B = V;
case 4: R = c3; G = c1; B = V;
case 5: R = V; G = c1; B = c2;
} // switch
} // if (!sat)
/ 255;
break;
break;
break;
break;
break;
break;
aBitmapBits[row*bytesPerLine + (col*3)]
= B;
aBitmapBits[row*bytesPerLine + (col*3)+ 1] = G;
aBitmapBits[row*bytesPerLine + col*3 + 2] = R;
Рисунок 3.6 – Конвертация HSV в RGB
3.3 Зашумление составляющей M
Алгоритм зашумления изображения по составляющей M представлен
на рисунке 3.7.
Определение ширины изображения
Определение высоты изображения
Определить коль-во битов в строке
for(int row = 0;row < y;row++)
for(int col = 0;col < x;col++){
Прочитать составляющую M
Добавить шум
if(M < 0) M = 0;
if(M > 255) M = 255;
Запись B обратно в массив пикселов;
}
Рисунок 3.7 – Алгоритм зашумления изображения по M
Для
того
чтобы
зашумить
составляющую
M
в
изображении
необходимо если в файле есть палитра, то к каждому цвету палитры
параметра M прибавить заданный шум.
Если в файле нет палитры, то каждый пиксель нужно разложить на
CMY , прибавить к M заданный шум, вернуть пиксель в предыдущий формат
и сохранить полученное значение.
В случае если полученное значение составляющей M выходит за
границы допустимого его корректируют в зависимости от максимально и
минимально возможного для значения пикселя данного размера.
3.4 Зашумление составляющей S
Алгоритм зашумления изображения составляющей S представлен на
рисунке 3.8.
Определение ширины изображения
Определение высоты изображения
Определить коль-во битов в строке
for(int row = 0;row < y;row++)
for(int col = 0;col < x;col++){
Прочитать составляющую S из массива HLS
Добавить шум
if(S < 0) S = 0;
if(S
> 255) S = 255;
Рисунок 3.2 - Алгоритм
зашумления
составляющей S.
Запись B обратно в массив пикселов HLS;
}
Рисунок 3.8 – Зашумление составляющей S
Данные в программе хранятся в базисе RGB и для зашумления
составляющей S базиса HSV необходимо выполнить преобразование цвета
каждого пикселя из базиса RGB в базис HSV, которое выполняет процедура
RGBtoHSV. Далее требуется выполнить зашумление составляющей S,
прибавлением
к
ней
шума,
заданного
пользователем,
и
обратное
преобразование из одного базиса в другой, выполняемое функцией
HSVtoRGB.
3.5 Фильтрация изображения
Для реализации фильтрации изображения использовался фильтр,
который задается матрицей 3x3. Матрица изображена на рис. 3.4.
1
2
1
2
4
2
1
2
1
Рисунок 3.9 – Матрица фильтра 6
Начало
Для I от 1 до Высота
Для J от 1 до Ширина
R = GetR(Цвет_Пикселя[J, I])
G = GetG(Цвет_Пикселя[J, I])
B = GetB(Цвет_Пикселя[J, I])
Цвет_Пикселя[J, I] = RGB(R, G, B) * M
Конец
Рисунок 3.10 – Алгоритм фильтрации изображения.
Изображение покрывается этой матрицей, в цикле попиксельно. При
этом каждый элемент матрицы умножается с каждой составляющей
соответствующего пикселя. При чем, если составляющая выходит за рамки
0...255, то она приравнивается к ближайшей границе этого интервала.
Алгоритм фильтра представлен на рис. 3.5
3.6 Эффект наложения кругов
Эффект наложения кругов выполняется при помощи созданной
вручную матрицы нулей и единиц, где нулями обозначен круг. Разложив
изображение в базис RGB, каждая компонента умножается на элемент
матрицы.
int Matrix[11][11]={1,1,1,1,0,0,0,1,1,1,1,
1,1,1,0,0,1,0,0,1,1,1,
1,1,0,0,1,1,1,0,0,1,1,
1,0,0,1,1,1,1,1,0,0,1,
0,0,1,1,1,1,1,1,1,0,0,
0,1,1,1,1,1,1,1,1,1,0,
0,0,1,1,1,1,1,1,1,0,0,
1,0,0,1,1,1,1,1,0,0,1,
1,1,0,0,1,1,1,0,0,1,1,
1,1,1,0,0,1,0,0,1,1,1,
1,1,1,1,0,0,0,1,1,1,1};
…
for(int i = 0;i < h; i++)
{
for(int j = 0;j < w; j++)
{
R = aBitmapBits[j*bytesPerLine + i*3 + 2];
G = aBitmapBits[j*bytesPerLine + (i*3)+ 1];
B = aBitmapBits[j*bytesPerLine + (i*3)];
R *=Matrix [I][J];
G *=Matrix [I][J];
B *=Matrix [I][J];
aBitmapBits[j*bytesPerLine + (i*3)] = B;
aBitmapBits[j*bytesPerLine + (i*3)+ 1] = G;
aBitmapBits[j*bytesPerLine + i*3 + 2] = R;
J++;
if (J==11)
{
J=0;
j+=1;
}
}
I++;
J=0;
if (I==11)
{
I=0;
i+=1
}
Рисунок 3.11 – Эффект наложения кругов
4 СТРУКТУРА ПРИЛОЖЕНИЯ
Структура системы представлена в виде схемы (см. рис. 4.1).
Запуск приложения
Открытие BMP-файла
О программе
Работа с изображением
Эффект шума для H
Сохранить как GIF
Фильтрация
Просмотр GIF
О базисах
Выход
Рисунок 4.1 – Структура приложения
5 ВЫБОР ИНСТРУМЕНТАЛЬНЫХ СРЕДСТВ ДЛЯ РЕАЛИЗАЦИИ
ПРОГРАММЫ, ОБОСНОВАНИЕ ВЫБОРА
В связи с тем, что сегодня уровень сложности программного
обеспечения
очень
высок,
разработка
приложений
Windows
с
использованием только одного какого-либо языка программирования
значительно затрудняется. Программист должен затратить массу времени на
решение стандартных задач по созданию многооконного интерфейса.
Реализация технологии связывания и встраивания объектов потребует от
программиста еще более сложной работы.
Одним
из
функциональных
самых
и
популярных
удобных
инструментов
Windows-приложений
для
создания
является
язык
программирования C++ и входящая в состав Microsoft Visual Studio 2008,
Windows Forms. Она включает в себя практически весь программный
интерфейс Windows и позволяют пользоваться при программировании
средствами более высокого уровня, чем обычные вызовы функций. За счет
этого значительно упрощается разработка приложений, имеющих сложный
интерфейс пользователя, облегчается поддержка технологии OLE и
взаимодействие с базами данных.
Данная курсовая работа выполнена на объектно-ориентированном языке
С++. Это объясняется тем, что программные продукты созданные в с
помощью C++ наиболее просты в использовании и не требуют от
пользователей глубоких знаний в управлении программами.
6 ОПИСАНИЕ ОСНОВНЫХ МОДУЛЕЙ ПРОГРАММЫ
Класс Format предназначен для работы с файлом BMP. Реализация
класса показана на рисунке 6.1.
class Format {
public:
BYTE* aBitmapBits;// массив пикселов
BYTE* aBitmapBits1;
BYTE* aBitmapCMY;
BYTE* aBitmapHSV;
BYTE R;
BYTE G;
BYTE B;
Format();
~Format();
BOOL CreateDib24(int w, int h, const char* fileName);
void StoreDib24();
BOOL LoadFromFile(const char* fileName);
void SetPixel(int x, int y, COLORREF color);
int Draw(HDC hdc, int xDst, int yDst, int wDst, int hDst,
int xSrc, int ySrc, int wSrc, int hSrc, DWORD rop);
int GetWidth() { return width; }
int GetHeight() { return height; }
const char* GetError() { return error.c_str();}
void RGBtoCMY();
void RGBtoHSV();
void CMYtoRGB();
void HSVtoRGB();
void BazisС();
void BazisM();
void BazisY();
void BazisH();
void BazisS();
void BazisV();
void NoiseM();
void NoiseS();
void Filter();
void Save();
Рисунок 6.1 – Интерфейс класса Format
void Circ();
void Transformtocirc(int x,int y);
int Draw1(HDC hdc, int xDst, int yDst, int wDst, int hDst,
int xSrc, int ySrc, int wSrc, int hSrc, DWORD rop);
void TransformPix(int x,int y);
private:
int width;
int height;
int bytesPerLine;
BITMAPFILEHEADER fileHead;
// заголовок растрового файла
BITMAPINFOHEADER infoHead;
// заголовок информационного
блока
BITMAPINFOHEADER* pInfoHead;
int fileHeadSize;
int infoHeadSize;
int imageSize;
string error;
ofstream outFile;
};
Кроме традиционных для класса методов, таких как конструктор и
деструктор, класс Format содержит методы, перечисленные ниже:
1) Метод CreateDib24 создает в памяти 24-разрядный растр с
заданными шириной и высотой. В процессе создания осуществляется
инициализация
полей
заголовка
растрового
файла
и
заголовка
информационного блока. После этого выделяется память для хранения
массива пикселов. В завершение открывается дисковый файл с заданным
именем, предназначенный для сохранения в нем создаваемого растра.
2) Метод SetPixel осуществляет прямой доступ к пикселам в 24разрядном растре. Цветовая информация для синей, зеленой и красной
составляющих цвета пиксела записывается в соответствующие элементы
массива aBitmapBits. Адреса элементов вычисляются через номер строки
развертки у и номер пиксела в строке х.
3) Метод StoreDib24 сохраняет созданный растр, записывая его на диск
в формате GIF.
4)
Метод LoadFromFile загружает BMP-файл с заданным именем,
считывая его с диска в оперативную память. В процессе загрузки сначала
читается заголовок растрового файла, затем вычисляются размер заголовка
информационного блока в совокупности с цветовой таблицей, который
указывается в поле infoHeadSize. Помимо этого в поле imageSize указывается
вычисленный размер изображения в байтах. Полученные величины
используются для указания размеров динамически выделяемой памяти под
заголовок информационного блока plnfoHead и под массив пикселов
aBitmapBits.
После
выделения
указанной
памяти
соответствующие
компоненты растра читаются из файла.
5) Метод Draw выводит растр на
поверхность графического
устройства, используя функцию StretchDIBits.
6)
Метод RgbToHls переводит даныые изображения из RGB в HLS.
7)
Метод HlsToRgb переводит данные изображения из HLS в RGB.
8)
Метод NoiseM зашумляет составляющую M
9)
Метод NoiseS переводит данные изображения из RGB в HLS,
зашумляет составляющую S и обратно переводит из HLS в RGB.
10)
Метод Filter применяет фильтр «размытие» к изображению.
Таблица 6.1 Методы класса KDib.
Метод
Тип
Назначение
CreateDib24
BOOL
Создает GIF – файл
StoreDib24
VOID
Пересылка данных изображения
LoadFromFile
BOOL
Загружает BMP – файл
SetPixel
VOID
Устанавливает пиксель в контексте
Draw
INT
Вывод на экран изображения
GetWidth
INT
Возвращает ширину
GetHeight
INT
Возвращает высоту
GetError
CHAR*
Возвращает ошибку
RGBtoHSV
VOID
Переводит из RGB в HSV
HSVToRGB
VOID
Переводит из HSV в RGB
LoadFromFile
BOOL
Загружает BMP – файл
GetA
BYTE*
Возвращает массив пикселов
Draw
INT
Вывод на экран изображения
GetWidth
INT
Возвращает ширину
GetHeight
INT
Возвращает высоту
NoiseB
VOID
Зашумляет B
NoiseS
VOID
Защумляет S
Filter
VOID
Фильтрует
7 ТЕСТИРОВАНИЕ, РЕЗУЛЬТАТЫ РАБОТЫ ПРОГРАММЫ
Программа написана под операционную систему от Windows XP до
Windows VISTA. Для установки системы нет необходимости в особых
настройках.
Требования к аппаратной платформе – процессор должен быть типа
Pentium или Celeron с тактовой частотой не ниже 166 МГц (рекомендуется
Pentium III 800 МГц), оперативной памяти - 512 Мбайт (рекомендуется 1024
Мбайт), достаточное количество свободного дискового пространства всего
50 Mб.
В результате была разработана программа, имеющая интерфейс,
удобный и простой для пользователя, выполняющая открытие, сохранение
файлов графических форматов GIF и BMP, и реализующая спецэффекты.
ВЫВОДЫ
В разработанном приложении решены поставленные в техническом
задании задачи, и реализованы требуемые возможности для работы с
графическими форматами GIF и ВМР.
Приложение является законченной программой, однако, оно может
быть дополнено многими другими возможностями, в частности, работа с
другими графическими форматами данных, реализованы дополнительные
графические эффекты: разложение по другим базисам, эффекты шума для
нескольких составляющих.
К недостаткам данного проекта можно отнести ограниченность его
применения только в учебных и методических целях.
Список используемой литературы
1. Компьютерная графика/Под ред. В.Н. Порева – К.: Юниор, 2005. – 520с.,
ил.
2. http://ru.wikipedia.org/wiki/BMP
3. http://ru.wikipedia.org/wiki/GIF
4. http://www.a-print.ru/index.php?page=d2-07
5. Методические указания и задания к курсовой работе по курсу “Методы и
способы компьютерных информационных технологий“/ сост.: доц.
Губенко Н.Е., асс. Чернышова А.В. – Донецк: ДонНТУ, 2003 - 88 с.
6. http://ru.wikipedia.org/
ПРИЛОЖЕНИЕ А ТЕХНИЧЕСКОЕ ЗАДАНИЕ
ПРИЛОЖЕНИЕ Б. ЭКРАННЫЕ ФОРМЫ
Рисунок 1 – Базис CMY
Рисунок 2 – Базис HSV
Рисунок 3 – Применение фильтра
Рисунок 4 – Зашумление S
Рисунок 5 – Зашумление M
Рисунок 6 – Эффект наложения кругов
ПРИЛОЖЕНИЕ В. ЛИСТИНГ ПРОГРАММЫ
Format.h
class Format {
public:
BYTE* aBitmapBits;// массив пикселов
BYTE* aBitmapBits1;
BYTE* aBitmapCMY;
BYTE* aBitmapHSV;
BYTE R;
BYTE G;
BYTE B;
Format();
~Format();
BOOL CreateDib24(int w, int h, const char* fileName);
void StoreDib24();
BOOL LoadFromFile(const char* fileName);
void SetPixel(int x, int y, COLORREF color);
int Draw(HDC hdc, int xDst, int yDst, int wDst, int hDst,
int xSrc, int ySrc, int wSrc, int hSrc, DWORD rop);
int GetWidth() { return width; }
int GetHeight() { return height; }
const char* GetError() { return error.c_str();}
void RGBtoCMY();
void RGBtoHSV();
void CMYtoRGB();
void HSVtoRGB();
void BazisС();
void BazisM();
void BazisY();
void BazisH();
void BazisS();
void BazisV();
void NoiseM();
void NoiseS();
void Filter();
void Save();
void Circ();
void Transformtocirc(int x,int y);
int Draw1(HDC hdc, int xDst, int yDst, int wDst, int hDst,
int xSrc, int ySrc, int wSrc, int hSrc, DWORD rop);
void TransformPix(int x,int y);
private:
int width;
int height;
int bytesPerLine;
BITMAPFILEHEADER fileHead;
// заголовок растрового файла
BITMAPINFOHEADER infoHead;
// заголовок информационного блока
BITMAPINFOHEADER* pInfoHead;
int fileHeadSize;
int infoHeadSize;
int imageSize;
string error;
ofstream outFile;
};
Format.cpp
#include "Format.h"
#include <math.h>
Format::Format()
{
fileHeadSize = sizeof(BITMAPFILEHEADER);
fileHead.bfType = 0x4d42;
aBitmapBits = NULL;
}
Format::~Format() {
if (pInfoHead) delete [] pInfoHead;
if (aBitmapBits) delete [] aBitmapBits;
if (outFile) outFile.close();
}
BOOL Format::CreateDib24(int w, int h, const char* fileName) {
width = w;
height = h;
bytesPerLine = ((width * 24 + 31) / 32) * 4;
imageSize = bytesPerLine * height;
infoHeadSize = sizeof(BITMAPINFOHEADER);
fileHead.bfSize = fileHeadSize + infoHeadSize + bytesPerLine * height;
fileHead.bfOffBits = fileHeadSize + infoHeadSize;
infoHead.biSize = infoHeadSize;
infoHead.biWidth = width;
infoHead.biHeight = height;
infoHead.biPlanes = 1;
infoHead.biBitCount = 24;
infoHead.biCompression = BI_RGB;
infoHead.biSizeImage = imageSize;
aBitmapBits;
outFile.open(fileName, ios::out | ios::binary | ios::trunc);
if (!outFile) return FALSE;
else return TRUE;
}
BOOL Format::LoadFromFile(const char* fileName) {
ifstream inpFile;
inpFile.open(fileName, ios::in | ios::binary);
if (!inpFile) {
error = "Неверное имя файла или каталога.";
return FALSE;
}
inpFile.read((char*)&fileHead, fileHeadSize);
if (fileHead.bfType != 0x4d42) {
error = "Это не BMP-файл";
return FALSE;
}
infoHeadSize = fileHead.bfOffBits - fileHeadSize;
int fileSize = fileHead.bfSize;
imageSize = fileSize - (fileHeadSize + infoHeadSize);
pInfoHead = (BITMAPINFOHEADER*)(new BYTE [infoHeadSize]);
inpFile.read((char*)pInfoHead, infoHeadSize);
width =
pInfoHead->biWidth;
height = pInfoHead->biHeight;
aBitmapBits = new BYTE[imageSize];
inpFile.read((char*)aBitmapBits, imageSize);
return true;
}
int Format::Draw(HDC hdc, int xDst, int yDst, int wDst, int hDst,
int xSrc, int ySrc, int wSrc, int hSrc, DWORD rop) {
return StretchDIBits(hdc, xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc,
hSrc,
aBitmapBits, (CONST BITMAPINFO*)pInfoHead, DIB_RGB_COLORS, rop);
}
void Format::SetPixel(int x, int y, COLORREF color) {
int row = y;
int col = 3 * x;
aBitmapBits[row*bytesPerLine + col]
= GetBValue(color);
aBitmapBits[row*bytesPerLine + col+1] = GetGValue(color);
aBitmapBits[row*bytesPerLine + col+2] = GetRValue(color);
aBitmapBits;
}
void Format::StoreDib24() {
// Запись заголовка BMP-файла
outFile.write((char*)&fileHead, fileHeadSize);
outFile.write((char*)&infoHead, infoHeadSize);
// Запись массива пикселей
outFile.write((char*)aBitmapBits, imageSize);
}
void Format::CMYtoRGB()
{
HDC hdc;
BYTE R,G,B,C,M,Y;
int x = GetWidth();
int y = GetHeight();
int bytesPerLine = ((x * 24 + 31) / 32) * 4;
int row = y;
int col = 3 * x;
for(int row =
for(int
C
M
Y
0;row < y;row++)
col = 0;col < x;col++){
= aBitmapCMY[row*bytesPerLine + col*3 + 2];
= aBitmapCMY[row*bytesPerLine + (col*3)+ 1];
= aBitmapCMY[row*bytesPerLine + (col*3)];
R = 255-C;
G = 255-M;
B = 255-Y;
aBitmapBits[row*bytesPerLine + (col*3)]
= B;
aBitmapBits[row*bytesPerLine + (col*3)+ 1] = G;
aBitmapBits[row*bytesPerLine + col*3 + 2] = R;
}
aBitmapBits;
}
void Format::RGBtoCMY()
{
BYTE R,G,B,C,M,Y;
int x = GetWidth();
int y = GetHeight();
int row = y;
int col = 3 * x;
int bytesPerLine = ((x * 24 + 31) / 32) * 4;
imageSize = x * y * 3;
aBitmapCMY = new BYTE[imageSize];
for(int row =
for(int
R
G
0;row < y;row++)
col = 0;col < x;col++){
= aBitmapBits[row*bytesPerLine + col*3 + 2];
= aBitmapBits[row*bytesPerLine + (col*3)+ 1];
B = aBitmapBits[row*bytesPerLine + (col*3)];
C = 255-R;
M = 255-G;
Y = 255-B;
aBitmapCMY[row*bytesPerLine + (col*3)]
= Y;
aBitmapCMY[row*bytesPerLine + (col*3)+ 1] = M;
aBitmapCMY[row*bytesPerLine + col*3 + 2] = C;
}
aBitmapBits=aBitmapCMY;
}
void Format::RGBtoHSV(){
BYTE R,G,B,H,S,V;
double
rgbmin,rgbmax;
double
rc,bc,gc;
double
ht=0.0,st=0.0;
int x = GetWidth();
int y = GetHeight();
int row = y;
int col = 3 * x;
int bytesPerLine = ((x * 24 + 31) / 32) * 4;
imageSize = x * y * 3;
aBitmapHSV = new BYTE[imageSize];
for(int row =
for(int
{
R
G
B
0;row < y;row++)
col = 0;col < x;col++)
= aBitmapBits[row*bytesPerLine + col*3 + 2];
= aBitmapBits[row*bytesPerLine + (col*3)+ 1];
= aBitmapBits[row*bytesPerLine + (col*3)];
rgbmin = min(R,min(G,B));
rgbmax = max(R,max(G,B));
if (rgbmax > 0.0)
st = (rgbmax - rgbmin) / rgbmax;
if (st > 0.0) {
rc = (rgbmax - R) / (rgbmax - rgbmin);
gc = (rgbmax - G) / (rgbmax - rgbmin);
bc = (rgbmax - B) / (rgbmax - rgbmin);
if (R == rgbmax) ht = bc - gc;
else if (G == rgbmax) ht = 2 + rc - bc;
else if (B == rgbmax) ht = 4 + gc - rc;
ht = ht * 60.0;
if (ht < 0.0) ht += 360.0;
}
H = ht / 360.0;
V = rgbmax;
S = st;
aBitmapHSV[row*bytesPerLine + (col*3)]
= V;
aBitmapHSV[row*bytesPerLine + (col*3)+ 1] = S;
aBitmapHSV[row*bytesPerLine + col*3 + 2] = H;
}
}
void Format::HSVtoRGB()
{
BYTE R,G,B,H,S,V,i_H;
int ii;
double fr;
unsigned char c1, c2, c3;
int x = GetWidth();
int y = GetHeight();
int row = y;
int col = 3 * x;
int bytesPerLine = ((x * 24 + 31) / 32) * 4;
for(int row = 0;row < y;row++)
for(int col = 0;col < x;col++){
i_H = aBitmapHSV[row*bytesPerLine + col*3 + 2];
S = aBitmapHSV[row*bytesPerLine + (col*3)+ 1];
V = aBitmapHSV[row*bytesPerLine + (col*3)];
if (!S)
R = G = B = V;
else
{
while (i_H >= 360)
i_H -= 360;
H = i_H;
ii = (int)(H /= 60.0);
fr = H - ii;
c1 = (V * (255 - S)) / 255;
c2 = (V * (255 - S * fr)) / 255;
c3 = (V * (255 - S * (1.0 - fr)))
switch (ii)
{
case 0: R = V; G = c3; B = c1;
case 1: R = c2; G = V; B = c1;
case 2: R = c1; G = V; B = c3;
case 3: R = c1; G = c2; B = V;
case 4: R = c3; G = c1; B = V;
case 5: R = V; G = c1; B = c2;
} // switch
} // if (!sat)
/ 255;
break;
break;
break;
break;
break;
break;
aBitmapBits[row*bytesPerLine + (col*3)]
= B;
aBitmapBits[row*bytesPerLine + (col*3)+ 1] = G;
aBitmapBits[row*bytesPerLine + col*3 + 2] = R;
}
}
void Format::NoiseM(){
BYTE M;
int x = GetWidth();
int y = GetHeight();
int bytesPerLine = ((x * 24 + 31) / 32) * 4;
for(int row = 0;row < y;row++)
for(int col = 0;col < x;col++){
M = aBitmapCMY[row*bytesPerLine + (col*3)+1];
M+=rand();
if(M < 0)
M = 0;
if(M > 255) M = 255;
aBitmapCMY[row*bytesPerLine + (col*3)+1] = M;
}
aBitmapBits = aBitmapCMY;
}
void Format::NoiseS(){
BYTE S;
int x = GetWidth();
int y = GetHeight();
int bytesPerLine = ((x * 24 + 31) / 32) * 4;
for(int row = 0;row < y;row++)
for(int col = 0;col < x;col++){
S = aBitmapHSV[row*bytesPerLine + (col*3)+1];
S+=rand();
if(S < 0)
S = 0;
if(S > 255) S = 255;
aBitmapHSV[row*bytesPerLine + (col*3)+1] = S;
}
aBitmapBits = aBitmapHSV;
}
void Format::TransformPix(int x,int y){
BYTE NewBGR[3];
int Pixel[3];
BYTE Pix[3];
int w = GetWidth();
int h = GetHeight();
int count = 9;
int Matrix[9]={1,2,1,
2,4,2,
1,2,1};
int bytesPerLine = ((w * 24 + 31) / 32) * 4;
// x y
for (int c = 0; c < 3; c++){
NewBGR[c] = aBitmapBits[y*bytesPerLine + x*3 + c];
Pixel[c] = NewBGR[c] * Matrix[4];
}
// x-1 y-1
for (int c = 0; c < 3; c++){
NewBGR[c] = aBitmapBits[(y-1)*bytesPerLine + (x-1)*3 +
Pixel[c] += NewBGR[c] * Matrix[0];
}
// x y-1
for (int c = 0; c < 3; c++){
NewBGR[c] = aBitmapBits[y*bytesPerLine + (x-1)*3 + c];
Pixel[c] += NewBGR[c] * Matrix[1];
}
// x+1 y-1
for (int c = 0; c < 3; c++){
NewBGR[c] = aBitmapBits[(y+1)*bytesPerLine + (x-1)*3 +
Pixel[c] += NewBGR[c] * Matrix[2];
}
// x+1 y
for (int c = 0; c < 3; c++){
NewBGR[c] = aBitmapBits[(y+1)*bytesPerLine + x*3 + c];
Pixel[c] += NewBGR[c] * Matrix[5];
}
// x+1 y+1
for (int c = 0; c < 3; c++){
NewBGR[c] = aBitmapBits[(y+1)*bytesPerLine + (x+1)*3 +
Pixel[c] += NewBGR[c] * Matrix[8];
}
// x y+1
for (int c = 0; c < 3; c++){
NewBGR[c] = aBitmapBits[y*bytesPerLine + (x+1)*3 + c];
Pixel[c] += NewBGR[c] * Matrix[7];
}
// x-1 y+1
for (int c = 0; c < 3; c++){
NewBGR[c] = aBitmapBits[(y-1)*bytesPerLine + (x+1)*3 +
Pixel[c] += NewBGR[c] * Matrix[6];
c];
c];
c];
c];
}
// x-1 y
for (int c = 0; c < 3; c++){
NewBGR[c] = aBitmapBits[(y-1)*bytesPerLine +
Pixel[c] += NewBGR[c] * Matrix[3];
}
x*3 + c];
for (int c = 0; c < 3; c++){
if(count!=0)
Pixel[c] = Pixel[c]/count;
Pix[c] = BYTE (Pixel[c]);
if(Pix[c] < 0)
Pix[c] = 0;
else if(Pix[c] > 255)
Pix[c]=255;
aBitmapHSV[y*bytesPerLine + x*3 + c] = Pix[c];
}
}
void Format::Filter(){
int w = GetWidth();
int h = GetHeight();
imageSize = w * h * 4;
aBitmapHSV = new BYTE[imageSize];
for(int y = 1; y < h-1;y++)
for(int x = 1;x < w-1;x++){
TransformPix(x,y);
}
aBitmapBits = aBitmapHSV;
}
void Format::BazisС()
{
BYTE R,G,B,C,M,Y;
int x = GetWidth();
int y = GetHeight();
int row = y;
int col = 3 * x;
int bytesPerLine = ((x * 24 + 31) / 32) * 4;
imageSize = x * y * 3;
aBitmapCMY = new BYTE[imageSize];
for(int row =
for(int
R
G
B
0;row < y;row++)
col = 0;col < x;col++){
= aBitmapBits[row*bytesPerLine + col*3 + 2];
= aBitmapBits[row*bytesPerLine + (col*3)+ 1];
= aBitmapBits[row*bytesPerLine + (col*3)];
C = 255-R;
M = G-G;
Y = B-B;
aBitmapCMY[row*bytesPerLine + (col*3)]
= Y;
aBitmapCMY[row*bytesPerLine + (col*3)+ 1] = M;
aBitmapCMY[row*bytesPerLine + col*3 + 2] = C;
}
aBitmapBits=aBitmapCMY;
}
void Format::BazisM()
{
BYTE R,G,B,C,M,Y;
int x = GetWidth();
int y = GetHeight();
int row = y;
int col = 3 * x;
int bytesPerLine = ((x * 24 + 31) / 32) * 4;
imageSize = x * y * 3;
aBitmapCMY = new BYTE[imageSize];
for(int row =
for(int
R
G
B
0;row < y;row++)
col = 0;col < x;col++){
= aBitmapBits[row*bytesPerLine + col*3 + 2];
= aBitmapBits[row*bytesPerLine + (col*3)+ 1];
= aBitmapBits[row*bytesPerLine + (col*3)];
C = R-R;
M = 255-G;
Y = B-B;
aBitmapCMY[row*bytesPerLine + (col*3)]
= Y;
aBitmapCMY[row*bytesPerLine + (col*3)+ 1] = M;
aBitmapCMY[row*bytesPerLine + col*3 + 2] = C;
}
aBitmapBits=aBitmapCMY;
}
void Format::BazisY()
{
BYTE R,G,B,C,M,Y;
int x = GetWidth();
int y = GetHeight();
int row = y;
int col = 3 * x;
int bytesPerLine = ((x * 24 + 31) / 32) * 4;
imageSize = x * y * 3;
aBitmapCMY = new BYTE[imageSize];
for(int row =
for(int
R
G
B
0;row < y;row++)
col = 0;col < x;col++){
= aBitmapBits[row*bytesPerLine + col*3 + 2];
= aBitmapBits[row*bytesPerLine + (col*3)+ 1];
= aBitmapBits[row*bytesPerLine + (col*3)];
C = R-R;
M = G-G;
Y = 255-B;
aBitmapCMY[row*bytesPerLine + (col*3)]
= Y;
aBitmapCMY[row*bytesPerLine + (col*3)+ 1] = M;
aBitmapCMY[row*bytesPerLine + col*3 + 2] = C;
}
aBitmapBits=aBitmapCMY;
}
void Format::BazisH(){
BYTE R,G,B,H,S,V;
double
rgbmin,rgbmax;
double
rc,bc,gc;
double
ht=0.0,st=0.0;
int x = GetWidth();
int y = GetHeight();
int row = y;
int col = 3 * x;
int bytesPerLine = ((x * 24 + 31) / 32) * 4;
imageSize = x * y * 3;
aBitmapHSV = new BYTE[imageSize];
for(int row =
for(int
{
R
G
B
0;row < y;row++)
col = 0;col < x;col++)
= aBitmapBits[row*bytesPerLine + col*3 + 2];
= aBitmapBits[row*bytesPerLine + (col*3)+ 1];
= aBitmapBits[row*bytesPerLine + (col*3)];
rgbmin = min(R,min(G,B));
rgbmax = max(R,max(G,B));
if (rgbmax > 0.0)
st = (rgbmax - rgbmin) / rgbmax;
if (st > 0.0) {
rc = (rgbmax - R) / (rgbmax - rgbmin);
gc = (rgbmax - G) / (rgbmax - rgbmin);
bc = (rgbmax - B) / (rgbmax - rgbmin);
if (R == rgbmax) ht = bc - gc;
else if (G == rgbmax) ht = 2 + rc - bc;
else if (B == rgbmax) ht = 4 + gc - rc;
ht = ht * 60.0;
if (ht < 0.0) ht += 360.0;
}
H = ht / 360.0;
V = rgbmin;
S = 0;
aBitmapHSV[row*bytesPerLine + (col*3)]
= V;
aBitmapHSV[row*bytesPerLine + (col*3)+ 1] = S;
aBitmapHSV[row*bytesPerLine + col*3 + 2] = H;
}
aBitmapBits=aBitmapHSV;
}
void Format::BazisS(){
BYTE R,G,B,H,S,V;
double
rgbmin,rgbmax;
double
rc,bc,gc;
double
ht=0.0,st=0.0;
int x = GetWidth();
int y = GetHeight();
int row = y;
int col = 3 * x;
int bytesPerLine = ((x * 24 + 31) / 32) * 4;
imageSize = x * y * 3;
aBitmapHSV = new BYTE[imageSize];
for(int row =
for(int
{
R
G
B
0;row < y;row++)
col = 0;col < x;col++)
= aBitmapBits[row*bytesPerLine + col*3 + 2];
= aBitmapBits[row*bytesPerLine + (col*3)+ 1];
= aBitmapBits[row*bytesPerLine + (col*3)];
rgbmin = min(R,min(G,B));
rgbmax = max(R,max(G,B));
if (rgbmax > 0.0)
st = (rgbmax - rgbmin) / rgbmax;
if (st > 0.0) {
rc = (rgbmax - R) / (rgbmax - rgbmin);
gc = (rgbmax - G) / (rgbmax - rgbmin);
bc = (rgbmax - B) / (rgbmax - rgbmin);
if (R == rgbmax) ht = bc - gc;
else if (G == rgbmax) ht = 2 + rc - bc;
else if (B == rgbmax) ht = 4 + gc - rc;
ht = ht * 60.0;
if (ht < 0.0) ht += 360.0;
}
H = 0.0;
V = 0 ;
S = st;
aBitmapHSV[row*bytesPerLine + (col*3)]
= V;
aBitmapHSV[row*bytesPerLine + (col*3)+ 1] = S;
aBitmapHSV[row*bytesPerLine + col*3 + 2] = H;
}
aBitmapBits=aBitmapHSV;
}
void Format::BazisV(){
BYTE R,G,B,H,S,V;
double
rgbmin,rgbmax;
double
rc,bc,gc;
double
ht=0.0,st=0.0;
int x = GetWidth();
int y = GetHeight();
int row = y;
int col = 3 * x;
int bytesPerLine = ((x * 24 + 31) / 32) * 4;
imageSize = x * y * 3;
aBitmapHSV = new BYTE[imageSize];
for(int row =
for(int
{
R
G
B
0;row < y;row++)
col = 0;col < x;col++)
= aBitmapBits[row*bytesPerLine + col*3 + 2];
= aBitmapBits[row*bytesPerLine + (col*3)+ 1];
= aBitmapBits[row*bytesPerLine + (col*3)];
rgbmin = min(R,min(G,B));
rgbmax = max(R,max(G,B));
if (rgbmax > 0.0)
st = (rgbmax - rgbmin) / rgbmax;
if (st > 0.0) {
rc = (rgbmax - R) / (rgbmax - rgbmin);
gc = (rgbmax - G) / (rgbmax - rgbmin);
bc = (rgbmax - B) / (rgbmax - rgbmin);
if (R == rgbmax) ht = bc - gc;
else if (G == rgbmax) ht = 2 + rc - bc;
else if (B == rgbmax) ht = 4 + gc - rc;
ht = ht * 60.0;
if (ht < 0.0) ht += 360.0;
}
H = 0.0;
V = rgbmax ;
S = 0;
aBitmapHSV[row*bytesPerLine + (col*3)]
= V;
aBitmapHSV[row*bytesPerLine + (col*3)+ 1] = S;
aBitmapHSV[row*bytesPerLine + col*3 + 2] = H;
}
aBitmapBits=aBitmapHSV;
}
void Format::Save()
{
int x = GetWidth();
int y = GetHeight();
int bytesPerLine = ((x * 24 + 31) / 32) * 4;
imageSize = x * y * 3;
aBitmapBits1 = new BYTE[imageSize];
aBitmapBits1=aBitmapBits;
}
int Format::Draw1(HDC hdc, int xDst, int yDst, int wDst, int hDst,
int xSrc, int ySrc, int wSrc, int hSrc, DWORD rop)
{
int x = GetWidth();
int y = GetHeight();
int bytesPerLine = ((x * 24 + 31) / 32) * 4;
imageSize = x * y * 3;
aBitmapBits = new BYTE[imageSize];
aBitmapBits=aBitmapBits1;
return StretchDIBits(hdc, xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc,
hSrc,
aBitmapBits1, (CONST BITMAPINFO*)pInfoHead, DIB_RGB_COLORS, rop);
}
void Format::Circ()
{
int Matrix[11][11]={1,1,1,1,0,0,0,1,1,1,1,
1,1,1,0,0,1,0,0,1,1,1,
1,1,0,0,1,1,1,0,0,1,1,
1,0,0,1,1,1,1,1,0,0,1,
0,0,1,1,1,1,1,1,1,0,0,
0,1,1,1,1,1,1,1,1,1,0,
0,0,1,1,1,1,1,1,1,0,0,
1,0,0,1,1,1,1,1,0,0,1,
1,1,0,0,1,1,1,0,0,1,1,
1,1,1,0,0,1,0,0,1,1,1,
1,1,1,1,0,0,0,1,1,1,1};
BYTE R,G,B;
int I=0,J=0;
int w = GetWidth();
int h = GetHeight();
int row = h;
int col = 3 * w;
int bytesPerLine = ((w * 24 + 31) / 32) * 4;
for(int i = 0;i < h; i++)
{
for(int j = 0;j < w; j++)
{
R = aBitmapBits[j*bytesPerLine + i*3 + 2];
G = aBitmapBits[j*bytesPerLine + (i*3)+ 1];
B = aBitmapBits[j*bytesPerLine + (i*3)];
R *=Matrix [I][J];
G *=Matrix [I][J];
B *=Matrix [I][J];
aBitmapBits[j*bytesPerLine + (i*3)] = B;
aBitmapBits[j*bytesPerLine + (i*3)+ 1] = G;
aBitmapBits[j*bytesPerLine + i*3 + 2] = R;
J++;
if (J==11)
{
J=0;
//j+=1;
}
}
I++;
J=0;
if (I==11)
{
I=0;
//i+=1;
}
}
aBitmapBits;
}
KP.cpp
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
<windows.h>
<winbase.h>
<math.h>
<stdlib.h>
<malloc.h>
<memory.h>
<tchar.h>
<conio.h>
<stdio.h>
<string.h>
"Format.h"
Format bmp;
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
ID_BUTTON1 1001
ID_BUTTON2 1002
ID_BUTTON3 1003
ID_BUTTON4 1004
ID_BUTTON5 1005
ID_BUTTON6 1006
ID_BUTTON7 1007
ID_BUTTON8 1008
ID_BUTTON9 1009
ID_BUTTON10 1010
ID_BUTTON11 1011
ID_BUTTON12 1012
ID_BUTTON13 1013
ID_BUTTON14 1014
static HWND
Button1,Button2,Button3,Button4,Button5,Button6,Button7,Button8,Button9,Butto
n10,Button11,Button12,Button13,Button14;
static HWND Static1,Static2;
LONG MyWinP(HWND, UINT,WPARAM,LPARAM);
//DWORD WINAPI SecondThread(LPVOID params);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR
lpCmdLine, int nCmdShow)
{
char ProgName[]="My Window";
char Title[]=" ";
HWND hWnd;
MSG msg;
WNDCLASS w;
w.lpszClassName=ProgName;
w.hInstance=hInstance;
w.lpfnWndProc=(WNDPROC)MyWinP;
w.hCursor=LoadCursor(NULL, IDC_ARROW);
w.hIcon=LoadIcon(NULL, IDI_APPLICATION);
w.lpszMenuName=NULL;
w.hbrBackground=(HBRUSH)(COLOR_WINDOW+1);
w.style=CS_HREDRAW | CS_VREDRAW;
w.cbClsExtra=0;
w.cbWndExtra=0;
if(!RegisterClass(&w)) return false;
hWnd=CreateWindow(ProgName,Title, WS_OVERLAPPEDWINDOW,
0,0,700,600, NULL, NULL, hInstance, NULL);
if (!hWnd) return false;
ShowWindow(hWnd, nCmdShow);
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LONG
{
MyWinP(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
PAINTSTRUCT ps;
HDC hdc;
static CHOOSECOLOR ccol;
static COLORREF clf;
static COLORREF clfusers[16];
RECT rt;
HBRUSH brush;
HANDLE hOldBrush, hOldFont;
static CHOOSEFONT cfont;
static COLORREF colfont;
GetClientRect(hWnd,&rt);
int ws, hs, wd, hd;
SIZE sz;
BOOL success;
BOOL isFileCreated;
static BOOL isFileLoadedT=false;
static BOOL isFileLoaded=false;
static OPENFILENAME ofn;
static char filename[256];
static char szFile[MAX_PATH];
static char szFileTitle[256];
static char szFilter[256]=
"Файлы GIF\0*.gif\0Файлы BMP\0*.bmp\0";
//int dX, dY;
static BOOL view=false;
FILE *f;
switch(msg)
{
case WM_CREATE:
hdc = GetDC(hWnd);
ofn.lStructSize = sizeof(OPENFILENAME);
ofn.hwndOwner = hWnd;
ofn.nFilterIndex = 1;
ofn.lpstrFile = szFile;
ofn.nMaxFile = sizeof(szFile);
//ofn.lpstrInitialDir = "..\";
Button1=CreateWindow( "button" ,"Open as BMP", WS_TABSTOP |
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 20, 25, 100, 25, hWnd,
(HMENU)(ID_BUTTON1), NULL, NULL);
Button2=CreateWindow( "button" ,"Save as GIF", WS_TABSTOP |
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 20, 55, 100, 25, hWnd,
(HMENU)(ID_BUTTON2), NULL, NULL);
Button3=CreateWindow( "button" ,"Return", WS_TABSTOP | WS_CHILD |
WS_VISIBLE | BS_PUSHBUTTON, 20, 85, 100, 25, hWnd, (HMENU)(ID_BUTTON3), NULL,
NULL);
Button4=CreateWindow( "button" ,"EXIT", WS_TABSTOP | WS_CHILD |
WS_VISIBLE | BS_PUSHBUTTON, 20, 115, 100, 25, hWnd, (HMENU)(ID_BUTTON4),
NULL, NULL);
Static1=CreateWindow( "static", "CMY", WS_CHILD | WS_VISIBLE |
WS_BORDER | SS_CENTER, 550, 5, 50, 20, hWnd, NULL, NULL, NULL);
Button5=CreateWindow( "button" ,"Cyan", WS_TABSTOP | WS_CHILD |
WS_VISIBLE | BS_PUSHBUTTON, 550, 25, 100, 25, hWnd, (HMENU)(ID_BUTTON5),
NULL, NULL);
Button6=CreateWindow( "button" ,"Magenta", WS_TABSTOP | WS_CHILD |
WS_VISIBLE | BS_PUSHBUTTON, 550, 55, 100, 25, hWnd, (HMENU)(ID_BUTTON6),
NULL, NULL);
Button7=CreateWindow( "button" ,"Yellow", WS_TABSTOP | WS_CHILD |
WS_VISIBLE | BS_PUSHBUTTON, 550, 85, 100, 25, hWnd, (HMENU)(ID_BUTTON7),
NULL, NULL);
Static2=CreateWindow( "static", "HSV", WS_CHILD | WS_VISIBLE |
WS_BORDER | SS_CENTER, 550, 155, 50, 20, hWnd, NULL, NULL, NULL);
Button8=CreateWindow( "button" ,"Hue", WS_TABSTOP | WS_CHILD |
WS_VISIBLE | BS_PUSHBUTTON, 550, 175, 100, 25, hWnd, (HMENU)(ID_BUTTON8),
NULL, NULL);
Button9=CreateWindow( "button" ,"Saturation", WS_TABSTOP |
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 550, 205, 100, 25, hWnd,
(HMENU)(ID_BUTTON9), NULL, NULL);
Button10=CreateWindow( "button" ,"Value", WS_TABSTOP | WS_CHILD |
WS_VISIBLE | BS_PUSHBUTTON, 550, 235, 100, 25, hWnd, (HMENU)(ID_BUTTON10),
NULL, NULL);
Button11=CreateWindow( "button" ,"Filtration", WS_TABSTOP | WS_CHILD
| WS_VISIBLE | BS_PUSHBUTTON, 550, 355, 100, 25, hWnd, (HMENU)(ID_BUTTON11),
NULL, NULL);
Button12=CreateWindow( "button" ,"Noiz S", WS_TABSTOP | WS_CHILD |
WS_VISIBLE | BS_PUSHBUTTON, 550, 385, 100, 25, hWnd, (HMENU)(ID_BUTTON12),
NULL, NULL);
Button13=CreateWindow( "button" ,"Noiz M", WS_TABSTOP | WS_CHILD |
WS_VISIBLE | BS_PUSHBUTTON, 550, 415, 100, 25, hWnd, (HMENU)(ID_BUTTON13),
NULL, NULL);
Button14=CreateWindow( "button" ,"Effect circls", WS_TABSTOP |
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 550, 445, 100, 25, hWnd,
(HMENU)(ID_BUTTON14), NULL, NULL);
break;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case ID_BUTTON1:
//hdc=GetDC(hWnd);
strcpy(szFile, " ");
ofn.nFilterIndex = 2;
ofn.lpstrFilter=szFilter;
success = GetOpenFileName(&ofn);
if (success)
{
isFileLoaded = bmp.LoadFromFile(ofn.lpstrFile);
if (!isFileLoaded) {
MessageBox(hWnd, "Файл " /*FILE_NAME*/ " не
загружен.", "Error", MB_OK|MB_ICONERROR);
MessageBox(hWnd, bmp.GetError(), "Error",
MB_OK);
break;
}
bmp.Save();
view=true;
break;
default:
break;
}
break;
case ID_BUTTON2:
strcpy(szFile, "outfile.gif");
ofn.nFilterIndex = 1;
success = GetSaveFileName(&ofn);
if (success){
//MessageBox(hWnd, ofn.lpstrFile,"Файл сохраняется с
именем:", MB_OK);
isFileCreated =
bmp.CreateDib24(bmp.GetWidth(),bmp.GetHeight(), ofn.lpstrFile);
if (!isFileCreated){
MessageBox(hWnd, "Файл ofn.lpstrFile не
создан.", "Error", MB_OK);
}
bmp.aBitmapBits;
bmp.StoreDib24();
}
else
//MessageBox(hWnd, ESC_OF"GetSaveFileName","Отказ от
выбора или ошибка", MB_ICONWARNING);
break;
break;
case ID_BUTTON3:
hdc=GetDC(hWnd);
wd = ws = bmp.GetWidth();
hd = hs = bmp.GetHeight();
bmp.Draw1(hdc,160,0,wd,hd,0,0,ws,hs,SRCCOPY);
ReleaseDC(hWnd,hdc);
break;
case ID_BUTTON5:
bmp.BazisС();
InvalidateRect(hWnd, NULL, TRUE);
bmp.CMYtoRGB();
break;
case ID_BUTTON6:
bmp.BazisM();
InvalidateRect(hWnd, NULL, TRUE);
bmp.CMYtoRGB();
break;
case ID_BUTTON7:
bmp.BazisY();
InvalidateRect(hWnd, NULL, TRUE);
bmp.CMYtoRGB();
break;
case ID_BUTTON8:
bmp.BazisH();
InvalidateRect(hWnd, NULL, TRUE);
bmp.HSVtoRGB();
break;
case ID_BUTTON9:
bmp.BazisS();
InvalidateRect(hWnd, NULL, TRUE);
bmp.HSVtoRGB();
break;
case ID_BUTTON10:
bmp.BazisV();
InvalidateRect(hWnd, NULL, TRUE);
bmp.HSVtoRGB();
break;
case ID_BUTTON12:
bmp.RGBtoHSV();
bmp.NoiseS();
InvalidateRect(hWnd, NULL, TRUE);
bmp.HSVtoRGB();
break;
case ID_BUTTON13:
bmp.RGBtoCMY();
bmp.NoiseM();
InvalidateRect(hWnd, NULL, TRUE);
bmp.CMYtoRGB();
break;
case ID_BUTTON11:
bmp.Filter();
InvalidateRect(hWnd, NULL, TRUE);
break;
case ID_BUTTON14:
bmp.Circ();
InvalidateRect(hWnd, NULL, TRUE);
break;
case ID_BUTTON4:
PostQuitMessage(0);
break;
}
break;
case WM_PAINT:
hdc=BeginPaint(hWnd,&ps);
GetClientRect(hWnd,&rt);
brush=CreateSolidBrush(clf);
hOldBrush=SelectObject(hdc,brush);
SelectObject(hdc, hOldBrush);
brush=CreateSolidBrush(RGB(85,0,85));
SelectObject(hdc,brush);
Rectangle(hdc,rt.left,rt.top,rt.right,rt.bottom);
DeleteObject(brush);
brush=CreateSolidBrush(RGB(195,35,90));
SelectObject(hdc,brush);
Rectangle(hdc,0,0,150,170);
Rectangle(hdc,530,0,670,130);
Rectangle(hdc,530,150,670,270);
Rectangle(hdc,530,340,670,480);
DeleteObject(brush);
if(!view){
//tga.Draw(hDC,tga.GetA());
}
else{
wd = ws = bmp.GetWidth();
hd = hs = bmp.GetHeight();
bmp.Draw(hdc,160,0,wd,hd,0,0,ws,hs,SRCCOPY);
}
EndPaint(hWnd,&ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, msg, wParam, lParam);
}
return 0;
}
ПРИЛОЖЕНИЕ Д. РУКОВОДСТВО ПОЛЬЗОВАТЕЛЯ
Программа является достаточно легкой в пользовании. Каждая кнопка
меню соответствует определенному
действию над изображением. Суть
действия можно понять, прочитав надпись на кнопках.
Download