Jump to content

Recommended Posts

Posted (edited)

Per-Monitor v2 DPI Awareness Scaling Test:

Have this GUI running and test by changing your DPI scaling settings from: Settings app > System > Display > Scale

Work in progress, but might become more interesting. Possible UDF if everything goes well.

EDIT: My initial script forgot to set the initial DPI scaled values. 🤦‍♂️

#include <GuiEdit.au3>
#include <FontConstants.au3>
#include <WindowsNotifsConstants.au3>
#include <WinAPISysWin.au3>
#include <WinAPIGdi.au3>
#include <GUIConstantsEx.au3>
#include <AutoItConstants.au3>
#include <StructureConstants.au3>

; Apply PER_MONITOR_AWARE_V2 DPI Awareness
DllCall("User32.dll", "bool", "SetProcessDpiAwarenessContext", "int_ptr", -4)

Global $g_iScale = _WinAPI_GetDPIForWindow(WinGetHandle(AutoItWinGetTitle())) / 96
If @error Then $g_iScale = 1
Global $g_hCtrlFont = 0

Global Const $fDefFontSize = 8.5, $fDefPixel = $fDefFontSize * 96 / 72 

Global Const $WM_DPICHANGED = 0x02E0
Global Const $WM_GETDPISCALEDSIZE = 0x02E4
Global Const $WM_DPICHANGED_BEFOREPARENT = 0x02E2
Global Const $WM_DPICHANGED_AFTERPARENT = 0x02E3

OnAutoItExitRegister("_Cleanup")

Example()

Func Example()
    Local $iCtrlSpaceV = 15
    Local $sAppName = "Testing PerMonitorV2 DPI"
    Local $hGUI = GUICreate($sAppName, 440, 340, -1, -1)

    Local $idTestName = GUICtrlCreateLabel("Test Name:", 15, 20, -1, -1)
    Local $aPos = ControlGetPos($hGUI, "", $idTestName)
    Local $iPrev = $aPos[1] + $aPos[3] + $iCtrlSpaceV

    Local $idTestInput = GUICtrlCreateInput("Test Input", 15, $iPrev, 300, 22 * $g_iScale)
    Local $aPos = ControlGetPos($hGUI, "", $idTestInput)
    Local $iPrev = $aPos[1] + $aPos[3] + $iCtrlSpaceV

    Local $idCheckbox1 = GUICtrlCreateCheckbox("Test Checkbox1 ", 15, $iPrev)
    Local $aPos = ControlGetPos($hGUI, "", $idCheckbox1)
    Local $iPrev = $aPos[1] + $aPos[3] + $iCtrlSpaceV

    Local $idCheckbox2 = GUICtrlCreateCheckbox("Test Checkbox2 ", 15, $iPrev)
    Local $aPos = ControlGetPos($hGUI, "", $idCheckbox2)
    Local $iPrev = $aPos[1] + $aPos[3] + $iCtrlSpaceV

    _GUICtrlEdit_Create($hGUI, "This is a test" & @CRLF & "Another Line", 15, $iPrev, 394, 64 * $g_iScale)

    GUISetState(@SW_SHOW, $hGUI)

    GUIRegisterMsg($WM_DPICHANGED, _WM_DPICHANGED)

    While 1
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE
                ExitLoop

        EndSwitch
    WEnd

    GUIDelete($hGUI)
EndFunc   ;==>Example

Func _WM_DPICHANGED($hWnd, $iMsg, $wParam, $lParam)
    #forceref $hWnd, $iMsg, $wParam, $lParam

    Local Static $iInitDPI = $g_iScale
    Local Static $iPrevDPI = $g_iScale

    ; Obtain new DPI values
    Local $iDPI = _WinAPI_LoWord($wParam)
    Local $iNewDPI = _WinAPI_LoWord($wParam) / 96

    Local $hFont = $g_hCtrlFont

    ; Create new scaled font
    $g_hCtrlFont = _WinAPI_CreateFont(-Round($fDefPixel * $iNewDPI), 0, 0, 0, $FW_NORMAL, False, False, False, _
            $DEFAULT_CHARSET, $OUT_DEFAULT_PRECIS, $CLIP_DEFAULT_PRECIS, $PROOF_QUALITY, $DEFAULT_PITCH, "Segoe UI")

    ; Lock window drawing for smoother transition
    _WinAPI_LockWindowUpdate($hWnd)

    ; Enumerate child windows for sizing adjustments
    Local $aCtrls = _WinAPI_EnumChildWindows($hWnd, False)
    Local $hCtrl, $sClass, $aPos, $tPoint, $iX, $iY, $iW, $iH, $iOrigDPI
    If IsArray($aCtrls) Then
        For $i = 1 To $aCtrls[0][0]
            $hCtrl = $aCtrls[$i][0]
            $sClass = $aCtrls[$i][1]
            $aPos = WinGetPos($hCtrl)
            $tPoint = DllStructCreate("int X;int Y")
            DllStructSetData($tPoint, "X", $aPos[0])
            DllStructSetData($tPoint, "Y", $aPos[1])

            ; Convert screen coordinates to client
            _WinAPI_ScreenToClient($hWnd, $tPoint)
            $iX = DllStructGetData($tPoint, "X")
            $iY = DllStructGetData($tPoint, "Y")
            $iW = _WinAPI_GetWindowWidth($hCtrl)
            $iH = _WinAPI_GetWindowHeight($hCtrl)

            ; Divide value by previous DPI factor to get original size, then multiply by new DPI factor (rounded to nearest .5)
            $iX = Round((($iX / $iPrevDPI) * $iNewDPI) / 0.5) * 0.5
            $iY = Round((($iY / $iPrevDPI) * $iNewDPI) / 0.5) * 0.5
            $iW = Round((($iW / $iPrevDPI) * $iNewDPI) / 0.5) * 0.5
            $iH = Round((($iH / $iPrevDPI) * $iNewDPI) / 0.5) * 0.5

            ; Set new position/sizes for control
            Local $iCtrlID = _WinAPI_GetDlgCtrlID($hCtrl)
            If $iCtrlID < 10000 Then
                GUICtrlSetResizing($iCtrlID, $GUI_DOCKLEFT + $GUI_DOCKTOP + $GUI_DOCKWIDTH + $GUI_DOCKHEIGHT)
                GUICtrlSetPos($iCtrlID, $iX, $iY, $iW, $iH) ; working for GUICtrlCreate* controls only
            ElseIf $iCtrlID >= 10000 Then
                ControlMove($hWnd, "", $iCtrlID, $iX, $iY, $iW, $iH) ; working for UDF-created controls only
            ElseIf Not $iCtrlID Then
                ; probably custom SysLink or something which has no CtrlID
                ; can't move without CtrlID
                ; only need to modify X here
            EndIf

            ; Set new scaled font to each control
            _SendMessage($hCtrl, $WM_SETFONT, $g_hCtrlFont, True)
        Next
    EndIf

    ; Resize GUI window
    Local $tRECT = DllStructCreate($tagRECT, $lParam)
    Local $iX = $tRECT.left, $iY = $tRECT.top, $iW = $tRECT.right - $iX, $iH = $tRECT.bottom - $iY
    _WinAPI_SetWindowPos($hWnd, 0, $iX, $iY, $iW, $iH, BitOR($SWP_NOZORDER, $SWP_NOACTIVATE))

    ;
    ; IMPORTANT: Desperately need a GUICtrlGetResizing() function to restore original control resizing behavior
    ;

    ; Update previous DPI with the now current DPI
    $iPrevDPI = $iNewDPI

    _WinAPI_LockWindowUpdate(0)

    ; Delete previous font
    If $hFont Then _WinAPI_DeleteObject($hFont)

    Return 0
EndFunc   ;==>_WM_DPICHANGED

Func _Cleanup()
    _WinAPI_DeleteObject($g_hCtrlFont)
EndFunc

Func _WinAPI_GetDPIForWindow($hWnd) ; UEZ
    Local $aResult = DllCall('user32.dll', "uint", "GetDpiForWindow", "hwnd", $hWnd) ;requires Win10 v1607+ / no server support
    If Not IsArray($aResult) Or @error Then Return SetError(1, @extended, 0)
    If Not $aResult[0] Then Return SetError(2, @extended, 0)
    Return $aResult[0]
EndFunc   ;==>_WinAPI_GetDPIForWindow

 

Edited by WildByDesign
Posted

It is rather cumbersome to have to wrap and then compute each control individually. At the very least, I have not been able to think of a better approach—for instance, one that would allow the interface to become fully adaptive simply by adding a function or a parameter.

#include <GuiEdit.au3>
#include <FontConstants.au3>
#include <WindowsNotifsConstants.au3>
#include <WinAPISysWin.au3>
#include <WinAPIGdi.au3>
#include <GUIConstantsEx.au3>
#include <AutoItConstants.au3>
#include <StructureConstants.au3>

; Apply PER_MONITOR_AWARE_V2 DPI Awareness
DllCall("User32.dll", "bool", "SetProcessDpiAwarenessContext", "int_ptr", -4)

Global $g_iScale = 1
Global $g_hCtrlFont = 0

Global Const $fDefFontSize = 8.5, $fDefPixel = $fDefFontSize * 96 / 72

Global Const $WM_DPICHANGED = 0x02E0
Global Const $WM_GETDPISCALEDSIZE = 0x02E4
Global Const $WM_DPICHANGED_BEFOREPARENT = 0x02E2
Global Const $WM_DPICHANGED_AFTERPARENT = 0x02E3

Global $g_aCtrlDesign[1][5] = [[0]]
Global $g_iDesignClientW = 440, $g_iDesignClientH = 340
Global $g_iFrameW = 0, $g_iFrameH = 0

OnAutoItExitRegister("_Cleanup")

Example()

Func Example()
    Local $sAppName = "Testing PerMonitorV2 DPI"
    Local $hGUI = GUICreate($sAppName, $g_iDesignClientW, $g_iDesignClientH)

    _DPICtrlCreateLabel("Test Name:", 15, 20)
    _DPICtrlCreateInput("Test Input", 15, 50, 300, 24)
    _DPICtrlCreateCheckbox("Test Checkbox1 ", 15, 85)
    _DPICtrlCreateCheckbox("Test Checkbox2 ", 15, 110)
    _DPICtrlEditCreate($hGUI, "This is a test" & @CRLF & "Another Line", 15, 140, 394, 80)

    $g_iScale = _WinAPI_GetDPIForWindow($hGUI) / 96
    If @error Then $g_iScale = 1

    Local $aWin = WinGetPos($hGUI)
    Local $aClient = WinGetClientSize($hGUI)
    $g_iFrameW = $aWin[2] - $aClient[0]
    $g_iFrameH = $aWin[3] - $aClient[1]

    _ApplyDPI($hGUI, $g_iScale * 96)

    GUISetState(@SW_SHOW, $hGUI)

    GUIRegisterMsg($WM_DPICHANGED, _WM_DPICHANGED)

    While 1
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE
                ExitLoop
        EndSwitch
    WEnd

    GUIDelete($hGUI)
EndFunc   ;==>Example

; ------------------------------------------------------------------------------
; Wrapper functions – auto‑register each control with its design size
; ------------------------------------------------------------------------------

