github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/exp/shiny/driver/x11driver/texture.go (about) 1 // Copyright 2015 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package x11driver 6 7 import ( 8 "image" 9 "image/color" 10 "image/draw" 11 "math" 12 "sync" 13 14 "github.com/BurntSushi/xgb/render" 15 "github.com/BurntSushi/xgb/xproto" 16 17 "golang.org/x/exp/shiny/screen" 18 "golang.org/x/image/math/f64" 19 ) 20 21 const textureDepth = 32 22 23 type textureImpl struct { 24 s *screenImpl 25 26 size image.Point 27 xm xproto.Pixmap 28 xp render.Picture 29 30 // renderMu is a mutex that enforces the atomicity of methods like 31 // Window.Draw that are conceptually one operation but are implemented by 32 // multiple X11/Render calls. X11/Render is a stateful API, so interleaving 33 // X11/Render calls from separate higher-level operations causes 34 // inconsistencies. 35 renderMu sync.Mutex 36 37 releasedMu sync.Mutex 38 released bool 39 } 40 41 func (t *textureImpl) Size() image.Point { return t.size } 42 func (t *textureImpl) Bounds() image.Rectangle { return image.Rectangle{Max: t.size} } 43 44 func (t *textureImpl) Release() { 45 t.releasedMu.Lock() 46 released := t.released 47 t.released = true 48 t.releasedMu.Unlock() 49 50 if released { 51 return 52 } 53 render.FreePicture(t.s.xc, t.xp) 54 xproto.FreePixmap(t.s.xc, t.xm) 55 } 56 57 func (t *textureImpl) Upload(dp image.Point, src screen.Buffer, sr image.Rectangle) { 58 src.(*bufferImpl).upload(t, xproto.Drawable(t.xm), t.s.gcontext32, textureDepth, dp, sr) 59 } 60 61 func (t *textureImpl) Fill(dr image.Rectangle, src color.Color, op draw.Op) { 62 fill(t.s.xc, t.xp, dr, src, op) 63 } 64 65 // f64ToFixed converts from float64 to X11/Render's 16.16 fixed point. 66 func f64ToFixed(x float64) render.Fixed { 67 return render.Fixed(x * 65536) 68 } 69 70 func inv(x *f64.Aff3) f64.Aff3 { 71 invDet := 1 / (x[0]*x[4] - x[1]*x[3]) 72 return f64.Aff3{ 73 +x[4] * invDet, 74 -x[1] * invDet, 75 (x[1]*x[5] - x[2]*x[4]) * invDet, 76 -x[3] * invDet, 77 +x[0] * invDet, 78 (x[2]*x[3] - x[0]*x[5]) * invDet, 79 } 80 } 81 82 func (t *textureImpl) draw(xp render.Picture, src2dst *f64.Aff3, sr image.Rectangle, op draw.Op, w, h int, opts *screen.DrawOptions) { 83 // TODO: honor sr.Max 84 85 t.renderMu.Lock() 86 defer t.renderMu.Unlock() 87 88 // For simple copies and scales, the inverse matrix is trivial to compute, 89 // and we do not need the "Src becomes OutReverse plus Over" dance (see 90 // below). Thus, draw can be one render.SetPictureTransform call and then 91 // one render.Composite call, regardless of whether or not op is Src. 92 if src2dst[1] == 0 && src2dst[3] == 0 { 93 dstXMin := float64(sr.Min.X)*src2dst[0] + src2dst[2] 94 dstXMax := float64(sr.Max.X)*src2dst[0] + src2dst[2] 95 if dstXMin > dstXMax { 96 // TODO: check if this (and below) works when src2dst[0] < 0. 97 dstXMin, dstXMax = dstXMax, dstXMin 98 } 99 dXMin := int(math.Floor(dstXMin)) 100 dXMax := int(math.Ceil(dstXMax)) 101 102 dstYMin := float64(sr.Min.Y)*src2dst[4] + src2dst[5] 103 dstYMax := float64(sr.Max.Y)*src2dst[4] + src2dst[5] 104 if dstYMin > dstYMax { 105 // TODO: check if this (and below) works when src2dst[4] < 0. 106 dstYMin, dstYMax = dstYMax, dstYMin 107 } 108 dYMin := int(math.Floor(dstYMin)) 109 dYMax := int(math.Ceil(dstYMax)) 110 111 render.SetPictureTransform(t.s.xc, t.xp, render.Transform{ 112 f64ToFixed(1 / src2dst[0]), 0, 0, 113 0, f64ToFixed(1 / src2dst[4]), 0, 114 0, 0, 1 << 16, 115 }) 116 render.Composite(t.s.xc, renderOp(op), t.xp, 0, xp, 117 int16(sr.Min.X), int16(sr.Min.Y), // SrcX, SrcY, 118 0, 0, // MaskX, MaskY, 119 int16(dXMin), int16(dYMin), // DstX, DstY, 120 uint16(dXMax-dXMin), uint16(dYMax-dYMin), // Width, Height, 121 ) 122 return 123 } 124 125 // The X11/Render transform matrix maps from destination pixels to source 126 // pixels, so we invert src2dst. 127 dst2src := inv(src2dst) 128 render.SetPictureTransform(t.s.xc, t.xp, render.Transform{ 129 f64ToFixed(dst2src[0]), f64ToFixed(dst2src[1]), f64ToFixed(dst2src[2]), 130 f64ToFixed(dst2src[3]), f64ToFixed(dst2src[4]), f64ToFixed(dst2src[5]), 131 0, 0, 1 << 16, 132 }) 133 134 if op == draw.Src { 135 // render.Composite visits every dst-space pixel in the rectangle 136 // defined by its args DstX, DstY, Width, Height. That axis-aligned 137 // bounding box (AABB) must contain the transformation of the sr 138 // rectangle in src-space to a quad in dst-space, but it need not be 139 // the smallest possible AABB. 140 // 141 // In any case, for arbitrary src2dst affine transformations, which 142 // include rotations, this means that a naive render.Composite call 143 // will affect those pixels inside the AABB but outside the quad. For 144 // the draw.Src operator, this means that pixels in that AABB can be 145 // incorrectly set to zero. 146 // 147 // Instead, we implement the draw.Src operator as two render.Composite 148 // calls. The first one (using the PictOpOutReverse operator) clears 149 // the dst-space quad but leaves pixels outside that quad (but inside 150 // the AABB) untouched. The second one (using the PictOpOver operator) 151 // fills in the quad and again does not touch the pixels outside. 152 // 153 // What X11/Render calls PictOpOutReverse is also known as dst-out. See 154 // http://www.w3.org/TR/SVGCompositing/examples/compop-porterduff-examples.png 155 // for a visualization. 156 // 157 // The arguments to this render.Composite call are identical to the 158 // second one call below, other than the compositing operator. 159 // 160 // TODO: the source picture for this call needs to be fully opaque even 161 // if t.xp isn't. 162 render.Composite(t.s.xc, render.PictOpOutReverse, t.xp, 0, xp, 163 int16(sr.Min.X), int16(sr.Min.Y), 0, 0, 0, 0, uint16(w), uint16(h), 164 ) 165 } 166 167 // TODO: tighten the (0, 0)-(w, h) dst rectangle. As it is, we're 168 // compositing an unnecessarily large number of pixels. 169 170 render.Composite(t.s.xc, render.PictOpOver, t.xp, 0, xp, 171 int16(sr.Min.X), int16(sr.Min.Y), // SrcX, SrcY, 172 0, 0, // MaskX, MaskY, 173 0, 0, // DstX, DstY, 174 uint16(w), uint16(h), // Width, Height, 175 ) 176 } 177 178 func renderOp(op draw.Op) byte { 179 if op == draw.Src { 180 return render.PictOpSrc 181 } 182 return render.PictOpOver 183 }