[Gdip] How to draw shapes and lines with the mouse

Post your working scripts, libraries and tools for AHK v1.1 and older
User avatar
SpeedMaster
Posts: 494
Joined: 12 Nov 2016, 16:09

[Gdip] How to draw shapes and lines with the mouse

29 Mar 2020, 17:52

Hello,
After reading this topic https://www.autohotkey.com/boards/viewtopic.php?t=60827
I was curious to find a way to draw several shapes and lines with the mouse and display them at the same time on the screen. :think:
I don't know if it's the right method, but the BitBlt() function seems to do the trick. ;)

Here's the script of course it can still be improved with a lot of options and other shapes but as I'm not sure if it's the right way to do it I prefer to keep it basic.
shortcuts:
Shift + lmouse = draw lines
Alt + lmouse = draw filled rectangles
Ctrl + lmouse = draw rectangles
Win + lmouse = draw ellipses
Ctrl + Z = undo last drawing
F9 = clear the screen
F1 = Toggle Gui
Ctrl+F1 = toggle help

Code: Select all

;================================================================================================================================
; Subject:        Gdip Draw shapes and lines with the mouse
; Description:    Proof of concept for drawing shapes and lines with the mouse using BitBlt()
; Topic:            https://www.autohotkey.com/boards/viewtopic.php?f=6&t=74009
; Sript version:  1.5
; AHK Version:    1.1.24.03 (U32)
; Tested on:      Win 7 (x64)
; Authors:        SpeedMaster, Hellbent
; Credits :       Special thanks to Linear Spoon (how to draw a filled rectangle)
; https://autohotkey.com/board/topic/92184-deleting-a-rectangle-or-range-created-with-gdi/
;
; Shortcuts:      Shift + left mouse = draw lines
;                 Alt + left mouse = draw filled rectangles
;                 Ctrl + left mouse = draw rectangles
;                 Win + left mouse = draw ellipses
;                 Ctrl + Z = undo last drawing 
;                 F1 = Toggle Gui 
;                 Ctrl + F1 = Display Help
;                 F9 = clear the screen
;                 
;
; other related topic: https://www.autohotkey.com/boards/viewtopic.php?f=76&t=60827&hilit=draw+on+screen

#SingleInstance force
;;#Include <My Altered Gdip Lib>  ;<------       Replace with your copy of GDIP

;*******   HB Alteration    ******
global ColorList := ["000000","7F7F7F","880015","ED1C24","FF7F27","FFF200","22B14C","00A2E8","3F48CC","A349A4","FFFFFF","C3C3C3","B97A57","FFAEC9","FFC90E","EFE4B0","B5E61D","99D9EA","7092BE","C8BFE7"]
global Handles := [],Handles2:=[],ALED,Thickness,LH,LA:="FF",LT:=5
;*********************************

SetBatchLines -1
SetMouseDelay -1 

coordmode, mouse, screen

Gui, 1: -Caption +E0x80000  +LastFound +AlwaysOnTop +ToolWindow +OwnDialogs 
Gui, 1: Show, NA
hwnd7 := WinExist() ; hwnd7 to avoid conflict ("hwnd1" name is too much used in other scripts)
steps:=[]
pens:=[]

gosub f1

for k, v in handles {
	if (v=1) {
		LH:=k
	}
}

Onexit, exit

;---------------------------------------------------- Gdip stuff ----------------------------------------------
Width := A_ScreenWidth, Height := A_ScreenHeight
If !pToken := Gdip_Startup()
{
	MsgBox, 48, gdiplus error!, Gdiplus failed to start. Please ensure you have gdiplus on your system
	ExitApp
}
hbm := CreateDIBSection(Width, Height)  ;screen
hdc := CreateCompatibleDC()             ;screen
obm := SelectObject(hdc, hbm)           ;screen
hbm2 := CreateDIBSection(Width, Height) ;buffer
hdc2 := CreateCompatibleDC()            ;buffer
obm2 := SelectObject(hdc2, hbm2)        ;buffer
hbm3 := CreateDIBSection(Width, Height) ;saving buffer
hdc3 := CreateCompatibleDC()            ;saving buffer
obm3 := SelectObject(hdc3, hbm3)        ;saving buffer
G := Gdip_GraphicsFromHDC(hdc)
G2 := Gdip_GraphicsFromHDC(hdc2)
Gdip_SetSmoothingMode(G, 4)
Gdip_SetSmoothingMode(G2, 4)


;------------------------------------------------  create some brushes and pencils ------------------------------------
global pPen := Gdip_CreatePen("0xFF" ColorList[1] , 5)      
, pBrush := Gdip_BrushCreateSolid("0xFF" ColorList[1])

;------------------------------------------------  Undo/Redo stuff  ----------------------------------------------------
;clear the screen
f9::
Gdip_GraphicsClear(G)  ;This sets the entire area of the graphics to 'transparent'
BitBlt(hdc2, 0, 0, Width, Height, hdc, 0, 0) 
BitBlt(hdc3, 0, 0, Width, Height, hdc, 0, 0) 
UpdateLayeredWindow(hwnd7, hdc, 0, 0, Width, Height)  ;This is what actually changes the display
steps:=[]
return

; undo last drawing
^Z::
steps.pop() ; erase last step

Gdip_GraphicsClear(G)  ;This sets the entire area of the graphics to 'transparent'
;UpdateLayeredWindow(hwnd7, hdc, 0, 0, Width, Height)  ;This is what actually changes the display

for k, v in steps {
	refresh(v)
	if (k=steps.maxindex()-1)
		BitBlt(hdc3, 0, 0, Width, Height, hdc, 0, 0) ; save previous hdc
}
UpdateLayeredWindow(hwnd7, hdc, 0, 0, Width, Height)
BitBlt(hdc2, 0, 0, Width, Height, hdc, 0, 0) ; refresh buffer


refresh(v) {
	global
	Gdip_DeleteBrush( pBrush )
	Gdip_DeletePen( pPen )
pPen := Gdip_CreatePen(v.1,v.2)
pBrush := Gdip_BrushCreateSolid(v.1)

LA:=substr(v.1,3,2) ; transparency
currentcontrol:="2_" _HasVal(colorlist, substr(v.1,5))

loop, % colorlist.length()
GuiControl,% "99:+Background" "555555", % "2_" A_Index

GuiControl,% "99:+Background" "00FFFF", % currentcontrol  ;select

LH:=_HasVal(handles, _HasVal(colorlist, substr(v.1,5)))

if (v.2!="br") {
	LT:=v.2	;Thickness
	v[3](G, pPen, v.4, v.5, v.6, v.7)  ;draw the shape
}
else
	v[3](G, pBrush, v.4, v.5, v.6, v.7) ;draw the shape

GuiControl,, % Thickness, % LT ;thickness
GuiControl,, % ALED, % LA    ;transparency

}
return

;------------------------------------------------ Help -------------------------------------------------

^F1::
help:=!help
if help {
	helptext:="Shift + lmouse = draw lines`n"
	helptext.="Shift + lmouse = draw lines`n"
	helptext.="Alt + lmouse = draw filled rectangles`n"
	helptext.="Ctrl + lmouse = draw rectangles`n"
	helptext.="Win + lmouse = draw ellipses`n"
	helptext.="Ctrl + Z = undo last drawing`n"
	helptext.="F9 = clear the screen`n" 
	helptext.="F1 = toggle help"
tooltip, % helptext
}
else
	tooltip
return

;------------------------------------------------  Draw rectangles  ----------------------------------------------------
;Draw rectangles
^LButton::   
CoordMode, Mouse, Screen
MouseGetPos, x1, y1
BitBlt(hdc3, 0, 0, Width, Height, hdc, 0, 0) ; save previous hdc first

while getKeyState("LButton", "P") ; draw in buffer
{
  MouseGetPos, x2, y2
  Gdip_GraphicsClear(G2)
  BitBlt(hdc2, 0, 0, Width, Height, hdc, 0, 0) ; BitBlt first before drawing
  Gdip_DrawRectangle(G2, pPen, min(x1,x2), min(y1,y2), abs(x2-x1), abs(y2-y1))
  UpdateLayeredWindow(hwnd7, hdc2, 0, 0, Width, Height)
}

step:=["0x" LA ColorList[Handles[LH]],LT,"Gdip_DrawRectangle",min(x1,x2),min(y1,y2),abs(x2-x1),abs(y2-y1)]
steps.push(step)

  BitBlt(hdc, 0, 0, Width, Height, hdc2, 0, 0) ;copy buffer to screen
  UpdateLayeredWindow(hwnd7, hdc, 0, 0, Width, Height) ; now draw on screen
return

