Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

ScreenMagnifier


  • Please log in to reply
104 replies to this topic
holomind
  • Members
  • 341 posts
  • Last active: Aug 23 2015 03:27 PM
  • Joined: 11 Mar 2006
I played around with DC and DllCalls a little bit and built it into a Screen Magnifier similar to the one built in at Windows/Accessories.

The advantage of this vs. the builtin.

* you have the sourcecode and can customize it
* you can enable antializing (the built in has none!)
* you can set the speed of repaint (delay in ms with the slider, windows default is about 1000ms)
* you can define different zoomlevels, even unmagnify (shrink)

Due to a trick and using transparent 254 the window appears solid, but is invisible for BitBlt (DC_Copy), so the window does not copy itself and you even can magnify regions behind the loupe- window!

Like in the Builtin Magnifier, the mouseposition is watched and used for the selected region.

you also can resize the zoom window and the content is adopted automatically. (no distort, but large area)

I can even manage to zoom video (with 1-10ms delay) and watch it.
sometimes id like to zoom little flash-videos on websites without having to change my screen-resolution.

so now if somebody could find out how to save the contents of the DC to a file (BMP or other, DIB? Binarysave..) one could make nice tools with it.
=> solved see save screenshot with dgiplus only

there are many "mutations/variations" of this script, so choose the one you like. perhaps if find time to make a little "gallery" of the different versions and their differences. (there is no one size fits all, and the nice thing of having the sourcecode anybody can improve it)

this is the second code-section.
http://www.holomind....fier_smooth.exe

OnExit handle_exit

  Gui,  +AlwaysOnTop  +Owner +Resize +ToolWindow ; window for the dock
  Gui, Show, NoActivate w400 h400 x300 y50 , PrintScreen
  Gui, Add, DDL, vzoom   , 0.5|1|2||4|8|16 
  Gui, Add, Checkbox, y12 x150 vantialize, Antialize ?
  Gui, Add, Slider, vdelay x220 y0  Range15-200
  Gui, Add, Text, x340 y12 w80  vdelay2

  WinGet PrintScreenID, id  ,PrintScreen  ; 
  WinSet, Transparent , 254, PrintScreen

  ;retrieve the unique ID number (HWND/handle) of that window
  WinGet, PrintSourceID, id 

  hotkey , #x           , toggle_follow
  hotkey , +$LButton    , click_through

  toolbar_def:=35
  toolbar := toolbar_def
  follow :=0

  hdd_frame := DllCall( "GetDC", UInt, PrintSourceID )
  hdc_frame := DllCall( "GetDC", UInt, PrintScreenID )

  hdc_buffer := DllCall("gdi32.dll\CreateCompatibleDC", UInt,  hdc_frame)  ; buffer
  hbm_buffer := DllCall("gdi32.dll\CreateCompatibleBitmap", UInt,hdc_frame, Int,A_ScreenWidth, Int,A_ScreenHeight)
  
  Gosub, Repaint
return 

toggle_follow: 
    follow := 1 - follow
  
    if follow = 1 
    {
        WinSet Region, 0-0  W%ww% H%wh% E  , PrintScreen
        toolbar := -32 ; height of window title
        GuiControl, Hide, zoom
    }
    else
    {
        WinSet Region,, PrintScreen
        toolbar :=toolbar_def
        GuiControl, Show, zoom
    }
Return

click_through:
    if follow = 1
    {
      Gui, Hide
      Send, {Click}
      SetTimer, Repaint , Off
      Sleep, 100
      Gui, Show
      SetTimer, Repaint, %delay%
    }
Return

