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} }