diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..dfe0770 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/Resources/Bloqueado.png b/Resources/Bloqueado.png new file mode 100644 index 0000000..d09d316 Binary files /dev/null and b/Resources/Bloqueado.png differ diff --git a/Resources/Desbloqueado.png b/Resources/Desbloqueado.png new file mode 100644 index 0000000..f3f3ce4 Binary files /dev/null and b/Resources/Desbloqueado.png differ diff --git a/Resources/Saliendo.png b/Resources/Saliendo.png new file mode 100644 index 0000000..75f9654 Binary files /dev/null and b/Resources/Saliendo.png differ diff --git a/Resources/Tirabolas.png b/Resources/Tirabolas.png new file mode 100644 index 0000000..f0473d5 Binary files /dev/null and b/Resources/Tirabolas.png differ diff --git a/Resources/Tutorial.png b/Resources/Tutorial.png new file mode 100644 index 0000000..e921a03 Binary files /dev/null and b/Resources/Tutorial.png differ diff --git a/Resources/icon.ico b/Resources/icon.ico new file mode 100644 index 0000000..4ca2b08 Binary files /dev/null and b/Resources/icon.ico differ diff --git a/Tirabolas 3000.rar b/Tirabolas 3000.rar new file mode 100644 index 0000000..fb4cdb1 Binary files /dev/null and b/Tirabolas 3000.rar differ diff --git a/lib/Gdip_All.ahk b/lib/Gdip_All.ahk new file mode 100644 index 0000000..5eee0f3 --- /dev/null +++ b/lib/Gdip_All.ahk @@ -0,0 +1,2973 @@ +; Gdip standard library v1.54 on 11/15/2017 +; Gdip standard library v1.53 on 6/19/2017 +; Gdip standard library v1.52 on 6/11/2017 +; Gdip standard library v1.51 on 1/27/2017 +; Gdip standard library v1.50 on 11/20/16 +; Gdip standard library v1.45 by tic (Tariq Porter) 07/09/11 +; Modifed by Rseding91 using fincs 64 bit compatible Gdip library 5/1/2013 +; Supports: Basic, _L ANSi, _L Unicode x86 and _L Unicode x64 +; +; Updated 11/15/2017 - compatibility with both AHK v2 and v1, restored by nnnik +; Updated 6/19/2017 - Fixed few bugs from old syntax by Bartlomiej Uliasz +; Updated 6/11/2017 - made code compatible with new AHK v2.0-a079-be5df98 by Bartlomiej Uliasz +; Updated 1/27/2017 - fixed some bugs and made #Warn All compatible by Bartlomiej Uliasz +; Updated 11/20/2016 - fixed Gdip_BitmapFromBRA() by 'just me' +; Updated 11/18/2016 - backward compatible support for both AHK v1.1 and AHK v2 +; Updated 11/15/2016 - initial AHK v2 support by guest3456 +; Updated 2/20/2014 - fixed Gdip_CreateRegion() and Gdip_GetClipRegion() on AHK Unicode x86 +; Updated 5/13/2013 - fixed Gdip_SetBitmapToClipboard() on AHK Unicode x64 +; +;##################################################################################### +;##################################################################################### +; STATUS ENUMERATION +; Return values for functions specified to have status enumerated return type +;##################################################################################### +; +; Ok = = 0 +; GenericError = 1 +; InvalidParameter = 2 +; OutOfMemory = 3 +; ObjectBusy = 4 +; InsufficientBuffer = 5 +; NotImplemented = 6 +; Win32Error = 7 +; WrongState = 8 +; Aborted = 9 +; FileNotFound = 10 +; ValueOverflow = 11 +; AccessDenied = 12 +; UnknownImageFormat = 13 +; FontFamilyNotFound = 14 +; FontStyleNotFound = 15 +; NotTrueTypeFont = 16 +; UnsupportedGdiplusVersion = 17 +; GdiplusNotInitialized = 18 +; PropertyNotFound = 19 +; PropertyNotSupported = 20 +; ProfileNotFound = 21 +; +;##################################################################################### +;##################################################################################### +; FUNCTIONS +;##################################################################################### +; +; UpdateLayeredWindow(hwnd, hdc, x:="", y:="", w:="", h:="", Alpha:=255) +; BitBlt(ddc, dx, dy, dw, dh, sdc, sx, sy, Raster:="") +; StretchBlt(dDC, dx, dy, dw, dh, sDC, sx, sy, sw, sh, Raster:="") +; SetImage(hwnd, hBitmap) +; Gdip_BitmapFromScreen(Screen:=0, Raster:="") +; CreateRectF(ByRef RectF, x, y, w, h) +; CreateSizeF(ByRef SizeF, w, h) +; CreateDIBSection +; +;##################################################################################### + +; Function: UpdateLayeredWindow +; Description: Updates a layered window with the handle to the DC of a gdi bitmap +; +; hwnd Handle of the layered window to update +; hdc Handle to the DC of the GDI bitmap to update the window with +; Layeredx x position to place the window +; Layeredy y position to place the window +; Layeredw Width of the window +; Layeredh Height of the window +; Alpha Default = 255 : The transparency (0-255) to set the window transparency +; +; return If the function succeeds, the return value is nonzero +; +; notes If x or y omitted, then layered window will use its current coordinates +; If w or h omitted then current width and height will be used + +UpdateLayeredWindow(hwnd, hdc, x:="", y:="", w:="", h:="", Alpha:=255) +{ + Ptr := A_PtrSize ? "UPtr" : "UInt" + + if ((x != "") && (y != "")) + VarSetCapacity(pt, 8), NumPut(x, pt, 0, "UInt"), NumPut(y, pt, 4, "UInt") + + if (w = "") || (h = "") + { + CreateRect( winRect, 0, 0, 0, 0 ) ;is 16 on both 32 and 64 + DllCall( "GetWindowRect", Ptr, hwnd, Ptr, &winRect ) + w := NumGet(winRect, 8, "UInt") - NumGet(winRect, 0, "UInt") + h := NumGet(winRect, 12, "UInt") - NumGet(winRect, 4, "UInt") + } + + 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) +} + +;##################################################################################### + +; Function BitBlt +; Description The BitBlt function performs a bit-block transfer of the color data corresponding to a rectangle +; of pixels from the specified source device context into a destination device context. +; +; dDC handle to destination DC +; dx x-coord of destination upper-left corner +; dy y-coord of destination upper-left corner +; dw width of the area to copy +; dh height of the area to copy +; sDC handle to source DC +; sx x-coordinate of source upper-left corner +; sy y-coordinate of source upper-left corner +; Raster raster operation code +; +; return If the function succeeds, the return value is nonzero +; +; notes If no raster operation is specified, then SRCCOPY is used, which copies the source directly to the destination rectangle +; +; BLACKNESS = 0x00000042 +; NOTSRCERASE = 0x001100A6 +; NOTSRCCOPY = 0x00330008 +; SRCERASE = 0x00440328 +; DSTINVERT = 0x00550009 +; PATINVERT = 0x005A0049 +; SRCINVERT = 0x00660046 +; SRCAND = 0x008800C6 +; MERGEPAINT = 0x00BB0226 +; MERGECOPY = 0x00C000CA +; SRCCOPY = 0x00CC0020 +; SRCPAINT = 0x00EE0086 +; PATCOPY = 0x00F00021 +; PATPAINT = 0x00FB0A09 +; WHITENESS = 0x00FF0062 +; CAPTUREBLT = 0x40000000 +; NOMIRRORBITMAP = 0x80000000 + +BitBlt(ddc, dx, dy, dw, dh, sdc, sx, sy, Raster:="") +{ + Ptr := A_PtrSize ? "UPtr" : "UInt" + + return DllCall("gdi32\BitBlt" + , Ptr, dDC + , "int", dx + , "int", dy + , "int", dw + , "int", dh + , Ptr, sDC + , "int", sx + , "int", sy + , "uint", Raster ? Raster : 0x00CC0020) +} + +;##################################################################################### + +; Function StretchBlt +; Description The StretchBlt function copies a bitmap from a source rectangle into a destination rectangle, +; stretching or compressing the bitmap to fit the dimensions of the destination rectangle, if necessary. +; The system stretches or compresses the bitmap according to the stretching mode currently set in the destination device context. +; +; ddc handle to destination DC +; dx x-coord of destination upper-left corner +; dy y-coord of destination upper-left corner +; dw width of destination rectangle +; dh height of destination rectangle +; sdc handle to source DC +; sx x-coordinate of source upper-left corner +; sy y-coordinate of source upper-left corner +; sw width of source rectangle +; sh height of source rectangle +; Raster raster operation code +; +; return If the function succeeds, the return value is nonzero +; +; notes If no raster operation is specified, then SRCCOPY is used. It uses the same raster operations as BitBlt + +StretchBlt(ddc, dx, dy, dw, dh, sdc, sx, sy, sw, sh, Raster:="") +{ + Ptr := A_PtrSize ? "UPtr" : "UInt" + + return DllCall("gdi32\StretchBlt" + , Ptr, ddc + , "int", dx + , "int", dy + , "int", dw + , "int", dh + , Ptr, sdc + , "int", sx + , "int", sy + , "int", sw + , "int", sh + , "uint", Raster ? Raster : 0x00CC0020) +} + +;##################################################################################### + +; Function SetStretchBltMode +; Description The SetStretchBltMode function sets the bitmap stretching mode in the specified device context +; +; hdc handle to the DC +; iStretchMode The stretching mode, describing how the target will be stretched +; +; return If the function succeeds, the return value is the previous stretching mode. If it fails it will return 0 +; +; STRETCH_ANDSCANS = 0x01 +; STRETCH_ORSCANS = 0x02 +; STRETCH_DELETESCANS = 0x03 +; STRETCH_HALFTONE = 0x04 + +SetStretchBltMode(hdc, iStretchMode:=4) +{ + return DllCall("gdi32\SetStretchBltMode" + , A_PtrSize ? "UPtr" : "UInt", hdc + , "int", iStretchMode) +} + +;##################################################################################### + +; Function SetImage +; Description Associates a new image with a static control +; +; hwnd handle of the control to update +; hBitmap a gdi bitmap to associate the static control with +; +; return If the function succeeds, the return value is nonzero + +SetImage(hwnd, hBitmap) +{ + Ptr := A_PtrSize ? "UPtr" : "UInt" + E := DllCall( "SendMessage", Ptr, hwnd, "UInt", 0x172, "UInt", 0x0, Ptr, hBitmap ) + DeleteObject(E) + return E +} + +;##################################################################################### + +; Function SetSysColorToControl +; Description Sets a solid colour to a control +; +; hwnd handle of the control to update +; SysColor A system colour to set to the control +; +; return If the function succeeds, the return value is zero +; +; notes A control must have the 0xE style set to it so it is recognised as a bitmap +; By default SysColor=15 is used which is COLOR_3DFACE. This is the standard background for a control +; +; COLOR_3DDKSHADOW = 21 +; COLOR_3DFACE = 15 +; COLOR_3DHIGHLIGHT = 20 +; COLOR_3DHILIGHT = 20 +; COLOR_3DLIGHT = 22 +; COLOR_3DSHADOW = 16 +; COLOR_ACTIVEBORDER = 10 +; COLOR_ACTIVECAPTION = 2 +; COLOR_APPWORKSPACE = 12 +; COLOR_BACKGROUND = 1 +; COLOR_BTNFACE = 15 +; COLOR_BTNHIGHLIGHT = 20 +; COLOR_BTNHILIGHT = 20 +; COLOR_BTNSHADOW = 16 +; COLOR_BTNTEXT = 18 +; COLOR_CAPTIONTEXT = 9 +; COLOR_DESKTOP = 1 +; COLOR_GRADIENTACTIVECAPTION = 27 +; COLOR_GRADIENTINACTIVECAPTION = 28 +; COLOR_GRAYTEXT = 17 +; COLOR_HIGHLIGHT = 13 +; COLOR_HIGHLIGHTTEXT = 14 +; COLOR_HOTLIGHT = 26 +; COLOR_INACTIVEBORDER = 11 +; COLOR_INACTIVECAPTION = 3 +; COLOR_INACTIVECAPTIONTEXT = 19 +; COLOR_INFOBK = 24 +; COLOR_INFOTEXT = 23 +; COLOR_MENU = 4 +; COLOR_MENUHILIGHT = 29 +; COLOR_MENUBAR = 30 +; COLOR_MENUTEXT = 7 +; COLOR_SCROLLBAR = 0 +; COLOR_WINDOW = 5 +; COLOR_WINDOWFRAME = 6 +; COLOR_WINDOWTEXT = 8 + +SetSysColorToControl(hwnd, SysColor:=15) +{ + Ptr := A_PtrSize ? "UPtr" : "UInt" + CreateRect( winRect, 0, 0, 0, 0 ) ;is 16 on both 32 and 64 + DllCall( "GetWindowRect", Ptr, hwnd, Ptr, &winRect ) + w := NumGet(winRect, 8, "UInt") - NumGet(winRect, 0, "UInt") + h := NumGet(winRect, 12, "UInt") - NumGet(winRect, 4, "UInt") + bc := DllCall("GetSysColor", "Int", SysColor, "UInt") + pBrushClear := Gdip_BrushCreateSolid(0xff000000 | (bc >> 16 | bc & 0xff00 | (bc & 0xff) << 16)) + pBitmap := Gdip_CreateBitmap(w, h), G := Gdip_GraphicsFromImage(pBitmap) + Gdip_FillRectangle(G, pBrushClear, 0, 0, w, h) + hBitmap := Gdip_CreateHBITMAPFromBitmap(pBitmap) + SetImage(hwnd, hBitmap) + Gdip_DeleteBrush(pBrushClear) + Gdip_DeleteGraphics(G), Gdip_DisposeImage(pBitmap), DeleteObject(hBitmap) + return 0 +} + +;##################################################################################### + +; Function Gdip_BitmapFromScreen +; Description Gets a gdi+ bitmap from the screen +; +; Screen 0 = All screens +; Any numerical value = Just that screen +; x|y|w|h = Take specific coordinates with a width and height +; Raster raster operation code +; +; return If the function succeeds, the return value is a pointer to a gdi+ bitmap +; -1: one or more of x,y,w,h not passed properly +; +; notes If no raster operation is specified, then SRCCOPY is used to the returned bitmap + +Gdip_BitmapFromScreen(Screen:=0, Raster:="") +{ + hhdc := 0 + Ptr := A_PtrSize ? "UPtr" : "UInt" + if (Screen = 0) + { + _x := DllCall( "GetSystemMetrics", "Int", 76 ) + _y := DllCall( "GetSystemMetrics", "Int", 77 ) + _w := DllCall( "GetSystemMetrics", "Int", 78 ) + _h := DllCall( "GetSystemMetrics", "Int", 79 ) + } + else if (SubStr(Screen, 1, 5) = "hwnd:") + { + Screen := SubStr(Screen, 6) + if !WinExist("ahk_id " Screen) + return -2 + CreateRect( winRect, 0, 0, 0, 0 ) ;is 16 on both 32 and 64 + DllCall( "GetWindowRect", Ptr, Screen, Ptr, &winRect ) + _w := NumGet(winRect, 8, "UInt") - NumGet(winRect, 0, "UInt") + _h := NumGet(winRect, 12, "UInt") - NumGet(winRect, 4, "UInt") + _x := _y := 0 + hhdc := GetDCEx(Screen, 3) + } + else if IsInteger(Screen) + { + M := GetMonitorInfo(Screen) + _x := M.Left, _y := M.Top, _w := M.Right-M.Left, _h := M.Bottom-M.Top + } + else + { + S := StrSplit(Screen, "|") + _x := S[1], _y := S[2], _w := S[3], _h := S[4] + } + + if (_x = "") || (_y = "") || (_w = "") || (_h = "") + return -1 + + chdc := CreateCompatibleDC(), hbm := CreateDIBSection(_w, _h, chdc), obm := SelectObject(chdc, hbm), hhdc := hhdc ? hhdc : GetDC() + BitBlt(chdc, 0, 0, _w, _h, hhdc, _x, _y, Raster) + ReleaseDC(hhdc) + + pBitmap := Gdip_CreateBitmapFromHBITMAP(hbm) + SelectObject(chdc, obm), DeleteObject(hbm), DeleteDC(hhdc), DeleteDC(chdc) + return pBitmap +} + +;##################################################################################### + +; Function Gdip_BitmapFromHWND +; Description Uses PrintWindow to get a handle to the specified window and return a bitmap from it +; +; hwnd handle to the window to get a bitmap from +; +; return If the function succeeds, the return value is a pointer to a gdi+ bitmap +; +; notes Window must not be not minimised in order to get a handle to it's client area + +Gdip_BitmapFromHWND(hwnd) +{ + Ptr := A_PtrSize ? "UPtr" : "UInt" + CreateRect( winRect, 0, 0, 0, 0 ) ;is 16 on both 32 and 64 + DllCall( "GetWindowRect", Ptr, hwnd, Ptr, &winRect ) + Width := NumGet(winRect, 8, "UInt") - NumGet(winRect, 0, "UInt") + Height := NumGet(winRect, 12, "UInt") - NumGet(winRect, 4, "UInt") + hbm := CreateDIBSection(Width, Height), hdc := CreateCompatibleDC(), obm := SelectObject(hdc, hbm) + PrintWindow(hwnd, hdc) + pBitmap := Gdip_CreateBitmapFromHBITMAP(hbm) + SelectObject(hdc, obm), DeleteObject(hbm), DeleteDC(hdc) + return pBitmap +} + +;##################################################################################### + +; Function CreateRectF +; Description Creates a RectF object, containing a the coordinates and dimensions of a rectangle +; +; RectF Name to call the RectF object +; x x-coordinate of the upper left corner of the rectangle +; y y-coordinate of the upper left corner of the rectangle +; w Width of the rectangle +; h Height of the rectangle +; +; return No return value + +CreateRectF(ByRef RectF, x, y, w, h) +{ + VarSetCapacity(RectF, 16) + NumPut(x, RectF, 0, "float"), NumPut(y, RectF, 4, "float"), NumPut(w, RectF, 8, "float"), NumPut(h, RectF, 12, "float") +} + +;##################################################################################### + +; Function CreateRect +; Description Creates a Rect object, containing a the coordinates and dimensions of a rectangle +; +; RectF Name to call the RectF object +; x x-coordinate of the upper left corner of the rectangle +; y y-coordinate of the upper left corner of the rectangle +; w Width of the rectangle +; h Height of the rectangle +; +; return No return value + +CreateRect(ByRef Rect, x, y, w, h) +{ + VarSetCapacity(Rect, 16) + NumPut(x, Rect, 0, "uint"), NumPut(y, Rect, 4, "uint"), NumPut(w, Rect, 8, "uint"), NumPut(h, Rect, 12, "uint") +} +;##################################################################################### + +; Function CreateSizeF +; Description Creates a SizeF object, containing an 2 values +; +; SizeF Name to call the SizeF object +; w w-value for the SizeF object +; h h-value for the SizeF object +; +; return No Return value + +CreateSizeF(ByRef SizeF, w, h) +{ + VarSetCapacity(SizeF, 8) + NumPut(w, SizeF, 0, "float"), NumPut(h, SizeF, 4, "float") +} +;##################################################################################### + +; Function CreatePointF +; Description Creates a SizeF object, containing an 2 values +; +; SizeF Name to call the SizeF object +; w w-value for the SizeF object +; h h-value for the SizeF object +; +; return No Return value + +CreatePointF(ByRef PointF, x, y) +{ + VarSetCapacity(PointF, 8) + NumPut(x, PointF, 0, "float"), NumPut(y, PointF, 4, "float") +} +;##################################################################################### + +; Function CreateDIBSection +; Description The CreateDIBSection function creates a DIB (Device Independent Bitmap) that applications can write to directly +; +; w width of the bitmap to create +; h height of the bitmap to create +; hdc a handle to the device context to use the palette from +; bpp bits per pixel (32 = ARGB) +; ppvBits A pointer to a variable that receives a pointer to the location of the DIB bit values +; +; return returns a DIB. A gdi bitmap +; +; notes ppvBits will receive the location of the pixels in the DIB + +CreateDIBSection(w, h, hdc:="", bpp:=32, ByRef ppvBits:=0) +{ + Ptr := A_PtrSize ? "UPtr" : "UInt" + + hdc2 := hdc ? hdc : GetDC() + 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(bpp, bi, 14, "ushort") + + hbm := DllCall("CreateDIBSection" + , Ptr, hdc2 + , Ptr, &bi + , "uint", 0 + , A_PtrSize ? "UPtr*" : "uint*", ppvBits + , Ptr, 0 + , "uint", 0, Ptr) + + if !hdc + ReleaseDC(hdc2) + return hbm +} + +;##################################################################################### + +; Function PrintWindow +; Description The PrintWindow function copies a visual window into the specified device context (DC), typically a printer DC +; +; hwnd A handle to the window that will be copied +; hdc A handle to the device context +; Flags Drawing options +; +; return If the function succeeds, it returns a nonzero value +; +; PW_CLIENTONLY = 1 + +PrintWindow(hwnd, hdc, Flags:=0) +{ + Ptr := A_PtrSize ? "UPtr" : "UInt" + + return DllCall("PrintWindow", Ptr, hwnd, Ptr, hdc, "uint", Flags) +} + +;##################################################################################### + +; Function DestroyIcon +; Description Destroys an icon and frees any memory the icon occupied +; +; hIcon Handle to the icon to be destroyed. The icon must not be in use +; +; return If the function succeeds, the return value is nonzero + +DestroyIcon(hIcon) +{ + return DllCall("DestroyIcon", A_PtrSize ? "UPtr" : "UInt", hIcon) +} + +;##################################################################################### + +; Function: GetIconDimensions +; Description: Retrieves a given icon/cursor's width and height +; +; hIcon Pointer to an icon or cursor +; Width ByRef variable. This variable is set to the icon's width +; Height ByRef variable. This variable is set to the icon's height +; +; return If the function succeeds, the return value is zero, otherwise: +; -1 = Could not retrieve the icon's info. Check A_LastError for extended information +; -2 = Could not delete the icon's bitmask bitmap +; -3 = Could not delete the icon's color bitmap + +GetIconDimensions(hIcon, ByRef Width, ByRef Height) { + Ptr := A_PtrSize ? "UPtr" : "UInt" + Width := Height := 0 + + VarSetCapacity(ICONINFO, size := 16 + 2 * A_PtrSize, 0) + + if !DllCall("user32\GetIconInfo", Ptr, hIcon, Ptr, &ICONINFO) + return -1 + + hbmMask := NumGet(&ICONINFO, 16, Ptr) + hbmColor := NumGet(&ICONINFO, 16 + A_PtrSize, Ptr) + VarSetCapacity(BITMAP, size, 0) + + if DllCall("gdi32\GetObject", Ptr, hbmColor, "Int", size, Ptr, &BITMAP) + { + Width := NumGet(&BITMAP, 4, "Int") + Height := NumGet(&BITMAP, 8, "Int") + } + + if !DllCall("gdi32\DeleteObject", Ptr, hbmMask) + return -2 + + if !DllCall("gdi32\DeleteObject", Ptr, hbmColor) + return -3 + + return 0 +} + +;##################################################################################### + +PaintDesktop(hdc) +{ + return DllCall("PaintDesktop", A_PtrSize ? "UPtr" : "UInt", hdc) +} + +;##################################################################################### + +CreateCompatibleBitmap(hdc, w, h) +{ + return DllCall("gdi32\CreateCompatibleBitmap", A_PtrSize ? "UPtr" : "UInt", hdc, "int", w, "int", h) +} + +;##################################################################################### + +; Function CreateCompatibleDC +; Description This function creates a memory device context (DC) compatible with the specified device +; +; hdc Handle to an existing device context +; +; return returns the handle to a device context or 0 on failure +; +; notes If this handle is 0 (by default), the function creates a memory device context compatible with the application's current screen + +CreateCompatibleDC(hdc:=0) +{ + return DllCall("CreateCompatibleDC", A_PtrSize ? "UPtr" : "UInt", hdc) +} + +;##################################################################################### + +; Function SelectObject +; Description The SelectObject function selects an object into the specified device context (DC). The new object replaces the previous object of the same type +; +; hdc Handle to a DC +; hgdiobj A handle to the object to be selected into the DC +; +; return If the selected object is not a region and the function succeeds, the return value is a handle to the object being replaced +; +; notes The specified object must have been created by using one of the following functions +; Bitmap - CreateBitmap, CreateBitmapIndirect, CreateCompatibleBitmap, CreateDIBitmap, CreateDIBSection (A single bitmap cannot be selected into more than one DC at the same time) +; Brush - CreateBrushIndirect, CreateDIBPatternBrush, CreateDIBPatternBrushPt, CreateHatchBrush, CreatePatternBrush, CreateSolidBrush +; Font - CreateFont, CreateFontIndirect +; Pen - CreatePen, CreatePenIndirect +; Region - CombineRgn, CreateEllipticRgn, CreateEllipticRgnIndirect, CreatePolygonRgn, CreateRectRgn, CreateRectRgnIndirect +; +; notes If the selected object is a region and the function succeeds, the return value is one of the following value +; +; SIMPLEREGION = 2 Region consists of a single rectangle +; COMPLEXREGION = 3 Region consists of more than one rectangle +; NULLREGION = 1 Region is empty + +SelectObject(hdc, hgdiobj) +{ + Ptr := A_PtrSize ? "UPtr" : "UInt" + + return DllCall("SelectObject", Ptr, hdc, Ptr, hgdiobj) +} + +;##################################################################################### + +; Function DeleteObject +; Description This function deletes a logical pen, brush, font, bitmap, region, or palette, freeing all system resources associated with the object +; After the object is deleted, the specified handle is no longer valid +; +; hObject Handle to a logical pen, brush, font, bitmap, region, or palette to delete +; +; return Nonzero indicates success. Zero indicates that the specified handle is not valid or that the handle is currently selected into a device context + +DeleteObject(hObject) +{ + return DllCall("DeleteObject", A_PtrSize ? "UPtr" : "UInt", hObject) +} + +;##################################################################################### + +; Function GetDC +; Description This function retrieves a handle to a display device context (DC) for the client area of the specified window. +; The display device context can be used in subsequent graphics display interface (GDI) functions to draw in the client area of the window. +; +; hwnd Handle to the window whose device context is to be retrieved. If this value is NULL, GetDC retrieves the device context for the entire screen +; +; return The handle the device context for the specified window's client area indicates success. NULL indicates failure + +GetDC(hwnd:=0) +{ + return DllCall("GetDC", A_PtrSize ? "UPtr" : "UInt", hwnd) +} + +;##################################################################################### + +; DCX_CACHE = 0x2 +; DCX_CLIPCHILDREN = 0x8 +; DCX_CLIPSIBLINGS = 0x10 +; DCX_EXCLUDERGN = 0x40 +; DCX_EXCLUDEUPDATE = 0x100 +; DCX_INTERSECTRGN = 0x80 +; DCX_INTERSECTUPDATE = 0x200 +; DCX_LOCKWINDOWUPDATE = 0x400 +; DCX_NORECOMPUTE = 0x100000 +; DCX_NORESETATTRS = 0x4 +; DCX_PARENTCLIP = 0x20 +; DCX_VALIDATE = 0x200000 +; DCX_WINDOW = 0x1 + +GetDCEx(hwnd, flags:=0, hrgnClip:=0) +{ + Ptr := A_PtrSize ? "UPtr" : "UInt" + + return DllCall("GetDCEx", Ptr, hwnd, Ptr, hrgnClip, "int", flags) +} + +;##################################################################################### + +; Function ReleaseDC +; Description This function releases a device context (DC), freeing it for use by other applications. The effect of ReleaseDC depends on the type of device context +; +; hdc Handle to the device context to be released +; hwnd Handle to the window whose device context is to be released +; +; return 1 = released +; 0 = not released +; +; notes The application must call the ReleaseDC function for each call to the GetWindowDC function and for each call to the GetDC function that retrieves a common device context +; An application cannot use the ReleaseDC function to release a device context that was created by calling the CreateDC function; instead, it must use the DeleteDC function. + +ReleaseDC(hdc, hwnd:=0) +{ + Ptr := A_PtrSize ? "UPtr" : "UInt" + + return DllCall("ReleaseDC", Ptr, hwnd, Ptr, hdc) +} + +;##################################################################################### + +; Function DeleteDC +; Description The DeleteDC function deletes the specified device context (DC) +; +; hdc A handle to the device context +; +; return If the function succeeds, the return value is nonzero +; +; notes An application must not delete a DC whose handle was obtained by calling the GetDC function. Instead, it must call the ReleaseDC function to free the DC + +DeleteDC(hdc) +{ + return DllCall("DeleteDC", A_PtrSize ? "UPtr" : "UInt", hdc) +} +;##################################################################################### + +; Function Gdip_LibraryVersion +; Description Get the current library version +; +; return the library version +; +; notes This is useful for non compiled programs to ensure that a person doesn't run an old version when testing your scripts + +Gdip_LibraryVersion() +{ + return 1.45 +} + +;##################################################################################### + +; Function Gdip_LibrarySubVersion +; Description Get the current library sub version +; +; return the library sub version +; +; notes This is the sub-version currently maintained by Rseding91 +; Updated by guest3456 preliminary AHK v2 support +Gdip_LibrarySubVersion() +{ + return 1.54 +} + +;##################################################################################### + +; Function: Gdip_BitmapFromBRA +; Description: Gets a pointer to a gdi+ bitmap from a BRA file +; +; BRAFromMemIn The variable for a BRA file read to memory +; File The name of the file, or its number that you would like (This depends on alternate parameter) +; Alternate Changes whether the File parameter is the file name or its number +; +; return If the function succeeds, the return value is a pointer to a gdi+ bitmap +; -1 = The BRA variable is empty +; -2 = The BRA has an incorrect header +; -3 = The BRA has information missing +; -4 = Could not find file inside the BRA + +Gdip_BitmapFromBRA(ByRef BRAFromMemIn, File, Alternate := 0) { + pBitmap := "" + + If !(BRAFromMemIn) + Return -1 + Headers := StrSplit(StrGet(&BRAFromMemIn, 256, "CP0"), "`n") + Header := StrSplit(Headers.1, "|") + If (Header.Length() != 4) || (Header.2 != "BRA!") + Return -2 + _Info := StrSplit(Headers.2, "|") + If (_Info.Length() != 3) + Return -3 + OffsetTOC := StrPut(Headers.1, "CP0") + StrPut(Headers.2, "CP0") ; + 2 + OffsetData := _Info.2 + SearchIndex := Alternate ? 1 : 2 + TOC := StrGet(&BRAFromMemIn + OffsetTOC, OffsetData - OffsetTOC - 1, "CP0") + RX1 := A_AhkVersion < "2" ? "mi`nO)^" : "mi`n)^" + Offset := Size := 0 + If RegExMatch(TOC, RX1 . (Alternate ? File "\|.+?" : "\d+\|" . File) . "\|(\d+)\|(\d+)$", FileInfo) { + Offset := OffsetData + FileInfo.1 + Size := FileInfo.2 + } + If (Size = 0) + Return -4 + hData := DllCall("GlobalAlloc", "UInt", 2, "UInt", Size, "UPtr") + pData := DllCall("GlobalLock", "Ptr", hData, "UPtr") + DllCall("RtlMoveMemory", "Ptr", pData, "Ptr", &BRAFromMemIn + Offset, "Ptr", Size) + DllCall("GlobalUnlock", "Ptr", hData) + DllCall("Ole32.dll\CreateStreamOnHGlobal", "Ptr", hData, "Int", 1, "PtrP", pStream) + DllCall("Gdiplus.dll\GdipCreateBitmapFromStream", "Ptr", pStream, "PtrP", pBitmap) + ObjRelease(pStream) + Return pBitmap +} + +;##################################################################################### + +; Function: Gdip_BitmapFromBase64 +; Description: Creates a bitmap from a Base64 encoded string +; +; Base64 ByRef variable. Base64 encoded string. Immutable, ByRef to avoid performance overhead of passing long strings. +; +; return If the function succeeds, the return value is a pointer to a bitmap, otherwise: +; -1 = Could not calculate the length of the required buffer +; -2 = Could not decode the Base64 encoded string +; -3 = Could not create a memory stream + +Gdip_BitmapFromBase64(ByRef Base64) +{ + Ptr := A_PtrSize ? "UPtr" : "UInt" + + ; calculate the length of the buffer needed + if !(DllCall("crypt32\CryptStringToBinary", Ptr, &Base64, "UInt", 0, "UInt", 0x01, Ptr, 0, "UIntP", DecLen, Ptr, 0, Ptr, 0)) + return -1 + + VarSetCapacity(Dec, DecLen, 0) + + ; decode the Base64 encoded string + if !(DllCall("crypt32\CryptStringToBinary", Ptr, &Base64, "UInt", 0, "UInt", 0x01, Ptr, &Dec, "UIntP", DecLen, Ptr, 0, Ptr, 0)) + return -2 + + ; create a memory stream + if !(pStream := DllCall("shlwapi\SHCreateMemStream", Ptr, &Dec, "UInt", DecLen, "UPtr")) + return -3 + + DllCall("gdiplus\GdipCreateBitmapFromStreamICM", Ptr, pStream, "PtrP", pBitmap) + ObjRelease(pStream) + + return pBitmap +} + +;##################################################################################### + +; Function Gdip_DrawRectangle +; Description This function uses a pen to draw the outline of a rectangle into the Graphics of a bitmap +; +; pGraphics Pointer to the Graphics of a bitmap +; pPen Pointer to a pen +; x x-coordinate of the top left of the rectangle +; y y-coordinate of the top left of the rectangle +; w width of the rectanlge +; h height of the rectangle +; +; return status enumeration. 0 = success +; +; notes as all coordinates are taken from the top left of each pixel, then the entire width/height should be specified as subtracting the pen width + +Gdip_DrawRectangle(pGraphics, pPen, x, y, w, h) +{ + Ptr := A_PtrSize ? "UPtr" : "UInt" + + return DllCall("gdiplus\GdipDrawRectangle", Ptr, pGraphics, Ptr, pPen, "float", x, "float", y, "float", w, "float", h) +} + +;##################################################################################### + +; Function Gdip_DrawRoundedRectangle +; Description This function uses a pen to draw the outline of a rounded rectangle into the Graphics of a bitmap +; +; pGraphics Pointer to the Graphics of a bitmap +; pPen Pointer to a pen +; x x-coordinate of the top left of the rounded rectangle +; y y-coordinate of the top left of the rounded rectangle +; w width of the rectanlge +; h height of the rectangle +; r radius of the rounded corners +; +; return status enumeration. 0 = success +; +; notes as all coordinates are taken from the top left of each pixel, then the entire width/height should be specified as subtracting the pen width + +Gdip_DrawRoundedRectangle(pGraphics, pPen, x, y, w, h, r) +{ + Gdip_SetClipRect(pGraphics, x-r, y-r, 2*r, 2*r, 4) + Gdip_SetClipRect(pGraphics, x+w-r, y-r, 2*r, 2*r, 4) + Gdip_SetClipRect(pGraphics, x-r, y+h-r, 2*r, 2*r, 4) + Gdip_SetClipRect(pGraphics, x+w-r, y+h-r, 2*r, 2*r, 4) + _E := Gdip_DrawRectangle(pGraphics, pPen, x, y, w, h) + Gdip_ResetClip(pGraphics) + Gdip_SetClipRect(pGraphics, x-(2*r), y+r, w+(4*r), h-(2*r), 4) + Gdip_SetClipRect(pGraphics, x+r, y-(2*r), w-(2*r), h+(4*r), 4) + Gdip_DrawEllipse(pGraphics, pPen, x, y, 2*r, 2*r) + Gdip_DrawEllipse(pGraphics, pPen, x+w-(2*r), y, 2*r, 2*r) + Gdip_DrawEllipse(pGraphics, pPen, x, y+h-(2*r), 2*r, 2*r) + Gdip_DrawEllipse(pGraphics, pPen, x+w-(2*r), y+h-(2*r), 2*r, 2*r) + Gdip_ResetClip(pGraphics) + return _E +} + +;##################################################################################### + +; Function Gdip_DrawEllipse +; Description This function uses a pen to draw the outline of an ellipse into the Graphics of a bitmap +; +; pGraphics Pointer to the Graphics of a bitmap +; pPen Pointer to a pen +; x x-coordinate of the top left of the rectangle the ellipse will be drawn into +; y y-coordinate of the top left of the rectangle the ellipse will be drawn into +; w width of the ellipse +; h height of the ellipse +; +; return status enumeration. 0 = success +; +; notes as all coordinates are taken from the top left of each pixel, then the entire width/height should be specified as subtracting the pen width + +Gdip_DrawEllipse(pGraphics, pPen, x, y, w, h) +{ + Ptr := A_PtrSize ? "UPtr" : "UInt" + + return DllCall("gdiplus\GdipDrawEllipse", Ptr, pGraphics, Ptr, pPen, "float", x, "float", y, "float", w, "float", h) +} + +;##################################################################################### + +; Function Gdip_DrawBezier +; Description This function uses a pen to draw the outline of a bezier (a weighted curve) into the Graphics of a bitmap +; +; pGraphics Pointer to the Graphics of a bitmap +; pPen Pointer to a pen +; x1 x-coordinate of the start of the bezier +; y1 y-coordinate of the start of the bezier +; x2 x-coordinate of the first arc of the bezier +; y2 y-coordinate of the first arc of the bezier +; x3 x-coordinate of the second arc of the bezier +; y3 y-coordinate of the second arc of the bezier +; x4 x-coordinate of the end of the bezier +; y4 y-coordinate of the end of the bezier +; +; return status enumeration. 0 = success +; +; notes as all coordinates are taken from the top left of each pixel, then the entire width/height should be specified as subtracting the pen width + +Gdip_DrawBezier(pGraphics, pPen, x1, y1, x2, y2, x3, y3, x4, y4) +{ + Ptr := A_PtrSize ? "UPtr" : "UInt" + + return DllCall("gdiplus\GdipDrawBezier" + , Ptr, pgraphics + , Ptr, pPen + , "float", x1 + , "float", y1 + , "float", x2 + , "float", y2 + , "float", x3 + , "float", y3 + , "float", x4 + , "float", y4) +} + +;##################################################################################### + +; Function Gdip_DrawArc +; Description This function uses a pen to draw the outline of an arc into the Graphics of a bitmap +; +; pGraphics Pointer to the Graphics of a bitmap +; pPen Pointer to a pen +; x x-coordinate of the start of the arc +; y y-coordinate of the start of the arc +; w width of the arc +; h height of the arc +; StartAngle specifies the angle between the x-axis and the starting point of the arc +; SweepAngle specifies the angle between the starting and ending points of the arc +; +; return status enumeration. 0 = success +; +; notes as all coordinates are taken from the top left of each pixel, then the entire width/height should be specified as subtracting the pen width + +Gdip_DrawArc(pGraphics, pPen, x, y, w, h, StartAngle, SweepAngle) +{ + Ptr := A_PtrSize ? "UPtr" : "UInt" + + return DllCall("gdiplus\GdipDrawArc" + , Ptr, pGraphics + , Ptr, pPen + , "float", x + , "float", y + , "float", w + , "float", h + , "float", StartAngle + , "float", SweepAngle) +} + +;##################################################################################### + +; Function Gdip_DrawPie +; Description This function uses a pen to draw the outline of a pie into the Graphics of a bitmap +; +; pGraphics Pointer to the Graphics of a bitmap +; pPen Pointer to a pen +; x x-coordinate of the start of the pie +; y y-coordinate of the start of the pie +; w width of the pie +; h height of the pie +; StartAngle specifies the angle between the x-axis and the starting point of the pie +; SweepAngle specifies the angle between the starting and ending points of the pie +; +; return status enumeration. 0 = success +; +; notes as all coordinates are taken from the top left of each pixel, then the entire width/height should be specified as subtracting the pen width + +Gdip_DrawPie(pGraphics, pPen, x, y, w, h, StartAngle, SweepAngle) +{ + Ptr := A_PtrSize ? "UPtr" : "UInt" + + return DllCall("gdiplus\GdipDrawPie", Ptr, pGraphics, Ptr, pPen, "float", x, "float", y, "float", w, "float", h, "float", StartAngle, "float", SweepAngle) +} + +;##################################################################################### + +; Function Gdip_DrawLine +; Description This function uses a pen to draw a line into the Graphics of a bitmap +; +; pGraphics Pointer to the Graphics of a bitmap +; pPen Pointer to a pen +; x1 x-coordinate of the start of the line +; y1 y-coordinate of the start of the line +; x2 x-coordinate of the end of the line +; y2 y-coordinate of the end of the line +; +; return status enumeration. 0 = success + +Gdip_DrawLine(pGraphics, pPen, x1, y1, x2, y2) +{ + Ptr := A_PtrSize ? "UPtr" : "UInt" + + return DllCall("gdiplus\GdipDrawLine" + , Ptr, pGraphics + , Ptr, pPen + , "float", x1 + , "float", y1 + , "float", x2 + , "float", y2) +} + +;##################################################################################### + +; Function Gdip_DrawLines +; Description This function uses a pen to draw a series of joined lines into the Graphics of a bitmap +; +; pGraphics Pointer to the Graphics of a bitmap +; pPen Pointer to a pen +; Points the coordinates of all the points passed as x1,y1|x2,y2|x3,y3..... +; +; return status enumeration. 0 = success + +Gdip_DrawLines(pGraphics, pPen, Points) +{ + Ptr := A_PtrSize ? "UPtr" : "UInt" + Points := StrSplit(Points, "|") + VarSetCapacity(PointF, 8*Points.Length()) + for eachPoint, Point in Points + { + Coord := StrSplit(Point, ",") + NumPut(Coord[1], PointF, 8*(A_Index-1), "float"), NumPut(Coord[2], PointF, (8*(A_Index-1))+4, "float") + } + return DllCall("gdiplus\GdipDrawLines", Ptr, pGraphics, Ptr, pPen, Ptr, &PointF, "int", Points.Length()) +} + +;##################################################################################### + +; Function Gdip_FillRectangle +; Description This function uses a brush to fill a rectangle in the Graphics of a bitmap +; +; pGraphics Pointer to the Graphics of a bitmap +; pBrush Pointer to a brush +; x x-coordinate of the top left of the rectangle +; y y-coordinate of the top left of the rectangle +; w width of the rectanlge +; h height of the rectangle +; +; return status enumeration. 0 = success + +Gdip_FillRectangle(pGraphics, pBrush, x, y, w, h) +{ + Ptr := A_PtrSize ? "UPtr" : "UInt" + + return DllCall("gdiplus\GdipFillRectangle" + , Ptr, pGraphics + , Ptr, pBrush + , "float", x + , "float", y + , "float", w + , "float", h) +} + +;##################################################################################### + +; Function Gdip_FillRoundedRectangle +; Description This function uses a brush to fill a rounded rectangle in the Graphics of a bitmap +; +; pGraphics Pointer to the Graphics of a bitmap +; pBrush Pointer to a brush +; x x-coordinate of the top left of the rounded rectangle +; y y-coordinate of the top left of the rounded rectangle +; w width of the rectanlge +; h height of the rectangle +; r radius of the rounded corners +; +; return status enumeration. 0 = success + +Gdip_FillRoundedRectangle(pGraphics, pBrush, x, y, w, h, r) +{ + Region := Gdip_GetClipRegion(pGraphics) + Gdip_SetClipRect(pGraphics, x-r, y-r, 2*r, 2*r, 4) + Gdip_SetClipRect(pGraphics, x+w-r, y-r, 2*r, 2*r, 4) + Gdip_SetClipRect(pGraphics, x-r, y+h-r, 2*r, 2*r, 4) + Gdip_SetClipRect(pGraphics, x+w-r, y+h-r, 2*r, 2*r, 4) + _E := Gdip_FillRectangle(pGraphics, pBrush, x, y, w, h) + Gdip_SetClipRegion(pGraphics, Region, 0) + Gdip_SetClipRect(pGraphics, x-(2*r), y+r, w+(4*r), h-(2*r), 4) + Gdip_SetClipRect(pGraphics, x+r, y-(2*r), w-(2*r), h+(4*r), 4) + Gdip_FillEllipse(pGraphics, pBrush, x, y, 2*r, 2*r) + Gdip_FillEllipse(pGraphics, pBrush, x+w-(2*r), y, 2*r, 2*r) + Gdip_FillEllipse(pGraphics, pBrush, x, y+h-(2*r), 2*r, 2*r) + Gdip_FillEllipse(pGraphics, pBrush, x+w-(2*r), y+h-(2*r), 2*r, 2*r) + Gdip_SetClipRegion(pGraphics, Region, 0) + Gdip_DeleteRegion(Region) + return _E +} + +;##################################################################################### + +; Function Gdip_FillPolygon +; Description This function uses a brush to fill a polygon in the Graphics of a bitmap +; +; pGraphics Pointer to the Graphics of a bitmap +; pBrush Pointer to a brush +; Points the coordinates of all the points passed as x1,y1|x2,y2|x3,y3..... +; +; return status enumeration. 0 = success +; +; notes Alternate will fill the polygon as a whole, wheras winding will fill each new "segment" +; Alternate = 0 +; Winding = 1 + +Gdip_FillPolygon(pGraphics, pBrush, Points, FillMode:=0) +{ + Ptr := A_PtrSize ? "UPtr" : "UInt" + + Points := StrSplit(Points, "|") + VarSetCapacity(PointF, 8*Points.Length()) + For eachPoint, Point in Points + { + Coord := StrSplit(Point, ",") + NumPut(Coord[1], PointF, 8*(A_Index-1), "float"), NumPut(Coord[2], PointF, (8*(A_Index-1))+4, "float") + } + return DllCall("gdiplus\GdipFillPolygon", Ptr, pGraphics, Ptr, pBrush, Ptr, &PointF, "int", Points.Length(), "int", FillMode) +} + +;##################################################################################### + +; Function Gdip_FillPie +; Description This function uses a brush to fill a pie in the Graphics of a bitmap +; +; pGraphics Pointer to the Graphics of a bitmap +; pBrush Pointer to a brush +; x x-coordinate of the top left of the pie +; y y-coordinate of the top left of the pie +; w width of the pie +; h height of the pie +; StartAngle specifies the angle between the x-axis and the starting point of the pie +; SweepAngle specifies the angle between the starting and ending points of the pie +; +; return status enumeration. 0 = success + +Gdip_FillPie(pGraphics, pBrush, x, y, w, h, StartAngle, SweepAngle) +{ + Ptr := A_PtrSize ? "UPtr" : "UInt" + + return DllCall("gdiplus\GdipFillPie" + , Ptr, pGraphics + , Ptr, pBrush + , "float", x + , "float", y + , "float", w + , "float", h + , "float", StartAngle + , "float", SweepAngle) +} + +;##################################################################################### + +; Function Gdip_FillEllipse +; Description This function uses a brush to fill an ellipse in the Graphics of a bitmap +; +; pGraphics Pointer to the Graphics of a bitmap +; pBrush Pointer to a brush +; x x-coordinate of the top left of the ellipse +; y y-coordinate of the top left of the ellipse +; w width of the ellipse +; h height of the ellipse +; +; return status enumeration. 0 = success + +Gdip_FillEllipse(pGraphics, pBrush, x, y, w, h) +{ + Ptr := A_PtrSize ? "UPtr" : "UInt" + + return DllCall("gdiplus\GdipFillEllipse", Ptr, pGraphics, Ptr, pBrush, "float", x, "float", y, "float", w, "float", h) +} + +;##################################################################################### + +; Function Gdip_FillRegion +; Description This function uses a brush to fill a region in the Graphics of a bitmap +; +; pGraphics Pointer to the Graphics of a bitmap +; pBrush Pointer to a brush +; Region Pointer to a Region +; +; return status enumeration. 0 = success +; +; notes You can create a region Gdip_CreateRegion() and then add to this + +Gdip_FillRegion(pGraphics, pBrush, Region) +{ + Ptr := A_PtrSize ? "UPtr" : "UInt" + + return DllCall("gdiplus\GdipFillRegion", Ptr, pGraphics, Ptr, pBrush, Ptr, Region) +} + +;##################################################################################### + +; Function Gdip_FillPath +; Description This function uses a brush to fill a path in the Graphics of a bitmap +; +; pGraphics Pointer to the Graphics of a bitmap +; pBrush Pointer to a brush +; Region Pointer to a Path +; +; return status enumeration. 0 = success + +Gdip_FillPath(pGraphics, pBrush, pPath) +{ + Ptr := A_PtrSize ? "UPtr" : "UInt" + + return DllCall("gdiplus\GdipFillPath", Ptr, pGraphics, Ptr, pBrush, Ptr, pPath) +} + +;##################################################################################### + +; Function Gdip_DrawImagePointsRect +; Description This function draws a bitmap into the Graphics of another bitmap and skews it +; +; pGraphics Pointer to the Graphics of a bitmap +; pBitmap Pointer to a bitmap to be drawn +; Points Points passed as x1,y1|x2,y2|x3,y3 (3 points: top left, top right, bottom left) describing the drawing of the bitmap +; sx x-coordinate of source upper-left corner +; sy y-coordinate of source upper-left corner +; sw width of source rectangle +; sh height of source rectangle +; Matrix a matrix used to alter image attributes when drawing +; +; return status enumeration. 0 = success +; +; notes if sx,sy,sw,sh are missed then the entire source bitmap will be used +; Matrix can be omitted to just draw with no alteration to ARGB +; Matrix may be passed as a digit from 0 - 1 to change just transparency +; Matrix can be passed as a matrix with any delimiter + +Gdip_DrawImagePointsRect(pGraphics, pBitmap, Points, sx:="", sy:="", sw:="", sh:="", Matrix:=1) +{ + Ptr := A_PtrSize ? "UPtr" : "UInt" + + Points := StrSplit(Points, "|") + VarSetCapacity(PointF, 8*Points.Length()) + For eachPoint, Point in Points + { + Coord := StrSplit(Point, ",") + NumPut(Coord[1], PointF, 8*(A_Index-1), "float"), NumPut(Coord[2], PointF, (8*(A_Index-1))+4, "float") + } + + if !IsNumber(Matrix) + ImageAttr := Gdip_SetImageAttributesColorMatrix(Matrix) + else if (Matrix != 1) + ImageAttr := Gdip_SetImageAttributesColorMatrix("1|0|0|0|0|0|1|0|0|0|0|0|1|0|0|0|0|0|" Matrix "|0|0|0|0|0|1") + + if (sx = "" && sy = "" && sw = "" && sh = "") + { + sx := 0, sy := 0 + sw := Gdip_GetImageWidth(pBitmap) + sh := Gdip_GetImageHeight(pBitmap) + } + + _E := DllCall("gdiplus\GdipDrawImagePointsRect" + , Ptr, pGraphics + , Ptr, pBitmap + , Ptr, &PointF + , "int", Points.Length() + , "float", sx + , "float", sy + , "float", sw + , "float", sh + , "int", 2 + , Ptr, ImageAttr + , Ptr, 0 + , Ptr, 0) + if ImageAttr + Gdip_DisposeImageAttributes(ImageAttr) + return _E +} + +;##################################################################################### + +; Function Gdip_DrawImage +; Description This function draws a bitmap into the Graphics of another bitmap +; +; pGraphics Pointer to the Graphics of a bitmap +; pBitmap Pointer to a bitmap to be drawn +; dx x-coord of destination upper-left corner +; dy y-coord of destination upper-left corner +; dw width of destination image +; dh height of destination image +; sx x-coordinate of source upper-left corner +; sy y-coordinate of source upper-left corner +; sw width of source image +; sh height of source image +; Matrix a matrix used to alter image attributes when drawing +; +; return status enumeration. 0 = success +; +; notes if sx,sy,sw,sh are missed then the entire source bitmap will be used +; Gdip_DrawImage performs faster +; Matrix can be omitted to just draw with no alteration to ARGB +; Matrix may be passed as a digit from 0 - 1 to change just transparency +; Matrix can be passed as a matrix with any delimiter. For example: +; MatrixBright= +; ( +; 1.5 |0 |0 |0 |0 +; 0 |1.5 |0 |0 |0 +; 0 |0 |1.5 |0 |0 +; 0 |0 |0 |1 |0 +; 0.05 |0.05 |0.05 |0 |1 +; ) +; +; notes MatrixBright = 1.5|0|0|0|0|0|1.5|0|0|0|0|0|1.5|0|0|0|0|0|1|0|0.05|0.05|0.05|0|1 +; MatrixGreyScale = 0.299|0.299|0.299|0|0|0.587|0.587|0.587|0|0|0.114|0.114|0.114|0|0|0|0|0|1|0|0|0|0|0|1 +; MatrixNegative = -1|0|0|0|0|0|-1|0|0|0|0|0|-1|0|0|0|0|0|1|0|1|1|1|0|1 + +Gdip_DrawImage(pGraphics, pBitmap, dx:="", dy:="", dw:="", dh:="", sx:="", sy:="", sw:="", sh:="", Matrix:=1) +{ + Ptr := A_PtrSize ? "UPtr" : "UInt" + + if !IsNumber(Matrix) + ImageAttr := Gdip_SetImageAttributesColorMatrix(Matrix) + else if (Matrix != 1) + ImageAttr := Gdip_SetImageAttributesColorMatrix("1|0|0|0|0|0|1|0|0|0|0|0|1|0|0|0|0|0|" Matrix "|0|0|0|0|0|1") + + if (sx = "" && sy = "" && sw = "" && sh = "") + { + if (dx = "" && dy = "" && dw = "" && dh = "") + { + sx := dx := 0, sy := dy := 0 + sw := dw := Gdip_GetImageWidth(pBitmap) + sh := dh := Gdip_GetImageHeight(pBitmap) + } + else + { + sx := sy := 0 + sw := Gdip_GetImageWidth(pBitmap) + sh := Gdip_GetImageHeight(pBitmap) + } + } + + _E := DllCall("gdiplus\GdipDrawImageRectRect" + , Ptr, pGraphics + , Ptr, pBitmap + , "float", dx + , "float", dy + , "float", dw + , "float", dh + , "float", sx + , "float", sy + , "float", sw + , "float", sh + , "int", 2 + , Ptr, ImageAttr + , Ptr, 0 + , Ptr, 0) + if ImageAttr + Gdip_DisposeImageAttributes(ImageAttr) + return _E +} + +;##################################################################################### + +; Function Gdip_SetImageAttributesColorMatrix +; Description This function creates an image matrix ready for drawing +; +; Matrix a matrix used to alter image attributes when drawing +; passed with any delimeter +; +; return returns an image matrix on sucess or 0 if it fails +; +; notes MatrixBright = 1.5|0|0|0|0|0|1.5|0|0|0|0|0|1.5|0|0|0|0|0|1|0|0.05|0.05|0.05|0|1 +; MatrixGreyScale = 0.299|0.299|0.299|0|0|0.587|0.587|0.587|0|0|0.114|0.114|0.114|0|0|0|0|0|1|0|0|0|0|0|1 +; MatrixNegative = -1|0|0|0|0|0|-1|0|0|0|0|0|-1|0|0|0|0|0|1|0|1|1|1|0|1 + +Gdip_SetImageAttributesColorMatrix(Matrix) +{ + Ptr := A_PtrSize ? "UPtr" : "UInt" + + VarSetCapacity(ColourMatrix, 100, 0) + Matrix := RegExReplace(RegExReplace(Matrix, "^[^\d-\.]+([\d\.])", "$1", , 1), "[^\d-\.]+", "|") + Matrix := StrSplit(Matrix, "|") + Loop 25 + { + M := (Matrix[A_Index] != "") ? Matrix[A_Index] : Mod(A_Index-1, 6) ? 0 : 1 + NumPut(M, ColourMatrix, (A_Index-1)*4, "float") + } + DllCall("gdiplus\GdipCreateImageAttributes", A_PtrSize ? "UPtr*" : "uint*", ImageAttr) + DllCall("gdiplus\GdipSetImageAttributesColorMatrix", Ptr, ImageAttr, "int", 1, "int", 1, Ptr, &ColourMatrix, Ptr, 0, "int", 0) + return ImageAttr +} + +;##################################################################################### + +; Function Gdip_GraphicsFromImage +; Description This function gets the graphics for a bitmap used for drawing functions +; +; pBitmap Pointer to a bitmap to get the pointer to its graphics +; +; return returns a pointer to the graphics of a bitmap +; +; notes a bitmap can be drawn into the graphics of another bitmap + +Gdip_GraphicsFromImage(pBitmap) +{ + DllCall("gdiplus\GdipGetImageGraphicsContext", A_PtrSize ? "UPtr" : "UInt", pBitmap, A_PtrSize ? "UPtr*" : "UInt*", pGraphics) + return pGraphics +} + +;##################################################################################### + +; Function Gdip_GraphicsFromHDC +; Description This function gets the graphics from the handle to a device context +; +; hdc This is the handle to the device context +; +; return returns a pointer to the graphics of a bitmap +; +; notes You can draw a bitmap into the graphics of another bitmap + +Gdip_GraphicsFromHDC(hdc) +{ + pGraphics := "" + + DllCall("gdiplus\GdipCreateFromHDC", A_PtrSize ? "UPtr" : "UInt", hdc, A_PtrSize ? "UPtr*" : "UInt*", pGraphics) + return pGraphics +} + +;##################################################################################### + +; Function Gdip_GetDC +; Description This function gets the device context of the passed Graphics +; +; hdc This is the handle to the device context +; +; return returns the device context for the graphics of a bitmap + +Gdip_GetDC(pGraphics) +{ + DllCall("gdiplus\GdipGetDC", A_PtrSize ? "UPtr" : "UInt", pGraphics, A_PtrSize ? "UPtr*" : "UInt*", hdc) + return hdc +} + +;##################################################################################### + +; Function Gdip_ReleaseDC +; Description This function releases a device context from use for further use +; +; pGraphics Pointer to the graphics of a bitmap +; hdc This is the handle to the device context +; +; return status enumeration. 0 = success + +Gdip_ReleaseDC(pGraphics, hdc) +{ + Ptr := A_PtrSize ? "UPtr" : "UInt" + + return DllCall("gdiplus\GdipReleaseDC", Ptr, pGraphics, Ptr, hdc) +} + +;##################################################################################### + +; Function Gdip_GraphicsClear +; Description Clears the graphics of a bitmap ready for further drawing +; +; pGraphics Pointer to the graphics of a bitmap +; ARGB The colour to clear the graphics to +; +; return status enumeration. 0 = success +; +; notes By default this will make the background invisible +; Using clipping regions you can clear a particular area on the graphics rather than clearing the entire graphics + +Gdip_GraphicsClear(pGraphics, ARGB:=0x00ffffff) +{ + return DllCall("gdiplus\GdipGraphicsClear", A_PtrSize ? "UPtr" : "UInt", pGraphics, "int", ARGB) +} + +;##################################################################################### + +; Function Gdip_BlurBitmap +; Description Gives a pointer to a blurred bitmap from a pointer to a bitmap +; +; pBitmap Pointer to a bitmap to be blurred +; Blur The Amount to blur a bitmap by from 1 (least blur) to 100 (most blur) +; +; return If the function succeeds, the return value is a pointer to the new blurred bitmap +; -1 = The blur parameter is outside the range 1-100 +; +; notes This function will not dispose of the original bitmap + +Gdip_BlurBitmap(pBitmap, Blur) +{ + if (Blur > 100) || (Blur < 1) + return -1 + + sWidth := Gdip_GetImageWidth(pBitmap), sHeight := Gdip_GetImageHeight(pBitmap) + dWidth := sWidth//Blur, dHeight := sHeight//Blur + + pBitmap1 := Gdip_CreateBitmap(dWidth, dHeight) + G1 := Gdip_GraphicsFromImage(pBitmap1) + Gdip_SetInterpolationMode(G1, 7) + Gdip_DrawImage(G1, pBitmap, 0, 0, dWidth, dHeight, 0, 0, sWidth, sHeight) + + Gdip_DeleteGraphics(G1) + + pBitmap2 := Gdip_CreateBitmap(sWidth, sHeight) + G2 := Gdip_GraphicsFromImage(pBitmap2) + Gdip_SetInterpolationMode(G2, 7) + Gdip_DrawImage(G2, pBitmap1, 0, 0, sWidth, sHeight, 0, 0, dWidth, dHeight) + + Gdip_DeleteGraphics(G2) + Gdip_DisposeImage(pBitmap1) + return pBitmap2 +} + +;##################################################################################### + +; Function: Gdip_SaveBitmapToFile +; Description: Saves a bitmap to a file in any supported format onto disk +; +; pBitmap Pointer to a bitmap +; sOutput The name of the file that the bitmap will be saved to. Supported extensions are: .BMP,.DIB,.RLE,.JPG,.JPEG,.JPE,.JFIF,.GIF,.TIF,.TIFF,.PNG +; Quality If saving as jpg (.JPG,.JPEG,.JPE,.JFIF) then quality can be 1-100 with default at maximum quality +; +; return If the function succeeds, the return value is zero, otherwise: +; -1 = Extension supplied is not a supported file format +; -2 = Could not get a list of encoders on system +; -3 = Could not find matching encoder for specified file format +; -4 = Could not get WideChar name of output file +; -5 = Could not save file to disk +; +; notes This function will use the extension supplied from the sOutput parameter to determine the output format + +Gdip_SaveBitmapToFile(pBitmap, sOutput, Quality:=75) +{ + Ptr := A_PtrSize ? "UPtr" : "UInt" + nCount := 0 + nSize := 0 + _p := 0 + + SplitPath sOutput,,, Extension + if !RegExMatch(Extension, "^(?i:BMP|DIB|RLE|JPG|JPEG|JPE|JFIF|GIF|TIF|TIFF|PNG)$") + return -1 + Extension := "." Extension + + 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" + + N := (A_AhkVersion < 2) ? nCount : "nCount" + Loop %N% + { + 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 { + N := (A_AhkVersion < 2) ? nCount : "nCount" + Loop %N% + { + 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 (Quality != 75) + { + Quality := (Quality < 0) ? 0 : (Quality > 100) ? 100 : Quality + if RegExMatch(Extension, "^\.(?i: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) + nCount := NumGet(EncoderParameters, "UInt") + N := (A_AhkVersion < 2) ? nCount : "nCount" + Loop %N% + { + 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 + } + } + } + } + + 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 +} + +;##################################################################################### + +; Function Gdip_GetPixel +; Description Gets the ARGB of a pixel in a bitmap +; +; pBitmap Pointer to a bitmap +; x x-coordinate of the pixel +; y y-coordinate of the pixel +; +; return Returns the ARGB value of the pixel + +Gdip_GetPixel(pBitmap, x, y) +{ + ARGB := 0 + + DllCall("gdiplus\GdipBitmapGetPixel", A_PtrSize ? "UPtr" : "UInt", pBitmap, "int", x, "int", y, "uint*", ARGB) + return ARGB +} + +;##################################################################################### + +; Function Gdip_SetPixel +; Description Sets the ARGB of a pixel in a bitmap +; +; pBitmap Pointer to a bitmap +; x x-coordinate of the pixel +; y y-coordinate of the pixel +; +; return status enumeration. 0 = success + +Gdip_SetPixel(pBitmap, x, y, ARGB) +{ + return DllCall("gdiplus\GdipBitmapSetPixel", A_PtrSize ? "UPtr" : "UInt", pBitmap, "int", x, "int", y, "int", ARGB) +} + +;##################################################################################### + +; Function Gdip_GetImageWidth +; Description Gives the width of a bitmap +; +; pBitmap Pointer to a bitmap +; +; return Returns the width in pixels of the supplied bitmap + +Gdip_GetImageWidth(pBitmap) +{ + DllCall("gdiplus\GdipGetImageWidth", A_PtrSize ? "UPtr" : "UInt", pBitmap, "uint*", Width) + return Width +} + +;##################################################################################### + +; Function Gdip_GetImageHeight +; Description Gives the height of a bitmap +; +; pBitmap Pointer to a bitmap +; +; return Returns the height in pixels of the supplied bitmap + +Gdip_GetImageHeight(pBitmap) +{ + DllCall("gdiplus\GdipGetImageHeight", A_PtrSize ? "UPtr" : "UInt", pBitmap, "uint*", Height) + return Height +} + +;##################################################################################### + +; Function Gdip_GetDimensions +; Description Gives the width and height of a bitmap +; +; pBitmap Pointer to a bitmap +; Width ByRef variable. This variable will be set to the width of the bitmap +; Height ByRef variable. This variable will be set to the height of the bitmap +; +; return No return value +; Gdip_GetDimensions(pBitmap, ThisWidth, ThisHeight) will set ThisWidth to the width and ThisHeight to the height + +Gdip_GetImageDimensions(pBitmap, ByRef Width, ByRef Height) +{ + Width := 0 + Height := 0 + Ptr := A_PtrSize ? "UPtr" : "UInt" + DllCall("gdiplus\GdipGetImageWidth", Ptr, pBitmap, "uint*", Width) + DllCall("gdiplus\GdipGetImageHeight", Ptr, pBitmap, "uint*", Height) +} + +;##################################################################################### + +Gdip_GetDimensions(pBitmap, ByRef Width, ByRef Height) +{ + Gdip_GetImageDimensions(pBitmap, Width, Height) +} + +;##################################################################################### + +Gdip_GetImagePixelFormat(pBitmap) +{ + DllCall("gdiplus\GdipGetImagePixelFormat", A_PtrSize ? "UPtr" : "UInt", pBitmap, A_PtrSize ? "UPtr*" : "UInt*", Format) + return Format +} + +;##################################################################################### + +; Function Gdip_GetDpiX +; Description Gives the horizontal dots per inch of the graphics of a bitmap +; +; pBitmap Pointer to a bitmap +; Width ByRef variable. This variable will be set to the width of the bitmap +; Height ByRef variable. This variable will be set to the height of the bitmap +; +; return No return value +; Gdip_GetDimensions(pBitmap, ThisWidth, ThisHeight) will set ThisWidth to the width and ThisHeight to the height + +Gdip_GetDpiX(pGraphics) +{ + DllCall("gdiplus\GdipGetDpiX", A_PtrSize ? "UPtr" : "uint", pGraphics, "float*", dpix) + return Round(dpix) +} + +;##################################################################################### + +Gdip_GetDpiY(pGraphics) +{ + DllCall("gdiplus\GdipGetDpiY", A_PtrSize ? "UPtr" : "uint", pGraphics, "float*", dpiy) + return Round(dpiy) +} + +;##################################################################################### + +Gdip_GetImageHorizontalResolution(pBitmap) +{ + DllCall("gdiplus\GdipGetImageHorizontalResolution", A_PtrSize ? "UPtr" : "uint", pBitmap, "float*", dpix) + return Round(dpix) +} + +;##################################################################################### + +Gdip_GetImageVerticalResolution(pBitmap) +{ + DllCall("gdiplus\GdipGetImageVerticalResolution", A_PtrSize ? "UPtr" : "uint", pBitmap, "float*", dpiy) + return Round(dpiy) +} + +;##################################################################################### + +Gdip_BitmapSetResolution(pBitmap, dpix, dpiy) +{ + return DllCall("gdiplus\GdipBitmapSetResolution", A_PtrSize ? "UPtr" : "uint", pBitmap, "float", dpix, "float", dpiy) +} + +;##################################################################################### + +Gdip_CreateBitmapFromFile(sFile, IconNumber:=1, IconSize:="") +{ + pBitmap := "" + Ptr := A_PtrSize ? "UPtr" : "UInt" + , PtrA := A_PtrSize ? "UPtr*" : "UInt*" + + SplitPath sFile,,, Extension + if RegExMatch(Extension, "^(?i:exe|dll)$") + { + Sizes := IconSize ? IconSize : 256 "|" 128 "|" 64 "|" 48 "|" 32 "|" 16 + BufSize := 16 + (2*(A_PtrSize ? A_PtrSize : 4)) + + VarSetCapacity(buf, BufSize, 0) + For eachSize, Size in StrSplit( Sizes, "|" ) + { + DllCall("PrivateExtractIcons", "str", sFile, "int", IconNumber-1, "int", Size, "int", Size, PtrA, hIcon, PtrA, 0, "uint", 1, "uint", 0) + + if !hIcon + continue + + if !DllCall("GetIconInfo", Ptr, hIcon, Ptr, &buf) + { + DestroyIcon(hIcon) + continue + } + + hbmMask := NumGet(buf, 12 + ((A_PtrSize ? A_PtrSize : 4) - 4)) + hbmColor := NumGet(buf, 12 + ((A_PtrSize ? A_PtrSize : 4) - 4) + (A_PtrSize ? A_PtrSize : 4)) + if !(hbmColor && DllCall("GetObject", Ptr, hbmColor, "int", BufSize, Ptr, &buf)) + { + DestroyIcon(hIcon) + continue + } + break + } + if !hIcon + return -1 + + Width := NumGet(buf, 4, "int"), Height := NumGet(buf, 8, "int") + hbm := CreateDIBSection(Width, -Height), hdc := CreateCompatibleDC(), obm := SelectObject(hdc, hbm) + if !DllCall("DrawIconEx", Ptr, hdc, "int", 0, "int", 0, Ptr, hIcon, "uint", Width, "uint", Height, "uint", 0, Ptr, 0, "uint", 3) + { + DestroyIcon(hIcon) + return -2 + } + + VarSetCapacity(dib, 104) + DllCall("GetObject", Ptr, hbm, "int", A_PtrSize = 8 ? 104 : 84, Ptr, &dib) ; sizeof(DIBSECTION) = 76+2*(A_PtrSize=8?4:0)+2*A_PtrSize + Stride := NumGet(dib, 12, "Int"), Bits := NumGet(dib, 20 + (A_PtrSize = 8 ? 4 : 0)) ; padding + DllCall("gdiplus\GdipCreateBitmapFromScan0", "int", Width, "int", Height, "int", Stride, "int", 0x26200A, Ptr, Bits, PtrA, pBitmapOld) + pBitmap := Gdip_CreateBitmap(Width, Height) + _G := Gdip_GraphicsFromImage(pBitmap) + , Gdip_DrawImage(_G, pBitmapOld, 0, 0, Width, Height, 0, 0, Width, Height) + SelectObject(hdc, obm), DeleteObject(hbm), DeleteDC(hdc) + Gdip_DeleteGraphics(_G), Gdip_DisposeImage(pBitmapOld) + DestroyIcon(hIcon) + } + else + { + if (!A_IsUnicode) + { + VarSetCapacity(wFile, 1024) + DllCall("kernel32\MultiByteToWideChar", "uint", 0, "uint", 0, Ptr, &sFile, "int", -1, Ptr, &wFile, "int", 512) + DllCall("gdiplus\GdipCreateBitmapFromFile", Ptr, &wFile, PtrA, pBitmap) + } + else + DllCall("gdiplus\GdipCreateBitmapFromFile", Ptr, &sFile, PtrA, pBitmap) + } + + return pBitmap +} + +;##################################################################################### + +Gdip_CreateBitmapFromHBITMAP(hBitmap, Palette:=0) +{ + Ptr := A_PtrSize ? "UPtr" : "UInt" + pBitmap := "" + + DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", Ptr, hBitmap, Ptr, Palette, A_PtrSize ? "UPtr*" : "uint*", pBitmap) + return pBitmap +} + +;##################################################################################### + +Gdip_CreateHBITMAPFromBitmap(pBitmap, Background:=0xffffffff) +{ + DllCall("gdiplus\GdipCreateHBITMAPFromBitmap", A_PtrSize ? "UPtr" : "UInt", pBitmap, A_PtrSize ? "UPtr*" : "uint*", hbm, "int", Background) + return hbm +} + +;##################################################################################### + +Gdip_CreateBitmapFromHICON(hIcon) +{ + pBitmap := "" + + DllCall("gdiplus\GdipCreateBitmapFromHICON", A_PtrSize ? "UPtr" : "UInt", hIcon, A_PtrSize ? "UPtr*" : "uint*", pBitmap) + return pBitmap +} + +;##################################################################################### + +Gdip_CreateHICONFromBitmap(pBitmap) +{ + pBitmap := "" + hIcon := 0 + + DllCall("gdiplus\GdipCreateHICONFromBitmap", A_PtrSize ? "UPtr" : "UInt", pBitmap, A_PtrSize ? "UPtr*" : "uint*", hIcon) + return hIcon +} + +;##################################################################################### + +Gdip_CreateBitmap(Width, Height, Format:=0x26200A) +{ + pBitmap := "" + + DllCall("gdiplus\GdipCreateBitmapFromScan0", "int", Width, "int", Height, "int", 0, "int", Format, A_PtrSize ? "UPtr" : "UInt", 0, A_PtrSize ? "UPtr*" : "uint*", pBitmap) + Return pBitmap +} + +;##################################################################################### + +Gdip_CreateBitmapFromClipboard() +{ + Ptr := A_PtrSize ? "UPtr" : "UInt" + + if !DllCall("OpenClipboard", Ptr, 0) + return -1 + if !DllCall("IsClipboardFormatAvailable", "uint", 8) + return -2 + if !hBitmap := DllCall("GetClipboardData", "uint", 2, Ptr) + return -3 + if !pBitmap := Gdip_CreateBitmapFromHBITMAP(hBitmap) + return -4 + if !DllCall("CloseClipboard") + return -5 + DeleteObject(hBitmap) + return pBitmap +} + +;##################################################################################### + +Gdip_SetBitmapToClipboard(pBitmap) +{ + Ptr := A_PtrSize ? "UPtr" : "UInt" + off1 := A_PtrSize = 8 ? 52 : 44, off2 := A_PtrSize = 8 ? 32 : 24 + hBitmap := Gdip_CreateHBITMAPFromBitmap(pBitmap) + DllCall("GetObject", Ptr, hBitmap, "int", VarSetCapacity(oi, A_PtrSize = 8 ? 104 : 84, 0), Ptr, &oi) + hdib := DllCall("GlobalAlloc", "uint", 2, Ptr, 40+NumGet(oi, off1, "UInt"), Ptr) + pdib := DllCall("GlobalLock", Ptr, hdib, Ptr) + DllCall("RtlMoveMemory", Ptr, pdib, Ptr, &oi+off2, Ptr, 40) + DllCall("RtlMoveMemory", Ptr, pdib+40, Ptr, NumGet(oi, off2 - (A_PtrSize ? A_PtrSize : 4), Ptr), Ptr, NumGet(oi, off1, "UInt")) + DllCall("GlobalUnlock", Ptr, hdib) + DllCall("DeleteObject", Ptr, hBitmap) + DllCall("OpenClipboard", Ptr, 0) + DllCall("EmptyClipboard") + DllCall("SetClipboardData", "uint", 8, Ptr, hdib) + DllCall("CloseClipboard") +} + +;##################################################################################### + +Gdip_CloneBitmapArea(pBitmap, x, y, w, h, Format:=0x26200A) +{ + DllCall("gdiplus\GdipCloneBitmapArea" + , "float", x + , "float", y + , "float", w + , "float", h + , "int", Format + , A_PtrSize ? "UPtr" : "UInt", pBitmap + , A_PtrSize ? "UPtr*" : "UInt*", pBitmapDest) + return pBitmapDest +} + +;##################################################################################### +; Create resources +;##################################################################################### + +Gdip_CreatePen(ARGB, w) +{ + DllCall("gdiplus\GdipCreatePen1", "UInt", ARGB, "float", w, "int", 2, A_PtrSize ? "UPtr*" : "UInt*", pPen) + return pPen +} + +;##################################################################################### + +Gdip_CreatePenFromBrush(pBrush, w) +{ + pPen := "" + + DllCall("gdiplus\GdipCreatePen2", A_PtrSize ? "UPtr" : "UInt", pBrush, "float", w, "int", 2, A_PtrSize ? "UPtr*" : "UInt*", pPen) + return pPen +} + +;##################################################################################### + +Gdip_BrushCreateSolid(ARGB:=0xff000000) +{ + pBrush := "" + + DllCall("gdiplus\GdipCreateSolidFill", "UInt", ARGB, A_PtrSize ? "UPtr*" : "UInt*", pBrush) + return pBrush +} + +;##################################################################################### + +; HatchStyleHorizontal = 0 +; HatchStyleVertical = 1 +; HatchStyleForwardDiagonal = 2 +; HatchStyleBackwardDiagonal = 3 +; HatchStyleCross = 4 +; HatchStyleDiagonalCross = 5 +; HatchStyle05Percent = 6 +; HatchStyle10Percent = 7 +; HatchStyle20Percent = 8 +; HatchStyle25Percent = 9 +; HatchStyle30Percent = 10 +; HatchStyle40Percent = 11 +; HatchStyle50Percent = 12 +; HatchStyle60Percent = 13 +; HatchStyle70Percent = 14 +; HatchStyle75Percent = 15 +; HatchStyle80Percent = 16 +; HatchStyle90Percent = 17 +; HatchStyleLightDownwardDiagonal = 18 +; HatchStyleLightUpwardDiagonal = 19 +; HatchStyleDarkDownwardDiagonal = 20 +; HatchStyleDarkUpwardDiagonal = 21 +; HatchStyleWideDownwardDiagonal = 22 +; HatchStyleWideUpwardDiagonal = 23 +; HatchStyleLightVertical = 24 +; HatchStyleLightHorizontal = 25 +; HatchStyleNarrowVertical = 26 +; HatchStyleNarrowHorizontal = 27 +; HatchStyleDarkVertical = 28 +; HatchStyleDarkHorizontal = 29 +; HatchStyleDashedDownwardDiagonal = 30 +; HatchStyleDashedUpwardDiagonal = 31 +; HatchStyleDashedHorizontal = 32 +; HatchStyleDashedVertical = 33 +; HatchStyleSmallConfetti = 34 +; HatchStyleLargeConfetti = 35 +; HatchStyleZigZag = 36 +; HatchStyleWave = 37 +; HatchStyleDiagonalBrick = 38 +; HatchStyleHorizontalBrick = 39 +; HatchStyleWeave = 40 +; HatchStylePlaid = 41 +; HatchStyleDivot = 42 +; HatchStyleDottedGrid = 43 +; HatchStyleDottedDiamond = 44 +; HatchStyleShingle = 45 +; HatchStyleTrellis = 46 +; HatchStyleSphere = 47 +; HatchStyleSmallGrid = 48 +; HatchStyleSmallCheckerBoard = 49 +; HatchStyleLargeCheckerBoard = 50 +; HatchStyleOutlinedDiamond = 51 +; HatchStyleSolidDiamond = 52 +; HatchStyleTotal = 53 +Gdip_BrushCreateHatch(ARGBfront, ARGBback, HatchStyle:=0) +{ + pBrush := "" + + DllCall("gdiplus\GdipCreateHatchBrush", "int", HatchStyle, "UInt", ARGBfront, "UInt", ARGBback, A_PtrSize ? "UPtr*" : "UInt*", pBrush) + return pBrush +} + +;##################################################################################### + +Gdip_CreateTextureBrush(pBitmap, WrapMode:=1, x:=0, y:=0, w:="", h:="") +{ + Ptr := A_PtrSize ? "UPtr" : "UInt" + , PtrA := A_PtrSize ? "UPtr*" : "UInt*" + + if !(w && h) + DllCall("gdiplus\GdipCreateTexture", Ptr, pBitmap, "int", WrapMode, PtrA, pBrush) + else + DllCall("gdiplus\GdipCreateTexture2", Ptr, pBitmap, "int", WrapMode, "float", x, "float", y, "float", w, "float", h, PtrA, pBrush) + return pBrush +} + +;##################################################################################### + +; WrapModeTile = 0 +; WrapModeTileFlipX = 1 +; WrapModeTileFlipY = 2 +; WrapModeTileFlipXY = 3 +; WrapModeClamp = 4 +Gdip_CreateLineBrush(x1, y1, x2, y2, ARGB1, ARGB2, WrapMode:=1) +{ + Ptr := A_PtrSize ? "UPtr" : "UInt" + + CreatePointF(PointF1, x1, y1), CreatePointF(PointF2, x2, y2) + DllCall("gdiplus\GdipCreateLineBrush", Ptr, &PointF1, Ptr, &PointF2, "Uint", ARGB1, "Uint", ARGB2, "int", WrapMode, A_PtrSize ? "UPtr*" : "UInt*", LGpBrush) + return LGpBrush +} + +;##################################################################################### + +; LinearGradientModeHorizontal = 0 +; LinearGradientModeVertical = 1 +; LinearGradientModeForwardDiagonal = 2 +; LinearGradientModeBackwardDiagonal = 3 +Gdip_CreateLineBrushFromRect(x, y, w, h, ARGB1, ARGB2, LinearGradientMode:=1, WrapMode:=1) +{ + CreateRectF(RectF, x, y, w, h) + DllCall("gdiplus\GdipCreateLineBrushFromRect", A_PtrSize ? "UPtr" : "UInt", &RectF, "int", ARGB1, "int", ARGB2, "int", LinearGradientMode, "int", WrapMode, A_PtrSize ? "UPtr*" : "UInt*", LGpBrush) + return LGpBrush +} + +;##################################################################################### + +Gdip_CloneBrush(pBrush) +{ + DllCall("gdiplus\GdipCloneBrush", A_PtrSize ? "UPtr" : "UInt", pBrush, A_PtrSize ? "UPtr*" : "UInt*", pBrushClone) + return pBrushClone +} + +;##################################################################################### +; Delete resources +;##################################################################################### + +Gdip_DeletePen(pPen) +{ + return DllCall("gdiplus\GdipDeletePen", A_PtrSize ? "UPtr" : "UInt", pPen) +} + +;##################################################################################### + +Gdip_DeleteBrush(pBrush) +{ + return DllCall("gdiplus\GdipDeleteBrush", A_PtrSize ? "UPtr" : "UInt", pBrush) +} + +;##################################################################################### + +Gdip_DisposeImage(pBitmap) +{ + return DllCall("gdiplus\GdipDisposeImage", A_PtrSize ? "UPtr" : "UInt", pBitmap) +} + +;##################################################################################### + +Gdip_DeleteGraphics(pGraphics) +{ + return DllCall("gdiplus\GdipDeleteGraphics", A_PtrSize ? "UPtr" : "UInt", pGraphics) +} + +;##################################################################################### + +Gdip_DisposeImageAttributes(ImageAttr) +{ + return DllCall("gdiplus\GdipDisposeImageAttributes", A_PtrSize ? "UPtr" : "UInt", ImageAttr) +} + +;##################################################################################### + +Gdip_DeleteFont(hFont) +{ + return DllCall("gdiplus\GdipDeleteFont", A_PtrSize ? "UPtr" : "UInt", hFont) +} + +;##################################################################################### + +Gdip_DeleteStringFormat(hFormat) +{ + return DllCall("gdiplus\GdipDeleteStringFormat", A_PtrSize ? "UPtr" : "UInt", hFormat) +} + +;##################################################################################### + +Gdip_DeleteFontFamily(hFamily) +{ + return DllCall("gdiplus\GdipDeleteFontFamily", A_PtrSize ? "UPtr" : "UInt", hFamily) +} + +;##################################################################################### + +Gdip_DeleteMatrix(Matrix) +{ + return DllCall("gdiplus\GdipDeleteMatrix", A_PtrSize ? "UPtr" : "UInt", Matrix) +} + +;##################################################################################### +; Text functions +;##################################################################################### + +Gdip_TextToGraphics(pGraphics, Text, Options, Font:="Arial", Width:="", Height:="", Measure:=0) +{ + IWidth := Width, IHeight:= Height + + pattern_opts := (A_AhkVersion < "2") ? "iO)" : "i)" + RegExMatch(Options, pattern_opts "X([\-\d\.]+)(p*)", xpos) + RegExMatch(Options, pattern_opts "Y([\-\d\.]+)(p*)", ypos) + RegExMatch(Options, pattern_opts "W([\-\d\.]+)(p*)", Width) + RegExMatch(Options, pattern_opts "H([\-\d\.]+)(p*)", Height) + RegExMatch(Options, pattern_opts "C(?!(entre|enter))([a-f\d]+)", Colour) + RegExMatch(Options, pattern_opts "Top|Up|Bottom|Down|vCentre|vCenter", vPos) + RegExMatch(Options, pattern_opts "NoWrap", NoWrap) + RegExMatch(Options, pattern_opts "R(\d)", Rendering) + RegExMatch(Options, pattern_opts "S(\d+)(p*)", Size) + + if Colour && !Gdip_DeleteBrush(Gdip_CloneBrush(Colour[2])) + PassBrush := 1, pBrush := Colour[2] + + if !(IWidth && IHeight) && ((xpos && xpos[2]) || (ypos && ypos[2]) || (Width && Width[2]) || (Height && Height[2]) || (Size && Size[2])) + return -1 + + Style := 0, Styles := "Regular|Bold|Italic|BoldItalic|Underline|Strikeout" + For eachStyle, valStyle in StrSplit( Styles, "|" ) + { + if RegExMatch(Options, "\b" valStyle) + Style |= (valStyle != "StrikeOut") ? (A_Index-1) : 8 + } + + Align := 0, Alignments := "Near|Left|Centre|Center|Far|Right" + For eachAlignment, valAlignment in StrSplit( Alignments, "|" ) + { + if RegExMatch(Options, "\b" valAlignment) + Align |= A_Index//2.1 ; 0|0|1|1|2|2 + } + + xpos := (xpos && (xpos[1] != "")) ? xpos[2] ? IWidth*(xpos[1]/100) : xpos[1] : 0 + ypos := (ypos && (ypos[1] != "")) ? ypos[2] ? IHeight*(ypos[1]/100) : ypos[1] : 0 + Width := (Width && Width[1]) ? Width[2] ? IWidth*(Width[1]/100) : Width[1] : IWidth + Height := (Height && Height[1]) ? Height[2] ? IHeight*(Height[1]/100) : Height[1] : IHeight + if !PassBrush + Colour := "0x" (Colour && Colour[2] ? Colour[2] : "ff000000") + Rendering := (Rendering && (Rendering[1] >= 0) && (Rendering[1] <= 5)) ? Rendering[1] : 4 + Size := (Size && (Size[1] > 0)) ? Size[2] ? IHeight*(Size[1]/100) : Size[1] : 12 + + hFamily := Gdip_FontFamilyCreate(Font) + hFont := Gdip_FontCreate(hFamily, Size, Style) + FormatStyle := NoWrap ? 0x4000 | 0x1000 : 0x4000 + hFormat := Gdip_StringFormatCreate(FormatStyle) + pBrush := PassBrush ? pBrush : Gdip_BrushCreateSolid(Colour) + if !(hFamily && hFont && hFormat && pBrush && pGraphics) + return !pGraphics ? -2 : !hFamily ? -3 : !hFont ? -4 : !hFormat ? -5 : !pBrush ? -6 : 0 + + CreateRectF(RC, xpos, ypos, Width, Height) + Gdip_SetStringFormatAlign(hFormat, Align) + Gdip_SetTextRenderingHint(pGraphics, Rendering) + ReturnRC := Gdip_MeasureString(pGraphics, Text, hFont, hFormat, RC) + + if vPos + { + ReturnRC := StrSplit(ReturnRC, "|") + + if (vPos[0] = "vCentre") || (vPos[0] = "vCenter") + ypos += (Height-ReturnRC[4])//2 + else if (vPos[0] = "Top") || (vPos[0] = "Up") + ypos := 0 + else if (vPos[0] = "Bottom") || (vPos[0] = "Down") + ypos := Height-ReturnRC[4] + + CreateRectF(RC, xpos, ypos, Width, ReturnRC[4]) + ReturnRC := Gdip_MeasureString(pGraphics, Text, hFont, hFormat, RC) + } + + if !Measure + _E := Gdip_DrawString(pGraphics, Text, hFont, hFormat, pBrush, RC) + + if !PassBrush + Gdip_DeleteBrush(pBrush) + Gdip_DeleteStringFormat(hFormat) + Gdip_DeleteFont(hFont) + Gdip_DeleteFontFamily(hFamily) + return _E ? _E : ReturnRC +} + +;##################################################################################### + +Gdip_DrawString(pGraphics, sString, hFont, hFormat, pBrush, ByRef RectF) +{ + Ptr := A_PtrSize ? "UPtr" : "UInt" + + if (!A_IsUnicode) + { + nSize := DllCall("MultiByteToWideChar", "uint", 0, "uint", 0, Ptr, &sString, "int", -1, Ptr, 0, "int", 0) + VarSetCapacity(wString, nSize*2) + DllCall("MultiByteToWideChar", "uint", 0, "uint", 0, Ptr, &sString, "int", -1, Ptr, &wString, "int", nSize) + } + + return DllCall("gdiplus\GdipDrawString" + , Ptr, pGraphics + , Ptr, A_IsUnicode ? &sString : &wString + , "int", -1 + , Ptr, hFont + , Ptr, &RectF + , Ptr, hFormat + , Ptr, pBrush) +} + +;##################################################################################### + +Gdip_MeasureString(pGraphics, sString, hFont, hFormat, ByRef RectF) +{ + Ptr := A_PtrSize ? "UPtr" : "UInt" + + VarSetCapacity(RC, 16) + if !A_IsUnicode + { + nSize := DllCall("MultiByteToWideChar", "uint", 0, "uint", 0, Ptr, &sString, "int", -1, "uint", 0, "int", 0) + VarSetCapacity(wString, nSize*2) + DllCall("MultiByteToWideChar", "uint", 0, "uint", 0, Ptr, &sString, "int", -1, Ptr, &wString, "int", nSize) + } + + DllCall("gdiplus\GdipMeasureString" + , Ptr, pGraphics + , Ptr, A_IsUnicode ? &sString : &wString + , "int", -1 + , Ptr, hFont + , Ptr, &RectF + , Ptr, hFormat + , Ptr, &RC + , "uint*", Chars + , "uint*", Lines) + + return &RC ? NumGet(RC, 0, "float") "|" NumGet(RC, 4, "float") "|" NumGet(RC, 8, "float") "|" NumGet(RC, 12, "float") "|" Chars "|" Lines : 0 +} + +; Near = 0 +; Center = 1 +; Far = 2 +Gdip_SetStringFormatAlign(hFormat, Align) +{ + return DllCall("gdiplus\GdipSetStringFormatAlign", A_PtrSize ? "UPtr" : "UInt", hFormat, "int", Align) +} + +; StringFormatFlagsDirectionRightToLeft = 0x00000001 +; StringFormatFlagsDirectionVertical = 0x00000002 +; StringFormatFlagsNoFitBlackBox = 0x00000004 +; StringFormatFlagsDisplayFormatControl = 0x00000020 +; StringFormatFlagsNoFontFallback = 0x00000400 +; StringFormatFlagsMeasureTrailingSpaces = 0x00000800 +; StringFormatFlagsNoWrap = 0x00001000 +; StringFormatFlagsLineLimit = 0x00002000 +; StringFormatFlagsNoClip = 0x00004000 +Gdip_StringFormatCreate(Format:=0, Lang:=0) +{ + DllCall("gdiplus\GdipCreateStringFormat", "int", Format, "int", Lang, A_PtrSize ? "UPtr*" : "UInt*", hFormat) + return hFormat +} + +; Regular = 0 +; Bold = 1 +; Italic = 2 +; BoldItalic = 3 +; Underline = 4 +; Strikeout = 8 +Gdip_FontCreate(hFamily, Size, Style:=0) +{ + DllCall("gdiplus\GdipCreateFont", A_PtrSize ? "UPtr" : "UInt", hFamily, "float", Size, "int", Style, "int", 0, A_PtrSize ? "UPtr*" : "UInt*", hFont) + return hFont +} + +Gdip_FontFamilyCreate(Font) +{ + Ptr := A_PtrSize ? "UPtr" : "UInt" + + if (!A_IsUnicode) + { + nSize := DllCall("MultiByteToWideChar", "uint", 0, "uint", 0, Ptr, &Font, "int", -1, "uint", 0, "int", 0) + VarSetCapacity(wFont, nSize*2) + DllCall("MultiByteToWideChar", "uint", 0, "uint", 0, Ptr, &Font, "int", -1, Ptr, &wFont, "int", nSize) + } + + DllCall("gdiplus\GdipCreateFontFamilyFromName" + , Ptr, A_IsUnicode ? &Font : &wFont + , "uint", 0 + , A_PtrSize ? "UPtr*" : "UInt*", hFamily) + + return hFamily +} + +;##################################################################################### +; Matrix functions +;##################################################################################### + +Gdip_CreateAffineMatrix(m11, m12, m21, m22, x, y) +{ + DllCall("gdiplus\GdipCreateMatrix2", "float", m11, "float", m12, "float", m21, "float", m22, "float", x, "float", y, A_PtrSize ? "UPtr*" : "UInt*", Matrix) + return Matrix +} + +Gdip_CreateMatrix() +{ + DllCall("gdiplus\GdipCreateMatrix", A_PtrSize ? "UPtr*" : "UInt*", Matrix) + return Matrix +} + +;##################################################################################### +; GraphicsPath functions +;##################################################################################### + +; Alternate = 0 +; Winding = 1 +Gdip_CreatePath(BrushMode:=0) +{ + DllCall("gdiplus\GdipCreatePath", "int", BrushMode, A_PtrSize ? "UPtr*" : "UInt*", pPath) + return pPath +} + +Gdip_AddPathEllipse(pPath, x, y, w, h) +{ + return DllCall("gdiplus\GdipAddPathEllipse", A_PtrSize ? "UPtr" : "UInt", pPath, "float", x, "float", y, "float", w, "float", h) +} + +Gdip_AddPathPolygon(pPath, Points) +{ + Ptr := A_PtrSize ? "UPtr" : "UInt" + + Points := StrSplit(Points, "|") + VarSetCapacity(PointF, 8*Points.Length()) + for eachPoint, Point in Points + { + Coord := StrSplit(Point, ",") + NumPut(Coord[1], PointF, 8*(A_Index-1), "float"), NumPut(Coord[2], PointF, (8*(A_Index-1))+4, "float") + } + + return DllCall("gdiplus\GdipAddPathPolygon", Ptr, pPath, Ptr, &PointF, "int", Points.Length()) +} + +Gdip_DeletePath(pPath) +{ + return DllCall("gdiplus\GdipDeletePath", A_PtrSize ? "UPtr" : "UInt", pPath) +} + +;##################################################################################### +; Quality functions +;##################################################################################### + +; SystemDefault = 0 +; SingleBitPerPixelGridFit = 1 +; SingleBitPerPixel = 2 +; AntiAliasGridFit = 3 +; AntiAlias = 4 +Gdip_SetTextRenderingHint(pGraphics, RenderingHint) +{ + return DllCall("gdiplus\GdipSetTextRenderingHint", A_PtrSize ? "UPtr" : "UInt", pGraphics, "int", RenderingHint) +} + +; Default = 0 +; LowQuality = 1 +; HighQuality = 2 +; Bilinear = 3 +; Bicubic = 4 +; NearestNeighbor = 5 +; HighQualityBilinear = 6 +; HighQualityBicubic = 7 +Gdip_SetInterpolationMode(pGraphics, InterpolationMode) +{ + return DllCall("gdiplus\GdipSetInterpolationMode", A_PtrSize ? "UPtr" : "UInt", pGraphics, "int", InterpolationMode) +} + +; Default = 0 +; HighSpeed = 1 +; HighQuality = 2 +; None = 3 +; AntiAlias = 4 +Gdip_SetSmoothingMode(pGraphics, SmoothingMode) +{ + return DllCall("gdiplus\GdipSetSmoothingMode", A_PtrSize ? "UPtr" : "UInt", pGraphics, "int", SmoothingMode) +} + +; CompositingModeSourceOver = 0 (blended) +; CompositingModeSourceCopy = 1 (overwrite) +Gdip_SetCompositingMode(pGraphics, CompositingMode:=0) +{ + return DllCall("gdiplus\GdipSetCompositingMode", A_PtrSize ? "UPtr" : "UInt", pGraphics, "int", CompositingMode) +} + +;##################################################################################### +; Extra functions +;##################################################################################### + +Gdip_Startup() +{ + Ptr := A_PtrSize ? "UPtr" : "UInt" + pToken := 0 + + if !DllCall("GetModuleHandle", "str", "gdiplus", Ptr) + DllCall("LoadLibrary", "str", "gdiplus") + VarSetCapacity(si, A_PtrSize = 8 ? 24 : 16, 0), si := Chr(1) + DllCall("gdiplus\GdiplusStartup", A_PtrSize ? "UPtr*" : "uint*", pToken, Ptr, &si, Ptr, 0) + return pToken +} + +Gdip_Shutdown(pToken) +{ + Ptr := A_PtrSize ? "UPtr" : "UInt" + + DllCall("gdiplus\GdiplusShutdown", Ptr, pToken) + if hModule := DllCall("GetModuleHandle", "str", "gdiplus", Ptr) + DllCall("FreeLibrary", Ptr, hModule) + return 0 +} + +; Prepend = 0; The new operation is applied before the old operation. +; Append = 1; The new operation is applied after the old operation. +Gdip_RotateWorldTransform(pGraphics, Angle, MatrixOrder:=0) +{ + return DllCall("gdiplus\GdipRotateWorldTransform", A_PtrSize ? "UPtr" : "UInt", pGraphics, "float", Angle, "int", MatrixOrder) +} + +Gdip_ScaleWorldTransform(pGraphics, x, y, MatrixOrder:=0) +{ + return DllCall("gdiplus\GdipScaleWorldTransform", A_PtrSize ? "UPtr" : "UInt", pGraphics, "float", x, "float", y, "int", MatrixOrder) +} + +Gdip_TranslateWorldTransform(pGraphics, x, y, MatrixOrder:=0) +{ + return DllCall("gdiplus\GdipTranslateWorldTransform", A_PtrSize ? "UPtr" : "UInt", pGraphics, "float", x, "float", y, "int", MatrixOrder) +} + +Gdip_ResetWorldTransform(pGraphics) +{ + return DllCall("gdiplus\GdipResetWorldTransform", A_PtrSize ? "UPtr" : "UInt", pGraphics) +} + +Gdip_GetRotatedTranslation(Width, Height, Angle, ByRef xTranslation, ByRef yTranslation) +{ + pi := 3.14159, TAngle := Angle*(pi/180) + + Bound := (Angle >= 0) ? Mod(Angle, 360) : 360-Mod(-Angle, -360) + if ((Bound >= 0) && (Bound <= 90)) + xTranslation := Height*Sin(TAngle), yTranslation := 0 + else if ((Bound > 90) && (Bound <= 180)) + xTranslation := (Height*Sin(TAngle))-(Width*Cos(TAngle)), yTranslation := -Height*Cos(TAngle) + else if ((Bound > 180) && (Bound <= 270)) + xTranslation := -(Width*Cos(TAngle)), yTranslation := -(Height*Cos(TAngle))-(Width*Sin(TAngle)) + else if ((Bound > 270) && (Bound <= 360)) + xTranslation := 0, yTranslation := -Width*Sin(TAngle) +} + +Gdip_GetRotatedDimensions(Width, Height, Angle, ByRef RWidth, ByRef RHeight) +{ + pi := 3.14159, TAngle := Angle*(pi/180) + if !(Width && Height) + return -1 + RWidth := Ceil(Abs(Width*Cos(TAngle))+Abs(Height*Sin(TAngle))) + RHeight := Ceil(Abs(Width*Sin(TAngle))+Abs(Height*Cos(Tangle))) +} + +; RotateNoneFlipNone = 0 +; Rotate90FlipNone = 1 +; Rotate180FlipNone = 2 +; Rotate270FlipNone = 3 +; RotateNoneFlipX = 4 +; Rotate90FlipX = 5 +; Rotate180FlipX = 6 +; Rotate270FlipX = 7 +; RotateNoneFlipY = Rotate180FlipX +; Rotate90FlipY = Rotate270FlipX +; Rotate180FlipY = RotateNoneFlipX +; Rotate270FlipY = Rotate90FlipX +; RotateNoneFlipXY = Rotate180FlipNone +; Rotate90FlipXY = Rotate270FlipNone +; Rotate180FlipXY = RotateNoneFlipNone +; Rotate270FlipXY = Rotate90FlipNone + +Gdip_ImageRotateFlip(pBitmap, RotateFlipType:=1) +{ + return DllCall("gdiplus\GdipImageRotateFlip", A_PtrSize ? "UPtr" : "UInt", pBitmap, "int", RotateFlipType) +} + +; Replace = 0 +; Intersect = 1 +; Union = 2 +; Xor = 3 +; Exclude = 4 +; Complement = 5 +Gdip_SetClipRect(pGraphics, x, y, w, h, CombineMode:=0) +{ + return DllCall("gdiplus\GdipSetClipRect", A_PtrSize ? "UPtr" : "UInt", pGraphics, "float", x, "float", y, "float", w, "float", h, "int", CombineMode) +} + +Gdip_SetClipPath(pGraphics, pPath, CombineMode:=0) +{ + Ptr := A_PtrSize ? "UPtr" : "UInt" + return DllCall("gdiplus\GdipSetClipPath", Ptr, pGraphics, Ptr, pPath, "int", CombineMode) +} + +Gdip_ResetClip(pGraphics) +{ + return DllCall("gdiplus\GdipResetClip", A_PtrSize ? "UPtr" : "UInt", pGraphics) +} + +Gdip_GetClipRegion(pGraphics) +{ + Region := Gdip_CreateRegion() + DllCall("gdiplus\GdipGetClip", A_PtrSize ? "UPtr" : "UInt", pGraphics, "UInt", Region) + return Region +} + +Gdip_SetClipRegion(pGraphics, Region, CombineMode:=0) +{ + Ptr := A_PtrSize ? "UPtr" : "UInt" + + return DllCall("gdiplus\GdipSetClipRegion", Ptr, pGraphics, Ptr, Region, "int", CombineMode) +} + +Gdip_CreateRegion() +{ + DllCall("gdiplus\GdipCreateRegion", "UInt*", Region) + return Region +} + +Gdip_DeleteRegion(Region) +{ + return DllCall("gdiplus\GdipDeleteRegion", A_PtrSize ? "UPtr" : "UInt", Region) +} + +;##################################################################################### +; BitmapLockBits +;##################################################################################### + +Gdip_LockBits(pBitmap, x, y, w, h, ByRef Stride, ByRef Scan0, ByRef BitmapData, LockMode := 3, PixelFormat := 0x26200a) +{ + Ptr := A_PtrSize ? "UPtr" : "UInt" + + CreateRect(_Rect, x, y, w, h) + VarSetCapacity(BitmapData, 16+2*(A_PtrSize ? A_PtrSize : 4), 0) + _E := DllCall("Gdiplus\GdipBitmapLockBits", Ptr, pBitmap, Ptr, &_Rect, "uint", LockMode, "int", PixelFormat, Ptr, &BitmapData) + Stride := NumGet(BitmapData, 8, "Int") + Scan0 := NumGet(BitmapData, 16, Ptr) + return _E +} + +;##################################################################################### + +Gdip_UnlockBits(pBitmap, ByRef BitmapData) +{ + Ptr := A_PtrSize ? "UPtr" : "UInt" + + return DllCall("Gdiplus\GdipBitmapUnlockBits", Ptr, pBitmap, Ptr, &BitmapData) +} + +;##################################################################################### + +Gdip_SetLockBitPixel(ARGB, Scan0, x, y, Stride) +{ + Numput(ARGB, Scan0+0, (x*4)+(y*Stride), "UInt") +} + +;##################################################################################### + +Gdip_GetLockBitPixel(Scan0, x, y, Stride) +{ + return NumGet(Scan0+0, (x*4)+(y*Stride), "UInt") +} + +;##################################################################################### + +Gdip_PixelateBitmap(pBitmap, ByRef pBitmapOut, BlockSize) +{ + static PixelateBitmap + + Ptr := A_PtrSize ? "UPtr" : "UInt" + + if (!PixelateBitmap) + { + if A_PtrSize != 8 ; x86 machine code + MCode_PixelateBitmap := " + (LTrim Join + 558BEC83EC3C8B4514538B5D1C99F7FB56578BC88955EC894DD885C90F8E830200008B451099F7FB8365DC008365E000894DC88955F08945E833FF897DD4 + 397DE80F8E160100008BCB0FAFCB894DCC33C08945F88945FC89451C8945143BD87E608B45088D50028BC82BCA8BF02BF2418945F48B45E02955F4894DC4 + 8D0CB80FAFCB03CA895DD08BD1895DE40FB64416030145140FB60201451C8B45C40FB604100145FC8B45F40FB604020145F883C204FF4DE475D6034D18FF + 4DD075C98B4DCC8B451499F7F98945148B451C99F7F989451C8B45FC99F7F98945FC8B45F899F7F98945F885DB7E648B450C8D50028BC82BCA83C103894D + C48BC82BCA41894DF48B4DD48945E48B45E02955E48D0C880FAFCB03CA895DD08BD18BF38A45148B7DC48804178A451C8B7DF488028A45FC8804178A45F8 + 8B7DE488043A83C2044E75DA034D18FF4DD075CE8B4DCC8B7DD447897DD43B7DE80F8CF2FEFFFF837DF0000F842C01000033C08945F88945FC89451C8945 + 148945E43BD87E65837DF0007E578B4DDC034DE48B75E80FAF4D180FAFF38B45088D500203CA8D0CB18BF08BF88945F48B45F02BF22BFA2955F48945CC0F + B6440E030145140FB60101451C0FB6440F010145FC8B45F40FB604010145F883C104FF4DCC75D8FF45E4395DE47C9B8B4DF00FAFCB85C9740B8B451499F7 + F9894514EB048365140033F63BCE740B8B451C99F7F989451CEB0389751C3BCE740B8B45FC99F7F98945FCEB038975FC3BCE740B8B45F899F7F98945F8EB + 038975F88975E43BDE7E5A837DF0007E4C8B4DDC034DE48B75E80FAF4D180FAFF38B450C8D500203CA8D0CB18BF08BF82BF22BFA2BC28B55F08955CC8A55 + 1488540E038A551C88118A55FC88540F018A55F888140183C104FF4DCC75DFFF45E4395DE47CA68B45180145E0015DDCFF4DC80F8594FDFFFF8B451099F7 + FB8955F08945E885C00F8E450100008B45EC0FAFC38365DC008945D48B45E88945CC33C08945F88945FC89451C8945148945103945EC7E6085DB7E518B4D + D88B45080FAFCB034D108D50020FAF4D18034DDC8BF08BF88945F403CA2BF22BFA2955F4895DC80FB6440E030145140FB60101451C0FB6440F010145FC8B + 45F40FB604080145F883C104FF4DC875D8FF45108B45103B45EC7CA08B4DD485C9740B8B451499F7F9894514EB048365140033F63BCE740B8B451C99F7F9 + 89451CEB0389751C3BCE740B8B45FC99F7F98945FCEB038975FC3BCE740B8B45F899F7F98945F8EB038975F88975103975EC7E5585DB7E468B4DD88B450C + 0FAFCB034D108D50020FAF4D18034DDC8BF08BF803CA2BF22BFA2BC2895DC88A551488540E038A551C88118A55FC88540F018A55F888140183C104FF4DC8 + 75DFFF45108B45103B45EC7CAB8BC3C1E0020145DCFF4DCC0F85CEFEFFFF8B4DEC33C08945F88945FC89451C8945148945103BC87E6C3945F07E5C8B4DD8 + 8B75E80FAFCB034D100FAFF30FAF4D188B45088D500203CA8D0CB18BF08BF88945F48B45F02BF22BFA2955F48945C80FB6440E030145140FB60101451C0F + B6440F010145FC8B45F40FB604010145F883C104FF4DC875D833C0FF45108B4DEC394D107C940FAF4DF03BC874068B451499F7F933F68945143BCE740B8B + 451C99F7F989451CEB0389751C3BCE740B8B45FC99F7F98945FCEB038975FC3BCE740B8B45F899F7F98945F8EB038975F88975083975EC7E63EB0233F639 + 75F07E4F8B4DD88B75E80FAFCB034D080FAFF30FAF4D188B450C8D500203CA8D0CB18BF08BF82BF22BFA2BC28B55F08955108A551488540E038A551C8811 + 8A55FC88540F018A55F888140883C104FF4D1075DFFF45088B45083B45EC7C9F5F5E33C05BC9C21800 + )" + else ; x64 machine code + MCode_PixelateBitmap := " + (LTrim Join + 4489442418488954241048894C24085355565741544155415641574883EC28418BC1448B8C24980000004C8BDA99488BD941F7F9448BD0448BFA8954240C + 448994248800000085C00F8E9D020000418BC04533E4458BF299448924244C8954241041F7F933C9898C24980000008BEA89542404448BE889442408EB05 + 4C8B5C24784585ED0F8E1A010000458BF1418BFD48897C2418450FAFF14533D233F633ED4533E44533ED4585C97E5B4C63BC2490000000418D040A410FAF + C148984C8D441802498BD9498BD04D8BD90FB642010FB64AFF4403E80FB60203E90FB64AFE4883C2044403E003F149FFCB75DE4D03C748FFCB75D0488B7C + 24188B8C24980000004C8B5C2478418BC59941F7FE448BE8418BC49941F7FE448BE08BC59941F7FE8BE88BC69941F7FE8BF04585C97E4048639C24900000 + 004103CA4D8BC1410FAFC94863C94A8D541902488BCA498BC144886901448821408869FF408871FE4883C10448FFC875E84803D349FFC875DA8B8C249800 + 0000488B5C24704C8B5C24784183C20448FFCF48897C24180F850AFFFFFF8B6C2404448B2424448B6C24084C8B74241085ED0F840A01000033FF33DB4533 + DB4533D24533C04585C97E53488B74247085ED7E42438D0C04418BC50FAF8C2490000000410FAFC18D04814863C8488D5431028BCD0FB642014403D00FB6 + 024883C2044403D80FB642FB03D80FB642FA03F848FFC975DE41FFC0453BC17CB28BCD410FAFC985C9740A418BC299F7F98BF0EB0233F685C9740B418BC3 + 99F7F9448BD8EB034533DB85C9740A8BC399F7F9448BD0EB034533D285C9740A8BC799F7F9448BC0EB034533C033D24585C97E4D4C8B74247885ED7E3841 + 8D0C14418BC50FAF8C2490000000410FAFC18D04814863C84A8D4431028BCD40887001448818448850FF448840FE4883C00448FFC975E8FFC2413BD17CBD + 4C8B7424108B8C2498000000038C2490000000488B5C24704503E149FFCE44892424898C24980000004C897424100F859EFDFFFF448B7C240C448B842480 + 000000418BC09941F7F98BE8448BEA89942498000000896C240C85C00F8E3B010000448BAC2488000000418BCF448BF5410FAFC9898C248000000033FF33 + ED33F64533DB4533D24533C04585FF7E524585C97E40418BC5410FAFC14103C00FAF84249000000003C74898488D541802498BD90FB642014403D00FB602 + 4883C2044403D80FB642FB03F00FB642FA03E848FFCB75DE488B5C247041FFC0453BC77CAE85C9740B418BC299F7F9448BE0EB034533E485C9740A418BC3 + 99F7F98BD8EB0233DB85C9740A8BC699F7F9448BD8EB034533DB85C9740A8BC599F7F9448BD0EB034533D24533C04585FF7E4E488B4C24784585C97E3541 + 8BC5410FAFC14103C00FAF84249000000003C74898488D540802498BC144886201881A44885AFF448852FE4883C20448FFC875E941FFC0453BC77CBE8B8C + 2480000000488B5C2470418BC1C1E00203F849FFCE0F85ECFEFFFF448BAC24980000008B6C240C448BA4248800000033FF33DB4533DB4533D24533C04585 + FF7E5A488B7424704585ED7E48418BCC8BC5410FAFC94103C80FAF8C2490000000410FAFC18D04814863C8488D543102418BCD0FB642014403D00FB60248 + 83C2044403D80FB642FB03D80FB642FA03F848FFC975DE41FFC0453BC77CAB418BCF410FAFCD85C9740A418BC299F7F98BF0EB0233F685C9740B418BC399 + F7F9448BD8EB034533DB85C9740A8BC399F7F9448BD0EB034533D285C9740A8BC799F7F9448BC0EB034533C033D24585FF7E4E4585ED7E42418BCC8BC541 + 0FAFC903CA0FAF8C2490000000410FAFC18D04814863C8488B442478488D440102418BCD40887001448818448850FF448840FE4883C00448FFC975E8FFC2 + 413BD77CB233C04883C428415F415E415D415C5F5E5D5BC3 + )" + + VarSetCapacity(PixelateBitmap, StrLen(MCode_PixelateBitmap)//2) + nCount := StrLen(MCode_PixelateBitmap)//2 + N := (A_AhkVersion < 2) ? nCount : "nCount" + Loop %N% + NumPut("0x" SubStr(MCode_PixelateBitmap, (2*A_Index)-1, 2), PixelateBitmap, A_Index-1, "UChar") + DllCall("VirtualProtect", Ptr, &PixelateBitmap, Ptr, VarSetCapacity(PixelateBitmap), "uint", 0x40, A_PtrSize ? "UPtr*" : "UInt*", 0) + } + + Gdip_GetImageDimensions(pBitmap, Width, Height) + + if (Width != Gdip_GetImageWidth(pBitmapOut) || Height != Gdip_GetImageHeight(pBitmapOut)) + return -1 + if (BlockSize > Width || BlockSize > Height) + return -2 + + E1 := Gdip_LockBits(pBitmap, 0, 0, Width, Height, Stride1, Scan01, BitmapData1) + E2 := Gdip_LockBits(pBitmapOut, 0, 0, Width, Height, Stride2, Scan02, BitmapData2) + if (E1 || E2) + return -3 + + ; E := - unused exit code + DllCall(&PixelateBitmap, Ptr, Scan01, Ptr, Scan02, "int", Width, "int", Height, "int", Stride1, "int", BlockSize) + + Gdip_UnlockBits(pBitmap, BitmapData1), Gdip_UnlockBits(pBitmapOut, BitmapData2) + return 0 +} + +;##################################################################################### + +Gdip_ToARGB(A, R, G, B) +{ + return (A << 24) | (R << 16) | (G << 8) | B +} + +;##################################################################################### + +Gdip_FromARGB(ARGB, ByRef A, ByRef R, ByRef G, ByRef B) +{ + A := (0xff000000 & ARGB) >> 24 + R := (0x00ff0000 & ARGB) >> 16 + G := (0x0000ff00 & ARGB) >> 8 + B := 0x000000ff & ARGB +} + +;##################################################################################### + +Gdip_AFromARGB(ARGB) +{ + return (0xff000000 & ARGB) >> 24 +} + +;##################################################################################### + +Gdip_RFromARGB(ARGB) +{ + return (0x00ff0000 & ARGB) >> 16 +} + +;##################################################################################### + +Gdip_GFromARGB(ARGB) +{ + return (0x0000ff00 & ARGB) >> 8 +} + +;##################################################################################### + +Gdip_BFromARGB(ARGB) +{ + return 0x000000ff & ARGB +} + +;##################################################################################### + +StrGetB(Address, Length:=-1, Encoding:=0) +{ + ; Flexible parameter handling: + if !IsInteger(Length) + Encoding := Length, Length := -1 + + ; Check for obvious errors. + if (Address+0 < 1024) + return + + ; Ensure 'Encoding' contains a numeric identifier. + if (Encoding = "UTF-16") + Encoding := 1200 + else if (Encoding = "UTF-8") + Encoding := 65001 + else if SubStr(Encoding,1,2)="CP" + Encoding := SubStr(Encoding,3) + + if !Encoding ; "" or 0 + { + ; No conversion necessary, but we might not want the whole string. + if (Length == -1) + Length := DllCall("lstrlen", "uint", Address) + VarSetCapacity(String, Length) + DllCall("lstrcpyn", "str", String, "uint", Address, "int", Length + 1) + } + else if (Encoding = 1200) ; UTF-16 + { + char_count := DllCall("WideCharToMultiByte", "uint", 0, "uint", 0x400, "uint", Address, "int", Length, "uint", 0, "uint", 0, "uint", 0, "uint", 0) + VarSetCapacity(String, char_count) + DllCall("WideCharToMultiByte", "uint", 0, "uint", 0x400, "uint", Address, "int", Length, "str", String, "int", char_count, "uint", 0, "uint", 0) + } + else if IsInteger(Encoding) + { + ; Convert from target encoding to UTF-16 then to the active code page. + char_count := DllCall("MultiByteToWideChar", "uint", Encoding, "uint", 0, "uint", Address, "int", Length, "uint", 0, "int", 0) + VarSetCapacity(String, char_count * 2) + char_count := DllCall("MultiByteToWideChar", "uint", Encoding, "uint", 0, "uint", Address, "int", Length, "uint", &String, "int", char_count * 2) + String := StrGetB(&String, char_count, 1200) + } + + return String +} + + +;##################################################################################### +; in AHK v1: uses normal 'if var is' command +; in AHK v2: all if's are expression-if, so the Integer variable is dereferenced to the string +;##################################################################################### +IsInteger(Var) { + Static Integer := "Integer" + If Var Is Integer + Return True + Return False +} + +IsNumber(Var) { + Static number := "number" + If Var Is number + Return True + Return False +} + + + +; ====================================================================================================================== +; Multiple Display Monitors Functions -> msdn.microsoft.com/en-us/library/dd145072(v=vs.85).aspx +; by 'just me' +; https://autohotkey.com/boards/viewtopic.php?f=6&t=4606 +; ====================================================================================================================== +GetMonitorCount() +{ + Monitors := MDMF_Enum() + for k,v in Monitors + count := A_Index + return count +} + +GetMonitorInfo(MonitorNum) +{ + Monitors := MDMF_Enum() + for k,v in Monitors + if (v.Num = MonitorNum) + return v +} + +GetPrimaryMonitor() +{ + Monitors := MDMF_Enum() + for k,v in Monitors + If (v.Primary) + return v.Num +} +; ====================================================================================================================== +; Enumerates display monitors and returns an object containing the properties of all monitors or the specified monitor. +; ====================================================================================================================== +MDMF_Enum(HMON := "") { + Static CbFunc := (A_AhkVersion < "2") ? Func("RegisterCallback") : Func("CallbackCreate") + Static EnumProc := %CbFunc%("MDMF_EnumProc") + Static Monitors := {} + If (HMON = "") ; new enumeration + Monitors := {} + If (Monitors.MaxIndex() = "") ; enumerate + If !DllCall("User32.dll\EnumDisplayMonitors", "Ptr", 0, "Ptr", 0, "Ptr", EnumProc, "Ptr", &Monitors, "UInt") + Return False + Return (HMON = "") ? Monitors : Monitors.HasKey(HMON) ? Monitors[HMON] : False +} +; ====================================================================================================================== +; Callback function that is called by the MDMF_Enum function. +; ====================================================================================================================== +MDMF_EnumProc(HMON, HDC, PRECT, ObjectAddr) { + Monitors := Object(ObjectAddr) + Monitors[HMON] := MDMF_GetInfo(HMON) + Return True +} +; ====================================================================================================================== +; Retrieves the display monitor that has the largest area of intersection with a specified window. +; ====================================================================================================================== +MDMF_FromHWND(HWND) { + Return DllCall("User32.dll\MonitorFromWindow", "Ptr", HWND, "UInt", 0, "UPtr") +} +; ====================================================================================================================== +; Retrieves the display monitor that contains a specified point. +; If either X or Y is empty, the function will use the current cursor position for this value. +; ====================================================================================================================== +MDMF_FromPoint(X := "", Y := "") { + VarSetCapacity(PT, 8, 0) + If (X = "") || (Y = "") { + DllCall("User32.dll\GetCursorPos", "Ptr", &PT) + If (X = "") + X := NumGet(PT, 0, "Int") + If (Y = "") + Y := NumGet(PT, 4, "Int") + } + Return DllCall("User32.dll\MonitorFromPoint", "Int64", (X & 0xFFFFFFFF) | (Y << 32), "UInt", 0, "UPtr") +} +; ====================================================================================================================== +; Retrieves the display monitor that has the largest area of intersection with a specified rectangle. +; Parameters are consistent with the common AHK definition of a rectangle, which is X, Y, W, H instead of +; Left, Top, Right, Bottom. +; ====================================================================================================================== +MDMF_FromRect(X, Y, W, H) { + VarSetCapacity(RC, 16, 0) + NumPut(X, RC, 0, "Int"), NumPut(Y, RC, 4, Int), NumPut(X + W, RC, 8, "Int"), NumPut(Y + H, RC, 12, "Int") + Return DllCall("User32.dll\MonitorFromRect", "Ptr", &RC, "UInt", 0, "UPtr") +} +; ====================================================================================================================== +; Retrieves information about a display monitor. +; ====================================================================================================================== +MDMF_GetInfo(HMON) { + NumPut(VarSetCapacity(MIEX, 40 + (32 << !!A_IsUnicode)), MIEX, 0, "UInt") + If DllCall("User32.dll\GetMonitorInfo", "Ptr", HMON, "Ptr", &MIEX) { + MonName := StrGet(&MIEX + 40, 32) ; CCHDEVICENAME = 32 + MonNum := RegExReplace(MonName, ".*(\d+)$", "$1") + Return { Name: (Name := StrGet(&MIEX + 40, 32)) + , Num: RegExReplace(Name, ".*(\d+)$", "$1") + , Left: NumGet(MIEX, 4, "Int") ; display rectangle + , Top: NumGet(MIEX, 8, "Int") ; " + , Right: NumGet(MIEX, 12, "Int") ; " + , Bottom: NumGet(MIEX, 16, "Int") ; " + , WALeft: NumGet(MIEX, 20, "Int") ; work area + , WATop: NumGet(MIEX, 24, "Int") ; " + , WARight: NumGet(MIEX, 28, "Int") ; " + , WABottom: NumGet(MIEX, 32, "Int") ; " + , Primary: NumGet(MIEX, 36, "UInt")} ; contains a non-zero value for the primary monitor. + } + Return False +} \ No newline at end of file diff --git a/lib/Graphics.ahk b/lib/Graphics.ahk new file mode 100644 index 0000000..c593e52 --- /dev/null +++ b/lib/Graphics.ahk @@ -0,0 +1,3884 @@ +; Script: Graphics.ahk +; Author: iseahound +; License: GPLv3 +; Version: August 2018 (not for public use.) +; Release: 2019-08-03 + +#include ; https://goo.gl/rUuEF5 + + +; EqualImage() - Ensures that the pixel vaules of multiple images across mutiple formats are identical. +EqualImage(images*){ + return Graphics.ImageRenderer.Equal(images*) +} + +; PreprocessImage() - Converts an image of any type into any new type with cropping and scaling. +PreprocessImage(cotype, image, crop:="", scale:="", terms*){ + return Graphics.ImageRenderer.Preprocess(cotype, image, crop, scale, terms*) +} + +; RenderImage() - Displays an image in customizable styles on the screen. +RenderImage(image:="", style:="", polygons:=""){ + return Graphics.ImageRenderer.Render(image, style, polygons) +} + +; RenderImageI() - Allows the user to interact with the displayed image. +RenderImageI(image:="", style:="", polygons:="", keybinds:=""){ + return new Graphics.INTERACTIVE(Graphics.ImageRenderer.Render(image, style, polygons), keybinds, 2) +} + +; RenderImageS() - Creates a sequence of images that can be interacted with. +RenderImageS(image:="", style:="", polygons:="", keybinds:=""){ + return new Graphics.SEQUENCER(new Graphics.INTERACTIVE(Graphics.ImageRenderer.Render(image, style, polygons), keybinds, 2, true)) +} + +; RenderPolygon() - Displays polygons in customizable styles on the screen. +RenderPolygon(polygon:="", style:=""){ + return Graphics.PolygonRenderer.Render(polygon, style) +} + +; RenderPolygonI() - Allows the user to interact with the displayed polygon. +RenderPolygonI(polygon:="", style:="", keybinds:=""){ + return new Graphics.INTERACTIVE(Graphics.PolygonRenderer.Render(polygon, style), keybinds, 0) +} + +; RenderPolygonS() - Creates a sequence of polygons that can be interacted with. +RenderPolygonS(polygon:="", style:="", keybinds:=""){ + return new Graphics.SEQUENCER(new Graphics.INTERACTIVE(Graphics.PolygonRenderer.Render(polygon, style), keybinds, 0, true)) +} + +; RenderText() - Displays text in customizable styles on the screen. +RenderText(text:="", background_style:="", text_style:=""){ + return Graphics.TextRenderer.Render(text, background_style, text_style) +} + +; RenderTextI() - Allows the user to interact with the displayed text. +RenderTextI(text:="", background_style:="", text_style:="", keybinds:=""){ + return new Graphics.INTERACTIVE(Graphics.TextRenderer.Render(text, background_style, text_style), keybinds, 2) +} + +; RenderTextS() - Creates a sequence of text that can be interacted with. +RenderTextS(text:="", background_style:="", text_style:="", keybinds:=""){ + return new Graphics.SEQUENCER(new Graphics.INTERACTIVE(Graphics.TextRenderer.Render(text, background_style, text_style), keybinds, 2, true)) +} + + +class Graphics { + + static pToken ; Pointer to an instance of the GDI+ library. + static renderers := 0 ; Number of active renderer objects currently being used. + + ; Duality #0 - Loads a local instance of the GDI+ library. + Startup() { + global pToken + if (this.renderers++ <= 0) { + ; Thanks to jeeswg and majkinetor for showing how to create a custom window class. + vWinClass := "AutoHotkeyGraphics" + if (A_AhkVersion < 2) { + _fn := "RegisterCallback" + pWndProc := %_fn%(this.callback, "F",,&this) + } else { + _fn := "CallbackCreate" + pWndProc := %_fn%(this.callback, "F") + } + hCursor := DllCall("LoadCursor", "ptr",0, "ptr",32512, "ptr") ; IDC_ARROW := 32512 + + ; struct tagWNDCLASSEXA - https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-wndclassexa + ; struct tagWNDCLASSEXW - https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-wndclassexw + vSize := A_PtrSize=8 ? 80:48 + VarSetCapacity(WNDCLASSEX, vSize, 0) ; sizeof(WNDCLASSEX) = 48 or 80 + , NumPut( vSize, WNDCLASSEX, 0, "uint") ; cbSize + , NumPut( 3, WNDCLASSEX, 4, "uint") ; style + , NumPut( pWndProc, WNDCLASSEX, 8, "ptr") ; lpfnWndProc + , NumPut( 0, WNDCLASSEX, A_PtrSize=8 ? 16:12, "int") ; cbClsExtra + , NumPut( 0, WNDCLASSEX, A_PtrSize=8 ? 20:16, "int") ; cbWndExtra + , NumPut( 0, WNDCLASSEX, A_PtrSize=8 ? 24:20, "ptr") ; hInstance + , NumPut( 0, WNDCLASSEX, A_PtrSize=8 ? 32:24, "ptr") ; hIcon + , NumPut( hCursor, WNDCLASSEX, A_PtrSize=8 ? 40:28, "ptr") ; hCursor + , NumPut( 16, WNDCLASSEX, A_PtrSize=8 ? 48:32, "ptr") ; hbrBackground + , NumPut( 0, WNDCLASSEX, A_PtrSize=8 ? 56:36, "ptr") ; lpszMenuName + , NumPut( &vWinClass, WNDCLASSEX, A_PtrSize=8 ? 64:40, "ptr") ; lpszClassName + , NumPut( 0, WNDCLASSEX, A_PtrSize=8 ? 72:44, "ptr") ; hIconSm + + ; Registers a window class for subsequent use in calls to the CreateWindow or CreateWindowEx function. + DllCall("RegisterClassEx", "ptr",&WNDCLASSEX, "ushort") + + ; Set pToken. + this.pToken := (pToken) ? pToken : Gdip_Startup() + } + return this.pToken + } + + ; Duality #0 - Releases a local instance of the GDI+ library. + Shutdown() { + global pToken + if (--this.renderers <= 0) { + DllCall("UnregisterClass", "str","AutoHotkeyGraphics", "ptr",0) + (pToken) ? pToken : Gdip_Shutdown(this.pToken) + } + return + } + + Callback(uMsg, wParam, lParam) { + return DllCall("DefWindowProc", "ptr",this, "uint",uMsg, "uptr",wParam, "ptr",lParam, "ptr") ; hWnd := this + } + + class parse { + + 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 + } + + colorMap(c) { + static map + + if !(map) { + 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" + map := color + } + + return map[c] + } + + dropShadow(d, vw, vh, width, height, font_size) { + static q1 := "(?i)^.*?\b(?\([^()]*\)|[^()]*)*\))(:\s*)?\(?(?(?<=\()([\s:#%_a-z\-\.\d]+|\([\s:#%_a-z\-\.\d]*\))*(?=\))|[#%_a-z\-\.\d]+).*$" + static valid := "(?i)^\s*(\-?(?:(?:\d+(?:\.\d*)?)|(?:\.\d+)))\s*(%|pt|px|vh|vmin|vw)?\s*$" + vmin := (vw < vh) ? vw : vh + + if IsObject(d) { + d.1 := (d.horizontal != "") ? d.horizontal : (d.h != "") ? d.h : d.1 + d.2 := (d.vertical != "") ? d.vertical : (d.v != "") ? d.h : d.2 + d.3 := (d.blur != "") ? d.blur : (d.b != "") ? d.h : d.3 + d.4 := (d.color != "") ? d.color : (d.c != "") ? d.h : d.4 + d.5 := (d.opacity != "") ? d.opacity : (d.o != "") ? d.h : d.5 + d.6 := (d.size != "") ? d.size : (d.s != "") ? d.h : d.6 + } else if (d != "") { + _ := RegExReplace(d, ":\s+", ":") + _ := RegExReplace(_, "\s+", " ") + _ := StrSplit(_, " ") + _.1 := ((___ := RegExReplace(d, q1 "(h(orizontal)?)" q2, "${value}")) != d) ? ___ : _.1 + _.2 := ((___ := RegExReplace(d, q1 "(v(ertical)?)" q2, "${value}")) != d) ? ___ : _.2 + _.3 := ((___ := RegExReplace(d, q1 "(b(lur)?)" q2, "${value}")) != d) ? ___ : _.3 + _.4 := ((___ := RegExReplace(d, q1 "(c(olor)?)" q2, "${value}")) != d) ? ___ : _.4 + _.5 := ((___ := RegExReplace(d, q1 "(o(pacity)?)" q2, "${value}")) != d) ? ___ : _.5 + _.6 := ((___ := RegExReplace(d, q1 "(s(ize)?)" q2, "${value}")) != d) ? ___ : _.6 + d := _ + } + else return {"void":true, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0} + + for key, value in d { + if (key = 4) ; Don't mess with color data. + continue + d[key] := (d[key] ~= valid) ? RegExReplace(d[key], "\s", "") : 0 ; Default for everything is 0. + d[key] := (d[key] ~= "i)(pt|px)$") ? SubStr(d[key], 1, -2) : d[key] + d[key] := (d[key] ~= "i)vw$") ? RegExReplace(d[key], "i)vw$", "") * vw : d[key] + d[key] := (d[key] ~= "i)vh$") ? RegExReplace(d[key], "i)vh$", "") * vh : d[key] + d[key] := (d[key] ~= "i)vmin$") ? RegExReplace(d[key], "i)vmin$", "") * vmin : d[key] + } + + d.1 := (d.1 ~= "%$") ? SubStr(d.1, 1, -1) * 0.01 * width : d.1 + d.2 := (d.2 ~= "%$") ? SubStr(d.2, 1, -1) * 0.01 * height : d.2 + d.3 := (d.3 ~= "%$") ? SubStr(d.3, 1, -1) * 0.01 * font_size : d.3 + d.4 := this.color(d.4, 0xFFFF0000) ; Default color is red. + d.5 := (d.5 ~= "%$") ? SubStr(d.5, 1, -1) / 100 : d.5 + d.5 := (d.5 <= 0 || d.5 > 1) ? 1 : d.5 ; Range Opacity is a float from 0-1. + d.6 := (d.6 ~= "%$") ? SubStr(d.6, 1, -1) * 0.01 * font_size : d.6 + return d + } + + font(f, default := "Arial"){ + + } + + grayscale(sRGB) { + static rY := 0.212655 + static gY := 0.715158 + static bY := 0.072187 + + c1 := 255 & ( sRGB >> 16 ) + c2 := 255 & ( sRGB >> 8 ) + c3 := 255 & ( sRGB ) + + loop 3 { + c%A_Index% := c%A_Index% / 255 + c%A_Index% := (c%A_Index% <= 0.04045) ? c%A_Index%/12.92 : ((c%A_Index%+0.055)/(1.055))**2.4 + } + + v := rY*c1 + gY*c2 + bY*c3 + v := (v <= 0.0031308) ? v * 12.92 : 1.055*(v**(1.0/2.4))-0.055 + return Round(v*255) + } + + margin_and_padding(m, vw, vh, default := "") { + static q1 := "(?i)^.*?\b(?\([^()]*\)|[^()]*)*\))(:\s*)?\(?(?(?<=\()([\s:#%_a-z\-\.\d]+|\([\s:#%_a-z\-\.\d]*\))*(?=\))|[#%_a-z\-\.\d]+).*$" + static valid := "(?i)^\s*(\-?(?:(?:\d+(?:\.\d*)?)|(?:\.\d+)))\s*(%|pt|px|vh|vmin|vw)?\s*$" + vmin := (vw < vh) ? vw : vh + + if IsObject(m) { + m.1 := (m.top != "") ? m.top : (m.t != "") ? m.t : m.1 + m.2 := (m.right != "") ? m.right : (m.r != "") ? m.r : m.2 + m.3 := (m.bottom != "") ? m.bottom : (m.b != "") ? m.b : m.3 + m.4 := (m.left != "") ? m.left : (m.l != "") ? m.l : m.4 + } else if (m != "") { + _ := RegExReplace(m, ":\s+", ":") + _ := RegExReplace(_, "\s+", " ") + _ := StrSplit(_, " ") + _.1 := ((___ := RegExReplace(m, q1 "(t(op)?)" q2, "${value}")) != m) ? ___ : _.1 + _.2 := ((___ := RegExReplace(m, q1 "(r(ight)?)" q2, "${value}")) != m) ? ___ : _.2 + _.3 := ((___ := RegExReplace(m, q1 "(b(ottom)?)" q2, "${value}")) != m) ? ___ : _.3 + _.4 := ((___ := RegExReplace(m, q1 "(l(eft)?)" q2, "${value}")) != m) ? ___ : _.4 + m := _ + } else if (default != "") + m := {1:default, 2:default, 3:default, 4:default} + else return {"void":true, 1:0, 2:0, 3:0, 4:0} + + ; Follow CSS guidelines for margin! + if (m.2 == "" && m.3 == "" && m.4 == "") + m.4 := m.3 := m.2 := m.1, exception := true + if (m.3 == "" && m.4 == "") + m.4 := m.2, m.3 := m.1 + if (m.4 == "") + m.4 := m.2 + + for key, value in m { + m[key] := (m[key] ~= valid) ? RegExReplace(m[key], "\s", "") : default + m[key] := (m[key] ~= "i)(pt|px)$") ? SubStr(m[key], 1, -2) : m[key] + m[key] := (m[key] ~= "i)vw$") ? RegExReplace(m[key], "i)vw$", "") * vw : m[key] + m[key] := (m[key] ~= "i)vh$") ? RegExReplace(m[key], "i)vh$", "") * vh : m[key] + m[key] := (m[key] ~= "i)vmin$") ? RegExReplace(m[key], "i)vmin$", "") * vmin : m[key] + } + m.1 := (m.1 ~= "%$") ? SubStr(m.1, 1, -1) * vh : m.1 + m.2 := (m.2 ~= "%$") ? SubStr(m.2, 1, -1) * (exception ? vh : vw) : m.2 + m.3 := (m.3 ~= "%$") ? SubStr(m.3, 1, -1) * vh : m.3 + m.4 := (m.4 ~= "%$") ? SubStr(m.4, 1, -1) * (exception ? vh : vw) : m.4 + return m + } + + outline(o, vw, vh, font_size, font_color) { + static q1 := "(?i)^.*?\b(?\([^()]*\)|[^()]*)*\))(:\s*)?\(?(?(?<=\()([\s:#%_a-z\-\.\d]+|\([\s:#%_a-z\-\.\d]*\))*(?=\))|[#%_a-z\-\.\d]+).*$" + static valid_positive := "(?i)^\s*((?:(?:\d+(?:\.\d*)?)|(?:\.\d+)))\s*(%|pt|px|vh|vmin|vw)?\s*$" + vmin := (vw < vh) ? vw : vh + + if IsObject(o) { + o.1 := (o.stroke != "") ? o.stroke : (o.s != "") ? o.s : o.1 + o.2 := (o.color != "") ? o.color : (o.c != "") ? o.c : o.2 + o.3 := (o.glow != "") ? o.glow : (o.g != "") ? o.g : o.3 + o.4 := (o.tint != "") ? o.tint : (o.t != "") ? o.t : o.4 + } else if (o != "") { + _ := RegExReplace(o, ":\s+", ":") + _ := RegExReplace(_, "\s+", " ") + _ := StrSplit(_, " ") + _.1 := ((___ := RegExReplace(o, q1 "(s(troke)?)" q2, "${value}")) != o) ? ___ : _.1 + _.2 := ((___ := RegExReplace(o, q1 "(c(olor)?)" q2, "${value}")) != o) ? ___ : _.2 + _.3 := ((___ := RegExReplace(o, q1 "(g(low)?)" q2, "${value}")) != o) ? ___ : _.3 + _.4 := ((___ := RegExReplace(o, q1 "(t(int)?)" q2, "${value}")) != o) ? ___ : _.4 + o := _ + } + else return {"void":true, 1:0, 2:0, 3:0, 4:0} + + for key, value in o { + if (key = 2) || (key = 4) ; Don't mess with color data. + continue + o[key] := (o[key] ~= valid_positive) ? RegExReplace(o[key], "\s", "") : 0 ; Default for everything is 0. + o[key] := (o[key] ~= "i)(pt|px)$") ? SubStr(o[key], 1, -2) : o[key] + o[key] := (o[key] ~= "i)vw$") ? RegExReplace(o[key], "i)vw$", "") * vw : o[key] + o[key] := (o[key] ~= "i)vh$") ? RegExReplace(o[key], "i)vh$", "") * vh : o[key] + o[key] := (o[key] ~= "i)vmin$") ? RegExReplace(o[key], "i)vmin$", "") * vmin : o[key] + } + + o.1 := (o.1 ~= "%$") ? SubStr(o.1, 1, -1) * 0.01 * font_size : o.1 + o.2 := this.color(o.2, font_color) ; Default color is the text font color. + o.3 := (o.3 ~= "%$") ? SubStr(o.3, 1, -1) * 0.01 * font_size : o.3 + o.4 := this.color(o.4, o.2) ; Default color is outline color. + return o + } + } + + class filter { + + GaussianBlur(ByRef pBitmap, radius, opacity := 1) { + static x86 := " + (LTrim + VYnlV1ZTg+xci0Uci30c2UUgx0WsAwAAAI1EAAGJRdiLRRAPr0UYicOJRdSLRRwP + r/sPr0UYiX2ki30UiUWoi0UQjVf/i30YSA+vRRgDRQgPr9ONPL0SAAAAiUWci0Uc + iX3Eg2XE8ECJVbCJRcCLRcSJZbToAAAAACnEi0XEiWXk6AAAAAApxItFxIllzOgA + AAAAKcSLRaiJZcjHRdwAAAAAx0W8AAAAAIlF0ItFvDtFFA+NcAEAAItV3DHAi12c + i3XQiVXgAdOLfQiLVdw7RRiNDDp9IQ+2FAGLTcyLfciJFIEPtgwDD69VwIkMh4tN + 5IkUgUDr0THSO1UcfBKLXdwDXQzHRbgAAAAAK13Q6yAxwDtFGH0Ni33kD7YcAQEc + h0Dr7kIDTRjrz/9FuAN1GItF3CtF0AHwiceLRbg7RRx/LDHJO00YfeGLRQiLfcwB + 8A+2BAgrBI+LfeQDBI+ZiQSPjTwz933YiAQPQevWi0UIK0Xci03AAfCJRbiLXRCJ + /itdHCt13AN14DnZfAgDdQwrdeDrSot1DDHbK3XcAf4DdeA7XRh9KItV4ItFuAHQ + A1UID7YEGA+2FBop0ItV5AMEmokEmpn3fdiIBB5D69OLRRhBAUXg66OLRRhDAUXg + O10QfTIxyTtNGH3ti33Ii0XgA0UID7YUCIsEjynQi1XkAwSKiQSKi1XgjTwWmfd9 + 2IgED0Hr0ItF1P9FvAFF3AFF0OmE/v//i0Wkx0XcAAAAAMdFvAAAAACJRdCLRbAD + RQyJRaCLRbw7RRAPjXABAACLTdwxwItdoIt10IlN4AHLi30Mi1XcO0UYjQw6fSEP + thQBi33MD7YMA4kUh4t9yA+vVcCJDIeLTeSJFIFA69Ex0jtVHHwSi13cA10Ix0W4 + AAAAACtd0OsgMcA7RRh9DYt95A+2HAEBHIdA6+5CA03U68//RbgDddSLRdwrRdAB + 8InHi0W4O0UcfywxyTtNGH3hi0UMi33MAfAPtgQIKwSPi33kAwSPmYkEj408M/d9 + 2IgED0Hr1otFDCtF3ItNwAHwiUW4i10Uif4rXRwrddwDdeA52XwIA3UIK3Xg60qL + dQgx2yt13AH+A3XgO10YfSiLVeCLRbgB0ANVDA+2BBgPthQaKdCLVeQDBJqJBJqZ + 933YiAQeQ+vTi0XUQQFF4Ouji0XUQwFF4DtdFH0yMck7TRh97Yt9yItF4ANFDA+2 + FAiLBI+LfeQp0ItV4AMEj4kEj408Fpn3fdiIBA9B69CLRRj/RbwBRdwBRdDphP7/ + //9NrItltA+Fofz//9no3+l2PzHJMds7XRR9OotFGIt9CA+vwY1EBwMx/zt9EH0c + D7Yw2cBHVtoMJFrZXeTzDyx15InyiBADRRjr30MDTRDrxd3Y6wLd2I1l9DHAW15f + XcM= + )" + static x64 := " + (LTrim + VUFXQVZBVUFUV1ZTSIHsqAAAAEiNrCSAAAAARIutkAAAAIuFmAAAAESJxkiJVRhB + jVH/SYnPi42YAAAARInHQQ+v9Y1EAAErvZgAAABEiUUARIlN2IlFFEljxcdFtAMA + AABIY96LtZgAAABIiUUID6/TiV0ESIld4A+vy4udmAAAAIl9qPMPEI2gAAAAiVXQ + SI0UhRIAAABBD6/1/8OJTbBIiVXoSINl6PCJXdxBifaJdbxBjXD/SWPGQQ+v9UiJ + RZhIY8FIiUWQiXW4RInOK7WYAAAAiXWMSItF6EiJZcDoAAAAAEgpxEiLRehIieHo + AAAAAEgpxEiLRehIiWX46AAAAABIKcRIi0UYTYn6SIll8MdFEAAAAADHRdQAAAAA + SIlFyItF2DlF1A+NqgEAAESLTRAxwEWJyEQDTbhNY8lNAflBOcV+JUEPthQCSIt9 + +EUPthwBSItd8IkUhw+vVdxEiRyDiRSBSP/A69aLVRBFMclEO42YAAAAfA9Ii0WY + RTHbMdtNjSQC6ytMY9oxwE0B+0E5xX4NQQ+2HAMBHIFI/8Dr7kH/wUQB6uvGTANd + CP/DRQHoO52YAAAAi0W8Ro00AH82SItFyEuNPCNFMclJjTQDRTnNftRIi1X4Qg+2 + BA9CKwSKQgMEiZlCiQSJ930UQogEDkn/wevZi0UQSWP4SAN9GItd3E1j9kUx200B + /kQpwIlFrEiJfaCLdaiLRaxEAcA580GJ8XwRSGP4TWPAMdtMAf9MA0UY60tIi0Wg + S408Hk+NJBNFMclKjTQYRTnNfiFDD7YUDEIPtgQPKdBCAwSJmUKJBIn3fRRCiAQO + Sf/B69r/w0UB6EwDXQjrm0gDXQhB/8FEO00AfTRMjSQfSY00GEUx20U53X7jSItF + 8EMPthQcQosEmCnQQgMEmZlCiQSZ930UQogEHkn/w+vXi0UEAUUQSItF4P9F1EgB + RchJAcLpSv7//0yLVRhMiX3Ix0UQAAAAAMdF1AAAAACLRQA5RdQPja0BAABEi00Q + McBFichEA03QTWPJTANNGEE5xX4lQQ+2FAJIi3X4RQ+2HAFIi33wiRSGD69V3ESJ + HIeJFIFI/8Dr1otVEEUxyUQ7jZgAAAB8D0iLRZBFMdsx202NJALrLUxj2kwDXRgx + wEE5xX4NQQ+2HAMBHIFI/8Dr7kH/wQNVBOvFRANFBEwDXeD/wzudmAAAAItFsEaN + NAB/NkiLRchLjTwjRTHJSY00A0U5zX7TSItV+EIPtgQPQisEikIDBImZQokEifd9 + FEKIBA5J/8Hr2YtFEE1j9klj+EwDdRiLXdxFMdtEKcCJRaxJjQQ/SIlFoIt1jItF + rEQBwDnzQYnxfBFNY8BIY/gx20gDfRhNAfjrTEiLRaBLjTweT40kE0UxyUqNNBhF + Oc1+IUMPthQMQg+2BA8p0EIDBImZQokEifd9FEKIBA5J/8Hr2v/DRANFBEwDXeDr + mkgDXeBB/8FEO03YfTRMjSQfSY00GEUx20U53X7jSItF8EMPthQcQosEmCnQQgME + mZlCiQSZ930UQogEHkn/w+vXSItFCP9F1EQBbRBIAUXISQHC6Uf+////TbRIi2XA + D4Ui/P//8w8QBQAAAAAPLsF2TTHJRTHARDtF2H1Cicgx0kEPr8VImEgrRQhNjQwH + McBIA0UIO1UAfR1FD7ZUAQP/wvNBDyrC8w9ZwfNEDyzQRYhUAQPr2kH/wANNAOu4 + McBIjWUoW15fQVxBXUFeQV9dw5CQkJCQkJCQkJCQkJAAAIA/ + )" + width := Gdip_GetImageWidth(pBitmap) + height := Gdip_GetImageHeight(pBitmap) + clone := Gdip_CloneBitmapArea(pBitmap, 0, 0, width, height) + E1 := Gdip_LockBits(pBitmap, 0, 0, width, height, Stride1, Scan01, BitmapData1) + E2 := Gdip_LockBits(clone, 0, 0, width, height, Stride2, Scan02, BitmapData2) + + DllCall("crypt32\CryptStringToBinary", "str",(A_PtrSize == 8) ? x64 : x86, "uint",0, "uint",0x1, "ptr",0, "uint*",s, "ptr",0, "ptr",0) + p := DllCall("GlobalAlloc", "uint",0, "ptr",s, "ptr") + if (A_PtrSize == 8) + DllCall("VirtualProtect", "ptr",p, "ptr",s, "uint",0x40, "uint*",op) + DllCall("crypt32\CryptStringToBinary", "str",(A_PtrSize == 8) ? x64 : x86, "uint",0, "uint",0x1, "ptr",p, "uint*",s, "ptr",0, "ptr",0) + value := DllCall(p, "ptr",Scan01, "ptr",Scan02, "uint",width, "uint",height, "uint",4, "uint",radius, "float",opacity) + DllCall("GlobalFree", "ptr", p) + + Gdip_UnlockBits(pBitmap, BitmapData1) + Gdip_UnlockBits(clone, BitmapData2) + Gdip_DisposeImage(clone) + return value + } + + } + + class safe_bitmap { + + outer[p:=""] { + get { + static period := ".", _period := (A_AhkVersion < 2) ? period : "period" + if ((__outer := RegExReplace(this.__class, "^(.*)\..*$", "$1")) != this.__class) + Loop Parse, __outer, %_period% + outer := (A_Index=1) ? %A_LoopField% : outer[A_LoopField] + return IsObject(outer) ? ((p) ? outer[p] : outer) : ((p) ? %p% : "") + } + } + + __New(pBitmap) { + global pToken + if !(this.outer.Startup()) + if !(pToken) + if !(this.pToken := Gdip_Startup()) + throw Exception("Gdiplus failed to start. Please ensure you have gdiplus on your system.") + + this.pBitmap := pBitmap + this.w := this.width := Gdip_GetImageWidth(pBitmap) + this.h := this.height := Gdip_GetImageHeight(pBitmap) + this.size := this.width * this.height + } + + __Delete() { + Gdip_DisposeImage(this.pBitmap) + + global pToken + if (this.outer.pToken) + return this.outer.Shutdown() + if (pToken) + return + if (this.pToken) + return Gdip_Shutdown(this.pToken) + } + + Lock() { + + this.isLockBits := true + Gdip_LockBits(this.pBitmap, 0, 0, this.width, this.height, Stride, Scan0, BitmapData) + this.BitmapData := BitmapData + this.Stride := Stride + this.Scan0 := Scan0+0 + } + + __Get(x, y:="", color:=""){ + static pBitmap, Stride + + if x is integer + { + + if (y == "" && color == "" && (ARGB := NumGet(pBitmap, x, "uint"))) + return ARGB + + if (color == "" && (ARGB := NumGet(pBitmap, y*Stride + x, "uint"))) + return ARGB + + if (y == "" && color = "alpha" && (ARGB := NumGet(pBitmap, x, "uchar"))) + return ARGB + + if (y == "" && color = "red" && (ARGB := NumGet(pBitmap, x + 1, "uchar"))) + return ARGB + + if (y == "" && color = "green" && (ARGB := NumGet(pBitmap, x + 2, "uchar"))) + return ARGB + + if (y == "" && color = "blue" && (ARGB := NumGet(pBitmap, x + 3, "uchar"))) + return ARGB + + if (color = "alpha" && (ARGB := NumGet(pBitmap, y*Stride, "uchar"))) + return ARGB + + if (color = "red" && (ARGB := NumGet(pBitmap, y*Stride + x + 1, "uchar"))) + return ARGB + + if (color = "green" && (ARGB := NumGet(pBitmap, y*Stride + x + 2, "uchar"))) + return ARGB + + if (color = "blue" && (ARGB := NumGet(pBitmap, y*Stride + x + 3, "uchar"))) + return ARGB + + if !(pBitmap) + pBitmap := this.pBitmap + + if !(this.isLockBits) + throw Exception("idgaf!") ; SET STRIDE, then this.Stride! + } + } + + __Get2(x, y:="", color:="") { + ; Get Bitmap[x,y] returns pixel. + if (y >= 0) { + + if !(this.isLockBits) { + this.isLockBits := true + Gdip_LockBits(this.pBitmap, 0, 0, this.width, this.height, Stride, Scan0, BitmapData) + this.BitmapData := BitmapData + this.Stride := Stride + this.Scan0 := Scan0+0 + } + + return NumGet(this.Scan0, x*4 + y*this.Stride, "uint") + + if !(y ~= "^\d+$") { + y := x // this.height + x := mod(x, this.height) + } + + if !(color) + return NumGet(this.Scan0, x*4 + y*this.Stride, "uint") + + ARGB := NumGet(this.Scan0, terms.1*4 + terms.2*this.Stride, "uint") + + if !(terms.3) + return ARGB + if (terms.3 ~= "i)a(lpha)?") + return (ARGB & 0xFF000000) >> 24 + if (terms.3 ~= "i)r(ed)?") + return (ARGB & 0x00FF0000) >> 16 + if (terms.3 ~= "i)g(reen)?") + return (ARGB & 0x0000FF00) >> 8 + if (terms.3 ~= "i)b(lue)?") + return (ARGB & 0x000000FF) + } + + ; Get Bitmap[] or Bitmap.ptr returns pBitmap. + if (x == "" || x = "ptr") { + if (this.isLockBits) { + this.isLockBits := false + this.Stride := this.Scan0 := "" + Gdip_UnlockBits(this.pBitmap, this.BitmapData) + } + return this.pBitmap + } + } + + __Set(terms*) { + ARGB := terms.pop() + + ; Set Bitmap[x,y] to a pixel. + if (terms.1 ~= "^\d+$") { + if !(this.isLockBits) { + this.isLockBits := true + Gdip_LockBits(this.pBitmap, 0, 0, this.width, this.height, Stride, Scan0, BitmapData) + this.Stride := Stride + this.Scan0 := Scan0 + this.BitmapData := BitmapData + this.size := this.Stride * this.height + } + + if !(terms.3) + NumPut(ARGB, Scan0+0, terms.1*4 + terms.2*this.Stride, "uint") + if (terms.3 ~= "i)a(lpha)?") + NumPut(ARGB, Scan0+0, terms.1*4 + terms.2*this.Stride + 0, "uchar") + if (terms.3 ~= "i)r(ed)?") + NumPut(ARGB, Scan0+0, terms.1*4 + terms.2*this.Stride + 1, "uchar") + if (terms.3 ~= "i)g(reen)?") + NumPut(ARGB, Scan0+0, terms.1*4 + terms.2*this.Stride + 2, "uchar") + if (terms.3 ~= "i)b(lue)?") + NumPut(ARGB, Scan0+0, terms.1*4 + terms.2*this.Stride + 3, "uchar") + } + } + } + + class memory { + + __New(width, height){ + this.hbm := CreateDIBSection(this.width := width, this.height := height) + this.hdc := CreateCompatibleDC() + this.obm := SelectObject(this.hdc, this.hbm) + this.gfx := Gdip_GraphicsFromHDC(this.hdc) + return this + } + + __Delete(){ + Gdip_DeleteGraphics(this.gfx) + SelectObject(this.hdc, this.obm) + DeleteObject(this.hbm) + DeleteDC(this.hdc) + return + } + } + + class queue { + + layers := [] + fn := [] + x := [] + y := [] + w := [] + h := [] + xx := [] + yy := [] + mx := [] + my := [] + x_mouse := "" + y_mouse := "" + + New(function, x_mouse := "", y_mouse := ""){ + if (function == this.fn.2) + return + + if (this.fn.2 != "" && this.lacuna(2)) + return + + this.shift() + this.fn.2 := function + this.x_mouse := x_mouse + this.y_mouse := y_mouse + return true ; useful for allowing a new function to execute when x,y coordinates have remained the same. + } + + ; This function takes any number of inputs, from zero to 8. It will populate the inputs with their + ; last known values if omitted. In the case of w & h it will check for a xx & yy input. In the case of + ; xx & yy, it will check for a w & h input AND check w & h last known value. This means that if w, h, xx, yy + ; are omitted, the width and height will remain constant, and the right and bottom values will change. + Queue(ByRef x:="", ByRef y:="", ByRef w:="", ByRef h:="", ByRef xx:="", ByRef yy:="", ByRef mx:="", ByRef my:=""){ + ; Store the last found w & h to check if size has changed, forcing a redraw. + old_w := (this.w.2 != "") ? this.w.2 : this.w.1 + old_h := (this.h.2 != "") ? this.h.2 : this.h.1 + + ; x & y are independent and mandatory inputs. + if (x != "") + this.x.2 := x + else if (this.x.2 == "" && this.x.1 != "") + this.x.2 := this.x.1 + else if (this.x.2 == "") + throw Exception("x coordinate is a mandatory parameter.") + + if (y != "") + this.y.2 := y + else if (this.y.2 == "" && this.y.1 != "") + this.y.2 := this.y.1 + else if (this.y.2 == "") + throw Exception("y coordinate is a mandatory parameter.") + + ; w & h are dependent on this.x.2 and this.y.2 + if (w != "") + this.w.2 := w + else if (xx != "") + this.w.2 := xx - this.x.2 + else if (this.w.2 == "" && this.w.1 != "") + this.w.2 := this.w.1 + + if (h != "") + this.h.2 := h + else if (yy != "") + this.h.2 := yy - this.y.2 + else if (this.h.2 == "" && this.h.1 != "") + this.h.2 := this.h.1 + + ; xx & yy are dependent on this.x.2, this.y.2, this.w.2, and this.h.2 + if (xx != "") + this.xx.2 := xx + else if (x != "") + this.xx.2 := this.w.2 + x + else if (w != "") + this.xx.2 := this.x.2 + w + else if (this.xx.2 == "" && this.xx.1 != "") + this.xx.2 := this.xx.1 + + if (yy != "") + this.yy.2 := yy + else if (y != "") + this.yy.2 := this.h.2 + y + else if (h != "") + this.yy.2 := this.y.2 + h + else if (this.yy.2 == "" && this.y.1 != "") + this.yy.2 := this.yy.1 + + ; mx & my are independent variables. + if (mx != "") + this.mx.2 := mx + else if (this.mx.2 == "" && this.mx.1 != "") + this.mx.2 := this.mx.1 + + if (my != "") + this.my.2 := my + else if (this.my.2 == "" && this.my.1 != "") + this.my.2 := this.my.1 + + ; Internal checking - can be commented out + if (this.xx.2 - this.x.2 != this.w.2) + throw Exception("Inconsistent width or x2.") + + if (this.yy.2 - this.y.2 != this.h.2) + throw Exception("Inconsistent height or y2.") + + ; Detect if width or height has changed, requiring the image to be redrawn. + this.identical := (this.w.2 == old_w) && (this.h.2 == old_h) + + ; Return coordinate values by reference. + x := this.x.2, w := this.w.2, xx := this.xx.2, mx := this.mx.2 + y := this.y.2, h := this.h.2, yy := this.yy.2, my := this.my.2 + } + + Shift(){ + this.fn.RemoveAt(1) + this.x.RemoveAt(1) + this.y.RemoveAt(1) + this.w.RemoveAt(1) + this.h.RemoveAt(1) + this.xx.RemoveAt(1) + this.yy.RemoveAt(1) + this.mx.RemoveAt(1) + this.my.RemoveAt(1) + } + + Lacuna(n := 2){ + return (this.x[n] == "" || this.y[n] == "" || this.w[n] == "" || this.h[n] == "" + || this.xx[n] == "" || this.yy[n] == "") + } + + Debug(){ + debug := "function: " this.fn.2 + . "`nx: " this.x.2 "`ty: " this.y.2 + . "`nw: " this.w.2 "`th: " this.h.2 + . "`nx2: " this.xx.2 "`ty2: " this.yy.2 + . "`nmx: " this.mx.2 "`tmy: " this.my.2 + + . "`nfunction: " this.fn.1 + . "`nx: " this.x.1 "`ty: " this.y.1 + . "`nw: " this.w.1 "`th: " this.h.1 + . "`nx2: " this.xx.1 "`ty2: " this.yy.1 + . "`nmx: " this.mx.1 "`tmy: " this.my.1 + _debug := (A_AhkVersion < 2) ? debug : "debug" + Tooltip %_debug% + } + } + + class renderer { + + ; IO - Capture input and internalize environmental data. + IO(terms*) { + static A_Frequency, f := DllCall("QueryPerformanceFrequency", "int64*",A_Frequency) + DllCall("QueryPerformanceCounter", "int64*",A_PreciseTime) + + this.PreciseTime := A_PreciseTime + this.TickCount := A_TickCount + this.Frequency := A_Frequency + this.ScreenWidth := A_ScreenWidth + this.ScreenHeight := A_ScreenHeight + this.IsAdmin := A_IsAdmin + return this.arg := terms + } + + ; Duality #1 - Safe wrapper for the GDI+ library during object instantiation. + __New(terms*) { + this.IO(terms*) + + global pToken + if !(this.outer.Startup()) + if !(pToken) + if !(this.pToken := Gdip_Startup()) + throw Exception("Gdiplus failed to start. Please ensure you have gdiplus on your system.") + + return this.CreateWindow(terms*) + } + + ; Duality #1 - Safe wrapper for the GDI+ library during object garbage collection. + __Delete() { + if (this.hwnd) + this.DestroyWindow() + + global pToken + if (this.outer.pToken) + return this.outer.Shutdown() + if (pToken) + return + if (this.pToken) + return Gdip_Shutdown(this.pToken) + } + + ; Duality #2 - Creates a window. + CreateWindow(title := "", window := "", activate := "") { + ; Retrieve original arguments upon window creation. + title := (title != "") ? title : this.arg.1 + window := (window != "") ? window : this.arg.2 + activate := (activate != "") ? activate : this.arg.3 + + ; Name the window by its inherited class. (Note: A_ThisFunc won't work.) + title := (title != "") ? title : RegExReplace(this.__class, "(.*\.)*(.*)$", "$2") + + ; Tokenize window styles. + window := RegExReplace(window, "\s+", " ") + window := StrSplit(window, " ") + for i, token in window { + ;if (token ~= "i)") + } + + ;window := (window != "") ? window : " +AlwaysOnTop -Caption +ToolWindow" + ;window .= " +LastFound -DPIScale +E0x80000 +hwndhwnd" + + ; Window Styles - https://docs.microsoft.com/en-us/windows/win32/winmsg/window-styles + ; Extended Window Styles - https://docs.microsoft.com/en-us/windows/win32/winmsg/extended-window-styles + + WS_OVERLAPPED := 0x0 + WS_TILED := 0x0 + WS_TABSTOP := 0x10000 + WS_MAXIMIZEBOX := 0x10000 + WS_MINIMIZEBOX := 0x20000 + WS_GROUP := 0x20000 + WS_SIZEBOX := 0x40000 + WS_THICKFRAME := 0x40000 + WS_SYSMENU := 0x80000 + WS_HSCROLL := 0x100000 + WS_VSCROLL := 0x200000 + WS_DLGFRAME := 0x400000 + WS_BORDER := 0x800000 + WS_MAXIMIZE := 0x1000000 + WS_CLIPCHILDREN := 0x2000000 + WS_CLIPSIBLINGS := 0x4000000 + WS_DISABLED := 0x8000000 + WS_VISIBLE := 0x10000000 + WS_ICONIC := 0x20000000 + WS_MINIMIZE := 0x20000000 + WS_CHILD := 0x40000000 + WS_CHILDWINDOW := 0x40000000 + WS_POPUP := 0x80000000 + WS_CAPTION := 0xC00000 + WS_OVERLAPPEDWINDOW := 0xCF0000 + WS_TILEDWINDOW := 0xCF0000 + WS_POPUPWINDOW := 0x80880000 + + WS_EX_LEFT := 0x0 + WS_EX_LTRREADING := 0x0 + WS_EX_RIGHTSCROLLBAR := 0x0 + WS_EX_DLGMODALFRAME := 0x1 + WS_EX_NOPARENTNOTIFY := 0x4 + WS_EX_ALWAYSONTOP := 0x4 ; custom + WS_EX_TOPMOST := 0x8 + WS_EX_ACCEPTFILES := 0x10 + WS_EX_TRANSPARENT := 0x20 + WS_EX_MDICHILD := 0x40 + WS_EX_TOOLWINDOW := 0x80 + WS_EX_WINDOWEDGE := 0x100 + WS_EX_CLIENTEDGE := 0x200 + WS_EX_CONTEXTHELP := 0x400 + WS_EX_RIGHT := 0x1000 + WS_EX_RTLREADING := 0x2000 + WS_EX_LEFTSCROLLBAR := 0x4000 + WS_EX_CONTROLPARENT := 0x10000 + WS_EX_STATICEDGE := 0x20000 + WS_EX_APPWINDOW := 0x40000 + WS_EX_LAYERED := 0x80000 + WS_EX_NOINHERITLAYOUT := 0x100000 + WS_EX_NOREDIRECTIONBITMAP := 0x200000 + WS_EX_LAYOUTRTL := 0x400000 + WS_EX_COMPOSITED := 0x2000000 + WS_EX_NOACTIVATE := 0x8000000 + WS_EX_OVERLAPPEDWINDOW := 0x300 + WS_EX_PALETTEWINDOW := 0x188 + + vWinStyle := WS_SYSMENU ; start off hidden with WS_VISIBLE off + vWinExStyle := WS_EX_TOPMOST | WS_EX_TOOLWINDOW | WS_EX_LAYERED + + ; The difference between the screen and the bitmap is that the screen defines the viewable area + ; while the bitmap defines the current size of the memory buffer. In practice the bitmap could be + ; a small part of the screen. Thus the DrawRaw() operations require the viewport width and height + ; calculated by 0.01*ScreenWidth and 0.01*ScreenHeight. + ; NOTE: DrawRaw() does not accept offsets, which are defined by BitmapLeft and BitmapTop. + this.BitmapLeft := 0 + this.BitmapTop := 0 + this.BitmapWidth := this.ScreenWidth + this.BitmapHeight := this.ScreenHeight + + this.hwnd := DllCall("CreateWindowEx" + , "uint", vWinExStyle ; dwExStyle + , "str", "AutoHotkeyGraphics" ; lpClassName + , "str", this.title ; lpWindowName + , "uint", vWinStyle ; dwStyle + , "int", this.BitmapLeft ; X + , "int", this.BitmapTop ; Y + , "int", this.BitmapWidth ; nWidth + , "int", this.BitmapHeight ; nHeight + , "ptr", 0 ; hWndParent + , "ptr", 0 ; hMenu + , "ptr", 0 ; hInstance + , "ptr", 0 ; lpParam + , "ptr") + + DllCall("ShowWindow", "ptr",this.hwnd, "int",(this.activateOnAdmin && !this.isDrawable()) ? 1 : 4) + + return this.LoadMemory() + } + + ; Duality #2 - Destroys a window. + DestroyWindow() { + if (this.hdc) + this.FreeMemory() + DllCall("DestroyWindow", "ptr",this.hwnd) + this.hwnd := "" + return this + } + + ; Duality #3 - Allocates the memory buffer. + LoadMemory() { + ; Creates a memory DC compatible with the application's current screen. + this.hdc := CreateCompatibleDC() + + ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader + VarSetCapacity(bi, 40, 0) ; sizeof(bi) = 40 + , NumPut( 40, bi, 0, "uint") ; Size + , NumPut( this.BitmapWidth, bi, 4, "uint") ; Width + , NumPut( -this.BitmapHeight, bi, 8, "int") ; Height - Negative so (0, 0) is top-left. + , NumPut( 1, bi, 12, "ushort") ; Planes + , NumPut( 32, bi, 14, "ushort") ; BitCount / BitsPerPixel + + ; Creates a Device Independent Bitmap giving us a pointer to the pixels. + this.hbm := DllCall("CreateDIBSection", "ptr",this.hdc, "ptr",&bi, "uint",0, "ptr*",pBits, "ptr",0, "uint",0, "ptr") + this.obm := SelectObject(this.hdc, this.hbm) + this.gfx := Gdip_GraphicsFromHDC(this.hdc) + + ; IMPORTANT: DIB pixels are pre-multiplied ARGB because that's how they're displayed on the screen. + ; enum PixelFormat - https://svn.eiffel.com/eiffelstudio/trunk/Src/library/wel/gdi/gdiplus/wel_gdip_pixel_format.e + this.pBits := pBits + this.pixelFormat := 0xE200B ; Format32bppPArgb + this.stride := 4 * this.BitmapWidth + this.size := this.stride * this.BitmapHeight + this.layers := {} + + ;global PreciseTime + ;DllCall("QueryPerformanceCounter", "int64*",A_PreciseTime) + ;Tooltip % PreciseTime := (A_PreciseTime - this.PreciseTime) / this.Frequency + + return this.Interop() + } + + ; Duality #3 - Frees the memory buffer. + FreeMemory() { + Gdip_DeleteGraphics(this.gfx) + SelectObject(this.hdc, this.obm) + DeleteObject(this.hbm) + DeleteDC(this.hdc) + this.gfx := this.obm := this.pBits := this.hbm := this.hdc := "" + return this + } + + Interop(){ + ; Use this.outer to dynamically search for DrawRaw() and Render() functions + ; of other classes mapping them to this.Draw%classname%. So DrawImage etc. + return this + } + + ; BROKEN - create a fixed size flag. + UpdateMemory(BitmapWidth := 0, BitmapHeight := 0) { + BitmapWidth := (BitmapWidth) ? BitmapWidth : A_ScreenWidth + BitmapHeight := (BitmapHeight) ? BitmapHeight : A_ScreenHeight + + if (BitmapWidth == this.BitmapWidth && BitmapHeight == this.BitmapHeight) + return this + + this.BitmapWidth := BitmapWidth + this.BitmapHeight := BitmapHeight + return this.FreeMemory().LoadMemory().Recover() + } + + CompressMemory() { + ; Create. + hdc := CreateCompatibleDC(this.hdc) + hbm := CreateDIBSection(this.w, -this.h, hdc, 32, pBits) + obm := SelectObject(hdc, hbm) + + ; Copy. + BitBlt(hdc, 0, 0, this.w, this.h, this.hdc, this.x, this.y) + + ; Delete. + Gdip_DeleteGraphics(this.gfx) + SelectObject(this.hdc, this.obm) + DeleteObject(this.hbm) + DeleteDC(this.hdc) + + ; Replace. + this.hdc := hdc + this.hbm := hbm + this.obm := obm + this.gfx := Gdip_GraphicsFromHDC(hdc) + this.pBits := pBits + + ; Update + this.BitmapLeft := this.x + this.BitmapTop := this.y + this.BitmapWidth := this.w + this.BitmapHeight := this.h + + this.w -= this.x + this.h -= this.y + this.x := 0 + this.y := 0 + } + + DumpMemory() { + VarSetCapacity(pixels, this.size) + DllCall("RtlMoveMemory", "ptr",&pixels, "ptr",this.pBits, "uptr",this.size) + return pixels + } + + DebugMemory() { + loop { + pixel := Format("0x{:08x}", NumGet(this.pBits, 4*(A_Index-1), "uint")) + _pixel := (A_AhkVersion < 2) ? pixel : "pixel" + MsgBox %_pixel% + } + } + + BitmapFromBits() { + DllCall("gdiplus\GdipCreateBitmapFromScan0", "int",this.BitmapWidth, "int",this.ScreenHeight + , "int",this.stride, "uint",this.pixelFormat, "ptr",this.pBits, "ptr*",pBitmap) + return pBitmap + } + + NewBitmap() { + DllCall("gdiplus\GdipCreateBitmapFromScan0", "int",this.BitmapWidth, "int",this.BitmapHeight + , "int",this.stride, "uint",this.pixelFormat, "ptr",this.pBits, "ptr*",pBitmap) + DllCall("gdiplus\GdipCloneBitmapAreaI", "int",0, "int",0, "int",this.BitmapWidth, "int",this.BitmapHeight + , "uint",0x26200a, "ptr",pBitmap, "ptr*",pBitmapDest) + Gdip_DisposeImage(pBitmap) + return pBitmapDest + } + + BitmapFromScan0() { + this.SetCapacity("pixels", this.size) + this.Scan0 := this.GetAddress("pixels") + DllCall("RtlMoveMemory", "ptr",this.Scan0, "ptr",this.pBits, "uptr",this.size) + DllCall("gdiplus\GdipCreateBitmapFromScan0", "int",this.BitmapWidth, "int",this.BitmapHeight + , "int",this.stride, "uint",this.pixelFormat, "ptr",this.Scan0, "ptr*",pBitmap) + return pBitmap + } + + Recover() { + layers := this.layers.maxIndex() + _layers := (A_AhkVersion < 2) ? layers : "layers" + loop %_layers% + this.Draw(this.layers[A_Index].1, this.layers[A_Index].2, this.layers[A_Index].3) + return this + } + + Draw(data := "", styles*) { + this.Clear() + for i, style in styles + if (style != "") { + okay := true + break + } + if !(okay) + styles := this.styles + this.data := data + this.styles := styles + this.layers.push([data, styles*]) + o := this.DrawRaw(this.gfx, this.ScreenWidth, this.ScreenHeight, data, styles*) + this.t := o.0 + this.x := (this.x == "") ? o.1 : (this.x < o.1) ? this.x : o.1 + this.y := (this.y == "") ? o.2 : (this.y < o.2) ? this.y : o.2 + this.w := (this.w == "") ? o.1 + o.3 - this.x : (this.x + this.w > o.1 + o.3) ? this.w : o.1 + o.3 - this.x + this.h := (this.h == "") ? o.2 + o.4 - this.y : (this.y + this.h > o.2 + o.4) ? this.h : o.2 + o.4 - this.y + return this + } + + Clear() { + if (this.final) { + DllCall("QueryPerformanceCounter", "int64*",A_PreciseTime) + this.PreciseTime := A_PreciseTime + this.TickCount := A_TickCount + this.layers := {} + this.x := this.y := this.w := this.h := "" ; not 0! BROKEN + Gdip_GraphicsClear(this.gfx) + this.final := "" ; BROKEN - should be right when final changes. + } + + return this.UpdateMemory() + } + + CRC32() { + return Format("0x{:08x}", DllCall("ntdll.dll\RtlComputeCrc32", "uint",0, "ptr",this.pBits, "uint",this.size, "uint")) + } + + Finalize() { + this.final := 1 + VarSetCapacity(puuid, 16, 0) + if !(DllCall("rpcrt4.dll\UuidCreate", "ptr", &puuid)) + if !(DllCall("rpcrt4.dll\UuidToString", "ptr", &puuid, "uint*", suuid)) + this.final := StrGet(suuid), DllCall("rpcrt4.dll\RpcStringFree", "uint*", suuid) + return this + } + + Output(terms*) { + this.Draw(terms*) + + if (!this.locked) + UpdateLayeredWindow(this.hwnd, this.hdc, this.BitmapLeft, this.BitmapTop, this.BitmapWidth, this.BitmapHeight) + + this.Finalize() + + if (this.t > 0) { + blank := ObjBindMethod(this, "blank") + time := -1 * this.t + _blank := (A_AhkVersion < 2) ? blank : "blank" + _time := (A_AhkVersion < 2) ? time : "time" + SetTimer %_blank%, %_time% + } + + return this + } + + Blank() { + Gdip_GraphicsClear(this.gfx) + UpdateLayeredWindow(this.hwnd, this.hdc, this.BitmapLeft, this.BitmapTop, this.BitmapWidth, this.BitmapHeight) + return this + } + + Wait(time) { + wait := time - (A_TickCount - this.TickCount) + _wait := (A_AhkVersion < 2) ? wait : "wait" + Sleep %_wait% + return this + } + + Bitmap(x := "", y := "", w := "", h := "") { + x := (x != "") ? x : this.x + y := (y != "") ? y : this.y + w := (w != "") ? w : this.w + h := (h != "") ? h : this.h + + pBitmap := Gdip_CreateBitmap(this.BitmapWidth, this.BitmapHeight) + pGraphics := Gdip_GraphicsFromImage(pBitmap) + layers := this.layers.maxIndex() + _layers := (A_AhkVersion < 2) ? layers : "layers" + loop %_layers% + this.DrawRaw(pGraphics, this.BitmapWidth, this.BitmapHeight, this.layers[A_Index].1, this.layers[A_Index].2, this.layers[A_Index].3) + Gdip_DeleteGraphics(pGraphics) + pBitmapCopy := Gdip_CloneBitmapArea(pBitmap, x, y, w, h) + Gdip_DisposeImage(pBitmap) + return pBitmapCopy ; Please dispose of this image responsibly. + } + + Save(filename := "", quality := 90) { + filename := (filename ~= "i)\.(bmp|dib|rle|jpg|jpeg|jpe|jfif|gif|tif|tiff|png)$") ? filename + : (filename != "") ? filename ".png" : this.title ".png" + pBitmap := this.Bitmap() + Gdip_SaveBitmapToFile(pBitmap, filename, quality) + Gdip_DisposeImage(pBitmap) + return this + } + + ; 3) Just takes a picture of the screen! + Screenshot(filename := "", quality := 90) { + filename := (filename ~= "i)\.(bmp|dib|rle|jpg|jpeg|jpe|jfif|gif|tif|tiff|png)$") ? filename + : (filename != "") ? filename ".png" : this.title ".png" + pBitmap := Gdip_BitmapFromScreen(this.x "|" this.y "|" this.w "|" this.h) + Gdip_SaveBitmapToFile(pBitmap, filename, quality) + Gdip_DisposeImage(pBitmap) + return this + } + + 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: + _win := (A_AhkVersion < 2) ? win : "win" + 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 + } + + Show(i := 8) { + DllCall("ShowWindow", "ptr",this.hwnd, "int",i) + return this + } + + Hide() { + DllCall("ShowWindow", "ptr",this.hwnd, "int",0) + return this + } + + Activate() { + DllCall("ShowWindow", "ptr",this.hwnd, "int",1) + return this + } + + isVisible() { + return DllCall("IsWindowVisible", "ptr",this.hwnd) + } + + ToggleVisible() { + return (this.isVisible()) ? this.Hide() : this.Show() + } + /* + AlwaysOnTop(s := -1) { + _dhw := A_DetectHiddenWindows + DetectHiddenWindows On + _s := (A_AhkVersion < 2) ? s : "s" + hwnd := "ahk_id" this.hwnd + _hwnd := (A_AhkVersion < 2) ? hwnd : "hwnd" + WinSet AlwaysOnTop, %_s%, %_hwnd% + DetectHiddenWindows %_dhw% + return this + } + + Bottom() { + _dhw := A_DetectHiddenWindows + DetectHiddenWindows On + hwnd := "ahk_id" this.hwnd + _hwnd := (A_AhkVersion < 2) ? hwnd : "hwnd" + WinSet Bottom,, %_hwnd% + DetectHiddenWindows %_dhw% + return this + } + + ClickThrough(s := -1) { + s := (s = -1) ? "^" : (s = 0) ? "-" : "+" + s .= "0x20" + _s := (A_AhkVersion < 2) ? s : "s" + _dhw := A_DetectHiddenWindows + DetectHiddenWindows On + hwnd := "ahk_id" this.hwnd + _hwnd := (A_AhkVersion < 2) ? hwnd : "hwnd" + WinSet ExStyle, %_s%, %_hwnd% + DetectHiddenWindows %_dhw% + return this + } + */ + Rect() { + x1 := this.x1(), y1 := this.y1(), x2 := this.x2(), y2 := this.y2() + return (x2 > x1 && y2 > y1) ? [x1, y1, x2, y2] : "" + } + + x1() { + return this.x + } + + y1() { + return this.y + } + + x2() { + return this.x + this.w + } + + y2() { + return this.y + this.h + } + + width() { + return this.w + } + + height() { + return this.h + } + } + + class ImageRenderer { + static extends := "renderer" + + _extends := this.__extends() + __extends(subbundle := "") { + object := this.outer[this.extends] + bundle := ((object.haskey("__extends")) ? object.__extends(true) : object) + (subbundle) ? (this.base := bundle) : (this.base.base := bundle) + return (subbundle) ? this : "" + } + + outer[p:=""] { + get { + static period := ".", _period := (A_AhkVersion < 2) ? period : "period" + if ((__outer := RegExReplace(this.__class, "^(.*)\..*$", "$1")) != this.__class) + Loop Parse, __outer, %_period% + outer := (A_Index=1) ? %A_LoopField% : outer[A_LoopField] + return IsObject(outer) ? ((p) ? outer[p] : outer) : ((p) ? %p% : "") + } + } + + Render(terms*) { + return (this.hwnd) ? this.Output(terms*) : (new this).Output(terms*) + } + + ; Types of input accepted + ; Objects: Rectangle Array (Screenshot) + ; Strings: File, URL, Window Title (ahk_class...), base64 + ; Numbers: hwnd, GDI Bitmap, GDI HBitmap + DrawRaw(pGraphics, ScreenWidth, ScreenHeight, image := "", style := "", polygons := "") { + ; Preprocess image to a native GDI bitmap. + if (image != "") { + if !(type := this.DontVerifyImageType(image)) + type := this.ImageType(image) + pBitmap := this.toBitmap(type, image) + } + + ; Remove excess whitespace for proper RegEx detection. + style := !IsObject(style) ? RegExReplace(style, "\s+", " ") : style + + ; RegEx help? https://regex101.com/r/xLzZzO/2 + static q1 := "(?i)^.*?\b(?\([^()]*\)|[^()]*)*\))(:\s*)?\(?(?(?<=\()([\s:#%_a-z\-\.\d]+|\([\s:#%_a-z\-\.\d]*\))*(?=\))|[#%_a-z\-\.\d]+).*$" + + ; Extract styles to variables. + if IsObject(style) { + t := (style.time != "") ? style.time : style.t + a := (style.anchor != "") ? style.anchor : style.a + x := (style.left != "") ? style.left : style.x + y := (style.top != "") ? style.top : style.y + w := (style.width != "") ? style.width : style.w + h := (style.height != "") ? style.height : style.h + m := (style.margin != "") ? style.margin : style.m + s := (style.scale != "") ? style.scale : style.s + c := (style.color != "") ? style.color : style.c + q := (style.quality != "") ? style.quality : (style.q) ? style.q : style.InterpolationMode + } else { + t := ((___ := RegExReplace(style, q1 "(t(ime)?)" q2, "${value}")) != style) ? ___ : "" + a := ((___ := RegExReplace(style, q1 "(a(nchor)?)" q2, "${value}")) != style) ? ___ : "" + x := ((___ := RegExReplace(style, q1 "(x|left)" q2, "${value}")) != style) ? ___ : "" + y := ((___ := RegExReplace(style, q1 "(y|top)" q2, "${value}")) != style) ? ___ : "" + w := ((___ := RegExReplace(style, q1 "(w(idth)?)" q2, "${value}")) != style) ? ___ : "" + h := ((___ := RegExReplace(style, q1 "(h(eight)?)" q2, "${value}")) != style) ? ___ : "" + m := ((___ := RegExReplace(style, q1 "(m(argin)?)" q2, "${value}")) != style) ? ___ : "" + s := ((___ := RegExReplace(style, q1 "(s(cale)?)" q2, "${value}")) != style) ? ___ : "" + c := ((___ := RegExReplace(style, q1 "(c(olor)?)" q2, "${value}")) != style) ? ___ : "" + q := ((___ := RegExReplace(style, q1 "(q(uality)?)" q2, "${value}")) != style) ? ___ : "" + } + + ; Extract the time variable and save it for a later when we Render() everything. + static times := "(?i)^\s*(\d+)\s*(ms|mil(li(second)?)?|s(ec(ond)?)?|m(in(ute)?)?|h(our)?|d(ay)?)?s?\s*$" + t := ( t ~= times) ? RegExReplace( t, "\s", "") : 0 ; Default time is zero. + t := ((___ := RegExReplace( t, "i)(\d+)(ms|mil(li(second)?)?)s?$", "$1")) != t) ? ___ * 1 : t + t := ((___ := RegExReplace( t, "i)(\d+)s(ec(ond)?)?s?$" , "$1")) != t) ? ___ * 1000 : t + t := ((___ := RegExReplace( t, "i)(\d+)m(in(ute)?)?s?$" , "$1")) != t) ? ___ * 60000 : t + t := ((___ := RegExReplace( t, "i)(\d+)h(our)?s?$" , "$1")) != t) ? ___ * 3600000 : t + t := ((___ := RegExReplace( t, "i)(\d+)d(ay)?s?$" , "$1")) != t) ? ___ * 86400000 : t + + ; These are the type checkers. + static valid := "(?i)^\s*(\-?(?:(?:\d+(?:\.\d*)?)|(?:\.\d+)))\s*(%|pt|px|vh|vmin|vw)?\s*$" + static valid_positive := "(?i)^\s*((?:(?:\d+(?:\.\d*)?)|(?:\.\d+)))\s*(%|pt|px|vh|vmin|vw)?\s*$" + + ; Define viewport width and height. This is the visible screen area. + vw := 0.01 * ScreenWidth ; 1% of viewport width. + vh := 0.01 * ScreenHeight ; 1% of viewport height. + vmin := (vw < vh) ? vw : vh ; 1vw or 1vh, whichever is smaller. + vr := ScreenWidth / ScreenHeight ; Aspect ratio of the viewport. + + ; Get original image width and height. + width := Gdip_GetImageWidth(pBitmap) + height := Gdip_GetImageHeight(pBitmap) + minimum := (width < height) ? width : height + aspect := width / height + + ; Get width and height. + w := ( w ~= valid_positive) ? RegExReplace( w, "\s", "") : "" + w := ( w ~= "i)(pt|px)$") ? SubStr( w, 1, -2) : w + w := ( w ~= "i)vw$") ? RegExReplace( w, "i)vw$", "") * vw : w + w := ( w ~= "i)vh$") ? RegExReplace( w, "i)vh$", "") * vh : w + w := ( w ~= "i)vmin$") ? RegExReplace( w, "i)vmin$", "") * vmin : w + w := ( w ~= "%$") ? RegExReplace( w, "%$", "") * 0.01 * width : w + + h := ( h ~= valid_positive) ? RegExReplace( h, "\s", "") : "" + h := ( h ~= "i)(pt|px)$") ? SubStr( h, 1, -2) : h + h := ( h ~= "i)vw$") ? RegExReplace( h, "i)vw$", "") * vw : h + h := ( h ~= "i)vh$") ? RegExReplace( h, "i)vh$", "") * vh : h + h := ( h ~= "i)vmin$") ? RegExReplace( h, "i)vmin$", "") * vmin : h + h := ( h ~= "%$") ? RegExReplace( h, "%$", "") * 0.01 * height : h + + ; Default width and height. + if (w == "" && h == "") + w := width, h := height, wh_unset := true + if (w == "") + w := h * aspect + if (h == "") + h := w / aspect + + ; If scale is "fill" scale the image until there are no empty spaces but two sides of the image are cut off. + ; If scale is "fit" scale the image so that the greatest edge will fit with empty borders along one edge. + ; If scale is "harmonic" automatically downscale by the harmonic series. Ex: 50%, 33%, 25%, 20%... + if (s = "auto" || s = "fill" || s = "fit" || s = "harmonic" || s = "limit") { + if (wh_unset == true) + w := ScreenWidth, h := ScreenHeight + s := (s = "auto" || s = "limit") + ? ((aspect > w / h) ? ((width > w) ? w / width : 1) : ((height > h) ? h / height : 1)) : s + s := (s = "fill") ? ((aspect < w / h) ? w / width : h / height) : s + s := (s = "fit") ? ((aspect > w / h) ? w / width : h / height) : s + s := (s = "harmonic") ? ((aspect > w / h) ? 1 / (width // w + 1) : 1 / (height // h + 1)) : s + w := width ; width and height given were maximum values, not actual values. + h := height ; Therefore restore the width and height to the image width and height. + } + + s := ( s ~= valid) ? RegExReplace( s, "\s", "") : "" + s := ( s ~= "i)(pt|px)$") ? SubStr( s, 1, -2) : s + s := ( s ~= "i)vw$") ? RegExReplace( s, "i)vw$", "") * vw / width : s + s := ( s ~= "i)vh$") ? RegExReplace( s, "i)vh$", "") * vh / height: s + s := ( s ~= "i)vmin$") ? RegExReplace( s, "i)vmin$", "") * vmin / minimum : s + s := ( s ~= "%$") ? RegExReplace( s, "%$", "") * 0.01 : s + + ; If scale is negative automatically scale by a geometric series constant. + ; Example: If scale is -0.5, then downscale by 50%, 25%, 12.5%, 6.25%... + ; What the equation is asking is how many powers of -1/s can we fit in width/w? + ; Then we use floor division and add 1 to ensure that we never exceed the bounds. + ; While this is only designed to handle negative scales from 0 to -1.0, + ; it works for negative numbers higher than -1.0. In this case, the 0 to -1 bounded + ; are the left adjoint, meaning they never surpass the w and h. Higher negative Numbers + ; are the right adjoint, meaning they never surpass w*-s and h*-s. Weird, huh. + ; To clarify: Left adjoint: w*-s to w, h*-s to h. Right adjoint: w to w*-s, h to h*-s + ; LaTex: \frac{1}{\frac{-1}{s}^{Floor(\frac{log(x)}{log(\frac{-1}{s})}) + 1}} + ; Vertical asymptote at s := -1, which resolves to the empty string "". + if (s < 0 && s != "") { + if (wh_unset == true) + w := ScreenWidth, h := ScreenHeight + s := (s < 0) ? ((aspect > w / h) + ? (-s) ** ((log(width/w) // log(-1/s)) + 1) : (-s) ** ((log(height/h) // log(-1/s)) + 1)) : s + w := width ; width and height given were maximum values, not actual values. + h := height ; Therefore restore the width and height to the image width and height. + } + + ; Default scale. + if (s == "") { + s := (x == "" && y == "" && wh_unset == true) ; shrink image if x,y,w,h,s are all unset. + ? ((aspect > vr) ; determine whether width or height exceeds screen. + ? ((width > ScreenWidth) ? ScreenWidth / width : 1) ; scale will downscale image by its width. + : ((height > ScreenHeight) ? ScreenHeight / height : 1)) ; scale will downscale image by its height. + : 1 ; Default scale is 1.00. + } + + ; Scale width and height. + w := w * s + h := h * s + + ; Get anchor. This is where the origin of the image is located. + a := RegExReplace( a, "\s", "") + a := (a ~= "i)top" && a ~= "i)left") ? 1 : (a ~= "i)top" && a ~= "i)cent(er|re)") ? 2 + : (a ~= "i)top" && a ~= "i)right") ? 3 : (a ~= "i)cent(er|re)" && a ~= "i)left") ? 4 + : (a ~= "i)cent(er|re)" && a ~= "i)right") ? 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 ~= "i)top") ? 2 : (a ~= "i)left") ? 4 : (a ~= "i)right") ? 6 : (a ~= "i)bottom") ? 8 + : (a ~= "i)cent(er|re)") ? 5 : (a ~= "^[1-9]$") ? a : 1 ; Default anchor is top-left. + + ; The anchor can be implied and overwritten by x and y (left, center, right, top, bottom). + a := ( x ~= "i)left") ? 1+((( a-1)//3)*3) : ( x ~= "i)cent(er|re)") ? 2+((( a-1)//3)*3) : ( x ~= "i)right") ? 3+((( a-1)//3)*3) : a + a := ( y ~= "i)top") ? 1+(mod( a-1,3)) : ( y ~= "i)cent(er|re)") ? 4+(mod( a-1,3)) : ( y ~= "i)bottom") ? 7+(mod( a-1,3)) : a + + ; Convert English words to numbers. Don't mess with these values any further. + x := ( x ~= "i)left") ? 0 : (x ~= "i)cent(er|re)") ? 0.5*ScreenWidth : (x ~= "i)right") ? ScreenWidth : x + y := ( y ~= "i)top") ? 0 : (y ~= "i)cent(er|re)") ? 0.5*ScreenHeight : (y ~= "i)bottom") ? ScreenHeight : y + + ; Get x and y. + x := ( x ~= valid) ? RegExReplace( x, "\s", "") : "" + x := ( x ~= "i)(pt|px)$") ? SubStr( x, 1, -2) : x + x := ( x ~= "i)(%|vw)$") ? RegExReplace( x, "i)(%|vw)$", "") * vw : x + x := ( x ~= "i)vh$") ? RegExReplace( x, "i)vh$", "") * vh : x + x := ( x ~= "i)vmin$") ? RegExReplace( x, "i)vmin$", "") * vmin : x + + y := ( y ~= valid) ? RegExReplace( y, "\s", "") : "" + y := ( y ~= "i)(pt|px)$") ? SubStr( y, 1, -2) : y + y := ( y ~= "i)vw$") ? RegExReplace( y, "i)vw$", "") * vw : y + y := ( y ~= "i)(%|vh)$") ? RegExReplace( y, "i)(%|vh)$", "") * vh : y + y := ( y ~= "i)vmin$") ? RegExReplace( y, "i)vmin$", "") * vmin : y + + ; Default x and y. + if (x == "") + x := 0.5*ScreenWidth, a := 2+((( a-1)//3)*3) + if (y == "") + y := 0.5*ScreenHeight, a := 4+(mod( a-1,3)) + + ; Modify x and y values with the anchor, so that the image has a new point of origin. + 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 + + ; Prevent half-pixel rendering and keep image sharp. + w := Round(x + w) - Round(x) ; Use real x2 coordinate to determine width. + h := Round(y + h) - Round(y) ; Use real y2 coordinate to determine height. + x := Round(x) ; NOTE: simple Floor(w) or Round(w) will NOT work. + y := Round(y) ; The float values need to be added up and then rounded! + + ; Get margin. + m := this.outer.parse.margin_and_padding(m, vw, vh) + + ; Calculate border using margin. + _w := w + Round(m.2) + Round(m.4) + _h := h + Round(m.1) + Round(m.3) + _x := x - Round(m.4) + _y := y - Round(m.1) + + ; Save original Graphics settings. + DllCall("gdiplus\GdipGetPixelOffsetMode", "ptr",pGraphics, "int*",PixelOffsetMode) + DllCall("gdiplus\GdipGetCompositingMode", "ptr",pGraphics, "int*",CompositingMode) + DllCall("gdiplus\GdipGetCompositingQuality", "ptr",pGraphics, "int*",CompositingQuality) + DllCall("gdiplus\GdipGetSmoothingMode", "ptr",pGraphics, "int*",SmoothingMode) + DllCall("gdiplus\GdipGetInterpolationMode", "ptr",pGraphics, "int*",InterpolationMode) + + ; Set some general Graphics settings. + DllCall("gdiplus\GdipSetPixelOffsetMode", "ptr",pGraphics, "int",2) ; Half pixel offset. + DllCall("gdiplus\GdipSetCompositingMode", "ptr",pGraphics, "int",1) ; Overwrite/SourceCopy. + DllCall("gdiplus\GdipSetCompositingQuality", "ptr",pGraphics, "int",0) ; AssumeLinear + DllCall("gdiplus\GdipSetSmoothingMode", "ptr",pGraphics, "int",0) ; No anti-alias. + DllCall("gdiplus\GdipSetInterpolationMode", "ptr",pGraphics, "int",7) ; HighQualityBicubic + + ; Begin drawing the image onto the canvas. + if (image != "") { + + ; Draw border. + if (!m.void) { + DllCall("gdiplus\GdipSetPixelOffsetMode", "ptr",pGraphics, "int",0) ; No pixel offset. + DllCall("gdiplus\GdipSetCompositingMode", "ptr",pGraphics, "int",0) ; Blend/SourceOver. + DllCall("gdiplus\GdipSetSmoothingMode", "ptr",pGraphics, "int",0) ; No anti-alias. + + c := this.outer.parse.color(c, 0xFF000000) ; Default color is black. + pBrush := Gdip_BrushCreateSolid(c) + DllCall("gdiplus\GdipFillRectangleI" + , "ptr", pGraphics + , "ptr", pBrush + , "int", _x + , "int", _y + , "int", _w + , "int", _h) + Gdip_DeleteBrush(pBrush) + } + + ; Draw image. + if (w == width && h == height) { + ; Get the device context (hdc) associated with the Graphics object. + ; Allocate a top-down device independent bitmap (hbm) by inputting a negative height. + ; Pass the existing device context so that CreateDIBSection can determine the pixel format. + ; Outputs a pointer to the pixel data. Select the new handle to a bitmap onto the cloned + ; compatible device context. The old bitmap (obm) is a monochrome 1x1 default bitmap that + ; will be reselected onto the device context (cdc) before deletion. + ; The following routine is 4ms faster than hbm := Gdip_CreateHBITMAPFromBitmap(pBitmap). + hdc := Gdip_GetDC(pGraphics) + hbm := CreateDIBSection(width, -height, hdc, 32, pBits) + cdc := CreateCompatibleDC(hdc) + obm := SelectObject(cdc, hbm) + + ; In the below code we do something really interesting to save a call of memcpy(). + ; When calling LockBits the third argument is set to 0x4 (ImageLockModeUserInputBuf). + ; This means that we can use the pointer to the bits from our memory bitmap (DIB) + ; as the Scan0 of the LockBits output. While this is not a speed up, this saves memory + ; because we are (1) allocating a DIB, (2) getting a pBitmap, (3) using a LockBits buffer. + ; Instead of (3), we can use the allocated buffer from (1) which makes the most sense. + ; The bottleneck in the code is LockBits(), which takes over 20 ms for a 1920 x 1080 image. + ; https://stackoverflow.com/questions/6782489/create-bitmap-from-a-byte-array-of-pixel-data + ; https://stackoverflow.com/questions/17030264/read-and-write-directly-to-unlocked-bitmap-unmanaged-memory-scan0 + VarSetCapacity(Rect, 16, 0) ; sizeof(Rect) = 16 + , NumPut( width, Rect, 8, "uint") ; Width + , NumPut( height, Rect, 12, "uint") ; Height + VarSetCapacity(BitmapData, 16+2*A_PtrSize, 0) ; sizeof(BitmapData) = 24, 32 + , NumPut( width, BitmapData, 0, "uint") ; Width + , NumPut( height, BitmapData, 4, "uint") ; Height + , NumPut( 4 * width, BitmapData, 8, "int") ; Stride + , NumPut( 0xE200B, BitmapData, 12, "int") ; PixelFormat + , NumPut( pBits, BitmapData, 16, "ptr") ; Scan0 + DllCall("gdiplus\GdipBitmapLockBits" + , "ptr", pBitmap + , "ptr", &Rect + , "uint", 5 ; ImageLockMode.UserInputBuffer | ImageLockMode.ReadOnly + , "int", 0xE200B ; Format32bppPArgb + , "ptr", &BitmapData) + DllCall("gdiplus\GdipBitmapUnlockBits", "ptr",pBitmap, "ptr",&BitmapData) + + ; A good question to ask is why don't we get the pointer to the bits of the hBitmap already + ; associated with the hdc? Well we need to set the x,y,w,h coordinates of the resulting transposition; + ; and if a Graphics object associated to a pBitmap via Gdip_GraphicsFromImage() is passed, + ; there would be no underlying handle to a device independent bitmap, and thus no pBits at all! + ; So a buffer is still needed, just not two buffers (read the notes above.) + BitBlt(hdc, x, y, w, h, cdc, 0, 0) ; BitBlt() is the fastest operation for copying pixels. + SelectObject(cdc, obm) + DeleteObject(hbm) + DeleteDC(cdc) + Gdip_ReleaseDC(pGraphics, hdc) + + } else { + ; Set InterpolationMode. + q := (q >= 0 && q <= 7) ? q : 7 ; HighQualityBicubic + + DllCall("gdiplus\GdipSetPixelOffsetMode", "ptr",pGraphics, "int",2) ; Half pixel offset. + DllCall("gdiplus\GdipSetCompositingMode", "ptr",pGraphics, "int",1) ; Overwrite/SourceCopy. + DllCall("gdiplus\GdipSetSmoothingMode", "ptr",pGraphics, "int",0) ; No anti-alias. + DllCall("gdiplus\GdipSetInterpolationMode", "ptr",pGraphics, "int",q) + DllCall("gdiplus\GdipSetCompositingQuality", "ptr",pGraphics, "int",0) ; AssumeLinear + + ; WrapModeTile = 0 + ; WrapModeTileFlipX = 1 + ; WrapModeTileFlipY = 2 + ; WrapModeTileFlipXY = 3 + ; WrapModeClamp = 4 + ; Values outside this range downgrades from HighQualityBicubic to something horrible. + ; Downgrading removes the pre-filtering on the algorithm, and the need for edge cases. + DllCall("gdiplus\GdipCreateImageAttributes", "ptr*",ImageAttr) + DllCall("gdiplus\GdipSetImageAttributesWrapMode", "ptr",ImageAttr, "int",3) + DllCall("gdiplus\GdipDrawImageRectRectI" + , "ptr", pGraphics + , "ptr", pBitmap + , "int", x ; destination rectangle + , "int", y + , "int", w + , "int", h + , "int", 0 ; source rectangle + , "int", 0 + , "int", width + , "int", height + , "int", 2 + , "ptr", ImageAttr + , "ptr", 0 + , "ptr", 0) + DllCall("gdiplus\GdipDisposeImageAttributes", "ptr",ImageAttr) + } + } + + ; Begin drawing the polygons onto the canvas. + if (polygons != "") { + DllCall("gdiplus\GdipSetPixelOffsetMode", "ptr",pGraphics, "int",0) ; No pixel offset. + DllCall("gdiplus\GdipSetCompositingMode", "ptr",pGraphics, "int",1) ; Overwrite/SourceCopy. + DllCall("gdiplus\GdipSetSmoothingMode", "ptr",pGraphics, "int",2) ; Use anti-alias. + + pPen := Gdip_CreatePen(0xFFFF0000, 1) + + for i, polygon in polygons { + DllCall("gdiplus\GdipCreatePath", "int",1, "ptr*",pPath) + VarSetCapacity(pointf, 8*polygons[i].polygon.maxIndex(), 0) + for j, point in polygons[i].polygon { + NumPut(point.x*s + x, pointf, 8*(A_Index-1) + 0, "float") + NumPut(point.y*s + y, pointf, 8*(A_Index-1) + 4, "float") + } + DllCall("gdiplus\GdipAddPathPolygon", "ptr",pPath, "ptr",&pointf, "uint",polygons[i].polygon.maxIndex()) + DllCall("gdiplus\GdipDrawPath", "ptr",pGraphics, "ptr",pPen, "ptr",pPath) ; DRAWING! + } + + Gdip_DeletePen(pPen) + } + + ; Restore original Graphics settings. + DllCall("gdiplus\GdipSetPixelOffsetMode", "ptr",pGraphics, "int",PixelOffsetMode) + DllCall("gdiplus\GdipSetCompositingMode", "ptr",pGraphics, "int",CompositingMode) + DllCall("gdiplus\GdipSetCompositingQuality", "ptr",pGraphics, "int",CompositingQuality) + DllCall("gdiplus\GdipSetSmoothingMode", "ptr",pGraphics, "int",SmoothingMode) + DllCall("gdiplus\GdipSetInterpolationMode", "ptr",pGraphics, "int",InterpolationMode) + + ; Dispose of the pre-processed bitmap. + if (type != "pBitmap") + Gdip_DisposeImage(pBitmap) + + ; Define bounds. + t_bound := t + x_bound := _x + y_bound := _y + w_bound := _w + h_bound := _h + + return {0:t_bound, 1:x_bound, 2:y_bound, 3:w_bound, 4:h_bound} + } + + Preprocess(cotype, image, crop := "", scale := "", terms*) { + if (!this.hwnd) { + _renderer := new this(RegExReplace(A_ThisFunc, "^.*(^|\.)(.*?\..*?)$", "$2")) + _renderer.title := _renderer.title "_" _renderer.hwnd + DllCall("SetWindowText", "ptr",_renderer.hwnd, "str",_renderer.title) + fn := RegExReplace(A_ThisFunc, "^.*(^|\.)(.*)$", "$2") + coimage := _renderer[fn](cotype, image, crop, scale, terms*) + _renderer.FreeMemory() + _renderer := "" + return coimage + } + + ; Determine the representation (type) of the input image. + if !(type := this.DontVerifyImageType(image)) + type := this.ImageType(image) + ; If the type and cotype match, do nothing. + if (type = cotype && !this.isCropArray(crop) && !(scale ~= "^\d+(\.\d+)?$" && scale != 1)) + return image + ; Convert the image to a pBitmap (byte array). + pBitmap := this.toBitmap(type, image) + ; Crop the image, disposing if type is not pBitmap. + if this.isCropArray(crop){ + pBitmap2 := this.Gdip_CropBitmap(pBitmap, crop) + if !(type = "pBitmap" || type = "bitmap") + Gdip_DisposeImage(pBitmap) + pBitmap := pBitmap2 + } + ; Scale the image, disposing if type is not pBitmap. + if (scale ~= "^\d+(\.\d+)?$" && scale != 1) { + pBitmap2 := this.Gdip_ScaleBitmap(pBitmap, scale) + if !(type = "pBitmap" || type = "bitmap" && !this.isCropArray(crop)) + Gdip_DisposeImage(pBitmap) + pBitmap := pBitmap2 + } + ; Convert from the pBitmap intermediate to the desired representation. + coimage := this.toCotype(cotype, pBitmap, terms*) + ; Delete the pBitmap intermediate, unless the input/output is pBitmap. + if !(cotype = "pBitmap" || cotype = "bitmap" + || ((type = "pBitmap" || type = "bitmap") + && !this.isCropArray(crop) && !(scale ~= "^\d+(\.\d+)?$" && scale != 1))) + Gdip_DisposeImage(pBitmap) + return coimage + } + + Equal(images*) { + if (images.Count() == 0) + return false + + if (images.Count() == 1) + return true + + ; Activate gdip basically LOL. + if (!this.hwnd) { + _renderer := new this(RegExReplace(A_ThisFunc, "^.*(^|\.)(.*?\..*?)$", "$2")) + _renderer.title := _renderer.title "_" _renderer.hwnd + DllCall("SetWindowText", "ptr",_renderer.hwnd, "str",_renderer.title) + fn := RegExReplace(A_ThisFunc, "^.*(^|\.)(.*)$", "$2") + answer := _renderer[fn](images*) + _renderer.FreeMemory() + _renderer := "" + return answer + } + + ; Convert the images to pBitmaps (byte arrays). + for i, image in images { + if !(type := this.DontVerifyImageType(image)) + try type := this.ImageType(image) + catch + return false + + if (A_Index == 1) { + pBitmap1 := this.toBitmap(type, image) + cleanup := type + } else { + pBitmap2 := this.toBitmap(type, image) + result := this.isBitmapEqual(pBitmap1, pBitmap2) + (type != "pBitmap") ? Gdip_DisposeImage(pBitmap2) : "" + if (result) + continue + else + return false + } + } + + if (cleanup = "pBitmap") + Gdip_DisposeImage(pBitmap1) + + return true + } + + DontVerifyImageType(ByRef image) { + ; Check for image type declaration. + if !IsObject(image) + return + + if ObjHasKey(image, "screen") { + image := image.screen + return "screen" + } + + if ObjHasKey(image, "clipboard") { + image := image.clipboard + return "clipboard" + } + + if ObjHasKey(image, "object") { + image := image.object + return "object" + } + + if ObjHasKey(image, "bitmap") { + image := image.bitmap + return "bitmap" + } + + if ObjHasKey(image, "screenshot") { + image := image.screenshot + return "screenshot" + } + + if ObjHasKey(image, "file") { + image := image.file + return "file" + } + + if ObjHasKey(image, "url") { + image := image.url + return "url" + } + + if ObjHasKey(image, "window") { + image := image.window + return "window" + } + + if ObjHasKey(image, "hwnd") { + image := image.hwnd + return "hwnd" + } + + if ObjHasKey(image, "pBitmap") { + image := image.pBitmap + return "pBitmap" + } + + if ObjHasKey(image, "hBitmap") { + image := image.hBitmap + return "hBitmap" + } + + if ObjHasKey(image, "base64") { + image := image.base64 + return "base64" + } + + return + } + + ImageType(image) { + ; Check if image refers to the clipboard. + if this.isClipboard(image) + return "clipboard" + ; Check if image is empty string. + if (image == "") + throw Exception("Image data is empty string.") + ; Check if image is an object with a Bitmap() method. + if IsObject(image) and IsFunc(image.Bitmap) + return "object" + ; Check if image is an instance of safe_bitmap. + if IsObject(image) and (image.__class = "safe_bitmap") + return "bitmap" + ; Check if image is an array of 4 numbers. + if IsObject(image) and (image.1 ~= "^\d+$" && image.2 ~= "^\d+$" && image.3 ~= "^\d+$" && image.4 ~= "^\d+$") + return "screenshot" + ; Check if image points to a valid file. + if FileExist(image) + return "file" + ; Check if image points to a valid URL. + if this.isURL(image) + return "url" + ; Check if image matches a window title. Also matches "A". + if WinExist(image) + return "window" + ; Check if image is a valid handle to a window. + if DllCall("IsWindow", "ptr", image) + return "hwnd" + ; Check if image is a valid GDI Bitmap. + if (DllCall("gdiplus\GdipGetImageType", "ptr",image, "ptr*",ErrorLevel) == 0) + return "pBitmap" + ; Check if image is a valid handle to a GDI Bitmap. + if (DllCall("GetObjectType", "ptr", image) == 7) + return "hBitmap" + ; Check if image is a base64 string. + if (image ~= "^\s*(?:data:image\/[a-z]+;base64,)?(?:[A-Za-z0-9+\/]{4})*+(?:[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{2}==)?\s*$") + return "base64" + + throw Exception("Image type could not be identified.") + } + + toBitmap(type, image) { + if (type = "object") + return image.Bitmap() + + if (type = "bitmap") + return image[] + + if (type = "clipboard") { + if DllCall("OpenClipboard", "ptr", 0) { + hBitmap := DllCall("GetClipboardData", "uint", 2, "ptr") + DllCall("CloseClipboard") + pBitmap := Gdip_CreateBitmapFromHBITMAP(hBitmap) + DeleteObject(hBitmap) + } + return pBitmap + } + + if (type = "screen") { + if (image > 0) { + M := GetMonitorInfo(image) + x := M.Left + y := M.Top + w := M.Right - M.Left + h := M.Bottom - M.Top + } else { + x := DllCall("GetSystemMetrics", "int",76) + y := DllCall("GetSystemMetrics", "int",77) + w := DllCall("GetSystemMetrics", "int",78) + h := DllCall("GetSystemMetrics", "int",79) + } + chdc := CreateCompatibleDC() + hbm := CreateDIBSection(w, h, chdc) + obm := SelectObject(chdc, hbm) + hhdc := GetDC() + BitBlt(chdc, 0, 0, w, h, hhdc, x, y, Raster := "") + ReleaseDC(hhdc) + pBitmap := Gdip_CreateBitmapFromHBITMAP(hbm) + SelectObject(chdc, obm), DeleteObject(hbm), DeleteDC(hhdc), DeleteDC(chdc) + return pBitmap + } + + if (type = "screenshot") { + chdc := CreateCompatibleDC() + hbm := CreateDIBSection(image.3, image.4, chdc) + obm := SelectObject(chdc, hbm) + hhdc := GetDC() + BitBlt(chdc, 0, 0, image.3, image.4, hhdc, image.1, image.2, Raster := "") + ReleaseDC(hhdc) + pBitmap := Gdip_CreateBitmapFromHBITMAP(hbm) + SelectObject(chdc, obm), DeleteObject(hbm), DeleteDC(hhdc), DeleteDC(chdc) + return pBitmap + } + + if (type = "file") + return Gdip_CreateBitmapFromFile(image) + + if (type = "url") { + 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, "ptr*",pBitmap) + ObjRelease(pStream) + return pBitmap + } + + if (type = "window" || type = "hwnd") { + image := (type = "window") ? WinExist(image) : image + if DllCall("IsIconic", "ptr",image) + DllCall("ShowWindow", "ptr",image, "int",4) ; Restore if minimized! + VarSetCapacity(rc, 16) + DllCall("GetClientRect", "ptr",image, "ptr",&rc) + hbm := CreateDIBSection(NumGet(rc, 8, "int"), NumGet(rc, 12, "int")) + VarSetCapacity(rc, 0) + hdc := CreateCompatibleDC() + obm := SelectObject(hdc, hbm) + DllCall("PrintWindow", "ptr",image, "ptr",hdc, "uint",0x3) + pBitmap := Gdip_CreateBitmapFromHBITMAP(hbm) + SelectObject(hdc, obm), DeleteObject(hbm), DeleteDC(hdc) + return pBitmap + } + + if (type = "pBitmap") + return image + + if (type = "hBitmap") { + ; struct BITMAP - https://docs.microsoft.com/en-us/windows/desktop/api/wingdi/ns-wingdi-tagbitmap + DllCall("GetObject" + , "ptr", image + , "int", VarSetCapacity(dib, 76+2*(A_PtrSize=8?4:0)+2*A_PtrSize) + , "ptr", &dib) ; sizeof(DIBSECTION) = 84, 104 + width := NumGet(dib, 4, "uint") + height := NumGet(dib, 8, "uint") + bpp := NumGet(dib, 18, "ushort") + + ; Fallback to built-in method if pixels are not ARGB. + if (bpp != 32) + return Gdip_CreateBitmapFromHBITMAP(image) + + ; Create a handle to a device context and associate the image. + hdc := CreateCompatibleDC() + obm := SelectObject(hdc, image) + + ; Buffer the image with a top-down device independent bitmap via negative height. + ; Note that a DIB is an hBitmap, pixels are formatted as pARGB, and has a pointer to the bits. + cdc := CreateCompatibleDC(hdc) + hbm := CreateDIBSection(width, -height, cdc, 32, pBits) + ob2 := SelectObject(cdc, hbm) + + ; Create a new Bitmap (different from an hBitmap) which holds ARGB pixel values. + pBitmap := Gdip_CreateBitmap(width, height) + + ; Create a Scan0 buffer pointing to pBits. The buffer has pixel format pARGB. + VarSetCapacity(Rect, 16, 0) ; sizeof(Rect) = 16 + , NumPut( width, Rect, 8, "uint") ; Width + , NumPut( height, Rect, 12, "uint") ; Height + VarSetCapacity(BitmapData, 16+2*A_PtrSize, 0) ; sizeof(BitmapData) = 24, 32 + , NumPut( width, BitmapData, 0, "uint") ; Width + , NumPut( height, BitmapData, 4, "uint") ; Height + , NumPut( 4 * width, BitmapData, 8, "int") ; Stride + , NumPut( 0xE200B, BitmapData, 12, "int") ; PixelFormat + , NumPut( pBits, BitmapData, 16, "ptr") ; Scan0 + DllCall("gdiplus\GdipBitmapLockBits" + , "ptr", pBitmap + , "ptr", &Rect + , "uint", 7 ; ImageLockMode.UserInputBuffer | ImageLockMode.ReadWrite + , "int", 0xE200B ; Format32bppPArgb + , "ptr", &BitmapData) + + ; Ensure that our hBitmap (image) is top-down by copying it to a top-down bitmap. + BitBlt(cdc, 0, 0, width, height, hdc, 0, 0) + + ; Convert the pARGB pixels copied into the device independent bitmap (hbm) to ARGB. + DllCall("gdiplus\GdipBitmapUnlockBits", "ptr",pBitmap, "ptr",&BitmapData) + + ; Cleanup the buffer and device contexts. + SelectObject(cdc, ob2) + DeleteObject(hbm) + DeleteDC(cdc) + SelectObject(hdc, obm) + DeleteDC(hdc) + + return pBitmap + } + + if (type = "base64") { + image := Trim(image) + image := RegExReplace(image, "^data:image\/[a-z]+;base64,", "") + DllCall("crypt32\CryptStringToBinary" (A_IsUnicode ? "W" : "A"), "ptr",&image, "uint",0, "uint",1, "ptr",0, "uint*",nSize, "ptr",0, "ptr",0) + VarSetCapacity(bin, nSize, 0) + DllCall("crypt32\CryptStringToBinary" (A_IsUnicode ? "W" : "A"), "ptr",&image, "uint",0, "uint",1, "ptr",&bin, "uint*",nSize, "ptr",0, "ptr",0) + hData := DllCall("GlobalAlloc", "uint",0x2, "ptr",nSize) + pData := DllCall("GlobalLock", "ptr",hData) + DllCall("RtlMoveMemory", "ptr",pData, "ptr",&bin, "uptr",nSize) + DllCall("ole32\CreateStreamOnHGlobal", "ptr",hData, "int",false, "ptr*",pStream) + DllCall("gdiplus\GdipCreateBitmapFromStream", "ptr",pStream, "ptr*",pBitmap) + pBitmap2 := Gdip_CloneBitmapArea(pBitmap, 0, 0, Gdip_GetImageWidth(pBitmap), Gdip_GetImageHeight(pBitmap)) + Gdip_DisposeImage(pBitmap) + ObjRelease(pStream) + DllCall("GlobalUnlock", "ptr",hData) + DllCall("GlobalFree", "ptr",hData) ; Will delete the original bitmap if not cloned. + return pBitmap2 + } + } + + toCotype(cotype, pBitmap, terms*) { + ; toCotype("clipboard", pBitmap) + if (cotype = "clipboard") { + off1 := A_PtrSize = 8 ? 52 : 44, off2 := A_PtrSize = 8 ? 32 : 24 + hBitmap := Gdip_CreateHBITMAPFromBitmap(pBitmap) + DllCall("GetObject", "ptr", hBitmap, "int", VarSetCapacity(oi, A_PtrSize = 8 ? 104 : 84, 0), "ptr", &oi) + hdib := DllCall("GlobalAlloc", "uint", 2, "ptr", 40+NumGet(oi, off1, "uint"), "ptr") + pdib := DllCall("GlobalLock", "ptr", hdib, "ptr") + DllCall("RtlMoveMemory", "ptr", pdib, "ptr", &oi+off2, "uptr", 40) + DllCall("RtlMoveMemory", "ptr", pdib+40, "ptr", NumGet(oi, off2 - (A_PtrSize ? A_PtrSize : 4), "ptr"), "uptr", NumGet(oi, off1, "uint")) + DllCall("GlobalUnlock", "ptr", hdib) + DllCall("DeleteObject", "ptr", hBitmap) + + DllCall("OpenClipboard", "ptr", 0) + DllCall("EmptyClipboard") + DllCall("SetClipboardData", "uint", 8, "ptr", hdib) + DllCall("CloseClipboard") + } + + ; toCotype("bitmap", pBitmap) + if (cotype = "bitmap") { + return new this.outer.safe_bitmap(pBitmap) + } + + ; toCotype("screenshot", pBitmap, style) + if (cotype = "screenshot") { + renderer := this.Render({"pBitmap":pBitmap}, terms.1) + return [renderer.x1(), renderer.y1(), renderer.width(), renderer.height()] + } + + ; toCotype("file", pBitmap, filename, quality) + if (cotype = "file") { + filename := (terms.1) ? terms.1 : this.title ".png" + Gdip_SaveBitmapToFile(pBitmap, filename, terms.2) + return filename + } + + ; toCotype("url", ???????????????????????? + if (cotype = "url") { + ; make a url + } + + ; toCotype("window", pBitmap) + if (cotype = "window") + return "ahk_id " . this.Render({"pBitmap":pBitmap}).AlwaysOnTop().ToolWindow().Caption().hwnd + + ; toCotype("hwnd", pBitmap) + if (cotype = "hwnd") + return this.Render({"pBitmap":pBitmap}).hwnd + + ; toCotype("pBitmap", pBitmap) + if (cotype = "pBitmap") + return pBitmap + + ; toCotype("hBitmap", pBitmap, alpha) + if (cotype = "hBitmap") + return Gdip_CreateHBITMAPFromBitmap(pBitmap, terms.1) + + ; toCotype("base64", pBitmap, extension, quality) + if (cotype = "base64") { ; Thanks to noname. + if !(terms.1 ~= "(?i)bmp|dib|rle|jpg|jpeg|jpe|jfif|gif|tif|tiff|png") + terms.1 := "png" + Extension := "." terms.1 + + Quality := terms.2 + + DllCall("gdiplus\GdipGetImageEncodersSize", "uint*",nCount, "uint*",nSize) + VarSetCapacity(ci, nSize) + DllCall("gdiplus\GdipGetImageEncoders", "uint",nCount, "uint",nSize, "ptr",&ci) + if !(nCount && nSize) + throw Exception("Could not get a list of image codec encoders on this system.") + + _nCount := (A_AhkVersion < 2) ? nCount : "nCount" + Loop %_nCount% + { + sString := StrGet(NumGet(ci, (idx := (48+7*A_PtrSize)*(A_Index-1))+32+3*A_PtrSize), "UTF-16") + if InStr(sString, "*" Extension) + break + } + + if !(pCodec := &ci+idx) + throw Exception("Could not find matching encoder for specified file format.") + + if RegExMatch(Extension, "^\.(?i:JPG|JPEG|JPE|JFIF)$") + { + Quality := (Quality < 0) ? 0 : (Quality > 100) ? 90 : Quality ; Default JPEG is 90. + DllCall("gdiplus\GdipGetEncoderParameterListSize", "ptr",pBitmap, "ptr",pCodec, "uint*",nSize) + VarSetCapacity(EncoderParameters, nSize, 0) + DllCall("gdiplus\GdipGetEncoderParameterList", "ptr",pBitmap, "ptr",pCodec, "uint",nSize, "ptr",&EncoderParameters) + nCount := NumGet(EncoderParameters, "uint") + N := (A_AhkVersion < 2) ? nCount : "nCount" + Loop %N% + { + elem := (24+A_PtrSize)*(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, "ptr") + nSize := DllCall("GlobalSize", "uint",pData) + + VarSetCapacity(bin, nSize, 0) + DllCall("RtlMoveMemory", "ptr",&bin, "ptr",pData, "uptr",nSize) + DllCall("GlobalUnlock", "ptr",hData) + ObjRelease(pStream) + DllCall("GlobalFree", "ptr",hData) + + ; Using CryptBinaryToStringA saves about 2MB in memory. + DllCall("Crypt32.dll\CryptBinaryToStringA", "ptr",&bin, "uint",nSize, "uint",0x40000001, "ptr",0, "uint*",base64Length) + VarSetCapacity(base64, base64Length, 0) + DllCall("Crypt32.dll\CryptBinaryToStringA", "ptr",&bin, "uint",nSize, "uint",0x40000001, "ptr",&base64, "uint*",base64Length) + VarSetCapacity(bin, 0) + + return StrGet(&base64, base64Length, "CP0") + } + } + + Gdip_CropBitmap(pBitmap, crop, width:="", height:="") { + width := (width) ? width : Gdip_GetImageWidth(pBitmap) + height := (height) ? height : Gdip_GetImageHeight(pBitmap) + + ; Are the numbers percentages? + crop.1 := (crop.1 ~= "%$") ? SubStr(crop.1, 1, -1) * 0.01 * width : crop.1 + crop.2 := (crop.2 ~= "%$") ? SubStr(crop.2, 1, -1) * 0.01 * height : crop.2 + crop.3 := (crop.3 ~= "%$") ? SubStr(crop.3, 1, -1) * 0.01 * width : crop.3 + crop.4 := (crop.4 ~= "%$") ? SubStr(crop.4, 1, -1) * 0.01 * height : crop.4 + + ; If numbers are negative, subtract the values from the edge. + crop.1 := (crop.1 < 0) ? Abs(crop.1) : crop.1 + crop.2 := (crop.2 < 0) ? Abs(crop.2) : crop.2 + crop.3 := (crop.3 < 0) ? width + crop.3 : crop.3 + crop.4 := (crop.4 < 0) ? height + crop.4 : crop.4 + + ; Round to the nearest integer. + crop.1 := Round(crop.1) + crop.2 := Round(crop.2) + crop.3 := Round(crop.3) + crop.4 := Round(crop.4) + + ; Ensure that coordinates can never exceed the expected Bitmap area. + safe_x := (crop.1 > width) ? 0 : crop.1 + safe_y := (crop.2 > height) ? 0 : crop.2 + safe_w := (crop.1 + crop.3 > width) ? width - safe_x : crop.3 + safe_h := (crop.2 + crop.4 > height) ? height - safe_y : crop.4 + return Gdip_CloneBitmapArea(pBitmap, safe_x, safe_y, safe_w, safe_h) + } + + Gdip_ScaleBitmap(pBitmap, scale, width:="", height:="") { + width := (width) ? width : Gdip_GetImageWidth(pBitmap) + height := (height) ? height : Gdip_GetImageHeight(pBitmap) + + safe_w := Ceil(width * scale) + safe_h := Ceil(height * scale) + + pBitmap2 := Gdip_CreateBitmap(safe_w, safe_h) + G2 := Gdip_GraphicsFromImage(pBitmap2) + Gdip_SetSmoothingMode(G2, 4) + Gdip_SetInterpolationMode(G2, 7) + Gdip_DrawImage(G2, pBitmap, 0, 0, safe_w, safe_h) + Gdip_DeleteGraphics(G2) + return pBitmap2 + } + + isBitmapEqual(pBitmap1, pBitmap2) { + ; Make sure both Bitmaps are valid pointers. + if !(pBitmap1 && pBitmap2) + return false + + ; Check if pointers are identical. + if (pBitmap1 == pBitmap2) + return true + + pBitmap1_width := Gdip_GetImageWidth(pBitmap1) + pBitmap1_height := Gdip_GetImageHeight(pBitmap1) + pBitmap2_width := Gdip_GetImageWidth(pBitmap2) + pBitmap2_height := Gdip_GetImageHeight(pBitmap2) + + ; Match image dimensions. De Morgan's rules! + if !(pBitmap1_width == pBitmap2_width && pBitmap1_height == pBitmap2_height) + return false + + ; Find smaller width and height to match bytes. + ; Sort of unnecessary due to the above statement, but nice to have. + width := (pBitmap1_width < pBitmap2_width) ? pBitmap1_width : pBitmap2_width + height := (pBitmap1_height < pBitmap2_height) ? pBitmap1_height : pBitmap2_height + 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. + size := width * height * 4 ; ARGB = 4 bytes + byte := DllCall("ntdll\RtlCompareMemory", "ptr", Scan01+0, "ptr", Scan02+0, "uptr", size) + + Gdip_UnlockBits(pBitmap1, BitmapData1) + Gdip_UnlockBits(pBitmap2, BitmapData2) + return (byte == size) ? true : false + } + + isClipboard(var) { + if !(var == ClipboardAll) + return false + + if DllCall("OpenClipboard", "ptr", 0) { + _answer := DllCall("IsClipboardFormatAvailable", "uint", 8) + DllCall("CloseClipboard") + if (_answer) + return true + else ; throw because (ClipboardAll == "") throws in the next line. + throw Exception("Clipboard does not contain compatible image data.") + } + } + + isCropArray(array) { + if (array.length() != 4) + return false + for index, value in array + if !(value ~= "^\-?\d+(?:\.\d*)?%?$") + return false + return true + } + + 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 + } + } ; End of ImageRenderer class. + + class PolygonRenderer { + static extends := "renderer" + + _extends := this.__extends() + __extends(subbundle := "") { + object := this.outer[this.extends] + bundle := ((object.haskey("__extends")) ? object.__extends(true) : object) + (subbundle) ? (this.base := bundle) : (this.base.base := bundle) + return (subbundle) ? this : "" + } + + outer[p:=""] { + get { + static period := ".", _period := (A_AhkVersion < 2) ? period : "period" + if ((__outer := RegExReplace(this.__class, "^(.*)\..*$", "$1")) != this.__class) + Loop Parse, __outer, %_period% + outer := (A_Index=1) ? %A_LoopField% : outer[A_LoopField] + return IsObject(outer) ? ((p) ? outer[p] : outer) : ((p) ? %p% : "") + } + } + + activateOnAdmin := true, ScreenWidth := A_ScreenWidth, ScreenHeight := A_ScreenHeight + + __New(title := "", terms*) { + global pToken + if !(this.outer.Startup()) + if !(pToken) + if !(this.pToken := Gdip_Startup()) + throw Exception("Gdiplus failed to start. Please ensure you have gdiplus on your system.") + + this.CreateWindow() + + this.__screen := new this.outer.memory(this.ScreenWidth, this.ScreenHeight) + this.state := new this.outer.queue() + return this + } + + Destroy() { + Gdip_DeleteGraphics(this.gfx) + SelectObject(this.hdc, this.obm) + DeleteObject(this.hbm) + DeleteDC(this.hdc) + this.gfx := this.obm := this.pBits := this.hbm := this.hdc := "" + + this.__screen := "" + DllCall("DestroyWindow", "ptr",this.hwnd) + return this + } + + Render(terms*) { + if !(this.hwnd) + return (new this).Render(terms*) + + this.state.new(A_ThisFunc) + + Gdip_GraphicsClear(this.__screen.gfx) + + this.UpdateMemory() + this.Draw(terms*) + + UpdateLayeredWindow(this.hwnd, this.hdc, 0, 0, this.ScreenWidth, this.ScreenHeight) + + if (this.t) { + self_destruct := ObjBindMethod(this, "Destroy") + time := -1 * this.t + _self_destruct := (A_AhkVersion < 2) ? self_destruct : "self_destruct" + _time := (A_AhkVersion < 2) ? time : "time" + SetTimer %_self_destruct%, %_time% + } + + ; Shift the layers. + this.state.layers.RemoveAt(1) + this.state.layers.2 := [] + + return this + } + + Redraw(x, y, w, h) { + ;this.UpdateMemory() + Gdip_SetSmoothingMode(this.__screen.gfx, 4) ;Adds one clickable pixel to the edge. + pBrush := Gdip_BrushCreateSolid(this.color) + + if (this.cache = 0) { + Gdip_GraphicsClear(this.__screen.gfx) + Gdip_FillRectangle(this.__screen.gfx, pBrush, x, y, w, h) + } + if (this.cache = 1) { + if (!this.state.identical) + this.__cache := "" + + if (!this.__cache) { + this.__cache := new this.outer.memory(w + 1, h + 1) + Gdip_FillRectangle(this.__cache.gfx, pBrush, 0, 0, w, h) + } + + Gdip_GraphicsClear(this.__screen.gfx) + BitBlt(this.__screen.hdc, x, y, w + 1, h + 1, this.__cache.hdc, 0, 0) + } + if (this.cache = 2) { + if (!this.state.identical) + this.__cache := "" + + if (!this.__cache) { + this.__cache := new this.outer.memory(w + 1, h + 1) + Gdip_FillRectangle(this.__cache.gfx, pBrush, 0, 0, w, h) + } + + Gdip_GraphicsClear(this.__screen.gfx) + StretchBlt(this.__screen.hdc, x, y, w + 1, h + 1, this.__cache.hdc, 0, 0, this.__cache.width, this.__cache.height) + } + UpdateLayeredWindow(this.hwnd, this.__screen.hdc, 0, 0, this.ScreenWidth, this.ScreenHeight) + Gdip_DeleteBrush(pBrush) + } + + Paint(x, y, w, h, pGraphics) { + pBrush := Gdip_BrushCreateSolid(this.color) + Gdip_FillRectangle(pGraphics, pBrush, x, y, w, h) + Gdip_DeleteBrush(pBrush) + } + + Draw(color := "", style := "") { + + ; Note that only the second layer is drawn on. The first layer is the reference layer. + if (pGraphics == "") { + if (!this.state.layers.2.MaxIndex()) + this.__buffer := new this.outer.memory(this.ScreenWidth, this.ScreenHeight) + pGraphics := this.__buffer.gfx + } + + + ; Retrieve last style if omitted. Reduce all whitespace to one space character. + style := !IsObject(style) ? RegExReplace(style, "\s+", " ") : style + style := (style == "") ? this.state.layers.2[this.state.layers.2.MaxIndex()].2 : style + this.state.layers.2.push([color, style, empty]) + + + + static q1 := "(?i)^.*?\b(?\([^()]*\)|[^()]*)*\))(:\s*)?\(?(?(?<=\()([\s:#%_a-z\-\.\d]+|\([\s:#%_a-z\-\.\d]*\))*(?=\))|[#%_a-z\-\.\d]+).*$" + + if IsObject(style) { + t := (style.time != "") ? style.time : style.t + x := (style.left != "") ? style.left : style.x + y := (style.top != "") ? style.top : style.y + w := (style.width != "") ? style.width : style.w + h := (style.height != "") ? style.height : style.h + a := (style.anchor != "") ? style.anchor : style.a + m := (style.margin != "") ? style.margin : style.m + s := (style.size != "") ? style.size : style.s + c := (style.color != "") ? style.color : style.c + q := (style.quality != "") ? style.quality : (style.q) ? style.q : style.InterpolationMode + } else { + t := ((___ := RegExReplace(style, q1 "(t(ime)?)" q2, "${value}")) != style) ? ___ : "" + x := ((___ := RegExReplace(style, q1 "(x|left)" q2, "${value}")) != style) ? ___ : "" + y := ((___ := RegExReplace(style, q1 "(y|top)" q2, "${value}")) != style) ? ___ : "" + w := ((___ := RegExReplace(style, q1 "(w(idth)?)" q2, "${value}")) != style) ? ___ : "" + h := ((___ := RegExReplace(style, q1 "(h(eight)?)" q2, "${value}")) != style) ? ___ : "" + a := ((___ := RegExReplace(style, q1 "(a(nchor)?)" q2, "${value}")) != style) ? ___ : "" + m := ((___ := RegExReplace(style, q1 "(m(argin)?)" q2, "${value}")) != style) ? ___ : "" + s := ((___ := RegExReplace(style, q1 "(s(ize)?)" q2, "${value}")) != style) ? ___ : "" + c := ((___ := RegExReplace(style, q1 "(c(olor)?)" q2, "${value}")) != style) ? ___ : "" + q := ((___ := RegExReplace(style, q1 "(q(uality)?)" q2, "${value}")) != style) ? ___ : "" + } + + static times := "(?i)^\s*(\d+)\s*(ms|mil(li(second)?)?|s(ec(ond)?)?|m(in(ute)?)?|h(our)?|d(ay)?)?s?\s*$" + t := ( t ~= times) ? RegExReplace( t, "\s", "") : 0 ; Default time is zero. + t := ((___ := RegExReplace( t, "i)(\d+)(ms|mil(li(second)?)?)s?$", "$1")) != t) ? ___ * 1 : t + t := ((___ := RegExReplace( t, "i)(\d+)s(ec(ond)?)?s?$" , "$1")) != t) ? ___ * 1000 : t + t := ((___ := RegExReplace( t, "i)(\d+)m(in(ute)?)?s?$" , "$1")) != t) ? ___ * 60000 : t + t := ((___ := RegExReplace( t, "i)(\d+)h(our)?s?$" , "$1")) != t) ? ___ * 3600000 : t + t := ((___ := RegExReplace( t, "i)(\d+)d(ay)?s?$" , "$1")) != t) ? ___ * 86400000 : t + + static valid := "(?i)^\s*(\-?(?:(?:\d+(?:\.\d*)?)|(?:\.\d+)))\s*(%|pt|px|vh|vmin|vw)?\s*$" + static valid_positive := "(?i)^\s*((?:(?:\d+(?:\.\d*)?)|(?:\.\d+)))\s*(%|pt|px|vh|vmin|vw)?\s*$" + + vw := 0.01 * this.ScreenWidth ; 1% of viewport width. + vh := 0.01 * this.ScreenHeight ; 1% of viewport height. + vmin := (vw < vh) ? vw : vh ; 1vw or 1vh, whichever is smaller. + + ; Default = 0, LowQuality = 1, HighQuality = 2, Bilinear = 3 + ; Bicubic = 4, NearestNeighbor = 5, HighQualityBilinear = 6, HighQualityBicubic = 7 + q := (q >= 0 && q <= 7) ? q : 7 ; Default InterpolationMode is HighQualityBicubic. + Gdip_SetInterpolationMode(pGraphics, q) + + w := ( w ~= valid_positive) ? RegExReplace( w, "\s", "") : width ; Default width is image width. + w := ( w ~= "i)(pt|px)$") ? SubStr( w, 1, -2) : w + w := ( w ~= "i)vw$") ? RegExReplace( w, "i)vw$", "") * vw : w + w := ( w ~= "i)vh$") ? RegExReplace( w, "i)vh$", "") * vh : w + w := ( w ~= "i)vmin$") ? RegExReplace( w, "i)vmin$", "") * vmin : w + w := ( w ~= "%$") ? RegExReplace( w, "%$", "") * 0.01 * width : w + + h := ( h ~= valid_positive) ? RegExReplace( h, "\s", "") : height ; Default height is image height. + h := ( h ~= "i)(pt|px)$") ? SubStr( h, 1, -2) : h + h := ( h ~= "i)vw$") ? RegExReplace( h, "i)vw$", "") * vw : h + h := ( h ~= "i)vh$") ? RegExReplace( h, "i)vh$", "") * vh : h + h := ( h ~= "i)vmin$") ? RegExReplace( h, "i)vmin$", "") * vmin : h + h := ( h ~= "%$") ? RegExReplace( h, "%$", "") * 0.01 * height : h + + ; If size is "auto" automatically downscale by a multiple of 2. Ex: 50%, 25%, 12.5%... + if (s = "auto") { + ; Determine what is smaller: declared width and height or screen width and height. + ; Since the declared w and h are overwritten by the size, they now determine the bounds. + ; Default bounds are the ScreenWidth and ScreenHeight, and can be decreased, never increased. + visible_w := (w > this.ScreenWidth) ? this.ScreenWidth : w + visible_h := (h > this.ScreenHeight) ? this.ScreenHeight : h + auto_w := (width > visible_w) ? width // visible_w + 1 : 1 + auto_h := (height > visible_h) ? height // visible_h + 1 : 1 + s := (auto_w > auto_h) ? (1 / auto_w) : (1 / auto_h) + w := width ; Since the width was overwritten, restore it to the default. + h := height ; w and h determine the bounds of the size. + } + + s := ( s ~= valid_positive) ? RegExReplace( s, "\s", "") : 1 ; Default size is 1.00. + s := ( s ~= "i)(pt|px)$") ? SubStr( s, 1, -2) : s + s := ( s ~= "i)vw$") ? RegExReplace( s, "i)vw$", "") * vw / width : s + s := ( s ~= "i)vh$") ? RegExReplace( s, "i)vh$", "") * vh / height: s + s := ( s ~= "i)vmin$") ? RegExReplace( s, "i)vmin$", "") * vmin / minimum : s + s := ( s ~= "%$") ? RegExReplace( s, "%$", "") * 0.01 : s + + ; Scale width and height. + w := Floor(w * s) + h := Floor(h * s) + + a := RegExReplace( a, "\s", "") + a := (a ~= "i)top" && a ~= "i)left") ? 1 : (a ~= "i)top" && a ~= "i)cent(er|re)") ? 2 + : (a ~= "i)top" && a ~= "i)right") ? 3 : (a ~= "i)cent(er|re)" && a ~= "i)left") ? 4 + : (a ~= "i)cent(er|re)" && a ~= "i)right") ? 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 ~= "i)top") ? 2 : (a ~= "i)left") ? 4 : (a ~= "i)right") ? 6 : (a ~= "i)bottom") ? 8 + : (a ~= "i)cent(er|re)") ? 5 : (a ~= "^[1-9]$") ? a : 1 ; Default anchor is top-left. + + a := ( x ~= "i)left") ? 1+((( a-1)//3)*3) : ( x ~= "i)cent(er|re)") ? 2+((( a-1)//3)*3) : ( x ~= "i)right") ? 3+((( a-1)//3)*3) : a + a := ( y ~= "i)top") ? 1+(mod( a-1,3)) : ( y ~= "i)cent(er|re)") ? 4+(mod( a-1,3)) : ( y ~= "i)bottom") ? 7+(mod( a-1,3)) : a + + ; Convert English words to numbers. Don't mess with these values any further. + x := ( x ~= "i)left") ? 0 : (x ~= "i)cent(er|re)") ? 0.5*this.ScreenWidth : (x ~= "i)right") ? this.ScreenWidth : x + y := ( y ~= "i)top") ? 0 : (y ~= "i)cent(er|re)") ? 0.5*this.ScreenHeight : (y ~= "i)bottom") ? this.ScreenHeight : y + + ; Validate x and y, convert to pixels. + x := ( x ~= valid) ? RegExReplace( x, "\s", "") : 0 ; Default x is 0. + x := ( x ~= "i)(pt|px)$") ? SubStr( x, 1, -2) : x + x := ( x ~= "i)(%|vw)$") ? RegExReplace( x, "i)(%|vw)$", "") * vw : x + x := ( x ~= "i)vh$") ? RegExReplace( x, "i)vh$", "") * vh : x + x := ( x ~= "i)vmin$") ? RegExReplace( x, "i)vmin$", "") * vmin : x + + y := ( y ~= valid) ? RegExReplace( y, "\s", "") : 0 ; Default y is 0. + y := ( y ~= "i)(pt|px)$") ? SubStr( y, 1, -2) : y + y := ( y ~= "i)vw$") ? RegExReplace( y, "i)vw$", "") * vw : y + y := ( y ~= "i)(%|vh)$") ? RegExReplace( y, "i)(%|vh)$", "") * vh : y + y := ( y ~= "i)vmin$") ? RegExReplace( y, "i)vmin$", "") * vmin : y + + ; Modify x and y values with the anchor, so that the image has a new point of origin. + 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 + + ; Prevent half-pixel rendering and keep image sharp. + x := Floor(x) + y := Floor(y) + + m := this.outer.parse.margin_and_padding(m, vw, vh) + + ; Calculate border using margin. + _x := x - (m.4) + _y := y - (m.1) + _w := w + (m.2 + m.4) + _h := h + (m.1 + m.3) + + ; Save size. + this.x := _x + this.y := _y + this.w := _w + this.h := _h + + if (image != "") { + ; Draw border. + c := this.outer.parse.color(c, 0xFF000000) ; Default color is black. + pBrush := Gdip_BrushCreateSolid(c) + Gdip_FillRectangle(pGraphics, pBrush, _x, _y, _w, _h) + Gdip_DeleteBrush(pBrush) + ; Draw image. + Gdip_DrawImage(pGraphics, pBitmap, x, y, w, h, 0, 0, width, height) + } + + ; POINTF + Gdip_SetSmoothingMode(pGraphics, 4) ; None = 3, AntiAlias = 4 + pPen := Gdip_CreatePen(0xFFFF0000, 1) + + for i, polygon in polygons { + DllCall("gdiplus\GdipCreatePath", "int",1, "ptr*",pPath) + VarSetCapacity(pointf, 8*polygons[i].polygon.maxIndex(), 0) + for j, point in polygons[i].polygon { + NumPut(point.x*s + x, pointf, 8*(A_Index-1) + 0, "float") + NumPut(point.y*s + y, pointf, 8*(A_Index-1) + 4, "float") + } + DllCall("gdiplus\GdipAddPathPolygon", "ptr",pPath, "ptr",&pointf, "uint",polygons[i].polygon.maxIndex()) + DllCall("gdiplus\GdipDrawPath", "ptr",pGraphics, "ptr",pPen, "ptr",pPath) ; DRAWING! + } + + Gdip_DeletePen(pPen) + + if (type != "pBitmap") + Gdip_DisposeImage(pBitmap) + + return (pGraphics == "") ? this : "" + } + + MouseGetPos(ByRef x_mouse, ByRef y_mouse) { + _cmm := A_CoordModeMouse + CoordMode Mouse, Screen + MouseGetPos x_mouse, y_mouse + CoordMode Mouse, %_cmm% + } + + Origin() { + this.MouseGetPos(x_mouse, y_mouse) + new_state := this.state.new(A_ThisFunc, x_mouse, y_mouse) + if (new_state = true || x_mouse != this.x_last || y_mouse != this.y_last) { + this.x_last := x_mouse, this.y_last := y_mouse + + x := x_mouse + y := y_mouse + w := 1 + h := 1 + ;stabilize x/y coordinates in window spy. + + this.state.queue(x, y, w, h, xx, yy, mx, my) + this.Redraw(x, y, w, h) + } + } + + Drag() { + this.MouseGetPos(x_mouse, y_mouse) + new_state := this.state.new(A_ThisFunc, x_mouse, y_mouse) + if (new_state = true || x_mouse != this.x_last || y_mouse != this.y_last) { + this.x_last := x_mouse, this.y_last := y_mouse + + x_origin := (this.state.mx.1) ? this.state.x.1 : this.state.xx.1 + y_origin := (this.state.my.1) ? this.state.y.1 : this.state.yy.1 + + mx := (x_mouse > x_origin) ? true : false + my := (y_mouse > y_origin) ? true : false + + x := (mx) ? x_origin : x_mouse + y := (my) ? y_origin : y_mouse + xx := (mx) ? x_mouse : x_origin + yy := (my) ? y_mouse : y_origin + ;a := (xr && yr) ? "top left" : (xr && !yr) ? "bottom left" : (!xr && yr) ? "top right" : "bottom right" + ;q := (xr && yr) ? "bottom right" : (xr && !yr) ? "top right" : (!xr && yr) ? "bottom left" : "top left" + + this.state.queue(x, y, w, h, xx, yy, mx, my) + this.Redraw(x, y, w, h) + } + } + + Move() { + this.MouseGetPos(x_mouse, y_mouse) + new_state := this.state.new(A_ThisFunc, x_mouse, y_mouse) + if (new_state = true || x_mouse != this.x_last || y_mouse != this.y_last) { + this.x_last := x_mouse, this.y_last := y_mouse + + dx := x_mouse - this.state.x_mouse + dy := y_mouse - this.state.y_mouse + x := this.state.x.1 + dx + y := this.state.y.1 + dy + + this.state.queue(x, y, w, h, xx, yy, mx, my) + this.Redraw(x, y, w, h) + } + } + + Hover() { + this.MouseGetPos(x_mouse, y_mouse) + new_state := this.state.new(A_ThisFunc, x_mouse, y_mouse) + this.state.queue() + } + + Recover() { + Gdip_SetSmoothingMode(this.gfx, 4) + return this + } + + ChangeColor(color) { + this.color := color + Gdip_DeleteBrush(this.pBrush) + this.pBrush := Gdip_BrushCreateSolid(this.color) + this.Redraw(this.state.x.2, this.state.y.2, this.state.w.2, this.state.h.2) + } + + ResizeCorners() { + this.MouseGetPos(x_mouse, y_mouse) + new_state := this.state.new(A_ThisFunc, x_mouse, y_mouse) + if (new_state = true || x_mouse != this.x_last || y_mouse != this.y_last) { + this.x_last := x_mouse, this.y_last := y_mouse + + xr := this.state.x_mouse - this.state.x.1 - (this.state.w.1 / 2) + yr := this.state.y.1 - this.state.y_mouse + (this.state.h.1 / 2) ; Keep Change Change + dx := x_mouse - this.state.x_mouse + dy := y_mouse - this.state.y_mouse + + if (xr < -1 && yr > 1) { + r := "top left" + x := this.state.x.1 + dx + y := this.state.y.1 + dy + w := this.state.w.1 - dx + h := this.state.h.1 - dy + } + if (xr >= -1 && yr > 1) { + r := "top right" + x := this.state.x.1 + y := this.state.y.1 + dy + w := this.state.w.1 + dx + h := this.state.h.1 - dy + } + if (xr < -1 && yr <= 1) { + r := "bottom left" + x := this.state.x.1 + dx + y := this.state.y.1 + w := this.state.w.1 - dx + h := this.state.h.1 + dy + } + if (xr >= -1 && yr <= 1) { + r := "bottom right" + x := this.state.x.1 + y := this.state.y.1 + w := this.state.w.1 + dx + h := this.state.h.1 + dy + } + + this.state.queue(x, y, w, h, xx, yy, mx, my) + this.Redraw(x, y, w, h) + } + } + + ; 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() { + this.MouseGetPos(x_mouse, y_mouse) + new_state := this.state.new(A_ThisFunc, x_mouse, y_mouse) + if (new_state = true || x_mouse != this.x_last || y_mouse != this.y_last) { + this.x_last := x_mouse, this.y_last := y_mouse + + m := -(this.state.h.1 / this.state.w.1) ; slope (dy/dx) + xr := this.state.x_mouse - this.state.x.1 - (this.state.w.1 / 2) ; draw a line across the center + yr := this.state.y.1 - this.state.y_mouse + (this.state.h.1 / 2) ; draw a vertical line halfing it + dx := x_mouse - this.state.x_mouse + dy := y_mouse - this.state.y_mouse + + if (m * xr >= yr && yr > -m * xr) + r := "left", x := this.state.x.1 + dx, w := this.state.w.1 - dx + if (m * xr < yr && yr > -m * xr) + r := "top", y := this.state.y.1 + dy, h := this.state.h.1 - dy + if (m * xr < yr && yr <= -m * xr) + r := "right", w := this.state.w.1 + dx + if (m * xr >= yr && yr <= -m * xr) + r := "bottom", h := this.state.h.1 + dy + + this.state.queue(x, y, w, h, xx, yy, mx, my) + this.Redraw(x, y, w, h) + } + } + + isMouseInside() { + this.MouseGetPos(x_mouse, y_mouse) + return (x_mouse >= this.state.x.2 && x_mouse <= this.state.xx.2 + && y_mouse >= this.state.y.2 && y_mouse <= this.state.yy.2) + } + + isMouseOutside() { + return !this.isMouseInside() + } + + isMouseOnCorner() { + this.MouseGetPos(x_mouse, y_mouse) + return (x_mouse == this.state.x.2 || x_mouse == this.state.xx.2) + && (y_mouse == this.state.y.2 || y_mouse == this.state.yy.2) + } + + isMouseOnEdge() { + this.MouseGetPos(x_mouse, y_mouse) + return ((x_mouse >= this.state.x.2 && x_mouse <= this.state.xx.2) + && (y_mouse == this.state.y.2 || y_mouse == this.state.yy.2)) + OR ((y_mouse >= this.state.y.2 && y_mouse <= this.state.yy.2) + && (x_mouse == this.state.x.2 || x_mouse == this.state.xx.2)) + } + + isMouseStopped() { + this.MouseGetPos(x_mouse, y_mouse) + return x_mouse == this.x_last && y_mouse == this.y_last + } + + ScreenshotCoordinates() { + return (this.state.w.2 > 0 && this.state.h.2 > 0) + ? (this.state.x.2 "|" this.state.y.2 "|" this.state.w.2 "|" this.state.h.2) : "" + } + + x1() { + return this.state.x.2 + } + + y1() { + return this.state.y.2 + } + + x2() { + return this.state.xx.2 + } + + y2() { + return this.state.yy.2 + } + + width() { + return this.state.w.2 + } + + height() { + return this.state.h.2 + } + } ; End of PolygonRenderer class. + + class TextRenderer { + static extends := "renderer" + + _extends := this.__extends() + __extends(subbundle := "") { + object := this.outer[this.extends] + bundle := ((object.haskey("__extends")) ? object.__extends(true) : object) + (subbundle) ? (this.base := bundle) : (this.base.base := bundle) + return (subbundle) ? this : "" + } + + outer[p:=""] { + get { + static period := ".", _period := (A_AhkVersion < 2) ? period : "period" + if ((__outer := RegExReplace(this.__class, "^(.*)\..*$", "$1")) != this.__class) + Loop Parse, __outer, %_period% + outer := (A_Index=1) ? %A_LoopField% : outer[A_LoopField] + return IsObject(outer) ? ((p) ? outer[p] : outer) : ((p) ? %p% : "") + } + } + + Render(terms*) { + return (this.hwnd) ? this.Output(terms*) : (new this).Output(terms*) + } + + DrawRaw(pGraphics, ScreenWidth, ScreenHeight, text := "", style1 := "", style2 := "") { + ; Remove excess whitespace for proper RegEx detection. + style1 := !IsObject(style1) ? RegExReplace(style1, "\s+", " ") : style1 + style2 := !IsObject(style2) ? RegExReplace(style2, "\s+", " ") : style2 + + ; RegEx help? https://regex101.com/r/xLzZzO/2 + static q1 := "(?i)^.*?\b(?\([^()]*\)|[^()]*)*\))(:\s*)?\(?(?(?<=\()([\s:#%_a-z\-\.\d]+|\([\s:#%_a-z\-\.\d]*\))*(?=\))|[#%_a-z\-\.\d]+).*$" + + ; Extract styles to variables. + if IsObject(style1) { + _t := (style1.time != "") ? style1.time : style1.t + _a := (style1.anchor != "") ? style1.anchor : style1.a + _x := (style1.left != "") ? style1.left : style1.x + _y := (style1.top != "") ? style1.top : style1.y + _w := (style1.width != "") ? style1.width : style1.w + _h := (style1.height != "") ? style1.height : style1.h + _r := (style1.radius != "") ? style1.radius : style1.r + _c := (style1.color != "") ? style1.color : style1.c + _m := (style1.margin != "") ? style1.margin : style1.m + _q := (style1.quality != "") ? style1.quality : (style1.q) ? style1.q : style1.SmoothingMode + } else { + _t := ((___ := RegExReplace(style1, q1 "(t(ime)?)" q2, "${value}")) != style1) ? ___ : "" + _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) ? ___ : "" + _q := ((___ := RegExReplace(style1, q1 "(q(uality)?)" q2, "${value}")) != style1) ? ___ : "" + } + + if IsObject(style2) { + t := (style2.time != "") ? style2.time : style2.t + a := (style2.anchor != "") ? style2.anchor : style2.a + x := (style2.left != "") ? style2.left : style2.x + y := (style2.top != "") ? style2.top : style2.y + w := (style2.width != "") ? style2.width : style2.w + h := (style2.height != "") ? style2.height : style2.h + m := (style2.margin != "") ? style2.margin : style2.m + f := (style2.font != "") ? style2.font : style2.f + s := (style2.size != "") ? style2.size : style2.s + c := (style2.color != "") ? style2.color : style2.c + b := (style2.bold != "") ? style2.bold : style2.b + i := (style2.italic != "") ? style2.italic : style2.i + u := (style2.underline != "") ? style2.underline : style2.u + j := (style2.justify != "") ? style2.justify : style2.j + n := (style2.noWrap != "") ? style2.noWrap : style2.n + z := (style2.condensed != "") ? style2.condensed : style2.z + d := (style2.dropShadow != "") ? style2.dropShadow : style2.d + o := (style2.outline != "") ? style2.outline : style2.o + q := (style2.quality != "") ? style2.quality : (style2.q) ? style2.q : style2.TextRenderingHint + } else { + t := ((___ := RegExReplace(style2, q1 "(t(ime)?)" q2, "${value}")) != style2) ? ___ : "" + 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) ? ___ : "" + } + + ; Extract the time variable and save it for a later when we Render() everything. + static times := "(?i)^\s*(\d+)\s*(ms|mil(li(second)?)?|s(ec(ond)?)?|m(in(ute)?)?|h(our)?|d(ay)?)?s?\s*$" + t := (_t) ? _t : t + t := ( t ~= times) ? RegExReplace( t, "\s", "") : 0 ; Default time is zero. + t := ((___ := RegExReplace( t, "i)(\d+)(ms|mil(li(second)?)?)s?$", "$1")) != t) ? ___ * 1 : t + t := ((___ := RegExReplace( t, "i)(\d+)s(ec(ond)?)?s?$" , "$1")) != t) ? ___ * 1000 : t + t := ((___ := RegExReplace( t, "i)(\d+)m(in(ute)?)?s?$" , "$1")) != t) ? ___ * 60000 : t + t := ((___ := RegExReplace( t, "i)(\d+)h(our)?s?$" , "$1")) != t) ? ___ * 3600000 : t + t := ((___ := RegExReplace( t, "i)(\d+)d(ay)?s?$" , "$1")) != t) ? ___ * 86400000 : t + + ; These are the type checkers. + static valid := "(?i)^\s*(\-?(?:(?:\d+(?:\.\d*)?)|(?:\.\d+)))\s*(%|pt|px|vh|vmin|vw)?\s*$" + static valid_positive := "(?i)^\s*((?:(?:\d+(?:\.\d*)?)|(?:\.\d+)))\s*(%|pt|px|vh|vmin|vw)?\s*$" + + ; Define viewport width and height. This is the visible screen area. + vw := 0.01 * ScreenWidth ; 1% of viewport width. + vh := 0.01 * ScreenHeight ; 1% of viewport height. + vmin := (vw < vh) ? vw : vh ; 1vw or 1vh, whichever is smaller. + vr := ScreenWidth / ScreenHeight ; Aspect ratio of the viewport. + + ; Get background width and height. + _w := (_w ~= valid_positive) ? RegExReplace(_w, "\s", "") : "" + _w := (_w ~= "i)(pt|px)$") ? SubStr(_w, 1, -2) : _w + _w := (_w ~= "i)(%|vw)$") ? RegExReplace(_w, "i)(%|vw)$", "") * vw : _w + _w := (_w ~= "i)vh$") ? RegExReplace(_w, "i)vh$", "") * vh : _w + _w := (_w ~= "i)vmin$") ? RegExReplace(_w, "i)vmin$", "") * vmin : _w + + _h := (_h ~= valid_positive) ? RegExReplace(_h, "\s", "") : "" + _h := (_h ~= "i)(pt|px)$") ? SubStr(_h, 1, -2) : _h + _h := (_h ~= "i)vw$") ? RegExReplace(_h, "i)vw$", "") * vw : _h + _h := (_h ~= "i)(%|vh)$") ? RegExReplace(_h, "i)(%|vh)$", "") * vh : _h + _h := (_h ~= "i)vmin$") ? RegExReplace(_h, "i)vmin$", "") * vmin : _h + + ; Save original Graphics settings. + DllCall("gdiplus\GdipGetPixelOffsetMode", "ptr",pGraphics, "int*",PixelOffsetMode) + DllCall("gdiplus\GdipGetCompositingMode", "ptr",pGraphics, "int*",CompositingMode) + DllCall("gdiplus\GdipGetCompositingQuality", "ptr",pGraphics, "int*",CompositingQuality) + DllCall("gdiplus\GdipGetSmoothingMode", "ptr",pGraphics, "int*",SmoothingMode) + DllCall("gdiplus\GdipGetInterpolationMode", "ptr",pGraphics, "int*",InterpolationMode) + DllCall("gdiplus\GdipGetTextRenderingHint", "ptr",pGraphics, "int*",TextRenderingHint) + + ; Get Rendering Quality. + _q := (_q >= 0 && _q <= 4) ? _q : 4 ; Default SmoothingMode is 4 if radius is set. See Draw 1. + q := (q >= 0 && q <= 5) ? q : 4 ; Default TextRenderingHint is 4 (antialias). + ; Anti-Alias = 4, Cleartype = 5 (and gives weird effects.) + + ; Set Graphics settings. + DllCall("gdiplus\GdipSetPixelOffsetMode", "ptr",pGraphics, "int",2) ; Half pixel offset. + ;DllCall("gdiplus\GdipSetCompositingMode", "ptr",pGraphics, "int",1) ; Overwrite/SourceCopy. + DllCall("gdiplus\GdipSetCompositingQuality", "ptr",pGraphics, "int",0) ; AssumeLinear + DllCall("gdiplus\GdipSetSmoothingMode", "ptr",pGraphics, "int",_q) + DllCall("gdiplus\GdipSetInterpolationMode", "ptr",pGraphics, "int",7) ; HighQualityBicubic + DllCall("gdiplus\GdipSetTextRenderingHint", "ptr",pGraphics, "int",q) + + ; Get Font size. + s := (s ~= valid_positive) ? RegExReplace(s, "\s", "") : "2.23vh" ; Default font size is 2.23vh. + s := (s ~= "i)(pt|px)$") ? SubStr(s, 1, -2) : s ; Strip spaces, px, and pt. + s := (s ~= "i)vh$") ? RegExReplace(s, "i)vh$", "") * vh : s ; Relative to viewport height. + s := (s ~= "i)vw$") ? RegExReplace(s, "i)vw$", "") * vw : s ; Relative to viewport width. + s := (s ~= "i)(%|vmin)$") ? RegExReplace(s, "i)(%|vmin)$", "") * vmin : s ; Relative to viewport minimum. + + ; Get Bold, Italic, Underline, NoWrap, and Justification of text. + style := (b) ? 1 : 0 ; bold + style += (i) ? 2 : 0 ; italic + style += (u) ? 4 : 0 ; underline + style += (strikeout) ? 8 : 0 ; strikeout, not implemented. + n := (n) ? 0x4000 | 0x1000 : 0x4000 + j := (j ~= "i)cent(er|re)") ? 1 : (j ~= "i)(far|right)") ? 2 : 0 ; Left/near, center/centre, far/right. + + ; Later when text x and w are finalized and it is found that x + width exceeds the screen, + ; then the _redrawBecauseOfCondensedFont flag is set to true. + static _redrawBecauseOfCondensedFont + if (_redrawBecauseOfCondensedFont == true) + f:=z, z:=0, _redrawBecauseOfCondensedFont := false + + ; Create Font. + f := (f) ? f : "Arial" ; Default font is Arial. + hFamily := (___ := Gdip_FontFamilyCreate(f)) ? ___ : Gdip_FontFamilyCreate("Arial") ; Default font is Arial. + hFont := Gdip_FontCreate(hFamily, s, style) + hFormat := Gdip_StringFormatCreate(n) + Gdip_SetStringFormatAlign(hFormat, j) ; Left = 0, Center = 1, Right = 2 + + ; Simulate string width and height. This will get the exact width and height of the text. + VarSetCapacity(RectF, 16, 0) ; sizeof(RectF) = 16 + if (_w != "") + NumPut(_w, RectF, 8, "float") ; Width + if (_h != "") + NumPut(_h, RectF, 12, "float") ; Height + DllCall("gdiplus\GdipMeasureString" + , "ptr", pGraphics + , "wstr", text + , "int", -1 ; string length. + , "ptr", hFont + , "ptr", &RectF ; (in) layout RectF that bounds the string. + , "ptr", hFormat + , "ptr", &RectF ; (out) simulated RectF that bounds the string. + , "uint*", Chars + , "uint*", Lines) + + ; Get simulated text width and height. + width := NumGet(RectF, 8, "float") + height := NumGet(RectF, 12, "float") + minimum := (width < height) ? width : height + aspect := (height != 0) ? width / height : 0 + + ; Default background width and height. + if (_w == "") + _w := width + if (_h == "") + _h := height + + + + ; Get background anchor. This is where the origin of the image is located. + _a := RegExReplace(_a, "\s", "") + _a := (_a ~= "i)top" && _a ~= "i)left") ? 1 : (_a ~= "i)top" && _a ~= "i)cent(er|re)") ? 2 + : (_a ~= "i)top" && _a ~= "i)right") ? 3 : (_a ~= "i)cent(er|re)" && _a ~= "i)left") ? 4 + : (_a ~= "i)cent(er|re)" && _a ~= "i)right") ? 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 ~= "i)top") ? 2 : (_a ~= "i)left") ? 4 : (_a ~= "i)right") ? 6 : (_a ~= "i)bottom") ? 8 + : (_a ~= "i)cent(er|re)") ? 5 : (_a ~= "^[1-9]$") ? _a : 1 ; Default anchor is top-left. + + ; The anchor can be implied from _x and _y (left, center, right, top, bottom). + _a := (_x ~= "i)left") ? 1+(((_a-1)//3)*3) : (_x ~= "i)cent(er|re)") ? 2+(((_a-1)//3)*3) : (_x ~= "i)right") ? 3+(((_a-1)//3)*3) : _a + _a := (_y ~= "i)top") ? 1+(mod(_a-1,3)) : (_y ~= "i)cent(er|re)") ? 4+(mod(_a-1,3)) : (_y ~= "i)bottom") ? 7+(mod(_a-1,3)) : _a + + ; Convert English words to numbers. Don't mess with these values any further. + _x := (_x ~= "i)left") ? 0 : (_x ~= "i)cent(er|re)") ? 0.5*ScreenWidth : (_x ~= "i)right") ? ScreenWidth : _x + _y := (_y ~= "i)top") ? 0 : (_y ~= "i)cent(er|re)") ? 0.5*ScreenHeight : (_y ~= "i)bottom") ? ScreenHeight : _y + + ; Get _x and _y. + _x := (_x ~= valid) ? RegExReplace(_x, "\s", "") : "" + _x := (_x ~= "i)(pt|px)$") ? SubStr(_x, 1, -2) : _x + _x := (_x ~= "i)(%|vw)$") ? RegExReplace(_x, "i)(%|vw)$", "") * vw : _x + _x := (_x ~= "i)vh$") ? RegExReplace(_x, "i)vh$", "") * vh : _x + _x := (_x ~= "i)vmin$") ? RegExReplace(_x, "i)vmin$", "") * vmin : _x + + _y := (_y ~= valid) ? RegExReplace(_y, "\s", "") : "" + _y := (_y ~= "i)(pt|px)$") ? SubStr(_y, 1, -2) : _y + _y := (_y ~= "i)vw$") ? RegExReplace(_y, "i)vw$", "") * vw : _y + _y := (_y ~= "i)(%|vh)$") ? RegExReplace(_y, "i)(%|vh)$", "") * vh : _y + _y := (_y ~= "i)vmin$") ? RegExReplace(_y, "i)vmin$", "") * vmin : _y + + ; Default x and y. + if (_x == "") + _x := 0.5*ScreenWidth, _a := 2+(((_a-1)//3)*3) + if (_y == "") + _y := 0.5*ScreenHeight, _a := 4+(mod(_a-1,3)) + + ; Now let's modify the _x and _y values with the _anchor, so that the image has a new point of origin. + ; We need our calculated _width and _height for this! + _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 + + ; Prevent half-pixel rendering and keep image sharp. + _w := Round(_x + _w) - Round(_x) ; Use real x2 coordinate to determine width. + _h := Round(_y + _h) - Round(_y) ; Use real y2 coordinate to determine height. + _x := Round(_x) ; NOTE: simple Floor(w) or Round(w) will NOT work. + _y := Round(_y) ; The float values need to be added up and then rounded! + + ; Get the text width and text height. + w := ( w ~= valid_positive) ? RegExReplace( w, "\s", "") : width ; Default is simulated text width. + w := ( w ~= "i)(pt|px)$") ? SubStr( w, 1, -2) : w + w := ( w ~= "i)vw$") ? RegExReplace( w, "i)vw$", "") * vw : w + w := ( w ~= "i)vh$") ? RegExReplace( w, "i)vh$", "") * vh : w + w := ( w ~= "i)vmin$") ? RegExReplace( w, "i)vmin$", "") * vmin : w + w := ( w ~= "%$") ? RegExReplace( w, "%$", "") * 0.01 * _w : w + + h := ( h ~= valid_positive) ? RegExReplace( h, "\s", "") : height ; Default is simulated text height. + h := ( h ~= "i)(pt|px)$") ? SubStr( h, 1, -2) : h + h := ( h ~= "i)vw$") ? RegExReplace( h, "i)vw$", "") * vw : h + h := ( h ~= "i)vh$") ? RegExReplace( h, "i)vh$", "") * vh : h + h := ( h ~= "i)vmin$") ? RegExReplace( h, "i)vmin$", "") * vmin : h + h := ( h ~= "%$") ? RegExReplace( h, "%$", "") * 0.01 * _h : h + + ; If text justification is set but x is not, align the justified text relative to the center + ; or right of the backgound, after taking into account the text width. + if (x == "") + x := (j = 1) ? _x + (_w/2) - (w/2) : (j = 2) ? _x + _w - w : x + + ; Get anchor. + a := RegExReplace( a, "\s", "") + a := (a ~= "i)top" && a ~= "i)left") ? 1 : (a ~= "i)top" && a ~= "i)cent(er|re)") ? 2 + : (a ~= "i)top" && a ~= "i)right") ? 3 : (a ~= "i)cent(er|re)" && a ~= "i)left") ? 4 + : (a ~= "i)cent(er|re)" && a ~= "i)right") ? 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 ~= "i)top") ? 2 : (a ~= "i)left") ? 4 : (a ~= "i)right") ? 6 : (a ~= "i)bottom") ? 8 + : (a ~= "i)cent(er|re)") ? 5 : (a ~= "^[1-9]$") ? a : 1 ; Default anchor is top-left. + + ; Text x and text y can be specified as locations (left, center, right, top, bottom). + ; These location words in text x and text y take precedence over the values in the text anchor. + a := ( x ~= "i)left") ? 1+((( a-1)//3)*3) : ( x ~= "i)cent(er|re)") ? 2+((( a-1)//3)*3) : ( x ~= "i)right") ? 3+((( a-1)//3)*3) : a + a := ( y ~= "i)top") ? 1+(mod( a-1,3)) : ( y ~= "i)cent(er|re)") ? 4+(mod( a-1,3)) : ( y ~= "i)bottom") ? 7+(mod( a-1,3)) : a + + ; Convert English words to numbers. Don't mess with these values any further. + ; Also, these values are relative to the background. + x := ( x ~= "i)left") ? _x : (x ~= "i)cent(er|re)") ? _x + 0.5*_w : (x ~= "i)right") ? _x + _w : x + y := ( y ~= "i)top") ? _y : (y ~= "i)cent(er|re)") ? _y + 0.5*_h : (y ~= "i)bottom") ? _y + _h : y + + ; Get text x and y. + x := ( x ~= valid) ? RegExReplace( x, "\s", "") : _x ; Default text x is background x. + x := ( x ~= "i)(pt|px)$") ? SubStr( x, 1, -2) : x + x := ( x ~= "i)vw$") ? RegExReplace( x, "i)vw$", "") * vw : x + x := ( x ~= "i)vh$") ? RegExReplace( x, "i)vh$", "") * vh : x + x := ( x ~= "i)vmin$") ? RegExReplace( x, "i)vmin$", "") * vmin : x + x := ( x ~= "%$") ? RegExReplace( x, "%$", "") * 0.01 * _w : x + + y := ( y ~= valid) ? RegExReplace( y, "\s", "") : _y ; Default text y is background y. + y := ( y ~= "i)(pt|px)$") ? SubStr( y, 1, -2) : y + y := ( y ~= "i)vw$") ? RegExReplace( y, "i)vw$", "") * vw : y + y := ( y ~= "i)vh$") ? RegExReplace( y, "i)vh$", "") * vh : y + y := ( y ~= "i)vmin$") ? RegExReplace( y, "i)vmin$", "") * vmin : y + y := ( y ~= "%$") ? RegExReplace( y, "%$", "") * 0.01 * _h : y + + ; Modify text x and text y values with the anchor, so that the text has a new point of origin. + ; The text anchor is relative to the text width and height before margin/padding. + ; This is NOT relative to the background width and height. + 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 + + ; Get margin. + m := this.outer.parse.margin_and_padding( m, vw, vh) + _m := this.outer.parse.margin_and_padding(_m, vw, vh, (m.void) ? "1vmin" : "") ; Default margin is 1vmin. + + ; Modify _x, _y, _w, _h with margin and padding, increasing the size of the background. + _w += Round(_m.2) + Round(_m.4) + Round(m.2) + Round(m.4) + _h += Round(_m.1) + Round(_m.3) + Round(m.1) + Round(m.3) + _x -= Round(_m.4) + _y -= Round(_m.1) + + ; If margin/padding are defined in the text parameter, shift the position of the text. + x += Round(m.4) + y += Round(m.1) + + ; Re-run: Condense Text using a Condensed Font if simulated text width exceeds screen width. + if (z) { + if (width + x > ScreenWidth) { + _redrawBecauseOfCondensedFont := true + return this.DrawRaw(pGraphics, ScreenWidth, ScreenHeight, text, style1, style2) + } + } + + ; Define radius of rounded corners. + _r := (_r ~= valid_positive) ? RegExReplace(_r, "\s", "") : 0 ; Default radius is 0, or square corners. + _r := (_r ~= "i)(pt|px)$") ? SubStr(_r, 1, -2) : _r + _r := (_r ~= "i)vw$") ? RegExReplace(_r, "i)vw$", "") * vw : _r + _r := (_r ~= "i)vh$") ? RegExReplace(_r, "i)vh$", "") * vh : _r + _r := (_r ~= "i)vmin$") ? RegExReplace(_r, "i)vmin$", "") * vmin : _r + ; percentage is defined as a percentage of the smaller background width/height. + _r := (_r ~= "%$") ? RegExReplace(_r, "%$", "") * 0.01 * ((_w > _h) ? _h : _w) : _r + ; the radius cannot exceed the half width or half height, whichever is smaller. + _r := (_r <= ((_w > _h) ? _h : _w) / 2) ? _r : 0 + + ; Define color. + _c := this.outer.parse.color(_c, 0xDD424242) ; Default background color is transparent gray. + SourceCopy := (c ~= "i)(delete|eraser?|overwrite|sourceCopy)") ? 1 : 0 ; Eraser brush for text. + if (!c) ; Default text color changes between white and black. + c := (this.outer.parse.grayscale(_c) < 128) ? 0xFFFFFFFF : 0xFF000000 + c := (SourceCopy) ? 0x00000000 : this.outer.parse.color( c) + + ; Define outline and dropShadow. + o := this.outer.parse.outline(o, vw, vh, s, c) + d := this.outer.parse.dropShadow(d, vw, vh, width, height, s) + + + + ; Draw 1 - Background + if (_w && _h && (_c & 0xFF000000)) { + if (_r == 0) + Gdip_SetSmoothingMode(pGraphics, 1) ; Turn antialiasing off if not a rounded rectangle. + pBrush := Gdip_BrushCreateSolid(_c) + Gdip_FillRoundedRectangle(pGraphics, pBrush, _x, _y, _w, _h, _r) ; DRAWING! + Gdip_DeleteBrush(pBrush) + if (_r == 0) + Gdip_SetSmoothingMode(pGraphics, _q) ; Turn antialiasing on for text rendering. + } + + ; Draw 2 - DropShadow + if (!d.void) { + offset2 := d.3 + d.6 + Ceil(0.5*o.1) + + if (d.3) { + DropShadow := Gdip_CreateBitmap(w + 2*offset2, h + 2*offset2) + DropShadowG := Gdip_GraphicsFromImage(DropShadow) + Gdip_SetSmoothingMode(DropShadowG, _q) + Gdip_SetTextRenderingHint(DropShadowG, q) + CreateRectF(RC, offset2, offset2, w + 2*offset2, h + 2*offset2) + } else { + CreateRectF(RC, x + d.1, y + d.2, w, h) + DropShadowG := pGraphics + } + + ; Use Gdip_DrawString if and only if there is a horizontal/vertical offset. + if (o.void && d.6 == 0) + { + pBrush := Gdip_BrushCreateSolid(d.4) + Gdip_DrawString(DropShadowG, Text, hFont, hFormat, pBrush, RC) ; DRAWING! + Gdip_DeleteBrush(pBrush) + } + else ; Otherwise, use the below code if blur, size, and opacity are set. + { + ; Draw the outer edge of the text string. + DllCall("gdiplus\GdipCreatePath", "int",1, "ptr*",pPath) + DllCall("gdiplus\GdipAddPathString" + , "ptr", pPath + , "wstr", text + , "int", -1 + , "ptr", hFamily + , "int", style + , "float", s + , "ptr", &RC + , "ptr", hFormat) + ;DllCall("gdiplus\GdipWindingModeOutline", "ptr",pPath) ;BROKEN + pPen := Gdip_CreatePen(d.4, 2*d.6 + o.1) + DllCall("gdiplus\GdipSetPenLineJoin", "ptr",pPen, "uint",2) + DllCall("gdiplus\GdipDrawPath", "ptr",DropShadowG, "ptr",pPen, "ptr",pPath) + Gdip_DeletePen(pPen) + + ; Fill in the outline. Turn off antialiasing and alpha blending so the gaps are 100% filled. + pBrush := Gdip_BrushCreateSolid(d.4) + Gdip_SetCompositingMode(DropShadowG, 1) ; Turn off alpha blending + Gdip_SetSmoothingMode(DropShadowG, 3) ; Turn off anti-aliasing + Gdip_FillPath(DropShadowG, pBrush, pPath) + Gdip_DeleteBrush(pBrush) + Gdip_DeletePath(pPath) + Gdip_SetCompositingMode(DropShadowG, 0) + Gdip_SetSmoothingMode(DropShadowG, _q) + } + + if (d.3) { + Gdip_DeleteGraphics(DropShadowG) + this.outer.filter.GaussianBlur(DropShadow, d.3, d.5) + Gdip_SetInterpolationMode(pGraphics, 5) ; NearestNeighbor + Gdip_SetSmoothingMode(pGraphics, 3) ; Turn off anti-aliasing + Gdip_DrawImage(pGraphics, DropShadow, x + d.1 - offset2, y + d.2 - offset2, w + 2*offset2, h + 2*offset2) ; DRAWING! + Gdip_SetSmoothingMode(pGraphics, _q) + Gdip_DisposeImage(DropShadow) + } + } + + ; Draw 3 - Text Outline + if (!o.void) { + ; Convert our text to a path. + CreateRectF(RC, x, y, w, h) + DllCall("gdiplus\GdipCreatePath", "int",1, "ptr*",pPath) + DllCall("gdiplus\GdipAddPathString" + , "ptr", pPath + , "wstr", text + , "int", -1 + , "ptr", hFamily + , "int", style + , "float", s + , "ptr", &RC + , "ptr", hFormat) + + ; Create a glow effect around the edges. + if (o.3) { + Gdip_SetClipPath(pGraphics, pPath, 3) ; Exclude original text region from being drawn on. + pPenGlow := Gdip_CreatePen(Format("0x{:02X}",((o.4 & 0xFF000000) >> 24)/o.3) . Format("{:06X}",(o.4 & 0x00FFFFFF)), 1) + DllCall("gdiplus\GdipSetPenLineJoin", "ptr",pPenGlow, "uint",2) + + o3 := o.3 + _o3 := (A_AhkVersion < 2) ? o3 : "o3" + loop %_o3% + { + DllCall("gdiplus\GdipSetPenWidth", "ptr",pPenGlow, "float",o.1 + 2*A_Index) + DllCall("gdiplus\GdipDrawPath", "ptr",pGraphics, "ptr",pPenGlow, "ptr",pPath) ; DRAWING! + } + Gdip_DeletePen(pPenGlow) + Gdip_ResetClip(pGraphics) + } + + ; Draw outline text. + if (o.1) { + pPen := Gdip_CreatePen(o.2, o.1) + DllCall("gdiplus\GdipSetPenLineJoin", "ptr",pPen, "uint",2) + DllCall("gdiplus\GdipDrawPath", "ptr",pGraphics, "ptr",pPen, "ptr",pPath) ; DRAWING! + Gdip_DeletePen(pPen) + } + + ; Fill outline text. + pBrush := Gdip_BrushCreateSolid(c) + Gdip_SetCompositingMode(pGraphics, SourceCopy) + Gdip_FillPath(pGraphics, pBrush, pPath) ; DRAWING! + Gdip_SetCompositingMode(pGraphics, 0) + Gdip_DeleteBrush(pBrush) + Gdip_DeletePath(pPath) + } + + ; Draw text only when outline has not filled in the text. + if (text != "" && o.void) { + DllCall("gdiplus\GdipSetCompositingMode", "ptr",pGraphics, "int",SourceCopy) + + VarSetCapacity(RectF, 16) ; sizeof(RectF) = 16 + , NumPut( x, RectF, 0, "float") ; Left + , NumPut( y, RectF, 4, "float") ; Top + , NumPut( w, RectF, 8, "float") ; Width + , NumPut( h, RectF, 12, "float") ; Height + + DllCall("gdiplus\GdipMeasureString" + , "ptr", pGraphics + , "wstr", text + , "int", -1 ; string length. + , "ptr", hFont + , "ptr", &RectF ; (in) layout RectF that bounds the string. + , "ptr", hFormat + , "ptr", &RectF ; (out) simulated RectF that bounds the string. + , "uint*", Chars + , "uint*", Lines) + + pBrush := Gdip_BrushCreateSolid(c) + DllCall("gdiplus\GdipDrawString" + , "ptr", pGraphics + , "wstr", text + , "int", -1 + , "ptr", hFont + , "ptr", &RectF + , "ptr", hFormat + , "ptr", pBrush) + Gdip_DeleteBrush(pBrush) + + x := NumGet(RectF, 0, "float") + y := NumGet(RectF, 4, "float") + w := NumGet(RectF, 8, "float") + h := NumGet(RectF, 12, "float") + } + + ; Delete Font Objects. + Gdip_DeleteStringFormat(hFormat) + Gdip_DeleteFont(hFont) + Gdip_DeleteFontFamily(hFamily) + + ; Set Graphics settings. + ;DllCall("gdiplus\GdipSetPixelOffsetMode", "ptr",pGraphics, "int",2) ; Half pixel offset. + ;DllCall("gdiplus\GdipSetCompositingMode", "ptr",pGraphics, "int",1) ; Overwrite/SourceCopy. + ;DllCall("gdiplus\GdipSetCompositingQuality", "ptr",pGraphics, "int",0) ; AssumeLinear + ;DllCall("gdiplus\GdipSetSmoothingMode", "ptr",pGraphics, "int",_q) + ;DllCall("gdiplus\GdipSetInterpolationMode", "ptr",pGraphics, "int",7) ; HighQualityBicubic + ;DllCall("gdiplus\GdipSetTextRenderingHint", "ptr",pGraphics, "int",q) + + ; Restore original Graphics settings. + DllCall("gdiplus\GdipSetPixelOffsetMode", "ptr",pGraphics, "int",PixelOffsetMode) + DllCall("gdiplus\GdipSetCompositingMode", "ptr",pGraphics, "int",CompositingMode) + DllCall("gdiplus\GdipSetCompositingQuality", "ptr",pGraphics, "int",CompositingQuality) + DllCall("gdiplus\GdipSetSmoothingMode", "ptr",pGraphics, "int",SmoothingMode) + DllCall("gdiplus\GdipSetInterpolationMode", "ptr",pGraphics, "int",InterpolationMode) + + ; Define bounds. BROKEN!!!! + t_bound := t + x_bound := _x + y_bound := _y + w_bound := _w + h_bound := _h + + o_bound := Ceil(0.5 * o.1 + o.3) ; outline boundary. + x_bound := (x - o_bound < x_bound) ? x - o_bound : x_bound + y_bound := (y - o_bound < y_bound) ? y - o_bound : y_bound + w_bound := (w + 2 * o_bound > w_bound) ? w + 2 * o_bound : w_bound + h_bound := (h + 2 * o_bound > h_bound) ? h + 2 * o_bound : h_bound + ;Tooltip % x_bound ", " y_bound ", " w_bound ", " h_bound + d_bound := Ceil(0.5 * o.1 + d.3 + d.6) ; dropShadow boundary. + x_bound := (x + d.1 - d_bound < x_bound) ? x + d.1 - d_bound : x_bound + y_bound := (y + d.2 - d_bound < y_bound) ? y + d.2 - d_bound : y_bound + w_bound := (w + 2 * d_bound > w_bound) ? w + 2 * d_bound : w_bound + h_bound := (h + 2 * d_bound > h_bound) ? h + 2 * d_bound : h_bound + + return {0:t_bound, 1:x_bound, 2:y_bound, 3:w_bound, 4:h_bound} + } + } ; End of TextRenderer class. + + class INTERACTIVE { + + ; BufferMode + ; None = 0 - No buffer is created. Instead the image is repeatedly rendered to screen. + ; FixedSize = 1 - The size of the screen and buffer bitmaps are minimized to save memory. + ; Because the bitmap representing the screen does not cover the whole screen, + ; interactions are limited only to movement and other actions which do not + ; change the size of the image to be displayed. + ; SmallBuffer = 2 - The buffer bitmap contains only the pixels that contain data. The screen + ; bitmap is fullsized. All user interactions can be applied. + ; FullScreen = 3 - Both the buffer and the screen bitmaps are full and equal in size. + ; This is a special mode that allows the buffer bitmap to be drawn on + ; by EXTERNAL programs. Call .Push() after drawing into the buffer to update + ; the screen. + __New(efx, keybinds := "", BufferMode := 2, NoTimers := 0) { + ; efx is the original graphics object. + this.efx := efx + this.keybinds := keybinds + this.key := {} + + ; Derive GDI+ objects from the original graphics object. + this.hdc := CreateCompatibleDC(this.efx.hdc) + this.hbm := CreateDIBSection(this.efx.BitmapWidth, -this.efx.BitmapHeight, this.hdc, 32, pBits) + this.obm := SelectObject(this.hdc, this.hbm) + this.gfx := Gdip_GraphicsFromHDC(this.hdc) + this.pBits := pBits + + ; Begin the timer loop that checks for user interactions. + if !(NoTimers) { + observe := ObjBindMethod(this, "observe") + _observe := (A_AhkVersion < 2) ? observe : "observe" + SetTimer %_observe%, -10 + } + + ; Enumeration of this object. + ;[efx, final, gfx, hbm, hdc, key, keybinds, obm, pBits, _move, _scale, _x, _y] + ;[__call, __class, __delete, __new, move, observe, paint, scale] + return this + } + + __Delete() { + Gdip_DeleteGraphics(this.gfx) + SelectObject(this.hdc, this.obm) + DeleteObject(this.hbm) + DeleteDC(this.hdc) + } + + ; Delegate any unknown calls to the original graphics object. + __Call(self, terms*) { + if !(this.base.haskey(self)) + return (this.efx)[self](terms*) + } + + ; Timer loop that checks for user interactions. + observe() { + Critical ; Insure this thread cannot be interrupted. + + ; Pull. "final" is like a certificate of authenticity. + if (this.final != this.efx.final) { + ; Extract transformations + if (this.locked) { + ;this.x_ := this.dx1 - this.dx0 + ;this.y_ := this.dy1 - this.dy0 + } else { + this.x_ := (this.x == "") ? 0 : this.x_ + this.x - this.x_save + this.y_ := (this.y == "") ? 0 : this.y_ + this.y - this.y_save + this.w_ := (this.w == "") ? 0 : this.w_ + (this.x + this.w) - (this.x_save + this.w_save) + this.h_ := (this.h == "") ? 0 : this.h_ + (this.y + this.h) - (this.y_save + this.h_save) + } + + ; Clone + this.x := this.x_save := this.efx.x + this.y := this.y_save := this.efx.y + this.w := this.w_save := this.efx.w + this.h := this.h_save := this.efx.h + + ; X/y WOrks fine. + if (this.locked) { + this.x0 := this.efx.x + this.y0 := this.efx.y + this.w0 := this.efx.w + this.h0 := this.efx.h + } + ; Update certificate to prevent this routine from being called again. + this.final := this.efx.final + + ; Refresh the screen. + this.Paint() + } + + ; Get mouse coordinates as dx1, dy1. + Mouse := "Mouse", Screen := "Screen" + CoordMode Mouse, Screen + MouseGetPos dx1, dy1 + this.dx1 := dx1 + this.dy1 := dy1 + + ; Get keys states and save them to determine their derivatives. + this.key.LButton := GetKeyState("LButton", "P") + this.key.RButton := GetKeyState("RButton", "P") + this.key.Space := GetKeyState("Space", "P") + this.key.Control := GetKeyState("Control", "P") + this.key.Alt := GetKeyState("Alt", "P") + this.key.Shift := GetKeyState("Shift", "P") + + ; Derive key states. 0 = off, 2 = pressed. 1 & 3 are derivatives. + this._scale := (this.key.Control && this.key.LButton + && DllCall("GetForegroundWindow") == this.efx.hwnd) + ? ((!this._scale) ? 1 : 2) : ((this._scale == 2) ? 3 : 0) + + this._drag := (this.key.Alt && this.key.LButton) + && !this._scale + ? ((!this._drag) ? 1 : 2) : ((this._drag == 2) ? 3 : 0) + + this._stretch := (this.key.Shift && this.key.LButton + && !this._scale + && !this._drag + && DllCall("GetForegroundWindow") == this.efx.hwnd) + ? ((!this._stretch) ? 1 : 2) : ((this._stretch == 2) ? 3 : 0) + + this._move := (this.key.LButton + && !this._scale && !this._stretch + && !this._drag + && DllCall("GetForegroundWindow") == this.efx.hwnd) + ? ((!this._move) ? 1 : 2) : ((this._move = 2) ? 3 : 0) + + ; Run once when 0 -> 2. + if (this._scale == 1) { + this.locked := true + this.x_locked := this.efx.x + this.y_locked := this.efx.y + this.w_locked := this.efx.w + this.h_locked := this.efx.h + this.dx0 := this.dx1 + this.dy0 := this.dy1 + this.x0 := this.x + this.y0 := this.y + this.w0 := this.w + this.h0 := this.h + } + + ; Run continuously. + if (this._scale == 2) { + dx := this.dx1 - this.dx0 + dy := this.dy1 - this.dy0 + rx := ((this.w0 < 0) ? -1 : 1)*(this.dx0 - this.x0 - (this.w0 / 2)) + ry := ((this.h0 < 0) ? -1 : 1)*(this.y0 - this.dy0 + (this.h0 / 2)) + + if (rx < -1 && ry > 1) { + r := "top left" + x := this.x0 + dx + y := this.y0 + dy + w := this.w0 - dx + h := this.h0 - dy + } + if (rx >= -1 && ry > 1) { + r := "top right" + x := this.x0 + y := this.y0 + dy + w := this.w0 + dx + h := this.h0 - dy + } + if (rx < -1 && ry <= 1) { + r := "bottom left" + x := this.x0 + dx + y := this.y0 + w := this.w0 - dx + h := this.h0 + dy + } + if (rx >= -1 && ry <= 1) { + r := "bottom right" + x := this.x0 + y := this.y0 + w := this.w0 + dx + h := this.h0 + dy + } + this.Scale(x, y, w, h) + } + + ; Run once when 2 -> 0. + if (this._scale == 3) { + this.locked := false + } + + ; Run once when 0 -> 2. + if (this._drag == 1) { + this.locked := true + this.x_locked := this.efx.x + this.y_locked := this.efx.y + this.w_locked := this.efx.w + this.h_locked := this.efx.h + this.dx0 := this.dx1 + this.dy0 := this.dy1 + this.x0 := this.x + this.y0 := this.y + this.w0 := this.w + this.h0 := this.h + } + + ; Run continuously. + if (this._drag == 2) { + cx := (this.dx1 > this.dx0) ? true : false + cy := (this.dy1 > this.dy0) ? true : false + + if (cx && cy) { + r := "bottom right" + x := this.dx0 + y := this.dy0 + w := Abs(this.dx1 - this.dx0) + h := Abs(this.dy1 - this.dy0) + } + if (!cx && cy) { + r := "bottom left" + x := this.dx1 + y := this.dy0 + w := Abs(this.dx1 - this.dx0) + h := Abs(this.dy1 - this.dy0) + } + if (cx && !cy) { + r := "top right" + x := this.dx0 + y := this.dy1 + w := Abs(this.dx1 - this.dx0) + h := Abs(this.dy1 - this.dy0) + } + if (!cx && !cy) { + r := "top left" + x := this.dx1 + y := this.dy1 + w := Abs(this.dx1 - this.dx0) + h := Abs(this.dy1 - this.dy0) + } + this.Scale(x, y, w, h) + } + + ; Run once when 2 -> 0. + if (this._drag == 3) { + this.locked := false + } + + ; Run once when 0 -> 2. + if (this._stretch == 1) { + this.locked := true + this.x_locked := this.efx.x + this.y_locked := this.efx.y + this.w_locked := this.efx.w + this.h_locked := this.efx.h + this.dx0 := this.dx1 + this.dy0 := this.dy1 + this.x0 := this.x + this.y0 := this.y + this.w0 := this.w + this.h0 := this.h + } + + ; Run continuously. + if (this._stretch == 2) { + dx := this.dx1 - this.dx0 + dy := this.dy1 - this.dy0 ; I don't know why 2 of this.h0 works. + rx := ((this.h0 < 0) ? -1 : 1)*(this.dx0 - this.x0 - (this.w0 / 2)) + ry := ((this.h0 < 0) ? -1 : 1)*(this.y0 - this.dy0 + (this.h0 / 2)) + m := -(this.h0 / this.w0) ; slope (dy/dx) + + if (m * rx >= ry && ry > -m * rx) { + r := "left" + x := this.x0 + dx + y := this.y0 + w := this.w0 - dx + h := this.h0 + } + if (m * rx < ry && ry > -m * rx) { + r := "top" + x := this.x0 + y := this.y0 + dy + w := this.w0 + h := this.h0 - dy + } + if (m * rx < ry && ry <= -m * rx) { + r := "right" + x := this.x0 + y := this.y0 + w := this.w0 + dx + h := this.h0 + } + if (m * rx >= ry && ry <= -m * rx) { + r := "bottom" + x := this.x0 + y := this.y0 + w := this.w0 + h := this.h0 + dy + } + + this.Scale(x, y, w, h) + } + + if (this._stretch == 3) { + this.locked := false + } + + if (this._move == 1) { + this.locked := true + this.x_locked := this.efx.x + this.y_locked := this.efx.y + this.w_locked := this.efx.w + this.h_locked := this.efx.h + this.dx0 := this.dx1 + this.dy0 := this.dy1 + this.x0 := this.x + this.y0 := this.y + this.w0 := this.w + this.h0 := this.h + } + + if (this._move == 2) { + dx := this.dx1 - this.dx0 + dy := this.dy1 - this.dy0 + this.Move(this.x0 + dx, this.y0 + dy) + } + + if (this._move == 3) { + this.locked := false + } + + ;Tooltip % "Mouse:`t" this.dx1 ", " this.dy1 + ; . "`nInital:`t" this.dx0 ", " this.dy0 + ; . "`nObject:`t" this.x ", " this.y ", " this.w ", " this.h + ; . "`nLast:`t" this.x0 ", " this.y0 ", " this.w0 ", " this.h0 + + observe := ObjBindMethod(this, "observe") + _observe := (A_AhkVersion < 2) ? observe : "observe" + SetTimer %_observe%, -10 + } + + Move(x, y) { + this.x := x, this.y := y + this.Paint() + } + + Scale(x, y, w, h) { + this.x := x, this.y := y, this.w := w, this.h := h + this.Paint() + ;Gdip_GraphicsClear(this.gfx) + ;StretchBlt(this.hdc, this.x, this.y, this.w, this.h, this.efx.hdc, this.efx.x, this.efx.y, this.efx.w, this.efx.h) + ;UpdateLayeredWindow(this.efx.hwnd, this.hdc, this.efx.BitmapLeft, this.efx.BitmapTop, this.efx.BitmapWidth, this.efx.BitmapHeight) + } + + Paint() { + Gdip_GraphicsClear(this.gfx) + if !(this.w == this.efx.w && this.h == this.efx.h) + StretchBlt(this.hdc, this.x + this.x_ - this.w_/2, this.y + this.y_ - this.h_/2, this.w + this.w_/2, this.h + this.h_/2, this.efx.hdc, this.efx.x, this.efx.y, this.efx.w, this.efx.h) + else + BitBlt(this.hdc, this.x + this.x_, this.y + this.y_, this.efx.w, this.efx.h, this.efx.hdc, this.efx.x, this.efx.y) + UpdateLayeredWindow(this.efx.hwnd, this.hdc, this.efx.BitmapLeft, this.efx.BitmapTop, this.efx.BitmapWidth, this.efx.BitmapHeight) + } + + ; Mouse + ; Move + ; Resize + ; Scale + ; Stretch + ; Rotate + + ; Keys + ; Rotate90 + ; Rotate180 + ; Move5 + ; Move10 + ; Mirror + ; Fade + ; Blur + + } ; End of INTERACTIVE class. + + class SEQUENCER { + + } ; End of SEQUENCER class. + + class SYNTHESIS { + ; the synthesis class contains all the lower functions. + ; DrawText(), DrawPolygon(), DrawImage(), DrawVideo()? + ; QueueText(), QueuePolygon(), QueueImage(), QueueVideo(), Flush() + ; RenderText(), RenderPolygon(), RenderImage(), RenderVideo() + ; Render() - which throws if you try to pass any parameters to it. + ; SYNTHESIS will auto allocate memories. + ; It will disallow stretching? Might be posible to keep it. Or not. + } ; End of SYNTHESIS class. +} diff --git a/tirabolas 3000.ahk b/tirabolas 3000.ahk new file mode 100644 index 0000000..76230e4 --- /dev/null +++ b/tirabolas 3000.ahk @@ -0,0 +1,78 @@ +#MaxThreadsPerHotkey 2 +#SingleInstance +#Include +#Persistent + +Menu, Tray, NoStandard +Menu, Tray, Icon, Resources\icon.ico +Menu, Tray, Add, Bloquear posición, MenuHandler +_Toggle := false ; 0 +Menu, Tray, Add, Salir, Exit + +RenderImageI("Resources\Tutorial.png", "time:" 3300 " x:50%vw") +Sleep, 3300 + ++S:: +RenderImageI("Resources\Saliendo.png", "time:" 2200 " x:50%vw") +Sleep, 2200 +ExitApp + +F1:: +{ + Toggle := !Toggle ; Toggle hotkey check + If (Toggle != false) + { + RenderImageI("Resources\Tirabolas.png", "time:" 720 " x:50%vw") + Sleep, 720 + } + While, Toggle + { + If (_Toggle != false) ; 1 + { + BlockInput, MouseMove + } + Else ; 0 + { + BlockInput, MouseMoveOff + } + Send t + Sleep, 100 + Send {Click} + } +} +BlockInput, MouseMoveOff +Return + +F2:: +If (_Toggle != false) ; 1 +{ + _Toggle := false ; 0 + RenderImageI("Resources\Desbloqueado.png", "time:" 350 " x:50%vw") + Menu, Tray, UnCheck, Bloquear posición +} +Else +{ + _Toggle := true ; 1 + RenderImageI("Resources\Bloqueado.png", "time:" 350 " x:50%vw") + Menu, Tray, Check, Bloquear posición +} + +Return + + +MenuHandler: +If (_Toggle != false) +{ + _Toggle := false ; 0 + Menu, Tray, UnCheck, Bloquear posición +} +Else +{ + _Toggle := true ; 1 + Menu, Tray, Check, Bloquear posición +} +Return + +!F4:: +Exit: +ExitApp \ No newline at end of file diff --git a/tirabolas 3000.exe b/tirabolas 3000.exe new file mode 100644 index 0000000..424638a Binary files /dev/null and b/tirabolas 3000.exe differ