Jump to content

Recommended Posts

Posted (edited)

_GUIImageList_AddGlyph

a variation of  _AddFontIconToImageList   (Thanks to the @WildByDesign )

 Creates a GDI+ rendered glyph from a font and adds it to an ImageList with supersampling and rotation support.

I added some extras to try. 😍
 

; https://www.autoitscript.com/forum/topic/213857-_guiimagelist_addglyph/
;----------------------------------------------------------------------------------------
; Title...........: _GUIImageList_AddGlyph.au3
; Description.....: Creates a GDI+ rendered glyph from a font and adds it to an ImageList with supersampling and rotation support.
; AutoIt Version..: 3.3.18.0   Mod: ioa747           Script Version: 0.3
; Note............: Testet in Windows 11 Pro 25H2       Date:28/8/2026
; Modification ...: https://www.autoitscript.com/forum/topic/139260-autoit-snippets/page/29/#findComment-1554342
;----------------------------------------------------------------------------------------
;~ #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, 700)
    GUISetBkColor(0x000000)

    Local $iCSZ = 48
    Local $hImageList = _GUIImageList_Create($iCSZ, $iCSZ, 5, 1)

    Local $aDataList[16][3] = [ _ ; id, sText, $iImageIndex
            [0, "Settings|Modify application settings", _GUIImageList_AddGlyph($hImageList, "Segoe UI Emoji", 0x26A0, $iCSZ, 0xFF41B310)], _  ; "Segoe UI Emoji", .Glyph = 0x26A0 ⚠
            [0, "Home|Emoji with GlyphScale=65", _GUIImageList_AddGlyph($hImageList, "Segoe UI Emoji", 0x1F4BE, $iCSZ, 0xFF41B310, 0, 65)], _ ; "Segoe UI Emoji", .Glyph = 0x1F4BE 💾
            [0, "Save|Save current project changes", _GUIImageList_AddGlyph($hImageList, "Segoe MDL2 Assets", 0xE74E, $iCSZ, 0xFF0094FF)], _  ; "Segoe MDL2 Assets", .Glyph = 0xE74E ; Save
            [0, "1|A 90-degree rotation without flipping", _GUIImageList_AddGlyph($hImageList, "Segoe MDL2 Assets", 0xE74E, $iCSZ, 0xFF69BDF9, 1)], _
            [0, "2|A 180-degree rotation without flipping", _GUIImageList_AddGlyph($hImageList, "Segoe MDL2 Assets", 0xE74E, $iCSZ, 0xFF69BDF9, 2)], _
            [0, "3|A 270-degree rotation without flipping", _GUIImageList_AddGlyph($hImageList, "Segoe MDL2 Assets", 0xE74E, $iCSZ, 0xFF69BDF9, 3)], _
            [0, "4|No rotation and a horizontal flip", _GUIImageList_AddGlyph($hImageList, "Segoe MDL2 Assets", 0xE74E, $iCSZ, 0xFF69BDF9, 4)], _
            [0, "5|A 90-degree rotation followed by a horizontal flip", _GUIImageList_AddGlyph($hImageList, "Segoe MDL2 Assets", 0xE74E, $iCSZ, 0xFF69BDF9, 5)], _
            [0, "6|A 180-degree rotation followed by a horizontal flip", _GUIImageList_AddGlyph($hImageList, "Segoe MDL2 Assets", 0xE74E, $iCSZ, 0xFF69BDF9, 6)], _
            [0, "7|A 270-degree rotation followed by a horizontal flip", _GUIImageList_AddGlyph($hImageList, "Segoe MDL2 Assets", 0xE74E, $iCSZ, 0xFF69BDF9, 7)], _
            [0, "50|GlyphScale", _GUIImageList_AddGlyph($hImageList, "Segoe MDL2 Assets", 0xE74E, $iCSZ, 0xFFFF6A00, 0, 50)], _
            [0, "60|GlyphScale", _GUIImageList_AddGlyph($hImageList, "Segoe MDL2 Assets", 0xE74E, $iCSZ, 0xFFFF6A00, 0, 60)], _
            [0, "70|GlyphScale", _GUIImageList_AddGlyph($hImageList, "Segoe MDL2 Assets", 0xE74E, $iCSZ, 0xFFFF6A00, 0, 70)], _
            [0, "80|GlyphScale (Default)", _GUIImageList_AddGlyph($hImageList, "Segoe MDL2 Assets", 0xE74E, $iCSZ, 0xFFFF6A00, 0, 80)], _
            [0, "90|GlyphScale", _GUIImageList_AddGlyph($hImageList, "Segoe MDL2 Assets", 0xE74E, $iCSZ, 0xFFFF6A00, 0, 90)], _
            [0, "100|GlyphScale", _GUIImageList_AddGlyph($hImageList, "Segoe MDL2 Assets", 0xE74E, $iCSZ, 0xFFFF6A00, 0, 100)] _
            ]

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

    ; ImageList -> ListView
    _GUICtrlListView_SetImageList($idListView, $hImageList, 1) ; 1 = Small icon list

    Local $hLV = GUICtrlGetHandle($idListView)
    For $i = 0 To UBound($aDataList) - 1
        $aDataList[$i][0] = GUICtrlCreateListViewItem($aDataList[$i][1], $idListView)
        _GUICtrlListView_SetItemImage($hLV, $i, $aDataList[$i][2])
    Next

    ; Information: Get item rectangle
    Local $aRect = _GUICtrlListView_GetItemRect($idListview, 0)
    ConsoleWrite(StringFormat("Item Rectangle : [%d, %d, %d, %d]", $aRect[0], $aRect[1], $aRect[2], $aRect[3]) & @CRLF)


    GUISetState(@SW_SHOW)

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

    ; Cleanup
    _GUIImageList_Destroy($hImageList)
    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, $iUnicodeDecimalOrHex[, $iSize = 32[, $iARGBColor = 0xFF000000[, $iRotateFlipType = 0]]])