Func _DPICtrlCreateLabel($sText, $iX, $iY, $iW = -1, $iH = -1, $iStyle = -1, $iExStyle = -1)
    Local $id = GUICtrlCreateLabel($sText, $iX, $iY, $iW, $iH, $iStyle, $iExStyle)
    Local $hCtrl = GUICtrlGetHandle($id)
    Local $iRealW = $iW, $iRealH = $iH
    If $iW = -1 Or $iH = -1 Then
        Local $aRect = _WinAPI_GetWindowRect($hCtrl)
        If Not @error And IsArray($aRect) Then
            If $iW = -1 Then $iRealW = $aRect[2] - $aRect[0]
            If $iH = -1 Then $iRealH = $aRect[3] - $aRect[1]
        Else
            If $iW = -1 Then $iRealW = 100
            If $iH = -1 Then $iRealH = 20
        EndIf
    EndIf
    _AddDesignCtrl($hCtrl, $iX, $iY, $iRealW, $iRealH)
    Return $id
EndFunc

Func _DPICtrlCreateInput($sText, $iX, $iY, $iW, $iH, $iStyle = -1, $iExStyle = -1)
    Local $id = GUICtrlCreateInput($sText, $iX, $iY, $iW, $iH, $iStyle, $iExStyle)
    _AddDesignCtrl(GUICtrlGetHandle($id), $iX, $iY, $iW, $iH)
    Return $id
EndFunc

Func _DPICtrlCreateCheckbox($sText, $iX, $iY, $iW = -1, $iH = -1, $iStyle = -1)
    Local $id = GUICtrlCreateCheckbox($sText, $iX, $iY, $iW, $iH, $iStyle)
    Local $hCtrl = GUICtrlGetHandle($id)
    Local $iRealW = $iW, $iRealH = $iH
    If $iW = -1 Or $iH = -1 Then
        Local $aRect = _WinAPI_GetWindowRect($hCtrl)
        If Not @error And IsArray($aRect) Then
            If $iW = -1 Then $iRealW = $aRect[2] - $aRect[0]
            If $iH = -1 Then $iRealH = $aRect[3] - $aRect[1]
        Else
            If $iW = -1 Then $iRealW = 120
            If $iH = -1 Then $iRealH = 20
        EndIf
    EndIf
    _AddDesignCtrl($hCtrl, $iX, $iY, $iRealW, $iRealH)
    Return $id
EndFunc

Func _DPICtrlEditCreate($hGUI, $sText, $iX, $iY, $iW, $iH, $iStyle = -1, $iExStyle = -1)
    Local $hEdit = _GUICtrlEdit_Create($hGUI, $sText, $iX, $iY, $iW, $iH, $iStyle, $iExStyle)
    _AddDesignCtrl($hEdit, $iX, $iY, $iW, $iH)
    Return $hEdit
EndFunc

; Store control’s design dimensions (96 DPI)
Func _AddDesignCtrl($hCtrlOrID, $iX, $iY, $iW, $iH)
    Local $hCtrl = GUICtrlGetHandle($hCtrlOrID)
    If $hCtrl = 0 Then $hCtrl = $hCtrlOrID
    Local $iCount = UBound($g_aCtrlDesign, 1)
    ReDim $g_aCtrlDesign[$iCount + 1][5]
    $g_aCtrlDesign[$iCount][0] = $hCtrl
    $g_aCtrlDesign[$iCount][1] = $iX
    $g_aCtrlDesign[$iCount][2] = $iY
    $g_aCtrlDesign[$iCount][3] = $iW
    $g_aCtrlDesign[$iCount][4] = $iH
    $g_aCtrlDesign[0][0] = $iCount
EndFunc

; ------------------------------------------------------------------------------
; DPI change handler – keeps your original variable naming and logic flow
; ------------------------------------------------------------------------------

Func _WM_DPICHANGED($hWnd, $iMsg, $wParam, $lParam)
    #forceref $hWnd, $iMsg, $wParam, $lParam

    Local Static $iInitDPI = $g_iScale
    Local Static $iPrevDPI = $g_iScale

    ; Obtain new DPI value (raw)
    Local $iDPI = _WinAPI_LoWord($wParam)
    Local $iNewDPI = $iDPI / 96

    _ApplyDPI($hWnd, $iDPI, DllStructCreate($tagRECT, $lParam))

    Return 0
EndFunc   ;==>_WM_DPICHANGED

; ------------------------------------------------------------------------------
; Core scaling function
; ------------------------------------------------------------------------------

Func _ApplyDPI($hWnd, $iDPI, $tRect = 0)
    Local $iNewDPI = $iDPI / 96
    If $iNewDPI == $g_iScale And $g_hCtrlFont Then Return

    Local $hFont = $g_hCtrlFont
    $g_hCtrlFont = _WinAPI_CreateFont(-Round($fDefPixel * $iNewDPI), 0, 0, 0, $FW_NORMAL, False, False, False, _
            $DEFAULT_CHARSET, $OUT_DEFAULT_PRECIS, $CLIP_DEFAULT_PRECIS, $PROOF_QUALITY, $DEFAULT_PITCH, "Segoe UI")

    _WinAPI_LockWindowUpdate($hWnd)

    ; Adjust window size
    If IsDllStruct($tRect) Then
        ; Use suggested rectangle from the system
        Local $iX = DllStructGetData($tRect, "Left")
        Local $iY = DllStructGetData($tRect, "Top")
        Local $iW = DllStructGetData($tRect, "Right") - $iX
        Local $iH = DllStructGetData($tRect, "Bottom") - $iY
        _WinAPI_SetWindowPos($hWnd, 0, $iX, $iY, $iW, $iH, $SWP_NOZORDER)

        ; Recalculate frame difference after system resize
        Local $aNewWin = WinGetPos($hWnd)
        Local $aNewClient = WinGetClientSize($hWnd)
        $g_iFrameW = $aNewWin[2] - $aNewClient[0]
        $g_iFrameH = $aNewWin[3] - $aNewClient[1]
    Else
        ; First‑time scaling: compute new client area and center the window
        Local $iNewClientW = Round($g_iDesignClientW * $iNewDPI)
        Local $iNewClientH = Round($g_iDesignClientH * $iNewDPI)
        Local $iNewWinW = $iNewClientW + $g_iFrameW
        Local $iNewWinH = $iNewClientH + $g_iFrameH
        WinMove($hWnd, "", Default, Default, $iNewWinW, $iNewWinH)
        _CenterWindow($hWnd)
    EndIf

    ; Enumerate child controls from the design array and apply new positions/sizes
    Local $aCtrls = $g_aCtrlDesign
    For $i = 1 To $aCtrls[0][0]
        Local $hCtrl = $aCtrls[$i][0]
        Local $sClass = "" ; not used but kept for compatibility with original structure
        If Not IsHWnd($hCtrl) Then ContinueLoop

        ; Calculate new coordinates and dimensions directly from design sizes
        Local $iX = Round($aCtrls[$i][1] * $iNewDPI)
        Local $iY = Round($aCtrls[$i][2] * $iNewDPI)
        Local $iW = Round($aCtrls[$i][3] * $iNewDPI)
        Local $iH = Round($aCtrls[$i][4] * $iNewDPI)

        ; Move control using its handle
        _WinAPI_SetWindowPos($hCtrl, 0, $iX, $iY, $iW, $iH, $SWP_NOZORDER)

        ; Set the new scaled font
        _SendMessage($hCtrl, $WM_SETFONT, $g_hCtrlFont, True)
    Next

    ; Update previous DPI factor
    $g_iScale = $iNewDPI

    _WinAPI_LockWindowUpdate(0)

    ; Delete previous font
    If $hFont Then _WinAPI_DeleteObject($hFont)
EndFunc   ;==>_ApplyDPI


Func _CenterWindow($hWnd)
    Local $aWin = WinGetPos($hWnd)
    If Not IsArray($aWin) Then Return
    Local $iW = $aWin[2], $iH = $aWin[3]
    Local $aWork = _GetWorkArea()
    Local $iX = ($aWork[2] - $aWork[0] - $iW) / 2 + $aWork[0]
    Local $iY = ($aWork[3] - $aWork[1] - $iH) / 2 + $aWork[1]
    WinMove($hWnd, "", $iX, $iY)
EndFunc

Func _GetWorkArea()
    Local $tRect = DllStructCreate($tagRECT)
    DllCall("user32.dll", "bool", "SystemParametersInfoW", "uint", 0x0030, "uint", 0, "struct*", $tRect, "uint", 0)
    Local $aRect[4] = [DllStructGetData($tRect, "Left"), DllStructGetData($tRect, "Top"), _
                        DllStructGetData($tRect, "Right"), DllStructGetData($tRect, "Bottom")]
    Return $aRect
EndFunc

Func _Cleanup()
    _WinAPI_DeleteObject($g_hCtrlFont)
EndFunc

Func _WinAPI_GetDPIForWindow($hWnd) ; UEZ
    Local $aResult = DllCall('user32.dll', "uint", "GetDpiForWindow", "hwnd", $hWnd) ;requires Win10 v1607+ / no server support
    If Not IsArray($aResult) Or @error Then Return SetError(1, @extended, 0)
    If Not $aResult[0] Then Return SetError(2, @extended, 0)
    Return $aResult[0]
EndFunc   ;==>_WinAPI_GetDPIForWindow

 

Posted (edited)
24 minutes ago, fanxing said:

It is rather cumbersome to have to wrap and then compute each control individually. At the very least, I have not been able to think of a better approach—for instance, one that would allow the interface to become fully adaptive simply by adding a function or a parameter.

This is fantastic. Your modified example worked wonderfully. Excellent work! :)

I updated my initial example now because when I posted it last night, I had completely forgotten to scale the initial control sizes by the DPI factor which is why it got off to a bad start especially on 200% or higher.

EDIT: By the way, the more that I read your updated code example, the more brilliant that I realize it is. I've got a lot to learn and your updated example will absolutely help me learn more. So I am very thankful.

Edited by WildByDesign
  • 3 weeks later...
Posted (edited)

For Per-Monitor v2 DPI awareness (needed in PMv2 UDF and GUIDarkTheme UDF), I needed a way to accurately obtain the LOGFONT and create font handle with the exact menubar font that is being used on a system level. And on top of that, I needed the scaled LOGFONT especially so that I could update the menubar font anytime WM_DPICHANGED is handled. WM_DPICHANGED gets the new DPI value and, at least in my usage, would then send that DPI as a parameter to the following new function:

Func _GetSystemMenuFont($iDPI = 96)
    Local $tNCM = DllStructCreate("uint cbSize;int iBorderWidth;int iScrollWidth;int iScrollHeight;" & _
            "int iCaptionWidth;int iCaptionHeight;byte lfCaptionFont[92];" & _
            "int iSmCaptionWidth;int iSmCaptionHeight;byte lfSmCaptionFont[92];" & _
            "int iMenuWidth;int iMenuHeight;byte lfMenuFont[92];" & _
            "byte lfStatusFont[92];byte lfMessageFont[92];int iPaddedBorderWidth")

    DllStructSetData($tNCM, "cbSize", DllStructGetSize($tNCM))

    Local $aResult = DllCall("user32.dll", "bool", "SystemParametersInfoForDpi", _
            "uint", $SPI_GETNONCLIENTMETRICS, _
            "uint", DllStructGetSize($tNCM), _
            "ptr", DllStructGetPtr($tNCM), _
            "uint", 0, _
            "uint", $iDPI)

    ; Obtain LOGFONT structure for system menu font
    Local $tLogFont = DllStructCreate("long lfHeight;long lfWidth;long lfEscapement;long lfOrientation;" & _
            "long lfWeight;byte lfItalic;byte lfUnderline;byte lfStrikeOut;byte lfCharSet;" & _
            "byte lfOutPrecision;byte lfClipPrecision;byte lfQuality;byte lfPitchAndFamily;" & _
            "wchar lfFaceName[32]", DllStructGetPtr($tNCM, "lfMenuFont"))

    ; Obtain font handle for system menu font
    Local $hMenuFont = _WinAPI_CreateFontIndirect($tLogFont)

    Return $hMenuFont
EndFunc   ;==>_GetSystemMenuFont

It works perfectly. And as usual, you still need to manage deleting font resources and such. So I create an updated font on DPI change and delete the previous font.

Edited by WildByDesign
Posted (edited)

Add font icons (Segoe Fluent Icons, Segoe MDL2 Assets, etc.) directly to ImageLists.

You can specify font name, unicode hex, size and color and add to any ImageList for use in ListViews, TreeViews, Toolbars, tab controls and much more.

This is a similar concept to my previous _CreateToolbarIconFromFont() function. However, this adds color and also adds directly to ImageList.

Oh... and it is significantly sharper and smoother overall. 🤩

#include <GUIConstantsEx.au3>
#include <GuiImageList.au3>
#include <GuiListView.au3>
#include <GDIPlus.au3>
#include <WinAPITHeme.au3>

DllCall("User32.dll", "bool", "SetProcessDpiAwarenessContext", "int_ptr", -2)

Local $hGUI = GUICreate("Add Font Icons To ImageList", 600, 400)
GUISetBkColor(0x000000)

Local $iSize = 64
Local $hImageList = _GUIImageList_Create($iSize, $iSize, 5, 3) 

; Add icons to the ImageList
_AddFontIconToImageList($hImageList, "Segoe Fluent Icons", 0xE713, $iSize, 0xFF0078D4) ; Settings
_AddFontIconToImageList($hImageList, "Segoe Fluent Icons", 0xE80F, $iSize, 0xFF107C10) ; Home
_AddFontIconToImageList($hImageList, "Segoe Fluent Icons", 0xE74E, $iSize, 0xFFD83B01) ; Save

Local $idListView = GUICtrlCreateListView("Icon Name        |Description", 20, 20, 560, 360, BitOR($GUI_SS_DEFAULT_LISTVIEW, $LVS_NOCOLUMNHEADER))
GUICtrlSetBkColor(-1, 0x202020)
GUICtrlSetColor(-1, 0xFFFFFF)

_GUICtrlListView_SetImageList($idListView, $hImageList, 1) ; 1 = Small icon list

Local $idItem1 = GUICtrlCreateListViewItem("Settings|Modify application settings", $idListView)
Local $idItem2 = GUICtrlCreateListViewItem("Home|Return to main menu", $idListView)
Local $idItem3 = GUICtrlCreateListViewItem("Save|Save current project changes", $idListView)

_GUICtrlListView_SetItemImage(GUICtrlGetHandle($idListView), 0, 0)
_GUICtrlListView_SetItemImage(GUICtrlGetHandle($idListView), 1, 1)
_GUICtrlListView_SetItemImage(GUICtrlGetHandle($idListView), 2, 2)


GUISetState(@SW_SHOW)

While 1
    Switch GUIGetMsg()
        Case $GUI_EVENT_CLOSE
            ExitLoop
    EndSwitch
WEnd

; Clean up resources
_GUIImageList_Destroy($hImageList)
GUIDelete($hGUI)

Func _AddFontIconToImageList($hImageList, $sFontName, $iUnicodeDecimalOrHex, $iSize = 32, $iARGBColor = 0xFF000000)
    _GDIPlus_Startup()

    ; Render at 2x resolution for supersampling sharpness
    Local $iRenderSize = $iSize * 2 

    Local $hBitmap  = _GDIPlus_BitmapCreateFromScan0($iRenderSize, $iRenderSize)
    Local $hGraphics     = _GDIPlus_ImageGetGraphicsContext($hBitmap)
    
    ; Use high quality rendering and ClearType hint
    _GDIPlus_GraphicsSetSmoothingMode($hGraphics, 2)
    _GDIPlus_GraphicsSetInterpolationMode($hGraphics, $GDIP_INTERPOLATIONMODE_HIGHQUALITYBICUBIC)
    _GDIPlus_GraphicsSetTextRenderingHint($hGraphics, $GDIP_TEXTRENDERINGHINTCLEARTYPEGRIDFIT)

    Local $hBrush  = _GDIPlus_BrushCreateSolid($iARGBColor)
    Local $hFamily = _GDIPlus_FontFamilyCreate($sFontName)
    Local $hFont   = _GDIPlus_FontCreate($hFamily, $iRenderSize * 0.48, 0, 3)
    Local $tLayout = _GDIPlus_RectFCreate(0, 0, $iRenderSize, $iRenderSize)

    Local $hFormat = _GDIPlus_StringFormatCreate()
    _GDIPlus_StringFormatSetAlign($hFormat, 1)
    _GDIPlus_StringFormatSetLineAlign($hFormat, 1)

    Local $sChar = ChrW($iUnicodeDecimalOrHex)
    _GDIPlus_GraphicsDrawStringEx($hGraphics, $sChar, $hFont, $tLayout, $hFormat, $hBrush)

    ; Create final target size bitmap and downscale smoothly
    Local $hFinalBitmap = _GDIPlus_BitmapCreateFromScan0($iSize, $iSize)
    Local $hFinalGraphics    = _GDIPlus_ImageGetGraphicsContext($hFinalBitmap)
    _GDIPlus_GraphicsSetInterpolationMode($hFinalGraphics, $GDIP_INTERPOLATIONMODE_HIGHQUALITYBICUBIC)
    _GDIPlus_GraphicsDrawImageRect($hFinalGraphics, $hBitmap, 0, 0, $iSize, $iSize)

    ; Convert to HBITMAP
    Local $hHBITMAP = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hFinalBitmap)
    _GUIImageList_Add($hImageList, $hHBITMAP, 0)

    ; Clean up all handles
    _WinAPI_DeleteObject($hHBITMAP)
    _GDIPlus_StringFormatDispose($hFormat)
    _GDIPlus_FontDispose($hFont)
    _GDIPlus_FontFamilyDispose($hFamily)
    _GDIPlus_BrushDispose($hBrush)
    _GDIPlus_GraphicsDispose($hFinalGraphics)
    _GDIPlus_BitmapDispose($hFinalBitmap)
    _GDIPlus_GraphicsDispose($hGraphics)
    _GDIPlus_BitmapDispose($hBitmap)
    _GDIPlus_Shutdown()
    
    Return 1
EndFunc   ;==>_AddFontIconToImageList

 

Edited by WildByDesign
Posted (edited)

I have still been trying to improve the sharpness of icons created from font files.

My previous _AddFontIconToImageList() function still was not very sharp at small sizes and particularly looked worse on light backgrounds. There is _GUIImageList_AddGlyph from @ioa747 which added many improvements over mine and is a fantastic function utilizing GDI+. However, it also suffered from sub-pixel blur with smaller icons sizes.

So I have a new approach using ExtTextOut because it forcefully stays along the pixel lines. Anytime single straight lines are half on a pixel line, it's going to be blurry. I'm not very good with making demos/examples, but I've reached a level of sharpness that I may finally be content with. I may still tinker with it a bit more, but I figured I would share it now and maybe some others will have some improvements/ideas for it.

ExtTextOutW can be improved more by passing an lpDx array which does more enforcement of font character widths as well to help ensure both sides of each character so that both sides end up on pixel lines. EDIT: But I do not understand this part yet.

#AutoIt3Wrapper_Au3Check_Parameters=-d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6 -w 7

#include <GUIConstantsEx.au3>
#include <GuiImageList.au3>
#include <GuiListView.au3>
#include <GDIPlus.au3>
#include <WinAPITHeme.au3>

; $DPI_AWARENESS_CONTEXT_SYSTEM_AWARE = -2
DllCall("User32.dll", "bool", "SetProcessDpiAwarenessContext", "int_ptr", -2)

_Example()