;----------------------------------------------  Draw a filled rectangles  --------------------------------------------
;Draw a filled rectangles
!LButton::   
CoordMode, Mouse, Screen
MouseGetPos, x1, y1
BitBlt(hdc3, 0, 0, Width, Height, hdc, 0, 0) ; save previous hdc first

while getKeyState("LButton", "P") ; draw in buffer
{
  MouseGetPos, x2, y2
  Gdip_GraphicsClear(G2)
  BitBlt(hdc2, 0, 0, Width, Height, hdc, 0, 0) ; BitBlt first before drawing
  ;FillRect seems to expect the (x,y) coordinates passed to always be the upper left corner and width,height to be positive
  Gdip_FillRectangle(G2, pBrush, min(x1,x2), min(y1,y2), abs(x2-x1), abs(y2-y1))
  UpdateLayeredWindow(hwnd7, hdc2, 0, 0, Width, Height)
}

step:=["0x" LA ColorList[Handles[LH]],"Br","Gdip_FillRectangle",min(x1,x2),min(y1,y2),abs(x2-x1),abs(y2-y1)]
steps.push(step)

  BitBlt(hdc, 0, 0, Width, Height, hdc2, 0, 0) ;copy buffer to screen
  UpdateLayeredWindow(hwnd7, hdc, 0, 0, Width, Height) ; now draw on screen
  
  min(a,b)
{
  return a < b ? a : b
}
return

f2::reload
f5::
GuiControl,, % Thickness, 11
return

;----------------------------------------------------  draw lines  -----------------------------------------------------
;draw lines
+lbutton:: 
CoordMode, Mouse, Screen
MouseGetPos, x1, y1
BitBlt(hdc3, 0, 0, Width, Height, hdc, 0, 0) ; save previous hdc first
    
while getKeyState("LButton", "P")
{
    MouseGetPos, x2, y2
    Gdip_GraphicsClear(G2)
    BitBlt(hdc2, 0, 0, Width, Height, hdc, 0, 0)
    Gdip_DrawLine(G2, pPen, x1, y1, x2, y2)
    UpdateLayeredWindow(hwnd7, hdc2, 0, 0, Width, Height)
}

step:=["0x" LA ColorList[Handles[LH]],LT,"Gdip_DrawLine",x1,y1,x2,y2]
steps.push(step)

BitBlt(hdc, 0, 0, Width, Height, hdc2, 0, 0) ;copy buffer to screen
UpdateLayeredWindow(hwnd7, hdc, 0, 0, Width, Height) ; now draw on screen



return

;----------------------------------------------------  Draw ellipse  -------------------------------------------------
;Draw ellipse
#LButton::   
CoordMode, Mouse, Screen
MouseGetPos, x1, y1
BitBlt(hdc3, 0, 0, Width, Height, hdc, 0, 0) ; save previous hdc first

while getKeyState("LButton", "P") ; draw in buffer
{
  MouseGetPos, x2, y2
  Gdip_GraphicsClear(G2)
  BitBlt(hdc2, 0, 0, Width, Height, hdc, 0, 0) ; BitBlt first before drawing
  ;seems to expect the (x,y) coordinates passed to always be the upper left corner and width,height to be positive
  Gdip_DrawEllipse(G2, pPen, min(x1,x2), min(y1,y2), abs(x2-x1), abs(y2-y1))
  UpdateLayeredWindow(hwnd7, hdc2, 0, 0, Width, Height)
}

step:=["0x" LA ColorList[Handles[LH]],LT,"Gdip_DrawEllipse",min(x1,x2),min(y1,y2),abs(x2-x1),abs(y2-y1)]
steps.push(step)

  BitBlt(hdc, 0, 0, Width, Height, hdc2, 0, 0) ;copy buffer to screen
  UpdateLayeredWindow(hwnd7, hdc, 0, 0, Width, Height) ; now draw on screen
  
return

exit:
SelectObject(hdc, obm)
DeleteObject(hbm)
DeleteDC(hdc)
Gdip_DeleteGraphics(G)

SelectObject(hdc2, obm2)
DeleteObject(hbm2)
DeleteDC(hdc2)
Gdip_DeleteGraphics(G2)

SelectObject(hdc3, obm3)
DeleteObject(hbm3)

Gdip_Shutdown(pToken)

exitapp
return

esc::exitapp


;*******   HB Alteration    ******
;********************************************************************************************************************************************
;********************************************************************************************************************************************
;********************************************************************************************************************************************
F1::
99GuiClose:

	if(showgui:=!showgui){
		if(!ft){
			ft:=1
			Gui,99:+AlwaysOnTop +ToolWindow
			Gui,99:Color,% ColorList[1], 333333
			Gui,99:Font,cWhite s8 ,Segoe UI
			y:=0,x:=m:=20,w:=m,C:="00ffff"
			Gui,99:Margin,20,20
			Loop,% ColorList.Length()	{
				if(A_Index=11)
					x:=m+w,y:=0
				y+=m
				Gui,99:Add,Text,% "x" x " y" y " w" m " h" m " hwndhwnd gChangeColor" " v1_" a_index, x
				Handles[hwnd] := A_Index
				Gui,99:Add,Progress,% "x" x " y" y " w" m " h" m " c" ColorList[A_Index] " Background" C " hwndhwnd" " v2_" a_index  ,100
				Handles2[A_Index]:= hwnd, C:="555555"
			}
			Gui,99:Add,Edit,xm y+1 w40 r1 Limit2 Center hwndALED gChangeBrush,FF
			Gui,99:Add,Edit,xm y+1 w40 r1 Limit2 Center hwndThickness gChangeBrush,5
			Gui,99:Add,button,xm y+2 w40 gapply, APPLY
			GuiControl,% "99:Focus", % hwnd
		}
		Gui,99:Show,% "x" A_ScreenWidth-250 " y150",Color
	}
	else{
		Gui,99:Hide
	}
	return
ChangeColor(hwnd){
	;global LH
	static LC := 1, C :="00FFFF"
	loop, % colorlist.length()
	GuiControl,% "99:+Background" "555555", % "2_" A_Index
	LH:=hwnd

	GuiControl,% "99:Focus", % Handles2[Handles[hwnd]]
	GuiControl,% "99:+Background555555",  % "2_" LC
	GuiControl,% "99:+Background" C, % Handles2[Handles[hwnd]]
	Gui,99:Color,% ColorList[Handles[hwnd]]
	LC := Handles[hwnd]
	ChangeBrush()
}
ChangeBrush(){
	;global LA, LT
	GuiControlGet,LA,99:,% ALED
	GuiControlGet,LT,99:,% Thickness
	Gdip_DeleteBrush( pBrush )
	Gdip_DeletePen( pPen )
	pPen := Gdip_CreatePen("0x" LA ColorList[Handles[LH]] , LT)      
	pBrush := Gdip_BrushCreateSolid("0x" LA ColorList[Handles[LH]])
}


apply:
if (!steps.Maxindex())
	return
BitBlt(hdc, 0, 0, Width, Height, hdc3, 0, 0)
steps[steps.maxindex()][1]:="0x" LA ColorList[Handles[LH]]  ;apply new selected color to the last shape
(steps[steps.maxindex()][2]!="br") && steps[steps.maxindex()][2]:=LT  ; apply transparency and thickness to the last shape
refresh(steps[steps.maxindex()])
UpdateLayeredWindow(hwnd7, hdc,0,0, Width, Height)
return


_HasVal(haystack, needle) {
    for index, value in haystack
        if (value = needle)
            return index
    if !(IsObject(haystack))
        throw Exception("Bad haystack!", -1, haystack)
    return 0
}


gdipdraw.png
gdipdraw.png (14.38 KiB) Viewed 8015 times
If this is the wrong way or if you have another method, please let me know. :crazy:
Cheers
Last edited by SpeedMaster on 31 Mar 2020, 17:36, edited 2 times in total.
User avatar
huyaowen
Posts: 109
Joined: 28 Jul 2014, 01:15

Re: [Gdip] How to draw shapes and lines with the mouse

30 Mar 2020, 03:56

Perfect script.It is the one what I wanna.
Could you control the pen in powerpoint slide show window to draw these shapes without the red line?
Image 1920x1080.jpg
Image 1920x1080.jpg (68.55 KiB) Viewed 7954 times
User avatar
Hellbent
Posts: 2109
Joined: 23 Sep 2017, 13:34

Re: [Gdip] How to draw shapes and lines with the mouse

30 Mar 2020, 08:49

@SpeedMaster
Well done :thumbup:

Here is a slightly altered version with the ability to change the color, alpha, and pen thickness on the fly.

Code: Select all

