[GDI+ Object] Subtitle.Render() - Beautiful Text on Screen

Post your working scripts, libraries and tools for AHK v1.1 and older
User avatar
moefr01
Posts: 115
Joined: 25 Nov 2015, 09:01
Location: Germany

Re: Subtitle.Render() - Show text with style (Easy, painless Gdip interpreter)

15 Sep 2017, 00:47

thanx a lot for these great examples... opened my eyes instantly! :wtf: :superhappy:
User avatar
moefr01
Posts: 115
Joined: 25 Nov 2015, 09:01
Location: Germany

Re: Subtitle.Render() - Show text with style (Easy, painless Gdip interpreter)

17 Sep 2017, 10:01

Any chance to get x and y position after dragging with left mouse? I tried it with postmessage, a1, 2... no success! :?:
iseahound
Posts: 1434
Joined: 13 Aug 2016, 21:04
Contact:

Re: Subtitle.Render() - Show text with style (Easy, painless Gdip interpreter)

17 Sep 2017, 13:56

I do not think so. When you send PostMessage, 0xA1, 2, you are moving the window, not the object drawn on the window.

You should be moving the object drawn on the window by changing the x and y values.

To give you an idea, you can use MouseGetPos to find your (x, y) value. When you click on the screen, save the values as x_click, y_click. Then when you are dragging, save the new values as x_drag, y_drag.
Subtract. dx := x_drag - x_click. Now add dx to the the x value you set in background. Add dy to the y value you set in background. I apologize if this isn't a clear explanation.

Keep in mind I am not trying to port Rainmeter over to AHK, even though it is possible. It's just too much work.

Even though this reminds me of string.meter
User avatar
moefr01
Posts: 115
Joined: 25 Nov 2015, 09:01
Location: Germany

Re: Subtitle.Render() - Show text with style (Easy, painless Gdip interpreter)

18 Sep 2017, 09:10

thanks for the clear explanation, iseahound... while checking out my tips'n'tricks-repository grabbed the following solution: :wtf:
after coding a little with sendmessage, 0xA1, 2 I found out that using a dummy-key for GetKeyState, status would position the objects (background and text) by dragging as expected.
So here's a tiny code for a desktop-clock using your fantastic subtitle-class for any purpose and testing... hints are welcome! :terms:

Code: Select all

; subtitleDESKTOPCLOCK - using class_Subtitle and Gdip_All (many thanks to iseahound and tic)
; draggable with CTRL + leftbutton (center-positioned)
; Exit with ESC
; ===========================================================================================
#include class_Subtitle.ahk
#include Gdip_All.ahk
CoordMode, Mouse, Screen
OnMessage(0x201, "OnLeftClick")
IfNotExist, %A_ScriptDir%\config.ini
  ExitApp
IniRead, xpos, %A_ScriptDir%\config.ini, desktopclock, xpos
IniRead, ypos, %A_ScriptDir%\config.ini, desktopclock, ypos

pToken := Gdip_Startup()
desktopclock    := new Subtitle()
font   := "Arial Black"
size   := 100
w      := size * 6
ydif   := size - 28

background      := {"x":xpos, "y":ypos, "w":"550", "h":"150", "r":"20" , "anchor":2, "color":"0x10FFFFFF"}
text            := {"x":xpos, "y":ypos, "w":w, "anchor":2, "font":font, "size":size, "color":"FFFFFFFF"}
text.outline    := {"width":"20", "color":"FF8000", "glow":"0", "glowcolor":"FFFFFFFF"}
text.dropShadow := {"blur":"8", "color":"25000000", "horizontal":"8", "vertical":"8", "strength":"100"}

SetTimer, ticktack, 992

ticktack:
text.x := background.x := xpos
text.y := background.y := ypos
FormatTime, time,, HH·mm·ss
desktopclock.Draw(time, background, text)
desktopclock.Render(time, "cNone", text)
Return

GuiEscape:
ExitApp

OnLeftClick() {
GetKeyState, status, Ctrl       ; draggable only with CTRL
If status = D                   ; while key is down...
  {
    SendMessage, 0xA1, 2        ; make draggable
    GetKeyState, status, SHIFT  ; get dummybutton-status
    If status = U               ; positioning after release dummybutton
      {
        global xpos, ypos, ydif
        MouseGetPos, dragx, dragy
        xpos := dragx
        ypos := dragy - ydif    ; repositioning mouse, centered for dragging after
        IniWrite, %xpos%, %A_ScriptDir%\config.ini, desktopclock, xpos
        IniWrite, %ypos%, %A_ScriptDir%\config.ini, desktopclock, ypos
      }
  }
}
Return
P.S.: we don't want just to copy any released application or its design, but never give up to be curious and inspired... :idea:
User avatar
moefr01
Posts: 115
Joined: 25 Nov 2015, 09:01
Location: Germany

Re: Subtitle.Render() - Show text with style (Easy, painless Gdip interpreter)

18 Sep 2017, 09:17

...content of my config.ini (has to be created and saved in script-dir :facepalm: )

Code: Select all

[desktopclock]
xpos =200
ypos =200
User avatar
kczx3
Posts: 1640
Joined: 06 Oct 2015, 21:39

Re: Subtitle.Render() - Show text with style (Easy, painless Gdip interpreter)

18 Sep 2017, 11:32

Why did you need a dummy button? I got it to work without. It still flashes someones when you release the left mouse button, but I imagine that is due to the timer and gosub clashing or something.

Code: Select all

; subtitleDESKTOPCLOCK - using class_Subtitle and Gdip_All (many thanks to iseahound and tic)
; draggable with CTRL + leftbutton (center-positioned)
; Exit with ESC
; ===========================================================================================
#include class_Subtitle.ahk
#include Gdip_All.ahk
CoordMode, Mouse, Screen
Global xpos, ypos
OnMessage(0x201, "OnLeftClick")
IfNotExist, %A_ScriptDir%\config.ini
	xpos := 400, ypos := 200
Else {
	IniRead, xpos, %A_ScriptDir%\config.ini, desktopclock, xpos
	IniRead, ypos, %A_ScriptDir%\config.ini, desktopclock, ypos
}

pToken := Gdip_Startup()
desktopclock    := new Subtitle()
font   := "Arial Black"
size   := 100
w      := size * 6
ydif   := size - 28

background      := {"x":xpos, "y":ypos, "w":"550", "h":"150", "r":"20" , "anchor":2, "color":"0x10FFFFFF"}
text            := {"x":xpos, "y":ypos, "w":w, "anchor":2, "font":font, "size":size, "color":"FFFFFFFF"}
text.outline    := {"width":"20", "color":"FF8000", "glow":"0", "glowcolor":"FFFFFFFF"}
text.dropShadow := {"blur":"8", "color":"25000000", "horizontal":"8", "vertical":"8", "strength":"100"}

SetTimer, ticktack, 992

ticktack:
	text.x := background.x := xpos
	text.y := background.y := ypos
	FormatTime, time,, HH:mm:ss
	desktopclock.Draw(time, background, text)
	desktopclock.Render(time, "cNone", text)
Return

GuiEscape:
ExitApp

OnLeftClick() {
	MouseGetPos, xClick, yClick     ; Where did we click on the screen
	GetKeyState, status, Ctrl       ; draggable only with CTRL
	If (status = "D") {             ; while key is down...
	    SendMessage, 0xA1, 2        ; make draggable
		MouseGetPos, dragx, dragy
		xpos := dragx - (xclick - xpos) ; Get our x-offset on the rendering
		ypos := dragy - (yClick - ypos) ; Get our y-offset on the rendering
	
		; Save the values
	    IniWrite, %xpos%, %A_ScriptDir%\config.ini, desktopclock, xpos
	    IniWrite, %ypos%, %A_ScriptDir%\config.ini, desktopclock, ypos
	
		; Update the position
		GoSub, ticktack
	}
}
Return
User avatar
moefr01
Posts: 115
Joined: 25 Nov 2015, 09:01
Location: Germany

Re: Subtitle.Render() - Show text with style (Easy, painless Gdip interpreter)

18 Sep 2017, 12:30

well improved... thanks for clean-up, kczx3
regarding the dummybutton, I stumbled over an old code and unfortunately decided to take that (crappy) one... :headwall:
Only positive vertical-movements (dragy > ypos) show no flashes... no idea, maybe someone else :?:
iseahound
Posts: 1434
Joined: 13 Aug 2016, 21:04
Contact:

