Skip to content

Instantly share code, notes, and snippets.

@CarsonSlovoka
Created September 14, 2024 08:39
Show Gist options
  • Save CarsonSlovoka/b34e2c252ee63932e46fe2b387bdacf8 to your computer and use it in GitHub Desktop.
Save CarsonSlovoka/b34e2c252ee63932e46fe2b387bdacf8 to your computer and use it in GitHub Desktop.
go draw_text eample: winapi.{TextOut, ExtTextOut}
module demo-draw-text
go 1.23.0
require github.com/CarsonSlovoka/go-pkg/v2 v2.4.2-0.20240619074555-8d486f5913b0
package main_test
import (
"encoding/binary"
"fmt"
"github.com/CarsonSlovoka/go-pkg/v2/w32"
"os"
"unicode/utf16"
"unsafe"
)
const testFontPath = "./DFGoldenButterflyJP13N-W2.otf" // 👈 置換成你的字型檔路徑
var (
Gdi = w32.NewGdi32DLL()
User = w32.NewUser32DLL()
)
func ImportFont(
fontPath string,
fontFamily string, // 要解字型檔的name表格,這邊不解,需要自己查
height int32,
) (w32.HFONT, error) {
numFont := Gdi.AddFontResource(fontPath)
if numFont == 0 {
return 0, fmt.Errorf("沒有任何字型被加載,請確定字型路徑是正確的")
}
// _ = User.PostMessage(w32.HWND_BROADCAST, w32.WM_FONTCHANGE, 0, 0) // 可選項
return NewHFont(fontFamily, height), nil
}
func NewHFont(fontFamily string, height int32) w32.HFONT {
lf := w32.LogFont{
Height: height,
Width: 0,
Escapement: 0,
Orientation: 0,
Weight: 400,
Italic: 0,
Underline: 0,
StrikeOut: 0,
CharSet: w32.DEFAULT_CHARSET,
OutPrecision: w32.OUT_TT_PRECIS,
ClipPrecision: w32.CLIP_DEFAULT_PRECIS,
Quality: w32.ANTIALIASED_QUALITY,
PitchAndFamily: w32.FF_DONTCARE,
}
return Gdi.CreateFont(
lf.Height,
lf.Width,
lf.Escapement,
lf.Orientation,
lf.Weight,
uint32(lf.Italic),
uint32(lf.Underline),
uint32(lf.StrikeOut),
uint32(lf.CharSet),
uint32(lf.OutPrecision),
uint32(lf.ClipPrecision),
uint32(lf.Quality),
uint32(lf.PitchAndFamily),
fontFamily, // fontFamilyName: nameID1; platformID, platformEncID, languageID // 系統要裝字, 或者用AddFontResource
)
}
func saveHBitmap(outputPath string, hdcMem w32.HDC, hBitmap w32.HBITMAP) error {
var bitmap w32.Bitmap
Gdi.GetObject(w32.HANDLE(hBitmap), int32(unsafe.Sizeof(bitmap)), uintptr(unsafe.Pointer(&bitmap)))
bitCount := uint16(32)
bmpSize := ((bitmap.Width*int32(bitCount) + 31) >> 5) * 4 * bitmap.Height
bmpData := make([]byte, bmpSize)
bitmapInfo := &w32.BitmapInfo{Header: w32.BitmapInfoHeader{
Size: 40,
Width: bitmap.Width, Height: bitmap.Height,
Planes: 1,
BitCount: bitCount,
Compression: w32.BI_RGB,
}}
Gdi.GetDIBits(
hdcMem, hBitmap, 0,
uint32(bitmap.Height),
bmpData, // lpBitmap, // [out]
bitmapInfo,
w32.DIB_RGB_COLORS,
)
f, err := os.Create(outputPath)
if err != nil {
return err
}
defer func() {
_ = f.Close()
}()
_ = binary.Write(f, binary.LittleEndian, w32.BitmapFileHeader{
Type: 0x4D42,
Size: 14 + 40 + uint32(bmpSize), // HEADER + INFO + DATA
OffsetBits: 14 + 40,
})
_ = binary.Write(f, binary.LittleEndian, bitmapInfo.Header)
_, err = f.Write(bmpData)
return err
}
const bitmapHeight = 100
const bitmapWidth = 600
func init() {
if bitmapHeight == 0 || bitmapWidth == 0 {
panic("invalid size 0")
}
}
func Example_textOut() {
// init
hdcScreen := User.GetDC(0)
defer User.ReleaseDC(0, hdcScreen)
hFont, err := ImportFont(testFontPath, "DFGoldenButterfly JP13N W2", -72)
if err != nil {
fmt.Println(err)
return
}
defer func() {
for {
if Gdi.RemoveFontResource(testFontPath) == 0 {
break
}
}
}()
defer Gdi.DeleteObject(w32.HGDIOBJ(hFont))
// hdc
hMemDC := Gdi.CreateCompatibleDC(hdcScreen)
defer Gdi.DeleteObject(w32.HGDIOBJ(hMemDC))
// select bitmap
hBitmap := Gdi.CreateCompatibleBitmap(hdcScreen, bitmapWidth, bitmapHeight)
defer Gdi.DeleteObject(w32.HGDIOBJ(hBitmap))
hObjOld := Gdi.SelectObject(hMemDC, w32.HGDIOBJ(hBitmap))
defer Gdi.SelectObject(hMemDC, hObjOld)
// select font
oldFont := Gdi.SelectObject(hMemDC, w32.HGDIOBJ(hFont))
defer Gdi.SelectObject(hMemDC, oldFont)
const text = "Hello 世界!"
textSize, _ := Gdi.GetTextExtentPoint32(hMemDC, text)
Gdi.TextOut(hMemDC,
(bitmapWidth-textSize.CX)/2, (bitmapHeight-textSize.CY)/2,
text,
)
if err = saveHBitmap("temp.textOut.png", hMemDC, hBitmap); err != nil {
fmt.Println(err)
}
// Output:
}
func Example_extTextOut() {
hdcScreen := User.GetDC(0)
defer User.ReleaseDC(0, hdcScreen)
hFont, err := ImportFont(testFontPath, "DFGoldenButterfly JP13N W2", -72)
if err != nil {
fmt.Println(err)
return
}
defer func() {
for {
if Gdi.RemoveFontResource(testFontPath) == 0 {
break
}
}
}()
defer Gdi.DeleteObject(w32.HGDIOBJ(hFont))
for i, d := range []struct {
x, y int32
options uint32
pRECT *w32.RECT
u16s []uint16
}{
{
x: 0, y: 0,
options: 0, // 不使用glyphID
pRECT: nil, // rect在ETO_OPAQUE或者ETO_CLIPPED才會有用
u16s: utf16.Encode([]rune("Hi 中文")),
},
// 範例二使用glyphID
{0, 0,
w32.ETO_GLYPH_INDEX,
nil,
[]uint16{41, 52}, // glyphID: 41, 52
},
} {
hMemDC := Gdi.CreateCompatibleDC(hdcScreen)
hBitmap := Gdi.CreateCompatibleBitmap(hdcScreen, bitmapWidth, bitmapHeight)
hObjOld := Gdi.SelectObject(hMemDC, w32.HGDIOBJ(hBitmap))
oldFont := Gdi.SelectObject(hMemDC, w32.HGDIOBJ(hFont))
Gdi.SetTextColor(hMemDC, w32.RGB(0xff, 0x00, 0x00))
Gdi.SetBkMode(hMemDC, w32.TRANSPARENT)
Gdi.ExtTextOut(hMemDC, d.x, d.y, d.options, d.pRECT, d.u16s)
if err = saveHBitmap(fmt.Sprintf("temp.%02d.png", i), hMemDC, hBitmap); err != nil {
fmt.Println("error: ", err)
}
Gdi.SelectObject(hMemDC, hObjOld)
Gdi.SelectObject(hMemDC, oldFont)
Gdi.DeleteObject(w32.HGDIOBJ(hBitmap))
Gdi.DeleteObject(w32.HGDIOBJ(hMemDC))
}
// Output:
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment