Blog. Just Blog

Экранная лупа на Ассемблере

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Алгоритм реализации экранной лупы достаточно простой. Надо получить часть изображения рабочего стола и скопировать его с масштабированием в нужную область вашего приложения. Сделать это можно при помощи функции StretchBlt. Если посмотрите описание, то увидите, что для работы этой функции требуются следующие параметры: размеры результирующей области, размеры исходной области и контексты устройств (окон), в которых находятся области. А поскольку мы сейчас разрабатываем лупу, значит она должна увеличивать, то есть размеры исходного окна должны быть пропорционально меньше результирующего. Коэффициент пропорциональности и есть коэффициент увеличения лупы. При инициализации окна выполним предварительные расчеты:
  1.         ...
  2.         ; Получить контекст окна лупы
  3.         invoke  GetDlgItem,[hwnddlg],ID_ZOOM
  4.         mov     ebx,eax
  5.         invoke  GetDC,eax
  6.         mov     [wDC],eax
  7.  
  8.         ; Получить размеры окна лупы
  9.         invoke  GetClientRect,ebx,coord
  10.         mov     eax,[coord.right]
  11.         sub     eax,[coord.left]
  12.         mov     [dWidth],eax
  13.         mov     eax,[coord.bottom]
  14.         sub     eax,[coord.top]
  15.         mov     [dHeight],eax
  16.  
  17.         ; Получить контекст десктопа
  18.         invoke  GetDesktopWindow
  19.         mov     [hDesktop],eax
  20.         invoke  GetDC,eax
  21.         mov     [dDC],eax
  22.         ...
Как теперь запустить экранную лупу? Здесь есть два варианта: при получении сообщения от мыши и по таймеру. У каждого способа свои достоинства и недостатки. Если обрабатывать сообщения мыши, то требуется инжект вспомогательной dll во все процессы, а экран лупы будет обновляться только при движении мыши. Это существенно снизит нагрузку на систему, но если окно под курсором будет обновляться, а курсор останется неподвижным, то лупа получит только тот кадр, который был при последнем движении курсора. Обновление по таймеру создаст дополнительную нагрузку на систему, но при этом изображение в лупе будет в точности соответствовать текущему состоянию окна под курсором. Оба способа имеют место быть, каждый под свои задачи.

Еще один важный момент. Координаты курсора соответствуют левому верхнему углу копируемой части экрана. Но для большего эффекта надо копировать фрагмент вокруг курсора, так что потребуется небольшая корректировка координат. Итак, код основной процедуры экранной лупы:
  1.         ; Получить позицию курсора
  2.         invoke  GetCursorPos,curs
  3.  
  4.         ; Расчитать координаты и размер копируемой
  5.         ; области рабочего стола
  6.         mov     edx,[dWidth]    ; Ширина / 4
  7.         shr     edx,2           ; EDX - ширина копируемой области
  8.         mov     eax,edx
  9.         shr     eax,1
  10.         sub     [curs.x],eax    ; Координата Х левого угла
  11.  
  12.         mov     ecx,[dHeight]   ; Высота / 4
  13.         shr     ecx,2           ; ECX - высота копируемой области
  14.         mov     eax,ecx
  15.         shr     eax,1
  16.         sub     [curs.y],eax    ; Координата Y левого угла
  17.  
  18.         ; Флаг в FASM не определен
  19.         CAPTUREBLT = 0x40000000
  20.  
  21.         ; Скопировать участок экрана с увеличением в окно лупы
  22.         invoke  StretchBlt, [wDC], 0, 0, [dWidth], [dHeight],\
  23.                             [dDC], [curs.x], [curs.y], edx, ecx,\
  24.                             CAPTUREBLT+MERGECOPY
В этом примере для удобства используется коэффициент увеличения равный 4, вы можете использовать другой коэффициент или менять его динамически по мере необходимости. Флаг CAPTUREBLT используется в случае, если требуется захватывать полупрозрачные окна. Но это может вызвать неприятный эффект мерцания курсора, причем программно это мерцание никак не подавить. Без флага CAPTUREBLT лупа будет просто игнорировать layered-окна, показывая информацию под ними.

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

