diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/vnc/rre.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/vnc/rre.c')
-rwxr-xr-x | sys/src/cmd/vnc/rre.c | 549 |
1 files changed, 549 insertions, 0 deletions
diff --git a/sys/src/cmd/vnc/rre.c b/sys/src/cmd/vnc/rre.c new file mode 100755 index 000000000..327d758bc --- /dev/null +++ b/sys/src/cmd/vnc/rre.c @@ -0,0 +1,549 @@ +#include "vnc.h" +#include "vncs.h" + +/* + * rise and run length encoding, aka rre. + * + * the pixel contained in r are subdivided into + * rectangles of uniform color, each of which + * is encoded by <color, x, y, w, h>. + * + * use raw encoding if it's shorter. + * + * for compact rre, use limited size rectangles, + * which are shorter to encode and therefor give better compression. + * + * hextile encoding uses rre encoding on at most 16x16 rectangles tiled + * across and then down the screen. + */ +static int encrre(uchar *raw, int stride, int w, int h, int back, int pixb, uchar *buf, int maxr, uchar *done, int (*eqpix)(uchar*, int, int), uchar *(putr)(uchar*, uchar*, int, int, int, int, int, int)); +static int eqpix16(uchar *raw, int p1, int p2); +static int eqpix32(uchar *raw, int p1, int p2); +static int eqpix8(uchar *raw, int p1, int p2); +static int findback(uchar *raw, int stride, int w, int h, int (*eqpix)(uchar*, int, int)); +static uchar* putcorre(uchar *buf, uchar *raw, int p, int pixb, int x, int y, int w, int h); +static uchar* putrre(uchar *buf, uchar *raw, int p, int pixb, int x, int y, int w, int h); +static void putpix(Vnc *v, uchar *raw, int p, int pixb); +static int hexcolors(uchar *raw, int stride, int w, int h, int (*eqpix)(uchar*, int, int), int back, int *fore); +static uchar *puthexfore(uchar *buf, uchar*, int, int, int x, int y, int w, int h); +static uchar *puthexcol(uchar *buf, uchar*, int, int, int x, int y, int w, int h); +static void sendtraw(Vnc *v, uchar *raw, int pixb, int stride, int w, int h); + +/* + * default routine, no compression, just the pixels + */ +int +sendraw(Vncs *v, Rectangle r) +{ + int pixb, stride; + uchar *raw; + + if(!rectinrect(r, v->image->r)) + sysfatal("sending bad rectangle"); + + pixb = v->bpp >> 3; + if((pixb << 3) != v->bpp) + sysfatal("bad pixel math in sendraw"); + stride = v->image->width*sizeof(ulong); + if(((stride / pixb) * pixb) != stride) + sysfatal("bad pixel math in sendraw"); + stride /= pixb; + + raw = byteaddr(v->image, r.min); + + vncwrrect(v, r); + vncwrlong(v, EncRaw); + sendtraw(v, raw, pixb, stride, Dx(r), Dy(r)); + return 1; +} + +int +countraw(Vncs*, Rectangle) +{ + return 1; +} + +/* + * grab the image for the entire rectangle, + * then encode each tile + */ +int +sendhextile(Vncs *v, Rectangle r) +{ + uchar *(*putr)(uchar*, uchar*, int, int, int, int, int, int); + int (*eq)(uchar*, int, int); + uchar *raw, *buf, *done, *traw; + int w, h, stride, pixb, pixlg, nr, bpr, back, fore; + int sy, sx, th, tw, oback, ofore, k, nc; + + h = Dy(r); + w = Dx(r); + if(h == 0 || w == 0 || !rectinrect(r, v->image->r)) + sysfatal("bad rectangle %R in sendhextile %R", r, v->image->r); + + switch(v->bpp){ + case 8: pixlg = 0; eq = eqpix8; break; + case 16: pixlg = 1; eq = eqpix16; break; + case 32: pixlg = 2; eq = eqpix32; break; + default: + sendraw(v, r); + return 1; + } + pixb = 1 << pixlg; + stride = v->image->width*sizeof(ulong); + if(((stride >> pixlg) << pixlg) != stride){ + sendraw(v, r); + return 1; + } + stride >>= pixlg; + + buf = malloc(HextileDim * HextileDim * pixb); + done = malloc(HextileDim * HextileDim); + if(buf == nil || done == nil){ + free(buf); + free(done); + sendraw(v, r); + return 1; + } + raw = byteaddr(v->image, r.min); + + vncwrrect(v, r); + vncwrlong(v, EncHextile); + oback = -1; + ofore = -1; + for(sy = 0; sy < h; sy += HextileDim){ + th = h - sy; + if(th > HextileDim) + th = HextileDim; + for(sx = 0; sx < w; sx += HextileDim){ + tw = w - sx; + if(tw > HextileDim) + tw = HextileDim; + + traw = raw + ((sy * stride + sx) << pixlg); + + back = findback(traw, stride, tw, th, eq); + nc = hexcolors(traw, stride, tw, th, eq, back, &fore); + k = 0; + if(oback < 0 || !(*eq)(raw, back + ((traw - raw) >> pixlg), oback)) + k |= HextileBack; + if(nc == 1){ + vncwrchar(v, k); + if(k & HextileBack){ + oback = back + ((traw - raw) >> pixlg); + putpix(v, raw, oback, pixb); + } + continue; + } + k |= HextileRects; + if(nc == 2){ + putr = puthexfore; + bpr = 2; + if(ofore < 0 || !(*eq)(raw, fore + ((traw - raw) >> pixlg), ofore)) + k |= HextileFore; + }else{ + putr = puthexcol; + bpr = 2 + pixb; + k |= HextileCols; + /* stupid vnc clients smash foreground in this case */ + ofore = -1; + } + + nr = th * tw << pixlg; + if(k & HextileBack) + nr -= pixb; + if(k & HextileFore) + nr -= pixb; + nr /= bpr; + memset(done, 0, HextileDim * HextileDim); + nr = encrre(traw, stride, tw, th, back, pixb, buf, nr, done, eq, putr); + if(nr < 0){ + vncwrchar(v, HextileRaw); + sendtraw(v, traw, pixb, stride, tw, th); + /* stupid vnc clients smash colors in this case */ + ofore = -1; + oback = -1; + }else{ + vncwrchar(v, k); + if(k & HextileBack){ + oback = back + ((traw - raw) >> pixlg); + putpix(v, raw, oback, pixb); + } + if(k & HextileFore){ + ofore = fore + ((traw - raw) >> pixlg); + putpix(v, raw, ofore, pixb); + } + vncwrchar(v, nr); + vncwrbytes(v, buf, nr * bpr); + } + } + } + free(buf); + free(done); + return 1; +} + +int +counthextile(Vncs*, Rectangle) +{ + return 1; +} + +static int +hexcolors(uchar *raw, int stride, int w, int h, int (*eqpix)(uchar*, int, int), int back, int *rfore) +{ + int s, es, sx, esx, fore; + + *rfore = -1; + fore = -1; + es = stride * h; + for(s = 0; s < es; s += stride){ + esx = s + w; + for(sx = s; sx < esx; sx++){ + if((*eqpix)(raw, back, sx)) + continue; + if(fore < 0){ + fore = sx; + *rfore = fore; + }else if(!(*eqpix)(raw, fore, sx)) + return 3; + } + } + + if(fore < 0) + return 1; + return 2; +} + +static uchar* +puthexcol(uchar *buf, uchar *raw, int p, int pixb, int x, int y, int w, int h) +{ + raw += p * pixb; + while(pixb--) + *buf++ = *raw++; + *buf++ = (x << 4) | y; + *buf++ = (w - 1) << 4 | (h - 1); + return buf; +} + +static uchar* +puthexfore(uchar *buf, uchar*, int, int, int x, int y, int w, int h) +{ + *buf++ = (x << 4) | y; + *buf++ = (w - 1) << 4 | (h - 1); + return buf; +} + +static void +sendtraw(Vnc *v, uchar *raw, int pixb, int stride, int w, int h) +{ + int y; + + for(y = 0; y < h; y++) + vncwrbytes(v, &raw[y * stride * pixb], w * pixb); +} + +static int +rrerects(Rectangle r, int split) +{ + return ((Dy(r) + split - 1) / split) * ((Dx(r) + split - 1) / split); +} + +enum +{ + MaxCorreDim = 48, + MaxRreDim = 64, +}; + +int +countrre(Vncs*, Rectangle r) +{ + return rrerects(r, MaxRreDim); +} + +int +countcorre(Vncs*, Rectangle r) +{ + return rrerects(r, MaxCorreDim); +} + +static int +_sendrre(Vncs *v, Rectangle r, int split, int compact) +{ + uchar *raw, *buf, *done; + int w, h, stride, pixb, pixlg, nraw, nr, bpr, back, totr; + int (*eq)(uchar*, int, int); + + totr = 0; + h = Dy(r); + while(h > split){ + h = r.max.y; + r.max.y = r.min.y + split; + totr += _sendrre(v, r, split, compact); + r.min.y = r.max.y; + r.max.y = h; + h = Dy(r); + } + w = Dx(r); + while(w > split){ + w = r.max.x; + r.max.x = r.min.x + split; + totr += _sendrre(v, r, split, compact); + r.min.x = r.max.x; + r.max.x = w; + w = Dx(r); + } + if(h == 0 || w == 0 || !rectinrect(r, v->image->r)) + sysfatal("bad rectangle in sendrre"); + + switch(v->bpp){ + case 8: pixlg = 0; eq = eqpix8; break; + case 16: pixlg = 1; eq = eqpix16; break; + case 32: pixlg = 2; eq = eqpix32; break; + default: + sendraw(v, r); + return totr + 1; + } + pixb = 1 << pixlg; + stride = v->image->width*sizeof(ulong); + if(((stride >> pixlg) << pixlg) != stride){ + sendraw(v, r); + return totr + 1; + } + stride >>= pixlg; + + nraw = w * pixb * h; + buf = malloc(nraw); + done = malloc(w * h); + if(buf == nil || done == nil){ + free(buf); + free(done); + sendraw(v, r); + return totr + 1; + } + memset(done, 0, w * h); + + raw = byteaddr(v->image, r.min); + + if(compact) + bpr = 4 * 1 + pixb; + else + bpr = 4 * 2 + pixb; + nr = (nraw - 4 - pixb) / bpr; + back = findback(raw, stride, w, h, eq); + if(compact) + nr = encrre(raw, stride, w, h, back, pixb, buf, nr, done, eq, putcorre); + else + nr = encrre(raw, stride, w, h, back, pixb, buf, nr, done, eq, putrre); + if(nr < 0){ + vncwrrect(v, r); + vncwrlong(v, EncRaw); + sendtraw(v, raw, pixb, stride, w, h); + }else{ + vncwrrect(v, r); + if(compact) + vncwrlong(v, EncCorre); + else + vncwrlong(v, EncRre); + vncwrlong(v, nr); + putpix(v, raw, back, pixb); + vncwrbytes(v, buf, nr * bpr); + } + free(buf); + free(done); + + return totr + 1; +} + +int +sendrre(Vncs *v, Rectangle r) +{ + return _sendrre(v, r, MaxRreDim, 0); +} + +int +sendcorre(Vncs *v, Rectangle r) +{ + return _sendrre(v, r, MaxCorreDim, 1); +} + +static int +encrre(uchar *raw, int stride, int w, int h, int back, int pixb, uchar *buf, + int maxr, uchar *done, int (*eqpix)(uchar*, int, int), + uchar *(*putr)(uchar*, uchar*, int, int, int, int, int, int)) +{ + int s, es, sx, esx, sy, syx, esyx, rh, rw, y, nr, dsy, dp; + + es = stride * h; + y = 0; + nr = 0; + dp = 0; + for(s = 0; s < es; s += stride){ + esx = s + w; + for(sx = s; sx < esx; ){ + rw = done[dp]; + if(rw){ + sx += rw; + dp += rw; + continue; + } + if((*eqpix)(raw, back, sx)){ + sx++; + dp++; + continue; + } + + if(nr >= maxr) + return -1; + + /* + * find the tallest maximally wide uniform colored rectangle + * with p at the upper left. + * this isn't an optimal parse, but it's pretty good for text + */ + rw = esx - sx; + rh = 0; + for(sy = sx; sy < es; sy += stride){ + if(!(*eqpix)(raw, sx, sy)) + break; + esyx = sy + rw; + for(syx = sy + 1; syx < esyx; syx++){ + if(!(*eqpix)(raw, sx, syx)){ + if(sy == sx) + break; + goto breakout; + } + } + if(sy == sx) + rw = syx - sy; + rh++; + } + breakout:; + + nr++; + buf = (*putr)(buf, raw, sx, pixb, sx - s, y, rw, rh); + + /* + * mark all pixels done + */ + dsy = dp; + while(rh--){ + esyx = dsy + rw; + for(syx = dsy; syx < esyx; syx++) + done[syx] = esyx - syx; + dsy += w; + } + + sx += rw; + dp += rw; + } + y++; + } + return nr; +} + +/* + * estimate the background color + * by finding the most frequent character in a small sample + */ +static int +findback(uchar *raw, int stride, int w, int h, int (*eqpix)(uchar*, int, int)) +{ + enum{ + NCol = 6, + NExamine = 4 + }; + int ccount[NCol], col[NCol], i, wstep, hstep, x, y, pix, c, max, maxc; + + wstep = w / NExamine; + if(wstep < 1) + wstep = 1; + hstep = h / NExamine; + if(hstep < 1) + hstep = 1; + + for(i = 0; i< NCol; i++) + ccount[i] = 0; + for(y = 0; y < h; y += hstep){ + for(x = 0; x < w; x += wstep){ + pix = y * stride + x; + for(i = 0; i < NCol; i++){ + if(ccount[i] == 0){ + ccount[i] = 1; + col[i] = pix; + break; + } + if((*eqpix)(raw, pix, col[i])){ + ccount[i]++; + break; + } + } + } + } + maxc = ccount[0]; + max = 0; + for(i = 1; i < NCol; i++){ + c = ccount[i]; + if(!c) + break; + if(c > maxc){ + max = i; + maxc = c; + } + } + return col[max]; +} + +static uchar* +putrre(uchar *buf, uchar *raw, int p, int pixb, int x, int y, int w, int h) +{ + raw += p * pixb; + while(pixb--) + *buf++ = *raw++; + *buf++ = x >> 8; + *buf++ = x; + *buf++ = y >> 8; + *buf++ = y; + *buf++ = w >> 8; + *buf++ = w; + *buf++ = h >> 8; + *buf++ = h; + return buf; +} + +static uchar* +putcorre(uchar *buf, uchar *raw, int p, int pixb, int x, int y, int w, int h) +{ + raw += p * pixb; + while(pixb--) + *buf++ = *raw++; + *buf++ = x; + *buf++ = y; + *buf++ = w; + *buf++ = h; + return buf; +} + +static int +eqpix8(uchar *raw, int p1, int p2) +{ + return raw[p1] == raw[p2]; +} + +static int +eqpix16(uchar *raw, int p1, int p2) +{ + return ((ushort*)raw)[p1] == ((ushort*)raw)[p2]; +} + +static int +eqpix32(uchar *raw, int p1, int p2) +{ + return ((ulong*)raw)[p1] == ((ulong*)raw)[p2]; +} + +static void +putpix(Vnc *v, uchar *raw, int p, int pixb) +{ + vncwrbytes(v, raw + p * pixb, pixb); +} |