--- /dev/null
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Native Client audio/video
+
+// Package av implements audio and video access for Native Client
+// binaries running standalone or embedded in a web browser window.
+package av
+
+import (
+ "bytes";
+ "draw";
+ "log";
+ "nacl/srpc";
+ "os";
+ "syscall";
+ "unsafe";
+)
+
+var srpcEnabled = srpc.Enabled();
+
+// native_client/src/trusted/service_runtime/include/sys/audio_video.h
+
+// Subsystem values for Init.
+const (
+ SubsystemVideo = 1<<iota;
+ SubsystemAudio;
+ SubsystemEmbed;
+)
+// SubsystemRawEvents;
+
+// Audio formats.
+const (
+ AudioFormatStereo44K = iota;
+ AudioFormatStereo48K;
+)
+
+// A Window represents a connection to the Native Client window.
+// It implements draw.Context.
+type Window struct {
+ Embedded bool; // running as part of a web page?
+ *Image; // screen image
+
+ mousec chan draw.Mouse;
+ kbdc chan int;
+ quitc chan bool;
+ resizec chan bool;
+}
+
+// *Window implements draw.Context
+var _ draw.Context = (*Window)(nil)
+
+func (w *Window) KeyboardChan() <-chan int {
+ return w.kbdc;
+}
+
+func (w *Window) MouseChan() <-chan draw.Mouse {
+ return w.mousec;
+}
+
+func (w *Window) QuitChan() <-chan bool {
+ return w.quitc;
+}
+
+func (w *Window) ResizeChan() <-chan bool {
+ return w.resizec;
+}
+
+func (w *Window) Screen() draw.Image {
+ return w.Image;
+}
+
+// Init initializes the Native Client subsystems specified by subsys.
+// Init must be called before using any of the other functions
+// in this package, and it must be called only once.
+//
+// If the SubsystemVideo flag is set, Init requests a window of size dx×dy.
+// When embedded in a web page, the web page's window specification
+// overrides the parameters to Init, so the returned Window may have
+// a different size than requested.
+//
+// If the SubsystemAudio flag is set, Init requests a connection to the
+// audio device carrying 44 kHz 16-bit stereo PCM audio samples.
+func Init(subsys int, dx, dy int) (*Window, os.Error) {
+ xsubsys := subsys;
+ if srpcEnabled {
+ waitBridge();
+ xsubsys &^= SubsystemVideo|SubsystemEmbed;
+ }
+
+ if xsubsys & SubsystemEmbed != 0 {
+ return nil, os.NewError("not embedded");
+ }
+
+ w := new(Window);
+ err := multimediaInit(xsubsys);
+ if err != nil {
+ return nil, err;
+ }
+
+ if subsys&SubsystemVideo != 0 {
+ if dx, dy, err = videoInit(dx, dy); err != nil {
+ return nil, err;
+ }
+ w.Image = newImage(dx, dy, bridge.pixel);
+ w.resizec = make(chan bool, 64);
+ w.kbdc = make(chan int, 64);
+ w.mousec = make(chan draw.Mouse, 64);
+ w.quitc = make(chan bool);
+ }
+
+ if subsys&SubsystemAudio != 0 {
+ var n int;
+ if n, err = audioInit(AudioFormatStereo44K, 2048); err != nil {
+ return nil, err;
+ }
+ println("audio", n);
+ }
+
+ if subsys&SubsystemVideo != 0 {
+ go w.readEvents();
+ }
+
+ return w, nil;
+}
+
+func (w *Window) FlushImage() {
+ if w.Image == nil {
+ return;
+ }
+ videoUpdate(w.Image.Linear);
+}
+
+func multimediaInit(subsys int) (err os.Error) {
+ return os.NewSyscallError("multimedia_init", syscall.MultimediaInit(subsys));
+}
+
+func videoInit(dx, dy int) (ndx, ndy int, err os.Error) {
+ if srpcEnabled {
+ bridge.share.ready = 1;
+ return int(bridge.share.width), int(bridge.share.height), nil;
+ }
+ if e := syscall.VideoInit(dx, dy); e != 0 {
+ return 0, 0, os.NewSyscallError("video_init", int(e));
+ }
+ return dx, dy, nil;
+}
+
+func videoUpdate(data []Color) (err os.Error) {
+ if srpcEnabled {
+ bridge.flushRPC.Call("upcall", nil);
+ return;
+ }
+ return os.NewSyscallError("video_update", syscall.VideoUpdate((*uint32)(&data[0])));
+}
+
+var noEvents = os.NewError("no events");
+
+func videoPollEvent(ev []byte) (err os.Error) {
+ if srpcEnabled {
+ r := bridge.share.eq.ri;
+ if r == bridge.share.eq.wi {
+ return noEvents;
+ }
+ bytes.Copy(ev, &bridge.share.eq.event[r]);
+ bridge.share.eq.ri = (r+1) % eqsize;
+ return nil;
+ }
+ return os.NewSyscallError("video_poll_event", syscall.VideoPollEvent(&ev[0]));
+}
+
+func audioInit(fmt int, want int) (got int, err os.Error) {
+ var x int;
+ e := syscall.AudioInit(fmt, want, &x);
+ if e == 0 {
+ return x, nil;
+ }
+ return 0, os.NewSyscallError("audio_init", e);
+}
+
+var audioSize uintptr
+
+// AudioStream provides access to the audio device.
+// Each call to AudioStream writes the given data,
+// which should be a slice of 16-bit stereo PCM audio samples,
+// and returns the number of samples required by the next
+// call to AudioStream.
+//
+// To find out the initial number of samples to write, call AudioStream(nil).
+//
+func AudioStream(data []uint16) (nextSize int, err os.Error) {
+ if audioSize == 0 {
+ e := os.NewSyscallError("audio_stream", syscall.AudioStream(nil, &audioSize));
+ return int(audioSize), e;
+ }
+ if data == nil {
+ return int(audioSize), nil;
+ }
+ if uintptr(len(data))*2 != audioSize {
+ log.Stdoutf("invalid audio size want %d got %d", audioSize, len(data));
+ }
+ e := os.NewSyscallError("audio_stream", syscall.AudioStream(&data[0], &audioSize));
+ return int(audioSize), e;
+}
+
+// Synchronization structure to wait for bridge to become ready.
+var bridge struct {
+ c chan bool;
+ displayFd int;
+ rpcFd int;
+ share *videoShare;
+ pixel []Color;
+ client *srpc.Client;
+ flushRPC *srpc.RPC;
+}
+
+// Wait for bridge to become ready.
+// When chan is first created, there is nothing in it,
+// so this blocks. Once the bridge is ready, multimediaBridge.Run
+// will drop a value into the channel. Then any calls
+// to waitBridge will finish, taking the value out and immediately putting it back.
+func waitBridge() {
+ bridge.c <- <-bridge.c;
+}
+
+const eqsize = 64;
+
+// Data structure shared with host via mmap.
+type videoShare struct {
+ revision int32; // definition below is rev 100 unless noted
+ mapSize int32;
+
+ // event queue
+ eq struct {
+ ri uint32; // read index [0,eqsize)
+ wi uint32; // write index [0,eqsize)
+ eof int32;
+ event [eqsize][64]byte;
+ };
+
+ // now unused
+ _, _, _, _ int32;
+
+ // video backing store information
+ width, height, _, size int32;
+ ready int32; // rev 0x101
+}
+
+// The frame buffer data is videoShareSize bytes after
+// the videoShare begins.
+const videoShareSize = 16*1024
+
+type multimediaBridge struct{}
+
+// If using SRPC, the runtime will call this method to pass in two file descriptors,
+// one to mmap to get the display memory, and another to use for SRPCs back
+// to the main process.
+func (multimediaBridge) Run(arg, ret []interface{}, size []int) srpc.Errno {
+ bridge.displayFd = arg[0].(int);
+ bridge.rpcFd = arg[1].(int);
+
+ var st syscall.Stat_t;
+ if errno := syscall.Fstat(bridge.displayFd, &st); errno != 0 {
+ log.Exitf("mmbridge stat display: %s", os.Errno(errno));
+ }
+
+ addr, _, errno := syscall.Syscall6(syscall.SYS_MMAP,
+ 0,
+ uintptr(st.Size),
+ syscall.PROT_READ|syscall.PROT_WRITE,
+ syscall.MAP_SHARED,
+ uintptr(bridge.displayFd),
+ 0);
+ if errno != 0 {
+ log.Exitf("mmap display: %s", os.Errno(errno));
+ }
+
+ bridge.share = (*videoShare)(unsafe.Pointer(addr));
+
+ // Overestimate frame buffer size
+ // (must use a compile-time constant)
+ // and then reslice. 256 megapixels (1 GB) should be enough.
+ fb := (*[256*1024*1024]Color)(unsafe.Pointer(addr+videoShareSize));
+ bridge.pixel = fb[0:(st.Size - videoShareSize)/4];
+
+ // Configure RPC connection back to client.
+ var err os.Error;
+ bridge.client, err = srpc.NewClient(bridge.rpcFd);
+ if err != nil {
+ log.Exitf("NewClient: %s", err);
+ }
+ bridge.flushRPC = bridge.client.NewRPC(nil);
+
+ // Notify waiters that the bridge is ready.
+ println("bridged", bridge.share.revision);
+ bridge.c <- true;
+
+ return srpc.OK;
+}
+
+func init() {
+ bridge.c = make(chan bool, 1);
+ if srpcEnabled {
+ srpc.Add("nacl_multimedia_bridge", "hh:", multimediaBridge{});
+ }
+}
+
--- /dev/null
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// NaCl GUI events.
+// Clients do not have raw access to the event stream
+// (only filtered through the lens of package draw)
+// but perhaps they will.
+
+package av
+
+import (
+ "bytes";
+ "debug/binary";
+ "draw";
+ "log";
+ "os";
+ "time";
+)
+
+// An eventType identifies the type of a Native Client Event.
+type eventType uint8;
+const (
+ eventActive = 1+iota;
+ eventExpose;
+ eventKeyDown;
+ eventKeyUp;
+ eventMouseMotion;
+ eventMouseButtonDown;
+ eventMouseButtonUp;
+ eventQuit;
+ eventUnsupported;
+)
+
+// A key represents a key on a keyboard.
+type key uint16
+const (
+ keyUnknown = 0;
+ keyFirst = 0;
+ keyBackspace = 8;
+ keyTab = 9;
+ keyClear = 12;
+ keyReturn = 13;
+ keyPause = 19;
+ keyEscape = 27;
+ keySpace = 32;
+ keyExclaim = 33;
+ keyQuotedbl = 34;
+ keyHash = 35;
+ keyDollar = 36;
+ keyAmpersand = 38;
+ keyQuote = 39;
+ keyLeftparen = 40;
+ keyRightparen = 41;
+ keyAsterisk = 42;
+ keyPlus = 43;
+ keyComma = 44;
+ keyMinus = 45;
+ keyPeriod = 46;
+ keySlash = 47;
+ key0 = 48;
+ key1 = 49;
+ key2 = 50;
+ key3 = 51;
+ key4 = 52;
+ key5 = 53;
+ key6 = 54;
+ key7 = 55;
+ key8 = 56;
+ key9 = 57;
+ keyColon = 58;
+ keySemicolon = 59;
+ keyLess = 60;
+ keyEquals = 61;
+ keyGreater = 62;
+ keyQuestion = 63;
+ keyAt = 64;
+ keyLeftbracket = 91;
+ keyBackslash = 92;
+ keyRightbracket = 93;
+ keyCaret = 94;
+ keyUnderscore = 95;
+ keyBackquote = 96;
+ keyA = 97;
+ keyB = 98;
+ keyC = 99;
+ keyD = 100;
+ keyE = 101;
+ keyF = 102;
+ keyG = 103;
+ keyH = 104;
+ keyI = 105;
+ keyJ = 106;
+ keyK = 107;
+ keyL = 108;
+ keyM = 109;
+ keyN = 110;
+ keyO = 111;
+ keyP = 112;
+ keyQ = 113;
+ keyR = 114;
+ keyS = 115;
+ keyT = 116;
+ keyU = 117;
+ keyV = 118;
+ keyW = 119;
+ keyX = 120;
+ keyY = 121;
+ keyZ = 122;
+ keyDelete = 127;
+ keyWorld0 = 160;
+ keyWorld1 = 161;
+ keyWorld2 = 162;
+ keyWorld3 = 163;
+ keyWorld4 = 164;
+ keyWorld5 = 165;
+ keyWorld6 = 166;
+ keyWorld7 = 167;
+ keyWorld8 = 168;
+ keyWorld9 = 169;
+ keyWorld10 = 170;
+ keyWorld11 = 171;
+ keyWorld12 = 172;
+ keyWorld13 = 173;
+ keyWorld14 = 174;
+ keyWorld15 = 175;
+ keyWorld16 = 176;
+ keyWorld17 = 177;
+ keyWorld18 = 178;
+ keyWorld19 = 179;
+ keyWorld20 = 180;
+ keyWorld21 = 181;
+ keyWorld22 = 182;
+ keyWorld23 = 183;
+ keyWorld24 = 184;
+ keyWorld25 = 185;
+ keyWorld26 = 186;
+ keyWorld27 = 187;
+ keyWorld28 = 188;
+ keyWorld29 = 189;
+ keyWorld30 = 190;
+ keyWorld31 = 191;
+ keyWorld32 = 192;
+ keyWorld33 = 193;
+ keyWorld34 = 194;
+ keyWorld35 = 195;
+ keyWorld36 = 196;
+ keyWorld37 = 197;
+ keyWorld38 = 198;
+ keyWorld39 = 199;
+ keyWorld40 = 200;
+ keyWorld41 = 201;
+ keyWorld42 = 202;
+ keyWorld43 = 203;
+ keyWorld44 = 204;
+ keyWorld45 = 205;
+ keyWorld46 = 206;
+ keyWorld47 = 207;
+ keyWorld48 = 208;
+ keyWorld49 = 209;
+ keyWorld50 = 210;
+ keyWorld51 = 211;
+ keyWorld52 = 212;
+ keyWorld53 = 213;
+ keyWorld54 = 214;
+ keyWorld55 = 215;
+ keyWorld56 = 216;
+ keyWorld57 = 217;
+ keyWorld58 = 218;
+ keyWorld59 = 219;
+ keyWorld60 = 220;
+ keyWorld61 = 221;
+ keyWorld62 = 222;
+ keyWorld63 = 223;
+ keyWorld64 = 224;
+ keyWorld65 = 225;
+ keyWorld66 = 226;
+ keyWorld67 = 227;
+ keyWorld68 = 228;
+ keyWorld69 = 229;
+ keyWorld70 = 230;
+ keyWorld71 = 231;
+ keyWorld72 = 232;
+ keyWorld73 = 233;
+ keyWorld74 = 234;
+ keyWorld75 = 235;
+ keyWorld76 = 236;
+ keyWorld77 = 237;
+ keyWorld78 = 238;
+ keyWorld79 = 239;
+ keyWorld80 = 240;
+ keyWorld81 = 241;
+ keyWorld82 = 242;
+ keyWorld83 = 243;
+ keyWorld84 = 244;
+ keyWorld85 = 245;
+ keyWorld86 = 246;
+ keyWorld87 = 247;
+ keyWorld88 = 248;
+ keyWorld89 = 249;
+ keyWorld90 = 250;
+ keyWorld91 = 251;
+ keyWorld92 = 252;
+ keyWorld93 = 253;
+ keyWorld94 = 254;
+ keyWorld95 = 255;
+
+ // Numeric keypad
+ keyKp0 = 256;
+ keyKp1 = 257;
+ keyKp2 = 258;
+ keyKp3 = 259;
+ keyKp4 = 260;
+ keyKp5 = 261;
+ keyKp6 = 262;
+ keyKp7 = 263;
+ keyKp8 = 264;
+ keyKp9 = 265;
+ keyKpPeriod = 266;
+ keyKpDivide = 267;
+ keyKpMultiply = 268;
+ keyKpMinus = 269;
+ keyKpPlus = 270;
+ keyKpEnter = 271;
+ keyKpEquals = 272;
+
+ // Arrow & insert/delete pad
+ keyUp = 273;
+ keyDown = 274;
+ keyRight = 275;
+ keyLeft = 276;
+ keyInsert = 277;
+ keyHome = 278;
+ keyEnd = 279;
+ keyPageup = 280;
+ keyPagedown = 281;
+
+ // Function keys
+ keyF1 = 282;
+ keyF2 = 283;
+ keyF3 = 284;
+ keyF4 = 285;
+ keyF5 = 286;
+ keyF6 = 287;
+ keyF7 = 288;
+ keyF8 = 289;
+ keyF9 = 290;
+ keyF10 = 291;
+ keyF11 = 292;
+ keyF12 = 293;
+ keyF13 = 294;
+ keyF14 = 295;
+ keyF15 = 296;
+
+ // Modifier keys
+ keyNumlock = 300;
+ keyCapslock = 301;
+ keyScrollock = 302;
+ keyRshift = 303;
+ keyLshift = 304;
+ keyRctrl = 305;
+ keyLctrl = 306;
+ keyRalt = 307;
+ keyLalt = 308;
+ keyRmeta = 309;
+ keyLmeta = 310;
+ keyLsuper = 311;
+ keyRsuper = 312;
+ keyMode = 313;
+ keyCompose = 314;
+
+ // Misc keys
+ keyHelp = 315;
+ keyPrint = 316;
+ keySysreq = 317;
+ keyBreak = 318;
+ keyMenu = 319;
+ keyPower = 320;
+ keyEuro = 321;
+ keyUndo = 322;
+
+ // Add any other keys here
+ keyLast
+)
+
+// A keymod is a set of bit flags
+type keymod uint16
+const (
+ keymodNone = 0x0000;
+ keymodLshift= 0x0001;
+ keymodRshift= 0x0002;
+ keymodLctrl = 0x0040;
+ keymodRctrl = 0x0080;
+ keymodLalt = 0x0100;
+ keymodRalt = 0x0200;
+ keymodLmeta = 0x0400;
+ keymodRmeta = 0x0800;
+ keymodNum = 0x1000;
+ keymodCaps = 0x2000;
+ keymodMode = 0x4000;
+ keymodReserved = 0x8000
+)
+
+const (
+ mouseButtonLeft = 1;
+ mouseButtonMiddle = 2;
+ mouseButtonRight = 3;
+ mouseScrollUp = 4;
+ mouseScrollDown = 5
+)
+
+const (
+ mouseStateLeftButtonPressed = 1;
+ mouseStateMiddleButtonPressed = 2;
+ mouseStateRightButtonPressed = 4
+)
+
+const (
+ activeMouse = 1; // mouse leaving/entering
+ activeInputFocus = 2; // input focus lost/restored
+ activeApplication = 4 // application minimized/restored
+)
+
+const maxEventBytes = 64
+
+type activeEvent struct {
+ EventType eventType;
+ Gain uint8;
+ State uint8;
+}
+
+type exposeEvent struct {
+ EventType eventType;
+}
+
+type keyboardEvent struct {
+ EventType eventType;
+ Device uint8;
+ State uint8;
+ Pad uint8;
+ ScanCode uint8;
+ Pad1 uint8;
+ Key key;
+ Mod keymod;
+ Unicode uint16;
+}
+
+type mouseMotionEvent struct {
+ EventType eventType;
+ Device uint8;
+ Buttons uint8;
+ Pad uint8;
+ X uint16;
+ Y uint16;
+ Xrel int16;
+ Yrel int16;
+}
+
+type mouseButtonEvent struct {
+ EventType eventType;
+ Device uint8;
+ Button uint8;
+ State uint8;
+ X uint16;
+ Y uint16;
+}
+
+type quitEvent struct {
+ EventType eventType;
+}
+
+type syncEvent struct {
+}
+
+type event interface {
+}
+
+type reader []byte
+func (r *reader) Read(p []byte) (n int, err os.Error) {
+ b := *r;
+ if len(b) == 0 && len(p) > 0 {
+ return 0, os.EOF;
+ }
+ n = bytes.Copy(p, b);
+ *r = b[n:len(b)];
+ return;
+}
+
+func (w *Window) readEvents() {
+ buf := make([]byte, maxEventBytes);
+ clean := false;
+ var (
+ ea *activeEvent;
+ ee *exposeEvent;
+ ke *keyboardEvent;
+ mme *mouseMotionEvent;
+ mbe *mouseButtonEvent;
+ qe *quitEvent;
+ )
+ var m draw.Mouse;
+ for {
+ if err := videoPollEvent(buf); err != nil {
+ if !clean {
+ clean = w.resizec <- false;
+ }
+ time.Sleep(10e6); // 10ms
+ continue;
+ }
+ clean = false;
+ var e event;
+ switch buf[0] {
+ default:
+ log.Stdout("unsupported event type", buf[0]);
+ continue;
+ case eventActive:
+ ea = new(activeEvent);
+ e = ea;
+ case eventExpose:
+ ee = new(exposeEvent);
+ e = ee;
+ case eventKeyDown, eventKeyUp:
+ ke = new(keyboardEvent);
+ e = ke;
+ case eventMouseMotion:
+ mme = new(mouseMotionEvent);
+ e = mme;
+ case eventMouseButtonDown, eventMouseButtonUp:
+ mbe = new(mouseButtonEvent);
+ e = mbe;
+ case eventQuit:
+ qe = new(quitEvent);
+ e = qe;
+ }
+ r := reader(buf);
+ if err := binary.Read(&r, binary.LittleEndian, e); err != nil {
+ log.Stdout("unpacking %T event: %s", e, err);
+ continue;
+ }
+ // log.Stdoutf("%#v\n", e);
+ switch buf[0] {
+ case eventExpose:
+ w.resizec <- true
+ case eventKeyDown:
+ w.kbdc <- int(ke.Key);
+ case eventKeyUp:
+ w.kbdc <- -int(ke.Key);
+ case eventMouseMotion:
+ m.X = int(mme.X);
+ m.Y = int(mme.Y);
+ m.Buttons = int(mme.Buttons);
+ m.Nsec = time.Nanoseconds();
+ _ = w.mousec <- m;
+ case eventMouseButtonDown:
+ m.X = int(mbe.X);
+ m.Y = int(mbe.Y);
+ // TODO(rsc): Remove uint cast once 8g bug is fixed.
+ m.Buttons |= 1<<uint(mbe.Button-1);
+ m.Nsec = time.Nanoseconds();
+ _ = w.mousec <- m;
+ case eventMouseButtonUp:
+ m.X = int(mbe.X);
+ m.Y = int(mbe.Y);
+ // TODO(rsc): Remove uint cast once 8g bug is fixed.
+ m.Buttons &^= 1<<uint(mbe.Button-1);
+ m.Nsec = time.Nanoseconds();
+ _ = w.mousec <- m;
+ case eventQuit:
+ w.quitc <- true;
+ }
+ }
+}
--- /dev/null
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package av
+
+import (
+ "image";
+)
+
+// Native Client image format:
+// a single linear array of 32-bit ARGB as packed uint32s.
+
+// An Image represents a Native Client frame buffer.
+// The pixels in the image can be accessed as a single
+// linear slice or as a two-dimensional slice of slices.
+// Image implements image.Image.
+type Image struct {
+ Linear []Color;
+ Pixel [][]Color;
+}
+
+var _ image.Image = (*Image)(nil);
+
+func (m *Image) ColorModel() image.ColorModel {
+ return ColorModel;
+}
+
+func (m *Image) Width() int {
+ if len(m.Pixel) == 0 {
+ return 0;
+ }
+ return len(m.Pixel[0]);
+}
+
+func (m *Image) Height() int {
+ return len(m.Pixel);
+}
+
+func (m *Image) At(x, y int) image.Color {
+ return m.Pixel[y][x];
+}
+
+func (m *Image) Set(x, y int, color image.Color) {
+ if c, ok := color.(Color); ok {
+ m.Pixel[y][x] = c;
+ }
+ m.Pixel[y][x] = makeColor(color.RGBA());
+}
+
+func newImage(dx, dy int, linear []Color) *Image {
+ if linear == nil {
+ linear = make([]Color, dx*dy);
+ }
+ pix := make([][]Color, dy);
+ for i := range pix {
+ pix[i] = linear[dx*i : dx*(i+1)];
+ }
+ return &Image{linear, pix};
+}
+
+// A Color represents a Native Client color value,
+// a 32-bit R, G, B, A value packed as 0xAARRGGBB.
+type Color uint32
+func (p Color) RGBA() (r, g, b, a uint32) {
+ x := uint32(p);
+ a = x>>24;
+ a |= a<<8;
+ a |= a<<16;
+ r = (x>>16) & 0xFF;
+ r |= r<<8;
+ r |= r<<16;
+ g = (x>>8) & 0xFF;
+ g |= g<<8;
+ g |= g<<16;
+ b = x & 0xFF;
+ b |= b<<8;
+ b |= b<<16;
+ return;
+}
+
+func makeColor(r, g, b, a uint32) Color {
+ return Color(a>>24<<24 | r>>24<<16 | g>>24<<8 | b>>24);
+}
+
+func toColor(color image.Color) image.Color {
+ if c, ok := color.(Color); ok {
+ return c;
+ }
+ return makeColor(color.RGBA());
+}
+
+// ColorModel is the color model corresponding to the Native Client Color.
+var ColorModel = image.ColorModelFunc(toColor);
+