Пример программы с исходным текстом (FASM)Пример программы с исходным текстом (FASM)

Magnifying.Glass.Demo.zip (2,781 bytes)


Поделиться ссылкой ВКонтакте
Просмотров: 6136 | Комментариев: 6

Метки: Assembler, окна
Внимание! Статья опубликована больше года назад, информация могла устареть!

Комментарии

Отзывы посетителей сайта о статье
ND (06.10.2015 в 11:02):
То ли я гуглем не умею пользоваться, то ли запросы у меня специфические, но в качестве незамороченной экранной лупы - лучший вариант.
Павел (28.05.2013 в 10:36):
а если надо все время увеличивать фиксированноую область экрана?
brute (09.01.2012 в 00:00):
понял, я не использовал 'STATIC', а сразу выводил в диалоговое окно..
ManHunter (08.01.2012 в 22:22):
GetDlgItem,[hwnddlg],ID_ZOOM возвращает хэндл окна лупы, а не всего окна приложения. Для прозрачных окон ставишь флаги CAPTUREBLT + MERGECOPY + BLACKNESS, но это очень сильно влияет на производительность.
brute (08.01.2012 в 22:15):
повторить на PB удалось, всего 30 строк:http://webfile.ru/5760396
Остались вопросы:
1. зачем  invoke  GetDlgItem,[hwnddlg],ID_ZOOM, если уже есть hwnddlg?
2. прозрачные окна не ловятся; например, NetMeter,YzDock
3. можно ли htimer=settimer_(hwnddlg,0,50,@ZoomProc()) и затем killtimer_(hwnddlg,htimer)?
Grey (05.01.2012 в 06:04):
В общем хотел поблагодарить Вас, я ранее пытался делать, что-то подобное, но другими средствами (GDI+). Так как WinAPI не сказать, чтобы часто пользуюсь, про такую функцию, чесно не знал. :) Вот реализация этой идеи на АХК:

#NoEnv
CoordMode, Mouse
Width:=159, Value:=15
Gui, +AlwaysOnTop -Caption +Resize +ToolWindow HwndGUIhWnd
Gui, Show, % "w"Width " h"Width, Magnifier
OnMessage(0x201, "WM_NCLBUTTONDOWN") ; WM_LBUTTONDOWN
hDCgui:=DllCall("GetDC", "UInt", GUIhWnd)
hDCdesk:=DllCall("GetDC", "UInt", DllCall("GetDesktopWindow"))
SetTimer, ChaseCursor, 100

^MButton::
^WheelUp::
^WheelDown::
   If SubStr(A_ThisHotkey, 2)="MButton"
   {
      Value:=15, Width:=159
      WinMove, % "ahk_id"GUIhWnd,,,, Width, Width
   }
   Else If SubStr(A_ThisHotkey, 2)="WheelDown"
      Value-=4, Value<0 ? Value:=1:Value
   Else Value+=4
   Return

GuiSize:
   If % A_EventInfo=0
   {
      Width:=A_GuiWidth
      WinMove, % "ahk_id"GUIhWnd,,,, Width, Width
   }
   Return

ChaseCursor:
   MouseGetPos, XPos, YPos
   DllCall("gdi32\StretchBlt", "UInt", hDCgui
                             , "Int", 0
                             , "Int", 0
                             , "Int", Width
                             , "Int", Width
                             , "UInt", hDCdesk
                             , "Int", XPos-(Value/2)+1
                             , "Int", YPos-(Value/2)+1
                             , "Int", Value
                             , "Int", Value
                             , "UInt", 0x40C000CA) ; CAPTUREBLT|MERGECOPY
   Return

WM_NCLBUTTONDOWN()
{
   global
   PostMessage, 0xA1, 2,,, % "ahk_id"GUIhWnd ; WM_NCLBUTTONDOWN, HTCAPTION
}

Esc::
GuiClose:
   ExitApp

Добавить комментарий

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2024
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.07 сек. / MySQL: 2 (0.0072 сек.) / Память: 4.5 Mb
Наверх