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 }