Revision 2f6f5c7a vnc-encoding-tight.c
b/vnc-encoding-tight.c | ||
---|---|---|
26 | 26 |
* THE SOFTWARE. |
27 | 27 |
*/ |
28 | 28 |
|
29 |
#include "qemu-common.h" |
|
30 |
|
|
31 |
#ifdef CONFIG_VNC_JPEG |
|
32 |
#include <stdio.h> |
|
33 |
#include <jpeglib.h> |
|
34 |
#endif |
|
35 |
|
|
36 |
#include "bswap.h" |
|
29 | 37 |
#include "qdict.h" |
30 | 38 |
#include "qint.h" |
31 | 39 |
#include "vnc.h" |
... | ... | |
56 | 64 |
}; |
57 | 65 |
|
58 | 66 |
/* |
67 |
* Code to guess if given rectangle is suitable for smooth image |
|
68 |
* compression (by applying "gradient" filter or JPEG coder). |
|
69 |
*/ |
|
70 |
|
|
71 |
static uint |
|
72 |
tight_detect_smooth_image24(VncState *vs, int w, int h) |
|
73 |
{ |
|
74 |
int off; |
|
75 |
int x, y, d, dx; |
|
76 |
uint c; |
|
77 |
uint stats[256]; |
|
78 |
int pixels = 0; |
|
79 |
int pix, left[3]; |
|
80 |
uint errors; |
|
81 |
unsigned char *buf = vs->tight.buffer; |
|
82 |
|
|
83 |
/* |
|
84 |
* If client is big-endian, color samples begin from the second |
|
85 |
* byte (offset 1) of a 32-bit pixel value. |
|
86 |
*/ |
|
87 |
off = !!(vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG); |
|
88 |
|
|
89 |
memset(stats, 0, sizeof (stats)); |
|
90 |
|
|
91 |
for (y = 0, x = 0; y < h && x < w;) { |
|
92 |
for (d = 0; d < h - y && d < w - x - VNC_TIGHT_DETECT_SUBROW_WIDTH; |
|
93 |
d++) { |
|
94 |
for (c = 0; c < 3; c++) { |
|
95 |
left[c] = buf[((y+d)*w+x+d)*4+off+c] & 0xFF; |
|
96 |
} |
|
97 |
for (dx = 1; dx <= VNC_TIGHT_DETECT_SUBROW_WIDTH; dx++) { |
|
98 |
for (c = 0; c < 3; c++) { |
|
99 |
pix = buf[((y+d)*w+x+d+dx)*4+off+c] & 0xFF; |
|
100 |
stats[abs(pix - left[c])]++; |
|
101 |
left[c] = pix; |
|
102 |
} |
|
103 |
pixels++; |
|
104 |
} |
|
105 |
} |
|
106 |
if (w > h) { |
|
107 |
x += h; |
|
108 |
y = 0; |
|
109 |
} else { |
|
110 |
x = 0; |
|
111 |
y += w; |
|
112 |
} |
|
113 |
} |
|
114 |
|
|
115 |
/* 95% smooth or more ... */ |
|
116 |
if (stats[0] * 33 / pixels >= 95) { |
|
117 |
return 0; |
|
118 |
} |
|
119 |
|
|
120 |
errors = 0; |
|
121 |
for (c = 1; c < 8; c++) { |
|
122 |
errors += stats[c] * (c * c); |
|
123 |
if (stats[c] == 0 || stats[c] > stats[c-1] * 2) { |
|
124 |
return 0; |
|
125 |
} |
|
126 |
} |
|
127 |
for (; c < 256; c++) { |
|
128 |
errors += stats[c] * (c * c); |
|
129 |
} |
|
130 |
errors /= (pixels * 3 - stats[0]); |
|
131 |
|
|
132 |
return errors; |
|
133 |
} |
|
134 |
|
|
135 |
#define DEFINE_DETECT_FUNCTION(bpp) \ |
|
136 |
\ |
|
137 |
static uint \ |
|
138 |
tight_detect_smooth_image##bpp(VncState *vs, int w, int h) { \ |
|
139 |
bool endian; \ |
|
140 |
uint##bpp##_t pix; \ |
|
141 |
int max[3], shift[3]; \ |
|
142 |
int x, y, d, dx; \ |
|
143 |
uint c; \ |
|
144 |
uint stats[256]; \ |
|
145 |
int pixels = 0; \ |
|
146 |
int sample, sum, left[3]; \ |
|
147 |
uint errors; \ |
|
148 |
unsigned char *buf = vs->tight.buffer; \ |
|
149 |
\ |
|
150 |
endian = ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) != \ |
|
151 |
(vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG)); \ |
|
152 |
\ |
|
153 |
\ |
|
154 |
max[0] = vs->clientds.pf.rmax; \ |
|
155 |
max[1] = vs->clientds.pf.gmax; \ |
|
156 |
max[2] = vs->clientds.pf.bmax; \ |
|
157 |
shift[0] = vs->clientds.pf.rshift; \ |
|
158 |
shift[1] = vs->clientds.pf.gshift; \ |
|
159 |
shift[2] = vs->clientds.pf.bshift; \ |
|
160 |
\ |
|
161 |
memset(stats, 0, sizeof(stats)); \ |
|
162 |
\ |
|
163 |
y = 0, x = 0; \ |
|
164 |
while (y < h && x < w) { \ |
|
165 |
for (d = 0; d < h - y && \ |
|
166 |
d < w - x - VNC_TIGHT_DETECT_SUBROW_WIDTH; d++) { \ |
|
167 |
pix = ((uint##bpp##_t *)buf)[(y+d)*w+x+d]; \ |
|
168 |
if (endian) { \ |
|
169 |
pix = bswap_##bpp(pix); \ |
|
170 |
} \ |
|
171 |
for (c = 0; c < 3; c++) { \ |
|
172 |
left[c] = (int)(pix >> shift[c] & max[c]); \ |
|
173 |
} \ |
|
174 |
for (dx = 1; dx <= VNC_TIGHT_DETECT_SUBROW_WIDTH; \ |
|
175 |
dx++) { \ |
|
176 |
pix = ((uint##bpp##_t *)buf)[(y+d)*w+x+d+dx]; \ |
|
177 |
if (endian) { \ |
|
178 |
pix = bswap_##bpp(pix); \ |
|
179 |
} \ |
|
180 |
sum = 0; \ |
|
181 |
for (c = 0; c < 3; c++) { \ |
|
182 |
sample = (int)(pix >> shift[c] & max[c]); \ |
|
183 |
sum += abs(sample - left[c]); \ |
|
184 |
left[c] = sample; \ |
|
185 |
} \ |
|
186 |
if (sum > 255) { \ |
|
187 |
sum = 255; \ |
|
188 |
} \ |
|
189 |
stats[sum]++; \ |
|
190 |
pixels++; \ |
|
191 |
} \ |
|
192 |
} \ |
|
193 |
if (w > h) { \ |
|
194 |
x += h; \ |
|
195 |
y = 0; \ |
|
196 |
} else { \ |
|
197 |
x = 0; \ |
|
198 |
y += w; \ |
|
199 |
} \ |
|
200 |
} \ |
|
201 |
\ |
|
202 |
if ((stats[0] + stats[1]) * 100 / pixels >= 90) { \ |
|
203 |
return 0; \ |
|
204 |
} \ |
|
205 |
\ |
|
206 |
errors = 0; \ |
|
207 |
for (c = 1; c < 8; c++) { \ |
|
208 |
errors += stats[c] * (c * c); \ |
|
209 |
if (stats[c] == 0 || stats[c] > stats[c-1] * 2) { \ |
|
210 |
return 0; \ |
|
211 |
} \ |
|
212 |
} \ |
|
213 |
for (; c < 256; c++) { \ |
|
214 |
errors += stats[c] * (c * c); \ |
|
215 |
} \ |
|
216 |
errors /= (pixels - stats[0]); \ |
|
217 |
\ |
|
218 |
return errors; \ |
|
219 |
} |
|
220 |
|
|
221 |
DEFINE_DETECT_FUNCTION(16) |
|
222 |
DEFINE_DETECT_FUNCTION(32) |
|
223 |
|
|
224 |
static int |
|
225 |
tight_detect_smooth_image(VncState *vs, int w, int h) |
|
226 |
{ |
|
227 |
uint errors; |
|
228 |
int compression = vs->tight_compression; |
|
229 |
int quality = vs->tight_quality; |
|
230 |
|
|
231 |
if (ds_get_bytes_per_pixel(vs->ds) == 1 || |
|
232 |
vs->clientds.pf.bytes_per_pixel == 1 || |
|
233 |
w < VNC_TIGHT_DETECT_MIN_WIDTH || h < VNC_TIGHT_DETECT_MIN_HEIGHT) { |
|
234 |
return 0; |
|
235 |
} |
|
236 |
|
|
237 |
if (vs->tight_quality != -1) { |
|
238 |
if (w * h < VNC_TIGHT_JPEG_MIN_RECT_SIZE) { |
|
239 |
return 0; |
|
240 |
} |
|
241 |
} else { |
|
242 |
if (w * h < tight_conf[compression].gradient_min_rect_size) { |
|
243 |
return 0; |
|
244 |
} |
|
245 |
} |
|
246 |
|
|
247 |
if (vs->clientds.pf.bytes_per_pixel == 4) { |
|
248 |
if (vs->tight_pixel24) { |
|
249 |
errors = tight_detect_smooth_image24(vs, w, h); |
|
250 |
if (vs->tight_quality != -1) { |
|
251 |
return (errors < tight_conf[quality].jpeg_threshold24); |
|
252 |
} |
|
253 |
return (errors < tight_conf[compression].gradient_threshold24); |
|
254 |
} else { |
|
255 |
errors = tight_detect_smooth_image32(vs, w, h); |
|
256 |
} |
|
257 |
} else { |
|
258 |
errors = tight_detect_smooth_image16(vs, w, h); |
|
259 |
} |
|
260 |
if (quality != -1) { |
|
261 |
return (errors < tight_conf[quality].jpeg_threshold); |
|
262 |
} |
|
263 |
return (errors < tight_conf[compression].gradient_threshold); |
|
264 |
} |
|
265 |
|
|
266 |
/* |
|
59 | 267 |
* Code to determine how many different colors used in rectangle. |
60 | 268 |
*/ |
61 | 269 |
|
... | ... | |
335 | 543 |
DEFINE_MONO_ENCODE_FUNCTION(32) |
336 | 544 |
|
337 | 545 |
/* |
546 |
* ``Gradient'' filter for 24-bit color samples. |
|
547 |
* Should be called only when redMax, greenMax and blueMax are 255. |
|
548 |
* Color components assumed to be byte-aligned. |
|
549 |
*/ |
|
550 |
|
|
551 |
static void |
|
552 |
tight_filter_gradient24(VncState *vs, uint8_t *buf, int w, int h) |
|
553 |
{ |
|
554 |
uint32_t *buf32; |
|
555 |
uint32_t pix32; |
|
556 |
int shift[3]; |
|
557 |
int *prev; |
|
558 |
int here[3], upper[3], left[3], upperleft[3]; |
|
559 |
int prediction; |
|
560 |
int x, y, c; |
|
561 |
|
|
562 |
buf32 = (uint32_t *)buf; |
|
563 |
memset(vs->tight_gradient.buffer, 0, w * 3 * sizeof(int)); |
|
564 |
|
|
565 |
if ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) == |
|
566 |
(vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG)) { |
|
567 |
shift[0] = vs->clientds.pf.rshift; |
|
568 |
shift[1] = vs->clientds.pf.gshift; |
|
569 |
shift[2] = vs->clientds.pf.bshift; |
|
570 |
} else { |
|
571 |
shift[0] = 24 - vs->clientds.pf.rshift; |
|
572 |
shift[1] = 24 - vs->clientds.pf.gshift; |
|
573 |
shift[2] = 24 - vs->clientds.pf.bshift; |
|
574 |
} |
|
575 |
|
|
576 |
for (y = 0; y < h; y++) { |
|
577 |
for (c = 0; c < 3; c++) { |
|
578 |
upper[c] = 0; |
|
579 |
here[c] = 0; |
|
580 |
} |
|
581 |
prev = (int *)vs->tight_gradient.buffer; |
|
582 |
for (x = 0; x < w; x++) { |
|
583 |
pix32 = *buf32++; |
|
584 |
for (c = 0; c < 3; c++) { |
|
585 |
upperleft[c] = upper[c]; |
|
586 |
left[c] = here[c]; |
|
587 |
upper[c] = *prev; |
|
588 |
here[c] = (int)(pix32 >> shift[c] & 0xFF); |
|
589 |
*prev++ = here[c]; |
|
590 |
|
|
591 |
prediction = left[c] + upper[c] - upperleft[c]; |
|
592 |
if (prediction < 0) { |
|
593 |
prediction = 0; |
|
594 |
} else if (prediction > 0xFF) { |
|
595 |
prediction = 0xFF; |
|
596 |
} |
|
597 |
*buf++ = (char)(here[c] - prediction); |
|
598 |
} |
|
599 |
} |
|
600 |
} |
|
601 |
} |
|
602 |
|
|
603 |
|
|
604 |
/* |
|
605 |
* ``Gradient'' filter for other color depths. |
|
606 |
*/ |
|
607 |
|
|
608 |
#define DEFINE_GRADIENT_FILTER_FUNCTION(bpp) \ |
|
609 |
\ |
|
610 |
static void \ |
|
611 |
tight_filter_gradient##bpp(VncState *vs, uint##bpp##_t *buf, \ |
|
612 |
int w, int h) { \ |
|
613 |
uint##bpp##_t pix, diff; \ |
|
614 |
bool endian; \ |
|
615 |
int *prev; \ |
|
616 |
int max[3], shift[3]; \ |
|
617 |
int here[3], upper[3], left[3], upperleft[3]; \ |
|
618 |
int prediction; \ |
|
619 |
int x, y, c; \ |
|
620 |
\ |
|
621 |
memset (vs->tight_gradient.buffer, 0, w * 3 * sizeof(int)); \ |
|
622 |
\ |
|
623 |
endian = ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) != \ |
|
624 |
(vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG)); \ |
|
625 |
\ |
|
626 |
max[0] = vs->clientds.pf.rmax; \ |
|
627 |
max[1] = vs->clientds.pf.gmax; \ |
|
628 |
max[2] = vs->clientds.pf.bmax; \ |
|
629 |
shift[0] = vs->clientds.pf.rshift; \ |
|
630 |
shift[1] = vs->clientds.pf.gshift; \ |
|
631 |
shift[2] = vs->clientds.pf.bshift; \ |
|
632 |
\ |
|
633 |
for (y = 0; y < h; y++) { \ |
|
634 |
for (c = 0; c < 3; c++) { \ |
|
635 |
upper[c] = 0; \ |
|
636 |
here[c] = 0; \ |
|
637 |
} \ |
|
638 |
prev = (int *)vs->tight_gradient.buffer; \ |
|
639 |
for (x = 0; x < w; x++) { \ |
|
640 |
pix = *buf; \ |
|
641 |
if (endian) { \ |
|
642 |
pix = bswap_##bpp(pix); \ |
|
643 |
} \ |
|
644 |
diff = 0; \ |
|
645 |
for (c = 0; c < 3; c++) { \ |
|
646 |
upperleft[c] = upper[c]; \ |
|
647 |
left[c] = here[c]; \ |
|
648 |
upper[c] = *prev; \ |
|
649 |
here[c] = (int)(pix >> shift[c] & max[c]); \ |
|
650 |
*prev++ = here[c]; \ |
|
651 |
\ |
|
652 |
prediction = left[c] + upper[c] - upperleft[c]; \ |
|
653 |
if (prediction < 0) { \ |
|
654 |
prediction = 0; \ |
|
655 |
} else if (prediction > max[c]) { \ |
|
656 |
prediction = max[c]; \ |
|
657 |
} \ |
|
658 |
diff |= ((here[c] - prediction) & max[c]) \ |
|
659 |
<< shift[c]; \ |
|
660 |
} \ |
|
661 |
if (endian) { \ |
|
662 |
diff = bswap_##bpp(diff); \ |
|
663 |
} \ |
|
664 |
*buf++ = diff; \ |
|
665 |
} \ |
|
666 |
} \ |
|
667 |
} |
|
668 |
|
|
669 |
DEFINE_GRADIENT_FILTER_FUNCTION(16) |
|
670 |
DEFINE_GRADIENT_FILTER_FUNCTION(32) |
|
671 |
|
|
672 |
/* |
|
338 | 673 |
* Check if a rectangle is all of the same color. If needSameColor is |
339 | 674 |
* set to non-zero, then also check that its color equals to the |
340 | 675 |
* *colorPtr value. The result is 1 if the test is successfull, and in |
... | ... | |
702 | 1037 |
} |
703 | 1038 |
} |
704 | 1039 |
|
1040 |
static bool send_gradient_rect(VncState *vs, int w, int h) |
|
1041 |
{ |
|
1042 |
int stream = 3; |
|
1043 |
int level = tight_conf[vs->tight_compression].gradient_zlib_level; |
|
1044 |
size_t bytes; |
|
1045 |
|
|
1046 |
if (vs->clientds.pf.bytes_per_pixel == 1) |
|
1047 |
return send_full_color_rect(vs, w, h); |
|
1048 |
|
|
1049 |
vnc_write_u8(vs, (stream | VNC_TIGHT_EXPLICIT_FILTER) << 4); |
|
1050 |
vnc_write_u8(vs, VNC_TIGHT_FILTER_GRADIENT); |
|
1051 |
|
|
1052 |
buffer_reserve(&vs->tight_gradient, w * 3 * sizeof (int)); |
|
1053 |
|
|
1054 |
if (vs->tight_pixel24) { |
|
1055 |
tight_filter_gradient24(vs, vs->tight.buffer, w, h); |
|
1056 |
bytes = 3; |
|
1057 |
} else if (vs->clientds.pf.bytes_per_pixel == 4) { |
|
1058 |
tight_filter_gradient32(vs, (uint32_t *)vs->tight.buffer, w, h); |
|
1059 |
bytes = 4; |
|
1060 |
} else { |
|
1061 |
tight_filter_gradient16(vs, (uint16_t *)vs->tight.buffer, w, h); |
|
1062 |
bytes = 2; |
|
1063 |
} |
|
1064 |
|
|
1065 |
buffer_reset(&vs->tight_gradient); |
|
1066 |
|
|
1067 |
bytes = w * h * bytes; |
|
1068 |
vs->tight.offset = bytes; |
|
1069 |
|
|
1070 |
bytes = tight_compress_data(vs, stream, bytes, |
|
1071 |
level, Z_FILTERED); |
|
1072 |
return (bytes >= 0); |
|
1073 |
} |
|
1074 |
|
|
705 | 1075 |
static int send_palette_rect(VncState *vs, int w, int h, struct QDict *palette) |
706 | 1076 |
{ |
707 | 1077 |
int stream = 2; |
... | ... | |
756 | 1126 |
return (bytes >= 0); |
757 | 1127 |
} |
758 | 1128 |
|
1129 |
/* |
|
1130 |
* JPEG compression stuff. |
|
1131 |
*/ |
|
1132 |
#ifdef CONFIG_VNC_JPEG |
|
1133 |
static void jpeg_prepare_row24(VncState *vs, uint8_t *dst, int x, int y, |
|
1134 |
int count) |
|
1135 |
{ |
|
1136 |
VncDisplay *vd = vs->vd; |
|
1137 |
uint32_t *fbptr; |
|
1138 |
uint32_t pix; |
|
1139 |
|
|
1140 |
fbptr = (uint32_t *)(vd->server->data + y * ds_get_linesize(vs->ds) + |
|
1141 |
x * ds_get_bytes_per_pixel(vs->ds)); |
|
1142 |
|
|
1143 |
while (count--) { |
|
1144 |
pix = *fbptr++; |
|
1145 |
*dst++ = (uint8_t)(pix >> vs->ds->surface->pf.rshift); |
|
1146 |
*dst++ = (uint8_t)(pix >> vs->ds->surface->pf.gshift); |
|
1147 |
*dst++ = (uint8_t)(pix >> vs->ds->surface->pf.bshift); |
|
1148 |
} |
|
1149 |
} |
|
1150 |
|
|
1151 |
#define DEFINE_JPEG_GET_ROW_FUNCTION(bpp) \ |
|
1152 |
\ |
|
1153 |
static void \ |
|
1154 |
jpeg_prepare_row##bpp(VncState *vs, uint8_t *dst, \ |
|
1155 |
int x, int y, int count) \ |
|
1156 |
{ \ |
|
1157 |
VncDisplay *vd = vs->vd; \ |
|
1158 |
uint##bpp##_t *fbptr; \ |
|
1159 |
uint##bpp##_t pix; \ |
|
1160 |
int r, g, b; \ |
|
1161 |
\ |
|
1162 |
fbptr = (uint##bpp##_t *) \ |
|
1163 |
(vd->server->data + y * ds_get_linesize(vs->ds) + \ |
|
1164 |
x * ds_get_bytes_per_pixel(vs->ds)); \ |
|
1165 |
\ |
|
1166 |
while (count--) { \ |
|
1167 |
pix = *fbptr++; \ |
|
1168 |
\ |
|
1169 |
r = (int)((pix >> vs->ds->surface->pf.rshift) \ |
|
1170 |
& vs->ds->surface->pf.rmax); \ |
|
1171 |
g = (int)((pix >> vs->ds->surface->pf.gshift) \ |
|
1172 |
& vs->ds->surface->pf.gmax); \ |
|
1173 |
b = (int)((pix >> vs->ds->surface->pf.bshift) \ |
|
1174 |
& vs->ds->surface->pf.bmax); \ |
|
1175 |
\ |
|
1176 |
*dst++ = (uint8_t)((r * 255 + vs->ds->surface->pf.rmax / 2) \ |
|
1177 |
/ vs->ds->surface->pf.rmax); \ |
|
1178 |
*dst++ = (uint8_t)((g * 255 + vs->ds->surface->pf.gmax / 2) \ |
|
1179 |
/ vs->ds->surface->pf.gmax); \ |
|
1180 |
*dst++ = (uint8_t)((b * 255 + vs->ds->surface->pf.bmax / 2) \ |
|
1181 |
/ vs->ds->surface->pf.bmax); \ |
|
1182 |
} \ |
|
1183 |
} |
|
1184 |
|
|
1185 |
DEFINE_JPEG_GET_ROW_FUNCTION(16) |
|
1186 |
DEFINE_JPEG_GET_ROW_FUNCTION(32) |
|
1187 |
|
|
1188 |
static void jpeg_prepare_row(VncState *vs, uint8_t *dst, int x, int y, |
|
1189 |
int count) |
|
1190 |
{ |
|
1191 |
if (vs->tight_pixel24) |
|
1192 |
jpeg_prepare_row24(vs, dst, x, y, count); |
|
1193 |
else if (ds_get_bytes_per_pixel(vs->ds) == 4) |
|
1194 |
jpeg_prepare_row32(vs, dst, x, y, count); |
|
1195 |
else |
|
1196 |
jpeg_prepare_row16(vs, dst, x, y, count); |
|
1197 |
} |
|
1198 |
|
|
1199 |
/* |
|
1200 |
* Destination manager implementation for JPEG library. |
|
1201 |
*/ |
|
1202 |
|
|
1203 |
/* This is called once per encoding */ |
|
1204 |
static void jpeg_init_destination(j_compress_ptr cinfo) |
|
1205 |
{ |
|
1206 |
VncState *vs = cinfo->client_data; |
|
1207 |
Buffer *buffer = &vs->tight_jpeg; |
|
1208 |
|
|
1209 |
cinfo->dest->next_output_byte = (JOCTET *)buffer->buffer + buffer->offset; |
|
1210 |
cinfo->dest->free_in_buffer = (size_t)(buffer->capacity - buffer->offset); |
|
1211 |
} |
|
1212 |
|
|
1213 |
/* This is called when we ran out of buffer (shouldn't happen!) */ |
|
1214 |
static boolean jpeg_empty_output_buffer(j_compress_ptr cinfo) |
|
1215 |
{ |
|
1216 |
VncState *vs = cinfo->client_data; |
|
1217 |
Buffer *buffer = &vs->tight_jpeg; |
|
1218 |
|
|
1219 |
buffer->offset = buffer->capacity; |
|
1220 |
buffer_reserve(buffer, 2048); |
|
1221 |
jpeg_init_destination(cinfo); |
|
1222 |
return TRUE; |
|
1223 |
} |
|
1224 |
|
|
1225 |
/* This is called when we are done processing data */ |
|
1226 |
static void jpeg_term_destination(j_compress_ptr cinfo) |
|
1227 |
{ |
|
1228 |
VncState *vs = cinfo->client_data; |
|
1229 |
Buffer *buffer = &vs->tight_jpeg; |
|
1230 |
|
|
1231 |
buffer->offset = buffer->capacity - cinfo->dest->free_in_buffer; |
|
1232 |
} |
|
1233 |
|
|
1234 |
static int send_jpeg_rect(VncState *vs, int x, int y, int w, int h, int quality) |
|
1235 |
{ |
|
1236 |
struct jpeg_compress_struct cinfo; |
|
1237 |
struct jpeg_error_mgr jerr; |
|
1238 |
struct jpeg_destination_mgr manager; |
|
1239 |
JSAMPROW row[1]; |
|
1240 |
uint8_t *buf; |
|
1241 |
int dy; |
|
1242 |
|
|
1243 |
if (ds_get_bytes_per_pixel(vs->ds) == 1) |
|
1244 |
return send_full_color_rect(vs, w, h); |
|
1245 |
|
|
1246 |
buf = qemu_malloc(w * 3); |
|
1247 |
row[0] = buf; |
|
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 |
for (dy = 0; dy < h; dy++) { |
|
1270 |
jpeg_prepare_row(vs, buf, x, y + dy, w); |
|
1271 |
jpeg_write_scanlines(&cinfo, row, 1); |
|
1272 |
} |
|
1273 |
|
|
1274 |
jpeg_finish_compress(&cinfo); |
|
1275 |
jpeg_destroy_compress(&cinfo); |
|
1276 |
|
|
1277 |
vnc_write_u8(vs, VNC_TIGHT_JPEG << 4); |
|
1278 |
|
|
1279 |
tight_send_compact_size(vs, vs->tight_jpeg.offset); |
|
1280 |
vnc_write(vs, vs->tight_jpeg.buffer, vs->tight_jpeg.offset); |
|
1281 |
buffer_reset(&vs->tight_jpeg); |
|
1282 |
|
|
1283 |
return 1; |
|
1284 |
} |
|
1285 |
#endif /* CONFIG_VNC_JPEG */ |
|
1286 |
|
|
759 | 1287 |
static void vnc_tight_start(VncState *vs) |
760 | 1288 |
{ |
761 | 1289 |
buffer_reset(&vs->tight); |
... | ... | |
788 | 1316 |
colors = tight_fill_palette(vs, x, y, w * h, &fg, &bg, &palette); |
789 | 1317 |
|
790 | 1318 |
if (colors == 0) { |
791 |
ret = send_full_color_rect(vs, w, h); |
|
1319 |
if (tight_detect_smooth_image(vs, w, h)) { |
|
1320 |
if (vs->tight_quality == -1) { |
|
1321 |
ret = send_gradient_rect(vs, w, h); |
|
1322 |
} else { |
|
1323 |
#ifdef CONFIG_VNC_JPEG |
|
1324 |
int quality = tight_conf[vs->tight_quality].jpeg_quality; |
|
1325 |
|
|
1326 |
ret = send_jpeg_rect(vs, x, y, w, h, quality); |
|
1327 |
#else |
|
1328 |
ret = send_full_color_rect(vs, w, h); |
|
1329 |
#endif |
|
1330 |
} |
|
1331 |
} else { |
|
1332 |
ret = send_full_color_rect(vs, w, h); |
|
1333 |
} |
|
792 | 1334 |
} else if (colors == 1) { |
793 | 1335 |
ret = send_solid_rect(vs); |
794 | 1336 |
} else if (colors == 2) { |
795 | 1337 |
ret = send_mono_rect(vs, w, h, bg, fg); |
796 | 1338 |
} else if (colors <= 256) { |
1339 |
#ifdef CONFIG_VNC_JPEG |
|
1340 |
if (colors > 96 && vs->tight_quality != -1 && vs->tight_quality <= 3 && |
|
1341 |
tight_detect_smooth_image(vs, w, h)) { |
|
1342 |
int quality = tight_conf[vs->tight_quality].jpeg_quality; |
|
1343 |
|
|
1344 |
ret = send_jpeg_rect(vs, x, y, w, h, quality); |
|
1345 |
} else { |
|
1346 |
ret = send_palette_rect(vs, w, h, palette); |
|
1347 |
} |
|
1348 |
#else |
|
797 | 1349 |
ret = send_palette_rect(vs, w, h, palette); |
1350 |
#endif |
|
798 | 1351 |
} |
799 | 1352 |
QDECREF(palette); |
800 | 1353 |
return ret; |
... | ... | |
956 | 1509 |
|
957 | 1510 |
buffer_free(&vs->tight); |
958 | 1511 |
buffer_free(&vs->tight_zlib); |
1512 |
buffer_free(&vs->tight_gradient); |
|
1513 |
#ifdef CONFIG_VNC_JPEG |
|
1514 |
buffer_free(&vs->tight_jpeg); |
|
1515 |
#endif |
|
959 | 1516 |
} |
Also available in: Unified diff