[a122] ImagePut - Windows Image Transformation Library

Share the finished AutoHotkey v2 Scripts and libraries you made here. Please put the current version of AutoHotkey v2 you used in Square Brackets at the start of the topic title.
iseahound
Posts: 608
Joined: 13 Aug 2016, 21:04
GitHub: iseahound

[a122] ImagePut - Windows Image Transformation Library

Post by iseahound » 29 May 2020, 15:23

ImagePut
Documentation on Github: https://github.com/iseahound/ImagePut (v1 and v2 download)
Forum for v1: https://www.autohotkey.com/boards/viewtopic.php?f=6&t=76301&p=330615 (v1 only)

Code: Select all

Types              | Data Type        | Example          | Explicit    | Inferred  | Input    | Output   | alpha | Notes
                   |                  |                  | Don'tVerify | ImageType | toBitmap | toCotype |       |
clipboard          | object / ""      |  ClipboardAll()  |     yes     |    yes    |    yes   |    yes   |  yes  |
object             | object           |  object.Bitmap() |     yes     |    yes    |    yes   |    N/A   |  yes  | ImageRender()
buffer             | object           |  bitmap.pBitmap  |     yes     |    yes    |    yes   |    yes   |  yes  |
screenshot         | object           | [x,y,w,h]        |     yes     |    yes    |    yes   |    yes   |  no   |
window / hwnd      | obj / str / int  | "A"              |     yes     |    yes    |    yes   |    yes   |  yes  |
desktop            | string (abc)     | "desktop"        |     yes     |    yes    |    yes   |    yes   |  no   |
wallpaper          | string (abc)     | "wallpaper"      |     yes     |    yes    |    yes   |    yes   |  no   |
cursor             | string (abc)     |  A_Cursor        |     yes     |    yes    |    yes   |    yes   |  yes  |
url                | string (abc)     | "https://...     |     yes     |    yes    |    yes   |    no    |  yes  |
file               | string (123)     | "picture.bmp"    |     yes     |    yes    |    yes   |    yes   |  yes  |
monitor            | integer          |  0               |     yes     |    yes    |    yes   |    no    |  no   |
hBitmap            | handle           | -838530967       |     yes     |    yes    |    yes   |    yes   |  yes  |
hIcon              | handle           | -1006302684      |     yes     |    yes    |    yes   |    yes   |  yes  |
bitmap             | pointer          |  8658704         |     yes     |    yes    |    yes   |    yes   |  yes  |
stream             | pointer          |  9814752         |     yes     |    yes    |    yes   |    yes   |  yes  |
RandomAccessStream | pointer          |  54273232        |     yes     |    yes    |    yes   |    yes   |  yes  |
hex                | string (123|abc) | "89504e470d0a... |     yes     |    yes    |    yes   |    yes   |  yes  |
base64             | string (123|abc) | "/9j/4AAQSkZJ... |     yes     |    yes    |    yes   |    yes   |  yes  |
sprite             | N/A              |                  |     yes     |    no     |    yes   |    no    |  N/A  |
formdata           | output           |                  |     no      |    no     |    no    |          |       |
icon               | string (123)     |                  |             |           |          |          |       |
rtf                | string (123)     |                  |             |           |          |          |       |
pdf                | string (123)     |                  |             |           |          |          |       |
printer            | output           |                  |             |           |          |          |       |
findtext           |                  |                  |             |           |          |          |       |
thumbnail          |                  |       ?          |             |           |          |          |       | DwmRegisterThumbnail
video              | input            |                  |             |           |          |          |
trayicon           |                  |                  |             |           |          |          |
email              |                  |                  |             |           |          |          |
AnimalCrossing     |                  |                  |             |           |          |          |



Surjective
hash               | a,p,d,w,color    |                  |             |           |          |          |

Current Version - https://github.com/iseahound/ImagePut/blob/master/v2/ImagePut.ahk
a109 - v2 only version.

Code: Select all

; Script:    ImagePut.ahk
; Author:    iseahound
; License:   MIT License
; Version:   2020-05-22
; Release:   2020-05-26

; ImagePut - Puts an image from anywhere to anywhere.
; This is a simple functor designed to be intuitive.
; I hope people find this reference library useful.


; Puts the image into a file format and returns a base64 encoded string.
;   extension  -  File Encoding           |  string   ->   bmp, gif, jpg, png, tiff
;   quality    -  JPEG Quality Level      |  integer  ->   0 - 100
ImagePutBase64(ByRef image, extension := "", quality := "") {
   return ImagePut("base64", image,,, extension, quality)
}

; Puts the image into a GDI+ Bitmap and returns a pointer.
ImagePutBitmap(ByRef image) {
   return ImagePut("bitmap", image)
}

; Puts the image into a GDI+ Bitmap and returns a buffer object with GDI+ scope.
ImagePutBuffer(ByRef image) {
   return ImagePut("buffer", image)
}

; Puts the image onto the clipboard and returns an empty string.
ImagePutClipboard(ByRef image) {
   return ImagePut("clipboard", image)
}

; Puts the image as the cursor and returns the string "A_Cursor".
;   xHotspot   -  X Click Point           |  pixel    ->   0 - width
;   yHotspot   -  Y Click Point           |  pixel    ->   0 - height
ImagePutCursor(ByRef image, xHotspot := "", yHotspot := "") {
   return ImagePut("cursor", image,,, xHotspot, yHotspot)
}

; Puts the image behind the desktop icons and returns the string "desktop".
;   scale      -  Scale Factor            |  real     ->   A_ScreenHeight / height.
ImagePutDesktop(ByRef image, scale := 1) {
   return ImagePut("desktop", image,, scale)
}

; Puts the image into a file and returns the file name.
;   filename   -  File Extension          |  string   ->   *.bmp, *.gif, *.jpg, *.png, *.tiff
;   quality    -  JPEG Quality Level      |  integer  ->   0 - 100
ImagePutFile(ByRef image, filename := "", quality := "") {
   return ImagePut("file", image,,, filename, quality)
}

; Puts the image into a device independent bitmap and returns a handle.
;   quality    -  JPEG Quality Level      |  integer  ->   0 - 100
;   alpha      -  Alpha Replacement Color |  RGB      ->   0xFFFFFF
ImagePutHBitmap(ByRef image, alpha := "") {
   return ImagePut("hBitmap", image,,, alpha)
}

; Puts the image on the shared screen device context and returns an array of coordinates.
;   screenshot -  Screen Coordinates      |  array    ->   [x,y,w,h] or [0,0]
;   alpha      -  Alpha Replacement Color |  RGB      ->   0xFFFFFF
ImagePutScreenshot(ByRef image, screenshot := "", alpha := "") {
   return ImagePut("screenshot", image,,, screenshot, alpha)
}

; Puts the image as the desktop wallpaper and returns the string "wallpaper".
ImagePutWallpaper(ByRef image) {
   return ImagePut("wallpaper", image)
}


; ImagePut() - Puts an image from anywhere to anywhere.
;   cotype     -  Output Type             |  string   ->   Case Insensitive. Read documentation.
;   image      -  Input Image             |  image    ->   Anything. Refer to ImageType().
;   crop       -  Crop Coordinates        |  array    ->   [x,y,w,h] could be negative or percent.
;   scale      -  Scale Factor            |  real     ->   2.0
;   terms*     -  Additional Parameters   |  variadic ->   Extra parameters found in toCotype().
ImagePut(cotype, ByRef image, crop := "", scale := "", terms*) {
   return ImagePut.call(cotype, image, crop, scale, terms*)
}


class ImagePut {

   static call(cotype, ByRef image, crop := "", scale := "", terms*) {

      ImagePut.gdiplusStartup()

      ; Take a guess as to what the image might be. (>90% accuracy!)
      try type := ImagePut.DontVerifyImageType(image)
      catch
         type := ImagePut.ImageType(image)

      ; Qualify additional parameters for correctness.
      _crop := IsObject(crop)
            && crop[1] ~= "^-?\d+(\.\d*)?%?$" && crop[2] ~= "^-?\d+(\.\d*)?%?$"
            && crop[3] ~= "^-?\d+(\.\d*)?%?$" && crop[4] ~= "^-?\d+(\.\d*)?%?$"
      _scale := scale != 1 && scale ~= "^\d+(\.\d+)?$"

      ; Make a copy of the image as a pBitmap.
      pBitmap := ImagePut.toBitmap(type, image)

      ; Crop the image.
      if (_crop) {
         pBitmap2 := ImagePut.BitmapCrop(pBitmap, crop)
         DllCall("gdiplus\GdipDisposeImage", "ptr", pBitmap)
         pBitmap := pBitmap2
      }

      ; Scale the image.
      if (_scale) {
         pBitmap2 := ImagePut.BitmapScale(pBitmap, scale)
         DllCall("gdiplus\GdipDisposeImage", "ptr", pBitmap)
         pBitmap := pBitmap2
      }

      ; Put the pBitmap to wherever the cotype specifies.
      coimage := ImagePut.toCotype(cotype, pBitmap, terms*)

      ; Clean up the pBitmap copy. Export raw pointers if requested.
      if !(cotype = "bitmap" || cotype = "buffer")
         DllCall("gdiplus\GdipDisposeImage", "ptr", pBitmap)

      ImagePut.gdiplusShutdown(cotype)

      return coimage
   }

   ; Types       | Example         | Explicit    | Inferred  | Input    | Output   |
   ;             |                 | Don'tVerify | ImageType | toBitmap | toCotype |
   ; clipboard   | ClipboardAll    |     yes     |    yes    |    yes   |    yes   | - no transparency
   ; object      | object.Bitmap() |     yes     |    yes    |    yes   |          |
   ; buffer      | bitmap.pBitmap  |     yes     |    yes    |    yes   |    yes   |
   ; screenshot  | [x,y,w,h]       |     yes     |    yes    |    yes   |          |
   ; desktop     | "desktop"       |     yes     |    yes    |    no    |    yes   |
   ; wallpaper   | "wallpaper"     |     yes     |    yes    |    yes   |    yes   |
   ; cursor      | A_Cursor        |     yes     |    yes    |    yes   |    yes   |
   ; url         | https://        |     yes     |    yes    |    yes   |          |
   ; file        | picture.bmp     |     yes     |    yes    |    yes   |    yes   |
   ; bitmap      | some number     |     yes     |    yes    |    yes   |    yes   |
   ; hBitmap     | some number     |     yes     |    yes    |    yes   |    yes   |
   ; monitor     | 0 or # < 10     |     yes     |    yes    |          |          |
   ; hwnd        | 0x              |     yes     |    yes    |    yes   |          |
   ; window      | A               |     yes     |    yes    |    yes   |          |
   ; base64      | base64 data     |     yes     |    yes    |    yes   |    yes   |
   ; sprite      | file or url     |     yes     |    no     |    yes   |    no    |
   ; icon        |                 |             |           |          |          |
   ; printer     |                 |             |           |          |          |
   ; findtext    |                 |             |           |          |          |
   ; thumbnail   |                 |       ?     |           |          |          | DwmRegisterThumbnail
   ; video       |                 |             |           |          |          |
   ; formdata    |                 |             |           |          |          |
   ; stream      |                 |             |           |          |          |
   ; randomaccessstream | pointer  |             |           |          |          |
   ; hIcon       |                 |             |           |          |          |
   ; trayicon    |                 |             |           |          |          |
   ; hash        | aHash, p, d, w  |             |           |          |          |
   ; Animal Crossing QR code       |             |           |          |          |


   static DontVerifyImageType(ByRef image) {

      if !IsObject(image)
         throw Exception("Must be an object.")

      ; Check for image type declarations.
      ; Assumes that the user is telling the truth.

      if ObjHasOwnProp(image, "clipboard") {
         image := image.clipboard
         return "clipboard"
      }

      if ObjHasOwnProp(image, "object") {
         image := image.object
         return "object"
      }

      if ObjHasOwnProp(image, "buffer") {
         image := image.buffer
         return "buffer"
      }

      if ObjHasOwnProp(image, "screenshot") {
         image := image.screenshot
         return "screenshot"
      }

      if ObjHasOwnProp(image, "desktop") {
         image := image.desktop
         return "desktop"
      }

      if ObjHasOwnProp(image, "wallpaper") {
         image := image.wallpaper
         return "wallpaper"
      }

      if ObjHasOwnProp(image, "cursor") {
         image := image.cursor
         return "cursor"
      }

      if ObjHasOwnProp(image, "url") {
         image := image.url
         return "url"
      }

      if ObjHasOwnProp(image, "file") {
         image := image.file
         return "file"
      }

      if ObjHasOwnProp(image, "bitmap") {
         image := image.bitmap
         return "bitmap"
      }

      if ObjHasOwnProp(image, "hBitmap") {
         image := image.hBitmap
         return "hBitmap"
      }

      if ObjHasOwnProp(image, "monitor") {
         image := image.monitor
         return "monitor"
      }

      if ObjHasOwnProp(image, "hwnd") {
         image := image.hwnd
         return "hwnd"
      }

      if ObjHasOwnProp(image, "window") {
         image := image.window
         return "window"
      }

      if ObjHasOwnProp(image, "base64") {
         image := image.base64
         return "base64"
      }

      if ObjHasOwnProp(image, "sprite") {
         image := image.sprite
         return "sprite"
      }

      throw Exception("Invalid type.")
   }

   static ImageType(ByRef image) {
         ; Must be first as ClipboardAll is just an empty string when passed through functions.
         if ImagePut.is_clipboard(image)
            return "clipboard"

         ; Throw if the image is an empty string.
         if (image == "")
            throw Exception("Image data is an empty string."
            . "`nIf you passed ClipboardAll it does not contain compatible image data.")

      if IsObject(image) {
         ; An "object" is an object that implements a Bitmap() method returning a pointer to a GDI+ bitmap.
         if IsFunc(image.Bitmap)
            return "object"

         ; A "buffer" is an AutoHotkey v2 buffer object.
         if ObjHasOwnProp(image, "pBitmap")
            return "buffer"

         ; A "screenshot" is an array of 4 numbers.
         if (image[1] ~= "^-?\d+$" && image[2] ~= "^-?\d+$" && image[3] ~= "^-?\d+$" && image[4] ~= "^-?\d+$")
            return "screenshot"
      }
         ; A "desktop" is a hidden window behind the desktop icons created by ImagePutDesktop.
         if (image = "desktop")
            return "desktop"

         ; A "wallpaper" is the desktop wallpaper.
         if (image = "wallpaper")
            return "wallpaper"

         ; A "cursor" is the name of a known cursor name.
         if (image ~= "(?i)^(IDC|OCR)?_?(A_Cursor|AppStarting|Arrow|Cross|Help|IBeam|"
         . "Icon|No|Size|SizeAll|SizeNESW|SizeNS|SizeNWSE|SizeWE|UpArrow|Wait|Unknown)$")
            return "cursor"

         ; A "url" satisfies the url format.
         if ImagePut.is_url(image)
            return "url"

         ; A "file" must exist.
         if FileExist(image)
            return "file"

         ; A "bitmap" is a pointer to a GDI+ Bitmap.
         if (DllCall("gdiplus\GdipGetImageType", "ptr", image, "ptr*", ErrorLevel) == 0)
            return "bitmap"

         ; A "hBitmap" is a handle to a GDI Bitmap.
         if (DllCall("GetObjectType", "ptr", image) == 7)
            return "hBitmap"

         ; A "monitor" is a number like 0 and 1.
         if (image = 0) ;if (image ~= "^\d+$" && image <= GetMonitorCount())
            return "monitor"

         ; A "hwnd" is a handle to a window and more commonly known as ahk_id.
         if DllCall("IsWindow", "ptr", image)
            return "hwnd"

         ; A "window" is anything considered a Window Title including ahk_class and "A".
         if WinExist(image)
            return "window"

         ; A "base64" string is binary image data encoded into text using only 64 characters.
         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.")
   }

   static toBitmap(type, ByRef image) {

      if (type = "clipboard")
         return ImagePut.from_clipboard()

      if (type = "object")
         return image.Bitmap()

      if (type = "buffer") {
         DllCall("gdiplus\GdipCloneImage", "ptr", image.pBitmap, "ptr*", pBitmap:=0)
         return pBitmap
      }

      if (type = "screenshot")
         return ImagePut.from_screenshot(image)

      if (type = "desktop")
         return ImagePut.from_desktop()

      if (type = "wallpaper")
         return ImagePut.from_wallpaper()

      if (type = "cursor")
         return ImagePut.from_cursor()

      if (type = "url")
         return ImagePut.from_url(image)

      if (type = "file") {
         DllCall("gdiplus\GdipCreateBitmapFromFile", "wstr", image, "ptr*", pBitmap:=0)
         return pBitmap
      }

      if (type = "bitmap") {
         DllCall("gdiplus\GdipCloneImage", "ptr", image, "ptr*", pBitmap:=0)
         return pBitmap
      }

      if (type = "hBitmap")
         return ImagePut.from_hBitmap(image)

      if (type = "monitor")
         return ImagePut.from_monitor(image)

      if (type = "hwnd" || type = "window") {
         image := (type = "window") ? WinExist(image) : image
         return ImagePut.from_hwnd(image)
      }

      if (type = "base64")
         return ImagePut.from_base64(image)

      if (type = "sprite")
         return ImagePut.from_sprite(image)

      throw Exception("Conversion from this type is not supported.")
   }

   static toCotype(cotype, ByRef pBitmap, terms*) {
      ; toCotype("clipboard", pBitmap)
      if (cotype = "clipboard")
         return ImagePut.put_clipboard(pBitmap)

      ; toCotype("buffer", pBitmap)
      if (cotype = "buffer") {
         buffer := {__New: ObjBindMethod(this, "gdiplusStartup") ; Increment GDI+ reference count
               , __Delete: ObjBindMethod(this, "gdiplusShutdown", "smart_pointer", pBitmap)}
         buffer := new buffer      ; On deletion the buffer object will dispose of the bitmap.
         buffer.pBitmap := pBitmap ; And it will decrement ImagePut.gdiplus.
         return buffer
      }

      ; toCotype("screenshot", pBitmap, screenshot, alpha)
      if (cotype = "screenshot")
         return ImagePut.put_screenshot(pBitmap, terms[1], terms[2])

      ; toCotype("desktop", pBitmap)
      if (cotype = "desktop")
         return ImagePut.put_desktop(pBitmap)

      ; toCotype("wallpaper", pBitmap)
      if (cotype = "wallpaper")
         return ImagePut.put_wallpaper(pBitmap)

      ; toCotype("cursor", pBitmap, xHotspot, yHotspot)
      if (cotype = "cursor")
         return ImagePut.put_cursor(pBitmap, terms[1], terms[2])

      ; toCotype("url", ????????????????????????
      if (cotype = "url") {
         ; put a url
      }

      ; toCotype("file", pBitmap, filename, quality)
      if (cotype = "file")
         return ImagePut.put_file(pBitmap, terms[1], terms[2])

      ; toCotype("window", pBitmap)
      if (cotype = "window")
         return "ahk_id " . ImagePut.Render({"bitmap":pBitmap}).AlwaysOnTop().ToolWindow().Caption().hwnd

      ; toCotype("hwnd", pBitmap)
      if (cotype = "hwnd")
         return ImagePut.Render({"bitmap":pBitmap}).hwnd

      ; toCotype("bitmap", pBitmap)
      if (cotype = "bitmap")
         return pBitmap

      ; toCotype("hBitmap", pBitmap, alpha)
      if (cotype = "hBitmap")
         return ImagePut.put_hBitmap(pBitmap, terms[1])

      ; toCotype("base64", pBitmap, extension, quality)
      if (cotype = "base64") ; Thanks to noname.
         return ImagePut.put_base64(pBitmap, terms[1], terms[2])

      throw Exception("Conversion to this type is not supported.")
   }

   static DisposeImage(ByRef pBitmap) {
      return DllCall("gdiplus\GdipDisposeImage", "ptr", pBitmap)
   }

   static BitmapCrop(ByRef pBitmap, crop) {
      ; Get Bitmap width and height and format.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0)
      DllCall("gdiplus\GdipGetImagePixelFormat", "ptr", pBitmap, "ptr*", format:=0)

      ; Are the numbers percentages?
      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]
      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]

      ; If numbers are negative, subtract the values from the edge.
      crop[3] := (crop[3] < 0) ?  width - Abs(crop[3]) - Abs(crop[1]) : crop[3]
      crop[4] := (crop[4] < 0) ? height - Abs(crop[4]) - Abs(crop[2]) : crop[4]
      crop[1] := Abs(crop[1])
      crop[2] := Abs(crop[2])

      ; Round to the nearest integer.
      crop[3] := Round(crop[1] + crop[3]) - Round(crop[1]) ; A reminder that width and height
      crop[4] := Round(crop[2] + crop[4]) - Round(crop[2]) ; are distances, not coordinates.
      crop[1] := Round(crop[1]) ; so the abstract concept of a distance must be resolved
      crop[2] := Round(crop[2]) ; into coordinates and then rounded and added up again.

      ; Variance Shift. Now place x,y before w,h because we are building abstracts from reals now.
      ; Before we were resolving abstracts into real coordinates, now it's the opposite.

      ; Ensure that coordinates can never exceed the expected Bitmap area.
      safe_x := (crop[1] > width) ? 0 : crop[1]                         ; Zero x if bigger.
      safe_y := (crop[2] > height) ? 0 : crop[2]                        ; Zero y if bigger.
      safe_w := (crop[1] + crop[3] > width) ? width - safe_x : crop[3]   ; Max w if bigger.
      safe_h := (crop[2] + crop[4] > height) ? height - safe_y : crop[4] ; Max h if bigger.

      ; Clone
      DllCall("gdiplus\GdipCloneBitmapAreaI"
               ,    "int", safe_x
               ,    "int", safe_y
               ,    "int", safe_w
               ,    "int", safe_h
               ,    "int", format
               ,    "ptr", pBitmap
               ,   "ptr*", pBitmapCrop:=0)

      return pBitmapCrop
   }

   static BitmapScale(ByRef pBitmap, scale) {
      ; Get Bitmap width and height and format.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0)
      DllCall("gdiplus\GdipGetImagePixelFormat", "ptr", pBitmap, "ptr*", format:=0)

      safe_w := Ceil(width * scale)
      safe_h := Ceil(height * scale)

      ; Create a new bitmap and get the graphics context.
      DllCall("gdiplus\GdipCreateBitmapFromScan0"
               , "int", safe_w, "int", safe_h, "int", 0, "int", format, "ptr", 0, "ptr*", pBitmapScale:=0)
      DllCall("gdiplus\GdipGetImageGraphicsContext", "ptr", pBitmapScale, "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. Not sure why the integer variant fails below.
      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", safe_w, "int", safe_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)
      return pBitmapScale
   }

   static is_clipboard(c) {
      ; ClipboardAll is always an empty string when passed into a function.
      if (c != "") ; Must be an empty string.
         return false

      ; Look through the clipboard for a memory object containing a BITMAPINFO structure followed by the bitmap bits.
      if DllCall("OpenClipboard", "ptr", 0) {
         _answer := DllCall("IsClipboardFormatAvailable", "uint", 8) ; CF_DIB
         DllCall("CloseClipboard")
         if (_answer)
            return true
      }

      ; Error messages are inaccurate and it can't be helped.
      return false
   }

   static is_url(url) {
      ; Thanks splattermania - https://www.php.net/manual/en/function.preg-match.php#93824

      regex := "^(?i)"
         . "((https?|ftp)\:\/\/)" ; SCHEME
         . "([a-z0-9+!*(),;?&=\$_.-]+(\:[a-z0-9+!*(),;?&=\$_.-]+)[email protected])?" ; User and Pass
         . "([a-z0-9-.]*)\.([a-z]{2,3})" ; Host or IP
         . "(\:[0-9]{2,5})?" ; Port
         . "(\/(?:[a-z0-9-_~!$&'()*+,;=:@]\.?)+)*\/?" ; Path
         . "(\?[a-z+&\$_.-][a-z0-9;:@&%=+\/\$_.-]*)?" ; GET Query
         . "(#[a-z_.-][a-z0-9+\$_.-]*)?$" ; Anchor
      return (url ~= regex)
   }

   static from_clipboard() {
      ; Thanks tic - https://www.autohotkey.com/boards/viewtopic.php?t=6517

      if DllCall("OpenClipboard", "ptr", 0) {
         hBitmap := DllCall("GetClipboardData", "uint", 2, "ptr")
         DllCall("CloseClipboard")
         DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", "ptr", hBitmap, "ptr", 0, "ptr*", pBitmap:=0)
         DllCall("DeleteObject", "ptr", hBitmap)
      }
      return pBitmap
   }

   static from_screenshot(ByRef image) {
      ; Thanks tic - https://www.autohotkey.com/boards/viewtopic.php?t=6517

      ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      VarSetCapacity(bi, 40, 0)               ; sizeof(bi) = 40
         , NumPut(      40, bi,  0,   "uint") ; Size
         , NumPut( image[3], bi,  4,   "uint") ; Width
         , NumPut(-image[4], bi,  8,    "int") ; Height - Negative so (0, 0) is top-left.
         , NumPut(       1, bi, 12, "ushort") ; Planes
         , NumPut(      32, bi, 14, "ushort") ; 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) ; SRCCOPY

      ; 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
   }

   static from_wallpaper() {
      ; Get the width and height of all monitors.
      width  := DllCall("GetSystemMetrics", "int", 78)
      height := DllCall("GetSystemMetrics", "int", 79)

      ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      VarSetCapacity(bi, 40, 0)               ; sizeof(bi) = 40
         , NumPut(      40, bi,  0,   "uint") ; Size
         , NumPut(   width, bi,  4,   "uint") ; Width
         , NumPut( -height, bi,  8,    "int") ; Height - Negative so (0, 0) is top-left.
         , NumPut(       1, bi, 12, "ushort") ; Planes
         , NumPut(      32, bi, 14, "ushort") ; 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")

      ; Paints the desktop.
      DllCall("PaintDesktop", "ptr", hdc)

      ; 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
   }

   static from_cursor() {
      ; Thanks 23W - https://stackoverflow.com/a/13295280

      ; struct CURSORINFO - https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-cursorinfo
      NumPut(VarSetCapacity(ci, 16+A_PtrSize, 0), ci, "int") ; sizeof(CURSORINFO) = 20, 24
      DllCall("GetCursorInfo", "ptr", &ci)
         ; cShow   := NumGet(ci,  4, "int")                  ; 0x1 = CURSOR_SHOWING, 0x2 = CURSOR_SUPPRESSED
         , hCursor := NumGet(ci,  8, "ptr")
         ; xCursor := NumGet(ci,  8+A_PtrSize, "int")
         ; yCursor := NumGet(ci, 12+A_PtrSize, "int")

      ; struct ICONINFO - https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-iconinfo
      VarSetCapacity(ii, 8+3*A_PtrSize, 0)               ; sizeof(ICONINFO) = 20, 32
      DllCall("GetIconInfo", "ptr", hCursor, "ptr", &ii)
         ; xHotspot := NumGet(ii, 4, "uint")
         ; yHotspot := NumGet(ii, 8, "uint")
         , hbmMask  := NumGet(ii, 8+A_PtrSize, "ptr")    ; x86:12, x64:16
         , hbmColor := NumGet(ii, 8+2*A_PtrSize, "ptr")  ; x86:16, x64:24

      ; struct BITMAP - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmap
      DllCall("GetObject"
               ,    "ptr", hbmMask
               ,    "int", VarSetCapacity(bm, 16+2*A_PtrSize)   ; sizeof(BITMAP) = 24, 32
               ,    "ptr", &bm)
         , width  := NumGet(bm, 4, "uint")
         , height := NumGet(bm, 8, "uint") / (hbmColor ? 1 : 2) ; Black and White cursors have doubled height.

      ; Clean up these hBitmaps.
      DllCall("DeleteObject", "ptr", hbmMask)
      DllCall("DeleteObject", "ptr", hbmColor)

      ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      VarSetCapacity(bi, 40, 0)               ; sizeof(bi) = 40
         , NumPut(      40, bi,  0,   "uint") ; Size
         , NumPut(   width, bi,  4,   "uint") ; Width
         , NumPut( -height, bi,  8,    "int") ; Height - Negative so (0, 0) is top-left.
         , NumPut(       1, bi, 12, "ushort") ; Planes
         , NumPut(      32, bi, 14, "ushort") ; 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")

      ; This is the 32-bit ARGB pBitmap (different from an hBitmap) that will receive the final converted pixels.
      DllCall("gdiplus\GdipCreateBitmapFromScan0"
               , "int", width, "int", height, "int", 0, "int", 0x26200A, "ptr", 0, "ptr*", pBitmap:=0)

      ; 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

      ; Use LockBits to create a writable buffer that converts pARGB to ARGB.
      DllCall("gdiplus\GdipBitmapLockBits"
               ,    "ptr", pBitmap
               ,    "ptr", &Rect
               ,   "uint", 6            ; ImageLockMode.UserInputBuffer | ImageLockMode.WriteOnly
               ,    "int", 0xE200B      ; Format32bppPArgb
               ,    "ptr", &BitmapData) ; Contains the pointer (pBits) to the hbm.

      ; Don't use DI_DEFAULTSIZE to draw the icon like DrawIcon does as it will resize to 32 x 32.
      DllCall("DrawIconEx"
               , "ptr", hdc,     "int", 0, "int", 0
               , "ptr", hCursor, "int", 0, "int", 0
               , "uint", 0, "ptr", 0, "uint", 0x1 | 0x2 | 0x4) ; DI_MASK | DI_IMAGE | DI_COMPAT

      ; Convert the pARGB pixels copied into the device independent bitmap (hbm) to ARGB.
      DllCall("gdiplus\GdipBitmapUnlockBits", "ptr", pBitmap, "ptr", &BitmapData)

      ; Clean up the icon and device context.
      DllCall("DestroyIcon",  "ptr", hCursor)
      DllCall("SelectObject", "ptr", hdc, "ptr", obm)
      DllCall("DeleteObject", "ptr", hbm)
      DllCall("DeleteDC",     "ptr", hdc)

      return pBitmap
   }

   static from_url(ByRef image) {
      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:=0)
      ObjRelease(pStream)
      return pBitmap
   }

   static from_hBitmap(ByRef image) {
      ; struct BITMAP - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmap
      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 32-bit ARGB.
      if (bpp != 32) { ; This built-in version is 120% faster but ignores transparency.
         DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", "ptr", image, "ptr", 0, "ptr*", pBitmap:=0)
         return pBitmap
      }

      ; Create a handle to a device context and associate the image.
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")           ; Creates a memory DC compatible with the current screen.
      obm := DllCall("SelectObject", "ptr", hdc, "ptr", image, "ptr") ; Put the (hBitmap) image onto the device context.

      ; Create a device independent bitmap with negative height. All DIBs use the screen pixel format (pARGB).
      ; Use hbm to buffer the image such that top-down and bottom-up images are mapped to this top-down buffer.
      cdc := DllCall("CreateCompatibleDC", "ptr", hdc, "ptr")
      VarSetCapacity(bi, 40, 0)               ; sizeof(bi) = 40
         , NumPut(      40, bi,  0,   "uint") ; Size
         , NumPut(   width, bi,  4,   "uint") ; Width
         , NumPut( -height, bi,  8,    "int") ; Height - Negative so (0, 0) is top-left.
         , NumPut(       1, bi, 12, "ushort") ; Planes
         , NumPut(      32, bi, 14, "ushort") ; BitCount / BitsPerPixel
      hbm := DllCall("CreateDIBSection", "ptr", cdc, "ptr", &bi, "uint", 0
               , "ptr*", pBits  ; pBits is the pointer to (top-down) pixel values.
               , "ptr", 0, "uint", 0, "ptr")
      ob2 := DllCall("SelectObject", "ptr", cdc, "ptr", hbm, "ptr")

      ; This is the 32-bit ARGB pBitmap (different from an hBitmap) that will receive the final converted pixels.
      DllCall("gdiplus\GdipCreateBitmapFromScan0"
               , "int", width, "int", height, "int", 0, "int", 0x26200A, "ptr", 0, "ptr*", pBitmap:=0)

      ; 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

      ; Use LockBits to create a writable buffer that converts pARGB to ARGB.
      DllCall("gdiplus\GdipBitmapLockBits"
               ,    "ptr", pBitmap
               ,    "ptr", &Rect
               ,   "uint", 6            ; ImageLockMode.UserInputBuffer | ImageLockMode.WriteOnly
               ,    "int", 0xE200B      ; Format32bppPArgb
               ,    "ptr", &BitmapData) ; Contains the pointer (pBits) to the hbm.

      ; Copies the image (hBitmap) to a top-down bitmap. Removes bottom-up-ness if present.
      DllCall("gdi32\BitBlt"
               , "ptr", cdc, "int", 0, "int", 0, "int", width, "int", height
               , "ptr", hdc, "int", 0, "int", 0, "uint", 0x00CC0020) ; SRCCOPY

      ; 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.
      DllCall("SelectObject", "ptr", cdc, "ptr", ob2)
      DllCall("DeleteObject", "ptr", hbm)
      DllCall("DeleteDC",     "ptr", cdc)
      DllCall("SelectObject", "ptr", hdc, "ptr", obm)
      DllCall("DeleteDC",     "ptr", hdc)

      return pBitmap
   }

   static from_monitor(ByRef image) {
      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)
      }
      return ImagePut.from_screenshot([x,y,w,h])
   }

   static from_hwnd(ByRef image) {
      ; Thanks tic - https://www.autohotkey.com/boards/viewtopic.php?t=6517

      ; Restore the window if minimized! Must be visible for capture.
      if DllCall("IsIconic", "ptr", image)
         DllCall("ShowWindow", "ptr", image, "int", 4)

      ; Get the width and height of the client window.
      VarSetCapacity(rect, 16) ; sizeof(RECT) = 16
      DllCall("GetClientRect", "ptr", image, "ptr", &rect)
         , width  := NumGet(rect, 8, "int")
         , height := NumGet(rect, 12, "int")

      ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      VarSetCapacity(bi, 40, 0)               ; sizeof(bi) = 40
         , NumPut(      40, bi,  0,   "uint") ; Size
         , NumPut(   width, bi,  4,   "uint") ; Width
         , NumPut( -height, bi,  8,    "int") ; Height - Negative so (0, 0) is top-left.
         , NumPut(       1, bi, 12, "ushort") ; Planes
         , NumPut(      32, bi, 14, "ushort") ; 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")

      ; Print the window onto the hBitmap using an undocumented flag. https://stackoverflow.com/a/40042587
      DllCall("PrintWindow", "ptr", image, "ptr", hdc, "uint", 0x3) ; PW_CLIENTONLY | PW_RENDERFULLCONTENT
      ; Additional info on how this is implemented: https://www.reddit.com/r/windows/comments/8ffr56/altprintscreen/

      ; 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
   }

   static from_base64(ByRef image) {
      ; Trim whitespace and remove header.
      image := Trim(image)
      image := RegExReplace(image, "^data:image\/[a-z]+;base64,")

      ; Converts the image to binary data by first asking for the size.
      DllCall("crypt32\CryptStringToBinary", "ptr",&image, "uint",0, "uint",0x1, "ptr",   0, "uint*",size:=0, "ptr",0, "ptr",0)
      VarSetCapacity(bin, size, 0)
      DllCall("crypt32\CryptStringToBinary", "ptr",&image, "uint",0, "uint",0x1, "ptr",&bin, "uint*",size, "ptr",0, "ptr",0)

      ; Makes a stream for conversion into a pBitmap.
      pStream := DllCall("shlwapi\SHCreateMemStream", "ptr", &bin, "uint", size, "ptr")
      DllCall("gdiplus\GdipCreateBitmapFromStream", "ptr", pStream, "ptr*", pBitmap:=0)
      ObjRelease(pStream)

      return pBitmap
   }

   static from_sprite(ByRef image) {
      ; Create a source pBitmap and extract the width and height.
      if DllCall("gdiplus\GdipCreateBitmapFromFile", "wstr", image, "ptr*", sBitmap:=0)
         if !(sBitmap := ImagePut.from_url(image))
            throw Exception("Could not be loaded from a valid file path or URL.")

      ; Get Bitmap width and height.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", sBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", sBitmap, "uint*", height:=0)

      ; Create a destination pBitmap in 32-bit ARGB and get its device context though GDI+.
      ; Note that a device context from a graphics context can only be drawn on, not read.
      ; Also note that using a graphics context and blitting does not create a pixel perfect image.
      ; Using a DIB and LockBits is about 5% faster.
      DllCall("gdiplus\GdipCreateBitmapFromScan0"
               , "int", width, "int", height, "int", 0, "int", 0x26200A, "ptr", 0, "ptr*", dBitmap:=0)
      DllCall("gdiplus\GdipGetImageGraphicsContext", "ptr", dBitmap, "ptr*", dGraphics:=0)
      DllCall("gdiplus\GdipGetDC", "ptr", dGraphics, "ptr*", ddc:=0)

      ; Keep any existing transparency for whatever reason.
      hBitmap := ImagePut.put_hBitmap(sBitmap) ; Could copy this code here for even more speed.

      ; Create a source device context and associate the source hBitmap.
      sdc := DllCall("CreateCompatibleDC", "ptr", ddc, "ptr")
      obm := DllCall("SelectObject", "ptr", sdc, "ptr", hBitmap, "ptr")

      ; Copy the image making the top-left pixel the color key.
      DllCall("msimg32\TransparentBlt"
               , "ptr", ddc, "int", 0, "int", 0, "int", width, "int", height  ; destination
               , "ptr", sdc, "int", 0, "int", 0, "int", width, "int", height  ; source
               , "uint", DllCall("GetPixel", "ptr", sdc, "int", 0, "int", 0)) ; RGB pixel.

      ; Cleanup the hBitmap and device contexts.
      DllCall("SelectObject", "ptr", sdc, "ptr", obm)
      DllCall("DeleteObject", "ptr", hBitmap)
      DllCall("DeleteDC",     "ptr", sdc)

      ; Release the graphics context and delete.
      DllCall("gdiplus\GdipReleaseDC", "ptr", dGraphics, "ptr", ddc)
      DllCall("gdiplus\GdipDeleteGraphics", "ptr", dGraphics)

      return dBitmap
   }

   static put_clipboard(ByRef pBitmap) {
      ; Thanks tic - https://www.autohotkey.com/boards/viewtopic.php?t=6517

      off1 := A_PtrSize = 8 ? 52 : 44, off2 := A_PtrSize = 8 ? 32 : 24
      DllCall("gdiplus\GdipCreateHBITMAPFromBitmap", "ptr", pBitmap, "ptr*", hBitmap:=0, "uint", 0xFFFFFFFF)
      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")

      ; Returns an empty string as ClipboardAll would also be an empty string.
      return ""
   }

   static put_screenshot(ByRef pBitmap, screenshot := "", alpha := "") {
      ; Get Bitmap width and height.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0)

      x := (screenshot[1] != "") ? screenshot[1] : Round((A_ScreenWidth - width) / 2)
      y := (screenshot[2] != "") ? screenshot[2] : Round((A_ScreenHeight - height) / 2)
      w := (screenshot[3] != "") ? screenshot[3] : width
      h := (screenshot[4] != "") ? screenshot[4] : height

      ; Convert the Bitmap to a hBitmap and associate a device context for blitting.
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      hbm := ImagePut.put_hBitmap(pBitmap, alpha)
      obm := DllCall("SelectObject", "ptr", hdc, "ptr", hbm, "ptr")

      ; Get device context of spawned window.
      ddc := DllCall("GetDC", "ptr", 0, "ptr")

      ; Copies a portion of the screen to a new device context.
      DllCall("gdi32\StretchBlt"
               , "ptr", ddc, "int", x, "int", y, "int", w,     "int", h
               , "ptr", hdc, "int", 0, "int", 0, "int", width, "int", height
               , "uint", 0x00CC0020) ; SRCCOPY

      ; Release device context of spawned window.
      DllCall("ReleaseDC", "ptr", 0, "ptr", ddc)

      ; Cleanup the hBitmap and device contexts.
      DllCall("SelectObject", "ptr", hdc, "ptr", obm)
      DllCall("DeleteObject", "ptr", hbm)
      DllCall("DeleteDC",     "ptr", hdc)

      return [x,y,w,h]
   }

   static put_desktop(ByRef pBitmap) {
      ; Thanks Gerald Degeneve - https://www.codeproject.com/Articles/856020/Draw-Behind-Desktop-Icons-in-Windows-plus

      ; Get Bitmap width and height.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0)

      ; Convert the Bitmap to a hBitmap and associate a device context for blitting.
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      hbm := ImagePut.put_hBitmap(pBitmap)
      obm := DllCall("SelectObject", "ptr", hdc, "ptr", hbm, "ptr")

      ; Post-Creator's Update Windows 10. WM_SPAWN_WORKER = 0x052C
      DllCall("SendMessage", "ptr", WinExist("ahk_class Progman"), "uint", 0x052C, "ptr", 0x0000000D, "ptr", 0)
      DllCall("SendMessage", "ptr", WinExist("ahk_class Progman"), "uint", 0x052C, "ptr", 0x0000000D, "ptr", 1)

      ; Find the child window.
      windows := WinGetList("ahk_class WorkerW")
      Loop windows.length
         hwnd := windows[A_Index]
      until DllCall("FindWindowEx", "ptr", hwnd, "ptr", 0, "str", "SHELLDLL_DefView", "ptr", 0)
      WorkerW := DllCall("FindWindowEx", "ptr", 0, "ptr", hwnd, "str", "WorkerW", "ptr", 0, "ptr")

      ; Maybe this hack gets patched. Tough luck!
      if (!WorkerW)
         throw Exception("Could not draw on the desktop.")

      ; Position the image in the center. This line can be removed.
      DllCall("SetWindowPos", "ptr", WorkerW, "ptr", 1
               , "int", Round((A_ScreenWidth - width) / 2)   ; x coordinate
               , "int", Round((A_ScreenHeight - height) / 2) ; y coordinate
               , "int", width, "int", height, "uint", 0)

      ; Get device context of spawned window.
      ddc := DllCall("GetDCEx", "ptr", WorkerW, "ptr", 0, "int", 0x403, "ptr")

      ; Copies a portion of the screen to a new device context.
      DllCall("gdi32\BitBlt"
               , "ptr", ddc, "int", 0, "int", 0, "int", width, "int", height
               , "ptr", hdc, "int", 0, "int", 0, "uint", 0x00CC0020) ; SRCCOPY

      ; Release device context of spawned window.
      DllCall("ReleaseDC", "ptr", 0, "ptr", ddc)

      ; Cleanup the hBitmap and device contexts.
      DllCall("SelectObject", "ptr", hdc, "ptr", obm)
      DllCall("DeleteObject", "ptr", hbm)
      DllCall("DeleteDC",     "ptr", hdc)

      return "desktop"
   }

   static put_wallpaper(ByRef pBitmap) {
      path := ImagePut.put_file(pBitmap, "temp.png")
      cc := DllCall("GetFullPathName", "str", path, "uint", 0, "ptr", 0, "ptr", 0, "uint")
      VarSetCapacity(buf, cc*(A_IsUnicode?2:1))
      DllCall("GetFullPathName", "str", path, "uint", cc, "str", buf, "ptr", 0, "uint")
      DllCall("SystemParametersInfo", "uint", 20, "uint", 0, "str", buf, "uint", 2)
      Sleep 1 ; Needed as there is some lag.
      FileDelete path
      return "wallpaper"
   }

   static put_cursor(ByRef pBitmap, xHotspot := "", yHotspot := "") {
      ; Thanks Nick - https://stackoverflow.com/a/550965

      ; Creates an icon that can be used as a cursor.
      DllCall("gdiplus\GdipCreateHICONFromBitmap", "ptr", pBitmap, "ptr*", hIcon:=0)

      ; Sets the hotspot of the cursor by changing the icon into a cursor.
      if (xHotspot != "" || yHotspot != "") {
         ; struct ICONINFO - https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-iconinfo
         VarSetCapacity(ii, 8+3*A_PtrSize, 0)                      ; sizeof(ICONINFO) = 20, 32
         DllCall("GetIconInfo", "ptr", hIcon, "ptr", &ii)          ; Fill the ICONINFO structure.
         DllCall("DestroyIcon", "ptr", hIcon)                      ; Destroy the icon after getting the ICONINFO structure.
         NumPut(false, ii, 0, "uint")                              ; true/false are icon/cursor respectively.
         (xHotspot != "") ? NumPut(xHotspot, ii, 4, "uint") : ""   ; Set the xHotspot value. (Default: center point)
         (yHotspot != "") ? NumPut(yHotspot, ii, 8, "uint") : ""   ; Set the yHotspot value. (Default: center point)
         hIcon := DllCall("CreateIconIndirect", "ptr", &ii, "ptr") ; Create a new cursor using ICONINFO.

         ; Clean up hbmMask and hbmColor created as a result of GetIconInfo.
         DllCall("DeleteObject", "ptr", NumGet(ii, 8+A_PtrSize, "ptr"))   ; hbmMask
         DllCall("DeleteObject", "ptr", NumGet(ii, 8+2*A_PtrSize, "ptr")) ; hbmColor
      }

      ; Loop over all 16 system cursors and change them all to the new cursor.
      SystemCursors := "32512IDC_ARROW,32513IDC_IBEAM,32514IDC_WAIT,32515IDC_CROSS,32516IDC_UPARROW"
      . ",32640IDC_SIZE,32641IDC_ICON,32642IDC_SIZENWSE,32643IDC_SIZENESW,32644IDC_SIZEWE"
      . ",32645IDC_SIZENS,32646IDC_SIZEALL,32648IDC_NO,32649IDC_HAND,32650IDC_APPSTARTING,32651IDC_HELP"
      Loop Parse, SystemCursors, ","
      { ; Must copy the handle 16 times as SetSystemCursor deletes the handle 16 times.
         hCursor := DllCall("CopyImage", "ptr", hIcon, "uint", 2, "int", 0, "int", 0, "uint", 0, "ptr")
         DllCall("SetSystemCursor", "ptr", hCursor, "int", SubStr(A_LoopField, 1, 5)) ; calls DestroyCursor
      }

      ; Destroy the original hIcon. DestroyCursor and DestroyIcon are the same function in C.
      DllCall("DestroyCursor", "ptr", hIcon)

      ; Returns the word A_Cursor so that it doesn't evaluate immediately.
      return "A_Cursor"
   }

   static put_file(ByRef pBitmap, sOutput, Quality:=75) {
      ; Thanks tic - https://www.autohotkey.com/boards/viewtopic.php?t=6517
      _p := 0

      SplitPath sOutput,,_path, Extension, _filename
      Extension := (Extension ~= "^(?i:bmp|dib|rle|jpg|jpeg|jpe|jfif|gif|tif|tiff|png)$") ? Extension : "png"
      Extension := "." Extension
      sOutput := _path . _filename . Extension

      DllCall("gdiplus\GdipGetImageEncodersSize", "uint*", nCount:=0, "uint*", nSize:=0)
      VarSetCapacity(ci, nSize)
      DllCall("gdiplus\GdipGetImageEncoders", "uint", nCount, "uint", nSize, "ptr", &ci)

      If (A_IsUnicode){
         StrGet_Name := "StrGet"

         Loop nCount
         {
            sString := %StrGet_Name%(NumGet(ci, (idx := (48+7*A_PtrSize)*(A_Index-1))+32+3*A_PtrSize), "UTF-16")
            if !InStr(sString, "*" Extension)
               continue

            pCodec := &ci+idx
            break
         }
      } else {
         Loop nCount
         {
            Location := NumGet(ci, 76*(A_Index-1)+44)
            nSize := DllCall("WideCharToMultiByte", "uint", 0, "uint", 0, "uint", Location, "int", -1, "uint", 0, "int",  0, "uint", 0, "uint", 0)
            VarSetCapacity(sString, nSize)
            DllCall("WideCharToMultiByte", "uint", 0, "uint", 0, "uint", Location, "int", -1, "str", sString, "int", nSize, "uint", 0, "uint", 0)

            if !InStr(sString, "*" Extension)
               continue

            pCodec := &ci+76*(A_Index-1)
            break
         }
      }

      if !pCodec
         return -3

      if (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:=0)
            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
               }
            }
         }
      }

      _E := DllCall("gdiplus\GdipSaveImageToFile", "ptr", pBitmap, "wstr", sOutput, "ptr", pCodec, "uint", _p ? _p : 0)
      return sOutput
   }

   static put_hBitmap(ByRef pBitmap, alpha := "") {
      ; Revert to built in functionality if a replacement color is declared.
      if (alpha != "") { ; This built-in version is about 25% slower.
         DllCall("gdiplus\GdipCreateHBITMAPFromBitmap", "ptr", pBitmap, "ptr*", hBitmap:=0, "uint", alpha)
         return hBitmap
      }

      ; Get Bitmap width and height.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0)

      ; 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")
      VarSetCapacity(bi, 40, 0)               ; sizeof(bi) = 40
         , NumPut(      40, bi,  0,   "uint") ; Size
         , NumPut(   width, bi,  4,   "uint") ; Width
         , NumPut( -height, bi,  8,    "int") ; Height - Negative so (0, 0) is top-left.
         , NumPut(       1, bi, 12, "ushort") ; Planes
         , NumPut(      32, bi, 14, "ushort") ; 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")

      ; Transfer data from source pBitmap to an hBitmap manually.
      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) ; Contains the pointer (pBits) to the hbm.
      DllCall("gdiplus\GdipBitmapUnlockBits", "ptr", pBitmap, "ptr", &BitmapData)

      ; Cleanup the hBitmap and device contexts.
      DllCall("SelectObject", "ptr", hdc, "ptr", obm)
      DllCall("DeleteDC",     "ptr", hdc)

      return hbm
   }

   static put_base64(ByRef pBitmap, file := "", Quality := "") {
      ; Thanks noname - https://www.autohotkey.com/boards/viewtopic.php?style=7&p=144247#p144247

      if !(file ~= "(?i)bmp|dib|rle|jpg|jpeg|jpe|jfif|gif|tif|tiff|png")
         file := "png"
      Extension := "." file

      DllCall("gdiplus\GdipGetImageEncodersSize", "uint*",nCount:=0, "uint*",nSize:=0)
      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:=0)
         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:=0)
      DllCall("gdiplus\GdipSaveImageToStream", "ptr",pBitmap, "ptr",pStream, "ptr",pCodec, "uint",p ? p : 0)
      DllCall("ole32\GetHGlobalFromStream", "ptr",pStream, "uint*",hData:=0)
      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:=0)
      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")
   }

   static gdiplus := 0

   static gdiplusStartup() {
      ImagePut.gdiplus := (ImagePut.gdiplus == "") ? 1 : ImagePut.gdiplus + 1

      ; Startup gdiplus when counter goes from 0 -> 1 or "" -> 1.
      if (ImagePut.gdiplus == 1) {
         DllCall("LoadLibrary", "str", "gdiplus")
         VarSetCapacity(si, A_PtrSize = 8 ? 24 : 16, 0)
         NumPut(0x1, si)
         pToken := 0
         DllCall("gdiplus\GdiplusStartup", "ptr*", pToken:=0, "ptr", &si, "ptr", 0)
         ImagePut.pToken := pToken
      }
   }

   static gdiplusShutdown(cotype := "", ByRef pBitmap := "") {
      ImagePut.gdiplus := ImagePut.gdiplus - 1

      ; When a buffer object is deleted a bitmap is sent here for disposal.
      if (cotype == "smart_pointer")
         if DllCall("gdiplus\GdipDisposeImage", "ptr", pBitmap)
            throw Exception("The bitmap of this buffer object has already been deleted.")

      ; Shutdown gdiplus if pToken is owned and when counter goes from 1 -> 0.
      if (ImagePut.gdiplus == 0) {
         DllCall("gdiplus\GdiplusShutdown", "ptr", ImagePut.pToken)
         DllCall("FreeLibrary", "ptr", DllCall("GetModuleHandle", "str", "gdiplus", "ptr"))

         ; Exit if GDI+ is still loaded. GdiplusNotInitialized = 18
         if (18 != DllCall("gdiplus\GdipCreateImageAttributes", "ptr*", ImageAttr:=0)) {
            DllCall("gdiplus\GdipDisposeImageAttributes", "ptr", ImageAttr)
            return
         }

         ; Otherwise GDI+ has been truly unloaded from the script and objects are out of scope.
         if (cotype = "bitmap")
            throw Exception("Out of scope error. `n`nIf you wish to handle raw pointers to GDI+ bitmaps, add the line"
               . "`n`n`t`t" ImagePut.__class ".gdiplusStartup()`n`nor 'pToken := Gdip_Startup()' to the top of your script."
               . "`nYou can copy this message by pressing Ctrl + C.")
      }
   }
} ; End of ImagePut class.
Gdip_All not required.
Last edited by iseahound on 15 Sep 2020, 10:35, edited 19 times in total.

iseahound
Posts: 608
Joined: 13 Aug 2016, 21:04
GitHub: iseahound

Re: [a210] ImagePut - Puts an image from anywhere to anywhere

Post by iseahound » 06 Jun 2020, 13:10

a110 compatible.

v1 - With fixes suggested by Helgef and swagfag

Code: Select all

; Script:    ImagePut.ahk
; Author:    iseahound
; License:   MIT License
; Version:   2020-05-22
; Release:   2020-06-06

; ImagePut - Puts an image from anywhere to anywhere.
; This is a simple functor designed to be intuitive.
; I hope people find this reference library useful.


; Puts the image into a file format and returns a base64 encoded string.
;   extension  -  File Encoding           |  string   ->   bmp, gif, jpg, png, tiff
;   quality    -  JPEG Quality Level      |  integer  ->   0 - 100
ImagePutBase64(ByRef image, extension := "", quality := "")
   => ImagePut("base64", image,,, extension, quality)


; Puts the image into a GDI+ Bitmap and returns a pointer.
ImagePutBitmap(ByRef image)
   => ImagePut("bitmap", image)


; Puts the image into a GDI+ Bitmap and returns a buffer object with GDI+ scope.
ImagePutBuffer(ByRef image)
   => ImagePut("buffer", image)


; Puts the image onto the clipboard and returns an empty string.
ImagePutClipboard(ByRef image)
   => ImagePut("clipboard", image)


; Puts the image as the cursor and returns the string "A_Cursor".
;   xHotspot   -  X Click Point           |  pixel    ->   0 - width
;   yHotspot   -  Y Click Point           |  pixel    ->   0 - height
ImagePutCursor(ByRef image, xHotspot := "", yHotspot := "")
   => ImagePut("cursor", image,,, xHotspot, yHotspot)


; Puts the image behind the desktop icons and returns the string "desktop".
;   scale      -  Scale Factor            |  real     ->   A_ScreenHeight / height.
ImagePutDesktop(ByRef image, scale := 1)
   => ImagePut("desktop", image,, scale)


; Puts the image into a file and returns the file name.
;   filename   -  File Extension          |  string   ->   *.bmp, *.gif, *.jpg, *.png, *.tiff
;   quality    -  JPEG Quality Level      |  integer  ->   0 - 100
ImagePutFile(ByRef image, filename := "", quality := "")
   => ImagePut("file", image,,, filename, quality)


; Puts the image into a device independent bitmap and returns a handle.
;   quality    -  JPEG Quality Level      |  integer  ->   0 - 100
;   alpha      -  Alpha Replacement Color |  RGB      ->   0xFFFFFF
ImagePutHBitmap(ByRef image, alpha := "")
   => ImagePut("hBitmap", image,,, alpha)


; Puts the image on the shared screen device context and returns an array of coordinates.
;   screenshot -  Screen Coordinates      |  array    ->   [x,y,w,h] or [0,0]
;   alpha      -  Alpha Replacement Color |  RGB      ->   0xFFFFFF
ImagePutScreenshot(ByRef image, screenshot := "", alpha := "")
   => ImagePut("screenshot", image,,, screenshot, alpha)


; Puts the image as the desktop wallpaper and returns the string "wallpaper".
ImagePutWallpaper(ByRef image)
   => ImagePut("wallpaper", image)



; ImagePut() - Puts an image from anywhere to anywhere.
;   cotype     -  Output Type             |  string   ->   Case Insensitive. Read documentation.
;   image      -  Input Image             |  image    ->   Anything. Refer to ImageType().
;   crop       -  Crop Coordinates        |  array    ->   [x,y,w,h] could be negative or percent.
;   scale      -  Scale Factor            |  real     ->   2.0
;   terms*     -  Additional Parameters   |  variadic ->   Extra parameters found in toCotype().
ImagePut(cotype, ByRef image, crop := "", scale := "", terms*)
   => ImagePut.call(cotype, image, crop, scale, terms*)



class ImagePut {

   static call(cotype, ByRef image, crop := "", scale := "", terms*) {

      this.gdiplusStartup()

      ; Take a guess as to what the image might be. (>90% accuracy!)
      try type := this.DontVerifyImageType(image)
      catch
         type := this.ImageType(image)

      ; Qualify additional parameters for correctness.
      _crop := IsObject(crop)
         && crop[1] ~= "^-?\d+(\.\d*)?%?$" && crop[2] ~= "^-?\d+(\.\d*)?%?$"
         && crop[3] ~= "^-?\d+(\.\d*)?%?$" && crop[4] ~= "^-?\d+(\.\d*)?%?$"
      _scale := scale != 1 && scale ~= "^\d+(\.\d+)?$"

      ; Make a copy of the image as a pBitmap.
      pBitmap := this.toBitmap(type, image)

      ; Crop the image.
      if (_crop) {
         pBitmap2 := this.BitmapCrop(pBitmap, crop)
         DllCall("gdiplus\GdipDisposeImage", "ptr", pBitmap)
         pBitmap := pBitmap2
      }

      ; Scale the image.
      if (_scale) {
         pBitmap2 := this.BitmapScale(pBitmap, scale)
         DllCall("gdiplus\GdipDisposeImage", "ptr", pBitmap)
         pBitmap := pBitmap2
      }

      ; Put the pBitmap to wherever the cotype specifies.
      coimage := this.toCotype(cotype, pBitmap, terms*)

      ; Clean up the pBitmap copy. Export raw pointers if requested.
      if !(cotype = "bitmap" || cotype = "buffer")
         DllCall("gdiplus\GdipDisposeImage", "ptr", pBitmap)

      this.gdiplusShutdown(cotype)

      return coimage
   }

   ; Types       | Example         | Explicit    | Inferred  | Input    | Output   |
   ;             |                 | Don'tVerify | ImageType | toBitmap | toCotype |
   ; clipboard   | ClipboardAll    |     yes     |    yes    |    yes   |    yes   | - no transparency
   ; object      | object.Bitmap() |     yes     |    yes    |    yes   |          |
   ; buffer      | bitmap.pBitmap  |     yes     |    yes    |    yes   |    yes   |
   ; screenshot  | [x,y,w,h]       |     yes     |    yes    |    yes   |          |
   ; desktop     | "desktop"       |     yes     |    yes    |    no    |    yes   |
   ; wallpaper   | "wallpaper"     |     yes     |    yes    |    yes   |    yes   |
   ; cursor      | A_Cursor        |     yes     |    yes    |    yes   |    yes   |
   ; url         | https://        |     yes     |    yes    |    yes   |          |
   ; file        | picture.bmp     |     yes     |    yes    |    yes   |    yes   |
   ; bitmap      | some number     |     yes     |    yes    |    yes   |    yes   |
   ; hBitmap     | some number     |     yes     |    yes    |    yes   |    yes   |
   ; monitor     | 0 or # < 10     |     yes     |    yes    |          |          |
   ; hwnd        | 0x              |     yes     |    yes    |    yes   |          |
   ; window      | A               |     yes     |    yes    |    yes   |          |
   ; base64      | base64 data     |     yes     |    yes    |    yes   |    yes   |
   ; sprite      | file or url     |     yes     |    no     |    yes   |    no    |
   ; icon        |                 |             |           |          |          |
   ; printer     |                 |             |           |          |          |
   ; findtext    |                 |             |           |          |          |
   ; thumbnail   |                 |       ?     |           |          |          | DwmRegisterThumbnail
   ; video       |                 |             |           |          |          |
   ; formdata    |                 |             |           |          |          |
   ; stream      |                 |             |           |          |          |
   ; randomaccessstream | pointer  |             |           |          |          |
   ; hIcon       |                 |             |           |          |          |
   ; trayicon    |                 |             |           |          |          |
   ; hash        | aHash, p, d, w  |             |           |          |          |
   ; Animal Crossing QR code       |             |           |          |          |


   static DontVerifyImageType(ByRef image) {

      if !IsObject(image)
         throw Exception("Must be an object.")

      ; Check for image type declarations.
      ; Assumes that the user is telling the truth.

      if ObjHasOwnProp(image, "clipboard") {
         image := image.clipboard
         return "clipboard"
      }

      if ObjHasOwnProp(image, "object") {
         image := image.object
         return "object"
      }

      if ObjHasOwnProp(image, "buffer") {
         image := image.buffer
         return "buffer"
      }

      if ObjHasOwnProp(image, "screenshot") {
         image := image.screenshot
         return "screenshot"
      }

      if ObjHasOwnProp(image, "desktop") {
         image := image.desktop
         return "desktop"
      }

      if ObjHasOwnProp(image, "wallpaper") {
         image := image.wallpaper
         return "wallpaper"
      }

      if ObjHasOwnProp(image, "cursor") {
         image := image.cursor
         return "cursor"
      }

      if ObjHasOwnProp(image, "url") {
         image := image.url
         return "url"
      }

      if ObjHasOwnProp(image, "file") {
         image := image.file
         return "file"
      }

      if ObjHasOwnProp(image, "bitmap") {
         image := image.bitmap
         return "bitmap"
      }

      if ObjHasOwnProp(image, "hBitmap") {
         image := image.hBitmap
         return "hBitmap"
      }

      if ObjHasOwnProp(image, "monitor") {
         image := image.monitor
         return "monitor"
      }

      if ObjHasOwnProp(image, "hwnd") {
         image := image.hwnd
         return "hwnd"
      }

      if ObjHasOwnProp(image, "window") {
         image := image.window
         return "window"
      }

      if ObjHasOwnProp(image, "base64") {
         image := image.base64
         return "base64"
      }

      if ObjHasOwnProp(image, "sprite") {
         image := image.sprite
         return "sprite"
      }

      throw Exception("Invalid type.")
   }

   static ImageType(ByRef image) {
         ; Must be first as ClipboardAll is just an empty string when passed through functions.
         if this.is_clipboard(image)
            return "clipboard"

         ; Throw if the image is an empty string.
         if (image == "")
            throw Exception("Image data is an empty string."
            . "`nIf you passed ClipboardAll it does not contain compatible image data.")

      if IsObject(image) {
         ; An "object" is an object that implements a Bitmap() method returning a pointer to a GDI+ bitmap.
         if image.HasOwnMethod("Bitmap")
            return "object"

         ; A "buffer" is an AutoHotkey v2 buffer object.
         if ObjHasOwnProp(image, "pBitmap")
            return "buffer"

         ; A "screenshot" is an array of 4 numbers.
         if (image[1] ~= "^-?\d+$" && image[2] ~= "^-?\d+$" && image[3] ~= "^-?\d+$" && image[4] ~= "^-?\d+$")
            return "screenshot"
      }
         ; A "desktop" is a hidden window behind the desktop icons created by ImagePutDesktop.
         if (image = "desktop")
            return "desktop"

         ; A "wallpaper" is the desktop wallpaper.
         if (image = "wallpaper")
            return "wallpaper"

         ; A "cursor" is the name of a known cursor name.
         if (image ~= "(?i)^(IDC|OCR)?_?(A_Cursor|AppStarting|Arrow|Cross|Help|IBeam|"
         . "Icon|No|Size|SizeAll|SizeNESW|SizeNS|SizeNWSE|SizeWE|UpArrow|Wait|Unknown)$")
            return "cursor"

         ; A "url" satisfies the url format.
         if this.is_url(image)
            return "url"

         ; A "file" must exist.
         if FileExist(image)
            return "file"

         ; A "bitmap" is a pointer to a GDI+ Bitmap.
         if (!DllCall("gdiplus\GdipGetImageType", "ptr", image, "ptr*", type:=0) && type == 1)
            return "bitmap"

         ; A "hBitmap" is a handle to a GDI Bitmap.
         if (DllCall("GetObjectType", "ptr", image) == 7)
            return "hBitmap"

         ; A "monitor" is a number like 0 and 1.
         if (image = 0) ;if (image ~= "^\d+$" && image <= GetMonitorCount())
            return "monitor"

         ; A "hwnd" is a handle to a window and more commonly known as ahk_id.
         if DllCall("IsWindow", "ptr", image)
            return "hwnd"

         ; A "window" is anything considered a Window Title including ahk_class and "A".
         if WinExist(image)
            return "window"

         ; A "base64" string is binary image data encoded into text using only 64 characters.
         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.")
   }

   static toBitmap(type, ByRef image) {

      if (type = "clipboard")
         return this.from_clipboard()

      if (type = "object")
         return image.Bitmap()

      if (type = "buffer") {
         DllCall("gdiplus\GdipCloneImage", "ptr", image.pBitmap, "ptr*", pBitmap:=0)
         return pBitmap
      }

      if (type = "screenshot")
         return this.from_screenshot(image)

      if (type = "desktop")
         return this.from_desktop()

      if (type = "wallpaper")
         return this.from_wallpaper()

      if (type = "cursor")
         return this.from_cursor()

      if (type = "url")
         return this.from_url(image)

      if (type = "file") {
         DllCall("gdiplus\GdipCreateBitmapFromFile", "wstr", image, "ptr*", pBitmap:=0)
         return pBitmap
      }

      if (type = "bitmap") {
         DllCall("gdiplus\GdipCloneImage", "ptr", image, "ptr*", pBitmap:=0)
         return pBitmap
      }

      if (type = "hBitmap")
         return this.from_hBitmap(image)

      if (type = "monitor")
         return this.from_monitor(image)

      if (type = "hwnd" || type = "window") {
         image := (type = "window") ? WinExist(image) : image
         return this.from_hwnd(image)
      }

      if (type = "base64")
         return this.from_base64(image)

      if (type = "sprite")
         return this.from_sprite(image)

      throw Exception("Conversion from this type is not supported.")
   }

   static toCotype(cotype, ByRef pBitmap, terms*) {
      ; toCotype("clipboard", pBitmap)
      if (cotype = "clipboard")
         return this.put_clipboard(pBitmap)

      ; toCotype("buffer", pBitmap)
      if (cotype = "buffer") {
         buffer := {pBitmap: pBitmap}
            .DefineMethod("__New" , (self) => (this.gdiplusStartup(), self)) ; Increment GDI+ reference count
            .DefineMethod("__Delete", (self) => (this.gdiplusShutdown("smart_pointer", self.pBitmap)))
            .__New()  ; On deletion the buffer object will dispose of the bitmap. And it will decrement this.gdiplus.
         return buffer
      }

      ; toCotype("screenshot", pBitmap, screenshot, alpha)
      if (cotype = "screenshot")
         return this.put_screenshot(pBitmap, terms[1], terms[2])

      ; toCotype("desktop", pBitmap)
      if (cotype = "desktop")
         return this.put_desktop(pBitmap)

      ; toCotype("wallpaper", pBitmap)
      if (cotype = "wallpaper")
         return this.put_wallpaper(pBitmap)

      ; toCotype("cursor", pBitmap, xHotspot, yHotspot)
      if (cotype = "cursor")
         return this.put_cursor(pBitmap, terms[1], terms[2])

      ; toCotype("url", ????????????????????????
      if (cotype = "url") {
         ; put a url
      }

      ; toCotype("file", pBitmap, filename, quality)
      if (cotype = "file")
         return this.put_file(pBitmap, terms[1], terms[2])

      ; toCotype("window", pBitmap)
      if (cotype = "window")
         return "ahk_id " . this.Render({"bitmap":pBitmap}).AlwaysOnTop().ToolWindow().Caption().hwnd

      ; toCotype("hwnd", pBitmap)
      if (cotype = "hwnd")
         return this.Render({"bitmap":pBitmap}).hwnd

      ; toCotype("bitmap", pBitmap)
      if (cotype = "bitmap")
         return pBitmap

      ; toCotype("hBitmap", pBitmap, alpha)
      if (cotype = "hBitmap")
         return this.put_hBitmap(pBitmap, terms[1])

      ; toCotype("base64", pBitmap, extension, quality)
      if (cotype = "base64") ; Thanks to noname.
         return this.put_base64(pBitmap, terms[1], terms[2])

      throw Exception("Conversion to this type is not supported.")
   }

   static DisposeImage(ByRef pBitmap) {
      return DllCall("gdiplus\GdipDisposeImage", "ptr", pBitmap)
   }

   static BitmapCrop(ByRef pBitmap, crop) {
      ; Get Bitmap width and height and format.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0)
      DllCall("gdiplus\GdipGetImagePixelFormat", "ptr", pBitmap, "ptr*", format:=0)

      ; Are the numbers percentages?
      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]
      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]

      ; If numbers are negative, subtract the values from the edge.
      crop[3] := (crop[3] < 0) ?  width - Abs(crop[3]) - Abs(crop[1]) : crop[3]
      crop[4] := (crop[4] < 0) ? height - Abs(crop[4]) - Abs(crop[2]) : crop[4]
      crop[1] := Abs(crop[1])
      crop[2] := Abs(crop[2])

      ; Round to the nearest integer.
      crop[3] := Round(crop[1] + crop[3]) - Round(crop[1]) ; A reminder that width and height
      crop[4] := Round(crop[2] + crop[4]) - Round(crop[2]) ; are distances, not coordinates.
      crop[1] := Round(crop[1]) ; so the abstract concept of a distance must be resolved
      crop[2] := Round(crop[2]) ; into coordinates and then rounded and added up again.

      ; Variance Shift. Now place x,y before w,h because we are building abstracts from reals now.
      ; Before we were resolving abstracts into real coordinates, now it's the opposite.

      ; Ensure that coordinates can never exceed the expected Bitmap area.
      safe_x := (crop[1] > width) ? 0 : crop[1]                          ; Zero x if bigger.
      safe_y := (crop[2] > height) ? 0 : crop[2]                         ; Zero y if bigger.
      safe_w := (crop[1] + crop[3] > width) ? width - safe_x : crop[3]   ; Max w if bigger.
      safe_h := (crop[2] + crop[4] > height) ? height - safe_y : crop[4] ; Max h if bigger.

      ; Clone
      DllCall("gdiplus\GdipCloneBitmapAreaI"
               ,    "int", safe_x
               ,    "int", safe_y
               ,    "int", safe_w
               ,    "int", safe_h
               ,    "int", format
               ,    "ptr", pBitmap
               ,   "ptr*", pBitmapCrop:=0)

      return pBitmapCrop
   }

   static BitmapScale(ByRef pBitmap, scale) {
      ; Get Bitmap width and height and format.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0)
      DllCall("gdiplus\GdipGetImagePixelFormat", "ptr", pBitmap, "ptr*", format:=0)

      safe_w := Ceil(width * scale)
      safe_h := Ceil(height * scale)

      ; Create a new bitmap and get the graphics context.
      DllCall("gdiplus\GdipCreateBitmapFromScan0"
               , "int", safe_w, "int", safe_h, "int", 0, "int", format, "ptr", 0, "ptr*", pBitmapScale:=0)
      DllCall("gdiplus\GdipGetImageGraphicsContext", "ptr", pBitmapScale, "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. Not sure why the integer variant fails below.
      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", safe_w, "int", safe_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)
      return pBitmapScale
   }

   static is_clipboard(c) {
      ; ClipboardAll is always an empty string when passed into a function.
      if (c != "") ; Must be an empty string.
         return false

      ; Look through the clipboard for a memory object containing a BITMAPINFO structure followed by the bitmap bits.
      if DllCall("OpenClipboard", "ptr", 0) {
         _answer := DllCall("IsClipboardFormatAvailable", "uint", 8) ; CF_DIB
         DllCall("CloseClipboard")
         if (_answer)
            return true
      }

      ; Error messages are inaccurate and it can't be helped.
      return false
   }

   static is_url(url) {
      ; Thanks splattermania - https://www.php.net/manual/en/function.preg-match.php#93824

      regex := "^(?i)"
         . "((https?|ftp)\:\/\/)" ; SCHEME
         . "([a-z0-9+!*(),;?&=\$_.-]+(\:[a-z0-9+!*(),;?&=\$_.-]+)[email protected])?" ; User and Pass
         . "([a-z0-9-.]*)\.([a-z]{2,3})" ; Host or IP
         . "(\:[0-9]{2,5})?" ; Port
         . "(\/(?:[a-z0-9-_~!$&'()*+,;=:@]\.?)+)*\/?" ; Path
         . "(\?[a-z+&\$_.-][a-z0-9;:@&%=+\/\$_.-]*)?" ; GET Query
         . "(#[a-z_.-][a-z0-9+\$_.-]*)?$" ; Anchor
      return (url ~= regex)
   }

   static from_clipboard() {
      ; Thanks tic - https://www.autohotkey.com/boards/viewtopic.php?t=6517

      if DllCall("OpenClipboard", "ptr", 0) {
         hBitmap := DllCall("GetClipboardData", "uint", 2, "ptr")
         DllCall("CloseClipboard")
         DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", "ptr", hBitmap, "ptr", 0, "ptr*", pBitmap:=0)
         DllCall("DeleteObject", "ptr", hBitmap)
      }
      return pBitmap
   }

   static from_screenshot(ByRef image) {
      ; Thanks tic - https://www.autohotkey.com/boards/viewtopic.php?t=6517

      ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      bi := BufferAlloc(40, 0)                 ; sizeof(bi) = 40
         , NumPut(       40, bi,  0,   "uint") ; Size
         , NumPut( image[3], bi,  4,   "uint") ; Width
         , NumPut(-image[4], bi,  8,    "int") ; Height - Negative so (0, 0) is top-left.
         , NumPut(        1, bi, 12, "ushort") ; Planes
         , NumPut(       32, bi, 14, "ushort") ; 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) ; SRCCOPY

      ; 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
   }

   static from_wallpaper() {
      ; Get the width and height of all monitors.
      width  := DllCall("GetSystemMetrics", "int", 78)
      height := DllCall("GetSystemMetrics", "int", 79)

      ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      bi := BufferAlloc(40, 0)                 ; sizeof(bi) = 40
         , NumPut(       40, bi,  0,   "uint") ; Size
         , NumPut(    width, bi,  4,   "uint") ; Width
         , NumPut(  -height, bi,  8,    "int") ; Height - Negative so (0, 0) is top-left.
         , NumPut(        1, bi, 12, "ushort") ; Planes
         , NumPut(       32, bi, 14, "ushort") ; 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")

      ; Paints the desktop.
      DllCall("PaintDesktop", "ptr", hdc)

      ; 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
   }

   static from_cursor() {
      ; Thanks 23W - https://stackoverflow.com/a/13295280

      ; struct CURSORINFO - https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-cursorinfo
      ci := BufferAlloc(16+A_PtrSize, 0)                 ; sizeof(CURSORINFO) = 20, 24
         , NumPut(ci.size, ci, "int")
      DllCall("GetCursorInfo", "ptr", ci)
         ; cShow   := NumGet(ci,  4, "int")              ; 0x1 = CURSOR_SHOWING, 0x2 = CURSOR_SUPPRESSED
         , hCursor := NumGet(ci,  8, "ptr")
         ; xCursor := NumGet(ci,  8+A_PtrSize, "int")
         ; yCursor := NumGet(ci, 12+A_PtrSize, "int")

      ; struct ICONINFO - https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-iconinfo
      ii := BufferAlloc(8+3*A_PtrSize, 0)                ; sizeof(ICONINFO) = 20, 32
      DllCall("GetIconInfo", "ptr", hCursor, "ptr", ii)
         ; xHotspot := NumGet(ii, 4, "uint")
         ; yHotspot := NumGet(ii, 8, "uint")
         , hbmMask  := NumGet(ii, 8+A_PtrSize, "ptr")    ; x86:12, x64:16
         , hbmColor := NumGet(ii, 8+2*A_PtrSize, "ptr")  ; x86:16, x64:24

      ; struct BITMAP - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmap
      bm := BufferAlloc(16+2*A_PtrSize) ; sizeof(BITMAP) = 24, 32
      DllCall("GetObject", "ptr", hbmMask, "int", bm.size, "ptr", bm)
         , width  := NumGet(bm, 4, "uint")
         , height := NumGet(bm, 8, "uint") / (hbmColor ? 1 : 2) ; Black and White cursors have doubled height.

      ; Clean up these hBitmaps.
      DllCall("DeleteObject", "ptr", hbmMask)
      DllCall("DeleteObject", "ptr", hbmColor)

      ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      bi := BufferAlloc(40, 0)                 ; sizeof(bi) = 40
         , NumPut(       40, bi,  0,   "uint") ; Size
         , NumPut(    width, bi,  4,   "uint") ; Width
         , NumPut(  -height, bi,  8,    "int") ; Height - Negative so (0, 0) is top-left.
         , NumPut(        1, bi, 12, "ushort") ; Planes
         , NumPut(       32, bi, 14, "ushort") ; 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")

      ; This is the 32-bit ARGB pBitmap (different from an hBitmap) that will receive the final converted pixels.
      DllCall("gdiplus\GdipCreateBitmapFromScan0"
               , "int", width, "int", height, "int", 0, "int", 0x26200A, "ptr", 0, "ptr*", pBitmap:=0)

      ; Create a Scan0 buffer pointing to pBits. The buffer has pixel format pARGB.
      Rect := BufferAlloc(16, 0)               ; sizeof(Rect) = 16
         , NumPut(  width, Rect,  8,   "uint") ; Width
         , NumPut( height, Rect, 12,   "uint") ; Height
      BitmapData := BufferAlloc(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

      ; Use LockBits to create a writable buffer that converts pARGB to ARGB.
      DllCall("gdiplus\GdipBitmapLockBits"
               ,    "ptr", pBitmap
               ,    "ptr", Rect
               ,   "uint", 6            ; ImageLockMode.UserInputBuffer | ImageLockMode.WriteOnly
               ,    "int", 0xE200B      ; Format32bppPArgb
               ,    "ptr", BitmapData)  ; Contains the pointer (pBits) to the hbm.

      ; Don't use DI_DEFAULTSIZE to draw the icon like DrawIcon does as it will resize to 32 x 32.
      DllCall("DrawIconEx"
               , "ptr", hdc,     "int", 0, "int", 0
               , "ptr", hCursor, "int", 0, "int", 0
               , "uint", 0, "ptr", 0, "uint", 0x1 | 0x2 | 0x4) ; DI_MASK | DI_IMAGE | DI_COMPAT

      ; Convert the pARGB pixels copied into the device independent bitmap (hbm) to ARGB.
      DllCall("gdiplus\GdipBitmapUnlockBits", "ptr", pBitmap, "ptr", BitmapData)

      ; Clean up the icon and device context.
      DllCall("DestroyIcon",  "ptr", hCursor)
      DllCall("SelectObject", "ptr", hdc, "ptr", obm)
      DllCall("DeleteObject", "ptr", hbm)
      DllCall("DeleteDC",     "ptr", hdc)

      return pBitmap
   }

   static from_url(ByRef image) {
      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:=0)
      ObjRelease(pStream)
      return pBitmap
   }

   static from_hBitmap(ByRef image) {
      ; struct BITMAP - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmap
      dib := BufferAlloc(76+2*(A_PtrSize=8?4:0)+2*A_PtrSize) ; sizeof(DIBSECTION) = 84, 104
      DllCall("GetObject", "ptr", image, "int", dib.size, "ptr", dib)
         , width  := NumGet(dib, 4, "uint")
         , height := NumGet(dib, 8, "uint")
         , bpp    := NumGet(dib, 18, "ushort")

      ; Fallback to built-in method if pixels are not 32-bit ARGB.
      if (bpp != 32) { ; This built-in version is 120% faster but ignores transparency.
         DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", "ptr", image, "ptr", 0, "ptr*", pBitmap:=0)
         return pBitmap
      }

      ; Create a handle to a device context and associate the image.
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")           ; Creates a memory DC compatible with the current screen.
      obm := DllCall("SelectObject", "ptr", hdc, "ptr", image, "ptr") ; Put the (hBitmap) image onto the device context.

      ; Create a device independent bitmap with negative height. All DIBs use the screen pixel format (pARGB).
      ; Use hbm to buffer the image such that top-down and bottom-up images are mapped to this top-down buffer.
      ; pBits is the pointer to (top-down) pixel values. The Scan0 will point to the pBits.
      ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
      cdc := DllCall("CreateCompatibleDC", "ptr", hdc, "ptr")
      bi := BufferAlloc(40, 0)                 ; sizeof(bi) = 40
         , NumPut(       40, bi,  0,   "uint") ; Size
         , NumPut(    width, bi,  4,   "uint") ; Width
         , NumPut(  -height, bi,  8,    "int") ; Height - Negative so (0, 0) is top-left.
         , NumPut(        1, bi, 12, "ushort") ; Planes
         , NumPut(       32, bi, 14, "ushort") ; BitCount / BitsPerPixel
      hbm := DllCall("CreateDIBSection", "ptr", cdc, "ptr", bi, "uint", 0, "ptr*", pBits:=0, "ptr", 0, "uint", 0, "ptr")
      ob2 := DllCall("SelectObject", "ptr", cdc, "ptr", hbm, "ptr")

      ; This is the 32-bit ARGB pBitmap (different from an hBitmap) that will receive the final converted pixels.
      DllCall("gdiplus\GdipCreateBitmapFromScan0"
               , "int", width, "int", height, "int", 0, "int", 0x26200A, "ptr", 0, "ptr*", pBitmap:=0)

      ; Create a Scan0 buffer pointing to pBits. The buffer has pixel format pARGB.
      Rect := BufferAlloc(16, 0)               ; sizeof(Rect) = 16
         , NumPut(  width, Rect,  8,   "uint") ; Width
         , NumPut( height, Rect, 12,   "uint") ; Height
      BitmapData := BufferAlloc(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

      ; Use LockBits to create a writable buffer that converts pARGB to ARGB.
      DllCall("gdiplus\GdipBitmapLockBits"
               ,    "ptr", pBitmap
               ,    "ptr", Rect
               ,   "uint", 6            ; ImageLockMode.UserInputBuffer | ImageLockMode.WriteOnly
               ,    "int", 0xE200B      ; Format32bppPArgb
               ,    "ptr", BitmapData) ; Contains the pointer (pBits) to the hbm.

      ; Copies the image (hBitmap) to a top-down bitmap. Removes bottom-up-ness if present.
      DllCall("gdi32\BitBlt"
               , "ptr", cdc, "int", 0, "int", 0, "int", width, "int", height
               , "ptr", hdc, "int", 0, "int", 0, "uint", 0x00CC0020) ; SRCCOPY

      ; 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.
      DllCall("SelectObject", "ptr", cdc, "ptr", ob2)
      DllCall("DeleteObject", "ptr", hbm)
      DllCall("DeleteDC",     "ptr", cdc)
      DllCall("SelectObject", "ptr", hdc, "ptr", obm)
      DllCall("DeleteDC",     "ptr", hdc)

      return pBitmap
   }

   static from_monitor(ByRef image) {
      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)
      }
      return this.from_screenshot([x,y,w,h])
   }

   static from_hwnd(ByRef image) {
      ; Thanks tic - https://www.autohotkey.com/boards/viewtopic.php?t=6517

      ; Restore the window if minimized! Must be visible for capture.
      if DllCall("IsIconic", "ptr", image)
         DllCall("ShowWindow", "ptr", image, "int", 4)

      ; Get the width and height of the client window.
      Rect := BufferAlloc(16) ; sizeof(RECT) = 16
      DllCall("GetClientRect", "ptr", image, "ptr", Rect)
         , width  := NumGet(Rect, 8, "int")
         , height := NumGet(Rect, 12, "int")

      ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      bi := BufferAlloc(40, 0)                 ; sizeof(bi) = 40
         , NumPut(       40, bi,  0,   "uint") ; Size
         , NumPut(    width, bi,  4,   "uint") ; Width
         , NumPut(  -height, bi,  8,    "int") ; Height - Negative so (0, 0) is top-left.
         , NumPut(        1, bi, 12, "ushort") ; Planes
         , NumPut(       32, bi, 14, "ushort") ; 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")

      ; Print the window onto the hBitmap using an undocumented flag. https://stackoverflow.com/a/40042587
      DllCall("PrintWindow", "ptr", image, "ptr", hdc, "uint", 0x3) ; PW_CLIENTONLY | PW_RENDERFULLCONTENT
      ; Additional info on how this is implemented: https://www.reddit.com/r/windows/comments/8ffr56/altprintscreen/

      ; 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
   }

   static from_base64(ByRef image) {
      ; Trim whitespace and remove header.
      image := Trim(image)
      image := RegExReplace(image, "^data:image\/[a-z]+;base64,")

      ; Converts the image to binary data by first asking for the size.
      size := 0
      DllCall("crypt32\CryptStringToBinary", "ptr",&image, "uint",0, "uint",0x1, "ptr",   0, "uint*",size, "ptr",0, "ptr",0)
      bin := BufferAlloc(size, 0)
      DllCall("crypt32\CryptStringToBinary", "ptr",&image, "uint",0, "uint",0x1, "ptr", bin, "uint*",size, "ptr",0, "ptr",0)

      ; Makes a stream for conversion into a pBitmap.
      pStream := DllCall("shlwapi\SHCreateMemStream", "ptr", bin, "uint", size, "ptr")
      DllCall("gdiplus\GdipCreateBitmapFromStream", "ptr", pStream, "ptr*", pBitmap:=0)
      ObjRelease(pStream)

      return pBitmap
   }

   static from_sprite(ByRef image) {
      ; Create a source pBitmap and extract the width and height.
      if DllCall("gdiplus\GdipCreateBitmapFromFile", "wstr", image, "ptr*", sBitmap:=0)
         if !(sBitmap := this.from_url(image))
            throw Exception("Could not be loaded from a valid file path or URL.")

      ; Get Bitmap width and height.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", sBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", sBitmap, "uint*", height:=0)

      ; Create a destination pBitmap in 32-bit ARGB and get its device context though GDI+.
      ; Note that a device context from a graphics context can only be drawn on, not read.
      ; Also note that using a graphics context and blitting does not create a pixel perfect image.
      ; Using a DIB and LockBits is about 5% faster.
      DllCall("gdiplus\GdipCreateBitmapFromScan0"
               , "int", width, "int", height, "int", 0, "int", 0x26200A, "ptr", 0, "ptr*", dBitmap:=0)
      DllCall("gdiplus\GdipGetImageGraphicsContext", "ptr", dBitmap, "ptr*", dGraphics:=0)
      DllCall("gdiplus\GdipGetDC", "ptr", dGraphics, "ptr*", ddc:=0)

      ; Keep any existing transparency for whatever reason.
      hBitmap := this.put_hBitmap(sBitmap) ; Could copy this code here for even more speed.

      ; Create a source device context and associate the source hBitmap.
      sdc := DllCall("CreateCompatibleDC", "ptr", ddc, "ptr")
      obm := DllCall("SelectObject", "ptr", sdc, "ptr", hBitmap, "ptr")

      ; Copy the image making the top-left pixel the color key.
      DllCall("msimg32\TransparentBlt"
               , "ptr", ddc, "int", 0, "int", 0, "int", width, "int", height  ; destination
               , "ptr", sdc, "int", 0, "int", 0, "int", width, "int", height  ; source
               , "uint", DllCall("GetPixel", "ptr", sdc, "int", 0, "int", 0)) ; RGB pixel.

      ; Cleanup the hBitmap and device contexts.
      DllCall("SelectObject", "ptr", sdc, "ptr", obm)
      DllCall("DeleteObject", "ptr", hBitmap)
      DllCall("DeleteDC",     "ptr", sdc)

      ; Release the graphics context and delete.
      DllCall("gdiplus\GdipReleaseDC", "ptr", dGraphics, "ptr", ddc)
      DllCall("gdiplus\GdipDeleteGraphics", "ptr", dGraphics)

      return dBitmap
   }

   static put_clipboard(ByRef pBitmap) {
      ; Thanks tic - https://www.autohotkey.com/boards/viewtopic.php?t=6517

      off1 := A_PtrSize = 8 ? 52 : 44, off2 := A_PtrSize = 8 ? 32 : 24
      DllCall("gdiplus\GdipCreateHBITMAPFromBitmap", "ptr", pBitmap, "ptr*", hBitmap:=0, "uint", 0xFFFFFFFF)
      oi := BufferAlloc(A_PtrSize = 8 ? 104 : 84, 0) ; sizeof(oi) = 84, 104
      DllCall("GetObject", "ptr", hBitmap, "int", oi.size, "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.ptr+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")

      ; Returns an empty string as ClipboardAll would also be an empty string.
      return ""
   }

   static put_screenshot(ByRef pBitmap, screenshot := "", alpha := "") {
      ; Get Bitmap width and height.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0)

      x := (screenshot[1] != "") ? screenshot[1] : Round((A_ScreenWidth - width) / 2)
      y := (screenshot[2] != "") ? screenshot[2] : Round((A_ScreenHeight - height) / 2)
      w := (screenshot[3] != "") ? screenshot[3] : width
      h := (screenshot[4] != "") ? screenshot[4] : height

      ; Convert the Bitmap to a hBitmap and associate a device context for blitting.
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      hbm := this.put_hBitmap(pBitmap, alpha)
      obm := DllCall("SelectObject", "ptr", hdc, "ptr", hbm, "ptr")

      ; Get device context of spawned window.
      ddc := DllCall("GetDC", "ptr", 0, "ptr")

      ; Copies a portion of the screen to a new device context.
      DllCall("gdi32\StretchBlt"
               , "ptr", ddc, "int", x, "int", y, "int", w,     "int", h
               , "ptr", hdc, "int", 0, "int", 0, "int", width, "int", height
               , "uint", 0x00CC0020) ; SRCCOPY

      ; Release device context of spawned window.
      DllCall("ReleaseDC", "ptr", 0, "ptr", ddc)

      ; Cleanup the hBitmap and device contexts.
      DllCall("SelectObject", "ptr", hdc, "ptr", obm)
      DllCall("DeleteObject", "ptr", hbm)
      DllCall("DeleteDC",     "ptr", hdc)

      return [x,y,w,h]
   }

   static put_desktop(ByRef pBitmap) {
      ; Thanks Gerald Degeneve - https://www.codeproject.com/Articles/856020/Draw-Behind-Desktop-Icons-in-Windows-plus

      ; Get Bitmap width and height.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0)

      ; Convert the Bitmap to a hBitmap and associate a device context for blitting.
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      hbm := this.put_hBitmap(pBitmap)
      obm := DllCall("SelectObject", "ptr", hdc, "ptr", hbm, "ptr")

      ; Post-Creator's Update Windows 10. WM_SPAWN_WORKER = 0x052C
      DllCall("SendMessage", "ptr", WinExist("ahk_class Progman"), "uint", 0x052C, "ptr", 0x0000000D, "ptr", 0)
      DllCall("SendMessage", "ptr", WinExist("ahk_class Progman"), "uint", 0x052C, "ptr", 0x0000000D, "ptr", 1)

      ; Find the child window.
      windows := WinGetList("ahk_class WorkerW")
      Loop windows.length
         hwnd := windows[A_Index]
      until DllCall("FindWindowEx", "ptr", hwnd, "ptr", 0, "str", "SHELLDLL_DefView", "ptr", 0)
      WorkerW := DllCall("FindWindowEx", "ptr", 0, "ptr", hwnd, "str", "WorkerW", "ptr", 0, "ptr")

      ; Maybe this hack gets patched. Tough luck!
      if (!WorkerW)
         throw Exception("Could not draw on the desktop.")

      ; Position the image in the center. This line can be removed.
      DllCall("SetWindowPos", "ptr", WorkerW, "ptr", 1
               , "int", Round((A_ScreenWidth - width) / 2)   ; x coordinate
               , "int", Round((A_ScreenHeight - height) / 2) ; y coordinate
               , "int", width, "int", height, "uint", 0)

      ; Get device context of spawned window.
      ddc := DllCall("GetDCEx", "ptr", WorkerW, "ptr", 0, "int", 0x403, "ptr")

      ; Copies a portion of the screen to a new device context.
      DllCall("gdi32\BitBlt"
               , "ptr", ddc, "int", 0, "int", 0, "int", width, "int", height
               , "ptr", hdc, "int", 0, "int", 0, "uint", 0x00CC0020) ; SRCCOPY

      ; Release device context of spawned window.
      DllCall("ReleaseDC", "ptr", 0, "ptr", ddc)

      ; Cleanup the hBitmap and device contexts.
      DllCall("SelectObject", "ptr", hdc, "ptr", obm)
      DllCall("DeleteObject", "ptr", hbm)
      DllCall("DeleteDC",     "ptr", hdc)

      return "desktop"
   }

   static put_wallpaper(ByRef pBitmap) {
      path := this.put_file(pBitmap, "temp.png")
      cc := DllCall("GetFullPathName", "str", path, "uint", 0, "ptr", 0, "ptr", 0, "uint")
      VarSetStrCapacity(buf, cc)
      DllCall("GetFullPathName", "str", path, "uint", cc, "str", buf, "ptr", 0, "uint")
      DllCall("SystemParametersInfo", "uint", 20, "uint", 0, "str", buf, "uint", 2)
      Sleep 1 ; Needed as there is some lag.
      FileDelete path
      return "wallpaper"
   }

   static put_cursor(ByRef pBitmap, xHotspot := "", yHotspot := "") {
      ; Thanks Nick - https://stackoverflow.com/a/550965

      ; Creates an icon that can be used as a cursor.
      DllCall("gdiplus\GdipCreateHICONFromBitmap", "ptr", pBitmap, "ptr*", hIcon:=0)

      ; Sets the hotspot of the cursor by changing the icon into a cursor.
      if (xHotspot != "" || yHotspot != "") {
         ; struct ICONINFO - https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-iconinfo
         ii := BufferAlloc(8+3*A_PtrSize, 0)                          ; sizeof(ICONINFO) = 20, 32
         DllCall("GetIconInfo", "ptr", hIcon, "ptr", ii)              ; Fill the ICONINFO structure.
            , NumPut(false, ii, 0, "uint")                            ; true/false are icon/cursor respectively.
            , (xHotspot != "") ? NumPut(xHotspot, ii, 4, "uint") : "" ; Set the xHotspot value. (Default: center point)
            , (yHotspot != "") ? NumPut(yHotspot, ii, 8, "uint") : "" ; Set the yHotspot value. (Default: center point)
         DllCall("DestroyIcon", "ptr", hIcon)                         ; Destroy the icon after getting the ICONINFO structure.
         hIcon := DllCall("CreateIconIndirect", "ptr", ii, "ptr")     ; Create a new cursor using ICONINFO.

         ; Clean up hbmMask and hbmColor created as a result of GetIconInfo.
         DllCall("DeleteObject", "ptr", NumGet(ii, 8+A_PtrSize, "ptr"))   ; hbmMask
         DllCall("DeleteObject", "ptr", NumGet(ii, 8+2*A_PtrSize, "ptr")) ; hbmColor
      }

      ; Loop over all 16 system cursors and change them all to the new cursor.
      SystemCursors := "32512,32513,32514,32515,32516,32640,32641,32642,32643,32644,32645,32646,32648,32649,32650,32651"
      Loop Parse, SystemCursors, ","
      { ; Must copy the handle 16 times as SetSystemCursor deletes the handle 16 times.
         hCursor := DllCall("CopyImage", "ptr", hIcon, "uint", 2, "int", 0, "int", 0, "uint", 0, "ptr")
         DllCall("SetSystemCursor", "ptr", hCursor, "int", A_LoopField) ; calls DestroyCursor
      }

      ; Destroy the original hIcon. DestroyCursor and DestroyIcon are the same function in C.
      DllCall("DestroyCursor", "ptr", hIcon)

      ; Returns the word A_Cursor so that it doesn't evaluate immediately.
      return "A_Cursor"
   }

   static put_file(ByRef pBitmap, sOutput, Quality:=75) {
      ; Thanks tic - https://www.autohotkey.com/boards/viewtopic.php?t=6517
      _p := 0

      SplitPath sOutput,,_path, Extension, _filename
      Extension := (Extension ~= "^(?i:bmp|dib|rle|jpg|jpeg|jpe|jfif|gif|tif|tiff|png)$") ? Extension : "png"
      Extension := "." Extension
      sOutput := _path . _filename . Extension

      DllCall("gdiplus\GdipGetImageEncodersSize", "uint*", nCount:=0, "uint*", nSize:=0)
      ci := BufferAlloc(nSize)
      DllCall("gdiplus\GdipGetImageEncoders", "uint", nCount, "uint", nSize, "ptr", ci)

      If (A_IsUnicode){
         StrGet_Name := "StrGet"

         Loop nCount
         {
            sString := %StrGet_Name%(NumGet(ci, (idx := (48+7*A_PtrSize)*(A_Index-1))+32+3*A_PtrSize), "UTF-16")
            if !InStr(sString, "*" Extension)
               continue

            pCodec := ci.ptr+idx
            break
         }
      } else {
         Loop nCount
         {
            Location := NumGet(ci, 76*(A_Index-1)+44)
            nSize := DllCall("WideCharToMultiByte", "uint", 0, "uint", 0, "uint", Location, "int", -1, "uint", 0, "int",  0, "uint", 0, "uint", 0)
            VarSetStrCapacity(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.ptr+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:=0)
            EncoderParameters := BufferAlloc(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.ptr-pad-4
                  NumPut(Quality, NumGet(NumPut(4, NumPut(1, _p+0)+20, "UInt")), "UInt")
                  break
               }
            }
         }
      }

      _E := DllCall("gdiplus\GdipSaveImageToFile", "ptr", pBitmap, "wstr", sOutput, "ptr", pCodec, "uint", _p ? _p : 0)
      return sOutput
   }

   static put_hBitmap(ByRef pBitmap, alpha := "") {
      ; Revert to built in functionality if a replacement color is declared.
      if (alpha != "") { ; This built-in version is about 25% slower.
         DllCall("gdiplus\GdipCreateHBITMAPFromBitmap", "ptr", pBitmap, "ptr*", hBitmap:=0, "uint", alpha)
         return hBitmap
      }

      ; Get Bitmap width and height.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0)

      ; 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 := BufferAlloc(40, 0)                 ; sizeof(bi) = 40
         , NumPut(       40, bi,  0,   "uint") ; Size
         , NumPut(    width, bi,  4,   "uint") ; Width
         , NumPut(  -height, bi,  8,    "int") ; Height - Negative so (0, 0) is top-left.
         , NumPut(        1, bi, 12, "ushort") ; Planes
         , NumPut(       32, bi, 14, "ushort") ; 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")

      ; Transfer data from source pBitmap to an hBitmap manually.
      Rect := BufferAlloc(16, 0)               ; sizeof(Rect) = 16
         , NumPut(  width, Rect,  8,   "uint") ; Width
         , NumPut( height, Rect, 12,   "uint") ; Height
      BitmapData := BufferAlloc(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)  ; Contains the pointer (pBits) to the hbm.
      DllCall("gdiplus\GdipBitmapUnlockBits", "ptr", pBitmap, "ptr", BitmapData)

      ; Cleanup the hBitmap and device contexts.
      DllCall("SelectObject", "ptr", hdc, "ptr", obm)
      DllCall("DeleteDC",     "ptr", hdc)

      return hbm
   }

   static put_base64(ByRef pBitmap, file := "", Quality := "") {
      ; Thanks noname - https://www.autohotkey.com/boards/viewtopic.php?style=7&p=144247#p144247

      if !(file ~= "(?i)bmp|dib|rle|jpg|jpeg|jpe|jfif|gif|tif|tiff|png")
         file := "png"
      Extension := "." file

      DllCall("gdiplus\GdipGetImageEncodersSize", "uint*",nCount:=0, "uint*",nSize:=0)
      ci := BufferAlloc(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.ptr+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:=0)
         EncoderParameters := BufferAlloc(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.ptr-pad-4
               NumPut(Quality, NumGet(NumPut(4, NumPut(1, p+0)+20, "uint")), "uint")
               break
            }
         }
      }

      DllCall("ole32\CreateStreamOnHGlobal", "ptr",0, "int",true, "ptr*",pStream:=0)
      DllCall("gdiplus\GdipSaveImageToStream", "ptr",pBitmap, "ptr",pStream, "ptr",pCodec, "uint",p ? p : 0)
      DllCall("ole32\GetHGlobalFromStream", "ptr",pStream, "uint*",hData:=0)
      pData := DllCall("GlobalLock", "ptr",hData, "ptr")
      nSize := DllCall("GlobalSize", "uint",pData)

      bin := BufferAlloc(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.
      base64Length := 0
      DllCall("Crypt32.dll\CryptBinaryToStringA", "ptr",bin, "uint",nSize, "uint",0x40000001, "ptr",0, "uint*",base64Length)
      base64 := BufferAlloc(base64Length, 0)
      DllCall("Crypt32.dll\CryptBinaryToStringA", "ptr",bin, "uint",nSize, "uint",0x40000001, "ptr",base64, "uint*",base64Length)

      return StrGet(base64, base64Length, "CP0")
   }

   static gdiplus := 0

   static gdiplusStartup() {
      this.gdiplus := (this.gdiplus == "") ? 1 : this.gdiplus + 1

      ; Startup gdiplus when counter goes from 0 -> 1 or "" -> 1.
      if (this.gdiplus == 1) {
         DllCall("LoadLibrary", "str", "gdiplus")
         si := BufferAlloc(A_PtrSize = 8 ? 24 : 16, 0)
            , NumPut(0x1, si)
         DllCall("gdiplus\GdiplusStartup", "ptr*", pToken:=0, "ptr", si, "ptr", 0)
         this.pToken := pToken
      }
   }

   static gdiplusShutdown(cotype := "", ByRef pBitmap := "") {
      this.gdiplus := this.gdiplus - 1

      ; When a buffer object is deleted a bitmap is sent here for disposal.
      if (cotype == "smart_pointer")
         if DllCall("gdiplus\GdipDisposeImage", "ptr", pBitmap)
            throw Exception("The bitmap of this buffer object has already been deleted.")

      ; Shutdown gdiplus if pToken is owned and when counter goes from 1 -> 0.
      if (this.gdiplus == 0) {
         DllCall("gdiplus\GdiplusShutdown", "ptr", this.pToken)
         DllCall("FreeLibrary", "ptr", DllCall("GetModuleHandle", "str", "gdiplus", "ptr"))

         ; Exit if GDI+ is still loaded. GdiplusNotInitialized = 18
         if (18 != DllCall("gdiplus\GdipCreateImageAttributes", "ptr*", ImageAttr:=0)) {
            DllCall("gdiplus\GdipDisposeImageAttributes", "ptr", ImageAttr)
            return
         }

         ; Otherwise GDI+ has been truly unloaded from the script and objects are out of scope.
         if (cotype = "bitmap")
            throw Exception("Out of scope error. `n`nIf you wish to handle raw pointers to GDI+ bitmaps, add the line"
               . "`n`n`t`t" this.__class ".gdiplusStartup()`n`nor 'pToken := Gdip_Startup()' to the top of your script."
               . "`nYou can copy this message by pressing Ctrl + C.")
      }
   }
} ; End of ImagePut class.
v0
Last edited by iseahound on 07 Jun 2020, 19:50, edited 5 times in total.

Helgef
Posts: 4458
Joined: 17 Jul 2016, 01:02
Contact:

Re: [a210] ImagePut - Puts an image from anywhere to anywhere

Post by Helgef » 06 Jun 2020, 13:30

Lines, 391, 393,not compatible. 1039, use char count. 1143, use ptr prop, not &. Note, you use the deprecated numput parameters.

I'll try this when I get to a PC.

Thanks for sharing, cheers.

Edit, that was relating to the second post.

guest3456
Posts: 3134
Joined: 09 Oct 2013, 10:31

Re: [a210] ImagePut - Puts an image from anywhere to anywhere

Post by guest3456 » 06 Jun 2020, 20:43

also should be v2-a110


iseahound
Posts: 608
Joined: 13 Aug 2016, 21:04
GitHub: iseahound

Re: [a110] ImagePut - Puts an image from anywhere to anywhere

Post by iseahound » 07 Jun 2020, 15:07

Thanks guest3456.

@Helgef
So VarSetStrCapacity now uses wide chars?

Also I can't make heads or tails translating this to v2.

Code: Select all

         buffer := {__New: ObjBindMethod(this, "gdiplusStartup") ; Increment GDI+ reference count
               , __Delete: ObjBindMethod(this, "gdiplusShutdown", "smart_pointer", pBitmap)}
         buffer := new buffer      ; On deletion the buffer object will dispose of the bitmap.
         buffer.pBitmap := pBitmap ; And it will decrement this.gdiplus.
I'm trying to:

1) Create a simple object with __New() and __Delete() methods that are copies of existing ones.
2) Instantiate the object.
3) Add a single property.

Alternatively, it is possible to manually construct an object with a property, and two meta-methods in the base, and call the __New() meta method once.

I asked the question here. https://www.autohotkey.com/boards/viewtopic.php?f=82&t=77021&p=334250#p334250

swagfag
Posts: 3972
Joined: 11 Jan 2017, 17:59

Re: [a110] ImagePut - Puts an image from anywhere to anywhere

Post by swagfag » 07 Jun 2020, 19:09

Code: Select all

Buffer := {}
Buffer.DefineMethod('New', (_this, bmp) => (
	this.gdiplusStartup(),
	_this.pBitmap := bmp,
	_this
))
Buffer.DefineMethod('__Delete', _this => this.gdiplusShutdown('smart_pointer', _this.pBitmap))

B := Buffer.New(pBitmap)
like this i guess if its meant to be defined in the context of some object's method

iseahound
Posts: 608
Joined: 13 Aug 2016, 21:04
GitHub: iseahound

Re: [a110] ImagePut - Puts an image from anywhere to anywhere

Post by iseahound » 07 Jun 2020, 19:46

Yes! This is what I ended up using:

Code: Select all

         buffer := {}.DefineMethod("__New" , (self) => (this.gdiplusStartup(), self)) ; Increment GDI+ reference count
                     .DefineMethod("__Delete", (self) => (this.gdiplusShutdown("smart_pointer", self.pBitmap)))
         buffer := buffer.__New()  ; On deletion the buffer object will dispose of the bitmap.
         buffer.pBitmap := pBitmap ; And it will decrement this.gdiplus.
It's too bad objects are so nasty in v1 and v2. Easy to use, horrible to construct.

EDIT: This looks better

Code: Select all

         buffer := {pBitmap: pBitmap}
            .DefineMethod("__New" , (self) => (this.gdiplusStartup(), self)) ; Increment GDI+ reference count
            .DefineMethod("__Delete", (self) => (this.gdiplusShutdown("smart_pointer", self.pBitmap)))
            .__New()  ; On deletion the buffer object will dispose of the bitmap. And it will decrement this.gdiplus.

User avatar
kczx3
Posts: 1140
Joined: 06 Oct 2015, 21:39

Re: [a110] ImagePut - Puts an image from anywhere to anywhere

Post by kczx3 » 07 Jun 2020, 19:58

Why are you calling __new? I didn’t think that was meant to be called directly

Helgef
Posts: 4458
Joined: 17 Jul 2016, 01:02
Contact:

Re: [a110] ImagePut - Puts an image from anywhere to anywhere

Post by Helgef » 08 Jun 2020, 07:18

VarSetStrCapacity wrote: Specify for RequestedCapacity the number of characters that the variable should be able to hold after the adjustment. RequestedCapacity does not include the internal zero terminator. For example, specifying 1 would allow the variable to hold up to one character in addition to its internal terminator.

iseahound
Posts: 608
Joined: 13 Aug 2016, 21:04
GitHub: iseahound

[a111] ImagePut - Puts an image from anywhere to anywhere

Post by iseahound » 12 Jun 2020, 11:02

a111 compatable

Code: Select all

; Script:    ImagePut.ahk
; Author:    iseahound
; License:   MIT License
; Version:   2020-05-22
; Release:   2020-06-13

; ImagePut - Puts an image from anywhere to anywhere.
; This is a simple functor designed to be intuitive.
; I hope people find this reference library useful.


; Puts the image into a file format and returns a base64 encoded string.
;   extension  -  File Encoding           |  string   ->   bmp, gif, jpg, png, tiff
;   quality    -  JPEG Quality Level      |  integer  ->   0 - 100
ImagePutBase64(ByRef image, extension := "", quality := "")
   => ImagePut("base64", image,,, extension, quality)


; Puts the image into a GDI+ Bitmap and returns a pointer.
ImagePutBitmap(ByRef image)
   => ImagePut("bitmap", image)


; Puts the image into a GDI+ Bitmap and returns a buffer object with GDI+ scope.
ImagePutBuffer(ByRef image)
   => ImagePut("buffer", image)


; Puts the image onto the clipboard and returns an empty string.
ImagePutClipboard(ByRef image)
   => ImagePut("clipboard", image)


; Puts the image as the cursor and returns the string "A_Cursor".
;   xHotspot   -  X Click Point           |  pixel    ->   0 - width
;   yHotspot   -  Y Click Point           |  pixel    ->   0 - height
ImagePutCursor(ByRef image, xHotspot := "", yHotspot := "")
   => ImagePut("cursor", image,,, xHotspot, yHotspot)


; Puts the image behind the desktop icons and returns the string "desktop".
;   scale      -  Scale Factor            |  real     ->   A_ScreenHeight / height.
ImagePutDesktop(ByRef image, scale := 1)
   => ImagePut("desktop", image,, scale)


; Puts the image into a file and returns the file name.
;   filename   -  File Extension          |  string   ->   *.bmp, *.gif, *.jpg, *.png, *.tiff
;   quality    -  JPEG Quality Level      |  integer  ->   0 - 100
ImagePutFile(ByRef image, filename := "", quality := "")
   => ImagePut("file", image,,, filename, quality)


; Puts the image into a device independent bitmap and returns a handle.
;   quality    -  JPEG Quality Level      |  integer  ->   0 - 100
;   alpha      -  Alpha Replacement Color |  RGB      ->   0xFFFFFF
ImagePutHBitmap(ByRef image, alpha := "")
   => ImagePut("hBitmap", image,,, alpha)


; Puts the image on the shared screen device context and returns an array of coordinates.
;   screenshot -  Screen Coordinates      |  array    ->   [x,y,w,h] or [0,0]
;   alpha      -  Alpha Replacement Color |  RGB      ->   0xFFFFFF
ImagePutScreenshot(ByRef image, screenshot := "", alpha := "")
   => ImagePut("screenshot", image,,, screenshot, alpha)


; Puts the image as the desktop wallpaper and returns the string "wallpaper".
ImagePutWallpaper(ByRef image)
   => ImagePut("wallpaper", image)



; ImagePut() - Puts an image from anywhere to anywhere.
;   cotype     -  Output Type             |  string   ->   Case Insensitive. Read documentation.
;   image      -  Input Image             |  image    ->   Anything. Refer to ImageType().
;   crop       -  Crop Coordinates        |  array    ->   [x,y,w,h] could be negative or percent.
;   scale      -  Scale Factor            |  real     ->   2.0
;   terms*     -  Additional Parameters   |  variadic ->   Extra parameters found in toCotype().
ImagePut(cotype, ByRef image, crop := "", scale := "", terms*)
   => ImagePut.call(cotype, image, crop, scale, terms*)



class ImagePut {

   static call(cotype, ByRef image, crop := "", scale := "", terms*) {

      this.gdiplusStartup()

      ; Take a guess as to what the image might be. (>90% accuracy!)
      try type := this.DontVerifyImageType(image)
      catch
         type := this.ImageType(image)

      ; Qualify additional parameters for correctness.
      _crop := IsObject(crop)
         && crop[1] ~= "^-?\d+(\.\d*)?%?$" && crop[2] ~= "^-?\d+(\.\d*)?%?$"
         && crop[3] ~= "^-?\d+(\.\d*)?%?$" && crop[4] ~= "^-?\d+(\.\d*)?%?$"
      _scale := scale != 1 && scale ~= "^\d+(\.\d+)?$"

      ; Make a copy of the image as a pBitmap.
      pBitmap := this.toBitmap(type, image)

      ; Crop the image.
      if (_crop) {
         pBitmap2 := this.BitmapCrop(pBitmap, crop)
         DllCall("gdiplus\GdipDisposeImage", "ptr", pBitmap)
         pBitmap := pBitmap2
      }

      ; Scale the image.
      if (_scale) {
         pBitmap2 := this.BitmapScale(pBitmap, scale)
         DllCall("gdiplus\GdipDisposeImage", "ptr", pBitmap)
         pBitmap := pBitmap2
      }

      ; Put the pBitmap to wherever the cotype specifies.
      coimage := this.toCotype(cotype, pBitmap, terms*)

      ; Clean up the pBitmap copy. Export raw pointers if requested.
      if !(cotype = "bitmap" || cotype = "buffer")
         DllCall("gdiplus\GdipDisposeImage", "ptr", pBitmap)

      this.gdiplusShutdown(cotype)

      return coimage
   }

   ; Types       | Example         | Explicit    | Inferred  | Input    | Output   |
   ;             |                 | Don'tVerify | ImageType | toBitmap | toCotype |
   ; clipboard   | ClipboardAll    |     yes     |    yes    |    yes   |    yes   | - no transparency
   ; object      | object.Bitmap() |     yes     |    yes    |    yes   |          |
   ; buffer      | bitmap.pBitmap  |     yes     |    yes    |    yes   |    yes   |
   ; screenshot  | [x,y,w,h]       |     yes     |    yes    |    yes   |          |
   ; desktop     | "desktop"       |     yes     |    yes    |    no    |    yes   |
   ; wallpaper   | "wallpaper"     |     yes     |    yes    |    yes   |    yes   |
   ; cursor      | A_Cursor        |     yes     |    yes    |    yes   |    yes   |
   ; url         | https://        |     yes     |    yes    |    yes   |          |
   ; file        | picture.bmp     |     yes     |    yes    |    yes   |    yes   |
   ; bitmap      | some number     |     yes     |    yes    |    yes   |    yes   |
   ; hBitmap     | some number     |     yes     |    yes    |    yes   |    yes   |
   ; monitor     | 0 or # < 10     |     yes     |    yes    |          |          |
   ; hwnd        | 0x              |     yes     |    yes    |    yes   |          |
   ; window      | A               |     yes     |    yes    |    yes   |          |
   ; base64      | base64 data     |     yes     |    yes    |    yes   |    yes   |
   ; sprite      | file or url     |     yes     |    no     |    yes   |    no    |
   ; icon        |                 |             |           |          |          |
   ; printer     |                 |             |           |          |          |
   ; findtext    |                 |             |           |          |          |
   ; thumbnail   |                 |       ?     |           |          |          | DwmRegisterThumbnail
   ; video       |                 |             |           |          |          |
   ; formdata    |                 |             |           |          |          |
   ; stream      |                 |             |           |          |          |
   ; randomaccessstream | pointer  |             |           |          |          |
   ; hIcon       |                 |             |           |          |          |
   ; trayicon    |                 |             |           |          |          |
   ; hash        | aHash, p, d, w  |             |           |          |          |
   ; Animal Crossing QR code       |             |           |          |          |


   static DontVerifyImageType(ByRef image) {

      if !IsObject(image)
         throw Exception("Must be an object.")

      ; Check for image type declarations.
      ; Assumes that the user is telling the truth.

      if ObjHasOwnProp(image, "clipboard") {
         image := image.clipboard
         return "clipboard"
      }

      if ObjHasOwnProp(image, "object") {
         image := image.object
         return "object"
      }

      if ObjHasOwnProp(image, "buffer") {
         image := image.buffer
         return "buffer"
      }

      if ObjHasOwnProp(image, "screenshot") {
         image := image.screenshot
         return "screenshot"
      }

      if ObjHasOwnProp(image, "desktop") {
         image := image.desktop
         return "desktop"
      }

      if ObjHasOwnProp(image, "wallpaper") {
         image := image.wallpaper
         return "wallpaper"
      }

      if ObjHasOwnProp(image, "cursor") {
         image := image.cursor
         return "cursor"
      }

      if ObjHasOwnProp(image, "url") {
         image := image.url
         return "url"
      }

      if ObjHasOwnProp(image, "file") {
         image := image.file
         return "file"
      }

      if ObjHasOwnProp(image, "bitmap") {
         image := image.bitmap
         return "bitmap"
      }

      if ObjHasOwnProp(image, "hBitmap") {
         image := image.hBitmap
         return "hBitmap"
      }

      if ObjHasOwnProp(image, "monitor") {
         image := image.monitor
         return "monitor"
      }

      if ObjHasOwnProp(image, "hwnd") {
         image := image.hwnd
         return "hwnd"
      }

      if ObjHasOwnProp(image, "window") {
         image := image.window
         return "window"
      }

      if ObjHasOwnProp(image, "base64") {
         image := image.base64
         return "base64"
      }

      if ObjHasOwnProp(image, "sprite") {
         image := image.sprite
         return "sprite"
      }

      throw Exception("Invalid type.")
   }

   static ImageType(ByRef image) {
         ; Must be first as ClipboardAll is just an empty string when passed through functions.
         if this.is_clipboard(image)
            return "clipboard"

         ; Throw if the image is an empty string.
         if (image == "")
            throw Exception("Image data is an empty string."
            . "`nIf you passed ClipboardAll it does not contain compatible image data.")

      if IsObject(image) {
         ; An "object" is an object that implements a Bitmap() method returning a pointer to a GDI+ bitmap.
         if image.HasOwnMethod("Bitmap")
            return "object"

         ; A "buffer" is an AutoHotkey v2 buffer object.
         if ObjHasOwnProp(image, "pBitmap")
            return "buffer"

         ; A "screenshot" is an array of 4 numbers.
         if (image[1] ~= "^-?\d+$" && image[2] ~= "^-?\d+$" && image[3] ~= "^-?\d+$" && image[4] ~= "^-?\d+$")
            return "screenshot"
      }
         ; A "desktop" is a hidden window behind the desktop icons created by ImagePutDesktop.
         if (image = "desktop")
            return "desktop"

         ; A "wallpaper" is the desktop wallpaper.
         if (image = "wallpaper")
            return "wallpaper"

         ; A "cursor" is the name of a known cursor name.
         if (image ~= "(?i)^(IDC|OCR)?_?(A_Cursor|AppStarting|Arrow|Cross|Help|IBeam|"
         . "Icon|No|Size|SizeAll|SizeNESW|SizeNS|SizeNWSE|SizeWE|UpArrow|Wait|Unknown)$")
            return "cursor"

         ; A "url" satisfies the url format.
         if this.is_url(image)
            return "url"

         ; A "file" must exist.
         if FileExist(image)
            return "file"

         ; A "bitmap" is a pointer to a GDI+ Bitmap.
         if (!DllCall("gdiplus\GdipGetImageType", "ptr", image, "ptr*", type:=0) && type == 1)
            return "bitmap"

         ; A "hBitmap" is a handle to a GDI Bitmap.
         if (DllCall("GetObjectType", "ptr", image) == 7)
            return "hBitmap"

         ; A "monitor" is a number like 0 and 1.
         if (image = 0) ;if (image ~= "^\d+$" && image <= GetMonitorCount())
            return "monitor"

         ; A "hwnd" is a handle to a window and more commonly known as ahk_id.
         if DllCall("IsWindow", "ptr", image)
            return "hwnd"

         ; A "window" is anything considered a Window Title including ahk_class and "A".
         if WinExist(image)
            return "window"

         ; A "base64" string is binary image data encoded into text using only 64 characters.
         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.")
   }

   static toBitmap(type, ByRef image) {

      if (type = "clipboard")
         return this.from_clipboard()

      if (type = "object")
         return image.Bitmap()

      if (type = "buffer") {
         DllCall("gdiplus\GdipCloneImage", "ptr", image.pBitmap, "ptr*", pBitmap:=0)
         return pBitmap
      }

      if (type = "screenshot")
         return this.from_screenshot(image)

      if (type = "desktop")
         return this.from_desktop()

      if (type = "wallpaper")
         return this.from_wallpaper()

      if (type = "cursor")
         return this.from_cursor()

      if (type = "url")
         return this.from_url(image)

      if (type = "file") {
         DllCall("gdiplus\GdipCreateBitmapFromFile", "wstr", image, "ptr*", pBitmap:=0)
         return pBitmap
      }

      if (type = "bitmap") {
         DllCall("gdiplus\GdipCloneImage", "ptr", image, "ptr*", pBitmap:=0)
         return pBitmap
      }

      if (type = "hBitmap")
         return this.from_hBitmap(image)

      if (type = "monitor")
         return this.from_monitor(image)

      if (type = "hwnd" || type = "window") {
         image := (type = "window") ? WinExist(image) : image
         return this.from_hwnd(image)
      }

      if (type = "base64")
         return this.from_base64(image)

      if (type = "sprite")
         return this.from_sprite(image)

      throw Exception("Conversion from this type is not supported.")
   }

   static toCotype(cotype, ByRef pBitmap, terms*) {
      ; toCotype("clipboard", pBitmap)
      if (cotype = "clipboard")
         return this.put_clipboard(pBitmap)

      ; toCotype("buffer", pBitmap)
      if (cotype = "buffer") {
         buffer := {pBitmap: pBitmap}
            .DefineMethod("__New" , (self) => (this.gdiplusStartup(), self)) ; Increment GDI+ reference count
            .DefineMethod("__Delete", (self) => (this.gdiplusShutdown("smart_pointer", self.pBitmap)))
            .__New()  ; On deletion the buffer object will dispose of the bitmap. And it will decrement this.gdiplus.
         return buffer
      }

      ; toCotype("screenshot", pBitmap, screenshot, alpha)
      if (cotype = "screenshot")
         return this.put_screenshot(pBitmap, terms[1], terms[2])

      ; toCotype("desktop", pBitmap)
      if (cotype = "desktop")
         return this.put_desktop(pBitmap)

      ; toCotype("wallpaper", pBitmap)
      if (cotype = "wallpaper")
         return this.put_wallpaper(pBitmap)

      ; toCotype("cursor", pBitmap, xHotspot, yHotspot)
      if (cotype = "cursor")
         return this.put_cursor(pBitmap, terms[1], terms[2])

      ; toCotype("url", ????????????????????????
      if (cotype = "url") {
         ; put a url
      }

      ; toCotype("file", pBitmap, filename, quality)
      if (cotype = "file")
         return this.put_file(pBitmap, terms[1], terms[2])

      ; toCotype("window", pBitmap)
      if (cotype = "window")
         return "ahk_id " . this.Render({"bitmap":pBitmap}).AlwaysOnTop().ToolWindow().Caption().hwnd

      ; toCotype("hwnd", pBitmap)
      if (cotype = "hwnd")
         return this.Render({"bitmap":pBitmap}).hwnd

      ; toCotype("bitmap", pBitmap)
      if (cotype = "bitmap")
         return pBitmap

      ; toCotype("hBitmap", pBitmap, alpha)
      if (cotype = "hBitmap")
         return this.put_hBitmap(pBitmap, terms[1])

      ; toCotype("base64", pBitmap, extension, quality)
      if (cotype = "base64") ; Thanks to noname.
         return this.put_base64(pBitmap, terms[1], terms[2])

      throw Exception("Conversion to this type is not supported.")
   }

   static DisposeImage(ByRef pBitmap) {
      return DllCall("gdiplus\GdipDisposeImage", "ptr", pBitmap)
   }

   static BitmapCrop(ByRef pBitmap, crop) {
      ; Get Bitmap width and height and format.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0)
      DllCall("gdiplus\GdipGetImagePixelFormat", "ptr", pBitmap, "ptr*", format:=0)

      ; Are the numbers percentages?
      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]
      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]

      ; If numbers are negative, subtract the values from the edge.
      crop[3] := (crop[3] < 0) ?  width - Abs(crop[3]) - Abs(crop[1]) : crop[3]
      crop[4] := (crop[4] < 0) ? height - Abs(crop[4]) - Abs(crop[2]) : crop[4]
      crop[1] := Abs(crop[1])
      crop[2] := Abs(crop[2])

      ; Round to the nearest integer.
      crop[3] := Round(crop[1] + crop[3]) - Round(crop[1]) ; A reminder that width and height
      crop[4] := Round(crop[2] + crop[4]) - Round(crop[2]) ; are distances, not coordinates.
      crop[1] := Round(crop[1]) ; so the abstract concept of a distance must be resolved
      crop[2] := Round(crop[2]) ; into coordinates and then rounded and added up again.

      ; Variance Shift. Now place x,y before w,h because we are building abstracts from reals now.
      ; Before we were resolving abstracts into real coordinates, now it's the opposite.

      ; Ensure that coordinates can never exceed the expected Bitmap area.
      safe_x := (crop[1] > width) ? 0 : crop[1]                          ; Zero x if bigger.
      safe_y := (crop[2] > height) ? 0 : crop[2]                         ; Zero y if bigger.
      safe_w := (crop[1] + crop[3] > width) ? width - safe_x : crop[3]   ; Max w if bigger.
      safe_h := (crop[2] + crop[4] > height) ? height - safe_y : crop[4] ; Max h if bigger.

      ; Clone
      DllCall("gdiplus\GdipCloneBitmapAreaI"
               ,    "int", safe_x
               ,    "int", safe_y
               ,    "int", safe_w
               ,    "int", safe_h
               ,    "int", format
               ,    "ptr", pBitmap
               ,   "ptr*", pBitmapCrop:=0)

      return pBitmapCrop
   }

   static BitmapScale(ByRef pBitmap, scale) {
      ; Get Bitmap width and height and format.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0)
      DllCall("gdiplus\GdipGetImagePixelFormat", "ptr", pBitmap, "ptr*", format:=0)

      safe_w := Ceil(width * scale)
      safe_h := Ceil(height * scale)

      ; Create a new bitmap and get the graphics context.
      DllCall("gdiplus\GdipCreateBitmapFromScan0"
               , "int", safe_w, "int", safe_h, "int", 0, "int", format, "ptr", 0, "ptr*", pBitmapScale:=0)
      DllCall("gdiplus\GdipGetImageGraphicsContext", "ptr", pBitmapScale, "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. Not sure why the integer variant fails below.
      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", safe_w, "int", safe_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)
      return pBitmapScale
   }

   static is_clipboard(c) {
      ; ClipboardAll is always an empty string when passed into a function.
      if (c != "") ; Must be an empty string.
         return false

      ; Look through the clipboard for a memory object containing a BITMAPINFO structure followed by the bitmap bits.
      if DllCall("OpenClipboard", "ptr", 0) {
         _answer := DllCall("IsClipboardFormatAvailable", "uint", 8) ; CF_DIB
         DllCall("CloseClipboard")
         if (_answer)
            return true
      }

      ; Error messages are inaccurate and it can't be helped.
      return false
   }

   static is_url(url) {
      ; Thanks splattermania - https://www.php.net/manual/en/function.preg-match.php#93824

      regex := "^(?i)"
         . "((https?|ftp)\:\/\/)" ; SCHEME
         . "([a-z0-9+!*(),;?&=\$_.-]+(\:[a-z0-9+!*(),;?&=\$_.-]+)[email protected])?" ; User and Pass
         . "([a-z0-9-.]*)\.([a-z]{2,3})" ; Host or IP
         . "(\:[0-9]{2,5})?" ; Port
         . "(\/(?:[a-z0-9-_~!$&'()*+,;=:@]\.?)+)*\/?" ; Path
         . "(\?[a-z+&\$_.-][a-z0-9;:@&%=+\/\$_.-]*)?" ; GET Query
         . "(#[a-z_.-][a-z0-9+\$_.-]*)?$" ; Anchor
      return (url ~= regex)
   }

   static from_clipboard() {
      ; Thanks tic - https://www.autohotkey.com/boards/viewtopic.php?t=6517

      if DllCall("OpenClipboard", "ptr", 0) {
         hBitmap := DllCall("GetClipboardData", "uint", 2, "ptr")
         DllCall("CloseClipboard")
         DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", "ptr", hBitmap, "ptr", 0, "ptr*", pBitmap:=0)
         DllCall("DeleteObject", "ptr", hBitmap)
      }
      return pBitmap
   }

   static from_screenshot(ByRef image) {
      ; Thanks tic - https://www.autohotkey.com/boards/viewtopic.php?t=6517

      ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      bi := BufferAlloc(40, 0)                 ; sizeof(bi) = 40
         , NumPut(  "uint",        40, bi,  0) ; Size
         , NumPut(  "uint",  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) ; SRCCOPY

      ; 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
   }

   static from_wallpaper() {
      ; Get the width and height of all monitors.
      width  := DllCall("GetSystemMetrics", "int", 78)
      height := DllCall("GetSystemMetrics", "int", 79)

      ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      bi := BufferAlloc(40, 0)                 ; sizeof(bi) = 40
         , NumPut(  "uint",        40, bi,  0) ; Size
         , NumPut(  "uint",     width, bi,  4) ; Width
         , NumPut(   "int",   -height, 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")

      ; Paints the desktop.
      DllCall("PaintDesktop", "ptr", hdc)

      ; 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
   }

   static from_cursor() {
      ; Thanks 23W - https://stackoverflow.com/a/13295280

      ; struct CURSORINFO - https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-cursorinfo
      ci := BufferAlloc(16+A_PtrSize, 0)                 ; sizeof(CURSORINFO) = 20, 24
         , NumPut("int", ci.size, ci)
      DllCall("GetCursorInfo", "ptr", ci)
         ; cShow   := NumGet(ci,  4, "int")              ; 0x1 = CURSOR_SHOWING, 0x2 = CURSOR_SUPPRESSED
         , hCursor := NumGet(ci,  8, "ptr")
         ; xCursor := NumGet(ci,  8+A_PtrSize, "int")
         ; yCursor := NumGet(ci, 12+A_PtrSize, "int")

      ; struct ICONINFO - https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-iconinfo
      ii := BufferAlloc(8+3*A_PtrSize, 0)                ; sizeof(ICONINFO) = 20, 32
      DllCall("GetIconInfo", "ptr", hCursor, "ptr", ii)
         ; xHotspot := NumGet(ii, 4, "uint")
         ; yHotspot := NumGet(ii, 8, "uint")
         , hbmMask  := NumGet(ii, 8+A_PtrSize, "ptr")    ; x86:12, x64:16
         , hbmColor := NumGet(ii, 8+2*A_PtrSize, "ptr")  ; x86:16, x64:24

      ; struct BITMAP - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmap
      bm := BufferAlloc(16+2*A_PtrSize) ; sizeof(BITMAP) = 24, 32
      DllCall("GetObject", "ptr", hbmMask, "int", bm.size, "ptr", bm)
         , width  := NumGet(bm, 4, "uint")
         , height := NumGet(bm, 8, "uint") / (hbmColor ? 1 : 2) ; Black and White cursors have doubled height.

      ; Clean up these hBitmaps.
      DllCall("DeleteObject", "ptr", hbmMask)
      DllCall("DeleteObject", "ptr", hbmColor)

      ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      bi := BufferAlloc(40, 0)                 ; sizeof(bi) = 40
         , NumPut(  "uint",        40, bi,  0) ; Size
         , NumPut(  "uint",     width, bi,  4) ; Width
         , NumPut(   "int",   -height, 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")

      ; This is the 32-bit ARGB pBitmap (different from an hBitmap) that will receive the final converted pixels.
      DllCall("gdiplus\GdipCreateBitmapFromScan0"
               , "int", width, "int", height, "int", 0, "int", 0x26200A, "ptr", 0, "ptr*", pBitmap:=0)

      ; Create a Scan0 buffer pointing to pBits. The buffer has pixel format pARGB.
      Rect := BufferAlloc(16, 0)               ; sizeof(Rect) = 16
         , NumPut(  "uint",   width, Rect,  8) ; Width
         , NumPut(  "uint",  height, Rect, 12) ; Height
      BitmapData := BufferAlloc(16+2*A_PtrSize, 0)      ; sizeof(BitmapData) = 24, 32
         , NumPut(  "uint",      width, BitmapData,  0) ; Width
         , NumPut(  "uint",     height, BitmapData,  4) ; Height
         , NumPut(   "int",  4 * width, BitmapData,  8) ; Stride
         , NumPut(   "int",    0xE200B, BitmapData, 12) ; PixelFormat
         , NumPut(   "ptr",      pBits, BitmapData, 16) ; Scan0

      ; Use LockBits to create a writable buffer that converts pARGB to ARGB.
      DllCall("gdiplus\GdipBitmapLockBits"
               ,    "ptr", pBitmap
               ,    "ptr", Rect
               ,   "uint", 6            ; ImageLockMode.UserInputBuffer | ImageLockMode.WriteOnly
               ,    "int", 0xE200B      ; Format32bppPArgb
               ,    "ptr", BitmapData)  ; Contains the pointer (pBits) to the hbm.

      ; Don't use DI_DEFAULTSIZE to draw the icon like DrawIcon does as it will resize to 32 x 32.
      DllCall("DrawIconEx"
               , "ptr", hdc,     "int", 0, "int", 0
               , "ptr", hCursor, "int", 0, "int", 0
               , "uint", 0, "ptr", 0, "uint", 0x1 | 0x2 | 0x4) ; DI_MASK | DI_IMAGE | DI_COMPAT

      ; Convert the pARGB pixels copied into the device independent bitmap (hbm) to ARGB.
      DllCall("gdiplus\GdipBitmapUnlockBits", "ptr", pBitmap, "ptr", BitmapData)

      ; Clean up the icon and device context.
      DllCall("DestroyIcon",  "ptr", hCursor)
      DllCall("SelectObject", "ptr", hdc, "ptr", obm)
      DllCall("DeleteObject", "ptr", hbm)
      DllCall("DeleteDC",     "ptr", hdc)

      return pBitmap
   }

   static from_url(ByRef image) {
      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:=0)
      ObjRelease(pStream)
      return pBitmap
   }

   static from_hBitmap(ByRef image) {
      ; struct BITMAP - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmap
      dib := BufferAlloc(68+4*A_PtrSize) ; sizeof(DIBSECTION) = 84, 100
      DllCall("GetObject", "ptr", image, "int", dib.size, "ptr", dib)
         , width  := NumGet(dib, 4, "uint")
         , height := NumGet(dib, 8, "uint")
         , bpp    := NumGet(dib, 18, "ushort")

      ; Fallback to built-in method if pixels are not 32-bit ARGB.
      if (bpp != 32) { ; This built-in version is 120% faster but ignores transparency.
         DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", "ptr", image, "ptr", 0, "ptr*", pBitmap:=0)
         return pBitmap
      }

      ; Create a handle to a device context and associate the image.
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")           ; Creates a memory DC compatible with the current screen.
      obm := DllCall("SelectObject", "ptr", hdc, "ptr", image, "ptr") ; Put the (hBitmap) image onto the device context.

      ; Create a device independent bitmap with negative height. All DIBs use the screen pixel format (pARGB).
      ; Use hbm to buffer the image such that top-down and bottom-up images are mapped to this top-down buffer.
      ; pBits is the pointer to (top-down) pixel values. The Scan0 will point to the pBits.
      ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
      cdc := DllCall("CreateCompatibleDC", "ptr", hdc, "ptr")
      bi := BufferAlloc(40, 0)                 ; sizeof(bi) = 40
         , NumPut(  "uint",        40, bi,  0) ; Size
         , NumPut(  "uint",     width, bi,  4) ; Width
         , NumPut(   "int",   -height, 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", cdc, "ptr", bi, "uint", 0, "ptr*", pBits:=0, "ptr", 0, "uint", 0, "ptr")
      ob2 := DllCall("SelectObject", "ptr", cdc, "ptr", hbm, "ptr")

      ; This is the 32-bit ARGB pBitmap (different from an hBitmap) that will receive the final converted pixels.
      DllCall("gdiplus\GdipCreateBitmapFromScan0"
               , "int", width, "int", height, "int", 0, "int", 0x26200A, "ptr", 0, "ptr*", pBitmap:=0)

      ; Create a Scan0 buffer pointing to pBits. The buffer has pixel format pARGB.
      Rect := BufferAlloc(16, 0)               ; sizeof(Rect) = 16
         , NumPut(  "uint",   width, Rect,  8) ; Width
         , NumPut(  "uint",  height, Rect, 12) ; Height
      BitmapData := BufferAlloc(16+2*A_PtrSize, 0)      ; sizeof(BitmapData) = 24, 32
         , NumPut(  "uint",      width, BitmapData,  0) ; Width
         , NumPut(  "uint",     height, BitmapData,  4) ; Height
         , NumPut(   "int",  4 * width, BitmapData,  8) ; Stride
         , NumPut(   "int",    0xE200B, BitmapData, 12) ; PixelFormat
         , NumPut(   "ptr",      pBits, BitmapData, 16) ; Scan0

      ; Use LockBits to create a writable buffer that converts pARGB to ARGB.
      DllCall("gdiplus\GdipBitmapLockBits"
               ,    "ptr", pBitmap
               ,    "ptr", Rect
               ,   "uint", 6            ; ImageLockMode.UserInputBuffer | ImageLockMode.WriteOnly
               ,    "int", 0xE200B      ; Format32bppPArgb
               ,    "ptr", BitmapData) ; Contains the pointer (pBits) to the hbm.

      ; Copies the image (hBitmap) to a top-down bitmap. Removes bottom-up-ness if present.
      DllCall("gdi32\BitBlt"
               , "ptr", cdc, "int", 0, "int", 0, "int", width, "int", height
               , "ptr", hdc, "int", 0, "int", 0, "uint", 0x00CC0020) ; SRCCOPY

      ; 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.
      DllCall("SelectObject", "ptr", cdc, "ptr", ob2)
      DllCall("DeleteObject", "ptr", hbm)
      DllCall("DeleteDC",     "ptr", cdc)
      DllCall("SelectObject", "ptr", hdc, "ptr", obm)
      DllCall("DeleteDC",     "ptr", hdc)

      return pBitmap
   }

   static from_monitor(ByRef image) {
      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)
      }
      return this.from_screenshot([x,y,w,h])
   }

   static from_hwnd(ByRef image) {
      ; Thanks tic - https://www.autohotkey.com/boards/viewtopic.php?t=6517

      ; Restore the window if minimized! Must be visible for capture.
      if DllCall("IsIconic", "ptr", image)
         DllCall("ShowWindow", "ptr", image, "int", 4)

      ; Get the width and height of the client window.
      Rect := BufferAlloc(16) ; sizeof(RECT) = 16
      DllCall("GetClientRect", "ptr", image, "ptr", Rect)
         , width  := NumGet(Rect, 8, "int")
         , height := NumGet(Rect, 12, "int")

      ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      bi := BufferAlloc(40, 0)                 ; sizeof(bi) = 40
         , NumPut(  "uint",        40, bi,  0) ; Size
         , NumPut(  "uint",     width, bi,  4) ; Width
         , NumPut(   "int",   -height, 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")

      ; Print the window onto the hBitmap using an undocumented flag. https://stackoverflow.com/a/40042587
      DllCall("PrintWindow", "ptr", image, "ptr", hdc, "uint", 0x3) ; PW_CLIENTONLY | PW_RENDERFULLCONTENT
      ; Additional info on how this is implemented: https://www.reddit.com/r/windows/comments/8ffr56/altprintscreen/

      ; 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
   }

   static from_base64(ByRef image) {
      ; Trim whitespace and remove header.
      image := Trim(image)
      image := RegExReplace(image, "^data:image\/[a-z]+;base64,")

      ; Converts the image to binary data by first asking for the size.
      size := 0
      DllCall("crypt32\CryptStringToBinary", "ptr",StrPtr(image), "uint",0, "uint",0x1, "ptr",   0, "uint*",size, "ptr",0, "ptr",0)
      bin := BufferAlloc(size, 0)
      DllCall("crypt32\CryptStringToBinary", "ptr",StrPtr(image), "uint",0, "uint",0x1, "ptr", bin, "uint*",size, "ptr",0, "ptr",0)

      ; Makes a stream for conversion into a pBitmap.
      pStream := DllCall("shlwapi\SHCreateMemStream", "ptr", bin, "uint", size, "ptr")
      DllCall("gdiplus\GdipCreateBitmapFromStream", "ptr", pStream, "ptr*", pBitmap:=0)
      ObjRelease(pStream)

      return pBitmap
   }

   static from_sprite(ByRef image) {
      ; Create a source pBitmap and extract the width and height.
      if DllCall("gdiplus\GdipCreateBitmapFromFile", "wstr", image, "ptr*", sBitmap:=0)
         if !(sBitmap := this.from_url(image))
            throw Exception("Could not be loaded from a valid file path or URL.")

      ; Get Bitmap width and height.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", sBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", sBitmap, "uint*", height:=0)

      ; Create a destination pBitmap in 32-bit ARGB and get its device context though GDI+.
      ; Note that a device context from a graphics context can only be drawn on, not read.
      ; Also note that using a graphics context and blitting does not create a pixel perfect image.
      ; Using a DIB and LockBits is about 5% faster.
      DllCall("gdiplus\GdipCreateBitmapFromScan0"
               , "int", width, "int", height, "int", 0, "int", 0x26200A, "ptr", 0, "ptr*", dBitmap:=0)
      DllCall("gdiplus\GdipGetImageGraphicsContext", "ptr", dBitmap, "ptr*", dGraphics:=0)
      DllCall("gdiplus\GdipGetDC", "ptr", dGraphics, "ptr*", ddc:=0)

      ; Keep any existing transparency for whatever reason.
      hBitmap := this.put_hBitmap(sBitmap) ; Could copy this code here for even more speed.

      ; Create a source device context and associate the source hBitmap.
      sdc := DllCall("CreateCompatibleDC", "ptr", ddc, "ptr")
      obm := DllCall("SelectObject", "ptr", sdc, "ptr", hBitmap, "ptr")

      ; Copy the image making the top-left pixel the color key.
      DllCall("msimg32\TransparentBlt"
               , "ptr", ddc, "int", 0, "int", 0, "int", width, "int", height  ; destination
               , "ptr", sdc, "int", 0, "int", 0, "int", width, "int", height  ; source
               , "uint", DllCall("GetPixel", "ptr", sdc, "int", 0, "int", 0)) ; RGB pixel.

      ; Cleanup the hBitmap and device contexts.
      DllCall("SelectObject", "ptr", sdc, "ptr", obm)
      DllCall("DeleteObject", "ptr", hBitmap)
      DllCall("DeleteDC",     "ptr", sdc)

      ; Release the graphics context and delete.
      DllCall("gdiplus\GdipReleaseDC", "ptr", dGraphics, "ptr", ddc)
      DllCall("gdiplus\GdipDeleteGraphics", "ptr", dGraphics)

      return dBitmap
   }

   static put_clipboard(ByRef pBitmap) {
      ; Thanks tic - https://www.autohotkey.com/boards/viewtopic.php?t=6517

      off1 := A_PtrSize = 8 ? 52 : 44, off2 := A_PtrSize = 8 ? 32 : 24
      DllCall("gdiplus\GdipCreateHBITMAPFromBitmap", "ptr", pBitmap, "ptr*", hBitmap:=0, "uint", 0xFFFFFFFF)
      oi := BufferAlloc(A_PtrSize = 8 ? 104 : 84, 0) ; sizeof(oi) = 84, 104
      DllCall("GetObject", "ptr", hBitmap, "int", oi.size, "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.ptr+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")

      ; Returns an empty string as ClipboardAll would also be an empty string.
      return ""
   }

   static put_screenshot(ByRef pBitmap, screenshot := "", alpha := "") {
      ; Get Bitmap width and height.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0)

      x := (screenshot[1] != "") ? screenshot[1] : Round((A_ScreenWidth - width) / 2)
      y := (screenshot[2] != "") ? screenshot[2] : Round((A_ScreenHeight - height) / 2)
      w := (screenshot[3] != "") ? screenshot[3] : width
      h := (screenshot[4] != "") ? screenshot[4] : height

      ; Convert the Bitmap to a hBitmap and associate a device context for blitting.
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      hbm := this.put_hBitmap(pBitmap, alpha)
      obm := DllCall("SelectObject", "ptr", hdc, "ptr", hbm, "ptr")

      ; Get device context of spawned window.
      ddc := DllCall("GetDC", "ptr", 0, "ptr")

      ; Copies a portion of the screen to a new device context.
      DllCall("gdi32\StretchBlt"
               , "ptr", ddc, "int", x, "int", y, "int", w,     "int", h
               , "ptr", hdc, "int", 0, "int", 0, "int", width, "int", height
               , "uint", 0x00CC0020) ; SRCCOPY

      ; Release device context of spawned window.
      DllCall("ReleaseDC", "ptr", 0, "ptr", ddc)

      ; Cleanup the hBitmap and device contexts.
      DllCall("SelectObject", "ptr", hdc, "ptr", obm)
      DllCall("DeleteObject", "ptr", hbm)
      DllCall("DeleteDC",     "ptr", hdc)

      return [x,y,w,h]
   }

   static put_desktop(ByRef pBitmap) {
      ; Thanks Gerald Degeneve - https://www.codeproject.com/Articles/856020/Draw-Behind-Desktop-Icons-in-Windows-plus

      ; Get Bitmap width and height.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0)

      ; Convert the Bitmap to a hBitmap and associate a device context for blitting.
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      hbm := this.put_hBitmap(pBitmap)
      obm := DllCall("SelectObject", "ptr", hdc, "ptr", hbm, "ptr")

      ; Post-Creator's Update Windows 10. WM_SPAWN_WORKER = 0x052C
      DllCall("SendMessage", "ptr", WinExist("ahk_class Progman"), "uint", 0x052C, "ptr", 0x0000000D, "ptr", 0)
      DllCall("SendMessage", "ptr", WinExist("ahk_class Progman"), "uint", 0x052C, "ptr", 0x0000000D, "ptr", 1)

      ; Find the child window.
      windows := WinGetList("ahk_class WorkerW")
      Loop windows.length
         hwnd := windows[A_Index]
      until DllCall("FindWindowEx", "ptr", hwnd, "ptr", 0, "str", "SHELLDLL_DefView", "ptr", 0)
      WorkerW := DllCall("FindWindowEx", "ptr", 0, "ptr", hwnd, "str", "WorkerW", "ptr", 0, "ptr")

      ; Maybe this hack gets patched. Tough luck!
      if (!WorkerW)
         throw Exception("Could not draw on the desktop.")

      ; Position the image in the center. This line can be removed.
      DllCall("SetWindowPos", "ptr", WorkerW, "ptr", 1
               , "int", Round((A_ScreenWidth - width) / 2)   ; x coordinate
               , "int", Round((A_ScreenHeight - height) / 2) ; y coordinate
               , "int", width, "int", height, "uint", 0)

      ; Get device context of spawned window.
      ddc := DllCall("GetDCEx", "ptr", WorkerW, "ptr", 0, "int", 0x403, "ptr")

      ; Copies a portion of the screen to a new device context.
      DllCall("gdi32\BitBlt"
               , "ptr", ddc, "int", 0, "int", 0, "int", width, "int", height
               , "ptr", hdc, "int", 0, "int", 0, "uint", 0x00CC0020) ; SRCCOPY

      ; Release device context of spawned window.
      DllCall("ReleaseDC", "ptr", 0, "ptr", ddc)

      ; Cleanup the hBitmap and device contexts.
      DllCall("SelectObject", "ptr", hdc, "ptr", obm)
      DllCall("DeleteObject", "ptr", hbm)
      DllCall("DeleteDC",     "ptr", hdc)

      return "desktop"
   }

   static put_wallpaper(ByRef pBitmap) {
      path := this.put_file(pBitmap, "temp.png")
      cc := DllCall("GetFullPathName", "str", path, "uint", 0, "ptr", 0, "ptr", 0, "uint")
      VarSetStrCapacity(buf, cc)
      DllCall("GetFullPathName", "str", path, "uint", cc, "str", buf, "ptr", 0, "uint")
      DllCall("SystemParametersInfo", "uint", 20, "uint", 0, "str", buf, "uint", 2)
      Sleep 1 ; Needed as there is some lag.
      FileDelete path
      return "wallpaper"
   }

   static put_cursor(ByRef pBitmap, xHotspot := "", yHotspot := "") {
      ; Thanks Nick - https://stackoverflow.com/a/550965

      ; Creates an icon that can be used as a cursor.
      DllCall("gdiplus\GdipCreateHICONFromBitmap", "ptr", pBitmap, "ptr*", hIcon:=0)

      ; Sets the hotspot of the cursor by changing the icon into a cursor.
      if (xHotspot != "" || yHotspot != "") {
         ; struct ICONINFO - https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-iconinfo
         ii := BufferAlloc(8+3*A_PtrSize, 0)                          ; sizeof(ICONINFO) = 20, 32
         DllCall("GetIconInfo", "ptr", hIcon, "ptr", ii)              ; Fill the ICONINFO structure.
            , NumPut("uint", false, ii, 0)                            ; true/false are icon/cursor respectively.
            , (xHotspot != "") ? NumPut("uint", xHotspot, ii, 4) : "" ; Set the xHotspot value. (Default: center point)
            , (yHotspot != "") ? NumPut("uint", yHotspot, ii, 8) : "" ; Set the yHotspot value. (Default: center point)
         DllCall("DestroyIcon", "ptr", hIcon)                         ; Destroy the icon after getting the ICONINFO structure.
         hIcon := DllCall("CreateIconIndirect", "ptr", ii, "ptr")     ; Create a new cursor using ICONINFO.

         ; Clean up hbmMask and hbmColor created as a result of GetIconInfo.
         DllCall("DeleteObject", "ptr", NumGet(ii, 8+A_PtrSize, "ptr"))   ; hbmMask
         DllCall("DeleteObject", "ptr", NumGet(ii, 8+2*A_PtrSize, "ptr")) ; hbmColor
      }

      ; Loop over all 16 system cursors and change them all to the new cursor.
      SystemCursors := "32512,32513,32514,32515,32516,32640,32641,32642,32643,32644,32645,32646,32648,32649,32650,32651"
      Loop Parse, SystemCursors, ","
      { ; Must copy the handle 16 times as SetSystemCursor deletes the handle 16 times.
         hCursor := DllCall("CopyImage", "ptr", hIcon, "uint", 2, "int", 0, "int", 0, "uint", 0, "ptr")
         DllCall("SetSystemCursor", "ptr", hCursor, "int", A_LoopField) ; calls DestroyCursor
      }

      ; Destroy the original hIcon. DestroyCursor and DestroyIcon are the same function in C.
      DllCall("DestroyCursor", "ptr", hIcon)

      ; Returns the word A_Cursor so that it doesn't evaluate immediately.
      return "A_Cursor"
   }

   static put_file(ByRef pBitmap, sOutput, Quality:=75) {
      ; Thanks tic - https://www.autohotkey.com/boards/viewtopic.php?t=6517
      _p := 0

      SplitPath sOutput,,_path, Extension, _filename
      Extension := (Extension ~= "^(?i:bmp|dib|rle|jpg|jpeg|jpe|jfif|gif|tif|tiff|png)$") ? Extension : "png"
      Extension := "." Extension
      sOutput := _path . _filename . Extension

      DllCall("gdiplus\GdipGetImageEncodersSize", "uint*", nCount:=0, "uint*", nSize:=0)
      ci := BufferAlloc(nSize)
      DllCall("gdiplus\GdipGetImageEncoders", "uint", nCount, "uint", nSize, "ptr", ci)

      If (true){
         StrGet_Name := "StrGet"

         Loop nCount
         {
            sString := %StrGet_Name%(NumGet(ci, (idx := (48+7*A_PtrSize)*(A_Index-1))+32+3*A_PtrSize), "UTF-16")
            if !InStr(sString, "*" Extension)
               continue

            pCodec := ci.ptr+idx
            break
         }
      } else {
         Loop nCount
         {
            Location := NumGet(ci, 76*(A_Index-1)+44)
            nSize := DllCall("WideCharToMultiByte", "uint", 0, "uint", 0, "uint", Location, "int", -1, "uint", 0, "int",  0, "uint", 0, "uint", 0)
            VarSetStrCapacity(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.ptr+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:=0)
            EncoderParameters := BufferAlloc(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.ptr-pad-4
                  NumPut("uint", Quality, NumGet(NumPut(4, NumPut(1, _p+0)+20, "UInt")))
                  break
               }
            }
         }
      }

      _E := DllCall("gdiplus\GdipSaveImageToFile", "ptr", pBitmap, "wstr", sOutput, "ptr", pCodec, "uint", _p ? _p : 0)
      return sOutput
   }

   static put_hBitmap(ByRef pBitmap, alpha := "") {
      ; Revert to built in functionality if a replacement color is declared.
      if (alpha != "") { ; This built-in version is about 25% slower.
         DllCall("gdiplus\GdipCreateHBITMAPFromBitmap", "ptr", pBitmap, "ptr*", hBitmap:=0, "uint", alpha)
         return hBitmap
      }

      ; Get Bitmap width and height.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0)

      ; 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 := BufferAlloc(40, 0)                 ; sizeof(bi) = 40
         , NumPut(  "uint",        40, bi,  0) ; Size
         , NumPut(  "uint",     width, bi,  4) ; Width
         , NumPut(   "int",   -height, 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")

      ; Transfer data from source pBitmap to an hBitmap manually.
      Rect := BufferAlloc(16, 0)               ; sizeof(Rect) = 16
         , NumPut(  "uint",   width, Rect,  8) ; Width
         , NumPut(  "uint",  height, Rect, 12) ; Height
      BitmapData := BufferAlloc(16+2*A_PtrSize, 0)      ; sizeof(BitmapData) = 24, 32
         , NumPut(  "uint",      width, BitmapData,  0) ; Width
         , NumPut(  "uint",     height, BitmapData,  4) ; Height
         , NumPut(   "int",  4 * width, BitmapData,  8) ; Stride
         , NumPut(   "int",    0xE200B, BitmapData, 12) ; PixelFormat
         , 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)

      ; Cleanup the hBitmap and device contexts.
      DllCall("SelectObject", "ptr", hdc, "ptr", obm)
      DllCall("DeleteDC",     "ptr", hdc)

      return hbm
   }

   static put_base64(ByRef pBitmap, file := "", Quality := "") {
      ; Thanks noname - https://www.autohotkey.com/boards/viewtopic.php?style=7&p=144247#p144247

      if !(file ~= "(?i)bmp|dib|rle|jpg|jpeg|jpe|jfif|gif|tif|tiff|png")
         file := "png"
      Extension := "." file

      DllCall("gdiplus\GdipGetImageEncodersSize", "uint*",nCount:=0, "uint*",nSize:=0)
      ci := BufferAlloc(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.ptr+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:=0)
         EncoderParameters := BufferAlloc(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.ptr-pad-4
               NumPut("uint", Quality, NumGet(NumPut(4, NumPut(1, p+0)+20, "uint")))
               break
            }
         }
      }

      DllCall("ole32\CreateStreamOnHGlobal", "ptr",0, "int",true, "ptr*",pStream:=0)
      DllCall("gdiplus\GdipSaveImageToStream", "ptr",pBitmap, "ptr",pStream, "ptr",pCodec, "uint",p ? p : 0)
      DllCall("ole32\GetHGlobalFromStream", "ptr",pStream, "uint*",hData:=0)
      pData := DllCall("GlobalLock", "ptr",hData, "ptr")
      nSize := DllCall("GlobalSize", "uint",pData)

      bin := BufferAlloc(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.
      base64Length := 0
      DllCall("Crypt32.dll\CryptBinaryToStringA", "ptr",bin, "uint",nSize, "uint",0x40000001, "ptr",0, "uint*",base64Length)
      base64 := BufferAlloc(base64Length, 0)
      DllCall("Crypt32.dll\CryptBinaryToStringA", "ptr",bin, "uint",nSize, "uint",0x40000001, "ptr",base64, "uint*",base64Length)
      VarSetStrCapacity(bin, 0)

      return StrGet(base64, base64Length, "CP0")
   }

   static gdiplus := 0

   static gdiplusStartup() {
      this.gdiplus := (this.gdiplus == "") ? 1 : this.gdiplus + 1

      ; Startup gdiplus when counter goes from 0 -> 1 or "" -> 1.
      if (this.gdiplus == 1) {
         DllCall("LoadLibrary", "str", "gdiplus")
         si := BufferAlloc(A_PtrSize = 8 ? 24 : 16, 0) ; sizeof(GdiplusStartupInput) = 16, 24
            , NumPut("uint", 0x1, si)
         DllCall("gdiplus\GdiplusStartup", "ptr*", pToken:=0, "ptr", si, "ptr", 0)
         this.pToken := pToken
      }
   }

   static gdiplusShutdown(cotype := "", ByRef pBitmap := "") {
      this.gdiplus := this.gdiplus - 1

      ; When a buffer object is deleted a bitmap is sent here for disposal.
      if (cotype == "smart_pointer")
         if DllCall("gdiplus\GdipDisposeImage", "ptr", pBitmap)
            throw Exception("The bitmap of this buffer object has already been deleted.")

      ; Shutdown gdiplus if pToken is owned and when counter goes from 1 -> 0.
      if (this.gdiplus == 0) {
         DllCall("gdiplus\GdiplusShutdown", "ptr", this.pToken)
         DllCall("FreeLibrary", "ptr", DllCall("GetModuleHandle", "str", "gdiplus", "ptr"))

         ; Exit if GDI+ is still loaded. GdiplusNotInitialized = 18
         if (18 != DllCall("gdiplus\GdipCreateImageAttributes", "ptr*", ImageAttr:=0)) {
            DllCall("gdiplus\GdipDisposeImageAttributes", "ptr", ImageAttr)
            return
         }

         ; Otherwise GDI+ has been truly unloaded from the script and objects are out of scope.
         if (cotype = "bitmap")
            throw Exception("Out of scope error. `n`nIf you wish to handle raw pointers to GDI+ bitmaps, add the line"
               . "`n`n`t`t" this.prototype.__class ".gdiplusStartup()`n`nor 'pToken := Gdip_Startup()' to the top of your script."
               . "`nAlternatively, use 'obj := ImagePutBuffer()' with 'obj.pBitmap'."
               . "`nYou can copy this message by pressing Ctrl + C.")
      }
   }
} ; End of ImagePut class.
will be away

iseahound
Posts: 608
Joined: 13 Aug 2016, 21:04
GitHub: iseahound

Re: [a118] ImagePut - Puts an image from anywhere to anywhere

Post by iseahound » 25 Jul 2020, 18:29

a118 compatible

Code: Select all

; Script:    ImagePut.ahk
; Author:    iseahound
; License:   MIT License
; Version:   2020-05-22
; Release:   2020-07-25

; ImagePut - Puts an image from anywhere to anywhere.
; This is a simple functor designed to be intuitive.
; I hope people find this reference library useful.


; Puts the image into a file format and returns a base64 encoded string.
;   extension  -  File Encoding           |  string   ->   bmp, gif, jpg, png, tiff
;   quality    -  JPEG Quality Level      |  integer  ->   0 - 100
ImagePutBase64(ByRef image, extension := "", quality := "")
   => ImagePut("base64", image,,, extension, quality)


; Puts the image into a GDI+ Bitmap and returns a pointer.
ImagePutBitmap(ByRef image)
   => ImagePut("bitmap", image)


; Puts the image into a GDI+ Bitmap and returns a buffer object with GDI+ scope.
ImagePutBuffer(ByRef image)
   => ImagePut("buffer", image)


; Puts the image onto the clipboard and returns an empty string.
ImagePutClipboard(ByRef image)
   => ImagePut("clipboard", image)


; Puts the image as the cursor and returns the string "A_Cursor".
;   xHotspot   -  X Click Point           |  pixel    ->   0 - width
;   yHotspot   -  Y Click Point           |  pixel    ->   0 - height
ImagePutCursor(ByRef image, xHotspot := "", yHotspot := "")
   => ImagePut("cursor", image,,, xHotspot, yHotspot)


; Puts the image behind the desktop icons and returns the string "desktop".
;   scale      -  Scale Factor            |  real     ->   A_ScreenHeight / height.
ImagePutDesktop(ByRef image, scale := 1)
   => ImagePut("desktop", image,, scale)


; Puts the image into a file and returns the filepath.
;   filepath   -  Filepath + Extension    |  string   ->   *.bmp, *.gif, *.jpg, *.png, *.tiff
;   quality    -  JPEG Quality Level      |  integer  ->   0 - 100
ImagePutFile(ByRef image, filepath := "", quality := "")
   => ImagePut("file", image,,, filepath, quality)


; Puts the image into a device independent bitmap and returns a handle.
;   quality    -  JPEG Quality Level      |  integer  ->   0 - 100
;   alpha      -  Alpha Replacement Color |  RGB      ->   0xFFFFFF
ImagePutHBitmap(ByRef image, alpha := "")
   => ImagePut("hBitmap", image,,, alpha)


; Puts the image on the shared screen device context and returns an array of coordinates.
;   screenshot -  Screen Coordinates      |  array    ->   [x,y,w,h] or [0,0]
;   alpha      -  Alpha Replacement Color |  RGB      ->   0xFFFFFF
ImagePutScreenshot(ByRef image, screenshot := "", alpha := "")
   => ImagePut("screenshot", image,,, screenshot, alpha)


; Puts the image into a file format and returns a memory stream.
;   extension  -  File Encoding           |  string   ->   bmp, gif, jpg, png, tiff
;   quality    -  JPEG Quality Level      |  integer  ->   0 - 100
ImagePutStream(ByRef image, extension := "", quality := "")
   => ImagePut("stream", image,,, extension, quality)


; Puts the image into a file format and returns a RandomAccessStream.
;   extension  -  File Encoding           |  string   ->   bmp, gif, jpg, png, tiff
;   quality    -  JPEG Quality Level      |  integer  ->   0 - 100
ImagePutRandomAccessStream(ByRef image, extension := "", quality := "")
   => ImagePut("randomAccessStream", image,,, extension, quality)


; Puts the image as the desktop wallpaper and returns the string "wallpaper".
ImagePutWallpaper(ByRef image)
   => ImagePut("wallpaper", image)



; ImagePut() - Puts an image from anywhere to anywhere.
;   cotype     -  Output Type             |  string   ->   Case Insensitive. Read documentation.
;   image      -  Input Image             |  image    ->   Anything. Refer to ImageType().
;   crop       -  Crop Coordinates        |  array    ->   [x,y,w,h] could be negative or percent.
;   scale      -  Scale Factor            |  real     ->   2.0
;   terms*     -  Additional Parameters   |  variadic ->   Extra parameters found in toCotype().
ImagePut(cotype, ByRef image, crop := "", scale := "", terms*)
   => ImagePut.call(cotype, image, crop, scale, terms*)



class ImagePut {

   static call(cotype, ByRef image, crop := "", scale := "", terms*) {

      this.gdiplusStartup()

      ; Take a guess as to what the image might be. (>90% accuracy!)
      try type := this.DontVerifyImageType(image)
      catch
         type := this.ImageType(image)

      ; Qualify additional parameters for correctness.
      _crop := IsObject(crop)
         && crop[1] ~= "^-?\d+(\.\d*)?%?$" && crop[2] ~= "^-?\d+(\.\d*)?%?$"
         && crop[3] ~= "^-?\d+(\.\d*)?%?$" && crop[4] ~= "^-?\d+(\.\d*)?%?$"
      _scale := scale != 1 && scale ~= "^\d+(\.\d+)?$"

      ; Make a copy of the image as a pBitmap.
      pBitmap := this.toBitmap(type, image)

      ; Crop the image.
      if (_crop) {
         pBitmap2 := this.BitmapCrop(pBitmap, crop)
         DllCall("gdiplus\GdipDisposeImage", "ptr", pBitmap)
         pBitmap := pBitmap2
      }

      ; Scale the image.
      if (_scale) {
         pBitmap2 := this.BitmapScale(pBitmap, scale)
         DllCall("gdiplus\GdipDisposeImage", "ptr", pBitmap)
         pBitmap := pBitmap2
      }

      ; Put the pBitmap to wherever the cotype specifies.
      coimage := this.toCotype(cotype, pBitmap, terms*)

      ; Clean up the pBitmap copy. Export raw pointers if requested.
      if !(cotype = "bitmap" || cotype = "buffer")
         DllCall("gdiplus\GdipDisposeImage", "ptr", pBitmap)

      this.gdiplusShutdown(cotype)

      return coimage
   }

   ; Types              | Data type       | Example         | Explicit    | Inferred  | Input    | Output   | Notes
   ;                    |                 |                 | Don'tVerify | ImageType | toBitmap | toCotype |
   ; clipboard          | null string?    | ClipboardAll    |     yes     |    yes    |    yes   |    yes   | - no transparency
   ; object             | object          | object.Bitmap() |     yes     |    yes    |    yes   |          |
   ; buffer             | object          | bitmap.pBitmap  |     yes     |    yes    |    yes   |    yes   |
   ; screenshot         | object          | [x,y,w,h]       |     yes     |    yes    |    yes   |    yes   |
   ; desktop            | string (abc)    | "desktop"       |     yes     |    yes    |    no    |    yes   |
   ; wallpaper          | string (abc)    | "wallpaper"     |     yes     |    yes    |    yes   |    yes   |
   ; cursor             | string (abc)    | A_Cursor        |     yes     |    yes    |    yes   |    yes   |
   ; url                | string (abc)    | https://        |     yes     |    yes    |    yes   |          |
   ; file               | string (abc)    | picture.bmp     |     yes     |    yes    |    yes   |    yes   |
   ; stream             | pointer         |                 |             |           |          |    yes   |
   ; randomaccessstream | pointer         |                 |             |           |          |    yes   |
   ; bitmap             | pointer         | some number     |     yes     |    yes    |    yes   |    yes   |
   ; hBitmap            | pointer         | some number     |     yes     |    yes    |    yes   |    yes   |
   ; hIcon              | pointer         |                 |             |           |          |          |
   ; monitor            | integer         | 0 or # < 10     |     yes     |    yes    |          |          |
   ; hwnd               | integer         | 0x              |     yes     |    yes    |    yes   |          |
   ; window             | string (123)    | A               |     yes     |    yes    |    yes   |          |
   ; base64             | string (123)    | base64 data     |     yes     |    yes    |    yes   |    yes   |
   ; sprite             | N/A             |                 |     yes     |    no     |    yes   |    no    |
   ; icon               |                 |             |           |          |          |
   ; printer            |                 |             |           |          |          |
   ; findtext           |                 |             |           |          |          |
   ; thumbnail          |                 |       ?     |           |          |          | DwmRegisterThumbnail
   ; video              |                 |             |           |          |          |
   ; formdata           |                 |             |           |          |          |
   ; trayicon           |                 |             |           |          |          |
   ; hash               | aHash, p, d, w  |             |           |          |          |
   ; Animal Crossing QR code       |             |           |          |          |


   static DontVerifyImageType(ByRef image) {

      if !IsObject(image)
         throw Exception("Must be an object.")

      ; Check for image type declarations.
      ; Assumes that the user is telling the truth.

      if ObjHasOwnProp(image, "clipboard") {
         image := image.clipboard
         return "clipboard"
      }

      if ObjHasOwnProp(image, "object") {
         image := image.object
         return "object"
      }

      if ObjHasOwnProp(image, "buffer") {
         image := image.buffer
         return "buffer"
      }

      if ObjHasOwnProp(image, "screenshot") {
         image := image.screenshot
         return "screenshot"
      }

      if ObjHasOwnProp(image, "desktop") {
         image := image.desktop
         return "desktop"
      }

      if ObjHasOwnProp(image, "wallpaper") {
         image := image.wallpaper
         return "wallpaper"
      }

      if ObjHasOwnProp(image, "cursor") {
         image := image.cursor
         return "cursor"
      }

      if ObjHasOwnProp(image, "url") {
         image := image.url
         return "url"
      }

      if ObjHasOwnProp(image, "file") {
         image := image.file
         return "file"
      }

      if ObjHasOwnProp(image, "bitmap") {
         image := image.bitmap
         return "bitmap"
      }

      if ObjHasOwnProp(image, "hBitmap") {
         image := image.hBitmap
         return "hBitmap"
      }

      if ObjHasOwnProp(image, "monitor") {
         image := image.monitor
         return "monitor"
      }

      if ObjHasOwnProp(image, "hwnd") {
         image := image.hwnd
         return "hwnd"
      }

      if ObjHasOwnProp(image, "window") {
         image := image.window
         return "window"
      }

      if ObjHasOwnProp(image, "base64") {
         image := image.base64
         return "base64"
      }

      if ObjHasOwnProp(image, "sprite") {
         image := image.sprite
         return "sprite"
      }

      throw Exception("Invalid type.")
   }

   static ImageType(ByRef image) {
         ; Must be first as ClipboardAll is just an empty string when passed through functions.
         if this.is_clipboard(image)
            return "clipboard"

         ; Throw if the image is an empty string.
         if (image == "")
            throw Exception("Image data is an empty string."
            . "`nIf you passed ClipboardAll it does not contain compatible image data.")

      if IsObject(image) {
         ; An "object" is an object that implements a Bitmap() method returning a pointer to a GDI+ bitmap.
         if image.HasOwnMethod("Bitmap")
            return "object"

         ; A "buffer" is an AutoHotkey v2 buffer object.
         if ObjHasOwnProp(image, "pBitmap")
            return "buffer"

         ; A "screenshot" is an array of 4 numbers.
         if (image[1] ~= "^-?\d+$" && image[2] ~= "^-?\d+$" && image[3] ~= "^-?\d+$" && image[4] ~= "^-?\d+$")
            return "screenshot"
      }
         ; A "desktop" is a hidden window behind the desktop icons created by ImagePutDesktop.
         if (image = "desktop")
            return "desktop"

         ; A "wallpaper" is the desktop wallpaper.
         if (image = "wallpaper")
            return "wallpaper"

         ; A "cursor" is the name of a known cursor name.
         if (image ~= "(?i)^(IDC|OCR)?_?(A_Cursor|AppStarting|Arrow|Cross|Help|IBeam|"
         . "Icon|No|Size|SizeAll|SizeNESW|SizeNS|SizeNWSE|SizeWE|UpArrow|Wait|Unknown)$")
            return "cursor"

         ; A "url" satisfies the url format.
         if this.is_url(image)
            return "url"

         ; A "file" must exist.
         if FileExist(image)
            return "file"

      if IsInteger(image) {
         ; A "bitmap" is a pointer to a GDI+ Bitmap.
         try if (!DllCall("gdiplus\GdipGetImageType", "ptr", image, "ptr*", type:=0) && type == 1)
            return "bitmap"

         ; A "hBitmap" is a handle to a GDI Bitmap.
         if (DllCall("GetObjectType", "ptr", image) == 7)
            return "hBitmap"

         ; A non-zero "monitor" number identifies each display uniquely; and 0 refers to the entire virtual screen.
         if (image ~= "^\d+$" && image <= MonitorGetCount())
            return "monitor"

         ; A "hwnd" is a handle to a window and more commonly known as ahk_id.
         if DllCall("IsWindow", "ptr", image)
            return "hwnd"
      }
         ; A "window" is anything considered a Window Title including ahk_class and "A".
         if WinExist(image)
            return "window"

         ; A "base64" string is binary image data encoded into text using only 64 characters.
         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.")
   }

   static toBitmap(type, ByRef image) {

      if (type = "clipboard")
         return this.from_clipboard()

      if (type = "object")
         return image.Bitmap()

      if (type = "buffer") {
         DllCall("gdiplus\GdipCloneImage", "ptr", image.pBitmap, "ptr*", pBitmap:=0)
         return pBitmap
      }

      if (type = "screenshot")
         return this.from_screenshot(image)

      if (type = "desktop")
         return this.from_desktop()

      if (type = "wallpaper")
         return this.from_wallpaper()

      if (type = "cursor")
         return this.from_cursor()

      if (type = "url")
         return this.from_url(image)

      if (type = "file") {
         DllCall("gdiplus\GdipCreateBitmapFromFile", "wstr", image, "ptr*", pBitmap:=0)
         return pBitmap
      }

      if (type = "bitmap") {
         DllCall("gdiplus\GdipCloneImage", "ptr", image, "ptr*", pBitmap:=0)
         return pBitmap
      }

      if (type = "hBitmap")
         return this.from_hBitmap(image)

      if (type = "monitor")
         return this.from_monitor(image)

      if (type = "hwnd" || type = "window") {
         image := (type = "window") ? WinExist(image) : image
         return this.from_hwnd(image)
      }

      if (type = "base64")
         return this.from_base64(image)

      if (type = "sprite")
         return this.from_sprite(image)

      throw Exception("Conversion from type " type " is not supported.")
   }

   static toCotype(cotype, ByRef pBitmap, terms*) {
      ; toCotype("clipboard", pBitmap)
      if (cotype = "clipboard")
         return this.put_clipboard(pBitmap)

      ; toCotype("buffer", pBitmap)
      if (cotype = "buffer") {
         buffer := {pBitmap: pBitmap}
            .DefineMethod("__New" , (self) => (this.gdiplusStartup(), self)) ; Increment GDI+ reference count
            .DefineMethod("__Delete", (self) => (this.gdiplusShutdown("smart_pointer", self.pBitmap)))
            .__New()  ; On deletion the buffer object will dispose of the bitmap. And it will decrement this.gdiplus.
         return buffer
      }

      ; toCotype("screenshot", pBitmap, screenshot, alpha)
      if (cotype = "screenshot")
         return this.put_screenshot(pBitmap, terms[1], terms[2])

      ; toCotype("desktop", pBitmap)
      if (cotype = "desktop")
         return this.put_desktop(pBitmap)

      ; toCotype("wallpaper", pBitmap)
      if (cotype = "wallpaper")
         return this.put_wallpaper(pBitmap)

      ; toCotype("cursor", pBitmap, xHotspot, yHotspot)
      if (cotype = "cursor")
         return this.put_cursor(pBitmap, terms[1], terms[2])

      ; toCotype("url", ????????????????????????
      if (cotype = "url") {
         ; put a url
      }

      ; toCotype("file", pBitmap, filename, quality)
      if (cotype = "file")
         return this.put_file(pBitmap, terms[1], terms[2])

      ; toCotype("stream", pBitmap, extension, quality)
      if (cotype = "stream")
         return this.put_stream(pBitmap, terms[1], terms[2])

      ; toCotype("randomAccessStream", pBitmap, extension, quality)
      if (cotype = "randomAccessStream")
         return this.put_randomAccessStream(pBitmap, terms[1], terms[2])

      ; toCotype("bitmap", pBitmap)
      if (cotype = "bitmap")
         return pBitmap

      ; toCotype("hBitmap", pBitmap, alpha)
      if (cotype = "hBitmap")
         return this.put_hBitmap(pBitmap, terms[1])

      ; toCotype("hwnd", pBitmap)
      if (cotype = "hwnd")
         return this.Render({bitmap:pBitmap}).hwnd

      ; toCotype("window", pBitmap)
      if (cotype = "window")
         return "ahk_id " . this.Render({bitmap:pBitmap}).AlwaysOnTop().ToolWindow().Caption().hwnd

      ; toCotype("base64", pBitmap, extension, quality)
      if (cotype = "base64")
         return this.put_base64(pBitmap, terms[1], terms[2])

      throw Exception("Conversion to type " cotype " is not supported.")
   }

   static DisposeImage(ByRef pBitmap) {
      return DllCall("gdiplus\GdipDisposeImage", "ptr", pBitmap)
   }

   static BitmapCrop(ByRef pBitmap, crop) {
      ; Get Bitmap width and height and format.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0)
      DllCall("gdiplus\GdipGetImagePixelFormat", "ptr", pBitmap, "ptr*", format:=0)

      ; Are the numbers percentages?
      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]
      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]

      ; If numbers are negative, subtract the values from the edge.
      crop[3] := (crop[3] < 0) ?  width - Abs(crop[3]) - Abs(crop[1]) : crop[3]
      crop[4] := (crop[4] < 0) ? height - Abs(crop[4]) - Abs(crop[2]) : crop[4]
      crop[1] := Abs(crop[1])
      crop[2] := Abs(crop[2])

      ; Round to the nearest integer.
      crop[3] := Round(crop[1] + crop[3]) - Round(crop[1]) ; A reminder that width and height
      crop[4] := Round(crop[2] + crop[4]) - Round(crop[2]) ; are distances, not coordinates.
      crop[1] := Round(crop[1]) ; so the abstract concept of a distance must be resolved
      crop[2] := Round(crop[2]) ; into coordinates and then rounded and added up again.

      ; Variance Shift. Now place x,y before w,h because we are building abstracts from reals now.
      ; Before we were resolving abstracts into real coordinates, now it's the opposite.

      ; Ensure that coordinates can never exceed the expected Bitmap area.
      safe_x := (crop[1] > width) ? 0 : crop[1]                          ; Zero x if bigger.
      safe_y := (crop[2] > height) ? 0 : crop[2]                         ; Zero y if bigger.
      safe_w := (crop[1] + crop[3] > width) ? width - safe_x : crop[3]   ; Max w if bigger.
      safe_h := (crop[2] + crop[4] > height) ? height - safe_y : crop[4] ; Max h if bigger.

      ; Clone
      DllCall("gdiplus\GdipCloneBitmapAreaI"
               ,    "int", safe_x
               ,    "int", safe_y
               ,    "int", safe_w
               ,    "int", safe_h
               ,    "int", format
               ,    "ptr", pBitmap
               ,   "ptr*", pBitmapCrop:=0)

      return pBitmapCrop
   }

   static BitmapScale(ByRef pBitmap, scale) {
      ; Get Bitmap width and height and format.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0)
      DllCall("gdiplus\GdipGetImagePixelFormat", "ptr", pBitmap, "ptr*", format:=0)

      safe_w := Ceil(width * scale)
      safe_h := Ceil(height * scale)

      ; Create a new bitmap and get the graphics context.
      DllCall("gdiplus\GdipCreateBitmapFromScan0"
               , "int", safe_w, "int", safe_h, "int", 0, "int", format, "ptr", 0, "ptr*", pBitmapScale:=0)
      DllCall("gdiplus\GdipGetImageGraphicsContext", "ptr", pBitmapScale, "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. Not sure why the integer variant fails below.
      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", safe_w, "int", safe_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)
      return pBitmapScale
   }

   static is_clipboard(c) {
      ; ClipboardAll is always an empty string when passed into a function.
      if (c != "") ; Must be an empty string.
         return false

      ; Look through the clipboard for a memory object containing a BITMAPINFO structure followed by the bitmap bits.
      if DllCall("OpenClipboard", "ptr", 0) {
         _answer := DllCall("IsClipboardFormatAvailable", "uint", 8) ; CF_DIB
         DllCall("CloseClipboard")
         if (_answer)
            return true
      }

      ; Error messages are inaccurate and it can't be helped.
      return false
   }

   static is_url(url) {
      ; Thanks splattermania - https://www.php.net/manual/en/function.preg-match.php#93824

      regex := "^(?i)"
         . "((https?|ftp)\:\/\/)" ; SCHEME
         . "([a-z0-9+!*(),;?&=\$_.-]+(\:[a-z0-9+!*(),;?&=\$_.-]+)[email protected])?" ; User and Pass
         . "([a-z0-9-.]*)\.([a-z]{2,3})" ; Host or IP
         . "(\:[0-9]{2,5})?" ; Port
         . "(\/(?:[a-z0-9-_~!$&'()*+,;=:@]\.?)+)*\/?" ; Path
         . "(\?[a-z+&\$_.-][a-z0-9;:@&%=+\/\$_.-]*)?" ; GET Query
         . "(#[a-z_.-][a-z0-9+\$_.-]*)?$" ; Anchor
      return (url ~= regex)
   }

   static from_clipboard() {
      ; Thanks tic - https://www.autohotkey.com/boards/viewtopic.php?t=6517

      if DllCall("OpenClipboard", "ptr", 0) {
         hBitmap := DllCall("GetClipboardData", "uint", 2, "ptr")
         DllCall("CloseClipboard")
         DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", "ptr", hBitmap, "ptr", 0, "ptr*", pBitmap:=0)
         DllCall("DeleteObject", "ptr", hBitmap)
      }
      return pBitmap
   }

   static from_screenshot(ByRef image) {
      ; Thanks tic - https://www.autohotkey.com/boards/viewtopic.php?t=6517

      ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      bi := BufferAlloc(40, 0)                 ; sizeof(bi) = 40
         , NumPut(  "uint",        40, bi,  0) ; Size
         , NumPut(  "uint",  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) ; SRCCOPY

      ; 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
   }

   static from_wallpaper() {
      ; Get the width and height of all monitors.
      width  := DllCall("GetSystemMetrics", "int", 78)
      height := DllCall("GetSystemMetrics", "int", 79)

      ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      bi := BufferAlloc(40, 0)                 ; sizeof(bi) = 40
         , NumPut(  "uint",        40, bi,  0) ; Size
         , NumPut(  "uint",     width, bi,  4) ; Width
         , NumPut(   "int",   -height, 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")

      ; Paints the desktop.
      DllCall("PaintDesktop", "ptr", hdc)

      ; 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
   }

   static from_cursor() {
      ; Thanks 23W - https://stackoverflow.com/a/13295280

      ; struct CURSORINFO - https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-cursorinfo
      ci := BufferAlloc(16+A_PtrSize, 0)                 ; sizeof(CURSORINFO) = 20, 24
         , NumPut("int", ci.size, ci)
      DllCall("GetCursorInfo", "ptr", ci)
         ; cShow   := NumGet(ci,  4, "int")              ; 0x1 = CURSOR_SHOWING, 0x2 = CURSOR_SUPPRESSED
         , hCursor := NumGet(ci,  8, "ptr")
         ; xCursor := NumGet(ci,  8+A_PtrSize, "int")
         ; yCursor := NumGet(ci, 12+A_PtrSize, "int")

      ; struct ICONINFO - https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-iconinfo
      ii := BufferAlloc(8+3*A_PtrSize, 0)                ; sizeof(ICONINFO) = 20, 32
      DllCall("GetIconInfo", "ptr", hCursor, "ptr", ii)
         ; xHotspot := NumGet(ii, 4, "uint")
         ; yHotspot := NumGet(ii, 8, "uint")
         , hbmMask  := NumGet(ii, 8+A_PtrSize, "ptr")    ; x86:12, x64:16
         , hbmColor := NumGet(ii, 8+2*A_PtrSize, "ptr")  ; x86:16, x64:24

      ; struct BITMAP - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmap
      bm := BufferAlloc(16+2*A_PtrSize) ; sizeof(BITMAP) = 24, 32
      DllCall("GetObject", "ptr", hbmMask, "int", bm.size, "ptr", bm)
         , width  := NumGet(bm, 4, "uint")
         , height := NumGet(bm, 8, "uint") / (hbmColor ? 1 : 2) ; Black and White cursors have doubled height.

      ; Clean up these hBitmaps.
      DllCall("DeleteObject", "ptr", hbmMask)
      DllCall("DeleteObject", "ptr", hbmColor)

      ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      bi := BufferAlloc(40, 0)                 ; sizeof(bi) = 40
         , NumPut(  "uint",        40, bi,  0) ; Size
         , NumPut(  "uint",     width, bi,  4) ; Width
         , NumPut(   "int",   -height, 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")

      ; This is the 32-bit ARGB pBitmap (different from an hBitmap) that will receive the final converted pixels.
      DllCall("gdiplus\GdipCreateBitmapFromScan0"
               , "int", width, "int", height, "int", 0, "int", 0x26200A, "ptr", 0, "ptr*", pBitmap:=0)

      ; Create a Scan0 buffer pointing to pBits. The buffer has pixel format pARGB.
      Rect := BufferAlloc(16, 0)               ; sizeof(Rect) = 16
         , NumPut(  "uint",   width, Rect,  8) ; Width
         , NumPut(  "uint",  height, Rect, 12) ; Height
      BitmapData := BufferAlloc(16+2*A_PtrSize, 0)      ; sizeof(BitmapData) = 24, 32
         , NumPut(  "uint",      width, BitmapData,  0) ; Width
         , NumPut(  "uint",     height, BitmapData,  4) ; Height
         , NumPut(   "int",  4 * width, BitmapData,  8) ; Stride
         , NumPut(   "int",    0xE200B, BitmapData, 12) ; PixelFormat
         , NumPut(   "ptr",      pBits, BitmapData, 16) ; Scan0

      ; Use LockBits to create a writable buffer that converts pARGB to ARGB.
      DllCall("gdiplus\GdipBitmapLockBits"
               ,    "ptr", pBitmap
               ,    "ptr", Rect
               ,   "uint", 6            ; ImageLockMode.UserInputBuffer | ImageLockMode.WriteOnly
               ,    "int", 0xE200B      ; Format32bppPArgb
               ,    "ptr", BitmapData)  ; Contains the pointer (pBits) to the hbm.

      ; Don't use DI_DEFAULTSIZE to draw the icon like DrawIcon does as it will resize to 32 x 32.
      DllCall("DrawIconEx"
               , "ptr", hdc,     "int", 0, "int", 0
               , "ptr", hCursor, "int", 0, "int", 0
               , "uint", 0, "ptr", 0, "uint", 0x1 | 0x2 | 0x4) ; DI_MASK | DI_IMAGE | DI_COMPAT

      ; Convert the pARGB pixels copied into the device independent bitmap (hbm) to ARGB.
      DllCall("gdiplus\GdipBitmapUnlockBits", "ptr", pBitmap, "ptr", BitmapData)

      ; Clean up the icon and device context.
      DllCall("DestroyIcon",  "ptr", hCursor)
      DllCall("SelectObject", "ptr", hdc, "ptr", obm)
      DllCall("DeleteObject", "ptr", hbm)
      DllCall("DeleteDC",     "ptr", hdc)

      return pBitmap
   }

   static from_url(ByRef image) {
      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:=0)
      ObjRelease(pStream)
      return pBitmap
   }

   static from_hBitmap(ByRef image) {
      ; struct BITMAP - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmap
      dib := BufferAlloc(68+4*A_PtrSize) ; sizeof(DIBSECTION) = 84, 100
      DllCall("GetObject", "ptr", image, "int", dib.size, "ptr", dib)
         , width  := NumGet(dib, 4, "uint")
         , height := NumGet(dib, 8, "uint")
         , bpp    := NumGet(dib, 18, "ushort")

      ; Fallback to built-in method if pixels are not 32-bit ARGB.
      if (bpp != 32) { ; This built-in version is 120% faster but ignores transparency.
         DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", "ptr", image, "ptr", 0, "ptr*", pBitmap:=0)
         return pBitmap
      }

      ; Create a handle to a device context and associate the image.
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")           ; Creates a memory DC compatible with the current screen.
      obm := DllCall("SelectObject", "ptr", hdc, "ptr", image, "ptr") ; Put the (hBitmap) image onto the device context.

      ; Create a device independent bitmap with negative height. All DIBs use the screen pixel format (pARGB).
      ; Use hbm to buffer the image such that top-down and bottom-up images are mapped to this top-down buffer.
      ; pBits is the pointer to (top-down) pixel values. The Scan0 will point to the pBits.
      ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
      cdc := DllCall("CreateCompatibleDC", "ptr", hdc, "ptr")
      bi := BufferAlloc(40, 0)                 ; sizeof(bi) = 40
         , NumPut(  "uint",        40, bi,  0) ; Size
         , NumPut(  "uint",     width, bi,  4) ; Width
         , NumPut(   "int",   -height, 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", cdc, "ptr", bi, "uint", 0, "ptr*", pBits:=0, "ptr", 0, "uint", 0, "ptr")
      ob2 := DllCall("SelectObject", "ptr", cdc, "ptr", hbm, "ptr")

      ; This is the 32-bit ARGB pBitmap (different from an hBitmap) that will receive the final converted pixels.
      DllCall("gdiplus\GdipCreateBitmapFromScan0"
               , "int", width, "int", height, "int", 0, "int", 0x26200A, "ptr", 0, "ptr*", pBitmap:=0)

      ; Create a Scan0 buffer pointing to pBits. The buffer has pixel format pARGB.
      Rect := BufferAlloc(16, 0)               ; sizeof(Rect) = 16
         , NumPut(  "uint",   width, Rect,  8) ; Width
         , NumPut(  "uint",  height, Rect, 12) ; Height
      BitmapData := BufferAlloc(16+2*A_PtrSize, 0)      ; sizeof(BitmapData) = 24, 32
         , NumPut(  "uint",      width, BitmapData,  0) ; Width
         , NumPut(  "uint",     height, BitmapData,  4) ; Height
         , NumPut(   "int",  4 * width, BitmapData,  8) ; Stride
         , NumPut(   "int",    0xE200B, BitmapData, 12) ; PixelFormat
         , NumPut(   "ptr",      pBits, BitmapData, 16) ; Scan0

      ; Use LockBits to create a writable buffer that converts pARGB to ARGB.
      DllCall("gdiplus\GdipBitmapLockBits"
               ,    "ptr", pBitmap
               ,    "ptr", Rect
               ,   "uint", 6            ; ImageLockMode.UserInputBuffer | ImageLockMode.WriteOnly
               ,    "int", 0xE200B      ; Format32bppPArgb
               ,    "ptr", BitmapData) ; Contains the pointer (pBits) to the hbm.

      ; Copies the image (hBitmap) to a top-down bitmap. Removes bottom-up-ness if present.
      DllCall("gdi32\BitBlt"
               , "ptr", cdc, "int", 0, "int", 0, "int", width, "int", height
               , "ptr", hdc, "int", 0, "int", 0, "uint", 0x00CC0020) ; SRCCOPY

      ; 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.
      DllCall("SelectObject", "ptr", cdc, "ptr", ob2)
      DllCall("DeleteObject", "ptr", hbm)
      DllCall("DeleteDC",     "ptr", cdc)
      DllCall("SelectObject", "ptr", hdc, "ptr", obm)
      DllCall("DeleteDC",     "ptr", hdc)

      return pBitmap
   }

   static from_monitor(ByRef image) {
      if (image > 0) {
         MonitorGet(image, Left, Top, Right, Bottom)
         x := Left
         y := Top
         w := Right - Left
         h := Bottom - Top
      } else {
         x := DllCall("GetSystemMetrics", "int", 76)
         y := DllCall("GetSystemMetrics", "int", 77)
         w := DllCall("GetSystemMetrics", "int", 78)
         h := DllCall("GetSystemMetrics", "int", 79)
      }
      return this.from_screenshot([x,y,w,h])
   }

   static from_hwnd(ByRef image) {
      ; Thanks tic - https://www.autohotkey.com/boards/viewtopic.php?t=6517

      ; Restore the window if minimized! Must be visible for capture.
      if DllCall("IsIconic", "ptr", image)
         DllCall("ShowWindow", "ptr", image, "int", 4)

      ; Get the width and height of the client window.
      Rect := BufferAlloc(16) ; sizeof(RECT) = 16
      DllCall("GetClientRect", "ptr", image, "ptr", Rect)
         , width  := NumGet(Rect, 8, "int")
         , height := NumGet(Rect, 12, "int")

      ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      bi := BufferAlloc(40, 0)                 ; sizeof(bi) = 40
         , NumPut(  "uint",        40, bi,  0) ; Size
         , NumPut(  "uint",     width, bi,  4) ; Width
         , NumPut(   "int",   -height, 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")

      ; Print the window onto the hBitmap using an undocumented flag. https://stackoverflow.com/a/40042587
      DllCall("PrintWindow", "ptr", image, "ptr", hdc, "uint", 0x3) ; PW_CLIENTONLY | PW_RENDERFULLCONTENT
      ; Additional info on how this is implemented: https://www.reddit.com/r/windows/comments/8ffr56/altprintscreen/

      ; 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
   }

   static from_base64(ByRef image) {
      ; Trim whitespace and remove header.
      image := Trim(image)
      image := RegExReplace(image, "^data:image\/[a-z]+;base64,")

      ; Converts the image to binary data by first asking for the size.
      size := 0
      DllCall("crypt32\CryptStringToBinary", "ptr",StrPtr(image), "uint",0, "uint",0x1, "ptr",   0, "uint*",size, "ptr",0, "ptr",0)
      bin := BufferAlloc(size, 0)
      DllCall("crypt32\CryptStringToBinary", "ptr",StrPtr(image), "uint",0, "uint",0x1, "ptr", bin, "uint*",size, "ptr",0, "ptr",0)

      ; Makes a stream for conversion into a pBitmap.
      pStream := DllCall("shlwapi\SHCreateMemStream", "ptr", bin, "uint", size, "ptr")
      DllCall("gdiplus\GdipCreateBitmapFromStream", "ptr", pStream, "ptr*", pBitmap:=0)
      ObjRelease(pStream)

      return pBitmap
   }

   static from_sprite(ByRef image) {
      ; Create a source pBitmap and extract the width and height.
      if DllCall("gdiplus\GdipCreateBitmapFromFile", "wstr", image, "ptr*", sBitmap:=0)
         if !(sBitmap := this.from_url(image))
            throw Exception("Could not be loaded from a valid file path or URL.")

      ; Get Bitmap width and height.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", sBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", sBitmap, "uint*", height:=0)

      ; Create a destination pBitmap in 32-bit ARGB and get its device context though GDI+.
      ; Note that a device context from a graphics context can only be drawn on, not read.
      ; Also note that using a graphics context and blitting does not create a pixel perfect image.
      ; Using a DIB and LockBits is about 5% faster.
      DllCall("gdiplus\GdipCreateBitmapFromScan0"
               , "int", width, "int", height, "int", 0, "int", 0x26200A, "ptr", 0, "ptr*", dBitmap:=0)
      DllCall("gdiplus\GdipGetImageGraphicsContext", "ptr", dBitmap, "ptr*", dGraphics:=0)
      DllCall("gdiplus\GdipGetDC", "ptr", dGraphics, "ptr*", ddc:=0)

      ; Keep any existing transparency for whatever reason.
      hBitmap := this.put_hBitmap(sBitmap) ; Could copy this code here for even more speed.

      ; Create a source device context and associate the source hBitmap.
      sdc := DllCall("CreateCompatibleDC", "ptr", ddc, "ptr")
      obm := DllCall("SelectObject", "ptr", sdc, "ptr", hBitmap, "ptr")

      ; Copy the image making the top-left pixel the color key.
      DllCall("msimg32\TransparentBlt"
               , "ptr", ddc, "int", 0, "int", 0, "int", width, "int", height  ; destination
               , "ptr", sdc, "int", 0, "int", 0, "int", width, "int", height  ; source
               , "uint", DllCall("GetPixel", "ptr", sdc, "int", 0, "int", 0)) ; RGB pixel.

      ; Cleanup the hBitmap and device contexts.
      DllCall("SelectObject", "ptr", sdc, "ptr", obm)
      DllCall("DeleteObject", "ptr", hBitmap)
      DllCall("DeleteDC",     "ptr", sdc)

      ; Release the graphics context and delete.
      DllCall("gdiplus\GdipReleaseDC", "ptr", dGraphics, "ptr", ddc)
      DllCall("gdiplus\GdipDeleteGraphics", "ptr", dGraphics)

      return dBitmap
   }

   static put_clipboard(ByRef pBitmap) {
      ; Thanks tic - https://www.autohotkey.com/boards/viewtopic.php?t=6517

      off1 := A_PtrSize = 8 ? 52 : 44, off2 := A_PtrSize = 8 ? 32 : 24
      DllCall("gdiplus\GdipCreateHBITMAPFromBitmap", "ptr", pBitmap, "ptr*", hBitmap:=0, "uint", 0xFFFFFFFF)
      oi := BufferAlloc(A_PtrSize = 8 ? 104 : 84, 0) ; sizeof(oi) = 84, 104
      DllCall("GetObject", "ptr", hBitmap, "int", oi.size, "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.ptr+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")

      ; Returns an empty string as ClipboardAll would also be an empty string.
      return ""
   }

   static put_screenshot(ByRef pBitmap, screenshot := "", alpha := "") {
      ; Get Bitmap width and height.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0)

      x := (IsObject(screenshot) && screenshot[1] != "") ? screenshot[1] : Round((A_ScreenWidth - width) / 2)
      y := (IsObject(screenshot) && screenshot[2] != "") ? screenshot[2] : Round((A_ScreenHeight - height) / 2)
      w := (IsObject(screenshot) && screenshot[3] != "") ? screenshot[3] : width
      h := (IsObject(screenshot) && screenshot[4] != "") ? screenshot[4] : height

      ; Convert the Bitmap to a hBitmap and associate a device context for blitting.
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      hbm := this.put_hBitmap(pBitmap, alpha)
      obm := DllCall("SelectObject", "ptr", hdc, "ptr", hbm, "ptr")

      ; Get device context of spawned window.
      ddc := DllCall("GetDC", "ptr", 0, "ptr")

      ; Copies a portion of the screen to a new device context.
      DllCall("gdi32\StretchBlt"
               , "ptr", ddc, "int", x, "int", y, "int", w,     "int", h
               , "ptr", hdc, "int", 0, "int", 0, "int", width, "int", height
               , "uint", 0x00CC0020) ; SRCCOPY

      ; Release device context of spawned window.
      DllCall("ReleaseDC", "ptr", 0, "ptr", ddc)

      ; Cleanup the hBitmap and device contexts.
      DllCall("SelectObject", "ptr", hdc, "ptr", obm)
      DllCall("DeleteObject", "ptr", hbm)
      DllCall("DeleteDC",     "ptr", hdc)

      return [x,y,w,h]
   }

   static put_desktop(ByRef pBitmap) {
      ; Thanks Gerald Degeneve - https://www.codeproject.com/Articles/856020/Draw-Behind-Desktop-Icons-in-Windows-plus

      ; Get Bitmap width and height.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0)

      ; Convert the Bitmap to a hBitmap and associate a device context for blitting.
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      hbm := this.put_hBitmap(pBitmap)
      obm := DllCall("SelectObject", "ptr", hdc, "ptr", hbm, "ptr")

      ; Post-Creator's Update Windows 10. WM_SPAWN_WORKER = 0x052C
      DllCall("SendMessage", "ptr", WinExist("ahk_class Progman"), "uint", 0x052C, "ptr", 0x0000000D, "ptr", 0)
      DllCall("SendMessage", "ptr", WinExist("ahk_class Progman"), "uint", 0x052C, "ptr", 0x0000000D, "ptr", 1)

      ; Find the child window.
      windows := WinGetList("ahk_class WorkerW")
      Loop windows.length
         hwnd := windows[A_Index]
      until DllCall("FindWindowEx", "ptr", hwnd, "ptr", 0, "str", "SHELLDLL_DefView", "ptr", 0)
      WorkerW := DllCall("FindWindowEx", "ptr", 0, "ptr", hwnd, "str", "WorkerW", "ptr", 0, "ptr")

      ; Maybe this hack gets patched. Tough luck!
      if (!WorkerW)
         throw Exception("Could not draw on the desktop.")

      ; Position the image in the center. This line can be removed.
      DllCall("SetWindowPos", "ptr", WorkerW, "ptr", 1
               , "int", Round((A_ScreenWidth - width) / 2)   ; x coordinate
               , "int", Round((A_ScreenHeight - height) / 2) ; y coordinate
               , "int", width, "int", height, "uint", 0)

      ; Get device context of spawned window.
      ddc := DllCall("GetDCEx", "ptr", WorkerW, "ptr", 0, "int", 0x403, "ptr")

      ; Copies a portion of the screen to a new device context.
      DllCall("gdi32\BitBlt"
               , "ptr", ddc, "int", 0, "int", 0, "int", width, "int", height
               , "ptr", hdc, "int", 0, "int", 0, "uint", 0x00CC0020) ; SRCCOPY

      ; Release device context of spawned window.
      DllCall("ReleaseDC", "ptr", 0, "ptr", ddc)

      ; Cleanup the hBitmap and device contexts.
      DllCall("SelectObject", "ptr", hdc, "ptr", obm)
      DllCall("DeleteObject", "ptr", hbm)
      DllCall("DeleteDC",     "ptr", hdc)

      return "desktop"
   }

   static put_wallpaper(ByRef pBitmap) {
      path := this.put_file(pBitmap, "temp.png")
      cc := DllCall("GetFullPathName", "str", path, "uint", 0, "ptr", 0, "ptr", 0, "uint")
      VarSetStrCapacity(buf, cc)
      DllCall("GetFullPathName", "str", path, "uint", cc, "str", buf, "ptr", 0, "uint")
      DllCall("SystemParametersInfo", "uint", 20, "uint", 0, "str", buf, "uint", 2)
      Sleep 1 ; Needed as there is some lag.
      FileDelete path
      return "wallpaper"
   }

   static put_cursor(ByRef pBitmap, xHotspot := "", yHotspot := "") {
      ; Thanks Nick - https://stackoverflow.com/a/550965

      ; Creates an icon that can be used as a cursor.
      DllCall("gdiplus\GdipCreateHICONFromBitmap", "ptr", pBitmap, "ptr*", hIcon:=0)

      ; Sets the hotspot of the cursor by changing the icon into a cursor.
      if (xHotspot != "" || yHotspot != "") {
         ; struct ICONINFO - https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-iconinfo
         ii := BufferAlloc(8+3*A_PtrSize, 0)                          ; sizeof(ICONINFO) = 20, 32
         DllCall("GetIconInfo", "ptr", hIcon, "ptr", ii)              ; Fill the ICONINFO structure.
            , NumPut("uint", false, ii, 0)                            ; true/false are icon/cursor respectively.
            , (xHotspot != "") ? NumPut("uint", xHotspot, ii, 4) : "" ; Set the xHotspot value. (Default: center point)
            , (yHotspot != "") ? NumPut("uint", yHotspot, ii, 8) : "" ; Set the yHotspot value. (Default: center point)
         DllCall("DestroyIcon", "ptr", hIcon)                         ; Destroy the icon after getting the ICONINFO structure.
         hIcon := DllCall("CreateIconIndirect", "ptr", ii, "ptr")     ; Create a new cursor using ICONINFO.

         ; Clean up hbmMask and hbmColor created as a result of GetIconInfo.
         DllCall("DeleteObject", "ptr", NumGet(ii, 8+A_PtrSize, "ptr"))   ; hbmMask
         DllCall("DeleteObject", "ptr", NumGet(ii, 8+2*A_PtrSize, "ptr")) ; hbmColor
      }

      ; Loop over all 16 system cursors and change them all to the new cursor.
      SystemCursors := "32512,32513,32514,32515,32516,32640,32641,32642,32643,32644,32645,32646,32648,32649,32650,32651"
      Loop Parse, SystemCursors, ","
      { ; Must copy the handle 16 times as SetSystemCursor deletes the handle 16 times.
         hCursor := DllCall("CopyImage", "ptr", hIcon, "uint", 2, "int", 0, "int", 0, "uint", 0, "ptr")
         DllCall("SetSystemCursor", "ptr", hCursor, "int", A_LoopField) ; calls DestroyCursor
      }

      ; Destroy the original hIcon. DestroyCursor and DestroyIcon are the same function in C.
      DllCall("DestroyCursor", "ptr", hIcon)

      ; Returns the word A_Cursor so that it doesn't evaluate immediately.
      return "A_Cursor"
   }

   static put_file(ByRef pBitmap, filepath, quality := unset) {
      ; Thanks tic - https://www.autohotkey.com/boards/viewtopic.php?t=6517

      ; Seperate the filepath and default the extension to PNG.
      SplitPath filepath,, directory, extension, filename
      filename := (filename != "") ? filename : "___date___"
      extension := (extension ~= "^(?i:bmp|dib|rle|jpg|jpeg|jpe|jfif|gif|tif|tiff|png)$") ? extension : "png"
      filepath := directory . filename "." extension

      ; Fill a buffer with the available encoders.
      DllCall("gdiplus\GdipGetImageEncodersSize", "uint*", count:=0, "uint*", size:=0)
      ci := BufferAlloc(size)
      DllCall("gdiplus\GdipGetImageEncoders", "uint", count, "uint", size, "ptr", ci)
      if !(count && size)
         throw Exception("Could not get a list of image codec encoders on this system.")

      ; Search for an encoder with a matching extension.
      Loop count
         EncoderExtensions := StrGet(NumGet(ci, (idx:=(48+7*A_PtrSize)*(A_Index-1))+32+3*A_PtrSize, "uptr"), "UTF-16")
      until InStr(EncoderExtensions, "*." extension)

      ; Get the pointer to the index/offset of the matching encoder.
      if !(pCodec := ci.ptr + idx)
         throw Exception("Could not find a matching encoder for the specified file format.")

      ; JPEG is a lossy image format that requires a quality value from 0-100. Default quality is 75.
      if (extension ~= "^(?i:jpg|jpeg|jpe|jfif)$"
      && IsInteger(quality) && 0 <= quality && quality <= 100 && quality != 75) {
         DllCall("gdiplus\GdipGetEncoderParameterListSize", "ptr", pBitmap, "ptr", pCodec, "uint*", size:=0)
         EncoderParameters := BufferAlloc(size, 0)
         DllCall("gdiplus\GdipGetEncoderParameterList", "ptr", pBitmap, "ptr", pCodec, "uint", size, "ptr", EncoderParameters)

         ; Search for an encoder parameter with 1 value of type 6.
         Loop NumGet(EncoderParameters, "uint")
            elem := (24+A_PtrSize)*(A_Index-1) + A_PtrSize
         until (NumGet(EncoderParameters, elem+16, "uint") = 1) && (NumGet(EncoderParameters, elem+20, "uint") = 6)

         ; struct EncoderParameter - http://www.jose.it-berater.org/gdiplus/reference/structures/encoderparameter.htm
         ep := EncoderParameters.ptr + elem - A_PtrSize                  ; sizeof(EncoderParameter) = 28, 32
            , NumPut(  "uptr",       1, ep)                              ; Must be 1.
            , NumPut(  "uint",       4, ep, 20+A_PtrSize)                ; Type
            , NumPut(  "uint", quality, NumGet(ep+24+A_PtrSize, "uptr")) ; Value (pointer)
      }

      ; Write the file to disk using the specified encoder and encoding parameters.
      if DllCall("gdiplus\GdipSaveImageToFile", "ptr", pBitmap, "wstr", filepath, "ptr", pCodec, "uint", IsSet(ep) ? ep : 0)
         throw Exception("Could not save file to disk.")

      ; If the filename was omitted, replace it with the current time (accurate to the second).
      ; Multiple files that are created within 1 second will be overwritten with the last file.
      ; The replacement colon is called a Modifier Letter Colon found at <U+A789>.
      if (filename == "___date___")
         FileMove(filepath, directory . FormatTime(FileGetTime(filepath), "yyyy-MM-dd HH꞉mm꞉ss") "." extension, true)




      return filepath
   }

   static put_stream(ByRef pBitmap, extension := "", quality := "") {
      ; Default extension is PNG.
      if !(extension ~= "^(?i:bmp|dib|rle|jpg|jpeg|jpe|jfif|gif|tif|tiff|png)$")
         extension := "png"

      ; Fill a buffer with the available encoders.
      DllCall("gdiplus\GdipGetImageEncodersSize", "uint*", count:=0, "uint*", size:=0)
      ci := BufferAlloc(size)
      DllCall("gdiplus\GdipGetImageEncoders", "uint", count, "uint", size, "ptr", ci)
      if !(count && size)
         throw Exception("Could not get a list of image codec encoders on this system.")

      ; Search for an encoder with a matching extension.
      Loop count
         EncoderExtensions := StrGet(NumGet(ci, (idx:=(48+7*A_PtrSize)*(A_Index-1))+32+3*A_PtrSize, "uptr"), "UTF-16")
      until InStr(EncoderExtensions, "*." extension)

      ; Get the pointer to the index/offset of the matching encoder.
      if !(pCodec := ci.ptr + idx)
         throw Exception("Could not find a matching encoder for the specified file format.")

      ; JPEG is a lossy image format that requires a quality value from 0-100. Default quality is 75.
      if (extension ~= "^(?i:jpg|jpeg|jpe|jfif)$"
      && IsInteger(quality) && 0 <= quality && quality <= 100 && quality != 75) {
         DllCall("gdiplus\GdipGetEncoderParameterListSize", "ptr", pBitmap, "ptr", pCodec, "uint*", size:=0)
         EncoderParameters := BufferAlloc(size, 0)
         DllCall("gdiplus\GdipGetEncoderParameterList", "ptr", pBitmap, "ptr", pCodec, "uint", size, "ptr", EncoderParameters)

         ; Search for an encoder parameter with 1 value of type 6.
         Loop NumGet(EncoderParameters, "uint")
            elem := (24+A_PtrSize)*(A_Index-1) + A_PtrSize
         until (NumGet(EncoderParameters, elem+16, "uint") = 1) && (NumGet(EncoderParameters, elem+20, "uint") = 6)

         ; struct EncoderParameter - http://www.jose.it-berater.org/gdiplus/reference/structures/encoderparameter.htm
         ep := EncoderParameters.ptr + elem - A_PtrSize                  ; sizeof(EncoderParameter) = 28, 32
            , NumPut(  "uptr",       1, ep)                              ; Must be 1.
            , NumPut(  "uint",       4, ep, 20+A_PtrSize)                ; Type
            , NumPut(  "uint", quality, NumGet(ep+24+A_PtrSize, "uptr")) ; Value (pointer)
      }

      ; Create a Stream.
      DllCall("ole32\CreateStreamOnHGlobal", "ptr", 0, "int", true, "ptr*", pStream:=0)
      DllCall("gdiplus\GdipSaveImageToStream", "ptr", pBitmap, "ptr", pStream, "ptr", pCodec, "uint", IsSet(ep) ? ep : 0)

      return pStream
   }

   static put_randomAccessStream(ByRef pBitmap, extension := "", quality := "") {
      ; Thanks teadrinker - https://www.autohotkey.com/boards/viewtopic.php?f=6&t=72674

      ; Which is faster, bmp or png?
      pStream := this.put_stream(pBitmap, extension, quality)

      ; Get the Class ID from a GUID string.
      CLSID := BufferAlloc(16, 0)
      if res := DllCall("ole32\CLSIDFromString", "wstr", "{905A0FE1-BC53-11DF-8C49-001E4FC686DA}", "ptr", CLSID, "uint")
         throw Exception("CLSIDFromString failed. Error: " . Format("{:#x}", res))

      ; Create a RandomAccessStream
      DllCall("ShCore\CreateRandomAccessStreamOverStream", "ptr", pStream, "uint", 1, "ptr", CLSID, "ptr*", pRandomAccessStream:=0, "uint")

      ; The handle to the stream object is automatically freed when the stream object is released.
      ObjRelease(pStream)

      return pRandomAccessStream
   }

   static put_hBitmap(ByRef pBitmap, alpha := "") {
      ; Revert to built in functionality if a replacement color is declared.
      if (alpha != "") { ; This built-in version is about 25% slower.
         DllCall("gdiplus\GdipCreateHBITMAPFromBitmap", "ptr", pBitmap, "ptr*", hBitmap:=0, "uint", alpha)
         return hBitmap
      }

      ; Get Bitmap width and height.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0)

      ; 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 := BufferAlloc(40, 0)                 ; sizeof(bi) = 40
         , NumPut(  "uint",        40, bi,  0) ; Size
         , NumPut(  "uint",     width, bi,  4) ; Width
         , NumPut(   "int",   -height, 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")

      ; Transfer data from source pBitmap to an hBitmap manually.
      Rect := BufferAlloc(16, 0)               ; sizeof(Rect) = 16
         , NumPut(  "uint",   width, Rect,  8) ; Width
         , NumPut(  "uint",  height, Rect, 12) ; Height
      BitmapData := BufferAlloc(16+2*A_PtrSize, 0)      ; sizeof(BitmapData) = 24, 32
         , NumPut(  "uint",      width, BitmapData,  0) ; Width
         , NumPut(  "uint",     height, BitmapData,  4) ; Height
         , NumPut(   "int",  4 * width, BitmapData,  8) ; Stride
         , NumPut(   "int",    0xE200B, BitmapData, 12) ; PixelFormat
         , 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)

      ; Cleanup the hBitmap and device contexts.
      DllCall("SelectObject", "ptr", hdc, "ptr", obm)
      DllCall("DeleteDC",     "ptr", hdc)

      return hbm
   }

   static put_base64(ByRef pBitmap, extension := "", quality := "") {
      ; Thanks noname - https://www.autohotkey.com/boards/viewtopic.php?style=7&p=144247#p144247

      pStream := this.put_stream(pBitmap, extension, quality)

      DllCall("ole32\GetHGlobalFromStream", "ptr", pStream, "uint*", hData:=0)
      pData := DllCall("GlobalLock", "ptr", hData, "ptr")
      nSize := DllCall("GlobalSize", "uint", pData)

      bin := BufferAlloc(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.
      base64Length := 0
      DllCall("Crypt32.dll\CryptBinaryToStringA", "ptr", bin, "uint", nSize, "uint", 0x40000001, "ptr", 0, "uint*", base64Length)
      base64 := BufferAlloc(base64Length, 0)
      DllCall("Crypt32.dll\CryptBinaryToStringA", "ptr", bin, "uint", nSize, "uint", 0x40000001, "ptr", base64, "uint*", base64Length)
      VarSetStrCapacity(bin, 0)

      return StrGet(base64, base64Length, "CP0")
   }

   static gdiplus := 0

   static gdiplusStartup() {
      this.gdiplus := (this.gdiplus == "") ? 1 : this.gdiplus + 1

      ; Startup gdiplus when counter goes from 0 -> 1 or "" -> 1.
      if (this.gdiplus == 1) {
         DllCall("LoadLibrary", "str", "gdiplus")
         si := BufferAlloc(A_PtrSize = 8 ? 24 : 16, 0) ; sizeof(GdiplusStartupInput) = 16, 24
            , NumPut("uint", 0x1, si)
         DllCall("gdiplus\GdiplusStartup", "ptr*", pToken:=0, "ptr", si, "ptr", 0)
         this.pToken := pToken
      }
   }

   static gdiplusShutdown(cotype := "", ByRef pBitmap := "") {
      this.gdiplus := this.gdiplus - 1

      ; When a buffer object is deleted a bitmap is sent here for disposal.
      if (cotype == "smart_pointer")
         if DllCall("gdiplus\GdipDisposeImage", "ptr", pBitmap)
            throw Exception("The bitmap of this buffer object has already been deleted.")

      ; Shutdown gdiplus if pToken is owned and when counter goes from 1 -> 0.
      if (this.gdiplus == 0) {
         DllCall("gdiplus\GdiplusShutdown", "ptr", this.pToken)
         DllCall("FreeLibrary", "ptr", DllCall("GetModuleHandle", "str", "gdiplus", "ptr"))

         ; Exit if GDI+ is still loaded. GdiplusNotInitialized = 18
         if (18 != DllCall("gdiplus\GdipCreateImageAttributes", "ptr*", ImageAttr:=0)) {
            DllCall("gdiplus\GdipDisposeImageAttributes", "ptr", ImageAttr)
            return
         }

         ; Otherwise GDI+ has been truly unloaded from the script and objects are out of scope.
         if (cotype = "bitmap")
            throw Exception("Out of scope error. `n`nIf you wish to handle raw pointers to GDI+ bitmaps, add the line"
               . "`n`n`t`t" this.prototype.__class ".gdiplusStartup()`n`nor 'pToken := Gdip_Startup()' to the top of your script."
               . "`nAlternatively, use 'obj := ImagePutBuffer()' with 'obj.pBitmap'."
               . "`nYou can copy this message by pressing Ctrl + C.")
      }
   }
} ; End of ImagePut class.
  • Refactored pBitmap -> base64 conversion
  • Refactored pBitmap -> file conversion
  • When ImagePutFile() is called without a filename, the current date and time is used instead.
  • ImagePutStream() has been added. Returns a pointer to IStream.
  • ImagePutRandomAccessStream() has been added. Returns a pointer to IRandomAccessStream.
  • Monitors now work properly! ImagePutFile(1) will retrieve monitor 1, ImagePutFile(2) will retrieve monitor 2...
  • Better error messages, and removed some comments.

iseahound
Posts: 608
Joined: 13 Aug 2016, 21:04
GitHub: iseahound

Re: [a119] ImagePut - Puts an image from anywhere to anywhere

Post by iseahound » 27 Jul 2020, 16:00

a119 compatible

Code: Select all

; Script:    ImagePut.ahk
; Author:    iseahound
; License:   MIT License
; Version:   2020-05-22
; Release:   2020-07-29

; ImagePut - Puts an image from anywhere to anywhere.
; This is a simple functor designed to be intuitive.
; I hope people find this reference library useful.


; Puts the image into a file format and returns a base64 encoded string.
;   extension  -  File Encoding           |  string   ->   bmp, gif, jpg, png, tiff
;   quality    -  JPEG Quality Level      |  integer  ->   0 - 100
ImagePutBase64(ByRef image, extension := "", quality := "")
   => ImagePut("base64", image,,, extension, quality)


; Puts the image into a GDI+ Bitmap and returns a pointer.
ImagePutBitmap(ByRef image)
   => ImagePut("bitmap", image)


; Puts the image into a GDI+ Bitmap and returns a buffer object with GDI+ scope.
ImagePutBuffer(ByRef image)
   => ImagePut("buffer", image)


; Puts the image onto the clipboard and returns an empty string.
ImagePutClipboard(ByRef image)
   => ImagePut("clipboard", image)


; Puts the image as the cursor and returns the string "A_Cursor".
;   xHotspot   -  X Click Point           |  pixel    ->   0 - width
;   yHotspot   -  Y Click Point           |  pixel    ->   0 - height
ImagePutCursor(ByRef image, xHotspot := "", yHotspot := "")
   => ImagePut("cursor", image,,, xHotspot, yHotspot)


; Puts the image behind the desktop icons and returns the string "desktop".
;   scale      -  Scale Factor            |  real     ->   A_ScreenHeight / height.
ImagePutDesktop(ByRef image, scale := 1)
   => ImagePut("desktop", image,, scale)


; Puts the image into a file and returns the filepath.
;   filepath   -  Filepath + Extension    |  string   ->   *.bmp, *.gif, *.jpg, *.png, *.tiff
;   quality    -  JPEG Quality Level      |  integer  ->   0 - 100
ImagePutFile(ByRef image, filepath := "", quality := "")
   => ImagePut("file", image,,, filepath, quality)


; Puts the image into a device independent bitmap and returns a handle.
;   quality    -  JPEG Quality Level      |  integer  ->   0 - 100
;   alpha      -  Alpha Replacement Color |  RGB      ->   0xFFFFFF
ImagePutHBitmap(ByRef image, alpha := "")
   => ImagePut("hBitmap", image,,, alpha)


; Puts the image on the shared screen device context and returns an array of coordinates.
;   screenshot -  Screen Coordinates      |  array    ->   [x,y,w,h] or [0,0]
;   alpha      -  Alpha Replacement Color |  RGB      ->   0xFFFFFF
ImagePutScreenshot(ByRef image, screenshot := "", alpha := "")
   => ImagePut("screenshot", image,,, screenshot, alpha)


; Puts the image into a file format and returns a memory stream.
;   extension  -  File Encoding           |  string   ->   bmp, gif, jpg, png, tiff
;   quality    -  JPEG Quality Level      |  integer  ->   0 - 100
ImagePutStream(ByRef image, extension := "", quality := "")
   => ImagePut("stream", image,,, extension, quality)


; Puts the image into a file format and returns a RandomAccessStream.
;   extension  -  File Encoding           |  string   ->   bmp, gif, jpg, png, tiff
;   quality    -  JPEG Quality Level      |  integer  ->   0 - 100
ImagePutRandomAccessStream(ByRef image, extension := "", quality := "")
   => ImagePut("randomAccessStream", image,,, extension, quality)


; Puts the image as the desktop wallpaper and returns the string "wallpaper".
ImagePutWallpaper(ByRef image)
   => ImagePut("wallpaper", image)



; ImagePut() - Puts an image from anywhere to anywhere.
;   cotype     -  Output Type             |  string   ->   Case Insensitive. Read documentation.
;   image      -  Input Image             |  image    ->   Anything. Refer to ImageType().
;   crop       -  Crop Coordinates        |  array    ->   [x,y,w,h] could be negative or percent.
;   scale      -  Scale Factor            |  real     ->   2.0
;   terms*     -  Additional Parameters   |  variadic ->   Extra parameters found in toCotype().
ImagePut(cotype, ByRef image, crop := "", scale := "", terms*)
   => ImagePut.call(cotype, image, crop, scale, terms*)



class ImagePut {

   static call(cotype, ByRef image, crop := "", scale := "", terms*) {

      this.gdiplusStartup()

      ; Take a guess as to what the image might be. (>90% accuracy!)
      try type := this.DontVerifyImageType(image)
      catch
         type := this.ImageType(image)

      ; Qualify additional parameters for correctness.
      _crop := IsObject(crop)
         && crop[1] ~= "^-?\d+(\.\d*)?%?$" && crop[2] ~= "^-?\d+(\.\d*)?%?$"
         && crop[3] ~= "^-?\d+(\.\d*)?%?$" && crop[4] ~= "^-?\d+(\.\d*)?%?$"
      _scale := scale != 1 && scale ~= "^\d+(\.\d+)?$"

      ; Make a copy of the image as a pBitmap.
      pBitmap := this.toBitmap(type, image)

      ; Crop the image.
      if (_crop) {
         pBitmap2 := this.BitmapCrop(pBitmap, crop)
         DllCall("gdiplus\GdipDisposeImage", "ptr", pBitmap)
         pBitmap := pBitmap2
      }

      ; Scale the image.
      if (_scale) {
         pBitmap2 := this.BitmapScale(pBitmap, scale)
         DllCall("gdiplus\GdipDisposeImage", "ptr", pBitmap)
         pBitmap := pBitmap2
      }

      ; Put the pBitmap to wherever the cotype specifies.
      coimage := this.toCotype(cotype, pBitmap, terms*)

      ; Clean up the pBitmap copy. Export raw pointers if requested.
      if !(cotype = "bitmap" || cotype = "buffer")
         DllCall("gdiplus\GdipDisposeImage", "ptr", pBitmap)

      this.gdiplusShutdown(cotype)

      return coimage
   }

   ; Types              | Data type       | Example         | Explicit    | Inferred  | Input    | Output   | Notes
   ;                    |                 |                 | Don'tVerify | ImageType | toBitmap | toCotype |
   ; clipboard          | null string?    | ClipboardAll    |     yes     |    yes    |    yes   |    yes   |
   ; object             | object          | object.Bitmap() |     yes     |    yes    |    yes   |    no    |
   ; buffer             | object          | bitmap.pBitmap  |     yes     |    yes    |    yes   |    yes   |
   ; screenshot         | object          | [x,y,w,h]       |     yes     |    yes    |    yes   |    yes   |
   ; desktop            | string (abc)    | "desktop"       |     yes     |    yes    |    no    |    yes   |
   ; wallpaper          | string (abc)    | "wallpaper"     |     yes     |    yes    |    yes   |    yes   |
   ; cursor             | string (abc)    | A_Cursor        |     yes     |    yes    |    yes   |    yes   |
   ; url                | string (abc)    | https://        |     yes     |    yes    |    yes   |          |
   ; file               | string (abc)    | picture.bmp     |     yes     |    yes    |    yes   |    yes   |
   ; stream             | pointer         |                 |             |           |          |    yes   |
   ; randomaccessstream | pointer         |                 |             |           |          |    yes   |
   ; bitmap             | pointer         | some number     |     yes     |    yes    |    yes   |    yes   |
   ; hBitmap            | pointer         | some number     |     yes     |    yes    |    yes   |    yes   |
   ; hIcon              | pointer         |                 |             |           |          |          |
   ; monitor            | integer         | 0 or # < 10     |     yes     |    yes    |          |          |
   ; hwnd               | integer         | 0x              |     yes     |    yes    |    yes   |          |
   ; window             | string (123)    | A               |     yes     |    yes    |    yes   |          |
   ; base64             | string (123)    | base64 data     |     yes     |    yes    |    yes   |    yes   |
   ; sprite             | N/A             |                 |     yes     |    no     |    yes   |    no    |
   ; icon               |                 |             |           |          |          |
   ; printer            |                 |             |           |          |          |
   ; findtext           |                 |             |           |          |          |
   ; thumbnail          |                 |       ?     |           |          |          | DwmRegisterThumbnail
   ; video              |                 |             |           |          |          |
   ; formdata           |                 |             |           |          |          |
   ; trayicon           |                 |             |           |          |          |
   ; hash               | aHash, p, d, w  |             |           |          |          |
   ; Animal Crossing QR code       |             |           |          |          |


   static DontVerifyImageType(ByRef image) {

      if !IsObject(image)
         throw Exception("Must be an object.")

      ; Check for image type declarations.
      ; Assumes that the user is telling the truth.

      if ObjHasOwnProp(image, "clipboard") {
         image := image.clipboard
         return "clipboard"
      }

      if ObjHasOwnProp(image, "object") {
         image := image.object
         return "object"
      }

      if ObjHasOwnProp(image, "buffer") {
         image := image.buffer
         return "buffer"
      }

      if ObjHasOwnProp(image, "screenshot") {
         image := image.screenshot
         return "screenshot"
      }

      if ObjHasOwnProp(image, "desktop") {
         image := image.desktop
         return "desktop"
      }

      if ObjHasOwnProp(image, "wallpaper") {
         image := image.wallpaper
         return "wallpaper"
      }

      if ObjHasOwnProp(image, "cursor") {
         image := image.cursor
         return "cursor"
      }

      if ObjHasOwnProp(image, "url") {
         image := image.url
         return "url"
      }

      if ObjHasOwnProp(image, "file") {
         image := image.file
         return "file"
      }

      if ObjHasOwnProp(image, "bitmap") {
         image := image.bitmap
         return "bitmap"
      }

      if ObjHasOwnProp(image, "hBitmap") {
         image := image.hBitmap
         return "hBitmap"
      }

      if ObjHasOwnProp(image, "monitor") {
         image := image.monitor
         return "monitor"
      }

      if ObjHasOwnProp(image, "hwnd") {
         image := image.hwnd
         return "hwnd"
      }

      if ObjHasOwnProp(image, "window") {
         image := image.window
         return "window"
      }

      if ObjHasOwnProp(image, "base64") {
         image := image.base64
         return "base64"
      }

      if ObjHasOwnProp(image, "sprite") {
         image := image.sprite
         return "sprite"
      }

      throw Exception("Invalid type.")
   }

   static ImageType(ByRef image) {
      ; Throw if the image is an empty string.
      if (image == "")
         throw Exception("Image data is an empty string.")

      if IsObject(image) {
         ; A "clipboard" is a buffer object containing binary data returned by ClipboardAll()
         if (image.base.HasOwnProp("__class") && image.base.__class == "ClipboardAll")
            return "clipboard"

         ; An "object" is an object that implements a Bitmap() method returning a pointer to a GDI+ bitmap.
         if image.HasOwnMethod("Bitmap")
            return "object"

         ; A "buffer" is an AutoHotkey v2 buffer object.
         if image.HasOwnProp("pBitmap")
            return "buffer"

         ; A "screenshot" is an array of 4 numbers.
         if (image[1] ~= "^-?\d+$" && image[2] ~= "^-?\d+$" && image[3] ~= "^-?\d+$" && image[4] ~= "^-?\d+$")
            return "screenshot"
      }
         ; A "desktop" is a hidden window behind the desktop icons created by ImagePutDesktop.
         if (image = "desktop")
            return "desktop"

         ; A "wallpaper" is the desktop wallpaper.
         if (image = "wallpaper")
            return "wallpaper"

         ; A "cursor" is the name of a known cursor name.
         if (image ~= "(?i)^(IDC|OCR)?_?(A_Cursor|AppStarting|Arrow|Cross|Help|IBeam|"
         . "Icon|No|Size|SizeAll|SizeNESW|SizeNS|SizeNWSE|SizeWE|UpArrow|Wait|Unknown)$")
            return "cursor"

         ; A "url" satisfies the url format.
         if this.is_url(image)
            return "url"

         ; A "file" must exist.
         if FileExist(image)
            return "file"

      if IsInteger(image) {
         ; A "bitmap" is a pointer to a GDI+ Bitmap.
         try if (!DllCall("gdiplus\GdipGetImageType", "ptr", image, "ptr*", type:=0) && type == 1)
            return "bitmap"

         ; A "hBitmap" is a handle to a GDI Bitmap.
         if (DllCall("GetObjectType", "ptr", image) == 7)
            return "hBitmap"

         ; A non-zero "monitor" number identifies each display uniquely; and 0 refers to the entire virtual screen.
         if (image >= 0 && image <= MonitorGetCount())
            return "monitor"

         ; A "hwnd" is a handle to a window and more commonly known as ahk_id.
         if DllCall("IsWindow", "ptr", image)
            return "hwnd"
      }
         ; A "window" is anything considered a Window Title including ahk_class and "A".
         if WinExist(image)
            return "window"

         ; A "base64" string is binary image data encoded into text using only 64 characters.
         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.")
   }

   static toBitmap(type, ByRef image) {

      if (type = "clipboard")
         return this.from_clipboard()

      if (type = "object")
         return image.Bitmap()

      if (type = "buffer") {
         DllCall("gdiplus\GdipCloneImage", "ptr", image.pBitmap, "ptr*", pBitmap:=0)
         return pBitmap
      }

      if (type = "screenshot")
         return this.from_screenshot(image)

      if (type = "desktop")
         return this.from_desktop()

      if (type = "wallpaper")
         return this.from_wallpaper()

      if (type = "cursor")
         return this.from_cursor()

      if (type = "url")
         return this.from_url(image)

      if (type = "file") {
         DllCall("gdiplus\GdipCreateBitmapFromFile", "wstr", image, "ptr*", pBitmap:=0)
         return pBitmap
      }

      if (type = "bitmap") {
         DllCall("gdiplus\GdipCloneImage", "ptr", image, "ptr*", pBitmap:=0)
         return pBitmap
      }

      if (type = "hBitmap")
         return this.from_hBitmap(image)

      if (type = "monitor")
         return this.from_monitor(image)

      if (type = "hwnd" || type = "window") {
         image := (type = "window") ? WinExist(image) : image
         return this.from_hwnd(image)
      }

      if (type = "base64")
         return this.from_base64(image)

      if (type = "sprite")
         return this.from_sprite(image)

      throw Exception("Conversion from type " type " is not supported.")
   }

   static toCotype(cotype, ByRef pBitmap, terms*) {
      ; toCotype("clipboard", pBitmap)
      if (cotype = "clipboard")
         return this.put_clipboard(pBitmap)

      ; toCotype("buffer", pBitmap)
      if (cotype = "buffer") {
         buffer := {pBitmap: pBitmap}
            .DefineMethod("__New" , (self) => (this.gdiplusStartup(), self)) ; Increment GDI+ reference count
            .DefineMethod("__Delete", (self) => (this.gdiplusShutdown("smart_pointer", self.pBitmap)))
            .__New()  ; On deletion the buffer object will dispose of the bitmap. And it will decrement this.gdiplus.
         return buffer
      }

      ; toCotype("screenshot", pBitmap, screenshot, alpha)
      if (cotype = "screenshot")
         return this.put_screenshot(pBitmap, terms[1], terms[2])

      ; toCotype("desktop", pBitmap)
      if (cotype = "desktop")
         return this.put_desktop(pBitmap)

      ; toCotype("wallpaper", pBitmap)
      if (cotype = "wallpaper")
         return this.put_wallpaper(pBitmap)

      ; toCotype("cursor", pBitmap, xHotspot, yHotspot)
      if (cotype = "cursor")
         return this.put_cursor(pBitmap, terms[1], terms[2])

      ; toCotype("url", ????????????????????????
      if (cotype = "url") {
         ; put a url
      }

      ; toCotype("file", pBitmap, filename, quality)
      if (cotype = "file")
         return this.put_file(pBitmap, terms[1], terms[2])

      ; toCotype("stream", pBitmap, extension, quality)
      if (cotype = "stream")
         return this.put_stream(pBitmap, terms[1], terms[2])

      ; toCotype("randomAccessStream", pBitmap, extension, quality)
      if (cotype = "randomAccessStream")
         return this.put_randomAccessStream(pBitmap, terms[1], terms[2])

      ; toCotype("bitmap", pBitmap)
      if (cotype = "bitmap")
         return pBitmap

      ; toCotype("hBitmap", pBitmap, alpha)
      if (cotype = "hBitmap")
         return this.put_hBitmap(pBitmap, terms[1])

      ; toCotype("hwnd", pBitmap)
      if (cotype = "hwnd")
         return this.Render({bitmap:pBitmap}).hwnd

      ; toCotype("window", pBitmap)
      if (cotype = "window")
         return "ahk_id " . this.Render({bitmap:pBitmap}).AlwaysOnTop().ToolWindow().Caption().hwnd

      ; toCotype("base64", pBitmap, extension, quality)
      if (cotype = "base64")
         return this.put_base64(pBitmap, terms[1], terms[2])

      throw Exception("Conversion to type " cotype " is not supported.")
   }

   static DisposeImage(ByRef pBitmap) {
      return DllCall("gdiplus\GdipDisposeImage", "ptr", pBitmap)
   }

   static BitmapCrop(ByRef pBitmap, crop) {
      ; Get Bitmap width, height, and format.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0)
      DllCall("gdiplus\GdipGetImagePixelFormat", "ptr", pBitmap, "uint*", format:=0)

      ; Are the numbers percentages?
      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]
      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]

      ; If numbers are negative, subtract the values from the edge.
      crop[3] := (crop[3] < 0) ?  width - Abs(crop[3]) - Abs(crop[1]) : crop[3]
      crop[4] := (crop[4] < 0) ? height - Abs(crop[4]) - Abs(crop[2]) : crop[4]
      crop[1] := Abs(crop[1])
      crop[2] := Abs(crop[2])

      ; Round to the nearest integer.
      crop[3] := Round(crop[1] + crop[3]) - Round(crop[1]) ; A reminder that width and height
      crop[4] := Round(crop[2] + crop[4]) - Round(crop[2]) ; are distances, not coordinates.
      crop[1] := Round(crop[1]) ; so the abstract concept of a distance must be resolved
      crop[2] := Round(crop[2]) ; into coordinates and then rounded and added up again.

      ; Variance Shift. Now place x,y before w,h because we are building abstracts from reals now.
      ; Before we were resolving abstracts into real coordinates, now it's the opposite.

      ; Ensure that coordinates can never exceed the expected Bitmap area.
      safe_x := (crop[1] > width) ? 0 : crop[1]                          ; Zero x if bigger.
      safe_y := (crop[2] > height) ? 0 : crop[2]                         ; Zero y if bigger.
      safe_w := (crop[1] + crop[3] > width) ? width - safe_x : crop[3]   ; Max w if bigger.
      safe_h := (crop[2] + crop[4] > height) ? height - safe_y : crop[4] ; Max h if bigger.

      ; Clone
      DllCall("gdiplus\GdipCloneBitmapAreaI"
               ,    "int", safe_x
               ,    "int", safe_y
               ,    "int", safe_w
               ,    "int", safe_h
               ,    "int", format
               ,    "ptr", pBitmap
               ,   "ptr*", pBitmapCrop:=0)

      return pBitmapCrop
   }

   static BitmapScale(ByRef pBitmap, scale) {
      ; Get Bitmap width, height, and format.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0)
      DllCall("gdiplus\GdipGetImagePixelFormat", "ptr", pBitmap, "uint*", format:=0)

      safe_w := Ceil(width * scale)
      safe_h := Ceil(height * scale)

      ; Create a new bitmap and get the graphics context.
      DllCall("gdiplus\GdipCreateBitmapFromScan0"
               , "int", safe_w, "int", safe_h, "int", 0, "int", format, "ptr", 0, "ptr*", pBitmapScale:=0)
      DllCall("gdiplus\GdipGetImageGraphicsContext", "ptr", pBitmapScale, "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", safe_w, "int", safe_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)
      return pBitmapScale
   }

   static is_url(url) {
      ; Thanks splattermania - https://www.php.net/manual/en/function.preg-match.php#93824

      regex := "^(?i)"
         . "((https?|ftp)\:\/\/)" ; SCHEME
         . "([a-z0-9+!*(),;?&=\$_.-]+(\:[a-z0-9+!*(),;?&=\$_.-]+)[email protected])?" ; User and Pass
         . "([a-z0-9-.]*)\.([a-z]{2,3})" ; Host or IP
         . "(\:[0-9]{2,5})?" ; Port
         . "(\/(?:[a-z0-9-_~!$&'()*+,;=:@]\.?)+)*\/?" ; Path
         . "(\?[a-z+&\$_.-][a-z0-9;:@&%=+\/\$_.-]*)?" ; GET Query
         . "(#[a-z_.-][a-z0-9+\$_.-]*)?$" ; Anchor
      return (url ~= regex)
   }

   static from_clipboard() {
      ; Open the clipboard.
      Loop 6 ; Try this 6 times.
         if (A_Index > 1)
            Sleep 30
      until (result := DllCall("OpenClipboard", "ptr", 0))
      if !(result)
         throw Exception("Clipboard could not be opened.")

      ; Prefer the PNG stream if available considering it supports transparency.
      png := DllCall("RegisterClipboardFormat", "str", "png", "uint")
      if DllCall("IsClipboardFormatAvailable", "uint", png, "int") {
         hData := DllCall("GetClipboardData", "uint", png, "ptr")
         DllCall("ole32\CreateStreamOnHGlobal", "ptr", hData, "int", true, "ptr*", pStream:=0)
         DllCall("gdiplus\GdipCreateBitmapFromStream", "ptr", pStream, "ptr*", pBitmap:=0)
         ObjRelease(pStream)
      }

      ; Fallback to CF_BITMAP.
      else if DllCall("IsClipboardFormatAvailable", "uint", 2, "int") {
         hBitmap := DllCall("GetClipboardData", "uint", 2, "ptr")
         DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", "ptr", hBitmap, "ptr", 0, "ptr*", pBitmap:=0)
         DllCall("DeleteObject", "ptr", hBitmap)
      }

      DllCall("CloseClipboard")
      return pBitmap
   }

   static from_screenshot(ByRef image) {
      ; Thanks tic - https://www.autohotkey.com/boards/viewtopic.php?t=6517

      ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      bi := BufferAlloc(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) ; SRCCOPY

      ; 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
   }

   static from_wallpaper() {
      ; Get the width and height of all monitors.
      width  := DllCall("GetSystemMetrics", "int", 78)
      height := DllCall("GetSystemMetrics", "int", 79)

      ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      bi := BufferAlloc(40, 0)                 ; sizeof(bi) = 40
         , NumPut(  "uint",        40, bi,  0) ; Size
         , NumPut(   "int",     width, bi,  4) ; Width
         , NumPut(   "int",   -height, 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")

      ; Paints the desktop.
      DllCall("PaintDesktop", "ptr", hdc)

      ; 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
   }

   static from_cursor() {
      ; Thanks 23W - https://stackoverflow.com/a/13295280

      ; struct CURSORINFO - https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-cursorinfo
      ci := BufferAlloc(16+A_PtrSize, 0)                 ; sizeof(CURSORINFO) = 20, 24
         , NumPut("int", ci.size, ci)
      DllCall("GetCursorInfo", "ptr", ci)
         ; cShow   := NumGet(ci,  4, "int")              ; 0x1 = CURSOR_SHOWING, 0x2 = CURSOR_SUPPRESSED
         , hCursor := NumGet(ci,  8, "ptr")
         ; xCursor := NumGet(ci,  8+A_PtrSize, "int")
         ; yCursor := NumGet(ci, 12+A_PtrSize, "int")

      ; struct ICONINFO - https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-iconinfo
      ii := BufferAlloc(8+3*A_PtrSize, 0)                ; sizeof(ICONINFO) = 20, 32
      DllCall("GetIconInfo", "ptr", hCursor, "ptr", ii)
         ; xHotspot := NumGet(ii, 4, "uint")
         ; yHotspot := NumGet(ii, 8, "uint")
         , hbmMask  := NumGet(ii, 8+A_PtrSize, "ptr")    ; x86:12, x64:16
         , hbmColor := NumGet(ii, 8+2*A_PtrSize, "ptr")  ; x86:16, x64:24

      ; struct BITMAP - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmap
      bm := BufferAlloc(16+2*A_PtrSize) ; sizeof(BITMAP) = 24, 32
      DllCall("GetObject", "ptr", hbmMask, "int", bm.size, "ptr", bm)
         , width  := NumGet(bm, 4, "uint")
         , height := NumGet(bm, 8, "uint") / (hbmColor ? 1 : 2) ; Black and White cursors have doubled height.

      ; Clean up these hBitmaps.
      DllCall("DeleteObject", "ptr", hbmMask)
      DllCall("DeleteObject", "ptr", hbmColor)

      ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      bi := BufferAlloc(40, 0)                 ; sizeof(bi) = 40
         , NumPut(  "uint",        40, bi,  0) ; Size
         , NumPut(   "int",     width, bi,  4) ; Width
         , NumPut(   "int",   -height, 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")

      ; This is the 32-bit ARGB pBitmap (different from an hBitmap) that will receive the final converted pixels.
      DllCall("gdiplus\GdipCreateBitmapFromScan0"
               , "int", width, "int", height, "int", 0, "int", 0x26200A, "ptr", 0, "ptr*", pBitmap:=0)

      ; Create a Scan0 buffer pointing to pBits. The buffer has pixel format pARGB.
      Rect := BufferAlloc(16, 0)               ; sizeof(Rect) = 16
         , NumPut(  "uint",   width, Rect,  8) ; Width
         , NumPut(  "uint",  height, Rect, 12) ; Height
      BitmapData := BufferAlloc(16+2*A_PtrSize, 0)      ; sizeof(BitmapData) = 24, 32
         , NumPut(  "uint",      width, BitmapData,  0) ; Width
         , NumPut(  "uint",     height, BitmapData,  4) ; Height
         , NumPut(   "int",  4 * width, BitmapData,  8) ; Stride
         , NumPut(   "int",    0xE200B, BitmapData, 12) ; PixelFormat
         , NumPut(   "ptr",      pBits, BitmapData, 16) ; Scan0

      ; Use LockBits to create a writable buffer that converts pARGB to ARGB.
      DllCall("gdiplus\GdipBitmapLockBits"
               ,    "ptr", pBitmap
               ,    "ptr", Rect
               ,   "uint", 6            ; ImageLockMode.UserInputBuffer | ImageLockMode.WriteOnly
               ,    "int", 0xE200B      ; Format32bppPArgb
               ,    "ptr", BitmapData)  ; Contains the pointer (pBits) to the hbm.

      ; Don't use DI_DEFAULTSIZE to draw the icon like DrawIcon does as it will resize to 32 x 32.
      DllCall("DrawIconEx"
               , "ptr", hdc,     "int", 0, "int", 0
               , "ptr", hCursor, "int", 0, "int", 0
               , "uint", 0, "ptr", 0, "uint", 0x1 | 0x2 | 0x4) ; DI_MASK | DI_IMAGE | DI_COMPAT

      ; Convert the pARGB pixels copied into the device independent bitmap (hbm) to ARGB.
      DllCall("gdiplus\GdipBitmapUnlockBits", "ptr", pBitmap, "ptr", BitmapData)

      ; Clean up the icon and device context.
      DllCall("DestroyIcon",  "ptr", hCursor)
      DllCall("SelectObject", "ptr", hdc, "ptr", obm)
      DllCall("DeleteObject", "ptr", hbm)
      DllCall("DeleteDC",     "ptr", hdc)

      return pBitmap
   }

   static from_url(ByRef image) {
      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:=0)
      ObjRelease(pStream)
      return pBitmap
   }

   static from_hBitmap(ByRef image) {
      ; struct DIBSECTION - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-dibsection
      ; struct BITMAP - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmap
      dib := BufferAlloc(64+5*A_PtrSize) ; sizeof(DIBSECTION) = 84, 104
      DllCall("GetObject", "ptr", image, "int", dib.size, "ptr", dib)
         , width  := NumGet(dib, 4, "uint")
         , height := NumGet(dib, 8, "uint")
         , bpp    := NumGet(dib, 18, "ushort")

      ; Fallback to built-in method if pixels are not 32-bit ARGB.
      if (bpp != 32) { ; This built-in version is 120% faster but ignores transparency.
         DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", "ptr", image, "ptr", 0, "ptr*", pBitmap:=0)
         return pBitmap
      }

      ; Create a handle to a device context and associate the image.
      sdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")           ; Creates a memory DC compatible with the current screen.
      sbm := DllCall("SelectObject", "ptr", sdc, "ptr", image, "ptr") ; Put the (hBitmap) image onto the device context.

      ; Create a device independent bitmap with negative height. All DIBs use the screen pixel format (pARGB).
      ; Use hbm to buffer the image such that top-down and bottom-up images are mapped to this top-down buffer.
      ; pBits is the pointer to (top-down) pixel values. The Scan0 will point to the pBits.
      ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      bi := BufferAlloc(40, 0)                 ; sizeof(bi) = 40
         , NumPut(  "uint",        40, bi,  0) ; Size
         , NumPut(   "int",     width, bi,  4) ; Width
         , NumPut(   "int",   -height, 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")

      ; This is the 32-bit ARGB pBitmap (different from an hBitmap) that will receive the final converted pixels.
      DllCall("gdiplus\GdipCreateBitmapFromScan0"
               , "int", width, "int", height, "int", 0, "int", 0x26200A, "ptr", 0, "ptr*", pBitmap:=0)

      ; Create a Scan0 buffer pointing to pBits. The buffer has pixel format pARGB.
      Rect := BufferAlloc(16, 0)               ; sizeof(Rect) = 16
         , NumPut(  "uint",   width, Rect,  8) ; Width
         , NumPut(  "uint",  height, Rect, 12) ; Height
      BitmapData := BufferAlloc(16+2*A_PtrSize, 0)      ; sizeof(BitmapData) = 24, 32
         , NumPut(  "uint",      width, BitmapData,  0) ; Width
         , NumPut(  "uint",     height, BitmapData,  4) ; Height
         , NumPut(   "int",  4 * width, BitmapData,  8) ; Stride
         , NumPut(   "int",    0xE200B, BitmapData, 12) ; PixelFormat
         , NumPut(   "ptr",      pBits, BitmapData, 16) ; Scan0

      ; Use LockBits to create a writable buffer that converts pARGB to ARGB.
      DllCall("gdiplus\GdipBitmapLockBits"
               ,    "ptr", pBitmap
               ,    "ptr", Rect
               ,   "uint", 6            ; ImageLockMode.UserInputBuffer | ImageLockMode.WriteOnly
               ,    "int", 0xE200B      ; Format32bppPArgb
               ,    "ptr", BitmapData) ; Contains the pointer (pBits) to the hbm.

      ; Copies the image (hBitmap) to a top-down bitmap. Removes bottom-up-ness if present.
      DllCall("gdi32\BitBlt"
               , "ptr", hdc, "int", 0, "int", 0, "int", width, "int", height
               , "ptr", sdc, "int", 0, "int", 0, "uint", 0x00CC0020) ; SRCCOPY

      ; 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.
      DllCall("SelectObject", "ptr", hdc, "ptr", obm)
      DllCall("DeleteObject", "ptr", hbm)
      DllCall("DeleteDC",     "ptr", hdc)
      DllCall("SelectObject", "ptr", sdc, "ptr", sbm)
      DllCall("DeleteDC",     "ptr", sdc)

      return pBitmap
   }

   static from_monitor(ByRef image) {
      if (image > 0) {
         MonitorGet(image, Left, Top, Right, Bottom)
         x := Left
         y := Top
         w := Right - Left
         h := Bottom - Top
      } else {
         x := DllCall("GetSystemMetrics", "int", 76)
         y := DllCall("GetSystemMetrics", "int", 77)
         w := DllCall("GetSystemMetrics", "int", 78)
         h := DllCall("GetSystemMetrics", "int", 79)
      }
      return this.from_screenshot([x,y,w,h])
   }

   static from_hwnd(ByRef image) {
      ; Thanks tic - https://www.autohotkey.com/boards/viewtopic.php?t=6517

      ; Restore the window if minimized! Must be visible for capture.
      if DllCall("IsIconic", "ptr", image)
         DllCall("ShowWindow", "ptr", image, "int", 4)

      ; Get the width and height of the client window.
      Rect := BufferAlloc(16) ; sizeof(RECT) = 16
      DllCall("GetClientRect", "ptr", image, "ptr", Rect)
         , width  := NumGet(Rect, 8, "int")
         , height := NumGet(Rect, 12, "int")

      ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      bi := BufferAlloc(40, 0)                 ; sizeof(bi) = 40
         , NumPut(  "uint",        40, bi,  0) ; Size
         , NumPut(   "int",     width, bi,  4) ; Width
         , NumPut(   "int",   -height, 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")

      ; Print the window onto the hBitmap using an undocumented flag. https://stackoverflow.com/a/40042587
      DllCall("PrintWindow", "ptr", image, "ptr", hdc, "uint", 0x3) ; PW_CLIENTONLY | PW_RENDERFULLCONTENT
      ; Additional info on how this is implemented: https://www.reddit.com/r/windows/comments/8ffr56/altprintscreen/

      ; 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
   }

   static from_base64(ByRef image) {
      ; Trim whitespace and remove header.
      image := Trim(image)
      image := RegExReplace(image, "^data:image\/[a-z]+;base64,")

      ; Converts the image to binary data by first asking for the size.
      size := 0
      DllCall("crypt32\CryptStringToBinary", "ptr",StrPtr(image), "uint",0, "uint",0x1, "ptr",   0, "uint*",size, "ptr",0, "ptr",0)
      bin := BufferAlloc(size, 0)
      DllCall("crypt32\CryptStringToBinary", "ptr",StrPtr(image), "uint",0, "uint",0x1, "ptr", bin, "uint*",size, "ptr",0, "ptr",0)

      ; Makes a stream for conversion into a pBitmap.
      pStream := DllCall("shlwapi\SHCreateMemStream", "ptr", bin, "uint", size, "ptr")
      DllCall("gdiplus\GdipCreateBitmapFromStream", "ptr", pStream, "ptr*", pBitmap:=0)
      ObjRelease(pStream)

      return pBitmap
   }

   static from_sprite(ByRef image) {
      ; Create a source pBitmap and extract the width and height.
      if DllCall("gdiplus\GdipCreateBitmapFromFile", "wstr", image, "ptr*", sBitmap:=0)
         if !(sBitmap := this.from_url(image))
            throw Exception("Could not be loaded from a valid file path or URL.")

      ; Get Bitmap width and height.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", sBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", sBitmap, "uint*", height:=0)

      ; Create a destination pBitmap in 32-bit ARGB and get its device context though GDI+.
      ; Note that a device context from a graphics context can only be drawn on, not read.
      ; Also note that using a graphics context and blitting does not create a pixel perfect image.
      ; Using a DIB and LockBits is about 5% faster.
      DllCall("gdiplus\GdipCreateBitmapFromScan0"
               , "int", width, "int", height, "int", 0, "int", 0x26200A, "ptr", 0, "ptr*", dBitmap:=0)
      DllCall("gdiplus\GdipGetImageGraphicsContext", "ptr", dBitmap, "ptr*", dGraphics:=0)
      DllCall("gdiplus\GdipGetDC", "ptr", dGraphics, "ptr*", ddc:=0)

      ; Keep any existing transparency for whatever reason.
      hBitmap := this.put_hBitmap(sBitmap) ; Could copy this code here for even more speed.

      ; Create a source device context and associate the source hBitmap.
      sdc := DllCall("CreateCompatibleDC", "ptr", ddc, "ptr")
      obm := DllCall("SelectObject", "ptr", sdc, "ptr", hBitmap, "ptr")

      ; Copy the image making the top-left pixel the color key.
      DllCall("msimg32\TransparentBlt"
               , "ptr", ddc, "int", 0, "int", 0, "int", width, "int", height  ; destination
               , "ptr", sdc, "int", 0, "int", 0, "int", width, "int", height  ; source
               , "uint", DllCall("GetPixel", "ptr", sdc, "int", 0, "int", 0)) ; RGB pixel.

      ; Cleanup the hBitmap and device contexts.
      DllCall("SelectObject", "ptr", sdc, "ptr", obm)
      DllCall("DeleteObject", "ptr", hBitmap)
      DllCall("DeleteDC",     "ptr", sdc)

      ; Release the graphics context and delete.
      DllCall("gdiplus\GdipReleaseDC", "ptr", dGraphics, "ptr", ddc)
      DllCall("gdiplus\GdipDeleteGraphics", "ptr", dGraphics)

      return dBitmap
   }

   static put_clipboard(ByRef pBitmap) {
      ; Standard Clipboard Formats - https://docs.microsoft.com/en-us/windows/win32/dataxchg/standard-clipboard-formats
      ; Synthesized Clipboard Formats - https://docs.microsoft.com/en-us/windows/win32/dataxchg/clipboard-formats

      ; Open the clipboard.
      Loop 6 ; Try this 6 times.
         if (A_Index > 1)
            Sleep 30
      until (result := DllCall("OpenClipboard", "ptr", 0))
      if !(result)
         throw Exception("Clipboard could not be opened.")

      ; Clear the clipboard.
      DllCall("EmptyClipboard")

      ; #1 - Place the image onto the clipboard as a PNG stream.
      ; Thanks Jochen Arndt - https://www.codeproject.com/Answers/1207927/Saving-an-image-to-the-clipboard#answer3
      pStream := this.put_stream(pBitmap, "png")
      DllCall("ole32\GetHGlobalFromStream", "ptr", pStream, "uint*", hData:=0)
      DllCall("SetClipboardData", "uint", DllCall("RegisterClipboardFormat", "str", "png", "uint"), "ptr", hData)
      ObjRelease(pStream)

      ; #2 - Place the image onto the clipboard in the CF_DIB format in ARGB using 3 color masks. (Extra 12 byte offset.)
      ; Thanks Nyerguds - https://stackoverflow.com/a/46424800

      ; Get Bitmap width, height, and format.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0)
      DllCall("gdiplus\GdipGetImagePixelFormat", "ptr", pBitmap, "uint*", format:=0)

      ; Get Bitmap bits per pixel, stride, and size.
      bpp := (format & 0x00FF00) >> 8
      stride := (bpp >> 3) * width
      size := stride * height

      ; struct DIBSECTION - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-dibsection
      ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
      hdib := DllCall("GlobalAlloc", "uint", 0x42, "uptr", 40 + 12 + size, "ptr")
      pdib := DllCall("GlobalLock", "ptr", hdib, "ptr")
         , NumPut(  "uint",         40, pdib,  0) ; Size
         , NumPut(   "int",      width, pdib,  4) ; Width
         , NumPut(   "int",    -height, pdib,  8) ; Height - Negative so (0, 0) is top-left.
         , NumPut("ushort",          1, pdib, 12) ; Planes
         , NumPut("ushort",        bpp, pdib, 14) ; BitCount / BitsPerPixel
         , NumPut(  "uint",        0x3, pdib, 16) ; Compression
         , NumPut(  "uint",       size, pdib, 20) ; SizeImage (bytes)
         ; The following bitfields when masked extract the respective color channels.
         , NumPut(  "uint", 0x00FF0000, pdib, 40) ; Red
         , NumPut(  "uint", 0x0000FF00, pdib, 44) ; Green
         , NumPut(  "uint", 0x000000FF, pdib, 48) ; Blue

      ; Transfer data from source pBitmap to the global memory manually.
      Rect := BufferAlloc(16, 0)               ; sizeof(Rect) = 16
         , NumPut(  "uint",   width, Rect,  8) ; Width
         , NumPut(  "uint",  height, Rect, 12) ; Height
      BitmapData := BufferAlloc(16+2*A_PtrSize, 0)      ; sizeof(BitmapData) = 24, 32
         , NumPut(  "uint",      width, BitmapData,  0) ; Width
         , NumPut(  "uint",     height, BitmapData,  4) ; Height
         , NumPut(   "int",     stride, BitmapData,  8) ; Stride
         , NumPut(   "int",     format, BitmapData, 12) ; PixelFormat
         , NumPut(   "ptr",  pdib + 52, BitmapData, 16) ; Scan0
      DllCall("gdiplus\GdipBitmapLockBits"
               ,    "ptr", pBitmap
               ,    "ptr", Rect
               ,   "uint", 5            ; ImageLockMode.UserInputBuffer | ImageLockMode.ReadOnly
               ,    "int", 0x26200A     ; Format32bppArgb
               ,    "ptr", BitmapData)  ; Contains the pointer (pdib) to the hData.
      DllCall("gdiplus\GdipBitmapUnlockBits", "ptr", pBitmap, "ptr", BitmapData)

      ; Unlock the memory as it is complete.
      DllCall("GlobalUnlock", "ptr", hdib)

      ; Add CF_DIB as a format to the clipboard.
      DllCall("SetClipboardData", "uint", 8, "ptr", hdib)

      ; Close the clipboard.
      DllCall("CloseClipboard")

      return ClipboardAll()
   }

   static put_screenshot(ByRef pBitmap, screenshot := "", alpha := "") {
      ; Get Bitmap width and height.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0)

      x := (IsObject(screenshot) && screenshot[1] != "") ? screenshot[1] : Round((A_ScreenWidth - width) / 2)
      y := (IsObject(screenshot) && screenshot[2] != "") ? screenshot[2] : Round((A_ScreenHeight - height) / 2)
      w := (IsObject(screenshot) && screenshot[3] != "") ? screenshot[3] : width
      h := (IsObject(screenshot) && screenshot[4] != "") ? screenshot[4] : height

      ; Convert the Bitmap to a hBitmap and associate a device context for blitting.
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      hbm := this.put_hBitmap(pBitmap, alpha)
      obm := DllCall("SelectObject", "ptr", hdc, "ptr", hbm, "ptr")

      ; Retrieve the device context for the screen.
      ddc := DllCall("GetDC", "ptr", 0, "ptr")

      ; Copies a portion of the screen to a new device context.
      DllCall("gdi32\StretchBlt"
               , "ptr", ddc, "int", x, "int", y, "int", w,     "int", h
               , "ptr", hdc, "int", 0, "int", 0, "int", width, "int", height
               , "uint", 0x00CC0020) ; SRCCOPY

      ; Release the device context to the screen.
      DllCall("ReleaseDC", "ptr", 0, "ptr", ddc)

      ; Cleanup the hBitmap and device contexts.
      DllCall("SelectObject", "ptr", hdc, "ptr", obm)
      DllCall("DeleteObject", "ptr", hbm)
      DllCall("DeleteDC",     "ptr", hdc)

      return [x,y,w,h]
   }

   static put_desktop(ByRef pBitmap) {
      ; Thanks Gerald Degeneve - https://www.codeproject.com/Articles/856020/Draw-Behind-Desktop-Icons-in-Windows-plus

      ; Get Bitmap width and height.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0)

      ; Convert the Bitmap to a hBitmap and associate a device context for blitting.
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      hbm := this.put_hBitmap(pBitmap)
      obm := DllCall("SelectObject", "ptr", hdc, "ptr", hbm, "ptr")

      ; Post-Creator's Update Windows 10. WM_SPAWN_WORKER = 0x052C
      DllCall("SendMessage", "ptr", WinExist("ahk_class Progman"), "uint", 0x052C, "ptr", 0x0000000D, "ptr", 0)
      DllCall("SendMessage", "ptr", WinExist("ahk_class Progman"), "uint", 0x052C, "ptr", 0x0000000D, "ptr", 1)

      ; Find the child window.
      windows := WinGetList("ahk_class WorkerW")
      Loop windows.length
         hwnd := windows[A_Index]
      until DllCall("FindWindowEx", "ptr", hwnd, "ptr", 0, "str", "SHELLDLL_DefView", "ptr", 0)

      ; Maybe this hack gets patched. Tough luck!
      if !(WorkerW := DllCall("FindWindowEx", "ptr", 0, "ptr", hwnd, "str", "WorkerW", "ptr", 0, "ptr"))
         throw Exception("Could not draw on the desktop.")

      ; Position the image in the center. This line can be removed.
      DllCall("SetWindowPos", "ptr", WorkerW, "ptr", 1
               , "int", Round((A_ScreenWidth - width) / 2)   ; x coordinate
               , "int", Round((A_ScreenHeight - height) / 2) ; y coordinate
               , "int", width, "int", height, "uint", 0)

      ; Get device context of spawned window.
      ddc := DllCall("GetDCEx", "ptr", WorkerW, "ptr", 0, "int", 0x403, "ptr")

      ; Copies a portion of the screen to a new device context.
      DllCall("gdi32\BitBlt"
               , "ptr", ddc, "int", 0, "int", 0, "int", width, "int", height
               , "ptr", hdc, "int", 0, "int", 0, "uint", 0x00CC0020) ; SRCCOPY

      ; Release device context of spawned window.
      DllCall("ReleaseDC", "ptr", 0, "ptr", ddc)

      ; Cleanup the hBitmap and device contexts.
      DllCall("SelectObject", "ptr", hdc, "ptr", obm)
      DllCall("DeleteObject", "ptr", hbm)
      DllCall("DeleteDC",     "ptr", hdc)

      return "desktop"
   }

   static put_wallpaper(ByRef pBitmap) {
      filepath := this.put_file(pBitmap)
      size := DllCall("GetFullPathName", "str", filepath, "uint", 0, "ptr", 0, "ptr", 0, "uint")
      VarSetStrCapacity(buf, size)
      DllCall("GetFullPathName", "str", filepath, "uint", size, "str", buf, "ptr", 0, "uint")
      DllCall("SystemParametersInfo", "uint", 20, "uint", 0, "str", buf, "uint", 2)
      Sleep 1 ; Needed as there is some lag.
      FileDelete filepath
      return "wallpaper"
   }

   static put_cursor(ByRef pBitmap, xHotspot := "", yHotspot := "") {
      ; Thanks Nick - https://stackoverflow.com/a/550965

      ; Creates an icon that can be used as a cursor.
      DllCall("gdiplus\GdipCreateHICONFromBitmap", "ptr", pBitmap, "ptr*", hIcon:=0)

      ; Sets the hotspot of the cursor by changing the icon into a cursor.
      if (xHotspot != "" || yHotspot != "") {
         ; struct ICONINFO - https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-iconinfo
         ii := BufferAlloc(8+3*A_PtrSize, 0)                          ; sizeof(ICONINFO) = 20, 32
         DllCall("GetIconInfo", "ptr", hIcon, "ptr", ii)              ; Fill the ICONINFO structure.
            , NumPut("uint", false, ii, 0)                            ; true/false are icon/cursor respectively.
            , (xHotspot != "") ? NumPut("uint", xHotspot, ii, 4) : "" ; Set the xHotspot value. (Default: center point)
            , (yHotspot != "") ? NumPut("uint", yHotspot, ii, 8) : "" ; Set the yHotspot value. (Default: center point)
         DllCall("DestroyIcon", "ptr", hIcon)                         ; Destroy the icon after getting the ICONINFO structure.
         hIcon := DllCall("CreateIconIndirect", "ptr", ii, "ptr")     ; Create a new cursor using ICONINFO.

         ; Clean up hbmMask and hbmColor created as a result of GetIconInfo.
         DllCall("DeleteObject", "ptr", NumGet(ii, 8+A_PtrSize, "ptr"))   ; hbmMask
         DllCall("DeleteObject", "ptr", NumGet(ii, 8+2*A_PtrSize, "ptr")) ; hbmColor
      }

      ; Loop over all 16 system cursors and change them all to the new cursor.
      SystemCursors := "32512,32513,32514,32515,32516,32640,32641,32642,32643,32644,32645,32646,32648,32649,32650,32651"
      Loop Parse, SystemCursors, ","
      { ; Must copy the handle 16 times as SetSystemCursor deletes the handle 16 times.
         hCursor := DllCall("CopyImage", "ptr", hIcon, "uint", 2, "int", 0, "int", 0, "uint", 0, "ptr")
         DllCall("SetSystemCursor", "ptr", hCursor, "int", A_LoopField) ; calls DestroyCursor
      }

      ; Destroy the original hIcon. DestroyCursor and DestroyIcon are the same function in C.
      DllCall("DestroyCursor", "ptr", hIcon)

      ; Returns the word A_Cursor so that it doesn't evaluate immediately.
      return "A_Cursor"
   }

   static put_file(ByRef pBitmap, filepath := "", quality := "") {
      ; Thanks tic - https://www.autohotkey.com/boards/viewtopic.php?t=6517

      ; Seperate the filepath and default the extension to PNG.
      SplitPath filepath,, directory, extension, filename
      filename := (filename != "") ? filename : "___date___"
      extension := (extension ~= "^(?i:bmp|dib|rle|jpg|jpeg|jpe|jfif|gif|tif|tiff|png)$") ? extension : "png"
      filepath := directory . filename "." extension

      ; Fill a buffer with the available encoders.
      DllCall("gdiplus\GdipGetImageEncodersSize", "uint*", count:=0, "uint*", size:=0)
      ci := BufferAlloc(size)
      DllCall("gdiplus\GdipGetImageEncoders", "uint", count, "uint", size, "ptr", ci)
      if !(count && size)
         throw Exception("Could not get a list of image codec encoders on this system.")

      ; Search for an encoder with a matching extension.
      Loop count
         EncoderExtensions := StrGet(NumGet(ci, (idx:=(48+7*A_PtrSize)*(A_Index-1))+32+3*A_PtrSize, "uptr"), "UTF-16")
      until InStr(EncoderExtensions, "*." extension)

      ; Get the pointer to the index/offset of the matching encoder.
      if !(pCodec := ci.ptr + idx)
         throw Exception("Could not find a matching encoder for the specified file format.")

      ; JPEG is a lossy image format that requires a quality value from 0-100. Default quality is 75.
      if (extension ~= "^(?i:jpg|jpeg|jpe|jfif)$"
      && IsInteger(quality) && 0 <= quality && quality <= 100 && quality != 75) {
         DllCall("gdiplus\GdipGetEncoderParameterListSize", "ptr", pBitmap, "ptr", pCodec, "uint*", size:=0)
         EncoderParameters := BufferAlloc(size, 0)
         DllCall("gdiplus\GdipGetEncoderParameterList", "ptr", pBitmap, "ptr", pCodec, "uint", size, "ptr", EncoderParameters)

         ; Search for an encoder parameter with 1 value of type 6.
         Loop NumGet(EncoderParameters, "uint")
            elem := (24+A_PtrSize)*(A_Index-1) + A_PtrSize
         until (NumGet(EncoderParameters, elem+16, "uint") = 1) && (NumGet(EncoderParameters, elem+20, "uint") = 6)

         ; struct EncoderParameter - http://www.jose.it-berater.org/gdiplus/reference/structures/encoderparameter.htm
         ep := EncoderParameters.ptr + elem - A_PtrSize                  ; sizeof(EncoderParameter) = 28, 32
            , NumPut(  "uptr",       1, ep)                              ; Must be 1.
            , NumPut(  "uint",       4, ep, 20+A_PtrSize)                ; Type
            , NumPut(  "uint", quality, NumGet(ep+24+A_PtrSize, "uptr")) ; Value (pointer)
      }

      ; Write the file to disk using the specified encoder and encoding parameters.
      Loop 6 ; Try this 6 times.
         if (A_Index > 1)
            Sleep 30
      until (result := !DllCall("gdiplus\GdipSaveImageToFile", "ptr", pBitmap, "wstr", filepath, "ptr", pCodec, "uint", IsSet(ep) ? ep : 0))
      if !(result)
         throw Exception("Could not save file to disk.")

      ; If the filename was omitted, replace it with the current time (accurate to the second).
      ; Multiple files that are created within 1 second will be overwritten with the last file.
      ; The replacement colon is called a Modifier Letter Colon found at <U+A789>.
      if (filename == "___date___")
         FileMove(filepath, directory . FormatTime(FileGetTime(filepath), "yyyy-MM-dd HH꞉mm꞉ss") "." extension, true)




      return filepath
   }

   static put_stream(ByRef pBitmap, extension := "", quality := "") {
      ; Default extension is BMP for fast speeds!
      if !(extension ~= "^(?i:bmp|dib|rle|jpg|jpeg|jpe|jfif|gif|tif|tiff|png)$")
         extension := "bmp"

      ; Fill a buffer with the available encoders.
      DllCall("gdiplus\GdipGetImageEncodersSize", "uint*", count:=0, "uint*", size:=0)
      ci := BufferAlloc(size)
      DllCall("gdiplus\GdipGetImageEncoders", "uint", count, "uint", size, "ptr", ci)
      if !(count && size)
         throw Exception("Could not get a list of image codec encoders on this system.")

      ; Search for an encoder with a matching extension.
      Loop count
         EncoderExtensions := StrGet(NumGet(ci, (idx:=(48+7*A_PtrSize)*(A_Index-1))+32+3*A_PtrSize, "uptr"), "UTF-16")
      until InStr(EncoderExtensions, "*." extension)

      ; Get the pointer to the index/offset of the matching encoder.
      if !(pCodec := ci.ptr + idx)
         throw Exception("Could not find a matching encoder for the specified file format.")

      ; JPEG is a lossy image format that requires a quality value from 0-100. Default quality is 75.
      if (extension ~= "^(?i:jpg|jpeg|jpe|jfif)$"
      && IsInteger(quality) && 0 <= quality && quality <= 100 && quality != 75) {
         DllCall("gdiplus\GdipGetEncoderParameterListSize", "ptr", pBitmap, "ptr", pCodec, "uint*", size:=0)
         EncoderParameters := BufferAlloc(size, 0)
         DllCall("gdiplus\GdipGetEncoderParameterList", "ptr", pBitmap, "ptr", pCodec, "uint", size, "ptr", EncoderParameters)

         ; Search for an encoder parameter with 1 value of type 6.
         Loop NumGet(EncoderParameters, "uint")
            elem := (24+A_PtrSize)*(A_Index-1) + A_PtrSize
         until (NumGet(EncoderParameters, elem+16, "uint") = 1) && (NumGet(EncoderParameters, elem+20, "uint") = 6)

         ; struct EncoderParameter - http://www.jose.it-berater.org/gdiplus/reference/structures/encoderparameter.htm
         ep := EncoderParameters.ptr + elem - A_PtrSize                  ; sizeof(EncoderParameter) = 28, 32
            , NumPut(  "uptr",       1, ep)                              ; Must be 1.
            , NumPut(  "uint",       4, ep, 20+A_PtrSize)                ; Type
            , NumPut(  "uint", quality, NumGet(ep+24+A_PtrSize, "uptr")) ; Value (pointer)
      }

      ; Create a Stream.
      DllCall("ole32\CreateStreamOnHGlobal", "ptr", 0, "int", true, "ptr*", pStream:=0)
      DllCall("gdiplus\GdipSaveImageToStream", "ptr", pBitmap, "ptr", pStream, "ptr", pCodec, "uint", IsSet(ep) ? ep : 0)

      return pStream
   }

   static put_randomAccessStream(ByRef pBitmap, extension := "", quality := "") {
      ; Thanks teadrinker - https://www.autohotkey.com/boards/viewtopic.php?f=6&t=72674

      ; Which is faster, bmp or png?
      pStream := this.put_stream(pBitmap, extension, quality)

      ; Get the Class ID from a GUID string.
      CLSID := BufferAlloc(16, 0)
      if res := DllCall("ole32\CLSIDFromString", "wstr", "{905A0FE1-BC53-11DF-8C49-001E4FC686DA}", "ptr", CLSID, "uint")
         throw Exception("CLSIDFromString failed. Error: " . Format("{:#x}", res))

      ; Create a RandomAccessStream
      DllCall("ShCore\CreateRandomAccessStreamOverStream", "ptr", pStream, "uint", 1, "ptr", CLSID, "ptr*", pRandomAccessStream:=0, "uint")

      ; The handle to the stream object is automatically freed when the stream object is released.
      ObjRelease(pStream)

      return pRandomAccessStream
   }

   static put_hBitmap(ByRef pBitmap, alpha := "") {
      ; Revert to built in functionality if a replacement color is declared.
      if (alpha != "") { ; This built-in version is about 25% slower.
         DllCall("gdiplus\GdipCreateHBITMAPFromBitmap", "ptr", pBitmap, "ptr*", hBitmap:=0, "uint", alpha)
         return hBitmap
      }

      ; Get Bitmap width and height.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0)

      ; 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 := BufferAlloc(40, 0)                 ; sizeof(bi) = 40
         , NumPut(  "uint",        40, bi,  0) ; Size
         , NumPut(   "int",     width, bi,  4) ; Width
         , NumPut(   "int",   -height, 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")

      ; Transfer data from source pBitmap to an hBitmap manually.
      Rect := BufferAlloc(16, 0)               ; sizeof(Rect) = 16
         , NumPut(  "uint",   width, Rect,  8) ; Width
         , NumPut(  "uint",  height, Rect, 12) ; Height
      BitmapData := BufferAlloc(16+2*A_PtrSize, 0)      ; sizeof(BitmapData) = 24, 32
         , NumPut(  "uint",      width, BitmapData,  0) ; Width
         , NumPut(  "uint",     height, BitmapData,  4) ; Height
         , NumPut(   "int",  4 * width, BitmapData,  8) ; Stride
         , NumPut(   "int",    0xE200B, BitmapData, 12) ; PixelFormat
         , 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)

      ; Cleanup the hBitmap and device contexts.
      DllCall("SelectObject", "ptr", hdc, "ptr", obm)
      DllCall("DeleteDC",     "ptr", hdc)

      return hbm
   }

   static put_base64(ByRef pBitmap, extension := "", quality := "") {
      ; Thanks noname - https://www.autohotkey.com/boards/viewtopic.php?style=7&p=144247#p144247

      ; Default extension is PNG for small sizes!
      if !(extension ~= "^(?i:bmp|dib|rle|jpg|jpeg|jpe|jfif|gif|tif|tiff|png)$")
         extension := "png"

      pStream := this.put_stream(pBitmap, extension, quality)

      DllCall("ole32\GetHGlobalFromStream", "ptr", pStream, "uint*", hData:=0)
      pData := DllCall("GlobalLock", "ptr", hData, "ptr")
      nSize := DllCall("GlobalSize", "uint", pData)

      bin := BufferAlloc(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.
      base64Length := 0
      DllCall("Crypt32.dll\CryptBinaryToStringA", "ptr", bin, "uint", nSize, "uint", 0x40000001, "ptr", 0, "uint*", base64Length)
      base64 := BufferAlloc(base64Length, 0)
      DllCall("Crypt32.dll\CryptBinaryToStringA", "ptr", bin, "uint", nSize, "uint", 0x40000001, "ptr", base64, "uint*", base64Length)
      VarSetStrCapacity(bin, 0)

      return StrGet(base64, base64Length, "CP0")
   }

   ; All references to gdiplus and pToken must be absolute!
   static gdiplus := 0, pToken := 0

   static gdiplusStartup() {
      ImagePut.gdiplus++

      ; Startup gdiplus when counter goes from 0 -> 1.
      if (ImagePut.gdiplus == 1) {
         DllCall("LoadLibrary", "str", "gdiplus")
         si := BufferAlloc(A_PtrSize = 8 ? 24 : 16, 0) ; sizeof(GdiplusStartupInput) = 16, 24
            , NumPut("uint", 0x1, si)
         DllCall("gdiplus\GdiplusStartup", "ptr*", pToken:=0, "ptr", si, "ptr", 0)
         ImagePut.pToken := pToken
      }
   }

   static gdiplusShutdown(cotype := "", ByRef pBitmap := "") {
      ImagePut.gdiplus--

      ; When a buffer object is deleted a bitmap is sent here for disposal.
      if (cotype == "smart_pointer")
         if DllCall("gdiplus\GdipDisposeImage", "ptr", pBitmap)
            throw Exception("The bitmap of this buffer object has already been deleted.")

      ; Check for unpaired calls of gdiplusShutdown. 
      if (ImagePut.gdiplus < 0)
         throw Exception("Missing ImagePut.gdiplusStartup().")

      ; Shutdown gdiplus when counter goes from 1 -> 0.
      if (ImagePut.gdiplus == 0) {
         DllCall("gdiplus\GdiplusShutdown", "ptr", ImagePut.pToken)
         DllCall("FreeLibrary", "ptr", DllCall("GetModuleHandle", "str", "gdiplus", "ptr"))

         ; Exit if GDI+ is still loaded. GdiplusNotInitialized = 18
         if (18 != DllCall("gdiplus\GdipCreateImageAttributes", "ptr*", ImageAttr:=0)) {
            DllCall("gdiplus\GdipDisposeImageAttributes", "ptr", ImageAttr)
            return
         }

         ; Otherwise GDI+ has been truly unloaded from the script and objects are out of scope.
         if (cotype = "bitmap")
            throw Exception("Out of scope error. `n`nIf you wish to handle raw pointers to GDI+ bitmaps, add the line"
               . "`n`n`t`t" this.prototype.__class ".gdiplusStartup()`n`nor 'pToken := Gdip_Startup()' to the top of your script."
               . "`nAlternatively, use 'obj := ImagePutBuffer()' with 'obj.pBitmap'."
               . "`nYou can copy this message by pressing Ctrl + C.")
      }
   }
} ; End of ImagePut class.
  • Pass ClipboardAll() to read from the clipboard. Ex. ImagePutFile(ClipboardAll())
  • Clipboard now supports transparency by using a PNG stream!
  • Some routines will now happen 6 times before throwing an error.

iseahound
Posts: 608
Joined: 13 Aug 2016, 21:04
GitHub: iseahound

Re: [a119] ImagePut - Puts an image from anywhere to anywhere (convert, crop, scale)

Post by iseahound » 01 Aug 2020, 16:46

a119 compatible

Code: Select all

; Script:    ImagePut.ahk
; Author:    iseahound
; License:   MIT License
; Version:   2020-05-22
; Release:   2020-08-01

; ImagePut - Puts an image from anywhere to anywhere.
; This is a simple functor designed to be intuitive.
; I hope people find this reference library useful.


; Puts the image into a file format and returns a base64 encoded string.
;   extension  -  File Encoding           |  string   ->   bmp, gif, jpg, png, tiff
;   quality    -  JPEG Quality Level      |  integer  ->   0 - 100
ImagePutBase64(ByRef image, extension := "", quality := "")
   => ImagePut("base64", image,,, extension, quality)


; Puts the image into a GDI+ Bitmap and returns a pointer.
ImagePutBitmap(ByRef image)
   => ImagePut("bitmap", image)


; Puts the image into a GDI+ Bitmap and returns a buffer object with GDI+ scope.
ImagePutBuffer(ByRef image)
   => ImagePut("buffer", image)


; Puts the image onto the clipboard and returns ClipboardAll().
ImagePutClipboard(ByRef image)
   => ImagePut("clipboard", image)


; Puts the image as the cursor and returns the variable A_Cursor.
;   xHotspot   -  X Click Point           |  pixel    ->   0 - width
;   yHotspot   -  Y Click Point           |  pixel    ->   0 - height
ImagePutCursor(ByRef image, xHotspot := "", yHotspot := "")
   => ImagePut("cursor", image,,, xHotspot, yHotspot)


; Puts the image behind the desktop icons and returns the string "desktop".
;   scale      -  Scale Factor            |  real     ->   A_ScreenHeight / height.
ImagePutDesktop(ByRef image, scale := 1)
   => ImagePut("desktop", image,, scale)


; Puts the image into a file and returns a relative filepath.
;   filepath   -  Filepath + Extension    |  string   ->   *.bmp, *.gif, *.jpg, *.png, *.tiff
;   quality    -  JPEG Quality Level      |  integer  ->   0 - 100
ImagePutFile(ByRef image, filepath := "", quality := "")
   => ImagePut("file", image,,, filepath, quality)


; Puts the image into a device independent bitmap and returns the handle.
;   alpha      -  Alpha Replacement Color |  RGB      ->   0xFFFFFF
ImagePutHBitmap(ByRef image, alpha := "")
   => ImagePut("hBitmap", image,,, alpha)


; Puts the image into an icon and returns the handle.
ImagePutHIcon(ByRef image)
   => ImagePut("hBitmap", image)


; Puts the image into a file format and returns a pointer to a RandomAccessStream.
;   extension  -  File Encoding           |  string   ->   bmp, gif, jpg, png, tiff
;   quality    -  JPEG Quality Level      |  integer  ->   0 - 100
ImagePutRandomAccessStream(ByRef image, extension := "", quality := "")
   => ImagePut("RandomAccessStream", image,,, extension, quality)


; Puts the image on the shared screen device context and returns an array of coordinates.
;   screenshot -  Screen Coordinates      |  array    ->   [x,y,w,h] or [0,0]
;   alpha      -  Alpha Replacement Color |  RGB      ->   0xFFFFFF
ImagePutScreenshot(ByRef image, screenshot := "", alpha := "")
   => ImagePut("screenshot", image,,, screenshot, alpha)


; Puts the image into a file format and returns a pointer to a stream.
;   extension  -  File Encoding           |  string   ->   bmp, gif, jpg, png, tiff
;   quality    -  JPEG Quality Level      |  integer  ->   0 - 100
ImagePutStream(ByRef image, extension := "", quality := "")
   => ImagePut("stream", image,,, extension, quality)


; Puts the image as the desktop wallpaper and returns the string "wallpaper".
ImagePutWallpaper(ByRef image)
   => ImagePut("wallpaper", image)



; ImagePut() - Puts an image from anywhere to anywhere.
;   cotype     -  Output Type             |  string   ->   Case Insensitive. Read documentation.
;   image      -  Input Image             |  image    ->   Anything. Refer to ImageType().
;   crop       -  Crop Coordinates        |  array    ->   [x,y,w,h] could be negative or percent.
;   scale      -  Scale Factor            |  real     ->   2.0
;   terms*     -  Additional Parameters   |  variadic ->   Extra parameters found in toCotype().
ImagePut(cotype, ByRef image, crop := "", scale := "", terms*)
   => ImagePut.call(cotype, image, crop, scale, terms*)



class ImagePut {

   static call(cotype, ByRef image, crop := "", scale := "", terms*) {

      this.gdiplusStartup()

      ; Take a guess as to what the image might be. (>90% accuracy!)
      try type := this.DontVerifyImageType(image)
      catch
         type := this.ImageType(image)

      ; Qualify additional parameters for correctness.
      _crop := IsObject(crop)
         && crop[1] ~= "^-?\d+(\.\d*)?%?$" && crop[2] ~= "^-?\d+(\.\d*)?%?$"
         && crop[3] ~= "^-?\d+(\.\d*)?%?$" && crop[4] ~= "^-?\d+(\.\d*)?%?$"
      _scale := scale != 1 && scale ~= "^\d+(\.\d+)?$"

      ; Make a copy of the image as a pBitmap.
      pBitmap := this.toBitmap(type, image)

      ; Crop the image.
      if (_crop) {
         pBitmap2 := this.BitmapCrop(pBitmap, crop)
         DllCall("gdiplus\GdipDisposeImage", "ptr", pBitmap)
         pBitmap := pBitmap2
      }

      ; Scale the image.
      if (_scale) {
         pBitmap2 := this.BitmapScale(pBitmap, scale)
         DllCall("gdiplus\GdipDisposeImage", "ptr", pBitmap)
         pBitmap := pBitmap2
      }

      ; Put the pBitmap to wherever the cotype specifies.
      coimage := this.toCotype(cotype, pBitmap, terms*)

      ; Clean up the pBitmap copy. Export raw pointers if requested.
      if !(cotype = "bitmap" || cotype = "buffer")
         DllCall("gdiplus\GdipDisposeImage", "ptr", pBitmap)

      this.gdiplusShutdown(cotype)

      return coimage
   }

   static DontVerifyImageType(ByRef image) {

      if !IsObject(image)
         throw Exception("Must be an object.")

      ; Check for image type declarations.
      ; Assumes that the user is telling the truth.

      if ObjHasOwnProp(image, "clipboard") {
         image := image.clipboard
         return "clipboard"
      }

      if ObjHasOwnProp(image, "object") {
         image := image.object
         return "object"
      }

      if ObjHasOwnProp(image, "buffer") {
         image := image.buffer
         return "buffer"
      }

      if ObjHasOwnProp(image, "screenshot") {
         image := image.screenshot
         return "screenshot"
      }

      if ObjHasOwnProp(image, "window") {
         image := image.window
         return "window"
      }

      if ObjHasOwnProp(image, "desktop") {
         image := image.desktop
         return "desktop"
      }

      if ObjHasOwnProp(image, "wallpaper") {
         image := image.wallpaper
         return "wallpaper"
      }

      if ObjHasOwnProp(image, "cursor") {
         image := image.cursor
         return "cursor"
      }

      if ObjHasOwnProp(image, "url") {
         image := image.url
         return "url"
      }

      if ObjHasOwnProp(image, "file") {
         image := image.file
         return "file"
      }

      if ObjHasOwnProp(image, "monitor") {
         image := image.monitor
         return "monitor"
      }

      if ObjHasOwnProp(image, "hBitmap") {
         image := image.hBitmap
         return "hBitmap"
      }

      if ObjHasOwnProp(image, "hIcon") {
         image := image.hIcon
         return "hIcon"
      }

      if ObjHasOwnProp(image, "bitmap") {
         image := image.bitmap
         return "bitmap"
      }

      if ObjHasOwnProp(image, "stream") {
         image := image.stream
         return "stream"
      }

      if ObjHasOwnProp(image, "RandomAccessStream") {
         image := image.RandomAccessStream
         return "RandomAccessStream"
      }

      if ObjHasOwnProp(image, "base64") {
         image := image.base64
         return "base64"
      }

      if ObjHasOwnProp(image, "sprite") {
         image := image.sprite
         return "sprite"
      }

      throw Exception("Invalid type.")
   }

   static ImageType(ByRef image) {
      ; Throw if the image is an empty string.
      if (image == "")
         throw Exception("Image data is an empty string.")

      if IsObject(image) {
         ; A "clipboard" is a buffer object containing binary data returned by ClipboardAll()
         if (image.base.HasOwnProp("__class") && image.base.__class == "ClipboardAll")
            return "clipboard"

         ; An "object" is an object that implements a Bitmap() method returning a pointer to a GDI+ bitmap.
         if image.HasOwnMethod("Bitmap")
            return "object"

         ; A "buffer" is an AutoHotkey v2 buffer object.
         if image.HasOwnProp("pBitmap")
            return "buffer"

         ; A "screenshot" is an array of 4 numbers.
         if (image[1] ~= "^-?\d+$" && image[2] ~= "^-?\d+$" && image[3] ~= "^-?\d+$" && image[4] ~= "^-?\d+$")
            return "screenshot"
      }
         ; A "window" is anything considered a Window Title including ahk_class and "A".
         if WinExist(image)
            return "window"

         ; A "desktop" is a hidden window behind the desktop icons created by ImagePutDesktop.
         if (image = "desktop")
            return "desktop"

         ; A "wallpaper" is the desktop wallpaper.
         if (image = "wallpaper")
            return "wallpaper"

         ; A "cursor" is the name of a known cursor name.
         if (image ~= "(?i)^(IDC|OCR)?_?(A_Cursor|AppStarting|Arrow|Cross|Help|IBeam|"
         . "Icon|No|Size|SizeAll|SizeNESW|SizeNS|SizeNWSE|SizeWE|UpArrow|Wait|Unknown)$")
            return "cursor"

         ; A "url" satisfies the url format.
         if this.is_url(image)
            return "url"

         ; A "file" is stored on the disk or network.
         if FileExist(image)
            return "file"

      if IsInteger(image) {
         ; A non-zero "monitor" number identifies each display uniquely; and 0 refers to the entire virtual screen.
         if (image >= 0 && image <= MonitorGetCount())
            return "monitor"

         ; An "hBitmap" is a handle to a GDI Bitmap.
         if (DllCall("GetObjectType", "ptr", image) == 7)
            return "hBitmap"

         ; An "hIcon" is a handle to a GDI icon.
         if DllCall("DestroyIcon", "ptr", DllCall("CopyIcon", "ptr", image, "ptr"))
            return "hIcon"

         ; A "bitmap" is a pointer to a GDI+ Bitmap.
         try if !DllCall("gdiplus\GdipGetImageType", "ptr", image, "ptr*", type:=0) && (type == 1)
            return "bitmap"

         ; A "stream" is a pointer to the IStream interface.
         try if ComObjQuery(image, "{0000000C-0000-0000-C000-000000000046}") {
            ObjRelease(image)
            return "stream"
         }

         ; A "RandomAccessStream" is a pointer to the IRandomAccessStream interface.
         try if ComObjQuery(image, "{905A0FE1-BC53-11DF-8C49-001E4FC686DA}") {
            ObjRelease(image)
            return "RandomAccessStream"
         }
      }
         ; A "base64" string is binary image data encoded into text using only 64 characters.
         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"

      for extension in ["bmp","dib","rle","jpg","jpeg","jpe","jfif","gif","tif","tiff","png","ico","exe","dll"]
         if FileExist(image "." extension)
            throw Exception("A ." extension " file extension is required!")

      throw Exception("Image type could not be identified.")
   }

   static toBitmap(type, ByRef image) {

      if (type = "clipboard")
         return this.from_clipboard()

      if (type = "object")                      ; Special
         return image.Bitmap()

      if (type = "buffer")
         return this.from_bitmap(image.pBitmap)

      if (type = "screenshot")
         return this.from_screenshot(image)

      if (type = "window")
         return this.from_window(image)

      if (type = "desktop")
         return this.from_desktop()

      if (type = "wallpaper")
         return this.from_wallpaper()

      if (type = "cursor")
         return this.from_cursor()

      if (type = "url")
         return this.from_url(image)

      if (type = "file")
         return this.from_file(image)

      if (type = "monitor")
         return this.from_monitor(image)

      if (type = "hBitmap")
         return this.from_hBitmap(image)

      if (type = "hIcon")
         return this.from_hIcon(image)

      if (type = "bitmap")
         return this.from_bitmap(image)

      if (type = "stream")
         return this.from_stream(image)

      if (type = "RandomAccessStream")
         return this.from_RandomAccessStream(image)

      if (type = "base64")
         return this.from_base64(image)

      if (type = "sprite")
         return this.from_sprite(image)

      throw Exception("Conversion from type " type " is not supported.")
   }

   static toCotype(cotype, ByRef pBitmap, term1 := "", term2 := "", *) {
      ; toCotype("clipboard", pBitmap)
      if (cotype = "clipboard")
         return this.put_clipboard(pBitmap)

      ; toCotype("buffer", pBitmap)
      if (cotype = "buffer")
         return this.put_buffer(pBitmap)

      ; toCotype("screenshot", pBitmap, screenshot, alpha)
      if (cotype = "screenshot")
         return this.put_screenshot(pBitmap, term1, term2)

      ; toCotype("window", pBitmap)
      if (cotype = "window")
         return "ahk_id " . this.Render({bitmap:pBitmap}).AlwaysOnTop().ToolWindow().Caption().hwnd

      ; toCotype("desktop", pBitmap)
      if (cotype = "desktop")
         return this.put_desktop(pBitmap)

      ; toCotype("wallpaper", pBitmap)
      if (cotype = "wallpaper")
         return this.put_wallpaper(pBitmap)

      ; toCotype("cursor", pBitmap, xHotspot, yHotspot)
      if (cotype = "cursor")
         return this.put_cursor(pBitmap, term1, term2)

      ; toCotype("url", pBitmap)
      if (cotype = "url")
         return this.put_url(pBitmap)

      ; toCotype("file", pBitmap, filename, quality)
      if (cotype = "file")
         return this.put_file(pBitmap, term1, term2)

      ; toCotype("hBitmap", pBitmap, alpha)
      if (cotype = "hBitmap")
         return this.put_hBitmap(pBitmap, term1)

      ; toCotype("hIcon", pBitmap)
      if (cotype = "hIcon")
         return this.put_hIcon(pBitmap)

      ; toCotype("bitmap", pBitmap)
      if (cotype = "bitmap")
         return pBitmap

      ; toCotype("stream", pBitmap, extension, quality)
      if (cotype = "stream")
         return this.put_stream(pBitmap, term1, term2)

      ; toCotype("RandomAccessStream", pBitmap, extension, quality)
      if (cotype = "RandomAccessStream")
         return this.put_RandomAccessStream(pBitmap, term1, term2)

      ; toCotype("base64", pBitmap, extension, quality)
      if (cotype = "base64")
         return this.put_base64(pBitmap, term1, term2)

      throw Exception("Conversion to type " cotype " is not supported.")
   }

   static DisposeImage(ByRef pBitmap) {
      return DllCall("gdiplus\GdipDisposeImage", "ptr", pBitmap)
   }

   static BitmapCrop(ByRef pBitmap, crop) {
      ; Get Bitmap width, height, and format.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0)
      DllCall("gdiplus\GdipGetImagePixelFormat", "ptr", pBitmap, "uint*", format:=0)

      ; Are the numbers percentages?
      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]
      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]

      ; If numbers are negative, subtract the values from the edge.
      crop[3] := (crop[3] < 0) ?  width - Abs(crop[3]) - Abs(crop[1]) : crop[3]
      crop[4] := (crop[4] < 0) ? height - Abs(crop[4]) - Abs(crop[2]) : crop[4]
      crop[1] := Abs(crop[1])
      crop[2] := Abs(crop[2])

      ; Round to the nearest integer.
      crop[3] := Round(crop[1] + crop[3]) - Round(crop[1]) ; A reminder that width and height
      crop[4] := Round(crop[2] + crop[4]) - Round(crop[2]) ; are distances, not coordinates.
      crop[1] := Round(crop[1]) ; so the abstract concept of a distance must be resolved
      crop[2] := Round(crop[2]) ; into coordinates and then rounded and added up again.

      ; Variance Shift. Now place x,y before w,h because we are building abstracts from reals now.
      ; Before we were resolving abstracts into real coordinates, now it's the opposite.

      ; Ensure that coordinates can never exceed the expected Bitmap area.
      safe_x := (crop[1] > width) ? 0 : crop[1]                          ; Zero x if bigger.
      safe_y := (crop[2] > height) ? 0 : crop[2]                         ; Zero y if bigger.
      safe_w := (crop[1] + crop[3] > width) ? width - safe_x : crop[3]   ; Max w if bigger.
      safe_h := (crop[2] + crop[4] > height) ? height - safe_y : crop[4] ; Max h if bigger.

      ; Clone
      DllCall("gdiplus\GdipCloneBitmapAreaI"
               ,    "int", safe_x
               ,    "int", safe_y
               ,    "int", safe_w
               ,    "int", safe_h
               ,    "int", format
               ,    "ptr", pBitmap
               ,   "ptr*", pBitmapCrop:=0)

      return pBitmapCrop
   }

   static BitmapScale(ByRef pBitmap, scale) {
      ; Get Bitmap width, height, and format.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0)
      DllCall("gdiplus\GdipGetImagePixelFormat", "ptr", pBitmap, "uint*", format:=0)

      safe_w := Ceil(width * scale)
      safe_h := Ceil(height * scale)

      ; Create a new bitmap and get the graphics context.
      DllCall("gdiplus\GdipCreateBitmapFromScan0"
               , "int", safe_w, "int", safe_h, "int", 0, "int", format, "ptr", 0, "ptr*", pBitmapScale:=0)
      DllCall("gdiplus\GdipGetImageGraphicsContext", "ptr", pBitmapScale, "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", safe_w, "int", safe_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)
      return pBitmapScale
   }

   static is_url(url) {
      ; Thanks splattermania - https://www.php.net/manual/en/function.preg-match.php#93824

      regex := "^(?i)"
         . "((https?|ftp)\:\/\/)" ; SCHEME
         . "([a-z0-9+!*(),;?&=\$_.-]+(\:[a-z0-9+!*(),;?&=\$_.-]+)[email protected])?" ; User and Pass
         . "([a-z0-9-.]*)\.([a-z]{2,3})" ; Host or IP
         . "(\:[0-9]{2,5})?" ; Port
         . "(\/(?:[a-z0-9-_~!$&'()*+,;=:@]\.?)+)*\/?" ; Path
         . "(\?[a-z+&\$_.-][a-z0-9;:@&%=+\/\$_.-]*)?" ; GET Query
         . "(#[a-z_.-][a-z0-9+\$_.-]*)?$" ; Anchor
      return (url ~= regex)
   }

   static from_clipboard() {
      ; Open the clipboard.
      Loop 6 ; Try this 6 times.
         if (A_Index > 1)
            Sleep (2**(A_Index-2) * 30)
      until (result := DllCall("OpenClipboard", "ptr", 0))
      if !(result)
         throw Exception("Clipboard could not be opened.")

      ; Prefer the PNG stream if available considering it supports transparency.
      png := DllCall("RegisterClipboardFormat", "str", "png", "uint")
      if DllCall("IsClipboardFormatAvailable", "uint", png, "int") {
         hData := DllCall("GetClipboardData", "uint", png, "ptr")
         DllCall("ole32\CreateStreamOnHGlobal", "ptr", hData, "int", true, "ptr*", pStream:=0)
         DllCall("gdiplus\GdipCreateBitmapFromStream", "ptr", pStream, "ptr*", pBitmap:=0)
         ObjRelease(pStream)
      }

      ; Fallback to CF_BITMAP.
      else if DllCall("IsClipboardFormatAvailable", "uint", 2, "int") {
         hBitmap := DllCall("GetClipboardData", "uint", 2, "ptr")
         DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", "ptr", hBitmap, "ptr", 0, "ptr*", pBitmap:=0)
         DllCall("DeleteObject", "ptr", hBitmap)
      }

      DllCall("CloseClipboard")
      return pBitmap
   }

   static from_screenshot(ByRef image) {
      ; Thanks tic - https://www.autohotkey.com/boards/viewtopic.php?t=6517

      ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      bi := BufferAlloc(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) ; SRCCOPY

      ; 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
   }

   static from_window(ByRef image) {
      ; Thanks tic - https://www.autohotkey.com/boards/viewtopic.php?t=6517

      ; Get the handle to the window.
      image := WinExist(image)

      ; Restore the window if minimized! Must be visible for capture.
      if DllCall("IsIconic", "ptr", image)
         DllCall("ShowWindow", "ptr", image, "int", 4)

      ; Get the width and height of the client window.
      Rect := BufferAlloc(16) ; sizeof(RECT) = 16
      DllCall("GetClientRect", "ptr", image, "ptr", Rect)
         , width  := NumGet(Rect, 8, "int")
         , height := NumGet(Rect, 12, "int")

      ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      bi := BufferAlloc(40, 0)                 ; sizeof(bi) = 40
         , NumPut(  "uint",        40, bi,  0) ; Size
         , NumPut(   "int",     width, bi,  4) ; Width
         , NumPut(   "int",   -height, 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")

      ; Print the window onto the hBitmap using an undocumented flag. https://stackoverflow.com/a/40042587
      DllCall("PrintWindow", "ptr", image, "ptr", hdc, "uint", 0x3) ; PW_CLIENTONLY | PW_RENDERFULLCONTENT
      ; Additional info on how this is implemented: https://www.reddit.com/r/windows/comments/8ffr56/altprintscreen/

      ; 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
   }

   static from_desktop() {
      ; Find the child window.
      windows := WinGetList("ahk_class WorkerW")
      if (windows.length == 0)
         throw Exception("The hidden desktop window has not been initalized. Call ImagePutDesktop() first.")

      Loop windows.length
         hwnd := windows[A_Index]
      until DllCall("FindWindowEx", "ptr", hwnd, "ptr", 0, "str", "SHELLDLL_DefView", "ptr", 0)

      ; Maybe this hack gets patched. Tough luck!
      if !(WorkerW := DllCall("FindWindowEx", "ptr", 0, "ptr", hwnd, "str", "WorkerW", "ptr", 0, "ptr"))
         throw Exception("Could not locate hidden window behind desktop.")

      ; Get the width and height of the client window.
      Rect := BufferAlloc(16) ; sizeof(RECT) = 16
      DllCall("GetClientRect", "ptr", WorkerW, "ptr", Rect)
         , width  := NumGet(Rect, 8, "int")
         , height := NumGet(Rect, 12, "int")

      ; Get device context of spawned window.
      sdc := DllCall("GetDCEx", "ptr", WorkerW, "ptr", 0, "int", 0x403, "ptr") ; LockWindowUpdate | Cache | Window

      ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      bi := BufferAlloc(40, 0)                 ; sizeof(bi) = 40
         , NumPut(  "uint",        40, bi,  0) ; Size
         , NumPut(   "int",     width, bi,  4) ; Width
         , NumPut(   "int",   -height, 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")

      ; Copies a portion of the hidden window to a new device context.
      DllCall("gdi32\BitBlt"
               , "ptr", hdc, "int", 0, "int", 0, "int", width, "int", height
               , "ptr", sdc, "int", 0, "int", 0, "uint", 0x00CC0020) ; SRCCOPY

      ; 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)

      ; Release device context of spawned window.
      DllCall("ReleaseDC", "ptr", 0, "ptr", sdc)

      return pBitmap
   }

   static from_wallpaper() {
      ; Get the width and height of all monitors.
      width  := DllCall("GetSystemMetrics", "int", 78)
      height := DllCall("GetSystemMetrics", "int", 79)

      ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      bi := BufferAlloc(40, 0)                 ; sizeof(bi) = 40
         , NumPut(  "uint",        40, bi,  0) ; Size
         , NumPut(   "int",     width, bi,  4) ; Width
         , NumPut(   "int",   -height, 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")

      ; Paints the desktop.
      DllCall("PaintDesktop", "ptr", hdc)

      ; 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
   }

   static from_cursor() {
      ; Thanks 23W - https://stackoverflow.com/a/13295280

      ; struct CURSORINFO - https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-cursorinfo
      ci := BufferAlloc(16+A_PtrSize, 0)                 ; sizeof(CURSORINFO) = 20, 24
         , NumPut("int", ci.size, ci)
      DllCall("GetCursorInfo", "ptr", ci)
         ; cShow   := NumGet(ci,  4, "int")              ; 0x1 = CURSOR_SHOWING, 0x2 = CURSOR_SUPPRESSED
         , hCursor := NumGet(ci,  8, "ptr")
         ; xCursor := NumGet(ci,  8+A_PtrSize, "int")
         ; yCursor := NumGet(ci, 12+A_PtrSize, "int")

      ; Cursors are the same as icons!
      pBitmap := this.from_hIcon(hCursor)

      ; Cleanup the handle to the cursor.
      DllCall("DestroyIcon",  "ptr", hCursor)

      return pBitmap
   }

   static from_url(ByRef image) {
      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:=0)
      ObjRelease(pStream)
      return pBitmap
   }

   static from_file(ByRef image) {
      DllCall("gdiplus\GdipCreateBitmapFromFile", "wstr", image, "ptr*", pBitmap:=0)
      return pBitmap
   }

   static from_monitor(ByRef image) {
      if (image > 0) {
         MonitorGet(image, Left, Top, Right, Bottom)
         x := Left
         y := Top
         w := Right - Left
         h := Bottom - Top
      } else {
         x := DllCall("GetSystemMetrics", "int", 76)
         y := DllCall("GetSystemMetrics", "int", 77)
         w := DllCall("GetSystemMetrics", "int", 78)
         h := DllCall("GetSystemMetrics", "int", 79)
      }
      return this.from_screenshot([x,y,w,h])
   }

   static from_hBitmap(ByRef image) {
      ; struct DIBSECTION - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-dibsection
      ; struct BITMAP - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmap
      dib := BufferAlloc(64+5*A_PtrSize) ; sizeof(DIBSECTION) = 84, 104
      DllCall("GetObject", "ptr", image, "int", dib.size, "ptr", dib)
         , width  := NumGet(dib, 4, "uint")
         , height := NumGet(dib, 8, "uint")
         , bpp    := NumGet(dib, 18, "ushort")

      ; Fallback to built-in method if pixels are not 32-bit ARGB.
      if (bpp != 32) { ; This built-in version is 120% faster but ignores transparency.
         DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", "ptr", image, "ptr", 0, "ptr*", pBitmap:=0)
         return pBitmap
      }

      ; Create a handle to a device context and associate the image.
      sdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")           ; Creates a memory DC compatible with the current screen.
      sbm := DllCall("SelectObject", "ptr", sdc, "ptr", image, "ptr") ; Put the (hBitmap) image onto the device context.

      ; Create a device independent bitmap with negative height. All DIBs use the screen pixel format (pARGB).
      ; Use hbm to buffer the image such that top-down and bottom-up images are mapped to this top-down buffer.
      ; pBits is the pointer to (top-down) pixel values. The Scan0 will point to the pBits.
      ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      bi := BufferAlloc(40, 0)                 ; sizeof(bi) = 40
         , NumPut(  "uint",        40, bi,  0) ; Size
         , NumPut(   "int",     width, bi,  4) ; Width
         , NumPut(   "int",   -height, 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")

      ; This is the 32-bit ARGB pBitmap (different from an hBitmap) that will receive the final converted pixels.
      DllCall("gdiplus\GdipCreateBitmapFromScan0"
               , "int", width, "int", height, "int", 0, "int", 0x26200A, "ptr", 0, "ptr*", pBitmap:=0)

      ; Create a Scan0 buffer pointing to pBits. The buffer has pixel format pARGB.
      Rect := BufferAlloc(16, 0)               ; sizeof(Rect) = 16
         , NumPut(  "uint",   width, Rect,  8) ; Width
         , NumPut(  "uint",  height, Rect, 12) ; Height
      BitmapData := BufferAlloc(16+2*A_PtrSize, 0)      ; sizeof(BitmapData) = 24, 32
         , NumPut(  "uint",      width, BitmapData,  0) ; Width
         , NumPut(  "uint",     height, BitmapData,  4) ; Height
         , NumPut(   "int",  4 * width, BitmapData,  8) ; Stride
         , NumPut(   "int",    0xE200B, BitmapData, 12) ; PixelFormat
         , NumPut(   "ptr",      pBits, BitmapData, 16) ; Scan0

      ; Use LockBits to create a writable buffer that converts pARGB to ARGB.
      DllCall("gdiplus\GdipBitmapLockBits"
               ,    "ptr", pBitmap
               ,    "ptr", Rect
               ,   "uint", 6            ; ImageLockMode.UserInputBuffer | ImageLockMode.WriteOnly
               ,    "int", 0xE200B      ; Format32bppPArgb
               ,    "ptr", BitmapData) ; Contains the pointer (pBits) to the hbm.

      ; Copies the image (hBitmap) to a top-down bitmap. Removes bottom-up-ness if present.
      DllCall("gdi32\BitBlt"
               , "ptr", hdc, "int", 0, "int", 0, "int", width, "int", height
               , "ptr", sdc, "int", 0, "int", 0, "uint", 0x00CC0020) ; SRCCOPY

      ; Convert the pARGB pixels copied into the device independent bitmap (hbm) to ARGB.
      DllCall("gdiplus\GdipBitmapUnlockBits", "ptr", pBitmap, "ptr", BitmapData)

      ; Cleanup the hBitmap and device contexts.
      DllCall("SelectObject", "ptr", hdc, "ptr", obm)
      DllCall("DeleteObject", "ptr", hbm)
      DllCall("DeleteDC",     "ptr", hdc)
      DllCall("SelectObject", "ptr", sdc, "ptr", sbm)
      DllCall("DeleteDC",     "ptr", sdc)

      return pBitmap
   }

   static from_hIcon(ByRef image) {
      ; struct ICONINFO - https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-iconinfo
      ii := BufferAlloc(8+3*A_PtrSize, 0)                ; sizeof(ICONINFO) = 20, 32
      DllCall("GetIconInfo", "ptr", image, "ptr", ii)
         ; xHotspot := NumGet(ii, 4, "uint")
         ; yHotspot := NumGet(ii, 8, "uint")
         , hbmMask  := NumGet(ii, 8+A_PtrSize, "ptr")    ; x86:12, x64:16
         , hbmColor := NumGet(ii, 8+2*A_PtrSize, "ptr")  ; x86:16, x64:24

      ; struct BITMAP - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmap
      bm := BufferAlloc(16+2*A_PtrSize) ; sizeof(BITMAP) = 24, 32
      DllCall("GetObject", "ptr", hbmMask, "int", bm.size, "ptr", bm)
         , width  := NumGet(bm, 4, "uint")
         , height := NumGet(bm, 8, "uint") / (hbmColor ? 1 : 2) ; Black and White cursors have doubled height.

      ; Clean up these hBitmaps.
      DllCall("DeleteObject", "ptr", hbmMask)
      DllCall("DeleteObject", "ptr", hbmColor)

      ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      bi := BufferAlloc(40, 0)                 ; sizeof(bi) = 40
         , NumPut(  "uint",        40, bi,  0) ; Size
         , NumPut(   "int",     width, bi,  4) ; Width
         , NumPut(   "int",   -height, 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")

      ; This is the 32-bit ARGB pBitmap (different from an hBitmap) that will receive the final converted pixels.
      DllCall("gdiplus\GdipCreateBitmapFromScan0"
               , "int", width, "int", height, "int", 0, "int", 0x26200A, "ptr", 0, "ptr*", pBitmap:=0)

      ; Create a Scan0 buffer pointing to pBits. The buffer has pixel format pARGB.
      Rect := BufferAlloc(16, 0)               ; sizeof(Rect) = 16
         , NumPut(  "uint",   width, Rect,  8) ; Width
         , NumPut(  "uint",  height, Rect, 12) ; Height
      BitmapData := BufferAlloc(16+2*A_PtrSize, 0)      ; sizeof(BitmapData) = 24, 32
         , NumPut(  "uint",      width, BitmapData,  0) ; Width
         , NumPut(  "uint",     height, BitmapData,  4) ; Height
         , NumPut(   "int",  4 * width, BitmapData,  8) ; Stride
         , NumPut(   "int",    0xE200B, BitmapData, 12) ; PixelFormat
         , NumPut(   "ptr",      pBits, BitmapData, 16) ; Scan0

      ; Use LockBits to create a writable buffer that converts pARGB to ARGB.
      DllCall("gdiplus\GdipBitmapLockBits"
               ,    "ptr", pBitmap
               ,    "ptr", Rect
               ,   "uint", 6            ; ImageLockMode.UserInputBuffer | ImageLockMode.WriteOnly
               ,    "int", 0xE200B      ; Format32bppPArgb
               ,    "ptr", BitmapData)  ; Contains the pointer (pBits) to the hbm.

      ; Don't use DI_DEFAULTSIZE to draw the icon like DrawIcon does as it will resize to 32 x 32.
      DllCall("DrawIconEx"
               , "ptr", hdc,     "int", 0, "int", 0
               , "ptr", image, "int", 0, "int", 0
               , "uint", 0, "ptr", 0, "uint", 0x1 | 0x2 | 0x4) ; DI_MASK | DI_IMAGE | DI_COMPAT

      ; Convert the pARGB pixels copied into the device independent bitmap (hbm) to ARGB.
      DllCall("gdiplus\GdipBitmapUnlockBits", "ptr", pBitmap, "ptr", BitmapData)

      ; Cleanup the hBitmap and device contexts.
      DllCall("SelectObject", "ptr", hdc, "ptr", obm)
      DllCall("DeleteObject", "ptr", hbm)
      DllCall("DeleteDC",     "ptr", hdc)

      return pBitmap
   }

   static from_bitmap(ByRef image) {
      DllCall("gdiplus\GdipCloneImage", "ptr", image, "ptr*", pBitmap:=0)
      return pBitmap
   }

   static from_stream(ByRef image) {
      DllCall("gdiplus\GdipCreateBitmapFromStream", "ptr", image, "ptr*", pBitmap:=0)
      return pBitmap
   }

   static from_RandomAccessStream(ByRef image) {
      ; Get the Class ID from a GUID string.
      CLSID := BufferAlloc(16, 0)
      if result := DllCall("ole32\CLSIDFromString", "wstr", "{0000000C-0000-0000-C000-000000000046}", "ptr", CLSID, "uint")
         throw Exception("CLSIDFromString failed. Error: " . Format("{:#x}", result))

      ; Convert RandomAccessStream to stream.
      DllCall("ShCore\CreateStreamOverRandomAccessStream", "ptr", image, "ptr", CLSID, "ptr*", pStream:=0, "uint")

      ; Read stream to pBitmap.
      DllCall("gdiplus\GdipCreateBitmapFromStream", "ptr", pStream, "ptr*", pBitmap:=0)

      ; Manually free the pointer to an IStream.
      ObjRelease(pStream)

      return pBitmap
   }

   static from_base64(ByRef image) {
      ; Trim whitespace and remove header.
      image := Trim(image)
      image := RegExReplace(image, "^data:image\/[a-z]+;base64,")

      ; Converts the image to binary data by first asking for the size.
      DllCall("crypt32\CryptStringToBinary"
               , "ptr", StrPtr(image), "uint", 0, "uint", 0x1, "ptr",   0, "uint*", size:=0, "ptr", 0, "ptr", 0)
      bin := BufferAlloc(size, 0)
      DllCall("crypt32\CryptStringToBinary"
               , "ptr", StrPtr(image), "uint", 0, "uint", 0x1, "ptr", bin, "uint*", size   , "ptr", 0, "ptr", 0)

      ; Makes a stream for conversion into a pBitmap.
      pStream := DllCall("shlwapi\SHCreateMemStream", "ptr", bin, "uint", size, "ptr")
      DllCall("gdiplus\GdipCreateBitmapFromStream", "ptr", pStream, "ptr*", pBitmap:=0)
      ObjRelease(pStream)

      return pBitmap
   }

   static from_sprite(ByRef image) {
      ; Create a source pBitmap and extract the width and height.
      if DllCall("gdiplus\GdipCreateBitmapFromFile", "wstr", image, "ptr*", sBitmap:=0)
         if !(sBitmap := this.from_url(image))
            throw Exception("Could not be loaded from a valid file path or URL.")

      ; Get Bitmap width and height.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", sBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", sBitmap, "uint*", height:=0)

      ; Create a destination pBitmap in 32-bit ARGB and get its device context though GDI+.
      ; Note that a device context from a graphics context can only be drawn on, not read.
      ; Also note that using a graphics context and blitting does not create a pixel perfect image.
      ; Using a DIB and LockBits is about 5% faster.
      DllCall("gdiplus\GdipCreateBitmapFromScan0"
               , "int", width, "int", height, "int", 0, "int", 0x26200A, "ptr", 0, "ptr*", dBitmap:=0)
      DllCall("gdiplus\GdipGetImageGraphicsContext", "ptr", dBitmap, "ptr*", dGraphics:=0)
      DllCall("gdiplus\GdipGetDC", "ptr", dGraphics, "ptr*", ddc:=0)

      ; Keep any existing transparency for whatever reason.
      hBitmap := this.put_hBitmap(sBitmap) ; Could copy this code here for even more speed.

      ; Create a source device context and associate the source hBitmap.
      sdc := DllCall("CreateCompatibleDC", "ptr", ddc, "ptr")
      obm := DllCall("SelectObject", "ptr", sdc, "ptr", hBitmap, "ptr")

      ; Copy the image making the top-left pixel the color key.
      DllCall("msimg32\TransparentBlt"
               , "ptr", ddc, "int", 0, "int", 0, "int", width, "int", height  ; destination
               , "ptr", sdc, "int", 0, "int", 0, "int", width, "int", height  ; source
               , "uint", DllCall("GetPixel", "ptr", sdc, "int", 0, "int", 0)) ; RGB pixel.

      ; Cleanup the hBitmap and device contexts.
      DllCall("SelectObject", "ptr", sdc, "ptr", obm)
      DllCall("DeleteObject", "ptr", hBitmap)
      DllCall("DeleteDC",     "ptr", sdc)

      ; Release the graphics context and delete.
      DllCall("gdiplus\GdipReleaseDC", "ptr", dGraphics, "ptr", ddc)
      DllCall("gdiplus\GdipDeleteGraphics", "ptr", dGraphics)

      return dBitmap
   }

   static put_clipboard(ByRef pBitmap) {
      ; Standard Clipboard Formats - https://docs.microsoft.com/en-us/windows/win32/dataxchg/standard-clipboard-formats
      ; Synthesized Clipboard Formats - https://docs.microsoft.com/en-us/windows/win32/dataxchg/clipboard-formats

      ; Open the clipboard.
      Loop 6 ; Try this 6 times.
         if (A_Index > 1)
            Sleep (2**(A_Index-2) * 30)
      until (result := DllCall("OpenClipboard", "ptr", 0))
      if !(result)
         throw Exception("Clipboard could not be opened.")

      ; Clear the clipboard.
      DllCall("EmptyClipboard")

      ; #1 - Place the image onto the clipboard as a PNG stream.
      ; Thanks Jochen Arndt - https://www.codeproject.com/Answers/1207927/Saving-an-image-to-the-clipboard#answer3
      pStream := this.put_stream(pBitmap, "png")
      DllCall("ole32\GetHGlobalFromStream", "ptr", pStream, "uint*", hData:=0)
      DllCall("SetClipboardData", "uint", DllCall("RegisterClipboardFormat", "str", "png", "uint"), "ptr", hData)
      ObjRelease(pStream)

      ; #2 - Place the image onto the clipboard in the CF_DIB format in ARGB using 3 color masks. (Extra 12 byte offset.)
      ; Thanks Nyerguds - https://stackoverflow.com/a/46424800

      ; Get Bitmap width, height, and format.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0)
      DllCall("gdiplus\GdipGetImagePixelFormat", "ptr", pBitmap, "uint*", format:=0)

      ; Get Bitmap bits per pixel, stride, and size.
      bpp := (format & 0x00FF00) >> 8
      stride := (bpp >> 3) * width
      size := stride * height

      ; struct DIBSECTION - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-dibsection
      ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
      hdib := DllCall("GlobalAlloc", "uint", 0x42, "uptr", 40 + 12 + size, "ptr")
      pdib := DllCall("GlobalLock", "ptr", hdib, "ptr")
         , NumPut(  "uint",         40, pdib,  0) ; Size
         , NumPut(   "int",      width, pdib,  4) ; Width
         , NumPut(   "int",    -height, pdib,  8) ; Height - Negative so (0, 0) is top-left.
         , NumPut("ushort",          1, pdib, 12) ; Planes
         , NumPut("ushort",        bpp, pdib, 14) ; BitCount / BitsPerPixel
         , NumPut(  "uint",        0x3, pdib, 16) ; Compression
         , NumPut(  "uint",       size, pdib, 20) ; SizeImage (bytes)
         ; The following bitfields when masked extract the respective color channels.
         , NumPut(  "uint", 0x00FF0000, pdib, 40) ; Red
         , NumPut(  "uint", 0x0000FF00, pdib, 44) ; Green
         , NumPut(  "uint", 0x000000FF, pdib, 48) ; Blue

      ; Transfer data from source pBitmap to the global memory manually.
      Rect := BufferAlloc(16, 0)               ; sizeof(Rect) = 16
         , NumPut(  "uint",   width, Rect,  8) ; Width
         , NumPut(  "uint",  height, Rect, 12) ; Height
      BitmapData := BufferAlloc(16+2*A_PtrSize, 0)      ; sizeof(BitmapData) = 24, 32
         , NumPut(  "uint",      width, BitmapData,  0) ; Width
         , NumPut(  "uint",     height, BitmapData,  4) ; Height
         , NumPut(   "int",     stride, BitmapData,  8) ; Stride
         , NumPut(   "int",     format, BitmapData, 12) ; PixelFormat
         , NumPut(   "ptr",  pdib + 52, BitmapData, 16) ; Scan0
      DllCall("gdiplus\GdipBitmapLockBits"
               ,    "ptr", pBitmap
               ,    "ptr", Rect
               ,   "uint", 5            ; ImageLockMode.UserInputBuffer | ImageLockMode.ReadOnly
               ,    "int", 0x26200A     ; Format32bppArgb
               ,    "ptr", BitmapData)  ; Contains the pointer (pdib) to the hData.
      DllCall("gdiplus\GdipBitmapUnlockBits", "ptr", pBitmap, "ptr", BitmapData)

      ; Unlock the memory as it is complete.
      DllCall("GlobalUnlock", "ptr", hdib)

      ; Add CF_DIB as a format to the clipboard.
      DllCall("SetClipboardData", "uint", 8, "ptr", hdib)

      ; Close the clipboard.
      DllCall("CloseClipboard")

      return ClipboardAll()
   }

   static put_buffer(ByRef pBitmap) {
      buffer := {pBitmap: pBitmap}
         .DefineMethod("__New" , (self) => (this.gdiplusStartup(), self)) ; Increment GDI+ reference count
         .DefineMethod("__Delete", (self) => (this.gdiplusShutdown("smart_pointer", self.pBitmap)))
         .__New()  ; On deletion the buffer object will dispose of the bitmap. And it will decrement this.gdiplus.
      return buffer
   }

   static put_screenshot(ByRef pBitmap, screenshot := "", alpha := "") {
      ; Get Bitmap width and height.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0)

      x := (IsObject(screenshot) && screenshot[1] != "") ? screenshot[1] : Round((A_ScreenWidth - width) / 2)
      y := (IsObject(screenshot) && screenshot[2] != "") ? screenshot[2] : Round((A_ScreenHeight - height) / 2)
      w := (IsObject(screenshot) && screenshot[3] != "") ? screenshot[3] : width
      h := (IsObject(screenshot) && screenshot[4] != "") ? screenshot[4] : height

      ; Convert the Bitmap to a hBitmap and associate a device context for blitting.
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      hbm := this.put_hBitmap(pBitmap, alpha)
      obm := DllCall("SelectObject", "ptr", hdc, "ptr", hbm, "ptr")

      ; Retrieve the device context for the screen.
      ddc := DllCall("GetDC", "ptr", 0, "ptr")

      ; Copies a portion of the screen to a new device context.
      DllCall("gdi32\StretchBlt"
               , "ptr", ddc, "int", x, "int", y, "int", w,     "int", h
               , "ptr", hdc, "int", 0, "int", 0, "int", width, "int", height
               , "uint", 0x00CC0020) ; SRCCOPY

      ; Release the device context to the screen.
      DllCall("ReleaseDC", "ptr", 0, "ptr", ddc)

      ; Cleanup the hBitmap and device contexts.
      DllCall("SelectObject", "ptr", hdc, "ptr", obm)
      DllCall("DeleteObject", "ptr", hbm)
      DllCall("DeleteDC",     "ptr", hdc)

      return [x,y,w,h]
   }

   static put_desktop(ByRef pBitmap) {
      ; Thanks Gerald Degeneve - https://www.codeproject.com/Articles/856020/Draw-Behind-Desktop-Icons-in-Windows-plus

      ; Get Bitmap width and height.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0)

      ; Convert the Bitmap to a hBitmap and associate a device context for blitting.
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      hbm := this.put_hBitmap(pBitmap)
      obm := DllCall("SelectObject", "ptr", hdc, "ptr", hbm, "ptr")

      ; Post-Creator's Update Windows 10. WM_SPAWN_WORKER = 0x052C
      DllCall("SendMessage", "ptr", WinExist("ahk_class Progman"), "uint", 0x052C, "ptr", 0x0000000D, "ptr", 0)
      DllCall("SendMessage", "ptr", WinExist("ahk_class Progman"), "uint", 0x052C, "ptr", 0x0000000D, "ptr", 1)

      ; Find the child window.
      windows := WinGetList("ahk_class WorkerW")
      Loop windows.length
         hwnd := windows[A_Index]
      until DllCall("FindWindowEx", "ptr", hwnd, "ptr", 0, "str", "SHELLDLL_DefView", "ptr", 0)

      ; Maybe this hack gets patched. Tough luck!
      if !(WorkerW := DllCall("FindWindowEx", "ptr", 0, "ptr", hwnd, "str", "WorkerW", "ptr", 0, "ptr"))
         throw Exception("Could not locate hidden window behind desktop.")

      ; Position the image in the center. This line can be removed.
      DllCall("SetWindowPos", "ptr", WorkerW, "ptr", 1
               , "int", Round((A_ScreenWidth - width) / 2)   ; x coordinate
               , "int", Round((A_ScreenHeight - height) / 2) ; y coordinate
               , "int", width, "int", height, "uint", 0)

      ; Get device context of spawned window.
      ddc := DllCall("GetDCEx", "ptr", WorkerW, "ptr", 0, "int", 0x403, "ptr") ; LockWindowUpdate | Cache | Window

      ; Copies a portion of the screen to a new device context.
      DllCall("gdi32\BitBlt"
               , "ptr", ddc, "int", 0, "int", 0, "int", width, "int", height
               , "ptr", hdc, "int", 0, "int", 0, "uint", 0x00CC0020) ; SRCCOPY

      ; Release device context of spawned window.
      DllCall("ReleaseDC", "ptr", 0, "ptr", ddc)

      ; Cleanup the hBitmap and device contexts.
      DllCall("SelectObject", "ptr", hdc, "ptr", obm)
      DllCall("DeleteObject", "ptr", hbm)
      DllCall("DeleteDC",     "ptr", hdc)

      return "desktop"
   }

   static put_wallpaper(ByRef pBitmap) {
      ; Create a temporary image file.
      filepath := this.put_file(pBitmap)

      ; Get the absolute path of the file.
      size := DllCall("GetFullPathName", "str", filepath, "uint", 0, "ptr", 0, "ptr", 0, "uint")
      VarSetStrCapacity(buf, size)
      DllCall("GetFullPathName", "str", filepath, "uint", size, "str", buf, "ptr", 0, "uint")

      ; Keep waiting until the file has been created. (It should be instant!)
      Loop 6 ; Try this 6 times.
         if (A_Index > 1)
            Sleep (2**(A_Index-2) * 30)
      until FileExist(filepath)
      if !FileExist(filepath)
         throw Exception("Unable to create temporary image file.")

      ; Set the temporary image file as the new desktop wallpaper.
      DllCall("SystemParametersInfo", "uint", 20, "uint", 0, "str", buf, "uint", 2)

      ; This is a delayed delete call. #Persistent may be required on v1.
      DeleteFile := Func("DllCall").Bind("DeleteFile", "str", filepath)
      SetTimer DeleteFile, -2000

      return "wallpaper"
   }

   static put_cursor(ByRef pBitmap, xHotspot := "", yHotspot := "") {
      ; Thanks Nick - https://stackoverflow.com/a/550965

      ; Creates an icon that can be used as a cursor.
      DllCall("gdiplus\GdipCreateHICONFromBitmap", "ptr", pBitmap, "ptr*", hIcon:=0)

      ; Sets the hotspot of the cursor by changing the icon into a cursor.
      if (xHotspot != "" || yHotspot != "") {
         ; struct ICONINFO - https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-iconinfo
         ii := BufferAlloc(8+3*A_PtrSize, 0)                          ; sizeof(ICONINFO) = 20, 32
         DllCall("GetIconInfo", "ptr", hIcon, "ptr", ii)              ; Fill the ICONINFO structure.
            , NumPut("uint", false, ii, 0)                            ; true/false are icon/cursor respectively.
            , (xHotspot != "") ? NumPut("uint", xHotspot, ii, 4) : "" ; Set the xHotspot value. (Default: center point)
            , (yHotspot != "") ? NumPut("uint", yHotspot, ii, 8) : "" ; Set the yHotspot value. (Default: center point)
         DllCall("DestroyIcon", "ptr", hIcon)                         ; Destroy the icon after getting the ICONINFO structure.
         hIcon := DllCall("CreateIconIndirect", "ptr", ii, "ptr")     ; Create a new cursor using ICONINFO.

         ; Clean up hbmMask and hbmColor created as a result of GetIconInfo.
         DllCall("DeleteObject", "ptr", NumGet(ii, 8+A_PtrSize, "ptr"))   ; hbmMask
         DllCall("DeleteObject", "ptr", NumGet(ii, 8+2*A_PtrSize, "ptr")) ; hbmColor
      }

      ; Loop over all 16 system cursors and change them all to the new cursor.
      SystemCursors := "32512,32513,32514,32515,32516,32640,32641,32642,32643,32644,32645,32646,32648,32649,32650,32651"
      Loop Parse, SystemCursors, ","
      { ; Must copy the handle 16 times as SetSystemCursor deletes the handle 16 times.
         hCursor := DllCall("CopyImage", "ptr", hIcon, "uint", 2, "int", 0, "int", 0, "uint", 0, "ptr")
         DllCall("SetSystemCursor", "ptr", hCursor, "int", A_LoopField) ; calls DestroyCursor
      }

      ; Destroy the original hIcon. DestroyCursor and DestroyIcon are the same function in C.
      DllCall("DestroyCursor", "ptr", hIcon)

      ; Returns the word A_Cursor so that it doesn't evaluate immediately.
      return "A_Cursor"
   }

   static put_file(ByRef pBitmap, filepath := "", quality := "") {
      ; Thanks tic - https://www.autohotkey.com/boards/viewtopic.php?t=6517

      ; Seperate the filepath and default the extension to PNG.
      SplitPath filepath,, directory, extension, filename
      filename := (filename != "") ? filename : "___date___"
      extension := (extension ~= "^(?i:bmp|dib|rle|jpg|jpeg|jpe|jfif|gif|tif|tiff|png)$") ? extension : "png"
      filepath := directory . filename "." extension

      ; Fill a buffer with the available encoders.
      DllCall("gdiplus\GdipGetImageEncodersSize", "uint*", count:=0, "uint*", size:=0)
      ci := BufferAlloc(size)
      DllCall("gdiplus\GdipGetImageEncoders", "uint", count, "uint", size, "ptr", ci)
      if !(count && size)
         throw Exception("Could not get a list of image codec encoders on this system.")

      ; Search for an encoder with a matching extension.
      Loop count
         EncoderExtensions := StrGet(NumGet(ci, (idx:=(48+7*A_PtrSize)*(A_Index-1))+32+3*A_PtrSize, "uptr"), "UTF-16")
      until InStr(EncoderExtensions, "*." extension)

      ; Get the pointer to the index/offset of the matching encoder.
      if !(pCodec := ci.ptr + idx)
         throw Exception("Could not find a matching encoder for the specified file format.")

      ; JPEG is a lossy image format that requires a quality value from 0-100. Default quality is 75.
      if (extension ~= "^(?i:jpg|jpeg|jpe|jfif)$"
      && IsInteger(quality) && 0 <= quality && quality <= 100 && quality != 75) {
         DllCall("gdiplus\GdipGetEncoderParameterListSize", "ptr", pBitmap, "ptr", pCodec, "uint*", size:=0)
         EncoderParameters := BufferAlloc(size, 0)
         DllCall("gdiplus\GdipGetEncoderParameterList", "ptr", pBitmap, "ptr", pCodec, "uint", size, "ptr", EncoderParameters)

         ; Search for an encoder parameter with 1 value of type 6.
         Loop NumGet(EncoderParameters, "uint")
            elem := (24+A_PtrSize)*(A_Index-1) + A_PtrSize
         until (NumGet(EncoderParameters, elem+16, "uint") = 1) && (NumGet(EncoderParameters, elem+20, "uint") = 6)

         ; struct EncoderParameter - http://www.jose.it-berater.org/gdiplus/reference/structures/encoderparameter.htm
         ep := EncoderParameters.ptr + elem - A_PtrSize                  ; sizeof(EncoderParameter) = 28, 32
            , NumPut(  "uptr",       1, ep)                              ; Must be 1.
            , NumPut(  "uint",       4, ep, 20+A_PtrSize)                ; Type
            , NumPut(  "uint", quality, NumGet(ep+24+A_PtrSize, "uptr")) ; Value (pointer)
      }

      ; Write the file to disk using the specified encoder and encoding parameters.
      Loop 6 ; Try this 6 times.
         if (A_Index > 1)
            Sleep (2**(A_Index-2) * 30)
      until (result := !DllCall("gdiplus\GdipSaveImageToFile", "ptr", pBitmap, "wstr", filepath, "ptr", pCodec, "uint", IsSet(ep) ? ep : 0))
      if !(result)
         throw Exception("Could not save file to disk.")

      ; If the filename was omitted, replace it with the current time (accurate to the second).
      ; Multiple files that are created within 1 second will be overwritten with the last file.
      ; The replacement colon is called a Modifier Letter Colon found at <U+A789>.
      if (filename == "___date___") {
         filename := FileGetTime(filepath)
         filename := FormatTime(filename, "yyyy-MM-dd HH꞉mm꞉ss")
         FileMove(filepath, directory . filename "." extension, true)
         filepath := directory . filename "." extension
      }

      return filepath
   }

   static put_hBitmap(ByRef pBitmap, alpha := "") {
      ; Revert to built in functionality if a replacement color is declared.
      if (alpha != "") { ; This built-in version is about 25% slower.
         DllCall("gdiplus\GdipCreateHBITMAPFromBitmap", "ptr", pBitmap, "ptr*", hBitmap:=0, "uint", alpha)
         return hBitmap
      }

      ; Get Bitmap width and height.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0)

      ; 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 := BufferAlloc(40, 0)                 ; sizeof(bi) = 40
         , NumPut(  "uint",        40, bi,  0) ; Size
         , NumPut(   "int",     width, bi,  4) ; Width
         , NumPut(   "int",   -height, 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")

      ; Transfer data from source pBitmap to an hBitmap manually.
      Rect := BufferAlloc(16, 0)               ; sizeof(Rect) = 16
         , NumPut(  "uint",   width, Rect,  8) ; Width
         , NumPut(  "uint",  height, Rect, 12) ; Height
      BitmapData := BufferAlloc(16+2*A_PtrSize, 0)      ; sizeof(BitmapData) = 24, 32
         , NumPut(  "uint",      width, BitmapData,  0) ; Width
         , NumPut(  "uint",     height, BitmapData,  4) ; Height
         , NumPut(   "int",  4 * width, BitmapData,  8) ; Stride
         , NumPut(   "int",    0xE200B, BitmapData, 12) ; PixelFormat
         , 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)

      ; Cleanup the hBitmap and device contexts.
      DllCall("SelectObject", "ptr", hdc, "ptr", obm)
      DllCall("DeleteDC",     "ptr", hdc)

      return hbm
   }

   static put_hIcon(ByRef pBitmap) {
      DllCall("gdiplus\GdipCreateHICONFromBitmap", "ptr", pBitmap, "ptr*", hIcon:=0)
      return hIcon
   }

   static put_stream(ByRef pBitmap, extension := "", quality := "") {
      ; Default extension is TIF for fast speeds!
      if !(extension ~= "^(?i:bmp|dib|rle|jpg|jpeg|jpe|jfif|gif|tif|tiff|png)$")
         extension := "tif"

      ; Fill a buffer with the available encoders.
      DllCall("gdiplus\GdipGetImageEncodersSize", "uint*", count:=0, "uint*", size:=0)
      ci := BufferAlloc(size)
      DllCall("gdiplus\GdipGetImageEncoders", "uint", count, "uint", size, "ptr", ci)
      if !(count && size)
         throw Exception("Could not get a list of image codec encoders on this system.")

      ; Search for an encoder with a matching extension.
      Loop count
         EncoderExtensions := StrGet(NumGet(ci, (idx:=(48+7*A_PtrSize)*(A_Index-1))+32+3*A_PtrSize, "uptr"), "UTF-16")
      until InStr(EncoderExtensions, "*." extension)

      ; Get the pointer to the index/offset of the matching encoder.
      if !(pCodec := ci.ptr + idx)
         throw Exception("Could not find a matching encoder for the specified file format.")

      ; JPEG is a lossy image format that requires a quality value from 0-100. Default quality is 75.
      if (extension ~= "^(?i:jpg|jpeg|jpe|jfif)$"
      && IsInteger(quality) && 0 <= quality && quality <= 100 && quality != 75) {
         DllCall("gdiplus\GdipGetEncoderParameterListSize", "ptr", pBitmap, "ptr", pCodec, "uint*", size:=0)
         EncoderParameters := BufferAlloc(size, 0)
         DllCall("gdiplus\GdipGetEncoderParameterList", "ptr", pBitmap, "ptr", pCodec, "uint", size, "ptr", EncoderParameters)

         ; Search for an encoder parameter with 1 value of type 6.
         Loop NumGet(EncoderParameters, "uint")
            elem := (24+A_PtrSize)*(A_Index-1) + A_PtrSize
         until (NumGet(EncoderParameters, elem+16, "uint") = 1) && (NumGet(EncoderParameters, elem+20, "uint") = 6)

         ; struct EncoderParameter - http://www.jose.it-berater.org/gdiplus/reference/structures/encoderparameter.htm
         ep := EncoderParameters.ptr + elem - A_PtrSize                  ; sizeof(EncoderParameter) = 28, 32
            , NumPut(  "uptr",       1, ep)                              ; Must be 1.
            , NumPut(  "uint",       4, ep, 20+A_PtrSize)                ; Type
            , NumPut(  "uint", quality, NumGet(ep+24+A_PtrSize, "uptr")) ; Value (pointer)
      }

      ; Create a Stream.
      DllCall("ole32\CreateStreamOnHGlobal", "ptr", 0, "int", true, "ptr*", pStream:=0)
      DllCall("gdiplus\GdipSaveImageToStream", "ptr", pBitmap, "ptr", pStream, "ptr", pCodec, "uint", IsSet(ep) ? ep : 0)

      return pStream
   }

   static put_RandomAccessStream(ByRef pBitmap, extension := "", quality := "") {
      ; Thanks teadrinker - https://www.autohotkey.com/boards/viewtopic.php?f=6&t=72674

      ; Which is faster, bmp or png?
      pStream := this.put_stream(pBitmap, extension, quality)

      ; Get the Class ID from a GUID string.
      CLSID := BufferAlloc(16, 0)
      if result := DllCall("ole32\CLSIDFromString", "wstr", "{905A0FE1-BC53-11DF-8C49-001E4FC686DA}", "ptr", CLSID, "uint")
         throw Exception("CLSIDFromString failed. Error: " . Format("{:#x}", result))

      ; Create a RandomAccessStream
      DllCall("ShCore\CreateRandomAccessStreamOverStream", "ptr", pStream, "uint", 1, "ptr", CLSID, "ptr*", pRandomAccessStream:=0, "uint")

      ; The handle to the stream object is automatically freed when the stream object is released.
      ObjRelease(pStream)

      return pRandomAccessStream
   }

   static put_base64(ByRef pBitmap, extension := "", quality := "") {
      ; Thanks noname - https://www.autohotkey.com/boards/viewtopic.php?style=7&p=144247#p144247

      ; Default extension is PNG for small sizes!
      if !(extension ~= "^(?i:bmp|dib|rle|jpg|jpeg|jpe|jfif|gif|tif|tiff|png)$")
         extension := "png"

      pStream := this.put_stream(pBitmap, extension, quality)

      DllCall("ole32\GetHGlobalFromStream", "ptr", pStream, "uint*", hData:=0)
      pData := DllCall("GlobalLock", "ptr", hData, "ptr")
      nSize := DllCall("GlobalSize", "uint", pData)

      bin := BufferAlloc(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.
      base64Length := 0
      DllCall("Crypt32.dll\CryptBinaryToStringA", "ptr", bin, "uint", nSize, "uint", 0x40000001, "ptr", 0, "uint*", base64Length)
      base64 := BufferAlloc(base64Length, 0)
      DllCall("Crypt32.dll\CryptBinaryToStringA", "ptr", bin, "uint", nSize, "uint", 0x40000001, "ptr", base64, "uint*", base64Length)
      VarSetStrCapacity(bin, 0)

      return StrGet(base64, base64Length, "CP0")
   }

   ; All references to gdiplus and pToken must be absolute!
   static gdiplus := 0, pToken := 0

   static gdiplusStartup() {
      ImagePut.gdiplus++

      ; Startup gdiplus when counter goes from 0 -> 1.
      if (ImagePut.gdiplus == 1) {
         DllCall("LoadLibrary", "str", "gdiplus")
         si := BufferAlloc(A_PtrSize = 8 ? 24 : 16, 0) ; sizeof(GdiplusStartupInput) = 16, 24
            , NumPut("uint", 0x1, si)
         DllCall("gdiplus\GdiplusStartup", "ptr*", pToken:=0, "ptr", si, "ptr", 0)
         ImagePut.pToken := pToken
      }
   }

   static gdiplusShutdown(cotype := "", ByRef pBitmap := "") {
      ImagePut.gdiplus--

      ; When a buffer object is deleted a bitmap is sent here for disposal.
      if (cotype == "smart_pointer")
         if DllCall("gdiplus\GdipDisposeImage", "ptr", pBitmap)
            throw Exception("The bitmap of this buffer object has already been deleted.")

      ; Check for unpaired calls of gdiplusShutdown. 
      if (ImagePut.gdiplus < 0)
         throw Exception("Missing ImagePut.gdiplusStartup().")

      ; Shutdown gdiplus when counter goes from 1 -> 0.
      if (ImagePut.gdiplus == 0) {
         DllCall("gdiplus\GdiplusShutdown", "ptr", ImagePut.pToken)
         DllCall("FreeLibrary", "ptr", DllCall("GetModuleHandle", "str", "gdiplus", "ptr"))

         ; Exit if GDI+ is still loaded. GdiplusNotInitialized = 18
         if (18 != DllCall("gdiplus\GdipCreateImageAttributes", "ptr*", ImageAttr:=0)) {
            DllCall("gdiplus\GdipDisposeImageAttributes", "ptr", ImageAttr)
            return
         }

         ; Otherwise GDI+ has been truly unloaded from the script and objects are out of scope.
         if (cotype = "bitmap")
            throw Exception("Out of scope error. `n`nIf you wish to handle raw pointers to GDI+ bitmaps, add the line"
               . "`n`n`t`t" this.prototype.__class ".gdiplusStartup()`n`nor 'pToken := Gdip_Startup()' to the top of your script."
               . "`nAlternatively, use 'obj := ImagePutBuffer()' with 'obj.pBitmap'."
               . "`nYou can copy this message by pressing Ctrl + C.")
      }
   }
} ; End of ImagePut class.
  • ImagePutHIcon() has been added. Returns a handle to an icon.
  • handles to icons: hIcon are now accepted as an input.
  • Pointers to stream are now accepted as an input.
  • Pointers to RandomAccessStream are now accepted as an input.
  • The hidden window behind icons: desktop is now accepted as an input.
  • ImagePut() now works as expected. (Was throwing errors due to improper v1 -> v2 translation.)
  • All functions that require writing to the disk now implement exponential backoff (max 6 tries). Helps remote access.
  • Calling ImagePutFile() without a filepath returns the generated filename as expected.
  • ImageType() now catches if you typed in a filename but forgot the extension.
  • ImagePutWallpaper() will now set the wallpaper before deleting the file.

iseahound
Posts: 608
Joined: 13 Aug 2016, 21:04
GitHub: iseahound

Re: [a122] ImagePut - Windows Image Transformation Library

Post by iseahound » 23 Aug 2020, 10:41

a122 compatible (most likely, I can't test it because it's detected as a trojan on my workstation)

Code: Select all

; Script:    ImagePut.ahk
; Author:    iseahound
; License:   MIT License
; Version:   2020-05-22
; Release:   2020-08-23

; ImagePut - Windows Image Transformation Library
; Copy and paste functions from this reference libary as you wish.
; -> All put_XXX functions map from a pBitmap to XXX.
; -> All from_XXX functions map from XXX to a pBitmap.
; Or use ImagePut functions as part of your standard libary.


; Puts the image into a file format and returns a base64 encoded string.
;   extension  -  File Encoding           |  string   ->   bmp, gif, jpg, png, tiff
;   quality    -  JPEG Quality Level      |  integer  ->   0 - 100
ImagePutBase64(ByRef image, extension := "", quality := "")
   => ImagePut("base64", image,,, extension, quality)


; Puts the image into a GDI+ Bitmap and returns a pointer.
ImagePutBitmap(ByRef image)
   => ImagePut("bitmap", image)


; Puts the image into a GDI+ Bitmap and returns a buffer object with GDI+ scope.
ImagePutBuffer(ByRef image)
   => ImagePut("buffer", image)


; Puts the image onto the clipboard and returns ClipboardAll().
ImagePutClipboard(ByRef image)
   => ImagePut("clipboard", image)


; Puts the image as the cursor and returns the variable A_Cursor.
;   xHotspot   -  X Click Point           |  pixel    ->   0 - width
;   yHotspot   -  Y Click Point           |  pixel    ->   0 - height
ImagePutCursor(ByRef image, xHotspot := "", yHotspot := "")
   => ImagePut("cursor", image,,, xHotspot, yHotspot)


; Puts the image behind the desktop icons and returns the string "desktop".
;   scale      -  Scale Factor            |  real     ->   A_ScreenHeight / height.
ImagePutDesktop(ByRef image, scale := 1)
   => ImagePut("desktop", image,, scale)


; Puts the image into a file and returns a relative filepath.
;   filepath   -  Filepath + Extension    |  string   ->   *.bmp, *.gif, *.jpg, *.png, *.tiff
;   quality    -  JPEG Quality Level      |  integer  ->   0 - 100
ImagePutFile(ByRef image, filepath := "", quality := "")
   => ImagePut("file", image,,, filepath, quality)


; Puts the image into a device independent bitmap and returns the handle.
;   alpha      -  Alpha Replacement Color |  RGB      ->   0xFFFFFF
ImagePutHBitmap(ByRef image, alpha := "")
   => ImagePut("hBitmap", image,,, alpha)


; Puts the image into a file format and returns a hexadecimal encoded string.
;   extension  -  File Encoding           |  string   ->   bmp, gif, jpg, png, tiff
;   quality    -  JPEG Quality Level      |  integer  ->   0 - 100
ImagePutHex(ByRef image, extension := "", quality := "")
   => ImagePut("hex", image,,, extension, quality)


; Puts the image into an icon and returns the handle.
ImagePutHIcon(ByRef image)
   => ImagePut("hBitmap", image)


; Puts the image into a file format and returns a pointer to a RandomAccessStream.
;   extension  -  File Encoding           |  string   ->   bmp, gif, jpg, png, tiff
;   quality    -  JPEG Quality Level      |  integer  ->   0 - 100
ImagePutRandomAccessStream(ByRef image, extension := "", quality := "")
   => ImagePut("RandomAccessStream", image,,, extension, quality)


; Puts the image on the shared screen device context and returns an array of coordinates.
;   screenshot -  Screen Coordinates      |  array    ->   [x,y,w,h] or [0,0]
;   alpha      -  Alpha Replacement Color |  RGB      ->   0xFFFFFF
ImagePutScreenshot(ByRef image, screenshot := "", alpha := "")
   => ImagePut("screenshot", image,,, screenshot, alpha)


; Puts the image into a file format and returns a pointer to a stream.
;   extension  -  File Encoding           |  string   ->   bmp, gif, jpg, png, tiff
;   quality    -  JPEG Quality Level      |  integer  ->   0 - 100
ImagePutStream(ByRef image, extension := "", quality := "")
   => ImagePut("stream", image,,, extension, quality)


; Puts the image as the desktop wallpaper and returns the string "wallpaper".
ImagePutWallpaper(ByRef image)
   => ImagePut("wallpaper", image)



; ImagePut() - Puts an image from anywhere to anywhere.
;   cotype     -  Output Type             |  string   ->   Case Insensitive. Read documentation.
;   image      -  Input Image             |  image    ->   Anything. Refer to ImageType().
;   crop       -  Crop Coordinates        |  array    ->   [x,y,w,h] could be negative or percent.
;   scale      -  Scale Factor            |  real     ->   2.0
;   terms*     -  Additional Parameters   |  variadic ->   Extra parameters found in toCotype().
ImagePut(cotype, ByRef image, crop := "", scale := "", terms*)
   => ImagePut.call(cotype, image, crop, scale, terms*)



class ImagePut {

   static call(cotype, ByRef image, crop := "", scale := "", terms*) {

      this.gdiplusStartup()

      ; Take a guess as to what the image might be. (>90% accuracy!)
      try type := this.DontVerifyImageType(image)
      catch
         type := this.ImageType(image)

      ; Qualify additional parameters for correctness.
      _crop := IsObject(crop)
         && crop[1] ~= "^-?\d+(\.\d*)?%?$" && crop[2] ~= "^-?\d+(\.\d*)?%?$"
         && crop[3] ~= "^-?\d+(\.\d*)?%?$" && crop[4] ~= "^-?\d+(\.\d*)?%?$"
      _scale := scale != 1 && scale ~= "^\d+(\.\d+)?$"

      ; Make a copy of the image as a pBitmap.
      pBitmap := this.toBitmap(type, image)

      ; Crop the image.
      if (_crop) {
         pBitmap2 := this.BitmapCrop(pBitmap, crop)
         DllCall("gdiplus\GdipDisposeImage", "ptr", pBitmap)
         pBitmap := pBitmap2
      }

      ; Scale the image.
      if (_scale) {
         pBitmap2 := this.BitmapScale(pBitmap, scale)
         DllCall("gdiplus\GdipDisposeImage", "ptr", pBitmap)
         pBitmap := pBitmap2
      }

      ; Put the pBitmap to wherever the cotype specifies.
      coimage := this.toCotype(cotype, pBitmap, terms*)

      ; Clean up the pBitmap copy. Export raw pointers if requested.
      if !(cotype = "bitmap" || cotype = "buffer")
         DllCall("gdiplus\GdipDisposeImage", "ptr", pBitmap)

      this.gdiplusShutdown(cotype)

      return coimage
   }

   static DontVerifyImageType(ByRef image) {

      if !IsObject(image)
         throw Exception("Must be an object.")

      ; Check for image type declarations.
      ; Assumes that the user is telling the truth.

      if ObjHasOwnProp(image, "clipboard") {
         image := image.clipboard
         return "clipboard"
      }

      if ObjHasOwnProp(image, "object") {
         image := image.object
         return "object"
      }

      if ObjHasOwnProp(image, "buffer") {
         image := image.buffer
         return "buffer"
      }

      if ObjHasOwnProp(image, "screenshot") {
         image := image.screenshot
         return "screenshot"
      }

      if ObjHasOwnProp(image, "window") {
         image := image.window
         return "window"
      }

      if ObjHasOwnProp(image, "desktop") {
         image := image.desktop
         return "desktop"
      }

      if ObjHasOwnProp(image, "wallpaper") {
         image := image.wallpaper
         return "wallpaper"
      }

      if ObjHasOwnProp(image, "cursor") {
         image := image.cursor
         return "cursor"
      }

      if ObjHasOwnProp(image, "url") {
         image := image.url
         return "url"
      }

      if ObjHasOwnProp(image, "file") {
         image := image.file
         return "file"
      }

      if ObjHasOwnProp(image, "monitor") {
         image := image.monitor
         return "monitor"
      }

      if ObjHasOwnProp(image, "hBitmap") {
         image := image.hBitmap
         return "hBitmap"
      }

      if ObjHasOwnProp(image, "hIcon") {
         image := image.hIcon
         return "hIcon"
      }

      if ObjHasOwnProp(image, "bitmap") {
         image := image.bitmap
         return "bitmap"
      }

      if ObjHasOwnProp(image, "stream") {
         image := image.stream
         return "stream"
      }

      if ObjHasOwnProp(image, "RandomAccessStream") {
         image := image.RandomAccessStream
         return "RandomAccessStream"
      }

      if ObjHasOwnProp(image, "base64") {
         image := image.base64
         return "base64"
      }

      if ObjHasOwnProp(image, "sprite") {
         image := image.sprite
         return "sprite"
      }

      throw Exception("Invalid type.")
   }

   static ImageType(ByRef image) {
      ; Throw if the image is an empty string.
      if (image == "")
         throw Exception("Image data is an empty string.")

      if IsObject(image) {
         ; A "clipboard" is a buffer object containing binary data returned by ClipboardAll()
         if (image.base.HasOwnProp("__class") && image.base.__class == "ClipboardAll")
            return "clipboard"

         ; An "object" is an object that implements a Bitmap() method returning a pointer to a GDI+ bitmap.
         if image.HasOwnMethod("Bitmap")
            return "object"

         ; A "buffer" is an AutoHotkey v2 buffer object.
         if image.HasOwnProp("pBitmap")
            return "buffer"

         ; A "screenshot" is an array of 4 numbers.
         if (image[1] ~= "^-?\d+$" && image[2] ~= "^-?\d+$" && image[3] ~= "^-?\d+$" && image[4] ~= "^-?\d+$")
            return "screenshot"
      }
         ; A "window" is anything considered a Window Title including ahk_class and "A".
         if WinExist(image)
            return "window"

         ; A "desktop" is a hidden window behind the desktop icons created by ImagePutDesktop.
         if (image = "desktop")
            return "desktop"

         ; A "wallpaper" is the desktop wallpaper.
         if (image = "wallpaper")
            return "wallpaper"

         ; A "cursor" is the name of a known cursor name.
         if (image ~= "(?i)^(IDC|OCR)?_?(A_Cursor|AppStarting|Arrow|Cross|Help|IBeam|"
         . "Icon|No|Size|SizeAll|SizeNESW|SizeNS|SizeNWSE|SizeWE|UpArrow|Wait|Unknown)$")
            return "cursor"

         ; A "url" satisfies the url format.
         if this.is_url(image)
            return "url"

         ; A "file" is stored on the disk or network.
         if FileExist(image)
            return "file"

      if IsInteger(image) {
         ; A non-zero "monitor" number identifies each display uniquely; and 0 refers to the entire virtual screen.
         if (image >= 0 && image <= MonitorGetCount())
            return "monitor"

         ; An "hBitmap" is a handle to a GDI Bitmap.
         if (DllCall("GetObjectType", "ptr", image) == 7)
            return "hBitmap"

         ; An "hIcon" is a handle to a GDI icon.
         if DllCall("DestroyIcon", "ptr", DllCall("CopyIcon", "ptr", image, "ptr"))
            return "hIcon"

         ; A "bitmap" is a pointer to a GDI+ Bitmap.
         try if !DllCall("gdiplus\GdipGetImageType", "ptr", image, "ptr*", type:=0) && (type == 1)
            return "bitmap"

         ; A "stream" is a pointer to the IStream interface.
         try if ComObjQuery(image, "{0000000C-0000-0000-C000-000000000046}") {
            ObjRelease(image)
            return "stream"
         }

         ; A "RandomAccessStream" is a pointer to the IRandomAccessStream interface.
         try if ComObjQuery(image, "{905A0FE1-BC53-11DF-8C49-001E4FC686DA}") {
            ObjRelease(image)
            return "RandomAccessStream"
         }
      }
         ; A "base64" string is binary image data encoded into text using only 64 characters.
         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"

      for extension in ["bmp","dib","rle","jpg","jpeg","jpe","jfif","gif","tif","tiff","png","ico","exe","dll"]
         if FileExist(image "." extension)
            throw Exception("A ." extension " file extension is required!")

      throw Exception("Image type could not be identified.")
   }

   static toBitmap(type, ByRef image) {

      if (type = "clipboard")
         return this.from_clipboard()

      if (type = "object")                      ; Special
         return image.Bitmap()

      if (type = "buffer")
         return this.from_bitmap(image.pBitmap)

      if (type = "screenshot")
         return this.from_screenshot(image)

      if (type = "window")
         return this.from_window(image)

      if (type = "desktop")
         return this.from_desktop()

      if (type = "wallpaper")
         return this.from_wallpaper()

      if (type = "cursor")
         return this.from_cursor()

      if (type = "url")
         return this.from_url(image)

      if (type = "file")
         return this.from_file(image)

      if (type = "monitor")
         return this.from_monitor(image)

      if (type = "hBitmap")
         return this.from_hBitmap(image)

      if (type = "hIcon")
         return this.from_hIcon(image)

      if (type = "bitmap")
         return this.from_bitmap(image)

      if (type = "stream")
         return this.from_stream(image)

      if (type = "RandomAccessStream")
         return this.from_RandomAccessStream(image)

      if (type = "base64")
         return this.from_base64(image)

      if (type = "sprite")
         return this.from_sprite(image)

      throw Exception("Conversion from type " type " is not supported.")
   }

   static toCotype(cotype, ByRef pBitmap, term1 := "", term2 := "", *) {
      ; toCotype("clipboard", pBitmap)
      if (cotype = "clipboard")
         return this.put_clipboard(pBitmap)

      ; toCotype("buffer", pBitmap)
      if (cotype = "buffer")
         return this.put_buffer(pBitmap)

      ; toCotype("screenshot", pBitmap, screenshot, alpha)
      if (cotype = "screenshot")
         return this.put_screenshot(pBitmap, term1, term2)

      ; toCotype("window", pBitmap)
      if (cotype = "window")
         return "ahk_id " . this.Render({bitmap:pBitmap}).AlwaysOnTop().ToolWindow().Caption().hwnd

      ; toCotype("desktop", pBitmap)
      if (cotype = "desktop")
         return this.put_desktop(pBitmap)

      ; toCotype("wallpaper", pBitmap)
      if (cotype = "wallpaper")
         return this.put_wallpaper(pBitmap)

      ; toCotype("cursor", pBitmap, xHotspot, yHotspot)
      if (cotype = "cursor")
         return this.put_cursor(pBitmap, term1, term2)

      ; toCotype("url", pBitmap)
      if (cotype = "url")
         return this.put_url(pBitmap)

      ; toCotype("file", pBitmap, filename, quality)
      if (cotype = "file")
         return this.put_file(pBitmap, term1, term2)

      ; toCotype("hBitmap", pBitmap, alpha)
      if (cotype = "hBitmap")
         return this.put_hBitmap(pBitmap, term1)

      ; toCotype("hIcon", pBitmap)
      if (cotype = "hIcon")
         return this.put_hIcon(pBitmap)

      ; toCotype("bitmap", pBitmap)
      if (cotype = "bitmap")
         return pBitmap

      ; toCotype("stream", pBitmap, extension, quality)
      if (cotype = "stream")
         return this.put_stream(pBitmap, term1, term2)

      ; toCotype("RandomAccessStream", pBitmap, extension, quality)
      if (cotype = "RandomAccessStream")
         return this.put_RandomAccessStream(pBitmap, term1, term2)

      ; toCotype("base64", pBitmap, extension, quality)
      if (cotype = "base64")
         return this.put_base64(pBitmap, term1, term2)

      ; toCotype("hex", pBitmap, extension, quality)
      if (cotype = "hex")
         return this.put_hex(pBitmap, term1, term2)

      throw Exception("Conversion to type " cotype " is not supported.")
   }

   static DisposeImage(ByRef pBitmap) {
      return DllCall("gdiplus\GdipDisposeImage", "ptr", pBitmap)
   }

   static BitmapCrop(ByRef pBitmap, crop) {
      ; Get Bitmap width, height, and format.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0)
      DllCall("gdiplus\GdipGetImagePixelFormat", "ptr", pBitmap, "uint*", format:=0)

      ; Are the numbers percentages?
      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]
      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]

      ; If numbers are negative, subtract the values from the edge.
      crop[3] := (crop[3] < 0) ?  width - Abs(crop[3]) - Abs(crop[1]) : crop[3]
      crop[4] := (crop[4] < 0) ? height - Abs(crop[4]) - Abs(crop[2]) : crop[4]
      crop[1] := Abs(crop[1])
      crop[2] := Abs(crop[2])

      ; Round to the nearest integer.
      crop[3] := Round(crop[1] + crop[3]) - Round(crop[1]) ; A reminder that width and height
      crop[4] := Round(crop[2] + crop[4]) - Round(crop[2]) ; are distances, not coordinates.
      crop[1] := Round(crop[1]) ; so the abstract concept of a distance must be resolved
      crop[2] := Round(crop[2]) ; into coordinates and then rounded and added up again.

      ; Variance Shift. Now place x,y before w,h because we are building abstracts from reals now.
      ; Before we were resolving abstracts into real coordinates, now it's the opposite.

      ; Ensure that coordinates can never exceed the expected Bitmap area.
      safe_x := (crop[1] > width) ? 0 : crop[1]                          ; Zero x if bigger.
      safe_y := (crop[2] > height) ? 0 : crop[2]                         ; Zero y if bigger.
      safe_w := (crop[1] + crop[3] > width) ? width - safe_x : crop[3]   ; Max w if bigger.
      safe_h := (crop[2] + crop[4] > height) ? height - safe_y : crop[4] ; Max h if bigger.

      ; Clone
      DllCall("gdiplus\GdipCloneBitmapAreaI"
               ,    "int", safe_x
               ,    "int", safe_y
               ,    "int", safe_w
               ,    "int", safe_h
               ,    "int", format
               ,    "ptr", pBitmap
               ,   "ptr*", pBitmapCrop:=0)

      return pBitmapCrop
   }

   static BitmapScale(ByRef pBitmap, scale) {
      ; Get Bitmap width, height, and format.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0)
      DllCall("gdiplus\GdipGetImagePixelFormat", "ptr", pBitmap, "uint*", format:=0)

      safe_w := Ceil(width * scale)
      safe_h := Ceil(height * scale)

      ; Create a new bitmap and get the graphics context.
      DllCall("gdiplus\GdipCreateBitmapFromScan0"
               , "int", safe_w, "int", safe_h, "int", 0, "int", format, "ptr", 0, "ptr*", pBitmapScale:=0)
      DllCall("gdiplus\GdipGetImageGraphicsContext", "ptr", pBitmapScale, "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", safe_w, "int", safe_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)
      return pBitmapScale
   }

   static is_url(url) {
      ; Thanks splattermania - https://www.php.net/manual/en/function.preg-match.php#93824

      regex := "^(?i)"
         . "((https?|ftp)\:\/\/)" ; SCHEME
         . "([a-z0-9+!*(),;?&=\$_.-]+(\:[a-z0-9+!*(),;?&=\$_.-]+)[email protected])?" ; User and Pass
         . "([a-z0-9-.]*)\.([a-z]{2,3})" ; Host or IP
         . "(\:[0-9]{2,5})?" ; Port
         . "(\/(?:[a-z0-9-_~!$&'()*+,;=:@]\.?)+)*\/?" ; Path
         . "(\?[a-z+&\$_.-][a-z0-9;:@&%=+\/\$_.-]*)?" ; GET Query
         . "(#[a-z_.-][a-z0-9+\$_.-]*)?$" ; Anchor
      return (url ~= regex)
   }

   static from_clipboard() {
      ; Open the clipboard.
      Loop 6 ; Try this 6 times.
         if (A_Index > 1)
            Sleep (2**(A_Index-2) * 30)
      until (result := DllCall("OpenClipboard", "ptr", 0))
      if !(result)
         throw Exception("Clipboard could not be opened.")

      ; Prefer the PNG stream if available considering it supports transparency.
      png := DllCall("RegisterClipboardFormat", "str", "png", "uint")
      if DllCall("IsClipboardFormatAvailable", "uint", png, "int") {
         hData := DllCall("GetClipboardData", "uint", png, "ptr")
         DllCall("ole32\CreateStreamOnHGlobal", "ptr", hData, "int", true, "ptr*", pStream:=0)
         DllCall("gdiplus\GdipCreateBitmapFromStream", "ptr", pStream, "ptr*", pBitmap:=0)
         ObjRelease(pStream)
      }

      ; Fallback to CF_BITMAP.
      else if DllCall("IsClipboardFormatAvailable", "uint", 2, "int") {
         hBitmap := DllCall("GetClipboardData", "uint", 2, "ptr")
         DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", "ptr", hBitmap, "ptr", 0, "ptr*", pBitmap:=0)
         DllCall("DeleteObject", "ptr", hBitmap)
      }

      DllCall("CloseClipboard")
      return pBitmap
   }

   static from_screenshot(ByRef image) {
      ; Thanks tic - https://www.autohotkey.com/boards/viewtopic.php?t=6517

      ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      bi := BufferAlloc(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) ; SRCCOPY

      ; 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
   }

   static from_window(ByRef image) {
      ; Thanks tic - https://www.autohotkey.com/boards/viewtopic.php?t=6517

      ; Get the handle to the window.
      image := WinExist(image)

      ; Restore the window if minimized! Must be visible for capture.
      if DllCall("IsIconic", "ptr", image)
         DllCall("ShowWindow", "ptr", image, "int", 4)

      ; Get the width and height of the client window.
      Rect := BufferAlloc(16) ; sizeof(RECT) = 16
      DllCall("GetClientRect", "ptr", image, "ptr", Rect)
         , width  := NumGet(Rect, 8, "int")
         , height := NumGet(Rect, 12, "int")

      ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      bi := BufferAlloc(40, 0)                 ; sizeof(bi) = 40
         , NumPut(  "uint",        40, bi,  0) ; Size
         , NumPut(   "int",     width, bi,  4) ; Width
         , NumPut(   "int",   -height, 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")

      ; Print the window onto the hBitmap using an undocumented flag. https://stackoverflow.com/a/40042587
      DllCall("PrintWindow", "ptr", image, "ptr", hdc, "uint", 0x3) ; PW_CLIENTONLY | PW_RENDERFULLCONTENT
      ; Additional info on how this is implemented: https://www.reddit.com/r/windows/comments/8ffr56/altprintscreen/

      ; 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
   }

   static from_desktop() {
      ; Find the child window.
      windows := WinGetList("ahk_class WorkerW")
      if (windows.length == 0)
         throw Exception("The hidden desktop window has not been initalized. Call ImagePutDesktop() first.")

      Loop windows.length
         hwnd := windows[A_Index]
      until DllCall("FindWindowEx", "ptr", hwnd, "ptr", 0, "str", "SHELLDLL_DefView", "ptr", 0)

      ; Maybe this hack gets patched. Tough luck!
      if !(WorkerW := DllCall("FindWindowEx", "ptr", 0, "ptr", hwnd, "str", "WorkerW", "ptr", 0, "ptr"))
         throw Exception("Could not locate hidden window behind desktop.")

      ; Get the width and height of the client window.
      Rect := BufferAlloc(16) ; sizeof(RECT) = 16
      DllCall("GetClientRect", "ptr", WorkerW, "ptr", Rect)
         , width  := NumGet(Rect, 8, "int")
         , height := NumGet(Rect, 12, "int")

      ; Get device context of spawned window.
      sdc := DllCall("GetDCEx", "ptr", WorkerW, "ptr", 0, "int", 0x403, "ptr") ; LockWindowUpdate | Cache | Window

      ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      bi := BufferAlloc(40, 0)                 ; sizeof(bi) = 40
         , NumPut(  "uint",        40, bi,  0) ; Size
         , NumPut(   "int",     width, bi,  4) ; Width
         , NumPut(   "int",   -height, 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")

      ; Copies a portion of the hidden window to a new device context.
      DllCall("gdi32\BitBlt"
               , "ptr", hdc, "int", 0, "int", 0, "int", width, "int", height
               , "ptr", sdc, "int", 0, "int", 0, "uint", 0x00CC0020) ; SRCCOPY

      ; 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)

      ; Release device context of spawned window.
      DllCall("ReleaseDC", "ptr", 0, "ptr", sdc)

      return pBitmap
   }

   static from_wallpaper() {
      ; Get the width and height of all monitors.
      width  := DllCall("GetSystemMetrics", "int", 78)
      height := DllCall("GetSystemMetrics", "int", 79)

      ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      bi := BufferAlloc(40, 0)                 ; sizeof(bi) = 40
         , NumPut(  "uint",        40, bi,  0) ; Size
         , NumPut(   "int",     width, bi,  4) ; Width
         , NumPut(   "int",   -height, 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")

      ; Paints the desktop.
      DllCall("PaintDesktop", "ptr", hdc)

      ; 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
   }

   static from_cursor() {
      ; Thanks 23W - https://stackoverflow.com/a/13295280

      ; struct CURSORINFO - https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-cursorinfo
      ci := BufferAlloc(16+A_PtrSize, 0)                 ; sizeof(CURSORINFO) = 20, 24
         , NumPut("int", ci.size, ci)
      DllCall("GetCursorInfo", "ptr", ci)
         ; cShow   := NumGet(ci,  4, "int")              ; 0x1 = CURSOR_SHOWING, 0x2 = CURSOR_SUPPRESSED
         , hCursor := NumGet(ci,  8, "ptr")
         ; xCursor := NumGet(ci,  8+A_PtrSize, "int")
         ; yCursor := NumGet(ci, 12+A_PtrSize, "int")

      ; Cursors are the same as icons!
      pBitmap := this.from_hIcon(hCursor)

      ; Cleanup the handle to the cursor.
      DllCall("DestroyIcon",  "ptr", hCursor)

      return pBitmap
   }

   static from_url(ByRef image) {
      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:=0)
      ObjRelease(pStream)
      return pBitmap
   }

   static from_file(ByRef image) {
      DllCall("gdiplus\GdipCreateBitmapFromFile", "wstr", image, "ptr*", pBitmap:=0)
      return pBitmap
   }

   static from_monitor(ByRef image) {
      if (image > 0) {
         MonitorGet(image, Left, Top, Right, Bottom)
         x := Left
         y := Top
         w := Right - Left
         h := Bottom - Top
      } else {
         x := DllCall("GetSystemMetrics", "int", 76)
         y := DllCall("GetSystemMetrics", "int", 77)
         w := DllCall("GetSystemMetrics", "int", 78)
         h := DllCall("GetSystemMetrics", "int", 79)
      }
      return this.from_screenshot([x,y,w,h])
   }

   static from_hBitmap(ByRef image) {
      ; struct DIBSECTION - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-dibsection
      ; struct BITMAP - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmap
      dib := BufferAlloc(64+5*A_PtrSize) ; sizeof(DIBSECTION) = 84, 104
      DllCall("GetObject", "ptr", image, "int", dib.size, "ptr", dib)
         , width  := NumGet(dib, 4, "uint")
         , height := NumGet(dib, 8, "uint")
         , bpp    := NumGet(dib, 18, "ushort")

      ; Fallback to built-in method if pixels are not 32-bit ARGB.
      if (bpp != 32) { ; This built-in version is 120% faster but ignores transparency.
         DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", "ptr", image, "ptr", 0, "ptr*", pBitmap:=0)
         return pBitmap
      }

      ; Create a handle to a device context and associate the image.
      sdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")           ; Creates a memory DC compatible with the current screen.
      sbm := DllCall("SelectObject", "ptr", sdc, "ptr", image, "ptr") ; Put the (hBitmap) image onto the device context.

      ; Create a device independent bitmap with negative height. All DIBs use the screen pixel format (pARGB).
      ; Use hbm to buffer the image such that top-down and bottom-up images are mapped to this top-down buffer.
      ; pBits is the pointer to (top-down) pixel values. The Scan0 will point to the pBits.
      ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      bi := BufferAlloc(40, 0)                 ; sizeof(bi) = 40
         , NumPut(  "uint",        40, bi,  0) ; Size
         , NumPut(   "int",     width, bi,  4) ; Width
         , NumPut(   "int",   -height, 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")

      ; This is the 32-bit ARGB pBitmap (different from an hBitmap) that will receive the final converted pixels.
      DllCall("gdiplus\GdipCreateBitmapFromScan0"
               , "int", width, "int", height, "int", 0, "int", 0x26200A, "ptr", 0, "ptr*", pBitmap:=0)

      ; Create a Scan0 buffer pointing to pBits. The buffer has pixel format pARGB.
      Rect := BufferAlloc(16, 0)               ; sizeof(Rect) = 16
         , NumPut(  "uint",   width, Rect,  8) ; Width
         , NumPut(  "uint",  height, Rect, 12) ; Height
      BitmapData := BufferAlloc(16+2*A_PtrSize, 0)      ; sizeof(BitmapData) = 24, 32
         , NumPut(  "uint",      width, BitmapData,  0) ; Width
         , NumPut(  "uint",     height, BitmapData,  4) ; Height
         , NumPut(   "int",  4 * width, BitmapData,  8) ; Stride
         , NumPut(   "int",    0xE200B, BitmapData, 12) ; PixelFormat
         , NumPut(   "ptr",      pBits, BitmapData, 16) ; Scan0

      ; Use LockBits to create a writable buffer that converts pARGB to ARGB.
      DllCall("gdiplus\GdipBitmapLockBits"
               ,    "ptr", pBitmap
               ,    "ptr", Rect
               ,   "uint", 6            ; ImageLockMode.UserInputBuffer | ImageLockMode.WriteOnly
               ,    "int", 0xE200B      ; Format32bppPArgb
               ,    "ptr", BitmapData) ; Contains the pointer (pBits) to the hbm.

      ; Copies the image (hBitmap) to a top-down bitmap. Removes bottom-up-ness if present.
      DllCall("gdi32\BitBlt"
               , "ptr", hdc, "int", 0, "int", 0, "int", width, "int", height
               , "ptr", sdc, "int", 0, "int", 0, "uint", 0x00CC0020) ; SRCCOPY

      ; Convert the pARGB pixels copied into the device independent bitmap (hbm) to ARGB.
      DllCall("gdiplus\GdipBitmapUnlockBits", "ptr", pBitmap, "ptr", BitmapData)

      ; Cleanup the hBitmap and device contexts.
      DllCall("SelectObject", "ptr", hdc, "ptr", obm)
      DllCall("DeleteObject", "ptr", hbm)
      DllCall("DeleteDC",     "ptr", hdc)
      DllCall("SelectObject", "ptr", sdc, "ptr", sbm)
      DllCall("DeleteDC",     "ptr", sdc)

      return pBitmap
   }

   static from_hIcon(ByRef image) {
      ; struct ICONINFO - https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-iconinfo
      ii := BufferAlloc(8+3*A_PtrSize, 0)                ; sizeof(ICONINFO) = 20, 32
      DllCall("GetIconInfo", "ptr", image, "ptr", ii)
         ; xHotspot := NumGet(ii, 4, "uint")
         ; yHotspot := NumGet(ii, 8, "uint")
         , hbmMask  := NumGet(ii, 8+A_PtrSize, "ptr")    ; x86:12, x64:16
         , hbmColor := NumGet(ii, 8+2*A_PtrSize, "ptr")  ; x86:16, x64:24

      ; struct BITMAP - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmap
      bm := BufferAlloc(16+2*A_PtrSize) ; sizeof(BITMAP) = 24, 32
      DllCall("GetObject", "ptr", hbmMask, "int", bm.size, "ptr", bm)
         , width  := NumGet(bm, 4, "uint")
         , height := NumGet(bm, 8, "uint") / (hbmColor ? 1 : 2) ; Black and White cursors have doubled height.

      ; Clean up these hBitmaps.
      DllCall("DeleteObject", "ptr", hbmMask)
      DllCall("DeleteObject", "ptr", hbmColor)

      ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      bi := BufferAlloc(40, 0)                 ; sizeof(bi) = 40
         , NumPut(  "uint",        40, bi,  0) ; Size
         , NumPut(   "int",     width, bi,  4) ; Width
         , NumPut(   "int",   -height, 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")

      ; This is the 32-bit ARGB pBitmap (different from an hBitmap) that will receive the final converted pixels.
      DllCall("gdiplus\GdipCreateBitmapFromScan0"
               , "int", width, "int", height, "int", 0, "int", 0x26200A, "ptr", 0, "ptr*", pBitmap:=0)

      ; Create a Scan0 buffer pointing to pBits. The buffer has pixel format pARGB.
      Rect := BufferAlloc(16, 0)               ; sizeof(Rect) = 16
         , NumPut(  "uint",   width, Rect,  8) ; Width
         , NumPut(  "uint",  height, Rect, 12) ; Height
      BitmapData := BufferAlloc(16+2*A_PtrSize, 0)      ; sizeof(BitmapData) = 24, 32
         , NumPut(  "uint",      width, BitmapData,  0) ; Width
         , NumPut(  "uint",     height, BitmapData,  4) ; Height
         , NumPut(   "int",  4 * width, BitmapData,  8) ; Stride
         , NumPut(   "int",    0xE200B, BitmapData, 12) ; PixelFormat
         , NumPut(   "ptr",      pBits, BitmapData, 16) ; Scan0

      ; Use LockBits to create a writable buffer that converts pARGB to ARGB.
      DllCall("gdiplus\GdipBitmapLockBits"
               ,    "ptr", pBitmap
               ,    "ptr", Rect
               ,   "uint", 6            ; ImageLockMode.UserInputBuffer | ImageLockMode.WriteOnly
               ,    "int", 0xE200B      ; Format32bppPArgb
               ,    "ptr", BitmapData)  ; Contains the pointer (pBits) to the hbm.

      ; Don't use DI_DEFAULTSIZE to draw the icon like DrawIcon does as it will resize to 32 x 32.
      DllCall("DrawIconEx"
               , "ptr", hdc,     "int", 0, "int", 0
               , "ptr", image, "int", 0, "int", 0
               , "uint", 0, "ptr", 0, "uint", 0x1 | 0x2 | 0x4) ; DI_MASK | DI_IMAGE | DI_COMPAT

      ; Convert the pARGB pixels copied into the device independent bitmap (hbm) to ARGB.
      DllCall("gdiplus\GdipBitmapUnlockBits", "ptr", pBitmap, "ptr", BitmapData)

      ; Cleanup the hBitmap and device contexts.
      DllCall("SelectObject", "ptr", hdc, "ptr", obm)
      DllCall("DeleteObject", "ptr", hbm)
      DllCall("DeleteDC",     "ptr", hdc)

      return pBitmap
   }

   static from_bitmap(ByRef image) {
      DllCall("gdiplus\GdipCloneImage", "ptr", image, "ptr*", pBitmap:=0)
      return pBitmap
   }

   static from_stream(ByRef image) {
      DllCall("gdiplus\GdipCreateBitmapFromStream", "ptr", image, "ptr*", pBitmap:=0)
      return pBitmap
   }

   static from_RandomAccessStream(ByRef image) {
      ; Get the Class ID from a GUID string.
      CLSID := BufferAlloc(16, 0)
      if result := DllCall("ole32\CLSIDFromString", "wstr", "{0000000C-0000-0000-C000-000000000046}", "ptr", CLSID, "uint")
         throw Exception("CLSIDFromString failed. Error: " . Format("{:#x}", result))

      ; Convert RandomAccessStream to stream.
      DllCall("ShCore\CreateStreamOverRandomAccessStream", "ptr", image, "ptr", CLSID, "ptr*", pStream:=0, "uint")

      ; Read stream to pBitmap.
      DllCall("gdiplus\GdipCreateBitmapFromStream", "ptr", pStream, "ptr*", pBitmap:=0)

      ; Manually free the pointer to an IStream.
      ObjRelease(pStream)

      return pBitmap
   }

   static from_base64(ByRef image) {
      ; Trim whitespace and remove header.
      image := Trim(image)
      image := RegExReplace(image, "^data:image\/[a-z]+;base64,")

      ; Converts the image to binary data by first asking for the size.
      DllCall("crypt32\CryptStringToBinary"
               , "ptr", StrPtr(image), "uint", 0, "uint", 0x1, "ptr",   0, "uint*", size:=0, "ptr", 0, "ptr", 0)
      bin := BufferAlloc(size, 0)
      DllCall("crypt32\CryptStringToBinary"
               , "ptr", StrPtr(image), "uint", 0, "uint", 0x1, "ptr", bin, "uint*", size   , "ptr", 0, "ptr", 0)

      ; Makes a stream for conversion into a pBitmap.
      pStream := DllCall("shlwapi\SHCreateMemStream", "ptr", bin, "uint", size, "ptr")
      DllCall("gdiplus\GdipCreateBitmapFromStream", "ptr", pStream, "ptr*", pBitmap:=0)
      ObjRelease(pStream)

      return pBitmap
   }

   static from_sprite(ByRef image) {
      ; Create a source pBitmap and extract the width and height.
      if DllCall("gdiplus\GdipCreateBitmapFromFile", "wstr", image, "ptr*", sBitmap:=0)
         if !(sBitmap := this.from_url(image))
            throw Exception("Could not be loaded from a valid file path or URL.")

      ; Get Bitmap width and height.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", sBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", sBitmap, "uint*", height:=0)

      ; Create a destination pBitmap in 32-bit ARGB and get its device context though GDI+.
      ; Note that a device context from a graphics context can only be drawn on, not read.
      ; Also note that using a graphics context and blitting does not create a pixel perfect image.
      ; Using a DIB and LockBits is about 5% faster.
      DllCall("gdiplus\GdipCreateBitmapFromScan0"
               , "int", width, "int", height, "int", 0, "int", 0x26200A, "ptr", 0, "ptr*", dBitmap:=0)
      DllCall("gdiplus\GdipGetImageGraphicsContext", "ptr", dBitmap, "ptr*", dGraphics:=0)
      DllCall("gdiplus\GdipGetDC", "ptr", dGraphics, "ptr*", ddc:=0)

      ; Keep any existing transparency for whatever reason.
      hBitmap := this.put_hBitmap(sBitmap) ; Could copy this code here for even more speed.

      ; Create a source device context and associate the source hBitmap.
      sdc := DllCall("CreateCompatibleDC", "ptr", ddc, "ptr")
      obm := DllCall("SelectObject", "ptr", sdc, "ptr", hBitmap, "ptr")

      ; Copy the image making the top-left pixel the color key.
      DllCall("msimg32\TransparentBlt"
               , "ptr", ddc, "int", 0, "int", 0, "int", width, "int", height  ; destination
               , "ptr", sdc, "int", 0, "int", 0, "int", width, "int", height  ; source
               , "uint", DllCall("GetPixel", "ptr", sdc, "int", 0, "int", 0)) ; RGB pixel.

      ; Cleanup the hBitmap and device contexts.
      DllCall("SelectObject", "ptr", sdc, "ptr", obm)
      DllCall("DeleteObject", "ptr", hBitmap)
      DllCall("DeleteDC",     "ptr", sdc)

      ; Release the graphics context and delete.
      DllCall("gdiplus\GdipReleaseDC", "ptr", dGraphics, "ptr", ddc)
      DllCall("gdiplus\GdipDeleteGraphics", "ptr", dGraphics)

      return dBitmap
   }

   static put_clipboard(ByRef pBitmap) {
      ; Standard Clipboard Formats - https://docs.microsoft.com/en-us/windows/win32/dataxchg/standard-clipboard-formats
      ; Synthesized Clipboard Formats - https://docs.microsoft.com/en-us/windows/win32/dataxchg/clipboard-formats

      ; Open the clipboard.
      Loop 6 ; Try this 6 times.
         if (A_Index > 1)
            Sleep (2**(A_Index-2) * 30)
      until (result := DllCall("OpenClipboard", "ptr", 0))
      if !(result)
         throw Exception("Clipboard could not be opened.")

      ; Clear the clipboard.
      DllCall("EmptyClipboard")

      ; #1 - Place the image onto the clipboard as a PNG stream.
      ; Thanks Jochen Arndt - https://www.codeproject.com/Answers/1207927/Saving-an-image-to-the-clipboard#answer3
      pStream := this.put_stream(pBitmap, "png")
      DllCall("ole32\GetHGlobalFromStream", "ptr", pStream, "uint*", hData:=0)
      DllCall("SetClipboardData", "uint", DllCall("RegisterClipboardFormat", "str", "png", "uint"), "ptr", hData)
      ObjRelease(pStream)

      ; #2 - Place the image onto the clipboard in the CF_DIB format in ARGB using 3 color masks. (Extra 12 byte offset.)
      ; Thanks Nyerguds - https://stackoverflow.com/a/46424800

      ; Get Bitmap width, height, and format.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0)
      DllCall("gdiplus\GdipGetImagePixelFormat", "ptr", pBitmap, "uint*", format:=0)

      ; Get Bitmap bits per pixel, stride, and size.
      bpp := (format & 0x00FF00) >> 8
      stride := (bpp >> 3) * width
      size := stride * height

      ; struct DIBSECTION - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-dibsection
      ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
      hdib := DllCall("GlobalAlloc", "uint", 0x42, "uptr", 40 + 12 + size, "ptr")
      pdib := DllCall("GlobalLock", "ptr", hdib, "ptr")
         , NumPut(  "uint",         40, pdib,  0) ; Size
         , NumPut(   "int",      width, pdib,  4) ; Width
         , NumPut(   "int",    -height, pdib,  8) ; Height - Negative so (0, 0) is top-left.
         , NumPut("ushort",          1, pdib, 12) ; Planes
         , NumPut("ushort",        bpp, pdib, 14) ; BitCount / BitsPerPixel
         , NumPut(  "uint",        0x3, pdib, 16) ; Compression
         , NumPut(  "uint",       size, pdib, 20) ; SizeImage (bytes)
         ; The following bitfields when masked extract the respective color channels.
         , NumPut(  "uint", 0x00FF0000, pdib, 40) ; Red
         , NumPut(  "uint", 0x0000FF00, pdib, 44) ; Green
         , NumPut(  "uint", 0x000000FF, pdib, 48) ; Blue

      ; Transfer data from source pBitmap to the global memory manually.
      Rect := BufferAlloc(16, 0)               ; sizeof(Rect) = 16
         , NumPut(  "uint",   width, Rect,  8) ; Width
         , NumPut(  "uint",  height, Rect, 12) ; Height
      BitmapData := BufferAlloc(16+2*A_PtrSize, 0)      ; sizeof(BitmapData) = 24, 32
         , NumPut(  "uint",      width, BitmapData,  0) ; Width
         , NumPut(  "uint",     height, BitmapData,  4) ; Height
         , NumPut(   "int",     stride, BitmapData,  8) ; Stride
         , NumPut(   "int",     format, BitmapData, 12) ; PixelFormat
         , NumPut(   "ptr",  pdib + 52, BitmapData, 16) ; Scan0
      DllCall("gdiplus\GdipBitmapLockBits"
               ,    "ptr", pBitmap
               ,    "ptr", Rect
               ,   "uint", 5            ; ImageLockMode.UserInputBuffer | ImageLockMode.ReadOnly
               ,    "int", 0x26200A     ; Format32bppArgb
               ,    "ptr", BitmapData)  ; Contains the pointer (pdib) to the hData.
      DllCall("gdiplus\GdipBitmapUnlockBits", "ptr", pBitmap, "ptr", BitmapData)

      ; Unlock the memory as it is complete.
      DllCall("GlobalUnlock", "ptr", hdib)

      ; Add CF_DIB as a format to the clipboard.
      DllCall("SetClipboardData", "uint", 8, "ptr", hdib)

      ; Close the clipboard.
      DllCall("CloseClipboard")

      return ClipboardAll()
   }

   static put_buffer(ByRef pBitmap) {
      buffer := {pBitmap: pBitmap}
         .DefineMethod("__New" , (self) => (this.gdiplusStartup(), self)) ; Increment GDI+ reference count
         .DefineMethod("__Delete", (self) => (this.gdiplusShutdown("smart_pointer", self.pBitmap)))
         .__New()  ; On deletion the buffer object will dispose of the bitmap. And it will decrement this.gdiplus.
      return buffer
   }

   static put_screenshot(ByRef pBitmap, screenshot := "", alpha := "") {
      ; Get Bitmap width and height.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0)

      x := (IsObject(screenshot) && screenshot[1] != "") ? screenshot[1] : Round((A_ScreenWidth - width) / 2)
      y := (IsObject(screenshot) && screenshot[2] != "") ? screenshot[2] : Round((A_ScreenHeight - height) / 2)
      w := (IsObject(screenshot) && screenshot[3] != "") ? screenshot[3] : width
      h := (IsObject(screenshot) && screenshot[4] != "") ? screenshot[4] : height

      ; Convert the Bitmap to a hBitmap and associate a device context for blitting.
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      hbm := this.put_hBitmap(pBitmap, alpha)
      obm := DllCall("SelectObject", "ptr", hdc, "ptr", hbm, "ptr")

      ; Retrieve the device context for the screen.
      ddc := DllCall("GetDC", "ptr", 0, "ptr")

      ; Copies a portion of the screen to a new device context.
      DllCall("gdi32\StretchBlt"
               , "ptr", ddc, "int", x, "int", y, "int", w,     "int", h
               , "ptr", hdc, "int", 0, "int", 0, "int", width, "int", height
               , "uint", 0x00CC0020) ; SRCCOPY

      ; Release the device context to the screen.
      DllCall("ReleaseDC", "ptr", 0, "ptr", ddc)

      ; Cleanup the hBitmap and device contexts.
      DllCall("SelectObject", "ptr", hdc, "ptr", obm)
      DllCall("DeleteObject", "ptr", hbm)
      DllCall("DeleteDC",     "ptr", hdc)

      return [x,y,w,h]
   }

   static put_desktop(ByRef pBitmap) {
      ; Thanks Gerald Degeneve - https://www.codeproject.com/Articles/856020/Draw-Behind-Desktop-Icons-in-Windows-plus

      ; Get Bitmap width and height.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0)

      ; Convert the Bitmap to a hBitmap and associate a device context for blitting.
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      hbm := this.put_hBitmap(pBitmap)
      obm := DllCall("SelectObject", "ptr", hdc, "ptr", hbm, "ptr")

      ; Post-Creator's Update Windows 10. WM_SPAWN_WORKER = 0x052C
      DllCall("SendMessage", "ptr", WinExist("ahk_class Progman"), "uint", 0x052C, "ptr", 0x0000000D, "ptr", 0)
      DllCall("SendMessage", "ptr", WinExist("ahk_class Progman"), "uint", 0x052C, "ptr", 0x0000000D, "ptr", 1)

      ; Find the child window.
      windows := WinGetList("ahk_class WorkerW")
      Loop windows.length
         hwnd := windows[A_Index]
      until DllCall("FindWindowEx", "ptr", hwnd, "ptr", 0, "str", "SHELLDLL_DefView", "ptr", 0)

      ; Maybe this hack gets patched. Tough luck!
      if !(WorkerW := DllCall("FindWindowEx", "ptr", 0, "ptr", hwnd, "str", "WorkerW", "ptr", 0, "ptr"))
         throw Exception("Could not locate hidden window behind desktop.")

      ; Position the image in the center. This line can be removed.
      DllCall("SetWindowPos", "ptr", WorkerW, "ptr", 1
               , "int", Round((A_ScreenWidth - width) / 2)   ; x coordinate
               , "int", Round((A_ScreenHeight - height) / 2) ; y coordinate
               , "int", width, "int", height, "uint", 0)

      ; Get device context of spawned window.
      ddc := DllCall("GetDCEx", "ptr", WorkerW, "ptr", 0, "int", 0x403, "ptr") ; LockWindowUpdate | Cache | Window

      ; Copies a portion of the screen to a new device context.
      DllCall("gdi32\BitBlt"
               , "ptr", ddc, "int", 0, "int", 0, "int", width, "int", height
               , "ptr", hdc, "int", 0, "int", 0, "uint", 0x00CC0020) ; SRCCOPY

      ; Release device context of spawned window.
      DllCall("ReleaseDC", "ptr", 0, "ptr", ddc)

      ; Cleanup the hBitmap and device contexts.
      DllCall("SelectObject", "ptr", hdc, "ptr", obm)
      DllCall("DeleteObject", "ptr", hbm)
      DllCall("DeleteDC",     "ptr", hdc)

      return "desktop"
   }

   static put_wallpaper(ByRef pBitmap) {
      ; Create a temporary image file.
      filepath := this.put_file(pBitmap)

      ; Get the absolute path of the file.
      length := DllCall("GetFullPathName", "str", filepath, "uint", 0, "ptr", 0, "ptr", 0, "uint")
      VarSetStrCapacity(buf, length)
      DllCall("GetFullPathName", "str", filepath, "uint", length, "str", buf, "ptr", 0, "uint")

      ; Keep waiting until the file has been created. (It should be instant!)
      Loop 6 ; Try this 6 times.
         if (A_Index > 1)
            Sleep (2**(A_Index-2) * 30)
      until FileExist(filepath)
      if !FileExist(filepath)
         throw Exception("Unable to create temporary image file.")

      ; Set the temporary image file as the new desktop wallpaper.
      DllCall("SystemParametersInfo", "uint", 20, "uint", 0, "str", buf, "uint", 2)

      ; This is a delayed delete call. #Persistent may be required on v1.
      DeleteFile := Func("DllCall").Bind("DeleteFile", "str", filepath)
      SetTimer DeleteFile, -2000

      return "wallpaper"
   }

   static put_cursor(ByRef pBitmap, xHotspot := "", yHotspot := "") {
      ; Thanks Nick - https://stackoverflow.com/a/550965

      ; Creates an icon that can be used as a cursor.
      DllCall("gdiplus\GdipCreateHICONFromBitmap", "ptr", pBitmap, "ptr*", hIcon:=0)

      ; Sets the hotspot of the cursor by changing the icon into a cursor.
      if (xHotspot != "" || yHotspot != "") {
         ; struct ICONINFO - https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-iconinfo
         ii := BufferAlloc(8+3*A_PtrSize, 0)                          ; sizeof(ICONINFO) = 20, 32
         DllCall("GetIconInfo", "ptr", hIcon, "ptr", ii)              ; Fill the ICONINFO structure.
            , NumPut("uint", false, ii, 0)                            ; true/false are icon/cursor respectively.
            , (xHotspot != "") ? NumPut("uint", xHotspot, ii, 4) : "" ; Set the xHotspot value. (Default: center point)
            , (yHotspot != "") ? NumPut("uint", yHotspot, ii, 8) : "" ; Set the yHotspot value. (Default: center point)
         DllCall("DestroyIcon", "ptr", hIcon)                         ; Destroy the icon after getting the ICONINFO structure.
         hIcon := DllCall("CreateIconIndirect", "ptr", ii, "ptr")     ; Create a new cursor using ICONINFO.

         ; Clean up hbmMask and hbmColor created as a result of GetIconInfo.
         DllCall("DeleteObject", "ptr", NumGet(ii, 8+A_PtrSize, "ptr"))   ; hbmMask
         DllCall("DeleteObject", "ptr", NumGet(ii, 8+2*A_PtrSize, "ptr")) ; hbmColor
      }

      ; Loop over all 16 system cursors and change them all to the new cursor.
      SystemCursors := "32512,32513,32514,32515,32516,32640,32641,32642,32643,32644,32645,32646,32648,32649,32650,32651"
      Loop Parse, SystemCursors, ","
      { ; Must copy the handle 16 times as SetSystemCursor deletes the handle 16 times.
         hCursor := DllCall("CopyImage", "ptr", hIcon, "uint", 2, "int", 0, "int", 0, "uint", 0, "ptr")
         DllCall("SetSystemCursor", "ptr", hCursor, "int", A_LoopField) ; calls DestroyCursor
      }

      ; Destroy the original hIcon. DestroyCursor and DestroyIcon are the same function in C.
      DllCall("DestroyCursor", "ptr", hIcon)

      ; Returns the word A_Cursor so that it doesn't evaluate immediately.
      return "A_Cursor"
   }

   static put_file(ByRef pBitmap, filepath := "", quality := "") {
      ; Thanks tic - https://www.autohotkey.com/boards/viewtopic.php?t=6517

      ; Seperate the filepath and default the extension to PNG.
      SplitPath filepath,, directory, extension, filename
      filename := (filename != "") ? filename : "___date___"
      extension := (extension ~= "^(?i:bmp|dib|rle|jpg|jpeg|jpe|jfif|gif|tif|tiff|png)$") ? extension : "png"
      filepath := directory . filename "." extension

      ; Fill a buffer with the available encoders.
      DllCall("gdiplus\GdipGetImageEncodersSize", "uint*", count:=0, "uint*", size:=0)
      ci := BufferAlloc(size)
      DllCall("gdiplus\GdipGetImageEncoders", "uint", count, "uint", size, "ptr", ci)
      if !(count && size)
         throw Exception("Could not get a list of image codec encoders on this system.")

      ; Search for an encoder with a matching extension.
      Loop count
         EncoderExtensions := StrGet(NumGet(ci, (idx:=(48+7*A_PtrSize)*(A_Index-1))+32+3*A_PtrSize, "uptr"), "UTF-16")
      until InStr(EncoderExtensions, "*." extension)

      ; Get the pointer to the index/offset of the matching encoder.
      if !(pCodec := ci.ptr + idx)
         throw Exception("Could not find a matching encoder for the specified file format.")

      ; JPEG is a lossy image format that requires a quality value from 0-100. Default quality is 75.
      if (extension ~= "^(?i:jpg|jpeg|jpe|jfif)$"
      && IsInteger(quality) && 0 <= quality && quality <= 100 && quality != 75) {
         DllCall("gdiplus\GdipGetEncoderParameterListSize", "ptr", pBitmap, "ptr", pCodec, "uint*", size:=0)
         EncoderParameters := BufferAlloc(size, 0)
         DllCall("gdiplus\GdipGetEncoderParameterList", "ptr", pBitmap, "ptr", pCodec, "uint", size, "ptr", EncoderParameters)

         ; Search for an encoder parameter with 1 value of type 6.
         Loop NumGet(EncoderParameters, "uint")
            elem := (24+A_PtrSize)*(A_Index-1) + A_PtrSize
         until (NumGet(EncoderParameters, elem+16, "uint") = 1) && (NumGet(EncoderParameters, elem+20, "uint") = 6)

         ; struct EncoderParameter - http://www.jose.it-berater.org/gdiplus/reference/structures/encoderparameter.htm
         ep := EncoderParameters.ptr + elem - A_PtrSize                  ; sizeof(EncoderParameter) = 28, 32
            , NumPut(  "uptr",       1, ep)                              ; Must be 1.
            , NumPut(  "uint",       4, ep, 20+A_PtrSize)                ; Type
            , NumPut(  "uint", quality, NumGet(ep+24+A_PtrSize, "uptr")) ; Value (pointer)
      }

      ; Write the file to disk using the specified encoder and encoding parameters.
      Loop 6 ; Try this 6 times.
         if (A_Index > 1)
            Sleep (2**(A_Index-2) * 30)
      until (result := !DllCall("gdiplus\GdipSaveImageToFile", "ptr", pBitmap, "wstr", filepath, "ptr", pCodec, "uint", IsSet(ep) ? ep : 0))
      if !(result)
         throw Exception("Could not save file to disk.")

      ; If the filename was omitted, replace it with the current time (accurate to the second).
      ; Multiple files that are created within 1 second will be overwritten with the last file.
      ; The replacement colon is called a Modifier Letter Colon found at <U+A789>.
      if (filename == "___date___") {
         filename := FileGetTime(filepath)
         filename := FormatTime(filename, "yyyy-MM-dd HH꞉mm꞉ss")
         FileMove(filepath, directory . filename "." extension, true)
         filepath := directory . filename "." extension
      }

      return filepath
   }

   static put_hBitmap(ByRef pBitmap, alpha := "") {
      ; Revert to built in functionality if a replacement color is declared.
      if (alpha != "") { ; This built-in version is about 25% slower.
         DllCall("gdiplus\GdipCreateHBITMAPFromBitmap", "ptr", pBitmap, "ptr*", hBitmap:=0, "uint", alpha)
         return hBitmap
      }

      ; Get Bitmap width and height.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0)

      ; 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 := BufferAlloc(40, 0)                 ; sizeof(bi) = 40
         , NumPut(  "uint",        40, bi,  0) ; Size
         , NumPut(   "int",     width, bi,  4) ; Width
         , NumPut(   "int",   -height, 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")

      ; Transfer data from source pBitmap to an hBitmap manually.
      Rect := BufferAlloc(16, 0)               ; sizeof(Rect) = 16
         , NumPut(  "uint",   width, Rect,  8) ; Width
         , NumPut(  "uint",  height, Rect, 12) ; Height
      BitmapData := BufferAlloc(16+2*A_PtrSize, 0)      ; sizeof(BitmapData) = 24, 32
         , NumPut(  "uint",      width, BitmapData,  0) ; Width
         , NumPut(  "uint",     height, BitmapData,  4) ; Height
         , NumPut(   "int",  4 * width, BitmapData,  8) ; Stride
         , NumPut(   "int",    0xE200B, BitmapData, 12) ; PixelFormat
         , 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)

      ; Cleanup the hBitmap and device contexts.
      DllCall("SelectObject", "ptr", hdc, "ptr", obm)
      DllCall("DeleteDC",     "ptr", hdc)

      return hbm
   }

   static put_hIcon(ByRef pBitmap) {
      DllCall("gdiplus\GdipCreateHICONFromBitmap", "ptr", pBitmap, "ptr*", hIcon:=0)
      return hIcon
   }

   static put_stream(ByRef pBitmap, extension := "", quality := "") {
      ; Default extension is TIF for fast speeds!
      if !(extension ~= "^(?i:bmp|dib|rle|jpg|jpeg|jpe|jfif|gif|tif|tiff|png)$")
         extension := "tif"

      ; Fill a buffer with the available encoders.
      DllCall("gdiplus\GdipGetImageEncodersSize", "uint*", count:=0, "uint*", size:=0)
      ci := BufferAlloc(size)
      DllCall("gdiplus\GdipGetImageEncoders", "uint", count, "uint", size, "ptr", ci)
      if !(count && size)
         throw Exception("Could not get a list of image codec encoders on this system.")

      ; Search for an encoder with a matching extension.
      Loop count
         EncoderExtensions := StrGet(NumGet(ci, (idx:=(48+7*A_PtrSize)*(A_Index-1))+32+3*A_PtrSize, "uptr"), "UTF-16")
      until InStr(EncoderExtensions, "*." extension)

      ; Get the pointer to the index/offset of the matching encoder.
      if !(pCodec := ci.ptr + idx)
         throw Exception("Could not find a matching encoder for the specified file format.")

      ; JPEG is a lossy image format that requires a quality value from 0-100. Default quality is 75.
      if (extension ~= "^(?i:jpg|jpeg|jpe|jfif)$"
      && IsInteger(quality) && 0 <= quality && quality <= 100 && quality != 75) {
         DllCall("gdiplus\GdipGetEncoderParameterListSize", "ptr", pBitmap, "ptr", pCodec, "uint*", size:=0)
         EncoderParameters := BufferAlloc(size, 0)
         DllCall("gdiplus\GdipGetEncoderParameterList", "ptr", pBitmap, "ptr", pCodec, "uint", size, "ptr", EncoderParameters)

         ; Search for an encoder parameter with 1 value of type 6.
         Loop NumGet(EncoderParameters, "uint")
            elem := (24+A_PtrSize)*(A_Index-1) + A_PtrSize
         until (NumGet(EncoderParameters, elem+16, "uint") = 1) && (NumGet(EncoderParameters, elem+20, "uint") = 6)

         ; struct EncoderParameter - http://www.jose.it-berater.org/gdiplus/reference/structures/encoderparameter.htm
         ep := EncoderParameters.ptr + elem - A_PtrSize                  ; sizeof(EncoderParameter) = 28, 32
            , NumPut(  "uptr",       1, ep)                              ; Must be 1.
            , NumPut(  "uint",       4, ep, 20+A_PtrSize)                ; Type
            , NumPut(  "uint", quality, NumGet(ep+24+A_PtrSize, "uptr")) ; Value (pointer)
      }

      ; Create a Stream.
      DllCall("ole32\CreateStreamOnHGlobal", "ptr", 0, "int", true, "ptr*", pStream:=0)
      DllCall("gdiplus\GdipSaveImageToStream", "ptr", pBitmap, "ptr", pStream, "ptr", pCodec, "uint", IsSet(ep) ? ep : 0)

      return pStream
   }

   static put_RandomAccessStream(ByRef pBitmap, extension := "", quality := "") {
      ; Thanks teadrinker - https://www.autohotkey.com/boards/viewtopic.php?f=6&t=72674

      ; Which is faster, bmp or png?
      pStream := this.put_stream(pBitmap, extension, quality)

      ; Get the Class ID from a GUID string.
      CLSID := BufferAlloc(16, 0)
      if result := DllCall("ole32\CLSIDFromString", "wstr", "{905A0FE1-BC53-11DF-8C49-001E4FC686DA}", "ptr", CLSID, "uint")
         throw Exception("CLSIDFromString failed. Error: " . Format("{:#x}", result))

      ; Create a RandomAccessStream
      DllCall("ShCore\CreateRandomAccessStreamOverStream", "ptr", pStream, "uint", 1, "ptr", CLSID, "ptr*", pRandomAccessStream:=0, "uint")

      ; The handle to the stream object is automatically freed when the stream object is released.
      ObjRelease(pStream)

      return pRandomAccessStream
   }

   static put_base64(ByRef pBitmap, extension := "", quality := "") {
      ; Thanks noname - https://www.autohotkey.com/boards/viewtopic.php?style=7&p=144247#p144247

      ; Default extension is PNG for small sizes!
      if !(extension ~= "^(?i:bmp|dib|rle|jpg|jpeg|jpe|jfif|gif|tif|tiff|png)$")
         extension := "png"

      pStream := this.put_stream(pBitmap, extension, quality)

      DllCall("ole32\GetHGlobalFromStream", "ptr", pStream, "uint*", hData:=0)
      pData := DllCall("GlobalLock", "ptr", hData, "ptr")
      nSize := DllCall("GlobalSize", "uint", pData)

      bin := BufferAlloc(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*", length:=0)
      base64 := BufferAlloc(length, 0)
      DllCall("Crypt32.dll\CryptBinaryToStringA", "ptr", bin, "uint", nSize, "uint", 0x40000001, "ptr", base64, "uint*", length)

      return StrGet(base64, length, "CP0")
   }

   static put_hex(ByRef pBitmap, extension := "", quality := "") {
      ; Default extension is PNG for small sizes!
      if !(extension ~= "^(?i:bmp|dib|rle|jpg|jpeg|jpe|jfif|gif|tif|tiff|png)$")
         extension := "png"

      pStream := this.put_stream(pBitmap, extension, quality)

      DllCall("ole32\GetHGlobalFromStream", "ptr", pStream, "uint*", hData:=0)
      pData := DllCall("GlobalLock", "ptr", hData, "ptr")
      nSize := DllCall("GlobalSize", "uint", pData)

      bin := BufferAlloc(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", 0x4000000c, "ptr", 0, "uint*", length:=0)
      hex := BufferAlloc(length, 0)
      DllCall("Crypt32.dll\CryptBinaryToStringA", "ptr", bin, "uint", nSize, "uint", 0x4000000c, "ptr", hex, "uint*", length)

      return StrGet(hex, length, "CP0")
   }

   ; All references to gdiplus and pToken must be absolute!
   static gdiplus := 0, pToken := 0

   static gdiplusStartup() {
      ImagePut.gdiplus++

      ; Startup gdiplus when counter goes from 0 -> 1.
      if (ImagePut.gdiplus == 1) {
         DllCall("LoadLibrary", "str", "gdiplus")
         si := BufferAlloc(A_PtrSize = 8 ? 24 : 16, 0) ; sizeof(GdiplusStartupInput) = 16, 24
            , NumPut("uint", 0x1, si)
         DllCall("gdiplus\GdiplusStartup", "ptr*", pToken:=0, "ptr", si, "ptr", 0)
         ImagePut.pToken := pToken
      }
   }

   static gdiplusShutdown(cotype := "", ByRef pBitmap := "") {
      ImagePut.gdiplus--

      ; When a buffer object is deleted a bitmap is sent here for disposal.
      if (cotype == "smart_pointer")
         if DllCall("gdiplus\GdipDisposeImage", "ptr", pBitmap)
            throw Exception("The bitmap of this buffer object has already been deleted.")

      ; Check for unpaired calls of gdiplusShutdown. 
      if (ImagePut.gdiplus < 0)
         throw Exception("Missing ImagePut.gdiplusStartup().")

      ; Shutdown gdiplus when counter goes from 1 -> 0.
      if (ImagePut.gdiplus == 0) {
         DllCall("gdiplus\GdiplusShutdown", "ptr", ImagePut.pToken)
         DllCall("FreeLibrary", "ptr", DllCall("GetModuleHandle", "str", "gdiplus", "ptr"))

         ; Exit if GDI+ is still loaded. GdiplusNotInitialized = 18
         if (18 != DllCall("gdiplus\GdipCreateImageAttributes", "ptr*", ImageAttr:=0)) {
            DllCall("gdiplus\GdipDisposeImageAttributes", "ptr", ImageAttr)
            return
         }

         ; Otherwise GDI+ has been truly unloaded from the script and objects are out of scope.
         if (cotype = "bitmap")
            throw Exception("Out of scope error. `n`nIf you wish to handle raw pointers to GDI+ bitmaps, add the line"
               . "`n`n`t`t" this.prototype.__class ".gdiplusStartup()`n`nor 'pToken := Gdip_Startup()' to the top of your script."
               . "`nAlternatively, use 'obj := ImagePutBuffer()' with 'obj.pBitmap'."
               . "`nYou can copy this message by pressing Ctrl + C.")
      }
   }
} ; End of ImagePut class.
  • Added ImagePutHex(). Returns a hexadecimal string of an image in the specified file format. (png is the default.)

iseahound
Posts: 608
Joined: 13 Aug 2016, 21:04
GitHub: iseahound

Re: [a122] ImagePut - Windows Image Transformation Library

Post by iseahound » 15 Sep 2020, 10:20

a122 compatible

Code: Select all

; Script:    ImagePut.ahk
; Author:    iseahound
; License:   MIT License
; Version:   2020-05-22
; Release:   2020-09-14

; ImagePut - Windows Image Transformation Library
; Copy and paste functions from this reference libary as you wish.
; -> All put_XXX functions map from a pBitmap to XXX.
; -> All from_XXX functions map from XXX to a pBitmap.
; Or use ImagePut functions as part of your standard libary.


; Puts the image into a file format and returns a base64 encoded string.
;   extension  -  File Encoding           |  string   ->   bmp, gif, jpg, png, tiff
;   quality    -  JPEG Quality Level      |  integer  ->   0 - 100
ImagePutBase64(ByRef image, extension := "", quality := "")
   => ImagePut("base64", image,,, extension, quality)


; Puts the image into a GDI+ Bitmap and returns a pointer.
ImagePutBitmap(ByRef image)
   => ImagePut("bitmap", image)


; Puts the image into a GDI+ Bitmap and returns a buffer object with GDI+ scope.
ImagePutBuffer(ByRef image)
   => ImagePut("buffer", image)


; Puts the image onto the clipboard and returns ClipboardAll().
ImagePutClipboard(ByRef image)
   => ImagePut("clipboard", image)


; Puts the image as the cursor and returns the variable A_Cursor.
;   xHotspot   -  X Click Point           |  pixel    ->   0 - width
;   yHotspot   -  Y Click Point           |  pixel    ->   0 - height
ImagePutCursor(ByRef image, xHotspot := "", yHotspot := "")
   => ImagePut("cursor", image,,, xHotspot, yHotspot)


; Puts the image behind the desktop icons and returns the string "desktop".
;   scale      -  Scale Factor            |  real     ->   A_ScreenHeight / height.
ImagePutDesktop(ByRef image, scale := 1)
   => ImagePut("desktop", image,, scale)


; Puts the image into a file and returns a relative filepath.
;   filepath   -  Filepath + Extension    |  string   ->   *.bmp, *.gif, *.jpg, *.png, *.tiff
;   quality    -  JPEG Quality Level      |  integer  ->   0 - 100
ImagePutFile(ByRef image, filepath := "", quality := "")
   => ImagePut("file", image,,, filepath, quality)


; Puts the image into a device independent bitmap and returns the handle.
;   alpha      -  Alpha Replacement Color |  RGB      ->   0xFFFFFF
ImagePutHBitmap(ByRef image, alpha := "")
   => ImagePut("hBitmap", image,,, alpha)


; Puts the image into a file format and returns a hexadecimal encoded string.
;   extension  -  File Encoding           |  string   ->   bmp, gif, jpg, png, tiff
;   quality    -  JPEG Quality Level      |  integer  ->   0 - 100
ImagePutHex(ByRef image, extension := "", quality := "")
   => ImagePut("hex", image,,, extension, quality)


; Puts the image into an icon and returns the handle.
ImagePutHIcon(ByRef image)
   => ImagePut("hBitmap", image)


; Puts the image into a file format and returns a pointer to a RandomAccessStream.
;   extension  -  File Encoding           |  string   ->   bmp, gif, jpg, png, tiff
;   quality    -  JPEG Quality Level      |  integer  ->   0 - 100
ImagePutRandomAccessStream(ByRef image, extension := "", quality := "")
   => ImagePut("RandomAccessStream", image,,, extension, quality)


; Puts the image on the shared screen device context and returns an array of coordinates.
;   screenshot -  Screen Coordinates      |  array    ->   [x,y,w,h] or [0,0]
;   alpha      -  Alpha Replacement Color |  RGB      ->   0xFFFFFF
ImagePutScreenshot(ByRef image, screenshot := "", alpha := "")
   => ImagePut("screenshot", image,,, screenshot, alpha)


; Puts the image into a file format and returns a pointer to a stream.
;   extension  -  File Encoding           |  string   ->   bmp, gif, jpg, png, tiff
;   quality    -  JPEG Quality Level      |  integer  ->   0 - 100
ImagePutStream(ByRef image, extension := "", quality := "")
   => ImagePut("stream", image,,, extension, quality)


; Puts the image as the desktop wallpaper and returns the string "wallpaper".
ImagePutWallpaper(ByRef image)
   => ImagePut("wallpaper", image)


; Puts the image in a window and returns a handle to a window.
;   title      -  Window Caption Title    |  string   ->   MyTitle
ImagePutWindow(ByRef image, title := "")
   => ImagePut("window", image,,, title)



; ImagePut() - Puts an image from anywhere to anywhere.
;   cotype     -  Output Type             |  string   ->   Case Insensitive. Read documentation.
;   image      -  Input Image             |  image    ->   Anything. Refer to ImageType().
;   crop       -  Crop Coordinates        |  array    ->   [x,y,w,h] could be negative or percent.
;   scale      -  Scale Factor            |  real     ->   2.0
;   terms*     -  Additional Parameters   |  variadic ->   Extra parameters found in toCotype().
ImagePut(cotype, ByRef image, crop := "", scale := "", terms*)
   => ImagePut.call(cotype, image, crop, scale, terms*)



class ImagePut {

   static call(cotype, ByRef image, crop := "", scale := "", terms*) {

      this.gdiplusStartup()

      ; Take a guess as to what the image might be. (>90% accuracy!)
      try type := this.DontVerifyImageType(image)
      catch
         type := this.ImageType(image)

      ; Qualify additional parameters for correctness.
      _crop := IsObject(crop)
         && crop[1] ~= "^-?\d+(\.\d*)?%?$" && crop[2] ~= "^-?\d+(\.\d*)?%?$"
         && crop[3] ~= "^-?\d+(\.\d*)?%?$" && crop[4] ~= "^-?\d+(\.\d*)?%?$"
      _scale := scale != 1 && scale ~= "^\d+(\.\d+)?$"

      ; Make a copy of the image as a pBitmap.
      pBitmap := this.toBitmap(type, image)

      ; Crop the image.
      if (_crop) {
         pBitmap2 := this.BitmapCrop(pBitmap, crop)
         DllCall("gdiplus\GdipDisposeImage", "ptr", pBitmap)
         pBitmap := pBitmap2
      }

      ; Scale the image.
      if (_scale) {
         pBitmap2 := this.BitmapScale(pBitmap, scale)
         DllCall("gdiplus\GdipDisposeImage", "ptr", pBitmap)
         pBitmap := pBitmap2
      }

      ; Put the pBitmap to wherever the cotype specifies.
      coimage := this.toCotype(cotype, pBitmap, terms*)

      ; Clean up the pBitmap copy. Export raw pointers if requested.
      if !(cotype = "bitmap" || cotype = "buffer")
         DllCall("gdiplus\GdipDisposeImage", "ptr", pBitmap)

      this.gdiplusShutdown(cotype)

      return coimage
   }

   static DontVerifyImageType(ByRef image) {

      if !IsObject(image)
         throw Exception("Must be an object.")

      ; Check for image type declarations.
      ; Assumes that the user is telling the truth.

      if ObjHasOwnProp(image, "clipboard") {
         image := image.clipboard
         return "clipboard"
      }

      if ObjHasOwnProp(image, "object") {
         image := image.object
         return "object"
      }

      if ObjHasOwnProp(image, "buffer") {
         image := image.buffer
         return "buffer"
      }

      if ObjHasOwnProp(image, "screenshot") {
         image := image.screenshot
         return "screenshot"
      }

      if ObjHasOwnProp(image, "window") {
         image := image.window
         return "window"
      }

      if ObjHasOwnProp(image, "desktop") {
         image := image.desktop
         return "desktop"
      }

      if ObjHasOwnProp(image, "wallpaper") {
         image := image.wallpaper
         return "wallpaper"
      }

      if ObjHasOwnProp(image, "cursor") {
         image := image.cursor
         return "cursor"
      }

      if ObjHasOwnProp(image, "url") {
         image := image.url
         return "url"
      }

      if ObjHasOwnProp(image, "file") {
         image := image.file
         return "file"
      }

      if ObjHasOwnProp(image, "monitor") {
         image := image.monitor
         return "monitor"
      }

      if ObjHasOwnProp(image, "hBitmap") {
         image := image.hBitmap
         return "hBitmap"
      }

      if ObjHasOwnProp(image, "hIcon") {
         image := image.hIcon
         return "hIcon"
      }

      if ObjHasOwnProp(image, "bitmap") {
         image := image.bitmap
         return "bitmap"
      }

      if ObjHasOwnProp(image, "stream") {
         image := image.stream
         return "stream"
      }

      if ObjHasOwnProp(image, "RandomAccessStream") {
         image := image.RandomAccessStream
         return "RandomAccessStream"
      }

      if ObjHasOwnProp(image, "hex") {
         image := image.hex
         return "hex"
      }

      if ObjHasOwnProp(image, "base64") {
         image := image.base64
         return "base64"
      }

      if ObjHasOwnProp(image, "sprite") {
         image := image.sprite
         return "sprite"
      }

      throw Exception("Invalid type.")
   }

   static ImageType(ByRef image) {
      ; Throw if the image is an empty string.
      if (image == "")
         throw Exception("Image data is an empty string.")

      if IsObject(image) {
         ; A "clipboard" is a buffer object containing binary data returned by ClipboardAll()
         if (image.base.HasOwnProp("__class") && image.base.__class == "ClipboardAll")
            return "clipboard"

         ; An "object" is an object that implements a Bitmap() method returning a pointer to a GDI+ bitmap.
         if image.HasOwnMethod("Bitmap")
            return "object"

         ; A "buffer" is an AutoHotkey v2 buffer object.
         if image.HasOwnProp("pBitmap")
            return "buffer"

         ; A "screenshot" is an array of 4 numbers.
         if (image[1] ~= "^-?\d+$" && image[2] ~= "^-?\d+$" && image[3] ~= "^-?\d+$" && image[4] ~= "^-?\d+$")
            return "screenshot"
      }
         ; A "window" is anything considered a Window Title including ahk_class and "A".
         if WinExist(image)
            return "window"

         ; A "desktop" is a hidden window behind the desktop icons created by ImagePutDesktop.
         if (image = "desktop")
            return "desktop"

         ; A "wallpaper" is the desktop wallpaper.
         if (image = "wallpaper")
            return "wallpaper"

         ; A "cursor" is the name of a known cursor name.
         if (image ~= "(?i)^(IDC|OCR)?_?(A_Cursor|AppStarting|Arrow|Cross|Help|IBeam|"
         . "Icon|No|Size|SizeAll|SizeNESW|SizeNS|SizeNWSE|SizeWE|UpArrow|Wait|Unknown)$")
            return "cursor"

         ; A "url" satisfies the url format.
         if this.is_url(image)
            return "url"

         ; A "file" is stored on the disk or network.
         if FileExist(image)
            return "file"

      if IsInteger(image) {
         ; A non-zero "monitor" number identifies each display uniquely; and 0 refers to the entire virtual screen.
         if (image >= 0 && image <= MonitorGetCount())
            return "monitor"

         ; An "hBitmap" is a handle to a GDI Bitmap.
         if (DllCall("GetObjectType", "ptr", image) == 7)
            return "hBitmap"

         ; An "hIcon" is a handle to a GDI icon.
         if DllCall("DestroyIcon", "ptr", DllCall("CopyIcon", "ptr", image, "ptr"))
            return "hIcon"

         ; A "bitmap" is a pointer to a GDI+ Bitmap.
         try if !DllCall("gdiplus\GdipGetImageType", "ptr", image, "ptr*", type:=0) && (type == 1)
            return "bitmap"

         ; Note 1: All GDI+ functions add 1 to the reference count of COM objects.
         ; Note 2: GDI+ pBitmaps that are queried cease to stay pBitmaps.
         ObjRelease(image)

         ; A "stream" is a pointer to the IStream interface.
         try if ComObjQuery(image, "{0000000C-0000-0000-C000-000000000046}")
            return "stream"

         ; A "RandomAccessStream" is a pointer to the IRandomAccessStream interface.
         try if ComObjQuery(image, "{905A0FE1-BC53-11DF-8C49-001E4FC686DA}")
            return "RandomAccessStream"
      }
         ; A "hex" string is binary image data encoded into text using hexadecimal.
         if (StrLen(image) >= 116) && (image ~= "(?i)^\s*(0x)?[0-9a-f]+\s*$")
            return "hex"

         ; A "base64" string is binary image data encoded into text using only 64 characters.
         if (StrLen(image) >= 80) && (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"


      ; For more helpful error messages: Catch file names without extensions!
      for extension in ["bmp","dib","rle","jpg","jpeg","jpe","jfif","gif","tif","tiff","png","ico","exe","dll"]
         if FileExist(image "." extension)
            throw Exception("A ." extension " file extension is required!")

      throw Exception("Image type could not be identified.")
   }

   static toBitmap(type, ByRef image) {

      if (type = "clipboard")
         return this.from_clipboard()

      if (type = "object")                      ; Special
         return image.Bitmap()

      if (type = "buffer")
         return this.from_bitmap(image.pBitmap)

      if (type = "screenshot")
         return this.from_screenshot(image)

      if (type = "window")
         return this.from_window(image)

      if (type = "desktop")
         return this.from_desktop()

      if (type = "wallpaper")
         return this.from_wallpaper()

      if (type = "cursor")
         return this.from_cursor()

      if (type = "url")
         return this.from_url(image)

      if (type = "file")
         return this.from_file(image)

      if (type = "monitor")
         return this.from_monitor(image)

      if (type = "hBitmap")
         return this.from_hBitmap(image)

      if (type = "hIcon")
         return this.from_hIcon(image)

      if (type = "bitmap")
         return this.from_bitmap(image)

      if (type = "stream")
         return this.from_stream(image)

      if (type = "RandomAccessStream")
         return this.from_RandomAccessStream(image)

      if (type = "hex")
         return this.from_hex(image)

      if (type = "base64")
         return this.from_base64(image)

      if (type = "sprite")
         return this.from_sprite(image)

      throw Exception("Conversion from type " type " is not supported.")
   }

   static toCotype(cotype, ByRef pBitmap, term1 := "", term2 := "", *) {
      ; toCotype("clipboard", pBitmap)
      if (cotype = "clipboard")
         return this.put_clipboard(pBitmap)

      ; toCotype("buffer", pBitmap)
      if (cotype = "buffer")
         return this.put_buffer(pBitmap)

      ; toCotype("screenshot", pBitmap, screenshot, alpha)
      if (cotype = "screenshot")
         return this.put_screenshot(pBitmap, term1, term2)

      ; toCotype("window", pBitmap, title)
      if (cotype = "window")
         return this.put_window(pBitmap, term1)

      ; toCotype("desktop", pBitmap)
      if (cotype = "desktop")
         return this.put_desktop(pBitmap)

      ; toCotype("wallpaper", pBitmap)
      if (cotype = "wallpaper")
         return this.put_wallpaper(pBitmap)

      ; toCotype("cursor", pBitmap, xHotspot, yHotspot)
      if (cotype = "cursor")
         return this.put_cursor(pBitmap, term1, term2)

      ; toCotype("url", pBitmap)
      if (cotype = "url")
         return this.put_url(pBitmap)

      ; toCotype("file", pBitmap, filename, quality)
      if (cotype = "file")
         return this.put_file(pBitmap, term1, term2)

      ; toCotype("hBitmap", pBitmap, alpha)
      if (cotype = "hBitmap")
         return this.put_hBitmap(pBitmap, term1)

      ; toCotype("hIcon", pBitmap)
      if (cotype = "hIcon")
         return this.put_hIcon(pBitmap)

      ; toCotype("bitmap", pBitmap)
      if (cotype = "bitmap")
         return pBitmap

      ; toCotype("stream", pBitmap, extension, quality)
      if (cotype = "stream")
         return this.put_stream(pBitmap, term1, term2)

      ; toCotype("RandomAccessStream", pBitmap, extension, quality)
      if (cotype = "RandomAccessStream")
         return this.put_RandomAccessStream(pBitmap, term1, term2)

      ; toCotype("hex", pBitmap, extension, quality)
      if (cotype = "hex")
         return this.put_hex(pBitmap, term1, term2)

      ; toCotype("base64", pBitmap, extension, quality)
      if (cotype = "base64")
         return this.put_base64(pBitmap, term1, term2)

      throw Exception("Conversion to type " cotype " is not supported.")
   }

   static DisposeImage(ByRef pBitmap) {
      return DllCall("gdiplus\GdipDisposeImage", "ptr", pBitmap)
   }

   static BitmapCrop(ByRef pBitmap, crop) {
      ; Get Bitmap width, height, and format.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0)
      DllCall("gdiplus\GdipGetImagePixelFormat", "ptr", pBitmap, "uint*", format:=0)

      ; Are the numbers percentages?
      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]
      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]

      ; If numbers are negative, subtract the values from the edge.
      crop[3] := (crop[3] < 0) ?  width - Abs(crop[3]) - Abs(crop[1]) : crop[3]
      crop[4] := (crop[4] < 0) ? height - Abs(crop[4]) - Abs(crop[2]) : crop[4]
      crop[1] := Abs(crop[1])
      crop[2] := Abs(crop[2])

      ; Round to the nearest integer.
      crop[3] := Round(crop[1] + crop[3]) - Round(crop[1]) ; A reminder that width and height
      crop[4] := Round(crop[2] + crop[4]) - Round(crop[2]) ; are distances, not coordinates.
      crop[1] := Round(crop[1]) ; so the abstract concept of a distance must be resolved
      crop[2] := Round(crop[2]) ; into coordinates and then rounded and added up again.

      ; Variance Shift. Now place x,y before w,h because we are building abstracts from reals now.
      ; Before we were resolving abstracts into real coordinates, now it's the opposite.

      ; Ensure that coordinates can never exceed the expected Bitmap area.
      safe_x := (crop[1] > width) ? 0 : crop[1]                          ; Zero x if bigger.
      safe_y := (crop[2] > height) ? 0 : crop[2]                         ; Zero y if bigger.
      safe_w := (crop[1] + crop[3] > width) ? width - safe_x : crop[3]   ; Max w if bigger.
      safe_h := (crop[2] + crop[4] > height) ? height - safe_y : crop[4] ; Max h if bigger.

      ; Clone
      DllCall("gdiplus\GdipCloneBitmapAreaI"
               ,    "int", safe_x
               ,    "int", safe_y
               ,    "int", safe_w
               ,    "int", safe_h
               ,    "int", format
               ,    "ptr", pBitmap
               ,   "ptr*", pBitmapCrop:=0)

      return pBitmapCrop
   }

   static BitmapScale(ByRef pBitmap, scale) {
      ; Get Bitmap width, height, and format.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0)
      DllCall("gdiplus\GdipGetImagePixelFormat", "ptr", pBitmap, "uint*", format:=0)

      safe_w := Ceil(width * scale)
      safe_h := Ceil(height * scale)

      ; Create a new bitmap and get the graphics context.
      DllCall("gdiplus\GdipCreateBitmapFromScan0"
               , "int", safe_w, "int", safe_h, "int", 0, "int", format, "ptr", 0, "ptr*", pBitmapScale:=0)
      DllCall("gdiplus\GdipGetImageGraphicsContext", "ptr", pBitmapScale, "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", safe_w, "int", safe_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)
      return pBitmapScale
   }

   static is_url(url) {
      ; Thanks splattermania - https://www.php.net/manual/en/function.preg-match.php#93824

      regex := "^(?i)"
         . "((https?|ftp)\:\/\/)" ; SCHEME
         . "([a-z0-9+!*(),;?&=\$_.-]+(\:[a-z0-9+!*(),;?&=\$_.-]+)[email protected])?" ; User and Pass
         . "([a-z0-9-.]*)\.([a-z]{2,3})" ; Host or IP
         . "(\:[0-9]{2,5})?" ; Port
         . "(\/(?:[a-z0-9-_~!$&'()*+,;=:@]\.?)+)*\/?" ; Path
         . "(\?[a-z+&\$_.-][a-z0-9;:@&%=+\/\$_.-]*)?" ; GET Query
         . "(#[a-z_.-][a-z0-9+\$_.-]*)?$" ; Anchor
      return (url ~= regex)
   }

   static from_clipboard() {
      ; Open the clipboard.
      Loop 6 ; Try this 6 times.
         if (A_Index > 1)
            Sleep (2**(A_Index-2) * 30)
      until (result := DllCall("OpenClipboard", "ptr", 0))
      if !(result)
         throw Exception("Clipboard could not be opened.")

      ; Prefer the PNG stream if available considering it supports transparency.
      png := DllCall("RegisterClipboardFormat", "str", "png", "uint")
      if DllCall("IsClipboardFormatAvailable", "uint", png, "int") {
         hData := DllCall("GetClipboardData", "uint", png, "ptr")
         DllCall("ole32\CreateStreamOnHGlobal", "ptr", hData, "int", true, "ptr*", pStream:=0)
         DllCall("gdiplus\GdipCreateBitmapFromStream", "ptr", pStream, "ptr*", pBitmap:=0)
         ObjRelease(pStream)
      }

      ; Fallback to CF_BITMAP.
      else if DllCall("IsClipboardFormatAvailable", "uint", 2, "int") {
         hBitmap := DllCall("GetClipboardData", "uint", 2, "ptr")
         DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", "ptr", hBitmap, "ptr", 0, "ptr*", pBitmap:=0)
         DllCall("DeleteObject", "ptr", hBitmap)
      }

      DllCall("CloseClipboard")
      return pBitmap
   }

   static from_screenshot(ByRef image) {
      ; Thanks tic - https://www.autohotkey.com/boards/viewtopic.php?t=6517

      ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      bi := BufferAlloc(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
   }

   static from_window(ByRef image) {
      ; Thanks tic - https://www.autohotkey.com/boards/viewtopic.php?t=6517

      ; Get the handle to the window.
      image := WinExist(image)

      ; Restore the window if minimized! Must be visible for capture.
      if DllCall("IsIconic", "ptr", image)
         DllCall("ShowWindow", "ptr", image, "int", 4)

      ; Get the width and height of the client window.
      Rect := BufferAlloc(16) ; sizeof(RECT) = 16
      DllCall("GetClientRect", "ptr", image, "ptr", Rect)
         , width  := NumGet(Rect, 8, "int")
         , height := NumGet(Rect, 12, "int")

      ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      bi := BufferAlloc(40, 0)                 ; sizeof(bi) = 40
         , NumPut(  "uint",        40, bi,  0) ; Size
         , NumPut(   "int",     width, bi,  4) ; Width
         , NumPut(   "int",   -height, 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")

      ; Print the window onto the hBitmap using an undocumented flag. https://stackoverflow.com/a/40042587
      DllCall("PrintWindow", "ptr", image, "ptr", hdc, "uint", 0x3) ; PW_RENDERFULLCONTENT | PW_CLIENTONLY
      ; Additional info on how this is implemented: https://www.reddit.com/r/windows/comments/8ffr56/altprintscreen/

      ; 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
   }

   static from_desktop() {
      ; Find the child window.
      windows := WinGetList("ahk_class WorkerW")
      if (windows.length == 0)
         throw Exception("The hidden desktop window has not been initalized. Call ImagePutDesktop() first.")

      Loop windows.length
         hwnd := windows[A_Index]
      until DllCall("FindWindowEx", "ptr", hwnd, "ptr", 0, "str", "SHELLDLL_DefView", "ptr", 0)

      ; Maybe this hack gets patched. Tough luck!
      if !(WorkerW := DllCall("FindWindowEx", "ptr", 0, "ptr", hwnd, "str", "WorkerW", "ptr", 0, "ptr"))
         throw Exception("Could not locate hidden window behind desktop.")

      ; Get the width and height of the client window.
      Rect := BufferAlloc(16) ; sizeof(RECT) = 16
      DllCall("GetClientRect", "ptr", WorkerW, "ptr", Rect)
         , width  := NumGet(Rect, 8, "int")
         , height := NumGet(Rect, 12, "int")

      ; Get device context of spawned window.
      sdc := DllCall("GetDCEx", "ptr", WorkerW, "ptr", 0, "int", 0x403, "ptr") ; LockWindowUpdate | Cache | Window

      ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      bi := BufferAlloc(40, 0)                 ; sizeof(bi) = 40
         , NumPut(  "uint",        40, bi,  0) ; Size
         , NumPut(   "int",     width, bi,  4) ; Width
         , NumPut(   "int",   -height, 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")

      ; Copies a portion of the hidden window to a new device context.
      DllCall("gdi32\BitBlt"
               , "ptr", hdc, "int", 0, "int", 0, "int", width, "int", height
               , "ptr", sdc, "int", 0, "int", 0, "uint", 0x00CC0020) ; SRCCOPY

      ; 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)

      ; Release device context of spawned window.
      DllCall("ReleaseDC", "ptr", 0, "ptr", sdc)

      return pBitmap
   }

   static from_wallpaper() {
      ; Get the width and height of all monitors.
      width  := DllCall("GetSystemMetrics", "int", 78)
      height := DllCall("GetSystemMetrics", "int", 79)

      ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      bi := BufferAlloc(40, 0)                 ; sizeof(bi) = 40
         , NumPut(  "uint",        40, bi,  0) ; Size
         , NumPut(   "int",     width, bi,  4) ; Width
         , NumPut(   "int",   -height, 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")

      ; Paints the desktop.
      DllCall("PaintDesktop", "ptr", hdc)

      ; 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
   }

   static from_cursor() {
      ; Thanks 23W - https://stackoverflow.com/a/13295280

      ; struct CURSORINFO - https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-cursorinfo
      ci := BufferAlloc(16+A_PtrSize, 0)                 ; sizeof(CURSORINFO) = 20, 24
         , NumPut("int", ci.size, ci)
      DllCall("GetCursorInfo", "ptr", ci)
         ; cShow   := NumGet(ci,  4, "int")              ; 0x1 = CURSOR_SHOWING, 0x2 = CURSOR_SUPPRESSED
         , hCursor := NumGet(ci,  8, "ptr")
         ; xCursor := NumGet(ci,  8+A_PtrSize, "int")
         ; yCursor := NumGet(ci, 12+A_PtrSize, "int")

      ; Cursors are the same as icons!
      pBitmap := this.from_hIcon(hCursor)

      ; Cleanup the handle to the cursor. Same as DestroyIcon.
      DllCall("DestroyCursor",  "ptr", hCursor)

      return pBitmap
   }

   static from_url(ByRef image) {
      req := ComObjCreate("WinHttp.WinHttpRequest.5.1")
      req.Open("GET", image)
      req.Send()
      IStream := ComObjQuery(req.ResponseStream, "{0000000C-0000-0000-C000-000000000046}")
      DllCall("gdiplus\GdipCreateBitmapFromStream", "ptr", IStream, "ptr*", pBitmap:=0)
      ObjRelease(IStream.ptr)
      return pBitmap
   }

   static from_file(ByRef image) {
      DllCall("gdiplus\GdipCreateBitmapFromFile", "wstr", image, "ptr*", pBitmap:=0)
      return pBitmap
   }

   static from_monitor(ByRef image) {
      if (image > 0) {
         MonitorGet(image, Left, Top, Right, Bottom)
         x := Left
         y := Top
         w := Right - Left
         h := Bottom - Top
      } else {
         x := DllCall("GetSystemMetrics", "int", 76)
         y := DllCall("GetSystemMetrics", "int", 77)
         w := DllCall("GetSystemMetrics", "int", 78)
         h := DllCall("GetSystemMetrics", "int", 79)
      }
      return this.from_screenshot([x,y,w,h])
   }

   static from_hBitmap(ByRef image) {
      ; struct DIBSECTION - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-dibsection
      ; struct BITMAP - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmap
      dib := BufferAlloc(64+5*A_PtrSize) ; sizeof(DIBSECTION) = 84, 104
      DllCall("GetObject", "ptr", image, "int", dib.size, "ptr", dib)
         , width  := NumGet(dib, 4, "uint")
         , height := NumGet(dib, 8, "uint")
         , bpp    := NumGet(dib, 18, "ushort")

      ; Fallback to built-in method if pixels are not 32-bit ARGB.
      if (bpp != 32) { ; This built-in version is 120% faster but ignores transparency.
         DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", "ptr", image, "ptr", 0, "ptr*", pBitmap:=0)
         return pBitmap
      }

      ; Create a handle to a device context and associate the image.
      sdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")           ; Creates a memory DC compatible with the current screen.
      sbm := DllCall("SelectObject", "ptr", sdc, "ptr", image, "ptr") ; Put the (hBitmap) image onto the device context.

      ; Create a device independent bitmap with negative height. All DIBs use the screen pixel format (pARGB).
      ; Use hbm to buffer the image such that top-down and bottom-up images are mapped to this top-down buffer.
      ; pBits is the pointer to (top-down) pixel values. The Scan0 will point to the pBits.
      ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      bi := BufferAlloc(40, 0)                 ; sizeof(bi) = 40
         , NumPut(  "uint",        40, bi,  0) ; Size
         , NumPut(   "int",     width, bi,  4) ; Width
         , NumPut(   "int",   -height, 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")

      ; This is the 32-bit ARGB pBitmap (different from an hBitmap) that will receive the final converted pixels.
      DllCall("gdiplus\GdipCreateBitmapFromScan0"
               , "int", width, "int", height, "int", 0, "int", 0x26200A, "ptr", 0, "ptr*", pBitmap:=0)

      ; Create a Scan0 buffer pointing to pBits. The buffer has pixel format pARGB.
      Rect := BufferAlloc(16, 0)               ; sizeof(Rect) = 16
         , NumPut(  "uint",   width, Rect,  8) ; Width
         , NumPut(  "uint",  height, Rect, 12) ; Height
      BitmapData := BufferAlloc(16+2*A_PtrSize, 0)      ; sizeof(BitmapData) = 24, 32
         , NumPut(  "uint",      width, BitmapData,  0) ; Width
         , NumPut(  "uint",     height, BitmapData,  4) ; Height
         , NumPut(   "int",  4 * width, BitmapData,  8) ; Stride
         , NumPut(   "int",    0xE200B, BitmapData, 12) ; PixelFormat
         , NumPut(   "ptr",      pBits, BitmapData, 16) ; Scan0

      ; Use LockBits to create a writable buffer that converts pARGB to ARGB.
      DllCall("gdiplus\GdipBitmapLockBits"
               ,    "ptr", pBitmap
               ,    "ptr", Rect
               ,   "uint", 6            ; ImageLockMode.UserInputBuffer | ImageLockMode.WriteOnly
               ,    "int", 0xE200B      ; Format32bppPArgb
               ,    "ptr", BitmapData)  ; Contains the pointer (pBits) to the hbm.

      ; Copies the image (hBitmap) to a top-down bitmap. Removes bottom-up-ness if present.
      DllCall("gdi32\BitBlt"
               , "ptr", hdc, "int", 0, "int", 0, "int", width, "int", height
               , "ptr", sdc, "int", 0, "int", 0, "uint", 0x00CC0020) ; SRCCOPY

      ; Convert the pARGB pixels copied into the device independent bitmap (hbm) to ARGB.
      DllCall("gdiplus\GdipBitmapUnlockBits", "ptr", pBitmap, "ptr", BitmapData)

      ; Cleanup the hBitmap and device contexts.
      DllCall("SelectObject", "ptr", hdc, "ptr", obm)
      DllCall("DeleteObject", "ptr", hbm)
      DllCall("DeleteDC",     "ptr", hdc)
      DllCall("SelectObject", "ptr", sdc, "ptr", sbm)
      DllCall("DeleteDC",     "ptr", sdc)

      return pBitmap
   }

   static from_hIcon(ByRef image) {
      ; struct ICONINFO - https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-iconinfo
      ii := BufferAlloc(8+3*A_PtrSize, 0)                ; sizeof(ICONINFO) = 20, 32
      DllCall("GetIconInfo", "ptr", image, "ptr", ii)
         ; xHotspot := NumGet(ii, 4, "uint")
         ; yHotspot := NumGet(ii, 8, "uint")
         , hbmMask  := NumGet(ii, 8+A_PtrSize, "ptr")    ; x86:12, x64:16
         , hbmColor := NumGet(ii, 8+2*A_PtrSize, "ptr")  ; x86:16, x64:24

      ; struct BITMAP - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmap
      bm := BufferAlloc(16+2*A_PtrSize) ; sizeof(BITMAP) = 24, 32
      DllCall("GetObject", "ptr", hbmMask, "int", bm.size, "ptr", bm)
         , width  := NumGet(bm, 4, "uint")
         , height := NumGet(bm, 8, "uint") / (hbmColor ? 1 : 2) ; Black and White cursors have doubled height.

      ; Clean up these hBitmaps.
      DllCall("DeleteObject", "ptr", hbmMask)
      DllCall("DeleteObject", "ptr", hbmColor)

      ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      bi := BufferAlloc(40, 0)                 ; sizeof(bi) = 40
         , NumPut(  "uint",        40, bi,  0) ; Size
         , NumPut(   "int",     width, bi,  4) ; Width
         , NumPut(   "int",   -height, 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")

      ; This is the 32-bit ARGB pBitmap (different from an hBitmap) that will receive the final converted pixels.
      DllCall("gdiplus\GdipCreateBitmapFromScan0"
               , "int", width, "int", height, "int", 0, "int", 0x26200A, "ptr", 0, "ptr*", pBitmap:=0)

      ; Create a Scan0 buffer pointing to pBits. The buffer has pixel format pARGB.
      Rect := BufferAlloc(16, 0)               ; sizeof(Rect) = 16
         , NumPut(  "uint",   width, Rect,  8) ; Width
         , NumPut(  "uint",  height, Rect, 12) ; Height
      BitmapData := BufferAlloc(16+2*A_PtrSize, 0)      ; sizeof(BitmapData) = 24, 32
         , NumPut(  "uint",      width, BitmapData,  0) ; Width
         , NumPut(  "uint",     height, BitmapData,  4) ; Height
         , NumPut(   "int",  4 * width, BitmapData,  8) ; Stride
         , NumPut(   "int",    0xE200B, BitmapData, 12) ; PixelFormat
         , NumPut(   "ptr",      pBits, BitmapData, 16) ; Scan0

      ; Use LockBits to create a writable buffer that converts pARGB to ARGB.
      DllCall("gdiplus\GdipBitmapLockBits"
               ,    "ptr", pBitmap
               ,    "ptr", Rect
               ,   "uint", 6            ; ImageLockMode.UserInputBuffer | ImageLockMode.WriteOnly
               ,    "int", 0xE200B      ; Format32bppPArgb
               ,    "ptr", BitmapData)  ; Contains the pointer (pBits) to the hbm.

      ; Don't use DI_DEFAULTSIZE to draw the icon like DrawIcon does as it will resize to 32 x 32.
      DllCall("DrawIconEx"
               , "ptr", hdc,   "int", 0, "int", 0
               , "ptr", image, "int", 0, "int", 0
               , "uint", 0, "ptr", 0, "uint", 0x1 | 0x2 | 0x4) ; DI_MASK | DI_IMAGE | DI_COMPAT

      ; Convert the pARGB pixels copied into the device independent bitmap (hbm) to ARGB.
      DllCall("gdiplus\GdipBitmapUnlockBits", "ptr", pBitmap, "ptr", BitmapData)

      ; Cleanup the hBitmap and device contexts.
      DllCall("SelectObject", "ptr", hdc, "ptr", obm)
      DllCall("DeleteObject", "ptr", hbm)
      DllCall("DeleteDC",     "ptr", hdc)

      return pBitmap
   }

   static from_bitmap(ByRef image) {
      DllCall("gdiplus\GdipCloneImage", "ptr", image, "ptr*", pBitmap:=0)
      return pBitmap
   }

   static from_stream(ByRef image) {
      DllCall("gdiplus\GdipCreateBitmapFromStream", "ptr", image, "ptr*", pBitmap:=0)
      return pBitmap
   }

   static from_RandomAccessStream(ByRef image) {
      ; Get the Class ID from a GUID string.
      CLSID := BufferAlloc(16, 0)
      if result := DllCall("ole32\CLSIDFromString", "wstr", "{0000000C-0000-0000-C000-000000000046}", "ptr", CLSID, "uint")
         throw Exception("CLSIDFromString failed. Error: " . Format("{:#x}", result))

      ; Convert RandomAccessStream to stream.
      DllCall("ShCore\CreateStreamOverRandomAccessStream", "ptr", image, "ptr", CLSID, "ptr*", pStream:=0, "uint")

      ; Read stream to pBitmap.
      DllCall("gdiplus\GdipCreateBitmapFromStream", "ptr", pStream, "ptr*", pBitmap:=0)

      ; Manually free the pointer to an IStream.
      ObjRelease(pStream)

      return pBitmap
   }

   static from_hex(ByRef image) {
      ; Trim whitespace and remove header.
      image := Trim(image)
      image := RegExReplace(image, "^(0[xX])")

      ; Converts the image to binary data by first asking for the size.
      DllCall("crypt32\CryptStringToBinary"
               , "ptr", StrPtr(image), "uint", 0, "uint", 0x0000000C, "ptr",   0, "uint*", size:=0, "ptr", 0, "ptr", 0)
      bin := BufferAlloc(size, 0)
      DllCall("crypt32\CryptStringToBinary"
               , "ptr", StrPtr(image), "uint", 0, "uint", 0x0000000C, "ptr", bin, "uint*", size   , "ptr", 0, "ptr", 0)

      ; Makes a stream for conversion into a pBitmap.
      pStream := DllCall("shlwapi\SHCreateMemStream", "ptr", bin, "uint", size, "ptr")
      DllCall("gdiplus\GdipCreateBitmapFromStream", "ptr", pStream, "ptr*", pBitmap:=0)
      ObjRelease(pStream)

      return pBitmap
   }

   static from_base64(ByRef image) {
      ; Trim whitespace and remove header.
      image := Trim(image)
      image := RegExReplace(image, "^data:image\/[a-z]+;base64,")

      ; Converts the image to binary data by first asking for the size.
      DllCall("crypt32\CryptStringToBinary"
               , "ptr", StrPtr(image), "uint", 0, "uint", 0x00000001, "ptr",   0, "uint*", size:=0, "ptr", 0, "ptr", 0)
      bin := BufferAlloc(size, 0)
      DllCall("crypt32\CryptStringToBinary"
               , "ptr", StrPtr(image), "uint", 0, "uint", 0x00000001, "ptr", bin, "uint*", size   , "ptr", 0, "ptr", 0)

      ; Makes a stream for conversion into a pBitmap.
      pStream := DllCall("shlwapi\SHCreateMemStream", "ptr", bin, "uint", size, "ptr")
      DllCall("gdiplus\GdipCreateBitmapFromStream", "ptr", pStream, "ptr*", pBitmap:=0)
      ObjRelease(pStream)

      return pBitmap
   }

   static from_sprite(ByRef image) {
      ; Create a source pBitmap and extract the width and height.
      if DllCall("gdiplus\GdipCreateBitmapFromFile", "wstr", image, "ptr*", sBitmap:=0)
         if !(sBitmap := this.from_url(image))
            throw Exception("Could not be loaded from a valid file path or URL.")

      ; Get Bitmap width and height.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", sBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", sBitmap, "uint*", height:=0)

      ; Create a destination pBitmap in 32-bit ARGB and get its device context though GDI+.
      ; Note that a device context from a graphics context can only be drawn on, not read.
      ; Also note that using a graphics context and blitting does not create a pixel perfect image.
      ; Using a DIB and LockBits is about 5% faster.
      DllCall("gdiplus\GdipCreateBitmapFromScan0"
               , "int", width, "int", height, "int", 0, "int", 0x26200A, "ptr", 0, "ptr*", dBitmap:=0)
      DllCall("gdiplus\GdipGetImageGraphicsContext", "ptr", dBitmap, "ptr*", dGraphics:=0)
      DllCall("gdiplus\GdipGetDC", "ptr", dGraphics, "ptr*", ddc:=0)

      ; Keep any existing transparency for whatever reason.
      hBitmap := this.put_hBitmap(sBitmap) ; Could copy this code here for even more speed.

      ; Create a source device context and associate the source hBitmap.
      sdc := DllCall("CreateCompatibleDC", "ptr", ddc, "ptr")
      obm := DllCall("SelectObject", "ptr", sdc, "ptr", hBitmap, "ptr")

      ; Copy the image making the top-left pixel the color key.
      DllCall("msimg32\TransparentBlt"
               , "ptr", ddc, "int", 0, "int", 0, "int", width, "int", height  ; destination
               , "ptr", sdc, "int", 0, "int", 0, "int", width, "int", height  ; source
               , "uint", DllCall("GetPixel", "ptr", sdc, "int", 0, "int", 0)) ; RGB pixel.

      ; Cleanup the hBitmap and device contexts.
      DllCall("SelectObject", "ptr", sdc, "ptr", obm)
      DllCall("DeleteObject", "ptr", hBitmap)
      DllCall("DeleteDC",     "ptr", sdc)

      ; Release the graphics context and delete.
      DllCall("gdiplus\GdipReleaseDC", "ptr", dGraphics, "ptr", ddc)
      DllCall("gdiplus\GdipDeleteGraphics", "ptr", dGraphics)

      return dBitmap
   }

   static put_clipboard(ByRef pBitmap) {
      ; Standard Clipboard Formats - https://docs.microsoft.com/en-us/windows/win32/dataxchg/standard-clipboard-formats
      ; Synthesized Clipboard Formats - https://docs.microsoft.com/en-us/windows/win32/dataxchg/clipboard-formats

      ; Open the clipboard.
      Loop 6 ; Try this 6 times.
         if (A_Index > 1)
            Sleep (2**(A_Index-2) * 30)
      until (result := DllCall("OpenClipboard", "ptr", 0))
      if !(result)
         throw Exception("Clipboard could not be opened.")

      ; Clear the clipboard.
      DllCall("EmptyClipboard")

      ; #1 - Place the image onto the clipboard as a PNG stream.
      ; Thanks Jochen Arndt - https://www.codeproject.com/Answers/1207927/Saving-an-image-to-the-clipboard#answer3
      pStream := this.put_stream(pBitmap, "png")
      DllCall("ole32\GetHGlobalFromStream", "ptr", pStream, "uint*", hData:=0)
      DllCall("SetClipboardData", "uint", DllCall("RegisterClipboardFormat", "str", "png", "uint"), "ptr", hData)
      ObjRelease(pStream)

      ; #2 - Place the image onto the clipboard in the CF_DIB format in ARGB using 3 color masks. (Extra 12 byte offset.)
      ; Thanks Nyerguds - https://stackoverflow.com/a/46424800

      ; Get Bitmap width, height, and format.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0)
      DllCall("gdiplus\GdipGetImagePixelFormat", "ptr", pBitmap, "uint*", format:=0)

      ; Get Bitmap bits per pixel, stride, and size.
      bpp := (format & 0x00FF00) >> 8
      stride := (bpp >> 3) * width
      size := stride * height

      ; struct DIBSECTION - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-dibsection
      ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
      hdib := DllCall("GlobalAlloc", "uint", 0x42, "uptr", 40 + 12 + size, "ptr")
      pdib := DllCall("GlobalLock", "ptr", hdib, "ptr")
         , NumPut(  "uint",         40, pdib,  0) ; Size
         , NumPut(   "int",      width, pdib,  4) ; Width
         , NumPut(   "int",    -height, pdib,  8) ; Height - Negative so (0, 0) is top-left.
         , NumPut("ushort",          1, pdib, 12) ; Planes
         , NumPut("ushort",        bpp, pdib, 14) ; BitCount / BitsPerPixel
         , NumPut(  "uint",        0x3, pdib, 16) ; Compression
         , NumPut(  "uint",       size, pdib, 20) ; SizeImage (bytes)
         ; The following bitfields when masked extract the respective color channels.
         , NumPut(  "uint", 0x00FF0000, pdib, 40) ; Red
         , NumPut(  "uint", 0x0000FF00, pdib, 44) ; Green
         , NumPut(  "uint", 0x000000FF, pdib, 48) ; Blue

      ; Transfer data from source pBitmap to the global memory manually.
      Rect := BufferAlloc(16, 0)               ; sizeof(Rect) = 16
         , NumPut(  "uint",   width, Rect,  8) ; Width
         , NumPut(  "uint",  height, Rect, 12) ; Height
      BitmapData := BufferAlloc(16+2*A_PtrSize, 0)      ; sizeof(BitmapData) = 24, 32
         , NumPut(  "uint",      width, BitmapData,  0) ; Width
         , NumPut(  "uint",     height, BitmapData,  4) ; Height
         , NumPut(   "int",     stride, BitmapData,  8) ; Stride
         , NumPut(   "int",     format, BitmapData, 12) ; PixelFormat
         , NumPut(   "ptr",  pdib + 52, BitmapData, 16) ; Scan0
      DllCall("gdiplus\GdipBitmapLockBits"
               ,    "ptr", pBitmap
               ,    "ptr", Rect
               ,   "uint", 5            ; ImageLockMode.UserInputBuffer | ImageLockMode.ReadOnly
               ,    "int", 0x26200A     ; Format32bppArgb
               ,    "ptr", BitmapData)  ; Contains the pointer (pdib) to the hData.
      DllCall("gdiplus\GdipBitmapUnlockBits", "ptr", pBitmap, "ptr", BitmapData)

      ; Unlock the memory as it is complete.
      DllCall("GlobalUnlock", "ptr", hdib)

      ; Add CF_DIB as a format to the clipboard.
      DllCall("SetClipboardData", "uint", 8, "ptr", hdib)

      ; Close the clipboard.
      DllCall("CloseClipboard")

      return ClipboardAll()
   }

   static put_buffer(ByRef pBitmap) {
      buffer := {pBitmap: pBitmap}
         .DefineMethod("__New" , (self) => (this.gdiplusStartup(), self)) ; Increment GDI+ reference count
         .DefineMethod("__Delete", (self) => (this.gdiplusShutdown("smart_pointer", self.pBitmap)))
         .__New()  ; On deletion the buffer object will dispose of the bitmap. And it will decrement this.gdiplus.
      return buffer
   }

   static put_screenshot(ByRef pBitmap, screenshot := "", alpha := "") {
      ; Get Bitmap width and height.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0)

      x := (IsObject(screenshot) && screenshot[1] != "") ? screenshot[1] : Round((A_ScreenWidth - width) / 2)
      y := (IsObject(screenshot) && screenshot[2] != "") ? screenshot[2] : Round((A_ScreenHeight - height) / 2)
      w := (IsObject(screenshot) && screenshot[3] != "") ? screenshot[3] : width
      h := (IsObject(screenshot) && screenshot[4] != "") ? screenshot[4] : height

      ; Convert the Bitmap to a hBitmap and associate a device context for blitting.
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      hbm := this.put_hBitmap(pBitmap, alpha)
      obm := DllCall("SelectObject", "ptr", hdc, "ptr", hbm, "ptr")

      ; Retrieve the device context for the screen.
      ddc := DllCall("GetDC", "ptr", 0, "ptr")

      ; Copies a portion of the screen to a new device context.
      DllCall("gdi32\StretchBlt"
               , "ptr", ddc, "int", x, "int", y, "int", w,     "int", h
               , "ptr", hdc, "int", 0, "int", 0, "int", width, "int", height
               , "uint", 0x00CC0020) ; SRCCOPY

      ; Release the device context to the screen.
      DllCall("ReleaseDC", "ptr", 0, "ptr", ddc)

      ; Cleanup the hBitmap and device contexts.
      DllCall("SelectObject", "ptr", hdc, "ptr", obm)
      DllCall("DeleteObject", "ptr", hbm)
      DllCall("DeleteDC",     "ptr", hdc)

      return [x,y,w,h]
   }

   static put_window(ByRef pBitmap, title := "") {

      WindowProc(hwnd, uMsg, wParam, lParam) {

         ; WM_DESTROY
         if (uMsg = 0x2) {
         ;  MsgBox "NICE TRY! LOL!"
         ;   return
         }

         ; WM_LBUTTONDOWN
         if (uMsg = 0x201) {
            parent := DllCall("GetParent", "ptr", hwnd)
            hwnd := (parent != A_ScriptHwnd && parent != 0) ? parent : hwnd
            PostMessage 0xA1, 2,,, hwnd
         }

         return DllCall("DefWindowProc", "ptr", hwnd, "uint", uMsg, "uptr", wParam, "ptr", lParam, "ptr")
      }

      ; Make it permanent. 
      void := ObjBindMethod({}, {})
      Hotkey "^+F12", void, "On"

      ; Get Bitmap width and height.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0)

      class_name := "ImagePut"
      pWndProc := CallbackCreate("WindowProc", "Fast")

      hCursor := DllCall("LoadCursor", "ptr", 0, "ptr", 32512, "ptr") ; IDC_ARROW
      ;hBrush := DllCall("CreateSolidBrush", "uint", 0x00F0F0F0)
      hBrush := DllCall("GetStockObject", "int", 5) ; 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
      WNDCLASSEX := BufferAlloc(A_PtrSize=8 ? 80:48, 0)                                ; sizeof(WNDCLASSEX) = 48, 80
         , NumPut(  "uint", WNDCLASSEX.size, WNDCLASSEX,                   0) ; cbSize
         , NumPut(  "uint",          0, WNDCLASSEX,                   4) ; style
         , NumPut(   "ptr",   pWndProc, WNDCLASSEX,                   8) ; lpfnWndProc
         , NumPut(   "int",          0, WNDCLASSEX, A_PtrSize=8 ? 16:12) ; cbClsExtra
         , NumPut(   "int",          0, WNDCLASSEX, A_PtrSize=8 ? 20:16) ; cbWndExtra
         , NumPut(   "ptr",          0, WNDCLASSEX, A_PtrSize=8 ? 24:20) ; hInstance
         , NumPut(   "ptr",          0, WNDCLASSEX, A_PtrSize=8 ? 32:24) ; hIcon
         , NumPut(   "ptr",    hCursor, WNDCLASSEX, A_PtrSize=8 ? 40:28) ; hCursor
         , NumPut(   "ptr",     hBrush, WNDCLASSEX, A_PtrSize=8 ? 48:32) ; hbrBackground
         , NumPut(   "ptr",          0, WNDCLASSEX, A_PtrSize=8 ? 56:36) ; lpszMenuName
         , NumPut(   "ptr", StrPtr(class_name), WNDCLASSEX, A_PtrSize=8 ? 64:40) ; lpszClassName
         , NumPut(   "ptr",          0, WNDCLASSEX, A_PtrSize=8 ? 72:44) ; hIconSm

      ; Registers a window class for subsequent use in calls to the CreateWindow or CreateWindowEx function.
      DllCall("RegisterClassEx", "ptr", WNDCLASSEX, "ushort")

         WS_VISIBLE                := 0x10000000
         WS_SYSMENU                :=    0x80000
         WS_CHILD                  := 0x40000000
         WS_EX_TOPMOST             :=        0x8
         WS_EX_LAYERED             :=    0x80000
         WS_TILEDWINDOW            :=   0xCF0000
         WS_CAPTION                :=   0xC00000
         WS_EX_STATICEDGE          :=    0x20000
         WS_EX_WINDOWEDGE          :=      0x100
         WS_SIZEBOX                :=    0x40000
         WS_CLIPCHILDREN           :=  0x2000000
         WS_POPUP                  := 0x80000000
         WS_BORDER                 :=   0x800000
         WS_EX_TOOLWINDOW          :=       0x80
         WS_CLIPSIBLINGS           :=  0x4000000
         WS_EX_TRANSPARENT         :=       0x20
         WS_EX_DLGMODALFRAME       :=        0x1

         rect := BufferAlloc(16, 0)
            , NumPut("int", Floor((A_ScreenWidth - width) / 2), rect,  0)
            , NumPut("int", Floor((A_ScreenHeight - height) / 2), rect,  4)
            , NumPut("int", Floor((A_ScreenWidth + width) / 2), rect,  8)
            , NumPut("int", Floor((A_ScreenHeight + height) / 2), rect, 12)

         style := WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_CLIPCHILDREN | WS_POPUP | WS_CLIPSIBLINGS ;| WS_SIZEBOX
         styleEx := WS_EX_TOPMOST | WS_EX_WINDOWEDGE | WS_EX_DLGMODALFRAME ;| WS_EX_LAYERED ;| WS_EX_STATICEDGE

         DllCall("AdjustWindowRectEx", "ptr", rect, "uint", style, "uint", 0, "uint", styleEx)

         x := NumGet(rect,  0, "int")
         y := NumGet(rect,  4, "int")
         w := NumGet(rect,  8, "int") - NumGet(rect,  0, "int")
         h := NumGet(rect, 12, "int") - NumGet(rect,  4, "int")

         hwnd0 := DllCall("CreateWindowEx"
            ,   "uint", styleEx
            ,    "str", "ImagePut"  ; lpClassName
            ,    "str", title ;"Pichu"            ; lpWindowName
            ,   "uint", style
            ,    "int", x      ; X
            ,    "int", y        ; Y
            ,    "int", w      ; nWidth
            ,    "int", h     ; nHeight
            ,    "ptr", A_ScriptHwnd                     ; hWndParent
            ,    "ptr", 0                     ; hMenu
            ,    "ptr", 0                     ; hInstance
            ,    "ptr", 0                     ; lpParam
            ,    "ptr")

         ;if transparent
            WinSetTransColor "F0F0F0", hwnd0

         vWinStyle := WS_VISIBLE | WS_CHILD
         vWinExStyle := WS_EX_LAYERED ;| WS_EX_TOPMOST

         hwnd := DllCall("CreateWindowEx"
            ,   "uint", vWinExStyle           ; dwExStyle
            ,    "str", "ImagePut"  ; lpClassName
            ,    "str", "Pikachu"            ; lpWindowName
            ,   "uint", vWinStyle             ; dwStyle
            ,    "int", 0       ; X
            ,    "int", 0        ; Y
            ,    "int", width      ; nWidth
            ,    "int", height     ; nHeight
            ,    "ptr", hwnd0                     ; hWndParent
            ,    "ptr", 0                     ; hMenu
            ,    "ptr", 0                     ; hInstance
            ,    "ptr", 0                     ; lpParam
            ,    "ptr")

         ;DllCall("ShowWindow", "ptr", hwnd, "int", 1)

         hdc := DllCall("CreateCompatibleDC", "ptr", 0)
         hbm := this.put_hBitmap(pBitmap)
         obm := DllCall("SelectObject", "ptr", hdc, "ptr", hbm)
         ;DllCall("gdiplus\GdipCreateFromHDC", "ptr", hdc , "ptr*", gfx:=0)

         DllCall("UpdateLayeredWindow"
                  ,    "ptr", hwnd                   ; hWnd
                  ,    "ptr", 0                           ; hdcDst
                  ,"uint64*", 0 | 0 << 32                      ; *pptDst
                  ,"uint64*", width | height << 32                     ; *psize
                  ,    "ptr", hdc                    ; hdcSrc
                  , "int64*", 0                           ; *pptSrc
                  ,   "uint", 0                           ; crKey
                  ,  "uint*", 0xFF << 16 | 0x01 << 24         ; *pblend
                  ,   "uint", 2)                          ; dwFlags



         ;MsgBox Format("{:X}", Style) " | " Format("{:X}", WinGetStyle(hwnd0))
         ;MsgBox Format("{:X}", StyleEx) " | " Format("{:X}", WinGetExStyle(hwnd0))

         ;MsgBox Format("{:X}", vWinStyle) " | " Format("{:X}", WinGetStyle(hwnd))
         ;MsgBox Format("{:X}", vWinExStyle) " | " Format("{:X}", WinGetExStyle(hwnd))

      return hwnd0
   }

   static put_desktop(ByRef pBitmap) {
      ; Thanks Gerald Degeneve - https://www.codeproject.com/Articles/856020/Draw-Behind-Desktop-Icons-in-Windows-plus

      ; Get Bitmap width and height.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0)

      ; Convert the Bitmap to a hBitmap and associate a device context for blitting.
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      hbm := this.put_hBitmap(pBitmap)
      obm := DllCall("SelectObject", "ptr", hdc, "ptr", hbm, "ptr")

      ; Post-Creator's Update Windows 10. WM_SPAWN_WORKER = 0x052C
      DllCall("SendMessage", "ptr", WinExist("ahk_class Progman"), "uint", 0x052C, "ptr", 0x0000000D, "ptr", 0)
      DllCall("SendMessage", "ptr", WinExist("ahk_class Progman"), "uint", 0x052C, "ptr", 0x0000000D, "ptr", 1)

      ; Find the child window.
      windows := WinGetList("ahk_class WorkerW")
      Loop windows.length
         hwnd := windows[A_Index]
      until DllCall("FindWindowEx", "ptr", hwnd, "ptr", 0, "str", "SHELLDLL_DefView", "ptr", 0)

      ; Maybe this hack gets patched. Tough luck!
      if !(WorkerW := DllCall("FindWindowEx", "ptr", 0, "ptr", hwnd, "str", "WorkerW", "ptr", 0, "ptr"))
         throw Exception("Could not locate hidden window behind desktop.")

      ; Position the image in the center. This line can be removed.
      DllCall("SetWindowPos", "ptr", WorkerW, "ptr", 1
               , "int", Round((A_ScreenWidth - width) / 2)   ; x coordinate
               , "int", Round((A_ScreenHeight - height) / 2) ; y coordinate
               , "int", width, "int", height, "uint", 0)

      ; Get device context of spawned window.
      ddc := DllCall("GetDCEx", "ptr", WorkerW, "ptr", 0, "int", 0x403, "ptr") ; LockWindowUpdate | Cache | Window

      ; Copies a portion of the screen to a new device context.
      DllCall("gdi32\BitBlt"
               , "ptr", ddc, "int", 0, "int", 0, "int", width, "int", height
               , "ptr", hdc, "int", 0, "int", 0, "uint", 0x00CC0020) ; SRCCOPY

      ; Release device context of spawned window.
      DllCall("ReleaseDC", "ptr", 0, "ptr", ddc)

      ; Cleanup the hBitmap and device contexts.
      DllCall("SelectObject", "ptr", hdc, "ptr", obm)
      DllCall("DeleteObject", "ptr", hbm)
      DllCall("DeleteDC",     "ptr", hdc)

      return "desktop"
   }

   static put_wallpaper(ByRef pBitmap) {
      ; Create a temporary image file.
      filepath := this.put_file(pBitmap)

      ; Get the absolute path of the file.
      length := DllCall("GetFullPathName", "str", filepath, "uint", 0, "ptr", 0, "ptr", 0, "uint")
      VarSetStrCapacity(buf, length)
      DllCall("GetFullPathName", "str", filepath, "uint", length, "str", buf, "ptr", 0, "uint")

      ; Keep waiting until the file has been created. (It should be instant!)
      Loop 6 ; Try this 6 times.
         if (A_Index > 1)
            Sleep (2**(A_Index-2) * 30)
      until FileExist(filepath)
      if !FileExist(filepath)
         throw Exception("Unable to create temporary image file.")

      ; Set the temporary image file as the new desktop wallpaper.
      DllCall("SystemParametersInfo", "uint", 20, "uint", 0, "str", buf, "uint", 2)

      ; This is a delayed delete call. #Persistent may be required on v1.
      DeleteFile := Func("DllCall").Bind("DeleteFile", "str", filepath)
      SetTimer DeleteFile, -2000

      return "wallpaper"
   }

   static put_cursor(ByRef pBitmap, xHotspot := "", yHotspot := "") {
      ; Thanks Nick - https://stackoverflow.com/a/550965

      ; Creates an icon that can be used as a cursor.
      DllCall("gdiplus\GdipCreateHICONFromBitmap", "ptr", pBitmap, "ptr*", hIcon:=0)

      ; Sets the hotspot of the cursor by changing the icon into a cursor.
      if (xHotspot != "" || yHotspot != "") {
         ; struct ICONINFO - https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-iconinfo
         ii := BufferAlloc(8+3*A_PtrSize, 0)                          ; sizeof(ICONINFO) = 20, 32
         DllCall("GetIconInfo", "ptr", hIcon, "ptr", ii)              ; Fill the ICONINFO structure.
            , NumPut("uint", false, ii, 0)                            ; true/false are icon/cursor respectively.
            , (xHotspot != "") ? NumPut("uint", xHotspot, ii, 4) : "" ; Set the xHotspot value. (Default: center point)
            , (yHotspot != "") ? NumPut("uint", yHotspot, ii, 8) : "" ; Set the yHotspot value. (Default: center point)
         DllCall("DestroyIcon", "ptr", hIcon)                         ; Destroy the icon after getting the ICONINFO structure.
         hIcon := DllCall("CreateIconIndirect", "ptr", ii, "ptr")     ; Create a new cursor using ICONINFO.

         ; Clean up hbmMask and hbmColor created as a result of GetIconInfo.
         DllCall("DeleteObject", "ptr", NumGet(ii, 8+A_PtrSize, "ptr"))   ; hbmMask
         DllCall("DeleteObject", "ptr", NumGet(ii, 8+2*A_PtrSize, "ptr")) ; hbmColor
      }

      ; Loop over all 16 system cursors and change them all to the new cursor.
      SystemCursors := "32512,32513,32514,32515,32516,32640,32641,32642,32643,32644,32645,32646,32648,32649,32650,32651"
      Loop Parse, SystemCursors, ","
      { ; Must copy the handle 16 times as SetSystemCursor deletes the handle 16 times.
         hCursor := DllCall("CopyImage", "ptr", hIcon, "uint", 2, "int", 0, "int", 0, "uint", 0, "ptr")
         DllCall("SetSystemCursor", "ptr", hCursor, "int", A_LoopField) ; calls DestroyCursor
      }

      ; Destroy the original hIcon. Same as DestroyCursor.
      DllCall("DestroyIcon", "ptr", hIcon)

      ; Returns the string A_Cursor to avoid evaluation.
      return "A_Cursor"
   }

   static put_file(ByRef pBitmap, filepath := "", quality := "") {
      ; Thanks tic - https://www.autohotkey.com/boards/viewtopic.php?t=6517

      ; Remove whitespace. Seperate the filepath. Adjust for directories.
      filepath := Trim(filepath)
      SplitPath filepath,, directory, extension, filename
      if DirExist(filepath)
         directory .= "\" filename, filename := ""
      if (directory != "" && !DirExist(directory))
         DirCreate(directory)
      directory := (directory != "") ? directory : "."

      ; Validate filepath, defaulting to PNG. https://stackoverflow.com/a/6804755
      if !(extension ~= "^(?i:bmp|dib|rle|jpg|jpeg|jpe|jfif|gif|tif|tiff|png)$") {
         if (extension != "")
            filename .= "." extension
         extension := "png"
      }
      filename := RegExReplace(filename, "S)(?i:^(CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])$|[<>:|?*\x00-\x1F\x22\/\\])")
      if (filename == "")
         filename := FormatTime(, "yyyy-MM-dd HH꞉mm꞉ss")
      filepath := directory "\" filename "." extension

      ; Fill a buffer with the available encoders.
      DllCall("gdiplus\GdipGetImageEncodersSize", "uint*", count:=0, "uint*", size:=0)
      ci := BufferAlloc(size)
      DllCall("gdiplus\GdipGetImageEncoders", "uint", count, "uint", size, "ptr", ci)
      if !(count && size)
         throw Exception("Could not get a list of image codec encoders on this system.")

      ; Search for an encoder with a matching extension.
      Loop count
         EncoderExtensions := StrGet(NumGet(ci, (idx:=(48+7*A_PtrSize)*(A_Index-1))+32+3*A_PtrSize, "uptr"), "UTF-16")
      until InStr(EncoderExtensions, "*." extension)

      ; Get the pointer to the index/offset of the matching encoder.
      if !(pCodec := ci.ptr + idx)
         throw Exception("Could not find a matching encoder for the specified file format.")

      ; JPEG is a lossy image format that requires a quality value from 0-100. Default quality is 75.
      if (extension ~= "^(?i:jpg|jpeg|jpe|jfif)$"
      && IsInteger(quality) && 0 <= quality && quality <= 100 && quality != 75) {
         DllCall("gdiplus\GdipGetEncoderParameterListSize", "ptr", pBitmap, "ptr", pCodec, "uint*", size:=0)
         EncoderParameters := BufferAlloc(size, 0)
         DllCall("gdiplus\GdipGetEncoderParameterList", "ptr", pBitmap, "ptr", pCodec, "uint", size, "ptr", EncoderParameters)

         ; Search for an encoder parameter with 1 value of type 6.
         Loop NumGet(EncoderParameters, "uint")
            elem := (24+A_PtrSize)*(A_Index-1) + A_PtrSize
         until (NumGet(EncoderParameters, elem+16, "uint") = 1) && (NumGet(EncoderParameters, elem+20, "uint") = 6)

         ; struct EncoderParameter - http://www.jose.it-berater.org/gdiplus/reference/structures/encoderparameter.htm
         ep := EncoderParameters.ptr + elem - A_PtrSize                  ; sizeof(EncoderParameter) = 28, 32
            , NumPut(  "uptr",       1, ep)                              ; Must be 1.
            , NumPut(  "uint",       4, ep, 20+A_PtrSize)                ; Type
            , NumPut(  "uint", quality, NumGet(ep+24+A_PtrSize, "uptr")) ; Value (pointer)
      }

      ; Write the file to disk using the specified encoder and encoding parameters.
      Loop 6 ; Try this 6 times.
         if (A_Index > 1)
            Sleep (2**(A_Index-2) * 30)
      until (result := !DllCall("gdiplus\GdipSaveImageToFile", "ptr", pBitmap, "wstr", filepath, "ptr", pCodec, "uint", IsSet(ep) ? ep : 0))
      if !(result)
         throw Exception("Could not save file to disk.")

      return filepath
   }

   static put_hBitmap(ByRef pBitmap, alpha := "") {
      ; Revert to built in functionality if a replacement color is declared.
      if (alpha != "") { ; This built-in version is about 25% slower.
         DllCall("gdiplus\GdipCreateHBITMAPFromBitmap", "ptr", pBitmap, "ptr*", hBitmap:=0, "uint", alpha)
         return hBitmap
      }

      ; Get Bitmap width and height.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0)

      ; 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 := BufferAlloc(40, 0)                 ; sizeof(bi) = 40
         , NumPut(  "uint",        40, bi,  0) ; Size
         , NumPut(   "int",     width, bi,  4) ; Width
         , NumPut(   "int",   -height, 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")

      ; Transfer data from source pBitmap to an hBitmap manually.
      Rect := BufferAlloc(16, 0)               ; sizeof(Rect) = 16
         , NumPut(  "uint",   width, Rect,  8) ; Width
         , NumPut(  "uint",  height, Rect, 12) ; Height
      BitmapData := BufferAlloc(16+2*A_PtrSize, 0)      ; sizeof(BitmapData) = 24, 32
         , NumPut(  "uint",      width, BitmapData,  0) ; Width
         , NumPut(  "uint",     height, BitmapData,  4) ; Height
         , NumPut(   "int",  4 * width, BitmapData,  8) ; Stride
         , NumPut(   "int",    0xE200B, BitmapData, 12) ; PixelFormat
         , 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)

      ; Cleanup the hBitmap and device contexts.
      DllCall("SelectObject", "ptr", hdc, "ptr", obm)
      DllCall("DeleteDC",     "ptr", hdc)

      return hbm
   }

   static put_hIcon(ByRef pBitmap) {
      DllCall("gdiplus\GdipCreateHICONFromBitmap", "ptr", pBitmap, "ptr*", hIcon:=0)
      return hIcon
   }

   static put_stream(ByRef pBitmap, extension := "", quality := "") {
      ; Default extension is TIF for fast speeds!
      if !(extension ~= "^(?i:bmp|dib|rle|jpg|jpeg|jpe|jfif|gif|tif|tiff|png)$")
         extension := "tif"

      ; Fill a buffer with the available encoders.
      DllCall("gdiplus\GdipGetImageEncodersSize", "uint*", count:=0, "uint*", size:=0)
      ci := BufferAlloc(size)
      DllCall("gdiplus\GdipGetImageEncoders", "uint", count, "uint", size, "ptr", ci)
      if !(count && size)
         throw Exception("Could not get a list of image codec encoders on this system.")

      ; Search for an encoder with a matching extension.
      Loop count
         EncoderExtensions := StrGet(NumGet(ci, (idx:=(48+7*A_PtrSize)*(A_Index-1))+32+3*A_PtrSize, "uptr"), "UTF-16")
      until InStr(EncoderExtensions, "*." extension)

      ; Get the pointer to the index/offset of the matching encoder.
      if !(pCodec := ci.ptr + idx)
         throw Exception("Could not find a matching encoder for the specified file format.")

      ; JPEG is a lossy image format that requires a quality value from 0-100. Default quality is 75.
      if (extension ~= "^(?i:jpg|jpeg|jpe|jfif)$"
      && IsInteger(quality) && 0 <= quality && quality <= 100 && quality != 75) {
         DllCall("gdiplus\GdipGetEncoderParameterListSize", "ptr", pBitmap, "ptr", pCodec, "uint*", size:=0)
         EncoderParameters := BufferAlloc(size, 0)
         DllCall("gdiplus\GdipGetEncoderParameterList", "ptr", pBitmap, "ptr", pCodec, "uint", size, "ptr", EncoderParameters)

         ; Search for an encoder parameter with 1 value of type 6.
         Loop NumGet(EncoderParameters, "uint")
            elem := (24+A_PtrSize)*(A_Index-1) + A_PtrSize
         until (NumGet(EncoderParameters, elem+16, "uint") = 1) && (NumGet(EncoderParameters, elem+20, "uint") = 6)

         ; struct EncoderParameter - http://www.jose.it-berater.org/gdiplus/reference/structures/encoderparameter.htm
         ep := EncoderParameters.ptr + elem - A_PtrSize                  ; sizeof(EncoderParameter) = 28, 32
            , NumPut(  "uptr",       1, ep)                              ; Must be 1.
            , NumPut(  "uint",       4, ep, 20+A_PtrSize)                ; Type
            , NumPut(  "uint", quality, NumGet(ep+24+A_PtrSize, "uptr")) ; Value (pointer)
      }

      ; Create a Stream.
      DllCall("ole32\CreateStreamOnHGlobal", "ptr", 0, "int", true, "ptr*", pStream:=0)
      DllCall("gdiplus\GdipSaveImageToStream", "ptr", pBitmap, "ptr", pStream, "ptr", pCodec, "uint", IsSet(ep) ? ep : 0)

      return pStream
   }

   static put_RandomAccessStream(ByRef pBitmap, extension := "", quality := "") {
      ; Thanks teadrinker - https://www.autohotkey.com/boards/viewtopic.php?f=6&t=72674

      ; Which is faster, bmp or png?
      pStream := this.put_stream(pBitmap, extension, quality)

      ; Get the Class ID from a GUID string.
      CLSID := BufferAlloc(16, 0)
      if result := DllCall("ole32\CLSIDFromString", "wstr", "{905A0FE1-BC53-11DF-8C49-001E4FC686DA}", "ptr", CLSID, "uint")
         throw Exception("CLSIDFromString failed. Error: " . Format("{:#x}", result))

      ; Create a RandomAccessStream
      DllCall("ShCore\CreateRandomAccessStreamOverStream", "ptr", pStream, "uint", 1, "ptr", CLSID, "ptr*", pRandomAccessStream:=0, "uint")

      ; The handle to the stream object is automatically freed when the stream object is released.
      ObjRelease(pStream)

      return pRandomAccessStream
   }

   static put_hex(ByRef pBitmap, extension := "", quality := "") {
      ; Default extension is PNG for small sizes!
      if !(extension ~= "^(?i:bmp|dib|rle|jpg|jpeg|jpe|jfif|gif|tif|tiff|png)$")
         extension := "png"

      pStream := this.put_stream(pBitmap, extension, quality)

      DllCall("ole32\GetHGlobalFromStream", "ptr", pStream, "uint*", hData:=0)
      pData := DllCall("GlobalLock", "ptr", hData, "ptr")
      nSize := DllCall("GlobalSize", "uint", pData)

      bin := BufferAlloc(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", 0x4000000C, "ptr", 0, "uint*", length:=0)
      hex := BufferAlloc(length, 0)
      DllCall("Crypt32.dll\CryptBinaryToStringA", "ptr", bin, "uint", nSize, "uint", 0x4000000C, "ptr", hex, "uint*", length)

      return StrGet(hex, length, "CP0")
   }

   static put_base64(ByRef pBitmap, extension := "", quality := "") {
      ; Thanks noname - https://www.autohotkey.com/boards/viewtopic.php?style=7&p=144247#p144247

      ; Default extension is PNG for small sizes!
      if !(extension ~= "^(?i:bmp|dib|rle|jpg|jpeg|jpe|jfif|gif|tif|tiff|png)$")
         extension := "png"

      pStream := this.put_stream(pBitmap, extension, quality)

      DllCall("ole32\GetHGlobalFromStream", "ptr", pStream, "uint*", hData:=0)
      pData := DllCall("GlobalLock", "ptr", hData, "ptr")
      nSize := DllCall("GlobalSize", "uint", pData)

      bin := BufferAlloc(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*", length:=0)
      base64 := BufferAlloc(length, 0)
      DllCall("Crypt32.dll\CryptBinaryToStringA", "ptr", bin, "uint", nSize, "uint", 0x40000001, "ptr", base64, "uint*", length)

      return StrGet(base64, length, "CP0")
   }

   ; All references to gdiplus and pToken must be absolute!
   static gdiplus := 0, pToken := 0

   static gdiplusStartup() {
      ImagePut.gdiplus++

      ; Startup gdiplus when counter goes from 0 -> 1.
      if (ImagePut.gdiplus == 1) {

         ; Startup gdiplus.
         DllCall("LoadLibrary", "str", "gdiplus")
         si := BufferAlloc(A_PtrSize = 8 ? 24 : 16, 0) ; sizeof(GdiplusStartupInput) = 16, 24
            , NumPut("uint", 0x1, si)
         DllCall("gdiplus\GdiplusStartup", "ptr*", pToken:=0, "ptr", si, "ptr", 0)

         ImagePut.pToken := pToken
      }
   }

   static gdiplusShutdown(cotype := "", ByRef pBitmap := "") {
      ImagePut.gdiplus--

      ; When a buffer object is deleted a bitmap is sent here for disposal.
      if (cotype == "smart_pointer")
         if DllCall("gdiplus\GdipDisposeImage", "ptr", pBitmap)
            throw Exception("The bitmap of this buffer object has already been deleted.")

      ; Check for unpaired calls of gdiplusShutdown. 
      if (ImagePut.gdiplus < 0)
         throw Exception("Missing ImagePut.gdiplusStartup().")

      ; Shutdown gdiplus when counter goes from 1 -> 0.
      if (ImagePut.gdiplus == 0) {
         pToken := ImagePut.pToken

         ; Shutdown gdiplus.
         DllCall("gdiplus\GdiplusShutdown", "ptr", pToken)
         DllCall("FreeLibrary", "ptr", DllCall("GetModuleHandle", "str", "gdiplus", "ptr"))

         ; Exit if GDI+ is still loaded. GdiplusNotInitialized = 18
         if (18 != DllCall("gdiplus\GdipCreateImageAttributes", "ptr*", ImageAttr:=0)) {
            DllCall("gdiplus\GdipDisposeImageAttributes", "ptr", ImageAttr)
            return
         }

         ; Otherwise GDI+ has been truly unloaded from the script and objects are out of scope.
         if (cotype = "bitmap")
            throw Exception("Out of scope error. `n`nIf you wish to handle raw pointers to GDI+ bitmaps, add the line"
               . "`n`n`t`t" this.prototype.__class ".gdiplusStartup()`n`nor 'pToken := Gdip_Startup()' to the top of your script."
               . "`nAlternatively, use 'obj := ImagePutBuffer()' with 'obj.pBitmap'."
               . "`nYou can copy this message by pressing Ctrl + C.")
      }
   }
} ; End of ImagePut class.
  • Added ImagePutWindow(). Displays an image in a window with transparency. Alpha values are completely transparent. Only works on Windows 8+.
  • ImagePutFile() properly interprets destination filepaths.
  • Fix warning when using a url as input.
Image

Code: Select all

ImagePutWindow("https://i.imgur.com/YZk4Rfg.png")

Post Reply

Return to “AutoHotkey v2 Scripts and Functions”