Repaint: 

   CoordMode, Mouse, Screen                
   MouseGetPos, start_x, start_y             ;  position of mouse
   Gui, Submit, NoHide                       ; needed to read the dropdown and slidervalue
   GuiControl,, delay2 , delay %delay% ms
   WinGetPos, wx, wy, ww, wh , PrintScreen

   wh2 := wh - toolbar

    DllCall( "gdi32.dll\SetStretchBltMode", "uint", hdc_frame, "int", 4 * antialize )  ; Halftone better quality with stretch
   
    DllCall("gdi32.dll\StretchBlt", UInt,hdc_frame, Int,0, Int,toolbar, Int,ww, Int,wh - toolbar
          , UInt,hdd_frame, Int
          , start_x-(ww / 2 / zoom)
          , Int,start_y -( wh2 / 2/zoom), Int,ww / zoom, Int,wh2 / zoom ,UInt,0xCC0020) ; SRCCOPY

   if follow = 1
     WinMove, PrintScreen, ,start_x -ww/2 , start_y-wh/2 
   
  SetTimer, Repaint , %delay% 
Return

GuiClose:
handle_exit:
   DllCall("gdi32.dll\DeleteObject", UInt,hbm_buffer)
   DllCall("gdi32.dll\DeleteDC", UInt,hdc_frame )
   DllCall("gdi32.dll\DeleteDC", UInt,hdd_frame )
   DllCall("gdi32.dll\DeleteDC", UInt,hdc_buffer)
ExitApp 

or with less gui, my current favorite (less frames is smoother)
#NoEnv
SetBatchLines -1

CoordMode Mouse, Screen
OnExit GuiClose
zoom = 2                ; initial magnification, 1..32
antialize = 0
Rx = 128                ; half vertical/horizontal side of magnifier window
Ry = 128
Zx := Rx/zoom           ; frame x/y size
Zy := Ry/zoom
                        ; GUI to show the magnified image
Gui +AlwaysOnTop +Resize +ToolWindow
Gui Show, % "w" 2*Rx " h" 2*Ry " x0 y0", Magnifier
WinGet MagnifierID, id,  Magnifier
WinSet Transparent, 255, Magnifier ; makes the window invisible to magnification
WinGet PrintSourceID, ID

hdd_frame := DllCall("GetDC", UInt, PrintSourceID)
hdc_frame := DllCall("GetDC", UInt, MagnifierID)

SetTimer Repaint, 50    ; flow through

Repaint:
   MouseGetPos x, y
   xz := In(x-Zx-6,0,A_ScreenWidth-2*Zx) ; keep the frame on screen
   yz := In(y-Zy-6,0,A_ScreenHeight-2*Zy)
  ; WinMove Frame,,%xz%, %yz%, % 2*Zx, % 2*Zy
   DllCall("gdi32.dll\StretchBlt", UInt,hdc_frame, Int,0, Int,0, Int,2*Rx, Int,2*Ry
   , UInt,hdd_frame, UInt,xz, UInt,yz, Int,2*Zx, Int,2*Zy, UInt,0xCC0020) ; SRCCOPY
Return


GuiSize:
   Rx := A_GuiWidth/2
   Ry := A_GuiHeight/2
   Zx := Rx/zoom
   Zy := Ry/zoom
   TrayTip,,% "Frame  =  " Round(2*Zx) " × " Round(2*Zy) "`nMagnified to = " A_GuiWidth "×" A_GuiHeight
Return

#a::
  antialize := !antialize
  DllCall( "gdi32.dll\SetStretchBltMode", "uint", hdc_frame, "int", 4*antialize )  ; Antializing ?
Return 

#x::
GuiClose:
   DllCall("gdi32.dll\DeleteDC", UInt,hdc_frame )
   DllCall("gdi32.dll\DeleteDC", UInt,hdd_frame )
ExitApp

#p::
MButton::
   if paused = 
   {
        Gui, 2:Hide 
        Gui, Hide 
        SetTimer, Repaint, Off
        paused = 1
   }
   else
   {
        Gui, 2:Show 
        Gui, Show 
        SetTimer, Repaint, 50
        paused =
   }
Return

^+Up::
^+Down::
^+WheelUp::                      ; Ctrl+Shift+WheelUp to zoom in
^+WheelDown::                    ; Ctrl+Shift+WheelUp to zoom out
   If (zoom < 31 and ( A_ThisHotKey = "^+WheelUp" or A_ThisHotKey = "^+Up" ))
      zoom *= 1.189207115         ; sqrt(sqrt(2))
   If (zoom >  1 and ( A_ThisHotKey = "^+WheelDown" or A_ThisHotKey = "^+Down" ))
      zoom /= 1.189207115
   Zx := Rx/zoom
   Zy := Ry/zoom
   TrayTip,,% "Zoom = " Round(100*zoom) "%"