Re: Subtitle.Render() - Show text with style (Easy, painless Gdip interpreter)

18 Sep 2017, 15:55

I ended up removing a lot of "extra" code

Code: Select all

; subtitleDESKTOPCLOCK - using class_Subtitle and Gdip_All (many thanks to iseahound and tic)
; draggable with CTRL + leftbutton (center-positioned)
; Exit with ESC
; ===========================================================================================
#include class_Subtitle.ahk
#include Gdip_All.ahk
CoordMode, Mouse, Screen
OnMessage(0x201, "OnLeftClick")
isClicked := 0

pToken := Gdip_Startup()
desktopclock    := new Subtitle()
font   := "Arial Black"
size   := 100

background      := {"x":400, "y":200, "anchor":2, "color":"0x10FFFFFF"}
text            := {"font":font, "size":size, "color":"FFFFFFFF"}
text.outline    := {"width":"20", "color":"FF8000", "glow":"0", "glowcolor":"FFFFFFFF"}
text.dropShadow := {"blur":"8", "color":"25000000", "horizontal":"8", "vertical":"8", "strength":"100"}

SetTimer, ticktack, 10 ; Changed to 10 milliseconds for smooth animation.
return

ticktack(){
	global desktopClock, background, text
	global xpos, ypos, isClicked, xClick, yClick

	FormatTime, time,, HH:mm:ss
	if (isClicked = 1 && GetKeyState("Ctrl")) {
		MouseGetPos, dragx, dragy
		background.x := xpos + dragx - xClick
		background.y := ypos + dragy - yClick
	}
	if (!GetKeyState("LButton"))
		isClicked := 0
	desktopclock.Render(time, background, text)
	Return
}

GuiEscape:
ExitApp

OnLeftClick() {
	global ; Make all these values global. 
	isClicked := 1
	MouseGetPos, xClick, yClick
	xpos := background.x
	ypos := background.y
}
Return
Try to avoid sendmessage, 0xA1, 2 and PostMessage, 0xA1, 2 because we want to move the Subtitle Object, not the window it is drawn on.
You don't need to specify the width or height ever, because my Subtitle class will compute that for you. Also, there's no need to ever change text x, y positions, as that is also computed for you!

I changed the render time to 10 milliseconds. Depending on your CPU, this is 10 milliseconds, 15.6 milliseconds, or 1 tick of the processor. This may eat up some CPU power, but I'm sure you can modify the code so that it only draws at 10 milliseconds a frame when it is being dragged.

Also strength doesn't do anything at the moment.
iseahound
Posts: 1434
Joined: 13 Aug 2016, 21:04
Contact:

Re: Subtitle.Render() - Show text with style (Easy, painless Gdip interpreter)

18 Sep 2017, 17:27

I've finally added some documentation.

Click on List of Methods on the main post.
User avatar
kczx3
Posts: 1640
Joined: 06 Oct 2015, 21:39

Re: Subtitle.Render() - Show text with style (Easy, painless Gdip interpreter)

19 Sep 2017, 12:17

Iseahound, your version is quite laggy imo.
iseahound
Posts: 1434
Joined: 13 Aug 2016, 21:04
Contact:

Re: Subtitle.Render() - Show text with style (Easy, painless Gdip interpreter)

19 Sep 2017, 20:06

kczx3 wrote:Iseahound, your version is quite laggy imo.
I did mention that it would eat up CPU power, as it is rendering every 10ms ≈ 100 frames per second. That's quite excessive, so it is possible to render a frame only when 1) time changes, 2) x position changes, or 3) y position changes.

Code: Select all

; subtitleDESKTOPCLOCK - using class_Subtitle and Gdip_All (many thanks to iseahound and tic)
; draggable with CTRL + leftbutton (center-positioned)
; Exit with ESC
; ===========================================================================================
#include class_Subtitle.ahk
#include <Gdip_All>
CoordMode, Mouse, Screen
OnMessage(0x201, "OnLeftClick")
isClicked := 0

pToken := Gdip_Startup()
desktopclock    := new Subtitle()
font   := "Arial Black"
size   := 100

