Окна нестандартной формы на Ассемблере. Часть 2
В первой части статьи я рассказал как создавать окна нестандартной формы при помощи регионов. У этого способа есть один большой недостаток: создаваемые окна так или иначе состоят из четких геометрических форм. Но высший пилотаж - это окна в форме картинок, и сейчас я расскажу как они делаются. Сперва немного теоретических выкладок. В графическом файле формата BMP информация о картинке хранится в растровом виде, то есть каждый пиксел описан определенным цветом. Рекомендую внимательно прочитать документацию о формате BMP-файла, так как есть несколько важных моментов. Создание окна нестандартной формы на основе растровой картинки заключается в наложении изображения на диалоговое окно и удалении всех его регионов, в которых находятся точки определенного цвета. Этот цвет мы будем считать "прозрачным", потому что настоящую прозрачность обычный формат BMP не поддерживает. А удалять отдельные регионы мы уже умеем.Итак, берем нужную картинку и накладываем ее на какой-нибудь однородный цветной фон, причем цвет фона не должен присутствовать на основной картинке. Можно добавить надпись, но главное чтобы границы всех элементов композиции были четкими и не сливались с фоном. Получится примерно следующее:
Картинка для окна
Картинку надо сохранить в формате BMP, с глубиной цвета 8 бит. Это очень важно, так как в этом случае количество цветов в палитре не превышает 256, а каждая точка описывается ровно одним байтом. Поскольку картинка будет накладываться на диалоговое окно, то и хранить ее надо будет в ресурсах. Тут есть важная особенность: в ресурсах картинка хранится без 14-байтного заголовка BITMAPFILEHEADER и все смещения считаются сразу же от начала блока BITMAPINFOHEADER.
Нам потребуется вспомогательная функция для получения цвета точки. В ней учтены особенности формата BMP, а именно то, что пиксели хранятся построчно, снизу вверх, а каждая строка изображения дополняется нулями до длины, кратной четырем байтам.
Code (Assembler) : Убрать нумерацию
- ;----------------------------------------------------------
- ; Процедура получения цвета точки с координатами X,Y
- ;----------------------------------------------------------
- ; dXCoord - X-координата запрашиваемой точки
- ; dYCoord - Y-координата запрашиваемой точки
- ; bmpX - ширина картинки
- ; bmpY - высота картинки
- ; bitmapData - указатель на начало массива картинки
- ; palData - указатель на начало массива палитры
- ; На выходе: EAX - RGB цвет точки из палитры
- ;----------------------------------------------------------
- proc GetPixelColorAt dXCoord:dword, dYCoord:dword, bmpX:dword,\
- bmpY:dword, bitmapData:dword, palData:dword
- ; Сохранить изменяемые регистры
- push esi ecx edx
- ; Проверка валидноости координат
- xor eax,eax
- mov esi,[dXCoord]
- cmp esi,[bmpX]
- ja @f
- mov esi,[dYCoord]
- cmp esi,[bmpY]
- ja @f
- ; Вычислить указатель на точку в картинке
- mov eax,[bmpY]
- dec eax
- sub eax,[dYCoord]
- mov ecx,[bmpX]
- ; Каждая строка изображения дополняется нулями до длины,
- ; кратной четырём байтам. Поэтому если ширина картинки не кратна
- ; 4, то вычислить поправку для нахождения правильного смещения
- ; точки в данных
- test ecx,3
- jz loc_no_fix
- and ecx,0FFFFFFFCh
- add ecx,4
- loc_no_fix:
- imul eax,ecx
- add eax,[dXCoord]
- mov esi,[bitmapData]
- ; Получить код цвета точки в палитре картинки
- movzx eax,byte [esi+eax]
- shl eax,2
- ; Получить значение цвета из палитры
- mov esi,[palData]
- mov eax,[esi+eax]
- @@:
- ; Восстановить измененные регистры
- pop edx ecx esi
- ret
- endp
Code (Assembler) : Убрать нумерацию
- ;----------------------------------------------------------
- ; Процедура установки скина на диалоговое окно
- ; Copyright (C) ManHunter / PCL
- ; http://www.manhunter.ru
- ;----------------------------------------------------------
- ; hwnddlg - хэндл окна, к которому будет применена
- ; битовая маска
- ; hInstance - хэндл приложения, в ресурсах которого
- ; хранится картинка
- ; BitmapID - идентификатор картинки в ресурсах
- ;----------------------------------------------------------
- proc SetBMPSkin hwnddlg:dword, hInstance:dword, BitmapID:dword
- local bitmapData:DWORD ; Указатель на начало данных картинки
- local palData:DWORD ; Указатель на начало палитры
- local bmpX:DWORD ; Размеры картинки
- local bmpY:DWORD
- local TransColor:DWORD ; Прозрачный цвет
- local hRMain:DWORD ; Хэндл главного региона окна
- local workX:DWORD ; Текущая X-координата
- local workY:DWORD ; Текущая Y-координата
- local regFlag:BYTE ; Флаг прозрачности
- local regStart:DWORD ; Начало прозрачной области в строке
- ; Сохранить все регистры
- pusha
- ; Найти в ресурсах картинку с маской
- invoke FindResource,[hInstance],[BitmapID],RT_BITMAP
- ; Загрузить картинку с маской
- invoke LoadResource,[hInstance],eax
- ; Получить указатель на память с картинкой
- invoke LockResource,eax
- ; Сохранить указатель
- mov edi,eax
- ; Получить все необходимые данные напрямую из файла
- mov ecx,[eax+00h]
- mov esi,ecx ; Размер заголовка
- add ecx,edi
- mov [palData],ecx ; Указатель на начало палитры
- mov ecx,[eax+04h] ; Ширина картинки
- mov [bmpX],ecx
- mov ecx,[eax+08h] ; Высота картинки
- mov [bmpY],ecx
- mov ecx,[eax+20h] ; Вычислить размер палитры
- or ecx,ecx
- jnz @f
- ; Если количество цветов = 0, значит используется максимальное
- ; доступное количество цветов для 8 бит, то есть 256 цветов
- mov ecx,256
- @@:
- shl ecx,2
- add ecx,esi
- add ecx,edi
- mov [bitmapData],ecx ; Указатель на начало данных
- ; Получить прозрачный цвет картинки. Подразумевается,
- ; что самый левый верхний пиксел прозрачный. Если это не так,
- ; то надо записать нужное значение в переменную TransColor
- stdcall GetPixelColorAt,0,0,[bmpX],[bmpY],[bitmapData],[palData]
- mov [TransColor],eax
- ; Cоздать главный регион для окна
- invoke CreateRectRgn,0,0,[bmpX],[bmpY]
- mov [hRMain],eax
- ; Установить начальные координаты
- mov [workY],0
- .loop_y:
- mov [workX],0
- mov [regStart],0
- mov [regFlag],0
- .loop_x:
- ; Получить цвет текущей точки
- stdcall GetPixelColorAt,[workX],[workY],[bmpX],[bmpY],\
- [bitmapData],[palData]
- cmp eax,[TransColor]
- jne .chk_pixel
- ; Точка прозрачная
- cmp [regFlag],1
- je .next_pixel
- mov eax,[workX]
- mov [regStart],eax
- mov [regFlag],1
- jmp .next_pixel
- .chk_pixel:
- ; Если точка непрозрачная и флаг не установлен
- cmp [regFlag],1
- jne .next_pixel
- mov eax,[workY]
- inc eax
- ; Вырезать прозрачный блок из главного региона
- invoke CreateRectRgn,[regStart],eax,[workX],[workY]
- push eax
- invoke CombineRgn,[hRMain],[hRMain],eax,RGN_DIFF
- ; Освободить память региона
- invoke DeleteObject
- ; Сбросить флаг и длину региона
- mov [regFlag],0
- .next_pixel:
- inc [workX]
- mov eax,[workX]
- cmp eax,[bmpX]
- jb .loop_x
- cmp [regFlag],1
- jne @f
- mov eax,[workY]
- inc eax
- ; Вырезать прозрачный блок из главного региона
- invoke CreateRectRgn,[regStart],eax,[workX],[workY]
- push eax
- invoke CombineRgn,[hRMain],[hRMain],eax,RGN_DIFF
- ; Освободить память региона
- invoke DeleteObject
- @@:
- inc [workY]
- mov eax,[workY]
- cmp eax,[bmpY]
- jb .loop_y
- ; Установить регион для окна с его перерисовкой
- invoke SetWindowRgn,[hwnddlg],[hRMain],TRUE
- ; Освободить память региона
- invoke DeleteObject,[hRMain]
- ; Восстановить все регистры
- popa
- ret
- endp
Code (Assembler) : Убрать нумерацию
- wminitdialog:
- ; Установить скин для диалогового окна
- stdcall SetBMPSkin,[hwnddlg],[hInstance],BITMAP_ID
- jmp processed
Окно нестандартной формы
Кнопки закрытия и сворачивания окна, а также их обработку придется реализовывать самостоятельно. Для большей красоты можно добавить эффект прозрачности и перетаскивание окна за любое место.
В приложении две программы с исходниками и картинками, реализующие приведенный выше алгоритм.
Просмотров: 7622 | Комментариев: 11
Внимание! Статья опубликована больше года назад, информация могла устареть!
Комментарии
Отзывы посетителей сайта о статье
Лёха
(06.02.2017 в 13:34):
Нашел MASM-ский исходник на http://www.allasm.ru/distr.php В нем есть различные способы реализации есть в том числе кнопки реализация регионом.Пытаюсь переделать на FASM.
ManHunter
(01.02.2017 в 15:28):
Можно, но только картинкой.
Лёха
(01.02.2017 в 14:28):
А можно ли сделать кнопку нестандартной формы?
brute
(23.08.2014 в 12:21):
Господи! Почти 300 строк Фasma! "получить цвет точки", "вычислить указатель точки", "проверить вылидность координат"...пока это держал в голове, уже забыл что хотел накодить.. Теперь прежде чем что-то кодить, надо изучить форматы bmp,jpg,gif,mp3,avi,итд итп? Наверное, это очень круто, но я тупоават и ленив для этого. Помню, как люди радовались, когда появились winAPI: сами следят за событиями, работают со строками, выводят целое окно на экран! Сейчас же писать на winapi считается пыткой, тратой времени в борьбе с багами. Быть может люди обленились? Но как заставить себя разбираться в этом, когда есть готовые кросплатформенные и кроссразрядные библиотеки? (которые иногда эффективнее аналогов на api, т.к. используют новые регистры или DirectX) Почему микрософт сама не понаделала удобных api для работы со строками, памятью, изображениями? В моем примере exe'шник 8,5кб: как PB это сделал - не знаю, но рисунка в ресурсах нет, возможно он сжат.
https://cloud.mail.ru/public/3...w.Region.rar
https://cloud.mail.ru/public/3...w.Region.rar
ManHunter
(13.10.2012 в 02:50):
Может сперва пару-тройку месяцев (а лучше лет) самостоятельно поизучать теорию? Это элементарные вопросы, самые азы, а здесь не бесплатные курсы с готовыми ответами на все вопросы.
Александр
(12.10.2012 в 22:23):
А можно еще вопрос, я хочу отлавливать нажатия других клавиш как это сделать в этом примере?
ManHunter
(10.10.2012 в 20:34):
Чо тут непонятного? Для обработчика в старшем слове дворда [wparam] должно быть значение BN_CLICKED, в младшем слове ID кнопки, то есть IDCANCEL. Через shl переводят константу BN_CLICKED из младшего слова в старшее и складывают с ID нажатой кнопки.
Александр
(10.10.2012 в 20:01):
Здравствуйте. Я не могу понять данный фрагмент.
Можете подробно объяснить.
wmcommand:
cmp [wparam],BN_CLICKED shl 16 + IDCANCEL
je wmclose
Заранее благодарю.
Можете подробно объяснить.
wmcommand:
cmp [wparam],BN_CLICKED shl 16 + IDCANCEL
je wmclose
Заранее благодарю.
Alexey
(25.03.2012 в 21:12):
Молодца
ManHunter
(31.08.2009 в 16:11):
Для PNG примеров нет, там другой принцип наложения маски на окно.
==DJ==[ZLO]
(31.08.2009 в 15:50):
Доброго времени суток ManHunter!
Интересная статья, а нет ли у Вас примера с форматом PNG для асма ?
Заранее спасибо.
Интересная статья, а нет ли у Вас примера с форматом PNG для асма ?
Заранее спасибо.
Добавить комментарий
Заполните форму для добавления комментария