/* * [MS-RDPEGDI]: Graphics Device Interface (GDI) Acceleration Extensions * http://msdn.microsoft.com/en-us/library/cc241537.aspx */ #include #include #include #include "dat.h" #include "fns.h" enum { Bits2= 3, Bits3= 7, Bits6= 63, Bits7= 127, Bits8= 255, }; enum /* 2.2.2.2.1 Drawing Order (DRAWING_ORDER) */ { Standard= 1<<0, Secondary= 1<<1, }; enum /* 2.2.2.2.1.1.2 Primary Drawing Order (PRIMARY_DRAWING_ORDER) */ { Clipped= 1<<2, NewOrder= 1<<3, Diff= 1<<4, SameClipping= 1<<5, ZeroFieldBit0= 1<<6, ZeroFieldBit1= 1<<7, }; enum { ROP2_COPY = 0xcc, }; enum { /* orderSupport indices for capability negotiation */ CanDstBlt = 0, CanPatBlt, /* also OpaqueRect */ CanScrBlt, CanMemBlt = 3, CanMem3Blt, CanDrawNineGrid = 7, CanLineTo, CanMultiDrawNineGrid, CanSaveBitmap = 0x0B, CanMultiDstBlt = 0x0F, CanMultiPatBlt = 0x10, CanMultiScrBlt, CanMultiOpaqueRect, CanFastIndex, CanPolygonSC, /* also PolygonCB */ CanPolygonCB, /* also PolygonCB */ CanPolyline, CanFastGlyph = 0x18, CanEllipseSC, /* also EllipseCB */ CanEllipseCB, /* also EllipseSC */ CanGlyphIndex, }; enum { /* 2.2.2.2.1.1.2 Primary Drawing Order (PRIMARY_DRAWING_ORDER) */ PatBlt=1, ScrBlt=2, OpaqueRect=10, MemBlt=13, MultiOpaqueRect=18, }; enum { /* 2.2.2.2.1.2.1.1 Secondary Drawing Order Header */ CacheImage = 0, CacheCmap, CacheCompressed, CacheGlyph, CacheImage2, CacheCompressed2, CacheBrush = 7, CacheCompressed3, }; typedef struct Order Order; struct Order { int fsize; uchar* (*fn)(uchar*,uchar*,int,int); }; static uchar* scrblt(uchar*,uchar*,int,int); static uchar* memblt(uchar*,uchar*,int,int); static uchar* cacheimage2(uchar*,uchar*,int,int); static uchar* cacheimage(uchar*,uchar*,int,int); static uchar* cachecmap(uchar*,uchar*,int,int); Order ordtab[NumOrders] = { [ScrBlt]= { 1, scrblt }, [MemBlt]= { 2, memblt }, [PatBlt]= { 2, nil }, [OpaqueRect]= { 1, nil }, [MultiOpaqueRect]= { 2, nil }, }; Order auxtab[8] = { [CacheImage2]= { 0, cacheimage2 }, [CacheCompressed2]= { 0, cacheimage2 }, [CacheImage]= { 0, cacheimage }, [CacheCompressed]= { 0, cacheimage }, [CacheCmap]= { 0, cachecmap }, }; uchar orderSupport[NumOrders] = { [CanScrBlt] 1, [CanMemBlt] 1, // [CanMultiOpaqueRect] 1, // [CanPatBlt] 1, /* and OpaqueRect */ }; static struct GdiContext { int order; Rectangle clipr; } gc = {PatBlt}; static Image* imgcache[3][600]; static uchar* getclipr(Rectangle*,uchar*,uchar*); static uchar* getpt(Point*,uchar*,uchar*,int,int); /* 2.2.2.2 Fast-Path Orders Update (TS_FP_UPDATE_ORDERS) */ void scanorders(uchar* p, uchar* ep, int count) { int ctl, fmask, fsize; int size, opt, xorder; while(count-- > 0 && pep) sysfatal(Eshort); size = ((short)GSHORT(p+1))+13; if(size < 0 || p+size > ep) sysfatal(Eshort); opt = GSHORT(p+3); xorder = p[5]; if(xorder >= nelem(auxtab) || auxtab[xorder].fn == nil){ fprint(2, "egdi: unsupported secondary order %d\n", xorder); p += size; continue; } auxtab[xorder].fn(p, p+size, xorder, opt); p += size; continue; } p++; if(ctl&NewOrder){ gc.order = *p++; if(gc.order >= NumOrders) // paranoia gc.order = PatBlt; } fsize = ordtab[gc.order].fsize - ((ctl>>6)&Bits2); switch(fsize){ default: goto ErrFsize; case 3: fmask = p[0]|(p[1]<<8)|(p[2]<<16); break; case 2: fmask = GSHORT(p); break; case 1: fmask = p[0]; case 0: break; } p += fsize; if(ctl&Clipped && !(ctl&SameClipping)) p = getclipr(&gc.clipr, p, ep); if(ordtab[gc.order].fn == nil) goto ErrNotsup; p = ordtab[gc.order].fn(p, ep, ctl, fmask); if(p == nil) break; } lockdisplay(display); flushimage(display, 1); unlockdisplay(display); return; ErrNstd: fprint(2, "egdi: non-standard order\n"); return; ErrFsize: fprint(2, "egdi: bad field encoding bytes count for order %d\n", gc.order); return; ErrNotsup: fprint(2, "egdi: unsupported order %d\n", gc.order); return; } static uchar* getclipr(Rectangle* pr, uchar* p, uchar* ep) { int bctl; bctl = *p++; if(bctl&1<<4) pr->min.x += (char)*p++; else if(bctl&1<<0){ pr->min.x = GSHORT(p); p += 2; } if(bctl&1<<5) pr->min.y += (char)*p++; else if(bctl&1<<1){ pr->min.y = GSHORT(p); p += 2; } if(bctl&1<<6) pr->max.x += (char)*p++; else if(bctl&1<<2){ pr->max.x = GSHORT(p)+1; p += 2; } if(bctl&1<<7) pr->max.y += (char)*p++; else if(bctl&1<<3){ pr->max.y = GSHORT(p)+1; p += 2; } if(p>ep) sysfatal(Eshort); return p; } static uchar* getpt(Point* pp, uchar* s, uchar *es, int ctl, int fmask) { Point p; p = *pp; if(ctl&Diff){ if(fmask&1<<0) p.x += (char)*s++; if(fmask&1<<1) p.y += (char)*s++; }else{ if(fmask&1<<0){ p.x = GSHORT(s); s += 2; }; if(fmask&1<<1){ p.y = GSHORT(s); s += 2; }; } if(s > es) sysfatal(Eshort); *pp = p; return s; } static uchar* getoffrect(Rectangle* pr, uchar* p, uchar* ep, int ctl, int fmask){ Rectangle r; r = *pr; r.max = subpt(r.max, r.min); p = getpt(&r.min, p, ep, ctl, fmask); p = getpt(&r.max, p, ep, ctl, fmask>>2); r.max = addpt(r.max, r.min); *pr = r; return p; } /* 2.2.2.2.1.1.2.7 ScrBlt (SCRBLT_ORDER) */ static uchar* scrblt(uchar* p, uchar* ep, int ctl, int fmask) { static Rectangle r; static Point pt; static int rop3; Rectangle rs; Point ps; p = getoffrect(&r, p, ep, ctl, fmask); if(fmask&1<<4) rop3 = *p++; p = getpt(&pt, p, ep, ctl, fmask>>5); if(rop3 != ROP2_COPY){ fprint(2, "scrblt: rop3 %#hhux is not supported\n", rop3); return p; } rs = r; if(ctl&Clipped) rectclip(&rs, gc.clipr); // not replclipr: need to clip dst only rs = rectaddpt(rs, screen->r.min); ps = addpt(pt, screen->r.min); lockdisplay(display); draw(screen, rs, screen, nil, ps); unlockdisplay(display); return p; } /* 2.2.2.2.1.1.2.9 MemBlt (MEMBLT_ORDER) */ static uchar* memblt(uchar* p, uchar* ep, int ctl, int fmask) { static int cid; /* cacheId */ static int coff; /* cacheIndex */ static int rop3; static Rectangle r; static Point pt; Image* img; if(fmask&1<<0){ cid = GSHORT(p); p += 2; } p = getoffrect(&r, p, ep, ctl, fmask>>1); if(fmask&1<<5) rop3 = *p++; p = getpt(&pt, p, ep, ctl, fmask>>6); if(fmask&1<<8){ coff = GSHORT(p); p += 2; } if(p>ep) sysfatal(Eshort); cid &= Bits8; if(cid >= nelem(imgcache) || coff >= nelem(*imgcache)){ fprint(2, "memblt: bad image cache spec [%d %d]\n", cid, coff); return p; } img = imgcache[cid][coff]; if(img == nil){ fprint(2, "memblt: empty cache entry cid %d coff %d\n", cid, coff); return p; } lockdisplay(display); if(ctl&Clipped) replclipr(screen, screen->repl, rectaddpt(gc.clipr, screen->r.min)); draw(screen, rectaddpt(r, screen->r.min), img, nil, pt); if(ctl&Clipped) replclipr(screen, screen->repl, screen->r); unlockdisplay(display); return p; } static Image* pickimage(int cid, int coff, Rectangle r, ulong chan) { Image* img; if(cid >= nelem(imgcache) || coff >= nelem(*imgcache)){ werrstr("bad image cache spec [%d %d]", cid, coff); return nil; } img = imgcache[cid][coff]; if(img==nil || !eqrect(img->r, r)){ if(img != nil) freeimage(img); img = allocimage(display, r, chan, 0, DNofill); if(img == nil) sysfatal("%r"); imgcache[cid][coff] = img; } return img; } /* 2.2.2.2.1.2.2 Cache Bitmap - Revision 1 (CACHE_BITMAP_ORDER) */ static uchar* cacheimage(uchar* p, uchar* ep, int xorder, int opt) { int cid; /* cacheId */ int coff; /* cacheIndex */ int chan; int zip; int err; int size; Image* img; Point d; /* width, height */ Rectangle r; if(p+15 >= ep) sysfatal(Eshort); cid = p[6]; d.x = p[8]; d.y = p[9]; size = GSHORT(p+11); coff = GSHORT(p+13); r.min = ZP; r.max = d; chan = rd.chan; zip = (xorder==CacheCompressed); if(zip) if(opt&1<<10){ p += 8; // bitmapComprHdr size -= 8; } if(p+size > ep) sysfatal(Eshort); if((img = pickimage(cid, coff, r, chan)) == nil) sysfatal("pickimage: %r"); err = (zip? loadrle : loadbmp)(img, r, p, size); if(err < 0) sysfatal("%r"); return p+size; } /* 2.2.2.2.1.2.3 Cache Bitmap - Revision 2 (CACHE_BITMAP_REV2_ORDER) */ static uchar* cacheimage2(uchar* p,uchar* ep, int xorder, int opt) { int n, c; int zip; int cid; /* cacheId */ int coff; /* cacheIndex */ int chan; int size; int err; Image* img; Point d; Rectangle r; if(p+9 >= ep) sysfatal(Eshort); p += 6; chan = rd.chan; zip = (xorder==CacheCompressed2); cid = opt&Bits3; opt >>= 7; if(opt&1<<1) p += 8; // persistent cache key c = *p++; if(c&1<<7) c = ((c&Bits7)<<8) | *p++; d.x = c; if(opt&1) d.y = d.x; else{ c = *p++; if(c&1<<7) c = ((c&Bits7)<<8) | *p++; d.y = c; } r.min = ZP; r.max = d; c = *p++; n = c>>6; c &= Bits6; switch(n){ case 3: c = (c<<8) | *p++; case 2: c = (c<<8) | *p++; case 1: c = (c<<8) | *p++; } size = c; // fixme: protect from integer overflow c = *p++; if(c&1<<7) c = ((c&Bits7)<<8) | *p++; coff = c; if(zip&& !(opt&1<<3)){ p += 8; // bitmapComprHdr size -= 8; } if(p+size > ep) sysfatal(Eshort); if((img = pickimage(cid, coff, r, chan)) == nil) sysfatal("pullimage: %r"); err = (zip? loadrle : loadbmp)(img, r, p, size); if(err < 0) sysfatal("%r"); return p+size; } /* 2.2.2.2.1.2.4 Cache Color Table (CACHE_COLOR_TABLE_ORDER) */ static uchar* cachecmap(uchar* p,uchar* ep, int, int) { int cid, n; cid = p[6]; n = GSHORT(p+7); if(n != 256){ fprint(2, "cachecmap: %d != 256\n", n); return nil; } if(p+9+4*256>ep){ werrstr(Eshort); return nil; } /* fixed cmap here */ USED(cid); return p+9+4*256; }