flat assembler
Message board for the users of flat assembler.

Index > Windows > Createfont save to png.

Author
Thread Post new topic Reply to topic
Roman



Joined: 21 Apr 2012
Posts: 2039
Roman 19 Dec 2025, 05:05
Windows 10
I want using Createfont for generated symbols and save each symbol to separate files.

Msdn write Createfont return font handle.
How using this handle and get symbol what I needed?
Post 19 Dec 2025, 05:05
View user's profile Send private message Reply with quote
Ali.Z



Joined: 08 Jan 2018
Posts: 844
Ali.Z 19 Dec 2025, 05:57
render to memory device context, get the pixels, save as bmp, finally use image editor to convert bmp to png.

_________________
Asm For Wise Humans
Post 19 Dec 2025, 05:57
View user's profile Send private message Reply with quote
bitRAKE



Joined: 21 Jul 2003
Posts: 4333
Location: vpcmpistri
bitRAKE 19 Dec 2025, 07:36
Since GDI+ is the easiest way to save a PNG, we also use it for font rendering:
Code:
; fasm2 fontgrid.asm

format PE GUI 4.0
entry start

include 'win32a.inc'

; Structs for GDI+
struct GdiplusStartupInput
    GdiplusVersion           dd ?
    DebugEventCallback       dd ?
    SuppressBackgroundThread dd ?
    SuppressExternalCodecs   dd ?
ends

; =============================================================
; Data Section: Configuration and Variables
; =============================================================
section '.data' data readable writeable

    ; ---------------------------------------------------------
    ; USER CONFIGURATION BLOCK
    ; ---------------------------------------------------------
    config_data:
        dd 48, 64       ; [0] Width, Height of each cell (pixels)
        dd 3, 4         ; [8] Grid Width, Height (characters)
        
        ; Rows of characters (null-terminated strings)
        db '123',0
        db '456',0
        db '789',0
        db ' 0 ',0
        db 0            ; Terminator for safety

    ; ---------------------------------------------------------
    ; Internal Variables
    ; ---------------------------------------------------------
    _filename       du 'output.png',0
    _font_family    du 'Consolas',0 ; Unicode for GDI+
    _mime_png       du 'image/png',0

    token           dd ?
    gdiplusInput    GdiplusStartupInput
    encoderClsid    rd 4 ; GUID

    pBitmap         dd ?
    pGraphics       dd ?
    pFamily         dd ?
    pFont           dd ?
    pBrushText      dd ?
    pBrushBg        dd ?

    ; Rendering vars
    cell_w          dd ?
    cell_h          dd ?
    cols            dd ?
    rows            dd ?
    curr_x          dd ?
    curr_y          dd ?
    
    ; RectF structure for drawing (X, Y, Width, Height)
    draw_rect       dd 0.0, 0.0, 0.0, 0.0

; =============================================================
; Code Section
; =============================================================
section '.text' code readable executable

; =============================================================
; Helper: Get Encoder CLSID for MimeType
; =============================================================
proc GetEncoderClsid uses ebx esi edi, _format, clsid
    locals
        numEncoders dd ?
        size        dd ?
        pImageCodecInfo dd ?
    endl

    invoke  GdipGetImageEncodersSize, addr numEncoders, addr size
    test    eax, eax
    jnz     .fail

    invoke  GlobalAlloc, 0x40, [size] ; GPTR
    mov     [pImageCodecInfo], eax
    
    invoke  GdipGetImageEncoders, [numEncoders], [size], [pImageCodecInfo]

    mov     ecx, [numEncoders]
    mov     esi, [pImageCodecInfo]

    .scan:
        push    ecx
        push    esi

        mov     eax, [esi + 48] ; MimeType is at offset 48
        mov     ebx, [_format]
        
        ; Simple string compare (WideChar)
        .strcmp:
            mov     dx, [eax]
            cmp     dx, [ebx]
            jne     .next_encoder
            test    dx, dx
            jz      .found
            add     eax, 2
            add     ebx, 2
            jmp     .strcmp

        .next_encoder:
        pop     esi
        pop     ecx
        add     esi, 76 ; Size of ImageCodecInfo structure
        dec     ecx
        jnz     .scan
        
    .fail:
        invoke  GlobalFree, [pImageCodecInfo]
        mov     eax, -1
        ret

    .found:
        pop     esi ; restore current codec info pointer
        pop     ecx ; balance stack
        
        ; Copy CLSID (offset 0) to destination
        mov     edi, [clsid]
        mov     ecx, 4 ; 16 bytes = 4 dwords
        rep     movsd
        
        invoke  GlobalFree, [pImageCodecInfo]
        xor     eax, eax ; Success
        ret