;================================================================================================================================
; Subject:        Gdip Draw shapes and lines with the mouse
; Description:    Proof of concept for drawing shapes and lines with the mouse using BitBlt()
; Topic:            https://www.autohotkey.com/boards/viewtopic.php?f=6&t=74009
; Sript version:  1.0
; AHK Version:    1.1.24.03 (U32)
; Tested on:      Win 7 (x64)
; Author:         SpeedMaster
; Credits :       Special thanks to Linear Spoon (how to draw a filled rectangle)
; https://autohotkey.com/board/topic/92184-deleting-a-rectangle-or-range-created-with-gdi/
;
; Shortcuts:      Shift + left mouse = draw lines
;                 Alt + left mouse = draw filled rectangles
;                 Ctrl + left mouse = draw rectangles
;                 Win + left mouse = draw ellipses
;                 Ctrl + Z = undo last drawing 
;                 F9 = clear the screen 
;                 F10 = restore the screen
;
; other related topic: https://www.autohotkey.com/boards/viewtopic.php?f=76&t=60827&hilit=draw+on+screen

#SingleInstance force
#Include <My Altered Gdip Lib>  ;<------       Replace with your copy of GDIP

;*******   HB Alteration    ******
global ColorList := ["000000","7F7F7F","880015","ED1C24","FF7F27","FFF200","22B14C","00A2E8","3F48CC","A349A4","FFFFFF","C3C3C3","B97A57","FFAEC9","FFC90E","EFE4B0","B5E61D","99D9EA","7092BE","C8BFE7"]
global Handles := [],Handles2:=[],ALED,Thickness,LH,LA:="FF",LT:=5
;*********************************

SetBatchLines -1
SetMouseDelay -1 

coordmode, mouse, screen

Gui, 1: -Caption +E0x80000  +LastFound +AlwaysOnTop +ToolWindow +OwnDialogs 
Gui, 1: Show, NA
hwnd7 := WinExist() ; hwnd7 to avoid conflict ("hwnd1" name is too much used in other scripts)

Onexit, exit

;---------------------------------------------------- Gdip stuff ----------------------------------------------
Width := A_ScreenWidth, Height := A_ScreenHeight
If !pToken := Gdip_Startup()
{
	MsgBox, 48, gdiplus error!, Gdiplus failed to start. Please ensure you have gdiplus on your system
	ExitApp
}
hbm := CreateDIBSection(Width, Height)  ;screen
hdc := CreateCompatibleDC()             ;screen
obm := SelectObject(hdc, hbm)           ;screen
hbm2 := CreateDIBSection(Width, Height) ;buffer
hdc2 := CreateCompatibleDC()            ;buffer
obm2 := SelectObject(hdc2, hbm2)        ;buffer
hbm3 := CreateDIBSection(Width, Height) ;saving buffer
hdc3 := CreateCompatibleDC()            ;saving buffer
obm3 := SelectObject(hdc3, hbm3)        ;saving buffer
G := Gdip_GraphicsFromHDC(hdc)
G2 := Gdip_GraphicsFromHDC(hdc2)
Gdip_SetSmoothingMode(G, 4)
Gdip_SetSmoothingMode(G2, 4)


;------------------------------------------------  create some brushes and pencils ------------------------------------
global pPen := Gdip_CreatePen("0xFF" ColorList[1] , 5)      
, pBrush := Gdip_BrushCreateSolid("0xFF" ColorList[1])

;------------------------------------------------  Undo/Redo stuff  ----------------------------------------------------
;clear the screen
f9::
Gdip_GraphicsClear(G)  ;This sets the entire area of the graphics to 'transparent'
UpdateLayeredWindow(hwnd7, hdc, 0, 0, Width, Height)  ;This is what actually changes the display
return

;get everithing from the buffer back to the screen
f10::
BitBlt(hdc, 0, 0, Width, Height, hdc2, 0, 0)
UpdateLayeredWindow(hwnd7, hdc, 0, 0, Width, Height) 
return

; undo last drawing
^Z::
BitBlt(hdc, 0, 0, Width, Height, hdc3, 0, 0)
UpdateLayeredWindow(hwnd7, hdc3, 0, 0, Width, Height)
return

;------------------------------------------------ Help -------------------------------------------------



;------------------------------------------------  Draw rectangles  ----------------------------------------------------
;Draw rectangles
^LButton::   
CoordMode, Mouse, Screen
MouseGetPos, x1, y1
BitBlt(hdc3, 0, 0, Width, Height, hdc, 0, 0) ; save previous hdc first

while getKeyState("LButton", "P") ; draw in buffer
{
  MouseGetPos, x2, y2
  Gdip_GraphicsClear(G2)
  BitBlt(hdc2, 0, 0, Width, Height, hdc, 0, 0) ; BitBlt first before drawing
  Gdip_DrawRectangle(G2, pPen, min(x1,x2), min(y1,y2), abs(x2-x1), abs(y2-y1))
  UpdateLayeredWindow(hwnd7, hdc2, 0, 0, Width, Height)
}

  BitBlt(hdc, 0, 0, Width, Height, hdc2, 0, 0) ;copy buffer to screen
  UpdateLayeredWindow(hwnd7, hdc, 0, 0, Width, Height) ; now draw on screen
return

;----------------------------------------------  Draw a filled rectangles  --------------------------------------------
;Draw a filled rectangles
!LButton::   
CoordMode, Mouse, Screen
MouseGetPos, x1, y1
BitBlt(hdc3, 0, 0, Width, Height, hdc, 0, 0) ; save previous hdc first

while getKeyState("LButton", "P") ; draw in buffer
{
  MouseGetPos, x2, y2
  Gdip_GraphicsClear(G2)
  BitBlt(hdc2, 0, 0, Width, Height, hdc, 0, 0) ; BitBlt first before drawing
  ;FillRect seems to expect the (x,y) coordinates passed to always be the upper left corner and width,height to be positive
  Gdip_FillRectangle(G2, pBrush, min(x1,x2), min(y1,y2), abs(x2-x1), abs(y2-y1))
  UpdateLayeredWindow(hwnd7, hdc2, 0, 0, Width, Height)
}
  
  BitBlt(hdc, 0, 0, Width, Height, hdc2, 0, 0) ;copy buffer to screen
  UpdateLayeredWindow(hwnd7, hdc, 0, 0, Width, Height) ; now draw on screen
  
  min(a,b)
{
  return a < b ? a : b
}
return

;----------------------------------------------------  draw lines  -----------------------------------------------------
;draw lines
+lbutton:: 
CoordMode, Mouse, Screen
MouseGetPos, x1, y1
BitBlt(hdc3, 0, 0, Width, Height, hdc, 0, 0) ; save previous hdc first
    
while getKeyState("LButton", "P")
{
    MouseGetPos, x2, y2
    Gdip_GraphicsClear(G2)
    BitBlt(hdc2, 0, 0, Width, Height, hdc, 0, 0)
    Gdip_DrawLine(G2, pPen, x1, y1, x2, y2)
    UpdateLayeredWindow(hwnd7, hdc2, 0, 0, Width, Height)
}

BitBlt(hdc, 0, 0, Width, Height, hdc2, 0, 0) ;copy buffer to screen
UpdateLayeredWindow(hwnd7, hdc, 0, 0, Width, Height) ; now draw on screen
return

;----------------------------------------------------  Draw ellipse  -------------------------------------------------
;Draw ellipse
#LButton::   
CoordMode, Mouse, Screen
MouseGetPos, x1, y1
BitBlt(hdc3, 0, 0, Width, Height, hdc, 0, 0) ; save previous hdc first

while getKeyState("LButton", "P") ; draw in buffer
{
  MouseGetPos, x2, y2
  Gdip_GraphicsClear(G2)
  BitBlt(hdc2, 0, 0, Width, Height, hdc, 0, 0) ; BitBlt first before drawing
  ;seems to expect the (x,y) coordinates passed to always be the upper left corner and width,height to be positive
  Gdip_DrawEllipse(G2, pPen, min(x1,x2), min(y1,y2), abs(x2-x1), abs(y2-y1))
  UpdateLayeredWindow(hwnd7, hdc2, 0, 0, Width, Height)
}
  
  BitBlt(hdc, 0, 0, Width, Height, hdc2, 0, 0) ;copy buffer to screen
  UpdateLayeredWindow(hwnd7, hdc, 0, 0, Width, Height) ; now draw on screen
  
return

exit:
SelectObject(hdc, obm)
DeleteObject(hbm)
DeleteDC(hdc)
Gdip_DeleteGraphics(G)

SelectObject(hdc2, obm2)
DeleteObject(hbm2)
DeleteDC(hdc2)
Gdip_DeleteGraphics(G2)

