diff options
Diffstat (limited to 'prototypes')
| -rw-r--r-- | prototypes/xgb-xrender.go | 193 | 
1 files changed, 191 insertions, 2 deletions
| diff --git a/prototypes/xgb-xrender.go b/prototypes/xgb-xrender.go index 3ae51ad..6e07b4e 100644 --- a/prototypes/xgb-xrender.go +++ b/prototypes/xgb-xrender.go @@ -1,9 +1,16 @@  package main  import ( +	"fmt"  	"github.com/BurntSushi/xgb"  	"github.com/BurntSushi/xgb/render"  	"github.com/BurntSushi/xgb/xproto" +	"github.com/golang/freetype" +	"github.com/golang/freetype/truetype" +	"golang.org/x/image/font" +	"golang.org/x/image/font/gofont/goregular" +	"golang.org/x/image/math/fixed" +	"image"  	"log"  	"math/rand"  ) @@ -11,6 +18,98 @@ import (  func F64ToFixed(f float64) render.Fixed { return render.Fixed(f * 65536) }  func FixedToF64(f render.Fixed) float64 { return float64(f) / 65536 } +func glyphListBytes(buf []byte, runes []rune, size int) int { +	b := 0 +	for _, r := range runes { +		switch size { +		default: +			buf[b] = byte(r) +			b += 1 +		case 2: +			xgb.Put16(buf[b:], uint16(r)) +			b += 2 +		case 4: +			xgb.Put32(buf[b:], uint32(r)) +			b += 4 +		} +	} +	return xgb.Pad(b) +} + +// When the len is 255, a GLYPHABLE follows, otherwise a list of CARD8/16/32. +func glyphEltHeaderBytes(buf []byte, len byte, deltaX, deltaY int16) int { +	b := 0 +	buf[b] = len +	b += 4 +	xgb.Put16(buf[b:], uint16(deltaX)) +	b += 2 +	xgb.Put16(buf[b:], uint16(deltaY)) +	b += 2 +	return xgb.Pad(b) +} + +type xgbCookie interface{ Check() error } + +// TODO: We actually need a higher-level function that also keeps track of +// and loads glyphs into the X server. + +// compositeString makes an appropriate render.CompositeGlyphs request, +// assuming that glyphs equal Unicode codepoints. +func compositeString(c *xgb.Conn, op byte, src, dst render.Picture, +	maskFormat render.Pictformat, glyphset render.Glyphset, srcX, srcY int16, +	destX, destY int16, text string) xgbCookie { +	runes := []rune(text) + +	var highest rune +	for _, r := range runes { +		if r > highest { +			highest = r +		} +	} + +	size := 1 +	switch { +	case highest > 1<<16: +		size = 4 +	case highest > 1<<8: +		size = 2 +	} + +	// They gave up on the XCB protocol API and we need to serialize explicitly. + +	// To spare us from caring about the padding, use the largest number lesser +	// than 255 that is divisible by 4 (for size 2 and 4 the requirements are +	// less strict but this works in the general case). +	const maxPerChunk = 252 + +	buf := make([]byte, (len(runes)+maxPerChunk-1)/maxPerChunk*8+len(runes)*size) +	b := 0 + +	for len(runes) > maxPerChunk { +		b += glyphEltHeaderBytes(buf[b:], maxPerChunk, 0, 0) +		b += glyphListBytes(buf[b:], runes[:maxPerChunk], size) +		runes = runes[maxPerChunk:] +	} +	if len(runes) > 0 { +		b += glyphEltHeaderBytes(buf[b:], byte(len(runes)), destX, destY) +		b += glyphListBytes(buf[b:], runes, size) +	} + +	switch size { +	default: +		return render.CompositeGlyphs8(c, op, src, dst, maskFormat, glyphset, +			srcX, srcY, buf) +	case 2: +		return render.CompositeGlyphs16(c, op, src, dst, maskFormat, glyphset, +			srcX, srcY, buf) +	case 4: +		return render.CompositeGlyphs32(c, op, src, dst, maskFormat, glyphset, +			srcX, srcY, buf) +	} +} + +// --- +  func main() {  	X, err := xgb.NewConn()  	if err != nil { @@ -119,6 +218,79 @@ func main() {  	// ...or just scan through pformats.Formats and look for matches, which is  	// what XRenderFindStandardFormat in Xlib does as well as exp/shiny. +	f, err := freetype.ParseFont(goregular.TTF) +	if err != nil { +		log.Fatalln(err) +	} + +	c := freetype.NewContext() +	c.SetDPI(96) // TODO: Take this from the screen or monitor. +	c.SetFont(f) +	c.SetFontSize(9) +	c.SetSrc(image.White) +	c.SetHinting(font.HintingFull) +	// TODO: Seems like we want to use NRGBA. Or RGBA if the A is always 1. +	// Or implement our own image.Image for direct gamma-corrected RGB! +	nrgb := image.NewRGBA(image.Rect(0, 0, 36, 36)) +	c.SetClip(nrgb.Bounds()) +	c.SetDst(nrgb) + +	bounds := f.Bounds(c.PointToFixed(9 /* FIXME: Duplication. */)) +	log.Println("+%v", bounds) + +	// FIXME: Duplication. +	opts := truetype.Options{ +		Size: 9, +		DPI:  96, +	} + +	// TODO: Seems this satisfies the sfnt interface, DrawString just adds +	// kerning and DrawMask on top. +	face := truetype.NewFace(f, &opts) +	_ = face + +	// TODO: Figure out a way to load glyphs into XRender. +	var rgbFormat render.Pictformat +	for _, pf := range pformats.Formats { +		// Hopefully. Might want to check ARGB/BGRA. +		if pf.Depth == 32 && pf.Direct.AlphaMask != 0 { +			rgbFormat = pf.Id +			log.Printf("%+v\n", pf) +			break +		} +	} + +	gsid, err := render.NewGlyphsetId(X) +	if err != nil { +		log.Fatalln(err) +	} + +	// NOTE: A depth of 24 will not work, the server always rejects it. +	_ = render.CreateGlyphSet(X, gsid, rgbFormat) + +	for r := rune(32); r < 128; r++ { +		for i := 0; i < len(nrgb.Pix); i++ { +			nrgb.Pix[i] = 0 +		} + +		advance, err := c.DrawString(string(r), fixed.P(18, 18)) +		_, _ = advance, err +		if err != nil { +			log.Println("skip") +			continue +		} + +		_ = render.AddGlyphs(X, gsid, 1, []uint32{uint32(r)}, +			[]render.Glyphinfo{{ +				Width:  uint16(36), +				Height: uint16(36), +				X:      int16(18), +				Y:      int16(18), +				XOff:   int16(18), +				YOff:   0, +			}}, []byte(nrgb.Pix)) +	} +  	pid, err := render.NewPictureId(X)  	if err != nil {  		log.Fatalln(err) @@ -133,9 +305,22 @@ func main() {  		log.Fatalln(err)  	} +	whiteid, err := render.NewPictureId(X) +	if err != nil { +		log.Fatalln(err) +	} + +	_ = render.CreateSolidFill(X, whiteid, render.Color{ +		Red:   0xffff, +		Green: 0xffff, +		Blue:  0xffff, +		Alpha: 0xffff, +	}) +  	var from, to render.Color +	var start, end uint32  	recolor := func() { -		start := rand.Uint32() & 0xffffff +		start = rand.Uint32() & 0xffffff  		from = render.Color{  			Red:   0x101 * uint16((start>>16)&0xff),  			Green: 0x101 * uint16((start>>8)&0xff), @@ -143,7 +328,7 @@ func main() {  			Alpha: 0xffff,  		} -		end := rand.Uint32() & 0xffffff +		end = rand.Uint32() & 0xffffff  		to = render.Color{  			Red:   0x101 * uint16((end>>16)&0xff),  			Green: 0x101 * uint16((end>>8)&0xff), @@ -169,6 +354,10 @@ func main() {  			0, 0, 0, 0, 50, 50, w-100, h-100)  		_ = render.FreePicture(X, gid) + +		_ = compositeString(X, render.PictOpOver, whiteid, pid, +			0 /* TODO: mask Pictureformat? */, gsid, 0, 0, 100, 100, +			fmt.Sprintf("%#06x - %#06x", start, end))  	}  	for { | 
