-
Notifications
You must be signed in to change notification settings - Fork 11
/
screenshoter.ahk
406 lines (363 loc) · 15.1 KB
/
screenshoter.ahk
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
/*adapted from original:
SilentScreenshooter v1.9
https://github.com/Drugoy/Autohotkey-scripts-.ahk/edit/master/SilentScreenshotter/SilentScreenshotter(+upload).ahk
*/
;{ Initialization before settings
#SingleInstance, Off
#NoEnv
Menu, tray, icon, %A_WinDir%\system32\shell32.dll, 140
SetWorkingDir, %A_ScriptDir%
FileInstall, optipng.exe, optipng.exe
CoordMode, Mouse
CoordMode, Pixel
SetBatchLines, -1
Hotkey, $Esc,, Off
OnExit, Exit
;}
;{ Settings
If (A_IsCompiled)
{
IniRead, imgPath, settings.ini, settings, imgPath, % A_Temp "\"
IniRead, imgName, settings.ini, settings, imgName, % A_Now
IniRead, imgExtension, settings.ini, settings, imgExtension, png
IniRead, optimizePNG, settings.ini, settings, optimizePNG, 7
IniRead, optipngPath, settings.ini, settings, optipngPath, % A_ScriptDir "\optipng.exe"
IniRead, clipURL, settings.ini, settings, clipURL, 1
IniRead, tempScreenshot, settings.ini, settings, tempScreenshot, 1
IniRead, jpgQuality, settings.ini, settings, jpgQuality, 100
}
Else
{
imgPath := A_ScriptDir "\" ; Specify path and screenshot's name. A_Temp %MyWin%
imgName := A_Now ; Specify locally saved image's name. Default name is the date stamp (with time).
imgExtension := "png" ; Specify desired file format (most of common formats are supported).
optimizePNG := 0 ; Use values from 0 to 7 to specify the compression level: 0 = no compression, 7 = max compression. Compression is always lossless, but works only for PNG.
optipngPath := A_ScriptDir "\optipng.exe" ; Specify path to "optipng.exe" If you would like to use it.
clipURL := 2 ; 0 = the image's URL will be opened in browser; 1 = copy to clipboard; 2 = do both.
tempScreenshot := 0 ; 0 = the local screenshot won't get deleted after it got uploaded to the server, 1 = it will be removed as soon as the file got uploaded to the server.
jpgQuality := 100 ; Use values from 0 to 100 to specify quality for screenshots in JPEG (by default PNG is used).
; ListLines, Off ; Uncomment this if the script is fully working for you and you'd like to save a bit of RAM by sacrificing script's self-debugging ability.
}
;}
Global imgurClientID, proxyEnable, proxyServer, clipURL, tempScreenshot, uploadProgress
Global ptr := A_PtrSize ? "UPtr" : "UInt"
targetToUpload := []
RegRead, proxyEnable, HKCU, Software\Microsoft\Windows\CurrentVersion\Internet Settings, ProxyEnable ; Detect wheter proxy is used or not.
If (proxyEnable)
RegRead, proxyServer, HKCU, Software\Microsoft\Windows\CurrentVersion\Internet Settings, ProxyServer ; Detect address of proxy.
imgName := A_Now
imgPath := A_ScriptDir "\" imgName "." imgExtension
pToken := Gdip_Startup()
If (%0%) ; Usually %0% contains the number of command line parameters, but when the user drag'n'drops files onto the script - each of the dropped file gets sent to script as a separate command line parameter, so %0% contains the number of dropped files.
{
Loop, %0%
targetToUpload.Insert({"path": %A_Index%, "dnd": 1})
upload(targetToUpload)
}
If (isThereAnotherInstance())
ExitApp
Return
$Esc:: ; Escape hotkey is used in this script to cancel screenshot area selection. This hotkey is temporary and is enabled only during the periods when the screenshot area selection is active (when red border follows the cursor), the rest of the time this hotkey is disabled in order not to cause any issues with the "Escape" key's work in other programs.
Hotkey, $Esc,, Off
DllCall("gdiplus\GdipDisposeImage", ptr, pBitmap)
firstHit_EventFired := ""
Gui, 1: Destroy
Return
PrintScreen:: ; Since we use the same hotkey trice, we have to distinguish the calls.
Thread, Priority, 1
KeyWait, %A_ThisHotkey%
If !(firstHit_EventFired) ; The user hit PrintScreen - this is a first step.
{
Hotkey, $Esc,, On
SysGet, x0, 76
SysGet, y0, 77
SysGet, w0, 78
SysGet, h0, 79
firstHit_EventFired := 1
MouseGetPos, x1, y1
Gui, 1: -Caption +E0x80000 +HWNDhwnd1 +LastFound +AlwaysOnTop +ToolWindow +OwnDialogs ; Create a GUI to use it as a canvas for GDI+ drawing.
Gui, 1: Show, NA
Loop
{
; Draw a rectangular following the cursor.
MouseGetPos, x2, y2
hbm := CreateDIBSection(w0, h0)
hdc := DllCall("CreateCompatibleDC", ptr, 0)
obm := DllCall("SelectObject", ptr, hdc, ptr, hbm)
DllCall("gdiplus\GdipCreateFromHDC", ptr, hdc, ptr "*", G)
DllCall("gdiplus\GdipSetSmoothingMode", ptr, G, "Int", 4)
DllCall("gdiplus\GdipCreatePen1", "UInt", 0xffff0000, "Float", 1, "Int", 2, ptr "*", pPen)
Gdip_DrawLines(G, pPen, x1 - x0 "," y1 - y0 "|" x2 - x0 "," y1 - y0 "|" x2 - x0 "," y2 - y0 "|" x1 - x0 "," y2 - y0 "|" x1 - x0 "," y1 - y0)
DllCall("gdiplus\GdipDeleteBrush", ptr, pPen)
UpdateLayeredWindow(hwnd1, hdc, x0, y0, w0, h0)
DllCall("SelectObject", ptr, hdc, ptr, obm)
DllCall("DeleteObject", ptr, hbm)
DllCall("DeleteDC", ptr, hdc)
DllCall("gdiplus\GdipDeleteGraphics", ptr, G)
Sleep, 20 ; This pause is used to redraw the rectangular less frequently in order to consume less CPU resources. You may adjust the value (it's in miliseconds).
If GetKeyState("Escape", "P") ; User decided to abort screenshooting.
{
GoSub, $Esc ; Abort drawing, unregister "Escape" hotkey.
Break
}
; Second step: user finished selecting the area to screenshoot.
If (GetKeyState("LButton", "P"))
Break
If (GetKeyState(A_ThisHotkey, "P"))
{
KeyWait, %A_ThisHotkey% ; In order to get away from possible issues - let's wait until the PrintScreen key is unpressed.
Break
}
}
Return
}
Else ; User has to hit PrintScreen once again (for the 3rd time) to take a screenshot. I inteionally made not 2, but 3 steps required to take a screenshot: so you can take a screenshot of some event happening, for example, only when you hover something special.
{
Hotkey, $Esc,, Off
firstHit_EventFired := ""
Gui, 1: Destroy ; Hide the rectangular before screenshotting the area
; Save a screenshot to a file.
pBitmap := Gdip_BitmapFromScreen((x1 < x2 ? x1 : x2) "|" (y1 < y2 ? y1 : y2) "|" (x1 < x2 ? x2 - x1 + 1 : x1 - x2 + 1) "|" (y1 < y2 ? y2 - y1 + 1 : y1 - y2 + 1))
imgName := A_Now
imgPath := A_ScriptDir "\" imgName "." imgExtension
Gdip_SaveBitmapToFile(pBitmap, imgPath, jpgQuality)
While !(FileExist(imgPath)) ; Wait until the file gets actually created (otherwise the script will execute the next part too fast).
Sleep, 25
DllCall("gdiplus\GdipDisposeImage", ptr, pBitmap) ; Clean after self.
If (optimizePNG) ; Run png optimizator if user chose to do so.
IfExist, %optipngPath% ; Run it only if it exists.
RunWait, %optipngPath% -o%optimizePNG% -i0 -nc -nb -q -clobber %imgPath%,, Hide
Else
TrayTip, Error, Optipng not found`, thus can't optimize the image.
targetToUpload := []
targetToUpload.Insert({"path": imgPath})
MsgBox, "Copy to clipboard :" %imgPath%
IfMsgBox, OK
Clipboard := imgPath
Return ; User pressed the "No" button.
}
ExitApp
Return
Exit:
DllCall("gdiplus\GdiplusShutdown", ptr, pToken)
If (hModule := DllCall("GetModuleHandle", "Str", "gdiplus", ptr))
DllCall("FreeLibrary", ptr, hModule)
ExitApp
upload(input) ; Thanks to: maestrith http://www.autohotkey.com/board/user/910-maestrith/ and GeekDude https://github.com/G33kDude
{ ; Upload to Imgur using it's API.
Critical, On
imgURLs := [], filtered := uploadProgress := trueIndex := 0
httpObj := Object()
Gui, -Caption +Border +AlwaysOnTop +ToolWindow
; Gui, ProgressBar: Color, EEAA99
Gui, +LastFound
; WinSet, TransColor, EEAA99
Gui, Add, Text, x0 y0, Uploading image(s)
Gui, Add, Progress, w300 h10 cGreen -0x1 vuploadProgress
Gui, Show, % "x" A_ScreenWidth - 300 " y" A_ScreenHeight -35, Image upload progress
img := ComObjCreate("WIA.ImageFile")
For k, v In input
{
FileGetSize, fileSize, % v.path
If (ErrorLevel)
{
MsgBox, % "There was a problem accessing that file:`n" v.path "`nErrorLevel: " Errorlevel
filtered++
Continue
}
Else If (filesize >= 10485760)
{
MsgBox, % "Skipping file " v.path " that is " filesize/1048576 "Mb, which is beyond Imgur's 10Mb limit for one file."
filtered++
Continue
}
Try img.LoadFile(v.path)
Catch, e
{
MsgBox, % "This was an invalid image or not an image file at all.`nFile: " v.path "`nError: " e "`nError message: " e.Message "`nError what: " e.What "`nError extra: " e.Extra "`nError file: " e.File "`nError line: " e.Line
filtered++
Continue
}
data := img.FileData.BinaryData
httpObj[++trueIndex] := ComObjCreate("WinHttp.WinHttpRequest.5.1")
;httpObj[trueIndex].Open("POST", "https://api.imgur.com/3/upload")
If (proxyEnable)
; {
httpObj[trueIndex].SetProxy(2, proxyServer)
If (tempScreenshot && !v.dnd) ; User specified to delete the local screenshot's file after uploading it.
FileDelete, % v.path
}
filtered := trueIndex := 0
While (httpObj.Length())
{
trueIndex := (++trueIndex > httpObj.Length() ? trueIndex - httpObj.Length() : trueIndex++)
}
For k, v in imgURLs
If (clipURL != 1)
Run, % v
If (clipURL)
Clipboard := arr2ASV(imgURLs)
Gui, Destroy
Critical, Off
}
arr2ASV(arr, separator := " ") ; Parses the input array and outputs it's values as a string with anchor-separated values.
{
For k, v In arr
var ? var .= separator v : var := v
Return var
}
isThereAnotherInstance() ; Thanks to: GeekDude http://www.autohotkey.com/board/user/10132-geekdude/
{
DetectHiddenWindows, On
WinGet, wins, List, ahk_class AutoHotkey
Loop, %wins%
{
WinGetTitle, win, % "ahk_id " wins%A_Index%
If (RegExReplace(win, "^(.*) - AutoHotkey v[0-9\.]+$", "$1") == A_ScriptFullPath)
{
WinGet, wpid, PID, % "ahk_id " wins%A_Index%
If (wpid != DllCall("GetCurrentProcessId"))
Return wpid
}
}
DetectHiddenWindows, Off
Return 0
}
;{ GDI+ functions
Gdip_Startup()
{
If !(DllCall("GetModuleHandle", "Str", "gdiplus", ptr))
DllCall("LoadLibrary", "Str", "gdiplus")
VarSetCapacity(si, A_PtrSize = 8 ? 24 : 16, 0), si := Chr(1)
DllCall("gdiplus\GdiplusStartup", ptr "*", pToken, ptr, &si, ptr, 0)
Return pToken
}
CreateDIBSection(w, h, hdc = "")
{
hdc2 := hdc ? hdc : DllCall("GetDC", ptr, 0)
VarSetCapacity(bi, 40, 0)
NumPut(w, bi, 4, "UInt"), NumPut(h, bi, 8, "UInt"), NumPut(40, bi, 0, "UInt"), NumPut(1, bi, 12, "ushort"), NumPut(0, bi, 16, "UInt"), NumPut(32, bi, 14, "ushort")
hbm := DllCall("CreateDIBSection", ptr, hdc2, ptr, &bi, "UInt", 0, ptr "*", 0, Ptr, 0, "UInt", 0, ptr)
If !(hdc)
DllCall("ReleaseDC", ptr, 0, ptr, hdc2)
Return hbm
}
Gdip_DrawLines(pGraphics, pPen, Points)
{
StringSplit, Points, Points, |
VarSetCapacity(PointF, 8 * Points0)
Loop, % Points0
{
StringSplit, Coord, Points%A_Index%, `,
NumPut(Coord1, PointF, 8 * (A_Index - 1), "Float"), NumPut(Coord2, PointF, (8 * (A_Index - 1)) + 4, "Float")
}
Return DllCall("gdiplus\GdipDrawLines", ptr, pGraphics, ptr, pPen, ptr, &PointF, "Int", Points0)
}
UpdateLayeredWindow(hwnd, hdc, x = "", y = "", w = "", h = "", Alpha = 255)
{
If ((x != "") && (y != ""))
VarSetCapacity(pt, 8), NumPut(x, pt, 0, "UInt"), NumPut(y, pt, 4, "UInt")
If ((w = "") || (h = ""))
WinGetPos,,, w, h, ahk_id %hwnd%
Return DllCall("UpdateLayeredWindow", ptr, hwnd, ptr, 0, ptr, ((x = "") && (y = "")) ? 0 : &pt, "Int64*", w | h << 32, ptr, hdc, "Int64*", 0, "UInt", 0, "UInt*", Alpha << 16 | 1 << 24, "UInt", 2)
}
Gdip_BitmapFromScreen(screen)
{
If (SubStr(screen, 1, 5) = "hwnd:")
{
screen := SubStr(screen, 6)
If !(WinExist("ahk_id " screen))
Return -2
WinGetPos,,, w, h, ahk_id %screen%
x := y := 0
hhdc := DllCall("GetDCEx", ptr, screen, ptr, 0, "Int", 3)
}
Else If (screen&1 != "")
{
Sysget, M, Monitor, %screen%
x := MLeft, y := MTop, w := MRight - MLeft, h := MBottom - MTop
}
Else
{
StringSplit, S, screen, |
x := S1, y := S2, w := S3, h := S4
}
If ((x = "") || (y = "") || (w = "") || (h = ""))
Return -1
chdc := DllCall("CreateCompatibleDC", ptr, 0), hbm := CreateDIBSection(w, h, chdc), obm := DllCall("SelectObject", ptr, chdc, ptr, hbm), hhdc := hhdc ? hhdc : DllCall("GetDC", ptr, 0)
DllCall("gdi32\BitBlt", ptr, chdc, "Int", 0, "Int", 0, "Int", w, "Int", h, ptr, hhdc, "Int", x, "Int", y, "UInt", 0x00CC0020)
DllCall("ReleaseDC", ptr, 0, ptr, hhdc)
DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", ptr, hbm, ptr, 0, ptr "*", pBitmap)
DllCall("SelectObject", ptr, chdc, Ptr, obm), DllCall("DeleteObject", ptr, hbm), DllCall("DeleteDC", ptr, hhdc), DllCall("DeleteDC", ptr, chdc)
Return pBitmap
}
Gdip_SaveBitmapToFile(pBitmap, sOutput, quality = 100)
{
SplitPath, sOutput,,, Extension
If Extension Not In BMP,DIB,RLE,JPG,JPEG,JPE,JFIF,GIF,TIF,TIFF,PNG
Return -1
DllCall("gdiplus\GdipGetImageEncodersSize", "UInt*", nCount, "UInt*", nSize)
VarSetCapacity(ci, nSize)
DllCall("gdiplus\GdipGetImageEncoders", "UInt", nCount, "UInt", nSize, ptr, &ci)
If !(nCount && nSize)
Return -2
If (A_IsUnicode)
{
StrGet_Name := "StrGet"
Loop, % nCount
{
sString := %StrGet_Name%(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
}
}
Else
{
Loop, % nCount
{
Location := NumGet(ci, 76 * (A_Index - 1) + 44)
nSize := DllCall("WideCharToMultiByte", "UInt", 0, "UInt", 0, "UInt", Location, "Int", -1, "UInt", 0, "Int", 0, "UInt", 0, "UInt", 0)
VarSetCapacity(sString, nSize)
DllCall("WideCharToMultiByte", "UInt", 0, "UInt", 0, "UInt", Location, "Int", -1, "Str", sString, "Int", nSize, "UInt", 0, "UInt", 0)
If !(InStr(sString, "*." Extension))
Continue
pCodec := &ci + 76 * (A_Index - 1)
Break
}
}
If !(pCodec)
Return -3
If Extension in JPG,JPEG,JPE,JFIF
{
quality := (quality < 0) ? 0 : ((quality > 100) ? 100 : quality)
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 +1 6, "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
}
}
}
If !(A_IsUnicode)
{
nSize := DllCall("MultiByteToWideChar", "UInt", 0, "UInt", 0, ptr, &sOutput, "Int", -1, ptr, 0, "Int", 0)
VarSetCapacity(wOutput, nSize * 2)
DllCall("MultiByteToWideChar", "UInt", 0, "UInt", 0, ptr, &sOutput, "Int", -1, ptr, &wOutput, "Int", nSize)
VarSetCapacity(wOutput, -1)
If !(VarSetCapacity(wOutput))
Return -4
E := DllCall("gdiplus\GdipSaveImageToFile", ptr, pBitmap, ptr, &wOutput, ptr, pCodec, "UInt", p ? p : 0)
}
Else
E := DllCall("gdiplus\GdipSaveImageToFile", ptr, pBitmap, ptr, &sOutput, ptr, pCodec, "UInt", p ? p : 0)
Return E ? -5 : 0
}
;}