~eliasnaur/gio

0061c73a89aa026b9c0dbe326c20ae8e08c0ca7b — Elias Naur 2 years ago 29993af
ui: split OpImage into OpImage and OpDraw

In preparation for an OpColor (and future OpGradient and similar).

Label and Editor no longer take an explicit source image. They
draw with the current image.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
M ui/app/internal/gpu/gpu.go => ui/app/internal/gpu/gpu.go +18 -9
@@ 66,6 66,10 @@ type drawOps struct {
	zimageOps   []imageOp
	pathOps     []*pathOp
	pathOpCache []pathOp

	// Current OpImage image and rect, if any.
	img     image.Image
	imgRect image.Rectangle
}

type drawState struct {


@@ 692,13 696,18 @@ loop:
		case ops.TypeImage:
			var op gdraw.OpImage
			op.Decode(data, r.Refs)
			d.img = op.Img
			d.imgRect = op.Rect
		case ops.TypeDraw:
			var op gdraw.OpDraw
			op.Decode(data, r.Refs)
			off := t.Transform(f32.Point{})
			clip := clip.Intersect(op.Rect.Add(off))
			if clip.Empty() {
				continue
			}
			bounds := boundRectF(clip)
			mat := materialFor(d.cache, op, off, bounds)
			mat := d.materialFor(d.cache, op.Rect, off, bounds)
			if bounds.Min == (image.Point{}) && bounds.Max == d.viewport && mat.opaque && mat.material == materialColor {
				// The image is a uniform opaque color and takes up the whole screen.
				// Scrap images up to and including this image and set clear color.


@@ 745,16 754,16 @@ func expandPathOp(p *pathOp, clip image.Rectangle) {
	}
}

func materialFor(cache *resourceCache, op gdraw.OpImage, off f32.Point, clip image.Rectangle) material {
func (d *drawOps) materialFor(cache *resourceCache, rect f32.Rectangle, off f32.Point, clip image.Rectangle) material {
	var m material
	if uniform, ok := op.Src.(*image.Uniform); ok {
	if uniform, ok := d.img.(*image.Uniform); ok {
		m.material = materialColor
		m.color = gamma(uniform.RGBA())
		m.opaque = m.color[3] == 1.0
	} else {
		m.material = materialTexture
		dr := boundRectF(op.Rect.Add(off))
		sr := op.SrcRect
		dr := boundRectF(rect.Add(off))
		sr := d.imgRect
		if dx := dr.Dx(); dx != 0 {
			// Don't clip 1 px width sources.
			if sdx := sr.Dx(); sdx > 1 {


@@ 769,16 778,16 @@ func materialFor(cache *resourceCache, op gdraw.OpImage, off f32.Point, clip ima
				sr.Max.Y -= ((dr.Max.Y-clip.Max.Y)*sdy + dy/2) / dy
			}
		}
		tex, exists := cache.get(op.Src)
		tex, exists := cache.get(d.img)
		if !exists {
			t := &texture{
				src: op.Src,
				src: d.img,
			}
			cache.put(op.Src, t)
			cache.put(d.img, t)
			tex = t
		}
		m.texture = tex.(*texture)
		m.uvScale, m.uvOffset = texSpaceTransform(sr, op.Src.Bounds().Size())
		m.uvScale, m.uvOffset = texSpaceTransform(sr, d.img.Bounds().Size())
	}
	return m
}

M ui/draw/draw.go => ui/draw/draw.go +51 -32
@@ 14,58 14,77 @@ import (
)

type OpImage struct {
	Rect    f32.Rectangle
	Src     image.Image
	SrcRect image.Rectangle
	Img  image.Image
	Rect image.Rectangle
}

type OpDraw struct {
	Rect f32.Rectangle
}

func (i OpImage) Add(o *ui.Ops) {
	data := make([]byte, ops.TypeImageLen)
	data[0] = byte(ops.TypeImage)
	bo := binary.LittleEndian
	ref := o.Ref(i.Src)
	ref := o.Ref(i.Img)
	bo.PutUint32(data[1:], uint32(ref))
	bo.PutUint32(data[5:], math.Float32bits(i.Rect.Min.X))
	bo.PutUint32(data[9:], math.Float32bits(i.Rect.Min.Y))
	bo.PutUint32(data[13:], math.Float32bits(i.Rect.Max.X))
	bo.PutUint32(data[17:], math.Float32bits(i.Rect.Max.Y))
	bo.PutUint32(data[21:], uint32(i.SrcRect.Min.X))
	bo.PutUint32(data[25:], uint32(i.SrcRect.Min.Y))
	bo.PutUint32(data[29:], uint32(i.SrcRect.Max.X))
	bo.PutUint32(data[33:], uint32(i.SrcRect.Max.Y))
	bo.PutUint32(data[5:], uint32(i.Rect.Min.X))
	bo.PutUint32(data[9:], uint32(i.Rect.Min.Y))
	bo.PutUint32(data[13:], uint32(i.Rect.Max.X))
	bo.PutUint32(data[17:], uint32(i.Rect.Max.Y))
	o.Write(data)
}

func (i *OpImage) Decode(d []byte, refs []interface{}) {
func (i *OpImage) Decode(data []byte, refs []interface{}) {
	bo := binary.LittleEndian
	if ops.OpType(d[0]) != ops.TypeImage {
	if ops.OpType(data[0]) != ops.TypeImage {
		panic("invalid op")
	}
	ref := int(bo.Uint32(d[1:]))
	r := f32.Rectangle{
		Min: f32.Point{
			X: math.Float32frombits(bo.Uint32(d[5:])),
			Y: math.Float32frombits(bo.Uint32(d[9:])),
		},
		Max: f32.Point{
			X: math.Float32frombits(bo.Uint32(d[13:])),
			Y: math.Float32frombits(bo.Uint32(d[17:])),
		},
	}
	ref := int(bo.Uint32(data[1:]))
	sr := image.Rectangle{
		Min: image.Point{
			X: int(bo.Uint32(d[21:])),
			Y: int(bo.Uint32(d[25:])),
			X: int(bo.Uint32(data[5:])),
			Y: int(bo.Uint32(data[9:])),
		},
		Max: image.Point{
			X: int(bo.Uint32(d[29:])),
			Y: int(bo.Uint32(d[33:])),
			X: int(bo.Uint32(data[13:])),
			Y: int(bo.Uint32(data[17:])),
		},
	}
	*i = OpImage{
		Rect:    r,
		Src:     refs[ref].(image.Image),
		SrcRect: sr,
		Img:  refs[ref].(image.Image),
		Rect: sr,
	}
}

func (d OpDraw) Add(o *ui.Ops) {
	data := make([]byte, ops.TypeDrawLen)
	data[0] = byte(ops.TypeDraw)
	bo := binary.LittleEndian
	bo.PutUint32(data[1:], math.Float32bits(d.Rect.Min.X))
	bo.PutUint32(data[5:], math.Float32bits(d.Rect.Min.Y))
	bo.PutUint32(data[9:], math.Float32bits(d.Rect.Max.X))
	bo.PutUint32(data[13:], math.Float32bits(d.Rect.Max.Y))
	o.Write(data)
}

func (d *OpDraw) Decode(data []byte, refs []interface{}) {
	bo := binary.LittleEndian
	if ops.OpType(data[0]) != ops.TypeDraw {
		panic("invalid op")
	}
	r := f32.Rectangle{
		Min: f32.Point{
			X: math.Float32frombits(bo.Uint32(data[1:])),
			Y: math.Float32frombits(bo.Uint32(data[5:])),
		},
		Max: f32.Point{
			X: math.Float32frombits(bo.Uint32(data[9:])),
			Y: math.Float32frombits(bo.Uint32(data[13:])),
		},
	}
	*d = OpDraw{
		Rect: r,
	}
}


M ui/internal/ops/ops.go => ui/internal/ops/ops.go +4 -1
@@ 28,6 28,7 @@ const (
	TypeRedraw
	TypeClip
	TypeImage
	TypeDraw
	TypePointerHandler
	TypeKeyHandler
	TypeHideInput


@@ 42,7 43,8 @@ const (
	TypeLayerLen          = 1
	TypeRedrawLen         = 1 + 8
	TypeClipLen           = 1 + 4
	TypeImageLen          = 1 + 4 + 4*4 + 4*4
	TypeImageLen          = 1 + 4 + 4*4
	TypeDrawLen           = 1 + 4*4
	TypePointerHandlerLen = 1 + 4 + 4 + 1
	TypeKeyHandlerLen     = 1 + 4 + 1
	TypeHideInputLen      = 1


@@ 58,6 60,7 @@ var typeLengths = [...]int{
	TypeRedrawLen,
	TypeClipLen,
	TypeImageLen,
	TypeDrawLen,
	TypePointerHandlerLen,
	TypeKeyHandlerLen,
	TypeHideInputLen,

M ui/text/editor.go => ui/text/editor.go +2 -4
@@ 20,7 20,6 @@ import (
)

type Editor struct {
	Src        image.Image
	Face       Face
	Alignment  Alignment
	SingleLine bool


@@ 175,7 174,7 @@ func (e *Editor) Layout(ops *ui.Ops, cs layout.Constraints) layout.Dimens {
		ops.Begin()
		ui.OpTransform{Transform: ui.Offset(lineOff)}.Add(ops)
		draw.OpClip{Path: path}.Add(ops)
		draw.OpImage{Rect: toRectF(clip).Sub(lineOff), Src: e.Src, SrcRect: e.Src.Bounds()}.Add(ops)
		draw.OpDraw{Rect: toRectF(clip).Sub(lineOff)}.Add(ops)
		ops.End().Add(ops)
	}
	if e.focused {


@@ 199,8 198,7 @@ func (e *Editor) Layout(ops *ui.Ops, cs layout.Constraints) layout.Dimens {
			})
			carRect = clip.Intersect(carRect)
			if !carRect.Empty() {
				img := draw.OpImage{Src: e.Src, Rect: toRectF(carRect), SrcRect: e.Src.Bounds()}
				img.Add(ops)
				draw.OpDraw{Rect: toRectF(carRect)}.Add(ops)
			}
		}
		if blinking {

M ui/text/label.go => ui/text/label.go +1 -2
@@ 17,7 17,6 @@ import (

type Label struct {
	Face      Face
	Src       image.Image
	Alignment Alignment
	Text      string



@@ 106,7 105,7 @@ func (l Label) Layout(ops *ui.Ops, cs layout.Constraints) layout.Dimens {
		ops.Begin()
		ui.OpTransform{Transform: ui.Offset(off)}.Add(ops)
		draw.OpClip{Path: path}.Add(ops)
		draw.OpImage{Rect: lclip, Src: l.Src, SrcRect: l.Src.Bounds()}.Add(ops)
		draw.OpDraw{Rect: lclip}.Add(ops)
		ops.End().Add(ops)
	}
	return dims

M ui/widget/image.go => ui/widget/image.go +2 -1
@@ 27,6 27,7 @@ func (im Image) Layout(ops *ui.Ops, cs layout.Constraints) layout.Dimens {
	dr := f32.Rectangle{
		Max: f32.Point{X: float32(d.X), Y: float32(d.Y)},
	}
	draw.OpImage{Rect: dr, Src: im.Src, SrcRect: im.Rect}.Add(ops)
	draw.OpImage{Img: im.Src, Rect: im.Rect}.Add(ops)
	draw.OpDraw{Rect: dr}.Add(ops)
	return layout.Dimens{Size: d, Baseline: d.Y}
}