SelectObject(hdc3, obm3)
DeleteObject(hbm3)

Gdip_Shutdown(pToken)

exitapp
return

esc::exitapp


;*******   HB Alteration    ******
;********************************************************************************************************************************************
;********************************************************************************************************************************************
;********************************************************************************************************************************************
F1::
99GuiClose:
	helptext:="Shift + lmouse = draw lines`n"
	helptext.="Alt + lmouse = draw filled rectangles`n"
	helptext.="Ctrl + lmouse = draw rectangles`n"
	helptext.="Win + lmouse = draw ellipses`n"
	helptext.="Ctrl + Z = undo last drawing`n"
	helptext.="F9 = clear the screen`n" 
	helptext.="F10 = restore the screen`n"
	helptext.="F1 = toggle help"
	if(help:=!help){
		tooltip, % helptext
		if(!ft){
			ft:=1
			Gui,99:+AlwaysOnTop +ToolWindow
			Gui,99:Color,% ColorList[1], 333333
			Gui,99:Font,cWhite s8 ,Segoe UI
			y:=0,x:=m:=20,w:=m,C:="00ffff"
			Gui,99:Margin,20,20
			Loop,% ColorList.Length()	{
				if(A_Index=11)
					x:=m+w,y:=0
				y+=m
				Gui,99:Add,Text,% "x" x " y" y " w" m " h" m " hwndhwnd gChangeColor",
				Handles[hwnd] := A_Index
				Gui,99:Add,Progress,% "x" x " y" y " w" m " h" m " c" ColorList[A_Index] " Background" C " hwndhwnd"  ,100
				Handles2[A_Index]:= hwnd, C:="555555"
			}
			Gui,99:Add,Edit,xm y+1 w40 r1 Limit2 Center hwndALED gChangeBrush,FF
			Gui,99:Add,Edit,xm y+1 w40 r1 Limit2 Center hwndThickness gChangeBrush,5
			GuiControl,% "99:Focus", % hwnd
		}
		Gui,99:Show,% "x" A_ScreenWidth-250 " y150",Color
	}
	else{
		tooltip,
		Gui,99:Hide
	}
	return
ChangeColor(hwnd){
	static LC := 1, C :="00FFFF"
	LH:=hwnd
	GuiControl,% "99:Focus", % Handles2[Handles[hwnd]]
	GuiControl,% "99:+Background555555", % Handles2[LC]
	GuiControl,% "99:+Background" C, % Handles2[Handles[hwnd]]
	Gui,99:Color,% ColorList[Handles[hwnd]]
	LC := Handles[hwnd]
	ChangeBrush()
}
ChangeBrush(){
	GuiControlGet,LA,99:,% ALED
	GuiControlGet,LT,99:,% Thickness
	Gdip_DeleteBrush( pBrush )
	Gdip_DeletePen( pPen )
	pPen := Gdip_CreatePen("0x" LA ColorList[Handles[LH]] , LT)      
	pBrush := Gdip_BrushCreateSolid("0x" LA ColorList[Handles[LH]])
}
User avatar
SpeedMaster
Posts: 494
Joined: 12 Nov 2016, 16:09

Re: [Gdip] How to draw shapes and lines with the mouse

31 Mar 2020, 17:26

huyaowen wrote:
30 Mar 2020, 03:56
Could you control the pen in powerpoint slide show window to draw these shapes without the red line?
No. You still have to draw manually (saving to a file is not implemented yet). :roll:
Hellbent wrote:
30 Mar 2020, 08:49
Here is a slightly altered version with the ability to change the color, alpha, and pen thickness on the fly.
Thanks for the altered version. It's very good. :thumbup:
I played with it and found it a bit difficult to modify because i'm not used to manipulate the controls with only their Hwnd :facepalm: .
I still managed to add some new features :D .
  • "Apply" button to apply color and size on the last shape
  • A step recorder which allows to make unlimited undo's or to export the drawing to a data file (not implemented yet).
There may still be a lot of bugs but it's already better. :problem:
See the new updated version (v1.5)

cheers
User avatar
Hellbent
Posts: 2109
Joined: 23 Sep 2017, 13:34

Re: [Gdip] How to draw shapes and lines with the mouse

31 Mar 2020, 22:15

@SpeedMaster

Good stuff :thumbup:

Here is replacing the 20th color with a new one.

Code: Select all

;This goes above the first editbox (Alpha)
Gui,99:Add,Button,xm y+1 w40 gGetColor,GET

;This goes anywhere
GetColor(){
	local tx, ty, out
	CoordMode,Mouse,Screen
	CoordMode,Pixel,Screen
	While(!GetKeyState("ctrl")){
		ToolTip, Hover over a color and press "ctrl" to capture it.
		MouseGetPos,tx,ty
		PixelGetColor,Out,tx,ty,RGB
		GuiControl,% "99:+C" Out, % Handles2[20]
		ColorList[20]:= SubStr(Out,3)
		sleep, 10
	}
	ToolTip,
}

User avatar
elModo7
Posts: 217
Joined: 01 Sep 2017, 02:38
Location: Spain
Contact:

Re: [Gdip] How to draw shapes and lines with the mouse

04 Apr 2020, 10:00

Damn, this is useful to showcase stuff on pressentations!
Great tool!
User avatar
Onimuru
Posts: 107
Joined: 08 Sep 2018, 18:35
Contact:

Re: [Gdip] How to draw shapes and lines with the mouse

05 Apr 2020, 02:48

Hellbent wrote:
30 Mar 2020, 08:49
@SpeedMaster
Well done :thumbup:

Here is a slightly altered version with the ability to change the color, alpha, and pen thickness on the fly.

Code: Select all

;================================================================================================================================
; Subject:        Gdip Draw shapes and lines with the mouse
; Description:    Proof of concept for drawing shapes and lines with the mouse using BitBlt()
; Topic:            https://www.autohotkey.com/boards/viewtopic.php?f=6&t=74009
; Sript version:  1.0
; AHK Version:    1.1.24.03 (U32)
; Tested on:      Win 7 (x64)
; Author:         SpeedMaster
; Credits :       Special thanks to Linear Spoon (how to draw a filled rectangle)
; https://autohotkey.com/board/topic/92184-deleting-a-rectangle-or-range-created-with-gdi/
;
; Shortcuts:      Shift + left mouse = draw lines
;                 Alt + left mouse = draw filled rectangles
;                 Ctrl + left mouse = draw rectangles
;                 Win + left mouse = draw ellipses
;                 Ctrl + Z = undo last drawing 
;                 F9 = clear the screen 
;                 F10 = restore the screen
;
; other related topic: https://www.autohotkey.com/boards/viewtopic.php?f=76&t=60827&hilit=draw+on+screen

#SingleInstance force
#Include <My Altered Gdip Lib>  ;<------       Replace with your copy of GDIP

;*******   HB Alteration    ******
global ColorList := ["000000","7F7F7F","880015","ED1C24","FF7F27","FFF200","22B14C","00A2E8","3F48CC","A349A4","FFFFFF","C3C3C3","B97A57","FFAEC9","FFC90E","EFE4B0","B5E61D","99D9EA","7092BE","C8BFE7"]
global Handles := [],Handles2:=[],ALED,Thickness,LH,LA:="FF",LT:=5
;*********************************

SetBatchLines -1
SetMouseDelay -1 

coordmode, mouse, screen

Gui, 1: -Caption +E0x80000  +LastFound +AlwaysOnTop +ToolWindow +OwnDialogs 
Gui, 1: Show, NA
hwnd7 := WinExist() ; hwnd7 to avoid conflict ("hwnd1" name is too much used in other scripts)

Onexit, exit

;---------------------------------------------------- Gdip stuff ----------------------------------------------
Width := A_ScreenWidth, Height := A_ScreenHeight
If !pToken := Gdip_Startup()
{
	MsgBox, 48, gdiplus error!, Gdiplus failed to start. Please ensure you have gdiplus on your system
	ExitApp
}
hbm := CreateDIBSection(Width, Height)  ;screen
hdc := CreateCompatibleDC()             ;screen
obm := SelectObject(hdc, hbm)           ;screen
hbm2 := CreateDIBSection(Width, Height) ;buffer
hdc2 := CreateCompatibleDC()            ;buffer
obm2 := SelectObject(hdc2, hbm2)        ;buffer
hbm3 := CreateDIBSection(Width, Height) ;saving buffer
hdc3 := CreateCompatibleDC()            ;saving buffer
obm3 := SelectObject(hdc3, hbm3)        ;saving buffer
G := Gdip_GraphicsFromHDC(hdc)
G2 := Gdip_GraphicsFromHDC(hdc2)
Gdip_SetSmoothingMode(G, 4)
Gdip_SetSmoothingMode(G2, 4)


