forked from joejulian/gio
gpu, op, internal/ops: add affine transformations
Add support for affine transformations. The key changes are outlined below. - Painting/clipping with rectangles is handled by, for complex transforms, creating clipping paths representing the transformed rectangle and using a larger bounding box. Cover/Blit shaders updated correspondingly to correctly map texture cordinates from the new bounding boxes. - Since path splitting must happen on CPU the transforms must happen CPU side as well - offsets removed from shaders. - Complex transforms will lead to different path splitting which means that GPU arrays can no longer be cached if the transform has changed. Thus the current transform is added as a key to the cache. - Add a public API to op for setting Affine transformations. There are a number of optimizations that could be explored further but which are left out now: - Caching also of CPU operations (e.g path splitting & transforms) and not only caching the GPU arrays. - Allow for re-use of cached GPU vertices if the transformation change is a pure offset / scaling since the splitting is then the same. Signed-off-by: Viktor <viktor.ogeman@gmail.com>
This commit is contained in:
+145
-49
@@ -61,10 +61,11 @@ type drawOps struct {
|
||||
// zimageOps are the rectangle clipped opaque images
|
||||
// that can use fast front-to-back rendering with z-test
|
||||
// and no blending.
|
||||
zimageOps []imageOp
|
||||
pathOps []*pathOp
|
||||
pathOpCache []pathOp
|
||||
qs quadSplitter
|
||||
zimageOps []imageOp
|
||||
pathOps []*pathOp
|
||||
pathOpCache []pathOp
|
||||
qs quadSplitter
|
||||
uniqueKeyCounter int
|
||||
}
|
||||
|
||||
type drawState struct {
|
||||
@@ -82,7 +83,6 @@ type drawState struct {
|
||||
}
|
||||
|
||||
type pathOp struct {
|
||||
off f32.Point
|
||||
// clip is the union of all
|
||||
// later clip rectangles.
|
||||
clip image.Rectangle
|
||||
@@ -96,7 +96,6 @@ type pathOp struct {
|
||||
type imageOp struct {
|
||||
z float32
|
||||
path *pathOp
|
||||
off f32.Point
|
||||
clip image.Rectangle
|
||||
material material
|
||||
clipType clipType
|
||||
@@ -109,9 +108,8 @@ type material struct {
|
||||
// For materialTypeColor.
|
||||
color f32color.RGBA
|
||||
// For materialTypeTexture.
|
||||
texture *texture
|
||||
uvScale f32.Point
|
||||
uvOffset f32.Point
|
||||
texture *texture
|
||||
uvTrans f32.Affine2D
|
||||
}
|
||||
|
||||
// clipOp is the shadow of clip.Op.
|
||||
@@ -227,7 +225,7 @@ type blitter struct {
|
||||
type blitColUniforms struct {
|
||||
vert struct {
|
||||
blitUniforms
|
||||
_ [10]byte // Padding to a multiple of 16.
|
||||
_ [12]byte // Padding to a multiple of 16.
|
||||
}
|
||||
frag struct {
|
||||
colorUniforms
|
||||
@@ -237,7 +235,7 @@ type blitColUniforms struct {
|
||||
type blitTexUniforms struct {
|
||||
vert struct {
|
||||
blitUniforms
|
||||
_ [10]byte // Padding to a multiple of 16.
|
||||
_ [12]byte // Padding to a multiple of 16.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,9 +251,10 @@ type program struct {
|
||||
}
|
||||
|
||||
type blitUniforms struct {
|
||||
transform [4]float32
|
||||
uvTransform [4]float32
|
||||
z float32
|
||||
transform [4]float32
|
||||
uvTransformR1 [4]float32
|
||||
uvTransformR2 [4]float32
|
||||
z float32
|
||||
}
|
||||
|
||||
type colorUniforms struct {
|
||||
@@ -507,7 +506,7 @@ func (r *renderer) stencilClips(pathCache *opCache, ops []*pathOp) {
|
||||
r.ctx.Clear(0.0, 0.0, 0.0, 0.0)
|
||||
}
|
||||
data, _ := pathCache.get(p.pathKey)
|
||||
r.pather.stencilPath(p.clip, p.off, p.place.Pos, data.(*pathData))
|
||||
r.pather.stencilPath(p.clip, p.place.Pos, data.(*pathData))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -682,6 +681,28 @@ func (d *drawOps) newPathOp() *pathOp {
|
||||
return &d.pathOpCache[len(d.pathOpCache)-1]
|
||||
}
|
||||
|
||||
func (d *drawOps) addClipPath(state *drawState, aux []byte, auxKey ops.Key) {
|
||||
npath := d.newPathOp()
|
||||
*npath = pathOp{
|
||||
parent: state.cpath,
|
||||
}
|
||||
state.cpath = npath
|
||||
if len(aux) > 0 {
|
||||
state.rect = false
|
||||
state.cpath.pathKey = auxKey
|
||||
state.cpath.path = true
|
||||
state.cpath.pathVerts = aux
|
||||
d.pathOps = append(d.pathOps, state.cpath)
|
||||
}
|
||||
}
|
||||
|
||||
// noCacheKey creates a new key for caches, but one that is unique and
|
||||
// thus will never lead to re-use.
|
||||
func (d *drawOps) noCacheKey() ops.Key {
|
||||
d.uniqueKeyCounter--
|
||||
return d.reader.NewKey(d.uniqueKeyCounter)
|
||||
}
|
||||
|
||||
func (d *drawOps) collectOps(r *ops.Reader, state drawState) int {
|
||||
var aux []byte
|
||||
var auxKey ops.Key
|
||||
@@ -699,32 +720,23 @@ loop:
|
||||
case opconst.TypeClip:
|
||||
var op clipOp
|
||||
op.decode(encOp.Data)
|
||||
off := state.t.Transform(f32.Point{})
|
||||
|
||||
bounds := op.bounds
|
||||
if len(aux) > 0 {
|
||||
// there is a clipping path, bounds is not filled before for performance
|
||||
aux, bounds = d.buildVerts(aux)
|
||||
// There is a clipping path, build the gpu data and update the
|
||||
// cache key such that it will be equal only if the transform is the
|
||||
// same also.
|
||||
aux, op.bounds = d.buildVerts(aux, state.t)
|
||||
auxKey = auxKey.SetTransform(state.t)
|
||||
} else {
|
||||
aux, op.bounds, _ = d.boundsForTransformedRect(bounds, state.t)
|
||||
auxKey = d.noCacheKey()
|
||||
}
|
||||
state.clip = state.clip.Intersect(bounds.Add(off))
|
||||
state.clip = state.clip.Intersect(op.bounds)
|
||||
if state.clip.Empty() {
|
||||
continue
|
||||
}
|
||||
|
||||
npath := d.newPathOp()
|
||||
*npath = pathOp{
|
||||
parent: state.cpath,
|
||||
off: off,
|
||||
}
|
||||
state.cpath = npath
|
||||
|
||||
if len(aux) > 0 {
|
||||
state.rect = false
|
||||
state.cpath.pathKey = auxKey
|
||||
state.cpath.path = true
|
||||
state.cpath.pathVerts = aux
|
||||
d.pathOps = append(d.pathOps, state.cpath)
|
||||
}
|
||||
d.addClipPath(&state, aux, auxKey)
|
||||
aux = nil
|
||||
auxKey = ops.Key{}
|
||||
case opconst.TypeColor:
|
||||
@@ -735,13 +747,25 @@ loop:
|
||||
state.image = decodeImageOp(encOp.Data, encOp.Refs)
|
||||
case opconst.TypePaint:
|
||||
op := decodePaintOp(encOp.Data)
|
||||
off := state.t.Transform(f32.Point{})
|
||||
clip := state.clip.Intersect(op.Rect.Add(off))
|
||||
// Transform (if needed) the painting rectangle and if so generate a clip path,
|
||||
// for those cases also compute a partialTrans that maps texture coordinates between
|
||||
// the new bounding rectangle and the transformed original paint rectangle.
|
||||
clipData, bnd, partialTrans := d.boundsForTransformedRect(op.Rect, state.t)
|
||||
clip := state.clip.Intersect(bnd).Canon()
|
||||
if clip.Empty() {
|
||||
continue
|
||||
}
|
||||
|
||||
wasrect := state.rect
|
||||
if clipData != nil {
|
||||
// The paint operation is sheared or rotated, add a clip path representing
|
||||
// this transformed rectangle.
|
||||
d.addClipPath(&state, clipData, d.noCacheKey())
|
||||
}
|
||||
|
||||
bounds := boundRectF(clip)
|
||||
mat := state.materialFor(d.cache, op.Rect, off, bounds)
|
||||
mat := state.materialFor(d.cache, bnd, partialTrans, bounds)
|
||||
|
||||
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.
|
||||
@@ -763,15 +787,20 @@ loop:
|
||||
img := imageOp{
|
||||
z: zf,
|
||||
path: state.cpath,
|
||||
off: off,
|
||||
clip: bounds,
|
||||
material: mat,
|
||||
}
|
||||
|
||||
if state.rect && img.material.opaque {
|
||||
d.zimageOps = append(d.zimageOps, img)
|
||||
} else {
|
||||
d.imageOps = append(d.imageOps, img)
|
||||
}
|
||||
if clipData != nil {
|
||||
// we added a clip path that should not remain
|
||||
state.cpath = state.cpath.parent
|
||||
state.rect = wasrect
|
||||
}
|
||||
case opconst.TypePush:
|
||||
state.z = d.collectOps(r, state)
|
||||
case opconst.TypePop:
|
||||
@@ -792,7 +821,7 @@ func expandPathOp(p *pathOp, clip image.Rectangle) {
|
||||
}
|
||||
}
|
||||
|
||||
func (d *drawState) materialFor(cache *resourceCache, rect f32.Rectangle, off f32.Point, clip image.Rectangle) material {
|
||||
func (d *drawState) materialFor(cache *resourceCache, rect f32.Rectangle, trans f32.Affine2D, clip image.Rectangle) material {
|
||||
var m material
|
||||
switch d.matType {
|
||||
case materialColor:
|
||||
@@ -801,7 +830,7 @@ func (d *drawState) materialFor(cache *resourceCache, rect f32.Rectangle, off f3
|
||||
m.opaque = m.color.A == 1.0
|
||||
case materialTexture:
|
||||
m.material = materialTexture
|
||||
dr := boundRectF(rect.Add(off))
|
||||
dr := boundRectF(rect)
|
||||
sz := d.image.src.Bounds().Size()
|
||||
sr := layout.FRect(d.image.rect)
|
||||
if dx := float32(dr.Dx()); dx != 0 {
|
||||
@@ -827,7 +856,8 @@ func (d *drawState) materialFor(cache *resourceCache, rect f32.Rectangle, off f3
|
||||
tex = t
|
||||
}
|
||||
m.texture = tex.(*texture)
|
||||
m.uvScale, m.uvOffset = texSpaceTransform(sr, sz)
|
||||
uvScale, uvOffset := texSpaceTransform(sr, sz)
|
||||
m.uvTrans = trans.Mul(f32.Affine2D{}.Scale(f32.Point{}, uvScale).Offset(uvOffset))
|
||||
}
|
||||
return m
|
||||
}
|
||||
@@ -846,7 +876,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.uvScale, m.uvOffset)
|
||||
r.blitter.blit(img.z, m.material, m.color, scale, off, m.uvTrans)
|
||||
}
|
||||
r.ctx.SetDepthTest(false)
|
||||
}
|
||||
@@ -865,11 +895,12 @@ func (r *renderer) drawOps(ops []imageOp) {
|
||||
r.ctx.BindTexture(0, r.texHandle(m.texture))
|
||||
}
|
||||
drc := img.clip
|
||||
|
||||
scale, off := clipSpaceTransform(drc, r.blitter.viewport)
|
||||
var fbo stencilFBO
|
||||
switch img.clipType {
|
||||
case clipTypeNone:
|
||||
r.blitter.blit(img.z, m.material, m.color, scale, off, m.uvScale, m.uvOffset)
|
||||
r.blitter.blit(img.z, m.material, m.color, scale, off, m.uvTrans)
|
||||
continue
|
||||
case clipTypePath:
|
||||
fbo = r.pather.stenciler.cover(img.place.Idx)
|
||||
@@ -885,13 +916,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.uvScale, m.uvOffset, coverScale, coverOff)
|
||||
r.pather.cover(img.z, m.material, m.color, 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, uvScale, uvOff f32.Point) {
|
||||
func (b *blitter) blit(z float32, mat materialType, col f32color.RGBA, scale, off f32.Point, uvTrans f32.Affine2D) {
|
||||
p := b.prog[mat]
|
||||
b.ctx.BindProgram(p.prog)
|
||||
var uniforms *blitUniforms
|
||||
@@ -900,7 +931,9 @@ func (b *blitter) blit(z float32, mat materialType, col f32color.RGBA, scale, of
|
||||
b.colUniforms.frag.color = col
|
||||
uniforms = &b.colUniforms.vert.blitUniforms
|
||||
case materialTexture:
|
||||
b.texUniforms.vert.uvTransform = [4]float32{uvScale.X, uvScale.Y, uvOff.X, uvOff.Y}
|
||||
t1, t2, t3, t4, t5, t6 := uvTrans.Elems()
|
||||
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
|
||||
}
|
||||
uniforms.z = z
|
||||
@@ -994,6 +1027,7 @@ func clipSpaceTransform(r image.Rectangle, viewport image.Point) (f32.Point, f32
|
||||
// the rectangle at (x, y) and dimensions (w, h).
|
||||
scale := f32.Point{X: w * .5, Y: h * .5}
|
||||
offset := f32.Point{X: x + w*.5, Y: y - h*.5}
|
||||
|
||||
return scale, offset
|
||||
}
|
||||
|
||||
@@ -1043,9 +1077,8 @@ func (d *drawOps) writeVertCache(n int) []byte {
|
||||
return d.vertCache[len(d.vertCache)-n:]
|
||||
}
|
||||
|
||||
func (d *drawOps) buildVerts(aux []byte) (verts []byte, bounds f32.Rectangle) {
|
||||
// split paths as needed, calculate maxY, bounds and create
|
||||
// vertices that will be sent to GPU.
|
||||
// transform, split paths as needed, calculate maxY, bounds and create GPU vertices.
|
||||
func (d *drawOps) buildVerts(aux []byte, tr f32.Affine2D) (verts []byte, bounds f32.Rectangle) {
|
||||
inf := float32(math.Inf(+1))
|
||||
d.qs.bounds = f32.Rectangle{
|
||||
Min: f32.Point{X: inf, Y: inf},
|
||||
@@ -1057,6 +1090,7 @@ func (d *drawOps) buildVerts(aux []byte) (verts []byte, bounds f32.Rectangle) {
|
||||
for qi := 0; len(aux) >= (ops.QuadSize + 4); qi++ {
|
||||
d.qs.contour = bo.Uint32(aux)
|
||||
quad := ops.DecodeQuad(aux[4:])
|
||||
quad = quad.Transform(tr)
|
||||
|
||||
d.qs.splitAndEncode(quad)
|
||||
|
||||
@@ -1066,3 +1100,65 @@ func (d *drawOps) buildVerts(aux []byte) (verts []byte, bounds f32.Rectangle) {
|
||||
fillMaxY(d.vertCache[startLength:])
|
||||
return d.vertCache[startLength:], d.qs.bounds
|
||||
}
|
||||
|
||||
// create GPU vertices for transformed r, find the bounds and establish texture transform.
|
||||
func (d *drawOps) boundsForTransformedRect(r f32.Rectangle, tr f32.Affine2D) (aux []byte, bnd f32.Rectangle, ptr f32.Affine2D) {
|
||||
if isPureOffset(tr) {
|
||||
// fast-path to allow blitting of pure rectangles
|
||||
_, _, ox, _, _, oy := tr.Elems()
|
||||
off := f32.Pt(ox, oy)
|
||||
bnd.Min = r.Min.Add(off)
|
||||
bnd.Max = r.Max.Add(off)
|
||||
return
|
||||
}
|
||||
|
||||
// transform all corners, find new bounds
|
||||
corners := [4]f32.Point{
|
||||
tr.Transform(r.Min), tr.Transform(f32.Pt(r.Max.X, r.Min.Y)),
|
||||
tr.Transform(r.Max), tr.Transform(f32.Pt(r.Min.X, r.Max.Y)),
|
||||
}
|
||||
bnd.Min = f32.Pt(math.MaxFloat32, math.MaxFloat32)
|
||||
bnd.Max = f32.Pt(-math.MaxFloat32, -math.MaxFloat32)
|
||||
for _, c := range corners {
|
||||
if c.X < bnd.Min.X {
|
||||
bnd.Min.X = c.X
|
||||
}
|
||||
if c.Y < bnd.Min.Y {
|
||||
bnd.Min.Y = c.Y
|
||||
}
|
||||
if c.X > bnd.Max.X {
|
||||
bnd.Max.X = c.X
|
||||
}
|
||||
if c.Y > bnd.Max.Y {
|
||||
bnd.Max.Y = c.Y
|
||||
}
|
||||
}
|
||||
|
||||
// build the GPU vertices
|
||||
l := len(d.vertCache)
|
||||
d.vertCache = append(d.vertCache, make([]byte, vertStride*4*4)...)
|
||||
aux = d.vertCache[l:]
|
||||
encodeQuadTo(aux, 0, corners[0], corners[0].Add(corners[1]).Mul(0.5), corners[1])
|
||||
encodeQuadTo(aux[vertStride*4:], 0, corners[1], corners[1].Add(corners[2]).Mul(0.5), corners[2])
|
||||
encodeQuadTo(aux[vertStride*4*2:], 0, corners[2], corners[2].Add(corners[3]).Mul(0.5), corners[3])
|
||||
encodeQuadTo(aux[vertStride*4*3:], 0, corners[3], corners[3].Add(corners[0]).Mul(0.5), corners[0])
|
||||
fillMaxY(aux)
|
||||
|
||||
// establish the transform mapping from bounds rectangle to transformed corners
|
||||
var P1, P2, P3 f32.Point
|
||||
P1.X = (corners[1].X - bnd.Min.X) / (bnd.Max.X - bnd.Min.X)
|
||||
P1.Y = (corners[1].Y - bnd.Min.Y) / (bnd.Max.Y - bnd.Min.Y)
|
||||
P2.X = (corners[2].X - bnd.Min.X) / (bnd.Max.X - bnd.Min.X)
|
||||
P2.Y = (corners[2].Y - bnd.Min.Y) / (bnd.Max.Y - bnd.Min.Y)
|
||||
P3.X = (corners[3].X - bnd.Min.X) / (bnd.Max.X - bnd.Min.X)
|
||||
P3.Y = (corners[3].Y - bnd.Min.Y) / (bnd.Max.Y - bnd.Min.Y)
|
||||
sx, sy := P2.X-P3.X, P2.Y-P3.Y
|
||||
ptr = f32.NewAffine2D(sx, P2.X-P1.X, P1.X-sx, sy, P2.Y-P1.Y, P1.Y-sy).Invert()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func isPureOffset(t f32.Affine2D) bool {
|
||||
a, b, _, d, e, _ := t.Elems()
|
||||
return a == 1 && b == 0 && d == 0 && e == 1
|
||||
}
|
||||
|
||||
+12
-12
@@ -54,7 +54,8 @@ type coverColUniforms struct {
|
||||
type coverUniforms struct {
|
||||
transform [4]float32
|
||||
uvCoverTransform [4]float32
|
||||
uvTransform [4]float32
|
||||
uvTransformR1 [4]float32
|
||||
uvTransformR2 [4]float32
|
||||
z float32
|
||||
}
|
||||
|
||||
@@ -77,9 +78,7 @@ type stenciler struct {
|
||||
|
||||
type stencilUniforms struct {
|
||||
vert struct {
|
||||
transform [4]float32
|
||||
pathOffset [2]float32
|
||||
_ [8]byte // Padding to multiple of 16.
|
||||
transform [4]float32
|
||||
}
|
||||
}
|
||||
|
||||
@@ -307,8 +306,8 @@ func (p *pather) begin(sizes []image.Point) {
|
||||
p.stenciler.begin(sizes)
|
||||
}
|
||||
|
||||
func (p *pather) stencilPath(bounds image.Rectangle, offset f32.Point, uv image.Point, data *pathData) {
|
||||
p.stenciler.stencilPath(bounds, offset, uv, data)
|
||||
func (p *pather) stencilPath(bounds image.Rectangle, uv image.Point, data *pathData) {
|
||||
p.stenciler.stencilPath(bounds, uv, data)
|
||||
}
|
||||
|
||||
func (s *stenciler) beginIntersect(sizes []image.Point) {
|
||||
@@ -337,14 +336,13 @@ func (s *stenciler) begin(sizes []image.Point) {
|
||||
s.ctx.BindIndexBuffer(s.indexBuf)
|
||||
}
|
||||
|
||||
func (s *stenciler) stencilPath(bounds image.Rectangle, offset f32.Point, uv image.Point, data *pathData) {
|
||||
func (s *stenciler) stencilPath(bounds image.Rectangle, uv image.Point, data *pathData) {
|
||||
s.ctx.Viewport(uv.X, uv.Y, bounds.Dx(), bounds.Dy())
|
||||
// Transform UI coordinates to OpenGL coordinates.
|
||||
texSize := f32.Point{X: float32(bounds.Dx()), Y: float32(bounds.Dy())}
|
||||
scale := f32.Point{X: 2 / texSize.X, Y: 2 / texSize.Y}
|
||||
orig := f32.Point{X: -1 - float32(bounds.Min.X)*2/texSize.X, Y: -1 - float32(bounds.Min.Y)*2/texSize.Y}
|
||||
s.prog.uniforms.vert.transform = [4]float32{scale.X, scale.Y, orig.X, orig.Y}
|
||||
s.prog.uniforms.vert.pathOffset = [2]float32{offset.X, offset.Y}
|
||||
s.prog.prog.UploadUniforms()
|
||||
// Draw in batches that fit in uint16 indices.
|
||||
start := 0
|
||||
@@ -361,11 +359,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, uvScale, uvOff, coverScale, coverOff f32.Point) {
|
||||
p.coverer.cover(z, mat, col, scale, off, uvScale, uvOff, coverScale, coverOff)
|
||||
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 (c *coverer) cover(z float32, mat materialType, col f32color.RGBA, scale, off, uvScale, uvOff, coverScale, coverOff f32.Point) {
|
||||
func (c *coverer) cover(z float32, mat materialType, col 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,7 +372,9 @@ func (c *coverer) cover(z float32, mat materialType, col f32color.RGBA, scale, o
|
||||
c.colUniforms.frag.color = col
|
||||
uniforms = &c.colUniforms.vert.coverUniforms
|
||||
case materialTexture:
|
||||
c.texUniforms.vert.uvTransform = [4]float32{uvScale.X, uvScale.Y, uvOff.X, uvOff.Y}
|
||||
t1, t2, t3, t4, t5, t6 := uvTrans.Elems()
|
||||
c.texUniforms.vert.uvTransformR1 = [4]float32{t1, t2, t3, 0}
|
||||
c.texUniforms.vert.uvTransformR2 = [4]float32{t4, t5, t6, 0}
|
||||
uniforms = &c.texUniforms.vert.coverUniforms
|
||||
}
|
||||
uniforms.z = z
|
||||
|
||||
+55
-43
File diff suppressed because one or more lines are too long
@@ -8,7 +8,8 @@ precision highp float;
|
||||
|
||||
layout(binding = 0) uniform Block {
|
||||
vec4 transform;
|
||||
vec4 uvTransform;
|
||||
vec4 uvTransformR1;
|
||||
vec4 uvTransformR2;
|
||||
float z;
|
||||
};
|
||||
|
||||
@@ -21,5 +22,5 @@ layout(location = 0) out vec2 vUV;
|
||||
void main() {
|
||||
vec2 p = pos*transform.xy + transform.zw;
|
||||
gl_Position = toClipSpace(vec4(p, z, 1));
|
||||
vUV = uv*uvTransform.xy + uvTransform.zw;
|
||||
vUV = transform3x2(m3x2(uvTransformR1.xyz, uvTransformR2.xyz), vec3(uv,1)).xy;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,8 @@ precision highp float;
|
||||
layout(binding = 0) uniform Block {
|
||||
vec4 transform;
|
||||
vec4 uvCoverTransform;
|
||||
vec4 uvTransform;
|
||||
vec4 uvTransformR1;
|
||||
vec4 uvTransformR2;
|
||||
float z;
|
||||
};
|
||||
|
||||
@@ -22,7 +23,7 @@ layout(location = 1) out vec2 vUV;
|
||||
|
||||
void main() {
|
||||
gl_Position = toClipSpace(vec4(pos*transform.xy + transform.zw, z, 1));
|
||||
vUV = uv*uvTransform.xy + uvTransform.zw;
|
||||
vUV = transform3x2(m3x2(uvTransformR1.xyz, uvTransformR2.xyz), vec3(uv,1)).xy;
|
||||
vec3 uv3 = transform3x2(fboTextureTransform, vec3(uv, 1.0));
|
||||
vCoverUV = (uv3*vec3(uvCoverTransform.xy, 1.0)+vec3(uvCoverTransform.zw, 0.0)).xy;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ precision highp float;
|
||||
|
||||
layout(binding = 0) uniform Block {
|
||||
vec4 transform;
|
||||
vec2 pathOffset;
|
||||
};
|
||||
|
||||
layout(location=0) in float corner;
|
||||
@@ -23,10 +22,10 @@ void main() {
|
||||
// Add a one pixel overlap so curve quads cover their
|
||||
// entire curves. Could use conservative rasterization
|
||||
// if available.
|
||||
vec2 from = from + pathOffset;
|
||||
vec2 ctrl = ctrl + pathOffset;
|
||||
vec2 to = to + pathOffset;
|
||||
float maxy = maxy + pathOffset.y;
|
||||
vec2 from = from;
|
||||
vec2 ctrl = ctrl;
|
||||
vec2 to = to;
|
||||
float maxy = maxy;
|
||||
vec2 pos;
|
||||
float c = corner;
|
||||
if (c >= 0.375) {
|
||||
|
||||
@@ -16,6 +16,13 @@ type Quad struct {
|
||||
From, Ctrl, To f32.Point
|
||||
}
|
||||
|
||||
func (q Quad) Transform(t f32.Affine2D) Quad {
|
||||
q.From = t.Transform(q.From)
|
||||
q.Ctrl = t.Transform(q.Ctrl)
|
||||
q.To = t.Transform(q.To)
|
||||
return q
|
||||
}
|
||||
|
||||
func EncodeQuad(d []byte, q Quad) {
|
||||
bo := binary.LittleEndian
|
||||
bo.PutUint32(d[0:], math.Float32bits(q.From.X))
|
||||
|
||||
+18
-3
@@ -5,6 +5,7 @@ package ops
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"gioui.org/f32"
|
||||
"gioui.org/internal/opconst"
|
||||
"gioui.org/op"
|
||||
)
|
||||
@@ -26,9 +27,10 @@ type EncodedOp struct {
|
||||
|
||||
// Key is a unique key for a given op.
|
||||
type Key struct {
|
||||
ops *op.Ops
|
||||
pc int
|
||||
version int
|
||||
ops *op.Ops
|
||||
pc int
|
||||
version int
|
||||
sx, hx, sy, hy float32
|
||||
}
|
||||
|
||||
// Shadow of op.MacroOp.
|
||||
@@ -52,6 +54,10 @@ type opMacroDef struct {
|
||||
endpc pc
|
||||
}
|
||||
|
||||
func (r *Reader) NewKey(pc int) Key {
|
||||
return Key{ops: r.ops, pc: pc, version: r.ops.Version()}
|
||||
}
|
||||
|
||||
// Reset start reading from the op list.
|
||||
func (r *Reader) Reset(ops *op.Ops) {
|
||||
r.stack = r.stack[:0]
|
||||
@@ -59,6 +65,15 @@ func (r *Reader) Reset(ops *op.Ops) {
|
||||
r.ops = ops
|
||||
}
|
||||
|
||||
func (k Key) SetTransform(t f32.Affine2D) Key {
|
||||
sx, hx, sy, hy, _, _ := t.Elems()
|
||||
k.sx = sx
|
||||
k.hx = hx
|
||||
k.sy = sy
|
||||
k.hy = hy
|
||||
return k
|
||||
}
|
||||
|
||||
func (r *Reader) Decode() (EncodedOp, bool) {
|
||||
if r.ops == nil {
|
||||
return EncodedOp{}, false
|
||||
|
||||
@@ -117,10 +117,10 @@ type InvalidateOp struct {
|
||||
At time.Time
|
||||
}
|
||||
|
||||
// TransformOp applies a transform to the current transform.
|
||||
// TransformOp applies a transform to the current transform. The zero value
|
||||
// for TransformOp represents the identity transform.
|
||||
type TransformOp struct {
|
||||
// TODO: general transformations.
|
||||
offset f32.Point
|
||||
t f32.Affine2D
|
||||
}
|
||||
|
||||
// stack tracks the integer identities of StackOp and MacroOp
|
||||
@@ -264,39 +264,33 @@ func (r InvalidateOp) Add(o *Ops) {
|
||||
}
|
||||
}
|
||||
|
||||
// Offset the transformation.
|
||||
// Offset creates a TransformOp with the offset o.
|
||||
func Offset(o f32.Point) TransformOp {
|
||||
return TransformOp{t: f32.Affine2D{}.Offset(o)}
|
||||
}
|
||||
|
||||
// Affine creates a TransformOp representing the transformation a.
|
||||
func Affine(a f32.Affine2D) TransformOp {
|
||||
return TransformOp{t: a}
|
||||
}
|
||||
|
||||
// Offset the transfomraiton.
|
||||
func (t TransformOp) Offset(o f32.Point) TransformOp {
|
||||
return t.Multiply(TransformOp{o})
|
||||
}
|
||||
|
||||
// Invert the transformation.
|
||||
func (t TransformOp) Invert() TransformOp {
|
||||
return TransformOp{offset: t.offset.Mul(-1)}
|
||||
}
|
||||
|
||||
// Transform a point.
|
||||
func (t TransformOp) Transform(p f32.Point) f32.Point {
|
||||
return p.Add(t.offset)
|
||||
}
|
||||
|
||||
// Multiply by a transformation.
|
||||
func (t TransformOp) Multiply(t2 TransformOp) TransformOp {
|
||||
return TransformOp{
|
||||
offset: t.offset.Add(t2.offset),
|
||||
}
|
||||
t.t = t.t.Offset(o)
|
||||
return t
|
||||
}
|
||||
|
||||
func (t TransformOp) Add(o *Ops) {
|
||||
data := o.Write(opconst.TypeTransformLen)
|
||||
data[0] = byte(opconst.TypeTransform)
|
||||
bo := binary.LittleEndian
|
||||
// write it out as an affine matrix although we only support offset yet
|
||||
bo.PutUint32(data[1:], math.Float32bits(1.0))
|
||||
bo.PutUint32(data[1+4*1:], math.Float32bits(0))
|
||||
bo.PutUint32(data[1+4*2:], math.Float32bits(t.offset.X))
|
||||
bo.PutUint32(data[1+4*3:], math.Float32bits(0))
|
||||
bo.PutUint32(data[1+4*4:], math.Float32bits(1))
|
||||
bo.PutUint32(data[1+4*5:], math.Float32bits(t.offset.Y))
|
||||
a, b, c, d, e, f := t.t.Elems()
|
||||
bo.PutUint32(data[1:], math.Float32bits(a))
|
||||
bo.PutUint32(data[1+4*1:], math.Float32bits(b))
|
||||
bo.PutUint32(data[1+4*2:], math.Float32bits(c))
|
||||
bo.PutUint32(data[1+4*3:], math.Float32bits(d))
|
||||
bo.PutUint32(data[1+4*4:], math.Float32bits(e))
|
||||
bo.PutUint32(data[1+4*5:], math.Float32bits(f))
|
||||
}
|
||||
|
||||
func (s *stack) push() stackID {
|
||||
|
||||
Reference in New Issue
Block a user