; Parameters ....: $hImageList             - Handle to the ImageList control.
;                  $sFontName              - Name of the font family (e.g., "Segoe MDL2 Assets", "Segoe UI Emoji").
;                  $iUnicodeDecimalOrHex   - Unicode code point (Decimal or Hex integer, e.g., 0xE74E or 0x1F60A).
;                  $iSize                  - [optional] Target icon size in pixels (Width & Height). Default is 32.
;                  $iARGBColor             - [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, $iUnicodeDecimalOrHex, $iSize = 32, $iARGBColor = 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($iARGBColor)
    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 $iUnicodeDecimalOrHex > 0xFFFF Then
        Local $iCode = $iUnicodeDecimalOrHex - 0x10000
        Local $iHigh = BitOR(0xD800, BitShift($iCode, 10))
        Local $iLow = BitOR(0xDC00, BitAND($iCode, 0x3FF))
        $sChar = ChrW($iHigh) & ChrW($iLow)
    Else
        $sChar = ChrW($iUnicodeDecimalOrHex)
    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

 

Please, every comment is appreciated!
leave your comments and experiences here!
Thank you very much  :)

 

Edited by ioa747
update to v0.3

I know that I know nothing

Posted

It looks like I used the wrong measurement for _GDIPlus_FontCreate in my original script.

Changing this:

Local $hFont = _GDIPlus_FontCreate($hFamily, $iRenderSize * 0.48, 0, 3)    ; 3 - A unit is 1 point or 1/72 inch

To this:

Local $hFont = _GDIPlus_FontCreate($hFamily, $iRenderSize * 0.90, 0, 2)     ; 2 - A unit is 1 pixel

This seems to be more appropriate across various DPI factors.

Posted (edited)

I added an optional  $iGlyphScale  parameter (1–100, default: 80)  to the function signature.

This allows fine-tuning the glyph size directly from the call,
which is useful not only for handling different font families,
but also for adapting to various UI themes and layouts where a custom padding or margin is required.

 

