Skip to content

Commit

Permalink
Split clipboard PNG streams and GDI bitmaps
Browse files Browse the repository at this point in the history
  • Loading branch information
iseahound authored Jan 2, 2022
1 parent 4509d51 commit 6db4f90
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 51 deletions.
94 changes: 66 additions & 28 deletions ImagePut (for v1).ahk
Original file line number Diff line number Diff line change
Expand Up @@ -158,9 +158,9 @@ class ImagePut {

; #1 - Stream intermediate.
if not decode and not crop and not scale
and (type ~= "^(?i:pdf|url|file|stream|RandomAccessStream|hex|base64)$")
and (type ~= "^(?i:clipboard_png|pdf|url|file|stream|RandomAccessStream|hex|base64)$")
and (cotype ~= "^(?i:file|stream|RandomAccessStream|hex|base64)$")
and (p[1] = "") { ; For now, disallow any specification of extensions.
and (p[1] == "") { ; For now, disallow any specification of extensions.

; Convert via stream intermediate.
pStream := this.ToStream(type, image, index)
Expand Down Expand Up @@ -208,6 +208,11 @@ class ImagePut {
; Check for image type declarations.
; Assumes that the user is telling the truth.

if ObjHasKey(image, "clipboard_png") {
image := image.clipboard_png
return "clipboard_png"
}

if ObjHasKey(image, "clipboard") {
image := image.clipboard
return "clipboard"
Expand Down Expand Up @@ -317,14 +322,22 @@ class ImagePut {
}

ImageType(image) {
; Throw if the image is an empty string.
if (image == "") {
DllCall("OpenClipboard", "ptr", A_ScriptHwnd)
result := !DllCall("IsClipboardFormatAvailable", "uint", DllCall("RegisterClipboardFormat", "str", "png", "uint")) && !DllCall("IsClipboardFormatAvailable", "uint", 2)
DllCall("CloseClipboard")
if !(result)
return "clipboard"
throw Exception("Image data is an empty string.")
}




; A "clipboard_png" is a pointer to a PNG stream saved as the "png" clipboard format.
if DllCall("IsClipboardFormatAvailable", "uint", DllCall("RegisterClipboardFormat", "str", "png", "uint"))
return "clipboard_png"

; A "clipboard" is a handle to a GDI bitmap saved as CF_BITMAP.
if DllCall("IsClipboardFormatAvailable", "uint", 2)
return "clipboard"

throw Exception("Image data is an empty string.")
}
if IsObject(image) {
; A "object" has a pBitmap property that points to an internal GDI+ bitmap.
if image.HasKey("pBitmap")
Expand Down Expand Up @@ -421,6 +434,9 @@ class ImagePut {

ToBitmap(type, image, index := 0) {

if (type = "clipboard_png")
return this.from_clipboard_png()

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

Expand Down Expand Up @@ -561,6 +577,9 @@ class ImagePut {

ToStream(type, image, index := 0) {

if (type = "clipboard_png")
return this.get_clipboard_png()

if (type = "pdf")
return this.get_pdf(image, index)

Expand Down Expand Up @@ -812,31 +831,50 @@ class ImagePut {
Sleep (2**(A_Index-1) * 30)
else throw Exception("Clipboard could not be opened.")

; Prefer the PNG stream if available because of transparency support.
png := DllCall("RegisterClipboardFormat", "str", "png", "uint")
if DllCall("IsClipboardFormatAvailable", "uint", png) {
if !(hData := DllCall("GetClipboardData", "uint", png, "ptr"))
throw Exception("Shared clipboard data has been deleted.")

; Allow the stream to be freed while leaving the hData intact.
; Please read: https://devblogs.microsoft.com/oldnewthing/20210930-00/?p=105745
DllCall("ole32\CreateStreamOnHGlobal", "ptr", hData, "int", false, "ptr*", pStream:=0, "uint")
DllCall("gdiplus\GdipCreateBitmapFromStream", "ptr", pStream, "ptr*", pBitmap:=0)
ObjRelease(pStream)
}

; Fallback to CF_BITMAP. This format does not support transparency even with put_hBitmap().
else if DllCall("IsClipboardFormatAvailable", "uint", 2) {
if !(hbm := DllCall("GetClipboardData", "uint", 2, "ptr"))
throw Exception("Shared clipboard data has been deleted.")
DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", "ptr", hbm, "ptr", 0, "ptr*", pBitmap:=0)
DllCall("DeleteObject", "ptr", hbm)
}
if !DllCall("IsClipboardFormatAvailable", "uint", 2)
throw Exception("Clipboard does not have CF_BITMAP data.")

if !(hbm := DllCall("GetClipboardData", "uint", 2, "ptr"))
throw Exception("Shared clipboard data has been deleted.")

DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", "ptr", hbm, "ptr", 0, "ptr*", pBitmap:=0)
DllCall("DeleteObject", "ptr", hbm)
DllCall("CloseClipboard")
return pBitmap
}

from_clipboard_png() {
pStream := this.get_clipboard_png()
DllCall("gdiplus\GdipCreateBitmapFromStream", "ptr", pStream, "ptr*", pBitmap:=0)
ObjRelease(pStream)
return pBitmap
}

get_clipboard_png() {
; Open the clipboard with exponential backoff.
loop
if DllCall("OpenClipboard", "ptr", A_ScriptHwnd)
break
else
if A_Index < 6
Sleep (2**(A_Index-1) * 30)
else throw Exception("Clipboard could not be opened.")

png := DllCall("RegisterClipboardFormat", "str", "png", "uint")
if !DllCall("IsClipboardFormatAvailable", "uint", png)
throw Exception("Clipboard does not have PNG stream data.")

if !(hData := DllCall("GetClipboardData", "uint", png, "ptr"))
throw Exception("Shared clipboard data has been deleted.")

; Allow the stream to be freed while leaving the hData intact.
; Please read: https://devblogs.microsoft.com/oldnewthing/20210930-00/?p=105745
DllCall("ole32\CreateStreamOnHGlobal", "ptr", hData, "int", false, "ptr*", pStream:=0, "uint")
DllCall("CloseClipboard")
return pStream
}

from_object(image) {
return this.from_bitmap(image.pBitmap)
}
Expand Down
84 changes: 61 additions & 23 deletions ImagePut.ahk
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ class ImagePut {

; #1 - Stream intermediate.
if not decode and not crop and not scale
and (type ~= "^(?i:pdf|url|file|stream|RandomAccessStream|hex|base64)$")
and (type ~= "^(?i:clipboard_png|pdf|url|file|stream|RandomAccessStream|hex|base64)$")
and (cotype ~= "^(?i:file|stream|RandomAccessStream|hex|base64)$")
and (!p.Has(1) || p[1] == "") { ; For now, disallow any specification of extensions.

Expand Down Expand Up @@ -208,6 +208,11 @@ class ImagePut {
; Check for image type declarations.
; Assumes that the user is telling the truth.

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

if ObjHasOwnProp(image, "clipboard") {
image := image.clipboard
return "clipboard"
Expand Down Expand Up @@ -322,9 +327,17 @@ class ImagePut {
throw Error("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"
if (image.base.HasOwnProp("__class") && image.base.__class == "ClipboardAll") {
; A "clipboard_png" is a pointer to a PNG stream saved as the "png" clipboard format.
if DllCall("IsClipboardFormatAvailable", "uint", DllCall("RegisterClipboardFormat", "str", "png", "uint"))
return "clipboard_png"

; A "clipboard" is a handle to a GDI bitmap saved as CF_BITMAP.
if DllCall("IsClipboardFormatAvailable", "uint", 2)
return "clipboard"

throw Error("Clipboard format not supported.")
}

; A "object" has a pBitmap property that points to an internal GDI+ bitmap.
if image.HasOwnProp("pBitmap")
Expand Down Expand Up @@ -421,6 +434,9 @@ class ImagePut {

static ToBitmap(type, image, index := 0) {

if (type = "clipboard_png")
return this.from_clipboard_png()

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

Expand Down Expand Up @@ -561,6 +577,9 @@ class ImagePut {

static ToStream(type, image, index := 0) {

if (type = "clipboard_png")
return this.get_clipboard_png()

if (type = "pdf")
return this.get_pdf(image, index)

Expand Down Expand Up @@ -812,31 +831,50 @@ class ImagePut {
Sleep (2**(A_Index-1) * 30)
else throw Error("Clipboard could not be opened.")

; Prefer the PNG stream if available because of transparency support.
png := DllCall("RegisterClipboardFormat", "str", "png", "uint")
if DllCall("IsClipboardFormatAvailable", "uint", png) {
if !(hData := DllCall("GetClipboardData", "uint", png, "ptr"))
throw Error("Shared clipboard data has been deleted.")

; Allow the stream to be freed while leaving the hData intact.
; Please read: https://devblogs.microsoft.com/oldnewthing/20210930-00/?p=105745
DllCall("ole32\CreateStreamOnHGlobal", "ptr", hData, "int", false, "ptr*", &pStream:=0, "HRESULT")
DllCall("gdiplus\GdipCreateBitmapFromStream", "ptr", pStream, "ptr*", &pBitmap:=0)
ObjRelease(pStream)
}

; Fallback to CF_BITMAP. This format does not support transparency even with put_hBitmap().
else if DllCall("IsClipboardFormatAvailable", "uint", 2) {
if !(hbm := DllCall("GetClipboardData", "uint", 2, "ptr"))
throw Error("Shared clipboard data has been deleted.")
DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", "ptr", hbm, "ptr", 0, "ptr*", &pBitmap:=0)
DllCall("DeleteObject", "ptr", hbm)
}
if !DllCall("IsClipboardFormatAvailable", "uint", 2)
throw Error("Clipboard does not have CF_BITMAP data.")

if !(hbm := DllCall("GetClipboardData", "uint", 2, "ptr"))
throw Error("Shared clipboard data has been deleted.")

DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", "ptr", hbm, "ptr", 0, "ptr*", &pBitmap:=0)
DllCall("DeleteObject", "ptr", hbm)
DllCall("CloseClipboard")
return pBitmap
}

static from_clipboard_png() {
pStream := this.get_clipboard_png()
DllCall("gdiplus\GdipCreateBitmapFromStream", "ptr", pStream, "ptr*", &pBitmap:=0)
ObjRelease(pStream)
return pBitmap
}

static get_clipboard_png() {
; Open the clipboard with exponential backoff.
loop
if DllCall("OpenClipboard", "ptr", A_ScriptHwnd)
break
else
if A_Index < 6
Sleep (2**(A_Index-1) * 30)
else throw Error("Clipboard could not be opened.")

png := DllCall("RegisterClipboardFormat", "str", "png", "uint")
if !DllCall("IsClipboardFormatAvailable", "uint", png)
throw Error("Clipboard does not have PNG stream data.")

if !(hData := DllCall("GetClipboardData", "uint", png, "ptr"))
throw Error("Shared clipboard data has been deleted.")

; Allow the stream to be freed while leaving the hData intact.
; Please read: https://devblogs.microsoft.com/oldnewthing/20210930-00/?p=105745
DllCall("ole32\CreateStreamOnHGlobal", "ptr", hData, "int", false, "ptr*", &pStream:=0, "HRESULT")
DllCall("CloseClipboard")
return pStream
}

static from_object(image) {
return this.from_bitmap(image.pBitmap)
}
Expand Down

0 comments on commit 6db4f90

Please sign in to comment.