|
|
1.1 ! root 1: /* ! 2: * QEMU VNC display driver: tight encoding ! 3: * ! 4: * From libvncserver/libvncserver/tight.c ! 5: * Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved. ! 6: * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. ! 7: * ! 8: * Copyright (C) 2010 Corentin Chary <[email protected]> ! 9: * ! 10: * Permission is hereby granted, free of charge, to any person obtaining a copy ! 11: * of this software and associated documentation files (the "Software"), to deal ! 12: * in the Software without restriction, including without limitation the rights ! 13: * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ! 14: * copies of the Software, and to permit persons to whom the Software is ! 15: * furnished to do so, subject to the following conditions: ! 16: * ! 17: * The above copyright notice and this permission notice shall be included in ! 18: * all copies or substantial portions of the Software. ! 19: * ! 20: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ! 21: * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ! 22: * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ! 23: * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ! 24: * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ! 25: * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ! 26: * THE SOFTWARE. ! 27: */ ! 28: ! 29: #include "config-host.h" ! 30: ! 31: #ifdef CONFIG_VNC_PNG ! 32: #include <png.h> ! 33: #endif ! 34: #ifdef CONFIG_VNC_JPEG ! 35: #include <stdio.h> ! 36: #include <jpeglib.h> ! 37: #endif ! 38: ! 39: #include "qemu-common.h" ! 40: ! 41: #include "bswap.h" ! 42: #include "qint.h" ! 43: #include "vnc.h" ! 44: #include "vnc-enc-tight.h" ! 45: #include "vnc-palette.h" ! 46: ! 47: /* Compression level stuff. The following array contains various ! 48: encoder parameters for each of 10 compression levels (0..9). ! 49: Last three parameters correspond to JPEG quality levels (0..9). */ ! 50: ! 51: static const struct { ! 52: int max_rect_size, max_rect_width; ! 53: int mono_min_rect_size, gradient_min_rect_size; ! 54: int idx_zlib_level, mono_zlib_level, raw_zlib_level, gradient_zlib_level; ! 55: int gradient_threshold, gradient_threshold24; ! 56: int idx_max_colors_divisor; ! 57: int jpeg_quality, jpeg_threshold, jpeg_threshold24; ! 58: } tight_conf[] = { ! 59: { 512, 32, 6, 65536, 0, 0, 0, 0, 0, 0, 4, 5, 10000, 23000 }, ! 60: { 2048, 128, 6, 65536, 1, 1, 1, 0, 0, 0, 8, 10, 8000, 18000 }, ! 61: { 6144, 256, 8, 65536, 3, 3, 2, 0, 0, 0, 24, 15, 6500, 15000 }, ! 62: { 10240, 1024, 12, 65536, 5, 5, 3, 0, 0, 0, 32, 25, 5000, 12000 }, ! 63: { 16384, 2048, 12, 65536, 6, 6, 4, 0, 0, 0, 32, 37, 4000, 10000 }, ! 64: { 32768, 2048, 12, 4096, 7, 7, 5, 4, 150, 380, 32, 50, 3000, 8000 }, ! 65: { 65536, 2048, 16, 4096, 7, 7, 6, 4, 170, 420, 48, 60, 2000, 5000 }, ! 66: { 65536, 2048, 16, 4096, 8, 8, 7, 5, 180, 450, 64, 70, 1000, 2500 }, ! 67: { 65536, 2048, 32, 8192, 9, 9, 8, 6, 190, 475, 64, 75, 500, 1200 }, ! 68: { 65536, 2048, 32, 8192, 9, 9, 9, 6, 200, 500, 96, 80, 200, 500 } ! 69: }; ! 70: ! 71: ! 72: static int tight_send_framebuffer_update(VncState *vs, int x, int y, ! 73: int w, int h); ! 74: ! 75: #ifdef CONFIG_VNC_PNG ! 76: static const struct { ! 77: int png_zlib_level, png_filters; ! 78: } tight_png_conf[] = { ! 79: { 0, PNG_NO_FILTERS }, ! 80: { 1, PNG_NO_FILTERS }, ! 81: { 2, PNG_NO_FILTERS }, ! 82: { 3, PNG_NO_FILTERS }, ! 83: { 4, PNG_NO_FILTERS }, ! 84: { 5, PNG_ALL_FILTERS }, ! 85: { 6, PNG_ALL_FILTERS }, ! 86: { 7, PNG_ALL_FILTERS }, ! 87: { 8, PNG_ALL_FILTERS }, ! 88: { 9, PNG_ALL_FILTERS }, ! 89: }; ! 90: ! 91: static int send_png_rect(VncState *vs, int x, int y, int w, int h, ! 92: VncPalette *palette); ! 93: ! 94: static bool tight_can_send_png_rect(VncState *vs, int w, int h) ! 95: { ! 96: if (vs->tight.type != VNC_ENCODING_TIGHT_PNG) { ! 97: return false; ! 98: } ! 99: ! 100: if (ds_get_bytes_per_pixel(vs->ds) == 1 || ! 101: vs->clientds.pf.bytes_per_pixel == 1) { ! 102: return false; ! 103: } ! 104: ! 105: return true; ! 106: } ! 107: #endif ! 108: ! 109: /* ! 110: * Code to guess if given rectangle is suitable for smooth image ! 111: * compression (by applying "gradient" filter or JPEG coder). ! 112: */ ! 113: ! 114: static uint ! 115: tight_detect_smooth_image24(VncState *vs, int w, int h) ! 116: { ! 117: int off; ! 118: int x, y, d, dx; ! 119: uint c; ! 120: uint stats[256]; ! 121: int pixels = 0; ! 122: int pix, left[3]; ! 123: uint errors; ! 124: unsigned char *buf = vs->tight.tight.buffer; ! 125: ! 126: /* ! 127: * If client is big-endian, color samples begin from the second ! 128: * byte (offset 1) of a 32-bit pixel value. ! 129: */ ! 130: off = !!(vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG); ! 131: ! 132: memset(stats, 0, sizeof (stats)); ! 133: ! 134: for (y = 0, x = 0; y < h && x < w;) { ! 135: for (d = 0; d < h - y && d < w - x - VNC_TIGHT_DETECT_SUBROW_WIDTH; ! 136: d++) { ! 137: for (c = 0; c < 3; c++) { ! 138: left[c] = buf[((y+d)*w+x+d)*4+off+c] & 0xFF; ! 139: } ! 140: for (dx = 1; dx <= VNC_TIGHT_DETECT_SUBROW_WIDTH; dx++) { ! 141: for (c = 0; c < 3; c++) { ! 142: pix = buf[((y+d)*w+x+d+dx)*4+off+c] & 0xFF; ! 143: stats[abs(pix - left[c])]++; ! 144: left[c] = pix; ! 145: } ! 146: pixels++; ! 147: } ! 148: } ! 149: if (w > h) { ! 150: x += h; ! 151: y = 0; ! 152: } else { ! 153: x = 0; ! 154: y += w; ! 155: } ! 156: } ! 157: ! 158: /* 95% smooth or more ... */ ! 159: if (stats[0] * 33 / pixels >= 95) { ! 160: return 0; ! 161: } ! 162: ! 163: errors = 0; ! 164: for (c = 1; c < 8; c++) { ! 165: errors += stats[c] * (c * c); ! 166: if (stats[c] == 0 || stats[c] > stats[c-1] * 2) { ! 167: return 0; ! 168: } ! 169: } ! 170: for (; c < 256; c++) { ! 171: errors += stats[c] * (c * c); ! 172: } ! 173: errors /= (pixels * 3 - stats[0]); ! 174: ! 175: return errors; ! 176: } ! 177: ! 178: #define DEFINE_DETECT_FUNCTION(bpp) \ ! 179: \ ! 180: static uint \ ! 181: tight_detect_smooth_image##bpp(VncState *vs, int w, int h) { \ ! 182: bool endian; \ ! 183: uint##bpp##_t pix; \ ! 184: int max[3], shift[3]; \ ! 185: int x, y, d, dx; \ ! 186: uint c; \ ! 187: uint stats[256]; \ ! 188: int pixels = 0; \ ! 189: int sample, sum, left[3]; \ ! 190: uint errors; \ ! 191: unsigned char *buf = vs->tight.tight.buffer; \ ! 192: \ ! 193: endian = ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) != \ ! 194: (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG)); \ ! 195: \ ! 196: \ ! 197: max[0] = vs->clientds.pf.rmax; \ ! 198: max[1] = vs->clientds.pf.gmax; \ ! 199: max[2] = vs->clientds.pf.bmax; \ ! 200: shift[0] = vs->clientds.pf.rshift; \ ! 201: shift[1] = vs->clientds.pf.gshift; \ ! 202: shift[2] = vs->clientds.pf.bshift; \ ! 203: \ ! 204: memset(stats, 0, sizeof(stats)); \ ! 205: \ ! 206: y = 0, x = 0; \ ! 207: while (y < h && x < w) { \ ! 208: for (d = 0; d < h - y && \ ! 209: d < w - x - VNC_TIGHT_DETECT_SUBROW_WIDTH; d++) { \ ! 210: pix = ((uint##bpp##_t *)buf)[(y+d)*w+x+d]; \ ! 211: if (endian) { \ ! 212: pix = bswap_##bpp(pix); \ ! 213: } \ ! 214: for (c = 0; c < 3; c++) { \ ! 215: left[c] = (int)(pix >> shift[c] & max[c]); \ ! 216: } \ ! 217: for (dx = 1; dx <= VNC_TIGHT_DETECT_SUBROW_WIDTH; \ ! 218: dx++) { \ ! 219: pix = ((uint##bpp##_t *)buf)[(y+d)*w+x+d+dx]; \ ! 220: if (endian) { \ ! 221: pix = bswap_##bpp(pix); \ ! 222: } \ ! 223: sum = 0; \ ! 224: for (c = 0; c < 3; c++) { \ ! 225: sample = (int)(pix >> shift[c] & max[c]); \ ! 226: sum += abs(sample - left[c]); \ ! 227: left[c] = sample; \ ! 228: } \ ! 229: if (sum > 255) { \ ! 230: sum = 255; \ ! 231: } \ ! 232: stats[sum]++; \ ! 233: pixels++; \ ! 234: } \ ! 235: } \ ! 236: if (w > h) { \ ! 237: x += h; \ ! 238: y = 0; \ ! 239: } else { \ ! 240: x = 0; \ ! 241: y += w; \ ! 242: } \ ! 243: } \ ! 244: \ ! 245: if ((stats[0] + stats[1]) * 100 / pixels >= 90) { \ ! 246: return 0; \ ! 247: } \ ! 248: \ ! 249: errors = 0; \ ! 250: for (c = 1; c < 8; c++) { \ ! 251: errors += stats[c] * (c * c); \ ! 252: if (stats[c] == 0 || stats[c] > stats[c-1] * 2) { \ ! 253: return 0; \ ! 254: } \ ! 255: } \ ! 256: for (; c < 256; c++) { \ ! 257: errors += stats[c] * (c * c); \ ! 258: } \ ! 259: errors /= (pixels - stats[0]); \ ! 260: \ ! 261: return errors; \ ! 262: } ! 263: ! 264: DEFINE_DETECT_FUNCTION(16) ! 265: DEFINE_DETECT_FUNCTION(32) ! 266: ! 267: static int ! 268: tight_detect_smooth_image(VncState *vs, int w, int h) ! 269: { ! 270: uint errors; ! 271: int compression = vs->tight.compression; ! 272: int quality = vs->tight.quality; ! 273: ! 274: if (!vs->vd->lossy) { ! 275: return 0; ! 276: } ! 277: ! 278: if (ds_get_bytes_per_pixel(vs->ds) == 1 || ! 279: vs->clientds.pf.bytes_per_pixel == 1 || ! 280: w < VNC_TIGHT_DETECT_MIN_WIDTH || h < VNC_TIGHT_DETECT_MIN_HEIGHT) { ! 281: return 0; ! 282: } ! 283: ! 284: if (vs->tight.quality != -1) { ! 285: if (w * h < VNC_TIGHT_JPEG_MIN_RECT_SIZE) { ! 286: return 0; ! 287: } ! 288: } else { ! 289: if (w * h < tight_conf[compression].gradient_min_rect_size) { ! 290: return 0; ! 291: } ! 292: } ! 293: ! 294: if (vs->clientds.pf.bytes_per_pixel == 4) { ! 295: if (vs->tight.pixel24) { ! 296: errors = tight_detect_smooth_image24(vs, w, h); ! 297: if (vs->tight.quality != -1) { ! 298: return (errors < tight_conf[quality].jpeg_threshold24); ! 299: } ! 300: return (errors < tight_conf[compression].gradient_threshold24); ! 301: } else { ! 302: errors = tight_detect_smooth_image32(vs, w, h); ! 303: } ! 304: } else { ! 305: errors = tight_detect_smooth_image16(vs, w, h); ! 306: } ! 307: if (quality != -1) { ! 308: return (errors < tight_conf[quality].jpeg_threshold); ! 309: } ! 310: return (errors < tight_conf[compression].gradient_threshold); ! 311: } ! 312: ! 313: /* ! 314: * Code to determine how many different colors used in rectangle. ! 315: */ ! 316: #define DEFINE_FILL_PALETTE_FUNCTION(bpp) \ ! 317: \ ! 318: static int \ ! 319: tight_fill_palette##bpp(VncState *vs, int x, int y, \ ! 320: int max, size_t count, \ ! 321: uint32_t *bg, uint32_t *fg, \ ! 322: VncPalette **palette) { \ ! 323: uint##bpp##_t *data; \ ! 324: uint##bpp##_t c0, c1, ci; \ ! 325: int i, n0, n1; \ ! 326: \ ! 327: data = (uint##bpp##_t *)vs->tight.tight.buffer; \ ! 328: \ ! 329: c0 = data[0]; \ ! 330: i = 1; \ ! 331: while (i < count && data[i] == c0) \ ! 332: i++; \ ! 333: if (i >= count) { \ ! 334: *bg = *fg = c0; \ ! 335: return 1; \ ! 336: } \ ! 337: \ ! 338: if (max < 2) { \ ! 339: return 0; \ ! 340: } \ ! 341: \ ! 342: n0 = i; \ ! 343: c1 = data[i]; \ ! 344: n1 = 0; \ ! 345: for (i++; i < count; i++) { \ ! 346: ci = data[i]; \ ! 347: if (ci == c0) { \ ! 348: n0++; \ ! 349: } else if (ci == c1) { \ ! 350: n1++; \ ! 351: } else \ ! 352: break; \ ! 353: } \ ! 354: if (i >= count) { \ ! 355: if (n0 > n1) { \ ! 356: *bg = (uint32_t)c0; \ ! 357: *fg = (uint32_t)c1; \ ! 358: } else { \ ! 359: *bg = (uint32_t)c1; \ ! 360: *fg = (uint32_t)c0; \ ! 361: } \ ! 362: return 2; \ ! 363: } \ ! 364: \ ! 365: if (max == 2) { \ ! 366: return 0; \ ! 367: } \ ! 368: \ ! 369: *palette = palette_new(max, bpp); \ ! 370: palette_put(*palette, c0); \ ! 371: palette_put(*palette, c1); \ ! 372: palette_put(*palette, ci); \ ! 373: \ ! 374: for (i++; i < count; i++) { \ ! 375: if (data[i] == ci) { \ ! 376: continue; \ ! 377: } else { \ ! 378: ci = data[i]; \ ! 379: if (!palette_put(*palette, (uint32_t)ci)) { \ ! 380: return 0; \ ! 381: } \ ! 382: } \ ! 383: } \ ! 384: \ ! 385: return palette_size(*palette); \ ! 386: } ! 387: ! 388: DEFINE_FILL_PALETTE_FUNCTION(8) ! 389: DEFINE_FILL_PALETTE_FUNCTION(16) ! 390: DEFINE_FILL_PALETTE_FUNCTION(32) ! 391: ! 392: static int tight_fill_palette(VncState *vs, int x, int y, ! 393: size_t count, uint32_t *bg, uint32_t *fg, ! 394: VncPalette **palette) ! 395: { ! 396: int max; ! 397: ! 398: max = count / tight_conf[vs->tight.compression].idx_max_colors_divisor; ! 399: if (max < 2 && ! 400: count >= tight_conf[vs->tight.compression].mono_min_rect_size) { ! 401: max = 2; ! 402: } ! 403: if (max >= 256) { ! 404: max = 256; ! 405: } ! 406: ! 407: switch(vs->clientds.pf.bytes_per_pixel) { ! 408: case 4: ! 409: return tight_fill_palette32(vs, x, y, max, count, bg, fg, palette); ! 410: case 2: ! 411: return tight_fill_palette16(vs, x, y, max, count, bg, fg, palette); ! 412: default: ! 413: max = 2; ! 414: return tight_fill_palette8(vs, x, y, max, count, bg, fg, palette); ! 415: } ! 416: return 0; ! 417: } ! 418: ! 419: /* ! 420: * Converting truecolor samples into palette indices. ! 421: */ ! 422: #define DEFINE_IDX_ENCODE_FUNCTION(bpp) \ ! 423: \ ! 424: static void \ ! 425: tight_encode_indexed_rect##bpp(uint8_t *buf, int count, \ ! 426: VncPalette *palette) { \ ! 427: uint##bpp##_t *src; \ ! 428: uint##bpp##_t rgb; \ ! 429: int i, rep; \ ! 430: uint8_t idx; \ ! 431: \ ! 432: src = (uint##bpp##_t *) buf; \ ! 433: \ ! 434: for (i = 0; i < count; i++) { \ ! 435: \ ! 436: rgb = *src++; \ ! 437: rep = 0; \ ! 438: while (i < count && *src == rgb) { \ ! 439: rep++, src++, i++; \ ! 440: } \ ! 441: idx = palette_idx(palette, rgb); \ ! 442: /* \ ! 443: * Should never happen, but don't break everything \ ! 444: * if it does, use the first color instead \ ! 445: */ \ ! 446: if (idx == -1) { \ ! 447: idx = 0; \ ! 448: } \ ! 449: while (rep >= 0) { \ ! 450: *buf++ = idx; \ ! 451: rep--; \ ! 452: } \ ! 453: } \ ! 454: } ! 455: ! 456: DEFINE_IDX_ENCODE_FUNCTION(16) ! 457: DEFINE_IDX_ENCODE_FUNCTION(32) ! 458: ! 459: #define DEFINE_MONO_ENCODE_FUNCTION(bpp) \ ! 460: \ ! 461: static void \ ! 462: tight_encode_mono_rect##bpp(uint8_t *buf, int w, int h, \ ! 463: uint##bpp##_t bg, uint##bpp##_t fg) { \ ! 464: uint##bpp##_t *ptr; \ ! 465: unsigned int value, mask; \ ! 466: int aligned_width; \ ! 467: int x, y, bg_bits; \ ! 468: \ ! 469: ptr = (uint##bpp##_t *) buf; \ ! 470: aligned_width = w - w % 8; \ ! 471: \ ! 472: for (y = 0; y < h; y++) { \ ! 473: for (x = 0; x < aligned_width; x += 8) { \ ! 474: for (bg_bits = 0; bg_bits < 8; bg_bits++) { \ ! 475: if (*ptr++ != bg) { \ ! 476: break; \ ! 477: } \ ! 478: } \ ! 479: if (bg_bits == 8) { \ ! 480: *buf++ = 0; \ ! 481: continue; \ ! 482: } \ ! 483: mask = 0x80 >> bg_bits; \ ! 484: value = mask; \ ! 485: for (bg_bits++; bg_bits < 8; bg_bits++) { \ ! 486: mask >>= 1; \ ! 487: if (*ptr++ != bg) { \ ! 488: value |= mask; \ ! 489: } \ ! 490: } \ ! 491: *buf++ = (uint8_t)value; \ ! 492: } \ ! 493: \ ! 494: mask = 0x80; \ ! 495: value = 0; \ ! 496: if (x >= w) { \ ! 497: continue; \ ! 498: } \ ! 499: \ ! 500: for (; x < w; x++) { \ ! 501: if (*ptr++ != bg) { \ ! 502: value |= mask; \ ! 503: } \ ! 504: mask >>= 1; \ ! 505: } \ ! 506: *buf++ = (uint8_t)value; \ ! 507: } \ ! 508: } ! 509: ! 510: DEFINE_MONO_ENCODE_FUNCTION(8) ! 511: DEFINE_MONO_ENCODE_FUNCTION(16) ! 512: DEFINE_MONO_ENCODE_FUNCTION(32) ! 513: ! 514: /* ! 515: * ``Gradient'' filter for 24-bit color samples. ! 516: * Should be called only when redMax, greenMax and blueMax are 255. ! 517: * Color components assumed to be byte-aligned. ! 518: */ ! 519: ! 520: static void ! 521: tight_filter_gradient24(VncState *vs, uint8_t *buf, int w, int h) ! 522: { ! 523: uint32_t *buf32; ! 524: uint32_t pix32; ! 525: int shift[3]; ! 526: int *prev; ! 527: int here[3], upper[3], left[3], upperleft[3]; ! 528: int prediction; ! 529: int x, y, c; ! 530: ! 531: buf32 = (uint32_t *)buf; ! 532: memset(vs->tight.gradient.buffer, 0, w * 3 * sizeof(int)); ! 533: ! 534: if ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) == ! 535: (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG)) { ! 536: shift[0] = vs->clientds.pf.rshift; ! 537: shift[1] = vs->clientds.pf.gshift; ! 538: shift[2] = vs->clientds.pf.bshift; ! 539: } else { ! 540: shift[0] = 24 - vs->clientds.pf.rshift; ! 541: shift[1] = 24 - vs->clientds.pf.gshift; ! 542: shift[2] = 24 - vs->clientds.pf.bshift; ! 543: } ! 544: ! 545: for (y = 0; y < h; y++) { ! 546: for (c = 0; c < 3; c++) { ! 547: upper[c] = 0; ! 548: here[c] = 0; ! 549: } ! 550: prev = (int *)vs->tight.gradient.buffer; ! 551: for (x = 0; x < w; x++) { ! 552: pix32 = *buf32++; ! 553: for (c = 0; c < 3; c++) { ! 554: upperleft[c] = upper[c]; ! 555: left[c] = here[c]; ! 556: upper[c] = *prev; ! 557: here[c] = (int)(pix32 >> shift[c] & 0xFF); ! 558: *prev++ = here[c]; ! 559: ! 560: prediction = left[c] + upper[c] - upperleft[c]; ! 561: if (prediction < 0) { ! 562: prediction = 0; ! 563: } else if (prediction > 0xFF) { ! 564: prediction = 0xFF; ! 565: } ! 566: *buf++ = (char)(here[c] - prediction); ! 567: } ! 568: } ! 569: } ! 570: } ! 571: ! 572: ! 573: /* ! 574: * ``Gradient'' filter for other color depths. ! 575: */ ! 576: ! 577: #define DEFINE_GRADIENT_FILTER_FUNCTION(bpp) \ ! 578: \ ! 579: static void \ ! 580: tight_filter_gradient##bpp(VncState *vs, uint##bpp##_t *buf, \ ! 581: int w, int h) { \ ! 582: uint##bpp##_t pix, diff; \ ! 583: bool endian; \ ! 584: int *prev; \ ! 585: int max[3], shift[3]; \ ! 586: int here[3], upper[3], left[3], upperleft[3]; \ ! 587: int prediction; \ ! 588: int x, y, c; \ ! 589: \ ! 590: memset (vs->tight.gradient.buffer, 0, w * 3 * sizeof(int)); \ ! 591: \ ! 592: endian = ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) != \ ! 593: (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG)); \ ! 594: \ ! 595: max[0] = vs->clientds.pf.rmax; \ ! 596: max[1] = vs->clientds.pf.gmax; \ ! 597: max[2] = vs->clientds.pf.bmax; \ ! 598: shift[0] = vs->clientds.pf.rshift; \ ! 599: shift[1] = vs->clientds.pf.gshift; \ ! 600: shift[2] = vs->clientds.pf.bshift; \ ! 601: \ ! 602: for (y = 0; y < h; y++) { \ ! 603: for (c = 0; c < 3; c++) { \ ! 604: upper[c] = 0; \ ! 605: here[c] = 0; \ ! 606: } \ ! 607: prev = (int *)vs->tight.gradient.buffer; \ ! 608: for (x = 0; x < w; x++) { \ ! 609: pix = *buf; \ ! 610: if (endian) { \ ! 611: pix = bswap_##bpp(pix); \ ! 612: } \ ! 613: diff = 0; \ ! 614: for (c = 0; c < 3; c++) { \ ! 615: upperleft[c] = upper[c]; \ ! 616: left[c] = here[c]; \ ! 617: upper[c] = *prev; \ ! 618: here[c] = (int)(pix >> shift[c] & max[c]); \ ! 619: *prev++ = here[c]; \ ! 620: \ ! 621: prediction = left[c] + upper[c] - upperleft[c]; \ ! 622: if (prediction < 0) { \ ! 623: prediction = 0; \ ! 624: } else if (prediction > max[c]) { \ ! 625: prediction = max[c]; \ ! 626: } \ ! 627: diff |= ((here[c] - prediction) & max[c]) \ ! 628: << shift[c]; \ ! 629: } \ ! 630: if (endian) { \ ! 631: diff = bswap_##bpp(diff); \ ! 632: } \ ! 633: *buf++ = diff; \ ! 634: } \ ! 635: } \ ! 636: } ! 637: ! 638: DEFINE_GRADIENT_FILTER_FUNCTION(16) ! 639: DEFINE_GRADIENT_FILTER_FUNCTION(32) ! 640: ! 641: /* ! 642: * Check if a rectangle is all of the same color. If needSameColor is ! 643: * set to non-zero, then also check that its color equals to the ! 644: * *colorPtr value. The result is 1 if the test is successfull, and in ! 645: * that case new color will be stored in *colorPtr. ! 646: */ ! 647: ! 648: #define DEFINE_CHECK_SOLID_FUNCTION(bpp) \ ! 649: \ ! 650: static bool \ ! 651: check_solid_tile##bpp(VncState *vs, int x, int y, int w, int h, \ ! 652: uint32_t* color, bool samecolor) \ ! 653: { \ ! 654: VncDisplay *vd = vs->vd; \ ! 655: uint##bpp##_t *fbptr; \ ! 656: uint##bpp##_t c; \ ! 657: int dx, dy; \ ! 658: \ ! 659: fbptr = (uint##bpp##_t *) \ ! 660: (vd->server->data + y * ds_get_linesize(vs->ds) + \ ! 661: x * ds_get_bytes_per_pixel(vs->ds)); \ ! 662: \ ! 663: c = *fbptr; \ ! 664: if (samecolor && (uint32_t)c != *color) { \ ! 665: return false; \ ! 666: } \ ! 667: \ ! 668: for (dy = 0; dy < h; dy++) { \ ! 669: for (dx = 0; dx < w; dx++) { \ ! 670: if (c != fbptr[dx]) { \ ! 671: return false; \ ! 672: } \ ! 673: } \ ! 674: fbptr = (uint##bpp##_t *) \ ! 675: ((uint8_t *)fbptr + ds_get_linesize(vs->ds)); \ ! 676: } \ ! 677: \ ! 678: *color = (uint32_t)c; \ ! 679: return true; \ ! 680: } ! 681: ! 682: DEFINE_CHECK_SOLID_FUNCTION(32) ! 683: DEFINE_CHECK_SOLID_FUNCTION(16) ! 684: DEFINE_CHECK_SOLID_FUNCTION(8) ! 685: ! 686: static bool check_solid_tile(VncState *vs, int x, int y, int w, int h, ! 687: uint32_t* color, bool samecolor) ! 688: { ! 689: VncDisplay *vd = vs->vd; ! 690: ! 691: switch(vd->server->pf.bytes_per_pixel) { ! 692: case 4: ! 693: return check_solid_tile32(vs, x, y, w, h, color, samecolor); ! 694: case 2: ! 695: return check_solid_tile16(vs, x, y, w, h, color, samecolor); ! 696: default: ! 697: return check_solid_tile8(vs, x, y, w, h, color, samecolor); ! 698: } ! 699: } ! 700: ! 701: static void find_best_solid_area(VncState *vs, int x, int y, int w, int h, ! 702: uint32_t color, int *w_ptr, int *h_ptr) ! 703: { ! 704: int dx, dy, dw, dh; ! 705: int w_prev; ! 706: int w_best = 0, h_best = 0; ! 707: ! 708: w_prev = w; ! 709: ! 710: for (dy = y; dy < y + h; dy += VNC_TIGHT_MAX_SPLIT_TILE_SIZE) { ! 711: ! 712: dh = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, y + h - dy); ! 713: dw = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, w_prev); ! 714: ! 715: if (!check_solid_tile(vs, x, dy, dw, dh, &color, true)) { ! 716: break; ! 717: } ! 718: ! 719: for (dx = x + dw; dx < x + w_prev;) { ! 720: dw = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, x + w_prev - dx); ! 721: ! 722: if (!check_solid_tile(vs, dx, dy, dw, dh, &color, true)) { ! 723: break; ! 724: } ! 725: dx += dw; ! 726: } ! 727: ! 728: w_prev = dx - x; ! 729: if (w_prev * (dy + dh - y) > w_best * h_best) { ! 730: w_best = w_prev; ! 731: h_best = dy + dh - y; ! 732: } ! 733: } ! 734: ! 735: *w_ptr = w_best; ! 736: *h_ptr = h_best; ! 737: } ! 738: ! 739: static void extend_solid_area(VncState *vs, int x, int y, int w, int h, ! 740: uint32_t color, int *x_ptr, int *y_ptr, ! 741: int *w_ptr, int *h_ptr) ! 742: { ! 743: int cx, cy; ! 744: ! 745: /* Try to extend the area upwards. */ ! 746: for ( cy = *y_ptr - 1; ! 747: cy >= y && check_solid_tile(vs, *x_ptr, cy, *w_ptr, 1, &color, true); ! 748: cy-- ); ! 749: *h_ptr += *y_ptr - (cy + 1); ! 750: *y_ptr = cy + 1; ! 751: ! 752: /* ... downwards. */ ! 753: for ( cy = *y_ptr + *h_ptr; ! 754: cy < y + h && ! 755: check_solid_tile(vs, *x_ptr, cy, *w_ptr, 1, &color, true); ! 756: cy++ ); ! 757: *h_ptr += cy - (*y_ptr + *h_ptr); ! 758: ! 759: /* ... to the left. */ ! 760: for ( cx = *x_ptr - 1; ! 761: cx >= x && check_solid_tile(vs, cx, *y_ptr, 1, *h_ptr, &color, true); ! 762: cx-- ); ! 763: *w_ptr += *x_ptr - (cx + 1); ! 764: *x_ptr = cx + 1; ! 765: ! 766: /* ... to the right. */ ! 767: for ( cx = *x_ptr + *w_ptr; ! 768: cx < x + w && ! 769: check_solid_tile(vs, cx, *y_ptr, 1, *h_ptr, &color, true); ! 770: cx++ ); ! 771: *w_ptr += cx - (*x_ptr + *w_ptr); ! 772: } ! 773: ! 774: static int tight_init_stream(VncState *vs, int stream_id, ! 775: int level, int strategy) ! 776: { ! 777: z_streamp zstream = &vs->tight.stream[stream_id]; ! 778: ! 779: if (zstream->opaque == NULL) { ! 780: int err; ! 781: ! 782: VNC_DEBUG("VNC: TIGHT: initializing zlib stream %d\n", stream_id); ! 783: VNC_DEBUG("VNC: TIGHT: opaque = %p | vs = %p\n", zstream->opaque, vs); ! 784: zstream->zalloc = vnc_zlib_zalloc; ! 785: zstream->zfree = vnc_zlib_zfree; ! 786: ! 787: err = deflateInit2(zstream, level, Z_DEFLATED, MAX_WBITS, ! 788: MAX_MEM_LEVEL, strategy); ! 789: ! 790: if (err != Z_OK) { ! 791: fprintf(stderr, "VNC: error initializing zlib\n"); ! 792: return -1; ! 793: } ! 794: ! 795: vs->tight.levels[stream_id] = level; ! 796: zstream->opaque = vs; ! 797: } ! 798: ! 799: if (vs->tight.levels[stream_id] != level) { ! 800: if (deflateParams(zstream, level, strategy) != Z_OK) { ! 801: return -1; ! 802: } ! 803: vs->tight.levels[stream_id] = level; ! 804: } ! 805: return 0; ! 806: } ! 807: ! 808: static void tight_send_compact_size(VncState *vs, size_t len) ! 809: { ! 810: int lpc = 0; ! 811: int bytes = 0; ! 812: char buf[3] = {0, 0, 0}; ! 813: ! 814: buf[bytes++] = len & 0x7F; ! 815: if (len > 0x7F) { ! 816: buf[bytes-1] |= 0x80; ! 817: buf[bytes++] = (len >> 7) & 0x7F; ! 818: if (len > 0x3FFF) { ! 819: buf[bytes-1] |= 0x80; ! 820: buf[bytes++] = (len >> 14) & 0xFF; ! 821: } ! 822: } ! 823: for (lpc = 0; lpc < bytes; lpc++) { ! 824: vnc_write_u8(vs, buf[lpc]); ! 825: } ! 826: } ! 827: ! 828: static int tight_compress_data(VncState *vs, int stream_id, size_t bytes, ! 829: int level, int strategy) ! 830: { ! 831: z_streamp zstream = &vs->tight.stream[stream_id]; ! 832: int previous_out; ! 833: ! 834: if (bytes < VNC_TIGHT_MIN_TO_COMPRESS) { ! 835: vnc_write(vs, vs->tight.tight.buffer, vs->tight.tight.offset); ! 836: return bytes; ! 837: } ! 838: ! 839: if (tight_init_stream(vs, stream_id, level, strategy)) { ! 840: return -1; ! 841: } ! 842: ! 843: /* reserve memory in output buffer */ ! 844: buffer_reserve(&vs->tight.zlib, bytes + 64); ! 845: ! 846: /* set pointers */ ! 847: zstream->next_in = vs->tight.tight.buffer; ! 848: zstream->avail_in = vs->tight.tight.offset; ! 849: zstream->next_out = vs->tight.zlib.buffer + vs->tight.zlib.offset; ! 850: zstream->avail_out = vs->tight.zlib.capacity - vs->tight.zlib.offset; ! 851: zstream->data_type = Z_BINARY; ! 852: previous_out = zstream->total_out; ! 853: ! 854: /* start encoding */ ! 855: if (deflate(zstream, Z_SYNC_FLUSH) != Z_OK) { ! 856: fprintf(stderr, "VNC: error during tight compression\n"); ! 857: return -1; ! 858: } ! 859: ! 860: vs->tight.zlib.offset = vs->tight.zlib.capacity - zstream->avail_out; ! 861: bytes = zstream->total_out - previous_out; ! 862: ! 863: tight_send_compact_size(vs, bytes); ! 864: vnc_write(vs, vs->tight.zlib.buffer, bytes); ! 865: ! 866: buffer_reset(&vs->tight.zlib); ! 867: ! 868: return bytes; ! 869: } ! 870: ! 871: /* ! 872: * Subencoding implementations. ! 873: */ ! 874: static void tight_pack24(VncState *vs, uint8_t *buf, size_t count, size_t *ret) ! 875: { ! 876: uint32_t *buf32; ! 877: uint32_t pix; ! 878: int rshift, gshift, bshift; ! 879: ! 880: buf32 = (uint32_t *)buf; ! 881: ! 882: if ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) == ! 883: (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG)) { ! 884: rshift = vs->clientds.pf.rshift; ! 885: gshift = vs->clientds.pf.gshift; ! 886: bshift = vs->clientds.pf.bshift; ! 887: } else { ! 888: rshift = 24 - vs->clientds.pf.rshift; ! 889: gshift = 24 - vs->clientds.pf.gshift; ! 890: bshift = 24 - vs->clientds.pf.bshift; ! 891: } ! 892: ! 893: if (ret) { ! 894: *ret = count * 3; ! 895: } ! 896: ! 897: while (count--) { ! 898: pix = *buf32++; ! 899: *buf++ = (char)(pix >> rshift); ! 900: *buf++ = (char)(pix >> gshift); ! 901: *buf++ = (char)(pix >> bshift); ! 902: } ! 903: } ! 904: ! 905: static int send_full_color_rect(VncState *vs, int x, int y, int w, int h) ! 906: { ! 907: int stream = 0; ! 908: size_t bytes; ! 909: ! 910: #ifdef CONFIG_VNC_PNG ! 911: if (tight_can_send_png_rect(vs, w, h)) { ! 912: return send_png_rect(vs, x, y, w, h, NULL); ! 913: } ! 914: #endif ! 915: ! 916: vnc_write_u8(vs, stream << 4); /* no flushing, no filter */ ! 917: ! 918: if (vs->tight.pixel24) { ! 919: tight_pack24(vs, vs->tight.tight.buffer, w * h, &vs->tight.tight.offset); ! 920: bytes = 3; ! 921: } else { ! 922: bytes = vs->clientds.pf.bytes_per_pixel; ! 923: } ! 924: ! 925: bytes = tight_compress_data(vs, stream, w * h * bytes, ! 926: tight_conf[vs->tight.compression].raw_zlib_level, ! 927: Z_DEFAULT_STRATEGY); ! 928: ! 929: return (bytes >= 0); ! 930: } ! 931: ! 932: static int send_solid_rect(VncState *vs) ! 933: { ! 934: size_t bytes; ! 935: ! 936: vnc_write_u8(vs, VNC_TIGHT_FILL << 4); /* no flushing, no filter */ ! 937: ! 938: if (vs->tight.pixel24) { ! 939: tight_pack24(vs, vs->tight.tight.buffer, 1, &vs->tight.tight.offset); ! 940: bytes = 3; ! 941: } else { ! 942: bytes = vs->clientds.pf.bytes_per_pixel; ! 943: } ! 944: ! 945: vnc_write(vs, vs->tight.tight.buffer, bytes); ! 946: return 1; ! 947: } ! 948: ! 949: static int send_mono_rect(VncState *vs, int x, int y, ! 950: int w, int h, uint32_t bg, uint32_t fg) ! 951: { ! 952: size_t bytes; ! 953: int stream = 1; ! 954: int level = tight_conf[vs->tight.compression].mono_zlib_level; ! 955: ! 956: #ifdef CONFIG_VNC_PNG ! 957: if (tight_can_send_png_rect(vs, w, h)) { ! 958: int ret; ! 959: int bpp = vs->clientds.pf.bytes_per_pixel * 8; ! 960: VncPalette *palette = palette_new(2, bpp); ! 961: ! 962: palette_put(palette, bg); ! 963: palette_put(palette, fg); ! 964: ret = send_png_rect(vs, x, y, w, h, palette); ! 965: palette_destroy(palette); ! 966: return ret; ! 967: } ! 968: #endif ! 969: ! 970: bytes = ((w + 7) / 8) * h; ! 971: ! 972: vnc_write_u8(vs, (stream | VNC_TIGHT_EXPLICIT_FILTER) << 4); ! 973: vnc_write_u8(vs, VNC_TIGHT_FILTER_PALETTE); ! 974: vnc_write_u8(vs, 1); ! 975: ! 976: switch(vs->clientds.pf.bytes_per_pixel) { ! 977: case 4: ! 978: { ! 979: uint32_t buf[2] = {bg, fg}; ! 980: size_t ret = sizeof (buf); ! 981: ! 982: if (vs->tight.pixel24) { ! 983: tight_pack24(vs, (unsigned char*)buf, 2, &ret); ! 984: } ! 985: vnc_write(vs, buf, ret); ! 986: ! 987: tight_encode_mono_rect32(vs->tight.tight.buffer, w, h, bg, fg); ! 988: break; ! 989: } ! 990: case 2: ! 991: vnc_write(vs, &bg, 2); ! 992: vnc_write(vs, &fg, 2); ! 993: tight_encode_mono_rect16(vs->tight.tight.buffer, w, h, bg, fg); ! 994: break; ! 995: default: ! 996: vnc_write_u8(vs, bg); ! 997: vnc_write_u8(vs, fg); ! 998: tight_encode_mono_rect8(vs->tight.tight.buffer, w, h, bg, fg); ! 999: break; ! 1000: } ! 1001: vs->tight.tight.offset = bytes; ! 1002: ! 1003: bytes = tight_compress_data(vs, stream, bytes, level, Z_DEFAULT_STRATEGY); ! 1004: return (bytes >= 0); ! 1005: } ! 1006: ! 1007: struct palette_cb_priv { ! 1008: VncState *vs; ! 1009: uint8_t *header; ! 1010: #ifdef CONFIG_VNC_PNG ! 1011: png_colorp png_palette; ! 1012: #endif ! 1013: }; ! 1014: ! 1015: static void write_palette(int idx, uint32_t color, void *opaque) ! 1016: { ! 1017: struct palette_cb_priv *priv = opaque; ! 1018: VncState *vs = priv->vs; ! 1019: uint32_t bytes = vs->clientds.pf.bytes_per_pixel; ! 1020: ! 1021: if (bytes == 4) { ! 1022: ((uint32_t*)priv->header)[idx] = color; ! 1023: } else { ! 1024: ((uint16_t*)priv->header)[idx] = color; ! 1025: } ! 1026: } ! 1027: ! 1028: static bool send_gradient_rect(VncState *vs, int x, int y, int w, int h) ! 1029: { ! 1030: int stream = 3; ! 1031: int level = tight_conf[vs->tight.compression].gradient_zlib_level; ! 1032: size_t bytes; ! 1033: ! 1034: if (vs->clientds.pf.bytes_per_pixel == 1) ! 1035: return send_full_color_rect(vs, x, y, w, h); ! 1036: ! 1037: vnc_write_u8(vs, (stream | VNC_TIGHT_EXPLICIT_FILTER) << 4); ! 1038: vnc_write_u8(vs, VNC_TIGHT_FILTER_GRADIENT); ! 1039: ! 1040: buffer_reserve(&vs->tight.gradient, w * 3 * sizeof (int)); ! 1041: ! 1042: if (vs->tight.pixel24) { ! 1043: tight_filter_gradient24(vs, vs->tight.tight.buffer, w, h); ! 1044: bytes = 3; ! 1045: } else if (vs->clientds.pf.bytes_per_pixel == 4) { ! 1046: tight_filter_gradient32(vs, (uint32_t *)vs->tight.tight.buffer, w, h); ! 1047: bytes = 4; ! 1048: } else { ! 1049: tight_filter_gradient16(vs, (uint16_t *)vs->tight.tight.buffer, w, h); ! 1050: bytes = 2; ! 1051: } ! 1052: ! 1053: buffer_reset(&vs->tight.gradient); ! 1054: ! 1055: bytes = w * h * bytes; ! 1056: vs->tight.tight.offset = bytes; ! 1057: ! 1058: bytes = tight_compress_data(vs, stream, bytes, ! 1059: level, Z_FILTERED); ! 1060: return (bytes >= 0); ! 1061: } ! 1062: ! 1063: static int send_palette_rect(VncState *vs, int x, int y, ! 1064: int w, int h, VncPalette *palette) ! 1065: { ! 1066: int stream = 2; ! 1067: int level = tight_conf[vs->tight.compression].idx_zlib_level; ! 1068: int colors; ! 1069: size_t bytes; ! 1070: ! 1071: #ifdef CONFIG_VNC_PNG ! 1072: if (tight_can_send_png_rect(vs, w, h)) { ! 1073: return send_png_rect(vs, x, y, w, h, palette); ! 1074: } ! 1075: #endif ! 1076: ! 1077: colors = palette_size(palette); ! 1078: ! 1079: vnc_write_u8(vs, (stream | VNC_TIGHT_EXPLICIT_FILTER) << 4); ! 1080: vnc_write_u8(vs, VNC_TIGHT_FILTER_PALETTE); ! 1081: vnc_write_u8(vs, colors - 1); ! 1082: ! 1083: switch(vs->clientds.pf.bytes_per_pixel) { ! 1084: case 4: ! 1085: { ! 1086: size_t old_offset, offset; ! 1087: uint32_t header[palette_size(palette)]; ! 1088: struct palette_cb_priv priv = { vs, (uint8_t *)header }; ! 1089: ! 1090: old_offset = vs->output.offset; ! 1091: palette_iter(palette, write_palette, &priv); ! 1092: vnc_write(vs, header, sizeof(header)); ! 1093: ! 1094: if (vs->tight.pixel24) { ! 1095: tight_pack24(vs, vs->output.buffer + old_offset, colors, &offset); ! 1096: vs->output.offset = old_offset + offset; ! 1097: } ! 1098: ! 1099: tight_encode_indexed_rect32(vs->tight.tight.buffer, w * h, palette); ! 1100: break; ! 1101: } ! 1102: case 2: ! 1103: { ! 1104: uint16_t header[palette_size(palette)]; ! 1105: struct palette_cb_priv priv = { vs, (uint8_t *)header }; ! 1106: ! 1107: palette_iter(palette, write_palette, &priv); ! 1108: vnc_write(vs, header, sizeof(header)); ! 1109: tight_encode_indexed_rect16(vs->tight.tight.buffer, w * h, palette); ! 1110: break; ! 1111: } ! 1112: default: ! 1113: return -1; /* No palette for 8bits colors */ ! 1114: break; ! 1115: } ! 1116: bytes = w * h; ! 1117: vs->tight.tight.offset = bytes; ! 1118: ! 1119: bytes = tight_compress_data(vs, stream, bytes, ! 1120: level, Z_DEFAULT_STRATEGY); ! 1121: return (bytes >= 0); ! 1122: } ! 1123: ! 1124: #if defined(CONFIG_VNC_JPEG) || defined(CONFIG_VNC_PNG) ! 1125: static void rgb_prepare_row24(VncState *vs, uint8_t *dst, int x, int y, ! 1126: int count) ! 1127: { ! 1128: VncDisplay *vd = vs->vd; ! 1129: uint32_t *fbptr; ! 1130: uint32_t pix; ! 1131: ! 1132: fbptr = (uint32_t *)(vd->server->data + y * ds_get_linesize(vs->ds) + ! 1133: x * ds_get_bytes_per_pixel(vs->ds)); ! 1134: ! 1135: while (count--) { ! 1136: pix = *fbptr++; ! 1137: *dst++ = (uint8_t)(pix >> vs->ds->surface->pf.rshift); ! 1138: *dst++ = (uint8_t)(pix >> vs->ds->surface->pf.gshift); ! 1139: *dst++ = (uint8_t)(pix >> vs->ds->surface->pf.bshift); ! 1140: } ! 1141: } ! 1142: ! 1143: #define DEFINE_RGB_GET_ROW_FUNCTION(bpp) \ ! 1144: \ ! 1145: static void \ ! 1146: rgb_prepare_row##bpp(VncState *vs, uint8_t *dst, \ ! 1147: int x, int y, int count) \ ! 1148: { \ ! 1149: VncDisplay *vd = vs->vd; \ ! 1150: uint##bpp##_t *fbptr; \ ! 1151: uint##bpp##_t pix; \ ! 1152: int r, g, b; \ ! 1153: \ ! 1154: fbptr = (uint##bpp##_t *) \ ! 1155: (vd->server->data + y * ds_get_linesize(vs->ds) + \ ! 1156: x * ds_get_bytes_per_pixel(vs->ds)); \ ! 1157: \ ! 1158: while (count--) { \ ! 1159: pix = *fbptr++; \ ! 1160: \ ! 1161: r = (int)((pix >> vs->ds->surface->pf.rshift) \ ! 1162: & vs->ds->surface->pf.rmax); \ ! 1163: g = (int)((pix >> vs->ds->surface->pf.gshift) \ ! 1164: & vs->ds->surface->pf.gmax); \ ! 1165: b = (int)((pix >> vs->ds->surface->pf.bshift) \ ! 1166: & vs->ds->surface->pf.bmax); \ ! 1167: \ ! 1168: *dst++ = (uint8_t)((r * 255 + vs->ds->surface->pf.rmax / 2) \ ! 1169: / vs->ds->surface->pf.rmax); \ ! 1170: *dst++ = (uint8_t)((g * 255 + vs->ds->surface->pf.gmax / 2) \ ! 1171: / vs->ds->surface->pf.gmax); \ ! 1172: *dst++ = (uint8_t)((b * 255 + vs->ds->surface->pf.bmax / 2) \ ! 1173: / vs->ds->surface->pf.bmax); \ ! 1174: } \ ! 1175: } ! 1176: ! 1177: DEFINE_RGB_GET_ROW_FUNCTION(16) ! 1178: DEFINE_RGB_GET_ROW_FUNCTION(32) ! 1179: ! 1180: static void rgb_prepare_row(VncState *vs, uint8_t *dst, int x, int y, ! 1181: int count) ! 1182: { ! 1183: if (ds_get_bytes_per_pixel(vs->ds) == 4) { ! 1184: if (vs->ds->surface->pf.rmax == 0xFF && ! 1185: vs->ds->surface->pf.gmax == 0xFF && ! 1186: vs->ds->surface->pf.bmax == 0xFF) { ! 1187: rgb_prepare_row24(vs, dst, x, y, count); ! 1188: } else { ! 1189: rgb_prepare_row32(vs, dst, x, y, count); ! 1190: } ! 1191: } else { ! 1192: rgb_prepare_row16(vs, dst, x, y, count); ! 1193: } ! 1194: } ! 1195: #endif /* CONFIG_VNC_JPEG or CONFIG_VNC_PNG */ ! 1196: ! 1197: /* ! 1198: * JPEG compression stuff. ! 1199: */ ! 1200: #ifdef CONFIG_VNC_JPEG ! 1201: /* ! 1202: * Destination manager implementation for JPEG library. ! 1203: */ ! 1204: ! 1205: /* This is called once per encoding */ ! 1206: static void jpeg_init_destination(j_compress_ptr cinfo) ! 1207: { ! 1208: VncState *vs = cinfo->client_data; ! 1209: Buffer *buffer = &vs->tight.jpeg; ! 1210: ! 1211: cinfo->dest->next_output_byte = (JOCTET *)buffer->buffer + buffer->offset; ! 1212: cinfo->dest->free_in_buffer = (size_t)(buffer->capacity - buffer->offset); ! 1213: } ! 1214: ! 1215: /* This is called when we ran out of buffer (shouldn't happen!) */ ! 1216: static boolean jpeg_empty_output_buffer(j_compress_ptr cinfo) ! 1217: { ! 1218: VncState *vs = cinfo->client_data; ! 1219: Buffer *buffer = &vs->tight.jpeg; ! 1220: ! 1221: buffer->offset = buffer->capacity; ! 1222: buffer_reserve(buffer, 2048); ! 1223: jpeg_init_destination(cinfo); ! 1224: return TRUE; ! 1225: } ! 1226: ! 1227: /* This is called when we are done processing data */ ! 1228: static void jpeg_term_destination(j_compress_ptr cinfo) ! 1229: { ! 1230: VncState *vs = cinfo->client_data; ! 1231: Buffer *buffer = &vs->tight.jpeg; ! 1232: ! 1233: buffer->offset = buffer->capacity - cinfo->dest->free_in_buffer; ! 1234: } ! 1235: ! 1236: static int send_jpeg_rect(VncState *vs, int x, int y, int w, int h, int quality) ! 1237: { ! 1238: struct jpeg_compress_struct cinfo; ! 1239: struct jpeg_error_mgr jerr; ! 1240: struct jpeg_destination_mgr manager; ! 1241: JSAMPROW row[1]; ! 1242: uint8_t *buf; ! 1243: int dy; ! 1244: ! 1245: if (ds_get_bytes_per_pixel(vs->ds) == 1) ! 1246: return send_full_color_rect(vs, x, y, w, h); ! 1247: ! 1248: buffer_reserve(&vs->tight.jpeg, 2048); ! 1249: ! 1250: cinfo.err = jpeg_std_error(&jerr); ! 1251: jpeg_create_compress(&cinfo); ! 1252: ! 1253: cinfo.client_data = vs; ! 1254: cinfo.image_width = w; ! 1255: cinfo.image_height = h; ! 1256: cinfo.input_components = 3; ! 1257: cinfo.in_color_space = JCS_RGB; ! 1258: ! 1259: jpeg_set_defaults(&cinfo); ! 1260: jpeg_set_quality(&cinfo, quality, true); ! 1261: ! 1262: manager.init_destination = jpeg_init_destination; ! 1263: manager.empty_output_buffer = jpeg_empty_output_buffer; ! 1264: manager.term_destination = jpeg_term_destination; ! 1265: cinfo.dest = &manager; ! 1266: ! 1267: jpeg_start_compress(&cinfo, true); ! 1268: ! 1269: buf = qemu_malloc(w * 3); ! 1270: row[0] = buf; ! 1271: for (dy = 0; dy < h; dy++) { ! 1272: rgb_prepare_row(vs, buf, x, y + dy, w); ! 1273: jpeg_write_scanlines(&cinfo, row, 1); ! 1274: } ! 1275: qemu_free(buf); ! 1276: ! 1277: jpeg_finish_compress(&cinfo); ! 1278: jpeg_destroy_compress(&cinfo); ! 1279: ! 1280: vnc_write_u8(vs, VNC_TIGHT_JPEG << 4); ! 1281: ! 1282: tight_send_compact_size(vs, vs->tight.jpeg.offset); ! 1283: vnc_write(vs, vs->tight.jpeg.buffer, vs->tight.jpeg.offset); ! 1284: buffer_reset(&vs->tight.jpeg); ! 1285: ! 1286: return 1; ! 1287: } ! 1288: #endif /* CONFIG_VNC_JPEG */ ! 1289: ! 1290: /* ! 1291: * PNG compression stuff. ! 1292: */ ! 1293: #ifdef CONFIG_VNC_PNG ! 1294: static void write_png_palette(int idx, uint32_t pix, void *opaque) ! 1295: { ! 1296: struct palette_cb_priv *priv = opaque; ! 1297: VncState *vs = priv->vs; ! 1298: png_colorp color = &priv->png_palette[idx]; ! 1299: ! 1300: if (vs->tight.pixel24) ! 1301: { ! 1302: color->red = (pix >> vs->clientds.pf.rshift) & vs->clientds.pf.rmax; ! 1303: color->green = (pix >> vs->clientds.pf.gshift) & vs->clientds.pf.gmax; ! 1304: color->blue = (pix >> vs->clientds.pf.bshift) & vs->clientds.pf.bmax; ! 1305: } ! 1306: else ! 1307: { ! 1308: int red, green, blue; ! 1309: ! 1310: red = (pix >> vs->clientds.pf.rshift) & vs->clientds.pf.rmax; ! 1311: green = (pix >> vs->clientds.pf.gshift) & vs->clientds.pf.gmax; ! 1312: blue = (pix >> vs->clientds.pf.bshift) & vs->clientds.pf.bmax; ! 1313: color->red = ((red * 255 + vs->clientds.pf.rmax / 2) / ! 1314: vs->clientds.pf.rmax); ! 1315: color->green = ((green * 255 + vs->clientds.pf.gmax / 2) / ! 1316: vs->clientds.pf.gmax); ! 1317: color->blue = ((blue * 255 + vs->clientds.pf.bmax / 2) / ! 1318: vs->clientds.pf.bmax); ! 1319: } ! 1320: } ! 1321: ! 1322: static void png_write_data(png_structp png_ptr, png_bytep data, ! 1323: png_size_t length) ! 1324: { ! 1325: VncState *vs = png_get_io_ptr(png_ptr); ! 1326: ! 1327: buffer_reserve(&vs->tight.png, vs->tight.png.offset + length); ! 1328: memcpy(vs->tight.png.buffer + vs->tight.png.offset, data, length); ! 1329: ! 1330: vs->tight.png.offset += length; ! 1331: } ! 1332: ! 1333: static void png_flush_data(png_structp png_ptr) ! 1334: { ! 1335: } ! 1336: ! 1337: static void *vnc_png_malloc(png_structp png_ptr, png_size_t size) ! 1338: { ! 1339: return qemu_malloc(size); ! 1340: } ! 1341: ! 1342: static void vnc_png_free(png_structp png_ptr, png_voidp ptr) ! 1343: { ! 1344: qemu_free(ptr); ! 1345: } ! 1346: ! 1347: static int send_png_rect(VncState *vs, int x, int y, int w, int h, ! 1348: VncPalette *palette) ! 1349: { ! 1350: png_byte color_type; ! 1351: png_structp png_ptr; ! 1352: png_infop info_ptr; ! 1353: png_colorp png_palette = NULL; ! 1354: size_t offset; ! 1355: int level = tight_png_conf[vs->tight.compression].png_zlib_level; ! 1356: int filters = tight_png_conf[vs->tight.compression].png_filters; ! 1357: uint8_t *buf; ! 1358: int dy; ! 1359: ! 1360: png_ptr = png_create_write_struct_2(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL, ! 1361: NULL, vnc_png_malloc, vnc_png_free); ! 1362: ! 1363: if (png_ptr == NULL) ! 1364: return -1; ! 1365: ! 1366: info_ptr = png_create_info_struct(png_ptr); ! 1367: ! 1368: if (info_ptr == NULL) { ! 1369: png_destroy_write_struct(&png_ptr, NULL); ! 1370: return -1; ! 1371: } ! 1372: ! 1373: png_set_write_fn(png_ptr, (void *) vs, png_write_data, png_flush_data); ! 1374: png_set_compression_level(png_ptr, level); ! 1375: png_set_filter(png_ptr, PNG_FILTER_TYPE_DEFAULT, filters); ! 1376: ! 1377: if (palette) { ! 1378: color_type = PNG_COLOR_TYPE_PALETTE; ! 1379: } else { ! 1380: color_type = PNG_COLOR_TYPE_RGB; ! 1381: } ! 1382: ! 1383: png_set_IHDR(png_ptr, info_ptr, w, h, ! 1384: 8, color_type, PNG_INTERLACE_NONE, ! 1385: PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); ! 1386: ! 1387: if (color_type == PNG_COLOR_TYPE_PALETTE) { ! 1388: struct palette_cb_priv priv; ! 1389: ! 1390: png_palette = png_malloc(png_ptr, sizeof(*png_palette) * ! 1391: palette_size(palette)); ! 1392: ! 1393: priv.vs = vs; ! 1394: priv.png_palette = png_palette; ! 1395: palette_iter(palette, write_png_palette, &priv); ! 1396: ! 1397: png_set_PLTE(png_ptr, info_ptr, png_palette, palette_size(palette)); ! 1398: ! 1399: offset = vs->tight.tight.offset; ! 1400: if (vs->clientds.pf.bytes_per_pixel == 4) { ! 1401: tight_encode_indexed_rect32(vs->tight.tight.buffer, w * h, palette); ! 1402: } else { ! 1403: tight_encode_indexed_rect16(vs->tight.tight.buffer, w * h, palette); ! 1404: } ! 1405: } ! 1406: ! 1407: png_write_info(png_ptr, info_ptr); ! 1408: ! 1409: buffer_reserve(&vs->tight.png, 2048); ! 1410: buf = qemu_malloc(w * 3); ! 1411: for (dy = 0; dy < h; dy++) ! 1412: { ! 1413: if (color_type == PNG_COLOR_TYPE_PALETTE) { ! 1414: memcpy(buf, vs->tight.tight.buffer + (dy * w), w); ! 1415: } else { ! 1416: rgb_prepare_row(vs, buf, x, y + dy, w); ! 1417: } ! 1418: png_write_row(png_ptr, buf); ! 1419: } ! 1420: qemu_free(buf); ! 1421: ! 1422: png_write_end(png_ptr, NULL); ! 1423: ! 1424: if (color_type == PNG_COLOR_TYPE_PALETTE) { ! 1425: png_free(png_ptr, png_palette); ! 1426: } ! 1427: ! 1428: png_destroy_write_struct(&png_ptr, &info_ptr); ! 1429: ! 1430: vnc_write_u8(vs, VNC_TIGHT_PNG << 4); ! 1431: ! 1432: tight_send_compact_size(vs, vs->tight.png.offset); ! 1433: vnc_write(vs, vs->tight.png.buffer, vs->tight.png.offset); ! 1434: buffer_reset(&vs->tight.png); ! 1435: return 1; ! 1436: } ! 1437: #endif /* CONFIG_VNC_PNG */ ! 1438: ! 1439: static void vnc_tight_start(VncState *vs) ! 1440: { ! 1441: buffer_reset(&vs->tight.tight); ! 1442: ! 1443: // make the output buffer be the zlib buffer, so we can compress it later ! 1444: vs->tight.tmp = vs->output; ! 1445: vs->output = vs->tight.tight; ! 1446: } ! 1447: ! 1448: static void vnc_tight_stop(VncState *vs) ! 1449: { ! 1450: // switch back to normal output/zlib buffers ! 1451: vs->tight.tight = vs->output; ! 1452: vs->output = vs->tight.tmp; ! 1453: } ! 1454: ! 1455: static int send_sub_rect_nojpeg(VncState *vs, int x, int y, int w, int h, ! 1456: int bg, int fg, int colors, VncPalette *palette) ! 1457: { ! 1458: int ret; ! 1459: ! 1460: if (colors == 0) { ! 1461: if (tight_detect_smooth_image(vs, w, h)) { ! 1462: ret = send_gradient_rect(vs, x, y, w, h); ! 1463: } else { ! 1464: ret = send_full_color_rect(vs, x, y, w, h); ! 1465: } ! 1466: } else if (colors == 1) { ! 1467: ret = send_solid_rect(vs); ! 1468: } else if (colors == 2) { ! 1469: ret = send_mono_rect(vs, x, y, w, h, bg, fg); ! 1470: } else if (colors <= 256) { ! 1471: ret = send_palette_rect(vs, x, y, w, h, palette); ! 1472: } ! 1473: return ret; ! 1474: } ! 1475: ! 1476: #ifdef CONFIG_VNC_JPEG ! 1477: static int send_sub_rect_jpeg(VncState *vs, int x, int y, int w, int h, ! 1478: int bg, int fg, int colors, ! 1479: VncPalette *palette) ! 1480: { ! 1481: int ret; ! 1482: ! 1483: if (colors == 0) { ! 1484: if (tight_detect_smooth_image(vs, w, h)) { ! 1485: int quality = tight_conf[vs->tight.quality].jpeg_quality; ! 1486: ! 1487: ret = send_jpeg_rect(vs, x, y, w, h, quality); ! 1488: } else { ! 1489: ret = send_full_color_rect(vs, x, y, w, h); ! 1490: } ! 1491: } else if (colors == 1) { ! 1492: ret = send_solid_rect(vs); ! 1493: } else if (colors == 2) { ! 1494: ret = send_mono_rect(vs, x, y, w, h, bg, fg); ! 1495: } else if (colors <= 256) { ! 1496: if (colors > 96 && ! 1497: tight_detect_smooth_image(vs, w, h)) { ! 1498: int quality = tight_conf[vs->tight.quality].jpeg_quality; ! 1499: ! 1500: ret = send_jpeg_rect(vs, x, y, w, h, quality); ! 1501: } else { ! 1502: ret = send_palette_rect(vs, x, y, w, h, palette); ! 1503: } ! 1504: } ! 1505: return ret; ! 1506: } ! 1507: #endif ! 1508: ! 1509: static int send_sub_rect(VncState *vs, int x, int y, int w, int h) ! 1510: { ! 1511: VncPalette *palette = NULL; ! 1512: uint32_t bg = 0, fg = 0; ! 1513: int colors; ! 1514: int ret = 0; ! 1515: ! 1516: vnc_framebuffer_update(vs, x, y, w, h, vs->tight.type); ! 1517: ! 1518: vnc_tight_start(vs); ! 1519: vnc_raw_send_framebuffer_update(vs, x, y, w, h); ! 1520: vnc_tight_stop(vs); ! 1521: ! 1522: colors = tight_fill_palette(vs, x, y, w * h, &fg, &bg, &palette); ! 1523: ! 1524: #ifdef CONFIG_VNC_JPEG ! 1525: if (vs->tight.quality != -1) { ! 1526: ret = send_sub_rect_jpeg(vs, x, y, w, h, bg, fg, colors, palette); ! 1527: } else { ! 1528: ret = send_sub_rect_nojpeg(vs, x, y, w, h, bg, fg, colors, palette); ! 1529: } ! 1530: #else ! 1531: ret = send_sub_rect_nojpeg(vs, x, y, w, h, bg, fg, colors, palette); ! 1532: #endif ! 1533: ! 1534: palette_destroy(palette); ! 1535: return ret; ! 1536: } ! 1537: ! 1538: static int send_sub_rect_solid(VncState *vs, int x, int y, int w, int h) ! 1539: { ! 1540: vnc_framebuffer_update(vs, x, y, w, h, vs->tight.type); ! 1541: ! 1542: vnc_tight_start(vs); ! 1543: vnc_raw_send_framebuffer_update(vs, x, y, w, h); ! 1544: vnc_tight_stop(vs); ! 1545: ! 1546: return send_solid_rect(vs); ! 1547: } ! 1548: ! 1549: static int send_rect_simple(VncState *vs, int x, int y, int w, int h) ! 1550: { ! 1551: int max_size, max_width; ! 1552: int max_sub_width, max_sub_height; ! 1553: int dx, dy; ! 1554: int rw, rh; ! 1555: int n = 0; ! 1556: ! 1557: max_size = tight_conf[vs->tight.compression].max_rect_size; ! 1558: max_width = tight_conf[vs->tight.compression].max_rect_width; ! 1559: ! 1560: if (w > max_width || w * h > max_size) { ! 1561: max_sub_width = (w > max_width) ? max_width : w; ! 1562: max_sub_height = max_size / max_sub_width; ! 1563: ! 1564: for (dy = 0; dy < h; dy += max_sub_height) { ! 1565: for (dx = 0; dx < w; dx += max_width) { ! 1566: rw = MIN(max_sub_width, w - dx); ! 1567: rh = MIN(max_sub_height, h - dy); ! 1568: n += send_sub_rect(vs, x+dx, y+dy, rw, rh); ! 1569: } ! 1570: } ! 1571: } else { ! 1572: n += send_sub_rect(vs, x, y, w, h); ! 1573: } ! 1574: ! 1575: return n; ! 1576: } ! 1577: ! 1578: static int find_large_solid_color_rect(VncState *vs, int x, int y, ! 1579: int w, int h, int max_rows) ! 1580: { ! 1581: int dx, dy, dw, dh; ! 1582: int n = 0; ! 1583: ! 1584: /* Try to find large solid-color areas and send them separately. */ ! 1585: ! 1586: for (dy = y; dy < y + h; dy += VNC_TIGHT_MAX_SPLIT_TILE_SIZE) { ! 1587: ! 1588: /* If a rectangle becomes too large, send its upper part now. */ ! 1589: ! 1590: if (dy - y >= max_rows) { ! 1591: n += send_rect_simple(vs, x, y, w, max_rows); ! 1592: y += max_rows; ! 1593: h -= max_rows; ! 1594: } ! 1595: ! 1596: dh = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, (y + h - dy)); ! 1597: ! 1598: for (dx = x; dx < x + w; dx += VNC_TIGHT_MAX_SPLIT_TILE_SIZE) { ! 1599: uint32_t color_value; ! 1600: int x_best, y_best, w_best, h_best; ! 1601: ! 1602: dw = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, (x + w - dx)); ! 1603: ! 1604: if (!check_solid_tile(vs, dx, dy, dw, dh, &color_value, false)) { ! 1605: continue ; ! 1606: } ! 1607: ! 1608: /* Get dimensions of solid-color area. */ ! 1609: ! 1610: find_best_solid_area(vs, dx, dy, w - (dx - x), h - (dy - y), ! 1611: color_value, &w_best, &h_best); ! 1612: ! 1613: /* Make sure a solid rectangle is large enough ! 1614: (or the whole rectangle is of the same color). */ ! 1615: ! 1616: if (w_best * h_best != w * h && ! 1617: w_best * h_best < VNC_TIGHT_MIN_SOLID_SUBRECT_SIZE) { ! 1618: continue; ! 1619: } ! 1620: ! 1621: /* Try to extend solid rectangle to maximum size. */ ! 1622: ! 1623: x_best = dx; y_best = dy; ! 1624: extend_solid_area(vs, x, y, w, h, color_value, ! 1625: &x_best, &y_best, &w_best, &h_best); ! 1626: ! 1627: /* Send rectangles at top and left to solid-color area. */ ! 1628: ! 1629: if (y_best != y) { ! 1630: n += send_rect_simple(vs, x, y, w, y_best-y); ! 1631: } ! 1632: if (x_best != x) { ! 1633: n += tight_send_framebuffer_update(vs, x, y_best, ! 1634: x_best-x, h_best); ! 1635: } ! 1636: ! 1637: /* Send solid-color rectangle. */ ! 1638: n += send_sub_rect_solid(vs, x_best, y_best, w_best, h_best); ! 1639: ! 1640: /* Send remaining rectangles (at right and bottom). */ ! 1641: ! 1642: if (x_best + w_best != x + w) { ! 1643: n += tight_send_framebuffer_update(vs, x_best+w_best, ! 1644: y_best, ! 1645: w-(x_best-x)-w_best, ! 1646: h_best); ! 1647: } ! 1648: if (y_best + h_best != y + h) { ! 1649: n += tight_send_framebuffer_update(vs, x, y_best+h_best, ! 1650: w, h-(y_best-y)-h_best); ! 1651: } ! 1652: ! 1653: /* Return after all recursive calls are done. */ ! 1654: return n; ! 1655: } ! 1656: } ! 1657: return n + send_rect_simple(vs, x, y, w, h); ! 1658: } ! 1659: ! 1660: static int tight_send_framebuffer_update(VncState *vs, int x, int y, ! 1661: int w, int h) ! 1662: { ! 1663: int max_rows; ! 1664: ! 1665: if (vs->clientds.pf.bytes_per_pixel == 4 && vs->clientds.pf.rmax == 0xFF && ! 1666: vs->clientds.pf.bmax == 0xFF && vs->clientds.pf.gmax == 0xFF) { ! 1667: vs->tight.pixel24 = true; ! 1668: } else { ! 1669: vs->tight.pixel24 = false; ! 1670: } ! 1671: ! 1672: if (w * h < VNC_TIGHT_MIN_SPLIT_RECT_SIZE) ! 1673: return send_rect_simple(vs, x, y, w, h); ! 1674: ! 1675: /* Calculate maximum number of rows in one non-solid rectangle. */ ! 1676: ! 1677: max_rows = tight_conf[vs->tight.compression].max_rect_size; ! 1678: max_rows /= MIN(tight_conf[vs->tight.compression].max_rect_width, w); ! 1679: ! 1680: return find_large_solid_color_rect(vs, x, y, w, h, max_rows); ! 1681: } ! 1682: ! 1683: int vnc_tight_send_framebuffer_update(VncState *vs, int x, int y, ! 1684: int w, int h) ! 1685: { ! 1686: vs->tight.type = VNC_ENCODING_TIGHT; ! 1687: return tight_send_framebuffer_update(vs, x, y, w, h); ! 1688: } ! 1689: ! 1690: int vnc_tight_png_send_framebuffer_update(VncState *vs, int x, int y, ! 1691: int w, int h) ! 1692: { ! 1693: vs->tight.type = VNC_ENCODING_TIGHT_PNG; ! 1694: return tight_send_framebuffer_update(vs, x, y, w, h); ! 1695: } ! 1696: ! 1697: void vnc_tight_clear(VncState *vs) ! 1698: { ! 1699: int i; ! 1700: for (i=0; i<ARRAY_SIZE(vs->tight.stream); i++) { ! 1701: if (vs->tight.stream[i].opaque) { ! 1702: deflateEnd(&vs->tight.stream[i]); ! 1703: } ! 1704: } ! 1705: ! 1706: buffer_free(&vs->tight.tight); ! 1707: buffer_free(&vs->tight.zlib); ! 1708: buffer_free(&vs->tight.gradient); ! 1709: #ifdef CONFIG_VNC_JPEG ! 1710: buffer_free(&vs->tight.jpeg); ! 1711: #endif ! 1712: #ifdef CONFIG_VNC_PNG ! 1713: buffer_free(&vs->tight.png); ! 1714: #endif ! 1715: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.