I changed the way that Vis2.Ahk deals with directories to make it easier to include into other scripts:
Code: Select all
; Script: Vis2.ahk
; License: Custom
; Author: Edison Hua (iseahound)
; Date: 2022-03-03
; Version: 2.1.0
#include %A_LineFile%\..\Gdip_All.ahk
#include %A_LineFile%\..\ImagePut.ahk
#include %A_LineFile%\..\JSON.ahk
; ImageIdentify() - Label and identify objects in images.
ImageIdentify(image:="", search:="", options:=""){
return Vis2.ImageIdentify(image, search, options)
}
; OCR() - Convert pictures of text into text.
OCR(image:="", language:="", options:=""){
return Vis2.OCR(image, language, options)
}
class Vis2 {
class ImageIdentify extends Vis2.functor {
call(self, image:="", search:="", options:=""){
return (image != "") ? (new Vis2.provider.GoogleCloudVision()).ImageIdentify(image, search, options)
: Vis2.core.returnText({"provider":(new Vis2.provider.GoogleCloudVision(search)), "tooltip":"Image Identification Tool", "splashImage":true})
}
}
class OCR extends Vis2.functor {
call(self, image:="", language:="", options:=""){
return (image != "") ? (new Vis2.provider.Tesseract()).OCR(image, language, options)
: Vis2.core.returnText({"provider":(new Vis2.provider.Tesseract(language)), "tooltip":"Optical Character Recognition Tool", "textPreview":true})
}
google(){
return (image != "") ? (new Vis2.provider.Tesseract()).OCR(image, language, options).google()
: Vis2.core.returnText({"provider":(new Vis2.provider.Tesseract(language)), "tooltip":"Any selected text will be Googled.", "textPreview":true, "noCopy":true}).google()
}
}
class core {
; returnText() is a wrapper function of Vis2.core.ux.start()
; Unlike Vis2.core.ux.start(), this function will return a string of text.
returnText(obj := ""){
obj := IsObject(obj) ? obj : {}
obj.callback := "returnText"
if (Vis2.core.ux.start(obj) == "") {
while !(EXITCODE := Vis2.obj.EXITCODE)
Sleep 1
text := Vis2.obj.database
Vis2.obj.callbackConfirmed := true
text.base.google := ObjBindMethod(Vis2.Text, "google")
text.base.clipboard := ObjBindMethod(Vis2.Text, "clipboard")
return (EXITCODE > 0) ? text : ""
}
}
class ux {
; start() is the function that launches the user interface.
; This can be called directly without calling Vis2.core.returnText().
start(obj := ""){
static void := ObjBindMethod({}, {})
if (Vis2.obj != "")
return "Already in use."
Vis2.Graphics.Startup()
Vis2.stdlib.setSystemCursor(32515) ; IDC_Cross := 32515
Hotkey, LButton, % void, On
Hotkey, ^LButton, % void, On
Hotkey, !LButton, % void, On
Hotkey, +LButton, % void, On
Hotkey, RButton, % void, On
Hotkey, Escape, % void, On
Vis2.obj := IsObject(obj) ? obj : {}
Vis2.obj.EXITCODE := 0 ; 0 = in progress, -1 = escape, 1 = success
Vis2.obj.selectMode := "Quick"
Vis2.obj.area := new Vis2.Graphics.Area("Vis2_Aries", "0x7FDDDDDD")
Vis2.obj.image := new Vis2.Graphics.Image("Vis2_Kitsune").Hide()
Vis2.obj.subtitle := new Vis2.Graphics.Subtitle("Vis2_Hermes")
Vis2.obj.style1_back := {"x":"center", "y":"83%", "padding":"1.35%", "color":"DD000000", "radius":8}
Vis2.obj.style1_text := {"q":4, "size":"2.23%", "font":"Arial", "z":"Arial Narrow", "justify":"left", "color":"White"}
Vis2.obj.style2_back := {"x":"center", "y":"83%", "padding":"1.35%", "color":"FF88EAB6", "radius":8}
Vis2.obj.style2_text := {"q":4, "size":"2.23%", "font":"Arial", "z":"Arial Narrow", "justify":"left", "color":"Black"}
Vis2.obj.subtitle.render(Vis2.obj.tooltip, Vis2.obj.style1_back, Vis2.obj.style1_text)
return Vis2.core.ux.waitForUserInput()
}
waitForUserInput(){
static escape := ObjBindMethod(Vis2.core.ux, "escape")
static waitForUserInput := ObjBindMethod(Vis2.core.ux, "waitForUserInput")
static selectImage := ObjBindMethod(Vis2.core.ux.process, "selectImage")
static textPreview := ObjBindMethod(Vis2.core.ux.process, "textPreview")
if (GetKeyState("Escape", "P")) {
Vis2.obj.EXITCODE := -1
SetTimer, % escape, -9
return
}
else if (GetKeyState("LButton", "P")) {
SetTimer, % selectImage, -10
if (Vis2.obj.textPreview)
SetTimer, % textPreview, -25
else
Vis2.obj.subtitle.render("Waiting for user selection...", Vis2.obj.style2_back, Vis2.obj.style2_text)
}
else {
Vis2.obj.area.origin()
SetTimer, % waitForUserInput, -10
}
return
}
class process {
selectImage(){
static selectImage := ObjBindMethod(Vis2.core.ux.process, "selectImage")
Critical
if (GetKeyState("Escape", "P")) {
Vis2.obj.EXITCODE := -1
return Vis2.core.ux.process.finale(A_ThisFunc)
}
if (Vis2.obj.selectMode == "Quick")
Vis2.core.ux.process.selectImageQuick()
if (Vis2.obj.selectMode == "Advanced")
Vis2.core.ux.process.selectImageAdvanced()
if (Vis2.core.ux.overlap()) {
if (Vis2.obj.textPreview && Vis2.obj.dialogue != Vis2.obj.dialogue_past) {
Vis2.obj.dialogue_past := Vis2.obj.dialogue
Vis2.obj.style1_back.y := (Vis2.obj.style1_back.y == "83%") ? "2.07%" : "83%"
Vis2.obj.subtitle.render(Vis2.obj.dialogue, Vis2.obj.style1_back, Vis2.obj.style1_text)
}
else if !(Vis2.obj.textPreview) {
Vis2.obj.style2_back.y := (Vis2.obj.style2_back.y == "83%") ? "2.07%" : "83%"
Vis2.obj.subtitle.render("Still patiently waiting for user selection...", Vis2.obj.style2_back, Vis2.obj.style2_text)
}
}
if !(Vis2.obj.unlock.1 ~= "^Vis2.core.ux.process.selectImage" || Vis2.obj.unlock.2 ~= "^Vis2.core.ux.process.selectImage")
SetTimer, % selectImage, -10
return
}
selectImageQuick(){
if (GetKeyState("LButton", "P")) {
if (GetKeyState("Control", "P") || GetKeyState("Alt", "P") || GetKeyState("Shift", "P"))
Vis2.core.ux.process.selectImageTransition()
else if (GetKeyState("RButton", "P")) {
Vis2.obj.area.move()
if (!Vis2.obj.area.isMouseOnCorner() && Vis2.obj.area.isMouseStopped())
Vis2.obj.area.draw() ; Error Correction of Offset
}
else
Vis2.obj.area.draw()
}
else
Vis2.core.ux.process.finale(A_ThisFunc)
; Do not return.
}
selectImageTransition(){
static void := ObjBindMethod({}, {})
DllCall("SystemParametersInfo", "uInt",0x57, "uInt",0, "uInt",0, "uInt",0) ; RestoreCursor()
Hotkey, Space, % void, On
Hotkey, ^Space, % void, On
Hotkey, !Space, % void, On
Hotkey, +Space, % void, On
Vis2.obj.note_01 := Vis2.Graphics.Subtitle.Render("Advanced Mode", "time: 2500, xCenter y75% p1.35% cFFB1AC r8", "c000000 s2.23%")
Vis2.obj.tokenMousePressed := 1
Vis2.obj.selectMode := "Advanced" ; Exit selectImageQuick.
Vis2.obj.key := {}
Vis2.obj.action := {}
}
selectImageAdvanced(){
static void := ObjBindMethod({}, {})
if ((Vis2.obj.area.width() < -25 || Vis2.obj.area.height() < -25) && !Vis2.obj.note_02)
Vis2.obj.note_02 := Vis2.Graphics.Subtitle.Render("Press Alt + LButton to create a new selection anywhere on screen", "time: 6250, x: center, y: 67%, p1.35%, c: FCF9AF, r8", "c000000 s2.23%")
Vis2.obj.key.LButton := GetKeyState("LButton", "P") ? 1 : 0
Vis2.obj.key.RButton := GetKeyState("RButton", "P") ? 1 : 0
Vis2.obj.key.Space := GetKeyState("Space", "P") ? 1 : 0
Vis2.obj.key.Control := GetKeyState("Control", "P") ? 1 : 0
Vis2.obj.key.Alt := GetKeyState("Alt", "P") ? 1 : 0
Vis2.obj.key.Shift := GetKeyState("Shift", "P") ? 1 : 0
; Check if mouse is inside on activation.
Vis2.obj.action.Control_LButton := (Vis2.obj.area.isMouseInside() && Vis2.obj.key.Control && Vis2.obj.key.LButton)
? 1 : (Vis2.obj.key.Control && Vis2.obj.key.LButton) ? Vis2.obj.action.Control_LButton : 0
Vis2.obj.action.Shift_LButton := (Vis2.obj.area.isMouseInside() && Vis2.obj.key.Shift && Vis2.obj.key.LButton)
? 1 : (Vis2.obj.key.Shift && Vis2.obj.key.LButton) ? Vis2.obj.action.Shift_LButton : 0
Vis2.obj.action.LButton := (Vis2.obj.area.isMouseInside() && Vis2.obj.key.LButton)
? 1 : (Vis2.obj.key.LButton) ? Vis2.obj.action.LButton : 0
Vis2.obj.action.RButton := (Vis2.obj.area.isMouseInside() && Vis2.obj.key.RButton)
? 1 : (Vis2.obj.key.RButton) ? Vis2.obj.action.RButton : 0
;___|¯¯¯|___ 00011111000 Keypress
;___|_______ 0001----000 Activation Function (pseudo heaviside)
Vis2.obj.action.Control_Space := (Vis2.obj.key.Control && Vis2.obj.key.Space)
? ((!Vis2.obj.action.Control_Space) ? 1 : -1) : 0
Vis2.obj.action.Alt_Space := (Vis2.obj.key.Alt && Vis2.obj.key.Space)
? ((!Vis2.obj.action.Alt_Space) ? 1 : -1) : 0
Vis2.obj.action.Shift_Space := (Vis2.obj.key.Shift && Vis2.obj.key.Space)
? ((!Vis2.obj.action.Shift_Space) ? 1 : -1) : 0
Vis2.obj.action.Alt_LButton := (Vis2.obj.key.Alt && Vis2.obj.key.LButton)
? ((!Vis2.obj.action.Alt_LButton) ? 1 : -1) : 0
; Ensure only Space is pressed.
Vis2.obj.action.Space := (Vis2.obj.key.Space && !Vis2.obj.key.Control && !Vis2.obj.key.Alt && !Vis2.obj.key.Shift)
? ((!Vis2.obj.action.Space) ? 1 : -1) : 0
; Space Hotkeys
if (Vis2.obj.action.Control_Space = 1){
Vis2.obj.image.render(Vis2.obj.provider.getPreprocessImage(), 0.5)
Vis2.obj.image.toggleVisible()
} else if (Vis2.obj.action.Alt_Space = 1){
Vis2.obj.area.toggleCoordinates()
} else if (Vis2.obj.action.Shift_Space = 1){
} else if (Vis2.obj.action.Space = 1)
Vis2.core.ux.process.finale(A_ThisFunc) ; Exit function.
; Mouse Hotkeys
if (Vis2.obj.action.Control_LButton)
Vis2.obj.area.resizeCorners()
else if (Vis2.obj.action.Alt_LButton = 1)
Vis2.obj.area.origin()
else if (Vis2.obj.action.Alt_LButton = -1)
Vis2.obj.area.draw()
else if (Vis2.obj.action.Shift_LButton)
Vis2.obj.area.resizeEdges()
else if (Vis2.obj.action.LButton || Vis2.obj.action.RButton)
Vis2.obj.area.move()
else {
Vis2.obj.area.hover() ; Collapse Stack
if Vis2.obj.area.isMouseInside() {
Hotkey, LButton, % void, On
Hotkey, RButton, % void, On
} else {
Hotkey, LButton, % void, Off
Hotkey, RButton, % void, Off
}
}
; Do not return.
}
textPreview(bypass:=""){
static textPreview := ObjBindMethod(Vis2.core.ux.process, "textPreview")
if (!Vis2.obj.unlock.1 || bypass) {
if (coordinates := Vis2.obj.Area.ScreenshotRectangle()) {
(overlap := Vis2.core.ux.overlap()) ? Vis2.obj.subtitle.hide() : ""
pBitmap := ImagePutBitmap({screenshot: coordinates}) ; To avoid the grey tint, call Area.Hide() but this will cause flickering.
(overlap) ? Vis2.obj.subtitle.show() : ""
if !(coordinates == Vis2.obj.coordinates && Vis2.stdlib.Gdip_isBitmapEqual(pBitmap, Vis2.obj.pBitmap)) {
Gdip_DisposeImage(Vis2.obj.pBitmap)
Vis2.obj.coordinates := coordinates
Vis2.obj.pBitmap := pBitmap
; Process screenshot.
try {
if (Vis2.obj.provider.file != "")
ImagePutFile({bitmap: pBitmap}, Vis2.obj.provider.file, Vis2.obj.provider.jpegQuality)
if (Vis2.obj.provider.base64 != "")
Vis2.obj.provider.base64 := ImagePutBase64({bitmap: pBitmap}, Vis2.obj.provider.ext, Vis2.obj.provider.jpegQuality)
Vis2.obj.provider.preprocess()
if (Vis2.obj.image.isVisible() == true)
Vis2.obj.image.render(Vis2.obj.provider.getPreprocessImage(), 0.5)
Vis2.obj.provider.convert()
Vis2.obj.database := Vis2.obj.provider.getText()
}
catch e {
MsgBox, 16,, % "Exception thrown!`n`nwhat: " e.what "`nfile: " e.file
. "`nline: " e.line "`nmessage: " e.message "`nextra: " e.extra "`ncoordinates: " coordinates
}
; Retrieve three lines of text.
dialogue := ""
i := 1
Loop, Parse, % Vis2.obj.database, `r`n
{
data := RegExReplace(A_LoopField, "^\s*(.*?)\s*$", "$1")
if (data != "") {
dialogue .= (dialogue) ? ("`n" . data) : data
i++
}
} until (i > 3)
if (dialogue != "") {
Vis2.obj.firstDialogue := true
Vis2.obj.dialogue := dialogue
}
else
Vis2.obj.dialogue := (Vis2.obj.firstDialogue == true) ? "ERROR: No Text Data Found" : "Searching for text..."
if !(bypass)
Vis2.obj.Subtitle.Render(Vis2.obj.dialogue, Vis2.obj.style1_back, Vis2.obj.style1_text)
}
else {
Gdip_DisposeImage(pBitmap)
}
}
}
if (Vis2.obj.unlock.1)
return Vis2.core.ux.process.finale(A_ThisFunc)
else
SetTimer, % textPreview, -100
return
}
finale(key){
static escape := ObjBindMethod(Vis2.core.ux, "escape")
(IsObject(Vis2.obj.unlock) && key != Vis2.obj.unlock.1) ? Vis2.obj.unlock.push(key) : (Vis2.obj.unlock := [key])
if (key ~= "^Vis2.core.ux.process.selectImage") {
Vis2.obj.Area.ChangeColor(0x01FFFFFF) ; Lighten Area object, but do not hide or delete it until key up.
if (!Vis2.obj.textPreview)
Vis2.core.ux.process.textPreview("bypass")
}
if (Vis2.obj.unlock.MaxIndex() == 2) {
if (Vis2.obj.area.screenshotRectangle() != Vis2.obj.coordinates)
Vis2.core.ux.process.textPreview("bypass")
if (Vis2.obj.database != "" && Vis2.obj.EXITCODE == 0) {
if (Vis2.obj.noCopy != true) {
clipboard := Vis2.obj.database
t := 2500
if (Vis2.obj.splashImage == true) {
t := 5000
w := Gdip_GetImageWidth(Vis2.obj.pBitmap)
h := Gdip_GetImageHeight(Vis2.obj.pBitmap)
Vis2.Graphics.Subtitle.Render("", {"time":t, "x":((A_ScreenWidth-w)/2)-10, "y":((A_ScreenHeight-h)/2)-10, "w":w+20, "h":h+20, "color":"Black"})
Vis2.Graphics.Image.Render(Vis2.obj.pBitmap, 1, t).Border()
}
Vis2.obj.Subtitle.Hide()
Vis2.Graphics.Subtitle.Render(Vis2.obj.dialogue
, {"time":t, "x":"center", "y":"83%", "padding":"1.35%", "color":"Black", "radius":8}, {"q":4, "size":"2.23%", "font":"Arial", "z":"Arial Narrow", "justify":"left", "color":"White"})
Vis2.Graphics.Subtitle.Render("Saved to Clipboard.", "time: " t ", x: center, y: 75%, p: 1.35%, c: F9E486, r: 8", "c: 0x000000, s:2.23%, f:Arial")
}
Vis2.obj.EXITCODE := 1
}
Vis2.obj.EXITCODE := (Vis2.obj.EXITCODE == 0) ? -1 : Vis2.obj.EXITCODE
SetTimer, % escape, -9
}
return
}
}
escape(){
static escape := ObjBindMethod(Vis2.core.ux, "escape")
static void := ObjBindMethod({}, {})
if (Vis2.obj.callback) {
if !(Vis2.obj.callbackConfirmed) {
SetTimer, % escape, -9
return
}
}
; Delete temporary image and text files.
Gdip_DisposeImage(Vis2.obj.pBitmap)
Vis2.obj.provider.cleanup()
Vis2.obj.area.destroy()
Vis2.obj.image.destroy()
Vis2.obj.subtitle.destroy()
Vis2.obj.note_01.hide() ; Let them time out instead of Destroy()
Vis2.obj.note_02.destroy()
Vis2.obj := "" ; Goodbye all, you were loved :c
Vis2.Graphics.Shutdown()
; Fixes a bug where AHK does not detect key releases if there is an admin-level window beneath.
if WinActive("ahk_id" Vis2.obj.area.hwnd) {
KeyWait Control
KeyWait Alt
KeyWait Shift
KeyWait RButton
KeyWait LButton
KeyWait Space
KeyWait Escape
}
Hotkey, LButton, % void, Off
Hotkey, ^LButton, % void, Off
Hotkey, !LButton, % void, Off
Hotkey, +LButton, % void, Off
Hotkey, RButton, % void, Off
Hotkey, Escape, % void, Off
Hotkey, Space, % void, Off
Hotkey, ^Space, % void, Off
Hotkey, !Space, % void, Off
Hotkey, +Space, % void, Off
return DllCall("SystemParametersInfo", "uInt",0x57, "uInt",0, "uInt",0, "uInt",0) ; RestoreCursor()
}
overlap() {
p1 := Vis2.obj.area.x1()
p2 := Vis2.obj.area.x2()
r1 := Vis2.obj.area.y1()
r2 := Vis2.obj.area.y2()
q1 := Vis2.obj.subtitle.x1()
q2 := Vis2.obj.subtitle.x2()
s1 := Vis2.obj.subtitle.y1()
s2 := Vis2.obj.subtitle.y2()
a := (p1 < q1 && q1 < p2) || (p1 < q2 && q2 < p2) || (q1 < p1 && p1 < q2) || (q1 < p2 && p2 < q2)
b := (r1 < s1 && s1 < r2) || (r1 < s2 && s2 < r2) || (s1 < r1 && r1 < s2) || (s1 < r2 && r2 < s2)
;Tooltip % a "`t" b "`n`n" p1 "`t" r1 "`n" p2 "`t" r2 "`n`n" q1 "`t" s1 "`n" q2 "`t" s2
return (a && b)
}
suspend(){
static void := ObjBindMethod({}, {})
Hotkey, LButton, % void, Off
Hotkey, ^LButton, % void, Off
Hotkey, !LButton, % void, Off
Hotkey, +LButton, % void, Off
Hotkey, RButton, % void, Off
Hotkey, Escape, % void, Off
Hotkey, Space, % void, Off
Hotkey, ^Space, % void, Off
Hotkey, !Space, % void, Off
Hotkey, +Space, % void, Off
DllCall("SystemParametersInfo", "uInt",0x57, "uInt",0, "uInt",0, "uInt",0) ; RestoreCursor
Vis2.obj.area.hide()
return
}
resume(){
Hotkey, LButton, % void, On
Hotkey, ^LButton, % void, On
Hotkey, !LButton, % void, On
Hotkey, +LButton, % void, On
Hotkey, RButton, % void, On
Hotkey, Escape, % void, On
if (Vis2.obj.selectMode == "Quick")
Vis2.stdlib.setSystemCursor(32515) ; IDC_Cross := 32515
if (Vis2.obj.selectMode == "Advanced") {
Hotkey, Space, % void, On
Hotkey, ^Space, % void, On
Hotkey, !Space, % void, On
Hotkey, +Space, % void, On
}
Vis2.obj.area.show()
return
}
}
}
class functor {
__Call(method, ByRef arg := "", args*) {
; When casting to Call(), use a new instance of the "function object"
; so as to avoid directly storing the properties(used across sub-methods)
; into the "function object" itself.
; Thanks to coco for this code. Modified by iseahound.
if IsObject(method)
return (new this).Call(method, arg, args*)
else if (method == "")
return (new this).Call(arg, args*)
}
}
class Graphics {
static pToken, Gdip := 0
Startup(){
global pToken
return Vis2.Graphics.pToken := (Vis2.Graphics.Gdip++ > 0) ? Vis2.Graphics.pToken : (pToken) ? pToken : Gdip_Startup()
}
Shutdown(){
global pToken
return Vis2.Graphics.pToken := (--Vis2.Graphics.Gdip <= 0) ? ((pToken) ? pToken : Gdip_Shutdown(Vis2.Graphics.pToken)) : Vis2.Graphics.pToken
}
Name(){
VarSetCapacity(UUID, 16, 0)
if (DllCall("rpcrt4.dll\UuidCreate", "ptr", &UUID) != 0)
return (ErrorLevel := 1) & 0
if (DllCall("rpcrt4.dll\UuidToString", "ptr", &UUID, "uint*", suuid) != 0)
return (ErrorLevel := 2) & 0
return A_TickCount "n" SubStr(StrGet(suuid), 1, 8), DllCall("rpcrt4.dll\RpcStringFree", "uint*", suuid)
}
class Area{
ScreenWidth := A_ScreenWidth, ScreenHeight := A_ScreenHeight,
action := ["base"], x := [0], y := [0], w := [1], h := [1], a := ["top left"], q := ["bottom right"]
__New(name := "", color := "0x7FDDDDDD") {
this.name := name := (name == "") ? Vis2.Graphics.Name() "_Graphics_Area" : name "_Graphics_Area"
this.color := color
Vis2.Graphics.Startup()
Gui, %name%:New, +LastFound +AlwaysOnTop -Caption -DPIScale +E0x80000 +ToolWindow +hwndSecretName, % this.name
Gui, %name%:Show, % (this.isDrawable()) ? "NoActivate" : ""
this.hwnd := SecretName
this.hbm := CreateDIBSection(this.ScreenWidth, this.ScreenHeight)
this.hdc := CreateCompatibleDC()
this.obm := SelectObject(this.hdc, this.hbm)
this.G := Gdip_GraphicsFromHDC(this.hdc)
Gdip_SetSmoothingMode(this.G, 4) ;Adds one clickable pixel to the edge.
this.pBrush := Gdip_BrushCreateSolid(this.color)
}
__Delete(){
Vis2.Graphics.Shutdown()
}
Destroy(){
Gdip_DeleteBrush(this.pBrush)
SelectObject(this.hdc, this.obm)
DeleteObject(this.hbm)
DeleteDC(this.hdc)
Gdip_DeleteGraphics(this.G)
Gui, % this.name ":Destroy"
}
Hide(){
DllCall("ShowWindow", "ptr",this.hWnd, "int",0)
}
Show(){ ; NoActivate
DllCall("ShowWindow", "ptr",this.hWnd, "int",8)
}
ToggleVisible(){
this.isVisible() ? this.Hide() : this.Show()
}
isVisible(){
return DllCall("IsWindowVisible", "ptr",this.hWnd)
}
isDrawable(win := "A"){
static WM_KEYDOWN := 0x100,
static WM_KEYUP := 0x101,
static vk_to_use := 7
; Test whether we can send keystrokes to this window.
; Use a virtual keycode which is unlikely to do anything:
PostMessage, WM_KEYDOWN, vk_to_use, 0,, % win
if !ErrorLevel
{ ; Seems best to post key-up, in case the window is keeping track.
PostMessage, WM_KEYUP, vk_to_use, 0xC0000000,, % win
return true
}
return false
}
DetectScreenResolutionChange(){
if (this.ScreenWidth != A_ScreenWidth || this.ScreenHeight != A_ScreenHeight) {
this.ScreenWidth := A_ScreenWidth, this.ScreenHeight := A_ScreenHeight
SelectObject(this.hdc, this.obm)
DeleteObject(this.hbm)
DeleteDC(this.hdc)
Gdip_DeleteGraphics(this.G)
this.hbm := CreateDIBSection(this.ScreenWidth, this.ScreenHeight)
this.hdc := CreateCompatibleDC()
this.obm := SelectObject(this.hdc, this.hbm)
this.G := Gdip_GraphicsFromHDC(this.hdc)
Gdip_SetSmoothingMode(this.G, 4)
}
}
Redraw(x, y, w, h){
Critical On
this.DetectScreenResolutionChange()
Gdip_GraphicsClear(this.G)
Gdip_FillRectangle(this.G, this.pBrush, x, y, w, h)
if (this.coordinates)
Vis2.Graphics.Subtitle.Draw("x: " x " │ y: " y " │ w: " w " │ h: " h
, {"a":"top_right", "x":"right", "y":"top", "color":"Black"}, {"font":"Lucida Sans Typewriter", "size":"1.67%"}, this.G)
UpdateLayeredWindow(this.hwnd, this.hdc, 0, 0, this.ScreenWidth, this.ScreenHeight)
Critical Off
}
ChangeColor(color){
this.color := color
Gdip_DeleteBrush(this.pBrush)
this.pBrush := Gdip_BrushCreateSolid(this.color)
this.Redraw(this.x[this.x.MaxIndex()], this.y[this.y.MaxIndex()], this.w[this.w.MaxIndex()], this.h[this.h.MaxIndex()])
}
ShowCoordinates(){
this.coordinates := true
this.Redraw(this.x[this.x.MaxIndex()], this.y[this.y.MaxIndex()], this.w[this.w.MaxIndex()], this.h[this.h.MaxIndex()])
}
HideCoordinates(){
this.coodinates := false
this.Redraw(this.x[this.x.MaxIndex()], this.y[this.y.MaxIndex()], this.w[this.w.MaxIndex()], this.h[this.h.MaxIndex()])
}
ToggleCoordinates(){
this.coordinates := !this.coordinates
this.Redraw(this.x[this.x.MaxIndex()], this.y[this.y.MaxIndex()], this.w[this.w.MaxIndex()], this.h[this.h.MaxIndex()])
}
Propagate(v){
this.a[v] := (this.a[v] == "") ? this.a[v-1] : this.a[v]
this.q[v] := (this.q[v] == "") ? this.q[v-1] : this.q[v]
this.x[v] := (this.x[v] == "") ? this.x[v-1] : this.x[v]
this.y[v] := (this.y[v] == "") ? this.y[v-1] : this.y[v]
this.w[v] := (this.w[v] == "") ? this.w[v-1] : this.w[v]
this.h[v] := (this.h[v] == "") ? this.h[v-1] : this.h[v]
}
BackPropagate(pasts){
action := this.action.pop()
a := this.a.pop()
q := this.q.pop()
x := this.x.pop()
y := this.y.pop()
w := this.w.pop()
h := this.h.pop()
dx := x - this.x[pasts-1]
dy := y - this.y[pasts-1]
dw := w - this.w[pasts-1]
dh := h - this.h[pasts-1]
i := pasts-1
while (i >= 1) {
this.x[i] += dx
this.y[i] += dy
this.w[i] += dw
this.h[i] += dh
i--
}
}
Converge(v := ""){
v := (v) ? v : this.action.MaxIndex()
if (v > 2) {
this.action := [this.action[v-1], this.action[v]]
this.a := [this.a[v-1], this.a[v]]
this.q := [this.q[v-1], this.q[v]]
this.x := [this.x[v-1], this.x[v]]
this.y := [this.y[v-1], this.y[v]]
this.w := [this.w[v-1], this.w[v]]
this.h := [this.h[v-1], this.h[v]]
}
}
Debug(function){
v := (v) ? v : this.action.MaxIndex()
Tooltip % function "`t" v . "`n" v-1 ": " this.action[v-1]
. "`n" this.x[v-2] ", " this.y[v-2] ", " this.w[v-2] ", " this.h[v-2]
. "`n" this.x[v-1] ", " this.y[v-1] ", " this.w[v-1] ", " this.h[v-1]
. "`n" this.x[v] ", " this.y[v] ", " this.w[v] ", " this.h[v]
. "`nAnchor:`t" this.a[v] "`nMouse:`t" this.q[v] "`t" this.isMouseInside()
}
Hover(){
CoordMode, Mouse, Screen
MouseGetPos, x_hover, y_hover
this.x_hover := x_hover
this.y_hover := y_hover
; Resets the stack to 1.
if (A_ThisFunc != this.action[this.action.MaxIndex()]){
this.action := [A_ThisFunc]
this.a := [this.a.pop()]
this.q := [this.q.pop()]
this.x := [this.x.pop()]
this.y := [this.y.pop()]
this.w := [this.w.pop()]
this.h := [this.h.pop()]
}
}
Origin(v := ""){
CoordMode, Mouse, Screen
MouseGetPos, x_mouse, y_mouse
if (A_ThisFunc != this.action[this.action.MaxIndex()]){
this.action.push(A_ThisFunc)
this.x_hover := x_mouse
this.y_hover := y_mouse
}
v := (v) ? v : this.action.MaxIndex()
if (x_mouse != this.x_last || y_mouse != this.y_last) {
this.x_last := x_mouse, this.y_last := y_mouse
this.x[v] := x_mouse
this.y[v] := y_mouse
this.Propagate(v)
this.Redraw(x_mouse, y_mouse, 1, 1) ;stabilize x/y corrdinates in window spy.
}
}
Draw(v := ""){
CoordMode, Mouse, Screen
MouseGetPos, x_mouse, y_mouse
if (A_ThisFunc == this.action[this.action.MaxIndex()-1]){
this.BackPropagate(this.action.MaxIndex())
this.x_hover := x_mouse
this.y_hover := y_mouse
pass := 1
}
if (A_ThisFunc != this.action[this.action.MaxIndex()]){
this.Converge()
this.action.push(A_ThisFunc)
this.x_hover := x_mouse
this.y_hover := y_mouse
pass := 1
}
v := (v) ? v : this.action.MaxIndex()
dx := x_mouse - this.x_hover
dy := y_mouse - this.y_hover
xr := (x_mouse > this.x[v-1]) ? 1 : 0
yr := (y_mouse > this.y[v-1]) ? 1 : 0
if (pass == 1 || x_mouse != this.x_last || y_mouse != this.y_last) {
this.x_last := x_mouse, this.y_last := y_mouse
this.x[v] := (xr) ? this.x[v-1] : x_mouse
this.y[v] := (yr) ? this.y[v-1] : y_mouse
this.w[v] := (xr) ? x_mouse - this.x[v-1] : this.x[v-1] - x_mouse
this.h[v] := (yr) ? y_mouse - this.y[v-1] : this.y[v-1] - y_mouse
this.a[v] := (xr && yr) ? "top left" : (xr && !yr) ? "bottom left" : (!xr && yr) ? "top right" : "bottom right"
this.q[v] := (xr && yr) ? "bottom right" : (xr && !yr) ? "top right" : (!xr && yr) ? "bottom left" : "top left"
this.Propagate(v)
this.Redraw(this.x[v], this.y[v], this.w[v], this.h[v])
}
}
Move(v := ""){
CoordMode, Mouse, Screen
MouseGetPos, x_mouse, y_mouse
if (A_ThisFunc != this.action[this.action.MaxIndex()]){
this.Converge()
this.action.push(A_ThisFunc)
this.x_hover := x_mouse
this.y_hover := y_mouse
pass := 1
}
v := (v) ? v : this.action.MaxIndex()
dx := x_mouse - this.x_hover
dy := y_mouse - this.y_hover
if (pass == 1 || x_mouse != this.x_last || y_mouse != this.y_last) {
this.x_last := x_mouse, this.y_last := y_mouse
this.x[v] := this.x[v-1] + dx
this.y[v] := this.y[v-1] + dy
this.Propagate(v)
this.Redraw(this.x[v], this.y[v], this.w[v], this.h[v])
}
}
ResizeCorners(v := ""){
CoordMode, Mouse, Screen
MouseGetPos, x_mouse, y_mouse
if (A_ThisFunc != this.action[this.action.MaxIndex()]){
this.Converge()
this.action.push(A_ThisFunc)
this.x_hover := x_mouse
this.y_hover := y_mouse
pass := 1
}
v := (v) ? v : this.action.MaxIndex()
xr := this.x_hover - this.x[v-1] - (this.w[v-1] / 2)
yr := this.y[v-1] - this.y_hover + (this.h[v-1] / 2) ; Keep Change Change
dx := x_mouse - this.x_hover
dy := y_mouse - this.y_hover
if (pass == 1 || x_mouse != this.x_last || y_mouse != this.y_last) {
this.x_last := x_mouse, this.y_last := y_mouse
if (xr < -1 && yr > 1) {
r := "top left"
this.x[v] := this.x[v-1] + dx
this.y[v] := this.y[v-1] + dy
this.w[v] := this.w[v-1] - dx
this.h[v] := this.h[v-1] - dy
}
if (xr >= -1 && yr > 1) {
r := "top right"
this.x[v] := this.x[v-1]
this.y[v] := this.y[v-1] + dy
this.w[v] := this.w[v-1] + dx
this.h[v] := this.h[v-1] - dy
}
if (xr < -1 && yr <= 1) {
r := "bottom left"
this.x[v] := this.x[v-1] + dx
this.y[v] := this.y[v-1]
this.w[v] := this.w[v-1] - dx
this.h[v] := this.h[v-1] + dy
}
if (xr >= -1 && yr <= 1) {
r := "bottom right"
this.x[v] := this.x[v-1]
this.y[v] := this.y[v-1]
this.w[v] := this.w[v-1] + dx
this.h[v] := this.h[v-1] + dy
}
this.Propagate(v)
this.Redraw(this.x[v], this.y[v], this.w[v], this.h[v])
}
}
; This works by finding the line equations of the diagonals of the rectangle.
; To identify the quadrant the cursor is located in, the while loop compares it's y value
; with the function line values f(x) = m * xr and y = -m * xr.
; So if yr is below both theoretical y values, then we know it's in the bottom quadrant.
; Be careful with this code, it converts the y plane inversely to match the Decartes tradition.
; Safety features include checking for past values to prevent flickering
; Sleep statements are required in every while loop.
ResizeEdges(v := ""){
CoordMode, Mouse, Screen
MouseGetPos, x_mouse, y_mouse
if (A_ThisFunc != this.action[this.action.MaxIndex()]){
this.Converge()
this.action.push(A_ThisFunc)
this.x_hover := x_mouse
this.y_hover := y_mouse
pass := 1
}
v := (v) ? v : this.action.MaxIndex()
m := -(this.h[v-1] / this.w[v-1]) ; slope (dy/dx)
xr := this.x_hover - this.x[v-1] - (this.w[v-1] / 2) ; draw a line across the center
yr := this.y[v-1] - this.y_hover + (this.h[v-1] / 2) ; draw a vertical line halfing it
dx := x_mouse - this.x_hover
dy := y_mouse - this.y_hover
if (pass == 1 || x_mouse != this.x_last || y_mouse != this.y_last) {
this.x_last := x_mouse, this.y_last := y_mouse
if (m * xr >= yr && yr > -m * xr)
r := "left", this.x[v] := this.x[v-1] + dx, this.w[v] := this.w[v-1] - dx
if (m * xr < yr && yr > -m * xr)
r := "top", this.y[v] := this.y[v-1] + dy, this.h[v] := this.h[v-1] - dy
if (m * xr < yr && yr <= -m * xr)
r := "right", this.w[v] := this.w[v-1] + dx
if (m * xr >= yr && yr <= -m * xr)
r := "bottom", this.h[v] := this.h[v-1] + dy
this.Propagate(v)
this.Redraw(this.x[v], this.y[v], this.w[v], this.h[v])
}
}
isMouseInside(){
CoordMode, Mouse, Screen
MouseGetPos, x_mouse, y_mouse
return (x_mouse >= this.x[this.x.MaxIndex()]
&& x_mouse <= this.x[this.x.MaxIndex()] + this.w[this.w.MaxIndex()]
&& y_mouse >= this.y[this.y.MaxIndex()]
&& y_mouse <= this.y[this.y.MaxIndex()] + this.h[this.h.MaxIndex()])
}
isMouseOutside(){
return !this.isMouseInside()
}
isMouseOnCorner(){
CoordMode, Mouse, Screen
MouseGetPos, x_mouse, y_mouse
return (x_mouse == this.x[this.x.MaxIndex()] || x_mouse == this.x[this.x.MaxIndex()] + this.w[this.w.MaxIndex()])
&& (y_mouse == this.y[this.y.MaxIndex()] || y_mouse == this.y[this.y.MaxIndex()] + this.h[this.h.MaxIndex()])
}
isMouseOnEdge(){
CoordMode, Mouse, Screen
MouseGetPos, x_mouse, y_mouse
return ((x_mouse >= this.x[this.x.MaxIndex()] && x_mouse <= this.x[this.x.MaxIndex()] + this.w[this.w.MaxIndex()])
&& (y_mouse == this.y[this.y.MaxIndex()] || y_mouse == this.y[this.y.MaxIndex()] + this.h[this.h.MaxIndex()]))
OR ((y_mouse >= this.y[this.y.MaxIndex()] && y_mouse <= this.y[this.y.MaxIndex()] + this.h[this.h.MaxIndex()])
&& (x_mouse == this.x[this.x.MaxIndex()] || x_mouse == this.x[this.x.MaxIndex()] + this.w[this.w.MaxIndex()]))
}
isMouseStopped(){
CoordMode, Mouse, Screen
MouseGetPos, x_mouse, y_mouse
return x_mouse == this.x_last && y_mouse == this.y_last
}
ScreenshotRectangle(){
x := this.x1(), y := this.y1(), w := this.width(), h := this.height()
return (w > 0 && h > 0) ? [x, y, w, h] : ""
}
x1(){
return this.x[this.x.MaxIndex()]
}
x2(){
return this.x[this.x.MaxIndex()] + this.w[this.w.MaxIndex()]
}
y1(){
return this.y[this.y.MaxIndex()]
}
y2(){
return this.y[this.y.MaxIndex()] + this.h[this.h.MaxIndex()]
}
width(){
return this.w[this.w.MaxIndex()]
}
height(){
return this.h[this.h.MaxIndex()]
}
}
Class CustomFont{
/*
CustomFont v2.00 (2016-2-24) by tmplinshi
---------------------------------------------------------
Description: Load font from file or resource, without needed install to system.
---------------------------------------------------------
Useage Examples:
* Load From File
font1 := New CustomFont("ewatch.ttf")
Gui, Font, s100, ewatch
* Load From Resource
Gui, Add, Text, HWNDhCtrl w400 h200, 12345
font2 := New CustomFont("res:ewatch.ttf", "ewatch", 80) ; <- Add a res: prefix to the resource name.
font2.ApplyTo(hCtrl)
* The fonts will removed automatically when script exits.
To remove a font manually, just clear the variable (e.g. font1 := "").
*/
static FR_PRIVATE := 0x10
__New(FontFile, FontName="", FontSize=30) {
if RegExMatch(FontFile, "i)res:\K.*", _FontFile) {
this.AddFromResource(_FontFile, FontName, FontSize)
} else {
this.AddFromFile(FontFile)
}
}
AddFromFile(FontFile) {
DllCall( "AddFontResourceEx", "Str", FontFile, "UInt", this.FR_PRIVATE, "UInt", 0 )
this.data := FontFile
}
AddFromResource(ResourceName, FontName, FontSize = 30) {
static FW_NORMAL := 400, DEFAULT_CHARSET := 0x1
nSize := this.ResRead(fData, ResourceName)
fh := DllCall( "AddFontMemResourceEx", "Ptr", &fData, "UInt", nSize, "UInt", 0, "UIntP", nFonts )
hFont := DllCall( "CreateFont", Int,FontSize, Int,0, Int,0, Int,0, UInt,FW_NORMAL, UInt,0
, Int,0, Int,0, UInt,DEFAULT_CHARSET, Int,0, Int,0, Int,0, Int,0, Str,FontName )
this.data := {fh: fh, hFont: hFont}
}
ApplyTo(hCtrl) {
SendMessage, 0x30, this.data.hFont, 1,, ahk_id %hCtrl%
}
__Delete() {
if IsObject(this.data) {
DllCall( "RemoveFontMemResourceEx", "UInt", this.data.fh )
DllCall( "DeleteObject" , "UInt", this.data.hFont )
} else {
DllCall( "RemoveFontResourceEx" , "Str", this.data, "UInt", this.FR_PRIVATE, "UInt", 0 )
}
}
; ResRead() By SKAN, from http://www.autohotkey.com/board/topic/57631-crazy-scripting-resource-only-dll-for-dummies-36l-v07/?p=609282
ResRead( ByRef Var, Key ) {
VarSetCapacity( Var, 128 ), VarSetCapacity( Var, 0 )
If ! ( A_IsCompiled ) {
FileGetSize, nSize, %Key%
FileRead, Var, *c %Key%
Return nSize
}
If hMod := DllCall( "GetModuleHandle", UInt,0 )
If hRes := DllCall( "FindResource", UInt,hMod, Str,Key, UInt,10 )
If hData := DllCall( "LoadResource", UInt,hMod, UInt,hRes )
If pData := DllCall( "LockResource", UInt,hData )
Return VarSetCapacity( Var, nSize := DllCall( "SizeofResource", UInt,hMod, UInt,hRes ) )
, DllCall( "RtlMoveMemory", Str,Var, UInt,pData, UInt,nSize )
Return 0
}
}
class Image{
ScreenWidth := A_ScreenWidth, ScreenHeight := A_ScreenHeight
__New(name := "") {
this.name := name := (name == "") ? Vis2.Graphics.Name() "_Graphics_Image" : name "_Graphics_Image"
Vis2.Graphics.Startup()
Gui, %name%: New, +LastFound +AlwaysOnTop -Caption -DPIScale +E0x80000 +ToolWindow +hwndSecretName, % this.name
this.hwnd := SecretName
DllCall("ShowWindow", "ptr",this.hwnd, "int",8)
this.hbm := CreateDIBSection(A_ScreenWidth, A_ScreenHeight)
this.hdc := CreateCompatibleDC()
this.obm := SelectObject(this.hdc, this.hbm)
this.G := Gdip_GraphicsFromHDC(this.hdc)
Gdip_SetInterpolationMode(this.G, 7)
}
__Delete(){
Vis2.Graphics.Shutdown()
}
Border() {
Gui, % this.name ": +Border"
UpdateLayeredWindow(this.hwnd, this.hdc, (A_ScreenWidth-this.w)/2, (A_ScreenHeight-this.h)/2, this.w, this.h)
return this
}
Destroy() {
SelectObject(this.hdc, this.obm)
DeleteObject(this.hbm)
DeleteDC(this.hdc)
Gdip_DeleteGraphics(this.G)
Gui, % this.name ":Destroy"
return this
}
Hide() {
Gui, % this.name ":Show", Hide
return this
}
Show() {
Gui, % this.name ":Show", NoActivate
return this
}
ToggleVisible() {
if DllCall("IsWindowVisible", "UInt", this.hwnd)
Gui, % this.name ":Show", Hide
else
Gui, % this.name ":Show", NoActivate
return this
}
isVisible() {
return DllCall("IsWindowVisible", "UInt", this.hwnd)
}
Render(file, scale := 1, time := 0) {
if (this.hwnd){
this.scale := scale
if (time) {
self_destruct := ObjBindMethod(this, "Destroy")
SetTimer, % self_destruct, % -1 * time
}
Critical On
if FileExist(file)
f := pBitmap := Gdip_CreateBitmapFromFile(file)
else pBitmap := file
Width := Gdip_GetImageWidth(pBitmap), Height := Gdip_GetImageHeight(pBitmap)
this.DetectScreenResolutionChange(Width, Height)
Gdip_DrawImage(this.G, pBitmap, 0, 0, Floor(Width*scale), Floor(Height*scale), 0, 0, Width, Height)
UpdateLayeredWindow(this.hwnd, this.hdc, 0, 0, Floor(Width*scale), Floor(Height*scale))
this.w := Floor(Width*scale)
this.h := Floor(Height*scale)
if f
Gdip_DisposeImage(pBitmap)
Critical Off
return this
}
else {
parent := ((___ := RegExReplace(A_ThisFunc, "^(.*)\..*\..*$", "$1")) != A_ThisFunc) ? ___ : ""
Loop, Parse, parent, .
parent := (A_Index=1) ? %A_LoopField% : parent[A_LoopField]
_image := (parent) ? new parent.image() : new image()
return _image.Render(file, scale, time)
}
}
DetectScreenResolutionChange(w:="", h:=""){
w := (w) ? w : A_ScreenWidth
h := (h) ? h : A_ScreenHeight
if (this.ScreenWidth != w || this.ScreenHeight != h) {
this.ScreenWidth := w, this.ScreenHeight := h
SelectObject(this.hdc, this.obm)
DeleteObject(this.hbm)
DeleteDC(this.hdc)
Gdip_DeleteGraphics(this.G)
this.hbm := CreateDIBSection(this.ScreenWidth, this.ScreenHeight)
this.hdc := CreateCompatibleDC()
this.obm := SelectObject(this.hdc, this.hbm)
this.G := Gdip_GraphicsFromHDC(this.hdc)
Gdip_SetInterpolationMode(this.G, 7)
}
}
}
class Subtitle{
layers := {}, ScreenWidth := A_ScreenWidth, ScreenHeight := A_ScreenHeight
__New(name := ""){
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()
}
FreeMemory(){
SelectObject(this.hdc, this.obm)
DeleteObject(this.hbm)
DeleteDC(this.hdc)
Gdip_DeleteGraphics(this.G)
return this
}
Destroy(){
this.FreeMemory()
DllCall("DestroyWindow", "ptr",this.hwnd)
return this
}
Hide(){
DllCall("ShowWindow", "ptr",this.hwnd, "int",0)
return this
}
Show(){
DllCall("ShowWindow", "ptr",this.hwnd, "int",8)
return this
}
ToggleVisible(){
this.isVisible() ? this.Hide() : this.Show()
return this
}
isVisible(){
return DllCall("IsWindowVisible", "ptr",this.hwnd)
}
ClickThrough(){
DetectHiddenWindows On
WinSet, ExStyle, +0x20, % "ahk_id" this.hwnd
DetectHiddenWindows Off
return this
}
DetectScreenResolutionChange(w:="", h:=""){
w := (w) ? w : A_ScreenWidth
h := (h) ? h : A_ScreenHeight
if (this.ScreenWidth != w || this.ScreenHeight != h) {
this.ScreenWidth := w, this.ScreenHeight := h
SelectObject(this.hdc, this.obm)
DeleteObject(this.hbm)
DeleteDC(this.hdc)
Gdip_DeleteGraphics(this.G)
this.hbm := CreateDIBSection(this.ScreenWidth, this.ScreenHeight)
this.hdc := CreateCompatibleDC()
this.obm := SelectObject(this.hdc, this.hbm)
this.G := Gdip_GraphicsFromHDC(this.hdc)
}
}
Draw(text := "", style1 := "", style2 := "", pGraphics := "") {
if (pGraphics == "") {
pGraphics := this.G
if (this.rendered == true) {
this.rendered := false
this.layers := {}
this.x := this.y := this.xx := this.yy := ""
Gdip_GraphicsClear(this.G)
}
this.layers.push([text, style1, style2])
}
; Remember styles so that they can be loaded next time.
style1 := (style1) ? this.style1 := style1 : this.style1
style2 := (style2) ? this.style2 := style2 : this.style2
static q1 := "i)^.*?(?<!-|:|:\s)\b(?![^\(]*\))"
static q2 := "(:\s?)?\(?(?<value>(?<=\()[\s\-\da-z\.#%]+(?=\))|[\-\da-z\.#%]+).*$"
time := (style1.t) ? style1.t : (style1.time) ? style1.time
: (!IsObject(style1) && (___ := RegExReplace(style1, q1 "(t(ime)?)" q2, "${value}")) != style1) ? ___
: (style2.t) ? style2.t : (style2.time) ? style2.time
: (!IsObject(style2) && (___ := RegExReplace(style2, q1 "(t(ime)?)" q2, "${value}")) != style2) ? ___
: 0
if (time) {
self_destruct := ObjBindMethod(this, "Destroy")
SetTimer, % self_destruct, % -1 * time
}
static alpha := "^[A-Za-z]+$"
static decimal := "^(\-?\d+(\.\d*)?)$"
static integer := "^\d+$"
static percentage := "^(\-?\d+(?:\.\d*)?)%$"
static positive := "^\d+(\.\d*)?$"
if IsObject(style1){
_a := (style1.a != "") ? style1.a : style1.anchor
_x := (style1.x != "") ? style1.x : style1.left
_y := (style1.y != "") ? style1.y : style1.top
_w := (style1.w != "") ? style1.w : style1.width
_h := (style1.h != "") ? style1.h : style1.height
_r := (style1.r != "") ? style1.r : style1.radius
_c := (style1.c != "") ? style1.c : style1.color
_m := (style1.m != "") ? style1.m : style1.margin
_p := (style1.p != "") ? style1.p : style1.padding
_q := (style1.q != "") ? style1.q : (style1.quality) ? style1.quality : style1.SmoothingMode
} else {
_a := ((___ := RegExReplace(style1, q1 "(a(nchor)?)" q2, "${value}")) != style1) ? ___ : ""
_x := ((___ := RegExReplace(style1, q1 "(x|left)" q2, "${value}")) != style1) ? ___ : ""
_y := ((___ := RegExReplace(style1, q1 "(y|top)" q2, "${value}")) != style1) ? ___ : ""
_w := ((___ := RegExReplace(style1, q1 "(w(idth)?)" q2, "${value}")) != style1) ? ___ : ""
_h := ((___ := RegExReplace(style1, q1 "(h(eight)?)" q2, "${value}")) != style1) ? ___ : ""
_r := ((___ := RegExReplace(style1, q1 "(r(adius)?)" q2, "${value}")) != style1) ? ___ : ""
_c := ((___ := RegExReplace(style1, q1 "(c(olor)?)" q2, "${value}")) != style1) ? ___ : ""
_m := ((___ := RegExReplace(style1, q1 "(m(argin)?)" q2, "${value}")) != style1) ? ___ : ""
_p := ((___ := RegExReplace(style1, q1 "(p(adding)?)" q2, "${value}")) != style1) ? ___ : ""
_q := ((___ := RegExReplace(style1, q1 "(q(uality)?)" q2, "${value}")) != style1) ? ___ : ""
}
if IsObject(style2){
a := (style2.a != "") ? style2.a : style2.anchor
x := (style2.x != "") ? style2.x : style2.left
y := (style2.y != "") ? style2.y : style2.top
w := (style2.w != "") ? style2.w : style2.width
h := (style2.h != "") ? style2.h : style2.height
m := (style2.m != "") ? style2.m : style2.margin
f := (style2.f != "") ? style2.f : style2.font
s := (style2.s != "") ? style2.s : style2.size
c := (style2.c != "") ? style2.c : style2.color
b := (style2.b != "") ? style2.b : style2.bold
i := (style2.i != "") ? style2.i : style2.italic
u := (style2.u != "") ? style2.u : style2.underline
j := (style2.j != "") ? style2.j : style2.justify
n := (style2.n != "") ? style2.n : style2.noWrap
z := (style2.z != "") ? style2.z : style2.condensed
d := (style2.d != "") ? style2.d : style2.dropShadow
o := (style2.o != "") ? style2.o : style2.outline
q := (style2.q != "") ? style2.q : (style2.quality) ? style2.quality : style2.TextRenderingHint
} else {
a := ((___ := RegExReplace(style2, q1 "(a(nchor)?)" q2, "${value}")) != style2) ? ___ : ""
x := ((___ := RegExReplace(style2, q1 "(x|left)" q2, "${value}")) != style2) ? ___ : ""
y := ((___ := RegExReplace(style2, q1 "(y|top)" q2, "${value}")) != style2) ? ___ : ""
w := ((___ := RegExReplace(style2, q1 "(w(idth)?)" q2, "${value}")) != style2) ? ___ : ""
h := ((___ := RegExReplace(style2, q1 "(h(eight)?)" q2, "${value}")) != style2) ? ___ : ""
m := ((___ := RegExReplace(style2, q1 "(m(argin)?)" q2, "${value}")) != style2) ? ___ : ""
f := ((___ := RegExReplace(style2, q1 "(f(ont)?)" q2, "${value}")) != style2) ? ___ : ""
s := ((___ := RegExReplace(style2, q1 "(s(ize)?)" q2, "${value}")) != style2) ? ___ : ""
c := ((___ := RegExReplace(style2, q1 "(c(olor)?)" q2, "${value}")) != style2) ? ___ : ""
b := ((___ := RegExReplace(style2, q1 "(b(old)?)" q2, "${value}")) != style2) ? ___ : ""
i := ((___ := RegExReplace(style2, q1 "(i(talic)?)" q2, "${value}")) != style2) ? ___ : ""
u := ((___ := RegExReplace(style2, q1 "(u(nderline)?)" q2, "${value}")) != style2) ? ___ : ""
j := ((___ := RegExReplace(style2, q1 "(j(ustify)?)" q2, "${value}")) != style2) ? ___ : ""
n := ((___ := RegExReplace(style2, q1 "(n(oWrap)?)" q2, "${value}")) != style2) ? ___ : ""
z := ((___ := RegExReplace(style2, q1 "(z|condensed?)" q2, "${value}")) != style2) ? ___ : ""
d := ((___ := RegExReplace(style2, q1 "(d(ropShadow)?)" q2, "${value}")) != style2) ? ___ : ""
o := ((___ := RegExReplace(style2, q1 "(o(utline)?)" q2, "${value}")) != style2) ? ___ : ""
q := ((___ := RegExReplace(style2, q1 "(q(uality)?)" q2, "${value}")) != style2) ? ___ : ""
}
; Step 1 - Simulate string width and height, setting only the variables we need to determine it.
style += (b) ? 1 : 0 ; bold
style += (i) ? 2 : 0 ; italic
style += (u) ? 4 : 0 ; underline
style += (strike) ? 8 : 0 ; strikeout, not implemented.
s := (s ~= percentage) ? A_ScreenHeight * SubStr(s, 1, -1) / 100 : s
s := (s ~= positive) ? s : 36
q := (q >= 0 && q <= 5) ? q : 4
n := (n) ? 0x4000 | 0x1000 : 0x4000
j := (j ~= "i)cent(er|re)") ? 1 : (j ~= "i)(far|right)") ? 2 : 0
_q := (_q >= 0 && _q <= 4) ? _q : 4
Gdip_SetSmoothingMode(pGraphics, _q)
Gdip_SetTextRenderingHint(pGraphics, q) ; 4 = Anti-Alias, 5 = Cleartype
hFamily := (___ := Gdip_FontFamilyCreate(f)) ? ___ : Gdip_FontFamilyCreate("Arial")
hFont := Gdip_FontCreate(hFamily, s, style)
hFormat := Gdip_StringFormatCreate(n)
Gdip_SetStringFormatAlign(hFormat, j)
CreateRectF(RC, 0, 0, 0, 0)
ReturnRC := Gdip_MeasureString(pGraphics, Text, hFont, hFormat, RC)
ReturnRC := StrSplit(ReturnRC, "|")
; Step 2 - Define margins and padding.
_m := this.margin(_m)
_p := this.margin(_p)
m := this.margin( m)
p := this.margin( p)
; Bonus - Condense Text using a Condensed Font if simulated text width exceeds screen width.
if (___ := Gdip_FontFamilyCreate(z)) {
ExtraMargin := (_m.2 + _m.4 + _p.2 + _p.4)
if (ReturnRC[3] + ExtraMargin > A_ScreenWidth){
hFamily := Gdip_FontFamilyCreate(z)
hFont := Gdip_FontCreate(hFamily, s, style)
ReturnRC := Gdip_MeasureString(pGraphics, Text, hFont, hFormat, RC)
ReturnRC := StrSplit(ReturnRC, "|")
_w := ReturnRC[3]
}
}
; Step 3 - Define _width and _height. Do not modify with margin and padding.
_w := (_w ~= percentage) ? A_ScreenWidth * SubStr(_w, 1, -1) / 100 : _w
_h := (_h ~= percentage) ? A_ScreenHeight * SubStr(_h, 1, -1) / 100 : _h
_w := (_w ~= positive) ? _w : ReturnRC[3]
_h := (_h ~= positive) ? _h : ReturnRC[4]
; Step 4 - Define _anchor with a default value of 1.
_a := (_a = "top") ? 2 : (_a = "left") ? 4 : (_a = "right") ? 6 : (_a = "bottom") ? 8
: (_a ~= "i)top" && _a ~= "i)left") ? 1 : (_a ~= "i)top" && _a ~= "i)cent(er|re)") ? 2
: (_a ~= "i)top" && _a ~= "i)bottom") ? 3 : (_a ~= "i)cent(er|re)" && _a ~= "i)left") ? 4
: (_a ~= "i)cent(er|re)") ? 5 : (_a ~= "i)cent(er|re)" && _a ~= "i)bottom") ? 6
: (_a ~= "i)bottom" && _a ~= "i)left") ? 7 : (_a ~= "i)bottom" && _a ~= "i)cent(er|re)") ? 8
: (_a ~= "i)bottom" && _a ~= "i)right") ? 9 : (_a ~= "^[1-9]$") ? _a : 1 ; default
; Step 5 - Modify _anchor with _x and _y.
_a := (_x = "left") ? 1+(((_a-1)//3)*3) : (_x ~= "i)cent(er|re)") ? 2+(((_a-1)//3)*3) : (_x = "right") ? 3+(((_a-1)//3)*3) : _a
_a := (_y = "top") ? 1+(mod(_a-1,3)) : (_y ~= "i)cent(er|re)") ? 4+(mod(_a-1,3)) : (_y = "bottom") ? 7+(mod(_a-1,3)) : _a
; Step 6 - Define _x and _y with respect to _anchor.
_x := (_x = "left") ? 0 : (_x ~= "i)cent(er|re)") ? 0.5*A_ScreenWidth : (_x = "right") ? A_ScreenWidth : _x
_y := (_y = "top") ? 0 : (_y ~= "i)cent(er|re)") ? 0.5*A_ScreenHeight : (_y = "bottom") ? A_ScreenHeight : _y
_x := (_x ~= percentage) ? A_ScreenWidth * SubStr(_x, 1, -1) / 100 : _x
_y := (_y ~= percentage) ? A_ScreenHeight * SubStr(_y, 1, -1) / 100 : _y
_x := (_x ~= decimal) ? _x : 0
_y := (_y ~= decimal) ? _y : 0
_x -= (mod(_a-1,3) == 0) ? 0 : (mod(_a-1,3) == 1) ? _w/2 : (mod(_a-1,3) == 2) ? _w : 0
_y -= (((_a-1)//3) == 0) ? 0 : (((_a-1)//3) == 1) ? _h/2 : (((_a-1)//3) == 2) ? _h : 0
; Fractional y values might cause gdi+ slowdown.
; Round 1 - Define width and height.
w := ( w ~= percentage) ? _w * RegExReplace( w, percentage, "$1") / 100 : w
h := ( h ~= percentage) ? _h * RegExReplace( h, percentage, "$1") / 100 : h
w := ( w ~= positive) ? w : (_w) ? _w : ReturnRC[3] ;if _w = 0
h := ( h ~= positive) ? h : (_h) ? _h : ReturnRC[4]
; Round 2 - Define anchor.
a := (a = "top") ? 2 : (a = "left") ? 4 : (a = "right") ? 6 : (a = "bottom") ? 8
: (a ~= "i)top" && a ~= "i)left") ? 1 : (a ~= "i)top" && a ~= "i)cent(er|re)") ? 2
: (a ~= "i)top" && a ~= "i)bottom") ? 3 : (a ~= "i)cent(er|re)" && a ~= "i)left") ? 4
: (a ~= "i)cent(er|re)") ? 5 : (_a ~= "i)cent(er|re)" && a ~= "i)bottom") ? 6
: (a ~= "i)bottom" && a ~= "i)left") ? 7 : (a ~= "i)bottom" && a ~= "i)cent(er|re)") ? 8
: (a ~= "i)bottom" && a ~= "i)right") ? 9 : (a ~= "^[1-9]$") ? a : 1 ; default
a := ( x = "left") ? 1+((( a-1)//3)*3) : ( x ~= "i)cent(er|re)") ? 2+((( a-1)//3)*3) : ( x = "right") ? 3+((( a-1)//3)*3) : a
a := ( y = "top") ? 1+(mod( a-1,3)) : ( y ~= "i)cent(er|re)") ? 4+(mod( a-1,3)) : ( y = "bottom") ? 7+(mod( a-1,3)) : a
; Round 3 - Define x and y with respect to anchor.
x := ( x = "left") ? _x : (x ~= "i)cent(er|re)") ? _x + 0.5*_w : (x = "right") ? _x + _w : x
y := ( y = "top") ? _y : (y ~= "i)cent(er|re)") ? _y + 0.5*_h : (y = "bottom") ? _y + _h : y
x := ( x ~= percentage) ? _x + (_w * RegExReplace( x, percentage, "$1") / 100) : x
y := ( y ~= percentage) ? _y + (_h * RegExReplace( y, percentage, "$1") / 100) : y
x := ( x ~= decimal) ? x : _x
y := ( y ~= decimal) ? y : _y
x -= (mod(a-1,3) == 0) ? 0 : (mod(a-1,3) == 1) ? ReturnRC[3]/2 : (mod(a-1,3) == 2) ? ReturnRC[3] : 0
y -= (((a-1)//3) == 0) ? 0 : (((a-1)//3) == 1) ? ReturnRC[4]/2 : (((a-1)//3) == 2) ? ReturnRC[4] : 0
; Round 4 - Modify _x, _y, _w, _h with margin and padding.
if (_w && _h) {
_w += (_m.2 + _m.4 + _p.2 + _p.4) + (m.2 + m.4 + p.2 + p.4)
_h += (_m.1 + _m.3 + _p.1 + _p.3) + (m.1 + m.3 + p.1 + p.3)
_x -= (_m.1 + _p.1)
_y -= (_m.4 + _p.4)
}
; Round 5 - Modify x, y with margin and padding.
x += (m.1 + p.1)
y += (m.4 + p.4)
; Round 6 - Define radius of rounded corners.
_smaller := (_w > _h) ? _h : _w
_r := (_r ~= percentage) ? _smaller * RegExReplace(_r, percentage, "$1") / 100 : _r
_r := (_r <= _smaller / 2 && _r ~= positive) ? _r : 0
; Round 7 - Define color.
_c := this.color(_c, 0xDD424242)
c := this.color( c, 0xFFFFFFFF)
; Round 8 - Define outline and dropShadow.
o := this.outline(o)
d := this.dropShadow(d)
; Round 9 - Define Text
if (!A_IsUnicode){
nSize := DllCall("MultiByteToWideChar", "uint",0, "uint",0, "ptr",&text, "int",-1, "ptr",0, "int",0)
VarSetCapacity(wtext, nSize*2)
DllCall("MultiByteToWideChar", "uint",0, "uint",0, "ptr",&text, "int",-1, "ptr",&wtext, "int",nSize)
}
; Draw 1 - Background
if (_w && _h && _c && (_c & 0xFF000000)) {
pBrushBackground := Gdip_BrushCreateSolid(_c)
Gdip_FillRoundedRectangle(pGraphics, pBrushBackground, _x, _y, _w, _h, _r)
Gdip_DeleteBrush(pBrushBackground)
}
; Draw 2 - DropShadow
if (!d.void) {
delta := 2*d.3 + 2*o.1
offset := d.3 + o.1
if (d.3) {
pBitmap := Gdip_CreateBitmap(w + delta, h + delta)
pGraphicsDropShadow := Gdip_GraphicsFromImage(pBitmap)
Gdip_SetSmoothingMode(pGraphicsDropShadow, _q)
Gdip_SetTextRenderingHint(pGraphicsDropShadow, q)
CreateRectF(RC, offset, offset, w + delta, h + delta)
} else {
CreateRectF(RC, x + d.1, y + d.2, w, h)
pGraphicsDropShadow := pGraphics
}
if (!o.void)
{
DllCall("gdiplus\GdipCreatePath", "int",1, "uptr*",pPath)
DllCall("gdiplus\GdipAddPathString", "ptr",pPath, "ptr", A_IsUnicode ? &text : &wtext, "int",-1
, "ptr",hFamily, "int",style, "float",s, "ptr",&RC, "ptr",hFormat)
pPen := Gdip_CreatePen(d.4, o.1)
DllCall("gdiplus\GdipSetPenLineJoin", "ptr",pPen, "uInt",2)
DllCall("gdiplus\GdipDrawPath", "ptr",pGraphicsDropShadow, "ptr",pPen, "ptr",pPath)
Gdip_DeletePen(pPen)
pBrush := Gdip_BrushCreateSolid(d.4)
Gdip_SetCompositingMode(pGraphicsDropShadow, 1) ; Turn off alpha blending
Gdip_SetSmoothingMode(pGraphicsDropShadow, 3) ; Turn off anti-aliasing
Gdip_FillPath(pGraphicsDropShadow, pBrush, pPath)
Gdip_DeleteBrush(pBrush)
Gdip_DeletePath(pPath)
Gdip_SetCompositingMode(pGraphicsDropShadow, 0)
Gdip_SetSmoothingMode(pGraphicsDropShadow, _q)
}
else
{
pBrush := Gdip_BrushCreateSolid(d.4)
Gdip_DrawString(pGraphicsDropShadow, Text, hFont, hFormat, pBrush, RC)
Gdip_DeleteBrush(pBrush)
}
if (d.3) {
Gdip_DeleteGraphics(pGraphicsDropShadow)
pBlur := Gdip_BlurBitmap(pBitmap, d.3)
Gdip_DisposeImage(pBitmap)
Gdip_DrawImage(pGraphics, pBlur, x + d.1 - offset, y + d.2 - offset, w + delta, h + delta)
Gdip_DisposeImage(pBlur)
}
}
; Draw 3 - Text Outline
if (!o.void) {
; Convert our text to a path.
CreateRectF(RC, x, y, w, h)
DllCall("gdiplus\GdipCreatePath", "int",1, "uptr*",pPath)
DllCall("gdiplus\GdipAddPathString", "ptr",pPath, "ptr", A_IsUnicode ? &text : &wtext, "int",-1
, "ptr",hFamily, "int",style, "float",s, "ptr",&RC, "ptr",hFormat)
; Create a pen.
pPen := Gdip_CreatePen(o.2, o.1)
DllCall("gdiplus\GdipSetPenLineJoin", "ptr",pPen, "uint",2)
; Create a glow effect around the edges.
if (o.3) {
DllCall("gdiplus\GdipClonePath", "ptr",pPath, "uptr*",pPathGlow)
DllCall("gdiplus\GdipWidenPath", "ptr",pPathGlow, "ptr",pPen, "ptr",0, "float",1)
; Set color to glowColor or use the previous color.
color := (o.4) ? o.4 : o.2
loop % o.3
{
ARGB := Format("0x{:02X}",((color & 0xFF000000) >> 24)/o.3) . Format("{:06X}",(color & 0x00FFFFFF))
pPenGlow := Gdip_CreatePen(ARGB, A_Index)
DllCall("gdiplus\GdipSetPenLineJoin", "ptr",pPenGlow, "uInt",2)
DllCall("gdiplus\GdipDrawPath", "ptr",pGraphics, "ptr",pPenGlow, "ptr",pPathGlow)
Gdip_DeletePen(pPenGlow)
}
Gdip_DeletePath(pPathGlow)
}
; Draw outline text.
if (o.1)
DllCall("gdiplus\GdipDrawPath", "ptr",pGraphics, "ptr",pPen, "ptr",pPath)
; Fill outline text.
if (c && (c & 0xFF000000)) {
pBrush := Gdip_BrushCreateSolid(c)
Gdip_FillPath(pGraphics, pBrush, pPath)
Gdip_DeleteBrush(pBrush)
}
Gdip_DeletePen(pPen)
Gdip_DeletePath(pPath)
}
; Draw Text
if (text != "" && d.void && o.void) {
CreateRectF(RC, x, y, w, h)
pBrushText := Gdip_BrushCreateSolid(c)
Gdip_DrawString(pGraphics, text, hFont, hFormat, pBrushText, RC)
Gdip_DeleteBrush(pBrushText)
}
; Complete
Gdip_DeleteStringFormat(hFormat)
Gdip_DeleteFont(hFont)
Gdip_DeleteFontFamily(hFamily)
; Correct Offsets
_w := (_w == 0) ? (ReturnRC[3] + d.1 + 2*d.3 + 2*o.1 + 2*o.3) : _w
_h := (_h == 0) ? (ReturnRC[4] + d.2 + 2*d.3 + 2*o.1 + 2*o.3) : _h
this.x := (this.x = "" || _x < this.x) ? _x : this.x
this.y := (this.y = "" || _y < this.y) ? _y : this.y
this.xx := (this.xx = "" || _x + _w > this.xx) ? _x + _w : this.xx
this.yy := (this.yy = "" || _y + _h > this.yy) ? _y + _h : this.yy
return
}
Render(text := "", style1 := "", style2 := "", update := 1){
if (this.hWnd){
Critical On
this.DetectScreenResolutionChange()
this.Draw(text, style1, style2)
if (update)
UpdateLayeredWindow(this.hwnd, this.hdc, 0, 0, A_ScreenWidth, A_ScreenHeight)
this.rendered := true
Critical Off
return this
}
else {
parent := ((___ := RegExReplace(A_ThisFunc, "^(.*)\..*\..*$", "$1")) != A_ThisFunc) ? ___ : ""
Loop, Parse, parent, .
parent := (A_Index=1) ? %A_LoopField% : parent[A_LoopField]
_subtitle := (parent) ? new parent.Subtitle() : new Subtitle()
return _subtitle.Render(text, style1, style2, update)
}
}
Bitmap(x:=0, y:=0, w:=0, h:=0){
pBitmap := Gdip_CreateBitmap(A_ScreenWidth, A_ScreenHeight)
pGraphics := Gdip_GraphicsFromImage(pBitmap)
loop % this.layers.MaxIndex()
this.Draw(this.layers[A_Index].1, this.layers[A_Index].2, this.layers[A_Index].3, pGraphics)
Gdip_DeleteGraphics(pGraphics)
if (x || y || w || h) {
w := (w = 0) ? A_ScreenWidth, h := (h = 0) ? A_ScreenHeight
pBitmap2 := Gdip_CloneBitmapArea(pBitmap, x, y, w, h)
Gdip_DisposeImage(pBitmap)
pBitmap := pBitmap2
}
return pBitmap ; Please dispose of this image responsibly.
}
Save(filename := "", quality := 92, fullscreen := 0){
filename := (filename ~= "i)\.(bmp|dib|rle|jpg|jpeg|jpe|jfif|gif|tif|tiff|png)$") ? filename
: (filename != "") ? filename ".png" : this.name ".png"
pBitmap := (fullscreen) ? this.Bitmap() : this.Bitmap(this.x, this.y, this.xx - this.x, this.yy - this.y)
Gdip_SaveBitmapToFile(pBitmap, filename, quality)
Gdip_DisposeImage(pBitmap)
}
SaveFullScreen(filename := "", quality := ""){
return this.Save(filename, quality, 1)
}
hBitmap(alpha := 0xFFFFFFFF){
; hBitmap converts alpha channel to specified alpha color.
; Add 1 pixel because Anti-Alias (SmoothingMode = 4)
; Should it be crop 1 pixel instead?
pBitmap := this.Bitmap(this.x, this.y, this.xx - this.x, this.yy - this.y)
hBitmap := Gdip_CreateHBITMAPFromBitmap(pBitmap, alpha)
Gdip_DisposeImage(pBitmap)
return hBitmap
}
RenderToHBitmap(text := "", style1 := "", style2 := ""){
if (this.hWnd){
this.Render(text, style1, style2, 0)
return this.hBitmap()
}
else {
parent := ((___ := RegExReplace(A_ThisFunc, "^(.*)\..*\..*$", "$1")) != A_ThisFunc) ? ___ : ""
Loop, Parse, parent, .
parent := (A_Index=1) ? %A_LoopField% : parent[A_LoopField]
_subtitle := (parent) ? new parent.Subtitle() : new Subtitle()
_subtitle.Render(text, style1, style2, 0)
return _subtitle.hBitmap() ; Does not return a subtitle object.
}
}
hIcon(){
pBitmap := this.Bitmap(this.x, this.y, this.xx - this.x, this.yy - this.y)
hIcon := Gdip_CreateHICONFromBitmap(pBitmap)
Gdip_DisposeImage(pBitmap)
return hIcon
}
color(c, default := 0xDD424242){
static colorRGB := "^0x([0-9A-Fa-f]{6})$"
static colorARGB := "^0x([0-9A-Fa-f]{8})$"
static hex6 := "^([0-9A-Fa-f]{6})$"
static hex8 := "^([0-9A-Fa-f]{8})$"
if ObjGetCapacity([c], 1){
c := (c ~= "^#") ? SubStr(c, 2) : c
c := ((___ := this.colorMap[c]) != "") ? ___ : c
c := (c ~= colorRGB) ? "0xFF" RegExReplace(c, colorRGB, "$1") : (c ~= hex8) ? "0x" c : (c ~= hex6) ? "0xFF" c : c
c := (c ~= colorARGB) ? c : default
}
return (c != "") ? c : default
}
margin(m, default := 0){
static percentage := "^(\-?\d+(?:\.\d*)?)%$"
static positive := "^\d+(\.\d*)?$"
if IsObject(m){
m.1 := (m.y != "") ? m.y : m.top
m.2 := (m.x2 != "") ? m.x2 : m.right
m.3 := (m.y2 != "") ? m.y2 : m.bottom
m.4 := (m.x != "") ? m.x : m.left
}
else if (m) {
m := StrSplit(m, " ")
if (m.length() == 3)
m.4 := m.2
else if (m.length() == 2)
m.4 := m.2, m.3 := m.1
else if (m.length() == 1)
m.4 := m.3 := m.2 := m.1, exception := true
else
m.Delete(5, m.MaxIndex())
}
else
return {1:default, 2:default, 3:default, 4:default}
m.1 := (m.1 ~= percentage) ? A_ScreenHeight * SubStr(m.1, 1, -1) / 100 : m.1
m.2 := (m.2 ~= percentage) ? (exception ? A_ScreenHeight : A_ScreenWidth) * SubStr(m.2, 1, -1) / 100 : m.2
m.3 := (m.3 ~= percentage) ? A_ScreenHeight * SubStr(m.3, 1, -1) / 100 : m.3
m.4 := (m.4 ~= percentage) ? (exception ? A_ScreenHeight : A_ScreenWidth) * SubStr(m.4, 1, -1) / 100 : m.4
m.1 := (m.1 ~= positive) ? m.1 : default
m.2 := (m.2 ~= positive) ? m.2 : default
m.3 := (m.3 ~= positive) ? m.3 : default
m.4 := (m.4 ~= positive) ? m.4 : default
return m
}
outline(o){
static percentage := "^(\-?\d+(?:\.\d*)?)%$"
static positive := "^\d+(\.\d*)?$"
if IsObject(o){
o.1 := (o.w != "") ? o.w : o.width
o.2 := (o.c != "") ? o.c : o.color
o.3 := (o.g != "") ? o.g : o.glow
o.4 := (o.c2 != "") ? o.c2 : o.glowColor
} else if (o)
o := StrSplit(o, " ")
else
return {"void":true, 1:0, 2:0, 3:0, 4:0}
o.1 := (o.1 ~= "px$") ? SubStr(o.1, 1, -2) : o.1
o.1 := (o.1 ~= percentage) ? s * RegExReplace(o.1, percentage, "$1") // 100 : o.1
o.1 := (o.1 ~= positive) ? o.1 : 1
o.2 := this.color(o.2, 0xFF000000)
o.3 := (o.3 ~= "px$") ? SubStr(o.3, 1, -2) : o.3
o.3 := (o.3 ~= percentage) ? s * RegExReplace(o.3, percentage, "$1") // 100 : o.3
o.3 := (o.3 ~= positive) ? o.3 : 0
o.4 := this.color(o.4, 0x00000000)
return o
}
dropShadow(d){
static decimal := "^(\-?\d+(\.\d*)?)$"
static percentage := "^(\-?\d+(?:\.\d*)?)%$"
static positive := "^\d+(\.\d*)?$"
if IsObject(d){
d.1 := (d.h != "") ? d.h : d.horizontal
d.2 := (d.v != "") ? d.v : d.vertical
d.3 := (d.b != "") ? d.b : d.blur
d.4 := (d.c != "") ? d.c : d.color
d.5 := (d.s != "") ? d.s : d.strength
} else if (d)
d := StrSplit(d, " ")
else
return {"void":true, 1:0, 2:0, 3:0, 4:0, 5:0}
d.1 := (d.1 ~= "px$") ? SubStr(d.1, 1, -2) : d.1
d.1 := (d.1 ~= percentage) ? ReturnRC[3] * RegExReplace(d.1, percentage, "$1") / 100 : d.1
d.1 := (d.1 ~= decimal) ? d.1 : 0
d.2 := (d.2 ~= "px$") ? SubStr(d.2, 1, -2) : d.2
d.2 := (d.2 ~= percentage) ? ReturnRC[4] * RegExReplace(d.2, percentage, "$1") / 100 : d.2
d.2 := (d.2 ~= decimal) ? d.2 : 0
d.3 := (d.3 ~= "px$") ? SubStr(d.3, 1, -2) : d.3
d.3 := (d.3 ~= percentage) ? s * RegExReplace(d.3, percentage, "$1") / 100 : d.3
d.3 := (d.3 ~= positive) ? d.3 : 1
d.4 := this.color(d.4, 0xFF000000)
d.5 := (d.5 ~= percentage) ? s * RegExReplace(d.5, percentage, "$1") / 100 : d.5
d.5 := (d.5 ~= positive) ? d.5 : 1
return d
}
colorMap(){
color := [] ; 73 LINES MAX
color["Clear"] := color["Off"] := color["None"] := color["Transparent"] := "0x00000000"
color["AliceBlue"] := "0xFFF0F8FF"
, color["AntiqueWhite"] := "0xFFFAEBD7"
, color["Aqua"] := "0xFF00FFFF"
, color["Aquamarine"] := "0xFF7FFFD4"
, color["Azure"] := "0xFFF0FFFF"
, color["Beige"] := "0xFFF5F5DC"
, color["Bisque"] := "0xFFFFE4C4"
, color["Black"] := "0xFF000000"
, color["BlanchedAlmond"] := "0xFFFFEBCD"
, color["Blue"] := "0xFF0000FF"
, color["BlueViolet"] := "0xFF8A2BE2"
, color["Brown"] := "0xFFA52A2A"
, color["BurlyWood"] := "0xFFDEB887"
, color["CadetBlue"] := "0xFF5F9EA0"
, color["Chartreuse"] := "0xFF7FFF00"
, color["Chocolate"] := "0xFFD2691E"
, color["Coral"] := "0xFFFF7F50"
, color["CornflowerBlue"] := "0xFF6495ED"
, color["Cornsilk"] := "0xFFFFF8DC"
, color["Crimson"] := "0xFFDC143C"
, color["Cyan"] := "0xFF00FFFF"
, color["DarkBlue"] := "0xFF00008B"
, color["DarkCyan"] := "0xFF008B8B"
, color["DarkGoldenRod"] := "0xFFB8860B"
, color["DarkGray"] := "0xFFA9A9A9"
, color["DarkGrey"] := "0xFFA9A9A9"
, color["DarkGreen"] := "0xFF006400"
, color["DarkKhaki"] := "0xFFBDB76B"
, color["DarkMagenta"] := "0xFF8B008B"
, color["DarkOliveGreen"] := "0xFF556B2F"
, color["DarkOrange"] := "0xFFFF8C00"
, color["DarkOrchid"] := "0xFF9932CC"
, color["DarkRed"] := "0xFF8B0000"
, color["DarkSalmon"] := "0xFFE9967A"
, color["DarkSeaGreen"] := "0xFF8FBC8F"
, color["DarkSlateBlue"] := "0xFF483D8B"
, color["DarkSlateGray"] := "0xFF2F4F4F"
, color["DarkSlateGrey"] := "0xFF2F4F4F"
, color["DarkTurquoise"] := "0xFF00CED1"
, color["DarkViolet"] := "0xFF9400D3"
, color["DeepPink"] := "0xFFFF1493"
, color["DeepSkyBlue"] := "0xFF00BFFF"
, color["DimGray"] := "0xFF696969"
, color["DimGrey"] := "0xFF696969"
, color["DodgerBlue"] := "0xFF1E90FF"
, color["FireBrick"] := "0xFFB22222"
, color["FloralWhite"] := "0xFFFFFAF0"
, color["ForestGreen"] := "0xFF228B22"
, color["Fuchsia"] := "0xFFFF00FF"
, color["Gainsboro"] := "0xFFDCDCDC"
, color["GhostWhite"] := "0xFFF8F8FF"
, color["Gold"] := "0xFFFFD700"
, color["GoldenRod"] := "0xFFDAA520"
, color["Gray"] := "0xFF808080"
, color["Grey"] := "0xFF808080"
, color["Green"] := "0xFF008000"
, color["GreenYellow"] := "0xFFADFF2F"
, color["HoneyDew"] := "0xFFF0FFF0"
, color["HotPink"] := "0xFFFF69B4"
, color["IndianRed"] := "0xFFCD5C5C"
, color["Indigo"] := "0xFF4B0082"
, color["Ivory"] := "0xFFFFFFF0"
, color["Khaki"] := "0xFFF0E68C"
, color["Lavender"] := "0xFFE6E6FA"
, color["LavenderBlush"] := "0xFFFFF0F5"
, color["LawnGreen"] := "0xFF7CFC00"
, color["LemonChiffon"] := "0xFFFFFACD"
, color["LightBlue"] := "0xFFADD8E6"
, color["LightCoral"] := "0xFFF08080"
, color["LightCyan"] := "0xFFE0FFFF"
, color["LightGoldenRodYellow"] := "0xFFFAFAD2"
, color["LightGray"] := "0xFFD3D3D3"
, color["LightGrey"] := "0xFFD3D3D3"
color["LightGreen"] := "0xFF90EE90"
, color["LightPink"] := "0xFFFFB6C1"
, color["LightSalmon"] := "0xFFFFA07A"
, color["LightSeaGreen"] := "0xFF20B2AA"
, color["LightSkyBlue"] := "0xFF87CEFA"
, color["LightSlateGray"] := "0xFF778899"
, color["LightSlateGrey"] := "0xFF778899"
, color["LightSteelBlue"] := "0xFFB0C4DE"
, color["LightYellow"] := "0xFFFFFFE0"
, color["Lime"] := "0xFF00FF00"
, color["LimeGreen"] := "0xFF32CD32"
, color["Linen"] := "0xFFFAF0E6"
, color["Magenta"] := "0xFFFF00FF"
, color["Maroon"] := "0xFF800000"
, color["MediumAquaMarine"] := "0xFF66CDAA"
, color["MediumBlue"] := "0xFF0000CD"
, color["MediumOrchid"] := "0xFFBA55D3"
, color["MediumPurple"] := "0xFF9370DB"
, color["MediumSeaGreen"] := "0xFF3CB371"
, color["MediumSlateBlue"] := "0xFF7B68EE"
, color["MediumSpringGreen"] := "0xFF00FA9A"
, color["MediumTurquoise"] := "0xFF48D1CC"
, color["MediumVioletRed"] := "0xFFC71585"
, color["MidnightBlue"] := "0xFF191970"
, color["MintCream"] := "0xFFF5FFFA"
, color["MistyRose"] := "0xFFFFE4E1"
, color["Moccasin"] := "0xFFFFE4B5"
, color["NavajoWhite"] := "0xFFFFDEAD"
, color["Navy"] := "0xFF000080"
, color["OldLace"] := "0xFFFDF5E6"
, color["Olive"] := "0xFF808000"
, color["OliveDrab"] := "0xFF6B8E23"
, color["Orange"] := "0xFFFFA500"
, color["OrangeRed"] := "0xFFFF4500"
, color["Orchid"] := "0xFFDA70D6"
, color["PaleGoldenRod"] := "0xFFEEE8AA"
, color["PaleGreen"] := "0xFF98FB98"
, color["PaleTurquoise"] := "0xFFAFEEEE"
, color["PaleVioletRed"] := "0xFFDB7093"
, color["PapayaWhip"] := "0xFFFFEFD5"
, color["PeachPuff"] := "0xFFFFDAB9"
, color["Peru"] := "0xFFCD853F"
, color["Pink"] := "0xFFFFC0CB"
, color["Plum"] := "0xFFDDA0DD"
, color["PowderBlue"] := "0xFFB0E0E6"
, color["Purple"] := "0xFF800080"
, color["RebeccaPurple"] := "0xFF663399"
, color["Red"] := "0xFFFF0000"
, color["RosyBrown"] := "0xFFBC8F8F"
, color["RoyalBlue"] := "0xFF4169E1"
, color["SaddleBrown"] := "0xFF8B4513"
, color["Salmon"] := "0xFFFA8072"
, color["SandyBrown"] := "0xFFF4A460"
, color["SeaGreen"] := "0xFF2E8B57"
, color["SeaShell"] := "0xFFFFF5EE"
, color["Sienna"] := "0xFFA0522D"
, color["Silver"] := "0xFFC0C0C0"
, color["SkyBlue"] := "0xFF87CEEB"
, color["SlateBlue"] := "0xFF6A5ACD"
, color["SlateGray"] := "0xFF708090"
, color["SlateGrey"] := "0xFF708090"
, color["Snow"] := "0xFFFFFAFA"
, color["SpringGreen"] := "0xFF00FF7F"
, color["SteelBlue"] := "0xFF4682B4"
, color["Tan"] := "0xFFD2B48C"
, color["Teal"] := "0xFF008080"
, color["Thistle"] := "0xFFD8BFD8"
, color["Tomato"] := "0xFFFF6347"
, color["Turquoise"] := "0xFF40E0D0"
, color["Violet"] := "0xFFEE82EE"
, color["Wheat"] := "0xFFF5DEB3"
, color["White"] := "0xFFFFFFFF"
, color["WhiteSmoke"] := "0xFFF5F5F5"
color["Yellow"] := "0xFFFFFF00"
, color["YellowGreen"] := "0xFF9ACD32"
return color
}
x1(){
return this.x
}
y1(){
return this.y
}
x2(){
return this.xx
}
y2(){
return this.yy
}
width(){
return this.xx - this.x
}
height(){
return this.yy - this.y
}
}
}
class provider {
class GoogleCloudVision {
static ext := "jpg"
static jpegQuality := "75"
base64 := true
; Cloud Platform Console Help - Setting up API keys
; Step 1: https://support.google.com/cloud/answer/6158862?hl=en
; Step 2: https://cloud.google.com/vision/docs/before-you-begin
; You must enter billing information to use the Cloud Vision API.
; https://cloud.google.com/vision/pricing
; First 1000 LABEL_DETECTION per month is free.
; Please enter your api_key for Google Cloud Vision API.
static api_key := "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
; FOR SAFETY REASONS, DO NOT PASTE YOUR API KEY HERE.
; Instead, keep your api_key in a separate file, "Vis2_API.txt"
getCredentials(error:=""){
if (error != "") {
(Vis2.obj) ? Vis2.core.ux.suspend() : ""
InputBox, api_key, Vis2.GoogleCloudVision.ImageIdentify, Enter your api_key for GoogleCloudVision.
(Vis2.obj) ? Vis2.core.ux.resume() : ""
FileAppend, GoogleCloudVision=%api_key%, Vis2_API.txt
return api_key
}
if (this.api_key ~= "^X{39}$") {
if FileExist("Vis2_API.txt") {
file := FileOpen("Vis2_API.txt", "r")
keys := file.Read()
api_key := ((___ := RegExReplace(keys, "s)^.*?GoogleCloudVision(?:\s*)=(?:\s*)([A-Za-z0-9_\-]+).*$", "$1")) != keys) ? ___ : ""
file.close()
if (api_key != "")
return api_key
}
}
else
return this.api_key
}
; https://cloud.google.com/vision/docs/supported-files
; Supported Image Formats
; JPEG, PNG8, PNG24, GIF, Animated GIF (first frame only)
; BMP, WEBP, RAW, ICO
; Maximum Image Size - 4 MB
; Maximum Size per Request - 8 MB
; Compression to 640 x 480 - LABEL_DETECTION
ImageIdentify(image, search:="", options:=""){
base64 := ImagePutBase64({image: image, crop: options}, "png", this.jpegQuality)
reply := this.convert(base64)
return this.getText()
}
convert(in:=""){
in := (in) ? in : this.base64
req := {}
req.requests := {}
req.requests[1] := {"image":{}, "features":{}}
req.requests[1].image.content := in
req.requests[1].features[1] := {"type":"LABEL_DETECTION"}
body := JSON.Dump(req)
whr := ComObjCreate("WinHttp.WinHttpRequest.5.1")
if (api_key := this.getCredentials())
whr.Open("POST", "https://vision.googleapis.com/v1/images:annotate?key=" api_key, true)
else
whr.Open("POST", "https://cxl-services.appspot.com/proxy?url=https%3A%2F%2Fvision.googleapis.com%2Fv1%2Fimages%3Aannotate", true)
whr.SetRequestHeader("Accept", "*/*")
whr.SetRequestHeader("Origin", "https://cloud.google.com")
whr.SetRequestHeader("Content-Type", "text/plain;charset=UTF-8")
whr.SetRequestHeader("Referer", "https://cloud.google.com/vision/")
whr.SetRequestHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36")
whr.Send(body)
whr.WaitForResponse()
try reply := JSON.Load(whr.ResponseText)
catch
this.getCredentials(whr.ResponseText)
i := 1
while (i <= reply.responses[1].labelAnnotations.length()) {
sentence .= (i == 1) ? "" : ", "
sentence2 .= (i == 1) ? "" : ", "
sentence .= reply.responses[1].labelAnnotations[i].description
sentence2 .= reply.responses[1].labelAnnotations[i].description " (" Format("{:i}", 100*reply.responses[1].labelAnnotations[i].score) "%)"
i++
}
this.text := sentence2
return reply
}
getText(){
return this.text
}
}
class Tesseract {
static leptonica := StrReplace(A_LineFile,"\lib\Vis2.ahk","") "\bin\leptonica_util\leptonica_util.exe"
static tesseract := StrReplace(A_LineFile,"\lib\Vis2.ahk","") "\bin\tesseract\tesseract.exe"
static tessdata_best := StrReplace(A_LineFile,"\lib\Vis2.ahk","") "\bin\tesseract\tessdata_best"
static tessdata_fast := StrReplace(A_LineFile,"\lib\Vis2.ahk","") "\bin\tesseract\tessdata_fast"
uuid := Vis2.stdlib.CreateUUID()
file := A_Temp "\Vis2_screenshot" this.uuid ".bmp"
fileProcessedImage := A_Temp "\Vis2_preprocess" this.uuid ".tif"
fileConvertedText := A_Temp "\Vis2_text" this.uuid ".txt"
; public OCR()
; public preprocess()
; public convert()
; public cleanup()
; public getPreprocessImage()
; public getText()
; private convert_best()
; private convert_fast()
; private getTextLines()
__New(language:=""){
this.language := language
}
OCR(image, language:="", options:=""){
this.language := language
try {
screenshot := ImagePutFile({image: image, crop: options}, this.file)
this.preprocess(screenshot, this.fileProcessedImage)
this.convert_best(this.fileProcessedImage, this.fileConvertedText)
text := this.getText(this.fileConvertedText)
} catch e {
MsgBox, 16,, % "Exception thrown!`n`nwhat: " e.what "`nfile: " e.file
. "`nline: " e.line "`nmessage: " e.message "`nextra: " e.extra
}
finally {
this.cleanup()
text.base.google := ObjBindMethod(Vis2.Text, "google")
text.base.clipboard := ObjBindMethod(Vis2.Text, "clipboard")
}
return text
}
cleanup(){
FileDelete, % this.file
FileDelete, % this.fileProcessedImage
FileDelete, % this.fileConvertedText
}
convert(in:="", out:="", fast:=1){
in := (in) ? in : this.fileProcessedImage
out := (out) ? out : this.fileConvertedText
fast := (fast) ? this.tessdata_fast : this.tessdata_best
if !(FileExist(in))
throw Exception("Input image for conversion not found.",, in)
if !(FileExist(this.tesseract))
throw Exception("Tesseract not found",, this.tesseract)
static q := Chr(0x22)
_cmd .= q this.tesseract q " --tessdata-dir " q fast q " " q in q " " q SubStr(out, 1, -4) q
_cmd .= (this.language) ? " -l " q this.language q : ""
_cmd := ComSpec " /C " q _cmd q
RunWait % _cmd,, Hide
if !(FileExist(out))
throw Exception("Tesseract failed.",, _cmd)
return out
}
convert_best(in:="", out:=""){
return this.convert(in, out, 0)
}
convert_fast(in:="", out:=""){
return this.convert(in, out, 1)
}
getPreprocessImage(){
return this.fileProcessedImage
}
getText(in:="", lines:=""){
in := (in) ? in : this.fileConvertedText
if !(database := FileOpen(in, "r`n", "UTF-8"))
throw Exception("Text file could not be found or opened.",, in)
if (lines == "") {
text := RegExReplace(database.Read(), "^\s*(.*?)\s*$", "$1")
text := RegExReplace(text, "(?<!\r)\n", "`r`n")
} else {
while (lines > 0) {
data := database.ReadLine()
data := RegExReplace(data, "^\s*(.*?)\s*$", "$1")
if (data != "") {
text .= (text) ? ("`n" . data) : data
lines--
}
if (!database || database.AtEOF)
break
}
}
database.Close()
return text
}
getTextLines(lines){
return this.read(, lines)
}
preprocess(in:="", out:=""){
static ocrPreProcessing := 1
static negateArg := 2
static performScaleArg := 1
static scaleFactor := 3.5
in := (in != "") ? in : this.file
out := (out != "") ? out : this.fileProcessedImage
if !(FileExist(in))
throw Exception("Input image for preprocessing not found.",, in)
if !(FileExist(this.leptonica))
throw Exception("Leptonica not found",, this.leptonica)
static q := Chr(0x22)
_cmd .= q this.leptonica q " " q in q " " q out q
_cmd .= " " negateArg " 0.5 " performScaleArg " " scaleFactor " " ocrPreProcessing " 5 2.5 " ocrPreProcessing " 2000 2000 0 0 0.0"
_cmd := ComSpec " /C " q _cmd q
RunWait, % _cmd,, Hide
if !(FileExist(out))
throw Exception("Preprocessing failed.",, _cmd)
return out
}
}
}
class stdlib {
isBinaryImageFormat(data){
Loop 12
bytes .= Chr(NumGet(data, A_Index-1, "uchar"))
; Null bytes are not passed, so they have been omitted below
if (bytes ~= "^BM")
return "bmp"
if (bytes ~= "^(GIF87a|GIF89a)")
return "gif"
if (bytes ~= "^ÿØÿÛ")
return "jpg"
if (bytes ~= "s)^ÿØÿà..\x4A\x46\x49\x46") ;\x00\x01
return "jfif"
if (bytes ~= "^\x89\x50\x4E\x47\x0D\x0A\x1A\x0A")
return "png"
if (bytes ~= "^(\x49\x49\x2A|\x4D\x4D\x2A)") ; 49 49 2A 00, 4D 4D 00 2A
return "tif"
return
}
isURL(url){
regex .= "((https?|ftp)\:\/\/)" ; SCHEME
regex .= "([a-z0-9+!*(),;?&=\$_.-]+(\:[a-z0-9+!*(),;?&=\$_.-]+)?@)?" ; User and Pass
regex .= "([a-z0-9-.]*)\.([a-z]{2,3})" ; Host or IP
regex .= "(\:[0-9]{2,5})?" ; Port
regex .= "(\/([a-z0-9+\$_-]\.?)+)*\/?" ; Path
regex .= "(\?[a-z+&\$_.-][a-z0-9;:@&%=+\/\$_.-]*)?" ; GET Query
regex .= "(#[a-z_.-][a-z0-9+\$_.-]*)?" ; Anchor
return (url ~= "i)" regex) ? true : false
}
b64Encode( ByRef buf, bufLen:="" ) {
bufLen := (bufLen) ? bufLen : StrLen(buf) << !!A_IsUnicode
DllCall( "crypt32\CryptBinaryToStringA", "ptr", &buf, "UInt", bufLen, "Uint", 1 | 0x40000000, "Ptr", 0, "UInt*", outLen )
VarSetCapacity( outBuf, outLen, 0 )
DllCall( "crypt32\CryptBinaryToStringA", "ptr", &buf, "UInt", bufLen, "Uint", 1 | 0x40000000, "Ptr", &outBuf, "UInt*", outLen )
return strget( &outBuf, outLen, "CP0" )
}
b64Decode( b64str, ByRef outBuf ) {
static CryptStringToBinary := "crypt32\CryptStringToBinary" (A_IsUnicode ? "W" : "A")
DllCall( CryptStringToBinary, "ptr", &b64str, "UInt", 0, "Uint", 1, "Ptr", 0, "UInt*", outLen, "ptr", 0, "ptr", 0 )
VarSetCapacity( outBuf, outLen, 0 )
DllCall( CryptStringToBinary, "ptr", &b64str, "UInt", 0, "Uint", 1, "Ptr", &outBuf, "UInt*", outLen, "ptr", 0, "ptr", 0 )
return outLen
}
CreateUUID() {
VarSetCapacity(puuid, 16, 0)
if !(DllCall("rpcrt4.dll\UuidCreate", "ptr", &puuid))
if !(DllCall("rpcrt4.dll\UuidToString", "ptr", &puuid, "uint*", suuid))
return StrGet(suuid), DllCall("rpcrt4.dll\RpcStringFree", "uint*", suuid)
return ""
}
Gdip_EncodeBitmapTo64string(pBitmap, ext, Quality=75) {
if Ext not in BMP,DIB,RLE,JPG,JPEG,JPE,JFIF,GIF,TIF,TIFF,PNG
return -1
Extension := "." Ext
DllCall("gdiplus\GdipGetImageEncodersSize", "uint*", nCount, "uint*", nSize)
VarSetCapacity(ci, nSize)
DllCall("gdiplus\GdipGetImageEncoders", "uint", nCount, "uint", nSize, Ptr, &ci)
if !(nCount && nSize)
return -2
Loop, %nCount%
{
sString := StrGet(NumGet(ci, (idx := (48+7*A_PtrSize)*(A_Index-1))+32+3*A_PtrSize), "UTF-16")
if !InStr(sString, "*" Extension)
continue
pCodec := &ci+idx
break
}
if !pCodec
return -3
if (Quality != 75)
{
Quality := (Quality < 0) ? 0 : (Quality > 100) ? 100 : Quality
if Extension in .JPG,.JPEG,.JPE,.JFIF
{
DllCall("gdiplus\GdipGetEncoderParameterListSize", Ptr, pBitmap, Ptr, pCodec, "uint*", nSize)
VarSetCapacity(EncoderParameters, nSize, 0)
DllCall("gdiplus\GdipGetEncoderParameterList", Ptr, pBitmap, Ptr, pCodec, "uint", nSize, Ptr, &EncoderParameters)
Loop, % NumGet(EncoderParameters, "UInt")
{
elem := (24+(A_PtrSize ? A_PtrSize : 4))*(A_Index-1) + 4 + (pad := A_PtrSize = 8 ? 4 : 0)
if (NumGet(EncoderParameters, elem+16, "UInt") = 1) && (NumGet(EncoderParameters, elem+20, "UInt") = 6)
{
p := elem+&EncoderParameters-pad-4
NumPut(Quality, NumGet(NumPut(4, NumPut(1, p+0)+20, "UInt")), "UInt")
break
}
}
}
}
DllCall("ole32\CreateStreamOnHGlobal", "ptr",0, "int",true, "ptr*",pStream)
DllCall("gdiplus\GdipSaveImageToStream", "ptr",pBitmap, "ptr",pStream, "ptr",pCodec, "uint",p ? p : 0)
DllCall("ole32\GetHGlobalFromStream", "ptr",pStream, "uint*",hData)
pData := DllCall("GlobalLock", "ptr",hData, "uptr")
nSize := DllCall("GlobalSize", "uint",pData)
VarSetCapacity(Bin, nSize, 0)
DllCall("RtlMoveMemory", "ptr",&Bin , "ptr",pData , "uint",nSize)
DllCall("GlobalUnlock", "ptr",hData)
DllCall(NumGet(NumGet(pStream + 0, 0, "uptr") + (A_PtrSize * 2), 0, "uptr"), "ptr",pStream)
DllCall("GlobalFree", "ptr",hData)
DllCall("Crypt32.dll\CryptBinaryToString", "ptr",&Bin, "uint",nSize, "uint",0x01, "ptr",0, "uint*",base64Length)
VarSetCapacity(base64, base64Length*2, 0)
DllCall("Crypt32.dll\CryptBinaryToString", "ptr",&Bin, "uint",nSize, "uint",0x01, "ptr",&base64, "uint*",base64Length)
Bin := ""
VarSetCapacity(Bin, 0)
VarSetCapacity(base64, -1)
return base64
}
Gdip_BitmapFromClientHWND(hwnd) {
VarSetCapacity(rc, 16)
DllCall("GetClientRect", "ptr", hwnd, "ptr", &rc)
hbm := CreateDIBSection(NumGet(rc, 8, "int"), NumGet(rc, 12, "int"))
VarSetCapacity(rc, 0)
hdc := CreateCompatibleDC()
obm := SelectObject(hdc, hbm)
PrintWindow(hwnd, hdc, 1)
pBitmap := Gdip_CreateBitmapFromHBITMAP(hbm)
SelectObject(hdc, obm), DeleteObject(hbm), DeleteDC(hdc)
return pBitmap
}
Gdip_CropBitmap(ByRef pBitmap, c, preserveOriginal:=false){
w := Gdip_GetImageWidth(pBitmap), h := Gdip_GetImageHeight(pBitmap)
pBitmap2 := Gdip_CloneBitmapArea(pBitmap, c.1, c.2, (c.1 + c.3 > w) ? w - c.1 : c.3 , (c.2 + c.4 > h) ? h - c.2 : c.4)
(preserveOriginal) ? "" : Gdip_DisposeImage(pBitmap)
pBitmap := pBitmap2
}
Gdip_isBitmapEqual(pBitmap1, pBitmap2, width:="", height:="") {
; Check if pointers are identical.
if (pBitmap1 == pBitmap2)
return true
; Assume both Bitmaps are equal in width and height.
width := (width) ? width : Gdip_GetImageWidth(pBitmap1)
height := (height) ? height : Gdip_GetImageHeight(pBitmap1)
E1 := Gdip_LockBits(pBitmap1, 0, 0, width, height, Stride1, Scan01, BitmapData1)
E2 := Gdip_LockBits(pBitmap2, 0, 0, width, height, Stride2, Scan02, BitmapData2)
; RtlCompareMemory preforms an unsafe comparison stopping at the first different byte.
length := width * height * 4 ; ARGB = 4 bytes
bytes := DllCall("RtlCompareMemory", "ptr", Scan01+0, "ptr", Scan02+0, "uint", length)
Gdip_UnlockBits(pBitmap1, BitmapData1)
Gdip_UnlockBits(pBitmap2, BitmapData2)
return (bytes == length) ? true : false
}
RPath_Absolute(AbsolutPath, RelativePath, s="\") {
len := InStr(AbsolutPath, s, "", InStr(AbsolutPath, s . s) + 2) - 1 ;get server or drive string length
pr := SubStr(AbsolutPath, 1, len) ;get server or drive name
AbsolutPath := SubStr(AbsolutPath, len + 1) ;remove server or drive from AbsolutPath
If InStr(AbsolutPath, s, "", 0) = StrLen(AbsolutPath) ;remove last \ from AbsolutPath if any
StringTrimRight, AbsolutPath, AbsolutPath, 1
If InStr(RelativePath, s) = 1 ;when first char is \ go to AbsolutPath of server or drive
AbsolutPath := "", RelativePath := SubStr(RelativePath, 2) ;set AbsolutPath to nothing and remove one char from RelativePath
Else If InStr(RelativePath,"." s) = 1 ;when first two chars are .\ add to current AbsolutPath directory
RelativePath := SubStr(RelativePath, 3) ;remove two chars from RelativePath
Else If InStr(RelativePath,".." s) = 1 { ;otherwise when first 3 char are ..\
StringReplace, RelativePath, RelativePath, ..%s%, , UseErrorLevel ;remove all ..\ from RelativePath
Loop, %ErrorLevel% ;for all ..\
AbsolutPath := SubStr(AbsolutPath, 1, InStr(AbsolutPath, s, "", 0) - 1) ;remove one folder from AbsolutPath
} Else ;relative path does not need any substitution
pr := "", AbsolutPath := "", s := "" ;clear all variables to just return RelativePath
Return, pr . AbsolutPath . s . RelativePath ;concatenate server + AbsolutPath + separator + RelativePath
}
setSystemCursor(CursorID = "", cx = 0, cy = 0 ) { ; Thanks to Serenity - https://autohotkey.com/board/topic/32608-changing-the-system-cursor/
static SystemCursors := "32512,32513,32514,32515,32516,32640,32641,32642,32643,32644,32645,32646,32648,32649,32650,32651"
Loop, Parse, SystemCursors, `,
{
Type := "SystemCursor"
CursorHandle := DllCall( "LoadCursor", "uInt",0, "Int",CursorID )
%Type%%A_Index% := DllCall( "CopyImage", "uInt",CursorHandle, "uInt",0x2, "Int",cx, "Int",cy, "uInt",0 )
CursorHandle := DllCall( "CopyImage", "uInt",%Type%%A_Index%, "uInt",0x2, "Int",0, "Int",0, "Int",0 )
DllCall( "SetSystemCursor", "uInt",CursorHandle, "Int",A_Loopfield)
}
}
; toBase64() - Converts the input to a Base 64 string.
; Types of input accepted
; Objects: Rectangle Array (Screenshot)
; Strings: File, URL, Window Title (ahk_class...) OR hwnd (hex)
; Numbers: GDI Bitmap, GDI HBitmap
; Rawfile: Binary, base64
toBase64(image, extension:="png", quality:="", crop:="", crop2:=""){
Vis2.Graphics.Startup()
; Check if image is an array of 4 numbers
if (image.1 ~= "^\d+$" && image.2 ~= "^\d+$" && image.3 ~= "^\d+$" && image.4 ~= "^\d+$") {
pBitmap := Gdip_BitmapFromScreen(image.1 "|" image.2 "|" image.3 "|" image.4)
base64 := Vis2.stdlib.Gdip_EncodeBitmapTo64string(pBitmap, extension, quality)
Gdip_DisposeImage(pBitmap)
}
; Check if image points to a valid file
else if FileExist(image) {
if !(crop) {
file := FileOpen(image, "r")
file.RawRead(data, file.length)
base64 := Vis2.stdlib.b64Encode(data, file.length)
file.Close()
} else {
pBitmap := Gdip_CreateBitmapFromFile(image)
(crop) ? Vis2.stdlib.Gdip_CropBitmap(pBitmap, crop) : ""
base64 := Vis2.stdlib.Gdip_EncodeBitmapTo64string(pBitmap, extension, quality)
Gdip_DisposeImage(pBitmap)
}
}
; Check if image points to a valid URL
else if Vis2.stdlib.isURL(image) {
static req := ComObjCreate("WinHttp.WinHttpRequest.5.1")
req.Open("GET",image)
req.Send()
pStream := ComObjQuery(req.ResponseStream, "{0000000C-0000-0000-C000-000000000046}")
if !(crop) {
DllCall("ole32\GetHGlobalFromStream", "ptr",pStream, "uint*",hData)
pData := DllCall("GlobalLock", "ptr",hData, "uptr")
nSize := DllCall("GlobalSize", "uint",pData)
VarSetCapacity(Bin, nSize, 0)
DllCall("RtlMoveMemory", "ptr",&Bin , "ptr",pData , "uint",nSize)
DllCall("GlobalUnlock", "ptr",hData)
DllCall(NumGet(NumGet(pStream + 0, 0, "uptr") + (A_PtrSize * 2), 0, "uptr"), "ptr",pStream)
DllCall("GlobalFree", "ptr",hData)
DllCall("Crypt32.dll\CryptBinaryToString", "ptr",&Bin, "uint",nSize, "uint",0x01, "ptr",0, "uint*",base64Length)
VarSetCapacity(base64, base64Length*2, 0)
DllCall("Crypt32.dll\CryptBinaryToString", "ptr",&Bin, "uint",nSize, "uint",0x01, "ptr",&base64, "uint*",base64Length)
Bin := ""
VarSetCapacity(Bin, 0)
VarSetCapacity(base64, -1)
} else {
DllCall("gdiplus\GdipCreateBitmapFromStream", "ptr",pStream, "uptr*",pBitmap)
ObjRelease(pStream)
(crop) ? Vis2.stdlib.Gdip_CropBitmap(pBitmap, crop) : ""
base64 := Vis2.stdlib.Gdip_EncodeBitmapTo64string(pBitmap, extension, quality)
Gdip_DisposeImage(pBitmap)
}
}
; Check if image matches a window title OR is a valid handle to a window
else if (DllCall("IsWindow", "ptr",image) || (hwnd := WinExist(image))) {
hwnd := (DllCall("IsWindow", "ptr",image)) ? image : hwnd
pBitmap := Vis2.stdlib.Gdip_BitmapFromClientHWND(hwnd)
(crop) ? Vis2.stdlib.Gdip_CropBitmap(pBitmap, crop) : ""
base64 := Vis2.stdlib.Gdip_EncodeBitmapTo64string(pBitmap, extension, quality)
Gdip_DisposeImage(pBitmap)
}
; Check if image is a valid GDI Bitmap
else if DeleteObject(Gdip_CreateHBITMAPFromBitmap(image)) {
(crop) ? Vis2.stdlib.Gdip_CropBitmap(image, crop, true) : ""
base64 := Vis2.stdlib.Gdip_EncodeBitmapTo64string(image, extension, quality)
(crop) ? Gdip_DisposeImage(image) : ""
}
; Check if image is a valid handle to a GDI Bitmap
else if (DllCall("GetObjectType", "ptr",image) == 7) {
pBitmap := Gdip_CreateBitmapFromHBITMAP(image)
(crop) ? Vis2.stdlib.Gdip_CropBitmap(pBitmap, crop) : ""
base64 := Vis2.stdlib.Gdip_EncodeBitmapTo64string(pBitmap, extension, quality)
Gdip_DisposeImage(pBitmap)
}
; Check if image is a base64 string
else if (image ~= "^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)$") {
base64 := image
}
Vis2.Graphics.Shutdown()
return base64
}
; toFile() - Saves the image as a temporary file.
toFile(image, outputFile:="", crop:=""){
Vis2.Graphics.Startup()
; Check if image is an array of 4 numbers
if (image.1 ~= "^\d+$" && image.2 ~= "^\d+$" && image.3 ~= "^\d+$" && image.4 ~= "^\d+$") {
pBitmap := Gdip_BitmapFromScreen(image.1 "|" image.2 "|" image.3 "|" image.4)
Gdip_SaveBitmapToFile(pBitmap, outputFile)
Gdip_DisposeImage(pBitmap)
}
; Check if image points to a valid file
else if FileExist(image) {
Loop, Files, % image
{
if (A_LoopFileExt != "bmp" || IsObject(crop)) {
pBitmap := Gdip_CreateBitmapFromFile(A_LoopFileLongPath)
(crop) ? Vis2.stdlib.Gdip_CropBitmap(pBitmap, crop) : ""
Gdip_SaveBitmapToFile(pBitmap, outputFile)
Gdip_DisposeImage(pBitmap)
}
else outputFile := A_LoopFileLongPath
}
}
; Check if image points to a valid URL
else if Vis2.stdlib.isURL(image) {
static req := ComObjCreate("WinHttp.WinHttpRequest.5.1")
req.Open("GET",image)
req.Send()
pStream := ComObjQuery(req.ResponseStream, "{0000000C-0000-0000-C000-000000000046}")
DllCall("gdiplus\GdipCreateBitmapFromStream", "ptr",pStream, "uptr*",pBitmap)
(crop) ? Vis2.stdlib.Gdip_CropBitmap(pBitmap, crop) : ""
Gdip_SaveBitmapToFile(pBitmap, outputFile, 92)
ObjRelease(pStream)
Gdip_DisposeImage(pBitmap)
}
; Check if image matches a window title OR is a valid handle to a window
else if (DllCall("IsWindow", "ptr",image) || (hwnd := WinExist(image))) {
hwnd := (DllCall("IsWindow", "ptr",image)) ? image : hwnd
pBitmap := Vis2.stdlib.Gdip_BitmapFromClientHWND(hwnd)
(crop) ? Vis2.stdlib.Gdip_CropBitmap(pBitmap, crop) : ""
Gdip_SaveBitmapToFile(pBitmap, outputFile)
Gdip_DisposeImage(pBitmap)
}
; Check if image is a valid GDI Bitmap
else if DeleteObject(Gdip_CreateHBITMAPFromBitmap(image)) {
(crop) ? Vis2.stdlib.Gdip_CropBitmap(image, crop, true) : ""
Gdip_SaveBitmapToFile(image, outputFile)
}
; Check if image is a valid handle to a GDI Bitmap
else if (DllCall("GetObjectType", "ptr",image) == 7) {
pBitmap := Gdip_CreateBitmapFromHBITMAP(image)
(crop) ? Vis2.stdlib.Gdip_CropBitmap(pBitmap, crop) : ""
Gdip_SaveBitmapToFile(pBitmap, outputFile)
Gdip_DisposeImage(pBitmap)
}
; Check if image is a base64 string
else if (image ~= "^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)$") {
nSize := Vis2.stdlib.b64Decode(image, bin)
hData := DllCall("GlobalAlloc", "uint",0x2, "ptr",nSize)
pData := DllCall("GlobalLock", "ptr",hData)
DllCall("RtlMoveMemory", "ptr",pData, "ptr",&bin, "ptr",nSize)
DllCall("GlobalUnlock", "ptr",hData)
DllCall("ole32\CreateStreamOnHGlobal", "ptr",hData, "int",1, "uptr*",pStream)
DllCall("gdiplus\GdipCreateBitmapFromStream", "ptr",pStream, "uptr*",pBitmap)
(crop) ? Vis2.stdlib.Gdip_CropBitmap(pBitmap, crop) : ""
Gdip_SaveBitmapToFile(pBitmap, outputFile, 92)
DllCall(NumGet(NumGet(pStream + 0, 0, "uptr") + (A_PtrSize * 2), 0, "uptr"), "ptr",pStream)
DllCall("GlobalFree", "ptr",hData)
ObjRelease(pStream)
Gdip_DisposeImage(pBitmap)
}
if !(FileExist(outputFile))
throw Exception("Could not find source image.")
Vis2.Graphics.Shutdown()
return outputFile
}
}
class Text {
copy() {
AutoTrim Off
c := ClipboardAll
Clipboard := "" ; Must start off blank for detection to work.
Send, ^c
ClipWait 0.5
if ErrorLevel
return
t := Clipboard
Clipboard := c
VarSetCapacity(c, 0)
return t
}
paste(t) {
c := ClipboardAll
Clipboard := t
Send, ^v
Sleep 50 ; Don't change clipboard while it is pasted! (Sleep > 0)
Clipboard := c
VarSetCapacity(c, 0) ; Free memory
AutoTrim On
}
restore() {
AutoTrim On
}
; Based on this paper: http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.81.8901
rmgarbage(data := ""){
; If the input value is blank, send ^c to capture highlighted text.
text := (data == "") ? Vis2.Text.copy() : data
; Split our text into strings, creating an array of words.
strings := [], whitespaces := [], pos := 1
while RegexMatch(text, "O)([^\s]+)", string, pos) {
strings.push(string.value())
pos := string.pos() + string.len()
RegexMatch(text, "O)([\s]+)", whitespace, pos)
whitespaces.push(whitespace.value)
}
for i in strings {
; (L) If a string is longer than 40 characters...
;strings[i] := RegExReplace(strings[i], "[^\s]{40,}", "")
; (A) If a string’s ratio of alphanumeric characters to total characters is less than 50%...
alnum_thresholds := {1: 0 ; single chars can be non-alphanumeric
,2: 0 ; so can doublets
,3: 0.32 ; at least one of three should be alnum
,4: 0.24 ; at least one of four should be alnum
,5: 0.39} ; at least two of five should be alnum
strings[i] := (StrLen(RegExReplace(strings[i], "\W")) / StrLen(strings[i]) < 0.5) ? "" : strings[i]
; (R) If a string has 4 identical characters in a row...
;strings[i] := (strings[i] ~= "([^\s])\1\1\1") ? "" : strings[i]
; (V) If a string has nothing but alphabetic characters, look at the number of consonants and vowels.
; If the number of one is less than 10% of the number of the other...
; This includes a length threshold.
/*
def bad_consonant_vowel_ratio(self, string):
"""
Rule V
======
if a string has nothing but alphabetic characters, look at the
number of consonants and vowels. If the number of one is less than 10%
of the number of the other, then the string is garbage.
This includes a length threshold.
:param string: string to be tested
:returns: either True or False
"""
alpha_string = filter(str.isalpha, string)
vowel_count = sum(1 for char in alpha_string if char in 'aeiouAEIOU')
consonant_count = len(alpha_string) - vowel_count
if (consonant_count > 0 and vowel_count > 0):
ratio = float(vowel_count)/consonant_count
if (ratio < 0.1 or ratio > 10):
return True
elif (vowel_count == 0 and consonant_count > len('rhythms')):
return True
elif (consonant_count == 0 and vowel_count > len('IEEE')):
return True
return False
*/
;strings[i] := (strings[i] ~= "^(\w(?<=\D))+$") ? () : strings[i]
; (P) Strip off the first and last characters of a string. If there are two distinct
; punctuation characters in the result...
; (C) If a string begins and ends with a lowercase letter, then if the string
; contains an uppercase letter anywhere in between...
}
; Reassemble the text
text := ""
for i, string in strings {
text .= string . whitespaces[i]
}
return (data == "") ? Vis2.Text.paste(text) : text
}
clipboard(data := ""){
text := (data == "") ? Vis2.Text.copy() : data
clipboard := text
return (data == "") ? Vis2.Text.restore() : text
}
google(data := "") {
text := data
if not RegExMatch(text, "^(http|ftp|telnet)")
text := "https://www.google.com/search?&q=" . RegExReplace(text, "\s", "+")
if (data)
Run % text
return data
}
}
}