Page 1 of 2

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

Posted: 29 Mar 2020, 17:52
by SpeedMaster
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 8023 times
If this is the wrong way or if you have another method, please let me know. :crazy:
Cheers

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

Posted: 30 Mar 2020, 03:56
by huyaowen
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 7962 times

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

Posted: 30 Mar 2020, 08:49
by Hellbent
@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]])
}

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

Posted: 31 Mar 2020, 17:26
by SpeedMaster
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

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

Posted: 31 Mar 2020, 22:15
by Hellbent
@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,
}


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

Posted: 04 Apr 2020, 10:00
by elModo7
Damn, this is useful to showcase stuff on pressentations!
Great tool!

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

Posted: 05 Apr 2020, 02:48
by Onimuru
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.

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

Posted: 05 Apr 2020, 09:42
by Hellbent
@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]])

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

Posted: 05 Apr 2020, 11:47
by Onimuru
@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.

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

Posted: 05 Apr 2020, 12:12
by Hellbent
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.

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

Posted: 05 Apr 2020, 12:48
by Onimuru
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))
	}
}

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

Posted: 05 Apr 2020, 15:14
by Hellbent
@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

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

Posted: 08 Apr 2020, 09:20
by neogna2
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.

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

Posted: 17 Mar 2021, 11:44
by sanmaodo
Hello, @SpeedMaster
Your script cannot be working, on win10, is it true?

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

Posted: 18 Mar 2021, 03:48
by sanmaodo
@SpeedMaster
Sorry, it works well with 32-bit ahk.
Perfect script, thank you very much!

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

Posted: 18 Mar 2021, 05:07
by tuzi
great !
i love it !

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

Posted: 11 Oct 2022, 17:56
by LAPIII
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]])
}

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

Posted: 11 Oct 2022, 21:24
by Hellbent
@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 2231 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.

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

Posted: 12 Oct 2022, 14:22
by LAPIII
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 2178 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.

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

Posted: 12 Oct 2022, 14:58
by Hellbent
@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()	{
...