summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--area_windows.go127
1 files changed, 127 insertions, 0 deletions
diff --git a/area_windows.go b/area_windows.go
index affdd9c..d83cc0c 100644
--- a/area_windows.go
+++ b/area_windows.go
@@ -7,6 +7,7 @@ import (
"syscall"
"unsafe"
"sync"
+ "image"
)
const (
@@ -23,9 +24,126 @@ var (
areaWndClassNumLock sync.Mutex
)
+var (
+ _getUpdateRect = user32.NewProc("GetUpdateRect")
+ _beginPaint = user32.NewProc("BeginPaint")
+ _endPaint = user32.NewProc("EndPaint")
+ _gdipCreateBitmapFromScan0 = gdiplus.NewProc("GdipCreateBitmapFromScan0")
+ _gdipCreateFromHDC = gdiplus.NewProc("GdipCreateFromHDC")
+ _gdipDrawImageI = gdiplus.NewProc("GdipDrawImageI")
+ _gdipDeleteGraphics = gdiplus.NewProc("GdipDeleteGraphics")
+ _gdipDisposeImage = gdiplus.NewProc("GdipDisposeImage")
+)
+
+const (
+ // from winuser.h
+ _WM_PAINT = 0x000F
+)
+
+func paintArea(s *sysData) {
+ const (
+ // from gdipluspixelformats.h
+ _PixelFormatGDI = 0x00020000
+ _PixelFormatAlpha = 0x00040000
+ _PixelFormatCanonical = 0x00200000
+ _PixelFormat32bppARGB = (10 | (32 << 8) | _PixelFormatAlpha | _PixelFormatGDI | _PixelFormatCanonical)
+ )
+
+ var xrect _RECT
+ var ps _PAINTSTRUCT
+
+ // TODO send _TRUE if we want to erase the clip area
+ r1, _, _ := _getUpdateRect.Call(
+ uintptr(s.hwnd),
+ uintptr(unsafe.Pointer(&xrect)),
+ uintptr(_FALSE))
+ if r1 == 0 { // no update rect; do nothing
+ return
+ }
+
+ cliprect := image.Rect(int(xrect.Left), int(xrect.Top), int(xrect.Right), int(xrect.Bottom))
+ // TODO offset cliprect by scroll position
+ // make sure the cliprect doesn't fall outside the size of the Area
+ cliprect = cliprect.Intersect(image.Rect(0, 0, 320, 240)) // TODO change when adding resizing
+ if cliprect.Empty() { // still no update rect
+ return
+ }
+
+ r1, _, err := _beginPaint.Call(
+ uintptr(s.hwnd),
+ uintptr(unsafe.Pointer(&ps)))
+ if r1 == 0 { // failure
+ panic(fmt.Errorf("error beginning Area repaint: %v", err))
+ }
+ hdc := _HANDLE(r1)
+
+ i := s.handler.Paint(cliprect)
+ // the pixels are arranged in RGBA order, but GDI+ requires BGRA
+ // we don't have a choice but to convert it ourselves
+ // TODO make realbits a part of sysData to conserve memory
+ realbits := make([]byte, 4 * i.Rect.Dx() * i.Rect.Dy())
+ p := 0
+ q := 0
+ for y := i.Rect.Min.Y; y < i.Rect.Max.Y; y++ {
+ nextp := p + i.Stride
+ for x := i.Rect.Min.X; x < i.Rect.Max.X; x++ {
+ realbits[q + 0] = byte(i.Pix[p + 2]) // B
+ realbits[q + 1] = byte(i.Pix[p + 1]) // G
+ realbits[q + 2] = byte(i.Pix[p + 0]) // R
+ realbits[q + 3] = byte(i.Pix[p + 3]) // A
+ p += 4
+ q += 4
+ }
+ p = nextp
+ }
+
+ var bitmap, graphics uintptr
+
+ r1, _, err = _gdipCreateBitmapFromScan0.Call(
+ uintptr(i.Rect.Dx()),
+ uintptr(i.Rect.Dy()),
+ uintptr(i.Rect.Dx() * 4), // got rid of extra stride
+ uintptr(_PixelFormat32bppARGB),
+ uintptr(unsafe.Pointer(&realbits[0])),
+ uintptr(unsafe.Pointer(&bitmap)))
+ if r1 != 0 { // failure
+ panic(fmt.Errorf("error creating GDI+ bitmap to blit (GDI+ error code %d; Windows last error %v)", r1, err))
+ }
+ r1, _, err = _gdipCreateFromHDC.Call(
+ uintptr(hdc),
+ uintptr(unsafe.Pointer(&graphics)))
+ if r1 != 0 { // failure
+ panic(fmt.Errorf("error creating GDI+ graphics context to blit to (GDI+ error code %d; Windows last error %v)", r1, err))
+ }
+ r1, _, err = _gdipDrawImageI.Call(
+ graphics,
+ bitmap,
+ uintptr(xrect.Left), // cliprect is adjusted; use original
+ uintptr(xrect.Top))
+ if r1 != 0 { // failure
+ panic(fmt.Errorf("error blitting GDI+ bitmap (GDI+ error code %d; Windows last error %v)", r1, err))
+ }
+ r1, _, err = _gdipDeleteGraphics.Call(graphics)
+ if r1 != 0 { // failure
+ panic(fmt.Errorf("error freeing GDI+ graphics context to blit to (GDI+ error code %d; Windows last error %v)", r1, err))
+ }
+ r1, _, err = _gdipDisposeImage.Call(bitmap)
+ if r1 != 0 { // failure
+ panic(fmt.Errorf("error freeing GDI+ bitmap to blit (GDI+ error code %d; Windows last error %v)", r1, err))
+ }
+
+ // return value always nonzero according to MSDN
+ _endPaint.Call(
+ uintptr(s.hwnd),
+ uintptr(unsafe.Pointer(&ps)))
+}
+
func areaWndProc(s *sysData) func(hwnd _HWND, uMsg uint32, wParam _WPARAM, lParam _LPARAM) _LRESULT {
return func(hwnd _HWND, uMsg uint32, wParam _WPARAM, lParam _LPARAM) _LRESULT {
switch uMsg {
+ case _WM_PAINT:
+ paintArea(s)
+ return _LRESULT(0)
default:
r1, _, _ := defWindowProc.Call(
uintptr(hwnd),
@@ -72,3 +190,12 @@ func registerAreaWndClass(s *sysData) (newClassName string, err error) {
}
return newClassName, nil
}
+
+type _PAINTSTRUCT struct {
+ hdc _HANDLE
+ fErase int32 // originally BOOL
+ rcPaint _RECT
+ fRestore int32 // originally BOOL
+ fIncUpdate int32 // originally BOOL
+ rgbReserved [32]byte
+}