function to create an icon with multiple images.
Code: Select all
SetBatchLines, -1
pngImagePath := "D:\Downloads\image.png"
destIcoPath := A_Desktop . "\test.ico"
HIcons := []
for k, v in [256, 48, 32, 24, 16] {
hBitmap := ScaleImage(pngImagePath, v)
HIcons.Push( HIconFromHBitmap(hBitmap) )
DllCall("DeleteObject", "Ptr", hBitmap)
}
CollectIcon(destIcoPath, HIcons)
for k, v in HIcons
DllCall("DestroyIcon", "Ptr", v)
ScaleImage(sourceImgFile, maxSize := 256) {
static CLSID_WICImagingFactory := "{CACAF262-9370-4615-A13B-9F5539DA4C0A}"
, IID_IWICImagingFactory := "{EC5EC8A9-C395-4314-9C77-54D7A935FF70}"
, GUID_WICPixelFormat32bppBGRA := "{6FDDC324-4E03-4BFE-B185-3D77768DC90F}"
, GENERIC_READ := 0x80000000, WICDecodeMetadataCacheOnDemand := 0
, WICBitmapUseAlpha := 0, WICBitmapInterpolationModeFant := 0x3, WICBitmapInterpolationModeHighQualityCubic := 0x4
, InterpolationMode := A_OSVersion ~= "^\d" ? WICBitmapInterpolationModeHighQualityCubic : WICBitmapInterpolationModeFant
IWICImagingFactory := ComObjCreate(CLSID_WICImagingFactory, IID_IWICImagingFactory)
Vtable( IWICImagingFactory, CreateDecoderFromFilename := 3 ).Call( "WStr", sourceImgFile, "Ptr", 0, "UInt", GENERIC_READ
, "UInt", WICDecodeMetadataCacheOnDemand, "PtrP", IWICBitmapDecoder, "UInt" )
Vtable( IWICBitmapDecoder , GetFrame := 13 ).Call( "UInt", 0, "PtrP", IWICBitmapSource )
VarSetCapacity(GUID, 16, 0)
DllCall("ole32\CLSIDFromString", "WStr", GUID_WICPixelFormat32bppBGRA, "Ptr", &GUID)
hr := DllCall("Windowscodecs\WICConvertBitmapSource", "Ptr", &GUID, "Ptr", IWICBitmapSource, "PtrP", DestIWICBitmapSource)
if (hr = 0)
ObjRelease(IWICBitmapSource), IWICBitmapSource := DestIWICBitmapSource
Vtable( IWICBitmapSource, GetSize := 3 ).Call("UIntP", width, "UIntP", height)
if (width > height)
height *= maxSize/width, width := maxSize
else
width *= maxSize/height, height := maxSize
width := Round(width), height := Round(height)
stride := width*4, size := stride*height
hBitmap := CreateDIBSection(width, -height, pBits)
Vtable( IWICImagingFactory, CreateBitmapScaler := 11 ).Call("PtrP", IWICBitmapScaler)
Vtable( IWICBitmapScaler , Initialize := 8 ).Call("Ptr", IWICBitmapSource, "UInt", width, "UInt", height, "UInt", InterpolationMode)
Vtable( IWICBitmapScaler , CopyPixels := 7 ).Call("Ptr", 0, "UInt", stride, "UInt", size, "Ptr", pBits)
for k, v in [IWICBitmapScaler, IWICBitmapSource, IWICBitmapDecoder, IWICImagingFactory]
ObjRelease(v)
Return hBitmap
}
Vtable(ptr, n) {
Return Func("DllCall").Bind(NumGet(NumGet(ptr+0), A_PtrSize*n), "Ptr", ptr)
}
CreateDIBSection(w, h, ByRef ppvBits := 0, bpp := 32) {
hDC := DllCall("GetDC", "Ptr", 0, "Ptr")
VarSetCapacity(BITMAPINFO, 40, 0)
NumPut(40 , BITMAPINFO, 0)
NumPut( w , BITMAPINFO, 4)
NumPut( h , BITMAPINFO, 8)
NumPut( 1 , BITMAPINFO, 12)
NumPut(bpp, BITMAPINFO, 14)
hBM := DllCall("CreateDIBSection", "Ptr", hDC, "Ptr", &BITMAPINFO, "UInt", 0, "PtrP", ppvBits, "Ptr", 0, "UInt", 0, "Ptr")
DllCall("ReleaseDC", "Ptr", 0, "Ptr", hDC)
return hBM
}
HIconFromHBitmap(hBitmap) {
VarSetCapacity(BITMAP, size := 4*4 + A_PtrSize*2, 0)
DllCall("GetObject", "Ptr", hBitmap, "Int", size, "Ptr", &BITMAP)
width := NumGet(BITMAP, 4, "UInt"), height := NumGet(BITMAP, 8, "UInt")
hDC := DllCall("GetDC", "Ptr", 0, "Ptr")
hCBM := DllCall("CreateCompatibleBitmap", "Ptr", hDC, "Int", width, "Int", height, "Ptr")
VarSetCapacity(ICONINFO, 4*2 + A_PtrSize*3, 0)
NumPut(1, ICONINFO)
NumPut(hCBM, ICONINFO, 4*2 + A_PtrSize)
NumPut(hBitmap, ICONINFO, 4*2 + A_PtrSize*2)
hIcon := DllCall("CreateIconIndirect", "Ptr", &ICONINFO, "Ptr")
DllCall("DeleteObject", "Ptr", hCBM), DllCall("ReleaseDC", "Ptr", 0, "Ptr", hDC)
Return hIcon
}
CollectIcon(destFile, HIcons) {
static szICONHEADER := 6, szICONDIRENTRY := 16, szBITMAP := 16 + A_PtrSize*2, szBITMAPINFOHEADER := 40
, IMAGE_BITMAP := 0, flags := (LR_COPYDELETEORG := 0x8) | (LR_CREATEDIBSECTION := 0x2000)
, szDIBSECTION := szBITMAP + szBITMAPINFOHEADER + 8 + A_PtrSize*3
, copyImageParams := ["UInt", IMAGE_BITMAP, "Int", 0, "Int", 0, "UInt", flags, "Ptr"]
VarSetCapacity(ICONINFO, 8 + A_PtrSize*3, 0)
VarSetCapacity(mskDIBSECTION, szDIBSECTION, 0)
VarSetCapacity(clrDIBSECTION, szDIBSECTION, 0)
VarSetCapacity(ICONHEADER, szICONHEADER, 0)
VarSetCapacity(ICONDIRENTRY, szICONDIRENTRY, 0)
NumPut(1, ICONHEADER, 2, "UShort")
NumPut(HIcons.Count(), ICONHEADER, 4, "UShort")
File := FileOpen(destFile, "w", "cp0")
File.RawWrite(ICONHEADER, szICONHEADER)
allDataSize := 0
for k, hIcon in HIcons {
DllCall("GetIconInfo", "Ptr", hIcon, "Ptr", &ICONINFO)
if !hbmMask := DllCall("CopyImage", "Ptr", NumGet(ICONINFO, 8 + A_PtrSize), copyImageParams*) {
MsgBox, % "CopyImage failed. LastError: " . SysError()
Return
}
hbmColor := DllCall("CopyImage", "Ptr", NumGet(ICONINFO, 8 + A_PtrSize*2), copyImageParams*)
DllCall("GetObject", "Ptr", hbmMask , "Int", szDIBSECTION, "Ptr", &mskDIBSECTION)
DllCall("GetObject", "Ptr", hbmColor, "Int", szDIBSECTION, "Ptr", &clrDIBSECTION)
clrWidth := NumGet(clrDIBSECTION, 4, "UInt")
clrHeight := NumGet(clrDIBSECTION, 8, "UInt")
clrBmWidthBytes := NumGet(clrDIBSECTION, 12, "UInt")
clrBmPlanes := NumGet(clrDIBSECTION, 16, "UShort")
clrBmBitsPixel := NumGet(clrDIBSECTION, 18, "UShort")
clrBits := NumGet(clrDIBSECTION, 16 + A_PtrSize)
colorCount := clrBmBitsPixel >= 8 ? 0 : 1 << (clrBmBitsPixel * clrBmPlanes)
clrDataSize := clrBmWidthBytes * clrHeight
mskHeight := NumGet(mskDIBSECTION, 8, "UInt")
mskBmWidthBytes := NumGet(mskDIBSECTION, 12, "UInt")
mskBits := NumGet(mskDIBSECTION, 16 + A_PtrSize)
mskDataSize := mskBmWidthBytes * mskHeight
iconDataSize := clrDataSize + mskDataSize
dwBytesInRes := szBITMAPINFOHEADER + iconDataSize
dwImageOffset := szICONHEADER + szICONDIRENTRY*HIcons.Count() + allDataSize
allDataSize += dwBytesInRes
NumPut(clrWidth , ICONDIRENTRY, 0, "UChar")
NumPut(clrHeight , ICONDIRENTRY, 1, "UChar")
NumPut(colorCount , ICONDIRENTRY, 2, "UChar")
NumPut(clrBmPlanes , ICONDIRENTRY, 4, "UShort")
NumPut(clrBmBitsPixel, ICONDIRENTRY, 6, "UShort")
NumPut(dwBytesInRes , ICONDIRENTRY, 8, "UInt")
NumPut(dwImageOffset , ICONDIRENTRY, 12, "UInt")
NumPut(clrHeight*2 , clrDIBSECTION, szBITMAP + 8, "UInt")
NumPut(iconDataSize, clrDIBSECTION, szBITMAP + 20, "UInt")
File.Pos := szICONHEADER + szICONDIRENTRY*(k - 1)
File.RawWrite(ICONDIRENTRY, szICONDIRENTRY)
File.Pos := dwImageOffset
File.RawWrite(&clrDIBSECTION + szBITMAP, szBITMAPINFOHEADER)
File.RawWrite(clrBits + 0, clrDataSize)
File.RawWrite(mskBits + 0, mskDataSize)
DllCall("DeleteObject", "Ptr", hbmColor)
DllCall("DeleteObject", "Ptr", hbmMask)
}
File.Close()
}
just for clarity, this greatly reduces performance. In the real code I'd use