Func _Example()
    ; Initialize GDI+
    _GDIPlus_Startup()

    Local $hGUI = GUICreate("Add Font Icons To ImageList", 600, 400)
    GUISetBkColor(0x000000)

    ; Check system availability of Segoe Fluent Icons font family
    Local Const $sFaceName = 'Segoe Fluent Icons'
    Local $sFluentIcons = _WinAPI_GetFontName($sFaceName)
    Local $bFluentIcons = True
    If Not $sFluentIcons Then $bFluentIcons = False
    Local $sIconFont = $bFluentIcons ? "Segoe Fluent Icons" : "Segoe MDL2 Assets"

    Local $iSize = 20
    Local $hImageList20 = _GUIImageList_Create($iSize, $iSize, 5, 3) 

    ; Add icons to the ImageList
    _AddFontIconToImageList($hImageList20, $sIconFont, 0xE713, $iSize, 0x0078D4, 0xFFFFFF) ; Settings
    _AddFontIconToImageList($hImageList20, $sIconFont, 0xE80F, $iSize, 0x107C10, 0xFFFFFF) ; Home
    _AddFontIconToImageList($hImageList20, $sIconFont, 0xE74E, $iSize, 0xD83B01, 0xFFFFFF) ; Save

    Local $idListView = GUICtrlCreateListView("Icon Name", 20, 20, 40, 360, BitOR($GUI_SS_DEFAULT_LISTVIEW, $LVS_NOCOLUMNHEADER, $LVS_NOSCROLL))
    ;GUICtrlSetBkColor(-1, 0x202020)
    ;GUICtrlSetColor(-1, 0xFFFFFF)

    _GUICtrlListView_SetImageList($idListView, $hImageList20, 1) ; 1 = Small icon list

    GUICtrlCreateListViewItem("", $idListView)
    GUICtrlCreateListViewItem("", $idListView)
    GUICtrlCreateListViewItem("", $idListView)

    _GUICtrlListView_SetItemImage(GUICtrlGetHandle($idListView), 0, 0)
    _GUICtrlListView_SetItemImage(GUICtrlGetHandle($idListView), 1, 1)
    _GUICtrlListView_SetItemImage(GUICtrlGetHandle($idListView), 2, 2)

    ;

    $iSize = 32
    Local $hImageList32 = _GUIImageList_Create($iSize, $iSize, 5, 3) 

    ; Add icons to the ImageList
    _AddFontIconToImageList($hImageList32, $sIconFont, 0xE713, $iSize, 0x0078D4, 0xFFFFFF) ; Settings
    _AddFontIconToImageList($hImageList32, $sIconFont, 0xE80F, $iSize, 0x107C10, 0xFFFFFF) ; Home
    _AddFontIconToImageList($hImageList32, $sIconFont, 0xE74E, $iSize, 0xD83B01, 0xFFFFFF) ; Save

    Local $idListView32 = GUICtrlCreateListView("Icon Name", 60, 20, 64, 360, BitOR($GUI_SS_DEFAULT_LISTVIEW, $LVS_NOCOLUMNHEADER, $LVS_NOSCROLL))
    ;GUICtrlSetBkColor(-1, 0x202020)
    ;GUICtrlSetColor(-1, 0xFFFFFF)

    _GUICtrlListView_SetImageList($idListView32, $hImageList32, 1) ; 1 = Small icon list

    GUICtrlCreateListViewItem("", $idListView32)
    GUICtrlCreateListViewItem("", $idListView32)
    GUICtrlCreateListViewItem("", $idListView32)

    _GUICtrlListView_SetItemImage(GUICtrlGetHandle($idListView32), 0, 0)
    _GUICtrlListView_SetItemImage(GUICtrlGetHandle($idListView32), 1, 1)
    _GUICtrlListView_SetItemImage(GUICtrlGetHandle($idListView32), 2, 2)

    ;

    $iSize = 64
    Local $hImageList64 = _GUIImageList_Create($iSize, $iSize, 5, 3) 

    ; Add icons to the ImageList
    _AddFontIconToImageList($hImageList64, $sIconFont, 0xE713, $iSize, 0x0078D4, 0xFFFFFF) ; Settings
    _AddFontIconToImageList($hImageList64, $sIconFont, 0xE80F, $iSize, 0x107C10, 0xFFFFFF) ; Home
    _AddFontIconToImageList($hImageList64, $sIconFont, 0xE74E, $iSize, 0xD83B01, 0xFFFFFF) ; Save

    Local $idListView64 = GUICtrlCreateListView("Icon Name", 100, 20, 128, 360, BitOR($GUI_SS_DEFAULT_LISTVIEW, $LVS_NOCOLUMNHEADER, $LVS_NOSCROLL))
    ;GUICtrlSetBkColor(-1, 0x202020)
    ;GUICtrlSetColor(-1, 0xFFFFFF)

    _GUICtrlListView_SetImageList($idListView64, $hImageList64, 1) ; 1 = Small icon list

    GUICtrlCreateListViewItem("", $idListView64)
    GUICtrlCreateListViewItem("", $idListView64)
    GUICtrlCreateListViewItem("", $idListView64)

    _GUICtrlListView_SetItemImage(GUICtrlGetHandle($idListView64), 0, 0)
    _GUICtrlListView_SetItemImage(GUICtrlGetHandle($idListView64), 1, 1)
    _GUICtrlListView_SetItemImage(GUICtrlGetHandle($idListView64), 2, 2)

    ;

    $iSize = 96
    Local $hImageList96 = _GUIImageList_Create($iSize, $iSize, 5, 3) 

    ; Add icons to the ImageList
    _AddFontIconToImageList($hImageList96, $sIconFont, 0xE713, $iSize, 0x0078D4, 0xFFFFFF) ; Settings
    _AddFontIconToImageList($hImageList96, $sIconFont, 0xE80F, $iSize, 0x107C10, 0xFFFFFF) ; Home
    _AddFontIconToImageList($hImageList96, $sIconFont, 0xE74E, $iSize, 0xD83B01, 0xFFFFFF) ; Save

    Local $idListView96 = GUICtrlCreateListView("Icon Name", 228, 20, 192, 360, BitOR($GUI_SS_DEFAULT_LISTVIEW, $LVS_NOCOLUMNHEADER, $LVS_NOSCROLL))
    ;GUICtrlSetBkColor(-1, 0x202020)
    ;GUICtrlSetColor(-1, 0xFFFFFF)

    _GUICtrlListView_SetImageList($idListView96, $hImageList96, 1) ; 1 = Small icon list

    GUICtrlCreateListViewItem("", $idListView96)
    GUICtrlCreateListViewItem("", $idListView96)
    GUICtrlCreateListViewItem("", $idListView96)

    _GUICtrlListView_SetItemImage(GUICtrlGetHandle($idListView96), 0, 0)
    _GUICtrlListView_SetItemImage(GUICtrlGetHandle($idListView96), 1, 1)
    _GUICtrlListView_SetItemImage(GUICtrlGetHandle($idListView96), 2, 2)


    GUISetState(@SW_SHOW)

    While 1
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE
                ExitLoop
        EndSwitch
    WEnd

    ; Cleanup
    _GUIImageList_Destroy($hImageList20)
    _GUIImageList_Destroy($hImageList32)
    _GUIImageList_Destroy($hImageList64)
    _GUIImageList_Destroy($hImageList96)
    GUIDelete($hGUI)
    _GDIPlus_Shutdown()
EndFunc   ;==>_Example

; #FUNCTION# ====================================================================================================================
; Name...........: _GUIImageList_AddGlyph
; Description ...: Creates a GDI+ rendered glyph from a font and adds it to an ImageList with supersampling and rotation support.
; Syntax.........: _GUIImageList_AddGlyph($hImageList, $sFontName, $iUnicodeCODE[, $iSize = 32[, $iTextColor = 0xFF000000[, $iRotateFlipType = 0]]])
; Parameters ....: $hImageList             - Handle to the ImageList control.
;                  $sFontName              - Name of the font family (e.g., "Segoe MDL2 Assets", "Segoe UI Emoji").
;                  $iUnicodeCODE   - Unicode code point (Decimal or Hex integer, e.g., 0xE74E or 0x1F60A).
;                  $iSize                  - [optional] Target icon size in pixels (Width & Height). Default is 32.
;                  $iTextColor             - [optional] Color of the glyph in ARGB format (0xAARRGGBB). Default is 0xFF000000 (Opaque Black).
;                  $iRotateFlipType        - [optional] GDI+ RotateFlipType enumeration (0-7). Default is 0 ($GDIP_ROTATENONEFLIPNONE).
;                                                              $GDIP_RotateNoneFlipNone = 0 (Default)
;                                                              $GDIP_Rotate90FlipNone = 1
;                                                              $GDIP_Rotate180FlipNone = 2
;                                                              $GDIP_Rotate270FlipNone = 3
;                                                              $GDIP_RotateNoneFlipX = 4
;                                                              $GDIP_Rotate90FlipX = 5
;                                                              $GDIP_Rotate180FlipX = 6
;                                                              $GDIP_Rotate270FlipX = 7
;                  $iGlyphScale            - [optional] Font scale percentage relative to canvas size (1-100). Default is 80.
; Return values .: Success - The 0-based index of the added image in the ImageList.
;                  Failure - Returns index from _GUIImageList_Add or -1.
; Author ........: ioa747
; Remarks .......: Requires _GDIPlus_Startup() to be called beforehand in your script.
; ===============================================================================================================================
Func _GUIImageList_AddGlyph($hImageList, $sFontName, $iUnicodeCODE, $iSize = 32, $iTextColor = 0xFF000000, $iRotateFlipType = 0, $iGlyphScale = 80)
    Local $iRenderSize = $iSize * 2

    Local $hBitmap = _GDIPlus_BitmapCreateFromScan0($iRenderSize, $iRenderSize)
    Local $hGraphics = _GDIPlus_ImageGetGraphicsContext($hBitmap)

    _GDIPlus_GraphicsSetSmoothingMode($hGraphics, 2)
    _GDIPlus_GraphicsSetInterpolationMode($hGraphics, $GDIP_INTERPOLATIONMODE_HIGHQUALITYBICUBIC)
    _GDIPlus_GraphicsSetTextRenderingHint($hGraphics, $GDIP_TEXTRENDERINGHINTCLEARTYPEGRIDFIT)

    Local $hBrush = _GDIPlus_BrushCreateSolid($iTextColor)
    Local $hFamily = _GDIPlus_FontFamilyCreate($sFontName)
    Local $hFont = _GDIPlus_FontCreate($hFamily, $iRenderSize * ($iGlyphScale / 100), 0, 2)
    Local $tLayout = _GDIPlus_RectFCreate(0, 0, $iRenderSize, $iRenderSize)

    Local $hFormat = _GDIPlus_StringFormatCreate()
    _GDIPlus_StringFormatSetAlign($hFormat, 1)
    _GDIPlus_StringFormatSetLineAlign($hFormat, 1)

    ; Surrogate Pairs for Unicode > 0xFFFF (Emojis, etc.)
    Local $sChar = ""
    If $iUnicodeCODE > 0xFFFF Then
        Local $iCode = $iUnicodeCODE - 0x10000
        Local $iHigh = BitOR(0xD800, BitShift($iCode, 10))
        Local $iLow = BitOR(0xDC00, BitAND($iCode, 0x3FF))
        $sChar = ChrW($iHigh) & ChrW($iLow)
    Else
        $sChar = ChrW($iUnicodeCODE)
    EndIf

    _GDIPlus_GraphicsDrawStringEx($hGraphics, $sChar, $hFont, $tLayout, $hFormat, $hBrush)

    ; Creation of final bitmap to $iSize and resampling (supersampling)
    Local $hFinalBitmap = _GDIPlus_BitmapCreateFromScan0($iSize, $iSize)
    Local $hFinalGraphics = _GDIPlus_ImageGetGraphicsContext($hFinalBitmap)
    _GDIPlus_GraphicsSetInterpolationMode($hFinalGraphics, $GDIP_INTERPOLATIONMODE_HIGHQUALITYBICUBIC)
    _GDIPlus_GraphicsDrawImageRect($hFinalGraphics, $hBitmap, 0, 0, $iSize, $iSize)

    ; ROTATE / FLIP TRANSFORMATION
    If $iRotateFlipType Then _GDIPlus_ImageRotateFlip($hFinalBitmap, $iRotateFlipType)

    ; Conversion to Win32 HBITMAP
    Local $hHBITMAP = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hFinalBitmap)

    ; Addition to ImageList
    Local $iIndex = _GUIImageList_Add($hImageList, $hHBITMAP, 0)

    ; Cleanup
    _WinAPI_DeleteObject($hHBITMAP)
    _GDIPlus_StringFormatDispose($hFormat)
    _GDIPlus_FontDispose($hFont)
    _GDIPlus_FontFamilyDispose($hFamily)
    _GDIPlus_BrushDispose($hBrush)
    _GDIPlus_GraphicsDispose($hFinalGraphics)
    _GDIPlus_BitmapDispose($hFinalBitmap)
    _GDIPlus_GraphicsDispose($hGraphics)
    _GDIPlus_BitmapDispose($hBitmap)

    Return $iIndex
EndFunc   ;==>_GUIImageList_AddGlyph

