aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--prototypes/xgb-draw.go107
1 files changed, 61 insertions, 46 deletions
diff --git a/prototypes/xgb-draw.go b/prototypes/xgb-draw.go
index 7a1bdfc..b893f6f 100644
--- a/prototypes/xgb-draw.go
+++ b/prototypes/xgb-draw.go
@@ -1,9 +1,6 @@
// Network-friendly drawing application based on XRender.
//
-// TODO
-// - use double buffering to remove flicker
-// (more pronounced over X11 forwarding)
-// - maybe keep the pixmap as large as the window
+// TODO: Maybe keep the pixmap as large as the window.
package main
import (
@@ -26,6 +23,23 @@ func findPictureFormat(formats []render.Pictforminfo,
return 0
}
+func createNewPicture(X *xgb.Conn, depth byte, drawable xproto.Drawable,
+ width uint16, height uint16, format render.Pictformat) render.Picture {
+ pixmapid, err := xproto.NewPixmapId(X)
+ if err != nil {
+ log.Fatalln(err)
+ }
+ _ = xproto.CreatePixmap(X, depth, pixmapid, drawable, width, height)
+
+ pictid, err := render.NewPictureId(X)
+ if err != nil {
+ log.Fatalln(err)
+ }
+ _ = render.CreatePicture(X, pictid, xproto.Drawable(pixmapid), format,
+ 0, []uint32{})
+ return pictid
+}
+
func main() {
X, err := xgb.NewConn()
if err != nil {
@@ -125,53 +139,44 @@ func main() {
log.Fatalln(err)
}
_ = render.CreateSolidFill(X, colorid, render.Color{
- Red: 0xeeee,
+ Red: 0x4444,
Green: 0x8888,
- Blue: 0x4444,
+ Blue: 0xffff,
Alpha: 0xffff,
})
- // Pixmaps.
+ // Various pixmaps.
const (
pixWidth = 1000
pixHeight = 1000
)
- pixid, err := xproto.NewPixmapId(X)
- if err != nil {
- log.Fatalln(err)
- }
- _ = xproto.CreatePixmap(X, 24, pixid, xproto.Drawable(screen.Root),
- pixWidth, pixHeight)
-
- pixpictid, err := render.NewPictureId(X)
- if err != nil {
- log.Fatalln(err)
- }
- render.CreatePicture(X, pixpictid, xproto.Drawable(pixid),
- pformatRGB, 0, []uint32{})
-
- pixmaskid, err := xproto.NewPixmapId(X)
- if err != nil {
- log.Fatalln(err)
- }
- _ = xproto.CreatePixmap(X, 8, pixmaskid, xproto.Drawable(screen.Root),
- pixWidth, pixHeight)
-
- pixmaskpictid, err := render.NewPictureId(X)
- if err != nil {
- log.Fatalln(err)
- }
- render.CreatePicture(X, pixmaskpictid, xproto.Drawable(pixmaskid),
- pformatAlpha, 0, []uint32{})
+ canvasid := createNewPicture(X, 24,
+ xproto.Drawable(screen.Root), pixWidth, pixHeight, pformatRGB)
+ bufferid := createNewPicture(X, 24,
+ xproto.Drawable(screen.Root), pixWidth, pixHeight, pformatRGB)
+ maskid := createNewPicture(X, 8,
+ xproto.Drawable(screen.Root), pixWidth, pixHeight, pformatAlpha)
+
+ // Smoothing by way of blur, apparently a misguided idea.
+ /*
+ _ = render.SetPictureFilter(X, maskid,
+ uint16(len("convolution")), "convolution",
+ []render.Fixed{F64ToFixed(3), F64ToFixed(3),
+ F64ToFixed(0), F64ToFixed(0.15), F64ToFixed(0),
+ F64ToFixed(0.15), F64ToFixed(0.40), F64ToFixed(0.15),
+ F64ToFixed(0), F64ToFixed(0.15), F64ToFixed(0)})
+ */
// Pixmaps come uninitialized.
- render.FillRectangles(X,
- render.PictOpSrc, pixpictid, render.Color{
+ _ = render.FillRectangles(X,
+ render.PictOpSrc, canvasid, render.Color{
Red: 0xffff, Green: 0xffff, Blue: 0xffff, Alpha: 0xffff,
}, []xproto.Rectangle{{Width: pixWidth, Height: pixHeight}})
- // This is the only method we can use to render proper brush strokes.
+ // This is the only method we can use to render brush strokes without
+ // alpha accumulation due to stamping. Though this also seems to be
+ // misguided. Keeping it here for educational purposes.
//
// ConjointOver is defined as: A = Aa * 1 + Ab * max(1-Aa/Ab,0)
// which basically resolves to: A = max(Aa, Ab)
@@ -184,20 +189,28 @@ func main() {
// - https://keithp.com/~keithp/talks/cairo2003.pdf
drawPointAt := func(x, y int16) {
_ = render.Composite(X, render.PictOpConjointOver,
- brushid, render.PictureNone, pixmaskpictid,
+ brushid, render.PictureNone, maskid,
0, 0, 0, 0, x-brushRadius, y-brushRadius,
brushRadius*2, brushRadius*2)
- _ = render.SetPictureClipRectangles(X, pid,
+ _ = render.SetPictureClipRectangles(X, bufferid,
x-brushRadius, y-brushRadius, []xproto.Rectangle{
{Width: brushRadius * 2, Height: brushRadius * 2}})
-
_ = render.Composite(X, render.PictOpSrc,
- pixpictid, render.PictureNone, pid,
+ canvasid, render.PictureNone, bufferid,
0, 0, 0, 0, 0 /* dst-x */, 0, /* dst-y */
pixWidth, pixHeight)
_ = render.Composite(X, render.PictOpOver,
- colorid, pixmaskpictid, pid,
+ colorid, maskid, bufferid,
+ 0, 0, 0, 0, 0 /* dst-x */, 0, /* dst-y */
+ pixWidth, pixHeight)
+
+ // Composited, now blit to window without flicker.
+ _ = render.SetPictureClipRectangles(X, pid,
+ x-brushRadius, y-brushRadius, []xproto.Rectangle{
+ {Width: brushRadius * 2, Height: brushRadius * 2}})
+ _ = render.Composite(X, render.PictOpSrc,
+ bufferid, render.PictureNone, pid,
0, 0, 0, 0, 0 /* dst-x */, 0, /* dst-y */
pixWidth, pixHeight)
}
@@ -271,14 +284,16 @@ func main() {
_ = render.SetPictureClipRectangles(X, pid, int16(e.X), int16(e.Y),
[]xproto.Rectangle{{Width: e.Width, Height: e.Height}})
+ // Not bothering to deflicker here using the buffer pixmap,
+ // with compositing this event is rare enough.
_ = render.Composite(X, render.PictOpSrc,
- pixpictid, render.PictureNone, pid,
+ canvasid, render.PictureNone, pid,
0, 0, 0, 0, 0 /* dst-x */, 0, /* dst-y */
pixWidth, pixHeight)
if drawing {
_ = render.Composite(X, render.PictOpOver,
- colorid, pixmaskpictid, pid,
+ colorid, maskid, pid,
0, 0, 0, 0, 0 /* dst-x */, 0, /* dst-y */
pixWidth, pixHeight)
}
@@ -286,7 +301,7 @@ func main() {
case xproto.ButtonPressEvent:
if e.Detail == xproto.ButtonIndex1 {
render.FillRectangles(X,
- render.PictOpSrc, pixmaskpictid, render.Color{},
+ render.PictOpSrc, maskid, render.Color{},
[]xproto.Rectangle{{Width: pixWidth, Height: pixHeight}})
drawing = true
@@ -303,7 +318,7 @@ func main() {
case xproto.ButtonReleaseEvent:
if e.Detail == xproto.ButtonIndex1 {
_ = render.Composite(X, render.PictOpOver,
- colorid, pixmaskpictid, pixpictid,
+ colorid, maskid, canvasid,
0, 0, 0, 0, 0 /* dst-x */, 0, /* dst-y */
pixWidth, pixHeight)