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>*/
36
#define NEEDFAR_POINTERS
41
/* Note: The following constant should not be changed. */
42
#define TIGHT_MIN_TO_COMPRESS 12
44
/* The parameters below may be adjusted. */
45
#define MIN_SPLIT_RECT_SIZE 4096
46
#define MIN_SOLID_SUBRECT_SIZE 2048
47
#define MAX_SPLIT_TILE_SIZE 16
49
/* May be set to TRUE with "-lazytight" Xvnc option. */
50
rfbBool rfbTightDisableGradient = FALSE;
52
/* This variable is set on every rfbSendRectEncodingTight() call. */
53
static rfbBool usePixelFormat24;
56
/* Compression level stuff. The following array contains various
57
encoder parameters for each of 10 compression levels (0..9).
58
Last three parameters correspond to JPEG quality levels (0..9). */
60
typedef struct TIGHT_CONF_s {
61
int maxRectSize, maxRectWidth;
62
int monoMinRectSize, gradientMinRectSize;
63
int idxZlibLevel, monoZlibLevel, rawZlibLevel, gradientZlibLevel;
64
int gradientThreshold, gradientThreshold24;
65
int idxMaxColorsDivisor;
66
int jpegQuality, jpegThreshold, jpegThreshold24;
69
static TIGHT_CONF tightConf[10] = {
70
{ 512, 32, 6, 65536, 0, 0, 0, 0, 0, 0, 4, 5, 10000, 23000 },
71
{ 2048, 128, 6, 65536, 1, 1, 1, 0, 0, 0, 8, 10, 8000, 18000 },
72
{ 6144, 256, 8, 65536, 3, 3, 2, 0, 0, 0, 24, 15, 6500, 15000 },
73
{ 10240, 1024, 12, 65536, 5, 5, 3, 0, 0, 0, 32, 25, 5000, 12000 },
74
{ 16384, 2048, 12, 65536, 6, 6, 4, 0, 0, 0, 32, 37, 4000, 10000 },
75
{ 32768, 2048, 12, 4096, 7, 7, 5, 4, 150, 380, 32, 50, 3000, 8000 },
76
{ 65536, 2048, 16, 4096, 7, 7, 6, 4, 170, 420, 48, 60, 2000, 5000 },
77
{ 65536, 2048, 16, 4096, 8, 8, 7, 5, 180, 450, 64, 70, 1000, 2500 },
78
{ 65536, 2048, 32, 8192, 9, 9, 8, 6, 190, 475, 64, 75, 500, 1200 },
79
{ 65536, 2048, 32, 8192, 9, 9, 9, 6, 200, 500, 96, 80, 200, 500 }
82
static int compressLevel;
83
static int qualityLevel;
85
/* Stuff dealing with palettes. */
87
typedef struct COLOR_LIST_s {
88
struct COLOR_LIST_s *next;
93
typedef struct PALETTE_ENTRY_s {
98
typedef struct PALETTE_s {
99
PALETTE_ENTRY entry[256];
100
COLOR_LIST *hash[256];
101
COLOR_LIST list[256];
104
static int paletteNumColors, paletteMaxColors;
105
static uint32_t monoBackground, monoForeground;
106
static PALETTE palette;
108
/* Pointers to dynamically-allocated buffers. */
110
static int tightBeforeBufSize = 0;
111
static char *tightBeforeBuf = NULL;
113
static int tightAfterBufSize = 0;
114
static char *tightAfterBuf = NULL;
116
static int *prevRowBuf = NULL;
118
void rfbTightCleanup()
120
if(tightBeforeBufSize) {
121
free(tightBeforeBuf);
122
tightBeforeBufSize=0;
124
if(tightAfterBufSize) {
130
/* Prototypes for static functions. */
132
static void FindBestSolidArea (rfbClientPtr cl, int x, int y, int w, int h,
133
uint32_t colorValue, int *w_ptr, int *h_ptr);
134
static void ExtendSolidArea (rfbClientPtr cl, int x, int y, int w, int h,
136
int *x_ptr, int *y_ptr, int *w_ptr, int *h_ptr);
137
static rfbBool CheckSolidTile (rfbClientPtr cl, int x, int y, int w, int h,
138
uint32_t *colorPtr, rfbBool needSameColor);
139
static rfbBool CheckSolidTile8 (rfbClientPtr cl, int x, int y, int w, int h,
140
uint32_t *colorPtr, rfbBool needSameColor);
141
static rfbBool CheckSolidTile16 (rfbClientPtr cl, int x, int y, int w, int h,
142
uint32_t *colorPtr, rfbBool needSameColor);
143
static rfbBool CheckSolidTile32 (rfbClientPtr cl, int x, int y, int w, int h,
144
uint32_t *colorPtr, rfbBool needSameColor);
146
static rfbBool SendRectSimple (rfbClientPtr cl, int x, int y, int w, int h);
147
static rfbBool SendSubrect (rfbClientPtr cl, int x, int y, int w, int h);
148
static rfbBool SendTightHeader (rfbClientPtr cl, int x, int y, int w, int h);
150
static rfbBool SendSolidRect (rfbClientPtr cl);
151
static rfbBool SendMonoRect (rfbClientPtr cl, int w, int h);
152
static rfbBool SendIndexedRect (rfbClientPtr cl, int w, int h);
153
static rfbBool SendFullColorRect (rfbClientPtr cl, int w, int h);
154
static rfbBool SendGradientRect (rfbClientPtr cl, int w, int h);
156
static rfbBool CompressData(rfbClientPtr cl, int streamId, int dataLen,
157
int zlibLevel, int zlibStrategy);
158
static rfbBool SendCompressedData(rfbClientPtr cl, int compressedLen);
160
static void FillPalette8(int count);
161
static void FillPalette16(int count);
162
static void FillPalette32(int count);
164
static void PaletteReset(void);
165
static int PaletteInsert(uint32_t rgb, int numPixels, int bpp);
167
static void Pack24(rfbClientPtr cl, char *buf, rfbPixelFormat *fmt, int count);
169
static void EncodeIndexedRect16(uint8_t *buf, int count);
170
static void EncodeIndexedRect32(uint8_t *buf, int count);
172
static void EncodeMonoRect8(uint8_t *buf, int w, int h);
173
static void EncodeMonoRect16(uint8_t *buf, int w, int h);
174
static void EncodeMonoRect32(uint8_t *buf, int w, int h);
176
static void FilterGradient24(rfbClientPtr cl, char *buf, rfbPixelFormat *fmt, int w, int h);
177
static void FilterGradient16(rfbClientPtr cl, uint16_t *buf, rfbPixelFormat *fmt, int w, int h);
178
static void FilterGradient32(rfbClientPtr cl, uint32_t *buf, rfbPixelFormat *fmt, int w, int h);
180
static int DetectSmoothImage(rfbClientPtr cl, rfbPixelFormat *fmt, int w, int h);
181
static unsigned long DetectSmoothImage24(rfbClientPtr cl, rfbPixelFormat *fmt, int w, int h);
182
static unsigned long DetectSmoothImage16(rfbClientPtr cl, rfbPixelFormat *fmt, int w, int h);
183
static unsigned long DetectSmoothImage32(rfbClientPtr cl, rfbPixelFormat *fmt, int w, int h);
185
static rfbBool SendJpegRect(rfbClientPtr cl, int x, int y, int w, int h,
187
static void PrepareRowForJpeg(rfbClientPtr cl, uint8_t *dst, int x, int y, int count);
188
static void PrepareRowForJpeg24(rfbClientPtr cl, uint8_t *dst, int x, int y, int count);
189
static void PrepareRowForJpeg16(rfbClientPtr cl, uint8_t *dst, int x, int y, int count);
190
static void PrepareRowForJpeg32(rfbClientPtr cl, uint8_t *dst, int x, int y, int count);
192
static void JpegInitDestination(j_compress_ptr cinfo);
193
static boolean JpegEmptyOutputBuffer(j_compress_ptr cinfo);
194
static void JpegTermDestination(j_compress_ptr cinfo);
195
static void JpegSetDstManager(j_compress_ptr cinfo);
199
* Tight encoding implementation.
203
rfbNumCodedRectsTight(cl, x, y, w, h)
207
int maxRectSize, maxRectWidth;
208
int subrectMaxWidth, subrectMaxHeight;
210
/* No matter how many rectangles we will send if LastRect markers
211
are used to terminate rectangle stream. */
212
if (cl->enableLastRectEncoding && w * h >= MIN_SPLIT_RECT_SIZE)
215
maxRectSize = tightConf[cl->tightCompressLevel].maxRectSize;
216
maxRectWidth = tightConf[cl->tightCompressLevel].maxRectWidth;
218
if (w > maxRectWidth || w * h > maxRectSize) {
219
subrectMaxWidth = (w > maxRectWidth) ? maxRectWidth : w;
220
subrectMaxHeight = maxRectSize / subrectMaxWidth;
221
return (((w - 1) / maxRectWidth + 1) *
222
((h - 1) / subrectMaxHeight + 1));
229
rfbSendRectEncodingTight(cl, x, y, w, h)
236
int x_best, y_best, w_best, h_best;
239
rfbSendUpdateBuf(cl);
241
compressLevel = cl->tightCompressLevel;
242
qualityLevel = cl->tightQualityLevel;
244
if ( cl->format.depth == 24 && cl->format.redMax == 0xFF &&
245
cl->format.greenMax == 0xFF && cl->format.blueMax == 0xFF ) {
246
usePixelFormat24 = TRUE;
248
usePixelFormat24 = FALSE;
251
if (!cl->enableLastRectEncoding || w * h < MIN_SPLIT_RECT_SIZE)
252
return SendRectSimple(cl, x, y, w, h);
254
/* Make sure we can write at least one pixel into tightBeforeBuf. */
256
if (tightBeforeBufSize < 4) {
257
tightBeforeBufSize = 4;
258
if (tightBeforeBuf == NULL)
259
tightBeforeBuf = (char *)malloc(tightBeforeBufSize);
261
tightBeforeBuf = (char *)realloc(tightBeforeBuf,
265
/* Calculate maximum number of rows in one non-solid rectangle. */
268
int maxRectSize, maxRectWidth, nMaxWidth;
270
maxRectSize = tightConf[compressLevel].maxRectSize;
271
maxRectWidth = tightConf[compressLevel].maxRectWidth;
272
nMaxWidth = (w > maxRectWidth) ? maxRectWidth : w;
273
nMaxRows = maxRectSize / nMaxWidth;
276
/* Try to find large solid-color areas and send them separately. */
278
for (dy = y; dy < y + h; dy += MAX_SPLIT_TILE_SIZE) {
280
/* If a rectangle becomes too large, send its upper part now. */
282
if (dy - y >= nMaxRows) {
283
if (!SendRectSimple(cl, x, y, w, nMaxRows))
289
dh = (dy + MAX_SPLIT_TILE_SIZE <= y + h) ?
290
MAX_SPLIT_TILE_SIZE : (y + h - dy);
292
for (dx = x; dx < x + w; dx += MAX_SPLIT_TILE_SIZE) {
294
dw = (dx + MAX_SPLIT_TILE_SIZE <= x + w) ?
295
MAX_SPLIT_TILE_SIZE : (x + w - dx);
297
if (CheckSolidTile(cl, dx, dy, dw, dh, &colorValue, FALSE)) {
299
/* Get dimensions of solid-color area. */
301
FindBestSolidArea(cl, dx, dy, w - (dx - x), h - (dy - y),
302
colorValue, &w_best, &h_best);
304
/* Make sure a solid rectangle is large enough
305
(or the whole rectangle is of the same color). */
307
if ( w_best * h_best != w * h &&
308
w_best * h_best < MIN_SOLID_SUBRECT_SIZE )
311
/* Try to extend solid rectangle to maximum size. */
313
x_best = dx; y_best = dy;
314
ExtendSolidArea(cl, x, y, w, h, colorValue,
315
&x_best, &y_best, &w_best, &h_best);
317
/* Send rectangles at top and left to solid-color area. */
320
!SendRectSimple(cl, x, y, w, y_best-y) )
323
!rfbSendRectEncodingTight(cl, x, y_best,
327
/* Send solid-color rectangle. */
329
if (!SendTightHeader(cl, x_best, y_best, w_best, h_best))
332
fbptr = (cl->screen->frameBuffer +
333
(cl->screen->paddedWidthInBytes * y_best) +
334
(x_best * (cl->screen->bitsPerPixel / 8)));
336
(*cl->translateFn)(cl->translateLookupTable, &cl->screen->rfbServerFormat,
337
&cl->format, fbptr, tightBeforeBuf,
338
cl->screen->paddedWidthInBytes, 1, 1);
340
if (!SendSolidRect(cl))
343
/* Send remaining rectangles (at right and bottom). */
345
if ( x_best + w_best != x + w &&
346
!rfbSendRectEncodingTight(cl, x_best+w_best, y_best,
347
w-(x_best-x)-w_best, h_best) )
349
if ( y_best + h_best != y + h &&
350
!rfbSendRectEncodingTight(cl, x, y_best+h_best,
351
w, h-(y_best-y)-h_best) )
354
/* Return after all recursive calls are done. */
363
/* No suitable solid-color rectangles found. */
365
return SendRectSimple(cl, x, y, w, h);
369
FindBestSolidArea(cl, x, y, w, h, colorValue, w_ptr, h_ptr)
377
int w_best = 0, h_best = 0;
381
for (dy = y; dy < y + h; dy += MAX_SPLIT_TILE_SIZE) {
383
dh = (dy + MAX_SPLIT_TILE_SIZE <= y + h) ?
384
MAX_SPLIT_TILE_SIZE : (y + h - dy);
385
dw = (w_prev > MAX_SPLIT_TILE_SIZE) ?
386
MAX_SPLIT_TILE_SIZE : w_prev;
388
if (!CheckSolidTile(cl, x, dy, dw, dh, &colorValue, TRUE))
391
for (dx = x + dw; dx < x + w_prev;) {
392
dw = (dx + MAX_SPLIT_TILE_SIZE <= x + w_prev) ?
393
MAX_SPLIT_TILE_SIZE : (x + w_prev - dx);
394
if (!CheckSolidTile(cl, dx, dy, dw, dh, &colorValue, TRUE))
400
if (w_prev * (dy + dh - y) > w_best * h_best) {
402
h_best = dy + dh - y;
411
ExtendSolidArea(cl, x, y, w, h, colorValue, x_ptr, y_ptr, w_ptr, h_ptr)
415
int *x_ptr, *y_ptr, *w_ptr, *h_ptr;
419
/* Try to extend the area upwards. */
420
for ( cy = *y_ptr - 1;
421
cy >= y && CheckSolidTile(cl, *x_ptr, cy, *w_ptr, 1, &colorValue, TRUE);
423
*h_ptr += *y_ptr - (cy + 1);
427
for ( cy = *y_ptr + *h_ptr;
429
CheckSolidTile(cl, *x_ptr, cy, *w_ptr, 1, &colorValue, TRUE);
431
*h_ptr += cy - (*y_ptr + *h_ptr);
433
/* ... to the left. */
434
for ( cx = *x_ptr - 1;
435
cx >= x && CheckSolidTile(cl, cx, *y_ptr, 1, *h_ptr, &colorValue, TRUE);
437
*w_ptr += *x_ptr - (cx + 1);
440
/* ... to the right. */
441
for ( cx = *x_ptr + *w_ptr;
443
CheckSolidTile(cl, cx, *y_ptr, 1, *h_ptr, &colorValue, TRUE);
445
*w_ptr += cx - (*x_ptr + *w_ptr);
449
* Check if a rectangle is all of the same color. If needSameColor is
450
* set to non-zero, then also check that its color equals to the
451
* *colorPtr value. The result is 1 if the test is successfull, and in
452
* that case new color will be stored in *colorPtr.
455
static rfbBool CheckSolidTile(rfbClientPtr cl, int x, int y, int w, int h, uint32_t* colorPtr, rfbBool needSameColor)
457
switch(cl->screen->rfbServerFormat.bitsPerPixel) {
459
return CheckSolidTile32(cl, x, y, w, h, colorPtr, needSameColor);
461
return CheckSolidTile16(cl, x, y, w, h, colorPtr, needSameColor);
463
return CheckSolidTile8(cl, x, y, w, h, colorPtr, needSameColor);
467
#define DEFINE_CHECK_SOLID_FUNCTION(bpp) \
470
CheckSolidTile##bpp(rfbClientPtr cl, int x, int y, int w, int h, uint32_t* colorPtr, rfbBool needSameColor) \
472
uint##bpp##_t *fbptr; \
473
uint##bpp##_t colorValue; \
476
fbptr = (uint##bpp##_t *) \
477
&cl->screen->frameBuffer[y * cl->screen->paddedWidthInBytes + x * (bpp/8)]; \
479
colorValue = *fbptr; \
480
if (needSameColor && (uint32_t)colorValue != *colorPtr) \
483
for (dy = 0; dy < h; dy++) { \
484
for (dx = 0; dx < w; dx++) { \
485
if (colorValue != fbptr[dx]) \
488
fbptr = (uint##bpp##_t *)((uint8_t *)fbptr + cl->screen->paddedWidthInBytes); \
491
*colorPtr = (uint32_t)colorValue; \
495
DEFINE_CHECK_SOLID_FUNCTION(8)
496
DEFINE_CHECK_SOLID_FUNCTION(16)
497
DEFINE_CHECK_SOLID_FUNCTION(32)
500
SendRectSimple(cl, x, y, w, h)
504
int maxBeforeSize, maxAfterSize;
505
int maxRectSize, maxRectWidth;
506
int subrectMaxWidth, subrectMaxHeight;
510
maxRectSize = tightConf[compressLevel].maxRectSize;
511
maxRectWidth = tightConf[compressLevel].maxRectWidth;
513
maxBeforeSize = maxRectSize * (cl->format.bitsPerPixel / 8);
514
maxAfterSize = maxBeforeSize + (maxBeforeSize + 99) / 100 + 12;
516
if (tightBeforeBufSize < maxBeforeSize) {
517
tightBeforeBufSize = maxBeforeSize;
518
if (tightBeforeBuf == NULL)
519
tightBeforeBuf = (char *)malloc(tightBeforeBufSize);
521
tightBeforeBuf = (char *)realloc(tightBeforeBuf,
525
if (tightAfterBufSize < maxAfterSize) {
526
tightAfterBufSize = maxAfterSize;
527
if (tightAfterBuf == NULL)
528
tightAfterBuf = (char *)malloc(tightAfterBufSize);
530
tightAfterBuf = (char *)realloc(tightAfterBuf,
534
if (w > maxRectWidth || w * h > maxRectSize) {
535
subrectMaxWidth = (w > maxRectWidth) ? maxRectWidth : w;
536
subrectMaxHeight = maxRectSize / subrectMaxWidth;
538
for (dy = 0; dy < h; dy += subrectMaxHeight) {
539
for (dx = 0; dx < w; dx += maxRectWidth) {
540
rw = (dx + maxRectWidth < w) ? maxRectWidth : w - dx;
541
rh = (dy + subrectMaxHeight < h) ? subrectMaxHeight : h - dy;
542
if (!SendSubrect(cl, x+dx, y+dy, rw, rh))
547
if (!SendSubrect(cl, x, y, w, h))
555
SendSubrect(cl, x, y, w, h)
560
rfbBool success = FALSE;
562
/* Send pending data if there is more than 128 bytes. */
563
if (cl->ublen > 128) {
564
if (!rfbSendUpdateBuf(cl))
568
if (!SendTightHeader(cl, x, y, w, h))
571
fbptr = (cl->screen->frameBuffer + (cl->screen->paddedWidthInBytes * y)
572
+ (x * (cl->screen->bitsPerPixel / 8)));
574
(*cl->translateFn)(cl->translateLookupTable, &cl->screen->rfbServerFormat,
575
&cl->format, fbptr, tightBeforeBuf,
576
cl->screen->paddedWidthInBytes, w, h);
578
paletteMaxColors = w * h / tightConf[compressLevel].idxMaxColorsDivisor;
579
if ( paletteMaxColors < 2 &&
580
w * h >= tightConf[compressLevel].monoMinRectSize ) {
581
paletteMaxColors = 2;
583
switch (cl->format.bitsPerPixel) {
588
FillPalette16(w * h);
591
FillPalette32(w * h);
594
switch (paletteNumColors) {
596
/* Truecolor image */
597
if (DetectSmoothImage(cl, &cl->format, w, h)) {
598
if (qualityLevel != -1) {
599
success = SendJpegRect(cl, x, y, w, h,
600
tightConf[qualityLevel].jpegQuality);
602
success = SendGradientRect(cl, w, h);
605
success = SendFullColorRect(cl, w, h);
609
/* Solid rectangle */
610
success = SendSolidRect(cl);
613
/* Two-color rectangle */
614
success = SendMonoRect(cl, w, h);
617
/* Up to 256 different colors */
618
if ( paletteNumColors > 96 &&
619
qualityLevel != -1 && qualityLevel <= 3 &&
620
DetectSmoothImage(cl, &cl->format, w, h) ) {
621
success = SendJpegRect(cl, x, y, w, h,
622
tightConf[qualityLevel].jpegQuality);
624
success = SendIndexedRect(cl, w, h);
631
SendTightHeader(cl, x, y, w, h)
635
rfbFramebufferUpdateRectHeader rect;
637
if (cl->ublen + sz_rfbFramebufferUpdateRectHeader > UPDATE_BUF_SIZE) {
638
if (!rfbSendUpdateBuf(cl))
642
rect.r.x = Swap16IfLE(x);
643
rect.r.y = Swap16IfLE(y);
644
rect.r.w = Swap16IfLE(w);
645
rect.r.h = Swap16IfLE(h);
646
rect.encoding = Swap32IfLE(rfbEncodingTight);
648
memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,
649
sz_rfbFramebufferUpdateRectHeader);
650
cl->ublen += sz_rfbFramebufferUpdateRectHeader;
652
cl->rfbRectanglesSent[rfbEncodingTight]++;
653
cl->rfbBytesSent[rfbEncodingTight] += sz_rfbFramebufferUpdateRectHeader;
659
* Subencoding implementations.
668
if (usePixelFormat24) {
669
Pack24(cl, tightBeforeBuf, &cl->format, 1);
672
len = cl->format.bitsPerPixel / 8;
674
if (cl->ublen + 1 + len > UPDATE_BUF_SIZE) {
675
if (!rfbSendUpdateBuf(cl))
679
cl->updateBuf[cl->ublen++] = (char)(rfbTightFill << 4);
680
memcpy (&cl->updateBuf[cl->ublen], tightBeforeBuf, len);
683
cl->rfbBytesSent[rfbEncodingTight] += len + 1;
689
SendMonoRect(cl, w, h)
694
int paletteLen, dataLen;
696
if ( cl->ublen + TIGHT_MIN_TO_COMPRESS + 6 +
697
2 * cl->format.bitsPerPixel / 8 > UPDATE_BUF_SIZE ) {
698
if (!rfbSendUpdateBuf(cl))
702
/* Prepare tight encoding header. */
703
dataLen = (w + 7) / 8;
706
cl->updateBuf[cl->ublen++] = (streamId | rfbTightExplicitFilter) << 4;
707
cl->updateBuf[cl->ublen++] = rfbTightFilterPalette;
708
cl->updateBuf[cl->ublen++] = 1;
710
/* Prepare palette, convert image. */
711
switch (cl->format.bitsPerPixel) {
714
EncodeMonoRect32((uint8_t *)tightBeforeBuf, w, h);
716
((uint32_t *)tightAfterBuf)[0] = monoBackground;
717
((uint32_t *)tightAfterBuf)[1] = monoForeground;
718
if (usePixelFormat24) {
719
Pack24(cl, tightAfterBuf, &cl->format, 2);
724
memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf, paletteLen);
725
cl->ublen += paletteLen;
726
cl->rfbBytesSent[rfbEncodingTight] += 3 + paletteLen;
730
EncodeMonoRect16((uint8_t *)tightBeforeBuf, w, h);
732
((uint16_t *)tightAfterBuf)[0] = (uint16_t)monoBackground;
733
((uint16_t *)tightAfterBuf)[1] = (uint16_t)monoForeground;
735
memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf, 4);
737
cl->rfbBytesSent[rfbEncodingTight] += 7;
741
EncodeMonoRect8((uint8_t *)tightBeforeBuf, w, h);
743
cl->updateBuf[cl->ublen++] = (char)monoBackground;
744
cl->updateBuf[cl->ublen++] = (char)monoForeground;
745
cl->rfbBytesSent[rfbEncodingTight] += 5;
748
return CompressData(cl, streamId, dataLen,
749
tightConf[compressLevel].monoZlibLevel,
754
SendIndexedRect(cl, w, h)
761
if ( cl->ublen + TIGHT_MIN_TO_COMPRESS + 6 +
762
paletteNumColors * cl->format.bitsPerPixel / 8 >
764
if (!rfbSendUpdateBuf(cl))
768
/* Prepare tight encoding header. */
769
cl->updateBuf[cl->ublen++] = (streamId | rfbTightExplicitFilter) << 4;
770
cl->updateBuf[cl->ublen++] = rfbTightFilterPalette;
771
cl->updateBuf[cl->ublen++] = (char)(paletteNumColors - 1);
773
/* Prepare palette, convert image. */
774
switch (cl->format.bitsPerPixel) {
777
EncodeIndexedRect32((uint8_t *)tightBeforeBuf, w * h);
779
for (i = 0; i < paletteNumColors; i++) {
780
((uint32_t *)tightAfterBuf)[i] =
781
palette.entry[i].listNode->rgb;
783
if (usePixelFormat24) {
784
Pack24(cl, tightAfterBuf, &cl->format, paletteNumColors);
789
memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf, paletteNumColors * entryLen);
790
cl->ublen += paletteNumColors * entryLen;
791
cl->rfbBytesSent[rfbEncodingTight] += 3 + paletteNumColors * entryLen;
795
EncodeIndexedRect16((uint8_t *)tightBeforeBuf, w * h);
797
for (i = 0; i < paletteNumColors; i++) {
798
((uint16_t *)tightAfterBuf)[i] =
799
(uint16_t)palette.entry[i].listNode->rgb;
802
memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf, paletteNumColors * 2);
803
cl->ublen += paletteNumColors * 2;
804
cl->rfbBytesSent[rfbEncodingTight] += 3 + paletteNumColors * 2;
808
return FALSE; /* Should never happen. */
811
return CompressData(cl, streamId, w * h,
812
tightConf[compressLevel].idxZlibLevel,
817
SendFullColorRect(cl, w, h)
824
if (cl->ublen + TIGHT_MIN_TO_COMPRESS + 1 > UPDATE_BUF_SIZE) {
825
if (!rfbSendUpdateBuf(cl))
829
cl->updateBuf[cl->ublen++] = 0x00; /* stream id = 0, no flushing, no filter */
830
cl->rfbBytesSent[rfbEncodingTight]++;
832
if (usePixelFormat24) {
833
Pack24(cl, tightBeforeBuf, &cl->format, w * h);
836
len = cl->format.bitsPerPixel / 8;
838
return CompressData(cl, streamId, w * h * len,
839
tightConf[compressLevel].rawZlibLevel,
844
SendGradientRect(cl, w, h)
851
if (cl->format.bitsPerPixel == 8)
852
return SendFullColorRect(cl, w, h);
854
if (cl->ublen + TIGHT_MIN_TO_COMPRESS + 2 > UPDATE_BUF_SIZE) {
855
if (!rfbSendUpdateBuf(cl))
859
if (prevRowBuf == NULL)
860
prevRowBuf = (int *)malloc(2048 * 3 * sizeof(int));
862
cl->updateBuf[cl->ublen++] = (streamId | rfbTightExplicitFilter) << 4;
863
cl->updateBuf[cl->ublen++] = rfbTightFilterGradient;
864
cl->rfbBytesSent[rfbEncodingTight] += 2;
866
if (usePixelFormat24) {
867
FilterGradient24(cl, tightBeforeBuf, &cl->format, w, h);
869
} else if (cl->format.bitsPerPixel == 32) {
870
FilterGradient32(cl, (uint32_t *)tightBeforeBuf, &cl->format, w, h);
873
FilterGradient16(cl, (uint16_t *)tightBeforeBuf, &cl->format, w, h);
877
return CompressData(cl, streamId, w * h * len,
878
tightConf[compressLevel].gradientZlibLevel,
883
CompressData(cl, streamId, dataLen, zlibLevel, zlibStrategy)
885
int streamId, dataLen, zlibLevel, zlibStrategy;
890
if (dataLen < TIGHT_MIN_TO_COMPRESS) {
891
memcpy(&cl->updateBuf[cl->ublen], tightBeforeBuf, dataLen);
892
cl->ublen += dataLen;
893
cl->rfbBytesSent[rfbEncodingTight] += dataLen;
897
pz = &cl->zsStruct[streamId];
899
/* Initialize compression stream if needed. */
900
if (!cl->zsActive[streamId]) {
905
err = deflateInit2 (pz, zlibLevel, Z_DEFLATED, MAX_WBITS,
906
MAX_MEM_LEVEL, zlibStrategy);
910
cl->zsActive[streamId] = TRUE;
911
cl->zsLevel[streamId] = zlibLevel;
914
/* Prepare buffer pointers. */
915
pz->next_in = (Bytef *)tightBeforeBuf;
916
pz->avail_in = dataLen;
917
pz->next_out = (Bytef *)tightAfterBuf;
918
pz->avail_out = tightAfterBufSize;
920
/* Change compression parameters if needed. */
921
if (zlibLevel != cl->zsLevel[streamId]) {
922
if (deflateParams (pz, zlibLevel, zlibStrategy) != Z_OK) {
925
cl->zsLevel[streamId] = zlibLevel;
928
/* Actual compression. */
929
if ( deflate (pz, Z_SYNC_FLUSH) != Z_OK ||
930
pz->avail_in != 0 || pz->avail_out == 0 ) {
934
return SendCompressedData(cl, tightAfterBufSize - pz->avail_out);
937
static rfbBool SendCompressedData(cl, compressedLen)
943
cl->updateBuf[cl->ublen++] = compressedLen & 0x7F;
944
cl->rfbBytesSent[rfbEncodingTight]++;
945
if (compressedLen > 0x7F) {
946
cl->updateBuf[cl->ublen-1] |= 0x80;
947
cl->updateBuf[cl->ublen++] = compressedLen >> 7 & 0x7F;
948
cl->rfbBytesSent[rfbEncodingTight]++;
949
if (compressedLen > 0x3FFF) {
950
cl->updateBuf[cl->ublen-1] |= 0x80;
951
cl->updateBuf[cl->ublen++] = compressedLen >> 14 & 0xFF;
952
cl->rfbBytesSent[rfbEncodingTight]++;
956
portionLen = UPDATE_BUF_SIZE;
957
for (i = 0; i < compressedLen; i += portionLen) {
958
if (i + portionLen > compressedLen) {
959
portionLen = compressedLen - i;
961
if (cl->ublen + portionLen > UPDATE_BUF_SIZE) {
962
if (!rfbSendUpdateBuf(cl))
965
memcpy(&cl->updateBuf[cl->ublen], &tightAfterBuf[i], portionLen);
966
cl->ublen += portionLen;
968
cl->rfbBytesSent[rfbEncodingTight] += compressedLen;
974
* Code to determine how many different colors used in rectangle.
981
uint8_t *data = (uint8_t *)tightBeforeBuf;
985
paletteNumColors = 0;
988
for (i = 1; i < count && data[i] == c0; i++);
990
paletteNumColors = 1;
991
return; /* Solid rectangle */
994
if (paletteMaxColors < 2)
1000
for (i++; i < count; i++) {
1001
if (data[i] == c0) {
1003
} else if (data[i] == c1) {
1010
monoBackground = (uint32_t)c0;
1011
monoForeground = (uint32_t)c1;
1013
monoBackground = (uint32_t)c1;
1014
monoForeground = (uint32_t)c0;
1016
paletteNumColors = 2; /* Two colors */
1020
#define DEFINE_FILL_PALETTE_FUNCTION(bpp) \
1023
FillPalette##bpp(count) \
1026
uint##bpp##_t *data = (uint##bpp##_t *)tightBeforeBuf; \
1027
uint##bpp##_t c0, c1, ci; \
1028
int i, n0, n1, ni; \
1031
for (i = 1; i < count && data[i] == c0; i++); \
1033
paletteNumColors = 1; /* Solid rectangle */ \
1037
if (paletteMaxColors < 2) { \
1038
paletteNumColors = 0; /* Full-color encoding preferred */ \
1045
for (i++; i < count; i++) { \
1049
} else if (ci == c1) { \
1056
monoBackground = (uint32_t)c0; \
1057
monoForeground = (uint32_t)c1; \
1059
monoBackground = (uint32_t)c1; \
1060
monoForeground = (uint32_t)c0; \
1062
paletteNumColors = 2; /* Two colors */ \
1067
PaletteInsert (c0, (uint32_t)n0, bpp); \
1068
PaletteInsert (c1, (uint32_t)n1, bpp); \
1071
for (i++; i < count; i++) { \
1072
if (data[i] == ci) { \
1075
if (!PaletteInsert (ci, (uint32_t)ni, bpp)) \
1081
PaletteInsert (ci, (uint32_t)ni, bpp); \
1084
DEFINE_FILL_PALETTE_FUNCTION(16)
1085
DEFINE_FILL_PALETTE_FUNCTION(32)
1089
* Functions to operate with palette structures.
1092
#define HASH_FUNC16(rgb) ((int)(((rgb >> 8) + rgb) & 0xFF))
1093
#define HASH_FUNC32(rgb) ((int)(((rgb >> 16) + (rgb >> 8)) & 0xFF))
1098
paletteNumColors = 0;
1099
memset(palette.hash, 0, 256 * sizeof(COLOR_LIST *));
1103
PaletteInsert(rgb, numPixels, bpp)
1109
COLOR_LIST *prev_pnode = NULL;
1110
int hash_key, idx, new_idx, count;
1112
hash_key = (bpp == 16) ? HASH_FUNC16(rgb) : HASH_FUNC32(rgb);
1114
pnode = palette.hash[hash_key];
1116
while (pnode != NULL) {
1117
if (pnode->rgb == rgb) {
1118
/* Such palette entry already exists. */
1119
new_idx = idx = pnode->idx;
1120
count = palette.entry[idx].numPixels + numPixels;
1121
if (new_idx && palette.entry[new_idx-1].numPixels < count) {
1123
palette.entry[new_idx] = palette.entry[new_idx-1];
1124
palette.entry[new_idx].listNode->idx = new_idx;
1127
while (new_idx && palette.entry[new_idx-1].numPixels < count);
1128
palette.entry[new_idx].listNode = pnode;
1129
pnode->idx = new_idx;
1131
palette.entry[new_idx].numPixels = count;
1132
return paletteNumColors;
1135
pnode = pnode->next;
1138
/* Check if palette is full. */
1139
if (paletteNumColors == 256 || paletteNumColors == paletteMaxColors) {
1140
paletteNumColors = 0;
1144
/* Move palette entries with lesser pixel counts. */
1145
for ( idx = paletteNumColors;
1146
idx > 0 && palette.entry[idx-1].numPixels < numPixels;
1148
palette.entry[idx] = palette.entry[idx-1];
1149
palette.entry[idx].listNode->idx = idx;
1152
/* Add new palette entry into the freed slot. */
1153
pnode = &palette.list[paletteNumColors];
1154
if (prev_pnode != NULL) {
1155
prev_pnode->next = pnode;
1157
palette.hash[hash_key] = pnode;
1162
palette.entry[idx].listNode = pnode;
1163
palette.entry[idx].numPixels = numPixels;
1165
return (++paletteNumColors);
1170
* Converting 32-bit color samples into 24-bit colors.
1171
* Should be called only when redMax, greenMax and blueMax are 255.
1172
* Color components assumed to be byte-aligned.
1175
static void Pack24(cl, buf, fmt, count)
1178
rfbPixelFormat *fmt;
1183
int r_shift, g_shift, b_shift;
1185
buf32 = (uint32_t *)buf;
1187
if (!cl->screen->rfbServerFormat.bigEndian == !fmt->bigEndian) {
1188
r_shift = fmt->redShift;
1189
g_shift = fmt->greenShift;
1190
b_shift = fmt->blueShift;
1192
r_shift = 24 - fmt->redShift;
1193
g_shift = 24 - fmt->greenShift;
1194
b_shift = 24 - fmt->blueShift;
1199
*buf++ = (char)(pix >> r_shift);
1200
*buf++ = (char)(pix >> g_shift);
1201
*buf++ = (char)(pix >> b_shift);
1207
* Converting truecolor samples into palette indices.
1210
#define DEFINE_IDX_ENCODE_FUNCTION(bpp) \
1213
EncodeIndexedRect##bpp(buf, count) \
1217
COLOR_LIST *pnode; \
1218
uint##bpp##_t *src; \
1219
uint##bpp##_t rgb; \
1222
src = (uint##bpp##_t *) buf; \
1226
while (count && *src == rgb) { \
1227
rep++, src++, count--; \
1229
pnode = palette.hash[HASH_FUNC##bpp(rgb)]; \
1230
while (pnode != NULL) { \
1231
if ((uint##bpp##_t)pnode->rgb == rgb) { \
1232
*buf++ = (uint8_t)pnode->idx; \
1234
*buf++ = (uint8_t)pnode->idx; \
1239
pnode = pnode->next; \
1244
DEFINE_IDX_ENCODE_FUNCTION(16)
1245
DEFINE_IDX_ENCODE_FUNCTION(32)
1247
#define DEFINE_MONO_ENCODE_FUNCTION(bpp) \
1250
EncodeMonoRect##bpp(buf, w, h) \
1254
uint##bpp##_t *ptr; \
1256
unsigned int value, mask; \
1257
int aligned_width; \
1258
int x, y, bg_bits; \
1260
ptr = (uint##bpp##_t *) buf; \
1261
bg = (uint##bpp##_t) monoBackground; \
1262
aligned_width = w - w % 8; \
1264
for (y = 0; y < h; y++) { \
1265
for (x = 0; x < aligned_width; x += 8) { \
1266
for (bg_bits = 0; bg_bits < 8; bg_bits++) { \
1270
if (bg_bits == 8) { \
1274
mask = 0x80 >> bg_bits; \
1276
for (bg_bits++; bg_bits < 8; bg_bits++) { \
1278
if (*ptr++ != bg) { \
1282
*buf++ = (uint8_t)value; \
1290
for (; x < w; x++) { \
1291
if (*ptr++ != bg) { \
1296
*buf++ = (uint8_t)value; \
1300
DEFINE_MONO_ENCODE_FUNCTION(8)
1301
DEFINE_MONO_ENCODE_FUNCTION(16)
1302
DEFINE_MONO_ENCODE_FUNCTION(32)
1306
* ``Gradient'' filter for 24-bit color samples.
1307
* Should be called only when redMax, greenMax and blueMax are 255.
1308
* Color components assumed to be byte-aligned.
1312
FilterGradient24(cl, buf, fmt, w, h)
1315
rfbPixelFormat *fmt;
1322
int pixHere[3], pixUpper[3], pixLeft[3], pixUpperLeft[3];
1326
buf32 = (uint32_t *)buf;
1327
memset (prevRowBuf, 0, w * 3 * sizeof(int));
1329
if (!cl->screen->rfbServerFormat.bigEndian == !fmt->bigEndian) {
1330
shiftBits[0] = fmt->redShift;
1331
shiftBits[1] = fmt->greenShift;
1332
shiftBits[2] = fmt->blueShift;
1334
shiftBits[0] = 24 - fmt->redShift;
1335
shiftBits[1] = 24 - fmt->greenShift;
1336
shiftBits[2] = 24 - fmt->blueShift;
1339
for (y = 0; y < h; y++) {
1340
for (c = 0; c < 3; c++) {
1344
prevRowPtr = prevRowBuf;
1345
for (x = 0; x < w; x++) {
1347
for (c = 0; c < 3; c++) {
1348
pixUpperLeft[c] = pixUpper[c];
1349
pixLeft[c] = pixHere[c];
1350
pixUpper[c] = *prevRowPtr;
1351
pixHere[c] = (int)(pix32 >> shiftBits[c] & 0xFF);
1352
*prevRowPtr++ = pixHere[c];
1354
prediction = pixLeft[c] + pixUpper[c] - pixUpperLeft[c];
1355
if (prediction < 0) {
1357
} else if (prediction > 0xFF) {
1360
*buf++ = (char)(pixHere[c] - prediction);
1368
* ``Gradient'' filter for other color depths.
1371
#define DEFINE_GRADIENT_FILTER_FUNCTION(bpp) \
1374
FilterGradient##bpp(cl, buf, fmt, w, h) \
1376
uint##bpp##_t *buf; \
1377
rfbPixelFormat *fmt; \
1380
uint##bpp##_t pix, diff; \
1381
rfbBool endianMismatch; \
1383
int maxColor[3], shiftBits[3]; \
1384
int pixHere[3], pixUpper[3], pixLeft[3], pixUpperLeft[3]; \
1388
memset (prevRowBuf, 0, w * 3 * sizeof(int)); \
1390
endianMismatch = (!cl->screen->rfbServerFormat.bigEndian != !fmt->bigEndian); \
1392
maxColor[0] = fmt->redMax; \
1393
maxColor[1] = fmt->greenMax; \
1394
maxColor[2] = fmt->blueMax; \
1395
shiftBits[0] = fmt->redShift; \
1396
shiftBits[1] = fmt->greenShift; \
1397
shiftBits[2] = fmt->blueShift; \
1399
for (y = 0; y < h; y++) { \
1400
for (c = 0; c < 3; c++) { \
1404
prevRowPtr = prevRowBuf; \
1405
for (x = 0; x < w; x++) { \
1407
if (endianMismatch) { \
1408
pix = Swap##bpp(pix); \
1411
for (c = 0; c < 3; c++) { \
1412
pixUpperLeft[c] = pixUpper[c]; \
1413
pixLeft[c] = pixHere[c]; \
1414
pixUpper[c] = *prevRowPtr; \
1415
pixHere[c] = (int)(pix >> shiftBits[c] & maxColor[c]); \
1416
*prevRowPtr++ = pixHere[c]; \
1418
prediction = pixLeft[c] + pixUpper[c] - pixUpperLeft[c]; \
1419
if (prediction < 0) { \
1421
} else if (prediction > maxColor[c]) { \
1422
prediction = maxColor[c]; \
1424
diff |= ((pixHere[c] - prediction) & maxColor[c]) \
1427
if (endianMismatch) { \
1428
diff = Swap##bpp(diff); \
1435
DEFINE_GRADIENT_FILTER_FUNCTION(16)
1436
DEFINE_GRADIENT_FILTER_FUNCTION(32)
1440
* Code to guess if given rectangle is suitable for smooth image
1441
* compression (by applying "gradient" filter or JPEG coder).
1444
#define JPEG_MIN_RECT_SIZE 4096
1446
#define DETECT_SUBROW_WIDTH 7
1447
#define DETECT_MIN_WIDTH 8
1448
#define DETECT_MIN_HEIGHT 8
1451
DetectSmoothImage (cl, fmt, w, h)
1453
rfbPixelFormat *fmt;
1458
if ( cl->screen->rfbServerFormat.bitsPerPixel == 8 || fmt->bitsPerPixel == 8 ||
1459
w < DETECT_MIN_WIDTH || h < DETECT_MIN_HEIGHT ) {
1463
if (qualityLevel != -1) {
1464
if (w * h < JPEG_MIN_RECT_SIZE) {
1468
if ( rfbTightDisableGradient ||
1469
w * h < tightConf[compressLevel].gradientMinRectSize ) {
1474
if (fmt->bitsPerPixel == 32) {
1475
if (usePixelFormat24) {
1476
avgError = DetectSmoothImage24(cl, fmt, w, h);
1477
if (qualityLevel != -1) {
1478
return (avgError < tightConf[qualityLevel].jpegThreshold24);
1480
return (avgError < tightConf[compressLevel].gradientThreshold24);
1482
avgError = DetectSmoothImage32(cl, fmt, w, h);
1485
avgError = DetectSmoothImage16(cl, fmt, w, h);
1487
if (qualityLevel != -1) {
1488
return (avgError < tightConf[qualityLevel].jpegThreshold);
1490
return (avgError < tightConf[compressLevel].gradientThreshold);
1493
static unsigned long
1494
DetectSmoothImage24 (cl, fmt, w, h)
1496
rfbPixelFormat *fmt;
1504
unsigned long avgError;
1506
/* If client is big-endian, color samples begin from the second
1507
byte (offset 1) of a 32-bit pixel value. */
1508
off = (fmt->bigEndian != 0);
1510
memset(diffStat, 0, 256*sizeof(int));
1513
while (y < h && x < w) {
1514
for (d = 0; d < h - y && d < w - x - DETECT_SUBROW_WIDTH; d++) {
1515
for (c = 0; c < 3; c++) {
1516
left[c] = (int)tightBeforeBuf[((y+d)*w+x+d)*4+off+c] & 0xFF;
1518
for (dx = 1; dx <= DETECT_SUBROW_WIDTH; dx++) {
1519
for (c = 0; c < 3; c++) {
1520
pix = (int)tightBeforeBuf[((y+d)*w+x+d+dx)*4+off+c] & 0xFF;
1521
diffStat[abs(pix - left[c])]++;
1536
if (diffStat[0] * 33 / pixelCount >= 95)
1540
for (c = 1; c < 8; c++) {
1541
avgError += (unsigned long)diffStat[c] * (unsigned long)(c * c);
1542
if (diffStat[c] == 0 || diffStat[c] > diffStat[c-1] * 2)
1545
for (; c < 256; c++) {
1546
avgError += (unsigned long)diffStat[c] * (unsigned long)(c * c);
1548
avgError /= (pixelCount * 3 - diffStat[0]);
1553
#define DEFINE_DETECT_FUNCTION(bpp) \
1555
static unsigned long \
1556
DetectSmoothImage##bpp (cl, fmt, w, h) \
1558
rfbPixelFormat *fmt; \
1561
rfbBool endianMismatch; \
1562
uint##bpp##_t pix; \
1563
int maxColor[3], shiftBits[3]; \
1564
int x, y, d, dx, c; \
1565
int diffStat[256]; \
1566
int pixelCount = 0; \
1567
int sample, sum, left[3]; \
1568
unsigned long avgError; \
1570
endianMismatch = (!cl->screen->rfbServerFormat.bigEndian != !fmt->bigEndian); \
1572
maxColor[0] = fmt->redMax; \
1573
maxColor[1] = fmt->greenMax; \
1574
maxColor[2] = fmt->blueMax; \
1575
shiftBits[0] = fmt->redShift; \
1576
shiftBits[1] = fmt->greenShift; \
1577
shiftBits[2] = fmt->blueShift; \
1579
memset(diffStat, 0, 256*sizeof(int)); \
1582
while (y < h && x < w) { \
1583
for (d = 0; d < h - y && d < w - x - DETECT_SUBROW_WIDTH; d++) { \
1584
pix = ((uint##bpp##_t *)tightBeforeBuf)[(y+d)*w+x+d]; \
1585
if (endianMismatch) { \
1586
pix = Swap##bpp(pix); \
1588
for (c = 0; c < 3; c++) { \
1589
left[c] = (int)(pix >> shiftBits[c] & maxColor[c]); \
1591
for (dx = 1; dx <= DETECT_SUBROW_WIDTH; dx++) { \
1592
pix = ((uint##bpp##_t *)tightBeforeBuf)[(y+d)*w+x+d+dx]; \
1593
if (endianMismatch) { \
1594
pix = Swap##bpp(pix); \
1597
for (c = 0; c < 3; c++) { \
1598
sample = (int)(pix >> shiftBits[c] & maxColor[c]); \
1599
sum += abs(sample - left[c]); \
1617
if ((diffStat[0] + diffStat[1]) * 100 / pixelCount >= 90) \
1621
for (c = 1; c < 8; c++) { \
1622
avgError += (unsigned long)diffStat[c] * (unsigned long)(c * c); \
1623
if (diffStat[c] == 0 || diffStat[c] > diffStat[c-1] * 2) \
1626
for (; c < 256; c++) { \
1627
avgError += (unsigned long)diffStat[c] * (unsigned long)(c * c); \
1629
avgError /= (pixelCount - diffStat[0]); \
1634
DEFINE_DETECT_FUNCTION(16)
1635
DEFINE_DETECT_FUNCTION(32)
1639
* JPEG compression stuff.
1642
static struct jpeg_destination_mgr jpegDstManager;
1643
static rfbBool jpegError;
1644
static int jpegDstDataLen;
1647
SendJpegRect(cl, x, y, w, h, quality)
1652
struct jpeg_compress_struct cinfo;
1653
struct jpeg_error_mgr jerr;
1655
JSAMPROW rowPointer[1];
1658
if (cl->screen->rfbServerFormat.bitsPerPixel == 8)
1659
return SendFullColorRect(cl, w, h);
1661
srcBuf = (uint8_t *)malloc(w * 3);
1662
if (srcBuf == NULL) {
1663
return SendFullColorRect(cl, w, h);
1665
rowPointer[0] = srcBuf;
1667
cinfo.err = jpeg_std_error(&jerr);
1668
jpeg_create_compress(&cinfo);
1670
cinfo.image_width = w;
1671
cinfo.image_height = h;
1672
cinfo.input_components = 3;
1673
cinfo.in_color_space = JCS_RGB;
1675
jpeg_set_defaults(&cinfo);
1676
jpeg_set_quality(&cinfo, quality, TRUE);
1678
JpegSetDstManager (&cinfo);
1680
jpeg_start_compress(&cinfo, TRUE);
1682
for (dy = 0; dy < h; dy++) {
1683
PrepareRowForJpeg(cl, srcBuf, x, y + dy, w);
1684
jpeg_write_scanlines(&cinfo, rowPointer, 1);
1690
jpeg_finish_compress(&cinfo);
1692
jpeg_destroy_compress(&cinfo);
1696
return SendFullColorRect(cl, w, h);
1698
if (cl->ublen + TIGHT_MIN_TO_COMPRESS + 1 > UPDATE_BUF_SIZE) {
1699
if (!rfbSendUpdateBuf(cl))
1703
cl->updateBuf[cl->ublen++] = (char)(rfbTightJpeg << 4);
1704
cl->rfbBytesSent[rfbEncodingTight]++;
1706
return SendCompressedData(cl, jpegDstDataLen);
1710
PrepareRowForJpeg(cl, dst, x, y, count)
1715
if (cl->screen->rfbServerFormat.bitsPerPixel == 32) {
1716
if ( cl->screen->rfbServerFormat.redMax == 0xFF &&
1717
cl->screen->rfbServerFormat.greenMax == 0xFF &&
1718
cl->screen->rfbServerFormat.blueMax == 0xFF ) {
1719
PrepareRowForJpeg24(cl, dst, x, y, count);
1721
PrepareRowForJpeg32(cl, dst, x, y, count);
1724
/* 16 bpp assumed. */
1725
PrepareRowForJpeg16(cl, dst, x, y, count);
1730
PrepareRowForJpeg24(cl, dst, x, y, count)
1738
fbptr = (uint32_t *)
1739
&cl->screen->frameBuffer[y * cl->screen->paddedWidthInBytes + x * 4];
1743
*dst++ = (uint8_t)(pix >> cl->screen->rfbServerFormat.redShift);
1744
*dst++ = (uint8_t)(pix >> cl->screen->rfbServerFormat.greenShift);
1745
*dst++ = (uint8_t)(pix >> cl->screen->rfbServerFormat.blueShift);
1749
#define DEFINE_JPEG_GET_ROW_FUNCTION(bpp) \
1752
PrepareRowForJpeg##bpp(cl, dst, x, y, count) \
1757
uint##bpp##_t *fbptr; \
1758
uint##bpp##_t pix; \
1759
int inRed, inGreen, inBlue; \
1761
fbptr = (uint##bpp##_t *) \
1762
&cl->screen->frameBuffer[y * cl->screen->paddedWidthInBytes + \
1769
(pix >> cl->screen->rfbServerFormat.redShift & cl->screen->rfbServerFormat.redMax); \
1771
(pix >> cl->screen->rfbServerFormat.greenShift & cl->screen->rfbServerFormat.greenMax); \
1773
(pix >> cl->screen->rfbServerFormat.blueShift & cl->screen->rfbServerFormat.blueMax); \
1775
*dst++ = (uint8_t)((inRed * 255 + cl->screen->rfbServerFormat.redMax / 2) / \
1776
cl->screen->rfbServerFormat.redMax); \
1777
*dst++ = (uint8_t)((inGreen * 255 + cl->screen->rfbServerFormat.greenMax / 2) / \
1778
cl->screen->rfbServerFormat.greenMax); \
1779
*dst++ = (uint8_t)((inBlue * 255 + cl->screen->rfbServerFormat.blueMax / 2) / \
1780
cl->screen->rfbServerFormat.blueMax); \
1784
DEFINE_JPEG_GET_ROW_FUNCTION(16)
1785
DEFINE_JPEG_GET_ROW_FUNCTION(32)
1788
* Destination manager implementation for JPEG library.
1792
JpegInitDestination(j_compress_ptr cinfo)
1795
jpegDstManager.next_output_byte = (JOCTET *)tightAfterBuf;
1796
jpegDstManager.free_in_buffer = (size_t)tightAfterBufSize;
1800
JpegEmptyOutputBuffer(j_compress_ptr cinfo)
1803
jpegDstManager.next_output_byte = (JOCTET *)tightAfterBuf;
1804
jpegDstManager.free_in_buffer = (size_t)tightAfterBufSize;
1810
JpegTermDestination(j_compress_ptr cinfo)
1812
jpegDstDataLen = tightAfterBufSize - jpegDstManager.free_in_buffer;
1816
JpegSetDstManager(j_compress_ptr cinfo)
1818
jpegDstManager.init_destination = JpegInitDestination;
1819
jpegDstManager.empty_output_buffer = JpegEmptyOutputBuffer;
1820
jpegDstManager.term_destination = JpegTermDestination;
1821
cinfo->dest = &jpegDstManager;
1824
#endif /* LIBJPEG */