mirror of
https://github.com/amitbet/vnc2video.git
synced 2025-04-27 10:20:53 +00:00
113 lines
2.7 KiB
Go
113 lines
2.7 KiB
Go
package vnc2video
|
|
|
|
import (
|
|
"image"
|
|
"image/color"
|
|
)
|
|
|
|
// RGBA is an in-memory image whose At method returns color.RGBA values.
|
|
type RGBImage struct {
|
|
// Pix holds the image's pixels, in R, G, B, A order. The pixel at
|
|
// (x, y) starts at Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*3].
|
|
Pix []uint8
|
|
// Stride is the Pix stride (in bytes) between vertically adjacent pixels.
|
|
Stride int
|
|
// Rect is the image's bounds.
|
|
Rect image.Rectangle
|
|
}
|
|
|
|
type RGBColor struct {
|
|
R, G, B uint8
|
|
}
|
|
|
|
func (c RGBColor) RGBA() (r, g, b, a uint32) {
|
|
return uint32(c.R), uint32(c.G), uint32(c.B), 1
|
|
}
|
|
|
|
func (p *RGBImage) ColorModel() color.Model { return nil }
|
|
|
|
func (p *RGBImage) Bounds() image.Rectangle { return p.Rect }
|
|
|
|
func (p *RGBImage) At(x, y int) color.Color {
|
|
col := p.RGBAt(x, y)
|
|
return color.RGBA{col.R, col.G, col.B, 1}
|
|
}
|
|
|
|
func (p *RGBImage) RGBAt(x, y int) *RGBColor {
|
|
if !(image.Point{x, y}.In(p.Rect)) {
|
|
return &RGBColor{}
|
|
}
|
|
i := p.PixOffset(x, y)
|
|
return &RGBColor{p.Pix[i+0], p.Pix[i+1], p.Pix[i+2]}
|
|
}
|
|
|
|
// PixOffset returns the index of the first element of Pix that corresponds to
|
|
// the pixel at (x, y).
|
|
func (p *RGBImage) PixOffset(x, y int) int {
|
|
return (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*3
|
|
}
|
|
|
|
func (p *RGBImage) Set(x, y int, c color.Color) {
|
|
if !(image.Point{x, y}.In(p.Rect)) {
|
|
return
|
|
}
|
|
i := p.PixOffset(x, y)
|
|
c1 := color.RGBAModel.Convert(c).(color.RGBA)
|
|
p.Pix[i+0] = c1.R
|
|
p.Pix[i+1] = c1.G
|
|
p.Pix[i+2] = c1.B
|
|
}
|
|
|
|
func (p *RGBImage) SetRGB(x, y int, c color.RGBA) {
|
|
if !(image.Point{x, y}.In(p.Rect)) {
|
|
return
|
|
}
|
|
i := p.PixOffset(x, y)
|
|
p.Pix[i+0] = c.R
|
|
p.Pix[i+1] = c.G
|
|
p.Pix[i+2] = c.B
|
|
}
|
|
|
|
// SubImage returns an image representing the portion of the image p visible
|
|
// through r. The returned value shares pixels with the original image.
|
|
func (p *RGBImage) SubImage(r image.Rectangle) image.Image {
|
|
r = r.Intersect(p.Rect)
|
|
// If r1 and r2 are Rectangles, r1.Intersect(r2) is not guaranteed to be inside
|
|
// either r1 or r2 if the intersection is empty. Without explicitly checking for
|
|
// this, the Pix[i:] expression below can panic.
|
|
if r.Empty() {
|
|
return &RGBImage{}
|
|
}
|
|
i := p.PixOffset(r.Min.X, r.Min.Y)
|
|
return &RGBImage{
|
|
Pix: p.Pix[i:],
|
|
Stride: p.Stride,
|
|
Rect: r,
|
|
}
|
|
}
|
|
|
|
// Opaque scans the entire image and reports whether it is fully opaque.
|
|
func (p *RGBImage) Opaque() bool {
|
|
if p.Rect.Empty() {
|
|
return true
|
|
}
|
|
i0, i1 := 3, p.Rect.Dx()*3
|
|
for y := p.Rect.Min.Y; y < p.Rect.Max.Y; y++ {
|
|
for i := i0; i < i1; i += 3 {
|
|
if p.Pix[i] != 0xff {
|
|
return false
|
|
}
|
|
}
|
|
i0 += p.Stride
|
|
i1 += p.Stride
|
|
}
|
|
return true
|
|
}
|
|
|
|
// NewRGBA returns a new RGBA image with the given bounds.
|
|
func NewRGBImage(r image.Rectangle) *RGBImage {
|
|
w, h := r.Dx(), r.Dy()
|
|
buf := make([]uint8, 3*w*h)
|
|
return &RGBImage{buf, 3 * w, r}
|
|
}
|