forked from joejulian/gio
gpu: add linear gradient
Signed-off-by: Egon Elbre <egonelbre@gmail.com>
This commit is contained in:
+168
-41
@@ -79,6 +79,12 @@ type drawState struct {
|
||||
image imageOpData
|
||||
// Current paint.ColorOp, if any.
|
||||
color color.RGBA
|
||||
|
||||
// Current paint.LinearGradientOp.
|
||||
stop1 f32.Point
|
||||
stop2 f32.Point
|
||||
color1 color.RGBA
|
||||
color2 color.RGBA
|
||||
}
|
||||
|
||||
type pathOp struct {
|
||||
@@ -109,6 +115,9 @@ type material struct {
|
||||
opaque bool
|
||||
// For materialTypeColor.
|
||||
color f32color.RGBA
|
||||
// For materialTypeLinearGradient.
|
||||
color1 f32color.RGBA
|
||||
color2 f32color.RGBA
|
||||
// For materialTypeTexture.
|
||||
texture *texture
|
||||
uvTrans f32.Affine2D
|
||||
@@ -127,6 +136,13 @@ type imageOpData struct {
|
||||
handle interface{}
|
||||
}
|
||||
|
||||
type linearGradientOpData struct {
|
||||
stop1 f32.Point
|
||||
color1 color.RGBA
|
||||
stop2 f32.Point
|
||||
color2 color.RGBA
|
||||
}
|
||||
|
||||
func (op *clipOp) decode(data []byte) {
|
||||
if opconst.OpType(data[0]) != opconst.TypeClip {
|
||||
panic("invalid op")
|
||||
@@ -184,6 +200,35 @@ func decodeColorOp(data []byte) color.RGBA {
|
||||
}
|
||||
}
|
||||
|
||||
func decodeLinearGradientOp(data []byte) linearGradientOpData {
|
||||
if opconst.OpType(data[0]) != opconst.TypeLinearGradient {
|
||||
panic("invalid op")
|
||||
}
|
||||
bo := binary.LittleEndian
|
||||
return linearGradientOpData{
|
||||
stop1: f32.Point{
|
||||
X: math.Float32frombits(bo.Uint32(data[1:])),
|
||||
Y: math.Float32frombits(bo.Uint32(data[5:])),
|
||||
},
|
||||
stop2: f32.Point{
|
||||
X: math.Float32frombits(bo.Uint32(data[9:])),
|
||||
Y: math.Float32frombits(bo.Uint32(data[13:])),
|
||||
},
|
||||
color1: color.RGBA{
|
||||
R: data[17+0],
|
||||
G: data[17+1],
|
||||
B: data[17+2],
|
||||
A: data[17+3],
|
||||
},
|
||||
color2: color.RGBA{
|
||||
R: data[21+0],
|
||||
G: data[21+1],
|
||||
B: data[21+2],
|
||||
A: data[21+3],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func decodePaintOp(data []byte) paint.PaintOp {
|
||||
bo := binary.LittleEndian
|
||||
if opconst.OpType(data[0]) != opconst.TypePaint {
|
||||
@@ -216,13 +261,14 @@ type texture struct {
|
||||
}
|
||||
|
||||
type blitter struct {
|
||||
ctx backend.Device
|
||||
viewport image.Point
|
||||
prog [2]*program
|
||||
layout backend.InputLayout
|
||||
colUniforms *blitColUniforms
|
||||
texUniforms *blitTexUniforms
|
||||
quadVerts backend.Buffer
|
||||
ctx backend.Device
|
||||
viewport image.Point
|
||||
prog [3]*program
|
||||
layout backend.InputLayout
|
||||
colUniforms *blitColUniforms
|
||||
texUniforms *blitTexUniforms
|
||||
linearGradientUniforms *blitLinearGradientUniforms
|
||||
quadVerts backend.Buffer
|
||||
}
|
||||
|
||||
type blitColUniforms struct {
|
||||
@@ -242,6 +288,16 @@ type blitTexUniforms struct {
|
||||
}
|
||||
}
|
||||
|
||||
type blitLinearGradientUniforms struct {
|
||||
vert struct {
|
||||
blitUniforms
|
||||
_ [12]byte // Padding to a multiple of 16.
|
||||
}
|
||||
frag struct {
|
||||
gradientUniforms
|
||||
}
|
||||
}
|
||||
|
||||
type uniformBuffer struct {
|
||||
buf backend.Buffer
|
||||
ptr []byte
|
||||
@@ -264,6 +320,11 @@ type colorUniforms struct {
|
||||
color f32color.RGBA
|
||||
}
|
||||
|
||||
type gradientUniforms struct {
|
||||
color1 f32color.RGBA
|
||||
color2 f32color.RGBA
|
||||
}
|
||||
|
||||
type materialType uint8
|
||||
|
||||
const (
|
||||
@@ -274,6 +335,7 @@ const (
|
||||
|
||||
const (
|
||||
materialColor materialType = iota
|
||||
materialLinearGradient
|
||||
materialTexture
|
||||
)
|
||||
|
||||
@@ -447,8 +509,11 @@ func newBlitter(ctx backend.Device) *blitter {
|
||||
}
|
||||
b.colUniforms = new(blitColUniforms)
|
||||
b.texUniforms = new(blitTexUniforms)
|
||||
b.linearGradientUniforms = new(blitLinearGradientUniforms)
|
||||
prog, layout, err := createColorPrograms(ctx, shader_blit_vert, shader_blit_frag,
|
||||
[2]interface{}{&b.colUniforms.vert, &b.texUniforms.vert}, [2]interface{}{&b.colUniforms.frag, nil})
|
||||
[3]interface{}{&b.colUniforms.vert, &b.linearGradientUniforms.vert, &b.texUniforms.vert},
|
||||
[3]interface{}{&b.colUniforms.frag, &b.linearGradientUniforms.frag, nil},
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -465,37 +530,59 @@ func (b *blitter) release() {
|
||||
b.layout.Release()
|
||||
}
|
||||
|
||||
func createColorPrograms(b backend.Device, vsSrc backend.ShaderSources, fsSrc [2]backend.ShaderSources, vertUniforms, fragUniforms [2]interface{}) ([2]*program, backend.InputLayout, error) {
|
||||
var progs [2]*program
|
||||
prog, err := b.NewProgram(vsSrc, fsSrc[materialTexture])
|
||||
if err != nil {
|
||||
return progs, nil, err
|
||||
func createColorPrograms(b backend.Device, vsSrc backend.ShaderSources, fsSrc [3]backend.ShaderSources, vertUniforms, fragUniforms [3]interface{}) ([3]*program, backend.InputLayout, error) {
|
||||
var progs [3]*program
|
||||
{
|
||||
prog, err := b.NewProgram(vsSrc, fsSrc[materialTexture])
|
||||
if err != nil {
|
||||
return progs, nil, err
|
||||
}
|
||||
var vertBuffer, fragBuffer *uniformBuffer
|
||||
if u := vertUniforms[materialTexture]; u != nil {
|
||||
vertBuffer = newUniformBuffer(b, u)
|
||||
prog.SetVertexUniforms(vertBuffer.buf)
|
||||
}
|
||||
if u := fragUniforms[materialTexture]; u != nil {
|
||||
fragBuffer = newUniformBuffer(b, u)
|
||||
prog.SetFragmentUniforms(fragBuffer.buf)
|
||||
}
|
||||
progs[materialTexture] = newProgram(prog, vertBuffer, fragBuffer)
|
||||
}
|
||||
var vertBuffer *uniformBuffer
|
||||
if u := vertUniforms[materialTexture]; u != nil {
|
||||
vertBuffer = newUniformBuffer(b, u)
|
||||
prog.SetVertexUniforms(vertBuffer.buf)
|
||||
{
|
||||
var vertBuffer, fragBuffer *uniformBuffer
|
||||
prog, err := b.NewProgram(vsSrc, fsSrc[materialColor])
|
||||
if err != nil {
|
||||
progs[materialTexture].Release()
|
||||
return progs, nil, err
|
||||
}
|
||||
if u := vertUniforms[materialColor]; u != nil {
|
||||
vertBuffer = newUniformBuffer(b, u)
|
||||
prog.SetVertexUniforms(vertBuffer.buf)
|
||||
}
|
||||
if u := fragUniforms[materialColor]; u != nil {
|
||||
fragBuffer = newUniformBuffer(b, u)
|
||||
prog.SetFragmentUniforms(fragBuffer.buf)
|
||||
}
|
||||
progs[materialColor] = newProgram(prog, vertBuffer, fragBuffer)
|
||||
}
|
||||
var fragBuffer *uniformBuffer
|
||||
if u := fragUniforms[materialTexture]; u != nil {
|
||||
fragBuffer = newUniformBuffer(b, u)
|
||||
prog.SetFragmentUniforms(fragBuffer.buf)
|
||||
{
|
||||
var vertBuffer, fragBuffer *uniformBuffer
|
||||
prog, err := b.NewProgram(vsSrc, fsSrc[materialLinearGradient])
|
||||
if err != nil {
|
||||
progs[materialTexture].Release()
|
||||
progs[materialColor].Release()
|
||||
return progs, nil, err
|
||||
}
|
||||
if u := vertUniforms[materialLinearGradient]; u != nil {
|
||||
vertBuffer = newUniformBuffer(b, u)
|
||||
prog.SetVertexUniforms(vertBuffer.buf)
|
||||
}
|
||||
if u := fragUniforms[materialLinearGradient]; u != nil {
|
||||
fragBuffer = newUniformBuffer(b, u)
|
||||
prog.SetFragmentUniforms(fragBuffer.buf)
|
||||
}
|
||||
progs[materialLinearGradient] = newProgram(prog, vertBuffer, fragBuffer)
|
||||
}
|
||||
progs[materialTexture] = newProgram(prog, vertBuffer, fragBuffer)
|
||||
prog, err = b.NewProgram(vsSrc, fsSrc[materialColor])
|
||||
if err != nil {
|
||||
progs[materialTexture].Release()
|
||||
return progs, nil, err
|
||||
}
|
||||
if u := vertUniforms[materialColor]; u != nil {
|
||||
vertBuffer = newUniformBuffer(b, u)
|
||||
prog.SetVertexUniforms(vertBuffer.buf)
|
||||
}
|
||||
if u := fragUniforms[materialColor]; u != nil {
|
||||
fragBuffer = newUniformBuffer(b, u)
|
||||
prog.SetFragmentUniforms(fragBuffer.buf)
|
||||
}
|
||||
progs[materialColor] = newProgram(prog, vertBuffer, fragBuffer)
|
||||
layout, err := b.NewInputLayout(vsSrc, []backend.InputDesc{
|
||||
{Type: backend.DataTypeFloat, Size: 2, Offset: 0},
|
||||
{Type: backend.DataTypeFloat, Size: 2, Offset: 4 * 2},
|
||||
@@ -503,6 +590,7 @@ func createColorPrograms(b backend.Device, vsSrc backend.ShaderSources, fsSrc [2
|
||||
if err != nil {
|
||||
progs[materialTexture].Release()
|
||||
progs[materialColor].Release()
|
||||
progs[materialLinearGradient].Release()
|
||||
return progs, nil, err
|
||||
}
|
||||
return progs, layout, nil
|
||||
@@ -768,6 +856,13 @@ loop:
|
||||
case opconst.TypeColor:
|
||||
state.matType = materialColor
|
||||
state.color = decodeColorOp(encOp.Data)
|
||||
case opconst.TypeLinearGradient:
|
||||
state.matType = materialLinearGradient
|
||||
op := decodeLinearGradientOp(encOp.Data)
|
||||
state.stop1 = op.stop1
|
||||
state.stop2 = op.stop2
|
||||
state.color1 = op.color1
|
||||
state.color2 = op.color2
|
||||
case opconst.TypeImage:
|
||||
state.matType = materialTexture
|
||||
state.image = decodeImageOp(encOp.Data, encOp.Refs)
|
||||
@@ -794,7 +889,7 @@ loop:
|
||||
bounds := boundRectF(clip)
|
||||
mat := state.materialFor(d.cache, bnd, off, partialTrans, bounds)
|
||||
|
||||
if bounds.Min == (image.Point{}) && bounds.Max == d.viewport && state.rect && mat.opaque && mat.material == materialColor {
|
||||
if bounds.Min == (image.Point{}) && bounds.Max == d.viewport && state.rect && 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.
|
||||
d.zimageOps = d.zimageOps[:0]
|
||||
@@ -857,6 +952,14 @@ func (d *drawState) materialFor(cache *resourceCache, rect f32.Rectangle, off f3
|
||||
m.material = materialColor
|
||||
m.color = f32color.RGBAFromSRGB(d.color)
|
||||
m.opaque = m.color.A == 1.0
|
||||
case materialLinearGradient:
|
||||
m.material = materialLinearGradient
|
||||
|
||||
m.color1 = f32color.RGBAFromSRGB(d.color1)
|
||||
m.color2 = f32color.RGBAFromSRGB(d.color2)
|
||||
m.opaque = m.color1.A == 1.0 && m.color2.A == 1.0
|
||||
|
||||
m.uvTrans = trans.Mul(gradientSpaceTransform(clip, off, d.stop1, d.stop2))
|
||||
case materialTexture:
|
||||
m.material = materialTexture
|
||||
dr := boundRectF(rect.Add(off))
|
||||
@@ -905,7 +1008,7 @@ func (r *renderer) drawZOps(ops []imageOp) {
|
||||
}
|
||||
drc := img.clip
|
||||
scale, off := clipSpaceTransform(drc, r.blitter.viewport)
|
||||
r.blitter.blit(img.z, m.material, m.color, scale, off, m.uvTrans)
|
||||
r.blitter.blit(img.z, m.material, m.color, m.color1, m.color2, scale, off, m.uvTrans)
|
||||
}
|
||||
r.ctx.SetDepthTest(false)
|
||||
}
|
||||
@@ -929,7 +1032,7 @@ func (r *renderer) drawOps(ops []imageOp) {
|
||||
var fbo stencilFBO
|
||||
switch img.clipType {
|
||||
case clipTypeNone:
|
||||
r.blitter.blit(img.z, m.material, m.color, scale, off, m.uvTrans)
|
||||
r.blitter.blit(img.z, m.material, m.color, m.color1, m.color2, scale, off, m.uvTrans)
|
||||
continue
|
||||
case clipTypePath:
|
||||
fbo = r.pather.stenciler.cover(img.place.Idx)
|
||||
@@ -945,13 +1048,13 @@ func (r *renderer) drawOps(ops []imageOp) {
|
||||
Max: img.place.Pos.Add(drc.Size()),
|
||||
}
|
||||
coverScale, coverOff := texSpaceTransform(toRectF(uv), fbo.size)
|
||||
r.pather.cover(img.z, m.material, m.color, scale, off, m.uvTrans, coverScale, coverOff)
|
||||
r.pather.cover(img.z, m.material, m.color, m.color1, m.color2, scale, off, m.uvTrans, coverScale, coverOff)
|
||||
}
|
||||
r.ctx.DepthMask(true)
|
||||
r.ctx.SetDepthTest(false)
|
||||
}
|
||||
|
||||
func (b *blitter) blit(z float32, mat materialType, col f32color.RGBA, scale, off f32.Point, uvTrans f32.Affine2D) {
|
||||
func (b *blitter) blit(z float32, mat materialType, col f32color.RGBA, col1, col2 f32color.RGBA, scale, off f32.Point, uvTrans f32.Affine2D) {
|
||||
p := b.prog[mat]
|
||||
b.ctx.BindProgram(p.prog)
|
||||
var uniforms *blitUniforms
|
||||
@@ -964,6 +1067,14 @@ func (b *blitter) blit(z float32, mat materialType, col f32color.RGBA, scale, of
|
||||
b.texUniforms.vert.blitUniforms.uvTransformR1 = [4]float32{t1, t2, t3, 0}
|
||||
b.texUniforms.vert.blitUniforms.uvTransformR2 = [4]float32{t4, t5, t6, 0}
|
||||
uniforms = &b.texUniforms.vert.blitUniforms
|
||||
case materialLinearGradient:
|
||||
b.linearGradientUniforms.frag.color1 = col1
|
||||
b.linearGradientUniforms.frag.color2 = col2
|
||||
|
||||
t1, t2, t3, t4, t5, t6 := uvTrans.Elems()
|
||||
b.linearGradientUniforms.vert.blitUniforms.uvTransformR1 = [4]float32{t1, t2, t3, 0}
|
||||
b.linearGradientUniforms.vert.blitUniforms.uvTransformR2 = [4]float32{t4, t5, t6, 0}
|
||||
uniforms = &b.linearGradientUniforms.vert.blitUniforms
|
||||
}
|
||||
uniforms.z = z
|
||||
uniforms.transform = [4]float32{scale.X, scale.Y, off.X, off.Y}
|
||||
@@ -1036,6 +1147,22 @@ func texSpaceTransform(r f32.Rectangle, bounds image.Point) (f32.Point, f32.Poin
|
||||
return scale, offset
|
||||
}
|
||||
|
||||
// gradientSpaceTransform transforms stop1 and stop2 to [(0,0), (1,1)].
|
||||
func gradientSpaceTransform(clip image.Rectangle, off f32.Point, stop1, stop2 f32.Point) f32.Affine2D {
|
||||
d := stop2.Sub(stop1)
|
||||
l := float32(math.Sqrt(float64(d.X*d.X + d.Y*d.Y)))
|
||||
a := float32(math.Atan2(float64(-d.Y), float64(d.X)))
|
||||
|
||||
// TODO: optimize
|
||||
zp := f32.Point{}
|
||||
return f32.Affine2D{}.
|
||||
Scale(zp, layout.FPt(clip.Size())). // scale to pixel space
|
||||
Offset(zp.Sub(off).Add(layout.FPt(clip.Min))). // offset to clip space
|
||||
Offset(zp.Sub(stop1)). // offset to first stop point
|
||||
Rotate(zp, a). // rotate to align gradient
|
||||
Scale(zp, f32.Pt(1/l, 1/l)) // scale gradient to right size
|
||||
}
|
||||
|
||||
// clipSpaceTransform returns the scale and offset that transforms the given
|
||||
// rectangle from a viewport into OpenGL clip space.
|
||||
func clipSpaceTransform(r image.Rectangle, viewport image.Point) (f32.Point, f32.Point) {
|
||||
|
||||
+30
-10
@@ -27,11 +27,12 @@ type pather struct {
|
||||
}
|
||||
|
||||
type coverer struct {
|
||||
ctx backend.Device
|
||||
prog [2]*program
|
||||
texUniforms *coverTexUniforms
|
||||
colUniforms *coverColUniforms
|
||||
layout backend.InputLayout
|
||||
ctx backend.Device
|
||||
prog [3]*program
|
||||
texUniforms *coverTexUniforms
|
||||
colUniforms *coverColUniforms
|
||||
linearGradientUniforms *coverLinearGradientUniforms
|
||||
layout backend.InputLayout
|
||||
}
|
||||
|
||||
type coverTexUniforms struct {
|
||||
@@ -51,6 +52,16 @@ type coverColUniforms struct {
|
||||
}
|
||||
}
|
||||
|
||||
type coverLinearGradientUniforms struct {
|
||||
vert struct {
|
||||
coverUniforms
|
||||
_ [12]byte // Padding to multiple of 16.
|
||||
}
|
||||
frag struct {
|
||||
gradientUniforms
|
||||
}
|
||||
}
|
||||
|
||||
type coverUniforms struct {
|
||||
transform [4]float32
|
||||
uvCoverTransform [4]float32
|
||||
@@ -149,9 +160,10 @@ func newCoverer(ctx backend.Device) *coverer {
|
||||
}
|
||||
c.colUniforms = new(coverColUniforms)
|
||||
c.texUniforms = new(coverTexUniforms)
|
||||
c.linearGradientUniforms = new(coverLinearGradientUniforms)
|
||||
prog, layout, err := createColorPrograms(ctx, shader_cover_vert, shader_cover_frag,
|
||||
[2]interface{}{&c.colUniforms.vert, &c.texUniforms.vert},
|
||||
[2]interface{}{&c.colUniforms.frag, nil},
|
||||
[3]interface{}{&c.colUniforms.vert, &c.linearGradientUniforms.vert, &c.texUniforms.vert},
|
||||
[3]interface{}{&c.colUniforms.frag, &c.linearGradientUniforms.frag, nil},
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@@ -362,11 +374,11 @@ func (s *stenciler) stencilPath(bounds image.Rectangle, offset f32.Point, uv ima
|
||||
}
|
||||
}
|
||||
|
||||
func (p *pather) cover(z float32, mat materialType, col f32color.RGBA, scale, off f32.Point, uvTrans f32.Affine2D, coverScale, coverOff f32.Point) {
|
||||
p.coverer.cover(z, mat, col, scale, off, uvTrans, coverScale, coverOff)
|
||||
func (p *pather) cover(z float32, mat materialType, col f32color.RGBA, col1, col2 f32color.RGBA, scale, off f32.Point, uvTrans f32.Affine2D, coverScale, coverOff f32.Point) {
|
||||
p.coverer.cover(z, mat, col, col1, col2, scale, off, uvTrans, coverScale, coverOff)
|
||||
}
|
||||
|
||||
func (c *coverer) cover(z float32, mat materialType, col f32color.RGBA, scale, off f32.Point, uvTrans f32.Affine2D, coverScale, coverOff f32.Point) {
|
||||
func (c *coverer) cover(z float32, mat materialType, col f32color.RGBA, col1, col2 f32color.RGBA, scale, off f32.Point, uvTrans f32.Affine2D, coverScale, coverOff f32.Point) {
|
||||
p := c.prog[mat]
|
||||
c.ctx.BindProgram(p.prog)
|
||||
var uniforms *coverUniforms
|
||||
@@ -374,6 +386,14 @@ func (c *coverer) cover(z float32, mat materialType, col f32color.RGBA, scale, o
|
||||
case materialColor:
|
||||
c.colUniforms.frag.color = col
|
||||
uniforms = &c.colUniforms.vert.coverUniforms
|
||||
case materialLinearGradient:
|
||||
c.linearGradientUniforms.frag.color1 = col1
|
||||
c.linearGradientUniforms.frag.color2 = col2
|
||||
|
||||
t1, t2, t3, t4, t5, t6 := uvTrans.Elems()
|
||||
c.linearGradientUniforms.vert.uvTransformR1 = [4]float32{t1, t2, t3, 0}
|
||||
c.linearGradientUniforms.vert.uvTransformR2 = [4]float32{t4, t5, t6, 0}
|
||||
uniforms = &c.linearGradientUniforms.vert.coverUniforms
|
||||
case materialTexture:
|
||||
t1, t2, t3, t4, t5, t6 := uvTrans.Elems()
|
||||
c.texUniforms.vert.uvTransformR1 = [4]float32{t1, t2, t3, 0}
|
||||
|
||||
+104
File diff suppressed because one or more lines are too long
@@ -48,6 +48,6 @@ void main() {
|
||||
vCtrl = ctrl-pos;
|
||||
vTo = to-pos;
|
||||
pos = pos*transform.xy + transform.zw;
|
||||
gl_Position = vec4(pos, 1, 1);
|
||||
gl_Position = vec4(pos, 1, 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ func generate() error {
|
||||
if ext := filepath.Ext(shader); ext != ".vert" && ext != ".frag" {
|
||||
continue
|
||||
}
|
||||
const nvariants = 2
|
||||
const nvariants = 3
|
||||
var variants [nvariants]struct {
|
||||
backend.ShaderSources
|
||||
hlslSrc string
|
||||
@@ -80,6 +80,10 @@ func generate() error {
|
||||
FetchColorExpr: `_color`,
|
||||
Header: `layout(binding=0) uniform Color { vec4 _color; };`,
|
||||
},
|
||||
{
|
||||
FetchColorExpr: `mix(_color1, _color2, clamp(vUV.x, 0.0, 1.0))`,
|
||||
Header: `layout(binding=0) uniform Gradient { vec4 _color1; vec4 _color2; };`,
|
||||
},
|
||||
{
|
||||
FetchColorExpr: `texture(tex, vUV)`,
|
||||
Header: `layout(binding=0) uniform sampler2D tex;`,
|
||||
|
||||
+21
-18
@@ -16,6 +16,7 @@ const (
|
||||
TypeImage
|
||||
TypePaint
|
||||
TypeColor
|
||||
TypeLinearGradient
|
||||
TypeArea
|
||||
TypePointerInput
|
||||
TypePass
|
||||
@@ -29,24 +30,25 @@ const (
|
||||
)
|
||||
|
||||
const (
|
||||
TypeMacroLen = 1 + 4 + 4
|
||||
TypeCallLen = 1 + 4 + 4
|
||||
TypeTransformLen = 1 + 4*6
|
||||
TypeLayerLen = 1
|
||||
TypeRedrawLen = 1 + 8
|
||||
TypeImageLen = 1 + 4*4
|
||||
TypePaintLen = 1 + 4*4
|
||||
TypeColorLen = 1 + 4
|
||||
TypeAreaLen = 1 + 1 + 4*4
|
||||
TypePointerInputLen = 1 + 1 + 1
|
||||
TypePassLen = 1 + 1
|
||||
TypeKeyInputLen = 1 + 1
|
||||
TypeHideInputLen = 1
|
||||
TypePushLen = 1
|
||||
TypePopLen = 1
|
||||
TypeAuxLen = 1
|
||||
TypeClipLen = 1 + 4*4
|
||||
TypeProfileLen = 1
|
||||
TypeMacroLen = 1 + 4 + 4
|
||||
TypeCallLen = 1 + 4 + 4
|
||||
TypeTransformLen = 1 + 4*6
|
||||
TypeLayerLen = 1
|
||||
TypeRedrawLen = 1 + 8
|
||||
TypeImageLen = 1 + 4*4
|
||||
TypePaintLen = 1 + 4*4
|
||||
TypeColorLen = 1 + 4
|
||||
TypeLinearGradientLen = 1 + 8*2 + 4*2
|
||||
TypeAreaLen = 1 + 1 + 4*4
|
||||
TypePointerInputLen = 1 + 1 + 1
|
||||
TypePassLen = 1 + 1
|
||||
TypeKeyInputLen = 1 + 1
|
||||
TypeHideInputLen = 1
|
||||
TypePushLen = 1
|
||||
TypePopLen = 1
|
||||
TypeAuxLen = 1
|
||||
TypeClipLen = 1 + 4*4
|
||||
TypeProfileLen = 1
|
||||
)
|
||||
|
||||
func (t OpType) Size() int {
|
||||
@@ -59,6 +61,7 @@ func (t OpType) Size() int {
|
||||
TypeImageLen,
|
||||
TypePaintLen,
|
||||
TypeColorLen,
|
||||
TypeLinearGradientLen,
|
||||
TypeAreaLen,
|
||||
TypePointerInputLen,
|
||||
TypePassLen,
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 716 B |
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
@@ -1,10 +1,12 @@
|
||||
package rendertest
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"gioui.org/f32"
|
||||
"gioui.org/internal/f32color"
|
||||
"gioui.org/op"
|
||||
"gioui.org/op/clip"
|
||||
"gioui.org/op/paint"
|
||||
@@ -194,3 +196,95 @@ func TestNegativeOverlaps(t *testing.T) {
|
||||
r.expect(60, 122, colornames.White)
|
||||
})
|
||||
}
|
||||
|
||||
type Gradient struct {
|
||||
From, To color.RGBA
|
||||
}
|
||||
|
||||
var gradients = []Gradient{
|
||||
{From: color.RGBA{R: 0x00, G: 0x00, B: 0x00, A: 0xFF}, To: color.RGBA{R: 0xFF, G: 0xFF, B: 0xFF, A: 0xFF}},
|
||||
{From: color.RGBA{R: 0x19, G: 0xFF, B: 0x19, A: 0xFF}, To: color.RGBA{R: 0xFF, G: 0x19, B: 0x19, A: 0xFF}},
|
||||
{From: color.RGBA{R: 0xFF, G: 0x19, B: 0x19, A: 0xFF}, To: color.RGBA{R: 0x19, G: 0x19, B: 0xFF, A: 0xFF}},
|
||||
{From: color.RGBA{R: 0x19, G: 0x19, B: 0xFF, A: 0xFF}, To: color.RGBA{R: 0x19, G: 0xFF, B: 0x19, A: 0xFF}},
|
||||
{From: color.RGBA{R: 0x19, G: 0xFF, B: 0xFF, A: 0xFF}, To: color.RGBA{R: 0xFF, G: 0x19, B: 0x19, A: 0xFF}},
|
||||
{From: color.RGBA{R: 0xFF, G: 0xFF, B: 0x19, A: 0xFF}, To: color.RGBA{R: 0x19, G: 0x19, B: 0xFF, A: 0xFF}},
|
||||
}
|
||||
|
||||
func TestLinearGradient(t *testing.T) {
|
||||
const gradienth = 8
|
||||
// 0.5 offset from ends to ensure that the center of the pixel
|
||||
// aligns with gradient from and to colors.
|
||||
pixelAligned := f32.Rect(0.5, 0, 127.5, gradienth)
|
||||
samples := []int{0, 12, 32, 64, 96, 115, 127}
|
||||
|
||||
run(t, func(ops *op.Ops) {
|
||||
gr := pixelAligned
|
||||
for _, g := range gradients {
|
||||
paint.LinearGradientOp{
|
||||
Stop1: f32.Pt(gr.Min.X, gr.Min.Y),
|
||||
Color1: g.From,
|
||||
Stop2: f32.Pt(gr.Max.X, gr.Min.Y),
|
||||
Color2: g.To,
|
||||
}.Add(ops)
|
||||
paint.PaintOp{Rect: gr}.Add(ops)
|
||||
gr = gr.Add(f32.Pt(0, gradienth))
|
||||
}
|
||||
}, func(r result) {
|
||||
gr := pixelAligned
|
||||
for _, g := range gradients {
|
||||
from := f32color.RGBAFromSRGB(g.From)
|
||||
to := f32color.RGBAFromSRGB(g.To)
|
||||
for _, p := range samples {
|
||||
exp := lerp(from, to, float32(p)/float32(r.img.Bounds().Dx()-1))
|
||||
r.expect(p, int(gr.Min.Y+gradienth/2), exp.SRGB())
|
||||
}
|
||||
gr = gr.Add(f32.Pt(0, gradienth))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestLinearGradientAngled(t *testing.T) {
|
||||
run(t, func(ops *op.Ops) {
|
||||
paint.LinearGradientOp{
|
||||
Stop1: f32.Pt(64, 64),
|
||||
Color1: colornames.Black,
|
||||
Stop2: f32.Pt(0, 0),
|
||||
Color2: colornames.Red,
|
||||
}.Add(ops)
|
||||
paint.PaintOp{Rect: f32.Rect(0, 0, 64, 64)}.Add(ops)
|
||||
|
||||
paint.LinearGradientOp{
|
||||
Stop1: f32.Pt(64, 64),
|
||||
Color1: colornames.White,
|
||||
Stop2: f32.Pt(128, 0),
|
||||
Color2: colornames.Green,
|
||||
}.Add(ops)
|
||||
paint.PaintOp{Rect: f32.Rect(64, 0, 128, 64)}.Add(ops)
|
||||
|
||||
paint.LinearGradientOp{
|
||||
Stop1: f32.Pt(64, 64),
|
||||
Color1: colornames.Black,
|
||||
Stop2: f32.Pt(128, 128),
|
||||
Color2: colornames.Blue,
|
||||
}.Add(ops)
|
||||
paint.PaintOp{Rect: f32.Rect(64, 64, 128, 128)}.Add(ops)
|
||||
|
||||
paint.LinearGradientOp{
|
||||
Stop1: f32.Pt(64, 64),
|
||||
Color1: colornames.White,
|
||||
Stop2: f32.Pt(0, 128),
|
||||
Color2: colornames.Magenta,
|
||||
}.Add(ops)
|
||||
paint.PaintOp{Rect: f32.Rect(0, 64, 64, 128)}.Add(ops)
|
||||
}, func(r result) {})
|
||||
}
|
||||
|
||||
// lerp calculates linear interpolation with color b and p.
|
||||
func lerp(a, b f32color.RGBA, p float32) f32color.RGBA {
|
||||
return f32color.RGBA{
|
||||
R: a.R*(1-p) + b.R*p,
|
||||
G: a.G*(1-p) + b.G*p,
|
||||
B: a.B*(1-p) + b.B*p,
|
||||
A: a.A*(1-p) + b.A*p,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,15 @@ type ColorOp struct {
|
||||
Color color.RGBA
|
||||
}
|
||||
|
||||
// LinearGradientOp sets the brush to a gradient starting at stop1 with color1 and
|
||||
// ending at stop2 with color2.
|
||||
type LinearGradientOp struct {
|
||||
Stop1 f32.Point
|
||||
Color1 color.RGBA
|
||||
Stop2 f32.Point
|
||||
Color2 color.RGBA
|
||||
}
|
||||
|
||||
// PaintOp fills an area with the current brush, respecting the
|
||||
// current clip path and transformation.
|
||||
type PaintOp struct {
|
||||
@@ -117,6 +126,26 @@ func (c ColorOp) Add(o *op.Ops) {
|
||||
data[4] = c.Color.A
|
||||
}
|
||||
|
||||
func (c LinearGradientOp) Add(o *op.Ops) {
|
||||
data := o.Write(opconst.TypeLinearGradientLen)
|
||||
data[0] = byte(opconst.TypeLinearGradient)
|
||||
|
||||
bo := binary.LittleEndian
|
||||
bo.PutUint32(data[1:], math.Float32bits(c.Stop1.X))
|
||||
bo.PutUint32(data[5:], math.Float32bits(c.Stop1.Y))
|
||||
bo.PutUint32(data[9:], math.Float32bits(c.Stop2.X))
|
||||
bo.PutUint32(data[13:], math.Float32bits(c.Stop2.Y))
|
||||
|
||||
data[17+0] = c.Color1.R
|
||||
data[17+1] = c.Color1.G
|
||||
data[17+2] = c.Color1.B
|
||||
data[17+3] = c.Color1.A
|
||||
data[21+0] = c.Color2.R
|
||||
data[21+1] = c.Color2.G
|
||||
data[21+2] = c.Color2.B
|
||||
data[21+3] = c.Color2.A
|
||||
}
|
||||
|
||||
func (d PaintOp) Add(o *op.Ops) {
|
||||
data := o.Write(opconst.TypePaintLen)
|
||||
data[0] = byte(opconst.TypePaint)
|
||||
|
||||
Reference in New Issue
Block a user