From: ChaiShushan Date: Thu, 12 Dec 2013 19:24:27 +0000 (-0800) Subject: image: add RGB and RGB48 X-Git-Tag: go1.3beta1~1255 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=e5902fc70f1a50920ce0ac95ffbed3a9af2b80b5;p=gostls13.git image: add RGB and RGB48 R=golang-dev, r, nigeltao CC=golang-dev https://golang.org/cl/13239051 --- diff --git a/src/pkg/image/color/color.go b/src/pkg/image/color/color.go index ff596a76a3..61114bd918 100644 --- a/src/pkg/image/color/color.go +++ b/src/pkg/image/color/color.go @@ -15,6 +15,33 @@ type Color interface { RGBA() (r, g, b, a uint32) } +// RGB represents a traditional 24-bit fully opaque color, +// having 8 bits for each of red, green and blue. +type RGB struct { + R, G, B uint8 +} + +func (c RGB) 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 = 0xFFFF + return +} + +// RGB48 represents a 48-bit fully opaque color, +// having 16 bits for each of red, green and blue. +type RGB48 struct { + R, G, B uint16 +} + +func (c RGB48) RGBA() (r, g, b, a uint32) { + return uint32(c.R), uint32(c.G), uint32(c.B), 0xFFFF +} + // RGBA represents a traditional 32-bit alpha-premultiplied color, // having 8 bits for each of red, green, blue and alpha. type RGBA struct { @@ -154,6 +181,8 @@ func (m *modelFunc) Convert(c Color) Color { // Models for the standard color types. var ( + RGBModel Model = ModelFunc(rgbModel) + RGB48Model Model = ModelFunc(rgb48Model) RGBAModel Model = ModelFunc(rgbaModel) RGBA64Model Model = ModelFunc(rgba64Model) NRGBAModel Model = ModelFunc(nrgbaModel) @@ -164,6 +193,22 @@ var ( Gray16Model Model = ModelFunc(gray16Model) ) +func rgbModel(c Color) Color { + if _, ok := c.(RGB); ok { + return c + } + r, g, b, _ := c.RGBA() + return RGB{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8)} +} + +func rgb48Model(c Color) Color { + if _, ok := c.(RGB48); ok { + return c + } + r, g, b, _ := c.RGBA() + return RGB48{uint16(r), uint16(g), uint16(b)} +} + func rgbaModel(c Color) Color { if _, ok := c.(RGBA); ok { return c diff --git a/src/pkg/image/image.go b/src/pkg/image/image.go index 32a89ef34c..9976143540 100644 --- a/src/pkg/image/image.go +++ b/src/pkg/image/image.go @@ -56,6 +56,176 @@ type PalettedImage interface { Image } +// RGB is an in-memory image whose At method returns color.RGB values. +type RGB struct { + // Pix holds the image's pixels, in R, G, B 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 Rectangle +} + +func (p *RGB) ColorModel() color.Model { return color.RGBModel } + +func (p *RGB) Bounds() Rectangle { return p.Rect } + +func (p *RGB) At(x, y int) color.Color { + if !(Point{x, y}.In(p.Rect)) { + return color.RGB{} + } + i := p.PixOffset(x, y) + return color.RGB{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 *RGB) PixOffset(x, y int) int { + return (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*3 +} + +func (p *RGB) Set(x, y int, c color.Color) { + if !(Point{x, y}.In(p.Rect)) { + return + } + i := p.PixOffset(x, y) + c1 := color.RGBModel.Convert(c).(color.RGB) + p.Pix[i+0] = c1.R + p.Pix[i+1] = c1.G + p.Pix[i+2] = c1.B +} + +func (p *RGB) SetRGB(x, y int, c color.RGB) { + if !(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 *RGB) SubImage(r Rectangle) 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 &RGB{} + } + i := p.PixOffset(r.Min.X, r.Min.Y) + return &RGB{ + Pix: p.Pix[i:], + Stride: p.Stride, + Rect: r, + } +} + +// Opaque scans the entire image and reports whether it is fully opaque. +func (p *RGB) Opaque() bool { + return true +} + +// NewRGB returns a new RGB with the given bounds. +func NewRGB(r Rectangle) *RGB { + w, h := r.Dx(), r.Dy() + buf := make([]uint8, 3*w*h) + return &RGB{buf, 3 * w, r} +} + +// RGB48 is an in-memory image whose At method returns color.RGB48 values. +type RGB48 struct { + // Pix holds the image's pixels, in R, G, B order and big-endian format. The pixel at + // (x, y) starts at Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*6]. + Pix []uint8 + // Stride is the Pix stride (in bytes) between vertically adjacent pixels. + Stride int + // Rect is the image's bounds. + Rect Rectangle +} + +func (p *RGB48) ColorModel() color.Model { return color.RGB48Model } + +func (p *RGB48) Bounds() Rectangle { return p.Rect } + +func (p *RGB48) At(x, y int) color.Color { + if !(Point{x, y}.In(p.Rect)) { + return color.RGB48{} + } + i := p.PixOffset(x, y) + return color.RGB48{ + uint16(p.Pix[i+0])<<8 | uint16(p.Pix[i+1]), + uint16(p.Pix[i+2])<<8 | uint16(p.Pix[i+3]), + uint16(p.Pix[i+4])<<8 | uint16(p.Pix[i+5]), + } +} + +// PixOffset returns the index of the first element of Pix that corresponds to +// the pixel at (x, y). +func (p *RGB48) PixOffset(x, y int) int { + return (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*6 +} + +func (p *RGB48) Set(x, y int, c color.Color) { + if !(Point{x, y}.In(p.Rect)) { + return + } + i := p.PixOffset(x, y) + c1 := color.RGB48Model.Convert(c).(color.RGB48) + p.Pix[i+0] = uint8(c1.R >> 8) + p.Pix[i+1] = uint8(c1.R) + p.Pix[i+2] = uint8(c1.G >> 8) + p.Pix[i+3] = uint8(c1.G) + p.Pix[i+4] = uint8(c1.B >> 8) + p.Pix[i+5] = uint8(c1.B) +} + +func (p *RGB48) SetRGB48(x, y int, c color.RGB48) { + if !(Point{x, y}.In(p.Rect)) { + return + } + i := p.PixOffset(x, y) + p.Pix[i+0] = uint8(c.R >> 8) + p.Pix[i+1] = uint8(c.R) + p.Pix[i+2] = uint8(c.G >> 8) + p.Pix[i+3] = uint8(c.G) + p.Pix[i+4] = uint8(c.B >> 8) + p.Pix[i+5] = uint8(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 *RGB48) SubImage(r Rectangle) 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 &RGB48{} + } + i := p.PixOffset(r.Min.X, r.Min.Y) + return &RGB48{ + Pix: p.Pix[i:], + Stride: p.Stride, + Rect: r, + } +} + +// Opaque scans the entire image and reports whether it is fully opaque. +func (p *RGB48) Opaque() bool { + return true +} + +// NewRGB48 returns a new RGB48 with the given bounds. +func NewRGB48(r Rectangle) *RGB48 { + w, h := r.Dx(), r.Dy() + pix := make([]uint8, 6*w*h) + return &RGB48{pix, 6 * w, r} +} + // RGBA is an in-memory image whose At method returns color.RGBA values. type RGBA struct { // Pix holds the image's pixels, in R, G, B, A order. The pixel at diff --git a/src/pkg/image/image_test.go b/src/pkg/image/image_test.go index 799c1a7a11..1f4105bf03 100644 --- a/src/pkg/image/image_test.go +++ b/src/pkg/image/image_test.go @@ -24,6 +24,8 @@ func cmp(t *testing.T, cm color.Model, c0, c1 color.Color) bool { func TestImage(t *testing.T) { testImage := []image{ + NewRGB(Rect(0, 0, 10, 10)), + NewRGB48(Rect(0, 0, 10, 10)), NewRGBA(Rect(0, 0, 10, 10)), NewRGBA64(Rect(0, 0, 10, 10)), NewNRGBA(Rect(0, 0, 10, 10)), @@ -97,6 +99,7 @@ func Test16BitsPerColorChannel(t *testing.T) { } } testImage := []image{ + NewRGB48(Rect(0, 0, 10, 10)), NewRGBA64(Rect(0, 0, 10, 10)), NewNRGBA64(Rect(0, 0, 10, 10)), NewAlpha16(Rect(0, 0, 10, 10)),