107 lines
2.7 KiB
Go
107 lines
2.7 KiB
Go
|
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!")
|
||
|
}
|
||
|
}
|