Im kind of figured how to do what I want, but it takes alot of actions between, so I thought maybe there is easier way.
Whay Ive done is by ahk hotkey i call windows snippet tool, then loop files in screenshot directory to find latest one, move it to desired location and rename, then using OCR check for this file using text := OCR (filename)
Vis2 - Image to Text OCR()
Re: Vis2 - Image to Text OCR()
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
}
}
}
Re: Vis2 - Image to Text OCR()
Okay! I've updated Vis2 to use the latest version of the image capture library.
You have 2 options to get the client area:
Documentation to using crop: https://github.com/iseahound/ImagePut/wiki/Crop,-Scale,-&-Other-Flags#crop
Documentation to input types: https://github.com/iseahound/ImagePut/wiki/Input-Types-&-Output-Functions#input-types (scroll to screenshot or window)
You have 2 options to get the client area:
Code: Select all
; For testing, you should probably use ImagePutWindow({screenshot: "ahk_class notepad", crop: [x, y, w, h]})
test := OCR({window: "ahk_class notepad", crop: [x, y, w, h]})
test := OCR({screenshot: "ahk_class notepad", crop: [x, y, w, h]})
Documentation to input types: https://github.com/iseahound/ImagePut/wiki/Input-Types-&-Output-Functions#input-types (scroll to screenshot or window)
Last edited by iseahound on 29 Mar 2023, 10:34, edited 1 time in total.
Re: Vis2 - Image to Text OCR()
I'm not sure what A_LineFile does although it seems to work. A_LineFile should designate the exact File itself, so if I'm understanding correctly,
Code: Select all
A_LineFile
C:/Vis2/lib/Vis2.ahk
A_LineFile\..\ImagePut.ahk
C:/Vis2/lib/ImagePut.ahk
Re: Vis2 - Image to Text OCR()
Yes, and this allows the library to be used even when it's in a directory other than the one where the script is.iseahound wrote: ↑29 Mar 2023, 10:11I'm not sure what A_LineFile does although it seems to work. A_LineFile should designate the exact File itself, so if I'm understanding correctly,
So the .. lets you "go up a directory" on a File!?Code: Select all
A_LineFile C:/Vis2/lib/Vis2.ahk A_LineFile\..\ImagePut.ahk C:/Vis2/lib/ImagePut.ahk
Re: Vis2 - Image to Text OCR()
I've been using this tool so far via the OCR([x, y, w, h]) function, and its mostly been working fine.
However, I found one case when it doesn't, with a white text on a blue background with a somewhat dotted pattern.
Doesn't recognise the text at all.
Also tried taking a screenshot, cropping the area, and feeding it to the function as a file - that also fails.
However, when I use demo.ahk and its interactive GUI function via Win+C - that identifies the exact same text every time no problem.
Why? What's the difference here?
However, I found one case when it doesn't, with a white text on a blue background with a somewhat dotted pattern.
Doesn't recognise the text at all.
Also tried taking a screenshot, cropping the area, and feeding it to the function as a file - that also fails.
However, when I use demo.ahk and its interactive GUI function via Win+C - that identifies the exact same text every time no problem.
Why? What's the difference here?
Re: Vis2 - Image to Text OCR()
So I read that:
If so, then I am getting the opposite result to what should be happening - fast version successfully recognising the text,
while best version failing entirely.
Is this why?Fast is used in the interactive GUI implementation, while best will be used for other cases.
If so, then I am getting the opposite result to what should be happening - fast version successfully recognising the text,
while best version failing entirely.
Re: Vis2 - Image to Text OCR()
Hello, how can i use the the OCR() function without the overlayed text?
Just select the area and get the text as a return value.
Thanks
Daniel
Just select the area and get the text as a return value.
Thanks
Daniel
-
- Posts: 1
- Joined: 15 Jun 2023, 21:58
Re: Vis2 - Image to Text OCR()
Seems that it is still to save the screen bits to a file, then use tesseract.exe to OCR the file.
can we ocr the hbm in the memory via tessearact API and skip the saving file?? not sure if it is possible...
can we ocr the hbm in the memory via tessearact API and skip the saving file?? not sure if it is possible...
Return to “Scripts and Functions (v1)”
Who is online
Users browsing this forum: No registered users and 132 guests