4 * Routines to implement Tight Encoding
8 * Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved.
9 * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
11 * This is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This software is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this software; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
27 /*#include <stdio.h>*/
34 #define NEEDFAR_POINTERS
39 /* Note: The following constant should not be changed. */
40 #define TIGHT_MIN_TO_COMPRESS 12
42 /* The parameters below may be adjusted. */
43 #define MIN_SPLIT_RECT_SIZE 4096
44 #define MIN_SOLID_SUBRECT_SIZE 2048
45 #define MAX_SPLIT_TILE_SIZE 16
47 /* May be set to TRUE with "-lazytight" Xvnc option. */
48 rfbBool rfbTightDisableGradient = FALSE;
50 /* This variable is set on every rfbSendRectEncodingTight() call. */
51 static rfbBool usePixelFormat24;
54 /* Compression level stuff. The following array contains various
55 encoder parameters for each of 10 compression levels (0..9).
56 Last three parameters correspond to JPEG quality levels (0..9). */
58 typedef struct TIGHT_CONF_s {
59 int maxRectSize, maxRectWidth;
60 int monoMinRectSize, gradientMinRectSize;
61 int idxZlibLevel, monoZlibLevel, rawZlibLevel, gradientZlibLevel;
62 int gradientThreshold, gradientThreshold24;
63 int idxMaxColorsDivisor;
64 int jpegQuality, jpegThreshold, jpegThreshold24;
67 static TIGHT_CONF tightConf[10] = {
68 { 512, 32, 6, 65536, 0, 0, 0, 0, 0, 0, 4, 5, 10000, 23000 },
69 { 2048, 128, 6, 65536, 1, 1, 1, 0, 0, 0, 8, 10, 8000, 18000 },
70 { 6144, 256, 8, 65536, 3, 3, 2, 0, 0, 0, 24, 15, 6500, 15000 },
71 { 10240, 1024, 12, 65536, 5, 5, 3, 0, 0, 0, 32, 25, 5000, 12000 },
72 { 16384, 2048, 12, 65536, 6, 6, 4, 0, 0, 0, 32, 37, 4000, 10000 },
73 { 32768, 2048, 12, 4096, 7, 7, 5, 4, 150, 380, 32, 50, 3000, 8000 },
74 { 65536, 2048, 16, 4096, 7, 7, 6, 4, 170, 420, 48, 60, 2000, 5000 },
75 { 65536, 2048, 16, 4096, 8, 8, 7, 5, 180, 450, 64, 70, 1000, 2500 },
76 { 65536, 2048, 32, 8192, 9, 9, 8, 6, 190, 475, 64, 75, 500, 1200 },
77 { 65536, 2048, 32, 8192, 9, 9, 9, 6, 200, 500, 96, 80, 200, 500 }
80 static int compressLevel;
81 static int qualityLevel;
83 /* Stuff dealing with palettes. */
85 typedef struct COLOR_LIST_s {
86 struct COLOR_LIST_s *next;
91 typedef struct PALETTE_ENTRY_s {
96 typedef struct PALETTE_s {
97 PALETTE_ENTRY entry[256];
98 COLOR_LIST *hash[256];
102 /* TODO: move into rfbScreen struct */
103 static int paletteNumColors, paletteMaxColors;
104 static uint32_t monoBackground, monoForeground;
105 static PALETTE palette;
107 /* Pointers to dynamically-allocated buffers. */
109 static int tightBeforeBufSize = 0;
110 static char *tightBeforeBuf = NULL;
112 static int tightAfterBufSize = 0;
113 static char *tightAfterBuf = NULL;
115 static int *prevRowBuf = NULL;
117 void rfbTightCleanup(rfbScreenInfoPtr screen)
119 if(tightBeforeBufSize) {
120 free(tightBeforeBuf);
121 tightBeforeBufSize=0;
123 if(tightAfterBufSize) {
129 /* Prototypes for static functions. */
131 static void FindBestSolidArea (rfbClientPtr cl, int x, int y, int w, int h,
132 uint32_t colorValue, int *w_ptr, int *h_ptr);
133 static void ExtendSolidArea (rfbClientPtr cl, int x, int y, int w, int h,
135 int *x_ptr, int *y_ptr, int *w_ptr, int *h_ptr);
136 static rfbBool CheckSolidTile (rfbClientPtr cl, int x, int y, int w, int h,
137 uint32_t *colorPtr, rfbBool needSameColor);
138 static rfbBool CheckSolidTile8 (rfbClientPtr cl, int x, int y, int w, int h,
139 uint32_t *colorPtr, rfbBool needSameColor);
140 static rfbBool CheckSolidTile16 (rfbClientPtr cl, int x, int y, int w, int h,
141 uint32_t *colorPtr, rfbBool needSameColor);
142 static rfbBool CheckSolidTile32 (rfbClientPtr cl, int x, int y, int w, int h,
143 uint32_t *colorPtr, rfbBool needSameColor);
145 static rfbBool SendRectSimple (rfbClientPtr cl, int x, int y, int w, int h);
146 static rfbBool SendSubrect (rfbClientPtr cl, int x, int y, int w, int h);
147 static rfbBool SendTightHeader (rfbClientPtr cl, int x, int y, int w, int h);
149 static rfbBool SendSolidRect (rfbClientPtr cl);
150 static rfbBool SendMonoRect (rfbClientPtr cl, int w, int h);
151 static rfbBool SendIndexedRect (rfbClientPtr cl, int w, int h);
152 static rfbBool SendFullColorRect (rfbClientPtr cl, int w, int h);
153 static rfbBool SendGradientRect (rfbClientPtr cl, int w, int h);
155 static rfbBool CompressData(rfbClientPtr cl, int streamId, int dataLen,
156 int zlibLevel, int zlibStrategy);
157 static rfbBool SendCompressedData(rfbClientPtr cl, int compressedLen);
159 static void FillPalette8(int count);
160 static void FillPalette16(int count);
161 static void FillPalette32(int count);
163 static void PaletteReset(void);
164 static int PaletteInsert(uint32_t rgb, int numPixels, int bpp);
166 static void Pack24(rfbClientPtr cl, char *buf, rfbPixelFormat *fmt, int count);
168 static void EncodeIndexedRect16(uint8_t *buf, int count);
169 static void EncodeIndexedRect32(uint8_t *buf, int count);
171 static void EncodeMonoRect8(uint8_t *buf, int w, int h);
172 static void EncodeMonoRect16(uint8_t *buf, int w, int h);
173 static void EncodeMonoRect32(uint8_t *buf, int w, int h);
175 static void FilterGradient24(rfbClientPtr cl, char *buf, rfbPixelFormat *fmt, int w, int h);
176 static void FilterGradient16(rfbClientPtr cl, uint16_t *buf, rfbPixelFormat *fmt, int w, int h);
177 static void FilterGradient32(rfbClientPtr cl, uint32_t *buf, rfbPixelFormat *fmt, int w, int h);
179 static int DetectSmoothImage(rfbClientPtr cl, rfbPixelFormat *fmt, int w, int h);
180 static unsigned long DetectSmoothImage24(rfbClientPtr cl, rfbPixelFormat *fmt, int w, int h);
181 static unsigned long DetectSmoothImage16(rfbClientPtr cl, rfbPixelFormat *fmt, int w, int h);
182 static unsigned long DetectSmoothImage32(rfbClientPtr cl, rfbPixelFormat *fmt, int w, int h);
184 static rfbBool SendJpegRect(rfbClientPtr cl, int x, int y, int w, int h,
186 static void PrepareRowForJpeg(rfbClientPtr cl, uint8_t *dst, int x, int y, int count);
187 static void PrepareRowForJpeg24(rfbClientPtr cl, uint8_t *dst, int x, int y, int count);
188 static void PrepareRowForJpeg16(rfbClientPtr cl, uint8_t *dst, int x, int y, int count);
189 static void PrepareRowForJpeg32(rfbClientPtr cl, uint8_t *dst, int x, int y, int count);
191 static void JpegInitDestination(j_compress_ptr cinfo);
192 static boolean JpegEmptyOutputBuffer(j_compress_ptr cinfo);
193 static void JpegTermDestination(j_compress_ptr cinfo);
194 static void JpegSetDstManager(j_compress_ptr cinfo);
198 * Tight encoding implementation.
202 rfbNumCodedRectsTight(rfbClientPtr cl,
208 int maxRectSize, maxRectWidth;
209 int subrectMaxWidth, subrectMaxHeight;
211 /* No matter how many rectangles we will send if LastRect markers
212 are used to terminate rectangle stream. */
213 if (cl->enableLastRectEncoding && w * h >= MIN_SPLIT_RECT_SIZE)
216 maxRectSize = tightConf[cl->tightCompressLevel].maxRectSize;
217 maxRectWidth = tightConf[cl->tightCompressLevel].maxRectWidth;
219 if (w > maxRectWidth || w * h > maxRectSize) {
220 subrectMaxWidth = (w > maxRectWidth) ? maxRectWidth : w;
221 subrectMaxHeight = maxRectSize / subrectMaxWidth;
222 return (((w - 1) / maxRectWidth + 1) *
223 ((h - 1) / subrectMaxHeight + 1));
230 rfbSendRectEncodingTight(rfbClientPtr cl,
239 int x_best, y_best, w_best, h_best;
242 rfbSendUpdateBuf(cl);
244 compressLevel = cl->tightCompressLevel;
245 qualityLevel = cl->tightQualityLevel;
247 if ( cl->format.depth == 24 && cl->format.redMax == 0xFF &&
248 cl->format.greenMax == 0xFF && cl->format.blueMax == 0xFF ) {
249 usePixelFormat24 = TRUE;
251 usePixelFormat24 = FALSE;
254 if (!cl->enableLastRectEncoding || w * h < MIN_SPLIT_RECT_SIZE)
255 return SendRectSimple(cl, x, y, w, h);
257 /* Make sure we can write at least one pixel into tightBeforeBuf. */
259 if (tightBeforeBufSize < 4) {
260 tightBeforeBufSize = 4;
261 if (tightBeforeBuf == NULL)
262 tightBeforeBuf = (char *)malloc(tightBeforeBufSize);
264 tightBeforeBuf = (char *)realloc(tightBeforeBuf,
268 /* Calculate maximum number of rows in one non-solid rectangle. */
271 int maxRectSize, maxRectWidth, nMaxWidth;
273 maxRectSize = tightConf[compressLevel].maxRectSize;
274 maxRectWidth = tightConf[compressLevel].maxRectWidth;
275 nMaxWidth = (w > maxRectWidth) ? maxRectWidth : w;
276 nMaxRows = maxRectSize / nMaxWidth;
279 /* Try to find large solid-color areas and send them separately. */
281 for (dy = y; dy < y + h; dy += MAX_SPLIT_TILE_SIZE) {
283 /* If a rectangle becomes too large, send its upper part now. */
285 if (dy - y >= nMaxRows) {
286 if (!SendRectSimple(cl, x, y, w, nMaxRows))
292 dh = (dy + MAX_SPLIT_TILE_SIZE <= y + h) ?
293 MAX_SPLIT_TILE_SIZE : (y + h - dy);
295 for (dx = x; dx < x + w; dx += MAX_SPLIT_TILE_SIZE) {
297 dw = (dx + MAX_SPLIT_TILE_SIZE <= x + w) ?
298 MAX_SPLIT_TILE_SIZE : (x + w - dx);
300 if (CheckSolidTile(cl, dx, dy, dw, dh, &colorValue, FALSE)) {
302 /* Get dimensions of solid-color area. */
304 FindBestSolidArea(cl, dx, dy, w - (dx - x), h - (dy - y),
305 colorValue, &w_best, &h_best);
307 /* Make sure a solid rectangle is large enough
308 (or the whole rectangle is of the same color). */
310 if ( w_best * h_best != w * h &&
311 w_best * h_best < MIN_SOLID_SUBRECT_SIZE )
314 /* Try to extend solid rectangle to maximum size. */
316 x_best = dx; y_best = dy;
317 ExtendSolidArea(cl, x, y, w, h, colorValue,
318 &x_best, &y_best, &w_best, &h_best);
320 /* Send rectangles at top and left to solid-color area. */
323 !SendRectSimple(cl, x, y, w, y_best-y) )
326 !rfbSendRectEncodingTight(cl, x, y_best,
330 /* Send solid-color rectangle. */
332 if (!SendTightHeader(cl, x_best, y_best, w_best, h_best))
335 fbptr = (cl->scaledScreen->frameBuffer +
336 (cl->scaledScreen->paddedWidthInBytes * y_best) +
337 (x_best * (cl->scaledScreen->bitsPerPixel / 8)));
339 (*cl->translateFn)(cl->translateLookupTable, &cl->screen->serverFormat,
340 &cl->format, fbptr, tightBeforeBuf,
341 cl->scaledScreen->paddedWidthInBytes, 1, 1);
343 if (!SendSolidRect(cl))
346 /* Send remaining rectangles (at right and bottom). */
348 if ( x_best + w_best != x + w &&
349 !rfbSendRectEncodingTight(cl, x_best+w_best, y_best,
350 w-(x_best-x)-w_best, h_best) )
352 if ( y_best + h_best != y + h &&
353 !rfbSendRectEncodingTight(cl, x, y_best+h_best,
354 w, h-(y_best-y)-h_best) )
357 /* Return after all recursive calls are done. */
366 /* No suitable solid-color rectangles found. */
368 return SendRectSimple(cl, x, y, w, h);
372 FindBestSolidArea(rfbClientPtr cl,
383 int w_best = 0, h_best = 0;
387 for (dy = y; dy < y + h; dy += MAX_SPLIT_TILE_SIZE) {
389 dh = (dy + MAX_SPLIT_TILE_SIZE <= y + h) ?
390 MAX_SPLIT_TILE_SIZE : (y + h - dy);
391 dw = (w_prev > MAX_SPLIT_TILE_SIZE) ?
392 MAX_SPLIT_TILE_SIZE : w_prev;
394 if (!CheckSolidTile(cl, x, dy, dw, dh, &colorValue, TRUE))
397 for (dx = x + dw; dx < x + w_prev;) {
398 dw = (dx + MAX_SPLIT_TILE_SIZE <= x + w_prev) ?
399 MAX_SPLIT_TILE_SIZE : (x + w_prev - dx);
400 if (!CheckSolidTile(cl, dx, dy, dw, dh, &colorValue, TRUE))
406 if (w_prev * (dy + dh - y) > w_best * h_best) {
408 h_best = dy + dh - y;
417 ExtendSolidArea(rfbClientPtr cl,
430 /* Try to extend the area upwards. */
431 for ( cy = *y_ptr - 1;
432 cy >= y && CheckSolidTile(cl, *x_ptr, cy, *w_ptr, 1, &colorValue, TRUE);
434 *h_ptr += *y_ptr - (cy + 1);
438 for ( cy = *y_ptr + *h_ptr;
440 CheckSolidTile(cl, *x_ptr, cy, *w_ptr, 1, &colorValue, TRUE);
442 *h_ptr += cy - (*y_ptr + *h_ptr);
444 /* ... to the left. */
445 for ( cx = *x_ptr - 1;
446 cx >= x && CheckSolidTile(cl, cx, *y_ptr, 1, *h_ptr, &colorValue, TRUE);
448 *w_ptr += *x_ptr - (cx + 1);
451 /* ... to the right. */
452 for ( cx = *x_ptr + *w_ptr;
454 CheckSolidTile(cl, cx, *y_ptr, 1, *h_ptr, &colorValue, TRUE);
456 *w_ptr += cx - (*x_ptr + *w_ptr);
460 * Check if a rectangle is all of the same color. If needSameColor is
461 * set to non-zero, then also check that its color equals to the
462 * *colorPtr value. The result is 1 if the test is successfull, and in
463 * that case new color will be stored in *colorPtr.
466 static rfbBool CheckSolidTile(rfbClientPtr cl, int x, int y, int w, int h, uint32_t* colorPtr, rfbBool needSameColor)
468 switch(cl->screen->serverFormat.bitsPerPixel) {
470 return CheckSolidTile32(cl, x, y, w, h, colorPtr, needSameColor);
472 return CheckSolidTile16(cl, x, y, w, h, colorPtr, needSameColor);
474 return CheckSolidTile8(cl, x, y, w, h, colorPtr, needSameColor);
478 #define DEFINE_CHECK_SOLID_FUNCTION(bpp) \
481 CheckSolidTile##bpp(rfbClientPtr cl, int x, int y, int w, int h, \
482 uint32_t* colorPtr, rfbBool needSameColor) \
484 uint##bpp##_t *fbptr; \
485 uint##bpp##_t colorValue; \
488 fbptr = (uint##bpp##_t *) \
489 &cl->scaledScreen->frameBuffer[y * cl->scaledScreen->paddedWidthInBytes + x * (bpp/8)]; \
491 colorValue = *fbptr; \
492 if (needSameColor && (uint32_t)colorValue != *colorPtr) \
495 for (dy = 0; dy < h; dy++) { \
496 for (dx = 0; dx < w; dx++) { \
497 if (colorValue != fbptr[dx]) \
500 fbptr = (uint##bpp##_t *)((uint8_t *)fbptr + cl->scaledScreen->paddedWidthInBytes); \
503 *colorPtr = (uint32_t)colorValue; \
507 DEFINE_CHECK_SOLID_FUNCTION(8)
508 DEFINE_CHECK_SOLID_FUNCTION(16)
509 DEFINE_CHECK_SOLID_FUNCTION(32)
512 SendRectSimple(rfbClientPtr cl, int x, int y, int w, int h)
514 int maxBeforeSize, maxAfterSize;
515 int maxRectSize, maxRectWidth;
516 int subrectMaxWidth, subrectMaxHeight;
520 maxRectSize = tightConf[compressLevel].maxRectSize;
521 maxRectWidth = tightConf[compressLevel].maxRectWidth;
523 maxBeforeSize = maxRectSize * (cl->format.bitsPerPixel / 8);
524 maxAfterSize = maxBeforeSize + (maxBeforeSize + 99) / 100 + 12;
526 if (tightBeforeBufSize < maxBeforeSize) {
527 tightBeforeBufSize = maxBeforeSize;
528 if (tightBeforeBuf == NULL)
529 tightBeforeBuf = (char *)malloc(tightBeforeBufSize);
531 tightBeforeBuf = (char *)realloc(tightBeforeBuf,
535 if (tightAfterBufSize < maxAfterSize) {
536 tightAfterBufSize = maxAfterSize;
537 if (tightAfterBuf == NULL)
538 tightAfterBuf = (char *)malloc(tightAfterBufSize);
540 tightAfterBuf = (char *)realloc(tightAfterBuf,
544 if (w > maxRectWidth || w * h > maxRectSize) {
545 subrectMaxWidth = (w > maxRectWidth) ? maxRectWidth : w;
546 subrectMaxHeight = maxRectSize / subrectMaxWidth;
548 for (dy = 0; dy < h; dy += subrectMaxHeight) {
549 for (dx = 0; dx < w; dx += maxRectWidth) {
550 rw = (dx + maxRectWidth < w) ? maxRectWidth : w - dx;
551 rh = (dy + subrectMaxHeight < h) ? subrectMaxHeight : h - dy;
552 if (!SendSubrect(cl, x+dx, y+dy, rw, rh))
557 if (!SendSubrect(cl, x, y, w, h))
565 SendSubrect(rfbClientPtr cl,
572 rfbBool success = FALSE;
574 /* Send pending data if there is more than 128 bytes. */
575 if (cl->ublen > 128) {
576 if (!rfbSendUpdateBuf(cl))
580 if (!SendTightHeader(cl, x, y, w, h))
583 fbptr = (cl->scaledScreen->frameBuffer + (cl->scaledScreen->paddedWidthInBytes * y)
584 + (x * (cl->scaledScreen->bitsPerPixel / 8)));
586 (*cl->translateFn)(cl->translateLookupTable, &cl->screen->serverFormat,
587 &cl->format, fbptr, tightBeforeBuf,
588 cl->scaledScreen->paddedWidthInBytes, w, h);
590 paletteMaxColors = w * h / tightConf[compressLevel].idxMaxColorsDivisor;
591 if ( paletteMaxColors < 2 &&
592 w * h >= tightConf[compressLevel].monoMinRectSize ) {
593 paletteMaxColors = 2;
595 switch (cl->format.bitsPerPixel) {
600 FillPalette16(w * h);
603 FillPalette32(w * h);
606 switch (paletteNumColors) {
608 /* Truecolor image */
609 if (DetectSmoothImage(cl, &cl->format, w, h)) {
610 if (qualityLevel != -1) {
611 success = SendJpegRect(cl, x, y, w, h,
612 tightConf[qualityLevel].jpegQuality);
614 success = SendGradientRect(cl, w, h);
617 success = SendFullColorRect(cl, w, h);
621 /* Solid rectangle */
622 success = SendSolidRect(cl);
625 /* Two-color rectangle */
626 success = SendMonoRect(cl, w, h);
629 /* Up to 256 different colors */
630 if ( paletteNumColors > 96 &&
631 qualityLevel != -1 && qualityLevel <= 3 &&
632 DetectSmoothImage(cl, &cl->format, w, h) ) {
633 success = SendJpegRect(cl, x, y, w, h,
634 tightConf[qualityLevel].jpegQuality);
636 success = SendIndexedRect(cl, w, h);
643 SendTightHeader(rfbClientPtr cl,
649 rfbFramebufferUpdateRectHeader rect;
651 if (cl->ublen + sz_rfbFramebufferUpdateRectHeader > UPDATE_BUF_SIZE) {
652 if (!rfbSendUpdateBuf(cl))
656 rect.r.x = Swap16IfLE(x);
657 rect.r.y = Swap16IfLE(y);
658 rect.r.w = Swap16IfLE(w);
659 rect.r.h = Swap16IfLE(h);
660 rect.encoding = Swap32IfLE(rfbEncodingTight);
662 memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,
663 sz_rfbFramebufferUpdateRectHeader);
664 cl->ublen += sz_rfbFramebufferUpdateRectHeader;
666 rfbStatRecordEncodingSent(cl, rfbEncodingTight, sz_rfbFramebufferUpdateRectHeader,
667 sz_rfbFramebufferUpdateRectHeader + w * (cl->format.bitsPerPixel / 8) * h);
673 * Subencoding implementations.
677 SendSolidRect(rfbClientPtr cl)
681 if (usePixelFormat24) {
682 Pack24(cl, tightBeforeBuf, &cl->format, 1);
685 len = cl->format.bitsPerPixel / 8;
687 if (cl->ublen + 1 + len > UPDATE_BUF_SIZE) {
688 if (!rfbSendUpdateBuf(cl))
692 cl->updateBuf[cl->ublen++] = (char)(rfbTightFill << 4);
693 memcpy (&cl->updateBuf[cl->ublen], tightBeforeBuf, len);
696 rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, len+1);
702 SendMonoRect(rfbClientPtr cl,
707 int paletteLen, dataLen;
709 if ( cl->ublen + TIGHT_MIN_TO_COMPRESS + 6 +
710 2 * cl->format.bitsPerPixel / 8 > UPDATE_BUF_SIZE ) {
711 if (!rfbSendUpdateBuf(cl))
715 /* Prepare tight encoding header. */
716 dataLen = (w + 7) / 8;
719 cl->updateBuf[cl->ublen++] = (streamId | rfbTightExplicitFilter) << 4;
720 cl->updateBuf[cl->ublen++] = rfbTightFilterPalette;
721 cl->updateBuf[cl->ublen++] = 1;
723 /* Prepare palette, convert image. */
724 switch (cl->format.bitsPerPixel) {
727 EncodeMonoRect32((uint8_t *)tightBeforeBuf, w, h);
729 ((uint32_t *)tightAfterBuf)[0] = monoBackground;
730 ((uint32_t *)tightAfterBuf)[1] = monoForeground;
731 if (usePixelFormat24) {
732 Pack24(cl, tightAfterBuf, &cl->format, 2);
737 memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf, paletteLen);
738 cl->ublen += paletteLen;
739 rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 3 + paletteLen);
743 EncodeMonoRect16((uint8_t *)tightBeforeBuf, w, h);
745 ((uint16_t *)tightAfterBuf)[0] = (uint16_t)monoBackground;
746 ((uint16_t *)tightAfterBuf)[1] = (uint16_t)monoForeground;
748 memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf, 4);
750 rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 7);
754 EncodeMonoRect8((uint8_t *)tightBeforeBuf, w, h);
756 cl->updateBuf[cl->ublen++] = (char)monoBackground;
757 cl->updateBuf[cl->ublen++] = (char)monoForeground;
758 rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 5);
761 return CompressData(cl, streamId, dataLen,
762 tightConf[compressLevel].monoZlibLevel,
767 SendIndexedRect(rfbClientPtr cl,
774 if ( cl->ublen + TIGHT_MIN_TO_COMPRESS + 6 +
775 paletteNumColors * cl->format.bitsPerPixel / 8 >
777 if (!rfbSendUpdateBuf(cl))
781 /* Prepare tight encoding header. */
782 cl->updateBuf[cl->ublen++] = (streamId | rfbTightExplicitFilter) << 4;
783 cl->updateBuf[cl->ublen++] = rfbTightFilterPalette;
784 cl->updateBuf[cl->ublen++] = (char)(paletteNumColors - 1);
786 /* Prepare palette, convert image. */
787 switch (cl->format.bitsPerPixel) {
790 EncodeIndexedRect32((uint8_t *)tightBeforeBuf, w * h);
792 for (i = 0; i < paletteNumColors; i++) {
793 ((uint32_t *)tightAfterBuf)[i] =
794 palette.entry[i].listNode->rgb;
796 if (usePixelFormat24) {
797 Pack24(cl, tightAfterBuf, &cl->format, paletteNumColors);
802 memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf, paletteNumColors * entryLen);
803 cl->ublen += paletteNumColors * entryLen;
804 rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 3 + paletteNumColors * entryLen);
808 EncodeIndexedRect16((uint8_t *)tightBeforeBuf, w * h);
810 for (i = 0; i < paletteNumColors; i++) {
811 ((uint16_t *)tightAfterBuf)[i] =
812 (uint16_t)palette.entry[i].listNode->rgb;
815 memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf, paletteNumColors * 2);
816 cl->ublen += paletteNumColors * 2;
817 rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 3 + paletteNumColors * 2);
821 return FALSE; /* Should never happen. */
824 return CompressData(cl, streamId, w * h,
825 tightConf[compressLevel].idxZlibLevel,
830 SendFullColorRect(rfbClientPtr cl,
837 if (cl->ublen + TIGHT_MIN_TO_COMPRESS + 1 > UPDATE_BUF_SIZE) {
838 if (!rfbSendUpdateBuf(cl))
842 cl->updateBuf[cl->ublen++] = 0x00; /* stream id = 0, no flushing, no filter */
843 rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 1);
845 if (usePixelFormat24) {
846 Pack24(cl, tightBeforeBuf, &cl->format, w * h);
849 len = cl->format.bitsPerPixel / 8;
851 return CompressData(cl, streamId, w * h * len,
852 tightConf[compressLevel].rawZlibLevel,
857 SendGradientRect(rfbClientPtr cl,
864 if (cl->format.bitsPerPixel == 8)
865 return SendFullColorRect(cl, w, h);
867 if (cl->ublen + TIGHT_MIN_TO_COMPRESS + 2 > UPDATE_BUF_SIZE) {
868 if (!rfbSendUpdateBuf(cl))
872 if (prevRowBuf == NULL)
873 prevRowBuf = (int *)malloc(2048 * 3 * sizeof(int));
875 cl->updateBuf[cl->ublen++] = (streamId | rfbTightExplicitFilter) << 4;
876 cl->updateBuf[cl->ublen++] = rfbTightFilterGradient;
877 rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 2);
879 if (usePixelFormat24) {
880 FilterGradient24(cl, tightBeforeBuf, &cl->format, w, h);
882 } else if (cl->format.bitsPerPixel == 32) {
883 FilterGradient32(cl, (uint32_t *)tightBeforeBuf, &cl->format, w, h);
886 FilterGradient16(cl, (uint16_t *)tightBeforeBuf, &cl->format, w, h);
890 return CompressData(cl, streamId, w * h * len,
891 tightConf[compressLevel].gradientZlibLevel,
896 CompressData(rfbClientPtr cl,
905 if (dataLen < TIGHT_MIN_TO_COMPRESS) {
906 memcpy(&cl->updateBuf[cl->ublen], tightBeforeBuf, dataLen);
907 cl->ublen += dataLen;
908 rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, dataLen);
912 pz = &cl->zsStruct[streamId];
914 /* Initialize compression stream if needed. */
915 if (!cl->zsActive[streamId]) {
920 err = deflateInit2 (pz, zlibLevel, Z_DEFLATED, MAX_WBITS,
921 MAX_MEM_LEVEL, zlibStrategy);
925 cl->zsActive[streamId] = TRUE;
926 cl->zsLevel[streamId] = zlibLevel;
929 /* Prepare buffer pointers. */
930 pz->next_in = (Bytef *)tightBeforeBuf;
931 pz->avail_in = dataLen;
932 pz->next_out = (Bytef *)tightAfterBuf;
933 pz->avail_out = tightAfterBufSize;
935 /* Change compression parameters if needed. */
936 if (zlibLevel != cl->zsLevel[streamId]) {
937 if (deflateParams (pz, zlibLevel, zlibStrategy) != Z_OK) {
940 cl->zsLevel[streamId] = zlibLevel;
943 /* Actual compression. */
944 if ( deflate (pz, Z_SYNC_FLUSH) != Z_OK ||
945 pz->avail_in != 0 || pz->avail_out == 0 ) {
949 return SendCompressedData(cl, tightAfterBufSize - pz->avail_out);
952 static rfbBool SendCompressedData(rfbClientPtr cl,
957 cl->updateBuf[cl->ublen++] = compressedLen & 0x7F;
958 rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 1);
959 if (compressedLen > 0x7F) {
960 cl->updateBuf[cl->ublen-1] |= 0x80;
961 cl->updateBuf[cl->ublen++] = compressedLen >> 7 & 0x7F;
962 rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 1);
963 if (compressedLen > 0x3FFF) {
964 cl->updateBuf[cl->ublen-1] |= 0x80;
965 cl->updateBuf[cl->ublen++] = compressedLen >> 14 & 0xFF;
966 rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 1);
970 portionLen = UPDATE_BUF_SIZE;
971 for (i = 0; i < compressedLen; i += portionLen) {
972 if (i + portionLen > compressedLen) {
973 portionLen = compressedLen - i;
975 if (cl->ublen + portionLen > UPDATE_BUF_SIZE) {
976 if (!rfbSendUpdateBuf(cl))
979 memcpy(&cl->updateBuf[cl->ublen], &tightAfterBuf[i], portionLen);
980 cl->ublen += portionLen;
982 rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, compressedLen);
988 * Code to determine how many different colors used in rectangle.
992 FillPalette8(int count)
994 uint8_t *data = (uint8_t *)tightBeforeBuf;
998 paletteNumColors = 0;
1001 for (i = 1; i < count && data[i] == c0; i++);
1003 paletteNumColors = 1;
1004 return; /* Solid rectangle */
1007 if (paletteMaxColors < 2)
1013 for (i++; i < count; i++) {
1014 if (data[i] == c0) {
1016 } else if (data[i] == c1) {
1023 monoBackground = (uint32_t)c0;
1024 monoForeground = (uint32_t)c1;
1026 monoBackground = (uint32_t)c1;
1027 monoForeground = (uint32_t)c0;
1029 paletteNumColors = 2; /* Two colors */
1033 #define DEFINE_FILL_PALETTE_FUNCTION(bpp) \
1036 FillPalette##bpp(int count) { \
1037 uint##bpp##_t *data = (uint##bpp##_t *)tightBeforeBuf; \
1038 uint##bpp##_t c0, c1, ci; \
1039 int i, n0, n1, ni; \
1042 for (i = 1; i < count && data[i] == c0; i++); \
1044 paletteNumColors = 1; /* Solid rectangle */ \
1048 if (paletteMaxColors < 2) { \
1049 paletteNumColors = 0; /* Full-color encoding preferred */ \
1056 for (i++; i < count; i++) { \
1060 } else if (ci == c1) { \
1067 monoBackground = (uint32_t)c0; \
1068 monoForeground = (uint32_t)c1; \
1070 monoBackground = (uint32_t)c1; \
1071 monoForeground = (uint32_t)c0; \
1073 paletteNumColors = 2; /* Two colors */ \
1078 PaletteInsert (c0, (uint32_t)n0, bpp); \
1079 PaletteInsert (c1, (uint32_t)n1, bpp); \
1082 for (i++; i < count; i++) { \
1083 if (data[i] == ci) { \
1086 if (!PaletteInsert (ci, (uint32_t)ni, bpp)) \
1092 PaletteInsert (ci, (uint32_t)ni, bpp); \
1095 DEFINE_FILL_PALETTE_FUNCTION(16)
1096 DEFINE_FILL_PALETTE_FUNCTION(32)
1100 * Functions to operate with palette structures.
1103 #define HASH_FUNC16(rgb) ((int)(((rgb >> 8) + rgb) & 0xFF))
1104 #define HASH_FUNC32(rgb) ((int)(((rgb >> 16) + (rgb >> 8)) & 0xFF))
1109 paletteNumColors = 0;
1110 memset(palette.hash, 0, 256 * sizeof(COLOR_LIST *));
1114 PaletteInsert(uint32_t rgb,
1119 COLOR_LIST *prev_pnode = NULL;
1120 int hash_key, idx, new_idx, count;
1122 hash_key = (bpp == 16) ? HASH_FUNC16(rgb) : HASH_FUNC32(rgb);
1124 pnode = palette.hash[hash_key];
1126 while (pnode != NULL) {
1127 if (pnode->rgb == rgb) {
1128 /* Such palette entry already exists. */
1129 new_idx = idx = pnode->idx;
1130 count = palette.entry[idx].numPixels + numPixels;
1131 if (new_idx && palette.entry[new_idx-1].numPixels < count) {
1133 palette.entry[new_idx] = palette.entry[new_idx-1];
1134 palette.entry[new_idx].listNode->idx = new_idx;
1137 while (new_idx && palette.entry[new_idx-1].numPixels < count);
1138 palette.entry[new_idx].listNode = pnode;
1139 pnode->idx = new_idx;
1141 palette.entry[new_idx].numPixels = count;
1142 return paletteNumColors;
1145 pnode = pnode->next;
1148 /* Check if palette is full. */
1149 if (paletteNumColors == 256 || paletteNumColors == paletteMaxColors) {
1150 paletteNumColors = 0;
1154 /* Move palette entries with lesser pixel counts. */
1155 for ( idx = paletteNumColors;
1156 idx > 0 && palette.entry[idx-1].numPixels < numPixels;
1158 palette.entry[idx] = palette.entry[idx-1];
1159 palette.entry[idx].listNode->idx = idx;
1162 /* Add new palette entry into the freed slot. */
1163 pnode = &palette.list[paletteNumColors];
1164 if (prev_pnode != NULL) {
1165 prev_pnode->next = pnode;
1167 palette.hash[hash_key] = pnode;
1172 palette.entry[idx].listNode = pnode;
1173 palette.entry[idx].numPixels = numPixels;
1175 return (++paletteNumColors);
1180 * Converting 32-bit color samples into 24-bit colors.
1181 * Should be called only when redMax, greenMax and blueMax are 255.
1182 * Color components assumed to be byte-aligned.
1185 static void Pack24(rfbClientPtr cl,
1187 rfbPixelFormat *fmt,
1192 int r_shift, g_shift, b_shift;
1194 buf32 = (uint32_t *)buf;
1196 if (!cl->screen->serverFormat.bigEndian == !fmt->bigEndian) {
1197 r_shift = fmt->redShift;
1198 g_shift = fmt->greenShift;
1199 b_shift = fmt->blueShift;
1201 r_shift = 24 - fmt->redShift;
1202 g_shift = 24 - fmt->greenShift;
1203 b_shift = 24 - fmt->blueShift;
1208 *buf++ = (char)(pix >> r_shift);
1209 *buf++ = (char)(pix >> g_shift);
1210 *buf++ = (char)(pix >> b_shift);
1216 * Converting truecolor samples into palette indices.
1219 #define DEFINE_IDX_ENCODE_FUNCTION(bpp) \
1222 EncodeIndexedRect##bpp(uint8_t *buf, int count) { \
1223 COLOR_LIST *pnode; \
1224 uint##bpp##_t *src; \
1225 uint##bpp##_t rgb; \
1228 src = (uint##bpp##_t *) buf; \
1232 while (count && *src == rgb) { \
1233 rep++, src++, count--; \
1235 pnode = palette.hash[HASH_FUNC##bpp(rgb)]; \
1236 while (pnode != NULL) { \
1237 if ((uint##bpp##_t)pnode->rgb == rgb) { \
1238 *buf++ = (uint8_t)pnode->idx; \
1240 *buf++ = (uint8_t)pnode->idx; \
1245 pnode = pnode->next; \
1250 DEFINE_IDX_ENCODE_FUNCTION(16)
1251 DEFINE_IDX_ENCODE_FUNCTION(32)
1253 #define DEFINE_MONO_ENCODE_FUNCTION(bpp) \
1256 EncodeMonoRect##bpp(uint8_t *buf, int w, int h) { \
1257 uint##bpp##_t *ptr; \
1259 unsigned int value, mask; \
1260 int aligned_width; \
1261 int x, y, bg_bits; \
1263 ptr = (uint##bpp##_t *) buf; \
1264 bg = (uint##bpp##_t) monoBackground; \
1265 aligned_width = w - w % 8; \
1267 for (y = 0; y < h; y++) { \
1268 for (x = 0; x < aligned_width; x += 8) { \
1269 for (bg_bits = 0; bg_bits < 8; bg_bits++) { \
1273 if (bg_bits == 8) { \
1277 mask = 0x80 >> bg_bits; \
1279 for (bg_bits++; bg_bits < 8; bg_bits++) { \
1281 if (*ptr++ != bg) { \
1285 *buf++ = (uint8_t)value; \
1293 for (; x < w; x++) { \
1294 if (*ptr++ != bg) { \
1299 *buf++ = (uint8_t)value; \
1303 DEFINE_MONO_ENCODE_FUNCTION(8)
1304 DEFINE_MONO_ENCODE_FUNCTION(16)
1305 DEFINE_MONO_ENCODE_FUNCTION(32)
1309 * ``Gradient'' filter for 24-bit color samples.
1310 * Should be called only when redMax, greenMax and blueMax are 255.
1311 * Color components assumed to be byte-aligned.
1315 FilterGradient24(rfbClientPtr cl, char *buf, rfbPixelFormat *fmt, int w, int h)
1321 int pixHere[3], pixUpper[3], pixLeft[3], pixUpperLeft[3];
1325 buf32 = (uint32_t *)buf;
1326 memset (prevRowBuf, 0, w * 3 * sizeof(int));
1328 if (!cl->screen->serverFormat.bigEndian == !fmt->bigEndian) {
1329 shiftBits[0] = fmt->redShift;
1330 shiftBits[1] = fmt->greenShift;
1331 shiftBits[2] = fmt->blueShift;
1333 shiftBits[0] = 24 - fmt->redShift;
1334 shiftBits[1] = 24 - fmt->greenShift;
1335 shiftBits[2] = 24 - fmt->blueShift;
1338 for (y = 0; y < h; y++) {
1339 for (c = 0; c < 3; c++) {
1343 prevRowPtr = prevRowBuf;
1344 for (x = 0; x < w; x++) {
1346 for (c = 0; c < 3; c++) {
1347 pixUpperLeft[c] = pixUpper[c];
1348 pixLeft[c] = pixHere[c];
1349 pixUpper[c] = *prevRowPtr;
1350 pixHere[c] = (int)(pix32 >> shiftBits[c] & 0xFF);
1351 *prevRowPtr++ = pixHere[c];
1353 prediction = pixLeft[c] + pixUpper[c] - pixUpperLeft[c];
1354 if (prediction < 0) {
1356 } else if (prediction > 0xFF) {
1359 *buf++ = (char)(pixHere[c] - prediction);
1367 * ``Gradient'' filter for other color depths.
1370 #define DEFINE_GRADIENT_FILTER_FUNCTION(bpp) \
1373 FilterGradient##bpp(rfbClientPtr cl, uint##bpp##_t *buf, \
1374 rfbPixelFormat *fmt, int w, int h) { \
1375 uint##bpp##_t pix, diff; \
1376 rfbBool endianMismatch; \
1378 int maxColor[3], shiftBits[3]; \
1379 int pixHere[3], pixUpper[3], pixLeft[3], pixUpperLeft[3]; \
1383 memset (prevRowBuf, 0, w * 3 * sizeof(int)); \
1385 endianMismatch = (!cl->screen->serverFormat.bigEndian != !fmt->bigEndian); \
1387 maxColor[0] = fmt->redMax; \
1388 maxColor[1] = fmt->greenMax; \
1389 maxColor[2] = fmt->blueMax; \
1390 shiftBits[0] = fmt->redShift; \
1391 shiftBits[1] = fmt->greenShift; \
1392 shiftBits[2] = fmt->blueShift; \
1394 for (y = 0; y < h; y++) { \
1395 for (c = 0; c < 3; c++) { \
1399 prevRowPtr = prevRowBuf; \
1400 for (x = 0; x < w; x++) { \
1402 if (endianMismatch) { \
1403 pix = Swap##bpp(pix); \
1406 for (c = 0; c < 3; c++) { \
1407 pixUpperLeft[c] = pixUpper[c]; \
1408 pixLeft[c] = pixHere[c]; \
1409 pixUpper[c] = *prevRowPtr; \
1410 pixHere[c] = (int)(pix >> shiftBits[c] & maxColor[c]); \
1411 *prevRowPtr++ = pixHere[c]; \
1413 prediction = pixLeft[c] + pixUpper[c] - pixUpperLeft[c]; \
1414 if (prediction < 0) { \
1416 } else if (prediction > maxColor[c]) { \
1417 prediction = maxColor[c]; \
1419 diff |= ((pixHere[c] - prediction) & maxColor[c]) \
1422 if (endianMismatch) { \
1423 diff = Swap##bpp(diff); \
1430 DEFINE_GRADIENT_FILTER_FUNCTION(16)
1431 DEFINE_GRADIENT_FILTER_FUNCTION(32)
1435 * Code to guess if given rectangle is suitable for smooth image
1436 * compression (by applying "gradient" filter or JPEG coder).
1439 #define JPEG_MIN_RECT_SIZE 4096
1441 #define DETECT_SUBROW_WIDTH 7
1442 #define DETECT_MIN_WIDTH 8
1443 #define DETECT_MIN_HEIGHT 8
1446 DetectSmoothImage (rfbClientPtr cl, rfbPixelFormat *fmt, int w, int h)
1450 if ( cl->screen->serverFormat.bitsPerPixel == 8 || fmt->bitsPerPixel == 8 ||
1451 w < DETECT_MIN_WIDTH || h < DETECT_MIN_HEIGHT ) {
1455 if (qualityLevel != -1) {
1456 if (w * h < JPEG_MIN_RECT_SIZE) {
1460 if ( rfbTightDisableGradient ||
1461 w * h < tightConf[compressLevel].gradientMinRectSize ) {
1466 if (fmt->bitsPerPixel == 32) {
1467 if (usePixelFormat24) {
1468 avgError = DetectSmoothImage24(cl, fmt, w, h);
1469 if (qualityLevel != -1) {
1470 return (avgError < tightConf[qualityLevel].jpegThreshold24);
1472 return (avgError < tightConf[compressLevel].gradientThreshold24);
1474 avgError = DetectSmoothImage32(cl, fmt, w, h);
1477 avgError = DetectSmoothImage16(cl, fmt, w, h);
1479 if (qualityLevel != -1) {
1480 return (avgError < tightConf[qualityLevel].jpegThreshold);
1482 return (avgError < tightConf[compressLevel].gradientThreshold);
1485 static unsigned long
1486 DetectSmoothImage24 (rfbClientPtr cl,
1487 rfbPixelFormat *fmt,
1496 unsigned long avgError;
1498 /* If client is big-endian, color samples begin from the second
1499 byte (offset 1) of a 32-bit pixel value. */
1500 off = (fmt->bigEndian != 0);
1502 memset(diffStat, 0, 256*sizeof(int));
1505 while (y < h && x < w) {
1506 for (d = 0; d < h - y && d < w - x - DETECT_SUBROW_WIDTH; d++) {
1507 for (c = 0; c < 3; c++) {
1508 left[c] = (int)tightBeforeBuf[((y+d)*w+x+d)*4+off+c] & 0xFF;
1510 for (dx = 1; dx <= DETECT_SUBROW_WIDTH; dx++) {
1511 for (c = 0; c < 3; c++) {
1512 pix = (int)tightBeforeBuf[((y+d)*w+x+d+dx)*4+off+c] & 0xFF;
1513 diffStat[abs(pix - left[c])]++;
1528 if (diffStat[0] * 33 / pixelCount >= 95)
1532 for (c = 1; c < 8; c++) {
1533 avgError += (unsigned long)diffStat[c] * (unsigned long)(c * c);
1534 if (diffStat[c] == 0 || diffStat[c] > diffStat[c-1] * 2)
1537 for (; c < 256; c++) {
1538 avgError += (unsigned long)diffStat[c] * (unsigned long)(c * c);
1540 avgError /= (pixelCount * 3 - diffStat[0]);
1545 #define DEFINE_DETECT_FUNCTION(bpp) \
1547 static unsigned long \
1548 DetectSmoothImage##bpp (rfbClientPtr cl, rfbPixelFormat *fmt, int w, int h) {\
1549 rfbBool endianMismatch; \
1550 uint##bpp##_t pix; \
1551 int maxColor[3], shiftBits[3]; \
1552 int x, y, d, dx, c; \
1553 int diffStat[256]; \
1554 int pixelCount = 0; \
1555 int sample, sum, left[3]; \
1556 unsigned long avgError; \
1558 endianMismatch = (!cl->screen->serverFormat.bigEndian != !fmt->bigEndian); \
1560 maxColor[0] = fmt->redMax; \
1561 maxColor[1] = fmt->greenMax; \
1562 maxColor[2] = fmt->blueMax; \
1563 shiftBits[0] = fmt->redShift; \
1564 shiftBits[1] = fmt->greenShift; \
1565 shiftBits[2] = fmt->blueShift; \
1567 memset(diffStat, 0, 256*sizeof(int)); \
1570 while (y < h && x < w) { \
1571 for (d = 0; d < h - y && d < w - x - DETECT_SUBROW_WIDTH; d++) { \
1572 pix = ((uint##bpp##_t *)tightBeforeBuf)[(y+d)*w+x+d]; \
1573 if (endianMismatch) { \
1574 pix = Swap##bpp(pix); \
1576 for (c = 0; c < 3; c++) { \
1577 left[c] = (int)(pix >> shiftBits[c] & maxColor[c]); \
1579 for (dx = 1; dx <= DETECT_SUBROW_WIDTH; dx++) { \
1580 pix = ((uint##bpp##_t *)tightBeforeBuf)[(y+d)*w+x+d+dx]; \
1581 if (endianMismatch) { \
1582 pix = Swap##bpp(pix); \
1585 for (c = 0; c < 3; c++) { \
1586 sample = (int)(pix >> shiftBits[c] & maxColor[c]); \
1587 sum += abs(sample - left[c]); \
1605 if ((diffStat[0] + diffStat[1]) * 100 / pixelCount >= 90) \
1609 for (c = 1; c < 8; c++) { \
1610 avgError += (unsigned long)diffStat[c] * (unsigned long)(c * c); \
1611 if (diffStat[c] == 0 || diffStat[c] > diffStat[c-1] * 2) \
1614 for (; c < 256; c++) { \
1615 avgError += (unsigned long)diffStat[c] * (unsigned long)(c * c); \
1617 avgError /= (pixelCount - diffStat[0]); \
1622 DEFINE_DETECT_FUNCTION(16)
1623 DEFINE_DETECT_FUNCTION(32)
1627 * JPEG compression stuff.
1630 static struct jpeg_destination_mgr jpegDstManager;
1631 static rfbBool jpegError;
1632 static int jpegDstDataLen;
1635 SendJpegRect(rfbClientPtr cl, int x, int y, int w, int h, int quality)
1637 struct jpeg_compress_struct cinfo;
1638 struct jpeg_error_mgr jerr;
1640 JSAMPROW rowPointer[1];
1643 if (cl->screen->serverFormat.bitsPerPixel == 8)
1644 return SendFullColorRect(cl, w, h);
1646 srcBuf = (uint8_t *)malloc(w * 3);
1647 if (srcBuf == NULL) {
1648 return SendFullColorRect(cl, w, h);
1650 rowPointer[0] = srcBuf;
1652 cinfo.err = jpeg_std_error(&jerr);
1653 jpeg_create_compress(&cinfo);
1655 cinfo.image_width = w;
1656 cinfo.image_height = h;
1657 cinfo.input_components = 3;
1658 cinfo.in_color_space = JCS_RGB;
1660 jpeg_set_defaults(&cinfo);
1661 jpeg_set_quality(&cinfo, quality, TRUE);
1663 JpegSetDstManager (&cinfo);
1665 jpeg_start_compress(&cinfo, TRUE);
1667 for (dy = 0; dy < h; dy++) {
1668 PrepareRowForJpeg(cl, srcBuf, x, y + dy, w);
1669 jpeg_write_scanlines(&cinfo, rowPointer, 1);
1675 jpeg_finish_compress(&cinfo);
1677 jpeg_destroy_compress(&cinfo);
1681 return SendFullColorRect(cl, w, h);
1683 if (cl->ublen + TIGHT_MIN_TO_COMPRESS + 1 > UPDATE_BUF_SIZE) {
1684 if (!rfbSendUpdateBuf(cl))
1688 cl->updateBuf[cl->ublen++] = (char)(rfbTightJpeg << 4);
1689 rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 1);
1691 return SendCompressedData(cl, jpegDstDataLen);
1695 PrepareRowForJpeg(rfbClientPtr cl,
1701 if (cl->screen->serverFormat.bitsPerPixel == 32) {
1702 if ( cl->screen->serverFormat.redMax == 0xFF &&
1703 cl->screen->serverFormat.greenMax == 0xFF &&
1704 cl->screen->serverFormat.blueMax == 0xFF ) {
1705 PrepareRowForJpeg24(cl, dst, x, y, count);
1707 PrepareRowForJpeg32(cl, dst, x, y, count);
1710 /* 16 bpp assumed. */
1711 PrepareRowForJpeg16(cl, dst, x, y, count);
1716 PrepareRowForJpeg24(rfbClientPtr cl,
1725 fbptr = (uint32_t *)
1726 &cl->scaledScreen->frameBuffer[y * cl->scaledScreen->paddedWidthInBytes + x * 4];
1730 *dst++ = (uint8_t)(pix >> cl->screen->serverFormat.redShift);
1731 *dst++ = (uint8_t)(pix >> cl->screen->serverFormat.greenShift);
1732 *dst++ = (uint8_t)(pix >> cl->screen->serverFormat.blueShift);
1736 #define DEFINE_JPEG_GET_ROW_FUNCTION(bpp) \
1739 PrepareRowForJpeg##bpp(rfbClientPtr cl, uint8_t *dst, int x, int y, int count) { \
1740 uint##bpp##_t *fbptr; \
1741 uint##bpp##_t pix; \
1742 int inRed, inGreen, inBlue; \
1744 fbptr = (uint##bpp##_t *) \
1745 &cl->scaledScreen->frameBuffer[y * cl->scaledScreen->paddedWidthInBytes + \
1752 (pix >> cl->screen->serverFormat.redShift & cl->screen->serverFormat.redMax); \
1754 (pix >> cl->screen->serverFormat.greenShift & cl->screen->serverFormat.greenMax); \
1756 (pix >> cl->screen->serverFormat.blueShift & cl->screen->serverFormat.blueMax); \
1758 *dst++ = (uint8_t)((inRed * 255 + cl->screen->serverFormat.redMax / 2) / \
1759 cl->screen->serverFormat.redMax); \
1760 *dst++ = (uint8_t)((inGreen * 255 + cl->screen->serverFormat.greenMax / 2) / \
1761 cl->screen->serverFormat.greenMax); \
1762 *dst++ = (uint8_t)((inBlue * 255 + cl->screen->serverFormat.blueMax / 2) / \
1763 cl->screen->serverFormat.blueMax); \
1767 DEFINE_JPEG_GET_ROW_FUNCTION(16)
1768 DEFINE_JPEG_GET_ROW_FUNCTION(32)
1771 * Destination manager implementation for JPEG library.
1775 JpegInitDestination(j_compress_ptr cinfo)
1778 jpegDstManager.next_output_byte = (JOCTET *)tightAfterBuf;
1779 jpegDstManager.free_in_buffer = (size_t)tightAfterBufSize;
1783 JpegEmptyOutputBuffer(j_compress_ptr cinfo)
1786 jpegDstManager.next_output_byte = (JOCTET *)tightAfterBuf;
1787 jpegDstManager.free_in_buffer = (size_t)tightAfterBufSize;
1793 JpegTermDestination(j_compress_ptr cinfo)
1795 jpegDstDataLen = tightAfterBufSize - jpegDstManager.free_in_buffer;
1799 JpegSetDstManager(j_compress_ptr cinfo)
1801 jpegDstManager.init_destination = JpegInitDestination;
1802 jpegDstManager.empty_output_buffer = JpegEmptyOutputBuffer;
1803 jpegDstManager.term_destination = JpegTermDestination;
1804 cinfo->dest = &jpegDstManager;