; #FUNCTION# ====================================================================================================================
; Author.........: WildByDesign
; ===============================================================================================================================
Func _WinAPI_ExtTextOut($hDC, $iX, $iY, $iOptions, $sText, $tRect = 0)
    Local $iLength = StringLen($sText)

    ; Pointer to the RECT structure, or NULL if no RECT is passed
    Local $pRECT = 0
    If IsDllStruct($tRect) Then $pRECT = DllStructGetPtr($tRect)

    Local $aResult = DllCall("gdi32.dll", "bool", "ExtTextOutW", _
            "handle", $hDC, _
            "int", $iX, _
            "int", $iY, _
            "uint", $iOptions, _
            "ptr", $pRECT, _
            "wstr", $sText, _
            "uint", $iLength, _
            "ptr", 0) ; lpDx array is omitted (NULL)

    If @error Or Not $aResult[0] Then Return SetError(1, 0, False)

    Return True
EndFunc   ;==>_WinAPI_ExtTextOut

; #FUNCTION# ====================================================================================================================
; Name...........: _AddFontIconToImageList
; Description....: Adds an icon from font file directly to ImageList
; Syntax.........: _AddFontIconToImageList($hImageList, $sFontName, $iUnicodeCODE, $iSize, $iTextColor)
; Parameters.....: $hImageList    - Handle to ImageList.
;                  $sFontName     - Font name.
;                  $iUnicodeCODE  - UNICODE code for character. Eg. 0xE72B
;                  $iSize         - Size used for ImageList and font.
;                  $iTextColor    - Color used for character. Eg. 0xRRGGBB
;                  $iBkColor      - Color used for background. Eg. 0xRRGGBB
; Return values..: ImageList index
; Author.........: WildByDesign (Thanks to ahmet)
; Remarks........: Requires _GDIPlus_Startup()
; 
; TODO: may need to also pass background color to match for toolbar buttons
; 
; ===============================================================================================================================
Func _AddFontIconToImageList($hImageList, $sFontName, $iUnicodeCODE, $iSize = 32, $iTextColor = 0x000000, $iBkColor = 0xFFFFFF)
    Local $hBitmap = _GDIPlus_BitmapCreateFromScan0($iSize, $iSize, $GDIP_PXF32PARGB)
    Local $hGraphics = _GDIPlus_ImageGetGraphicsContext($hBitmap)
    Local $hDC = _GDIPlus_GraphicsGetDC($hGraphics)
    Local $hFont = _WinAPI_CreateFont(-($iSize - 4), 0, 0, 0, 400, False, False, False, $DEFAULT_CHARSET, _
            $OUT_DEFAULT_PRECIS, $CLIP_DEFAULT_PRECIS, $CLEARTYPE_QUALITY, 0, $sFontName)
    Local $hOldFont = _WinAPI_SelectObject($hDC, $hFont)
    Local $tRect = _WinAPI_CreateRectEx(0, 0, $iSize, $iSize)

    _WinAPI_SetTextColor($hDC, _WinAPI_SwitchColor($iTextColor))
    _WinAPI_SetBkColor($hDC, _WinAPI_SwitchColor($iBkColor))
    ;_WinAPI_SetBkMode($hDC, $TRANSPARENT)
    ;Local $tRect = _WinAPI_CreateRectEx(1, 0, $iSize, $iSize)
    Local $iOptions = 0
    _WinAPI_ExtTextOut($hDC, 2, 2, $iOptions, ChrW($iUnicodeCODE), $tRect)
    _GDIPlus_GraphicsReleaseDC($hGraphics, $hDC)
    _WinAPI_SelectObject($hDC, $hOldFont)
    _WinAPI_DeleteObject($hFont)
    Local $hIcon = _GDIPlus_HICONCreateFromBitmap($hBitmap)
    _GDIPlus_GraphicsDispose($hGraphics)
    Local $hHBitmap = _WinAPI_Create32BitHBITMAP($hIcon, True)
    _WinAPI_DestroyIcon($hIcon)
    _GDIPlus_BitmapDispose($hBitmap)

    Local $iIndex = _GUIImageList_Add($hImageList, $hHBitmap, 0)
    _WinAPI_DeleteObject($hHBitmap)

    Return $iIndex
EndFunc   ;==>_AddFontIconToImageList

 

Edited by WildByDesign
Posted

I'm working on a custom toolbar project (maybe) that uses these font characters/symbols for icons. It's got custom ImageLists for states: Normal, Hot, Disabled and also Pressed which did not exist in AutoIt standard UDF functions, so I created matching functions.

The example is geared toward dark mode, but if I do some more work with it, it will do either dark or light mode. I will also set sizing of icons later depending on DPI. More work to do, but I'm pretty satisfied with the progress.

#AutoIt3Wrapper_Au3Check_Parameters=-d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6 -w 7

#include <GuiToolbar.au3>
#include <FontConstants.au3>
#include <GDIPlusConstants.au3>
#include <ListViewConstants.au3>
#include <ToolbarConstants.au3>
#include <WinAPIConstants.au3>
#include <APIGdiConstants.au3>
#include <GUIConstantsEx.au3>
#include <GuiImageList.au3>
#include <GuiListView.au3>
#include <GDIPlus.au3>
#include <WinAPITheme.au3>

; $DPI_AWARENESS_CONTEXT_SYSTEM_AWARE = -2
DllCall("User32.dll", "bool", "SetProcessDpiAwarenessContext", "int_ptr", -2)

Global $g_hToolbar
Global $g_iItem ; Command identifier of the button associated with the notification.
Global Enum $e_idNew = 1000, $e_idOpen, $e_idSave, $e_idHelp

Global Const $TB_SETPRESSEDIMAGELIST = $__TOOLBARCONSTANTS_WM_USER + 104
Global Const $TB_GETPRESSEDIMAGELIST = $__TOOLBARCONSTANTS_WM_USER + 105

_Example()


Func _Example()
    ; Initialize GDI+
    _GDIPlus_Startup()
    Local $iSlideDown = 60

    Local $hGUI = GUICreate("Add Font Icons To ImageList", 600, 400 + $iSlideDown)
    GUISetBkColor(0x202020)

    ; Check system availability of Segoe Fluent Icons font family
    Local Const $sFaceName = 'Segoe Fluent Icons'
    Local $sFluentIcons = _WinAPI_GetFontName($sFaceName)
    Local $bFluentIcons = True
    If Not $sFluentIcons Then $bFluentIcons = False
    Local $sIconFont = $bFluentIcons ? "Segoe Fluent Icons" : "Segoe MDL2 Assets"

    Local $iBkColor = 0x383838


    $g_hToolbar = _GUICtrlToolbar_Create($hGUI)
    _GUICtrlToolbar_SetColorScheme($g_hToolbar, $iBkColor, $iBkColor)
    _GUICtrlToolbar_SetStyleTransparent($g_hToolbar, False)

    Local $iBtnWidth = 32

    Local $hImageListDef = _GUIImageList_Create($iBtnWidth, $iBtnWidth, 5, 1)
    _AddFontIconToImageList($hImageListDef, $sIconFont, 0xE713, $iBtnWidth, 0xE0E0E0, $iBkColor) ; Settings
    _AddFontIconToImageList($hImageListDef, $sIconFont, 0xE80F, $iBtnWidth, 0xE0E0E0, $iBkColor) ; Home
    _AddFontIconToImageList($hImageListDef, $sIconFont, 0xE74E, $iBtnWidth, 0xE0E0E0, $iBkColor) ; Save

    Local $hImageListDis = _GUIImageList_Create($iBtnWidth, $iBtnWidth, 5, 1)
    _AddFontIconToImageList($hImageListDis, $sIconFont, 0xE713, $iBtnWidth, 0x606060, $iBkColor) ; Settings
    _AddFontIconToImageList($hImageListDis, $sIconFont, 0xE80F, $iBtnWidth, 0x606060, $iBkColor) ; Home
    _AddFontIconToImageList($hImageListDis, $sIconFont, 0xE74E, $iBtnWidth, 0x606060, $iBkColor) ; Save

    Local $hImageListHot = _GUIImageList_Create($iBtnWidth, $iBtnWidth, 5, 1)
    _AddFontIconToImageList($hImageListHot, $sIconFont, 0xE713, $iBtnWidth, 0xE0E0E0, 0x434343) ; Settings
    _AddFontIconToImageList($hImageListHot, $sIconFont, 0xE80F, $iBtnWidth, 0xE0E0E0, 0x434343) ; Home
    _AddFontIconToImageList($hImageListHot, $sIconFont, 0xE74E, $iBtnWidth, 0xE0E0E0, 0x434343) ; Save

    Local $hImageListSel = _GUIImageList_Create($iBtnWidth, $iBtnWidth, 5, 1)
    _AddFontIconToImageList($hImageListSel, $sIconFont, 0xE713, $iBtnWidth, 0xE0E0E0, 0x212121) ; Settings
    _AddFontIconToImageList($hImageListSel, $sIconFont, 0xE80F, $iBtnWidth, 0xE0E0E0, 0x212121) ; Home
    _AddFontIconToImageList($hImageListSel, $sIconFont, 0xE74E, $iBtnWidth, 0xE0E0E0, 0x212121) ; Save

    _GUICtrlToolbar_SetImageList($g_hToolbar, $hImageListDef)
    _GUICtrlToolbar_SetDisabledImageList($g_hToolbar, $hImageListDis)
    _GUICtrlToolbar_SetHotImageList($g_hToolbar, $hImageListHot)
    _GUICtrlToolbar_SetPressedImageList($g_hToolbar, $hImageListSel)

    _GUICtrlToolbar_AddButton($g_hToolbar, $e_idNew, 0)
    _GUICtrlToolbar_AddButton($g_hToolbar, $e_idOpen, 1)
    _GUICtrlToolbar_AddButtonSep($g_hToolbar)
    _GUICtrlToolbar_AddButton($g_hToolbar, $e_idSave, 2)

    _GUICtrlToolbar_EnableButton($g_hToolbar, $e_idSave, False)

    _WinAPI_SetWindowTheme($g_hToolbar, "DarkMode")

    Local $iSize = 20
    Local $hImageList20 = _GUIImageList_Create($iSize, $iSize, 5, 3) 

    ; Add icons to the ImageList
    _AddFontIconToImageList($hImageList20, $sIconFont, 0xE713, $iSize, 0x0078D4, $iBkColor) ; Settings
    _AddFontIconToImageList($hImageList20, $sIconFont, 0xE80F, $iSize, 0x107C10, $iBkColor) ; Home
    _AddFontIconToImageList($hImageList20, $sIconFont, 0xE74E, $iSize, 0xD83B01, $iBkColor) ; Save

    Local $idListView = GUICtrlCreateListView("Icon Name", 20, 20 + $iSlideDown, 40, 360, BitOR($GUI_SS_DEFAULT_LISTVIEW, $LVS_NOCOLUMNHEADER, $LVS_NOSCROLL))
    GUICtrlSetBkColor(-1, $iBkColor)
    ;GUICtrlSetColor(-1, 0xFFFFFF)

    _GUICtrlListView_SetImageList($idListView, $hImageList20, 1) ; 1 = Small icon list

    GUICtrlCreateListViewItem("", $idListView)
    GUICtrlCreateListViewItem("", $idListView)
    GUICtrlCreateListViewItem("", $idListView)

    _GUICtrlListView_SetItemImage(GUICtrlGetHandle($idListView), 0, 0)
    _GUICtrlListView_SetItemImage(GUICtrlGetHandle($idListView), 1, 1)
    _GUICtrlListView_SetItemImage(GUICtrlGetHandle($idListView), 2, 2)

    ;

    $iSize = 32
    Local $hImageList32 = _GUIImageList_Create($iSize, $iSize, 5, 3) 

    ; Add icons to the ImageList
    _AddFontIconToImageList($hImageList32, $sIconFont, 0xE713, $iSize, 0x0078D4, $iBkColor) ; Settings
    _AddFontIconToImageList($hImageList32, $sIconFont, 0xE80F, $iSize, 0x107C10, $iBkColor) ; Home
    _AddFontIconToImageList($hImageList32, $sIconFont, 0xE74E, $iSize, 0xD83B01, $iBkColor) ; Save

    Local $idListView32 = GUICtrlCreateListView("Icon Name", 60, 20 + $iSlideDown, 64, 360, BitOR($GUI_SS_DEFAULT_LISTVIEW, $LVS_NOCOLUMNHEADER, $LVS_NOSCROLL))
    GUICtrlSetBkColor(-1, $iBkColor)
    ;GUICtrlSetColor(-1, 0xFFFFFF)

    _GUICtrlListView_SetImageList($idListView32, $hImageList32, 1) ; 1 = Small icon list

    GUICtrlCreateListViewItem("", $idListView32)
    GUICtrlCreateListViewItem("", $idListView32)
    GUICtrlCreateListViewItem("", $idListView32)

    _GUICtrlListView_SetItemImage(GUICtrlGetHandle($idListView32), 0, 0)
    _GUICtrlListView_SetItemImage(GUICtrlGetHandle($idListView32), 1, 1)
    _GUICtrlListView_SetItemImage(GUICtrlGetHandle($idListView32), 2, 2)

    ;

    $iSize = 64
    Local $hImageList64 = _GUIImageList_Create($iSize, $iSize, 5, 3) 

    ; Add icons to the ImageList
    _AddFontIconToImageList($hImageList64, $sIconFont, 0xE713, $iSize, 0x0078D4, $iBkColor) ; Settings
    _AddFontIconToImageList($hImageList64, $sIconFont, 0xE80F, $iSize, 0x107C10, $iBkColor) ; Home
    _AddFontIconToImageList($hImageList64, $sIconFont, 0xE74E, $iSize, 0xD83B01, $iBkColor) ; Save

    Local $idListView64 = GUICtrlCreateListView("Icon Name", 100, 20 + $iSlideDown, 128, 360, BitOR($GUI_SS_DEFAULT_LISTVIEW, $LVS_NOCOLUMNHEADER, $LVS_NOSCROLL))
    GUICtrlSetBkColor(-1, $iBkColor)
    ;GUICtrlSetColor(-1, 0xFFFFFF)

    _GUICtrlListView_SetImageList($idListView64, $hImageList64, 1) ; 1 = Small icon list

    GUICtrlCreateListViewItem("", $idListView64)
    GUICtrlCreateListViewItem("", $idListView64)
    GUICtrlCreateListViewItem("", $idListView64)

    _GUICtrlListView_SetItemImage(GUICtrlGetHandle($idListView64), 0, 0)
    _GUICtrlListView_SetItemImage(GUICtrlGetHandle($idListView64), 1, 1)
    _GUICtrlListView_SetItemImage(GUICtrlGetHandle($idListView64), 2, 2)

    ;

    $iSize = 96
    Local $hImageList96 = _GUIImageList_Create($iSize, $iSize, 5, 3) 

    ; Add icons to the ImageList
    _AddFontIconToImageList($hImageList96, $sIconFont, 0xE713, $iSize, 0x0078D4, $iBkColor) ; Settings
    _AddFontIconToImageList($hImageList96, $sIconFont, 0xE80F, $iSize, 0x107C10, $iBkColor) ; Home
    _AddFontIconToImageList($hImageList96, $sIconFont, 0xE74E, $iSize, 0xD83B01, $iBkColor) ; Save

    Local $idListView96 = GUICtrlCreateListView("Icon Name", 228, 20 + $iSlideDown, 192, 360, BitOR($GUI_SS_DEFAULT_LISTVIEW, $LVS_NOCOLUMNHEADER, $LVS_NOSCROLL))
    GUICtrlSetBkColor(-1, $iBkColor)
    ;GUICtrlSetColor(-1, 0xFFFFFF)

    _GUICtrlListView_SetImageList($idListView96, $hImageList96, 1) ; 1 = Small icon list

    GUICtrlCreateListViewItem("", $idListView96)
    GUICtrlCreateListViewItem("", $idListView96)
    GUICtrlCreateListViewItem("", $idListView96)

    _GUICtrlListView_SetItemImage(GUICtrlGetHandle($idListView96), 0, 0)
    _GUICtrlListView_SetItemImage(GUICtrlGetHandle($idListView96), 1, 1)
    _GUICtrlListView_SetItemImage(GUICtrlGetHandle($idListView96), 2, 2)


    GUISetState(@SW_SHOW)

    While 1
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE
                ExitLoop
        EndSwitch
    WEnd

    ; Cleanup
    _GUIImageList_Destroy($hImageList20)
    _GUIImageList_Destroy($hImageList32)
    _GUIImageList_Destroy($hImageList64)
    _GUIImageList_Destroy($hImageList96)
    _GUIImageList_Destroy($hImageListDef)
    _GUIImageList_Destroy($hImageListDis)
    _GUIImageList_Destroy($hImageListHot)
    _GUIImageList_Destroy($hImageListSel)
    GUIDelete($hGUI)
    _GDIPlus_Shutdown()