endp


start:
    ; 1. Initialize GDI+
    mov     [gdiplusInput.GdiplusVersion], 1
    invoke  GdiplusStartup, token, gdiplusInput, NULL
    test    eax, eax
    jnz     .exit

    ; 2. Parse Config Data
    mov     esi, config_data
    lodsd
    mov     [cell_w], eax
    lodsd
    mov     [cell_h], eax
    lodsd
    mov     [cols], eax
    lodsd
    mov     [rows], eax
    ; ESI now points to the first string

    ; Calculate total image size
    mov     eax, [cell_w]
    imul    eax, [cols]
    mov     ebx, [cell_h]
    imul    ebx, [rows]

    ; 3. Create Bitmap (Canvas)
    ; PixelFormat24bppRGB = 0x21808 (defined in headers or magic number)
    invoke  GdipCreateBitmapFromScan0, eax, ebx, 0, 0x21808, NULL, pBitmap
    invoke  GdipGetImageGraphicsContext, [pBitmap], pGraphics

    ; 4. Setup Graphics (Font, Brush, Clear)
    invoke  GdipGraphicsClear, [pGraphics], 0xFFFFFFFF ; White background
    
    ; Create Font: Consolas, Size 32 (approx), Regular Style (0), UnitPixel (2)
    invoke  GdipCreateFontFamilyFromName, _font_family, NULL, pFamily
    invoke  GdipCreateFont, [pFamily], 32.0, 0, 2, pFont
    
    invoke  GdipCreateSolidFill, 0xFF000000, pBrushText ; Black text

    ; 5. Rendering Loop
    mov     edi, 0          ; Row counter
    xor     ecx, ecx
    mov     [curr_y], ecx

.row_loop:
    cmp     edi, [rows]
    jge     .save_image

    mov     [curr_x], 0
    mov     ebx, 0          ; Col counter

    .col_loop:
        cmp     ebx, [cols]
        jge     .next_row_prep

        ; Check for null terminator in string
        mov     al, [esi]
        test    al, al
        jz      .next_row_prep ; End of string line

        ; Setup RectF for this character
        fild    [curr_x]
        fstp    [draw_rect]
        fild    [curr_y]
        fstp    [draw_rect+4]
        fild    [cell_w]
        fstp    [draw_rect+8]
        fild    [cell_h]
        fstp    [draw_rect+12]

        ; Draw the single character (ESI points to it)
        ; -1 length indicates null terminated, but we want 1 char.
        ; Convert char to Unicode? GdipDrawString expects WCHAR.
        ; Quick hack: We use GdipDrawString which needs WCHAR.
        ; We will convert ASCII char to WCHAR buffer on stack.
        xor     eax, eax
        mov     al, [esi]
        push    0           ; Null terminator high byte
        push    ax          ; Char + Null
        mov     edx, esp    ; Pointer to temporary WCHAR string

        invoke  GdipDrawString, [pGraphics], edx, -1, [pFont], draw_rect, NULL, [pBrushText]
        add     esp, 4      ; Restore stack

        ; Advance
        inc     esi
        inc     ebx
        
        mov     eax, [curr_x]
        add     eax, [cell_w]
        mov     [curr_x], eax
        
        jmp     .col_loop

    .next_row_prep:
        ; Skip to the next null terminator in ESI if we exited loop early
        .skip_null:
            cmp     byte [esi], 0
            je      .found_null
            inc     esi
            jmp     .skip_null
        .found_null:
        inc     esi ; Skip the null itself

        inc     edi
        mov     eax, [curr_y]
        add     eax, [cell_h]
        mov     [curr_y], eax
        jmp     .row_loop

    ; 6. Save to PNG
.save_image:
    ; Find PNG Encoder CLSID
    stdcall GetEncoderClsid, _mime_png, encoderClsid
    test    eax, eax
    js      .cleanup ; Failed (-1)

    invoke  GdipSaveImageToFile, [pBitmap], _filename, encoderClsid, NULL