I updated the first post  with the new version.

Thank you once again   :)
for the continuous feedback and support!
 

Edited by ioa747
to GlyphScale

I know that I know nothing

Posted
5 hours ago, ioa747 said:

I added an optional  $iGlyphScale  parameter (1–100, default: 80)  to the function signature.

I really like what you've done with this function. Adding GlyphScale is an excellent idea because of how different fonts have different padding and characteristics. Your function is fantastic and is quite powerful for giving users a modern experience. This can now be used quite easily for making changes to ImageLists during WM_DPICHANGED messages to always have sharp icons/images.

The technique reminds me quite a lot of SVG since these modern fonts are also designed to scale quite well.

It opens up the possibility to have custom, modern icons for toolbar buttons, tabs and so much more. It can also be used to make nice checkboxes that some of these fonts have. The possibilities are almost endless. :)

Excellent work, as always.

Posted (edited)

following the same philosophy
to approach _GUICtrlSetGlyph
(Renders a GDI+ font glyph and assigns it directly to a GUI control)

 

because the function has all the extras flip/ rotate/ color/ size 

#include <GUIConstants.au3>
#include <GDIPlus.au3>
#include <WinAPISysWin.au3>

_Example()

Func _Example()
    _GDIPlus_Startup()

    Local $hGUI = GUICreate("GUICtrlSetGlyph Example", 400, 200)
    GUISetBkColor(0x002844)


    ; Button Control
    Local $idBtn1 = GUICtrlCreateButton(ChrW(0xE74E), 30, 40, 64, 64)
    GUICtrlSetFont ( -1, 40, 0, 0, "Segoe MDL2 Assets") ; usual way to set directly to the Button
;~  _GUICtrlSetGlyph($idBtn1, "Segoe MDL2 Assets", 0xE74E, 64, 0xFF0094FF)

    Local $idBtn2 = GUICtrlCreateButton("Save Icon", 120, 40, 64, 64)
    _GUICtrlSetGlyph($idBtn2, "Segoe MDL2 Assets", 0xE74E, 64, 0xFFFF00DC)

    Local $idBtn3 = GUICtrlCreateButton("", 210, 40, 64, 64)
    _GUICtrlSetGlyph($idBtn3, "Segoe MDL2 Assets", 0xE74E, 64, 0xFFFF6A00, 2) ; 180° Rotated



    Local $idPic1 = GUICtrlCreatePic("", 30, 120, 64, 64) ; Pic Control ??
    _GUICtrlSetGlyph($idPic1,  "Segoe MDL2 Assets", 0xE74E, 64, 0xFF41B310)

    Local $idIco1 = GUICtrlCreateIcon("shell32.dll", 7, 120, 120, 64, 64)
    _GUICtrlSetGlyph($idIco1,  "Segoe MDL2 Assets", 0xE74E, 64, 0xFFFF00DC)

    Local $idIco2 = GUICtrlCreateIcon("shell32.dll", 7, 210, 120, 64, 64)
    _GUICtrlSetGlyph($idIco2,  "Segoe MDL2 Assets", 0xE74E, 64, 0xFFFF6A00, 2) ; 180° Rotated


    GUISetState(@SW_SHOW)

    While GUIGetMsg() <> -3
    WEnd

    _GDIPlus_Shutdown()
EndFunc   ;==>_Example