EndFunc   ;==>_Example

; #FUNCTION# ====================================================================================================================
; Name...........: _GUIImageList_AddGlyph
; Description ...: Creates a GDI+ rendered glyph from a font and adds it to an ImageList with supersampling and rotation support.
; Syntax.........: _GUIImageList_AddGlyph($hImageList, $sFontName, $iUnicodeCODE[, $iSize = 32[, $iTextColor = 0xFF000000[, $iRotateFlipType = 0]]])
; Parameters ....: $hImageList             - Handle to the ImageList control.
;                  $sFontName              - Name of the font family (e.g., "Segoe MDL2 Assets", "Segoe UI Emoji").
;                  $iUnicodeCODE   - Unicode code point (Decimal or Hex integer, e.g., 0xE74E or 0x1F60A).
;                  $iSize                  - [optional] Target icon size in pixels (Width & Height). Default is 32.
;                  $iTextColor             - [optional] Color of the glyph in ARGB format (0xAARRGGBB). Default is 0xFF000000 (Opaque Black).
;                  $iRotateFlipType        - [optional] GDI+ RotateFlipType enumeration (0-7). Default is 0 ($GDIP_ROTATENONEFLIPNONE).
;                                                              $GDIP_RotateNoneFlipNone = 0 (Default)
;                                                              $GDIP_Rotate90FlipNone = 1
;                                                              $GDIP_Rotate180FlipNone = 2
;                                                              $GDIP_Rotate270FlipNone = 3
;                                                              $GDIP_RotateNoneFlipX = 4
;                                                              $GDIP_Rotate90FlipX = 5
;                                                              $GDIP_Rotate180FlipX = 6
;                                                              $GDIP_Rotate270FlipX = 7
;                  $iGlyphScale            - [optional] Font scale percentage relative to canvas size (1-100). Default is 80.
; Return values .: Success - The 0-based index of the added image in the ImageList.
;                  Failure - Returns index from _GUIImageList_Add or -1.
; Author ........: ioa747
; Remarks .......: Requires _GDIPlus_Startup() to be called beforehand in your script.
; ===============================================================================================================================
Func _GUIImageList_AddGlyph($hImageList, $sFontName, $iUnicodeCODE, $iSize = 32, $iTextColor = 0xFF000000, $iRotateFlipType = 0, $iGlyphScale = 80)
    Local $iRenderSize = $iSize * 2

    Local $hBitmap = _GDIPlus_BitmapCreateFromScan0($iRenderSize, $iRenderSize)
    Local $hGraphics = _GDIPlus_ImageGetGraphicsContext($hBitmap)

    _GDIPlus_GraphicsSetSmoothingMode($hGraphics, 2)
    _GDIPlus_GraphicsSetInterpolationMode($hGraphics, $GDIP_INTERPOLATIONMODE_HIGHQUALITYBICUBIC)
    _GDIPlus_GraphicsSetTextRenderingHint($hGraphics, $GDIP_TEXTRENDERINGHINTCLEARTYPEGRIDFIT)

    Local $hBrush = _GDIPlus_BrushCreateSolid($iTextColor)
    Local $hFamily = _GDIPlus_FontFamilyCreate($sFontName)
    Local $hFont = _GDIPlus_FontCreate($hFamily, $iRenderSize * ($iGlyphScale / 100), 0, 2)
    Local $tLayout = _GDIPlus_RectFCreate(0, 0, $iRenderSize, $iRenderSize)

    Local $hFormat = _GDIPlus_StringFormatCreate()
    _GDIPlus_StringFormatSetAlign($hFormat, 1)
    _GDIPlus_StringFormatSetLineAlign($hFormat, 1)

    ; Surrogate Pairs for Unicode > 0xFFFF (Emojis, etc.)
    Local $sChar = ""
    If $iUnicodeCODE > 0xFFFF Then
        Local $iCode = $iUnicodeCODE - 0x10000
        Local $iHigh = BitOR(0xD800, BitShift($iCode, 10))
        Local $iLow = BitOR(0xDC00, BitAND($iCode, 0x3FF))
        $sChar = ChrW($iHigh) & ChrW($iLow)
    Else
        $sChar = ChrW($iUnicodeCODE)
    EndIf

    _GDIPlus_GraphicsDrawStringEx($hGraphics, $sChar, $hFont, $tLayout, $hFormat, $hBrush)

    ; Creation of final bitmap to $iSize and resampling (supersampling)
    Local $hFinalBitmap = _GDIPlus_BitmapCreateFromScan0($iSize, $iSize)
    Local $hFinalGraphics = _GDIPlus_ImageGetGraphicsContext($hFinalBitmap)
    _GDIPlus_GraphicsSetInterpolationMode($hFinalGraphics, $GDIP_INTERPOLATIONMODE_HIGHQUALITYBICUBIC)
    _GDIPlus_GraphicsDrawImageRect($hFinalGraphics, $hBitmap, 0, 0, $iSize, $iSize)

    ; ROTATE / FLIP TRANSFORMATION
    If $iRotateFlipType Then _GDIPlus_ImageRotateFlip($hFinalBitmap, $iRotateFlipType)

    ; Conversion to Win32 HBITMAP
    Local $hHBITMAP = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hFinalBitmap)

    ; Addition to ImageList
    Local $iIndex = _GUIImageList_Add($hImageList, $hHBITMAP, 0)

    ; Cleanup
    _WinAPI_DeleteObject($hHBITMAP)
    _GDIPlus_StringFormatDispose($hFormat)
    _GDIPlus_FontDispose($hFont)
    _GDIPlus_FontFamilyDispose($hFamily)
    _GDIPlus_BrushDispose($hBrush)
    _GDIPlus_GraphicsDispose($hFinalGraphics)
    _GDIPlus_BitmapDispose($hFinalBitmap)
    _GDIPlus_GraphicsDispose($hGraphics)
    _GDIPlus_BitmapDispose($hBitmap)

    Return $iIndex
