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
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
}
+51 -32
View File
@@ -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,
}
}
+4 -1
View File
@@ -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,
+2 -4
View File
@@ -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 {
+1 -2
View File
@@ -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
+2 -1
View File
@@ -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}
}