;------------------------------------------------  create some brushes and pencils ------------------------------------
global pPen := Gdip_CreatePen("0xFF" ColorList[1] , 5)      
, pBrush := Gdip_BrushCreateSolid("0xFF" ColorList[1])

;------------------------------------------------  Undo/Redo stuff  ----------------------------------------------------
;clear the screen
f9::
Gdip_GraphicsClear(G)  ;This sets the entire area of the graphics to 'transparent'
UpdateLayeredWindow(hwnd7, hdc, 0, 0, Width, Height)  ;This is what actually changes the display
return

;get everithing from the buffer back to the screen
f10::
BitBlt(hdc, 0, 0, Width, Height, hdc2, 0, 0)
UpdateLayeredWindow(hwnd7, hdc, 0, 0, Width, Height) 
return

; undo last drawing
^Z::
BitBlt(hdc, 0, 0, Width, Height, hdc3, 0, 0)
UpdateLayeredWindow(hwnd7, hdc3, 0, 0, Width, Height)
return

;------------------------------------------------ Help -------------------------------------------------



;------------------------------------------------  Draw rectangles  ----------------------------------------------------
;Draw rectangles
^LButton::   
CoordMode, Mouse, Screen
MouseGetPos, x1, y1
BitBlt(hdc3, 0, 0, Width, Height, hdc, 0, 0) ; save previous hdc first

while getKeyState("LButton", "P") ; draw in buffer
{
  MouseGetPos, x2, y2
  Gdip_GraphicsClear(G2)
  BitBlt(hdc2, 0, 0, Width, Height, hdc, 0, 0) ; BitBlt first before drawing
  Gdip_DrawRectangle(G2, pPen, min(x1,x2), min(y1,y2), abs(x2-x1), abs(y2-y1))
  UpdateLayeredWindow(hwnd7, hdc2, 0, 0, Width, Height)
}

  BitBlt(hdc, 0, 0, Width, Height, hdc2, 0, 0) ;copy buffer to screen
  UpdateLayeredWindow(hwnd7, hdc, 0, 0, Width, Height) ; now draw on screen
return

;----------------------------------------------  Draw a filled rectangles  --------------------------------------------
;Draw a filled rectangles
!LButton::   
CoordMode, Mouse, Screen
MouseGetPos, x1, y1
BitBlt(hdc3, 0, 0, Width, Height, hdc, 0, 0) ; save previous hdc first

while getKeyState("LButton", "P") ; draw in buffer
{
  MouseGetPos, x2, y2
  Gdip_GraphicsClear(G2)
  BitBlt(hdc2, 0, 0, Width, Height, hdc, 0, 0) ; BitBlt first before drawing
  ;FillRect seems to expect the (x,y) coordinates passed to always be the upper left corner and width,height to be positive
  Gdip_FillRectangle(G2, pBrush, min(x1,x2), min(y1,y2), abs(x2-x1), abs(y2-y1))
  UpdateLayeredWindow(hwnd7, hdc2, 0, 0, Width, Height)
}
  
  BitBlt(hdc, 0, 0, Width, Height, hdc2, 0, 0) ;copy buffer to screen
  UpdateLayeredWindow(hwnd7, hdc, 0, 0, Width, Height) ; now draw on screen
  
  min(a,b)
{
  return a < b ? a : b
}
return

;----------------------------------------------------  draw lines  -----------------------------------------------------
;draw lines
+lbutton:: 
CoordMode, Mouse, Screen
MouseGetPos, x1, y1
BitBlt(hdc3, 0, 0, Width, Height, hdc, 0, 0) ; save previous hdc first
    
while getKeyState("LButton", "P")
{
    MouseGetPos, x2, y2
    Gdip_GraphicsClear(G2)
    BitBlt(hdc2, 0, 0, Width, Height, hdc, 0, 0)
    Gdip_DrawLine(G2, pPen, x1, y1, x2, y2)
    UpdateLayeredWindow(hwnd7, hdc2, 0, 0, Width, Height)
}

BitBlt(hdc, 0, 0, Width, Height, hdc2, 0, 0) ;copy buffer to screen
UpdateLayeredWindow(hwnd7, hdc, 0, 0, Width, Height) ; now draw on screen
return

;----------------------------------------------------  Draw ellipse  -------------------------------------------------
;Draw ellipse
#LButton::   
CoordMode, Mouse, Screen
MouseGetPos, x1, y1
BitBlt(hdc3, 0, 0, Width, Height, hdc, 0, 0) ; save previous hdc first

while getKeyState("LButton", "P") ; draw in buffer
{
  MouseGetPos, x2, y2
  Gdip_GraphicsClear(G2)
  BitBlt(hdc2, 0, 0, Width, Height, hdc, 0, 0) ; BitBlt first before drawing
  ;seems to expect the (x,y) coordinates passed to always be the upper left corner and width,height to be positive
  Gdip_DrawEllipse(G2, pPen, min(x1,x2), min(y1,y2), abs(x2-x1), abs(y2-y1))
  UpdateLayeredWindow(hwnd7, hdc2, 0, 0, Width, Height)
}
  
  BitBlt(hdc, 0, 0, Width, Height, hdc2, 0, 0) ;copy buffer to screen
  UpdateLayeredWindow(hwnd7, hdc, 0, 0, Width, Height) ; now draw on screen
  
return

exit:
SelectObject(hdc, obm)
DeleteObject(hbm)
DeleteDC(hdc)
Gdip_DeleteGraphics(G)

SelectObject(hdc2, obm2)
DeleteObject(hbm2)
DeleteDC(hdc2)
Gdip_DeleteGraphics(G2)

SelectObject(hdc3, obm3)
DeleteObject(hbm3)

Gdip_Shutdown(pToken)

exitapp
return

esc::exitapp


;*******   HB Alteration    ******
;********************************************************************************************************************************************
;********************************************************************************************************************************************
;********************************************************************************************************************************************
F1::
99GuiClose:
	helptext:="Shift + lmouse = draw lines`n"
	helptext.="Alt + lmouse = draw filled rectangles`n"
	helptext.="Ctrl + lmouse = draw rectangles`n"
	helptext.="Win + lmouse = draw ellipses`n"
	helptext.="Ctrl + Z = undo last drawing`n"
	helptext.="F9 = clear the screen`n" 
	helptext.="F10 = restore the screen`n"
	helptext.="F1 = toggle help"
	if(help:=!help){
		tooltip, % helptext
		if(!ft){
			ft:=1
			Gui,99:+AlwaysOnTop +ToolWindow
			Gui,99:Color,% ColorList[1], 333333
			Gui,99:Font,cWhite s8 ,Segoe UI
			y:=0,x:=m:=20,w:=m,C:="00ffff"
			Gui,99:Margin,20,20
			Loop,% ColorList.Length()	{
				if(A_Index=11)
					x:=m+w,y:=0
				y+=m
				Gui,99:Add,Text,% "x" x " y" y " w" m " h" m " hwndhwnd gChangeColor",
				Handles[hwnd] := A_Index
				Gui,99:Add,Progress,% "x" x " y" y " w" m " h" m " c" ColorList[A_Index] " Background" C " hwndhwnd"  ,100
				Handles2[A_Index]:= hwnd, C:="555555"
			}
			Gui,99:Add,Edit,xm y+1 w40 r1 Limit2 Center hwndALED gChangeBrush,FF
			Gui,99:Add,Edit,xm y+1 w40 r1 Limit2 Center hwndThickness gChangeBrush,5
			GuiControl,% "99:Focus", % hwnd
		}
		Gui,99:Show,% "x" A_ScreenWidth-250 " y150",Color
	}
	else{
		tooltip,
		Gui,99:Hide
	}
	return
ChangeColor(hwnd){
	static LC := 1, C :="00FFFF"
	LH:=hwnd
	GuiControl,% "99:Focus", % Handles2[Handles[hwnd]]
	GuiControl,% "99:+Background555555", % Handles2[LC]
	GuiControl,% "99:+Background" C, % Handles2[Handles[hwnd]]
	Gui,99:Color,% ColorList[Handles[hwnd]]
	LC := Handles[hwnd]
	ChangeBrush()
}
ChangeBrush(){
	GuiControlGet,LA,99:,% ALED
	GuiControlGet,LT,99:,% Thickness
	Gdip_DeleteBrush( pBrush )
	Gdip_DeletePen( pPen )
	pPen := Gdip_CreatePen("0x" LA ColorList[Handles[LH]] , LT)      
	pBrush := Gdip_BrushCreateSolid("0x" LA ColorList[Handles[LH]])
}
There are actually gdip functions to change color of a pen/brush object on the fly:

