Jump to content

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

Draw Functions


  • Please log in to reply
12 replies to this topic
iPhilip
  • Members
  • 133 posts
  • Last active: May 06 2014 06:45 PM
  • Joined: 03 Jun 2010
Folks,

Here is my contribution to those of you who like to plot dots, lines, or boxes on the screen for fun or for work. It's a set of functions that can be included in your script. Here they are:

;
; DrawFunctions.ahk
;
; by iPhilip
; November 15, 2011
;
; Inspired by the work of Shimanov, Metaxal, maximo3491, Relayer
; Reference: http://www.autohotkey.com/forum/topic41756.html
;
; Defines a set of functions to draw color dots, lines, and boxes on the screen
;
; init_draw(c=1)
;      Moves the mouse off the screen and blocks any further input.
;      Initializes buffers and paints the canvas with color index c (default = 1 = black).
;      See below for the index/color map. A value of 0 makes the canvas appear transparent.
;
; exit_draw()
;      Launches when the left mouse button is clicked, restoring the mouse to its original coordinates
;      and exiting the application.
;
; paint_canvas(c=1)
;      Paints the canvas with color index c (default = 1 = black). See below for the index/color map.
;      A value of 0 makes the canvas appear to be transparent. It's actually a screenshot of the background.
;      The canvas gets erased each time this function is called.
;
; draw_dot(x, y, r=1, c=16)
;      Draws a dot of radius r pixels and color index c at (x,y). The default radius is 1.
;      The default color index is 16 (white). See below for the index/color map.
;
; draw_line(x0, y0, x1, y1, w=1, c=16)
;      Draws a line of width 2w pixels and color index c from (x0,y0) to (x1,y1). The line has rounded ends.
;      The default width is 1. The default color index is 16 (white). See below for the index/color map.
;
; draw_box(x0, y0, x1, y1, c=16)
;      Draws a box defined by the upper-left coordinates (x0,y0) and the bottom-right coordinates (x1,y1)
;      with color index c. The default color index is 16 (white). See below for the index/color map.
;
; Gloval variables: origX, origY, hdc_buffer1, hdc_buffer2, hdc_canvas, color[0-16]
;
;---------------------------------------------------------------------------------------------------------