; #FUNCTION# ====================================================================================================================
; Name...........: _GUICtrlSetGlyph
; Description ...: Renders a GDI+ font glyph and assigns it directly to a GUI control (Button, Pic, Icon).
; Syntax.........: _GUICtrlSetGlyph($idControl, $sFontName, $iUnicodeDecimalOrHex[, $iSize = 32[, $iARGBColor = 0xFF000000[, $iRotateFlipType = 0[, $iGlyphScale = 80]]]])
; Parameters ....: $idControl            - Control ID or HWND of the target control.
;                  $sFontName            - Name of the font family (e.g., "Segoe MDL2 Assets", "Segoe UI Emoji").
;                  $iUnicodeDecimalOrHex - Unicode code point (Decimal or Hex integer, e.g., 0xE74E or 0x1F60A).
;                  $iSize                - [optional] Icon size in pixels (Width & Height). Default is 32.
;                  $iARGBColor           - [optional] Color of the glyph in ARGB format (0xAARRGGBB). Default is 0xFF000000.
;                  $iRotateFlipType      - [optional] GDI+ RotateFlipType enumeration (0-7). Default is 0 ($GDIP_ROTATENONEFLIPNONE).
;                  $iGlyphScale          - [optional] Font scale percentage relative to canvas size (1-100). Default is 80.
; Return values .: Success - Returns the handle to the previously assigned HICON (or 0 if none).
;                  Failure - Returns 0 and sets @error.
; Author ........: ioa747
; Remarks .......: Requires _GDIPlus_Startup() to be called beforehand in your script.
; ===============================================================================================================================
Func _GUICtrlSetGlyph($idControl, $sFontName, $iUnicodeDecimalOrHex, $iSize = 32, $iARGBColor = 0xFF000000, $iRotateFlipType = 0, $iGlyphScale = 80)
    Local $hCtrl = GUICtrlGetHandle($idControl)
    If Not $hCtrl Then $hCtrl = $idControl

    Local $sClassName = _WinAPI_GetClassName($hCtrl)
    ConsoleWrite("$sClassName=" & $sClassName & @CRLF)
    Local $iMsg = 0
    Local $iStyle = _WinAPI_GetWindowLong($hCtrl, -16) ; GWL_STYLE

    Switch $sClassName
        Case "Button"
            $iMsg = $BM_SETIMAGE
            If BitAND($iStyle, 0x0040) = 0 Then
                _WinAPI_SetWindowLong($hCtrl, -16, BitOR($iStyle, 0x0040)) ; BS_ICON
            EndIf
        Case "Static"
            $iMsg = $STM_SETIMAGE
            ; Remove SS_BITMAP (0x0E) and apply SS_ICON (0x03) + SS_REALSIZECONTROL (0x40)
            $iStyle = BitAND($iStyle, BitNOT(0x0E))
            _WinAPI_SetWindowLong($hCtrl, -16, BitOR($iStyle, 0x0043))
        Case Else
            Return SetError(1, 0, 0)
    EndSwitch

    ; Supersampling Canvas Setup (2x Size for crisp AA)
    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($iARGBColor)
    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
    Local $sChar = ""
    If $iUnicodeDecimalOrHex > 0xFFFF Then
        Local $iCode = $iUnicodeDecimalOrHex - 0x10000
        Local $iHigh = BitOR(0xD800, BitShift($iCode, 10))
        Local $iLow = BitOR(0xDC00, BitAND($iCode, 0x3FF))
        $sChar = ChrW($iHigh) & ChrW($iLow)
    Else
        $sChar = ChrW($iUnicodeDecimalOrHex)
    EndIf

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

    ; Resample to Target Size
    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)

    ; Apply Rotation / Flip if needed
    If $iRotateFlipType Then _GDIPlus_ImageRotateFlip($hFinalBitmap, $iRotateFlipType)

    ; Convert to Win32 HICON (Preserves full Alpha Transparency)
    Local $hIcon = _GDIPlus_HICONCreateFromBitmap($hFinalBitmap)

    ; Send HICON message to Control ($IMAGE_ICON = 1)
    Local $hPrevIcon = _SendMessage($hCtrl, $iMsg, 1, $hIcon)

    ; Destroy previous icon if any existed to prevent memory leaks
    If $hPrevIcon Then _WinAPI_DestroyIcon($hPrevIcon)

    ; Cleanup GDI+ Resources
    _GDIPlus_StringFormatDispose($hFormat)
    _GDIPlus_FontDispose($hFont)
    _GDIPlus_FontFamilyDispose($hFamily)
    _GDIPlus_BrushDispose($hBrush)
    _GDIPlus_GraphicsDispose($hFinalGraphics)
    _GDIPlus_BitmapDispose($hFinalBitmap)
    _GDIPlus_GraphicsDispose($hGraphics)
    _GDIPlus_BitmapDispose($hBitmap)

    _WinAPI_InvalidateRect($hCtrl)

    Return $hIcon
