├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── canvas.go ├── canvas_test.go ├── examples ├── basic │ └── main.go ├── image │ └── main.go └── strobe │ └── main.go ├── matrix.go └── matrix_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "vendor/rpi_ws281x"] 2 | path = vendor/rpi_ws281x 3 | url = https://github.com/jgarff/rpi_ws281x 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Máximo Cuadros 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go-rpi-ws281x [![GoDoc](https://godoc.org/github.com/mcuadros/go-rpi-ws281x?status.svg)](http://godoc.org/github.com/mcuadros/go-rpi-ws281x) 2 | 3 | golang binding for [rpi_ws281x](https://github.com/jgarff/rpi_ws281x), userspace Raspberry Pi PWM library for WS281X LEDs. Supports any Raspberry and WS2812, SK6812RGB and [SK6812RGBW](https://www.adafruit.com/category/168?q=SK6812RGBW&) LEDs strips, this includes [Unicorn pHAT](https://shop.pimoroni.com/products/unicorn-phat) and [NeoPixels](https://www.adafruit.com/category/168) 4 | 5 | 6 | Installation 7 | ------------ 8 | 9 | The recommended way to install `go-rpi-ws281x` is: 10 | 11 | ```sh 12 | go get github.com/mcuadros/go-rpi-ws281x 13 | cd $GOPATH/src/github.com/mcuadros/go-rpi-ws281x/vendor/rpi_ws281x 14 | scons 15 | ``` 16 | 17 | Requires having install golang and scons (on raspbian, apt-get install scons). 18 | 19 | Examples 20 | -------- 21 | 22 | ```go 23 | // create a new canvas with the given width and height, and the config, in this 24 | // case the configuration is for a Unicorn pHAT (8x4 pixels matrix) with the 25 | // default configuration 26 | c, _ := ws281x.NewCanvas(8, 4, &ws281x.DefaultConfig) 27 | 28 | 29 | // initialize the canvas and the matrix 30 | c.Initialize() 31 | 32 | // since ws281x implements image.Image any function like draw.Draw from the std 33 | // library may be used with it. 34 | // 35 | // now we copy a white image into the ws281x.Canvas, this turn on all the leds 36 | // to white 37 | draw.Draw(c, c.Bounds(), image.NewUniform(color.White), image.ZP, draw.Over) 38 | 39 | // render and sleep to see the leds on 40 | c.Render() 41 | time.Sleep(time.Second * 5) 42 | 43 | // don't forget close the canvas, if not you leds may remain on 44 | c.Close() 45 | ``` 46 | 47 | Check the folder [`examples`](https://github.com/mcuadros/go-rpi-ws281x/tree/master/examples) folder for more examples 48 | 49 | License 50 | ------- 51 | 52 | MIT, see [LICENSE](LICENSE) -------------------------------------------------------------------------------- /canvas.go: -------------------------------------------------------------------------------- 1 | package ws281x 2 | 3 | import ( 4 | "image" 5 | "image/color" 6 | "image/draw" 7 | ) 8 | 9 | // Canvas is a image.Image representation of a WS281x matrix, it implements 10 | // image.Image interface and can be used with draw.Draw for example 11 | type Canvas struct { 12 | w, h int 13 | m Matrix 14 | closed bool 15 | } 16 | 17 | // NewCanvas returns a new Canvas using the given width and height and creates 18 | // a new WS281x matrix using the given config 19 | func NewCanvas(w, h int, config *HardwareConfig) (*Canvas, error) { 20 | m, err := NewWS281x(w*h, config) 21 | if err != nil { 22 | return nil, err 23 | } 24 | 25 | return &Canvas{ 26 | w: w, 27 | h: h, 28 | m: m, 29 | }, nil 30 | } 31 | 32 | // Initialize initialize the matrix and the canvas 33 | func (c *Canvas) Initialize() error { 34 | return c.m.Initialize() 35 | } 36 | 37 | // Render update the display with the data from the LED buffer 38 | func (c *Canvas) Render() error { 39 | return c.m.Render() 40 | } 41 | 42 | // ColorModel returns the canvas' color model, always color.RGBAModel 43 | func (c *Canvas) ColorModel() color.Model { 44 | return color.RGBAModel 45 | } 46 | 47 | // Bounds return the topology of the Canvas 48 | func (c *Canvas) Bounds() image.Rectangle { 49 | return image.Rect(0, 0, c.w, c.h) 50 | } 51 | 52 | // At returns the color of the pixel at (x, y) 53 | func (c *Canvas) At(x, y int) color.Color { 54 | return c.m.At(c.position(x, y)) 55 | } 56 | 57 | // Set set LED at position x,y to the provided 24-bit color value 58 | func (c *Canvas) Set(x, y int, color color.Color) { 59 | c.m.Set(c.position(x, y), color) 60 | } 61 | 62 | func (c *Canvas) position(x, y int) int { 63 | return x + (y * c.w) 64 | } 65 | 66 | // Clear set all the leds on the matrix with color.Black 67 | func (c *Canvas) Clear() error { 68 | draw.Draw(c, c.Bounds(), &image.Uniform{color.Black}, image.ZP, draw.Src) 69 | return c.m.Render() 70 | } 71 | 72 | // Close clears the matrix and close the matrix 73 | func (c *Canvas) Close() error { 74 | c.Clear() 75 | return c.m.Close() 76 | } 77 | 78 | type Matrix interface { 79 | Initialize() error 80 | At(position int) color.Color 81 | Set(position int, c color.Color) 82 | Render() error 83 | Close() error 84 | } 85 | -------------------------------------------------------------------------------- /canvas_test.go: -------------------------------------------------------------------------------- 1 | package ws281x 2 | 3 | import ( 4 | "image/color" 5 | 6 | . "gopkg.in/check.v1" 7 | ) 8 | 9 | type CanvasSuite struct{} 10 | 11 | var _ = Suite(&CanvasSuite{}) 12 | 13 | func (s *CanvasSuite) TestNewCanvas(c *C) { 14 | canvas, err := NewCanvas(5, 42, &DefaultConfig) 15 | c.Assert(err, IsNil) 16 | c.Assert(canvas, NotNil) 17 | c.Assert(canvas.w, Equals, 5) 18 | c.Assert(canvas.h, Equals, 42) 19 | 20 | _, ok := canvas.m.(*WS281x) 21 | c.Assert(ok, Equals, true) 22 | } 23 | 24 | func (s *CanvasSuite) TestInitialize(c *C) { 25 | m := NewMatrixMock() 26 | canvas := &Canvas{m: m} 27 | canvas.Initialize() 28 | 29 | c.Assert(m.called["Initialize"], Equals, true) 30 | } 31 | 32 | func (s *CanvasSuite) TestRender(c *C) { 33 | m := NewMatrixMock() 34 | canvas := &Canvas{m: m} 35 | canvas.Render() 36 | 37 | c.Assert(m.called["Render"], Equals, true) 38 | } 39 | 40 | func (s *CanvasSuite) TestColorModel(c *C) { 41 | canvas := &Canvas{} 42 | 43 | c.Assert(canvas.ColorModel(), Equals, color.RGBAModel) 44 | } 45 | 46 | func (s *CanvasSuite) TestBounds(c *C) { 47 | 48 | canvas := &Canvas{w: 10, h: 20} 49 | 50 | b := canvas.Bounds() 51 | c.Assert(b.Min.X, Equals, 0) 52 | c.Assert(b.Min.Y, Equals, 0) 53 | c.Assert(b.Max.X, Equals, 10) 54 | c.Assert(b.Max.Y, Equals, 20) 55 | } 56 | 57 | func (s *CanvasSuite) TestAt(c *C) { 58 | m := NewMatrixMock() 59 | canvas := &Canvas{w: 10, h: 20, m: m} 60 | canvas.At(5, 15) 61 | 62 | c.Assert(m.called["At"], Equals, 155) 63 | } 64 | 65 | func (s *CanvasSuite) TestSet(c *C) { 66 | m := NewMatrixMock() 67 | canvas := &Canvas{w: 10, h: 20, m: m} 68 | canvas.Set(5, 15, color.White) 69 | 70 | c.Assert(m.called["Set"], Equals, 155) 71 | c.Assert(m.colors[155], Equals, color.White) 72 | } 73 | 74 | func (s *CanvasSuite) TestClear(c *C) { 75 | m := NewMatrixMock() 76 | 77 | canvas := &Canvas{w: 10, h: 20, m: m} 78 | err := canvas.Clear() 79 | c.Assert(err, IsNil) 80 | 81 | for _, px := range m.colors { 82 | c.Assert(px, Equals, color.Black) 83 | } 84 | 85 | c.Assert(m.called["Render"], Equals, true) 86 | } 87 | 88 | func (s *CanvasSuite) TestClose(c *C) { 89 | m := NewMatrixMock() 90 | canvas := &Canvas{w: 10, h: 20, m: m} 91 | err := canvas.Close() 92 | c.Assert(err, IsNil) 93 | 94 | for _, px := range m.colors { 95 | c.Assert(px, Equals, color.Black) 96 | } 97 | 98 | c.Assert(m.called["Render"], Equals, true) 99 | } 100 | 101 | type MatrixMock struct { 102 | called map[string]interface{} 103 | colors []color.Color 104 | } 105 | 106 | func NewMatrixMock() *MatrixMock { 107 | return &MatrixMock{ 108 | called: make(map[string]interface{}, 0), 109 | colors: make([]color.Color, 200), 110 | } 111 | } 112 | 113 | func (m *MatrixMock) Initialize() error { 114 | m.called["Initialize"] = true 115 | return nil 116 | } 117 | 118 | func (m *MatrixMock) At(position int) color.Color { 119 | m.called["At"] = position 120 | return color.Black 121 | } 122 | 123 | func (m *MatrixMock) Set(position int, c color.Color) { 124 | m.called["Set"] = position 125 | m.colors[position] = c 126 | } 127 | 128 | func (m *MatrixMock) Render() error { 129 | m.called["Render"] = true 130 | return nil 131 | } 132 | 133 | func (m *MatrixMock) Close() error { 134 | m.called["Close"] = true 135 | return nil 136 | } 137 | -------------------------------------------------------------------------------- /examples/basic/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "image/color" 6 | "time" 7 | 8 | "github.com/mcuadros/go-rpi-ws281x" 9 | ) 10 | 11 | var ( 12 | pin = flag.Int("gpio-pin", 18, "GPIO pin") 13 | width = flag.Int("width", 4, "LED matrix width") 14 | height = flag.Int("height", 8, "LED matrix height") 15 | brightness = flag.Int("brightness", 64, "Brightness (0-255)") 16 | ) 17 | 18 | func main() { 19 | config := ws281x.DefaultConfig 20 | config.Brightness = *brightness 21 | config.Pin = *pin 22 | 23 | c, err := ws281x.NewCanvas(*width, *height, &config) 24 | fatal(err) 25 | 26 | defer c.Close() 27 | err = c.Initialize() 28 | fatal(err) 29 | 30 | bounds := c.Bounds() 31 | 32 | color := color.RGBA{255, 0, 0, 255} 33 | for y := bounds.Min.Y; y < bounds.Max.Y; y++ { 34 | for x := bounds.Min.X; x < bounds.Max.X; x++ { 35 | c.Set(x, y, color) 36 | c.Render() 37 | time.Sleep(10 * time.Millisecond) 38 | } 39 | } 40 | } 41 | 42 | func init() { 43 | flag.Parse() 44 | } 45 | 46 | func fatal(err error) { 47 | if err != nil { 48 | panic(err) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /examples/image/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "image" 6 | "os" 7 | "time" 8 | 9 | "github.com/mcuadros/go-rpi-ws281x" 10 | 11 | "image/draw" 12 | _ "image/png" 13 | ) 14 | 15 | var ( 16 | pin = flag.Int("gpio-pin", 18, "GPIO pin") 17 | width = flag.Int("width", 8, "LED matrix width") 18 | height = flag.Int("height", 32, "LED matrix height") 19 | brightness = flag.Int("brightness", 64, "Brightness (0-255)") 20 | img = flag.String("image", "", "image path") 21 | ) 22 | 23 | func main() { 24 | f, err := os.Open(*img) 25 | if err != nil { 26 | fatal(err) 27 | } 28 | 29 | m, _, err := image.Decode(f) 30 | if err != nil { 31 | fatal(err) 32 | } 33 | 34 | config := ws281x.DefaultConfig 35 | config.Brightness = *brightness 36 | config.Pin = *pin 37 | 38 | c, err := ws281x.NewCanvas(*width, *height, &config) 39 | if err != nil { 40 | fatal(err) 41 | } 42 | 43 | defer c.Close() 44 | 45 | err = c.Initialize() 46 | fatal(err) 47 | 48 | draw.Draw(c, c.Bounds(), m, image.ZP, draw.Over) 49 | 50 | c.Render() 51 | time.Sleep(time.Second * 5) 52 | } 53 | 54 | func init() { 55 | flag.Parse() 56 | } 57 | 58 | func fatal(err error) { 59 | if err != nil { 60 | panic(err) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /examples/strobe/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "image" 6 | "time" 7 | 8 | "github.com/mcuadros/go-rpi-ws281x" 9 | 10 | "image/color" 11 | "image/draw" 12 | _ "image/png" 13 | ) 14 | 15 | var ( 16 | pin = flag.Int("gpio-pin", 18, "GPIO pin") 17 | width = flag.Int("width", 8, "LED matrix width") 18 | height = flag.Int("height", 32, "LED matrix height") 19 | brightness = flag.Int("brightness", 64, "Brightness (0-255)") 20 | freq = flag.Duration("freq", time.Millisecond*5, "frequency") 21 | ) 22 | 23 | func main() { 24 | config := ws281x.DefaultConfig 25 | config.Brightness = *brightness 26 | config.Pin = *pin 27 | 28 | c, err := ws281x.NewCanvas(*width, *height, &config) 29 | if err != nil { 30 | fatal(err) 31 | } 32 | 33 | defer c.Close() 34 | 35 | err = c.Initialize() 36 | fatal(err) 37 | 38 | colors := []color.Color{ 39 | color.RGBA{255, 0, 0, 255}, 40 | color.Black, 41 | } 42 | 43 | var i int 44 | for { 45 | draw.Draw(c, c.Bounds(), image.NewUniform(colors[i]), image.ZP, draw.Over) 46 | c.Render() 47 | time.Sleep(*freq) 48 | 49 | i++ 50 | if i > 1 { 51 | i = 0 52 | } 53 | } 54 | 55 | } 56 | 57 | func init() { 58 | flag.Parse() 59 | } 60 | 61 | func fatal(err error) { 62 | if err != nil { 63 | panic(err) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /matrix.go: -------------------------------------------------------------------------------- 1 | package ws281x 2 | 3 | /* 4 | #cgo CFLAGS: -std=c99 -Ivendor/rpi_ws281x 5 | #cgo LDFLAGS: -lws2811 -Lvendor/rpi_ws281x 6 | #include 7 | #include 8 | #include 9 | void ws2811_set_led(ws2811_t *ws2811, int ch, int index, uint32_t value) { 10 | ws2811->channel[ch].leds[index] = value; 11 | } 12 | 13 | uint32_t ws2811_get_led(ws2811_t *ws2811, int ch, int index) { 14 | return ws2811->channel[ch].leds[index]; 15 | } 16 | */ 17 | import "C" 18 | 19 | import ( 20 | "fmt" 21 | "image/color" 22 | "unsafe" 23 | ) 24 | 25 | // StripType layout 26 | type StripType int 27 | 28 | const ( 29 | // 4 color R, G, B and W ordering 30 | StripRGBW StripType = 0x18100800 31 | StripRBGW StripType = 0x18100008 32 | StripGRBW StripType = 0x18081000 33 | StripGBRW StripType = 0x18080010 34 | StripBRGW StripType = 0x18001008 35 | StripBGRW StripType = 0x18000810 36 | 37 | // 3 color R, G and B ordering 38 | StripRGB StripType = 0x00100800 39 | StripRBG StripType = 0x00100008 40 | StripGRB StripType = 0x00081000 41 | StripGBR StripType = 0x00080010 42 | StripBRG StripType = 0x00001008 43 | StripBGR StripType = 0x00000810 44 | ) 45 | 46 | // DefaultConfig default WS281x configuration 47 | var DefaultConfig = HardwareConfig{ 48 | Pin: 18, 49 | Frequency: 800000, // 800khz 50 | DMA: 10, 51 | Brightness: 30, 52 | StripType: StripGRB, 53 | } 54 | 55 | // HardwareConfig WS281x configuration 56 | type HardwareConfig struct { 57 | Pin int // GPIO Pin with PWM alternate function, 0 if unused 58 | Frequency int // the frequency of the display signal in hertz, can go as low as 400000 59 | DMA int // the DMA channel to use 60 | Invert bool // specifying if the signal line should be inverted 61 | Channel int // PWM channel to us 62 | Brightness int // brightness value between 0 and 255 63 | StripType StripType // strip color layout 64 | } 65 | 66 | // WS281x matrix representation for ws281x 67 | type WS281x struct { 68 | Config *HardwareConfig 69 | 70 | size int 71 | leds *C.ws2811_t 72 | closed bool 73 | } 74 | 75 | // NewWS281x returns a new matrix using the given size and config 76 | func NewWS281x(size int, config *HardwareConfig) (Matrix, error) { 77 | c := &WS281x{ 78 | Config: config, 79 | 80 | size: size, 81 | leds: (*C.ws2811_t)(C.malloc(C.sizeof_ws2811_t)), 82 | } 83 | 84 | if c.leds == nil { 85 | return nil, fmt.Errorf("unable to allocate memory") 86 | } 87 | 88 | C.memset(unsafe.Pointer(c.leds), 0, C.sizeof_ws2811_t) 89 | 90 | c.initializeChannels() 91 | c.initializeController() 92 | return c, nil 93 | } 94 | 95 | func (c *WS281x) initializeChannels() { 96 | for ch := 0; ch < 2; ch++ { 97 | c.setChannel(0, 0, 0, 0, false, StripRGB) 98 | } 99 | 100 | c.setChannel( 101 | c.Config.Channel, 102 | c.size, 103 | c.Config.Pin, 104 | c.Config.Brightness, 105 | c.Config.Invert, 106 | c.Config.StripType, 107 | ) 108 | } 109 | 110 | func (c *WS281x) setChannel(ch, count, pin, brightness int, inverse bool, t StripType) { 111 | c.leds.channel[ch].count = C.int(count) 112 | c.leds.channel[ch].gpionum = C.int(pin) 113 | c.leds.channel[ch].brightness = C.uint8_t(brightness) 114 | c.leds.channel[ch].invert = C.int(btoi(inverse)) 115 | c.leds.channel[ch].strip_type = C.int(int(t)) 116 | } 117 | 118 | func (c *WS281x) initializeController() { 119 | c.leds.freq = C.uint32_t(c.Config.Frequency) 120 | c.leds.dmanum = C.int(c.Config.DMA) 121 | } 122 | 123 | // Initialize initialize library, must be called once before other functions are 124 | // called. 125 | func (c *WS281x) Initialize() error { 126 | if resp := int(C.ws2811_init(c.leds)); resp != 0 { 127 | return fmt.Errorf("ws2811_init failed with code: %d", resp) 128 | } 129 | 130 | return nil 131 | } 132 | 133 | // Render update the display with the data from the LED buffer 134 | func (c *WS281x) Render() error { 135 | if resp := int(C.ws2811_render(c.leds)); resp != 0 { 136 | return fmt.Errorf("ws2811_render failed with code: %d", resp) 137 | } 138 | 139 | return nil 140 | } 141 | 142 | // At return an Color which allows access to the LED display data as 143 | // if it were a sequence of 24-bit RGB values. 144 | func (c *WS281x) At(position int) color.Color { 145 | color := C.ws2811_get_led(c.leds, C.int(c.Config.Channel), C.int(position)) 146 | return uint32ToColor(uint32(color)) 147 | } 148 | 149 | // Set set LED at position x,y to the provided 24-bit color value. 150 | func (c *WS281x) Set(position int, color color.Color) { 151 | cu := C.uint32_t(colorToUint32(color)) 152 | C.ws2811_set_led(c.leds, C.int(c.Config.Channel), C.int(position), cu) 153 | } 154 | 155 | // Close finalizes the ws281x interface 156 | func (c *WS281x) Close() error { 157 | if c.closed { 158 | return nil 159 | } 160 | 161 | c.closed = true 162 | C.ws2811_fini(c.leds) 163 | return nil 164 | } 165 | 166 | func colorToUint32(c color.Color) uint32 { 167 | // A color's RGBA method returns values in the range [0, 65535] 168 | red, green, blue, alpha := c.RGBA() 169 | 170 | return (alpha>>8)<<24 | (red>>8)<<16 | (green>>8)<<8 | blue>>8 171 | } 172 | 173 | func uint32ToColor(u uint32) color.Color { 174 | return color.RGBA{ 175 | uint8(u>>16) & 255, 176 | uint8(u>>8) & 255, 177 | uint8(u>>0) & 255, 178 | uint8(u>>24) & 255, 179 | } 180 | } 181 | 182 | func btoi(b bool) int { 183 | if b { 184 | return 1 185 | } 186 | 187 | return 0 188 | } 189 | -------------------------------------------------------------------------------- /matrix_test.go: -------------------------------------------------------------------------------- 1 | package ws281x 2 | 3 | import ( 4 | "image/color" 5 | "testing" 6 | 7 | . "gopkg.in/check.v1" 8 | ) 9 | 10 | func Test(t *testing.T) { TestingT(t) } 11 | 12 | type MatrixSuite struct{} 13 | 14 | var _ = Suite(&MatrixSuite{}) 15 | 16 | func (s *MatrixSuite) TestColorToUint32(c *C) { 17 | u := colorToUint32(color.RGBA{2, 42, 84, 168}) 18 | 19 | c.Assert(int(u>>24&255), Equals, 168) 20 | c.Assert(int(u>>16&255), Equals, 2) 21 | c.Assert(int(u>>8&255), Equals, 42) 22 | c.Assert(int(u>>0&255), Equals, 84) 23 | 24 | c.Assert(uint32(u), Equals, uint32(0xa8022a54)) 25 | } 26 | 27 | func (s *MatrixSuite) TestUint32ToColor(c *C) { 28 | color := uint32ToColor(uint32(0xa8022a54)).(color.RGBA) 29 | 30 | c.Assert(color.A, Equals, uint8(168)) 31 | c.Assert(color.R, Equals, uint8(2)) 32 | c.Assert(color.G, Equals, uint8(42)) 33 | c.Assert(color.B, Equals, uint8(84)) 34 | } 35 | --------------------------------------------------------------------------------