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>
This commit is contained in:
Elias Naur
2019-06-01 12:43:33 +02:00
parent 29993af408
commit 0061c73a89
6 changed files with 78 additions and 49 deletions
+18 -9
View File
@@ -66,6 +66,10 @@ type drawOps struct {
zimageOps []imageOp zimageOps []imageOp
pathOps []*pathOp pathOps []*pathOp
pathOpCache []pathOp pathOpCache []pathOp
// Current OpImage image and rect, if any.
img image.Image
imgRect image.Rectangle
} }
type drawState struct { type drawState struct {
@@ -692,13 +696,18 @@ loop:
case ops.TypeImage: case ops.TypeImage:
var op gdraw.OpImage var op gdraw.OpImage
op.Decode(data, r.Refs) 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{}) off := t.Transform(f32.Point{})
clip := clip.Intersect(op.Rect.Add(off)) clip := clip.Intersect(op.Rect.Add(off))
if clip.Empty() { if clip.Empty() {
continue continue
} }
bounds := boundRectF(clip) 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 { 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. // 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. // 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 var m material
if uniform, ok := op.Src.(*image.Uniform); ok { if uniform, ok := d.img.(*image.Uniform); ok {
m.material = materialColor m.material = materialColor
m.color = gamma(uniform.RGBA()) m.color = gamma(uniform.RGBA())
m.opaque = m.color[3] == 1.0 m.opaque = m.color[3] == 1.0
} else { } else {
m.material = materialTexture m.material = materialTexture
dr := boundRectF(op.Rect.Add(off)) dr := boundRectF(rect.Add(off))
sr := op.SrcRect sr := d.imgRect
if dx := dr.Dx(); dx != 0 { if dx := dr.Dx(); dx != 0 {
// Don't clip 1 px width sources. // Don't clip 1 px width sources.
if sdx := sr.Dx(); sdx > 1 { 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 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 { if !exists {
t := &texture{ t := &texture{
src: op.Src, src: d.img,
} }
cache.put(op.Src, t) cache.put(d.img, t)
tex = t tex = t
} }
m.texture = tex.(*texture) 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 return m
} }
+51 -32
View File
@@ -14,58 +14,77 @@ import (
) )
type OpImage struct { type OpImage struct {
Rect f32.Rectangle Img image.Image
Src image.Image Rect image.Rectangle
SrcRect image.Rectangle }
type OpDraw struct {
Rect f32.Rectangle
} }
func (i OpImage) Add(o *ui.Ops) { func (i OpImage) Add(o *ui.Ops) {
data := make([]byte, ops.TypeImageLen) data := make([]byte, ops.TypeImageLen)
data[0] = byte(ops.TypeImage) data[0] = byte(ops.TypeImage)
bo := binary.LittleEndian bo := binary.LittleEndian
ref := o.Ref(i.Src) ref := o.Ref(i.Img)
bo.PutUint32(data[1:], uint32(ref)) bo.PutUint32(data[1:], uint32(ref))
bo.PutUint32(data[5:], math.Float32bits(i.Rect.Min.X)) bo.PutUint32(data[5:], uint32(i.Rect.Min.X))
bo.PutUint32(data[9:], math.Float32bits(i.Rect.Min.Y)) bo.PutUint32(data[9:], uint32(i.Rect.Min.Y))
bo.PutUint32(data[13:], math.Float32bits(i.Rect.Max.X)) bo.PutUint32(data[13:], uint32(i.Rect.Max.X))
bo.PutUint32(data[17:], math.Float32bits(i.Rect.Max.Y)) bo.PutUint32(data[17:], uint32(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))
o.Write(data) o.Write(data)
} }
func (i *OpImage) Decode(d []byte, refs []interface{}) { func (i *OpImage) Decode(data []byte, refs []interface{}) {
bo := binary.LittleEndian bo := binary.LittleEndian
if ops.OpType(d[0]) != ops.TypeImage { if ops.OpType(data[0]) != ops.TypeImage {
panic("invalid op") panic("invalid op")
} }
ref := int(bo.Uint32(d[1:])) ref := int(bo.Uint32(data[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:])),
},
}
sr := image.Rectangle{ sr := image.Rectangle{
Min: image.Point{ Min: image.Point{
X: int(bo.Uint32(d[21:])), X: int(bo.Uint32(data[5:])),
Y: int(bo.Uint32(d[25:])), Y: int(bo.Uint32(data[9:])),
}, },
Max: image.Point{ Max: image.Point{
X: int(bo.Uint32(d[29:])), X: int(bo.Uint32(data[13:])),
Y: int(bo.Uint32(d[33:])), Y: int(bo.Uint32(data[17:])),
}, },
} }
*i = OpImage{ *i = OpImage{
Rect: r, Img: refs[ref].(image.Image),
Src: refs[ref].(image.Image), Rect: sr,
SrcRect: 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,
} }
} }
+4 -1
View File
@@ -28,6 +28,7 @@ const (
TypeRedraw TypeRedraw
TypeClip TypeClip
TypeImage TypeImage
TypeDraw
TypePointerHandler TypePointerHandler
TypeKeyHandler TypeKeyHandler
TypeHideInput TypeHideInput
@@ -42,7 +43,8 @@ const (
TypeLayerLen = 1 TypeLayerLen = 1
TypeRedrawLen = 1 + 8 TypeRedrawLen = 1 + 8
TypeClipLen = 1 + 4 TypeClipLen = 1 + 4
TypeImageLen = 1 + 4 + 4*4 + 4*4 TypeImageLen = 1 + 4 + 4*4
TypeDrawLen = 1 + 4*4
TypePointerHandlerLen = 1 + 4 + 4 + 1 TypePointerHandlerLen = 1 + 4 + 4 + 1
TypeKeyHandlerLen = 1 + 4 + 1 TypeKeyHandlerLen = 1 + 4 + 1
TypeHideInputLen = 1 TypeHideInputLen = 1
@@ -58,6 +60,7 @@ var typeLengths = [...]int{
TypeRedrawLen, TypeRedrawLen,
TypeClipLen, TypeClipLen,
TypeImageLen, TypeImageLen,
TypeDrawLen,
TypePointerHandlerLen, TypePointerHandlerLen,
TypeKeyHandlerLen, TypeKeyHandlerLen,
TypeHideInputLen, TypeHideInputLen,
+2 -4
View File
@@ -20,7 +20,6 @@ import (
) )
type Editor struct { type Editor struct {
Src image.Image
Face Face Face Face
Alignment Alignment Alignment Alignment
SingleLine bool SingleLine bool
@@ -175,7 +174,7 @@ func (e *Editor) Layout(ops *ui.Ops, cs layout.Constraints) layout.Dimens {
ops.Begin() ops.Begin()
ui.OpTransform{Transform: ui.Offset(lineOff)}.Add(ops) ui.OpTransform{Transform: ui.Offset(lineOff)}.Add(ops)
draw.OpClip{Path: path}.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) ops.End().Add(ops)
} }
if e.focused { if e.focused {
@@ -199,8 +198,7 @@ func (e *Editor) Layout(ops *ui.Ops, cs layout.Constraints) layout.Dimens {
}) })
carRect = clip.Intersect(carRect) carRect = clip.Intersect(carRect)
if !carRect.Empty() { if !carRect.Empty() {
img := draw.OpImage{Src: e.Src, Rect: toRectF(carRect), SrcRect: e.Src.Bounds()} draw.OpDraw{Rect: toRectF(carRect)}.Add(ops)
img.Add(ops)
} }
} }
if blinking { if blinking {
+1 -2
View File
@@ -17,7 +17,6 @@ import (
type Label struct { type Label struct {
Face Face Face Face
Src image.Image
Alignment Alignment Alignment Alignment
Text string Text string
@@ -106,7 +105,7 @@ func (l Label) Layout(ops *ui.Ops, cs layout.Constraints) layout.Dimens {
ops.Begin() ops.Begin()
ui.OpTransform{Transform: ui.Offset(off)}.Add(ops) ui.OpTransform{Transform: ui.Offset(off)}.Add(ops)
draw.OpClip{Path: path}.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) ops.End().Add(ops)
} }
return dims return dims
+2 -1
View File
@@ -27,6 +27,7 @@ func (im Image) Layout(ops *ui.Ops, cs layout.Constraints) layout.Dimens {
dr := f32.Rectangle{ dr := f32.Rectangle{
Max: f32.Point{X: float32(d.X), Y: float32(d.Y)}, 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} return layout.Dimens{Size: d, Baseline: d.Y}
} }