background      := {"x":400, "y":200, "anchor":2, "color":"0x10FFFFFF"}
text            := {"font":font, "size":size, "color":"FFFFFFFF"}
text.outline    := {"width":"20", "color":"FF8000", "glow":"0", "glowcolor":"FFFFFFFF"}
text.dropShadow := {"blur":"8", "color":"25000000", "horizontal":"8", "vertical":"8", "strength":"100"}

SetTimer, ticktack, 10
return

ticktack(){
	global desktopClock, background, text
	global xpos, ypos, isClicked, xClick, yClick

	static time_past, x_past, y_past

	FormatTime, time,, HH:mm:ss
	if (isClicked = 1 && GetKeyState("Ctrl")) {
		MouseGetPos, dragx, dragy
		background.x := xpos + dragx - xClick
		background.y := ypos + dragy - yClick
	}
	if (!GetKeyState("LButton"))
		isClicked := 0

	if (time != time_past || background.x != x_past || background.y != y_past) {
		time_past := time, x_past := background.x, y_past := background.y
		desktopclock.Render(time, background, text)
	}
	Return
}

GuiEscape:
ExitApp

OnLeftClick() {
	global ; Make all these values global. 
	isClicked := 1
	MouseGetPos, xClick, yClick
	xpos := background.x
	ypos := background.y
}
Return
This should make it as efficient as the original script moefr01 posted.
User avatar
kczx3
Posts: 1640
Joined: 06 Oct 2015, 21:39

Re: Subtitle.Render() - Show text with style (Easy, painless Gdip interpreter)

20 Sep 2017, 09:00

Meh, it seems better but still laggy.
User avatar
moefr01
Posts: 115
Joined: 25 Nov 2015, 09:01
Location: Germany

Re: Subtitle.Render() - Show text with style (Easy, painless Gdip interpreter)

21 Sep 2017, 01:34

Found out that the blur-function eats most of memory (>38MB at 4% load) on my barebone (i5) while displaying with fontsize 48... increases extremly with sizes above!. So I decided to code with gdip+ and simulate the shadow with another Gdip_TextToGraphics underneath the coloured text (see pic). In addition I use sendmessage, 0xA1,2 in combination with a transparent GUI... works very smooth!
Image
There are many combinations possible with tic's great gdip+, like alphablending gradients, reliefs, etc. My current project (smartClock) is taking shape. Thanks to tic, iseahound, kczx3 for inspiration and motivation. :wave:moefr01
iseahound
Posts: 1434
Joined: 13 Aug 2016, 21:04
Contact:

Re: Subtitle.Render() - Show text with style (Easy, painless Gdip interpreter)

21 Sep 2017, 10:36

Nice! I'm looking forward to seeing how your project turns out.

If you think the blur function is slow, the glow function is much slower... Alpha Blending is one of the costliest operations to perform. If you set the blur to 0 and only set horizontal and vertical offsets the shadow renders fast with low memory usage.