EndFunc   ;==>_GUICtrlSetGlyph

 

Thank you once again   :)
for the continuous feedback and support!
 

Edited by ioa747

I know that I know nothing

Posted (edited)

I've spent some time trying to learn and understand how to get sharper fonts with GDI+ because apparently it can be difficult to line up perfectly on the pixel grid. And when we use fonts such as Segoe Fluent Icons or Segoe MDL2 Assets which have single pixel lines in the majority of the glyphs, you can end up with a lot of sub-pixel blur.

The following puts us on the pixel grid:

_GDIPlus_GraphicsSetTextRenderingHint($hGraphics, $GDIP_TEXTRENDERINGHINTCLEARTYPEGRIDFIT)

But from what I've read, the following can potentially take us back off the pixel grid and end up sub-pixel:

_GDIPlus_StringFormatSetAlign($hFormat, 1)
_GDIPlus_StringFormatSetLineAlign($hFormat, 1)

Also, _GDIPlus_StringFormatCreate() can take us off the pixel grid because it adds quite a bit of horizontal padding in GDI+ which causes problems with text measurements.

Instead, as a potential idea:

Local $hFormat = _GDIPlus_StringFormatCreateTypographic()
; #FUNCTION# ====================================================================================================================
; Name ..........: _GDIPlus_StringFormatCreateTypographic
; Description ...: Helper function to determine if control is native AutoIt control or UDF-created control.
; Author.........: UEZ
; Link...........: https://www.autoitscript.com/forum/topic/159683-hard-death-of-autoit-x64-with-gdiplus-call/#findComment-1158866
; Remarks........: A generic, typographic StringFormat object has the following characteristics:
;
;       - String format flags StringFormatFlagsLineLimit, StringFormatFlagsNoClip, and StringFormatFlagsNoFitBlackBox are set.
;       - Character alignment and line alignment are set to StringAlignmentNear.
;       - Language ID is set to neutral language, which means that the current language associated with the calling thread is used.
;       - String digit substitution is set to StringDigitSubstituteUser.
;       - Hot key prefix is set to HotkeyPrefixNone.
;       - Number of tab stops is set to zero.
;       - String trimming is set to StringTrimmingNone.
; ===============================================================================================================================
Func _GDIPlus_StringFormatCreateTypographic()
    Local $aResult = DllCall($__g_hGDIPDll, "uint", "GdipStringFormatGetGenericTypographic", "ptr*", 0)
    If @error Then Return SetError(@error, @extended, 0)
    Return $aResult[1]
EndFunc   ;==>_GDIPlus_StringFormatCreateTypographic

The only downside is that we would not get the automatic vertical and horizontal centering. We would have to use _GDIPlus_GraphicsMeasureString() with the included _GDIPlus_StringFormatCreateTypographic() as the hFormat to measure the string (now without extra GDI+ padding) and do the math to determine how many pixels to move the character along the pixel grid to handle the vertical and horizontal centering.

Anyway, it's just an idea to slightly improve sharpness but it would involve some extra work/math in the function to center the glyph. I've tested it in a few of my functions an it works quite well.

EDIT: I am still achieving much sharper glyphs (at smaller font sizes) using pure GDI with ExtTextOut, but I am having significant issues getting a transparent background which is needed for ImageLists. Solid background is sharp and great due to cleartype blending, but transparent ends up jagged.

Edited by WildByDesign
Posted

@WildByDesign  Excellent approach!

I adopted your idea about using typographic measurement to eliminate GDI+ padding and implemented it into a clean, reusable function: _GUIImageList_AddGlyph()

