Re: How to save image in clipboard.
Posted: 06 Sep 2021, 13:03
As a result, I don't see any advantages to use GDIp. Even though in some cases it can be faster, but WIC packs files better (and gives an ability to use custom codecs).
Let's help each other out
https://www.autohotkey.com/boards/
https://www.autohotkey.com/boards/viewtopic.php?f=76&t=94345
Code: Select all
HBitmapToPngWIC(hBitmap, destPngFilePath) {
static CLSID_WICImagingFactory := "{CACAF262-9370-4615-A13B-9F5539DA4C0A}"
, IID_IWICImagingFactory := "{EC5EC8A9-C395-4314-9C77-54D7A935FF70}"
, GUID_ContainerFormatPng := "{1B7CFAF4-713F-473C-BBCD-6137425FAEAF}"
, WICBitmapUseAlpha := 0x00000000, GENERIC_WRITE := 0x40000000
, WICBitmapEncoderNoCache := 0x00000002
VarSetCapacity(GUID, 16, 0)
DllCall("Ole32\CLSIDFromString", "WStr", GUID_ContainerFormatPng, "Ptr", &GUID)
IWICImagingFactory := ComObjCreate(CLSID_WICImagingFactory, IID_IWICImagingFactory)
; IWICImagingFactory::CreateBitmapFromHBITMAP
DllCall(NumGet(NumGet(IWICImagingFactory + 0) + A_PtrSize*21), "Ptr", IWICImagingFactory, "Ptr", hBitmap, "Ptr", 0, "UInt", WICBitmapUseAlpha, "PtrP", IWICBitmap)
; IWICImagingFactory::CreateStream
DllCall(NumGet(NumGet(IWICImagingFactory + 0) + A_PtrSize*14), "Ptr", IWICImagingFactory, "PtrP", IWICStream)
; IWICStream::InitializeFromFilename
DllCall(NumGet(NumGet(IWICStream + 0) + A_PtrSize*15), "Ptr", IWICStream, "WStr", destPngFilePath, "UInt", GENERIC_WRITE)
; IWICImagingFactory::CreateEncoder
DllCall(NumGet(NumGet(IWICImagingFactory + 0) + A_PtrSize*8), "Ptr", IWICImagingFactory, "Ptr", &GUID, "Ptr", 0, "PtrP", IWICBitmapEncoder)
; IWICBitmapEncoder::Initialize
DllCall(NumGet(NumGet(IWICBitmapEncoder + 0) + A_PtrSize*3), "Ptr", IWICBitmapEncoder, "Ptr", IWICStream, "UInt", WICBitmapEncoderNoCache)
; IWICBitmapEncoder::CreateNewFrame
DllCall(NumGet(NumGet(IWICBitmapEncoder + 0) + A_PtrSize*10), "Ptr", IWICBitmapEncoder, "PtrP", IWICBitmapFrameEncode, "PtrP", pPropertybag)
VarSetCapacity(variant, 8+A_PtrSize*2, 0)
NumPut(VT_UI1 := 0x11, variant, 0, "ushort")
NumPut(WICPngFilterNone := 1, variant, 8, "uchar")
VarSetCapacity(PROPBAG2, A_PtrSize*2+24, 0)
NumPut(11, PROPBAG2, 8, "uint")
NumPut(&(pstrName := "FilterOption"), PROPBAG2, A_PtrSize+8, "ptr")
DllCall(NumGet(NumGet(pPropertybag + 0) + A_PtrSize*4), "ptr", pPropertybag, "uint", 1, "ptr", &PROPBAG2, "ptr", &variant) ; IPropertyBag2::Write
; IWICBitmapFrameEncode::Initialize
DllCall(NumGet(NumGet(IWICBitmapFrameEncode + 0) + A_PtrSize*3), "Ptr", IWICBitmapFrameEncode, "Ptr", pPropertybag)
; IWICBitmapFrameEncode::WriteSource
DllCall(NumGet(NumGet(IWICBitmapFrameEncode + 0) + A_PtrSize*11), "Ptr", IWICBitmapFrameEncode, "Ptr", IWICBitmap, "Ptr", 0)
; IWICBitmapFrameEncode::Commit
DllCall(NumGet(NumGet(IWICBitmapFrameEncode + 0) + A_PtrSize*12), "Ptr", IWICBitmapFrameEncode)
; IWICBitmapEncoder::Commit
DllCall(NumGet(NumGet(IWICBitmapEncoder + 0) + A_PtrSize*11), "Ptr", IWICBitmapEncoder)
for k, v in [IWICBitmapFrameEncode, IWICBitmapEncoder, IWICStream, IWICBitmap, IWICImagingFactory, pPropertybag]
ObjRelease(v)
}
and
contradict each other. It just takes longer for better packing, it could be assumed.
PL doesn't pack files, but algorithm does. Why do you think c++ will give the better result?
I would say it just chooses wrong filter for compression optimization.It just takes longer for better packing
If You use c++, You can forget about wic algorithms, because they are not perfect, for example with gif they will not work at all, and use custom libraries like this:PL doesn't pack files, but algorithm does. Why do you think c++ will give the better result?
This what I see:
Why do you call it «wrong filter»? What exactly is «wrong»?[urlhttps://docs.microsoft.com/en-us/windows/win32/api/wincodec/ne-wincodec-wicpngfilteroption#constants]MSDN[/url] wrote:WICPngFilterUnspecified
Indicates an unspecified PNG filter. This enables WIC to algorithmically choose the best filtering option for the image.
What do you mean? I've already created gif files using WIC. And why not compile this library as a dll file and use it with AHK?
It chooses algorithm that takes time, but the size remaining the same.Why do you call it «wrong filter»? What exactly is «wrong»?
I mean that Wic creates very big gif files.What do you mean? I've already created gif files using WIC.
If it exists and it is OK for You, of course You can compile.And why not compile this library as a dll file and use it with AHK?
Code: Select all
UrlDownloadToFile, https://i.imgur.com/QuB8MoV.jpeg, %A_Desktop%\test.jpeg
hBitmap := LoadPicture(A_Desktop . "\test.jpeg", "GDI+")
HBitmapToPngWIC_1(hBitmap, A_Desktop . "\test_1.png")
HBitmapToPngWIC_2(hBitmap, A_Desktop . "\test_2.png")
FileGetSize, size1, %A_Desktop%\test_1.png
FileGetSize, size2, %A_Desktop%\test_2.png
MsgBox, % "size1: " . size1 . "`nsize2: " size2
HBitmapToPngWIC_1(hBitmap, destPngFilePath) {
static CLSID_WICImagingFactory := "{CACAF262-9370-4615-A13B-9F5539DA4C0A}"
, IID_IWICImagingFactory := "{EC5EC8A9-C395-4314-9C77-54D7A935FF70}"
, GUID_ContainerFormatPng := "{1B7CFAF4-713F-473C-BBCD-6137425FAEAF}"
, WICBitmapUseAlpha := 0x00000000, GENERIC_WRITE := 0x40000000
, WICBitmapEncoderNoCache := 0x00000002
VarSetCapacity(GUID, 16, 0)
DllCall("Ole32\CLSIDFromString", "WStr", GUID_ContainerFormatPng, "Ptr", &GUID)
IWICImagingFactory := ComObjCreate(CLSID_WICImagingFactory, IID_IWICImagingFactory)
Vtable( IWICImagingFactory , CreateBitmapFromHBITMAP := 21 ).Call("Ptr", hBitmap, "Ptr", 0, "UInt", WICBitmapUseAlpha, "PtrP", IWICBitmap)
Vtable( IWICImagingFactory , CreateStream := 14 ).Call("PtrP", IWICStream)
Vtable( IWICStream , InitializeFromFilename := 15 ).Call("WStr", destPngFilePath, "UInt", GENERIC_WRITE)
Vtable( IWICImagingFactory , CreateEncoder := 8 ).Call("Ptr", &GUID, "Ptr", 0, "PtrP", IWICBitmapEncoder)
Vtable( IWICBitmapEncoder , Initialize := 3 ).Call("Ptr", IWICStream, "UInt", WICBitmapEncoderNoCache)
Vtable( IWICBitmapEncoder , CreateNewFrame := 10 ).Call("PtrP", IWICBitmapFrameEncode, "Ptr", 0)
Vtable( IWICBitmapFrameEncode , Initialize := 3 ).Call("Ptr", 0)
Vtable( IWICBitmapFrameEncode , WriteSource := 11 ).Call("Ptr", IWICBitmap, "Ptr", 0)
Vtable( IWICBitmapFrameEncode , Commit := 12 ).Call()
Vtable( IWICBitmapEncoder , Commit := 11 ).Call()
for k, v in [IWICBitmapFrameEncode, IWICBitmapEncoder, IWICStream, IWICBitmap, IWICImagingFactory]
ObjRelease(v)
}
Vtable(ptr, n) {
return Func("DllCall").Bind(NumGet(NumGet(ptr+0), A_PtrSize*n), "Ptr", ptr)
}
HBitmapToPngWIC_2(hBitmap, destPngFilePath) {
static CLSID_WICImagingFactory := "{CACAF262-9370-4615-A13B-9F5539DA4C0A}"
, IID_IWICImagingFactory := "{EC5EC8A9-C395-4314-9C77-54D7A935FF70}"
, GUID_ContainerFormatPng := "{1B7CFAF4-713F-473C-BBCD-6137425FAEAF}"
, WICBitmapUseAlpha := 0x00000000, GENERIC_WRITE := 0x40000000
, WICBitmapEncoderNoCache := 0x00000002
VarSetCapacity(GUID, 16, 0)
DllCall("Ole32\CLSIDFromString", "WStr", GUID_ContainerFormatPng, "Ptr", &GUID)
IWICImagingFactory := ComObjCreate(CLSID_WICImagingFactory, IID_IWICImagingFactory)
; IWICImagingFactory::CreateBitmapFromHBITMAP
DllCall(NumGet(NumGet(IWICImagingFactory + 0) + A_PtrSize*21), "Ptr", IWICImagingFactory, "Ptr", hBitmap, "Ptr", 0, "UInt", WICBitmapUseAlpha, "PtrP", IWICBitmap)
; IWICImagingFactory::CreateStream
DllCall(NumGet(NumGet(IWICImagingFactory + 0) + A_PtrSize*14), "Ptr", IWICImagingFactory, "PtrP", IWICStream)
; IWICStream::InitializeFromFilename
DllCall(NumGet(NumGet(IWICStream + 0) + A_PtrSize*15), "Ptr", IWICStream, "WStr", destPngFilePath, "UInt", GENERIC_WRITE)
; IWICImagingFactory::CreateEncoder
DllCall(NumGet(NumGet(IWICImagingFactory + 0) + A_PtrSize*8), "Ptr", IWICImagingFactory, "Ptr", &GUID, "Ptr", 0, "PtrP", IWICBitmapEncoder)
; IWICBitmapEncoder::Initialize
DllCall(NumGet(NumGet(IWICBitmapEncoder + 0) + A_PtrSize*3), "Ptr", IWICBitmapEncoder, "Ptr", IWICStream, "UInt", WICBitmapEncoderNoCache)
; IWICBitmapEncoder::CreateNewFrame
DllCall(NumGet(NumGet(IWICBitmapEncoder + 0) + A_PtrSize*10), "Ptr", IWICBitmapEncoder, "PtrP", IWICBitmapFrameEncode, "PtrP", pPropertybag)
VarSetCapacity(variant, 8+A_PtrSize*2, 0)
NumPut(VT_UI1 := 0x11, variant, 0, "ushort")
NumPut(WICPngFilterNone := 1, variant, 8, "uchar")
VarSetCapacity(PROPBAG2, A_PtrSize*2+24, 0)
NumPut(11, PROPBAG2, 8, "uint")
NumPut(&(pstrName := "FilterOption"), PROPBAG2, A_PtrSize+8, "ptr")
DllCall(NumGet(NumGet(pPropertybag + 0) + A_PtrSize*4), "ptr", pPropertybag, "uint", 1, "ptr", &PROPBAG2, "ptr", &variant) ; IPropertyBag2::Write
; IWICBitmapFrameEncode::Initialize
DllCall(NumGet(NumGet(IWICBitmapFrameEncode + 0) + A_PtrSize*3), "Ptr", IWICBitmapFrameEncode, "Ptr", pPropertybag)
; IWICBitmapFrameEncode::WriteSource
DllCall(NumGet(NumGet(IWICBitmapFrameEncode + 0) + A_PtrSize*11), "Ptr", IWICBitmapFrameEncode, "Ptr", IWICBitmap, "Ptr", 0)
; IWICBitmapFrameEncode::Commit
DllCall(NumGet(NumGet(IWICBitmapFrameEncode + 0) + A_PtrSize*12), "Ptr", IWICBitmapFrameEncode)
; IWICBitmapEncoder::Commit
DllCall(NumGet(NumGet(IWICBitmapEncoder + 0) + A_PtrSize*11), "Ptr", IWICBitmapEncoder)
for k, v in [IWICBitmapFrameEncode, IWICBitmapEncoder, IWICStream, IWICBitmap, IWICImagingFactory, pPropertybag]
ObjRelease(v)
}
Code: Select all
UrlDownloadToFile, https://i.imgur.com/psgZToZ.png, %A_Desktop%\test.png
hBitmap := LoadPicture(A_Desktop . "\test.png", "GDI+")
HBitmapToPngWIC_1(hBitmap, A_Desktop . "\test_1.png")
HBitmapToPngWIC_2(hBitmap, A_Desktop . "\test_2.png")
FileGetSize, size1, %A_Desktop%\test_1.png
FileGetSize, size2, %A_Desktop%\test_2.png
MsgBox, % "size1: " . size1 . "`nsize2: " size2
HBitmapToPngWIC_1(hBitmap, destPngFilePath) {
static CLSID_WICImagingFactory := "{CACAF262-9370-4615-A13B-9F5539DA4C0A}"
, IID_IWICImagingFactory := "{EC5EC8A9-C395-4314-9C77-54D7A935FF70}"
, GUID_ContainerFormatPng := "{1B7CFAF4-713F-473C-BBCD-6137425FAEAF}"
, WICBitmapUseAlpha := 0x00000000, GENERIC_WRITE := 0x40000000
, WICBitmapEncoderNoCache := 0x00000002
VarSetCapacity(GUID, 16, 0)
DllCall("Ole32\CLSIDFromString", "WStr", GUID_ContainerFormatPng, "Ptr", &GUID)
IWICImagingFactory := ComObjCreate(CLSID_WICImagingFactory, IID_IWICImagingFactory)
Vtable( IWICImagingFactory , CreateBitmapFromHBITMAP := 21 ).Call("Ptr", hBitmap, "Ptr", 0, "UInt", WICBitmapUseAlpha, "PtrP", IWICBitmap)
Vtable( IWICImagingFactory , CreateStream := 14 ).Call("PtrP", IWICStream)
Vtable( IWICStream , InitializeFromFilename := 15 ).Call("WStr", destPngFilePath, "UInt", GENERIC_WRITE)
Vtable( IWICImagingFactory , CreateEncoder := 8 ).Call("Ptr", &GUID, "Ptr", 0, "PtrP", IWICBitmapEncoder)
Vtable( IWICBitmapEncoder , Initialize := 3 ).Call("Ptr", IWICStream, "UInt", WICBitmapEncoderNoCache)
Vtable( IWICBitmapEncoder , CreateNewFrame := 10 ).Call("PtrP", IWICBitmapFrameEncode, "Ptr", 0)
Vtable( IWICBitmapFrameEncode , Initialize := 3 ).Call("Ptr", 0)
Vtable( IWICBitmapFrameEncode , WriteSource := 11 ).Call("Ptr", IWICBitmap, "Ptr", 0)
Vtable( IWICBitmapFrameEncode , Commit := 12 ).Call()
Vtable( IWICBitmapEncoder , Commit := 11 ).Call()
for k, v in [IWICBitmapFrameEncode, IWICBitmapEncoder, IWICStream, IWICBitmap, IWICImagingFactory]
ObjRelease(v)
}
Vtable(ptr, n) {
return Func("DllCall").Bind(NumGet(NumGet(ptr+0), A_PtrSize*n), "Ptr", ptr)
}
HBitmapToPngWIC_2(hBitmap, destPngFilePath) {
static CLSID_WICImagingFactory := "{CACAF262-9370-4615-A13B-9F5539DA4C0A}"
, IID_IWICImagingFactory := "{EC5EC8A9-C395-4314-9C77-54D7A935FF70}"
, GUID_ContainerFormatPng := "{1B7CFAF4-713F-473C-BBCD-6137425FAEAF}"
, WICBitmapUseAlpha := 0x00000000, GENERIC_WRITE := 0x40000000
, WICBitmapEncoderNoCache := 0x00000002
VarSetCapacity(GUID, 16, 0)
DllCall("Ole32\CLSIDFromString", "WStr", GUID_ContainerFormatPng, "Ptr", &GUID)
IWICImagingFactory := ComObjCreate(CLSID_WICImagingFactory, IID_IWICImagingFactory)
; IWICImagingFactory::CreateBitmapFromHBITMAP
DllCall(NumGet(NumGet(IWICImagingFactory + 0) + A_PtrSize*21), "Ptr", IWICImagingFactory, "Ptr", hBitmap, "Ptr", 0, "UInt", WICBitmapUseAlpha, "PtrP", IWICBitmap)
; IWICImagingFactory::CreateStream
DllCall(NumGet(NumGet(IWICImagingFactory + 0) + A_PtrSize*14), "Ptr", IWICImagingFactory, "PtrP", IWICStream)
; IWICStream::InitializeFromFilename
DllCall(NumGet(NumGet(IWICStream + 0) + A_PtrSize*15), "Ptr", IWICStream, "WStr", destPngFilePath, "UInt", GENERIC_WRITE)
; IWICImagingFactory::CreateEncoder
DllCall(NumGet(NumGet(IWICImagingFactory + 0) + A_PtrSize*8), "Ptr", IWICImagingFactory, "Ptr", &GUID, "Ptr", 0, "PtrP", IWICBitmapEncoder)
; IWICBitmapEncoder::Initialize
DllCall(NumGet(NumGet(IWICBitmapEncoder + 0) + A_PtrSize*3), "Ptr", IWICBitmapEncoder, "Ptr", IWICStream, "UInt", WICBitmapEncoderNoCache)
; IWICBitmapEncoder::CreateNewFrame
DllCall(NumGet(NumGet(IWICBitmapEncoder + 0) + A_PtrSize*10), "Ptr", IWICBitmapEncoder, "PtrP", IWICBitmapFrameEncode, "PtrP", pPropertybag)
VarSetCapacity(variant, 8+A_PtrSize*2, 0)
NumPut(VT_UI1 := 0x11, variant, 0, "ushort")
NumPut(WICPngFilterNone := 1, variant, 8, "uchar")
VarSetCapacity(PROPBAG2, A_PtrSize*2+24, 0)
NumPut(11, PROPBAG2, 8, "uint")
NumPut(&(pstrName := "FilterOption"), PROPBAG2, A_PtrSize+8, "ptr")
DllCall(NumGet(NumGet(pPropertybag + 0) + A_PtrSize*4), "ptr", pPropertybag, "uint", 1, "ptr", &PROPBAG2, "ptr", &variant) ; IPropertyBag2::Write
; IWICBitmapFrameEncode::Initialize
DllCall(NumGet(NumGet(IWICBitmapFrameEncode + 0) + A_PtrSize*3), "Ptr", IWICBitmapFrameEncode, "Ptr", pPropertybag)
; IWICBitmapFrameEncode::WriteSource
DllCall(NumGet(NumGet(IWICBitmapFrameEncode + 0) + A_PtrSize*11), "Ptr", IWICBitmapFrameEncode, "Ptr", IWICBitmap, "Ptr", 0)
; IWICBitmapFrameEncode::Commit
DllCall(NumGet(NumGet(IWICBitmapFrameEncode + 0) + A_PtrSize*12), "Ptr", IWICBitmapFrameEncode)
; IWICBitmapEncoder::Commit
DllCall(NumGet(NumGet(IWICBitmapEncoder + 0) + A_PtrSize*11), "Ptr", IWICBitmapEncoder)
for k, v in [IWICBitmapFrameEncode, IWICBitmapEncoder, IWICStream, IWICBitmap, IWICImagingFactory, pPropertybag]
ObjRelease(v)
}
You forgot that we initially compared GDIplus and WIC. Compared to GDIplus, WIC packs files better, and at least in most cases is not inferior in speed with the same quality. Therefore, WIC is preferable, that's all.
Code: Select all
filepath := "test.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.")
; 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("shlwapi\SHCreateStreamOnFileEx"
, "wstr", filepath
, "uint", 0x1001 ; STGM_CREATE | STGM_WRITE
, "uint", 0x80 ; FILE_ATTRIBUTE_NORMAL
, "int", true ; fCreate is ignored when STGM_CREATE is set.
, "ptr", 0 ; pstmTemplate (reserved)
, "ptr*", pFileStream:=0
, "uint")
DllCall("shlwapi\IStream_Size", "ptr", pStream, "ptr*", size:=0, "uint")
DllCall("shlwapi\IStream_Reset", "ptr", pStream, "uint")
DllCall("shlwapi\IStream_Copy", "ptr", pStream, "ptr", pFileStream, "uint", size, "uint")
ObjRelease(pFileStream)
ObjRelease(pStream)
}
DllCall("CloseClipboard")