Still looking for a better blur function (tic's Gdip_BlurBitmap is terribly slow) preferably in machine code.
User avatar
Relayer
Posts: 160
Joined: 30 Sep 2013, 13:09
Location: Delaware, USA

Re: Subtitle.Render() - Show text with style (Easy, painless Gdip interpreter)

08 Nov 2017, 09:44

I have added a few static variables and augmented your __New() and __Delete() to automatically start and shutdown Gdip so that the user need not be bothered.

Snippet below:

Code: Select all

static instances := []
	static pToken := ""
	past := {}, ScreenWidth := A_ScreenWidth, ScreenHeight := A_ScreenHeight

	__New(name := "")
	{
		if (Subtitle.instances.Length() = 0)
			Subtitle.pToken := Gdip_Startup()
		Subtitle.instances.Push(&this)
		
		parent := ((___ := RegExReplace(A_ThisFunc, "^(.*)\..*\..*$", "$1")) != A_ThisFunc) ? ___ : ""
		Loop, Parse, parent, .
			this.parent := (A_Index=1) ? %A_LoopField% : this.parent[A_LoopField]

		this.parent.Startup()
		Gui, New, +LastFound +AlwaysOnTop -Caption -DPIScale +E0x80000 +ToolWindow +hwndSecretName
		this.hwnd := SecretName
		this.name := (name != "") ? name "_Subtitle" : "Subtitle_" this.hwnd
		DllCall("ShowWindow", "ptr",this.hWnd, "int",8)
		DllCall("SetWindowText", "ptr",this.hWnd, "str",this.name)
		this.hbm := CreateDIBSection(this.ScreenWidth, this.ScreenHeight)
		this.hdc := CreateCompatibleDC()
		this.obm := SelectObject(this.hdc, this.hbm)
		this.G := Gdip_GraphicsFromHDC(this.hdc)
		this.colorMap := this.colorMap()
	}

	__Delete(){
		this.parent.Shutdown()
		for k, v in Subtitle.instances
			if (v = &this)
				Subtitle.instances.RemoveAt(k)
		if (Subtitle.instances.Length() = 0)
			Gdip_Shutdown(Subtitle.pToken)
	}
iseahound
Posts: 1434
Joined: 13 Aug 2016, 21:04
Contact:

Re: Subtitle.Render() - Show text with style (Easy, painless Gdip interpreter)

08 Nov 2017, 15:42

@Relayer

Here's an alternative Gdip_Startup() and Gdip_Shutdown() wrapper.

Code: Select all

   class Graphics {

      static pToken, Gdip := 0

      Startup(){
         return Graphics.pToken := (Graphics.Gdip++ > 0) ? Graphics.pToken : Gdip_Startup()
      }

      Shutdown(){
         return Graphics.pToken := (--Graphics.Gdip == 0) ? Gdip_Shutdown(Graphics.pToken) : Graphics.pToken
      }
      
      // insert Subtitle class here
   }
That's actually what this code is referring to:

Code: Select all

; Check if this class is nested within a parent class
		parent := ((___ := RegExReplace(A_ThisFunc, "^(.*)\..*\..*$", "$1")) != A_ThisFunc) ? ___ : ""
; Find the name of the parent class
		Loop, Parse, parent, .
			this.parent := (A_Index=1) ? %A_LoopField% : this.parent[A_LoopField]
; Call the startup method of the parent class
		this.parent.Startup()
The reason that I do not save the instances within class Subtitle is because I have multiple Graphics objects, and not all of them are Subtitle objects.

Still, many people will find your addition to be useful!

Warning: Calling Gdip_Startup() twice in the same AHK script may cause random crashing. That's another reason I was hesitant to include it.
burque505
Posts: 1731
Joined: 22 Jan 2017, 19:37

Re: Subtitle.Render() - Show text with style (Easy, painless Gdip interpreter)

11 Nov 2017, 13:54

Thank you very much, iseahound. I have used it in a UiPath workflow to add subtitles as part of a text-to-speech activity that also uses AHK.
https://forum.uipath.com/t/text-to-spee ... le-s/15391
Regards,
burque505
iseahound
Posts: 1434
Joined: 13 Aug 2016, 21:04
Contact:

Re: Subtitle.Render() - Beautiful Text on Screen

17 Apr 2018, 01:21

UPDATE

Some changes have been made. It's not perfect, but I was running into some limitations with the previous version that have been resolved in this version.

Some syntax for drop shadow:
horizontal:50px - denotes the horizontal offset.
vertical:50px - denotes the vertical offset.
blur:5px - blurs by 5 pixels (radius)
color:Black - changes color to black.
opacity:0.5 - opacity is at 50%. you might want to do 50% if you like integers.
size:10px - extends the edges by 10 pixels.

So dropshadow:(h:50px v:50px b:5px c:Black o:50% s:10px) would be used.

Syntax for outline:
stroke:1px - a one pixel outline does wonders to increase the readability of text without changing the font size.
color:Blue - outline color.
glow:7px - extends the edge (counting from the end of the outline) and creates a glow of 7pixels. Try to use a value less than 4 pixels if you want fast rendering.
tint:Purple - specifies the glow tint. This will give the glow a unique color.

So outline:(stroke:1px color:Blue glow:7px tint:purple) would be used.

You can drop the colons but it's harder to read. Also I increased rendering speeds by 10x. Also you don't need to call Gdip_Startup anymore. Also there's a real gaussian blur made in C.
burque505
Posts: 1731
Joined: 22 Jan 2017, 19:37

Re: Subtitle.Render() - Beautiful Text on Screen

17 Apr 2018, 11:31

That's really a great script, iseahound. Awesome.
Regards,
burque505

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: No registered users and 115 guests