That version of gdi+ hasn't been updated in ages.
Code: Select all
#Requires AutoHotkey v2.0
DllCall("LoadLibrary", "str", "gdiplus")
si := Buffer(A_PtrSize = 4 ? 16:24, 0) ; sizeof(GdiplusStartupInput) = 16, 24
NumPut("uint", 0x1, si)
DllCall("gdiplus\GdiplusStartup", "ptr*", &pToken:=0, "ptr", si, "ptr", 0)
show(from_screenshot([0, 0, 100, 100]))
show(pBitmap, title := "", pos := "", style := 0x90000000, styleEx := 0x80088, parent := "") {
; Window Styles - https://docs.microsoft.com/en-us/windows/win32/winmsg/window-styles
WS_POPUP := 0x80000000 ; Allow small windows.
WS_VISIBLE := 0x10000000 ; Show on creation.
; Extended Window Styles - https://docs.microsoft.com/en-us/windows/win32/winmsg/extended-window-styles
WS_EX_TOPMOST := 0x8 ; Always on top.
WS_EX_TOOLWINDOW := 0x80 ; Hides from Alt+Tab menu. Removes small icon.
WS_EX_LAYERED := 0x80000 ; For UpdateLayeredWindow.
; Default styles can be overwritten by previous functions.
(style == "") && style := WS_POPUP | WS_VISIBLE
(styleEx == "") && styleEx := WS_EX_TOPMOST | WS_EX_TOOLWINDOW | WS_EX_LAYERED
; Get Bitmap width and height.
DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", &width:=0)
DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", &height:=0)
; Get Screen width and height with DPI awareness.
dpi := DllCall("SetThreadDpiAwarenessContext", "ptr", -3, "ptr")
ScreenWidth := A_ScreenWidth
ScreenHeight := A_ScreenHeight
DllCall("SetThreadDpiAwarenessContext", "ptr", dpi, "ptr")
; If both dimensions exceed the screen boundaries, compare the aspect ratio of the image
; to the aspect ratio of the screen to determine the scale factor. Default scale is 1.
s := (width > ScreenWidth) && (width / height > ScreenWidth / ScreenHeight) ? ScreenWidth / width
: (height > ScreenHeight) && (width / height <= ScreenWidth / ScreenHeight) ? ScreenHeight / height
: 1
w := IsObject(pos) && pos.Has(3) ? pos[3] : s * width
h := IsObject(pos) && pos.Has(4) ? pos[4] : s * height
x := IsObject(pos) && pos.Has(1) ? pos[1] : 0.5*(ScreenWidth - w)
y := IsObject(pos) && pos.Has(2) ? pos[2] : 0.5*(ScreenHeight - h)
; Resolve dependent coordinates first, coordinates second, and distances last.
x2 := Round(x + w)
y2 := Round(y + h)
x := Round(x)
y := Round(y)
w := x2 - x
h := y2 - y
; Convert the source pBitmap into a hBitmap manually.
; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
bi := Buffer(40, 0) ; sizeof(bi) = 40
NumPut( "uint", 40, bi, 0) ; Size
NumPut( "int", w, bi, 4) ; Width
NumPut( "int", -h, bi, 8) ; Height - Negative so (0, 0) is top-left.
NumPut("ushort", 1, bi, 12) ; Planes
NumPut("ushort", 32, bi, 14) ; BitCount / BitsPerPixel
hbm := DllCall("CreateDIBSection", "ptr", hdc, "ptr", bi, "uint", 0, "ptr*", &pBits:=0, "ptr", 0, "uint", 0, "ptr")
obm := DllCall("SelectObject", "ptr", hdc, "ptr", hbm, "ptr")
; Case 1: Image is not scaled.
if (s = 1) {
; Transfer data from source pBitmap to an hBitmap manually.
Rect := Buffer(16, 0) ; sizeof(Rect) = 16
NumPut( "uint", width, Rect, 8) ; Width
NumPut( "uint", height, Rect, 12) ; Height
BitmapData := Buffer(16+2*A_PtrSize, 0) ; sizeof(BitmapData) = 24, 32
NumPut( "int", 4 * width, BitmapData, 8) ; Stride
NumPut( "ptr", pBits, BitmapData, 16) ; Scan0
DllCall("gdiplus\GdipBitmapLockBits"
, "ptr", pBitmap
, "ptr", Rect
, "uint", 5 ; ImageLockMode.UserInputBuffer | ImageLockMode.ReadOnly
, "int", 0xE200B ; Format32bppPArgb
, "ptr", BitmapData) ; Contains the pointer (pBits) to the hbm.
DllCall("gdiplus\GdipBitmapUnlockBits", "ptr", pBitmap, "ptr", BitmapData)
}
; Case 2: Image is scaled.
else {
; Create a graphics context from the device context.
DllCall("gdiplus\GdipCreateFromHDC", "ptr", hdc , "ptr*", &pGraphics:=0)
; Set settings in graphics context.
DllCall("gdiplus\GdipSetPixelOffsetMode", "ptr", pGraphics, "int", 2) ; Half pixel offset.
DllCall("gdiplus\GdipSetCompositingMode", "ptr", pGraphics, "int", 1) ; Overwrite/SourceCopy.
DllCall("gdiplus\GdipSetInterpolationMode", "ptr", pGraphics, "int", 7) ; HighQualityBicubic
; Draw Image.
DllCall("gdiplus\GdipCreateImageAttributes", "ptr*", &ImageAttr:=0)
DllCall("gdiplus\GdipSetImageAttributesWrapMode", "ptr", ImageAttr, "int", 3) ; WrapModeTileFlipXY
DllCall("gdiplus\GdipDrawImageRectRectI"
, "ptr", pGraphics
, "ptr", pBitmap
, "int", 0, "int", 0, "int", w, "int", h ; destination rectangle
, "int", 0, "int", 0, "int", width, "int", height ; source rectangle
, "int", 2
, "ptr", ImageAttr
, "ptr", 0
, "ptr", 0)
DllCall("gdiplus\GdipDisposeImageAttributes", "ptr", ImageAttr)
; Clean up the graphics context.
DllCall("gdiplus\GdipDeleteGraphics", "ptr", pGraphics)
}
dpi := DllCall("SetThreadDpiAwarenessContext", "ptr", -3, "ptr")
hwnd := DllCall("CreateWindowEx"
, "uint", styleEx | WS_EX_LAYERED ; dwExStyle
, "str", WindowClass() ; lpClassName
, "str", title ; lpWindowName
, "uint", style ; dwStyle
, "int", x
, "int", y
, "int", w
, "int", h
, "ptr", (parent != "") ? parent : A_ScriptHwnd
, "ptr", 0 ; hMenu
, "ptr", 0 ; hInstance
, "ptr", 0 ; lpParam
, "ptr")
DllCall("SetThreadDpiAwarenessContext", "ptr", dpi, "ptr")
; Draw the contents of the device context onto the layered window.
DllCall("UpdateLayeredWindow"
, "ptr", hwnd ; hWnd
, "ptr", 0 ; hdcDst
, "ptr", 0 ; *pptDst
,"uint64*", w | h << 32 ; *psize
, "ptr", hdc ; hdcSrc
, "int64*", 0 ; *pptSrc
, "uint", 0 ; crKey
, "uint*", 0xFF << 16 | 0x01 << 24 ; *pblend
, "uint", 2) ; dwFlags
; Cleanup the hBitmap and device contexts.
DllCall("SelectObject", "ptr", hdc, "ptr", obm)
DllCall("DeleteObject", "ptr", hbm)
DllCall("DeleteDC", "ptr", hdc)
return hwnd
}
WindowClass(style := 0) {
; The window class shares the name of this class.
cls := "iseahound's window code"
wc := Buffer(A_PtrSize = 4 ? 48:80) ; sizeof(WNDCLASSEX) = 48, 80
; Check if the window class is already registered.
hInstance := DllCall("GetModuleHandle", "ptr", 0, "ptr")
if DllCall("GetClassInfoEx", "ptr", hInstance, "str", cls, "ptr", wc)
return cls
; Create window data.
pWndProc := CallbackCreate(WindowProc)
hCursor := DllCall("LoadCursor", "ptr", 0, "ptr", 32512, "ptr") ; IDC_ARROW
hBrush := DllCall("GetStockObject", "int", 5, "ptr") ; Hollow_brush
; 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
_ := (A_PtrSize = 4)
NumPut( "uint", wc.size, wc, 0) ; cbSize
NumPut( "uint", style, wc, 4) ; style
NumPut( "ptr", pWndProc, wc, 8) ; lpfnWndProc
NumPut( "int", 0, wc, _ ? 12:16) ; cbClsExtra
NumPut( "int", 40, wc, _ ? 16:20) ; cbWndExtra
NumPut( "ptr", 0, wc, _ ? 20:24) ; hInstance
NumPut( "ptr", 0, wc, _ ? 24:32) ; hIcon
NumPut( "ptr", hCursor, wc, _ ? 28:40) ; hCursor
NumPut( "ptr", hBrush, wc, _ ? 32:48) ; hbrBackground
NumPut( "ptr", 0, wc, _ ? 36:56) ; lpszMenuName
NumPut( "ptr", StrPtr(cls), wc, _ ? 40:64) ; lpszClassName
NumPut( "ptr", 0, wc, _ ? 44:72) ; hIconSm
; Registers a window class for subsequent use in calls to the CreateWindow or CreateWindowEx function.
DllCall("RegisterClassEx", "ptr", wc, "ushort")
; Return the class name as a string.
return cls
; Define window behavior.
WindowProc(hwnd, uMsg, wParam, lParam) {
; Prevent the script from exiting early.
static active_windows := Persistent()
; WM_CREATE
if (uMsg = 0x1)
Persistent(++active_windows)
; WM_DESTROY
if (uMsg = 0x2) {
Persistent(--active_windows)
}
; WM_LBUTTONDOWN
if (uMsg = 0x201) {
return DllCall("DefWindowProc", "ptr", hwnd, "uint", 0xA1, "uptr", 2, "ptr", 0, "ptr")
}
; WM_RBUTTONUP
if (uMsg = 0x205) {
DllCall("DestroyWindow", "ptr", hwnd)
return 0
}
; Must return
return DllCall("DefWindowProc", "ptr", hwnd, "uint", uMsg, "uptr", wParam, "ptr", lParam, "ptr")
}
}
from_screenshot(image) {
; Thanks tic - https://www.autohotkey.com/boards/viewtopic.php?t=6517
if !IsObject(image) {
dpi := DllCall("SetThreadDpiAwarenessContext", "ptr", -3, "ptr")
WinGetClientPos &x, &y, &w, &h, image
DllCall("SetThreadDpiAwarenessContext", "ptr", dpi, "ptr")
image := [x, y, w, h]
}
; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
bi := Buffer(40, 0) ; sizeof(bi) = 40
NumPut( "uint", 40, bi, 0) ; Size
NumPut( "int", image[3], bi, 4) ; Width
NumPut( "int", -image[4], bi, 8) ; Height - Negative so (0, 0) is top-left.
NumPut("ushort", 1, bi, 12) ; Planes
NumPut("ushort", 32, bi, 14) ; BitCount / BitsPerPixel
hbm := DllCall("CreateDIBSection", "ptr", hdc, "ptr", bi, "uint", 0, "ptr*", &pBits:=0, "ptr", 0, "uint", 0, "ptr")
obm := DllCall("SelectObject", "ptr", hdc, "ptr", hbm, "ptr")
; Retrieve the device context for the screen.
sdc := DllCall("GetDC", "ptr", 0, "ptr")
; Copies a portion of the screen to a new device context.
DllCall("gdi32\BitBlt"
, "ptr", hdc, "int", 0, "int", 0, "int", image[3], "int", image[4]
, "ptr", sdc, "int", image[1], "int", image[2], "uint", 0x00CC0020 | 0x40000000) ; SRCCOPY | CAPTUREBLT
; Release the device context to the screen.
DllCall("ReleaseDC", "ptr", 0, "ptr", sdc)
; Convert the hBitmap to a Bitmap using a built in function as there is no transparency.
DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", "ptr", hbm, "ptr", 0, "ptr*", &pBitmap:=0)
; Cleanup the hBitmap and device contexts.
DllCall("SelectObject", "ptr", hdc, "ptr", obm)
DllCall("DeleteObject", "ptr", hbm)
DllCall("DeleteDC", "ptr", hdc)
return pBitmap
}
Is it the window styles? If you want to test your theory set the scale (variable s) to something other than 1 in my script. Then it will use GDI+ pGraphics to scale the object.