; https://www.autoitscript.com/forum/topic/213857-_guiimagelist_addglyph/
;----------------------------------------------------------------------------------------
; Title...........: _GUIImageList_AddGlyph.au3
; Description.....: Creates a GDI+ rendered glyph from a font and adds it to an ImageList with supersampling and rotation support.
; AutoIt Version..: 3.3.18.0   Mod: ioa747           Script Version: 0.5
; Note............: Testet in Windows 11 Pro 25H2       Date:28/8/2026
; Modification ...: https://www.autoitscript.com/forum/topic/139260-autoit-snippets/page/29/#findComment-1554342
;----------------------------------------------------------------------------------------
#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>

; Set DPI awareness to System Aware
DllCall("User32.dll", "bool", "SetProcessDpiAwarenessContext", "int_ptr", -2)
; Initialize GDI+
_GDIPlus_Startup()

_Example()

; Cleanup
_GDIPlus_Shutdown()

Func _Example()

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

    Local $iCSZ = 48
    Local $hImageList = _GUIImageList_Create($iCSZ, $iCSZ, 5, 1)

    Local $aDataList[16][3] = [ _ ; id, sText, $iImageIndex
            [0, "Settings|Emoji GlyphScale=80", _GUIImageList_AddGlyph($hImageList, "Segoe UI Emoji", "⚠", $iCSZ, 0xFF41B310)], _
            [0, "Home|Emoji with GlyphScale=50", _GUIImageList_AddGlyph($hImageList, "Segoe UI Emoji", "💾", $iCSZ, 0xFF41B310, 0, 50)], _
            [0, "Save|Save current project changes", _GUIImageList_AddGlyph($hImageList, "Segoe MDL2 Assets", 0xE74E, $iCSZ, 0xFF0094FF)], _
            [0, "1|A 90-degree rotation without flipping", _GUIImageList_AddGlyph($hImageList, "Segoe MDL2 Assets", 0xE74E, $iCSZ, 0xFF69BDF9, 1)], _
            [0, "2|A 180-degree rotation without flipping", _GUIImageList_AddGlyph($hImageList, "Segoe MDL2 Assets", 0xE74E, $iCSZ, 0xFF69BDF9, 2)], _
            [0, "3|A 270-degree rotation without flipping", _GUIImageList_AddGlyph($hImageList, "Segoe MDL2 Assets", 0xE74E, $iCSZ, 0xFF69BDF9, 3)], _
            [0, "4|No rotation and a horizontal flip", _GUIImageList_AddGlyph($hImageList, "Segoe MDL2 Assets", 0xE74E, $iCSZ, 0xFF69BDF9, 4)], _
            [0, "5|A 90-degree rotation followed by a horizontal flip", _GUIImageList_AddGlyph($hImageList, "Segoe MDL2 Assets", 0xE74E, $iCSZ, 0xFF69BDF9, 5)], _
            [0, "6|A 180-degree rotation followed by a horizontal flip", _GUIImageList_AddGlyph($hImageList, "Segoe MDL2 Assets", 0xE74E, $iCSZ, 0xFF69BDF9, 6)], _
            [0, "7|A 270-degree rotation followed by a horizontal flip", _GUIImageList_AddGlyph($hImageList, "Segoe MDL2 Assets", 0xE74E, $iCSZ, 0xFF69BDF9, 7)], _
            [0, "50|GlyphScale", _GUIImageList_AddGlyph($hImageList, "Segoe MDL2 Assets", 0xE74E, $iCSZ, 0xFFFF6A00, 0, 50)], _
            [0, "60|GlyphScale", _GUIImageList_AddGlyph($hImageList, "Segoe MDL2 Assets", 0xE74E, $iCSZ, 0xFFFF6A00, 0, 60)], _
            [0, "70|GlyphScale", _GUIImageList_AddGlyph($hImageList, "Segoe MDL2 Assets", 0xE74E, $iCSZ, 0xFFFF6A00, 0, 70)], _
            [0, "80|GlyphScale (Default)", _GUIImageList_AddGlyph($hImageList, "Segoe MDL2 Assets", 0xE74E, $iCSZ, 0xFFFF6A00, 0, 80)], _
            [0, "90|GlyphScale", _GUIImageList_AddGlyph($hImageList, "Segoe MDL2 Assets", 0xE74E, $iCSZ, 0xFFFF6A00, 0, 90)], _
            [0, "100|GlyphScale", _GUIImageList_AddGlyph($hImageList, "Segoe MDL2 Assets", 0xE74E, $iCSZ, 0xFFFF6A00, 0, 100)] _
            ]

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

    ; Bind ImageList to ListView (1 = Small icon list)
    _GUICtrlListView_SetImageList($idListView, $hImageList, 1)

    Local $hLV = GUICtrlGetHandle($idListView)
    For $i = 0 To UBound($aDataList) - 1
        $aDataList[$i][0] = GUICtrlCreateListViewItem($aDataList[$i][1], $idListView)
        _GUICtrlListView_SetItemImage($hLV, $i, $aDataList[$i][2])
    Next

    ; Information: Get item rectangle
    Local $aRect = _GUICtrlListView_GetItemRect($idListView, 0)
    ConsoleWrite(StringFormat("Item Rectangle : [%d, %d, %d, %d]", $aRect[0], $aRect[1], $aRect[2], $aRect[3]) & @CRLF)

    GUISetState(@SW_SHOW)

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

    ; Cleanup
    GUIDelete($hGUI)
    _GUIImageList_Destroy($hImageList)
