Skip to content

Commit

Permalink
gdi.GetTextExtentPoint32 (#5)
Browse files Browse the repository at this point in the history
	- 簡化CreateCompatibleBitmap的範例
	- TextOut的長度自動依據輸入的字串來計算
	- TextOut置中的方法
  • Loading branch information
CarsonSlovoka committed Jun 14, 2024
1 parent ff7ca12 commit 2aa6a61
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 20 deletions.
1 change: 1 addition & 0 deletions v2/w32/gdi32.go
Original file line number Diff line number Diff line change
Expand Up @@ -769,6 +769,7 @@ const (
)

type (
// COLORREF BGR
COLORREF uint32
HBITMAP HGDIOBJ
HBRUSH HGDIOBJ
Expand Down
43 changes: 33 additions & 10 deletions v2/w32/gdi32_func.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package w32
import (
"fmt"
"syscall"
"unicode/utf16"
"unsafe"
)

Expand Down Expand Up @@ -32,10 +33,11 @@ const (

PNFillRgn ProcName = "FillRgn"

PNGetBitmapBits ProcName = "GetBitmapBits"
PNGetDIBits ProcName = "GetDIBits"
PNGetObject ProcName = "GetObjectW"
PNGetPixel ProcName = "GetPixel"
PNGetBitmapBits ProcName = "GetBitmapBits"
PNGetDIBits ProcName = "GetDIBits"
PNGetObject ProcName = "GetObjectW"
PNGetPixel ProcName = "GetPixel"
PNGetTextExtentPoint32 ProcName = "GetTextExtentPoint32W"

PNLineTo ProcName = "LineTo"

Expand Down Expand Up @@ -95,6 +97,7 @@ func NewGdi32DLL(procList ...ProcName) *Gdi32DLL {
PNGetDIBits,
PNGetObject,
PNGetPixel,
PNGetTextExtentPoint32,

PNLineTo,

Expand Down Expand Up @@ -559,6 +562,28 @@ func (dll *Gdi32DLL) GetPixel(hdc HDC, x int32, y int32) COLORREF {
return COLORREF(r1)
}

// GetTextExtentPoint32 https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-gettextextentpoint32w
// 你可以測量到輸出到此dc裝置上所被使用的寬和高
//
// GetTextExtentPoint32 doesn't consider "\n" (new line) or "\r\n" (carriage return and new line) characters
// when it computes the height of a text string.
//
// If the function succeeds, the return value is nonzero.
func (dll *Gdi32DLL) GetTextExtentPoint32(hdc HDC, str string) (*SIZE, bool) {
// fmt.Println(utf16.Encode([]rune("𫟅"))) // [55405 57285]
//pp, _ := syscall.UTF16FromString("𫟅") // [55405 57285 0]
var size SIZE
proc := dll.mustProc(PNGetTextExtentPoint32)
u16 := utf16.Encode([]rune(str)) // 這個沒有包含結尾的 0
r1, _, _ := syscall.SyscallN(proc.Addr(),
uintptr(hdc),
uintptr(unsafe.Pointer(&u16[0])),
uintptr(len(u16)), // 如果是用syscall.UTF16FromString取得的u16還要再-1(因為他有包含結尾0)
uintptr(unsafe.Pointer(&size)),
)
return &size, r1 != 0
}

// LineTo https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-lineto
// If the function succeeds, the return value is nonzero.
func (dll *Gdi32DLL) LineTo(hdc HDC, x, y int32) bool {
Expand Down Expand Up @@ -734,17 +759,15 @@ func (dll *Gdi32DLL) StretchBlt(

// TextOut https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-textoutw
// If the function fails, the return value is zero.
func (dll *Gdi32DLL) TextOut(hdc HDC, x int32, y int32, lpString string, length int32) bool {
func (dll *Gdi32DLL) TextOut(hdc HDC, x int32, y int32, str string) bool {
proc := dll.mustProc(PNTextOut)
if length == 0 {
length = int32((len(lpString) / 2) + 1) // 指的是utf16的個數,非utf8
}
u16s := utf16.Encode([]rune(str))
r1, _, _ := syscall.SyscallN(proc.Addr(),
uintptr(hdc),
uintptr(x),
uintptr(y),
UintptrFromStr(lpString),
uintptr(length),
uintptr(unsafe.Pointer(&u16s[0])),
uintptr(len(u16s)), // 不用包含結尾0
)
return r1 != 0
}
57 changes: 48 additions & 9 deletions v2/w32/gdi32_func_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,25 @@ func ExampleNewFontMemResource() {
// ok
}

func ExampleGdi32DLL_CreateCompatibleBitmap() {
hdcScreen := userDll.GetDC(0)
defer userDll.ReleaseDC(0, hdcScreen)

hMemDC := gdiDll.CreateCompatibleDC(hdcScreen)
defer gdiDll.DeleteObject(w32.HGDIOBJ(hMemDC))

var width, height int32
hMemBM := gdiDll.CreateCompatibleBitmap(hdcScreen, width, height)
defer gdiDll.DeleteObject(w32.HGDIOBJ(hMemBM))

hObjOld := gdiDll.SelectObject(hMemDC, w32.HGDIOBJ(hMemBM))
defer gdiDll.SelectObject(hMemDC, hObjOld)
}

// CaptureAnImage https://learn.microsoft.com/en-us/windows/win32/gdi/capturing-an-image
// https://zh.wikipedia.org/zh-tw/BMP
// 本範例簡述: 抓取當前的視窗,畫在notepad上,之後再保存在檔案之中,完成後檔案(testdata/captureNotepad.bmp)會刪除
func ExampleGdi32DLL_CreateCompatibleBitmap() {
func ExampleGdi32DLL_CreateCompatibleBitmap2() {
user32dll := w32.NewUser32DLL()
gdi32dll := w32.NewGdi32DLL()
kernel32dll := w32.NewKernel32DLL()
Expand Down Expand Up @@ -1289,19 +1304,37 @@ func ExampleGdi32DLL_GetBitmapBits() {
}

func TestGdi32DLL_TextOut(t *testing.T) {
// 取得螢幕的dc設備
hdcScreen := userDll.GetDC(0)
defer userDll.ReleaseDC(0, hdcScreen)

// clone出新的一份dc設備
hMemDC := gdiDll.CreateCompatibleDC(hdcScreen)
defer gdiDll.DeleteObject(w32.HGDIOBJ(hMemDC))

const width, height int32 = 600, 72
const width, height int32 = 1000, 72
// 利用螢幕的dc取得到
hBitmap := gdiDll.CreateCompatibleBitmap(hdcScreen, width, height)
defer gdiDll.DeleteObject(w32.HGDIOBJ(hBitmap))

// 選擇此bitmap
hObjOld := gdiDll.SelectObject(hMemDC, w32.HGDIOBJ(hBitmap))
defer gdiDll.SelectObject(hMemDC, hObjOld) // 不用之後可以考慮選回之前的物件

gdiDll.SetTextColor(hMemDC, w32.RGB(0, 1, 0)) // 全為0為黑色 // 我們故意把g改成1,來藉此判斷該元素需不需要被畫
var fgColor w32.COLORREF = 0x000000 // black
var bgColor w32.COLORREF = 0xffff00 // b: 255
fgColorR := fgColor.R()
fgColorG := fgColor.G()
fgColorB := fgColor.B()
bgColorR := bgColor.R()
bgColorG := bgColor.G()
bgColorB := bgColor.B()

if fgColor == 0 { // 全為0為純黑色
// 我們故意把g改成1,來藉此判斷該元素需不需要被畫
fgColorG = 1 // 由於創建的bitmapBits預設都是0,為了能區分出背景與前景,故意調整g數值
}
gdiDll.SetTextColor(hMemDC, w32.RGB(fgColorR, fgColorG, fgColorB))
gdiDll.SetBkColor(hMemDC, w32.RGB(0, 255, 255)) // 如果你的SetBkMode是TRANSPARENT,那麼這個顏色就看不出來了
// gdiDll.SetBkMode(hMemDC, w32.TRANSPARENT)

Expand Down Expand Up @@ -1344,10 +1377,16 @@ func TestGdi32DLL_TextOut(t *testing.T) {
oldFont := gdiDll.SelectObject(hMemDC, w32.HGDIOBJ(hFont))
defer gdiDll.SelectObject(hMemDC, oldFont)

text := "Hello! 世界"
gdiDll.TextOut(hMemDC, 5, 5, text, int32(len(text)))
const text = "Hello! 世界"

// 都字形選好之後,我們給入輸入的文字,測量此文字會用到的寬、高
textSize, _ := gdiDll.GetTextExtentPoint32(hMemDC, text)
gdiDll.TextOut(hMemDC,
(width-textSize.CX)/2, (height-textSize.CY)/2, // 畫的位置用居中的方式
text,
)

bitmapBits := make([]byte, width*height*4)
bitmapBits := make([]byte, width*height*4) // 保存hBitmap所提供的 `前景` 與 `背景` 顏色
gdiDll.GetBitmapBits(hBitmap, int32(len(bitmapBits)), uintptr(unsafe.Pointer(&bitmapBits[0])))

// Create an image from the bitmap bits
Expand All @@ -1358,10 +1397,10 @@ func TestGdi32DLL_TextOut(t *testing.T) {
idx := (y*width + x) * 4
b, g, r := bitmapBits[idx], bitmapBits[idx+1], bitmapBits[idx+2]
// alpha = bitmapBits[idx+3] // 這個都會是0
if g == 1 {
if r == fgColorR && g == fgColorG && b == fgColorB {
alpha = uint8(255) // a為255表示不透明
g = 0
} else if g == 255 || b == 255 { // 我們訂的背景顏色
} else if r == bgColorR && g == bgColorG && b == bgColorB {
alpha = uint8(255)
} else {
alpha = 0
Expand All @@ -1372,7 +1411,7 @@ func TestGdi32DLL_TextOut(t *testing.T) {
}

if false {
file, err := os.Create("output.png")
file, err := os.Create("output.png") // 建議用瀏覽器(chrome)來查看,可以觀察到alpha
if err != nil {
fmt.Println("Error creating file:", err)
return
Expand Down
2 changes: 2 additions & 0 deletions v2/w32/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ type (
PCWSTR *uint16
LPCWSTR *uint16
LPWSTR *uint16

LPSIZE *SIZE
)

type HOOKPROC func(code int32, wParam WPARAM, lParam LPARAM) LRESULT
Expand Down
2 changes: 1 addition & 1 deletion v2/w32/user32_func_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2011,7 +2011,7 @@ func ExampleUser32DLL_BeginPaint() {
gdi32dll.SetTextColor(hdc, w32.RGB(255, 128, 0))

user32dll.DrawText(hdc, "Hello World 您好 世界", -1, &rect, w32.DT_NOCLIP)
gdi32dll.TextOut(hdc, 100, 200, "Hi, 您好", 0)
gdi32dll.TextOut(hdc, 100, 200, "Hi, 您好")
user32dll.EndPaint(hwnd, &paintStruct)
}
return user32dll.DefWindowProc(hwnd, uMsg, wParam, lParam) // default window proc
Expand Down

0 comments on commit 2aa6a61

Please sign in to comment.