// drawPencil takes an (x, y) position (from a MotionNotify event) and draws // a rectangle of size pencilTip on to canvas. func drawPencil(canvas *xgraphics.Image, win *xwindow.Window, x, y int) { // Create a subimage at (x, y) with pencilTip width and height from canvas. // Creating subimages is very cheap---no pixels are copied. // Moreover, when subimages are drawn to the screen, only the pixels in // the sub-image are sent to X. tipRect := midRect(x, y, pencilTip, pencilTip, width, height) // If the rectangle contains no pixels, don't draw anything. if tipRect.Empty() { return } // Output a little message. log.Printf("Drawing pencil point at (%d, %d)", x, y) // Create the subimage of the canvas to draw to. tip := canvas.SubImage(tipRect) fmt.Println(tip.Rect) // Now color each pixel in tip with the pencil color. tip.For(func(x, y int) xgraphics.BGRA { return xgraphics.BlendBGRA(tip.At(x, y).(xgraphics.BGRA), pencil) }) // Now draw the changes to the pixmap. tip.XDraw() // And paint them to the window. tip.XPaint(win.Id) }
// This. This seems to be the solution. 0.72ns/op. RGBA -> RGBA is 0.02ns/op. Draw is 180ns/op. // from https://github.com/BurntSushi/xgbutil/blob/master/xgraphics/convert.go func convertRGBAtoXgb(dest *xgraphics.Image, src *image.RGBA) { var x, y, i, si int for x = dest.Rect.Min.X; x < dest.Rect.Max.X; x++ { for y = dest.Rect.Min.Y; y < dest.Rect.Max.Y; y++ { si = src.PixOffset(x, y) i = dest.PixOffset(x, y) dest.Pix[i+0] = src.Pix[si+2] dest.Pix[i+1] = src.Pix[si+1] dest.Pix[i+2] = src.Pix[si+0] dest.Pix[i+3] = src.Pix[si+3] } } }
// paint uses the xgbutil/xgraphics package to copy the area corresponding // to ximg in its pixmap to the window. It will also issue a clear request // before hand to try and avoid artifacts. func (w *Window) paint(ximg *xgraphics.Image) { // If the image is bigger than the canvas, this is always (0, 0). // If the image is the same size, then it is also (0, 0). // If a dimension of the image is smaller than the canvas, then: // x = (canvas_width - image_width) / 2 and // y = (canvas_height - image_height) / 2 xmargin, ymargin := 0, 0 if ximg.Bounds().Dx() < w.Geom.Width() { xmargin = (w.Geom.Width() - ximg.Bounds().Dx()) / 2 } if ximg.Bounds().Dy() < w.Geom.Height() { ymargin = (w.Geom.Height() - ximg.Bounds().Dy()) / 2 } ximg.XExpPaint(w.Id, xmargin, ymargin) }
// vpCenter inspects the canvas and image geometry, and determines where the // origin of the image should be painted into the canvas. // If the image is bigger than the canvas, this is always (0, 0). // If the image is the same size, then it is also (0, 0). // If a dimension of the image is smaller than the canvas, then: // x = (canvas_width - image_width) / 2 and // y = (canvas_height - image_height) / 2 func vpCenter(ximg *xgraphics.Image, canWidth, canHeight int) image.Point { xmargin, ymargin := 0, 0 if ximg.Bounds().Dx() < canWidth { xmargin = (canWidth - ximg.Bounds().Dx()) / 2 } if ximg.Bounds().Dy() < canHeight { ymargin = (canHeight - ximg.Bounds().Dy()) / 2 } return image.Point{xmargin, ymargin} }
// SetImageToBg sets the given image into the background at the proper location // for the named output to display. func SetImageToBg(X *xgbutil.XUtil, bg *xgraphics.Image, img image.Image, name string) error { output, err := util.GetOutputByName(X, name) if err != nil { return err } geom, err := util.OutputRect(X, output) if err != nil { return err } if err = bg.CreatePixmap(); err != nil { return err } bg.XDraw() draw.Draw(bg, geom, img, img.Bounds().Min, draw.Src) return nil }
func copyToXGraphicsImage(xs *xgraphics.Image, buffer *image.RGBA) { //Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*4] xdata := xs.Pix bdata := buffer.Pix if xs.Bounds() == buffer.Bounds() { copy(xdata, bdata) } else { xb := xs.Bounds() bb := buffer.Bounds() miny := xb.Min.Y if miny < bb.Min.Y { miny = bb.Min.Y } maxy := xb.Max.Y if maxy > bb.Max.Y { maxy = bb.Max.Y } xRowLen := (xb.Max.X - xb.Min.X) bRowLen := (bb.Max.X - bb.Min.X) rowLen := xRowLen if bRowLen < rowLen { rowLen = bRowLen } for y := miny; y < maxy; y++ { xstart := (y-xb.Min.Y)*xs.Stride - xb.Min.X*4 bstart := (y-bb.Min.Y)*buffer.Stride - bb.Min.X*4 xrow := xdata[xstart : xstart+rowLen*4] brow := bdata[bstart : bstart+rowLen*4] copy(xrow, brow) } } // xgraphics.Image is BGRA, not RGBA, so swap some bits for i := 0; i < len(xdata)/4; i++ { index := i * 4 xdata[index], xdata[index+2] = xdata[index+2], xdata[index] } }
// blendCheckered is basically a copy of xgraphics.Blend with no interfaces. // (It's faster.) Also, it is hardcoded to blend into a checkered background. func blendCheckered(dest *xgraphics.Image) { dsrc := dest.Bounds() dmnx, dmxx, dmny, dmxy := dsrc.Min.X, dsrc.Max.X, dsrc.Min.Y, dsrc.Max.Y clr1 := xgraphics.BGRA{B: 0xff, G: 0xff, R: 0xff, A: 0xff} clr2 := xgraphics.BGRA{B: 0xde, G: 0xdc, R: 0xdf, A: 0xff} var dx, dy int var bgra, clr xgraphics.BGRA for dx = dmnx; dx < dmxx; dx++ { for dy = dmny; dy < dmxy; dy++ { if dx%30 >= 15 { if dy%30 >= 15 { clr = clr1 } else { clr = clr2 } } else { if dy%30 >= 15 { clr = clr2 } else { clr = clr1 } } bgra = dest.At(dx, dy).(xgraphics.BGRA) dest.SetBGRA(dx, dy, xgraphics.BlendBGRA(clr, bgra)) } } }
func updater(img *xgraphics.Image, win *xwindow.Window) { //We keep track of times based on 1024 frames frame := 0 start := time.Now() var genStart, drawStart time.Time var genTotal, drawTotal time.Duration for { frame = frame + 1 if frame > 1024 { frame = 0 log.Printf("Time elapsed: %s\n", time.Now().Sub(start)) log.Printf("Time generate: %s\n", genTotal) log.Printf("Time drawing: %s\n", drawTotal) start = time.Now() drawTotal, genTotal = 0, 0 } genStart = time.Now() var x, y, i int for y = 0; y < 768; y++ { //set last pixel back to black img.SetBGRA(frame-1, y, xgraphics.BGRA{0, 0, 0, 255}) for i = 0; i < 10; i++ { x = i + frame if x > 1024 { x = 1024 } img.SetBGRA(x, y, xgraphics.BGRA{0, 0, 255, 255}) } } genTotal += time.Now().Sub(genStart) drawStart = time.Now() //hopefully using checked will block us from drawing again before x //draws although XDraw might block anyway, we can check for an error //here err := img.XDrawChecked() if err != nil { log.Println(err) return } //img.XDraw() img.XPaint(win.Id) drawTotal += time.Now().Sub(drawStart) } }
// clearCanvas erases all your pencil marks. func clearCanvas(canvas *xgraphics.Image, win *xwindow.Window) { log.Println("Clearing canvas...") canvas.For(func(x, y int) xgraphics.BGRA { return bg }) canvas.XDraw() canvas.XPaint(win.Id) }
// drawGopher draws the gopher image to the canvas. func drawGopher(canvas *xgraphics.Image, gopher image.Image, win *xwindow.Window, x, y int) { // Find the rectangle of the canvas where we're going to draw the gopher. gopherRect := midRect(x, y, gopherWidth, gopherHeight, width, height) // If the rectangle contains no pixels, don't draw anything. if gopherRect.Empty() { return } // Output a little message. log.Printf("Drawing gopher at (%d, %d)", x, y) // Get a subimage of the gopher that's in sync with gopherRect. gopherPt := image.Pt(gopher.Bounds().Min.X, gopher.Bounds().Min.Y) if gopherRect.Min.X == 0 { gopherPt.X = gopherWidth - gopherRect.Dx() } if gopherRect.Min.Y == 0 { gopherPt.Y = gopherHeight - gopherRect.Dy() } // Create the canvas subimage. subCanvas := canvas.SubImage(gopherRect) // Blend the gopher image into the sub-canvas. // This does alpha blending. xgraphics.Blend(subCanvas, gopher, gopherPt) // Now draw the changes to the pixmap. subCanvas.XDraw() // And paint them to the window. subCanvas.XPaint(win.Id) }
// This is a slightly modified version of xgraphics.XShowExtra that does // not set any resize constraints on the window (so that it can go // fullscreen). func showImage(im *xgraphics.Image, name string, quit bool) *xwindow.Window { if len(name) == 0 { name = "xgbutil Image Window" } w, h := im.Rect.Dx(), im.Rect.Dy() win, err := xwindow.Generate(im.X) if err != nil { xgbutil.Logger.Printf("Could not generate new window id: %s", err) return nil } // Create a very simple window with dimensions equal to the image. win.Create(im.X.RootWin(), 0, 0, w, h, 0) // Make this window close gracefully. win.WMGracefulClose(func(w *xwindow.Window) { xevent.Detach(w.X, w.Id) keybind.Detach(w.X, w.Id) mousebind.Detach(w.X, w.Id) w.Destroy() if quit { xevent.Quit(w.X) } }) // Set WM_STATE so it is interpreted as a top-level window. err = icccm.WmStateSet(im.X, win.Id, &icccm.WmState{ State: icccm.StateNormal, }) if err != nil { // not a fatal error xgbutil.Logger.Printf("Could not set WM_STATE: %s", err) } // Set _NET_WM_NAME so it looks nice. err = ewmh.WmNameSet(im.X, win.Id, name) if err != nil { // not a fatal error xgbutil.Logger.Printf("Could not set _NET_WM_NAME: %s", err) } // Paint our image before mapping. im.XSurfaceSet(win.Id) im.XDraw() im.XPaint(win.Id) // Now we can map, since we've set all our properties. // (The initial map is when the window manager starts managing.) win.Map() return win }
func (p *piece) Create(act, inact *xgraphics.Image) { if act != nil { if p.active > 0 { xgraphics.FreePixmap(p.X, p.active) } act.CreatePixmap() act.XDraw() p.active = act.Pixmap } if inact != nil { if p.inactive > 0 { xgraphics.FreePixmap(p.X, p.inactive) } inact.CreatePixmap() inact.XDraw() p.inactive = inact.Pixmap } }
// SetRoot sets the given background as the root window background. func SetRoot(X *xgbutil.XUtil, bg *xgraphics.Image) error { root := X.RootWin() if err := bg.XSurfaceSet(root); err != nil { return err } bg.XDraw() bg.XPaint(root) // FIXME: This doesn't set the pixmap persistently. As soon as the program // exits, the pixmap is destroyed. Find a way to make it persistent. xprop.ChangeProp32(X, root, "_XROOTPMAP_ID", "PIXMAP", uint(bg.Pixmap)) xprop.ChangeProp32(X, root, "ESETROOT_PMAP_ID", "PIXMAP", uint(bg.Pixmap)) return nil }
// paint uses the xgbutil/xgraphics package to copy the area corresponding // to ximg in its pixmap to the window. It will also issue a clear request // before hand to try and avoid artifacts. func (w *window) paint(ximg *xgraphics.Image) { dst := vpCenter(ximg, w.Geom.Width(), w.Geom.Height()) // UUU Commenting this out avoids flickering, and I see no artifacts! // w.ClearAll() ximg.XExpPaint(w.Id, dst.X, dst.Y) }
func makeWindow(ximage *xgraphics.Image) (*xwindow.Window, *bool) { w, h := ximage.Rect.Dx(), ximage.Rect.Dy() window, err := xwindow.Generate(ximage.X) if err != nil { xgbutil.Logger.Printf("Could not generate new window id: %s", err) return nil, nil } window.Create(ximage.X.RootWin(), 0, 0, w, h, xproto.CwBackPixel, 0x00000000) window.Listen(xproto.EventMaskExposure, xproto.EventMaskKeyPress, xproto.EventMaskStructureNotify, xproto.EventMaskVisibilityChange) window.WMGracefulClose(func(w *xwindow.Window) { xevent.Detach(w.X, w.Id) keybind.Detach(w.X, w.Id) mousebind.Detach(w.X, w.Id) w.Destroy() xevent.Quit(w.X) }) err = icccm.WmStateSet(ximage.X, window.Id, &icccm.WmState{ State: icccm.StateNormal, }) if err != nil { xgbutil.Logger.Printf("Could not set WM_STATE: %s", err) } err = ewmh.WmNameSet(ximage.X, window.Id, "Computer System Monitor") if err != nil { xgbutil.Logger.Printf("Could not set _NET_WM_NAME: %s", err) } err = keybind.KeyPressFun( func(X *xgbutil.XUtil, ev xevent.KeyPressEvent) { err := ewmh.WmStateReq(ximage.X, window.Id, ewmh.StateToggle, "_NET_WM_STATE_FULLSCREEN") if err != nil { log.Fatal(err) } }).Connect(ximage.X, window.Id, "f", false) if err != nil { log.Fatal(err) } xevent.ExposeFun( func(xu *xgbutil.XUtil, event xevent.ExposeEvent) { ximage.XExpPaint(window.Id, 0, 0) }).Connect(ximage.X, window.Id) obscured := false xevent.VisibilityNotifyFun( func(xu *xgbutil.XUtil, event xevent.VisibilityNotifyEvent) { obscured = event.State == xproto.VisibilityFullyObscured }).Connect(ximage.X, window.Id) window.Map() return window, &obscured }