Code: Select all

	Class Brush {
		__New(vAlpha := "FF", vColor := "FFFFFF")  {
			DllCall("gdiplus\GdipCreateSolidFill", "UInt", "0x" . vAlpha . vColor, Canvas.__Ptr[1], h), ObjRawSet(this, "Handle", h)
		}

		__Delete() {
			If (!this.Handle)
				MsgBox("Brush.__Delete()")

			Return, (DllCall("gdiplus\GdipDeleteBrush", Canvas.__Ptr[0], this.Handle))
		}

		__Get(vKey){
			Switch (vKey) {
				Case "Alpha":
					DllCall("gdiplus\GdipGetSolidFillColor", Canvas.__Ptr[0], this.Handle, "UInt*", c)

					Return, (Math.ToBase(c, 10, 16)[0, 2])
				Case "Color":
					DllCall("gdiplus\GdipGetSolidFillColor", Canvas.__Ptr[0], this.Handle, "UInt*", c)

					Return, (Math.ToBase(c, 10, 16)[2, 8])
			}
		}

		__Set(vKey, vValue) {
			Switch (vKey) {
				Case "Alpha":
					DllCall("gdiplus\GdipSetSolidFillColor", Canvas.__Ptr[0], this.Handle, "UInt", "0x" . vValue . this.Color)
				Case "Color":
					DllCall("gdiplus\GdipSetSolidFillColor", Canvas.__Ptr[0], this.Handle, "UInt", "0x" . this.Alpha . vValue)
			}
			Return
		}

		Clone() {
			Return, (new vCanvas.Brush(this.Alpha, this.Color))  ;! DllCall("gdiplus\GdipCloneBrush", Canvas.__Ptr[0], this.Handle, Canvas.__Ptr[1], h)
		}
	}
The Pen object actually has a ton of functions, Brush kind of got left out lol. Pen vs Brush.
User avatar
Hellbent
Posts: 2109
Joined: 23 Sep 2017, 13:34

Re: [Gdip] How to draw shapes and lines with the mouse

05 Apr 2020, 09:42

@Onimuru
Help me out here.
What does that have to do with adding a gui that allows the user to select a new color, alpha, and pen thickness?

Also, this is more than sufficient to change the actual brush and pen.

Code: Select all

Gdip_DeleteBrush( pBrush )
Gdip_DeletePen( pPen )
pPen := Gdip_CreatePen("0x" LA ColorList[Handles[LH]] , LT)      
pBrush := Gdip_BrushCreateSolid("0x" LA ColorList[Handles[LH]])
User avatar
Onimuru
Posts: 107
Joined: 08 Sep 2018, 18:35
Contact:

Re: [Gdip] How to draw shapes and lines with the mouse

05 Apr 2020, 11:47

@Hellbent

I am very sorry sir, I didn't mean to get personal. I'd have been grateful if someone pointed that out to me.
User avatar
Hellbent
Posts: 2109
Joined: 23 Sep 2017, 13:34

Re: [Gdip] How to draw shapes and lines with the mouse

05 Apr 2020, 12:12

Onimuru wrote:
05 Apr 2020, 11:47
@Hellbent
I am very sorry sir, I didn't mean to get personal. I'd have been grateful if someone pointed that out to me.
I didn't take it personal, I just was / am confused as to how the code you posted relates to changing the brush and pen properties for this tool on the fly?

Also, perhaps I should have wrote this:
Also, this is more than sufficient to change the actual brush and pen.
Like this instead:
Also, in my opinion this is more than sufficient to change the actual brush and pen. Do you have a different opinion and if so why?
as that is more to the point of the intended meaning.

So is there something that I might be missing about the code you posted and how it relates to changing the color+ of the drawing tool?
I don't see it, so that's why I ask. No tone intended, just a question.
User avatar
Onimuru
Posts: 107
Joined: 08 Sep 2018, 18:35
Contact:

Re: [Gdip] How to draw shapes and lines with the mouse

05 Apr 2020, 12:48

This is simply a more elegant and hands-free way of changing properties of a brush/pen. Please consider this example:

Code: Select all

a := new Pen()

MsgBox(a.Alpha)  ;FF
a.Alpha := "80"
MsgBox(a.Alpha)  ;80

MsgBox(a.Color)  ;FFFFFF
a.Color := "00FF80"
MsgBox(a.Color)  ;00FF80

MsgBox(a.Width)  ;1
a.Width := 3
MsgBox(a.Width)  ;3

a := new Pen()  ;__Delete(a)
MsgBox(a.Width)  ;1

ToBase(vNumber, vCurrentBase, vTargetBase) {
	Static vIsUnicode := A_IsUnicode ? ["_wcstoui64", "_i64tow"] : ["_strtoui64", "_i64toa"], vResult := VarSetCapacity(vResult, 66, 0)

	DllCall("msvcrt.dll\" . vIsUnicode[2], "Int64", DllCall("msvcrt.dll\" . vIsUnicode[1], "Str", vNumber, "UInt", 0, "UInt", vCurrentBase, "Int64"), "Str", vResult, "UInt", vTargetBase)

	Return, (vResult)
}

Class Brush {
	Static __Ptr := [p := A_PtrSize ? "UPtr" : "UInt", p . "*"]

	__New(vAlpha := "FF", vColor := "FFFFFF")  {
		DllCall("gdiplus\GdipCreateSolidFill", "UInt", "0x" . vAlpha . vColor, this.__Ptr[2], h), ObjRawSet(this, "Handle", h)
	}

	__Delete() {
		If (!this.Handle)
			MsgBox("Brush.__Delete()")

		Return, (DllCall("gdiplus\GdipDeleteBrush", this.__Ptr[1], this.Handle))
	}

	__Get(vKey){
		Switch (vKey) {
			Case "Alpha":
				DllCall("gdiplus\GdipGetSolidFillColor", this.__Ptr[1], this.Handle, "UInt*", c)

				Return, (SubStr(ToBase(c, 10, 16), 1, 2))
			Case "Color":
				DllCall("gdiplus\GdipGetSolidFillColor", this.__Ptr[1], this.Handle, "UInt*", c)

				Return, (SubStr(ToBase(c, 10, 16), 3, 6))
		}
	}

	__Set(vKey, vValue) {
		Switch (vKey) {
			Case "Alpha":
				Return, (DllCall("gdiplus\GdipSetSolidFillColor", this.__Ptr[1], this.Handle, "UInt", "0x" . vValue . this.Color))
			Case "Color":
				Return, (DllCall("gdiplus\GdipSetSolidFillColor", this.__Ptr[1], this.Handle, "UInt", "0x" . this.Alpha . vValue))
		}
		Return
	}

	Clone() {
		Return, (new vCanvas.Brush(this.Alpha, this.Color))
	}
}

Class Pen {
	Static __Ptr := [p := A_PtrSize ? "UPtr" : "UInt", p . "*"]

	__New(vAlpha := "FF", vColor := "FFFFFF", vWidth := 1)  {
		DllCall("gdiplus\GdipCreatePen1", "UInt", "0x" . vAlpha . vColor, "Float", vWidth, "Int", 2, this.__Ptr[2], h), ObjRawSet(this, "Handle", h)
	}

	__Delete() {
		If (!this.Handle)
			MsgBox("Pen.__Delete()")

		Return, (DllCall("gdiplus\GdipDeletePen", this.__Ptr[1], this.Handle))
	}

	__Get(vKey){
		Switch (vKey) {
			Case "Alpha":
				DllCall("gdiplus\GdipGetPenColor", this.__Ptr[1], this.Handle, "UInt*", c)

				Return, (SubStr(ToBase(c, 10, 16), 1, 2))
			Case "Color":
				DllCall("gdiplus\GdipGetPenColor", this.__Ptr[1], this.Handle, "UInt*", c)

				Return, (SubStr(ToBase(c, 10, 16), 3, 6))
			Case "Width":
				DllCall("gdiplus\GdipGetPenWidth", this.__Ptr[1], this.Handle, "Float*", c)

				Return, (~~c)
		}
	}

	__Set(vKey, vValue) {
		Switch (vKey) {
			Case "Alpha":
				Return, (DllCall("gdiplus\GdipSetPenColor", this.__Ptr[1], this.Handle, "UInt", "0x" . vValue . this.Color))
			Case "Color":
				Return, (DllCall("gdiplus\GdipSetPenColor", this.__Ptr[1], this.Handle, "UInt", "0x" . this.Alpha . vValue))
			Case "Width":
				Return, (DllCall("gdiplus\GdipSetPenWidth", this.__Ptr[1], this.Handle, "Float", vValue))
		}
		Return
	}

	Clone() {
		Return, (new vCanvas.Pen(this.Alpha, this.Color, this.Width))
	}
}
User avatar
Hellbent
Posts: 2109
Joined: 23 Sep 2017, 13:34

Re: [Gdip] How to draw shapes and lines with the mouse

05 Apr 2020, 15:14

@Onimuru

OK :lol:

It's a nice class, but it doesn't exactly allow this tool to change the color+ on the fly. That is what the gui does ;)
In a case like this (this tool) I don't really see a need to do more than just replacing the old brush and pen with a new one.
If anything, I might do something like this.

