github.com/jmigpin/editor@v1.6.0/driver/xdriver/wimage/pixmapwimage.go (about)

     1  package wimage
     2  
     3  import (
     4  	"fmt"
     5  	"image"
     6  	"image/draw"
     7  	"sync"
     8  
     9  	"github.com/BurntSushi/xgb/xproto"
    10  	"github.com/jmigpin/editor/util/imageutil"
    11  )
    12  
    13  type PixmapWImage struct {
    14  	opt        *Options
    15  	pixId      xproto.Pixmap
    16  	pixCreated bool
    17  	img        *imageutil.BGRA
    18  }
    19  
    20  func NewPixmapWImage(opt *Options) (*PixmapWImage, error) {
    21  	wi := &PixmapWImage{opt: opt}
    22  
    23  	// pixmap id
    24  	pixId, err := xproto.NewPixmapId(opt.Conn)
    25  	if err != nil {
    26  		return nil, err
    27  	}
    28  	wi.pixId = pixId
    29  
    30  	// initial image
    31  	r := image.Rect(0, 0, 1, 1)
    32  	if err := wi.Resize(r); err != nil {
    33  		return nil, err
    34  	}
    35  
    36  	return wi, nil
    37  }
    38  
    39  func (wi *PixmapWImage) Close() error {
    40  	wi.img = &imageutil.BGRA{}
    41  	if wi.pixCreated {
    42  		return xproto.FreePixmapChecked(wi.opt.Conn, wi.pixId).Check()
    43  	}
    44  	return nil
    45  }
    46  
    47  //----------
    48  
    49  func (wi *PixmapWImage) Resize(r image.Rectangle) error {
    50  	// clear old pixmap
    51  	if wi.pixCreated {
    52  		err := xproto.FreePixmapChecked(wi.opt.Conn, wi.pixId).Check()
    53  		if err != nil {
    54  			return err
    55  		}
    56  		wi.pixCreated = false
    57  	}
    58  
    59  	// create new pixmap
    60  	err := xproto.CreatePixmapChecked(
    61  		wi.opt.Conn,
    62  		wi.opt.ScreenInfo.RootDepth,
    63  		wi.pixId,
    64  		xproto.Drawable(wi.opt.Window),
    65  		uint16(r.Dx()),
    66  		uint16(r.Dy())).Check()
    67  	if err != nil {
    68  		return err
    69  	}
    70  	wi.pixCreated = true
    71  
    72  	// new image
    73  	wi.img = imageutil.NewBGRA(&r)
    74  	return nil
    75  }
    76  
    77  //----------
    78  
    79  func (wi *PixmapWImage) Image() draw.Image {
    80  	return wi.img
    81  }
    82  
    83  func (wi *PixmapWImage) PutImage(r image.Rectangle) error {
    84  	// X max data length = (2^16) * 4 = 262144, need to send it in chunks
    85  
    86  	putImgReqSize := 28 // TODO: link to header size
    87  	maxReqSize := (1 << 16) * 4
    88  	maxSize := (maxReqSize - putImgReqSize) / 4
    89  	if r.Dx() > maxSize {
    90  		return fmt.Errorf("pixmapwimage: dx>max, %v>%v", r.Dx(), maxSize)
    91  	}
    92  
    93  	xsize := r.Dx()
    94  	ysize := maxSize / xsize
    95  	chunk := image.Point{xsize, ysize}
    96  
    97  	getData := func(minY int) (int, int, int, int, []byte) {
    98  		h := chunk.Y
    99  		h2 := r.Max.Y - minY
   100  		if h2 < h {
   101  			h = h2
   102  		}
   103  		data := make([]uint8, chunk.X*h*4)
   104  		for y := 0; y < h; y++ {
   105  			i := y * chunk.X * 4
   106  			j := wi.img.PixOffset(r.Min.X, minY+y)
   107  			copy(data[i:i+chunk.X*4], wi.img.Pix[j:])
   108  		}
   109  		return r.Min.X, minY, chunk.X, h, data
   110  	}
   111  
   112  	send := func(x, y, w, h int, data []byte) error {
   113  		_ = xproto.PutImage( // unchecked (performance; errors handled in ev loop)
   114  			wi.opt.Conn,
   115  			xproto.ImageFormatZPixmap,
   116  			xproto.Drawable(wi.opt.Window),
   117  			wi.opt.GCtx,
   118  			uint16(w), uint16(h), // width/height
   119  			int16(x), int16(y), // dst X/Y
   120  			0, // left pad, must be 0 for ZPixmap format
   121  			wi.opt.ScreenInfo.RootDepth,
   122  			data)
   123  		return nil
   124  	}
   125  
   126  	wg := sync.WaitGroup{}
   127  	for minY := r.Min.Y; minY < r.Max.Y; minY += chunk.Y {
   128  		wg.Add(1)
   129  		go func(minY int) {
   130  			defer wg.Done()
   131  			x, y, w, h, data := getData(minY)
   132  			if err := send(x, y, w, h, data); err != nil {
   133  				fmt.Printf("pixmapwimage: putimage: %v", err)
   134  			}
   135  		}(minY)
   136  	}
   137  	wg.Wait()
   138  
   139  	return nil
   140  }
   141  
   142  func (wi *PixmapWImage) PutImageCompleted() {
   143  	panic("pixmapwimage: not expecting async put image completed")
   144  }