diff --git a/Makefile b/Makefile index a971cec..1ef1059 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ -include $(GOROOT)/src/Make.inc +#include $(GOROOT)/src/Make.inc all: install @@ -7,12 +7,14 @@ install: cd draw2d && make install cd draw2dgl && make install cd postscript && make install + #cd wingui && make install clean: cd draw2d && make clean cd draw2dgl && make clean cd postscript && make clean cd cmd && make clean + #cd wingui && make clean nuke: cd draw2d && make nuke @@ -20,3 +22,8 @@ nuke: cd postscript && make nuke command: cd cmd && make + #cd wingui && make nuke + +fmt: + gofmt -w draw2d postscript wingui cmd + diff --git a/cmd/Makefile b/cmd/Makefile index 6c456a3..1d2f034 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -1,7 +1,7 @@ include $(GOROOT)/src/Make.inc -TARG=gettingStarted testdraw2d testX11draw testandroid testgopher testimage testpostscript +TARG=gettingStarted testdraw2d testX11draw testandroid testgopher testimage testpostscript testWalkDraw OFILES=$(TARG:%=%.$O) diff --git a/cmd/testWalkDraw.go b/cmd/testWalkDraw.go new file mode 100644 index 0000000..b012952 --- /dev/null +++ b/cmd/testWalkDraw.go @@ -0,0 +1,228 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "syscall" + "os" + "unsafe" + "image" + "io/ioutil" + "strings" + "draw2d.googlecode.com/hg/draw2d" + "draw2d.googlecode.com/hg/postscript" + "draw2d.googlecode.com/hg/wingui" +) + +// some help functions + +func abortf(format string, a ...interface{}) { + fmt.Fprintf(os.Stdout, format, a...) + os.Exit(1) +} + +func abortErrNo(funcname string, err int) { + abortf("%s failed: %d %s\n", funcname, err, syscall.Errstr(err)) +} + +// global vars + + +func TestDrawCubicCurve(gc draw2d.GraphicContext) { + // draw a cubic curve + x, y := 25.6, 128.0 + x1, y1 := 102.4, 230.4 + x2, y2 := 153.6, 25.6 + x3, y3 := 230.4, 128.0 + + gc.SetFillColor(image.NRGBAColor{0xAA, 0xAA, 0xAA, 0xFF}) + gc.SetLineWidth(10) + gc.MoveTo(x, y) + gc.CubicCurveTo(x1, y1, x2, y2, x3, y3) + gc.Stroke() + + gc.SetStrokeColor(image.NRGBAColor{0xFF, 0x33, 0x33, 0x88}) + + gc.SetLineWidth(6) + // draw segment of curve + gc.MoveTo(x, y) + gc.LineTo(x1, y1) + gc.LineTo(x2, y2) + gc.LineTo(x3, y3) + gc.Stroke() +} + +var ( + mh uint32 + wndBufferHeader uint32 + wndBuffer wingui.BITMAP + hdcWndBuffer uint32 + ppvBits *image.RGBAColor + backBuffer *image.RGBA + postscriptContent string +) + +// WinProc called by windows to notify us of all windows events we might be interested in. +func WndProc(hwnd, msg uint32, wparam, lparam int32) uintptr { + var rc int32 + + switch msg { + case wingui.WM_CREATE: + hdc := wingui.GetDC(hwnd) + wndBufferHeader = wingui.CreateCompatibleBitmap(hdc, 600, 800) + wingui.GetObject(wndBufferHeader, unsafe.Sizeof(wndBuffer), uintptr(unsafe.Pointer(&wndBuffer))) + hdcWndBuffer = wingui.CreateCompatibleDC(hdc) + wingui.SelectObject(hdcWndBuffer, wndBufferHeader) + + var bmp_header wingui.BITMAPINFOHEADER + bmp_header.Size = uint32(unsafe.Sizeof(bmp_header)) + bmp_header.Width = 600 + bmp_header.Height = 800 + bmp_header.SizeImage = 0 // the api says this must be 0 for BI_RGB images + bmp_header.Compression = wingui.BI_RGB + bmp_header.BitCount = 32 + bmp_header.Planes = 1 + bmp_header.XPelsPerMeter = 0 + bmp_header.YPelsPerMeter = 0 + bmp_header.ClrUsed = 0 + bmp_header.ClrImportant = 0 + //bitmap info + var bmpinfo wingui.BITMAPINFO + bmpinfo.Colors[0].Blue = 0 + bmpinfo.Colors[0].Green = 0 + bmpinfo.Colors[0].Red = 0 + bmpinfo.Colors[0].Reserved = 0 + bmpinfo.Header = bmp_header + wndBufferHeader = wingui.CreateDIBSection(hdc, &bmpinfo, wingui.DIB_RGB_COLORS, uintptr(unsafe.Pointer(&ppvBits)), 0, 0) + wingui.GetObject(wndBufferHeader, unsafe.Sizeof(wndBufferHeader), uintptr(unsafe.Pointer(&wndBuffer))) + hdcWndBuffer = wingui.CreateCompatibleDC(hdc) + wingui.SelectObject(hdcWndBuffer, wndBufferHeader) + + pixel := (*[600 * 800]image.RGBAColor)(unsafe.Pointer(ppvBits)) + pixelSlice := pixel[:] + backBuffer = &image.RGBA{pixelSlice, 600, image.Rect(0, 0, 600, 800)} + fmt.Println("Create windows") + rc = wingui.DefWindowProc(hwnd, msg, wparam, lparam) + case wingui.WM_COMMAND: + switch uint32(lparam) { + default: + rc = wingui.DefWindowProc(hwnd, msg, wparam, lparam) + } + case wingui.WM_PAINT: + var ps wingui.PAINTSTRUCT + hdc := wingui.BeginPaint(hwnd, &ps) + gc := draw2d.NewImageGraphicContext(backBuffer) + gc.SetFillColor(image.RGBAColor{0xFF, 0xFF, 0xFF, 0xFF}) + gc.Clear() + gc.Save() + //gc.Translate(0, -380) + interpreter := postscript.NewInterpreter(gc) + reader := strings.NewReader(postscriptContent) + interpreter.Execute(reader) + gc.Restore() + wingui.BitBlt(hdc, 0, 0, int(wndBuffer.Width), int(wndBuffer.Height), hdcWndBuffer, 0, 0, wingui.SRCCOPY) + wingui.EndPaint(hwnd, &ps) + rc = wingui.DefWindowProc(hwnd, msg, wparam, lparam) + case wingui.WM_CLOSE: + wingui.DestroyWindow(hwnd) + case wingui.WM_DESTROY: + wingui.PostQuitMessage(0) + default: + rc = wingui.DefWindowProc(hwnd, msg, wparam, lparam) + } + return uintptr(rc) +} + +func rungui() int { + var e int + + // GetModuleHandle + mh, e = wingui.GetModuleHandle(nil) + if e != 0 { + abortErrNo("GetModuleHandle", e) + } + + // Get icon we're going to use. + myicon, e := wingui.LoadIcon(0, wingui.IDI_APPLICATION) + if e != 0 { + abortErrNo("LoadIcon", e) + } + + // Get cursor we're going to use. + mycursor, e := wingui.LoadCursor(0, wingui.IDC_ARROW) + if e != 0 { + abortErrNo("LoadCursor", e) + } + + // Create callback + wproc := syscall.NewCallback(WndProc) + + // RegisterClassEx + wcname := syscall.StringToUTF16Ptr("Test Draw2d") + var wc wingui.Wndclassex + wc.Size = uint32(unsafe.Sizeof(wc)) + wc.WndProc = wproc + wc.Instance = mh + wc.Icon = myicon + wc.Cursor = mycursor + wc.Background = wingui.COLOR_BTNFACE + 1 + wc.MenuName = nil + wc.ClassName = wcname + wc.IconSm = myicon + if _, e := wingui.RegisterClassEx(&wc); e != 0 { + abortErrNo("RegisterClassEx", e) + } + + // CreateWindowEx + wh, e := wingui.CreateWindowEx( + wingui.WS_EX_CLIENTEDGE, + wcname, + syscall.StringToUTF16Ptr("My window"), + wingui.WS_OVERLAPPEDWINDOW, + wingui.CW_USEDEFAULT, wingui.CW_USEDEFAULT, 600, 800, + 0, 0, mh, 0) + if e != 0 { + abortErrNo("CreateWindowEx", e) + } + fmt.Printf("main window handle is %x\n", wh) + + // ShowWindow + wingui.ShowWindow(wh, wingui.SW_SHOWDEFAULT) + + // UpdateWindow + if e := wingui.UpdateWindow(wh); e != 0 { + abortErrNo("UpdateWindow", e) + } + + // Process all windows messages until WM_QUIT. + var m wingui.Msg + for { + r, e := wingui.GetMessage(&m, 0, 0, 0) + if e != 0 { + abortErrNo("GetMessage", e) + } + if r == 0 { + // WM_QUIT received -> get out + break + } + wingui.TranslateMessage(&m) + wingui.DispatchMessage(&m) + } + return int(m.Wparam) +} + +func main() { + src, err := os.Open("../resource/postscript/tiger.ps", 0, 0) + if err != nil { + fmt.Println("can't find postscript file.") + return + } + defer src.Close() + bytes, err := ioutil.ReadAll(src) + postscriptContent = string(bytes) + rc := rungui() + os.Exit(rc) +} diff --git a/cmd/testimage.go b/cmd/testimage.go index 7521ee9..4bd5a84 100644 --- a/cmd/testimage.go +++ b/cmd/testimage.go @@ -9,6 +9,7 @@ import ( "image" "time" "image/png" + "exp/draw" "draw2d.googlecode.com/hg/draw2d" ) @@ -17,19 +18,19 @@ func saveToPngFile(filePath string, m image.Image) { f, err := os.Open(filePath, os.O_CREAT|os.O_WRONLY, 0600) if err != nil { log.Println(err) - os.Exit(1) + return } defer f.Close() b := bufio.NewWriter(f) err = png.Encode(b, m) if err != nil { log.Println(err) - os.Exit(1) + return } err = b.Flush() if err != nil { log.Println(err) - os.Exit(1) + return } fmt.Printf("Wrote %s OK.\n", filePath) } @@ -52,29 +53,19 @@ func loadFromPngFile(filePath string) image.Image { } -func testBubble(gc draw2d.GraphicContext) { - gc.BeginPath() - gc.MoveTo(75, 25) - gc.QuadCurveTo(25, 25, 25, 62.5) - gc.QuadCurveTo(25, 100, 50, 100) - gc.QuadCurveTo(50, 120, 30, 125) - gc.QuadCurveTo(60, 120, 65, 100) - gc.QuadCurveTo(125, 100, 125, 62.5) - gc.QuadCurveTo(125, 25, 75, 25) - gc.Stroke() -} - func main() { - - source := loadFromPngFile("../resource/image/Varna_Railway_Station_HDR.png") - i := image.NewRGBA(1024, 768) - gc := draw2d.NewGraphicContext(i) - gc.Scale(2, 0.5) - //gc.Translate(75, 25) - gc.Rotate(30 * math.Pi / 180) + source := loadFromPngFile("../resource/image/TestAndroid.png") + dest := image.NewRGBA(1024, 768) + width, height := float64(source.Bounds().Dx()), float64(source.Bounds().Dy()) + tr := draw2d.NewIdentityMatrix() + tr.Translate(width/2, height/2) + tr.Rotate(30 * math.Pi / 180) + //tr.Scale(3, 3) + tr.Translate(-width/2, -height/2) + tr.Translate(200, 5) lastTime := time.Nanoseconds() - gc.DrawImage(source) + draw2d.DrawImage(source, dest, tr, draw.Over, draw2d.BilinearFilter) dt := time.Nanoseconds() - lastTime fmt.Printf("Draw image: %f ms\n", float64(dt)*1e-6) - saveToPngFile("../resource/result/TestDrawImage.png", i) + saveToPngFile("../resource/result/TestDrawImage.png", dest) } diff --git a/draw2d/Makefile b/draw2d/Makefile index ca713f6..2ea9829 100644 --- a/draw2d/Makefile +++ b/draw2d/Makefile @@ -20,5 +20,6 @@ GOFILES=\ gc.go\ paint.go\ stack_gc.go\ + rgba_interpolation.go\ include $(GOROOT)/src/Make.pkg diff --git a/draw2d/image.go b/draw2d/image.go index fc1dbca..be17066 100644 --- a/draw2d/image.go +++ b/draw2d/image.go @@ -10,7 +10,7 @@ import ( "freetype-go.googlecode.com/hg/freetype/raster" ) -type Painter interface{ +type Painter interface { raster.Painter SetColor(color image.Color) } @@ -48,7 +48,6 @@ func NewGraphicContext(img draw.Image) *ImageGraphicContext { ftContext.SetDPI(dpi) ftContext.SetClip(img.Bounds()) ftContext.SetDst(img) - gc := &ImageGraphicContext{ NewStackGraphicContext(), img, @@ -58,7 +57,6 @@ func NewGraphicContext(img draw.Image) *ImageGraphicContext { ftContext, dpi, } - return gc } @@ -82,37 +80,10 @@ func (gc *ImageGraphicContext) ClearRect(x1, y1, x2, y2 int) { draw.Draw(gc.img, image.Rect(x1, y1, x2, y2), imageColor, image.ZP) } -func (gc *ImageGraphicContext) DrawImage(image image.Image) { - width := raster.Fix32(gc.img.Bounds().Dx() * 256) - height := raster.Fix32(gc.img.Bounds().Dy() * 256) - - p0 := raster.Point{0, 0} - p1 := raster.Point{0, 0} - p2 := raster.Point{0, 0} - p3 := raster.Point{0, 0} - var i raster.Fix32 = 0 - for ; i < width; i += 256 { - var j raster.Fix32 = 0 - for ; j < height; j += 256 { - p0.X, p0.Y = i, j - p1.X, p1.Y = p0.X+256, p0.Y - p2.X, p2.Y = p1.X, p0.Y+256 - p3.X, p3.Y = p0.X, p2.Y - - gc.current.Tr.TransformRasterPoint(&p0, &p1, &p2, &p3) - gc.fillRasterizer.Start(p0) - gc.fillRasterizer.Add1(p1) - gc.fillRasterizer.Add1(p2) - gc.fillRasterizer.Add1(p3) - gc.fillRasterizer.Add1(p0) - gc.painter.SetColor(image.At(int(i>>8), int(j>>8))) - gc.fillRasterizer.Rasterize(gc.painter) - gc.fillRasterizer.Clear() - } - } +func (gc *ImageGraphicContext) DrawImage(img image.Image) { + DrawImage(img, gc.img, gc.current.Tr, draw.Over, BilinearFilter) } - func (gc *ImageGraphicContext) FillString(text string) (cursor float64) { gc.freetype.SetSrc(image.NewColorImage(gc.current.StrokeColor)) // Draw the text. diff --git a/draw2d/math.go b/draw2d/math.go index 703d9ed..9323818 100644 --- a/draw2d/math.go +++ b/draw2d/math.go @@ -21,3 +21,31 @@ func squareDistance(x1, y1, x2, y2 float64) float64 { dy := y2 - y1 return dx*dx + dy*dy } + +func min(x, y float64) float64 { + if x < y { + return x + } + return y +} + +func max(x, y float64) float64 { + if x > y { + return x + } + return y +} + +func minMax(x, y float64) (min, max float64) { + if x > y { + return y, x + } + return x, y +} + +func minUint32(a, b uint32) uint32 { + if a < b { + return a + } + return b +} diff --git a/draw2d/paint.go b/draw2d/paint.go index 8a156a5..be3bbd1 100644 --- a/draw2d/paint.go +++ b/draw2d/paint.go @@ -19,13 +19,6 @@ type NRGBAPainter struct { cr, cg, cb, ca uint32 } -func min(a, b uint32) uint32 { - if a < b { - return a - } - return b -} - // Paint satisfies the Painter interface by painting ss onto an image.RGBA. func (r *NRGBAPainter) Paint(ss []raster.Span, done bool) { b := r.Image.Bounds() @@ -57,9 +50,9 @@ func (r *NRGBAPainter) Paint(ss []raster.Span, done bool) { a := M - (r.ca*ma)/M da = (da*a + r.ca*ma) / M if da != 0 { - dr = min(M, (dr*a+r.cr*ma)/da) - dg = min(M, (dg*a+r.cg*ma)/da) - db = min(M, (db*a+r.cb*ma)/da) + dr = minUint32(M, (dr*a+r.cr*ma)/da) + dg = minUint32(M, (dg*a+r.cg*ma)/da) + db = minUint32(M, (db*a+r.cb*ma)/da) } else { dr, dg, db = 0, 0, 0 } @@ -71,9 +64,9 @@ func (r *NRGBAPainter) Paint(ss []raster.Span, done bool) { a := M - ma da = (da*a + r.ca*ma) / M if da != 0 { - dr = min(M, (dr*a+r.cr*ma)/da) - dg = min(M, (dg*a+r.cg*ma)/da) - db = min(M, (db*a+r.cb*ma)/da) + dr = minUint32(M, (dr*a+r.cr*ma)/da) + dg = minUint32(M, (dg*a+r.cg*ma)/da) + db = minUint32(M, (db*a+r.cb*ma)/da) } else { dr, dg, db = 0, 0, 0 } diff --git a/draw2d/rgba_interpolation.go b/draw2d/rgba_interpolation.go new file mode 100644 index 0000000..1f9ab8a --- /dev/null +++ b/draw2d/rgba_interpolation.go @@ -0,0 +1,154 @@ +// see http://pippin.gimp.org/image_processing/chap_resampling.html +package draw2d + +import ( + "exp/draw" + "image" + "math" +) + +type ImageFilter int + +const ( + LinearFilter ImageFilter = iota + BilinearFilter + BicubicFilter +) + +//see http://pippin.gimp.org/image_processing/chap_resampling.html +func getColorLinear(img image.Image, x, y float64) image.Color { + return img.At(int(x), int(y)) +} + +func getColorBilinear(img image.Image, x, y float64) image.Color { + x0 := math.Floor(x) + y0 := math.Floor(y) + dx := x - x0 + dy := y - y0 + + c0 := img.At(int(x0), int(y0)) + c1 := img.At(int(x0+1), int(y0)) + c2 := img.At(int(x0+1), int(y0+1)) + c3 := img.At(int(x0), int(y0+1)) + rt, gt, bt, at := c0.RGBA() + r0, g0, b0, a0 := float64(rt), float64(gt), float64(bt), float64(at) + rt, gt, bt, at = c1.RGBA() + r1, g1, b1, a1 := float64(rt), float64(gt), float64(bt), float64(at) + rt, gt, bt, at = c2.RGBA() + r2, g2, b2, a2 := float64(rt), float64(gt), float64(bt), float64(at) + rt, gt, bt, at = c3.RGBA() + r3, g3, b3, a3 := float64(rt), float64(gt), float64(bt), float64(at) + r := int(lerp(lerp(r0, r1, dx), lerp(r3, r2, dx), dy)) + g := int(lerp(lerp(g0, g1, dx), lerp(g3, g2, dx), dy)) + b := int(lerp(lerp(b0, b1, dx), lerp(b3, b2, dx), dy)) + a := int(lerp(lerp(a0, a1, dx), lerp(a3, a2, dx), dy)) + return image.RGBAColor{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8), uint8(a >> 8)} +} +/** +-- LERP +-- /lerp/, vi.,n. +-- +-- Quasi-acronym for Linear Interpolation, used as a verb or noun for +-- the operation. "Bresenham's algorithm lerps incrementally between the +-- two endpoints of the line." (From Jargon File (4.4.4, 14 Aug 2003) +*/ +func lerp(v1, v2, ratio float64) float64 { + return v1*(1-ratio) + v2*ratio +} + + +func getColorCubicRow(img image.Image, x, y, offset float64) image.Color { + c0 := img.At(int(x), int(y)) + c1 := img.At(int(x+1), int(y)) + c2 := img.At(int(x+2), int(y)) + c3 := img.At(int(x+3), int(y)) + rt, gt, bt, at := c0.RGBA() + r0, g0, b0, a0 := float64(rt), float64(gt), float64(bt), float64(at) + rt, gt, bt, at = c1.RGBA() + r1, g1, b1, a1 := float64(rt), float64(gt), float64(bt), float64(at) + rt, gt, bt, at = c2.RGBA() + r2, g2, b2, a2 := float64(rt), float64(gt), float64(bt), float64(at) + rt, gt, bt, at = c3.RGBA() + r3, g3, b3, a3 := float64(rt), float64(gt), float64(bt), float64(at) + r, g, b, a := cubic(offset, r0, r1, r2, r3), cubic(offset, g0, g1, g2, g3), cubic(offset, b0, b1, b2, b3), cubic(offset, a0, a1, a2, a3) + return image.RGBAColor{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8), uint8(a >> 8)} +} + +func getColorBicubic(img image.Image, x, y float64) image.Color { + x0 := math.Floor(x) + y0 := math.Floor(y) + dx := x - x0 + dy := y - y0 + c0 := getColorCubicRow(img, x0-1, y0-1, dx) + c1 := getColorCubicRow(img, x0-1, y0, dx) + c2 := getColorCubicRow(img, x0-1, y0+1, dx) + c3 := getColorCubicRow(img, x0-1, y0+2, dx) + rt, gt, bt, at := c0.RGBA() + r0, g0, b0, a0 := float64(rt), float64(gt), float64(bt), float64(at) + rt, gt, bt, at = c1.RGBA() + r1, g1, b1, a1 := float64(rt), float64(gt), float64(bt), float64(at) + rt, gt, bt, at = c2.RGBA() + r2, g2, b2, a2 := float64(rt), float64(gt), float64(bt), float64(at) + rt, gt, bt, at = c3.RGBA() + r3, g3, b3, a3 := float64(rt), float64(gt), float64(bt), float64(at) + r, g, b, a := cubic(dy, r0, r1, r2, r3), cubic(dy, g0, g1, g2, g3), cubic(dy, b0, b1, b2, b3), cubic(dy, a0, a1, a2, a3) + return image.RGBAColor{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8), uint8(a >> 8)} +} + +func cubic(offset, v0, v1, v2, v3 float64) uint32 { + // offset is the offset of the sampled value between v1 and v2 + return uint32(((((-7*v0+21*v1-21*v2+7*v3)*offset+ + (15*v0-36*v1+27*v2-6*v3))*offset+ + (-9*v0+9*v2))*offset + (v0 + 16*v1 + v2)) / 18.0) +} + +func compose(c1, c2 image.Color) image.Color { + r1, g1, b1, a1 := c1.RGBA() + r2, g2, b2, a2 := c2.RGBA() + ia := M - a2 + r := ((r1 * ia) / M) + r2 + g := ((g1 * ia) / M) + g2 + b := ((b1 * ia) / M) + b2 + a := ((a1 * ia) / M) + a2 + return image.RGBAColor{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8), uint8(a >> 8)} +} + + +func DrawImage(src image.Image, dest draw.Image, tr MatrixTransform, op draw.Op, filter ImageFilter) { + bounds := src.Bounds() + x0, y0, x1, y1 := float64(bounds.Min.X), float64(bounds.Min.Y), float64(bounds.Max.X), float64(bounds.Max.Y) + tr.TransformRectangle(&x0, &y0, &x1, &y1) + var x, y, u, v float64 + var c1, c2, cr image.Color + var r, g, b, a, ia, r1, g1, b1, a1, r2, g2, b2, a2 uint32 + for x = x0; x < x1; x++ { + for y = y0; y < y1; y++ { + u = x + v = y + tr.InverseTransform(&u, &v) + c1 = dest.At(int(x), int(y)) + switch filter { + case LinearFilter: + c2 = src.At(int(u), int(v)) + case BilinearFilter: + c2 = getColorBilinear(src, u, v) + case BicubicFilter: + c2 = getColorBicubic(src, u, v) + } + switch op { + case draw.Over: + r1, g1, b1, a1 = c1.RGBA() + r2, g2, b2, a2 = c2.RGBA() + ia = M - a2 + r = ((r1 * ia) / M) + r2 + g = ((g1 * ia) / M) + g2 + b = ((b1 * ia) / M) + b2 + a = ((a1 * ia) / M) + a2 + cr = image.RGBAColor{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8), uint8(a >> 8)} + default: + cr = c2 + } + dest.Set(int(x), int(y), cr) + } + } +} diff --git a/draw2d/transform.go b/draw2d/transform.go index ff22ca2..2cc2b7f 100644 --- a/draw2d/transform.go +++ b/draw2d/transform.go @@ -26,6 +26,23 @@ func (tr MatrixTransform) Transform(points ...*float64) { } } +func (tr MatrixTransform) TransformRectangle(x0, y0, x2, y2 *float64) { + x1 := *x2 + y1 := *y0 + x3 := *x0 + y3 := *y2 + tr.Transform(x0, y0, &x1, &y1, x2, y2, &x3, &y3) + *x0, x1 = minMax(*x0, x1) + *x2, x3 = minMax(*x2, x3) + *y0, y1 = minMax(*y0, y1) + *y2, y3 = minMax(*y2, y3) + + *x0 = min(*x0, *x2) + *y0 = min(*y0, *y2) + *x2 = max(x1, x3) + *y2 = max(y1, y3) +} + func (tr MatrixTransform) TransformRasterPoint(points ...*raster.Point) { for _, point := range points { x := float64(point.X) / 256 @@ -127,33 +144,30 @@ func (tr1 MatrixTransform) Multiply(tr2 MatrixTransform) MatrixTransform { func (tr *MatrixTransform) Scale(sx, sy float64) *MatrixTransform { - tr[0] = tr[0] * sx - tr[1] = tr[1] * sx - tr[4] = tr[4] * sx - tr[2] = tr[2] * sy - tr[3] = tr[3] * sy - tr[5] = tr[5] * sy + tr[0] = sx * tr[0] + tr[1] = sx * tr[1] + tr[2] = sy * tr[2] + tr[3] = sy * tr[3] return tr } func (tr *MatrixTransform) Translate(tx, ty float64) *MatrixTransform { - tr[4] = tr[4] + tx - tr[5] = tr[5] + ty + tr[4] = tx*tr[0] + ty*tr[2] + tr[4] + tr[5] = ty*tr[3] + tx*tr[1] + tr[5] return tr } func (tr *MatrixTransform) Rotate(angle float64) *MatrixTransform { - ca := math.Cos(angle) - sa := math.Sin(angle) - t0 := tr[0]*ca - tr[1]*sa - t2 := tr[1]*ca - tr[3]*sa - t4 := tr[4]*ca - tr[5]*sa - tr[1] = tr[0]*sa + tr[1]*ca - tr[3] = tr[2]*sa + tr[3]*ca - tr[5] = tr[4]*sa + tr[5]*ca + c := math.Cos(angle) + s := math.Sin(angle) + t0 := c*tr[0] + s*tr[2] + t1 := s*tr[3] + c*tr[1] + t2 := c*tr[2] - s*tr[0] + t3 := c*tr[3] - s*tr[1] tr[0] = t0 + tr[1] = t1 tr[2] = t2 - tr[4] = t4 + tr[3] = t3 return tr } diff --git a/resource/image/TestAndroid.png b/resource/image/TestAndroid.png new file mode 100644 index 0000000..b0294d0 Binary files /dev/null and b/resource/image/TestAndroid.png differ diff --git a/resource/result/TestDrawImage.png b/resource/result/TestDrawImage.png index 3100ab7..7836523 100644 Binary files a/resource/result/TestDrawImage.png and b/resource/result/TestDrawImage.png differ diff --git a/wingui/Makefile b/wingui/Makefile new file mode 100644 index 0000000..9f58925 --- /dev/null +++ b/wingui/Makefile @@ -0,0 +1,10 @@ +include $(GOROOT)/src/Make.inc + +TARG=draw2d.googlecode.com/hg/wingui +GOFILES=\ + winapi.go\ + wingdi.go\ + zwinapi.go\ + + +include $(GOROOT)/src/Make.pkg diff --git a/wingui/winapi.go b/wingui/winapi.go new file mode 100644 index 0000000..2ae48d3 --- /dev/null +++ b/wingui/winapi.go @@ -0,0 +1,155 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +package wingui + +import ( + "syscall" + "unsafe" + "fmt" + "os" +) + + +func abortf(format string, a ...interface{}) { + fmt.Fprintf(os.Stdout, format, a...) + os.Exit(1) +} + +func loadDll(fname string) uint32 { + h, e := syscall.LoadLibrary(fname) + if e != 0 { + abortf("LoadLibrary(%s) failed with err=%d.\n", fname, e) + } + return h +} + +func getSysProcAddr(m uint32, pname string) uintptr { + p, e := syscall.GetProcAddress(m, pname) + if e != 0 { + abortf("GetProcAddress(%s) failed with err=%d.\n", pname, e) + } + return uintptr(p) +} + +type Wndclassex struct { + Size uint32 + Style uint32 + WndProc uintptr + ClsExtra int32 + WndExtra int32 + Instance uint32 + Icon uint32 + Cursor uint32 + Background uint32 + MenuName *uint16 + ClassName *uint16 + IconSm uint32 +} + +type Point struct { + X int32 + Y int32 +} + +type Msg struct { + Hwnd uint32 + Message uint32 + Wparam int32 + Lparam int32 + Time uint32 + Pt Point +} + +const ( + // Window styles + WS_OVERLAPPED = 0 + WS_POPUP = 0x80000000 + WS_CHILD = 0x40000000 + WS_MINIMIZE = 0x20000000 + WS_VISIBLE = 0x10000000 + WS_DISABLED = 0x8000000 + WS_CLIPSIBLINGS = 0x4000000 + WS_CLIPCHILDREN = 0x2000000 + WS_MAXIMIZE = 0x1000000 + WS_CAPTION = WS_BORDER | WS_DLGFRAME + WS_BORDER = 0x800000 + WS_DLGFRAME = 0x400000 + WS_VSCROLL = 0x200000 + WS_HSCROLL = 0x100000 + WS_SYSMENU = 0x80000 + WS_THICKFRAME = 0x40000 + WS_GROUP = 0x20000 + WS_TABSTOP = 0x10000 + WS_MINIMIZEBOX = 0x20000 + WS_MAXIMIZEBOX = 0x10000 + WS_TILED = WS_OVERLAPPED + WS_ICONIC = WS_MINIMIZE + WS_SIZEBOX = WS_THICKFRAME + // Common Window Styles + WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX + WS_TILEDWINDOW = WS_OVERLAPPEDWINDOW + WS_POPUPWINDOW = WS_POPUP | WS_BORDER | WS_SYSMENU + WS_CHILDWINDOW = WS_CHILD + + WS_EX_CLIENTEDGE = 0x200 + + // Some windows messages + WM_CREATE = 1 + WM_DESTROY = 2 + WM_CLOSE = 16 + WM_COMMAND = 273 + + // Some button control styles + BS_DEFPUSHBUTTON = 1 + + // Some colour constants + COLOR_WINDOW = 5 + COLOR_BTNFACE = 15 + + // Default window position + CW_USEDEFAULT = 0x80000000 - 0x100000000 + + // Show window default style + SW_SHOWDEFAULT = 10 +) + +var ( + // Some globaly known cusrors + IDC_ARROW = MakeIntResource(32512) + IDC_IBEAM = MakeIntResource(32513) + IDC_WAIT = MakeIntResource(32514) + IDC_CROSS = MakeIntResource(32515) + + // Some globaly known icons + IDI_APPLICATION = MakeIntResource(32512) + IDI_HAND = MakeIntResource(32513) + IDI_QUESTION = MakeIntResource(32514) + IDI_EXCLAMATION = MakeIntResource(32515) + IDI_ASTERISK = MakeIntResource(32516) + IDI_WINLOGO = MakeIntResource(32517) + IDI_WARNING = IDI_EXCLAMATION + IDI_ERROR = IDI_HAND + IDI_INFORMATION = IDI_ASTERISK +) + +//sys GetModuleHandle(modname *uint16) (handle uint32, errno int) = GetModuleHandleW +//sys RegisterClassEx(wndclass *Wndclassex) (atom uint16, errno int) = user32.RegisterClassExW +//sys CreateWindowEx(exstyle uint32, classname *uint16, windowname *uint16, style uint32, x int32, y int32, width int32, height int32, wndparent uint32, menu uint32, instance uint32, param uintptr) (hwnd uint32, errno int) = user32.CreateWindowExW +//sys DefWindowProc(hwnd uint32, msg uint32, wparam int32, lparam int32) (lresult int32) = user32.DefWindowProcW +//sys DestroyWindow(hwnd uint32) (errno int) = user32.DestroyWindow +//sys PostQuitMessage(exitcode int32) = user32.PostQuitMessage +//sys ShowWindow(hwnd uint32, cmdshow int32) (wasvisible bool) = user32.ShowWindow +//sys UpdateWindow(hwnd uint32) (errno int) = user32.UpdateWindow +//sys GetMessage(msg *Msg, hwnd uint32, MsgFilterMin uint32, MsgFilterMax uint32) (ret int32, errno int) [failretval==-1] = user32.GetMessageW +//sys TranslateMessage(msg *Msg) (done bool) = user32.TranslateMessage +//sys DispatchMessage(msg *Msg) (ret int32) = user32.DispatchMessageW +//sys LoadIcon(instance uint32, iconname *uint16) (icon uint32, errno int) = user32.LoadIconW +//sys LoadCursor(instance uint32, cursorname *uint16) (cursor uint32, errno int) = user32.LoadCursorW +//sys SetCursor(cursor uint32) (precursor uint32, errno int) = user32.SetCursor +//sys SendMessage(hwnd uint32, msg uint32, wparam int32, lparam int32) (lresult int32) = user32.SendMessageW +//sys PostMessage(hwnd uint32, msg uint32, wparam int32, lparam int32) (errno int) = user32.PostMessageW + +func MakeIntResource(id uint16) *uint16 { + return (*uint16)(unsafe.Pointer(uintptr(id))) +} diff --git a/wingui/wingdi.go b/wingui/wingdi.go new file mode 100644 index 0000000..fe796f3 --- /dev/null +++ b/wingui/wingdi.go @@ -0,0 +1,132 @@ +package wingui + +import ( + "syscall" + "unsafe" +) + +const ( + WM_PAINT = 15 + + BI_RGB = 0 + + DIB_PAL_COLORS = 1 + DIB_RGB_COLORS = 0 + + SRCCOPY = 0xCC0020 +) + +type RECT struct { + Left int32 + Top int32 + Right int32 + Bottom int32 +} + +type PAINTSTRUCT struct { + HDC uint32 + Erase int32 // bool + RcPaint RECT + Restore int32 // bool + IncUpdate int32 // bool + rgbReserved [32]byte +} + +type BITMAP struct { + Type int32 + Width int32 + Height int32 + WidthBytes int32 + Planes uint16 + BitsPixel uint16 + Bits *byte +} + +type BITMAPINFOHEADER struct { + Size uint32 + Width int32 + Height int32 + Planes uint16 + BitCount uint16 + Compression uint32 + SizeImage uint32 + XPelsPerMeter int32 + YPelsPerMeter int32 + ClrUsed uint32 + ClrImportant uint32 +} + +type BITMAPINFO struct { + Header BITMAPINFOHEADER + Colors [1]RGBQUAD +} + +type RGBQUAD struct { + Blue byte + Green byte + Red byte + Reserved byte +} + +var ( + modgdi32 = loadDll("gdi32.dll") + + procGetDC = getSysProcAddr(moduser32, "GetDC") + procCreateCompatibleDC = getSysProcAddr(modgdi32, "CreateCompatibleDC") + procGetObject = getSysProcAddr(modgdi32, "GetObjectW") + procSelectObject = getSysProcAddr(modgdi32, "SelectObject") + procBeginPaint = getSysProcAddr(moduser32, "BeginPaint") + procEndPaint = getSysProcAddr(moduser32, "EndPaint") + procCreateCompatibleBitmap = getSysProcAddr(modgdi32, "CreateCompatibleBitmap") + procCreateDIBSection = getSysProcAddr(modgdi32, "CreateDIBSection") + procBitBlt = getSysProcAddr(modgdi32, "BitBlt") +) + +func GetDC(hwnd uint32) (hdc uint32) { + r0, _, _ := syscall.Syscall(procGetDC, 1, uintptr(hwnd), 0, 0) + hdc = uint32(r0) + return hdc +} + +func CreateCompatibleDC(hwnd uint32) (hdc uint32) { + r0, _, _ := syscall.Syscall(procCreateCompatibleDC, 1, uintptr(hwnd), 0, 0) + hdc = uint32(r0) + return hdc +} + +func GetObject(hgdiobj uint32, cbBuffer int, object uintptr) (size uint32) { + r0, _, _ := syscall.Syscall(procGetObject, 3, uintptr(hgdiobj), uintptr(cbBuffer), object) + size = uint32(r0) + return size +} + +func SelectObject(hdc uint32, hgdiobj uint32) uint32 { + r0, _, _ := syscall.Syscall(procSelectObject, 2, uintptr(hdc), uintptr(hgdiobj), 0) + return uint32(r0) +} + +func BeginPaint(hwnd uint32, ps *PAINTSTRUCT) (hdc uint32) { + r0, _, _ := syscall.Syscall(procBeginPaint, 2, uintptr(hwnd), uintptr(unsafe.Pointer(ps)), 0) + hdc = uint32(r0) + return hdc +} + +func EndPaint(hwnd uint32, ps *PAINTSTRUCT) bool { + syscall.Syscall(procEndPaint, 2, uintptr(hwnd), uintptr(unsafe.Pointer(ps)), 0) + return true +} + +func CreateCompatibleBitmap(hdc uint32, width, height int) (hbitmap uint32) { + r0, _, _ := syscall.Syscall(procCreateCompatibleBitmap, 3, uintptr(hdc), uintptr(width), uintptr(height)) + return uint32(r0) +} + +func CreateDIBSection(hdc uint32, pbmi *BITMAPINFO, iUsage uint, ppvBits uintptr, hSection uint32, dwOffset uint32) (hbitmap uint32) { + r0, _, _ := syscall.Syscall6(procCreateDIBSection, 6, uintptr(hdc), uintptr(unsafe.Pointer(pbmi)), uintptr(iUsage), ppvBits, uintptr(hSection), uintptr(dwOffset)) + return uint32(r0) +} + +func BitBlt(hdc uint32, nXDest, nYDest, nWidth, nHeight int, hdcSrc uint32, nXSrc, nYSrc int, dwRop uint32) bool { + r0, _, _ := syscall.Syscall9(procBitBlt, 9, uintptr(hdc), uintptr(nXDest), uintptr(nYDest), uintptr(nWidth), uintptr(nHeight), uintptr(hdcSrc), uintptr(nXSrc), uintptr(nYSrc), uintptr(dwRop)) + return r0 != 0 +} diff --git a/wingui/zwinapi.go b/wingui/zwinapi.go new file mode 100644 index 0000000..9ced49b --- /dev/null +++ b/wingui/zwinapi.go @@ -0,0 +1,209 @@ +package wingui + + +import "unsafe" +import "syscall" + +var ( + modkernel32 = loadDll("kernel32.dll") + moduser32 = loadDll("user32.dll") + + procGetModuleHandleW = getSysProcAddr(modkernel32, "GetModuleHandleW") + procRegisterClassExW = getSysProcAddr(moduser32, "RegisterClassExW") + procCreateWindowExW = getSysProcAddr(moduser32, "CreateWindowExW") + procDefWindowProcW = getSysProcAddr(moduser32, "DefWindowProcW") + procDestroyWindow = getSysProcAddr(moduser32, "DestroyWindow") + procPostQuitMessage = getSysProcAddr(moduser32, "PostQuitMessage") + procShowWindow = getSysProcAddr(moduser32, "ShowWindow") + procUpdateWindow = getSysProcAddr(moduser32, "UpdateWindow") + procGetMessageW = getSysProcAddr(moduser32, "GetMessageW") + procTranslateMessage = getSysProcAddr(moduser32, "TranslateMessage") + procDispatchMessageW = getSysProcAddr(moduser32, "DispatchMessageW") + procLoadIconW = getSysProcAddr(moduser32, "LoadIconW") + procLoadCursorW = getSysProcAddr(moduser32, "LoadCursorW") + procSetCursor = getSysProcAddr(moduser32, "SetCursor") + procSendMessageW = getSysProcAddr(moduser32, "SendMessageW") + procPostMessageW = getSysProcAddr(moduser32, "PostMessageW") +) + +func GetModuleHandle(modname *uint16) (handle uint32, errno int) { + r0, _, e1 := syscall.Syscall(procGetModuleHandleW, 1, uintptr(unsafe.Pointer(modname)), 0, 0) + handle = uint32(r0) + if handle == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = syscall.EINVAL + } + } else { + errno = 0 + } + return +} + +func RegisterClassEx(wndclass *Wndclassex) (atom uint16, errno int) { + r0, _, e1 := syscall.Syscall(procRegisterClassExW, 1, uintptr(unsafe.Pointer(wndclass)), 0, 0) + atom = uint16(r0) + if atom == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = syscall.EINVAL + } + } else { + errno = 0 + } + return +} + +func CreateWindowEx(exstyle uint32, classname *uint16, windowname *uint16, style uint32, x int32, y int32, width int32, height int32, wndparent uint32, menu uint32, instance uint32, param uintptr) (hwnd uint32, errno int) { + r0, _, e1 := syscall.Syscall12(procCreateWindowExW, 12, uintptr(exstyle), uintptr(unsafe.Pointer(classname)), uintptr(unsafe.Pointer(windowname)), uintptr(style), uintptr(x), uintptr(y), uintptr(width), uintptr(height), uintptr(wndparent), uintptr(menu), uintptr(instance), uintptr(param)) + hwnd = uint32(r0) + if hwnd == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = syscall.EINVAL + } + } else { + errno = 0 + } + return +} + +func DefWindowProc(hwnd uint32, msg uint32, wparam int32, lparam int32) (lresult int32) { + r0, _, _ := syscall.Syscall6(procDefWindowProcW, 4, uintptr(hwnd), uintptr(msg), uintptr(wparam), uintptr(lparam), 0, 0) + lresult = int32(r0) + return +} + +func DestroyWindow(hwnd uint32) (errno int) { + r1, _, e1 := syscall.Syscall(procDestroyWindow, 1, uintptr(hwnd), 0, 0) + if int(r1) == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = syscall.EINVAL + } + } else { + errno = 0 + } + return +} + +func PostQuitMessage(exitcode int32) { + syscall.Syscall(procPostQuitMessage, 1, uintptr(exitcode), 0, 0) + return +} + +func ShowWindow(hwnd uint32, cmdshow int32) (wasvisible bool) { + r0, _, _ := syscall.Syscall(procShowWindow, 2, uintptr(hwnd), uintptr(cmdshow), 0) + wasvisible = bool(r0 != 0) + return +} + +func UpdateWindow(hwnd uint32) (errno int) { + r1, _, e1 := syscall.Syscall(procUpdateWindow, 1, uintptr(hwnd), 0, 0) + if int(r1) == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = syscall.EINVAL + } + } else { + errno = 0 + } + return +} + +func GetMessage(msg *Msg, hwnd uint32, MsgFilterMin uint32, MsgFilterMax uint32) (ret int32, errno int) { + r0, _, e1 := syscall.Syscall6(procGetMessageW, 4, uintptr(unsafe.Pointer(msg)), uintptr(hwnd), uintptr(MsgFilterMin), uintptr(MsgFilterMax), 0, 0) + ret = int32(r0) + if ret == -1 { + if e1 != 0 { + errno = int(e1) + } else { + errno = syscall.EINVAL + } + } else { + errno = 0 + } + return +} + +func TranslateMessage(msg *Msg) (done bool) { + r0, _, _ := syscall.Syscall(procTranslateMessage, 1, uintptr(unsafe.Pointer(msg)), 0, 0) + done = bool(r0 != 0) + return +} + +func DispatchMessage(msg *Msg) (ret int32) { + r0, _, _ := syscall.Syscall(procDispatchMessageW, 1, uintptr(unsafe.Pointer(msg)), 0, 0) + ret = int32(r0) + return +} + +func LoadIcon(instance uint32, iconname *uint16) (icon uint32, errno int) { + r0, _, e1 := syscall.Syscall(procLoadIconW, 2, uintptr(instance), uintptr(unsafe.Pointer(iconname)), 0) + icon = uint32(r0) + if icon == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = syscall.EINVAL + } + } else { + errno = 0 + } + return +} + +func LoadCursor(instance uint32, cursorname *uint16) (cursor uint32, errno int) { + r0, _, e1 := syscall.Syscall(procLoadCursorW, 2, uintptr(instance), uintptr(unsafe.Pointer(cursorname)), 0) + cursor = uint32(r0) + if cursor == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = syscall.EINVAL + } + } else { + errno = 0 + } + return +} + +func SetCursor(cursor uint32) (precursor uint32, errno int) { + r0, _, e1 := syscall.Syscall(procSetCursor, 1, uintptr(cursor), 0, 0) + precursor = uint32(r0) + if precursor == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = syscall.EINVAL + } + } else { + errno = 0 + } + return +} + +func SendMessage(hwnd uint32, msg uint32, wparam int32, lparam int32) (lresult int32) { + r0, _, _ := syscall.Syscall6(procSendMessageW, 4, uintptr(hwnd), uintptr(msg), uintptr(wparam), uintptr(lparam), 0, 0) + lresult = int32(r0) + return +} + +func PostMessage(hwnd uint32, msg uint32, wparam int32, lparam int32) (errno int) { + r1, _, e1 := syscall.Syscall6(procPostMessageW, 4, uintptr(hwnd), uintptr(msg), uintptr(wparam), uintptr(lparam), 0, 0) + if int(r1) == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = syscall.EINVAL + } + } else { + errno = 0 + } + return +}