1 /*
2 * tight.c
3 *
4 * Routines to implement Tight Encoding
5 *
6 * Our Tight encoder is based roughly on the TurboVNC v0.6 encoder with some
7 * additional enhancements from TurboVNC 1.1. For lower compression levels,
8 * this encoder provides a tremendous reduction in CPU usage (and subsequently,
9 * an increase in throughput for CPU-limited environments) relative to the
10 * TightVNC encoder, whereas Compression Level 9 provides a low-bandwidth mode
11 * that behaves similarly to Compression Levels 5-9 in the old TightVNC
12 * encoder.
13 */
14
15 /*
16 * Copyright (C) 2010-2012 D. R. Commander. All Rights Reserved.
17 * Copyright (C) 2005-2008 Sun Microsystems, Inc. All Rights Reserved.
18 * Copyright (C) 2004 Landmark Graphics Corporation. All Rights Reserved.
19 * Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved.
20 * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
21 *
22 * This is free software; you can redistribute it and/or modify
23 * it under the terms of the GNU General Public License as published by
24 * the Free Software Foundation; either version 2 of the License, or
25 * (at your option) any later version.
26 *
27 * This software is distributed in the hope that it will be useful,
28 * but WITHOUT ANY WARRANTY; without even the implied warranty of
29 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 * GNU General Public License for more details.
31 *
32 * You should have received a copy of the GNU General Public License
33 * along with this software; if not, write to the Free Software
34 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
35 * USA.
36 */
37
38 #include <rfb/rfb.h>
39 #include "private.h"
40
41 #ifdef LIBVNCSERVER_HAVE_LIBPNG
42 #include <png.h>
43 #endif
44 #include "turbojpeg.h"
45
46
47 /* Note: The following constant should not be changed. */
48 #define TIGHT_MIN_TO_COMPRESS 12
49
50 /* The parameters below may be adjusted. */
51 #define MIN_SPLIT_RECT_SIZE 4096
52 #define MIN_SOLID_SUBRECT_SIZE 2048
53 #define MAX_SPLIT_TILE_SIZE 16
54
55 /*
56 * There is so much access of the Tight encoding static data buffers
57 * that we resort to using thread local storage instead of having
58 * per-client data.
59 */
60 #if LIBVNCSERVER_HAVE_LIBPTHREAD && LIBVNCSERVER_HAVE_TLS && !defined(TLS) && defined(__linux__)
61 #define TLS __thread
62 #endif
63 #ifndef TLS
64 #define TLS
65 #endif
66
67 /* This variable is set on every rfbSendRectEncodingTight() call. */
68 static TLS rfbBool usePixelFormat24 = FALSE;
69
70
71 /* Compression level stuff. The following array contains various
72 encoder parameters for each of 10 compression levels (0..9).
73 Last three parameters correspond to JPEG quality levels (0..9). */
74
75 typedef struct TIGHT_CONF_s {
76 int maxRectSize, maxRectWidth;
77 int monoMinRectSize;
78 int idxZlibLevel, monoZlibLevel, rawZlibLevel;
79 int idxMaxColorsDivisor;
80 int palMaxColorsWithJPEG;
81 } TIGHT_CONF;
82
83 static TIGHT_CONF tightConf[4] = {
84 { 65536, 2048, 6, 0, 0, 0, 4, 24 }, /* 0 (used only without JPEG) */
85 { 65536, 2048, 32, 1, 1, 1, 96, 24 }, /* 1 */
86 { 65536, 2048, 32, 3, 3, 2, 96, 96 }, /* 2 (used only with JPEG) */
87 { 65536, 2048, 32, 7, 7, 5, 96, 256 } /* 9 */
88 };
89
90 #ifdef LIBVNCSERVER_HAVE_LIBPNG
91 typedef struct TIGHT_PNG_CONF_s {
92 int png_zlib_level, png_filters;
93 } TIGHT_PNG_CONF;
94
95 static TIGHT_PNG_CONF tightPngConf[10] = {
96 { 0, PNG_NO_FILTERS },
97 { 1, PNG_NO_FILTERS },
98 { 2, PNG_NO_FILTERS },
99 { 3, PNG_NO_FILTERS },
100 { 4, PNG_NO_FILTERS },
101 { 5, PNG_ALL_FILTERS },
102 { 6, PNG_ALL_FILTERS },
103 { 7, PNG_ALL_FILTERS },
104 { 8, PNG_ALL_FILTERS },
105 { 9, PNG_ALL_FILTERS },
106 };
107 #endif
108
109 static TLS int compressLevel = 1;
110 static TLS int qualityLevel = 95;
111 static TLS int subsampLevel = TJ_444;
112
113 static const int subsampLevel2tjsubsamp[4] = {
114 TJ_444, TJ_420, TJ_422, TJ_GRAYSCALE
115 };
116
117
118 /* Stuff dealing with palettes. */
119
120 typedef struct COLOR_LIST_s {
121 struct COLOR_LIST_s *next;
122 int idx;
123 uint32_t rgb;
124 } COLOR_LIST;
125
126 typedef struct PALETTE_ENTRY_s {
127 COLOR_LIST *listNode;
128 int numPixels;
129 } PALETTE_ENTRY;
130
131 typedef struct PALETTE_s {
132 PALETTE_ENTRY entry[256];
133 COLOR_LIST *hash[256];
134 COLOR_LIST list[256];
135 } PALETTE;
136
137 /* TODO: move into rfbScreen struct */
138 static TLS int paletteNumColors = 0;
139 static TLS int paletteMaxColors = 0;
140 static TLS uint32_t monoBackground = 0;
141 static TLS uint32_t monoForeground = 0;
142 static TLS PALETTE palette;
143
144 /* Pointers to dynamically-allocated buffers. */
145
146 static TLS int tightBeforeBufSize = 0;
147 static TLS char *tightBeforeBuf = NULL;
148
149 static TLS int tightAfterBufSize = 0;
150 static TLS char *tightAfterBuf = NULL;
151
152 static TLS tjhandle j = NULL;
153
rfbTightCleanup(rfbScreenInfoPtr screen)154 void rfbTightCleanup (rfbScreenInfoPtr screen)
155 {
156 if (tightBeforeBufSize) {
157 free (tightBeforeBuf);
158 tightBeforeBufSize = 0;
159 tightBeforeBuf = NULL;
160 }
161 if (tightAfterBufSize) {
162 free (tightAfterBuf);
163 tightAfterBufSize = 0;
164 tightAfterBuf = NULL;
165 }
166 if (j) tjDestroy(j);
167 }
168
169
170 /* Prototypes for static functions. */
171
172 static rfbBool SendRectEncodingTight(rfbClientPtr cl, int x, int y,
173 int w, int h);
174 static void FindBestSolidArea (rfbClientPtr cl, int x, int y, int w, int h,
175 uint32_t colorValue, int *w_ptr, int *h_ptr);
176 static void ExtendSolidArea (rfbClientPtr cl, int x, int y, int w, int h,
177 uint32_t colorValue,
178 int *x_ptr, int *y_ptr, int *w_ptr, int *h_ptr);
179 static rfbBool CheckSolidTile (rfbClientPtr cl, int x, int y, int w, int h,
180 uint32_t *colorPtr, rfbBool needSameColor);
181 static rfbBool CheckSolidTile8 (rfbClientPtr cl, int x, int y, int w, int h,
182 uint32_t *colorPtr, rfbBool needSameColor);
183 static rfbBool CheckSolidTile16 (rfbClientPtr cl, int x, int y, int w, int h,
184 uint32_t *colorPtr, rfbBool needSameColor);
185 static rfbBool CheckSolidTile32 (rfbClientPtr cl, int x, int y, int w, int h,
186 uint32_t *colorPtr, rfbBool needSameColor);
187
188 static rfbBool SendRectSimple (rfbClientPtr cl, int x, int y, int w, int h);
189 static rfbBool SendSubrect (rfbClientPtr cl, int x, int y, int w, int h);
190 static rfbBool SendTightHeader (rfbClientPtr cl, int x, int y, int w, int h);
191
192 static rfbBool SendSolidRect (rfbClientPtr cl);
193 static rfbBool SendMonoRect (rfbClientPtr cl, int x, int y, int w, int h);
194 static rfbBool SendIndexedRect (rfbClientPtr cl, int x, int y, int w, int h);
195 static rfbBool SendFullColorRect (rfbClientPtr cl, int x, int y, int w, int h);
196
197 static rfbBool CompressData (rfbClientPtr cl, int streamId, int dataLen,
198 int zlibLevel, int zlibStrategy);
199 static rfbBool SendCompressedData (rfbClientPtr cl, char *buf,
200 int compressedLen);
201
202 static void FillPalette8 (int count);
203 static void FillPalette16 (int count);
204 static void FillPalette32 (int count);
205 static void FastFillPalette16 (rfbClientPtr cl, uint16_t *data, int w,
206 int pitch, int h);
207 static void FastFillPalette32 (rfbClientPtr cl, uint32_t *data, int w,
208 int pitch, int h);
209
210 static void PaletteReset (void);
211 static int PaletteInsert (uint32_t rgb, int numPixels, int bpp);
212
213 static void Pack24 (rfbClientPtr cl, char *buf, rfbPixelFormat *fmt,
214 int count);
215
216 static void EncodeIndexedRect16 (uint8_t *buf, int count);
217 static void EncodeIndexedRect32 (uint8_t *buf, int count);
218
219 static void EncodeMonoRect8 (uint8_t *buf, int w, int h);
220 static void EncodeMonoRect16 (uint8_t *buf, int w, int h);
221 static void EncodeMonoRect32 (uint8_t *buf, int w, int h);
222
223 static rfbBool SendJpegRect (rfbClientPtr cl, int x, int y, int w, int h,
224 int quality);
225 static void PrepareRowForImg(rfbClientPtr cl, uint8_t *dst, int x, int y, int count);
226 static void PrepareRowForImg24(rfbClientPtr cl, uint8_t *dst, int x, int y, int count);
227 static void PrepareRowForImg16(rfbClientPtr cl, uint8_t *dst, int x, int y, int count);
228 static void PrepareRowForImg32(rfbClientPtr cl, uint8_t *dst, int x, int y, int count);
229
230 #ifdef LIBVNCSERVER_HAVE_LIBPNG
231 static rfbBool SendPngRect(rfbClientPtr cl, int x, int y, int w, int h);
232 static rfbBool CanSendPngRect(rfbClientPtr cl, int w, int h);
233 #endif
234
235 /*
236 * Tight encoding implementation.
237 */
238
239 int
rfbNumCodedRectsTight(rfbClientPtr cl,int x,int y,int w,int h)240 rfbNumCodedRectsTight(rfbClientPtr cl,
241 int x,
242 int y,
243 int w,
244 int h)
245 {
246 int maxRectSize, maxRectWidth;
247 int subrectMaxWidth, subrectMaxHeight;
248
249 /* No matter how many rectangles we will send if LastRect markers
250 are used to terminate rectangle stream. */
251 if (cl->enableLastRectEncoding && w * h >= MIN_SPLIT_RECT_SIZE)
252 return 0;
253
254 maxRectSize = tightConf[compressLevel].maxRectSize;
255 maxRectWidth = tightConf[compressLevel].maxRectWidth;
256
257 if (w > maxRectWidth || w * h > maxRectSize) {
258 subrectMaxWidth = (w > maxRectWidth) ? maxRectWidth : w;
259 subrectMaxHeight = maxRectSize / subrectMaxWidth;
260 return (((w - 1) / maxRectWidth + 1) *
261 ((h - 1) / subrectMaxHeight + 1));
262 } else {
263 return 1;
264 }
265 }
266
267 rfbBool
rfbSendRectEncodingTight(rfbClientPtr cl,int x,int y,int w,int h)268 rfbSendRectEncodingTight(rfbClientPtr cl,
269 int x,
270 int y,
271 int w,
272 int h)
273 {
274 cl->tightEncoding = rfbEncodingTight;
275 return SendRectEncodingTight(cl, x, y, w, h);
276 }
277
278 rfbBool
rfbSendRectEncodingTightPng(rfbClientPtr cl,int x,int y,int w,int h)279 rfbSendRectEncodingTightPng(rfbClientPtr cl,
280 int x,
281 int y,
282 int w,
283 int h)
284 {
285 cl->tightEncoding = rfbEncodingTightPng;
286 return SendRectEncodingTight(cl, x, y, w, h);
287 }
288
289
290 rfbBool
SendRectEncodingTight(rfbClientPtr cl,int x,int y,int w,int h)291 SendRectEncodingTight(rfbClientPtr cl,
292 int x,
293 int y,
294 int w,
295 int h)
296 {
297 int nMaxRows;
298 uint32_t colorValue;
299 int dx, dy, dw, dh;
300 int x_best, y_best, w_best, h_best;
301 char *fbptr;
302
303 rfbSendUpdateBuf(cl);
304
305 compressLevel = cl->tightCompressLevel;
306 qualityLevel = cl->turboQualityLevel;
307 subsampLevel = cl->turboSubsampLevel;
308
309 /* We only allow compression levels that have a demonstrable performance
310 benefit. CL 0 with JPEG reduces CPU usage for workloads that have low
311 numbers of unique colors, but the same thing can be accomplished by
312 using CL 0 without JPEG (AKA "Lossless Tight.") For those same
313 low-color workloads, CL 2 can provide typically 20-40% better
314 compression than CL 1 (with a commensurate increase in CPU usage.) For
315 high-color workloads, CL 1 should always be used, as higher compression
316 levels increase CPU usage for these workloads without providing any
317 significant reduction in bandwidth. */
318 if (qualityLevel != -1) {
319 if (compressLevel < 1) compressLevel = 1;
320 if (compressLevel > 2) compressLevel = 2;
321 }
322
323 /* With JPEG disabled, CL 2 offers no significant bandwidth savings over
324 CL 1, so we don't include it. */
325 else if (compressLevel > 1) compressLevel = 1;
326
327 /* CL 9 (which maps internally to CL 3) is included mainly for backward
328 compatibility with TightVNC Compression Levels 5-9. It should be used
329 only in extremely low-bandwidth cases in which it can be shown to have a
330 benefit. For low-color workloads, it provides typically only 10-20%
331 better compression than CL 2 with JPEG and CL 1 without JPEG, and it
332 uses, on average, twice as much CPU time. */
333 if (cl->tightCompressLevel == 9) compressLevel = 3;
334
335 if ( cl->format.depth == 24 && cl->format.redMax == 0xFF &&
336 cl->format.greenMax == 0xFF && cl->format.blueMax == 0xFF ) {
337 usePixelFormat24 = TRUE;
338 } else {
339 usePixelFormat24 = FALSE;
340 }
341
342 if (!cl->enableLastRectEncoding || w * h < MIN_SPLIT_RECT_SIZE)
343 return SendRectSimple(cl, x, y, w, h);
344
345 /* Make sure we can write at least one pixel into tightBeforeBuf. */
346
347 if (tightBeforeBufSize < 4) {
348 tightBeforeBufSize = 4;
349 if (tightBeforeBuf == NULL)
350 tightBeforeBuf = (char *)malloc(tightBeforeBufSize);
351 else
352 tightBeforeBuf = (char *)realloc(tightBeforeBuf,
353 tightBeforeBufSize);
354 }
355
356 /* Calculate maximum number of rows in one non-solid rectangle. */
357
358 {
359 int maxRectSize, maxRectWidth, nMaxWidth;
360
361 maxRectSize = tightConf[compressLevel].maxRectSize;
362 maxRectWidth = tightConf[compressLevel].maxRectWidth;
363 nMaxWidth = (w > maxRectWidth) ? maxRectWidth : w;
364 nMaxRows = maxRectSize / nMaxWidth;
365 }
366
367 /* Try to find large solid-color areas and send them separately. */
368
369 for (dy = y; dy < y + h; dy += MAX_SPLIT_TILE_SIZE) {
370
371 /* If a rectangle becomes too large, send its upper part now. */
372
373 if (dy - y >= nMaxRows) {
374 if (!SendRectSimple(cl, x, y, w, nMaxRows))
375 return 0;
376 y += nMaxRows;
377 h -= nMaxRows;
378 }
379
380 dh = (dy + MAX_SPLIT_TILE_SIZE <= y + h) ?
381 MAX_SPLIT_TILE_SIZE : (y + h - dy);
382
383 for (dx = x; dx < x + w; dx += MAX_SPLIT_TILE_SIZE) {
384
385 dw = (dx + MAX_SPLIT_TILE_SIZE <= x + w) ?
386 MAX_SPLIT_TILE_SIZE : (x + w - dx);
387
388 if (CheckSolidTile(cl, dx, dy, dw, dh, &colorValue, FALSE)) {
389
390 if (subsampLevel == TJ_GRAYSCALE && qualityLevel != -1) {
391 uint32_t r = (colorValue >> 16) & 0xFF;
392 uint32_t g = (colorValue >> 8) & 0xFF;
393 uint32_t b = (colorValue) & 0xFF;
394 double y = (0.257 * (double)r) + (0.504 * (double)g)
395 + (0.098 * (double)b) + 16.;
396 colorValue = (int)y + (((int)y) << 8) + (((int)y) << 16);
397 }
398
399 /* Get dimensions of solid-color area. */
400
401 FindBestSolidArea(cl, dx, dy, w - (dx - x), h - (dy - y),
402 colorValue, &w_best, &h_best);
403
404 /* Make sure a solid rectangle is large enough
405 (or the whole rectangle is of the same color). */
406
407 if ( w_best * h_best != w * h &&
408 w_best * h_best < MIN_SOLID_SUBRECT_SIZE )
409 continue;
410
411 /* Try to extend solid rectangle to maximum size. */
412
413 x_best = dx; y_best = dy;
414 ExtendSolidArea(cl, x, y, w, h, colorValue,
415 &x_best, &y_best, &w_best, &h_best);
416
417 /* Send rectangles at top and left to solid-color area. */
418
419 if ( y_best != y &&
420 !SendRectSimple(cl, x, y, w, y_best-y) )
421 return FALSE;
422 if ( x_best != x &&
423 !SendRectEncodingTight(cl, x, y_best,
424 x_best-x, h_best) )
425 return FALSE;
426
427 /* Send solid-color rectangle. */
428
429 if (!SendTightHeader(cl, x_best, y_best, w_best, h_best))
430 return FALSE;
431
432 fbptr = (cl->scaledScreen->frameBuffer +
433 (cl->scaledScreen->paddedWidthInBytes * y_best) +
434 (x_best * (cl->scaledScreen->bitsPerPixel / 8)));
435
436 (*cl->translateFn)(cl->translateLookupTable, &cl->screen->serverFormat,
437 &cl->format, fbptr, tightBeforeBuf,
438 cl->scaledScreen->paddedWidthInBytes, 1, 1);
439
440 if (!SendSolidRect(cl))
441 return FALSE;
442
443 /* Send remaining rectangles (at right and bottom). */
444
445 if ( x_best + w_best != x + w &&
446 !SendRectEncodingTight(cl, x_best + w_best, y_best,
447 w - (x_best-x) - w_best, h_best) )
448 return FALSE;
449 if ( y_best + h_best != y + h &&
450 !SendRectEncodingTight(cl, x, y_best + h_best,
451 w, h - (y_best-y) - h_best) )
452 return FALSE;
453
454 /* Return after all recursive calls are done. */
455
456 return TRUE;
457 }
458
459 }
460
461 }
462
463 /* No suitable solid-color rectangles found. */
464
465 return SendRectSimple(cl, x, y, w, h);
466 }
467
468
469 static void
FindBestSolidArea(rfbClientPtr cl,int x,int y,int w,int h,uint32_t colorValue,int * w_ptr,int * h_ptr)470 FindBestSolidArea(rfbClientPtr cl,
471 int x,
472 int y,
473 int w,
474 int h,
475 uint32_t colorValue,
476 int *w_ptr,
477 int *h_ptr)
478 {
479 int dx, dy, dw, dh;
480 int w_prev;
481 int w_best = 0, h_best = 0;
482
483 w_prev = w;
484
485 for (dy = y; dy < y + h; dy += MAX_SPLIT_TILE_SIZE) {
486
487 dh = (dy + MAX_SPLIT_TILE_SIZE <= y + h) ?
488 MAX_SPLIT_TILE_SIZE : (y + h - dy);
489 dw = (w_prev > MAX_SPLIT_TILE_SIZE) ?
490 MAX_SPLIT_TILE_SIZE : w_prev;
491
492 if (!CheckSolidTile(cl, x, dy, dw, dh, &colorValue, TRUE))
493 break;
494
495 for (dx = x + dw; dx < x + w_prev;) {
496 dw = (dx + MAX_SPLIT_TILE_SIZE <= x + w_prev) ?
497 MAX_SPLIT_TILE_SIZE : (x + w_prev - dx);
498 if (!CheckSolidTile(cl, dx, dy, dw, dh, &colorValue, TRUE))
499 break;
500 dx += dw;
501 }
502
503 w_prev = dx - x;
504 if (w_prev * (dy + dh - y) > w_best * h_best) {
505 w_best = w_prev;
506 h_best = dy + dh - y;
507 }
508 }
509
510 *w_ptr = w_best;
511 *h_ptr = h_best;
512 }
513
514
515 static void
ExtendSolidArea(rfbClientPtr cl,int x,int y,int w,int h,uint32_t colorValue,int * x_ptr,int * y_ptr,int * w_ptr,int * h_ptr)516 ExtendSolidArea(rfbClientPtr cl,
517 int x,
518 int y,
519 int w,
520 int h,
521 uint32_t colorValue,
522 int *x_ptr,
523 int *y_ptr,
524 int *w_ptr,
525 int *h_ptr)
526 {
527 int cx, cy;
528
529 /* Try to extend the area upwards. */
530 for ( cy = *y_ptr - 1;
531 cy >= y && CheckSolidTile(cl, *x_ptr, cy, *w_ptr, 1, &colorValue, TRUE);
532 cy-- );
533 *h_ptr += *y_ptr - (cy + 1);
534 *y_ptr = cy + 1;
535
536 /* ... downwards. */
537 for ( cy = *y_ptr + *h_ptr;
538 cy < y + h &&
539 CheckSolidTile(cl, *x_ptr, cy, *w_ptr, 1, &colorValue, TRUE);
540 cy++ );
541 *h_ptr += cy - (*y_ptr + *h_ptr);
542
543 /* ... to the left. */
544 for ( cx = *x_ptr - 1;
545 cx >= x && CheckSolidTile(cl, cx, *y_ptr, 1, *h_ptr, &colorValue, TRUE);
546 cx-- );
547 *w_ptr += *x_ptr - (cx + 1);
548 *x_ptr = cx + 1;
549
550 /* ... to the right. */
551 for ( cx = *x_ptr + *w_ptr;
552 cx < x + w &&
553 CheckSolidTile(cl, cx, *y_ptr, 1, *h_ptr, &colorValue, TRUE);
554 cx++ );
555 *w_ptr += cx - (*x_ptr + *w_ptr);
556 }
557
558
559 /*
560 * Check if a rectangle is all of the same color. If needSameColor is
561 * set to non-zero, then also check that its color equals to the
562 * *colorPtr value. The result is 1 if the test is successfull, and in
563 * that case new color will be stored in *colorPtr.
564 */
565
CheckSolidTile(rfbClientPtr cl,int x,int y,int w,int h,uint32_t * colorPtr,rfbBool needSameColor)566 static rfbBool CheckSolidTile(rfbClientPtr cl, int x, int y, int w, int h, uint32_t* colorPtr, rfbBool needSameColor)
567 {
568 switch(cl->screen->serverFormat.bitsPerPixel) {
569 case 32:
570 return CheckSolidTile32(cl, x, y, w, h, colorPtr, needSameColor);
571 case 16:
572 return CheckSolidTile16(cl, x, y, w, h, colorPtr, needSameColor);
573 default:
574 return CheckSolidTile8(cl, x, y, w, h, colorPtr, needSameColor);
575 }
576 }
577
578
579 #define DEFINE_CHECK_SOLID_FUNCTION(bpp) \
580 \
581 static rfbBool \
582 CheckSolidTile##bpp(rfbClientPtr cl, int x, int y, int w, int h, \
583 uint32_t* colorPtr, rfbBool needSameColor) \
584 { \
585 uint##bpp##_t *fbptr; \
586 uint##bpp##_t colorValue; \
587 int dx, dy; \
588 \
589 fbptr = (uint##bpp##_t *)&cl->scaledScreen->frameBuffer \
590 [y * cl->scaledScreen->paddedWidthInBytes + x * (bpp/8)]; \
591 \
592 colorValue = *fbptr; \
593 if (needSameColor && (uint32_t)colorValue != *colorPtr) \
594 return FALSE; \
595 \
596 for (dy = 0; dy < h; dy++) { \
597 for (dx = 0; dx < w; dx++) { \
598 if (colorValue != fbptr[dx]) \
599 return FALSE; \
600 } \
601 fbptr = (uint##bpp##_t *)((uint8_t *)fbptr \
602 + cl->scaledScreen->paddedWidthInBytes); \
603 } \
604 \
605 *colorPtr = (uint32_t)colorValue; \
606 return TRUE; \
607 }
608
609 DEFINE_CHECK_SOLID_FUNCTION(8)
610 DEFINE_CHECK_SOLID_FUNCTION(16)
611 DEFINE_CHECK_SOLID_FUNCTION(32)
612
613 static rfbBool
SendRectSimple(rfbClientPtr cl,int x,int y,int w,int h)614 SendRectSimple(rfbClientPtr cl, int x, int y, int w, int h)
615 {
616 int maxBeforeSize, maxAfterSize;
617 int maxRectSize, maxRectWidth;
618 int subrectMaxWidth, subrectMaxHeight;
619 int dx, dy;
620 int rw, rh;
621
622 maxRectSize = tightConf[compressLevel].maxRectSize;
623 maxRectWidth = tightConf[compressLevel].maxRectWidth;
624
625 maxBeforeSize = maxRectSize * (cl->format.bitsPerPixel / 8);
626 maxAfterSize = maxBeforeSize + (maxBeforeSize + 99) / 100 + 12;
627
628 if (tightBeforeBufSize < maxBeforeSize) {
629 tightBeforeBufSize = maxBeforeSize;
630 if (tightBeforeBuf == NULL)
631 tightBeforeBuf = (char *)malloc(tightBeforeBufSize);
632 else
633 tightBeforeBuf = (char *)realloc(tightBeforeBuf,
634 tightBeforeBufSize);
635 }
636
637 if (tightAfterBufSize < maxAfterSize) {
638 tightAfterBufSize = maxAfterSize;
639 if (tightAfterBuf == NULL)
640 tightAfterBuf = (char *)malloc(tightAfterBufSize);
641 else
642 tightAfterBuf = (char *)realloc(tightAfterBuf,
643 tightAfterBufSize);
644 }
645
646 if (w > maxRectWidth || w * h > maxRectSize) {
647 subrectMaxWidth = (w > maxRectWidth) ? maxRectWidth : w;
648 subrectMaxHeight = maxRectSize / subrectMaxWidth;
649
650 for (dy = 0; dy < h; dy += subrectMaxHeight) {
651 for (dx = 0; dx < w; dx += maxRectWidth) {
652 rw = (dx + maxRectWidth < w) ? maxRectWidth : w - dx;
653 rh = (dy + subrectMaxHeight < h) ? subrectMaxHeight : h - dy;
654 if (!SendSubrect(cl, x + dx, y + dy, rw, rh))
655 return FALSE;
656 }
657 }
658 } else {
659 if (!SendSubrect(cl, x, y, w, h))
660 return FALSE;
661 }
662
663 return TRUE;
664 }
665
666 static rfbBool
SendSubrect(rfbClientPtr cl,int x,int y,int w,int h)667 SendSubrect(rfbClientPtr cl,
668 int x,
669 int y,
670 int w,
671 int h)
672 {
673 char *fbptr;
674 rfbBool success = FALSE;
675
676 /* Send pending data if there is more than 128 bytes. */
677 if (cl->ublen > 128) {
678 if (!rfbSendUpdateBuf(cl))
679 return FALSE;
680 }
681
682 if (!SendTightHeader(cl, x, y, w, h))
683 return FALSE;
684
685 fbptr = (cl->scaledScreen->frameBuffer
686 + (cl->scaledScreen->paddedWidthInBytes * y)
687 + (x * (cl->scaledScreen->bitsPerPixel / 8)));
688
689 if (subsampLevel == TJ_GRAYSCALE && qualityLevel != -1)
690 return SendJpegRect(cl, x, y, w, h, qualityLevel);
691
692 paletteMaxColors = w * h / tightConf[compressLevel].idxMaxColorsDivisor;
693 if(qualityLevel != -1)
694 paletteMaxColors = tightConf[compressLevel].palMaxColorsWithJPEG;
695 if ( paletteMaxColors < 2 &&
696 w * h >= tightConf[compressLevel].monoMinRectSize ) {
697 paletteMaxColors = 2;
698 }
699
700 if (cl->format.bitsPerPixel == cl->screen->serverFormat.bitsPerPixel &&
701 cl->format.redMax == cl->screen->serverFormat.redMax &&
702 cl->format.greenMax == cl->screen->serverFormat.greenMax &&
703 cl->format.blueMax == cl->screen->serverFormat.blueMax &&
704 cl->format.bitsPerPixel >= 16) {
705
706 /* This is so we can avoid translating the pixels when compressing
707 with JPEG, since it is unnecessary */
708 switch (cl->format.bitsPerPixel) {
709 case 16:
710 FastFillPalette16(cl, (uint16_t *)fbptr, w,
711 cl->scaledScreen->paddedWidthInBytes / 2, h);
712 break;
713 default:
714 FastFillPalette32(cl, (uint32_t *)fbptr, w,
715 cl->scaledScreen->paddedWidthInBytes / 4, h);
716 }
717
718 if(paletteNumColors != 0 || qualityLevel == -1) {
719 (*cl->translateFn)(cl->translateLookupTable,
720 &cl->screen->serverFormat, &cl->format, fbptr,
721 tightBeforeBuf,
722 cl->scaledScreen->paddedWidthInBytes, w, h);
723 }
724 }
725 else {
726 (*cl->translateFn)(cl->translateLookupTable, &cl->screen->serverFormat,
727 &cl->format, fbptr, tightBeforeBuf,
728 cl->scaledScreen->paddedWidthInBytes, w, h);
729
730 switch (cl->format.bitsPerPixel) {
731 case 8:
732 FillPalette8(w * h);
733 break;
734 case 16:
735 FillPalette16(w * h);
736 break;
737 default:
738 FillPalette32(w * h);
739 }
740 }
741
742 switch (paletteNumColors) {
743 case 0:
744 /* Truecolor image */
745 if (qualityLevel != -1) {
746 success = SendJpegRect(cl, x, y, w, h, qualityLevel);
747 } else {
748 success = SendFullColorRect(cl, x, y, w, h);
749 }
750 break;
751 case 1:
752 /* Solid rectangle */
753 success = SendSolidRect(cl);
754 break;
755 case 2:
756 /* Two-color rectangle */
757 success = SendMonoRect(cl, x, y, w, h);
758 break;
759 default:
760 /* Up to 256 different colors */
761 success = SendIndexedRect(cl, x, y, w, h);
762 }
763 return success;
764 }
765
766 static rfbBool
SendTightHeader(rfbClientPtr cl,int x,int y,int w,int h)767 SendTightHeader(rfbClientPtr cl,
768 int x,
769 int y,
770 int w,
771 int h)
772 {
773 rfbFramebufferUpdateRectHeader rect;
774
775 if (cl->ublen + sz_rfbFramebufferUpdateRectHeader > UPDATE_BUF_SIZE) {
776 if (!rfbSendUpdateBuf(cl))
777 return FALSE;
778 }
779
780 rect.r.x = Swap16IfLE(x);
781 rect.r.y = Swap16IfLE(y);
782 rect.r.w = Swap16IfLE(w);
783 rect.r.h = Swap16IfLE(h);
784 rect.encoding = Swap32IfLE(cl->tightEncoding);
785
786 memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,
787 sz_rfbFramebufferUpdateRectHeader);
788 cl->ublen += sz_rfbFramebufferUpdateRectHeader;
789
790 rfbStatRecordEncodingSent(cl, cl->tightEncoding,
791 sz_rfbFramebufferUpdateRectHeader,
792 sz_rfbFramebufferUpdateRectHeader
793 + w * (cl->format.bitsPerPixel / 8) * h);
794
795 return TRUE;
796 }
797
798 /*
799 * Subencoding implementations.
800 */
801
802 static rfbBool
SendSolidRect(rfbClientPtr cl)803 SendSolidRect(rfbClientPtr cl)
804 {
805 int len;
806
807 if (usePixelFormat24) {
808 Pack24(cl, tightBeforeBuf, &cl->format, 1);
809 len = 3;
810 } else
811 len = cl->format.bitsPerPixel / 8;
812
813 if (cl->ublen + 1 + len > UPDATE_BUF_SIZE) {
814 if (!rfbSendUpdateBuf(cl))
815 return FALSE;
816 }
817
818 cl->updateBuf[cl->ublen++] = (char)(rfbTightFill << 4);
819 memcpy (&cl->updateBuf[cl->ublen], tightBeforeBuf, len);
820 cl->ublen += len;
821
822 rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, len + 1);
823
824 return TRUE;
825 }
826
827 static rfbBool
SendMonoRect(rfbClientPtr cl,int x,int y,int w,int h)828 SendMonoRect(rfbClientPtr cl,
829 int x,
830 int y,
831 int w,
832 int h)
833 {
834 int streamId = 1;
835 int paletteLen, dataLen;
836
837 #ifdef LIBVNCSERVER_HAVE_LIBPNG
838 if (CanSendPngRect(cl, w, h)) {
839 /* TODO: setup palette maybe */
840 return SendPngRect(cl, x, y, w, h);
841 /* TODO: destroy palette maybe */
842 }
843 #endif
844
845 if ( cl->ublen + TIGHT_MIN_TO_COMPRESS + 6 +
846 2 * cl->format.bitsPerPixel / 8 > UPDATE_BUF_SIZE ) {
847 if (!rfbSendUpdateBuf(cl))
848 return FALSE;
849 }
850
851 /* Prepare tight encoding header. */
852 dataLen = (w + 7) / 8;
853 dataLen *= h;
854
855 if (tightConf[compressLevel].monoZlibLevel == 0 &&
856 cl->tightEncoding != rfbEncodingTightPng)
857 cl->updateBuf[cl->ublen++] =
858 (char)((rfbTightNoZlib | rfbTightExplicitFilter) << 4);
859 else
860 cl->updateBuf[cl->ublen++] = (streamId | rfbTightExplicitFilter) << 4;
861 cl->updateBuf[cl->ublen++] = rfbTightFilterPalette;
862 cl->updateBuf[cl->ublen++] = 1;
863
864 /* Prepare palette, convert image. */
865 switch (cl->format.bitsPerPixel) {
866
867 case 32:
868 EncodeMonoRect32((uint8_t *)tightBeforeBuf, w, h);
869
870 ((uint32_t *)tightAfterBuf)[0] = monoBackground;
871 ((uint32_t *)tightAfterBuf)[1] = monoForeground;
872 if (usePixelFormat24) {
873 Pack24(cl, tightAfterBuf, &cl->format, 2);
874 paletteLen = 6;
875 } else
876 paletteLen = 8;
877
878 memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf, paletteLen);
879 cl->ublen += paletteLen;
880 rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, 3 + paletteLen);
881 break;
882
883 case 16:
884 EncodeMonoRect16((uint8_t *)tightBeforeBuf, w, h);
885
886 ((uint16_t *)tightAfterBuf)[0] = (uint16_t)monoBackground;
887 ((uint16_t *)tightAfterBuf)[1] = (uint16_t)monoForeground;
888
889 memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf, 4);
890 cl->ublen += 4;
891 rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, 7);
892 break;
893
894 default:
895 EncodeMonoRect8((uint8_t *)tightBeforeBuf, w, h);
896
897 cl->updateBuf[cl->ublen++] = (char)monoBackground;
898 cl->updateBuf[cl->ublen++] = (char)monoForeground;
899 rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, 5);
900 }
901
902 return CompressData(cl, streamId, dataLen,
903 tightConf[compressLevel].monoZlibLevel,
904 Z_DEFAULT_STRATEGY);
905 }
906
907 static rfbBool
SendIndexedRect(rfbClientPtr cl,int x,int y,int w,int h)908 SendIndexedRect(rfbClientPtr cl,
909 int x,
910 int y,
911 int w,
912 int h)
913 {
914 int streamId = 2;
915 int i, entryLen;
916
917 #ifdef LIBVNCSERVER_HAVE_LIBPNG
918 if (CanSendPngRect(cl, w, h)) {
919 return SendPngRect(cl, x, y, w, h);
920 }
921 #endif
922
923 if ( cl->ublen + TIGHT_MIN_TO_COMPRESS + 6 +
924 paletteNumColors * cl->format.bitsPerPixel / 8 >
925 UPDATE_BUF_SIZE ) {
926 if (!rfbSendUpdateBuf(cl))
927 return FALSE;
928 }
929
930 /* Prepare tight encoding header. */
931 if (tightConf[compressLevel].idxZlibLevel == 0 &&
932 cl->tightEncoding != rfbEncodingTightPng)
933 cl->updateBuf[cl->ublen++] =
934 (char)((rfbTightNoZlib | rfbTightExplicitFilter) << 4);
935 else
936 cl->updateBuf[cl->ublen++] = (streamId | rfbTightExplicitFilter) << 4;
937 cl->updateBuf[cl->ublen++] = rfbTightFilterPalette;
938 cl->updateBuf[cl->ublen++] = (char)(paletteNumColors - 1);
939
940 /* Prepare palette, convert image. */
941 switch (cl->format.bitsPerPixel) {
942
943 case 32:
944 EncodeIndexedRect32((uint8_t *)tightBeforeBuf, w * h);
945
946 for (i = 0; i < paletteNumColors; i++) {
947 ((uint32_t *)tightAfterBuf)[i] =
948 palette.entry[i].listNode->rgb;
949 }
950 if (usePixelFormat24) {
951 Pack24(cl, tightAfterBuf, &cl->format, paletteNumColors);
952 entryLen = 3;
953 } else
954 entryLen = 4;
955
956 memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf,
957 paletteNumColors * entryLen);
958 cl->ublen += paletteNumColors * entryLen;
959 rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding,
960 3 + paletteNumColors * entryLen);
961 break;
962
963 case 16:
964 EncodeIndexedRect16((uint8_t *)tightBeforeBuf, w * h);
965
966 for (i = 0; i < paletteNumColors; i++) {
967 ((uint16_t *)tightAfterBuf)[i] =
968 (uint16_t)palette.entry[i].listNode->rgb;
969 }
970
971 memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf, paletteNumColors * 2);
972 cl->ublen += paletteNumColors * 2;
973 rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding,
974 3 + paletteNumColors * 2);
975 break;
976
977 default:
978 return FALSE; /* Should never happen. */
979 }
980
981 return CompressData(cl, streamId, w * h,
982 tightConf[compressLevel].idxZlibLevel,
983 Z_DEFAULT_STRATEGY);
984 }
985
986 static rfbBool
SendFullColorRect(rfbClientPtr cl,int x,int y,int w,int h)987 SendFullColorRect(rfbClientPtr cl,
988 int x,
989 int y,
990 int w,
991 int h)
992 {
993 int streamId = 0;
994 int len;
995
996 #ifdef LIBVNCSERVER_HAVE_LIBPNG
997 if (CanSendPngRect(cl, w, h)) {
998 return SendPngRect(cl, x, y, w, h);
999 }
1000 #endif
1001
1002 if (cl->ublen + TIGHT_MIN_TO_COMPRESS + 1 > UPDATE_BUF_SIZE) {
1003 if (!rfbSendUpdateBuf(cl))
1004 return FALSE;
1005 }
1006
1007 if (tightConf[compressLevel].rawZlibLevel == 0 &&
1008 cl->tightEncoding != rfbEncodingTightPng)
1009 cl->updateBuf[cl->ublen++] = (char)(rfbTightNoZlib << 4);
1010 else
1011 cl->updateBuf[cl->ublen++] = 0x00; /* stream id = 0, no flushing, no filter */
1012 rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, 1);
1013
1014 if (usePixelFormat24) {
1015 Pack24(cl, tightBeforeBuf, &cl->format, w * h);
1016 len = 3;
1017 } else
1018 len = cl->format.bitsPerPixel / 8;
1019
1020 return CompressData(cl, streamId, w * h * len,
1021 tightConf[compressLevel].rawZlibLevel,
1022 Z_DEFAULT_STRATEGY);
1023 }
1024
1025 static rfbBool
CompressData(rfbClientPtr cl,int streamId,int dataLen,int zlibLevel,int zlibStrategy)1026 CompressData(rfbClientPtr cl,
1027 int streamId,
1028 int dataLen,
1029 int zlibLevel,
1030 int zlibStrategy)
1031 {
1032 z_streamp pz;
1033 int err;
1034
1035 if (dataLen < TIGHT_MIN_TO_COMPRESS) {
1036 memcpy(&cl->updateBuf[cl->ublen], tightBeforeBuf, dataLen);
1037 cl->ublen += dataLen;
1038 rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, dataLen);
1039 return TRUE;
1040 }
1041
1042 if (zlibLevel == 0)
1043 return SendCompressedData (cl, tightBeforeBuf, dataLen);
1044
1045 pz = &cl->zsStruct[streamId];
1046
1047 /* Initialize compression stream if needed. */
1048 if (!cl->zsActive[streamId]) {
1049 pz->zalloc = Z_NULL;
1050 pz->zfree = Z_NULL;
1051 pz->opaque = Z_NULL;
1052
1053 err = deflateInit2 (pz, zlibLevel, Z_DEFLATED, MAX_WBITS,
1054 MAX_MEM_LEVEL, zlibStrategy);
1055 if (err != Z_OK)
1056 return FALSE;
1057
1058 cl->zsActive[streamId] = TRUE;
1059 cl->zsLevel[streamId] = zlibLevel;
1060 }
1061
1062 /* Prepare buffer pointers. */
1063 pz->next_in = (Bytef *)tightBeforeBuf;
1064 pz->avail_in = dataLen;
1065 pz->next_out = (Bytef *)tightAfterBuf;
1066 pz->avail_out = tightAfterBufSize;
1067
1068 /* Change compression parameters if needed. */
1069 if (zlibLevel != cl->zsLevel[streamId]) {
1070 if (deflateParams (pz, zlibLevel, zlibStrategy) != Z_OK) {
1071 return FALSE;
1072 }
1073 cl->zsLevel[streamId] = zlibLevel;
1074 }
1075
1076 /* Actual compression. */
1077 if (deflate(pz, Z_SYNC_FLUSH) != Z_OK ||
1078 pz->avail_in != 0 || pz->avail_out == 0) {
1079 return FALSE;
1080 }
1081
1082 return SendCompressedData(cl, tightAfterBuf,
1083 tightAfterBufSize - pz->avail_out);
1084 }
1085
SendCompressedData(rfbClientPtr cl,char * buf,int compressedLen)1086 static rfbBool SendCompressedData(rfbClientPtr cl, char *buf,
1087 int compressedLen)
1088 {
1089 int i, portionLen;
1090
1091 cl->updateBuf[cl->ublen++] = compressedLen & 0x7F;
1092 rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, 1);
1093 if (compressedLen > 0x7F) {
1094 cl->updateBuf[cl->ublen-1] |= 0x80;
1095 cl->updateBuf[cl->ublen++] = compressedLen >> 7 & 0x7F;
1096 rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, 1);
1097 if (compressedLen > 0x3FFF) {
1098 cl->updateBuf[cl->ublen-1] |= 0x80;
1099 cl->updateBuf[cl->ublen++] = compressedLen >> 14 & 0xFF;
1100 rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, 1);
1101 }
1102 }
1103
1104 portionLen = UPDATE_BUF_SIZE;
1105 for (i = 0; i < compressedLen; i += portionLen) {
1106 if (i + portionLen > compressedLen) {
1107 portionLen = compressedLen - i;
1108 }
1109 if (cl->ublen + portionLen > UPDATE_BUF_SIZE) {
1110 if (!rfbSendUpdateBuf(cl))
1111 return FALSE;
1112 }
1113 memcpy(&cl->updateBuf[cl->ublen], &buf[i], portionLen);
1114 cl->ublen += portionLen;
1115 }
1116 rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, compressedLen);
1117
1118 return TRUE;
1119 }
1120
1121
1122 /*
1123 * Code to determine how many different colors used in rectangle.
1124 */
1125
1126 static void
FillPalette8(int count)1127 FillPalette8(int count)
1128 {
1129 uint8_t *data = (uint8_t *)tightBeforeBuf;
1130 uint8_t c0, c1;
1131 int i, n0, n1;
1132
1133 paletteNumColors = 0;
1134
1135 c0 = data[0];
1136 for (i = 1; i < count && data[i] == c0; i++);
1137 if (i == count) {
1138 paletteNumColors = 1;
1139 return; /* Solid rectangle */
1140 }
1141
1142 if (paletteMaxColors < 2)
1143 return;
1144
1145 n0 = i;
1146 c1 = data[i];
1147 n1 = 0;
1148 for (i++; i < count; i++) {
1149 if (data[i] == c0) {
1150 n0++;
1151 } else if (data[i] == c1) {
1152 n1++;
1153 } else
1154 break;
1155 }
1156 if (i == count) {
1157 if (n0 > n1) {
1158 monoBackground = (uint32_t)c0;
1159 monoForeground = (uint32_t)c1;
1160 } else {
1161 monoBackground = (uint32_t)c1;
1162 monoForeground = (uint32_t)c0;
1163 }
1164 paletteNumColors = 2; /* Two colors */
1165 }
1166 }
1167
1168
1169 #define DEFINE_FILL_PALETTE_FUNCTION(bpp) \
1170 \
1171 static void \
1172 FillPalette##bpp(int count) { \
1173 uint##bpp##_t *data = (uint##bpp##_t *)tightBeforeBuf; \
1174 uint##bpp##_t c0, c1, ci; \
1175 int i, n0, n1, ni; \
1176 \
1177 c0 = data[0]; \
1178 for (i = 1; i < count && data[i] == c0; i++); \
1179 if (i >= count) { \
1180 paletteNumColors = 1; /* Solid rectangle */ \
1181 return; \
1182 } \
1183 \
1184 if (paletteMaxColors < 2) { \
1185 paletteNumColors = 0; /* Full-color encoding preferred */ \
1186 return; \
1187 } \
1188 \
1189 n0 = i; \
1190 c1 = data[i]; \
1191 n1 = 0; \
1192 for (i++; i < count; i++) { \
1193 ci = data[i]; \
1194 if (ci == c0) { \
1195 n0++; \
1196 } else if (ci == c1) { \
1197 n1++; \
1198 } else \
1199 break; \
1200 } \
1201 if (i >= count) { \
1202 if (n0 > n1) { \
1203 monoBackground = (uint32_t)c0; \
1204 monoForeground = (uint32_t)c1; \
1205 } else { \
1206 monoBackground = (uint32_t)c1; \
1207 monoForeground = (uint32_t)c0; \
1208 } \
1209 paletteNumColors = 2; /* Two colors */ \
1210 return; \
1211 } \
1212 \
1213 PaletteReset(); \
1214 PaletteInsert (c0, (uint32_t)n0, bpp); \
1215 PaletteInsert (c1, (uint32_t)n1, bpp); \
1216 \
1217 ni = 1; \
1218 for (i++; i < count; i++) { \
1219 if (data[i] == ci) { \
1220 ni++; \
1221 } else { \
1222 if (!PaletteInsert (ci, (uint32_t)ni, bpp)) \
1223 return; \
1224 ci = data[i]; \
1225 ni = 1; \
1226 } \
1227 } \
1228 PaletteInsert (ci, (uint32_t)ni, bpp); \
1229 }
1230
1231 DEFINE_FILL_PALETTE_FUNCTION(16)
1232 DEFINE_FILL_PALETTE_FUNCTION(32)
1233
1234 #define DEFINE_FAST_FILL_PALETTE_FUNCTION(bpp) \
1235 \
1236 static void \
1237 FastFillPalette##bpp(rfbClientPtr cl, uint##bpp##_t *data, int w, \
1238 int pitch, int h) \
1239 { \
1240 uint##bpp##_t c0, c1, ci, mask, c0t, c1t, cit; \
1241 int i, j, i2 = 0, j2, n0, n1, ni; \
1242 \
1243 if (cl->translateFn != rfbTranslateNone) { \
1244 mask = cl->screen->serverFormat.redMax \
1245 << cl->screen->serverFormat.redShift; \
1246 mask |= cl->screen->serverFormat.greenMax \
1247 << cl->screen->serverFormat.greenShift; \
1248 mask |= cl->screen->serverFormat.blueMax \
1249 << cl->screen->serverFormat.blueShift; \
1250 } else mask = ~0; \
1251 \
1252 c0 = data[0] & mask; \
1253 for (j = 0; j < h; j++) { \
1254 for (i = 0; i < w; i++) { \
1255 if ((data[j * pitch + i] & mask) != c0) \
1256 goto done; \
1257 } \
1258 } \
1259 done: \
1260 if (j >= h) { \
1261 paletteNumColors = 1; /* Solid rectangle */ \
1262 return; \
1263 } \
1264 if (paletteMaxColors < 2) { \
1265 paletteNumColors = 0; /* Full-color encoding preferred */ \
1266 return; \
1267 } \
1268 \
1269 n0 = j * w + i; \
1270 c1 = data[j * pitch + i] & mask; \
1271 n1 = 0; \
1272 i++; if (i >= w) {i = 0; j++;} \
1273 for (j2 = j; j2 < h; j2++) { \
1274 for (i2 = i; i2 < w; i2++) { \
1275 ci = data[j2 * pitch + i2] & mask; \
1276 if (ci == c0) { \
1277 n0++; \
1278 } else if (ci == c1) { \
1279 n1++; \
1280 } else \
1281 goto done2; \
1282 } \
1283 i = 0; \
1284 } \
1285 done2: \
1286 (*cl->translateFn)(cl->translateLookupTable, \
1287 &cl->screen->serverFormat, &cl->format, \
1288 (char *)&c0, (char *)&c0t, bpp/8, 1, 1); \
1289 (*cl->translateFn)(cl->translateLookupTable, \
1290 &cl->screen->serverFormat, &cl->format, \
1291 (char *)&c1, (char *)&c1t, bpp/8, 1, 1); \
1292 if (j2 >= h) { \
1293 if (n0 > n1) { \
1294 monoBackground = (uint32_t)c0t; \
1295 monoForeground = (uint32_t)c1t; \
1296 } else { \
1297 monoBackground = (uint32_t)c1t; \
1298 monoForeground = (uint32_t)c0t; \
1299 } \
1300 paletteNumColors = 2; /* Two colors */ \
1301 return; \
1302 } \
1303 \
1304 PaletteReset(); \
1305 PaletteInsert (c0t, (uint32_t)n0, bpp); \
1306 PaletteInsert (c1t, (uint32_t)n1, bpp); \
1307 \
1308 ni = 1; \
1309 i2++; if (i2 >= w) {i2 = 0; j2++;} \
1310 for (j = j2; j < h; j++) { \
1311 for (i = i2; i < w; i++) { \
1312 if ((data[j * pitch + i] & mask) == ci) { \
1313 ni++; \
1314 } else { \
1315 (*cl->translateFn)(cl->translateLookupTable, \
1316 &cl->screen->serverFormat, \
1317 &cl->format, (char *)&ci, \
1318 (char *)&cit, bpp/8, 1, 1); \
1319 if (!PaletteInsert (cit, (uint32_t)ni, bpp)) \
1320 return; \
1321 ci = data[j * pitch + i] & mask; \
1322 ni = 1; \
1323 } \
1324 } \
1325 i2 = 0; \
1326 } \
1327 \
1328 (*cl->translateFn)(cl->translateLookupTable, \
1329 &cl->screen->serverFormat, &cl->format, \
1330 (char *)&ci, (char *)&cit, bpp/8, 1, 1); \
1331 PaletteInsert (cit, (uint32_t)ni, bpp); \
1332 }
1333
1334 DEFINE_FAST_FILL_PALETTE_FUNCTION(16)
1335 DEFINE_FAST_FILL_PALETTE_FUNCTION(32)
1336
1337
1338 /*
1339 * Functions to operate with palette structures.
1340 */
1341
1342 #define HASH_FUNC16(rgb) ((int)((((rgb) >> 8) + (rgb)) & 0xFF))
1343 #define HASH_FUNC32(rgb) ((int)((((rgb) >> 16) + ((rgb) >> 8)) & 0xFF))
1344
1345
1346 static void
PaletteReset(void)1347 PaletteReset(void)
1348 {
1349 paletteNumColors = 0;
1350 memset(palette.hash, 0, 256 * sizeof(COLOR_LIST *));
1351 }
1352
1353
1354 static int
PaletteInsert(uint32_t rgb,int numPixels,int bpp)1355 PaletteInsert(uint32_t rgb,
1356 int numPixels,
1357 int bpp)
1358 {
1359 COLOR_LIST *pnode;
1360 COLOR_LIST *prev_pnode = NULL;
1361 int hash_key, idx, new_idx, count;
1362
1363 hash_key = (bpp == 16) ? HASH_FUNC16(rgb) : HASH_FUNC32(rgb);
1364
1365 pnode = palette.hash[hash_key];
1366
1367 while (pnode != NULL) {
1368 if (pnode->rgb == rgb) {
1369 /* Such palette entry already exists. */
1370 new_idx = idx = pnode->idx;
1371 count = palette.entry[idx].numPixels + numPixels;
1372 if (new_idx && palette.entry[new_idx-1].numPixels < count) {
1373 do {
1374 palette.entry[new_idx] = palette.entry[new_idx-1];
1375 palette.entry[new_idx].listNode->idx = new_idx;
1376 new_idx--;
1377 }
1378 while (new_idx && palette.entry[new_idx-1].numPixels < count);
1379 palette.entry[new_idx].listNode = pnode;
1380 pnode->idx = new_idx;
1381 }
1382 palette.entry[new_idx].numPixels = count;
1383 return paletteNumColors;
1384 }
1385 prev_pnode = pnode;
1386 pnode = pnode->next;
1387 }
1388
1389 /* Check if palette is full. */
1390 if (paletteNumColors == 256 || paletteNumColors == paletteMaxColors) {
1391 paletteNumColors = 0;
1392 return 0;
1393 }
1394
1395 /* Move palette entries with lesser pixel counts. */
1396 for ( idx = paletteNumColors;
1397 idx > 0 && palette.entry[idx-1].numPixels < numPixels;
1398 idx-- ) {
1399 palette.entry[idx] = palette.entry[idx-1];
1400 palette.entry[idx].listNode->idx = idx;
1401 }
1402
1403 /* Add new palette entry into the freed slot. */
1404 pnode = &palette.list[paletteNumColors];
1405 if (prev_pnode != NULL) {
1406 prev_pnode->next = pnode;
1407 } else {
1408 palette.hash[hash_key] = pnode;
1409 }
1410 pnode->next = NULL;
1411 pnode->idx = idx;
1412 pnode->rgb = rgb;
1413 palette.entry[idx].listNode = pnode;
1414 palette.entry[idx].numPixels = numPixels;
1415
1416 return (++paletteNumColors);
1417 }
1418
1419
1420 /*
1421 * Converting 32-bit color samples into 24-bit colors.
1422 * Should be called only when redMax, greenMax and blueMax are 255.
1423 * Color components assumed to be byte-aligned.
1424 */
1425
Pack24(rfbClientPtr cl,char * buf,rfbPixelFormat * fmt,int count)1426 static void Pack24(rfbClientPtr cl,
1427 char *buf,
1428 rfbPixelFormat *fmt,
1429 int count)
1430 {
1431 uint32_t *buf32;
1432 uint32_t pix;
1433 int r_shift, g_shift, b_shift;
1434
1435 buf32 = (uint32_t *)buf;
1436
1437 if (!cl->screen->serverFormat.bigEndian == !fmt->bigEndian) {
1438 r_shift = fmt->redShift;
1439 g_shift = fmt->greenShift;
1440 b_shift = fmt->blueShift;
1441 } else {
1442 r_shift = 24 - fmt->redShift;
1443 g_shift = 24 - fmt->greenShift;
1444 b_shift = 24 - fmt->blueShift;
1445 }
1446
1447 while (count--) {
1448 pix = *buf32++;
1449 *buf++ = (char)(pix >> r_shift);
1450 *buf++ = (char)(pix >> g_shift);
1451 *buf++ = (char)(pix >> b_shift);
1452 }
1453 }
1454
1455
1456 /*
1457 * Converting truecolor samples into palette indices.
1458 */
1459
1460 #define DEFINE_IDX_ENCODE_FUNCTION(bpp) \
1461 \
1462 static void \
1463 EncodeIndexedRect##bpp(uint8_t *buf, int count) { \
1464 COLOR_LIST *pnode; \
1465 uint##bpp##_t *src; \
1466 uint##bpp##_t rgb; \
1467 int rep = 0; \
1468 \
1469 src = (uint##bpp##_t *) buf; \
1470 \
1471 while (count--) { \
1472 rgb = *src++; \
1473 while (count && *src == rgb) { \
1474 rep++, src++, count--; \
1475 } \
1476 pnode = palette.hash[HASH_FUNC##bpp(rgb)]; \
1477 while (pnode != NULL) { \
1478 if ((uint##bpp##_t)pnode->rgb == rgb) { \
1479 *buf++ = (uint8_t)pnode->idx; \
1480 while (rep) { \
1481 *buf++ = (uint8_t)pnode->idx; \
1482 rep--; \
1483 } \
1484 break; \
1485 } \
1486 pnode = pnode->next; \
1487 } \
1488 } \
1489 }
1490
1491 DEFINE_IDX_ENCODE_FUNCTION(16)
1492 DEFINE_IDX_ENCODE_FUNCTION(32)
1493
1494
1495 #define DEFINE_MONO_ENCODE_FUNCTION(bpp) \
1496 \
1497 static void \
1498 EncodeMonoRect##bpp(uint8_t *buf, int w, int h) { \
1499 uint##bpp##_t *ptr; \
1500 uint##bpp##_t bg; \
1501 unsigned int value, mask; \
1502 int aligned_width; \
1503 int x, y, bg_bits; \
1504 \
1505 ptr = (uint##bpp##_t *) buf; \
1506 bg = (uint##bpp##_t) monoBackground; \
1507 aligned_width = w - w % 8; \
1508 \
1509 for (y = 0; y < h; y++) { \
1510 for (x = 0; x < aligned_width; x += 8) { \
1511 for (bg_bits = 0; bg_bits < 8; bg_bits++) { \
1512 if (*ptr++ != bg) \
1513 break; \
1514 } \
1515 if (bg_bits == 8) { \
1516 *buf++ = 0; \
1517 continue; \
1518 } \
1519 mask = 0x80 >> bg_bits; \
1520 value = mask; \
1521 for (bg_bits++; bg_bits < 8; bg_bits++) { \
1522 mask >>= 1; \
1523 if (*ptr++ != bg) { \
1524 value |= mask; \
1525 } \
1526 } \
1527 *buf++ = (uint8_t)value; \
1528 } \
1529 \
1530 mask = 0x80; \
1531 value = 0; \
1532 if (x >= w) \
1533 continue; \
1534 \
1535 for (; x < w; x++) { \
1536 if (*ptr++ != bg) { \
1537 value |= mask; \
1538 } \
1539 mask >>= 1; \
1540 } \
1541 *buf++ = (uint8_t)value; \
1542 } \
1543 }
1544
1545 DEFINE_MONO_ENCODE_FUNCTION(8)
1546 DEFINE_MONO_ENCODE_FUNCTION(16)
1547 DEFINE_MONO_ENCODE_FUNCTION(32)
1548
1549
1550 /*
1551 * JPEG compression stuff.
1552 */
1553
1554 static rfbBool
SendJpegRect(rfbClientPtr cl,int x,int y,int w,int h,int quality)1555 SendJpegRect(rfbClientPtr cl, int x, int y, int w, int h, int quality)
1556 {
1557 unsigned char *srcbuf;
1558 int ps = cl->screen->serverFormat.bitsPerPixel / 8;
1559 int subsamp = subsampLevel2tjsubsamp[subsampLevel];
1560 unsigned long size = 0;
1561 int flags = 0, pitch;
1562 unsigned char *tmpbuf = NULL;
1563
1564 if (cl->screen->serverFormat.bitsPerPixel == 8)
1565 return SendFullColorRect(cl, x, y, w, h);
1566
1567 if (ps < 2) {
1568 rfbLog("Error: JPEG requires 16-bit, 24-bit, or 32-bit pixel format.\n");
1569 return 0;
1570 }
1571 if (!j) {
1572 if ((j = tjInitCompress()) == NULL) {
1573 rfbLog("JPEG Error: %s\n", tjGetErrorStr());
1574 return 0;
1575 }
1576 }
1577
1578 if (tightAfterBufSize < TJBUFSIZE(w, h)) {
1579 if (tightAfterBuf == NULL)
1580 tightAfterBuf = (char *)malloc(TJBUFSIZE(w, h));
1581 else
1582 tightAfterBuf = (char *)realloc(tightAfterBuf,
1583 TJBUFSIZE(w, h));
1584 if (!tightAfterBuf) {
1585 rfbLog("Memory allocation failure!\n");
1586 return 0;
1587 }
1588 tightAfterBufSize = TJBUFSIZE(w, h);
1589 }
1590
1591 if (ps == 2) {
1592 uint16_t *srcptr, pix;
1593 unsigned char *dst;
1594 int inRed, inGreen, inBlue, i, j;
1595
1596 if((tmpbuf = (unsigned char *)malloc(w * h * 3)) == NULL)
1597 rfbLog("Memory allocation failure!\n");
1598 srcptr = (uint16_t *)&cl->scaledScreen->frameBuffer
1599 [y * cl->scaledScreen->paddedWidthInBytes + x * ps];
1600 dst = tmpbuf;
1601 for(j = 0; j < h; j++) {
1602 uint16_t *srcptr2 = srcptr;
1603 unsigned char *dst2 = dst;
1604 for (i = 0; i < w; i++) {
1605 pix = *srcptr2++;
1606 inRed = (int) (pix >> cl->screen->serverFormat.redShift
1607 & cl->screen->serverFormat.redMax);
1608 inGreen = (int) (pix >> cl->screen->serverFormat.greenShift
1609 & cl->screen->serverFormat.greenMax);
1610 inBlue = (int) (pix >> cl->screen->serverFormat.blueShift
1611 & cl->screen->serverFormat.blueMax);
1612 *dst2++ = (uint8_t)((inRed * 255
1613 + cl->screen->serverFormat.redMax / 2)
1614 / cl->screen->serverFormat.redMax);
1615 *dst2++ = (uint8_t)((inGreen * 255
1616 + cl->screen->serverFormat.greenMax / 2)
1617 / cl->screen->serverFormat.greenMax);
1618 *dst2++ = (uint8_t)((inBlue * 255
1619 + cl->screen->serverFormat.blueMax / 2)
1620 / cl->screen->serverFormat.blueMax);
1621 }
1622 srcptr += cl->scaledScreen->paddedWidthInBytes / ps;
1623 dst += w * 3;
1624 }
1625 srcbuf = tmpbuf;
1626 pitch = w * 3;
1627 ps = 3;
1628 } else {
1629 if (cl->screen->serverFormat.bigEndian && ps == 4)
1630 flags |= TJ_ALPHAFIRST;
1631 if (cl->screen->serverFormat.redShift == 16
1632 && cl->screen->serverFormat.blueShift == 0)
1633 flags |= TJ_BGR;
1634 if (cl->screen->serverFormat.bigEndian)
1635 flags ^= TJ_BGR;
1636 pitch = cl->scaledScreen->paddedWidthInBytes;
1637 srcbuf = (unsigned char *)&cl->scaledScreen->frameBuffer
1638 [y * pitch + x * ps];
1639 }
1640
1641 if (tjCompress(j, srcbuf, w, pitch, h, ps, (unsigned char *)tightAfterBuf,
1642 &size, subsamp, quality, flags) == -1) {
1643 rfbLog("JPEG Error: %s\n", tjGetErrorStr());
1644 if (tmpbuf) {
1645 free(tmpbuf);
1646 tmpbuf = NULL;
1647 }
1648 return 0;
1649 }
1650
1651 if (tmpbuf) {
1652 free(tmpbuf);
1653 tmpbuf = NULL;
1654 }
1655
1656 if (cl->ublen + TIGHT_MIN_TO_COMPRESS + 1 > UPDATE_BUF_SIZE) {
1657 if (!rfbSendUpdateBuf(cl))
1658 return FALSE;
1659 }
1660
1661 cl->updateBuf[cl->ublen++] = (char)(rfbTightJpeg << 4);
1662 rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, 1);
1663
1664 return SendCompressedData(cl, tightAfterBuf, (int)size);
1665 }
1666
1667 static void
PrepareRowForImg(rfbClientPtr cl,uint8_t * dst,int x,int y,int count)1668 PrepareRowForImg(rfbClientPtr cl,
1669 uint8_t *dst,
1670 int x,
1671 int y,
1672 int count)
1673 {
1674 if (cl->screen->serverFormat.bitsPerPixel == 32) {
1675 if ( cl->screen->serverFormat.redMax == 0xFF &&
1676 cl->screen->serverFormat.greenMax == 0xFF &&
1677 cl->screen->serverFormat.blueMax == 0xFF ) {
1678 PrepareRowForImg24(cl, dst, x, y, count);
1679 } else {
1680 PrepareRowForImg32(cl, dst, x, y, count);
1681 }
1682 } else {
1683 /* 16 bpp assumed. */
1684 PrepareRowForImg16(cl, dst, x, y, count);
1685 }
1686 }
1687
1688 static void
PrepareRowForImg24(rfbClientPtr cl,uint8_t * dst,int x,int y,int count)1689 PrepareRowForImg24(rfbClientPtr cl,
1690 uint8_t *dst,
1691 int x,
1692 int y,
1693 int count)
1694 {
1695 uint32_t *fbptr;
1696 uint32_t pix;
1697
1698 fbptr = (uint32_t *)
1699 &cl->scaledScreen->frameBuffer[y * cl->scaledScreen->paddedWidthInBytes + x * 4];
1700
1701 while (count--) {
1702 pix = *fbptr++;
1703 *dst++ = (uint8_t)(pix >> cl->screen->serverFormat.redShift);
1704 *dst++ = (uint8_t)(pix >> cl->screen->serverFormat.greenShift);
1705 *dst++ = (uint8_t)(pix >> cl->screen->serverFormat.blueShift);
1706 }
1707 }
1708
1709 #define DEFINE_JPEG_GET_ROW_FUNCTION(bpp) \
1710 \
1711 static void \
1712 PrepareRowForImg##bpp(rfbClientPtr cl, uint8_t *dst, int x, int y, int count) { \
1713 uint##bpp##_t *fbptr; \
1714 uint##bpp##_t pix; \
1715 int inRed, inGreen, inBlue; \
1716 \
1717 fbptr = (uint##bpp##_t *) \
1718 &cl->scaledScreen->frameBuffer[y * cl->scaledScreen->paddedWidthInBytes + \
1719 x * (bpp / 8)]; \
1720 \
1721 while (count--) { \
1722 pix = *fbptr++; \
1723 \
1724 inRed = (int) \
1725 (pix >> cl->screen->serverFormat.redShift & cl->screen->serverFormat.redMax); \
1726 inGreen = (int) \
1727 (pix >> cl->screen->serverFormat.greenShift & cl->screen->serverFormat.greenMax); \
1728 inBlue = (int) \
1729 (pix >> cl->screen->serverFormat.blueShift & cl->screen->serverFormat.blueMax); \
1730 \
1731 *dst++ = (uint8_t)((inRed * 255 + cl->screen->serverFormat.redMax / 2) / \
1732 cl->screen->serverFormat.redMax); \
1733 *dst++ = (uint8_t)((inGreen * 255 + cl->screen->serverFormat.greenMax / 2) / \
1734 cl->screen->serverFormat.greenMax); \
1735 *dst++ = (uint8_t)((inBlue * 255 + cl->screen->serverFormat.blueMax / 2) / \
1736 cl->screen->serverFormat.blueMax); \
1737 } \
1738 }
1739
1740 DEFINE_JPEG_GET_ROW_FUNCTION(16)
1741 DEFINE_JPEG_GET_ROW_FUNCTION(32)
1742
1743 /*
1744 * PNG compression stuff.
1745 */
1746
1747 #ifdef LIBVNCSERVER_HAVE_LIBPNG
1748
1749 static TLS int pngDstDataLen = 0;
1750
CanSendPngRect(rfbClientPtr cl,int w,int h)1751 static rfbBool CanSendPngRect(rfbClientPtr cl, int w, int h) {
1752 if (cl->tightEncoding != rfbEncodingTightPng) {
1753 return FALSE;
1754 }
1755
1756 if ( cl->screen->serverFormat.bitsPerPixel == 8 ||
1757 cl->format.bitsPerPixel == 8) {
1758 return FALSE;
1759 }
1760
1761 return TRUE;
1762 }
1763
pngWriteData(png_structp png_ptr,png_bytep data,png_size_t length)1764 static void pngWriteData(png_structp png_ptr, png_bytep data,
1765 png_size_t length)
1766 {
1767 #if 0
1768 rfbClientPtr cl = png_get_io_ptr(png_ptr);
1769
1770 buffer_reserve(&vs->tight.png, vs->tight.png.offset + length);
1771 memcpy(vs->tight.png.buffer + vs->tight.png.offset, data, length);
1772 #endif
1773 memcpy(tightAfterBuf + pngDstDataLen, data, length);
1774
1775 pngDstDataLen += length;
1776 }
1777
pngFlushData(png_structp png_ptr)1778 static void pngFlushData(png_structp png_ptr)
1779 {
1780 }
1781
1782
pngMalloc(png_structp png_ptr,png_size_t size)1783 static void *pngMalloc(png_structp png_ptr, png_size_t size)
1784 {
1785 return malloc(size);
1786 }
1787
pngFree(png_structp png_ptr,png_voidp ptr)1788 static void pngFree(png_structp png_ptr, png_voidp ptr)
1789 {
1790 free(ptr);
1791 }
1792
SendPngRect(rfbClientPtr cl,int x,int y,int w,int h)1793 static rfbBool SendPngRect(rfbClientPtr cl, int x, int y, int w, int h) {
1794 /* rfbLog(">> SendPngRect x:%d, y:%d, w:%d, h:%d\n", x, y, w, h); */
1795
1796 png_byte color_type;
1797 png_structp png_ptr;
1798 png_infop info_ptr;
1799 png_colorp png_palette = NULL;
1800 int level = tightPngConf[cl->tightCompressLevel].png_zlib_level;
1801 int filters = tightPngConf[cl->tightCompressLevel].png_filters;
1802 uint8_t *buf;
1803 int dy;
1804
1805 pngDstDataLen = 0;
1806
1807 png_ptr = png_create_write_struct_2(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL,
1808 NULL, pngMalloc, pngFree);
1809
1810 if (png_ptr == NULL)
1811 return FALSE;
1812
1813 info_ptr = png_create_info_struct(png_ptr);
1814
1815 if (info_ptr == NULL) {
1816 png_destroy_write_struct(&png_ptr, NULL);
1817 return FALSE;
1818 }
1819
1820 png_set_write_fn(png_ptr, (void *) cl, pngWriteData, pngFlushData);
1821 png_set_compression_level(png_ptr, level);
1822 png_set_filter(png_ptr, PNG_FILTER_TYPE_DEFAULT, filters);
1823
1824 #if 0
1825 /* TODO: */
1826 if (palette) {
1827 color_type = PNG_COLOR_TYPE_PALETTE;
1828 } else {
1829 color_type = PNG_COLOR_TYPE_RGB;
1830 }
1831 #else
1832 color_type = PNG_COLOR_TYPE_RGB;
1833 #endif
1834 png_set_IHDR(png_ptr, info_ptr, w, h,
1835 8, color_type, PNG_INTERLACE_NONE,
1836 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
1837
1838 #if 0
1839 if (color_type == PNG_COLOR_TYPE_PALETTE) {
1840 struct palette_cb_priv priv;
1841
1842 png_palette = pngMalloc(png_ptr, sizeof(*png_palette) *
1843 palette_size(palette));
1844
1845 priv.vs = vs;
1846 priv.png_palette = png_palette;
1847 palette_iter(palette, write_png_palette, &priv);
1848
1849 png_set_PLTE(png_ptr, info_ptr, png_palette, palette_size(palette));
1850
1851 offset = vs->tight.tight.offset;
1852 if (vs->clientds.pf.bytes_per_pixel == 4) {
1853 tight_encode_indexed_rect32(vs->tight.tight.buffer, w * h, palette);
1854 } else {
1855 tight_encode_indexed_rect16(vs->tight.tight.buffer, w * h, palette);
1856 }
1857 }
1858
1859 buffer_reserve(&vs->tight.png, 2048);
1860 #endif
1861
1862 png_write_info(png_ptr, info_ptr);
1863 buf = malloc(w * 3);
1864 for (dy = 0; dy < h; dy++)
1865 {
1866 #if 0
1867 if (color_type == PNG_COLOR_TYPE_PALETTE) {
1868 memcpy(buf, vs->tight.tight.buffer + (dy * w), w);
1869 } else {
1870 PrepareRowForImg(cl, buf, x, y + dy, w);
1871 }
1872 #else
1873 PrepareRowForImg(cl, buf, x, y + dy, w);
1874 #endif
1875 png_write_row(png_ptr, buf);
1876 }
1877 free(buf);
1878
1879 png_write_end(png_ptr, NULL);
1880
1881 if (color_type == PNG_COLOR_TYPE_PALETTE) {
1882 pngFree(png_ptr, png_palette);
1883 }
1884
1885 png_destroy_write_struct(&png_ptr, &info_ptr);
1886
1887 /* done v */
1888
1889 if (cl->ublen + TIGHT_MIN_TO_COMPRESS + 1 > UPDATE_BUF_SIZE) {
1890 if (!rfbSendUpdateBuf(cl))
1891 return FALSE;
1892 }
1893
1894 cl->updateBuf[cl->ublen++] = (char)(rfbTightPng << 4);
1895 rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, 1);
1896
1897 /* rfbLog("<< SendPngRect\n"); */
1898 return SendCompressedData(cl, tightAfterBuf, pngDstDataLen);
1899 }
1900 #endif
1901