EndFunc   ;==>_GUIImageList_AddGlyph

; #FUNCTION# ====================================================================================================================
; Author.........: WildByDesign
; ===============================================================================================================================
Func _WinAPI_ExtTextOut($hDC, $iX, $iY, $iOptions, $sText, $tRect = 0)
    Local $iLength = StringLen($sText)

    ; Pointer to the RECT structure, or NULL if no RECT is passed
    Local $pRECT = 0
    If IsDllStruct($tRect) Then $pRECT = DllStructGetPtr($tRect)

    Local $aResult = DllCall("gdi32.dll", "bool", "ExtTextOutW", _
            "handle", $hDC, _
            "int", $iX, _
            "int", $iY, _
            "uint", $iOptions, _
            "ptr", $pRECT, _
            "wstr", $sText, _
            "uint", $iLength, _
            "ptr", 0) ; lpDx array is omitted (NULL)

    If @error Or Not $aResult[0] Then Return SetError(1, 0, False)

    Return True
EndFunc   ;==>_WinAPI_ExtTextOut

; #FUNCTION# ====================================================================================================================
; Name...........: _AddFontIconToImageList
; Description....: Adds an icon from font file directly to ImageList
; Syntax.........: _AddFontIconToImageList($hImageList, $sFontName, $iUnicodeCODE, $iSize, $iTextColor)
; Parameters.....: $hImageList    - Handle to ImageList.
;                  $sFontName     - Font name.
;                  $iUnicodeCODE  - UNICODE code for character. Eg. 0xE72B
;                  $iSize         - Size used for ImageList and font.
;                  $iTextColor    - Color used for character. Eg. 0xRRGGBB
;                  $iBkColor      - Color used for background. Eg. 0xRRGGBB
; Return values..: ImageList index
; Author.........: WildByDesign (Thanks to ahmet)
; Remarks........: Requires _GDIPlus_Startup()
; ===============================================================================================================================
Func _AddFontIconToImageList($hImageList, $sFontName, $iUnicodeCODE, $iSize = 32, $iTextColor = 0x000000, $iBkColor = 0xFFFFFF)
    Local $hBitmap = _GDIPlus_BitmapCreateFromScan0($iSize, $iSize, $GDIP_PXF32PARGB)
    Local $hGraphics = _GDIPlus_ImageGetGraphicsContext($hBitmap)
    Local $hDC = _GDIPlus_GraphicsGetDC($hGraphics)
    Local $hFont = _WinAPI_CreateFont(-($iSize - 4), 0, 0, 0, 400, False, False, False, $DEFAULT_CHARSET, _
            $OUT_DEFAULT_PRECIS, $CLIP_DEFAULT_PRECIS, $CLEARTYPE_QUALITY, 0, $sFontName)
    Local $hOldFont = _WinAPI_SelectObject($hDC, $hFont)
    Local $tRect = _WinAPI_CreateRectEx(0, 0, $iSize, $iSize)

    _WinAPI_SetTextColor($hDC, _WinAPI_SwitchColor($iTextColor))
    _WinAPI_SetBkColor($hDC, _WinAPI_SwitchColor($iBkColor))

    Local $iOptions = 0
    _WinAPI_ExtTextOut($hDC, 2, 2, $iOptions, ChrW($iUnicodeCODE), $tRect)

    _GDIPlus_GraphicsReleaseDC($hGraphics, $hDC)
    _WinAPI_SelectObject($hDC, $hOldFont)
    _WinAPI_DeleteObject($hFont)
    Local $hIcon = _GDIPlus_HICONCreateFromBitmap($hBitmap)
    _GDIPlus_GraphicsDispose($hGraphics)
    Local $hHBitmap = _WinAPI_Create32BitHBITMAP($hIcon, True)
    _WinAPI_DestroyIcon($hIcon)
    _GDIPlus_BitmapDispose($hBitmap)

    Local $iIndex = _GUIImageList_Add($hImageList, $hHBitmap, 0)
    _WinAPI_DeleteObject($hHBitmap)

    Return $iIndex
EndFunc   ;==>_AddFontIconToImageList

Func _GUICtrlToolbar_GetPressedImageList($hWnd)
    Return Ptr(_SendMessage($hWnd, $TB_GETPRESSEDIMAGELIST))
EndFunc   ;==>_GUICtrlToolbar_GetPressedImageList

Func _GUICtrlToolbar_SetPressedImageList($hWnd, $hImageList)
    Return _SendMessage($hWnd, $TB_SETPRESSEDIMAGELIST, 0, $hImageList, 0, "wparam", "handle", "handle")
EndFunc   ;==>_GUICtrlToolbar_SetPressedImageList

 

Posted

Made a post in a thread and created a log entry as I tend to do at times.
Here is a calculator for the stardate:

#include <GUIConstantsEx.au3>
#include <Date.au3>

Local $hGui = GUICreate("Star Trek Stardate Calculator", 450, 200)

GUICtrlCreateLabel("Earth Date & Time (YYYY/MM/DD HH:MM:SS):", 20, 20, 300, 20)
Local $inputEarth = GUICtrlCreateInput(_NowCalc(), 20, 40, 280, 25)
Local $btnToStar = GUICtrlCreateButton("-> Stardate", 310, 40, 110, 25)

GUICtrlCreateLabel("Stardate:", 20, 90, 300, 20)
Local $inputStar = GUICtrlCreateInput("", 20, 110, 280, 25)
Local $btnToEarth = GUICtrlCreateButton("-> Earth Date", 310, 110, 110, 25)

GUICtrlCreateLabel("Base reference: January 1, 2323 = Stardate 0 (1,000 units per year).", 20, 160, 400, 20)

GUISetState(@SW_SHOW)

While 1
    Switch GUIGetMsg()
        Case $GUI_EVENT_CLOSE
            ExitLoop

        Case $btnToStar
            Local $sEarth = StringReplace(GUICtrlRead($inputEarth), "-", "/")
            If _DateIsValid($sEarth) Then
                Local $fStar = EarthToStardate($sEarth)
                GUICtrlSetData($inputStar, StringFormat("%.10f", $fStar)) ; to match "https://www.hillschmidt.de/gbr/sternenzeit.htm" length
                ; In Star Trek: The Next Generation and its successor series (Deep Space Nine and Voyager),
                ; the stardate decimal (the single digit following the dot, like 41153.7) is officially defined
                ; in the show's writing guides as a fractional day counter (representing tenths of a day).

            Else
                MsgBox(48, "Error", "Invalid Earth Date format. Use YYYY/MM/DD HH:MM:SS")
            EndIf

        Case $btnToEarth
            Local $fStar = Number(GUICtrlRead($inputStar))
            Local $sEarth = StardateToEarth($fStar)
            GUICtrlSetData($inputEarth, $sEarth)

    EndSwitch
WEnd

Func EarthToStardate($sDate)
    Local $iYear = Number(StringLeft($sDate, 4))
    Local $iBaseYear = 2323

    Local $sJan1 = $iYear & "/01/01 00:00:00"
    Local $iTotalDaysInYear = 365
    If _DateIsLeapYear($iYear) Then $iTotalDaysInYear = 366

    ; Calculate exact year fraction using total seconds elapsed from Jan 1
    Local $iSecPassed = _DateDiff('s', $sJan1, $sDate)
    Local $fYearFraction = $iSecPassed / (86400 * $iTotalDaysInYear)

    Local $fStardate = (($iYear - $iBaseYear) + $fYearFraction) * 1000
    Return $fStardate
EndFunc   ;==>EarthToStardate

Func StardateToEarth($fStardate)
    Local $iBaseYear = 2323
    Local $fYearsSinceBase = $fStardate / 1000
    Local $iYear = $iBaseYear + Int($fYearsSinceBase)
    Local $fFraction = $fYearsSinceBase - Int($fYearsSinceBase)

    Local $iTotalDaysInYear = 365
    If _DateIsLeapYear($iYear) Then $iTotalDaysInYear = 366

    Local $iTotalSecInYear = 86400 * $iTotalDaysInYear
    Local $iSecsIntoYear = Round($fFraction * $iTotalSecInYear)

    Local $sResultDate = $iYear & "/01/01 00:00:00"
    $sResultDate = _DateAdd('s', $iSecsIntoYear, $sResultDate)

    Return $sResultDate
EndFunc   ;==>StardateToEarth

:)

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting  image.gif.922e3a93535f431de08b31ee669cc446.gif
autoit_scripter_blue_userbar.png

Posted (edited)

I've done a few examples for adding a font glyph to an ImageList. One used DrawText, while another used ExtTextOut and another used GDI+. The best GDI+ example right now is _GUIImageList_AddGlyph by @ioa747. I recommend that for anyone who wants a GDI+ method and particularly for larger sized icons.

From my experience with GDI's DrawText and ExtTextOut, they were the best for sharp font glyphs (especially smaller sized icons) since they strictly stay on the pixel grid. The problem that I had here was that I would have beautiful icons as long as I set a background. Whenever I used transparency, which is really needed for ImageLists, the fonts would be grainy because GDI generally needs a background color to blend with.

I had good success with GDI+ as well, but only at larger font sizes. Font sizes 16, 20 and 24 would end up off the pixel grid and blurry.

So, I got the idea to use GetGlyphOutlineW and keep it strictly GDI only. GetGlyphOutlineW is supposed to guarantee font glyph crispness. But I had no idea how to use GetGlyphOutlineW properly and still don't understand it very well. So I did have to use AI for the GetGlyphOutlineW part of this example, otherwise I never would have succeeded with this idea to use GetGlyphOutlineW.

With the following font glyph to ImageList icons example, we get great font quality on dark or light, big or small and with proper transparency. I am very impressed. :)

; $DPI_AWARENESS_CONTEXT_SYSTEM_AWARE = -2
DllCall("User32.dll", "bool", "SetProcessDpiAwarenessContext", "int_ptr", -2)

#include <GUIConstantsEx.au3>
#include <GuiListView.au3>
#include <GuiImageList.au3>
#include <WinAPI.au3>
#include <WindowsConstants.au3>

Example()

