]>
Commit | Line | Data |
---|---|---|
380282b0 CC |
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 <corentin.chary@gmail.com> | |
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 <stdbool.h> | |
30 | ||
31 | #include "vnc.h" | |
32 | #include "vnc-encoding-tight.h" | |
33 | ||
34 | /* Compression level stuff. The following array contains various | |
35 | encoder parameters for each of 10 compression levels (0..9). | |
36 | Last three parameters correspond to JPEG quality levels (0..9). */ | |
37 | ||
38 | static const struct { | |
39 | int max_rect_size, max_rect_width; | |
40 | int mono_min_rect_size, gradient_min_rect_size; | |
41 | int idx_zlib_level, mono_zlib_level, raw_zlib_level, gradient_zlib_level; | |
42 | int gradient_threshold, gradient_threshold24; | |
43 | int idx_max_colors_divisor; | |
44 | int jpeg_quality, jpeg_threshold, jpeg_threshold24; | |
45 | } tight_conf[] = { | |
46 | { 512, 32, 6, 65536, 0, 0, 0, 0, 0, 0, 4, 5, 10000, 23000 }, | |
47 | { 2048, 128, 6, 65536, 1, 1, 1, 0, 0, 0, 8, 10, 8000, 18000 }, | |
48 | { 6144, 256, 8, 65536, 3, 3, 2, 0, 0, 0, 24, 15, 6500, 15000 }, | |
49 | { 10240, 1024, 12, 65536, 5, 5, 3, 0, 0, 0, 32, 25, 5000, 12000 }, | |
50 | { 16384, 2048, 12, 65536, 6, 6, 4, 0, 0, 0, 32, 37, 4000, 10000 }, | |
51 | { 32768, 2048, 12, 4096, 7, 7, 5, 4, 150, 380, 32, 50, 3000, 8000 }, | |
52 | { 65536, 2048, 16, 4096, 7, 7, 6, 4, 170, 420, 48, 60, 2000, 5000 }, | |
53 | { 65536, 2048, 16, 4096, 8, 8, 7, 5, 180, 450, 64, 70, 1000, 2500 }, | |
54 | { 65536, 2048, 32, 8192, 9, 9, 8, 6, 190, 475, 64, 75, 500, 1200 }, | |
55 | { 65536, 2048, 32, 8192, 9, 9, 9, 6, 200, 500, 96, 80, 200, 500 } | |
56 | }; | |
57 | ||
b4bea3f2 CC |
58 | /* |
59 | * Check if a rectangle is all of the same color. If needSameColor is | |
60 | * set to non-zero, then also check that its color equals to the | |
61 | * *colorPtr value. The result is 1 if the test is successfull, and in | |
62 | * that case new color will be stored in *colorPtr. | |
63 | */ | |
64 | ||
65 | #define DEFINE_CHECK_SOLID_FUNCTION(bpp) \ | |
66 | \ | |
67 | static bool \ | |
68 | check_solid_tile##bpp(VncState *vs, int x, int y, int w, int h, \ | |
69 | uint32_t* color, bool samecolor) \ | |
70 | { \ | |
71 | VncDisplay *vd = vs->vd; \ | |
72 | uint##bpp##_t *fbptr; \ | |
73 | uint##bpp##_t c; \ | |
74 | int dx, dy; \ | |
75 | \ | |
76 | fbptr = (uint##bpp##_t *) \ | |
77 | (vd->server->data + y * ds_get_linesize(vs->ds) + \ | |
78 | x * ds_get_bytes_per_pixel(vs->ds)); \ | |
79 | \ | |
80 | c = *fbptr; \ | |
81 | if (samecolor && (uint32_t)c != *color) { \ | |
82 | return false; \ | |
83 | } \ | |
84 | \ | |
85 | for (dy = 0; dy < h; dy++) { \ | |
86 | for (dx = 0; dx < w; dx++) { \ | |
87 | if (c != fbptr[dx]) { \ | |
88 | return false; \ | |
89 | } \ | |
90 | } \ | |
91 | fbptr = (uint##bpp##_t *) \ | |
92 | ((uint8_t *)fbptr + ds_get_linesize(vs->ds)); \ | |
93 | } \ | |
94 | \ | |
95 | *color = (uint32_t)c; \ | |
96 | return true; \ | |
97 | } | |
98 | ||
99 | DEFINE_CHECK_SOLID_FUNCTION(32) | |
100 | DEFINE_CHECK_SOLID_FUNCTION(16) | |
101 | DEFINE_CHECK_SOLID_FUNCTION(8) | |
102 | ||
103 | static bool check_solid_tile(VncState *vs, int x, int y, int w, int h, | |
104 | uint32_t* color, bool samecolor) | |
105 | { | |
106 | VncDisplay *vd = vs->vd; | |
107 | ||
108 | switch(vd->server->pf.bytes_per_pixel) { | |
109 | case 4: | |
110 | return check_solid_tile32(vs, x, y, w, h, color, samecolor); | |
111 | case 2: | |
112 | return check_solid_tile16(vs, x, y, w, h, color, samecolor); | |
113 | default: | |
114 | return check_solid_tile8(vs, x, y, w, h, color, samecolor); | |
115 | } | |
116 | } | |
117 | ||
118 | static void find_best_solid_area(VncState *vs, int x, int y, int w, int h, | |
119 | uint32_t color, int *w_ptr, int *h_ptr) | |
120 | { | |
121 | int dx, dy, dw, dh; | |
122 | int w_prev; | |
123 | int w_best = 0, h_best = 0; | |
124 | ||
125 | w_prev = w; | |
126 | ||
127 | for (dy = y; dy < y + h; dy += VNC_TIGHT_MAX_SPLIT_TILE_SIZE) { | |
128 | ||
129 | dh = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, y + h - dy); | |
130 | dw = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, w_prev); | |
131 | ||
132 | if (!check_solid_tile(vs, x, dy, dw, dh, &color, true)) { | |
133 | break; | |
134 | } | |
135 | ||
136 | for (dx = x + dw; dx < x + w_prev;) { | |
137 | dw = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, x + w_prev - dx); | |
138 | ||
139 | if (!check_solid_tile(vs, dx, dy, dw, dh, &color, true)) { | |
140 | break; | |
141 | } | |
142 | dx += dw; | |
143 | } | |
144 | ||
145 | w_prev = dx - x; | |
146 | if (w_prev * (dy + dh - y) > w_best * h_best) { | |
147 | w_best = w_prev; | |
148 | h_best = dy + dh - y; | |
149 | } | |
150 | } | |
151 | ||
152 | *w_ptr = w_best; | |
153 | *h_ptr = h_best; | |
154 | } | |
155 | ||
156 | static void extend_solid_area(VncState *vs, int x, int y, int w, int h, | |
157 | uint32_t color, int *x_ptr, int *y_ptr, | |
158 | int *w_ptr, int *h_ptr) | |
159 | { | |
160 | int cx, cy; | |
161 | ||
162 | /* Try to extend the area upwards. */ | |
163 | for ( cy = *y_ptr - 1; | |
164 | cy >= y && check_solid_tile(vs, *x_ptr, cy, *w_ptr, 1, &color, true); | |
165 | cy-- ); | |
166 | *h_ptr += *y_ptr - (cy + 1); | |
167 | *y_ptr = cy + 1; | |
168 | ||
169 | /* ... downwards. */ | |
170 | for ( cy = *y_ptr + *h_ptr; | |
171 | cy < y + h && | |
172 | check_solid_tile(vs, *x_ptr, cy, *w_ptr, 1, &color, true); | |
173 | cy++ ); | |
174 | *h_ptr += cy - (*y_ptr + *h_ptr); | |
175 | ||
176 | /* ... to the left. */ | |
177 | for ( cx = *x_ptr - 1; | |
178 | cx >= x && check_solid_tile(vs, cx, *y_ptr, 1, *h_ptr, &color, true); | |
179 | cx-- ); | |
180 | *w_ptr += *x_ptr - (cx + 1); | |
181 | *x_ptr = cx + 1; | |
182 | ||
183 | /* ... to the right. */ | |
184 | for ( cx = *x_ptr + *w_ptr; | |
185 | cx < x + w && | |
186 | check_solid_tile(vs, cx, *y_ptr, 1, *h_ptr, &color, true); | |
187 | cx++ ); | |
188 | *w_ptr += cx - (*x_ptr + *w_ptr); | |
189 | } | |
190 | ||
380282b0 CC |
191 | static int tight_init_stream(VncState *vs, int stream_id, |
192 | int level, int strategy) | |
193 | { | |
194 | z_streamp zstream = &vs->tight_stream[stream_id]; | |
195 | ||
196 | if (zstream->opaque == NULL) { | |
197 | int err; | |
198 | ||
199 | VNC_DEBUG("VNC: TIGHT: initializing zlib stream %d\n", stream_id); | |
200 | VNC_DEBUG("VNC: TIGHT: opaque = %p | vs = %p\n", zstream->opaque, vs); | |
201 | zstream->zalloc = vnc_zlib_zalloc; | |
202 | zstream->zfree = vnc_zlib_zfree; | |
203 | ||
204 | err = deflateInit2(zstream, level, Z_DEFLATED, MAX_WBITS, | |
205 | MAX_MEM_LEVEL, strategy); | |
206 | ||
207 | if (err != Z_OK) { | |
208 | fprintf(stderr, "VNC: error initializing zlib\n"); | |
209 | return -1; | |
210 | } | |
211 | ||
212 | vs->tight_levels[stream_id] = level; | |
213 | zstream->opaque = vs; | |
214 | } | |
215 | ||
216 | if (vs->tight_levels[stream_id] != level) { | |
217 | if (deflateParams(zstream, level, strategy) != Z_OK) { | |
218 | return -1; | |
219 | } | |
220 | vs->tight_levels[stream_id] = level; | |
221 | } | |
222 | return 0; | |
223 | } | |
224 | ||
225 | static void tight_send_compact_size(VncState *vs, size_t len) | |
226 | { | |
227 | int lpc = 0; | |
228 | int bytes = 0; | |
229 | char buf[3] = {0, 0, 0}; | |
230 | ||
231 | buf[bytes++] = len & 0x7F; | |
232 | if (len > 0x7F) { | |
233 | buf[bytes-1] |= 0x80; | |
234 | buf[bytes++] = (len >> 7) & 0x7F; | |
235 | if (len > 0x3FFF) { | |
236 | buf[bytes-1] |= 0x80; | |
237 | buf[bytes++] = (len >> 14) & 0xFF; | |
238 | } | |
239 | } | |
b4bea3f2 | 240 | for (lpc = 0; lpc < bytes; lpc++) { |
380282b0 CC |
241 | vnc_write_u8(vs, buf[lpc]); |
242 | } | |
243 | } | |
244 | ||
245 | static int tight_compress_data(VncState *vs, int stream_id, size_t bytes, | |
246 | int level, int strategy) | |
247 | { | |
248 | z_streamp zstream = &vs->tight_stream[stream_id]; | |
249 | int previous_out; | |
250 | ||
251 | if (bytes < VNC_TIGHT_MIN_TO_COMPRESS) { | |
252 | vnc_write(vs, vs->tight.buffer, vs->tight.offset); | |
253 | return bytes; | |
254 | } | |
255 | ||
256 | if (tight_init_stream(vs, stream_id, level, strategy)) { | |
257 | return -1; | |
258 | } | |
259 | ||
260 | /* reserve memory in output buffer */ | |
261 | buffer_reserve(&vs->tight_zlib, bytes + 64); | |
262 | ||
263 | /* set pointers */ | |
264 | zstream->next_in = vs->tight.buffer; | |
265 | zstream->avail_in = vs->tight.offset; | |
266 | zstream->next_out = vs->tight_zlib.buffer + vs->tight_zlib.offset; | |
267 | zstream->avail_out = vs->tight_zlib.capacity - vs->tight_zlib.offset; | |
268 | zstream->data_type = Z_BINARY; | |
269 | previous_out = zstream->total_out; | |
270 | ||
271 | /* start encoding */ | |
272 | if (deflate(zstream, Z_SYNC_FLUSH) != Z_OK) { | |
273 | fprintf(stderr, "VNC: error during tight compression\n"); | |
274 | return -1; | |
275 | } | |
276 | ||
277 | vs->tight_zlib.offset = vs->tight_zlib.capacity - zstream->avail_out; | |
278 | bytes = zstream->total_out - previous_out; | |
279 | ||
280 | tight_send_compact_size(vs, bytes); | |
281 | vnc_write(vs, vs->tight_zlib.buffer, bytes); | |
282 | ||
283 | buffer_reset(&vs->tight_zlib); | |
284 | ||
285 | return bytes; | |
286 | } | |
287 | ||
288 | /* | |
289 | * Subencoding implementations. | |
290 | */ | |
291 | static void tight_pack24(VncState *vs, size_t count) | |
292 | { | |
293 | unsigned char *buf; | |
294 | uint32_t *buf32; | |
295 | uint32_t pix; | |
296 | int rshift, gshift, bshift; | |
297 | ||
298 | buf = vs->tight.buffer; | |
299 | buf32 = (uint32_t *)buf; | |
300 | ||
301 | if ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) == | |
302 | (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG)) { | |
303 | rshift = vs->clientds.pf.rshift; | |
304 | gshift = vs->clientds.pf.gshift; | |
305 | bshift = vs->clientds.pf.bshift; | |
306 | } else { | |
307 | rshift = 24 - vs->clientds.pf.rshift; | |
308 | gshift = 24 - vs->clientds.pf.gshift; | |
309 | bshift = 24 - vs->clientds.pf.bshift; | |
310 | } | |
311 | ||
312 | vs->tight.offset = count * 3; | |
313 | ||
314 | while (count--) { | |
315 | pix = *buf32++; | |
316 | *buf++ = (char)(pix >> rshift); | |
317 | *buf++ = (char)(pix >> gshift); | |
318 | *buf++ = (char)(pix >> bshift); | |
319 | } | |
320 | } | |
321 | ||
322 | static int send_full_color_rect(VncState *vs, int w, int h) | |
323 | { | |
324 | int stream = 0; | |
325 | size_t bytes; | |
326 | ||
327 | vnc_write_u8(vs, stream << 4); /* no flushing, no filter */ | |
328 | ||
329 | if (vs->tight_pixel24) { | |
330 | tight_pack24(vs, w * h); | |
331 | bytes = 3; | |
332 | } else { | |
333 | bytes = vs->clientds.pf.bytes_per_pixel; | |
334 | } | |
335 | ||
336 | bytes = tight_compress_data(vs, stream, w * h * bytes, | |
337 | tight_conf[vs->tight_compression].raw_zlib_level, | |
338 | Z_DEFAULT_STRATEGY); | |
339 | ||
340 | return (bytes >= 0); | |
341 | } | |
342 | ||
b4bea3f2 CC |
343 | static int send_solid_rect(VncState *vs) |
344 | { | |
345 | size_t bytes; | |
346 | ||
347 | vnc_write_u8(vs, VNC_TIGHT_FILL << 4); /* no flushing, no filter */ | |
348 | ||
349 | if (vs->tight_pixel24) { | |
350 | tight_pack24(vs, 1); | |
351 | bytes = 3; | |
352 | } else { | |
353 | bytes = vs->clientds.pf.bytes_per_pixel; | |
354 | } | |
355 | ||
356 | vnc_write(vs, vs->tight.buffer, bytes); | |
357 | return 1; | |
358 | } | |
359 | ||
380282b0 CC |
360 | static void vnc_tight_start(VncState *vs) |
361 | { | |
362 | buffer_reset(&vs->tight); | |
363 | ||
364 | // make the output buffer be the zlib buffer, so we can compress it later | |
365 | vs->tight_tmp = vs->output; | |
366 | vs->output = vs->tight; | |
367 | } | |
368 | ||
369 | static void vnc_tight_stop(VncState *vs) | |
370 | { | |
371 | // switch back to normal output/zlib buffers | |
372 | vs->tight = vs->output; | |
373 | vs->output = vs->tight_tmp; | |
374 | } | |
375 | ||
376 | static int send_sub_rect(VncState *vs, int x, int y, int w, int h) | |
377 | { | |
378 | vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_TIGHT); | |
379 | ||
380 | /* | |
381 | * Convert pixels and store them in vs->tight | |
382 | * We will probably rework that later, probably | |
383 | * when adding other sub-encodings | |
384 | */ | |
385 | vnc_tight_start(vs); | |
386 | vnc_raw_send_framebuffer_update(vs, x, y, w, h); | |
387 | vnc_tight_stop(vs); | |
388 | ||
389 | return send_full_color_rect(vs, w, h); | |
390 | } | |
391 | ||
b4bea3f2 CC |
392 | static int send_sub_rect_solid(VncState *vs, int x, int y, int w, int h) |
393 | { | |
394 | vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_TIGHT); | |
395 | ||
396 | vnc_tight_start(vs); | |
397 | vnc_raw_send_framebuffer_update(vs, x, y, w, h); | |
398 | vnc_tight_stop(vs); | |
399 | ||
400 | return send_solid_rect(vs); | |
401 | } | |
402 | ||
380282b0 CC |
403 | static int send_rect_simple(VncState *vs, int x, int y, int w, int h) |
404 | { | |
405 | int max_size, max_width; | |
406 | int max_sub_width, max_sub_height; | |
407 | int dx, dy; | |
408 | int rw, rh; | |
409 | int n = 0; | |
410 | ||
411 | max_size = tight_conf[vs->tight_compression].max_rect_size; | |
412 | max_width = tight_conf[vs->tight_compression].max_rect_width; | |
413 | ||
414 | if (w > max_width || w * h > max_size) { | |
415 | max_sub_width = (w > max_width) ? max_width : w; | |
416 | max_sub_height = max_size / max_sub_width; | |
417 | ||
418 | for (dy = 0; dy < h; dy += max_sub_height) { | |
419 | for (dx = 0; dx < w; dx += max_width) { | |
420 | rw = MIN(max_sub_width, w - dx); | |
421 | rh = MIN(max_sub_height, h - dy); | |
422 | n += send_sub_rect(vs, x+dx, y+dy, rw, rh); | |
423 | } | |
424 | } | |
425 | } else { | |
426 | n += send_sub_rect(vs, x, y, w, h); | |
427 | } | |
428 | ||
429 | return n; | |
430 | } | |
431 | ||
b4bea3f2 CC |
432 | static int find_large_solid_color_rect(VncState *vs, int x, int y, |
433 | int w, int h, int max_rows) | |
434 | { | |
435 | int dx, dy, dw, dh; | |
436 | int n = 0; | |
437 | ||
438 | /* Try to find large solid-color areas and send them separately. */ | |
439 | ||
440 | for (dy = y; dy < y + h; dy += VNC_TIGHT_MAX_SPLIT_TILE_SIZE) { | |
441 | ||
442 | /* If a rectangle becomes too large, send its upper part now. */ | |
443 | ||
444 | if (dy - y >= max_rows) { | |
445 | n += send_rect_simple(vs, x, y, w, max_rows); | |
446 | y += max_rows; | |
447 | h -= max_rows; | |
448 | } | |
449 | ||
450 | dh = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, (y + h - dy)); | |
451 | ||
452 | for (dx = x; dx < x + w; dx += VNC_TIGHT_MAX_SPLIT_TILE_SIZE) { | |
453 | uint32_t color_value; | |
454 | int x_best, y_best, w_best, h_best; | |
455 | ||
456 | dw = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, (x + w - dx)); | |
457 | ||
458 | if (!check_solid_tile(vs, dx, dy, dw, dh, &color_value, false)) { | |
459 | continue ; | |
460 | } | |
461 | ||
462 | /* Get dimensions of solid-color area. */ | |
463 | ||
464 | find_best_solid_area(vs, dx, dy, w - (dx - x), h - (dy - y), | |
465 | color_value, &w_best, &h_best); | |
466 | ||
467 | /* Make sure a solid rectangle is large enough | |
468 | (or the whole rectangle is of the same color). */ | |
469 | ||
470 | if (w_best * h_best != w * h && | |
471 | w_best * h_best < VNC_TIGHT_MIN_SOLID_SUBRECT_SIZE) { | |
472 | continue; | |
473 | } | |
474 | ||
475 | /* Try to extend solid rectangle to maximum size. */ | |
476 | ||
477 | x_best = dx; y_best = dy; | |
478 | extend_solid_area(vs, x, y, w, h, color_value, | |
479 | &x_best, &y_best, &w_best, &h_best); | |
480 | ||
481 | /* Send rectangles at top and left to solid-color area. */ | |
482 | ||
483 | if (y_best != y) { | |
484 | n += send_rect_simple(vs, x, y, w, y_best-y); | |
485 | } | |
486 | if (x_best != x) { | |
487 | n += vnc_tight_send_framebuffer_update(vs, x, y_best, | |
488 | x_best-x, h_best); | |
489 | } | |
490 | ||
491 | /* Send solid-color rectangle. */ | |
492 | n += send_sub_rect_solid(vs, x_best, y_best, w_best, h_best); | |
493 | ||
494 | /* Send remaining rectangles (at right and bottom). */ | |
495 | ||
496 | if (x_best + w_best != x + w) { | |
497 | n += vnc_tight_send_framebuffer_update(vs, x_best+w_best, | |
498 | y_best, | |
499 | w-(x_best-x)-w_best, | |
500 | h_best); | |
501 | } | |
502 | if (y_best + h_best != y + h) { | |
503 | n += vnc_tight_send_framebuffer_update(vs, x, y_best+h_best, | |
504 | w, h-(y_best-y)-h_best); | |
505 | } | |
506 | ||
507 | /* Return after all recursive calls are done. */ | |
508 | return n; | |
509 | } | |
510 | } | |
511 | return n + send_rect_simple(vs, x, y, w, h); | |
512 | } | |
513 | ||
380282b0 CC |
514 | int vnc_tight_send_framebuffer_update(VncState *vs, int x, int y, |
515 | int w, int h) | |
516 | { | |
b4bea3f2 CC |
517 | int max_rows; |
518 | ||
380282b0 CC |
519 | if (vs->clientds.pf.bytes_per_pixel == 4 && vs->clientds.pf.rmax == 0xFF && |
520 | vs->clientds.pf.bmax == 0xFF && vs->clientds.pf.gmax == 0xFF) { | |
521 | vs->tight_pixel24 = true; | |
522 | } else { | |
523 | vs->tight_pixel24 = false; | |
524 | } | |
525 | ||
b4bea3f2 CC |
526 | if (w * h < VNC_TIGHT_MIN_SPLIT_RECT_SIZE) |
527 | return send_rect_simple(vs, x, y, w, h); | |
528 | ||
529 | /* Calculate maximum number of rows in one non-solid rectangle. */ | |
530 | ||
531 | max_rows = tight_conf[vs->tight_compression].max_rect_size; | |
532 | max_rows /= MIN(tight_conf[vs->tight_compression].max_rect_width, w); | |
533 | ||
534 | return find_large_solid_color_rect(vs, x, y, w, h, max_rows); | |
380282b0 CC |
535 | } |
536 | ||
537 | void vnc_tight_clear(VncState *vs) | |
538 | { | |
539 | int i; | |
540 | for (i=0; i<ARRAY_SIZE(vs->tight_stream); i++) { | |
541 | if (vs->tight_stream[i].opaque) { | |
542 | deflateEnd(&vs->tight_stream[i]); | |
543 | } | |
544 | } | |
545 | ||
546 | buffer_free(&vs->tight); | |
547 | buffer_free(&vs->tight_zlib); | |
548 | } |