.cleanup:
    invoke  GdipDeleteBrush, [pBrushText]
    invoke  GdipDeleteFont, [pFont]
    invoke  GdipDeleteFontFamily, [pFamily]
    invoke  GdipDeleteGraphics, [pGraphics]
    invoke  GdipDisposeImage, [pBitmap]
    invoke  GdiplusShutdown, [token]

.exit:
    invoke  ExitProcess, 0

; =============================================================
; Imports
; =============================================================
section '.idata' import data readable writeable

    library kernel32,'KERNEL32.DLL',\
            gdiplus,'gdiplus.dll'

    include 'api/kernel32.inc'

    import gdiplus,\
           GdiplusStartup,'GdiplusStartup',\
           GdiplusShutdown,'GdiplusShutdown',\
           GdipCreateBitmapFromScan0,'GdipCreateBitmapFromScan0',\
           GdipGetImageGraphicsContext,'GdipGetImageGraphicsContext',\
           GdipGraphicsClear,'GdipGraphicsClear',\
           GdipCreateFontFamilyFromName,'GdipCreateFontFamilyFromName',\
           GdipCreateFont,'GdipCreateFont',\
           GdipDrawString,'GdipDrawString',\
           GdipCreateSolidFill,'GdipCreateSolidFill',\
           GdipDeleteBrush,'GdipDeleteBrush',\
           GdipDeleteFont,'GdipDeleteFont',\
           GdipDeleteFontFamily,'GdipDeleteFontFamily',\
           GdipDeleteGraphics,'GdipDeleteGraphics',\
           GdipDisposeImage,'GdipDisposeImage',\
           GdipSaveImageToFile,'GdipSaveImageToFile',\
           GdipGetImageEncodersSize,'GdipGetImageEncodersSize',\
           GdipGetImageEncoders,'GdipGetImageEncoders'    

_________________
¯\(°_o)/¯ AI may [not] have aided with the above reply.
Post 19 Dec 2025, 07:36
View user's profile Send private message Visit poster's website Reply with quote
Roman



Joined: 21 Apr 2012
Posts: 2039
Roman 19 Dec 2025, 11:07
bitRAKE crash.
in
Code:
proc GetEncoderClsid uses ebx esi edi, _format, clsid
    locals
        numEncoders dd ?
        size        dd ?
        pImageCodecInfo dd ?
    endl

    invoke  GdipGetImageEncodersSize,  [numEncoders],  [size] ;this crash memory 0xFFFFFFFF can't be read    
Post 19 Dec 2025, 11:07
View user's profile Send private message Reply with quote
bitRAKE



Joined: 21 Jul 2003
Posts: 4333
Location: vpcmpistri
bitRAKE 19 Dec 2025, 12:00
I've pushed both the x86/x64 versions of the program to:
https://github.com/bitRAKE/odd_tests/tree/main/fontgrid

I don't know why you're getting errors, but you could try the executables I've had success with.
(Not that this will solve your problem.)

_________________
¯\(°_o)/¯ AI may [not] have aided with the above reply.
Post 19 Dec 2025, 12:00
View user's profile Send private message Visit poster's website Reply with quote
bitRAKE



Joined: 21 Jul 2003
Posts: 4333
Location: vpcmpistri
bitRAKE 19 Dec 2025, 12:10
Roman wrote:
Code:
invoke  GdipGetImageEncodersSize,  [numEncoders],  [size] ;this crash memory 0xFFFFFFFF can't be read    
... it looks like you changed the program. "addr" means address -- moving the data is the wrong parameter type. GdipGetImageEncodersSize is returning values and needs somewhere to put them.

_________________
¯\(°_o)/¯ AI may [not] have aided with the above reply.
Post 19 Dec 2025, 12:10
View user's profile Send private message Visit poster's website Reply with quote
Roman



Joined: 21 Apr 2012
Posts: 2039
Roman 19 Dec 2025, 12:20
program not compiled with addr.
I remove addr.
Code:
proc GetEncoderClsid uses ebx esi edi, _format, clsid
    locals
        numEncoders dd ?
        size        dd ?
        pImageCodecInfo dd ?
    endl

    invoke  GdipGetImageEncodersSize, addr numEncoders, addr size
    