Return

In(x,a,b) {                      ; closest number to x in [a,b]
   IfLess x,%a%, Return a
   IfLess b,%x%, Return b
   Return x
}


d-man
  • Members
  • 290 posts
  • Last active: Jun 28 2015 09:26 AM
  • Joined: 08 Jun 2006
That is sweet , good job !

polyethene
  • Members
  • 5519 posts
  • Last active: May 17 2015 06:39 AM
  • Joined: 26 Oct 2012
Hell yeah, thanks.

autohotkey.com/net Site Manager

 

Contact me by email (polyethene at autohotkey.net) or message tidbit


holomind
  • Members
  • 341 posts
  • Last active: Aug 23 2015 03:27 PM
  • Joined: 11 Mar 2006
Thanks, i hope you like it.

Some things i noticed myself.

* 40ms delay is 1sec / 25, means 25Frames / sec, which should be enough for video playback. (so perhaps instead of ms show framerate ?)
* having a flash movie in background does not cause flicker when the zoom window is in foreground (having video with hardware overlay flickers )
* even quicktime and wmv can be zoomed. dont know why but it works. often its difficult to work with video as it gets blacked out.
* the antialize sometimes does not *kick in* when there is only text on the zoomed part. if there is a graphic or a button or windowborder antialize is normally on. (m$'s fault)
* as this is scriptable, one could make zoom my tray-icons on hover ? the mouse then is on the perfect correct position already!
* the zoomwindow could easily follow the mouse (winmove zoomwindow x%startx%-offset ...) and simulate a real "loupe" ;)
* clickthrough could be good but idealy should not disable the gui-controls for slider and dropdown, also moving the window on the windowtitle.
so something like if coordmode mouse relative => getmousepos is inside region x0 y%toolbar% winw winH. then let mouseclicks through
ala $LButton::
;hide loupe
Send {Click}
;show loupe
Return
* the impact of antialize is quite recognizable, also the strech/zooming.
* simply copy with 1 zoom no antialize has very little speed impact.

M. Phelps
  • Guests
  • Last active:
  • Joined: --
Your mission, holomind, if you accept it, is to
- Dock the ScreenMagnifier window in the top of the screen, without title bar, so when a window is created it can't overlap the magnifier's one (like in the original one)
- follow the text entered in dialog boxes, test fields, etc...
This SD Card will be destroyed in 10 seconds, and if you or one of your partners is dicovered, we'll say that we don't know anyone of you.

holomind
  • Members
  • 341 posts
  • Last active: Aug 23 2015 03:27 PM
  • Joined: 11 Mar 2006
I added some funstuff, now you can simulate a real Loupe with win-x (on/off) where the window gets into a circle and follows your mouse.
you even can clickthrough with shift-leftMouseButton.

Actually its not so usefull (usability) but its fun.

holomind
  • Members
  • 341 posts
  • Last active: Aug 23 2015 03:27 PM
  • Joined: 11 Mar 2006

- Dock the ScreenMagnifier window in the top of the screen, without title bar, so when a window is created it can't overlap the magnifier's one (like in the original one)


this could be done with moving the window a little offscreen so the titlebar is invisible. or clip it away with winset region... making it owner/always ontop, no other program should go in foreground.
do you really want the effect that the whole desktop shifts down when you start the loupe ? you already can see the regions behind the zoomed window!

another solution would be to make the zoom window automatically move out of the way. means you move your mouse inside the zoomwindow and then it jumps to the other side of the screen (y or x axis) and you see what you want.

- follow the text entered in dialog boxes, test fields, etc...

hm, i dont understand this exactly. do you want that the region around the cursor / selection is zoomed ? hmm perhaps with a click you could "freeze/unfreeze" the following of the mouse (manual selection of region of interest) ? or using drag-select to define the region of interest ?

polyethene
  • Members
  • 5519 posts
  • Last active: May 17 2015 06:39 AM
  • Joined: 26 Oct 2012
