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  }