func (t *textureImpl) draw(xp render.Picture, src2dst *f64.Aff3, sr image.Rectangle, op draw.Op, w, h int, opts *screen.DrawOptions) { // TODO: honor sr.Max // The XTransform matrix maps from destination pixels to source // pixels, so we invert src2dst. dst2src := inv(src2dst) err := render.SetPictureTransformChecked(t.s.xc, t.xp, render.Transform{ f64ToFixed(dst2src[0]), f64ToFixed(dst2src[1]), f64ToFixed(dst2src[2]), f64ToFixed(dst2src[3]), f64ToFixed(dst2src[4]), f64ToFixed(dst2src[5]), f64ToFixed(0), f64ToFixed(0), f64ToFixed(1), }).Check() if err != nil { panic(fmt.Errorf("x11driver: cannot transform picture: %v", err)) } err = render.SetPictureFilterChecked(t.s.xc, t.xp, uint16(len("bilinear")), "bilinear", nil).Check() if err != nil { panic(fmt.Errorf("x11driver: cannot filter picture: %v", err)) } render.Composite(t.s.xc, renderOp(op), t.xp, 0, xp, int16(sr.Min.X), int16(sr.Min.Y), // SrcX, SrcY, 0, 0, // MaskX, MaskY, 0, 0, // DstX, DstY, uint16(w), uint16(h), // Width, Height, ) }
func (t *textureImpl) draw(xp render.Picture, src2dst *f64.Aff3, sr image.Rectangle, op draw.Op, w, h int, opts *screen.DrawOptions) { // TODO: honor sr.Max t.renderMu.Lock() defer t.renderMu.Unlock() // For simple copies and scales, the inverse matrix is trivial to compute, // and we do not need the "Src becomes OutReverse plus Over" dance (see // below). Thus, draw can be one render.SetPictureTransform call and then // one render.Composite call, regardless of whether or not op is Src. if src2dst[1] == 0 && src2dst[3] == 0 { dstXMin := float64(sr.Min.X)*src2dst[0] + src2dst[2] dstXMax := float64(sr.Max.X)*src2dst[0] + src2dst[2] if dstXMin > dstXMax { // TODO: check if this (and below) works when src2dst[0] < 0. dstXMin, dstXMax = dstXMax, dstXMin } dXMin := int(math.Floor(dstXMin)) dXMax := int(math.Ceil(dstXMax)) dstYMin := float64(sr.Min.Y)*src2dst[4] + src2dst[5] dstYMax := float64(sr.Max.Y)*src2dst[4] + src2dst[5] if dstYMin > dstYMax { // TODO: check if this (and below) works when src2dst[4] < 0. dstYMin, dstYMax = dstYMax, dstYMin } dYMin := int(math.Floor(dstYMin)) dYMax := int(math.Ceil(dstYMax)) render.SetPictureTransform(t.s.xc, t.xp, render.Transform{ f64ToFixed(1 / src2dst[0]), 0, 0, 0, f64ToFixed(1 / src2dst[4]), 0, 0, 0, 1 << 16, }) render.Composite(t.s.xc, renderOp(op), t.xp, 0, xp, int16(sr.Min.X), int16(sr.Min.Y), // SrcX, SrcY, 0, 0, // MaskX, MaskY, int16(dXMin), int16(dYMin), // DstX, DstY, uint16(dXMax-dXMin), uint16(dYMax-dYMin), // Width, Height, ) return } // The X11/Render transform matrix maps from destination pixels to source // pixels, so we invert src2dst. dst2src := inv(src2dst) render.SetPictureTransform(t.s.xc, t.xp, render.Transform{ f64ToFixed(dst2src[0]), f64ToFixed(dst2src[1]), f64ToFixed(dst2src[2]), f64ToFixed(dst2src[3]), f64ToFixed(dst2src[4]), f64ToFixed(dst2src[5]), 0, 0, 1 << 16, }) if op == draw.Src { // render.Composite visits every dst-space pixel in the rectangle // defined by its args DstX, DstY, Width, Height. That axis-aligned // bounding box (AABB) must contain the transformation of the sr // rectangle in src-space to a quad in dst-space, but it need not be // the smallest possible AABB. // // In any case, for arbitrary src2dst affine transformations, which // include rotations, this means that a naive render.Composite call // will affect those pixels inside the AABB but outside the quad. For // the draw.Src operator, this means that pixels in that AABB can be // incorrectly set to zero. // // Instead, we implement the draw.Src operator as two render.Composite // calls. The first one (using the PictOpOutReverse operator) clears // the dst-space quad but leaves pixels outside that quad (but inside // the AABB) untouched. The second one (using the PictOpOver operator) // fills in the quad and again does not touch the pixels outside. // // What X11/Render calls PictOpOutReverse is also known as dst-out. See // http://www.w3.org/TR/SVGCompositing/examples/compop-porterduff-examples.png // for a visualization. // // The arguments to this render.Composite call are identical to the // second one call below, other than the compositing operator. // // TODO: the source picture for this call needs to be fully opaque even // if t.xp isn't. render.Composite(t.s.xc, render.PictOpOutReverse, t.xp, 0, xp, int16(sr.Min.X), int16(sr.Min.Y), 0, 0, 0, 0, uint16(w), uint16(h), ) } // TODO: tighten the (0, 0)-(w, h) dst rectangle. As it is, we're // compositing an unnecessarily large number of pixels. render.Composite(t.s.xc, render.PictOpOver, t.xp, 0, xp, int16(sr.Min.X), int16(sr.Min.Y), // SrcX, SrcY, 0, 0, // MaskX, MaskY, 0, 0, // DstX, DstY, uint16(w), uint16(h), // Width, Height, ) }
func (t *textureImpl) draw(xp render.Picture, src2dst *f64.Aff3, sr image.Rectangle, op draw.Op, w, h int, opts *screen.DrawOptions) { // TODO: honor sr.Max // TODO: use a mutex a la https://go-review.googlesource.com/14861, so that // the render.Xxx calls in this method are effectively one atomic // operation, in case multiple concurrent Draw(etc, t, etc) calls occur. // TODO: recognize simple copies or scales, which do not need the "Src // becomes OutReverse plus Over" dance and can be one simple // render.Composite(etc, renderOp(op), etc) call, regardless of whether or // not op is Src. // The XTransform matrix maps from destination pixels to source // pixels, so we invert src2dst. dst2src := inv(src2dst) render.SetPictureTransform(t.s.xc, t.xp, render.Transform{ f64ToFixed(dst2src[0]), f64ToFixed(dst2src[1]), f64ToFixed(dst2src[2]), f64ToFixed(dst2src[3]), f64ToFixed(dst2src[4]), f64ToFixed(dst2src[5]), f64ToFixed(0), f64ToFixed(0), f64ToFixed(1), }) if op == draw.Src { // render.Composite visits every dst-space pixel in the rectangle // defined by its args DstX, DstY, Width, Height. That axis-aligned // bounding box (AABB) must contain the transformation of the sr // rectangle in src-space to a quad in dst-space, but it need not be // the smallest possible AABB. // // In any case, for arbitrary src2dst affine transformations, which // include rotations, this means that a naive render.Composite call // will affect those pixels inside the AABB but outside the quad. For // the draw.Src operator, this means that pixels in that AABB can be // incorrectly set to zero. // // Instead, we implement the draw.Src operator as two render.Composite // calls. The first one (using the PictOpOutReverse operator) clears // the dst-space quad but leaves pixels outside that quad (but inside // the AABB) untouched. The second one (using the PictOpOver operator) // fills in the quad and again does not touch the pixels outside. // // What X11/Render calls PictOpOutReverse is also known as dst-out. See // http://www.w3.org/TR/SVGCompositing/examples/compop-porterduff-examples.png // for a visualization. // // The arguments to this render.Composite call are identical to the // second one call below, other than the compositing operator. // // TODO: the source picture for this call needs to be fully opaque even // if t.xp isn't. render.Composite(t.s.xc, render.PictOpOutReverse, t.xp, 0, xp, int16(sr.Min.X), int16(sr.Min.Y), 0, 0, 0, 0, uint16(w), uint16(h), ) } // TODO: tighten the (0, 0)-(w, h) dst rectangle. As it is, we're // compositing an unnecessarily large number of pixels. render.Composite(t.s.xc, render.PictOpOver, t.xp, 0, xp, int16(sr.Min.X), int16(sr.Min.Y), // SrcX, SrcY, 0, 0, // MaskX, MaskY, 0, 0, // DstX, DstY, uint16(w), uint16(h), // Width, Height, ) }