Would it be possible to save the current image (as bitmap)?

autohotkey.com/net Site Manager

 

Contact me by email (polyethene at autohotkey.net) or message tidbit


holomind
  • Members
  • 341 posts
  • Last active: Aug 23 2015 03:27 PM
  • Joined: 11 Mar 2006

Would it be possible to save the current image (as bitmap)?


if you know how to copy the contents of the DC (DeviceContext) to a file, then yes. but i dont know an easy solution.

if somebody manages to convert this VB-Code into AHK it would work.
http://www.codeguru....icle.php/c1741/

isnt there a thread in the forum to run VB-Code inside of AHK ? Or is it only VB-Script ?
http://www.autohotke...opic.php?t=5379
Can this be used to run VB-Code ? Is there a big difference between VB-Code and VB-Script ?

e.g VB-Code
memDC.CreateCompatibleDC(&dc);

translates int AHK-Code (hdc_frame == &dc ? memDC == hdc_buffer ?)
hdc_buffer := DllCall("gdi32.dll\CreateCompatibleDC", UInt,  hdc_frame)  


holomind
  • Members
  • 341 posts
  • Last active: Aug 23 2015 03:27 PM
  • Joined: 11 Mar 2006
ist there a way to use CImage::Save
http://msdn2.microso...y/d06f3fhw.aspx

as a DLLCall (with help from gdi32.dll?) or similar..

this seams quite straightforward

// Demonstrating saving various file formats
int _tmain(int argc, _TCHAR* argv[])
{
   CImage myimage;
   // load existing image
   myimage.Load("image.bmp"); 
      // save an image in BMP format
   myimage.Save("c:\image1.bmp");
   // save an image in BMP format
   myimage.Save("c:\image2",ImageFormatBMP);
   // save an image in JPEG format
   myimage.Save("c:\image3.jpg");
   // save an image in BMP format, even though jpg file extension is used
   myimage.Save("c:\image4.jpg",ImageFormatBMP);
   return 0;
}

so if one manages to create a CImage "object", and copies the DC with SelectObject or BitBlt into the myimage, then perhaps its possible to save DC to BMP with AHK ?

this looks similar to :
http://www.autohotke...light=dib#46244
so perhaps kiu might help us ?

Blablabla
  • Guests
  • Last active:
  • Joined: --
My Dear Friend holomind: :p
Wow! What a holomind! :lol:

Here's my mod base on your code (and I prefer mine :wink: ):
Note:
Just move your mouse around!
Alt+Shift+WheelUp/Down to zoom in/out.
Win+X to exit.
;original by: 	holomind
;see more: 		http://www.autohotkey.com/forum/topic11700.html 
 
  #NoTrayIcon
  OnExit handle_exit 
  CoordMode, Mouse, Screen
  zoom = 2
  Gui,  +AlwaysOnTop +Resize +ToolWindow -SysMenu +E0x00000020
  MouseGetPos, start_x, start_y
  start_x -= 200
  start_y -= 200
  Gui, Show, w400 h400 x%start_x% y%start_y% , Magnifier 
  SetTimer, MoveWin, 0
  WinGet MagnifierID, id  ,Magnifier
  WinSet, Transparent , 254, Magnifier 
  Gui, -Caption
  ;retrieve the unique ID number (HWND/handle) of that window 
  WinGet, PrintSourceID, id 
 
  hdd_frame := DllCall( "GetDC", UInt, PrintSourceID ) 
  hdc_frame := DllCall( "GetDC", UInt, MagnifierID ) 

  hdc_buffer := DllCall("gdi32.dll\CreateCompatibleDC", UInt,  hdc_frame)  ; buffer 
  hbm_buffer := DllCall("gdi32.dll\CreateCompatibleBitmap", UInt,hdc_frame, Int,A_ScreenWidth, Int,A_ScreenHeight) 
  
  Gosub, Repaint 
return 

