|
|
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.