9fans.net/go@v0.0.5/draw/alloc.go (about) 1 package draw 2 3 import ( 4 "fmt" 5 "os" 6 "runtime" 7 "strings" 8 ) 9 10 // AllocImage allocates a new Image on display d. 11 // It will have the rectangle, pixel format (RGBA32 etc), 12 // and replication flag given by its arguments. 13 // 14 // All the new image's pixels will have the initial color 15 // (Black, White, Red, and so on). 16 // If color is NoFill, the pixels will be left uninitialized. 17 // (In Go, uninitialized means zeroed, so the pixels will be 18 // Transparent or Black depending on whether the pixel 19 // format includes an alpha channel.) 20 // 21 // If repl is true, the result's Clipr rectangle is set to a very large region. 22 // Otherwise, the result's Clipr is set to r. 23 // 24 // The result's Depth is computed from pix. 25 // 26 // AllocImage returns an error if the display cannot allocate the image 27 // or is no longer available. 28 // 29 // For example, to allocate a single-pixel replicated image that may be 30 // used to paint a region red: 31 // 32 // red, err := display.AllocImage(draw.Rect(0, 0, 1, 1), draw.RGB24, true, draw.Red) 33 func (d *Display) AllocImage(r Rectangle, pix Pix, repl bool, color Color) (*Image, error) { 34 d.mu.Lock() 35 defer d.mu.Unlock() 36 return allocImage(d, nil, r, pix, repl, color, 0, 0) 37 } 38 39 func (d *Display) allocImage(r Rectangle, pix Pix, repl bool, val Color) (i *Image, err error) { 40 return allocImage(d, nil, r, pix, repl, val, 0, 0) 41 } 42 43 func allocImage(d *Display, ai *Image, r Rectangle, pix Pix, repl bool, val Color, screenid uint32, refresh int) (i *Image, err error) { 44 defer func() { 45 if err != nil { 46 err = fmt.Errorf("allocimage %v %v: %v", r, pix, err) 47 i.free() 48 i = nil 49 } 50 }() 51 52 depth := pix.Depth() 53 if depth == 0 { 54 err = fmt.Errorf("bad channel descriptor") 55 return 56 } 57 58 // flush pending data so we don't get error allocating the image 59 d.flush(false) 60 a := d.bufimage(1 + 4 + 4 + 1 + 4 + 1 + 4*4 + 4*4 + 4) 61 d.imageid++ 62 id := d.imageid 63 a[0] = 'b' 64 bplong(a[1:], id) 65 bplong(a[5:], screenid) 66 a[9] = byte(refresh) 67 bplong(a[10:], uint32(pix)) 68 if repl { 69 a[14] = 1 70 } else { 71 a[14] = 0 72 } 73 bplong(a[15:], uint32(r.Min.X)) 74 bplong(a[19:], uint32(r.Min.Y)) 75 bplong(a[23:], uint32(r.Max.X)) 76 bplong(a[27:], uint32(r.Max.Y)) 77 clipr := r 78 if repl { 79 // huge but not infinite, so various offsets will leave it huge, not overflow 80 clipr = Rect(-0x3FFFFFFF, -0x3FFFFFFF, 0x3FFFFFFF, 0x3FFFFFFF) 81 } 82 bplong(a[31:], uint32(clipr.Min.X)) 83 bplong(a[35:], uint32(clipr.Min.Y)) 84 bplong(a[39:], uint32(clipr.Max.X)) 85 bplong(a[43:], uint32(clipr.Max.Y)) 86 bplong(a[47:], uint32(val)) 87 if err = d.flush(false); err != nil { 88 return 89 } 90 91 i = ai 92 if i == nil { 93 i = new(Image) 94 } 95 *i = Image{ 96 Display: d, 97 id: id, 98 Pix: pix, 99 Depth: pix.Depth(), 100 R: r, 101 Clipr: clipr, 102 Repl: repl, 103 } 104 runtime.SetFinalizer(i, (*Image).Free) 105 return i, nil 106 } 107 108 func namedImage(d *Display, ai *Image, name string) (i *Image, err error) { 109 n := len(name) 110 if n >= 256 { 111 return nil, fmt.Errorf("namedImage: name too long") 112 } 113 // flush pending data so we don't get error allocating the image 114 d.flush(false) 115 a := d.bufimage(1 + 4 + 1 + n) 116 d.imageid++ 117 id := d.imageid 118 a[0] = 'n' 119 bplong(a[1:], id) 120 a[5] = byte(n) 121 copy(a[6:], name) 122 if err := d.flush(false); err != nil { 123 fmt.Fprintf(os.Stderr, "namedImage: %v\n", err) 124 return nil, err 125 } 126 127 a = d.bufimage(1) 128 a[0] = 'I' 129 if err := d.flush(false); err != nil { 130 fmt.Fprintf(os.Stderr, "cannot read image info: %v\n", err) 131 return nil, err 132 } 133 info := make([]byte, 12*12) 134 n, err = d.conn.ReadDraw(info) 135 if err != nil { 136 return nil, err 137 } 138 if n < len(info) { 139 return nil, fmt.Errorf("short info from rddraw") 140 } 141 142 pix, err := ParsePix(strings.TrimSpace(string(info[2*12 : 3*12]))) 143 if err != nil { 144 a := d.bufimage(1 + 4) 145 a[0] = 'f' 146 bplong(a[1:], id) 147 d.flush(false) 148 return nil, fmt.Errorf("bad channel %q from devdraw", info[2*12:3*12]) 149 } 150 i = ai 151 if i == nil { 152 i = new(Image) 153 } 154 *i = Image{ 155 Display: d, 156 id: id, 157 Pix: pix, 158 Depth: pix.Depth(), 159 Repl: atoi(info[3*12:]) > 0, 160 R: ator(info[4*12:]), 161 Clipr: ator(info[8*12:]), 162 Screen: nil, 163 next: nil, 164 } 165 runtime.SetFinalizer(i, (*Image).Free) 166 return i, nil 167 } 168 169 /* 170 func nameimage(i *Image, name string, in bool) error { 171 a := i.Display.bufimage(1+4+1+1+len(name)) 172 a[0] = 'N' 173 bplong(a[1:], i.ID) 174 if in { 175 a[5] = 1 176 } 177 a[6] = len(name) 178 copy(a[7:], name) 179 return d.flushimage(false) 180 } 181 */ 182 183 func (i *Image) free() error { 184 if i == nil || i.Display == nil { 185 return nil 186 } 187 // make sure no refresh events occur on this if we block in the write 188 d := i.Display 189 // flush pending data so we don't get error deleting the image 190 d.flush(false) 191 a := d.bufimage(1 + 4) 192 a[0] = 'f' 193 bplong(a[1:], i.id) 194 if i.Screen != nil { 195 w := d.Windows 196 if w == i { 197 d.Windows = i.next 198 } else { 199 for ; w != nil; w = w.next { 200 if w.next == i { 201 w.next = i.next 202 break 203 } 204 } 205 } 206 } 207 i.Display = nil // so a second free will be OK 208 runtime.SetFinalizer(i, nil) 209 return d.flush(i.Screen != nil) 210 } 211 212 // Free frees the server resources for the image. 213 // Images have a finalizer that calls Free automatically, 214 // if necessary, when the Image is garbage collected, 215 // but it is more efficient to be explicit. 216 func (i *Image) Free() error { 217 if i == nil { 218 return nil 219 } 220 if i.Display != nil && i == i.Display.ScreenImage { 221 panic("freeimage of ScreenImage") 222 } 223 i.Display.mu.Lock() 224 defer i.Display.mu.Unlock() 225 return i.free() 226 }