Repaint:                 
   MouseGetPos, start_x, start_y             ;  position of mouse  
   WinGetPos, wx, wy, ww, wh , Magnifier 
   wh2 := wh

   DllCall( "gdi32.dll\SetStretchBltMode", "uint", hdc_frame, "int", 0 )  ; No Antializing 
  
   DllCall("gdi32.dll\StretchBlt", UInt,hdc_frame, Int,0, Int,0, Int,ww, Int,wh 
          , UInt,hdd_frame, Int, start_x-(ww / 2 / zoom) 
          , Int,start_y -( wh2 / 2/zoom), Int,ww / zoom, Int,wh2 / zoom ,UInt,0xCC0020) ; SRCCOPY 
  
   SetTimer, Repaint , 0 
Return 

MoveWin:
MouseGetPos, now_x, now_y
now_x -= 200
now_y -= 200
WinMove, Magnifier, , %now_x%, %now_y%
Return

GuiClose:
handle_exit: 
#x::
   DllCall("gdi32.dll\DeleteObject", UInt,hbm_buffer) 
   DllCall("gdi32.dll\DeleteDC", UInt,hdc_frame ) 
   DllCall("gdi32.dll\DeleteDC", UInt,hdd_frame ) 
   DllCall("gdi32.dll\DeleteDC", UInt,hdc_buffer) 
ExitApp 

!+WheelUp::			; Alt+Shift+WheelUp to zoom in
  If zoom != 16
      zoom *= 2
Return

!+WheelDown::		; Alt+Shift+WheelUp to zoom out
  If zoom != 2
      zoom /= 2
Return

I am confused now:
1) Why the main window keeps trembling when user moving it?
2) Is there a way to zoom the mouse cursor?

Thanks!

holomind
  • Members
  • 341 posts
  • Last active: Aug 23 2015 03:27 PM
  • Joined: 11 Mar 2006

My Dear Friend holomind: :p
Wow! What a holomind! :lol:
...
Here's my mod base on your code (and I prefer mine :wink: ):
I am confused now:
1) Why the main window keeps trembling when user moving it?
2) Is there a way to zoom the mouse cursor?

Thanks!


Nice to see, somebody uses it ;)

the clickthrough effect is nice, is it done with " +E0x00000020" ?
so the gui never gets activated ?

i have problems using hotkey with Mousewheel on a laptop-touchpad but the idea is good. perhaps i make it fallback to something like alt-shift-arrow-up arrow-down for zoom.

1) the winmove is to slow, so it has a delay i guess. hmm perhaps hide the window until winmove is finished ? another perhaps better way would be to
have the zoom window fullscreen (0,0,A_ScreenHeight, A_ScreenWidth) , and then WinSet Region... Clip the visible part and also use CallDLL Stetchblt to paint in this region.
then no winmove is neededn and it should react much more smooth.
i also would not use delay 0 "SetTimer, Repaint , 0 " , as this uses 100% cpu, and you cant see that fast. 15 or 40 ms (25 or 60 Frames/s) should be enough ?

2) i dont think so , as the mouse is not part of the DC and drawn by hardware directly but you could change the whole mousepointer, and use a bigger one. or a crosshair for example. (dont know how to ahk-script this)

holomind
  • Members
  • 341 posts
  • Last active: Aug 23 2015 03:27 PM
  • Joined: 11 Mar 2006
Hi, i experimented a little bit and solved the flicker problem in a way:

the solution is not beatiful, but works.
now the Desktop (original) is copied to the Magnify image to restore the zoomed areas with the original. to make things faster and not repaint the whole screen all the time it only restores the zoomwindow.
so every second there is a full screen refresh to restore errors. also your desktop could have changed.

i sneaked in some hotkeys. so zoom also works with alt-shift-up down, not only mousewheel, and i can toggle antialize with alt-shilft a.

the bmp buffers are not needed here, they come from another script i copied but here the DCs are copied diretly!

I experimented with "Winset Region" and "Gui Show ..size" and "Winmove" but they all flicker. so this kind of doublebuffer works better.

it would be much better, to only change the size of the Magnifier window, but this flickers

i changed the dimensions to variables so you can change the radius easily.

when you comment "SetTimer, Init" and uncomment "SetTimer, ClipWindow", then you can see how slow the "WinSet Region" is.

