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