Code: Select all

;+++++++++++++++++++++++++++++++++++++++++++++++++++++
pBrush := New_Brush(ColorList[Handles[LH]], LA, pBrush)
pPen := New_Pen(ColorList[Handles[LH]], LA, LT, pPen)
;+++++++++++++++++++++++++++++++++++++++++++++++++++++

New_Brush(Color:="000000",Alpha:="FF",OldBrush:=""){
	local pBrush
	if(OldBrush)
		DllCall("gdiplus\GdipDeleteBrush", A_PtrSize ? "UPtr" : "UInt", OldBrush)
	DllCall("gdiplus\GdipCreateSolidFill", "UInt", "0x" Alpha Color, A_PtrSize ? "UPtr*" : "UInt*", pBrush)
	return pBrush
}
New_Pen(Color:="000000",Alpha:="FF",Thickness:=5,OldPen:=""){
	local pPen
	if(OldPen)
		DllCall("gdiplus\GdipDeletePen", A_PtrSize ? "UPtr" : "UInt", OldPen)
	DllCall("gdiplus\GdipCreatePen1", "UInt", "0x" Alpha Color, "float", Thickness, "int", 2, A_PtrSize ? "UPtr*" : "UInt*", pPen)
	return pPen
}

But even that is a bit overkill when the way it is now is more than sufficient to change the brush and pen.


Ultimately this is SpeedMasters script and he can do with it as he wishes.

At least now I know that you weren't actually taking about changing the color+ on the fly, that had me confused lol
neogna2
Posts: 590
Joined: 15 Sep 2016, 15:44

Re: [Gdip] How to draw shapes and lines with the mouse

08 Apr 2020, 09:20

Very nice script SpeedMaster! It is a bit similar to ZoomIt.

There's a small error in the tooltip text

Code: Select all

	helptext.="F1 = toggle help"
should be

Code: Select all

	helptext.="F1 = toggle Gui`n"
	helptext.="Ctrl+F1 = toggle help"
It would be cool to make it work with with Screen Clipping to let us draw on the clipping before saving it as an image.
sanmaodo
Posts: 45
Joined: 28 Aug 2020, 01:39

Re: [Gdip] How to draw shapes and lines with the mouse

17 Mar 2021, 11:44

Hello, @SpeedMaster
Your script cannot be working, on win10, is it true?
sanmaodo
Posts: 45
Joined: 28 Aug 2020, 01:39

Re: [Gdip] How to draw shapes and lines with the mouse

18 Mar 2021, 03:48

@SpeedMaster
Sorry, it works well with 32-bit ahk.
Perfect script, thank you very much!
tuzi
Posts: 223
Joined: 27 Apr 2016, 23:40

Re: [Gdip] How to draw shapes and lines with the mouse

18 Mar 2021, 05:07

great !
i love it !
LAPIII
Posts: 668
Joined: 01 Aug 2021, 06:01

Re: [Gdip] How to draw shapes and lines with the mouse

11 Oct 2022, 17:56

None of these scripts are working for me. Am I missing something, I run it just like this:

Code: Select all

;================================================================================================================================
; Subject:        Gdip Draw shapes and lines with the mouse
; Description:    Proof of concept for drawing shapes and lines with the mouse using BitBlt()
; Topic:            https://www.autohotkey.com/boards/viewtopic.php?f=6&t=74009
; Sript version:  1.0
; AHK Version:    1.1.24.03 (U32)
; Tested on:      Win 7 (x64)
; Author:         SpeedMaster
; Credits :       Special thanks to Linear Spoon (how to draw a filled rectangle)
; https://autohotkey.com/board/topic/92184-deleting-a-rectangle-or-range-created-with-gdi/
;
; Shortcuts:      Shift + left mouse = draw lines
;                 Alt + left mouse = draw filled rectangles
;                 Ctrl + left mouse = draw rectangles
;                 Win + left mouse = draw ellipses
;                 Ctrl + Z = undo last drawing 
;                 F9 = clear the screen 
;                 F10 = restore the screen
;
; other related topic: https://www.autohotkey.com/boards/viewtopic.php?f=76&t=60827&hilit=draw+on+screen

#SingleInstance force
#Include C:\Users\LPIII\Documents\AutoHotkey\Lib\GDIP_All.ahk
;*******   HB Alteration    ******
global ColorList := ["000000","7F7F7F","880015","ED1C24","FF7F27","FFF200","22B14C","00A2E8","3F48CC","A349A4","FFFFFF","C3C3C3","B97A57","FFAEC9","FFC90E","EFE4B0","B5E61D","99D9EA","7092BE","C8BFE7"]
global Handles := [],Handles2:=[],ALED,Thickness,LH,LA:="FF",LT:=5
;*********************************

SetBatchLines -1
SetMouseDelay -1 

coordmode, mouse, screen

Gui, 1: -Caption +E0x80000  +LastFound +AlwaysOnTop +ToolWindow +OwnDialogs 
Gui, 1: Show, NA
hwnd7 := WinExist() ; hwnd7 to avoid conflict ("hwnd1" name is too much used in other scripts)

Onexit, exit

;---------------------------------------------------- Gdip stuff ----------------------------------------------
Width := A_ScreenWidth, Height := A_ScreenHeight
If !pToken := Gdip_Startup()
{
	MsgBox, 48, gdiplus error!, Gdiplus failed to start. Please ensure you have gdiplus on your system
	ExitApp
}
hbm := CreateDIBSection(Width, Height)  ;screen
hdc := CreateCompatibleDC()             ;screen
obm := SelectObject(hdc, hbm)           ;screen
hbm2 := CreateDIBSection(Width, Height) ;buffer
hdc2 := CreateCompatibleDC()            ;buffer
obm2 := SelectObject(hdc2, hbm2)        ;buffer
hbm3 := CreateDIBSection(Width, Height) ;saving buffer
hdc3 := CreateCompatibleDC()            ;saving buffer
obm3 := SelectObject(hdc3, hbm3)        ;saving buffer
G := Gdip_GraphicsFromHDC(hdc)
G2 := Gdip_GraphicsFromHDC(hdc2)
Gdip_SetSmoothingMode(G, 4)
Gdip_SetSmoothingMode(G2, 4)


;------------------------------------------------  create some brushes and pencils ------------------------------------
global pPen := Gdip_CreatePen("0xFF" ColorList[1] , 5)      
, pBrush := Gdip_BrushCreateSolid("0xFF" ColorList[1])

;------------------------------------------------  Undo/Redo stuff  ----------------------------------------------------
;clear the screen
f9::
Gdip_GraphicsClear(G)  ;This sets the entire area of the graphics to 'transparent'
UpdateLayeredWindow(hwnd7, hdc, 0, 0, Width, Height)  ;This is what actually changes the display
return

;get everithing from the buffer back to the screen
f10::
BitBlt(hdc, 0, 0, Width, Height, hdc2, 0, 0)
UpdateLayeredWindow(hwnd7, hdc, 0, 0, Width, Height) 
return

; undo last drawing
^Z::
BitBlt(hdc, 0, 0, Width, Height, hdc3, 0, 0)
UpdateLayeredWindow(hwnd7, hdc3, 0, 0, Width, Height)
return

;------------------------------------------------ Help -------------------------------------------------



;------------------------------------------------  Draw rectangles  ----------------------------------------------------
;Draw rectangles
^LButton::   
CoordMode, Mouse, Screen
MouseGetPos, x1, y1
BitBlt(hdc3, 0, 0, Width, Height, hdc, 0, 0) ; save previous hdc first

while getKeyState("LButton", "P") ; draw in buffer
{
  MouseGetPos, x2, y2
  Gdip_GraphicsClear(G2)
  BitBlt(hdc2, 0, 0, Width, Height, hdc, 0, 0) ; BitBlt first before drawing
  Gdip_DrawRectangle(G2, pPen, min(x1,x2), min(y1,y2), abs(x2-x1), abs(y2-y1))
  UpdateLayeredWindow(hwnd7, hdc2, 0, 0, Width, Height)
}

  BitBlt(hdc, 0, 0, Width, Height, hdc2, 0, 0) ;copy buffer to screen
  UpdateLayeredWindow(hwnd7, hdc, 0, 0, Width, Height) ; now draw on screen