i guess the "winset region" forces a full refresh/redraw of the Window!

;original by:    holomind
;modified by: Blablabla
;modified again by: holomind ;)
;see more:       http://www.autohotkey.com/forum/topic11700.html
 
  #NoTrayIcon
  OnExit handle_exit
  SetBatchLines, -1

  CoordMode, Mouse, Screen
  zoom      = 2
  antialize = 0
  refresh_delay = 40
  refresh_full  = 1000
  radius = 400
  m_w  := radius  ; magnifiere_width
  m_h  := radius
  m_wh := m_w / 2 ; half
  m_hh := m_h / 2 

  Gui,  +AlwaysOnTop -Resize -Caption -Border +ToolWindow -SysMenu +E0x00000020
  Gui,Show, x0 y0 w%A_ScreenWidth% h%A_ScreenHeight% , Magnifier 

  WinGet MagnifierID, id  ,Magnifier
  WinSet, Transparent , 254, Magnifier

  ;retrieve the unique ID number (HWND/handle) of that window
  WinGet, SourceWinID, id
 
  hdc_source_frame := DllCall( "GetDC", UInt, SourceWinID )
  hdc_target_frame := DllCall( "GetDC", UInt, MagnifierID )
  
  SetTimer, Init    , %refresh_full% ; force full refresh 1 each second
  SetTimer, Repaint , %refresh_delay%
;  SetTimer, ClipWindow, 500
  Gosub, Init
  Gosub, Repaint
return

Init: 
    DllCall("gdi32.dll\BitBlt", UInt,hdc_target_frame
          , Int     , 0
          , Int     , 0
          , Int     , A_ScreenWidth
          , Int     , A_ScreenHeight
          , UInt    , hdc_source_frame
          , Int     , 0
          , Int     , 0
          , UInt    , 0xCC0020) ; SRCCOPY

Return

Repaint: 

   MouseGetPos, start_x, start_y             ;  position of mouse 
   now_x := start_x - m_wh
   now_y := start_y - m_hh
  
   if  now_x <> old_x       ; only repaint if mouse is moved ?
   {
    ; redraw original desktop
    DllCall("gdi32.dll\BitBlt", UInt,hdc_target_frame
          , UInt     , old_x 
          , UInt     , old_y 
          , Int     , m_w 
          , Int     , m_h 
          , UInt    , hdc_source_frame
          , UInt     , old_x 
          , UInt     , old_y 
          , UInt    , 0xCC0020) ; SRCCOPY
   }

   DllCall("gdi32.dll\StretchBlt", UInt,hdc_target_frame
          , Int     , now_x
          , Int     , now_y
          , Int     , m_w
          , Int     , m_h
          , UInt    , hdc_source_frame
          , Int     , start_x - m_wh / zoom
          , Int     , start_y - m_hh / zoom
          , Int     , m_w / zoom
          , Int     , m_h / zoom 
          , UInt    , 0xCC0020) ; SRCCOPY
   

   old_x := now_x
   old_y := now_y

Return

ClipWindow:
   WinSet, Region, %now_x%-%now_y% W%m_w% H%m_h%  , Magnifier
Return

GuiClose:
handle_exit:
#x::
   DllCall("gdi32.dll\DeleteDC", UInt,hdc_target_frame )
   DllCall("gdi32.dll\DeleteDC", UInt,hdc_source_frame )
ExitApp

!+a::
  antialize := 4 - antialize
  DllCall( "gdi32.dll\SetStretchBltMode", "uint", hdc_target_frame, "int", antialize )  ; Antializing ?
Return

!+WheelUp::         ; Alt+Shift+WheelUp to zoom in
!+Up::
If zoom != 16
      zoom *= 2
Return

!+WheelDown::      ; Alt+Shift+WheelUp to zoom out
!+Down::
  If zoom != 2
      zoom /= 2
Return 


Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
These are very nice scripts! I did not think that a fully functional magnifier can be coded with 50 AHK commands!