Func Example()
    ; Check system availability of Segoe Fluent Icons font family
    Local Const $sFaceName = 'Segoe Fluent Icons'
    Local $sFluentIcons = _WinAPI_GetFontName($sFaceName)
    Local $bFluentIcons = True
    If Not $sFluentIcons Then $bFluentIcons = False
    Local $sIconFont = $bFluentIcons ? "Segoe Fluent Icons" : "Segoe MDL2 Assets"

    Local $iIconSize = 28
    Local $iFontHeight = 24
    Local $iFontColor = 0xBEBEBE
    Local $iGuiBkColor = 0x202020
    Local $iListViewBkColor = 0x202020
    Local $iListViewColor = 0xBEBEBE

    Local $hGUI = GUICreate("GetGlyphOutlineW Example", 500, 300)
    GUISetBkColor($iGuiBkColor)
    Local $idListView = GUICtrlCreateListView("ListView", 10, 10, 480, 240, BitOR($GUI_SS_DEFAULT_LISTVIEW, $LVS_NOCOLUMNHEADER))
    GUICtrlSetBkColor(-1, $iListViewBkColor)
    GUICtrlSetColor(-1, $iListViewColor)
    Local $hListView = GUICtrlGetHandle($idListView)

    Local $hImageList = _GUIImageList_Create($iIconSize, $iIconSize, 5, 4)

    _AddFontIconToImageList($hImageList, $sIconFont, 0xE713, $iIconSize, $iFontHeight, $iFontColor, True) ; True is needed for dark mode
    _AddFontIconToImageList($hImageList, $sIconFont, 0xE80F, $iIconSize, $iFontHeight, $iFontColor, True)
    _AddFontIconToImageList($hImageList, $sIconFont, 0xE74E, $iIconSize, $iFontHeight, $iFontColor, True)
    _AddFontIconToImageList($hImageList, $sIconFont, 0xE946, $iIconSize, $iFontHeight, $iFontColor, True)

    _GUICtrlListView_SetImageList($hListView, $hImageList, 1)

    ; Add rows mapping to different icon indices
    _GUICtrlListView_AddItem($hListView, "Settings", 0)
    _GUICtrlListView_AddItem($hListView, "Home", 1)
    _GUICtrlListView_AddItem($hListView, "Save", 2)
    _GUICtrlListView_AddItem($hListView, "Info", 3)

    _GUICtrlListView_SetColumnWidth($hListView, 0, $LVSCW_AUTOSIZE)

    GUISetState(@SW_SHOW)

    While 1
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE
                ExitLoop
        EndSwitch
    WEnd
EndFunc

; #FUNCTION# ====================================================================================================================
; Name...........: _AddFontIconToImageList
; Description....: Adds an icon from font file directly to ImageList
; Syntax.........: _AddFontIconToImageList($hImageList, $sFont, $iCodePoint, $iIconSize, $iFontSize, $iTextColor, $bDarkMode)
; Parameters.....: $hImageList    - Handle to ImageList.
;                  $sFont         - Font name. Eg. Segoe MDL2 Assets
;                  $iCodePoint    - Unicode code point for character. Eg. 0xE72B
;                  $iIconSize     - Size used for ImageList icon size.
;                  $iFontSize     - Size used for character height of font glyph.
;                  $iTextColor    - Color used for font glyph. Eg. 0xRRGGBB
;                  $bDarkMode     - If True, applies gamma curve to improve light on dark
; Return values..: ImageList index
; Author.........: WildByDesign
; ===============================================================================================================================
Func _AddFontIconToImageList($hImageList, $sFont, $iCodePoint, $iIconSize = 32, $iFontSize = 24, $iTextColor = 0x000000, $bDarkMode = False)
    Local $hScreenDC = _WinAPI_GetDC(0)
    Local $hMemDC = _WinAPI_CreateCompatibleDC($hScreenDC)
    Local $hFont = _WinAPI_CreateFont(-($iFontSize), 0, 0, 0, 400, False, False, False, 1, 0, 0, 0, 0, $sFont)
    Local $hOldFont = _WinAPI_SelectObject($hMemDC, $hFont)

    Local $tGM = DllStructCreate("uint bmBlackBoxX;uint bmBlackBoxY;long gmptGlyphOriginX;long gmptGlyphOriginY;short gmCellIncX;short gmCellIncY")
    Local $tMat2 = DllStructCreate("int eM11;int eM12;int eM21;int eM22")
    DllStructSetData($tMat2, "eM11", 0x00010000)
    DllStructSetData($tMat2, "eM22", 0x00010000)

    ; Obtain glyph outline
    Local $iRet = DllCall("gdi32.dll", "uint", "GetGlyphOutlineW", "handle", $hMemDC, "uint", AscW(ChrW($iCodePoint)), "uint", 6, "struct*", $tGM, "dword", 0, "ptr", 0, "struct*", $tMat2)
    
    If $iRet[0] > 0 Then
        Local $iBufSize = $iRet[0]
        Local $tBuffer = DllStructCreate("byte[" & $iBufSize & "]")
        DllCall("gdi32.dll", "uint", "GetGlyphOutlineW", "handle", $hMemDC, "uint", AscW(ChrW($iCodePoint)), "uint", 6, "struct*", $tGM, "dword", $iBufSize, "struct*", $tBuffer, "struct*", $tMat2)

        ; Determine offsets to center font glyph
        Local $iBoxX = DllStructGetData($tGM, "bmBlackBoxX")
        Local $iBoxY = DllStructGetData($tGM, "bmBlackBoxY")
        Local $iStride = Int(($iBoxX + 3) / 4) * 4
        Local $iOffsetX = Int(($iIconSize - $iBoxX) / 2)
        Local $iOffsetY = Int(($iIconSize - $iBoxY) / 2)

        Local $iHexColor = $iTextColor
        Local $bBaseR = BitAnd(BitShift($iHexColor, 16), 0xFF)
        Local $bBaseG = BitAnd(BitShift($iHexColor, 8), 0xFF)
        Local $bBaseB = BitAnd($iHexColor, 0xFF)

        ; Create DIB Section
        Local $tBMI = DllStructCreate("dword biSize;long biWidth;long biHeight;ushort biPlanes;ushort biBitCount;dword biCompression;dword biSizeImage;long biXPelsPerMeter;long biYPelsPerMeter;dword biClrUsed;dword biClrImportant")
        DllStructSetData($tBMI, "biSize", 40)
        DllStructSetData($tBMI, "biWidth", $iIconSize)
        DllStructSetData($tBMI, "biHeight", -$iIconSize)
        DllStructSetData($tBMI, "biPlanes", 1)
        DllStructSetData($tBMI, "biBitCount", 32)

        Local $aDIB = DllCall("gdi32.dll", "handle", "CreateDIBSection", "handle", $hMemDC, "struct*", $tBMI, "uint", 0, "ptr*", 0, "handle", 0, "dword", 0)
        Local $hBitmap = $aDIB[0]
        Local $pBits = $aDIB[4]

        If $pBits Then
            For $y = 0 To $iBoxY - 1
                Local $targetY = $y + $iOffsetY
                If $targetY < 0 Or $targetY >= $iIconSize Then ContinueLoop

                For $x = 0 To $iBoxX - 1
                    Local $targetX = $x + $iOffsetX
                    If $targetX < 0 Or $targetX >= $iIconSize Then ContinueLoop

                    Local $iSrcIndex = ($y * $iStride) + $x
                    Local $iGrayVal = DllStructGetData($tBuffer, 1, $iSrcIndex + 1)

                    If $iGrayVal > 0 Then
                        Local $iAlpha = Int(($iGrayVal * 255) / 64)

                        ; Improve quality for dark mode (light text on dark background)
                        If $bDarkMode Then
                            ; Apply a power curve (gamma adjustment) to lift shadows/edges
                            Local $fNormalized = $iAlpha / 255.0
                            If $iFontSize <= 20 Then
                                $iAlpha = Int((($fNormalized ^ 0.5) * 255.0))
                            Else
                                $iAlpha = Int((($fNormalized ^ 0.8) * 255.0))
                            EndIf
                            If $iAlpha > 255 Then $iAlpha = 255
                        EndIf

                        Local $bB = Int(($bBaseB * $iAlpha) / 255)
                        Local $bG = Int(($bBaseG * $iAlpha) / 255)
                        Local $bR = Int(($bBaseR * $iAlpha) / 255)

                        Local $pPixel = $pBits + (($targetY * $iIconSize + $targetX) * 4)
                        DllStructSetData(DllStructCreate("byte b;byte g;byte r;byte a", $pPixel), "b", $bB)
                        DllStructSetData(DllStructCreate("byte b;byte g;byte r;byte a", $pPixel), "g", $bG)
                        DllStructSetData(DllStructCreate("byte b;byte g;byte r;byte a", $pPixel), "r", $bR)
                        DllStructSetData(DllStructCreate("byte b;byte g;byte r;byte a", $pPixel), "a", $iAlpha)
                    EndIf
                Next
            Next
        EndIf

        Local $iIndex = _GUIImageList_Add($hImageList, $hBitmap)
        _WinAPI_DeleteObject($hBitmap)
    EndIf

    _WinAPI_SelectObject($hMemDC, $hOldFont)
    _WinAPI_ReleaseDC(0, $hScreenDC)
    _WinAPI_DeleteObject($hFont)
    _WinAPI_DeleteDC($hMemDC)

    Return $iIndex
EndFunc   ;==>_AddFontIconToImageList

 

Edited by WildByDesign
Removed *memset call, improved gamma for dark theme
Posted (edited)
23 minutes ago, Nine said:

Tested your latest snippet and it crashes on me (!>07:01:20 AutoIt3 ended. rc:-1073741819).  x86 though...

Thanks for the heads up on the crash. I'll try to switch it to use AutoIt's standard UDF function for _WinAPI_GetGlyphOutline which is likely better designed to handle x86 and x64. Although it may be more to do with the "memset" memory buffer stuff, more likely now that I think about it. My knowledge in that area is next to zero. Sorry about the crash.

I do have to leave soon for most of the day, so I will have to look into the x86 crash later in the day. If anyone is able to pinpoint the cause of the crash and/or potentially have a fix, please let me know. Thanks. :)

EDIT:

I was also able to reproduce the crash after setting #AutoIt3Wrapper_UseX64=N

Edited by WildByDesign
Posted

@Nine I updated the example above that was crashing for you on x86. AI was not able to help me fix the crash. So I did my own research and the fix was simply to change "memset" to "wmemset" and it seems to have resolved the x86 crash on my end. Thanks again for pointing out that crash.

Posted (edited)

Works great now.  Good work....But :

@WildByDesign I was curious as to why it would crash on x86.  And guess what I have found : there is no memset nor wmemset anymore in msvcrt.dll.

All those mem* functions have been reworked in C to be inline code instead of function call (which is much faster).
So I don't know where you got the code (some AI ?), but the memset line is useless. 
If you catch the @error after the dllcall you can see it returns 3 ("function" not found in the DLL file) when used with wmemset.

However (this is where I was wrong), memset does exist in msvcrt.dll.  It is declared as C function (the real reason of the crash). 
So the correct way to call it is :

DllCall("msvcrt.dll", "ptr:cdecl", "memset", "ptr", $pbuffer, "int", $iVal, "int", $nCount)

 

Edited by Nine
Posted
15 hours ago, Nine said:

All those mem* functions have been reworked in C to be inline code instead of function call (which is much faster).
So I don't know where you got the code (some AI ?), but the memset line is useless. 
If you catch the @error after the dllcall you can see it returns 3 ("function" not found in the DLL file).

You are absolutely right, the memset call does not seem to be necessary (or functional) at all and even after removing the line everything seems to work great. Thank you for following up on this, I appreciate it very much. And yes, the memset and GetGlyphOutlineW code came from AI.

I updated the function in the same post above to remove the memset line. Also, I noticed that while light mode (dark text on light background) was pixel-perfect even at smaller font sizes, dark mode (light text on dark background) on the other hand had some quality issues at smaller sizes. So I added a flag to pass if dark mode is used which then allows the function to do some additional gamma corrections that improves the edges of the glyphs significantly.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...