return

;----------------------------------------------  Draw a filled rectangles  --------------------------------------------
;Draw a filled rectangles
!LButton::   
CoordMode, Mouse, Screen
MouseGetPos, x1, y1
BitBlt(hdc3, 0, 0, Width, Height, hdc, 0, 0) ; save previous hdc first

while getKeyState("LButton", "P") ; draw in buffer
{
  MouseGetPos, x2, y2
  Gdip_GraphicsClear(G2)
  BitBlt(hdc2, 0, 0, Width, Height, hdc, 0, 0) ; BitBlt first before drawing
  ;FillRect seems to expect the (x,y) coordinates passed to always be the upper left corner and width,height to be positive
  Gdip_FillRectangle(G2, pBrush, min(x1,x2), min(y1,y2), abs(x2-x1), abs(y2-y1))
  UpdateLayeredWindow(hwnd7, hdc2, 0, 0, Width, Height)
}
  
  BitBlt(hdc, 0, 0, Width, Height, hdc2, 0, 0) ;copy buffer to screen
  UpdateLayeredWindow(hwnd7, hdc, 0, 0, Width, Height) ; now draw on screen
  
  min(a,b)
{
  return a < b ? a : b
}
return

;----------------------------------------------------  draw lines  -----------------------------------------------------
;draw lines
+lbutton:: 
CoordMode, Mouse, Screen
MouseGetPos, x1, y1
BitBlt(hdc3, 0, 0, Width, Height, hdc, 0, 0) ; save previous hdc first
    
while getKeyState("LButton", "P")
{
    MouseGetPos, x2, y2
    Gdip_GraphicsClear(G2)
    BitBlt(hdc2, 0, 0, Width, Height, hdc, 0, 0)
    Gdip_DrawLine(G2, pPen, x1, y1, x2, y2)
    UpdateLayeredWindow(hwnd7, hdc2, 0, 0, Width, Height)
}

BitBlt(hdc, 0, 0, Width, Height, hdc2, 0, 0) ;copy buffer to screen
UpdateLayeredWindow(hwnd7, hdc, 0, 0, Width, Height) ; now draw on screen
return

;----------------------------------------------------  Draw ellipse  -------------------------------------------------
;Draw ellipse
#LButton::   
CoordMode, Mouse, Screen
MouseGetPos, x1, y1
BitBlt(hdc3, 0, 0, Width, Height, hdc, 0, 0) ; save previous hdc first

while getKeyState("LButton", "P") ; draw in buffer
{
  MouseGetPos, x2, y2
  Gdip_GraphicsClear(G2)
  BitBlt(hdc2, 0, 0, Width, Height, hdc, 0, 0) ; BitBlt first before drawing
  ;seems to expect the (x,y) coordinates passed to always be the upper left corner and width,height to be positive
  Gdip_DrawEllipse(G2, pPen, min(x1,x2), min(y1,y2), abs(x2-x1), abs(y2-y1))
  UpdateLayeredWindow(hwnd7, hdc2, 0, 0, Width, Height)
}
  
  BitBlt(hdc, 0, 0, Width, Height, hdc2, 0, 0) ;copy buffer to screen
  UpdateLayeredWindow(hwnd7, hdc, 0, 0, Width, Height) ; now draw on screen
  
return

exit:
SelectObject(hdc, obm)
DeleteObject(hbm)
DeleteDC(hdc)
Gdip_DeleteGraphics(G)

SelectObject(hdc2, obm2)
DeleteObject(hbm2)
DeleteDC(hdc2)
Gdip_DeleteGraphics(G2)

SelectObject(hdc3, obm3)
DeleteObject(hbm3)

Gdip_Shutdown(pToken)

exitapp
return

esc::exitapp


;*******   HB Alteration    ******
;********************************************************************************************************************************************
;********************************************************************************************************************************************
;********************************************************************************************************************************************
F1::
99GuiClose:
	helptext:="Shift + lmouse = draw lines`n"
	helptext.="Alt + lmouse = draw filled rectangles`n"
	helptext.="Ctrl + lmouse = draw rectangles`n"
	helptext.="Win + lmouse = draw ellipses`n"
	helptext.="Ctrl + Z = undo last drawing`n"
	helptext.="F9 = clear the screen`n" 
	helptext.="F10 = restore the screen`n"
	helptext.="F1 = toggle help"
	if(help:=!help){
		tooltip, % helptext
		if(!ft){
			ft:=1
			Gui,99:+AlwaysOnTop +ToolWindow
			Gui,99:Color,% ColorList[1], 333333
			Gui,99:Font,cWhite s8 ,Segoe UI
			y:=0,x:=m:=20,w:=m,C:="00ffff"
			Gui,99:Margin,20,20
			Loop,% ColorList.Length()	{
				if(A_Index=11)
					x:=m+w,y:=0
				y+=m
				Gui,99:Add,Text,% "x" x " y" y " w" m " h" m " hwndhwnd gChangeColor",
				Handles[hwnd] := A_Index
				Gui,99:Add,Progress,% "x" x " y" y " w" m " h" m " c" ColorList[A_Index] " Background" C " hwndhwnd"  ,100
				Handles2[A_Index]:= hwnd, C:="555555"
			}
			Gui,99:Add,Edit,xm y+1 w40 r1 Limit2 Center hwndALED gChangeBrush,FF
			Gui,99:Add,Edit,xm y+1 w40 r1 Limit2 Center hwndThickness gChangeBrush,5
			GuiControl,% "99:Focus", % hwnd
		}
		Gui,99:Show,% "x" A_ScreenWidth-250 " y150",Color
	}
	else{
		tooltip,
		Gui,99:Hide
	}
	return
ChangeColor(hwnd){
	static LC := 1, C :="00FFFF"
	LH:=hwnd
	GuiControl,% "99:Focus", % Handles2[Handles[hwnd]]
	GuiControl,% "99:+Background555555", % Handles2[LC]
	GuiControl,% "99:+Background" C, % Handles2[Handles[hwnd]]
	Gui,99:Color,% ColorList[Handles[hwnd]]
	LC := Handles[hwnd]
	ChangeBrush()
}
ChangeBrush(){
	GuiControlGet,LA,99:,% ALED
	GuiControlGet,LT,99:,% Thickness
	Gdip_DeleteBrush( pBrush )
	Gdip_DeletePen( pPen )
	pPen := Gdip_CreatePen("0x" LA ColorList[Handles[LH]] , LT)      
	pBrush := Gdip_BrushCreateSolid("0x" LA ColorList[Handles[LH]])
}
User avatar
Hellbent
Posts: 2109
Joined: 23 Sep 2017, 13:34

Re: [Gdip] How to draw shapes and lines with the mouse

11 Oct 2022, 21:24

@LAPIII The code you posted works fine for me when I set the correct path to the gdi+ lib and press the correct hotkeys.


Run the script and press F1
draw on screen 1.gif
draw on screen 1.gif (337.32 KiB) Viewed 2223 times

Edit: This is set to only function on your main monitor. In order to use it on other monitors you would need to do a number of alterations to the script.
LAPIII
Posts: 668
Joined: 01 Aug 2021, 06:01

Re: [Gdip] How to draw shapes and lines with the mouse

12 Oct 2022, 14:22

This is what I'm getting:

Program_Manager 12-10-2022 02⦂10⦂26⦂610 PM.jpg
Program_Manager 12-10-2022 02⦂10⦂26⦂610 PM.jpg (20.91 KiB) Viewed 2170 times

And the hot keys that work with the mouse don't do anything for me. I'm on my laptop and with a razor mouse but I also tried my trackpad and those with all of my other scripts turned off to make sure there's no interference. Those hotkeys for the mouse still won't work.
User avatar
Hellbent
Posts: 2109
Joined: 23 Sep 2017, 13:34

Re: [Gdip] How to draw shapes and lines with the mouse

12 Oct 2022, 14:58

@LAPIII The default color is black so I hope you aren't just trying to draw on a black background and not seeing what you are drawing lol.

I really don't know what you have going on. I don't understand why you can see the 2 edit controls but not the progress controls.

Try adding a tooltip just before this line

Code: Select all

Loop,% ColorList.Length()	{
and have it display the value of ColorList.Length() like so

Code: Select all

Tooltip, % "length: " ColorList.Length()

Loop,% ColorList.Length()	{
...

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: No registered users and 241 guests