EndFunc   ;==>_Example

; #FUNCTION# ====================================================================================================================
; Name...........: _GUIImageList_AddGlyph
; Description ...: Creates a GDI+ rendered glyph from a font using typographic measurement and adds it directly to an ImageList.
; Syntax.........: _GUIImageList_AddGlyph($hImageList, $sFontName, $vUnicodeOrChar[, $iSize = 32[, $iARGBColor = 0xFF000000[, $iRotateFlipType = 0[, $iGlyphScale = 80]]]])
; Parameters ....: $hImageList     - Handle to the ImageList control.
;                  $sFontName      - Name of the font family (e.g., "Segoe MDL2 Assets", "Segoe UI Emoji").
;                  $vUnicodeOrChar - Unicode CodePoint Hex/Dec (e.g. 0xE74E) OR literal String for Emojis (e.g. "⚠️").
;                  $iSize          - [optional] Target icon size in pixels. Default is 32.
;                  $iARGBColor     - [optional] Color of the glyph in ARGB format. Default is 0xFF000000.
;                  $iRotateFlipType- [optional] GDI+ RotateFlipType enumeration (0-7). Default is 0.
;                  $iGlyphScale    - [optional] Font scale percentage (1-100). Default is 80.
; Return values .: Success - The 0-based index of the added image in the ImageList.
;                  Failure - Returns -1 and sets @error.
; Author ........: ioa747
; ===============================================================================================================================
Func _GUIImageList_AddGlyph($hImageList, $sFontName, $vUnicodeOrChar, $iSize = 32, $iARGBColor = 0xFF000000, $iRotateFlipType = 0, $iGlyphScale = 80)
    Local $sChar = "", $hFormat, $bIsEmoji = False

    ; Static handle for Typographic format (created only once)
    Static $hStaticTypographic = 0

    ; Calculate scaled font size based on percentage
    Local $fScaledSize = $iSize * ($iGlyphScale / 100)

    ; 1. Check if String -> Standard Format & Full Canvas Layout (Emoji mode)
    If IsString($vUnicodeOrChar) Then
        $sChar = $vUnicodeOrChar
        $bIsEmoji = True
        $hFormat = _GDIPlus_StringFormatCreate()
        _GDIPlus_StringFormatSetAlign($hFormat, 1)     ; Center alignment
        _GDIPlus_StringFormatSetLineAlign($hFormat, 1) ; Center line alignment

        ; 2. Check if Numeric -> Typographic Format & Custom tRect (Icon Font mode)
    Else
        If $vUnicodeOrChar > 0xFFFF Then
            Local $iCode = $vUnicodeOrChar - 0x10000
            Local $iHigh = BitOR(0xD800, BitShift($iCode, 10))
            Local $iLow = BitOR(0xDC00, BitAND($iCode, 0x3FF))
            $sChar = ChrW($iHigh) & ChrW($iLow)
        Else
            $sChar = ChrW($vUnicodeOrChar)
        EndIf

        ; Acquire Generic Typographic format handle if not already cached
        If Not IsPtr($hStaticTypographic) Then
            Local $aResult = DllCall($__g_hGDIPDll, "uint", "GdipStringFormatGetGenericTypographic", "ptr*", 0)
            If Not @error And $aResult[1] Then $hStaticTypographic = $aResult[1]
        EndIf
        $hFormat = $hStaticTypographic
    EndIf

    ; Create 1:1 Target Bitmap canvas
    Local $hBitmap = _GDIPlus_BitmapCreateFromScan0($iSize, $iSize)
    Local $hGraphics = _GDIPlus_ImageGetGraphicsContext($hBitmap)

    _GDIPlus_GraphicsSetTextRenderingHint($hGraphics, $GDIP_TEXTRENDERINGHINTANTIALIASGRIDFIT)

    Local $hBrush = _GDIPlus_BrushCreateSolid($iARGBColor)
    Local $hFamily = _GDIPlus_FontFamilyCreate($sFontName)
    Local $hFont = _GDIPlus_FontCreate($hFamily, $fScaledSize, 0, 2)

    Local $tLayout = _GDIPlus_RectFCreate(0, 0, $iSize, $iSize)

    ; Render Glyph
    If $bIsEmoji Then
        ; Emojis: Draw directly inside $tLayout
        _GDIPlus_GraphicsDrawStringEx($hGraphics, $sChar, $hFont, $tLayout, $hFormat, $hBrush)
    Else
        ; Icon Fonts: Measure bounds and snap to exact integer pixel grid
        Local $aBounds = _GDIPlus_GraphicsMeasureString($hGraphics, $sChar, $hFont, $tLayout, $hFormat)
        Local $fDrawWidth = DllStructGetData($aBounds[0], "Width")
        Local $fDrawHeight = DllStructGetData($aBounds[0], "Height")

        Local $iX = Floor(($iSize - $fDrawWidth) / 2)
        Local $iY = Floor(($iSize - $fDrawHeight) / 2)
        Local $tRect = _GDIPlus_RectFCreate($iX, $iY, $fDrawWidth, $fDrawHeight)

        _GDIPlus_GraphicsDrawStringEx($hGraphics, $sChar, $hFont, $tRect, $hFormat, $hBrush)
    EndIf

    If $iRotateFlipType Then _GDIPlus_ImageRotateFlip($hBitmap, $iRotateFlipType)

    Local $hHBITMAP = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hBitmap)
    Local $aRet = DllCall("comctl32.dll", "int", "ImageList_Add", "ptr", $hImageList, "ptr", $hHBITMAP, "ptr", 0)
    Local $iIndex = (IsArray($aRet) And Not @error) ? $aRet[0] : -1

    ; Cleanup Win32 & GDI+ Handles
    _WinAPI_DeleteObject($hHBITMAP)

    ; Dispose ONLY if it was a dynamically allocated StringFormat (Emoji mode)
    If $bIsEmoji And $hFormat Then _GDIPlus_StringFormatDispose($hFormat)

    _GDIPlus_FontDispose($hFont)
    _GDIPlus_FontFamilyDispose($hFamily)
    _GDIPlus_BrushDispose($hBrush)
    _GDIPlus_GraphicsDispose($hGraphics)
    _GDIPlus_BitmapDispose($hBitmap)

    Return $iIndex
EndFunc   ;==>_GUIImageList_AddGlyph

 

Please, every comment is appreciated!
leave your comments and experiences here!
Thank you very much  :)

 

I know that I know nothing

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...