init_draw(c=1) {
   global origX, origY, hdc_buffer1, hdc_buffer2, hdc_canvas, color0

   ; Move the mouse off the screen and block any further input
   CoordMode, Mouse, Screen
   MouseGetPos origX, origY
   MouseMove A_ScreenWidth, A_ScreenHeight, 0
   BlockInput MouseMove

   ; Create a working buffer
   hdc_screen  := DllCall("GetDC", "uint", 0)
   hdc_buffer1 := DllCall("CreateCompatibleDC", "uint", hdc_screen)
   hbm_buffer1 := DllCall("CreateCompatibleBitmap", "uint", hdc_screen, "int", A_ScreenWidth, "int", A_ScreenHeight)
   DllCall("SelectObject", "uint", hdc_buffer1, "uint", hbm_buffer1)

   ; Create a secondary buffer
   hdc_buffer2 := DllCall("CreateCompatibleDC", "uint", hdc_screen)
   hbm_buffer2 := DllCall("CreateCompatibleBitmap", "uint", hdc_screen, "int", A_ScreenWidth, "int", A_ScreenHeight)
   DllCall("SelectObject", "uint", hdc_buffer2, "uint", hbm_buffer2)

   ; Initialize the buffers with a screenshot so that the canvas appears transparent
   DllCall("BitBlt", "uint", hdc_buffer1, "int", 0, "int", 0, "int", A_ScreenWidth, "int", A_ScreenHeight, "uint", hdc_screen, "int", 0, "int", 0, "uint", 0x00CC0020)
   DllCall("BitBlt", "uint", hdc_buffer2, "int", 0, "int", 0, "int", A_ScreenWidth, "int", A_ScreenHeight, "uint", hdc_screen, "int", 0, "int", 0, "uint", 0x00CC0020)

   ; Create a window with the size of the display
   Gui, +AlwaysOnTop -Caption
   Gui, Show, x0 y0 w%A_ScreenWidth% h%A_ScreenHeight%

   ; Get the handle of the window associated with the canvas
   PID := DllCall("GetCurrentProcessId")
   WinGet, hw_canvas, ID, ahk_class AutoHotkeyGUI ahk_pid %PID%
   hdc_canvas := DllCall("GetDC", "uint", hw_canvas)

   ; Define color array
   ; References: http://en.wikipedia.org/wiki/Web_colors#HTML_color_names
   ;             http://www.autohotkey.com/forum/topic47746.html
   color_codes = 0x000000   ; 1.  Black
                ,0x000080   ; 2.  Navy
                ,0x008000   ; 3.  Green
                ,0x008080   ; 4.  Teal
                ,0x800000   ; 5.  Maroon
                ,0x800080   ; 6.  Purple
                ,0x808000   ; 7.  Olive
                ,0xC0C0C0   ; 8.  Silver
                ,0x808080   ; 9.  Gray
                ,0x0000FF   ; 10. Blue
                ,0x00FF00   ; 11. Lime
                ,0x00FFFF   ; 12. Aqua
                ,0xFF0000   ; 13. Red
                ,0xFF00FF   ; 14. Fuchsia
                ,0xFFFF00   ; 15. Yellow
                ,0xFFFFFF   ; 16. White
   StringSplit color, color_codes, `,
   Loop 16
      color%A_Index% := SubStr(color%A_Index%,1,2) SubStr(color%A_Index%,7,2) SubStr(color%A_Index%,5,2) SubStr(color%A_Index%,3,2)

   ; Paint the canvas with the initial color
   paint_canvas(c)

   ; Define the exit function that gets called when the mouse is clicked
   ; Reference: http://www.autohotkey.com/docs/commands/OnMessage.htm
   WM_LBUTTONDOWN = 0x201
   OnMessage(WM_LBUTTONDOWN, "exit_draw")
}

exit_draw() {
   global origX, origY
   BlockInput MouseMoveOff
   MouseMove origX, origY, 0
   ExitApp
}

paint_canvas(c=1) {   ; Default canvas color is black
   global hdc_buffer1, hdc_buffer2, hdc_canvas, color0
   if !c   ; 0 = transparent
   {
      DllCall("BitBlt", "uint", hdc_buffer2, "int", 0, "int", 0, "int", A_ScreenWidth, "int", A_ScreenHeight, "uint", hdc_buffer1, "int", 0, "int", 0, "uint", 0x00CC0020)
      DllCall("BitBlt", "uint", hdc_canvas , "int", 0, "int", 0, "int", A_ScreenWidth, "int", A_ScreenHeight, "uint", hdc_buffer2, "int", 0, "int", 0, "uint", 0x00CC0020)
      return
   }
   cBrush := DllCall("gdi32.dll\CreateSolidBrush", "uint", color%c%)
   DllCall("SelectObject", "uint", hdc_buffer2, "uint", cBrush)
   DllCall("BitBlt", "uint", hdc_buffer2, "int", 0, "int", 0, "int", A_ScreenWidth, "int", A_ScreenHeight, "uint", hdc_buffer1, "int", 0, "int", 0, "uint", 0x00F00021)
   DllCall("SelectObject", "uint", hdc_buffer2, "uint", 0)
   DllCall("gdi32.dll\DeleteObject", "uint", cBrush)
   DllCall("BitBlt", "uint", hdc_canvas , "int", 0, "int", 0, "int", A_ScreenWidth, "int", A_ScreenHeight, "uint", hdc_buffer2, "int", 0, "int", 0, "uint", 0x00CC0020)
}

draw_dot(x, y, r=1, c=16) {
   global hdc_buffer2, hdc_canvas, color0
   cBrush  := DllCall("gdi32.dll\CreateSolidBrush", "uint", color%c%)
   cRegion := DllCall("gdi32.dll\CreateRoundRectRgn", "int", x-r, "int", y-r, "int", x+r, "int", y+r, "int", r*2, "int", r*2)
   DllCall("gdi32.dll\FillRgn" , "uint", hdc_buffer2 , "uint", cRegion , "uint", cBrush)
   DllCall("gdi32.dll\DeleteObject", "uint", cRegion)
   DllCall("gdi32.dll\DeleteObject", "uint", cBrush)
   DllCall("BitBlt", "uint", hdc_canvas , "int", 0, "int", 0, "int", A_ScreenWidth, "int", A_ScreenHeight, "uint", hdc_buffer2, "int", 0, "int", 0, "uint", 0x00CC0020)
}

draw_line(x0, y0, x1, y1, w=1, c=16) {
   global hdc_buffer2, hdc_canvas, color0
   dx := x1 - x0
   dy := y1 - y0
   dxy := abs(dx) > abs(dy) ? abs(dx) : abs(dy)
   dx := dx / dxy
   dy := dy / dxy
   cBrush := DllCall("gdi32.dll\CreateSolidBrush", "uint", color%c%)
   Loop % round(dxy) + 1
   {
      cRegion := DllCall("gdi32.dll\CreateRoundRectRgn", "int", x0-w, "int", y0-w, "int", x0+w, "int", y0+w, "int", w*2, "int", w*2)
      DllCall("gdi32.dll\FillRgn" , "uint", hdc_buffer2 , "uint", cRegion , "uint", cBrush)
      DllCall("gdi32.dll\DeleteObject", "uint", cRegion)
      x0 += dx
      y0 += dy
   }
   DllCall("gdi32.dll\DeleteObject", "uint", cBrush)
   DllCall("BitBlt", "uint", hdc_canvas , "int", 0, "int", 0, "int", A_ScreenWidth, "int", A_ScreenHeight, "uint", hdc_buffer2, "int", 0, "int", 0, "uint", 0x00CC0020)
}

draw_box(x0, y0, x1, y1, c=16) {
   global hdc_buffer2, hdc_canvas, color0
   cBrush  := DllCall("gdi32.dll\CreateSolidBrush", "uint", color%c%)
   cRegion := DllCall("gdi32.dll\CreateRectRgn", "int", x0, "int", y0, "int", x1, "int", y1)
   DllCall("gdi32.dll\FillRgn" , "uint", hdc_buffer2 , "uint", cRegion , "uint", cBrush)
   DllCall("gdi32.dll\DeleteObject", "uint", cRegion)
   DllCall("gdi32.dll\DeleteObject", "uint", cBrush)
   DllCall("BitBlt", "uint", hdc_canvas , "int", 0, "int", 0, "int", A_ScreenWidth, "int", A_ScreenHeight, "uint", hdc_buffer2, "int", 0, "int", 0, "uint", 0x00CC0020)
}

Here is a set of examples that you can try with the above functions:

; start with a white (16) background
init_draw(16) 

; draw a black (1) circle of radius 100 pixels centered at x=250 y=250
draw_dot(250,250,100,1)
Sleep 1000

; draw thin red (13) lines around the circle
draw_line(150,150,350,150,1,13) ; top line
draw_line(350,150,350,350,1,13) ; right line
draw_line(350,350,150,350,1,13) ; bottom line
draw_line(150,350,150,150,1,13) ; left line
Sleep 1000

; draw a blue (10) line of thickness 200 (100*2) pixels from x=350 y=250 to x=750 y=250
draw_line(350,250,750,250,100,10)

; redraw the right line around the circle as it gets covered by the blue line
draw_line(350,150,350,350,1,13) ; right line
Sleep 1000

; draw a green (3) box of dimensions 400x200 in the middle of the screen
draw_box(A_ScreenWidth/2-200,A_ScreenHeight/2-100,A_ScreenWidth/2+200,A_ScreenHeight/2+100,3)
Sleep 1500

; repaint canvas with transparent background
paint_canvas(0)

; draw a red (13) circle of radius 250 centered in the middle of the screen
draw_dot(A_ScreenWidth/2,A_ScreenHeight/2,250,13)

; draw a white (default) circle of radius 50 on top of the red circle
draw_dot(A_ScreenWidth/2,A_ScreenHeight/2,50)
Sleep 1500

; repaint canvas with black (default) background
paint_canvas()

; draw 15 dots of different colors along a diagonal
; don't include a black dot since the background is black
Loop 15
{
   x0 := A_ScreenWidth *(A_Index/15-1/30)
   y0 := A_ScreenHeight*(A_Index/15-1/30)
   draw_dot(x0, y0, 10, A_Index+1)
}
Sleep 1500

; erase the canvas
paint_canvas()

; draw 15 lines of different colors along a diagonal
; don't include a black line since the background is black
Loop 15
{
   y0 := A_ScreenHeight*(A_Index/15-1/30)
   draw_line(0, y0, A_ScreenWidth, A_ScreenHeight, 1, A_Index+1)
}
Return

; Use Esc to exit
Esc::exit_draw()

#Include DrawFunctions.ahk

Let me know if you find these useful or if you have any suggestions.

- iPhilip

tidbit
  • Administrators
  • 2599 posts
  • Last active: Today, 07:08 PM
  • Joined: 09 Mar 2008
Thank you! This is how I expected the GDI library was going to be, back when I had interest in trying it (then gave up because it was to confusing). this looks much simpler.

I haven't tried it yet, but will tomorrow.
I think a handy feature would be to specify an hwnd for the drawn shapes to follow. that way you can add separators and custom shapes or whatever to a program.

iPhilip
  • Members
  • 133 posts
  • Last active: May 06 2014 06:45 PM
  • Joined: 03 Jun 2010
You are welcome. I am not sue what you mean by your last statement:

I think a handy feature would be to specify an hwnd for the drawn shapes to follow. that way you can add separators and custom shapes or whatever to a program.


I will wait to hear more after you have had a chance to play with it tomorrow.

tidbit
  • Administrators
  • 2599 posts
  • Last active: Today, 07:08 PM
  • Joined: 09 Mar 2008
tested and, great :)

although, I see how my idea would not work. "Moves the mouse off the screen and blocks any further input. "

this is what I was thinking:
(concept image)
<!-- m -->http://i54.tinypic.com/314v2he.png<!-- m -->

As you can see, there are lines and rectangles, right? Good. Well, lots of programs have a simple horizontal line or section with a different background color (like a lot of installers).
I was hoping that I could draw a rectangle and Parent it to the gui. That way it follows the gui where ever it goes. We could then make prettier guis :D (unlike my fugly sample).

edit: here's a more professional sample. notice there are 2 distinct sections? 1 with a white BG and 1 without? thats what I was wanting to do.

RandomUser
  • Guests
  • Last active:
  • Joined: --
Awesome work.
I look forward to playing with this script.

PS: @tidbit you can create more professional layouts without these GUI drawing scripts. I just layer images on top of each other. But then again i custom make all my buttons & images for my GUI.

iDrug
  • Members
  • 387 posts
  • Last active: Jun 14 2014 12:28 AM
  • Joined: 13 Oct 2009
This script creates a canvas and allows to draw there and only there.
Did I get it right?
How about "hooking" into existing windows?
I'd like to specify which windows to draw above.

And I also have a quite general question: is it possible to draw something using AHK that is transparent to clicks?
I would like to draw a cross-hair in the center of the screen, that would be persistent (even if I run a full-screen application like a game) - is it possible?

njciancio
  • Members
  • 141 posts
  • Last active: Mar 27 2014 11:36 AM
  • Joined: 31 May 2012

I love it! Love those angled lines - reminds of old school screen savers. I'll have to use that for something...



Xx7
  • Members
  • 670 posts
  • Last active: May 31 2014 10:37 PM
  • Joined: 19 Apr 2011

This is very nice. thanks for the functions :)



jleslie48
  • Members
  • 145 posts
  • Last active: May 22 2014 03:03 PM
  • Joined: 29 Jun 2010

Wow!!!   I just tried this, and it worked!   I almost understand it wink.png  anyway,  I'd like to incorporate this into my gui, but I'm not sure how to do it.   

 

I have a gui, and want to use this, but I want the drawings with respect to the boundaries of the gui.  so for example I have a gui with stuff in it, and its last line is:

 

Gui ,1: Show,  x50 y50 w800 h500, first screen

 

so now I have a gui that is 800x500, and I  would like to draw a circle centered at say, 100,100 of the gui with a radius of 40 when the use pushes a button that was set up in Gui, 1:   there is another button in gui, 1: that allows the user to switch to gui ,2: and when that happens, the circle should disappear as its only a part of gui, 1:   

 

I'm guessing that if I had a demo like that , I'll be able to add buttons like, clear screen, add a new dot, etc

 

I'm also thinking the draw_dot (x,y,r,c) could use a sister function, draw_circle (x,y,r1,r2,c1,c2) where r1 is the old radius (or the outer radius) and r2 is the inner radius which would be nothing more than another dot drawn with the background color or an invisible see through setting.  in this way you can make a ring (aka, circle) or a dot with a different color edge.    

 

Tia, 

 

jleslie48



iPhilip
  • Members
  • 133 posts
  • Last active: May 06 2014 06:45 PM
  • Joined: 03 Jun 2010

Hi jleslie48,

 

You should be able to replace A_ScreenWidth and A_ScreenHeight in the DrawFunctions with the dimensions of the Gui and make it work with a smaller area. The only thing is that it will cover your Gui and you won't be able to click through it.

 

You should be able to draw a ring with two draw_dot's of different radii.

 

Good luck!

 

iPhilip



jleslie48
  • Members
  • 145 posts
  • Last active: May 22 2014 03:03 PM
  • Joined: 29 Jun 2010

thanks Iphilip,  I was working on that as well.   and when I switch gui's, I have to clear the dots.    I'm going to be away for a few days, but I'll let you know how It turns out. - Jon



jleslie48
  • Members
  • 145 posts
  • Last active: May 22 2014 03:03 PM
  • Joined: 29 Jun 2010

ok, I'm having a problem.   the first few lines of my script are:


; Uncomment if Gdip.ahk is not in your standard library
#Include, Gdip.ahk
#Include, Gdip_Helper.ahk   ; for testdraw
     SetUpGDIP()

; Start gdi+
If !pToken := Gdip_Startup()
{
	MsgBox, 48, gdiplus error! 
           , Gdiplus failed to start. Please ensure you have gdiplus on your system
	ExitApp
}

I have a bunch of  different gui's that I can toggle between.   number 6 is defined as such:

        
; gui 6: begin

sinkimg_xlocation := window_w/2 - sinkimg_w/2
sinkimg_ylocation := window_h/2 - sinkimg_h/2

crossimg_xlocation := window_w/2 - crossimg_w/2
crossimg_ylocation := window_h/2 - crossimg_h/2

 gui, 6: Color, ffffff
 WinSet TransColor, FF40FF    ; using a pink bacground as an invisible color
Gui +LastFound 
gui, 6: -Caption
gui, 6: Margin, 1, 1


Gui, 6: Add, Picture,  +BackgroundTrans x001 y001  w%window_w% h%window_h%      , map_ue06.png
Gui, 6: Add, Picture, vjlmove gdragger x10 y10  w10 h10 , move.bmp
Gui, 6: Add, Picture, vjlexit gjlclose x31 y10 w10 h10 , exit.bmp
Gui, 6: Add, Picture, vvjlswitch00 ggjlswitch00 x640  y15 w40 h20 , nextscreen.png
Gui, 6: Add, Picture, vvjlswitch01 ggjlswitch01 xp+45 y15 w40 h20 , nextscreen.png
gui ,6: Add, Picture, vvjlswitch02 ggjlswitch02 xp+50 y15 w45 h20 , nextscreen.png
gui ,6: Add, Picture, vvjlswitch03 ggjlswitch03 xp+50 y15 w75 h20 , nextscreen.png
gui ,6: Add, Picture, vvjlswitch04 ggjlswitch04 xp+80 y15 w40 h20 , nextscreen.png

Gui, 6: Add, Picture,  x617 y10 , tabs06.png
Gui, 6: Add, Picture,                x%window_w% y%window_h%      , space.bmp

gui ,6: Add, Picture
           , +BackgroundTrans x%crossimg_xlocation% y%crossimg_ylocation% w%crossimg_w% h%crossimg_h% 
           , %crossimg%
gui ,6: Add, Picture
           , +BackgroundTrans x%sinkimg_xlocation% y%sinkimg_ylocation% w%sinkimg_w% h%sinkimg_h% 
           , %sinkimg%
  if (b1_xlocation > 0)  
        {
         gui ,6: Add, Picture
           , +BackgroundTrans x%b1_xlocation% y%b1_ylocation% gMoveOrRun01
           , %boomimg%
        } ; then
   else {
        } ; else
  if (b2_xlocation > 0)  
        {
         gui ,6: Add, Picture
           , +BackgroundTrans x%b2_xlocation% y%b2_ylocation% gMoveOrRun02
           , %boomimg%
        } ; then
   else {
        } ; else

gui,6: Add, Text,  x30 y650,  azimuth 
gui,6: Add, Edit,  x30 y675 w50 vbt_az,  
gui,6: Add, Text,  x90 y650,  range 
gui,6: Add, Edit,  x90 y675 w50 vbt_range,  
gui,6: Add, Button,x150 y675 ,  Fire Shot
gui,6: Add, Button,x350 y675 gtestdraw ,  test draw



; gui 6: end
        


I just added the testdraw button to it (see the last functional line above) 

 

but the gosub doesn't draw the shape, any ideas about what I'm doing wrong?

 

here is the gosub: 

 




testdraw:  
 msgbox, hello testdraw
     StartDrawGDIP()
     ClearDrawGDIP()
 
     pBrush := Gdip_BrushCreateSolid(0xffff0000)
     Random, X, 20, 300
     Gdip_FillRectangle(G, pBrush, X, 55, 200, 300)
     Gdip_DeleteBrush(pBrush)
 
     EndDrawGDIP()
     return   ; testdraw



rpboulan
  • Members
  • 1 posts
  • Last active: Nov 21 2013 02:47 PM
  • Joined: 21 Nov 2013

I would like to draw text on the screen. Do you know of an easy way to do that with out creating my own font line by line point by point?