Start work on transmitting RGB images

This commit is contained in:
Kovid Goyal 2023-01-02 10:57:55 +05:30
parent df06578c2d
commit 143fd6e4dd
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C

View File

@ -0,0 +1,141 @@
// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>
package images
import (
"fmt"
"image"
"image/color"
)
var _ = fmt.Print
type NRGBColor struct {
R, G, B uint8
}
func (c NRGBColor) RGBA() (r, g, b, a uint32) {
r = uint32(c.R)
r |= r << 8
g = uint32(c.G)
g |= g << 8
b = uint32(c.B)
b |= b << 8
a = 65280 // ( 255 << 8 )
return
}
// NRGB is an in-memory image whose At method returns NRGBColor values.
type NRGB 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)*4].
Pix []uint8
// Stride is the Pix stride (in bytes) between vertically adjacent pixels.
Stride int
// Rect is the image's bounds.
Rect image.Rectangle
}
func nrgbModel(c color.Color) color.Color {
if _, ok := c.(NRGBColor); ok {
return c
}
r, g, b, a := c.RGBA()
if a == 0xffff {
return NRGBColor{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8)}
}
if a == 0 {
return NRGBColor{0, 0, 0}
}
// Since Color.RGBA returns an alpha-premultiplied color, we should have r <= a && g <= a && b <= a.
r = (r * 0xffff) / a
g = (g * 0xffff) / a
b = (b * 0xffff) / a
return NRGBColor{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8)}
}
var NRGBModel color.Model = color.ModelFunc(nrgbModel)
func (p *NRGB) ColorModel() color.Model { return NRGBModel }
func (p *NRGB) Bounds() image.Rectangle { return p.Rect }
func (p *NRGB) At(x, y int) color.Color {
return p.NRGBAt(x, y)
}
func (p *NRGB) NRGBAt(x, y int) NRGBColor {
if !(image.Point{x, y}.In(p.Rect)) {
return NRGBColor{}
}
i := p.PixOffset(x, y)
s := p.Pix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857
return NRGBColor{s[0], s[1], s[2]}
}
// PixOffset returns the index of the first element of Pix that corresponds to
// the pixel at (x, y).
func (p *NRGB) PixOffset(x, y int) int {
return (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*4
}
func (p *NRGB) Set(x, y int, c color.Color) {
if !(image.Point{x, y}.In(p.Rect)) {
return
}
i := p.PixOffset(x, y)
c1 := NRGBModel.Convert(c).(NRGBColor)
s := p.Pix[i : i+3 : i+3] // Small cap improves performance, see https://golang.org/issue/27857
s[0] = c1.R
s[1] = c1.G
s[2] = c1.B
}
func (p *NRGB) SetRGBA64(x, y int, c color.RGBA64) {
if !(image.Point{x, y}.In(p.Rect)) {
return
}
r, g, b, a := uint32(c.R), uint32(c.G), uint32(c.B), uint32(c.A)
if (a != 0) && (a != 0xffff) {
r = (r * 0xffff) / a
g = (g * 0xffff) / a
b = (b * 0xffff) / a
}
i := p.PixOffset(x, y)
s := p.Pix[i : i+3 : i+3] // Small cap improves performance, see https://golang.org/issue/27857
s[0] = uint8(r >> 8)
s[1] = uint8(g >> 8)
s[2] = uint8(b >> 8)
}
func (p *NRGB) SetNRGBA(x, y int, c color.NRGBA) {
if !(image.Point{x, y}.In(p.Rect)) {
return
}
i := p.PixOffset(x, y)
s := p.Pix[i : i+3 : i+3] // Small cap improves performance, see https://golang.org/issue/27857
s[0] = c.R
s[1] = c.G
s[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 *NRGB) 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 &NRGB{}
}
i := p.PixOffset(r.Min.X, r.Min.Y)
return &NRGB{
Pix: p.Pix[i:],
Stride: p.Stride,
Rect: r,
}
}
// Opaque scans the entire image and reports whether it is fully opaque.
func (p *NRGB) Opaque() bool { return true }