I also played a little with both flavors, and found the original fixed window for the magnified image easier to use. Navigation is simpler if a frame is drawn around the magnified area. Also, if it could get partially out of the screen, ugly side effects occur, so we'd better keep this frame fully on screen. Interestingly, semi transparent windows remain invisible, even if the transparency level is set to full opaque (255).

I made a few more trivial changes, like finer control of the magnification, showing the current zoom factor in a TrayTip for a short time after a change and putting the window size in a variable ® for easy change.
#NoEnv
SetBatchLines -1

CoordMode Mouse, Screen
OnExit GuiClose
zoom = 2                ; initial magnification, 1..32
Rx = 128                ; half vertical/horizontal side of magnifier window
Ry = 128
Zx := Rx/zoom           ; frame x/y size
Zy := Ry/zoom
                        ; GUI to show the magnified image
Gui +AlwaysOnTop +Resize +ToolWindow
Gui Show, % "w" 2*Rx " h" 2*Ry " x0 y0", Magnifier
WinGet MagnifierID, id,  Magnifier
WinSet Transparent, 255, Magnifier ; makes the window invisible to magnification
WinGet PrintSourceID, ID
                        ; Frame around magnified area
Gui 2:+AlwaysOnTop -Caption +ToolWindow +0x800000
Gui 2:Color, C
MouseGetPos x, y
Gui 2:Show, % "w" 2*Zx " h" 2*Zy " x" x-Zx " y" y-Zy, Frame
WinSet TransColor, C, Frame

hdd_frame := DllCall("GetDC", UInt, PrintSourceID)
hdc_frame := DllCall("GetDC", UInt, MagnifierID)

SetTimer Repaint, 50    ; flow through

Repaint:
   MouseGetPos x, y
   xz := In(x-Zx-6,0,A_ScreenWidth-2*Zx) ; keep the frame on screen
   yz := In(y-Zy-6,0,A_ScreenHeight-2*Zy)
   WinMove Frame,,%xz%, %yz%, % 2*Zx, % 2*Zy
   DllCall("gdi32.dll\StretchBlt", UInt,hdc_frame, Int,0, Int,0, Int,2*Rx, Int,2*Ry
   , UInt,hdd_frame, UInt,xz, UInt,yz, Int,2*Zx, Int,2*Zy, UInt,0xCC0020) ; SRCCOPY
Return

GuiSize:
   Rx := A_GuiWidth/2
   Ry := A_GuiHeight/2
   Zx := Rx/zoom
   Zy := Ry/zoom
   TrayTip,,% "Frame  =  " Round(2*Zx) " × " Round(2*Zy) "`nMagnified to = " A_GuiWidth "×" A_GuiHeight
Return

GuiClose:
   DllCall("gdi32.dll\DeleteDC", UInt,hdc_frame )
   DllCall("gdi32.dll\DeleteDC", UInt,hdd_frame )
ExitApp

^+WheelUp::                      ; Ctrl+Shift+WheelUp to zoom in
^+WheelDown::                    ; Ctrl+Shift+WheelUp to zoom out
   If (zoom < 31 and A_ThisHotKey = "^+WheelUp")
      zoom *= 1.189207115         ; sqrt(sqrt(2))
   If (zoom >  1 and A_ThisHotKey = "^+WheelDown")
      zoom /= 1.189207115
   Zx := Rx/zoom
   Zy := Ry/zoom
   TrayTip,,% "Zoom = " Round(100*zoom) "%"
Return

In(x,a,b) {                      ; closest number to x in [a,b]
   IfLess x,%a%, Return a
   IfLess b,%x%, Return b
   Return x
}


holomind
  • Members
  • 341 posts
  • Last active: Aug 23 2015 03:27 PM
  • Joined: 11 Mar 2006

These are very nice scripts! I did not think that a fully functional magnifier can be coded with 50 AHK commands!


to make it even shorter, i recognized the buffers are not used.
so you can remove the codelines with hdc_buffer and hdm_buffer.
due to +E0x20, the magnifier is stuck in the top-left corner. perhaps it could be made "move out of way align top-right ?" when the mouse enters the magnifier region? and snap back when the mouse is in ?
or have a hotkey to move it topleft or top-right ?