Now working fine. And I get in one png 6 digits images.
Code:
    lea eax,[numEncoders]
    lea ecx,[size]
    invoke  GdipGetImageEncodersSize,  eax,ecx     


But how save each text symbol to each png files ?
If I want 10 symbols, I needed get 10 png files.
Post 19 Dec 2025, 12:20
View user's profile Send private message Reply with quote
bitRAKE



Joined: 21 Jul 2003
Posts: 4333
Location: vpcmpistri
bitRAKE 19 Dec 2025, 12:31
Oh, you're trying to build with fasm. It might work:
Code:
lea eax, [numEncoders]
lea edx, [size]
invoke GdipGetImageEncodersSize,eax,edx    
... perhaps there is much to change. Nothing stands out, but I am poor with compatibility skill.

Edit: you solved it already.

Roman wrote:
But how save each text symbol to each png files ?
If I want 10 symbols, I needed get 10 png files.
... well, do you know how to juggle? Very Happy
Put the save after the GdipDrawString and update the file name. You can delete all the grid code and have a simple string iterator.
Post 19 Dec 2025, 12:31
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8495
Location: Kraków, Poland
Tomasz Grysztar 19 Dec 2025, 12:51
bitRAKE wrote:
perhaps there is much to change. Nothing stands out, but I am poor with compatibility skill.
A much easier fix is to include "win32ax.inc" instead of "win32a.inc", fasm needed this for ADDR feature to be available.

With this simple change the source is compatible with both.
Post 19 Dec 2025, 12:51
View user's profile Send private message Visit poster's website Reply with quote
Roman



Joined: 21 Apr 2012
Posts: 2039
Roman 19 Dec 2025, 13:29
bitRAKE I am trying do GdipSetStringFormatAlign, but not work.
I want alignment symbol to center.
Post 19 Dec 2025, 13:29
View user's profile Send private message Reply with quote
bitRAKE



Joined: 21 Jul 2003
Posts: 4333
Location: vpcmpistri
bitRAKE 19 Dec 2025, 14:01
You've probably worked through it, but here is the centering:
Code:
invoke GdipCreateStringFormat, 0, 0, addr pFormat ; pointer/handle sized location to receive creation
test eax, eax
jnz .fail
; horizontal
invoke GdipSetStringFormatAlign, [pFormat], 1 ; StringAlignmentCenter
; vertical
invoke GdipSetStringFormatLineAlign, [pFormat], 1


; use format object in draw call
invoke GdipDrawString, [pGraphics], addr char_buf, -1, \
        [pFont], addr draw_rect, [pFormat], [pBrushText]


invoke GdipDeleteStringFormat, [pFormat]    
... format object setup/cleanup can be outside loop.
(GdipMeasureString for more custom work. Flat API reference.)

Fixed: parameter order correction.


Last edited by bitRAKE on 19 Dec 2025, 16:07; edited 1 time in total
Post 19 Dec 2025, 14:01
View user's profile Send private message Visit poster's website Reply with quote
Roman



Joined: 21 Apr 2012
Posts: 2039
Roman 19 Dec 2025, 14:18
Thanks. Alignment now work.

But png not have transparent background Color.
I want get whyte symbol on transparent background.
When I open symbol in photoshop background Color white, but not transparent.
I do GdipGraphicsClear, [Graphics], 0 But it not helped.
Post 19 Dec 2025, 14:18
View user's profile Send private message Reply with quote
Roman



Joined: 21 Apr 2012
Posts: 2039
Roman 20 Dec 2025, 08:56
I found
Do this
invoke GdipCreateBitmapFromScan0, eax, ebx, 0, 0x26200A, 0, pBitmap

PixelFormat32bppARGB = 0x26200A

For transparent background Color do
Invoke GdipGraphicsClear, [Graphics], 0
Post 20 Dec 2025, 08:56
View user's profile Send private message Reply with quote
Roman



Joined: 21 Apr 2012
Posts: 2039
Roman 20 Dec 2025, 10:51
Oh, how do antialiasing for draw Text?
I found.
invoke GdipSetTextRenderingHint, [Graphics], 1 or 4
Post 20 Dec 2025, 10:51
View user's profile Send private message Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  


< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2025, Tomasz Grysztar. Also on GitHub, YouTube.

Website powered by rwasa.