package xcanvas import "image" import "image/color" import "github.com/jezek/xgbutil/xgraphics" import "git.tebibyte.media/tomo/tomo/canvas" // Texture is a read-only image texture that can be quickly written to a canvas. // It must be closed manually after use. type Texture struct { pix []uint8 stride int rect image.Rectangle transparent bool } // NewTextureFrom creates a new texture from a source image. func NewTextureFrom (source image.Image) *Texture { bounds := source.Bounds() texture := &Texture { pix: make([]uint8, bounds.Dx() * bounds.Dy() * 4), stride: bounds.Dx() * 4, rect: bounds.Sub(bounds.Min), } index := 0 var point image.Point for point.Y = bounds.Min.Y; point.Y < bounds.Max.Y; point.Y ++ { for point.X = bounds.Min.X; point.X < bounds.Max.X; point.X ++ { r, g, b, a := source.At(point.X, point.Y).RGBA() texture.pix[index + 0] = uint8(b >> 8) texture.pix[index + 1] = uint8(g >> 8) texture.pix[index + 2] = uint8(r >> 8) texture.pix[index + 3] = uint8(a >> 8) index += 4 if a != 0xFFFF { texture.transparent = true } }} return texture } func (this *Texture) BGRAAt (x, y int) xgraphics.BGRA { if !(image.Point{ x, y }.In(this.rect)) { return xgraphics.BGRA { } } index := this.PixOffset(x, y) return xgraphics.BGRA { B: this.pix[index ], G: this.pix[index + 1], R: this.pix[index + 2], A: this.pix[index + 3], } } func (this *Texture) At (x, y int) color.Color { return this.BGRAAt(x, y) } // Bounds returns the bounding rectangle of this texture. func (this *Texture) Bounds () image.Rectangle { return this.rect } func (this *Texture) ColorModel () color.Model { return xgraphics.BGRAModel } // Opaque reports whether or not the texture is fully opaque. func (this *Texture) Opaque () bool { return !this.transparent } func (this *Texture) PixOffset (x, y int) int { x = wrap(x, this.rect.Min.X, this.rect.Max.X) y = wrap(y, this.rect.Min.Y, this.rect.Max.Y) return x * 4 + y * this.stride } // Close frees the texture from memory. func (this *Texture) Close () error { // i lied we dont actually need to close this, but we will once this // texture resides on the x server or in video memory. return nil } // SubTexture returns a subset of this texture that points to the same data. func (this *Texture) SubTexture (bounds image.Rectangle) canvas.Texture { clipped := *this clipped.rect = bounds return &clipped } // AssertTexture checks if a given canvas.Texture is a texture from this package. func AssertTexture (unknown canvas.Texture) *Texture { if unknown == nil { return nil } if tx, ok := unknown.(*Texture); ok { return tx } else { panic("foregin texture implementation, i did not make this!") } }