4 * Routines to implement Tight Encoding
8 * Copyright 2000, 2001 Const Kaplinsky <const@ce.cctpu.edu.ru> All Rights Reserved.
9 * Copyright 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
27 /*#include <stdio.h>*/
33 #define NEEDFAR_POINTERS
38 /* Note: The following constant should not be changed. */
39 #define TIGHT_MIN_TO_COMPRESS 12
41 /* The parameters below may be adjusted. */
42 #define MIN_SPLIT_RECT_SIZE 4096
43 #define MIN_SOLID_SUBRECT_SIZE 2048
44 #define MAX_SPLIT_TILE_SIZE 16
46 /* May be set to TRUE with "-lazytight" Xvnc option. */
47 Bool rfbTightDisableGradient
= FALSE
;
49 /* This variable is set on every rfbSendRectEncodingTight() call. */
50 static Bool usePixelFormat24
;
53 /* Compression level stuff. The following array contains various
54 encoder parameters for each of 10 compression levels (0..9).
55 Last three parameters correspond to JPEG quality levels (0..9). */
57 typedef struct TIGHT_CONF_s
{
58 int maxRectSize
, maxRectWidth
;
59 int monoMinRectSize
, gradientMinRectSize
;
60 int idxZlibLevel
, monoZlibLevel
, rawZlibLevel
, gradientZlibLevel
;
61 int gradientThreshold
, gradientThreshold24
;
62 int idxMaxColorsDivisor
;
63 int jpegQuality
, jpegThreshold
, jpegThreshold24
;
66 static TIGHT_CONF tightConf
[10] = {
67 { 512, 32, 6, 65536, 0, 0, 0, 0, 0, 0, 4, 20, 10000, 23000 },
68 { 2048, 128, 6, 65536, 1, 1, 1, 0, 0, 0, 8, 30, 8000, 18000 },
69 { 6144, 256, 8, 65536, 3, 3, 2, 0, 0, 0, 24, 40, 6500, 15000 },
70 { 10240, 1024, 12, 65536, 5, 5, 3, 0, 0, 0, 32, 50, 5000, 12000 },
71 { 16384, 2048, 12, 65536, 6, 6, 4, 0, 0, 0, 32, 55, 4000, 10000 },
72 { 32768, 2048, 12, 4096, 7, 7, 5, 4, 150, 380, 32, 60, 3000, 8000 },
73 { 65536, 2048, 16, 4096, 7, 7, 6, 4, 170, 420, 48, 65, 2000, 5000 },
74 { 65536, 2048, 16, 4096, 8, 8, 7, 5, 180, 450, 64, 70, 1000, 2500 },
75 { 65536, 2048, 32, 8192, 9, 9, 8, 6, 190, 475, 64, 75, 500, 1200 },
76 { 65536, 2048, 32, 8192, 9, 9, 9, 6, 200, 500, 96, 80, 200, 500 }
79 static int compressLevel
;
80 static int qualityLevel
;
82 /* Stuff dealing with palettes. */
84 typedef struct COLOR_LIST_s
{
85 struct COLOR_LIST_s
*next
;
90 typedef struct PALETTE_ENTRY_s
{
95 typedef struct PALETTE_s
{
96 PALETTE_ENTRY entry
[256];
97 COLOR_LIST
*hash
[256];
101 static int paletteNumColors
, paletteMaxColors
;
102 static CARD32 monoBackground
, monoForeground
;
103 static PALETTE palette
;
105 /* Pointers to dynamically-allocated buffers. */
107 static int tightBeforeBufSize
= 0;
108 static char *tightBeforeBuf
= NULL
;
110 static int tightAfterBufSize
= 0;
111 static char *tightAfterBuf
= NULL
;
113 static int *prevRowBuf
= NULL
;
116 /* Prototypes for static functions. */
118 static void FindBestSolidArea (rfbClientPtr cl
, int x
, int y
, int w
, int h
,
119 CARD32 colorValue
, int *w_ptr
, int *h_ptr
);
120 static void ExtendSolidArea (rfbClientPtr cl
, int x
, int y
, int w
, int h
,
122 int *x_ptr
, int *y_ptr
, int *w_ptr
, int *h_ptr
);
123 static Bool
CheckSolidTile (rfbClientPtr cl
, int x
, int y
, int w
, int h
,
124 CARD32
*colorPtr
, Bool needSameColor
);
125 static Bool
CheckSolidTile8 (rfbClientPtr cl
, int x
, int y
, int w
, int h
,
126 CARD32
*colorPtr
, Bool needSameColor
);
127 static Bool
CheckSolidTile16 (rfbClientPtr cl
, int x
, int y
, int w
, int h
,
128 CARD32
*colorPtr
, Bool needSameColor
);
129 static Bool
CheckSolidTile32 (rfbClientPtr cl
, int x
, int y
, int w
, int h
,
130 CARD32
*colorPtr
, Bool needSameColor
);
132 static Bool
SendRectSimple (rfbClientPtr cl
, int x
, int y
, int w
, int h
);
133 static Bool
SendSubrect (rfbClientPtr cl
, int x
, int y
, int w
, int h
);
134 static Bool
SendTightHeader (rfbClientPtr cl
, int x
, int y
, int w
, int h
);
136 static Bool
SendSolidRect (rfbClientPtr cl
);
137 static Bool
SendMonoRect (rfbClientPtr cl
, int w
, int h
);
138 static Bool
SendIndexedRect (rfbClientPtr cl
, int w
, int h
);
139 static Bool
SendFullColorRect (rfbClientPtr cl
, int w
, int h
);
140 static Bool
SendGradientRect (rfbClientPtr cl
, int w
, int h
);
142 static Bool
CompressData(rfbClientPtr cl
, int streamId
, int dataLen
,
143 int zlibLevel
, int zlibStrategy
);
144 static Bool
SendCompressedData(rfbClientPtr cl
, int compressedLen
);
146 static void FillPalette8(int count
);
147 static void FillPalette16(int count
);
148 static void FillPalette32(int count
);
150 static void PaletteReset(void);
151 static int PaletteInsert(CARD32 rgb
, int numPixels
, int bpp
);
153 static void Pack24(rfbClientPtr cl
, char *buf
, rfbPixelFormat
*fmt
, int count
);
155 static void EncodeIndexedRect16(CARD8
*buf
, int count
);
156 static void EncodeIndexedRect32(CARD8
*buf
, int count
);
158 static void EncodeMonoRect8(CARD8
*buf
, int w
, int h
);
159 static void EncodeMonoRect16(CARD8
*buf
, int w
, int h
);
160 static void EncodeMonoRect32(CARD8
*buf
, int w
, int h
);
162 static void FilterGradient24(rfbClientPtr cl
, char *buf
, rfbPixelFormat
*fmt
, int w
, int h
);
163 static void FilterGradient16(rfbClientPtr cl
, CARD16
*buf
, rfbPixelFormat
*fmt
, int w
, int h
);
164 static void FilterGradient32(rfbClientPtr cl
, CARD32
*buf
, rfbPixelFormat
*fmt
, int w
, int h
);
166 static int DetectSmoothImage(rfbClientPtr cl
, rfbPixelFormat
*fmt
, int w
, int h
);
167 static unsigned long DetectSmoothImage24(rfbClientPtr cl
, rfbPixelFormat
*fmt
, int w
, int h
);
168 static unsigned long DetectSmoothImage16(rfbClientPtr cl
, rfbPixelFormat
*fmt
, int w
, int h
);
169 static unsigned long DetectSmoothImage32(rfbClientPtr cl
, rfbPixelFormat
*fmt
, int w
, int h
);
171 static Bool
SendJpegRect(rfbClientPtr cl
, int x
, int y
, int w
, int h
,
173 static void PrepareRowForJpeg(rfbClientPtr cl
, CARD8
*dst
, int x
, int y
, int count
);
174 static void PrepareRowForJpeg24(rfbClientPtr cl
, CARD8
*dst
, int x
, int y
, int count
);
175 static void PrepareRowForJpeg16(rfbClientPtr cl
, CARD8
*dst
, int x
, int y
, int count
);
176 static void PrepareRowForJpeg32(rfbClientPtr cl
, CARD8
*dst
, int x
, int y
, int count
);
178 static void JpegInitDestination(j_compress_ptr cinfo
);
179 static boolean
JpegEmptyOutputBuffer(j_compress_ptr cinfo
);
180 static void JpegTermDestination(j_compress_ptr cinfo
);
181 static void JpegSetDstManager(j_compress_ptr cinfo
);
185 * Tight encoding implementation.
189 rfbNumCodedRectsTight(cl
, x
, y
, w
, h
)
193 int maxRectSize
, maxRectWidth
;
194 int subrectMaxWidth
, subrectMaxHeight
;
196 /* No matter how many rectangles we will send if LastRect markers
197 are used to terminate rectangle stream. */
198 if (cl
->enableLastRectEncoding
&& w
* h
>= MIN_SPLIT_RECT_SIZE
)
201 maxRectSize
= tightConf
[cl
->tightCompressLevel
].maxRectSize
;
202 maxRectWidth
= tightConf
[cl
->tightCompressLevel
].maxRectWidth
;
204 if (w
> maxRectWidth
|| w
* h
> maxRectSize
) {
205 subrectMaxWidth
= (w
> maxRectWidth
) ? maxRectWidth
: w
;
206 subrectMaxHeight
= maxRectSize
/ subrectMaxWidth
;
207 return (((w
- 1) / maxRectWidth
+ 1) *
208 ((h
- 1) / subrectMaxHeight
+ 1));
215 rfbSendRectEncodingTight(cl
, x
, y
, w
, h
)
222 int x_best
, y_best
, w_best
, h_best
;
225 compressLevel
= cl
->tightCompressLevel
;
226 qualityLevel
= cl
->tightQualityLevel
;
228 if ( cl
->format
.depth
== 24 && cl
->format
.redMax
== 0xFF &&
229 cl
->format
.greenMax
== 0xFF && cl
->format
.blueMax
== 0xFF ) {
230 usePixelFormat24
= TRUE
;
232 usePixelFormat24
= FALSE
;
235 if (!cl
->enableLastRectEncoding
|| w
* h
< MIN_SPLIT_RECT_SIZE
)
236 return SendRectSimple(cl
, x
, y
, w
, h
);
238 /* Make sure we can write at least one pixel into tightBeforeBuf. */
240 if (tightBeforeBufSize
< 4) {
241 tightBeforeBufSize
= 4;
242 if (tightBeforeBuf
== NULL
)
243 tightBeforeBuf
= (char *)malloc(tightBeforeBufSize
);
245 tightBeforeBuf
= (char *)realloc(tightBeforeBuf
,
249 /* Calculate maximum number of rows in one non-solid rectangle. */
252 int maxRectSize
, maxRectWidth
, nMaxWidth
;
254 maxRectSize
= tightConf
[compressLevel
].maxRectSize
;
255 maxRectWidth
= tightConf
[compressLevel
].maxRectWidth
;
256 nMaxWidth
= (w
> maxRectWidth
) ? maxRectWidth
: w
;
257 nMaxRows
= maxRectSize
/ nMaxWidth
;
260 /* Try to find large solid-color areas and send them separately. */
262 for (dy
= y
; dy
< y
+ h
; dy
+= MAX_SPLIT_TILE_SIZE
) {
264 /* If a rectangle becomes too large, send its upper part now. */
266 if (dy
- y
>= nMaxRows
) {
267 if (!SendRectSimple(cl
, x
, y
, w
, nMaxRows
))
273 dh
= (dy
+ MAX_SPLIT_TILE_SIZE
<= y
+ h
) ?
274 MAX_SPLIT_TILE_SIZE
: (y
+ h
- dy
);
276 for (dx
= x
; dx
< x
+ w
; dx
+= MAX_SPLIT_TILE_SIZE
) {
278 dw
= (dx
+ MAX_SPLIT_TILE_SIZE
<= x
+ w
) ?
279 MAX_SPLIT_TILE_SIZE
: (x
+ w
- dx
);
281 if (CheckSolidTile(cl
, dx
, dy
, dw
, dh
, &colorValue
, FALSE
)) {
283 /* Get dimensions of solid-color area. */
285 FindBestSolidArea(cl
, dx
, dy
, w
- (dx
- x
), h
- (dy
- y
),
286 colorValue
, &w_best
, &h_best
);
288 /* Make sure a solid rectangle is large enough
289 (or the whole rectangle is of the same color). */
291 if ( w_best
* h_best
!= w
* h
&&
292 w_best
* h_best
< MIN_SOLID_SUBRECT_SIZE
)
295 /* Try to extend solid rectangle to maximum size. */
297 x_best
= dx
; y_best
= dy
;
298 ExtendSolidArea(cl
, x
, y
, w
, h
, colorValue
,
299 &x_best
, &y_best
, &w_best
, &h_best
);
301 /* Send rectangles at top and left to solid-color area. */
304 !SendRectSimple(cl
, x
, y
, w
, y_best
-y
) )
307 !rfbSendRectEncodingTight(cl
, x
, y_best
,
311 /* Send solid-color rectangle. */
313 if (!SendTightHeader(cl
, x_best
, y_best
, w_best
, h_best
))
316 fbptr
= (cl
->screen
->frameBuffer
+
317 (cl
->screen
->paddedWidthInBytes
* y_best
) +
318 (x_best
* (cl
->screen
->bitsPerPixel
/ 8)));
320 (*cl
->translateFn
)(cl
->translateLookupTable
, &cl
->screen
->rfbServerFormat
,
321 &cl
->format
, fbptr
, tightBeforeBuf
,
322 cl
->screen
->paddedWidthInBytes
, 1, 1);
324 if (!SendSolidRect(cl
))
327 /* Send remaining rectangles (at right and bottom). */
329 if ( x_best
+ w_best
!= x
+ w
&&
330 !rfbSendRectEncodingTight(cl
, x_best
+w_best
, y_best
,
331 w
-(x_best
-x
)-w_best
, h_best
) )
333 if ( y_best
+ h_best
!= y
+ h
&&
334 !rfbSendRectEncodingTight(cl
, x
, y_best
+h_best
,
335 w
, h
-(y_best
-y
)-h_best
) )
338 /* Return after all recursive calls are done. */
347 /* No suitable solid-color rectangles found. */
349 return SendRectSimple(cl
, x
, y
, w
, h
);
353 FindBestSolidArea(cl
, x
, y
, w
, h
, colorValue
, w_ptr
, h_ptr
)
361 int w_best
= 0, h_best
= 0;
365 for (dy
= y
; dy
< y
+ h
; dy
+= MAX_SPLIT_TILE_SIZE
) {
367 dh
= (dy
+ MAX_SPLIT_TILE_SIZE
<= y
+ h
) ?
368 MAX_SPLIT_TILE_SIZE
: (y
+ h
- dy
);
369 dw
= (w_prev
> MAX_SPLIT_TILE_SIZE
) ?
370 MAX_SPLIT_TILE_SIZE
: w_prev
;
372 if (!CheckSolidTile(cl
, x
, dy
, dw
, dh
, &colorValue
, TRUE
))
375 for (dx
= x
+ dw
; dx
< x
+ w_prev
;) {
376 dw
= (dx
+ MAX_SPLIT_TILE_SIZE
<= x
+ w_prev
) ?
377 MAX_SPLIT_TILE_SIZE
: (x
+ w_prev
- dx
);
378 if (!CheckSolidTile(cl
, dx
, dy
, dw
, dh
, &colorValue
, TRUE
))
384 if (w_prev
* (dy
+ dh
- y
) > w_best
* h_best
) {
386 h_best
= dy
+ dh
- y
;
395 ExtendSolidArea(cl
, x
, y
, w
, h
, colorValue
, x_ptr
, y_ptr
, w_ptr
, h_ptr
)
399 int *x_ptr
, *y_ptr
, *w_ptr
, *h_ptr
;
403 /* Try to extend the area upwards. */
404 for ( cy
= *y_ptr
- 1;
405 cy
>= y
&& CheckSolidTile(cl
, *x_ptr
, cy
, *w_ptr
, 1, &colorValue
, TRUE
);
407 *h_ptr
+= *y_ptr
- (cy
+ 1);
411 for ( cy
= *y_ptr
+ *h_ptr
;
413 CheckSolidTile(cl
, *x_ptr
, cy
, *w_ptr
, 1, &colorValue
, TRUE
);
415 *h_ptr
+= cy
- (*y_ptr
+ *h_ptr
);
417 /* ... to the left. */
418 for ( cx
= *x_ptr
- 1;
419 cx
>= x
&& CheckSolidTile(cl
, cx
, *y_ptr
, 1, *h_ptr
, &colorValue
, TRUE
);
421 *w_ptr
+= *x_ptr
- (cx
+ 1);
424 /* ... to the right. */
425 for ( cx
= *x_ptr
+ *w_ptr
;
427 CheckSolidTile(cl
, cx
, *y_ptr
, 1, *h_ptr
, &colorValue
, TRUE
);
429 *w_ptr
+= cx
- (*x_ptr
+ *w_ptr
);
433 CheckSolidTile(cl
, x
, y
, w
, h
, colorPtr
, needSameColor
)
439 switch(cl
->screen
->rfbServerFormat
.bitsPerPixel
) {
441 return CheckSolidTile32(cl
, x
, y
, w
, h
, colorPtr
, needSameColor
);
443 return CheckSolidTile16(cl
, x
, y
, w
, h
, colorPtr
, needSameColor
);
445 return CheckSolidTile8(cl
, x
, y
, w
, h
, colorPtr
, needSameColor
);
449 #define DEFINE_CHECK_SOLID_FUNCTION(bpp) \
452 CheckSolidTile##bpp(cl, x, y, w, h, colorPtr, needSameColor) \
456 Bool needSameColor; \
459 CARD##bpp colorValue; \
462 fbptr = (CARD##bpp *) \
463 &cl->screen->frameBuffer[y * cl->screen->paddedWidthInBytes + x * (bpp/8)]; \
465 colorValue = *fbptr; \
466 if (needSameColor && (CARD32)colorValue != *colorPtr) \
469 for (dy = 0; dy < h; dy++) { \
470 for (dx = 0; dx < w; dx++) { \
471 if (colorValue != fbptr[dx]) \
474 fbptr = (CARD##bpp *)((CARD8 *)fbptr + cl->screen->paddedWidthInBytes); \
477 *colorPtr = (CARD32)colorValue; \
481 DEFINE_CHECK_SOLID_FUNCTION(8)
482 DEFINE_CHECK_SOLID_FUNCTION(16)
483 DEFINE_CHECK_SOLID_FUNCTION(32)
486 SendRectSimple(cl
, x
, y
, w
, h
)
490 int maxBeforeSize
, maxAfterSize
;
491 int maxRectSize
, maxRectWidth
;
492 int subrectMaxWidth
, subrectMaxHeight
;
496 maxRectSize
= tightConf
[compressLevel
].maxRectSize
;
497 maxRectWidth
= tightConf
[compressLevel
].maxRectWidth
;
499 maxBeforeSize
= maxRectSize
* (cl
->format
.bitsPerPixel
/ 8);
500 maxAfterSize
= maxBeforeSize
+ (maxBeforeSize
+ 99) / 100 + 12;
502 if (tightBeforeBufSize
< maxBeforeSize
) {
503 tightBeforeBufSize
= maxBeforeSize
;
504 if (tightBeforeBuf
== NULL
)
505 tightBeforeBuf
= (char *)malloc(tightBeforeBufSize
);
507 tightBeforeBuf
= (char *)realloc(tightBeforeBuf
,
511 if (tightAfterBufSize
< maxAfterSize
) {
512 tightAfterBufSize
= maxAfterSize
;
513 if (tightAfterBuf
== NULL
)
514 tightAfterBuf
= (char *)malloc(tightAfterBufSize
);
516 tightAfterBuf
= (char *)realloc(tightAfterBuf
,
520 if (w
> maxRectWidth
|| w
* h
> maxRectSize
) {
521 subrectMaxWidth
= (w
> maxRectWidth
) ? maxRectWidth
: w
;
522 subrectMaxHeight
= maxRectSize
/ subrectMaxWidth
;
524 for (dy
= 0; dy
< h
; dy
+= subrectMaxHeight
) {
525 for (dx
= 0; dx
< w
; dx
+= maxRectWidth
) {
526 rw
= (dx
+ maxRectWidth
< w
) ? maxRectWidth
: w
- dx
;
527 rh
= (dy
+ subrectMaxHeight
< h
) ? subrectMaxHeight
: h
- dy
;
528 if (!SendSubrect(cl
, x
+dx
, y
+dy
, rw
, rh
))
533 if (!SendSubrect(cl
, x
, y
, w
, h
))
541 SendSubrect(cl
, x
, y
, w
, h
)
546 Bool success
= FALSE
;
548 /* Send pending data if there is more than 128 bytes. */
549 if (cl
->ublen
> 128) {
550 if (!rfbSendUpdateBuf(cl
))
554 if (!SendTightHeader(cl
, x
, y
, w
, h
))
557 fbptr
= (cl
->screen
->frameBuffer
+ (cl
->screen
->paddedWidthInBytes
* y
)
558 + (x
* (cl
->screen
->bitsPerPixel
/ 8)));
560 (*cl
->translateFn
)(cl
->translateLookupTable
, &cl
->screen
->rfbServerFormat
,
561 &cl
->format
, fbptr
, tightBeforeBuf
,
562 cl
->screen
->paddedWidthInBytes
, w
, h
);
564 paletteMaxColors
= w
* h
/ tightConf
[compressLevel
].idxMaxColorsDivisor
;
565 if ( paletteMaxColors
< 2 &&
566 w
* h
>= tightConf
[compressLevel
].monoMinRectSize
) {
567 paletteMaxColors
= 2;
569 switch (cl
->format
.bitsPerPixel
) {
574 FillPalette16(w
* h
);
577 FillPalette32(w
* h
);
580 switch (paletteNumColors
) {
582 /* Truecolor image */
583 if (DetectSmoothImage(cl
, &cl
->format
, w
, h
)) {
584 if (qualityLevel
!= -1) {
585 success
= SendJpegRect(cl
, x
, y
, w
, h
,
586 tightConf
[qualityLevel
].jpegQuality
);
588 success
= SendGradientRect(cl
, w
, h
);
591 success
= SendFullColorRect(cl
, w
, h
);
595 /* Solid rectangle */
596 success
= SendSolidRect(cl
);
599 /* Two-color rectangle */
600 success
= SendMonoRect(cl
, w
, h
);
603 /* Up to 256 different colors */
604 if ( paletteNumColors
> 96 &&
605 qualityLevel
!= -1 && qualityLevel
<= 3 &&
606 DetectSmoothImage(cl
, &cl
->format
, w
, h
) ) {
607 success
= SendJpegRect(cl
, x
, y
, w
, h
,
608 tightConf
[qualityLevel
].jpegQuality
);
610 success
= SendIndexedRect(cl
, w
, h
);
617 SendTightHeader(cl
, x
, y
, w
, h
)
621 rfbFramebufferUpdateRectHeader rect
;
623 if (cl
->ublen
+ sz_rfbFramebufferUpdateRectHeader
> UPDATE_BUF_SIZE
) {
624 if (!rfbSendUpdateBuf(cl
))
628 rect
.r
.x
= Swap16IfLE(x
);
629 rect
.r
.y
= Swap16IfLE(y
);
630 rect
.r
.w
= Swap16IfLE(w
);
631 rect
.r
.h
= Swap16IfLE(h
);
632 rect
.encoding
= Swap32IfLE(rfbEncodingTight
);
634 memcpy(&cl
->updateBuf
[cl
->ublen
], (char *)&rect
,
635 sz_rfbFramebufferUpdateRectHeader
);
636 cl
->ublen
+= sz_rfbFramebufferUpdateRectHeader
;
638 cl
->rfbRectanglesSent
[rfbEncodingTight
]++;
639 cl
->rfbBytesSent
[rfbEncodingTight
] += sz_rfbFramebufferUpdateRectHeader
;
645 * Subencoding implementations.
654 if (usePixelFormat24
) {
655 Pack24(cl
, tightBeforeBuf
, &cl
->format
, 1);
658 len
= cl
->format
.bitsPerPixel
/ 8;
660 if (cl
->ublen
+ 1 + len
> UPDATE_BUF_SIZE
) {
661 if (!rfbSendUpdateBuf(cl
))
665 cl
->updateBuf
[cl
->ublen
++] = (char)(rfbTightFill
<< 4);
666 memcpy (&cl
->updateBuf
[cl
->ublen
], tightBeforeBuf
, len
);
669 cl
->rfbBytesSent
[rfbEncodingTight
] += len
+ 1;
675 SendMonoRect(cl
, w
, h
)
680 int paletteLen
, dataLen
;
682 if ( cl
->ublen
+ TIGHT_MIN_TO_COMPRESS
+ 6 +
683 2 * cl
->format
.bitsPerPixel
/ 8 > UPDATE_BUF_SIZE
) {
684 if (!rfbSendUpdateBuf(cl
))
688 /* Prepare tight encoding header. */
689 dataLen
= (w
+ 7) / 8;
692 cl
->updateBuf
[cl
->ublen
++] = (streamId
| rfbTightExplicitFilter
) << 4;
693 cl
->updateBuf
[cl
->ublen
++] = rfbTightFilterPalette
;
694 cl
->updateBuf
[cl
->ublen
++] = 1;
696 /* Prepare palette, convert image. */
697 switch (cl
->format
.bitsPerPixel
) {
700 EncodeMonoRect32((CARD8
*)tightBeforeBuf
, w
, h
);
702 ((CARD32
*)tightAfterBuf
)[0] = monoBackground
;
703 ((CARD32
*)tightAfterBuf
)[1] = monoForeground
;
704 if (usePixelFormat24
) {
705 Pack24(cl
, tightAfterBuf
, &cl
->format
, 2);
710 memcpy(&cl
->updateBuf
[cl
->ublen
], tightAfterBuf
, paletteLen
);
711 cl
->ublen
+= paletteLen
;
712 cl
->rfbBytesSent
[rfbEncodingTight
] += 3 + paletteLen
;
716 EncodeMonoRect16((CARD8
*)tightBeforeBuf
, w
, h
);
718 ((CARD16
*)tightAfterBuf
)[0] = (CARD16
)monoBackground
;
719 ((CARD16
*)tightAfterBuf
)[1] = (CARD16
)monoForeground
;
721 memcpy(&cl
->updateBuf
[cl
->ublen
], tightAfterBuf
, 4);
723 cl
->rfbBytesSent
[rfbEncodingTight
] += 7;
727 EncodeMonoRect8((CARD8
*)tightBeforeBuf
, w
, h
);
729 cl
->updateBuf
[cl
->ublen
++] = (char)monoBackground
;
730 cl
->updateBuf
[cl
->ublen
++] = (char)monoForeground
;
731 cl
->rfbBytesSent
[rfbEncodingTight
] += 5;
734 return CompressData(cl
, streamId
, dataLen
,
735 tightConf
[compressLevel
].monoZlibLevel
,
740 SendIndexedRect(cl
, w
, h
)
747 if ( cl
->ublen
+ TIGHT_MIN_TO_COMPRESS
+ 6 +
748 paletteNumColors
* cl
->format
.bitsPerPixel
/ 8 >
750 if (!rfbSendUpdateBuf(cl
))
754 /* Prepare tight encoding header. */
755 cl
->updateBuf
[cl
->ublen
++] = (streamId
| rfbTightExplicitFilter
) << 4;
756 cl
->updateBuf
[cl
->ublen
++] = rfbTightFilterPalette
;
757 cl
->updateBuf
[cl
->ublen
++] = (char)(paletteNumColors
- 1);
759 /* Prepare palette, convert image. */
760 switch (cl
->format
.bitsPerPixel
) {
763 EncodeIndexedRect32((CARD8
*)tightBeforeBuf
, w
* h
);
765 for (i
= 0; i
< paletteNumColors
; i
++) {
766 ((CARD32
*)tightAfterBuf
)[i
] =
767 palette
.entry
[i
].listNode
->rgb
;
769 if (usePixelFormat24
) {
770 Pack24(cl
, tightAfterBuf
, &cl
->format
, paletteNumColors
);
775 memcpy(&cl
->updateBuf
[cl
->ublen
], tightAfterBuf
, paletteNumColors
* entryLen
);
776 cl
->ublen
+= paletteNumColors
* entryLen
;
777 cl
->rfbBytesSent
[rfbEncodingTight
] += 3 + paletteNumColors
* entryLen
;
781 EncodeIndexedRect16((CARD8
*)tightBeforeBuf
, w
* h
);
783 for (i
= 0; i
< paletteNumColors
; i
++) {
784 ((CARD16
*)tightAfterBuf
)[i
] =
785 (CARD16
)palette
.entry
[i
].listNode
->rgb
;
788 memcpy(&cl
->updateBuf
[cl
->ublen
], tightAfterBuf
, paletteNumColors
* 2);
789 cl
->ublen
+= paletteNumColors
* 2;
790 cl
->rfbBytesSent
[rfbEncodingTight
] += 3 + paletteNumColors
* 2;
794 return FALSE
; /* Should never happen. */
797 return CompressData(cl
, streamId
, w
* h
,
798 tightConf
[compressLevel
].idxZlibLevel
,
803 SendFullColorRect(cl
, w
, h
)
810 if (cl
->ublen
+ TIGHT_MIN_TO_COMPRESS
+ 1 > UPDATE_BUF_SIZE
) {
811 if (!rfbSendUpdateBuf(cl
))
815 cl
->updateBuf
[cl
->ublen
++] = 0x00; /* stream id = 0, no flushing, no filter */
816 cl
->rfbBytesSent
[rfbEncodingTight
]++;
818 if (usePixelFormat24
) {
819 Pack24(cl
, tightBeforeBuf
, &cl
->format
, w
* h
);
822 len
= cl
->format
.bitsPerPixel
/ 8;
824 return CompressData(cl
, streamId
, w
* h
* len
,
825 tightConf
[compressLevel
].rawZlibLevel
,
830 SendGradientRect(cl
, w
, h
)
837 if (cl
->format
.bitsPerPixel
== 8)
838 return SendFullColorRect(cl
, w
, h
);
840 if (cl
->ublen
+ TIGHT_MIN_TO_COMPRESS
+ 2 > UPDATE_BUF_SIZE
) {
841 if (!rfbSendUpdateBuf(cl
))
845 if (prevRowBuf
== NULL
)
846 prevRowBuf
= (int *)malloc(2048 * 3 * sizeof(int));
848 cl
->updateBuf
[cl
->ublen
++] = (streamId
| rfbTightExplicitFilter
) << 4;
849 cl
->updateBuf
[cl
->ublen
++] = rfbTightFilterGradient
;
850 cl
->rfbBytesSent
[rfbEncodingTight
] += 2;
852 if (usePixelFormat24
) {
853 FilterGradient24(cl
, tightBeforeBuf
, &cl
->format
, w
, h
);
855 } else if (cl
->format
.bitsPerPixel
== 32) {
856 FilterGradient32(cl
, (CARD32
*)tightBeforeBuf
, &cl
->format
, w
, h
);
859 FilterGradient16(cl
, (CARD16
*)tightBeforeBuf
, &cl
->format
, w
, h
);
863 return CompressData(cl
, streamId
, w
* h
* len
,
864 tightConf
[compressLevel
].gradientZlibLevel
,
869 CompressData(cl
, streamId
, dataLen
, zlibLevel
, zlibStrategy
)
871 int streamId
, dataLen
, zlibLevel
, zlibStrategy
;
876 if (dataLen
< TIGHT_MIN_TO_COMPRESS
) {
877 memcpy(&cl
->updateBuf
[cl
->ublen
], tightBeforeBuf
, dataLen
);
878 cl
->ublen
+= dataLen
;
879 cl
->rfbBytesSent
[rfbEncodingTight
] += dataLen
;
883 pz
= &cl
->zsStruct
[streamId
];
885 /* Initialize compression stream if needed. */
886 if (!cl
->zsActive
[streamId
]) {
891 err
= deflateInit2 (pz
, zlibLevel
, Z_DEFLATED
, MAX_WBITS
,
892 MAX_MEM_LEVEL
, zlibStrategy
);
896 cl
->zsActive
[streamId
] = TRUE
;
897 cl
->zsLevel
[streamId
] = zlibLevel
;
900 /* Prepare buffer pointers. */
901 pz
->next_in
= (Bytef
*)tightBeforeBuf
;
902 pz
->avail_in
= dataLen
;
903 pz
->next_out
= (Bytef
*)tightAfterBuf
;
904 pz
->avail_out
= tightAfterBufSize
;
906 /* Change compression parameters if needed. */
907 if (zlibLevel
!= cl
->zsLevel
[streamId
]) {
908 if (deflateParams (pz
, zlibLevel
, zlibStrategy
) != Z_OK
) {
911 cl
->zsLevel
[streamId
] = zlibLevel
;
914 /* Actual compression. */
915 if ( deflate (pz
, Z_SYNC_FLUSH
) != Z_OK
||
916 pz
->avail_in
!= 0 || pz
->avail_out
== 0 ) {
920 return SendCompressedData(cl
, tightAfterBufSize
- pz
->avail_out
);
923 static Bool
SendCompressedData(cl
, compressedLen
)
929 cl
->updateBuf
[cl
->ublen
++] = compressedLen
& 0x7F;
930 cl
->rfbBytesSent
[rfbEncodingTight
]++;
931 if (compressedLen
> 0x7F) {
932 cl
->updateBuf
[cl
->ublen
-1] |= 0x80;
933 cl
->updateBuf
[cl
->ublen
++] = compressedLen
>> 7 & 0x7F;
934 cl
->rfbBytesSent
[rfbEncodingTight
]++;
935 if (compressedLen
> 0x3FFF) {
936 cl
->updateBuf
[cl
->ublen
-1] |= 0x80;
937 cl
->updateBuf
[cl
->ublen
++] = compressedLen
>> 14 & 0xFF;
938 cl
->rfbBytesSent
[rfbEncodingTight
]++;
942 portionLen
= UPDATE_BUF_SIZE
;
943 for (i
= 0; i
< compressedLen
; i
+= portionLen
) {
944 if (i
+ portionLen
> compressedLen
) {
945 portionLen
= compressedLen
- i
;
947 if (cl
->ublen
+ portionLen
> UPDATE_BUF_SIZE
) {
948 if (!rfbSendUpdateBuf(cl
))
951 memcpy(&cl
->updateBuf
[cl
->ublen
], &tightAfterBuf
[i
], portionLen
);
952 cl
->ublen
+= portionLen
;
954 portionLen
= UPDATE_BUF_SIZE
;
955 cl
->rfbBytesSent
[rfbEncodingTight
] += compressedLen
;
960 * Code to determine how many different colors used in rectangle.
967 CARD8
*data
= (CARD8
*)tightBeforeBuf
;
971 paletteNumColors
= 0;
974 for (i
= 1; i
< count
&& data
[i
] == c0
; i
++);
976 paletteNumColors
= 1;
977 return; /* Solid rectangle */
980 if (paletteMaxColors
< 2)
986 for (i
++; i
< count
; i
++) {
989 } else if (data
[i
] == c1
) {
996 monoBackground
= (CARD32
)c0
;
997 monoForeground
= (CARD32
)c1
;
999 monoBackground
= (CARD32
)c1
;
1000 monoForeground
= (CARD32
)c0
;
1002 paletteNumColors
= 2; /* Two colors */
1006 #define DEFINE_FILL_PALETTE_FUNCTION(bpp) \
1009 FillPalette##bpp(count) \
1012 CARD##bpp *data = (CARD##bpp *)tightBeforeBuf; \
1013 CARD##bpp c0, c1, ci; \
1014 int i, n0, n1, ni; \
1017 for (i = 1; i < count && data[i] == c0; i++); \
1019 paletteNumColors = 1; /* Solid rectangle */ \
1023 if (paletteMaxColors < 2) { \
1024 paletteNumColors = 0; /* Full-color encoding preferred */ \
1031 for (i++; i < count; i++) { \
1035 } else if (ci == c1) { \
1042 monoBackground = (CARD32)c0; \
1043 monoForeground = (CARD32)c1; \
1045 monoBackground = (CARD32)c1; \
1046 monoForeground = (CARD32)c0; \
1048 paletteNumColors = 2; /* Two colors */ \
1053 PaletteInsert (c0, (CARD32)n0, bpp); \
1054 PaletteInsert (c1, (CARD32)n1, bpp); \
1057 for (i++; i < count; i++) { \
1058 if (data[i] == ci) { \
1061 if (!PaletteInsert (ci, (CARD32)ni, bpp)) \
1067 PaletteInsert (ci, (CARD32)ni, bpp); \
1070 DEFINE_FILL_PALETTE_FUNCTION(16)
1071 DEFINE_FILL_PALETTE_FUNCTION(32)
1075 * Functions to operate with palette structures.
1078 #define HASH_FUNC16(rgb) ((int)(((rgb >> 8) + rgb) & 0xFF))
1079 #define HASH_FUNC32(rgb) ((int)(((rgb >> 16) + (rgb >> 8)) & 0xFF))
1084 paletteNumColors
= 0;
1085 memset(palette
.hash
, 0, 256 * sizeof(COLOR_LIST
*));
1089 PaletteInsert(rgb
, numPixels
, bpp
)
1095 COLOR_LIST
*prev_pnode
= NULL
;
1096 int hash_key
, idx
, new_idx
, count
;
1098 hash_key
= (bpp
== 16) ? HASH_FUNC16(rgb
) : HASH_FUNC32(rgb
);
1100 pnode
= palette
.hash
[hash_key
];
1102 while (pnode
!= NULL
) {
1103 if (pnode
->rgb
== rgb
) {
1104 /* Such palette entry already exists. */
1105 new_idx
= idx
= pnode
->idx
;
1106 count
= palette
.entry
[idx
].numPixels
+ numPixels
;
1107 if (new_idx
&& palette
.entry
[new_idx
-1].numPixels
< count
) {
1109 palette
.entry
[new_idx
] = palette
.entry
[new_idx
-1];
1110 palette
.entry
[new_idx
].listNode
->idx
= new_idx
;
1113 while (new_idx
&& palette
.entry
[new_idx
-1].numPixels
< count
);
1114 palette
.entry
[new_idx
].listNode
= pnode
;
1115 pnode
->idx
= new_idx
;
1117 palette
.entry
[new_idx
].numPixels
= count
;
1118 return paletteNumColors
;
1121 pnode
= pnode
->next
;
1124 /* Check if palette is full. */
1125 if (paletteNumColors
== 256 || paletteNumColors
== paletteMaxColors
) {
1126 paletteNumColors
= 0;
1130 /* Move palette entries with lesser pixel counts. */
1131 for ( idx
= paletteNumColors
;
1132 idx
> 0 && palette
.entry
[idx
-1].numPixels
< numPixels
;
1134 palette
.entry
[idx
] = palette
.entry
[idx
-1];
1135 palette
.entry
[idx
].listNode
->idx
= idx
;
1138 /* Add new palette entry into the freed slot. */
1139 pnode
= &palette
.list
[paletteNumColors
];
1140 if (prev_pnode
!= NULL
) {
1141 prev_pnode
->next
= pnode
;
1143 palette
.hash
[hash_key
] = pnode
;
1148 palette
.entry
[idx
].listNode
= pnode
;
1149 palette
.entry
[idx
].numPixels
= numPixels
;
1151 return (++paletteNumColors
);
1156 * Converting 32-bit color samples into 24-bit colors.
1157 * Should be called only when redMax, greenMax and blueMax are 255.
1158 * Color components assumed to be byte-aligned.
1161 static void Pack24(cl
, buf
, fmt
, count
)
1164 rfbPixelFormat
*fmt
;
1169 int r_shift
, g_shift
, b_shift
;
1171 buf32
= (CARD32
*)buf
;
1173 if (!cl
->screen
->rfbServerFormat
.bigEndian
== !fmt
->bigEndian
) {
1174 r_shift
= fmt
->redShift
;
1175 g_shift
= fmt
->greenShift
;
1176 b_shift
= fmt
->blueShift
;
1178 r_shift
= 24 - fmt
->redShift
;
1179 g_shift
= 24 - fmt
->greenShift
;
1180 b_shift
= 24 - fmt
->blueShift
;
1185 *buf
++ = (char)(pix
>> r_shift
);
1186 *buf
++ = (char)(pix
>> g_shift
);
1187 *buf
++ = (char)(pix
>> b_shift
);
1193 * Converting truecolor samples into palette indices.
1196 #define DEFINE_IDX_ENCODE_FUNCTION(bpp) \
1199 EncodeIndexedRect##bpp(buf, count) \
1203 COLOR_LIST *pnode; \
1208 src = (CARD##bpp *) buf; \
1212 while (count && *src == rgb) { \
1213 rep++, src++, count--; \
1215 pnode = palette.hash[HASH_FUNC##bpp(rgb)]; \
1216 while (pnode != NULL) { \
1217 if ((CARD##bpp)pnode->rgb == rgb) { \
1218 *buf++ = (CARD8)pnode->idx; \
1220 *buf++ = (CARD8)pnode->idx; \
1225 pnode = pnode->next; \
1230 DEFINE_IDX_ENCODE_FUNCTION(16)
1231 DEFINE_IDX_ENCODE_FUNCTION(32)
1233 #define DEFINE_MONO_ENCODE_FUNCTION(bpp) \
1236 EncodeMonoRect##bpp(buf, w, h) \
1242 unsigned int value, mask; \
1243 int aligned_width; \
1244 int x, y, bg_bits; \
1246 ptr = (CARD##bpp *) buf; \
1247 bg = (CARD##bpp) monoBackground; \
1248 aligned_width = w - w % 8; \
1250 for (y = 0; y < h; y++) { \
1251 for (x = 0; x < aligned_width; x += 8) { \
1252 for (bg_bits = 0; bg_bits < 8; bg_bits++) { \
1256 if (bg_bits == 8) { \
1260 mask = 0x80 >> bg_bits; \
1262 for (bg_bits++; bg_bits < 8; bg_bits++) { \
1264 if (*ptr++ != bg) { \
1268 *buf++ = (CARD8)value; \
1276 for (; x < w; x++) { \
1277 if (*ptr++ != bg) { \
1282 *buf++ = (CARD8)value; \
1286 DEFINE_MONO_ENCODE_FUNCTION(8)
1287 DEFINE_MONO_ENCODE_FUNCTION(16)
1288 DEFINE_MONO_ENCODE_FUNCTION(32)
1292 * ``Gradient'' filter for 24-bit color samples.
1293 * Should be called only when redMax, greenMax and blueMax are 255.
1294 * Color components assumed to be byte-aligned.
1298 FilterGradient24(cl
, buf
, fmt
, w
, h
)
1301 rfbPixelFormat
*fmt
;
1308 int pixHere
[3], pixUpper
[3], pixLeft
[3], pixUpperLeft
[3];
1312 buf32
= (CARD32
*)buf
;
1313 memset (prevRowBuf
, 0, w
* 3 * sizeof(int));
1315 if (!cl
->screen
->rfbServerFormat
.bigEndian
== !fmt
->bigEndian
) {
1316 shiftBits
[0] = fmt
->redShift
;
1317 shiftBits
[1] = fmt
->greenShift
;
1318 shiftBits
[2] = fmt
->blueShift
;
1320 shiftBits
[0] = 24 - fmt
->redShift
;
1321 shiftBits
[1] = 24 - fmt
->greenShift
;
1322 shiftBits
[2] = 24 - fmt
->blueShift
;
1325 for (y
= 0; y
< h
; y
++) {
1326 for (c
= 0; c
< 3; c
++) {
1330 prevRowPtr
= prevRowBuf
;
1331 for (x
= 0; x
< w
; x
++) {
1333 for (c
= 0; c
< 3; c
++) {
1334 pixUpperLeft
[c
] = pixUpper
[c
];
1335 pixLeft
[c
] = pixHere
[c
];
1336 pixUpper
[c
] = *prevRowPtr
;
1337 pixHere
[c
] = (int)(pix32
>> shiftBits
[c
] & 0xFF);
1338 *prevRowPtr
++ = pixHere
[c
];
1340 prediction
= pixLeft
[c
] + pixUpper
[c
] - pixUpperLeft
[c
];
1341 if (prediction
< 0) {
1343 } else if (prediction
> 0xFF) {
1346 *buf
++ = (char)(pixHere
[c
] - prediction
);
1354 * ``Gradient'' filter for other color depths.
1357 #define DEFINE_GRADIENT_FILTER_FUNCTION(bpp) \
1360 FilterGradient##bpp(cl, buf, fmt, w, h) \
1363 rfbPixelFormat *fmt; \
1366 CARD##bpp pix, diff; \
1367 Bool endianMismatch; \
1369 int maxColor[3], shiftBits[3]; \
1370 int pixHere[3], pixUpper[3], pixLeft[3], pixUpperLeft[3]; \
1374 memset (prevRowBuf, 0, w * 3 * sizeof(int)); \
1376 endianMismatch = (!cl->screen->rfbServerFormat.bigEndian != !fmt->bigEndian); \
1378 maxColor[0] = fmt->redMax; \
1379 maxColor[1] = fmt->greenMax; \
1380 maxColor[2] = fmt->blueMax; \
1381 shiftBits[0] = fmt->redShift; \
1382 shiftBits[1] = fmt->greenShift; \
1383 shiftBits[2] = fmt->blueShift; \
1385 for (y = 0; y < h; y++) { \
1386 for (c = 0; c < 3; c++) { \
1390 prevRowPtr = prevRowBuf; \
1391 for (x = 0; x < w; x++) { \
1393 if (endianMismatch) { \
1394 pix = Swap##bpp(pix); \
1397 for (c = 0; c < 3; c++) { \
1398 pixUpperLeft[c] = pixUpper[c]; \
1399 pixLeft[c] = pixHere[c]; \
1400 pixUpper[c] = *prevRowPtr; \
1401 pixHere[c] = (int)(pix >> shiftBits[c] & maxColor[c]); \
1402 *prevRowPtr++ = pixHere[c]; \
1404 prediction = pixLeft[c] + pixUpper[c] - pixUpperLeft[c]; \
1405 if (prediction < 0) { \
1407 } else if (prediction > maxColor[c]) { \
1408 prediction = maxColor[c]; \
1410 diff |= ((pixHere[c] - prediction) & maxColor[c]) \
1413 if (endianMismatch) { \
1414 diff = Swap##bpp(diff); \
1421 DEFINE_GRADIENT_FILTER_FUNCTION(16)
1422 DEFINE_GRADIENT_FILTER_FUNCTION(32)
1426 * Code to guess if given rectangle is suitable for smooth image
1427 * compression (by applying "gradient" filter or JPEG coder).
1430 #define JPEG_MIN_RECT_SIZE 4096
1432 #define DETECT_SUBROW_WIDTH 7
1433 #define DETECT_MIN_WIDTH 8
1434 #define DETECT_MIN_HEIGHT 8
1437 DetectSmoothImage (cl
, fmt
, w
, h
)
1439 rfbPixelFormat
*fmt
;
1444 if ( cl
->screen
->rfbServerFormat
.bitsPerPixel
== 8 || fmt
->bitsPerPixel
== 8 ||
1445 w
< DETECT_MIN_WIDTH
|| h
< DETECT_MIN_HEIGHT
) {
1449 if (qualityLevel
!= -1) {
1450 if (w
* h
< JPEG_MIN_RECT_SIZE
) {
1454 if ( rfbTightDisableGradient
||
1455 w
* h
< tightConf
[compressLevel
].gradientMinRectSize
) {
1460 if (fmt
->bitsPerPixel
== 32) {
1461 if (usePixelFormat24
) {
1462 avgError
= DetectSmoothImage24(cl
, fmt
, w
, h
);
1463 if (qualityLevel
!= -1) {
1464 return (avgError
< tightConf
[qualityLevel
].jpegThreshold24
);
1466 return (avgError
< tightConf
[compressLevel
].gradientThreshold24
);
1468 avgError
= DetectSmoothImage32(cl
, fmt
, w
, h
);
1471 avgError
= DetectSmoothImage16(cl
, fmt
, w
, h
);
1473 if (qualityLevel
!= -1) {
1474 return (avgError
< tightConf
[qualityLevel
].jpegThreshold
);
1476 return (avgError
< tightConf
[compressLevel
].gradientThreshold
);
1479 static unsigned long
1480 DetectSmoothImage24 (cl
, fmt
, w
, h
)
1482 rfbPixelFormat
*fmt
;
1490 unsigned long avgError
;
1492 /* If client is big-endian, color samples begin from the second
1493 byte (offset 1) of a 32-bit pixel value. */
1494 off
= (fmt
->bigEndian
!= 0);
1496 memset(diffStat
, 0, 256*sizeof(int));
1499 while (y
< h
&& x
< w
) {
1500 for (d
= 0; d
< h
- y
&& d
< w
- x
- DETECT_SUBROW_WIDTH
; d
++) {
1501 for (c
= 0; c
< 3; c
++) {
1502 left
[c
] = (int)tightBeforeBuf
[((y
+d
)*w
+x
+d
)*4+off
+c
] & 0xFF;
1504 for (dx
= 1; dx
<= DETECT_SUBROW_WIDTH
; dx
++) {
1505 for (c
= 0; c
< 3; c
++) {
1506 pix
= (int)tightBeforeBuf
[((y
+d
)*w
+x
+d
+dx
)*4+off
+c
] & 0xFF;
1507 diffStat
[abs(pix
- left
[c
])]++;
1522 if (diffStat
[0] * 33 / pixelCount
>= 95)
1526 for (c
= 1; c
< 8; c
++) {
1527 avgError
+= (unsigned long)diffStat
[c
] * (unsigned long)(c
* c
);
1528 if (diffStat
[c
] == 0 || diffStat
[c
] > diffStat
[c
-1] * 2)
1531 for (; c
< 256; c
++) {
1532 avgError
+= (unsigned long)diffStat
[c
] * (unsigned long)(c
* c
);
1534 avgError
/= (pixelCount
* 3 - diffStat
[0]);
1539 #define DEFINE_DETECT_FUNCTION(bpp) \
1541 static unsigned long \
1542 DetectSmoothImage##bpp (cl, fmt, w, h) \
1544 rfbPixelFormat *fmt; \
1547 Bool endianMismatch; \
1549 int maxColor[3], shiftBits[3]; \
1550 int x, y, d, dx, c; \
1551 int diffStat[256]; \
1552 int pixelCount = 0; \
1553 int sample, sum, left[3]; \
1554 unsigned long avgError; \
1556 endianMismatch = (!cl->screen->rfbServerFormat.bigEndian != !fmt->bigEndian); \
1558 maxColor[0] = fmt->redMax; \
1559 maxColor[1] = fmt->greenMax; \
1560 maxColor[2] = fmt->blueMax; \
1561 shiftBits[0] = fmt->redShift; \
1562 shiftBits[1] = fmt->greenShift; \
1563 shiftBits[2] = fmt->blueShift; \
1565 memset(diffStat, 0, 256*sizeof(int)); \
1568 while (y < h && x < w) { \
1569 for (d = 0; d < h - y && d < w - x - DETECT_SUBROW_WIDTH; d++) { \
1570 pix = ((CARD##bpp *)tightBeforeBuf)[(y+d)*w+x+d]; \
1571 if (endianMismatch) { \
1572 pix = Swap##bpp(pix); \
1574 for (c = 0; c < 3; c++) { \
1575 left[c] = (int)(pix >> shiftBits[c] & maxColor[c]); \
1577 for (dx = 1; dx <= DETECT_SUBROW_WIDTH; dx++) { \
1578 pix = ((CARD##bpp *)tightBeforeBuf)[(y+d)*w+x+d+dx]; \
1579 if (endianMismatch) { \
1580 pix = Swap##bpp(pix); \
1583 for (c = 0; c < 3; c++) { \
1584 sample = (int)(pix >> shiftBits[c] & maxColor[c]); \
1585 sum += abs(sample - left[c]); \
1603 if ((diffStat[0] + diffStat[1]) * 100 / pixelCount >= 90) \
1607 for (c = 1; c < 8; c++) { \
1608 avgError += (unsigned long)diffStat[c] * (unsigned long)(c * c); \
1609 if (diffStat[c] == 0 || diffStat[c] > diffStat[c-1] * 2) \
1612 for (; c < 256; c++) { \
1613 avgError += (unsigned long)diffStat[c] * (unsigned long)(c * c); \
1615 avgError /= (pixelCount - diffStat[0]); \
1620 DEFINE_DETECT_FUNCTION(16)
1621 DEFINE_DETECT_FUNCTION(32)
1625 * JPEG compression stuff.
1628 static struct jpeg_destination_mgr jpegDstManager
;
1629 static Bool jpegError
;
1630 static int jpegDstDataLen
;
1633 SendJpegRect(cl
, x
, y
, w
, h
, quality
)
1638 struct jpeg_compress_struct cinfo
;
1639 struct jpeg_error_mgr jerr
;
1641 JSAMPROW rowPointer
[1];
1644 if (cl
->screen
->rfbServerFormat
.bitsPerPixel
== 8)
1645 return SendFullColorRect(cl
, w
, h
);
1647 srcBuf
= (CARD8
*)malloc(w
* 3);
1648 if (srcBuf
== NULL
) {
1649 return SendFullColorRect(cl
, w
, h
);
1651 rowPointer
[0] = srcBuf
;
1653 cinfo
.err
= jpeg_std_error(&jerr
);
1654 jpeg_create_compress(&cinfo
);
1656 cinfo
.image_width
= w
;
1657 cinfo
.image_height
= h
;
1658 cinfo
.input_components
= 3;
1659 cinfo
.in_color_space
= JCS_RGB
;
1661 jpeg_set_defaults(&cinfo
);
1662 jpeg_set_quality(&cinfo
, quality
, TRUE
);
1664 JpegSetDstManager (&cinfo
);
1666 jpeg_start_compress(&cinfo
, TRUE
);
1668 for (dy
= 0; dy
< h
; dy
++) {
1669 PrepareRowForJpeg(cl
, srcBuf
, x
, y
+ dy
, w
);
1670 jpeg_write_scanlines(&cinfo
, rowPointer
, 1);
1676 jpeg_finish_compress(&cinfo
);
1678 jpeg_destroy_compress(&cinfo
);
1682 return SendFullColorRect(cl
, w
, h
);
1684 if (cl
->ublen
+ TIGHT_MIN_TO_COMPRESS
+ 1 > UPDATE_BUF_SIZE
) {
1685 if (!rfbSendUpdateBuf(cl
))
1689 cl
->updateBuf
[cl
->ublen
++] = (char)(rfbTightJpeg
<< 4);
1690 cl
->rfbBytesSent
[rfbEncodingTight
]++;
1692 return SendCompressedData(cl
, jpegDstDataLen
);
1696 PrepareRowForJpeg(cl
, dst
, x
, y
, count
)
1701 if (cl
->screen
->rfbServerFormat
.bitsPerPixel
== 32) {
1702 if ( cl
->screen
->rfbServerFormat
.redMax
== 0xFF &&
1703 cl
->screen
->rfbServerFormat
.greenMax
== 0xFF &&
1704 cl
->screen
->rfbServerFormat
.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(cl
, dst
, x
, y
, count
)
1725 &cl
->screen
->frameBuffer
[y
* cl
->screen
->paddedWidthInBytes
+ x
* 4];
1729 *dst
++ = (CARD8
)(pix
>> cl
->screen
->rfbServerFormat
.redShift
);
1730 *dst
++ = (CARD8
)(pix
>> cl
->screen
->rfbServerFormat
.greenShift
);
1731 *dst
++ = (CARD8
)(pix
>> cl
->screen
->rfbServerFormat
.blueShift
);
1735 #define DEFINE_JPEG_GET_ROW_FUNCTION(bpp) \
1738 PrepareRowForJpeg##bpp(cl, dst, x, y, count) \
1745 int inRed, inGreen, inBlue; \
1747 fbptr = (CARD##bpp *) \
1748 &cl->screen->frameBuffer[y * cl->screen->paddedWidthInBytes + \
1755 (pix >> cl->screen->rfbServerFormat.redShift & cl->screen->rfbServerFormat.redMax); \
1757 (pix >> cl->screen->rfbServerFormat.greenShift & cl->screen->rfbServerFormat.greenMax); \
1759 (pix >> cl->screen->rfbServerFormat.blueShift & cl->screen->rfbServerFormat.blueMax); \
1761 *dst++ = (CARD8)((inRed * 255 + cl->screen->rfbServerFormat.redMax / 2) / \
1762 cl->screen->rfbServerFormat.redMax); \
1763 *dst++ = (CARD8)((inGreen * 255 + cl->screen->rfbServerFormat.greenMax / 2) / \
1764 cl->screen->rfbServerFormat.greenMax); \
1765 *dst++ = (CARD8)((inBlue * 255 + cl->screen->rfbServerFormat.blueMax / 2) / \
1766 cl->screen->rfbServerFormat.blueMax); \
1770 DEFINE_JPEG_GET_ROW_FUNCTION(16)
1771 DEFINE_JPEG_GET_ROW_FUNCTION(32)
1774 * Destination manager implementation for JPEG library.
1778 JpegInitDestination(j_compress_ptr cinfo
)
1781 jpegDstManager
.next_output_byte
= (JOCTET
*)tightAfterBuf
;
1782 jpegDstManager
.free_in_buffer
= (size_t)tightAfterBufSize
;
1786 JpegEmptyOutputBuffer(j_compress_ptr cinfo
)
1789 jpegDstManager
.next_output_byte
= (JOCTET
*)tightAfterBuf
;
1790 jpegDstManager
.free_in_buffer
= (size_t)tightAfterBufSize
;
1796 JpegTermDestination(j_compress_ptr cinfo
)
1798 jpegDstDataLen
= tightAfterBufSize
- jpegDstManager
.free_in_buffer
;
1802 JpegSetDstManager(j_compress_ptr cinfo
)
1804 jpegDstManager
.init_destination
= JpegInitDestination
;
1805 jpegDstManager
.empty_output_buffer
= JpegEmptyOutputBuffer
;
1806 jpegDstManager
.term_destination
= JpegTermDestination
;
1807 cinfo
->dest
= &jpegDstManager
;