Commit 2742c957 authored by runge's avatar runge

x11vnc: add kludge to experiment with turbovnc.

parent 453166ec
...@@ -774,7 +774,7 @@ if test "$build_x11vnc" = "yes"; then ...@@ -774,7 +774,7 @@ if test "$build_x11vnc" = "yes"; then
# #
# configure.ac:690: required file `x11vnc/Makefile.in' not found # configure.ac:690: required file `x11vnc/Makefile.in' not found
# #
AC_CONFIG_FILES([x11vnc/Makefile x11vnc/misc/Makefile]) AC_CONFIG_FILES([x11vnc/Makefile x11vnc/misc/Makefile x11vnc/misc/turbovnc/Makefile])
fi fi
AC_CONFIG_COMMANDS([chmod-libvncserver-config],[chmod a+x libvncserver-config]) AC_CONFIG_COMMANDS([chmod-libvncserver-config],[chmod a+x libvncserver-config])
......
2009-02-28 Karl Runge <runge@karlrunge.com>
* x11vnc: add kludge to experiment with turbovnc.
2009-02-25 Karl Runge <runge@karlrunge.com> 2009-02-25 Karl Runge <runge@karlrunge.com>
* x11vnc: fix some -QD cases for use in tkx11vnc. * x11vnc: fix some -QD cases for use in tkx11vnc.
......
x11vnc README file Date: Wed Feb 25 21:10:01 EST 2009 x11vnc README file Date: Sat Feb 28 10:23:00 EST 2009
The following information is taken from these URLs: The following information is taken from these URLs:
......
SUBDIRS = turbovnc
DIST_SUBDIRS = turbovnc
EXTRA_DIST=README blockdpy.c dtVncPopup rx11vnc rx11vnc.pl shm_clear ranfb.pl slide.pl vcinject.pl x11vnc_loop Xdummy EXTRA_DIST=README blockdpy.c dtVncPopup rx11vnc rx11vnc.pl shm_clear ranfb.pl slide.pl vcinject.pl x11vnc_loop Xdummy
EXTRA_DIST=README apply_turbovnc convert tight.c turbojpeg.h undo_turbovnc
INTRO:
------
This is a "patch" to make x11vnc/libvncserver work with TurboVNC:
http://www.virtualgl.org/About/TurboVNC
It is very experimental/kludgy. Not all TurboVNC features may be enabled.
We are currently evaluating whether TurboVNC support should be officially
put into x11vnc/libvncserver.
TurboVNC is an optimized VNC for fast refresh rates on fast networks.
It does pretty well on good broadband as well. But it is not as fast
as regular TightVNC on slow links.
TURBOJPEG:
---------
TurboVNC uses the TurboJPEG library based on a fast proprietary JPEG
implementation. You will need to download it from the VirtualGL
sourceforge site:
http://sourceforge.net/project/showfiles.php?group_id=117509&package_id=166100
Either install it or simply unpack the .deb or .rpm file into a directory.
N.B. you can unpack a .deb via 'ar x package.deb' and the extracting
from the data.tar.gz file. rpm2cpio can be used to unpack .rpm's.
QUICK-START:
------------
For those in a hurry:
cd x11vnc-x.y.z/x11vnc/misc/turbovnc
./apply_turbovnc
cd ../../..
env LDFLAGS='-L/DIR -Xlinker --rpath=/DIR' ./configure
make AM_LDFLAGS='-lturbojpeg'
where you replace /DIR with your directory containing libturbojpeg.so.
PATCHING AND BUILDING:
----------------------
After unpacking your x11vnc-x.y.z.tar.gz tarball cd to the
x11vnc-x.y.z/x11vnc/misc/turbovnc (where this README file is) and from
that directory run:
./apply_turbovnc
that will modify files in the libvncserver and x11vnc directories above
this directory. (To undo these changes run: ./undo_turbovnc) The input
sources, tight.c and turbojpeg.h are from the TurboVNC source package.
After applying, go back to the top level source directory and run:
env LDFLAGS='-L/path/to/turbojpeg -Xlinker --rpath=/path/to/turbojpeg' ./configure
where the turbojpeg library is:
/path/to/turbojpeg/libturbojpeg.so
(change /path/to/turbojpeg to the directory where you installed or
unpacked it.)
If you are not using gnu gcc and gnu linker the options may be a little
different (e.g. -R instead of -Xlinker --rpath).
If you need additional ./configure options or env. var. settings,
add them too.
Next, run this make command:
make AM_LDFLAGS='-lturbojpeg'
This is a hack and may not always work, if it doesn't edit x11vnc/Makefile
and add '-lturbojpeg' to the LIBS variable.
This should create a binary:
./x11vnc/x11vnc
that supports VirtualGL's TurboVNC.
You will need a TurboVNC viewer, you can get one here:
http://sourceforge.net/project/showfiles.php?group_id=117509&package_id=128130
Let us know how it goes.
PERFORMANCE:
------------
Note that x11vnc has to read the display's screen pixels from the
graphics card memory. This can be slow, e.g. 10 MB/sec.
There is not a big need for graphics card manufacturers to optimize the
read rate; the write rate is the one they optimize greatly.
http://www.karlrunge.com/x11vnc/#limitations
If you run x11vnc and see lines like this:
28/02/2009 00:52:07 Autoprobing selected port 5900
28/02/2009 00:52:07 fb read rate: 10 MB/sec
28/02/2009 00:52:07 screen setup finished.
you have a typical slow one.
Whereas if you see this:
28/02/2009 00:54:46 Autoprobing selected port 5900
28/02/2009 00:54:46 fb read rate: 321 MB/sec
28/02/2009 00:54:46 fast read: reset wait ms to: 10
28/02/2009 00:54:46 fast read: reset defer ms to: 10
28/02/2009 00:54:46 screen setup finished.
that is very fast.
We have only seen it this fast on Linux by using the nvidia proprietary
graphics drivers. The Xorg drivers are typically slow 10 MB/sec.
It will also be fast if the X server is virtual: Xvfb or Xdummy
since the screen pixels are stored in RAM:
http://www.karlrunge.com/x11vnc/faq.html#faq-xvfb
And it will be fast if the ShadowFB xorg.conf option is enabled (if the
card supports it.)
The point we are trying to make is that even though TurboVNC uses a
wicked fast JPEG implementation, and cuts out overhead in its attempt to
pump out as many frames per second as it can, if it is slow for x11vnc
to read the screen pixels in the first place then you might not even
notice the TurboVNC speedup.
So TurboVNC+x11vnc will be faster than TightVNC+x11vnc, but if there is
a large overhead/bottleneck from reading the graphics card framebuffer,
then the speedup will be marginal.
#!/bin/sh
ldir="../../../libvncserver"
fail=""
if [ ! -f "./tight.c" ]; then
fail=1
fi
if [ ! -f "./turbojpeg.h" ]; then
fail=1
fi
if [ ! -f "./convert" ]; then
fail=1
fi
if [ ! -f "$ldir/tight.c" ]; then
ls -l "$ldir/tight.c"
fail=1
fi
if [ "X$fail" = "X1" ]; then
echo "Must be run from inside the directory containing 'apply_turbovnc'"
exit 1
fi
if [ -f "$ldir/tight.c.ORIG" ]; then
set -xv
else
set -xv
cp -p "$ldir/tight.c" "$ldir/tight.c.ORIG"
fi
perl ./convert ./tight.c > "$ldir/tight.c"
cp -p ./turbojpeg.h "$ldir"
ls -l $ldir/tight.c* $ldir/turbojpeg.h
#!/usr/bin/perl
while (<>) {
if (/^#include.*"rfb.h"/) {
print <<END;
#include <rfb/rfb.h>
#define Bool rfbBool
#define CARD32 uint32_t
#define CARD16 uint16_t
#define CARD8 uint8_t
#define xalloc malloc
#define xrealloc realloc
#define rfbTightNoZlib 0x0A
END
next;
}
foreach $func (qw(FindBestSolidArea ExtendSolidArea CheckSolidTile CheckSolidTile##bpp CheckSolidTile8 CheckSolidTile16 CheckSolidTile32 Pack24)) {
if (/static.*\b\Q$func\E\b/ && !exists $did_static{$func}) {
$_ =~ s/\b\Q$func\E\b(\s*)\(/$func$1(rfbClientPtr cl, /;
$did_static{$func} = 1;
} elsif (/\b\Q$func\E\b\s*\(/) {
$_ =~ s/\b\Q$func\E\b(\s*)\(/$func$1(cl, /;
}
}
if (/^\s*subsampLevel\s*=\s*cl/) {
$_ = "//$_";
print "subsampLevel = 0;\n";
}
$_ =~ s/cl->tightQualityLevel;/cl->tightQualityLevel * 10;/;
$_ =~ s/rfbScreen.pfbMemory/cl->scaledScreen->frameBuffer/g;
$_ =~ s/rfbScreen.paddedWidthInBytes/cl->scaledScreen->paddedWidthInBytes/g;
$_ =~ s/rfbScreen.bitsPerPixel/cl->scaledScreen->bitsPerPixel/g;
$_ =~ s/rfbServerFormat/cl->screen->serverFormat/g;
if (/^(FindBestSolidArea|ExtendSolidArea|static void Pack24|CheckSolidTile)\(cl/) {
$_ .= "rfbClientPtr cl;\n";
}
if (/^(CheckSolidTile##bpp)\(cl/) {
$_ .= "rfbClientPtr cl; \\\n";
}
$_ =~ s/\bublen\b/cl->ublen/;
$_ =~ s/\bupdateBuf\b/cl->updateBuf/;
if (/cl->(rfbRectanglesSent|rfbBytesSent)/) {
$_ = "//$_";
}
print;
}
print <<END;
void rfbTightCleanup(rfbScreenInfoPtr screen) {
}
END
/*
* tight.c
*
* Routines to implement Tight Encoding
*/
/*
* Copyright (C) 2005-2008 Sun Microsystems, Inc. All Rights Reserved.
* Copyright (C) 2004 Landmark Graphics Corporation. All Rights Reserved.
* Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved.
* Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "rfb.h"
#include "turbojpeg.h"
/* Note: The following constant should not be changed. */
#define TIGHT_MIN_TO_COMPRESS 12
/* The parameters below may be adjusted. */
#define MIN_SPLIT_RECT_SIZE 4096
#define MIN_SOLID_SUBRECT_SIZE 2048
#define MAX_SPLIT_TILE_SIZE 16
/* This variable is set on every rfbSendRectEncodingTight() call. */
static Bool usePixelFormat24;
/* Compression level stuff. The following array contains various
encoder parameters for each of 10 compression levels (0..9).
Last three parameters correspond to JPEG quality levels (0..9). */
typedef struct TIGHT_CONF_s {
int maxRectSize, maxRectWidth;
int monoMinRectSize;
int idxZlibLevel, monoZlibLevel, rawZlibLevel;
int idxMaxColorsDivisor;
} TIGHT_CONF;
static TIGHT_CONF tightConf[2] = {
{ 65536, 2048, 6, 0, 0, 0, 4 },
#if 0
{ 2048, 128, 6, 1, 1, 1, 8 },
{ 6144, 256, 8, 3, 3, 2, 24 },
{ 10240, 1024, 12, 5, 5, 3, 32 },
{ 16384, 2048, 12, 6, 6, 4, 32 },
{ 32768, 2048, 12, 7, 7, 5, 32 },
{ 65536, 2048, 16, 7, 7, 6, 48 },
{ 65536, 2048, 16, 8, 8, 7, 64 },
{ 65536, 2048, 32, 9, 9, 8, 64 },
#endif
{ 65536, 2048, 32, 1, 1, 1, 96 }
};
static int compressLevel;
static int qualityLevel;
static int subsampLevel;
static const int subsampLevel2tjsubsamp[4] = {
TJ_444, TJ_411, TJ_422, TJ_GRAYSCALE
};
/* Stuff dealing with palettes. */
typedef struct COLOR_LIST_s {
struct COLOR_LIST_s *next;
int idx;
CARD32 rgb;
} COLOR_LIST;
typedef struct PALETTE_ENTRY_s {
COLOR_LIST *listNode;
int numPixels;
} PALETTE_ENTRY;
typedef struct PALETTE_s {
PALETTE_ENTRY entry[256];
COLOR_LIST *hash[256];
COLOR_LIST list[256];
} PALETTE;
static int paletteNumColors, paletteMaxColors;
static CARD32 monoBackground, monoForeground;
static PALETTE palette;
/* Pointers to dynamically-allocated buffers. */
static int tightBeforeBufSize = 0;
static char *tightBeforeBuf = NULL;
static int tightAfterBufSize = 0;
static char *tightAfterBuf = NULL;
static int *prevRowBuf = NULL;
/* Prototypes for static functions. */
static void FindBestSolidArea (int x, int y, int w, int h,
CARD32 colorValue, int *w_ptr, int *h_ptr);
static void ExtendSolidArea (int x, int y, int w, int h,
CARD32 colorValue,
int *x_ptr, int *y_ptr, int *w_ptr, int *h_ptr);
static Bool CheckSolidTile (int x, int y, int w, int h,
CARD32 *colorPtr, Bool needSameColor);
static Bool CheckSolidTile8 (int x, int y, int w, int h,
CARD32 *colorPtr, Bool needSameColor);
static Bool CheckSolidTile16 (int x, int y, int w, int h,
CARD32 *colorPtr, Bool needSameColor);
static Bool CheckSolidTile32 (int x, int y, int w, int h,
CARD32 *colorPtr, Bool needSameColor);
static Bool SendRectSimple (rfbClientPtr cl, int x, int y, int w, int h);
static Bool SendSubrect (rfbClientPtr cl, int x, int y, int w, int h);
static Bool SendTightHeader (rfbClientPtr cl, int x, int y, int w, int h);
static Bool SendSolidRect (rfbClientPtr cl);
static Bool SendMonoRect (rfbClientPtr cl, int w, int h);
static Bool SendIndexedRect (rfbClientPtr cl, int w, int h);
static Bool SendFullColorRect (rfbClientPtr cl, int w, int h);
static Bool CompressData(rfbClientPtr cl, int streamId, int dataLen,
int zlibLevel, int zlibStrategy);
static Bool SendCompressedData(rfbClientPtr cl, char *buf, int compressedLen);
static void FillPalette8(int count);
static void FillPalette16(int count);
static void FillPalette32(int count);
static void FastFillPalette16(rfbClientPtr cl, CARD16 *data, int w, int pitch,
int h);
static void FastFillPalette32(rfbClientPtr cl, CARD32 *data, int w, int pitch,
int h);
static void PaletteReset(void);
static int PaletteInsert(CARD32 rgb, int numPixels, int bpp);
static void Pack24(char *buf, rfbPixelFormat *fmt, int count);
static void EncodeIndexedRect16(CARD8 *buf, int count);
static void EncodeIndexedRect32(CARD8 *buf, int count);
static void EncodeMonoRect8(CARD8 *buf, int w, int h);
static void EncodeMonoRect16(CARD8 *buf, int w, int h);
static void EncodeMonoRect32(CARD8 *buf, int w, int h);
static Bool SendJpegRect(rfbClientPtr cl, int x, int y, int w, int h,
int quality);
/*
* Tight encoding implementation.
*/
int
rfbNumCodedRectsTight(cl, x, y, w, h)
rfbClientPtr cl;
int x, y, w, h;
{
int maxRectSize, maxRectWidth;
int subrectMaxWidth, subrectMaxHeight;
/* No matter how many rectangles we will send if LastRect markers
are used to terminate rectangle stream. */
if (cl->enableLastRectEncoding && w * h >= MIN_SPLIT_RECT_SIZE)
return 0;
maxRectSize = tightConf[compressLevel].maxRectSize;
maxRectWidth = tightConf[compressLevel].maxRectWidth;
if (w > maxRectWidth || w * h > maxRectSize) {
subrectMaxWidth = (w > maxRectWidth) ? maxRectWidth : w;
subrectMaxHeight = maxRectSize / subrectMaxWidth;
return (((w - 1) / maxRectWidth + 1) *
((h - 1) / subrectMaxHeight + 1));
} else {
return 1;
}
}
Bool
rfbSendRectEncodingTight(cl, x, y, w, h)
rfbClientPtr cl;
int x, y, w, h;
{
int nMaxRows;
CARD32 colorValue;
int dx, dy, dw, dh;
int x_best, y_best, w_best, h_best;
char *fbptr;
compressLevel = cl->tightCompressLevel > 0 ? 1 : 0;
qualityLevel = cl->tightQualityLevel;
if (qualityLevel != -1) {
compressLevel = 1;
tightConf[compressLevel].idxZlibLevel = 1;
tightConf[compressLevel].monoZlibLevel = 1;
tightConf[compressLevel].rawZlibLevel = 1;
} else {
tightConf[compressLevel].idxZlibLevel = cl->tightCompressLevel;
tightConf[compressLevel].monoZlibLevel = cl->tightCompressLevel;
tightConf[compressLevel].rawZlibLevel = cl->tightCompressLevel;
}
subsampLevel = cl->tightSubsampLevel;
if ( cl->format.depth == 24 && cl->format.redMax == 0xFF &&
cl->format.greenMax == 0xFF && cl->format.blueMax == 0xFF ) {
usePixelFormat24 = TRUE;
} else {
usePixelFormat24 = FALSE;
}
if (!cl->enableLastRectEncoding || w * h < MIN_SPLIT_RECT_SIZE)
return SendRectSimple(cl, x, y, w, h);
/* Make sure we can write at least one pixel into tightBeforeBuf. */
if (tightBeforeBufSize < 4) {
tightBeforeBufSize = 4;
if (tightBeforeBuf == NULL)
tightBeforeBuf = (char *)xalloc(tightBeforeBufSize);
else
tightBeforeBuf = (char *)xrealloc(tightBeforeBuf,
tightBeforeBufSize);
}
/* Calculate maximum number of rows in one non-solid rectangle. */
{
int maxRectSize, maxRectWidth, nMaxWidth;
maxRectSize = tightConf[compressLevel].maxRectSize;
maxRectWidth = tightConf[compressLevel].maxRectWidth;
nMaxWidth = (w > maxRectWidth) ? maxRectWidth : w;
nMaxRows = maxRectSize / nMaxWidth;
}
/* Try to find large solid-color areas and send them separately. */
for (dy = y; dy < y + h; dy += MAX_SPLIT_TILE_SIZE) {
/* If a rectangle becomes too large, send its upper part now. */
if (dy - y >= nMaxRows) {
if (!SendRectSimple(cl, x, y, w, nMaxRows))
return 0;
y += nMaxRows;
h -= nMaxRows;
}
dh = (dy + MAX_SPLIT_TILE_SIZE <= y + h) ?
MAX_SPLIT_TILE_SIZE : (y + h - dy);
for (dx = x; dx < x + w; dx += MAX_SPLIT_TILE_SIZE) {
dw = (dx + MAX_SPLIT_TILE_SIZE <= x + w) ?
MAX_SPLIT_TILE_SIZE : (x + w - dx);
if (CheckSolidTile(dx, dy, dw, dh, &colorValue, FALSE)) {
if (subsampLevel == TJ_GRAYSCALE && qualityLevel != -1) {
CARD32 r=(colorValue>>16)&0xFF;
CARD32 g=(colorValue>>8)&0xFF;
CARD32 b=(colorValue)&0xFF;
double y=(0.257*(double)r)+(0.504*(double)g)
+(0.098*(double)b)+16.;
colorValue=(int)y+(((int)y)<<8)+(((int)y)<<16);
}
/* Get dimensions of solid-color area. */
FindBestSolidArea(dx, dy, w - (dx - x), h - (dy - y),
colorValue, &w_best, &h_best);
/* Make sure a solid rectangle is large enough
(or the whole rectangle is of the same color). */
if ( w_best * h_best != w * h &&
w_best * h_best < MIN_SOLID_SUBRECT_SIZE )
continue;
/* Try to extend solid rectangle to maximum size. */
x_best = dx; y_best = dy;
ExtendSolidArea(x, y, w, h, colorValue,
&x_best, &y_best, &w_best, &h_best);
/* Send rectangles at top and left to solid-color area. */
if ( y_best != y &&
!SendRectSimple(cl, x, y, w, y_best-y) )
return FALSE;
if ( x_best != x &&
!rfbSendRectEncodingTight(cl, x, y_best,
x_best-x, h_best) )
return FALSE;
/* Send solid-color rectangle. */
if (!SendTightHeader(cl, x_best, y_best, w_best, h_best))
return FALSE;
fbptr = (rfbScreen.pfbMemory +
(rfbScreen.paddedWidthInBytes * y_best) +
(x_best * (rfbScreen.bitsPerPixel / 8)));
(*cl->translateFn)(cl->translateLookupTable, &rfbServerFormat,
&cl->format, fbptr, tightBeforeBuf,
rfbScreen.paddedWidthInBytes, 1, 1);
if (!SendSolidRect(cl))
return FALSE;
/* Send remaining rectangles (at right and bottom). */
if ( x_best + w_best != x + w &&
!rfbSendRectEncodingTight(cl, x_best+w_best, y_best,
w-(x_best-x)-w_best, h_best) )
return FALSE;
if ( y_best + h_best != y + h &&
!rfbSendRectEncodingTight(cl, x, y_best+h_best,
w, h-(y_best-y)-h_best) )
return FALSE;
/* Return after all recursive calls are done. */
return TRUE;
}
}
}
/* No suitable solid-color rectangles found. */
return SendRectSimple(cl, x, y, w, h);
}
static void
FindBestSolidArea(x, y, w, h, colorValue, w_ptr, h_ptr)
int x, y, w, h;
CARD32 colorValue;
int *w_ptr, *h_ptr;
{
int dx, dy, dw, dh;
int w_prev;
int w_best = 0, h_best = 0;
w_prev = w;
for (dy = y; dy < y + h; dy += MAX_SPLIT_TILE_SIZE) {
dh = (dy + MAX_SPLIT_TILE_SIZE <= y + h) ?
MAX_SPLIT_TILE_SIZE : (y + h - dy);
dw = (w_prev > MAX_SPLIT_TILE_SIZE) ?
MAX_SPLIT_TILE_SIZE : w_prev;
if (!CheckSolidTile(x, dy, dw, dh, &colorValue, TRUE))
break;
for (dx = x + dw; dx < x + w_prev;) {
dw = (dx + MAX_SPLIT_TILE_SIZE <= x + w_prev) ?
MAX_SPLIT_TILE_SIZE : (x + w_prev - dx);
if (!CheckSolidTile(dx, dy, dw, dh, &colorValue, TRUE))
break;
dx += dw;
}
w_prev = dx - x;
if (w_prev * (dy + dh - y) > w_best * h_best) {
w_best = w_prev;
h_best = dy + dh - y;
}
}
*w_ptr = w_best;
*h_ptr = h_best;
}
static void
ExtendSolidArea(x, y, w, h, colorValue, x_ptr, y_ptr, w_ptr, h_ptr)
int x, y, w, h;
CARD32 colorValue;
int *x_ptr, *y_ptr, *w_ptr, *h_ptr;
{
int cx, cy;
/* Try to extend the area upwards. */
for ( cy = *y_ptr - 1;
cy >= y && CheckSolidTile(*x_ptr, cy, *w_ptr, 1, &colorValue, TRUE);
cy-- );
*h_ptr += *y_ptr - (cy + 1);
*y_ptr = cy + 1;
/* ... downwards. */
for ( cy = *y_ptr + *h_ptr;
cy < y + h &&
CheckSolidTile(*x_ptr, cy, *w_ptr, 1, &colorValue, TRUE);
cy++ );
*h_ptr += cy - (*y_ptr + *h_ptr);
/* ... to the left. */
for ( cx = *x_ptr - 1;
cx >= x && CheckSolidTile(cx, *y_ptr, 1, *h_ptr, &colorValue, TRUE);
cx-- );
*w_ptr += *x_ptr - (cx + 1);
*x_ptr = cx + 1;
/* ... to the right. */
for ( cx = *x_ptr + *w_ptr;
cx < x + w &&
CheckSolidTile(cx, *y_ptr, 1, *h_ptr, &colorValue, TRUE);
cx++ );
*w_ptr += cx - (*x_ptr + *w_ptr);
}
/*
* Check if a rectangle is all of the same color. If needSameColor is
* set to non-zero, then also check that its color equals to the
* *colorPtr value. The result is 1 if the test is successfull, and in
* that case new color will be stored in *colorPtr.
*/
static Bool
CheckSolidTile(x, y, w, h, colorPtr, needSameColor)
int x, y, w, h;
CARD32 *colorPtr;
Bool needSameColor;
{
switch(rfbServerFormat.bitsPerPixel) {
case 32:
return CheckSolidTile32(x, y, w, h, colorPtr, needSameColor);
case 16:
return CheckSolidTile16(x, y, w, h, colorPtr, needSameColor);
default:
return CheckSolidTile8(x, y, w, h, colorPtr, needSameColor);
}
}
#define DEFINE_CHECK_SOLID_FUNCTION(bpp) \
\
static Bool \
CheckSolidTile##bpp(x, y, w, h, colorPtr, needSameColor) \
int x, y, w, h; \
CARD32 *colorPtr; \
Bool needSameColor; \
{ \
CARD##bpp *fbptr; \
CARD##bpp colorValue; \
int dx, dy; \
\
fbptr = (CARD##bpp *) \
&rfbScreen.pfbMemory[y * rfbScreen.paddedWidthInBytes + x * (bpp/8)]; \
\
colorValue = *fbptr; \
if (needSameColor && (CARD32)colorValue != *colorPtr) \
return FALSE; \
\
for (dy = 0; dy < h; dy++) { \
for (dx = 0; dx < w; dx++) { \
if (colorValue != fbptr[dx]) \
return FALSE; \
} \
fbptr = (CARD##bpp *)((CARD8 *)fbptr + rfbScreen.paddedWidthInBytes); \
} \
\
*colorPtr = (CARD32)colorValue; \
return TRUE; \
}
DEFINE_CHECK_SOLID_FUNCTION(8)
DEFINE_CHECK_SOLID_FUNCTION(16)
DEFINE_CHECK_SOLID_FUNCTION(32)
static Bool
SendRectSimple(cl, x, y, w, h)
rfbClientPtr cl;
int x, y, w, h;
{
int maxBeforeSize, maxAfterSize;
int maxRectSize, maxRectWidth;
int subrectMaxWidth, subrectMaxHeight;
int dx, dy;
int rw, rh;
maxRectSize = tightConf[compressLevel].maxRectSize;
maxRectWidth = tightConf[compressLevel].maxRectWidth;
maxBeforeSize = maxRectSize * (cl->format.bitsPerPixel / 8);
maxAfterSize = maxBeforeSize + (maxBeforeSize + 99) / 100 + 12;
if (tightBeforeBufSize < maxBeforeSize) {
tightBeforeBufSize = maxBeforeSize;
if (tightBeforeBuf == NULL)
tightBeforeBuf = (char *)xalloc(tightBeforeBufSize);
else
tightBeforeBuf = (char *)xrealloc(tightBeforeBuf,
tightBeforeBufSize);
}
if (tightAfterBufSize < maxAfterSize) {
tightAfterBufSize = maxAfterSize;
if (tightAfterBuf == NULL)
tightAfterBuf = (char *)xalloc(tightAfterBufSize);
else
tightAfterBuf = (char *)xrealloc(tightAfterBuf,
tightAfterBufSize);
}
if (w > maxRectWidth || w * h > maxRectSize) {
subrectMaxWidth = (w > maxRectWidth) ? maxRectWidth : w;
subrectMaxHeight = maxRectSize / subrectMaxWidth;
for (dy = 0; dy < h; dy += subrectMaxHeight) {
for (dx = 0; dx < w; dx += maxRectWidth) {
rw = (dx + maxRectWidth < w) ? maxRectWidth : w - dx;
rh = (dy + subrectMaxHeight < h) ? subrectMaxHeight : h - dy;
if (!SendSubrect(cl, x+dx, y+dy, rw, rh))
return FALSE;
}
}
} else {
if (!SendSubrect(cl, x, y, w, h))
return FALSE;
}
return TRUE;
}
static Bool
SendSubrect(cl, x, y, w, h)
rfbClientPtr cl;
int x, y, w, h;
{
char *fbptr;
Bool success = FALSE;
/* Send pending data if there is more than 128 bytes. */
if (ublen > 128) {
if (!rfbSendUpdateBuf(cl))
return FALSE;
}
if (!SendTightHeader(cl, x, y, w, h))
return FALSE;
fbptr = (rfbScreen.pfbMemory + (rfbScreen.paddedWidthInBytes * y)
+ (x * (rfbScreen.bitsPerPixel / 8)));
if (subsampLevel == TJ_GRAYSCALE && qualityLevel != -1)
return SendJpegRect(cl, x, y, w, h, qualityLevel);
paletteMaxColors = w * h / tightConf[compressLevel].idxMaxColorsDivisor;
if(qualityLevel != -1)
paletteMaxColors = 24;
if ( paletteMaxColors < 2 &&
w * h >= tightConf[compressLevel].monoMinRectSize ) {
paletteMaxColors = 2;
}
if (cl->format.bitsPerPixel == rfbServerFormat.bitsPerPixel &&
cl->format.redMax == rfbServerFormat.redMax &&
cl->format.greenMax == rfbServerFormat.greenMax &&
cl->format.blueMax == rfbServerFormat.blueMax &&
cl->format.bitsPerPixel >= 16) {
/* This is so we can avoid translating the pixels when compressing
with JPEG, since it is unnecessary */
switch (cl->format.bitsPerPixel) {
case 16:
FastFillPalette16(cl, (CARD16 *)fbptr, w,
rfbScreen.paddedWidthInBytes/2, h);
break;
default:
FastFillPalette32(cl, (CARD32 *)fbptr, w,
rfbScreen.paddedWidthInBytes/4, h);
}
if(paletteNumColors != 0 || qualityLevel == -1) {
(*cl->translateFn)(cl->translateLookupTable, &rfbServerFormat,
&cl->format, fbptr, tightBeforeBuf,
rfbScreen.paddedWidthInBytes, w, h);
}
}
else {
(*cl->translateFn)(cl->translateLookupTable, &rfbServerFormat,
&cl->format, fbptr, tightBeforeBuf,
rfbScreen.paddedWidthInBytes, w, h);
switch (cl->format.bitsPerPixel) {
case 8:
FillPalette8(w * h);
break;
case 16:
FillPalette16(w * h);
break;
default:
FillPalette32(w * h);
}
}
switch (paletteNumColors) {
case 0:
/* Truecolor image */
if (qualityLevel != -1) {
success = SendJpegRect(cl, x, y, w, h, qualityLevel);
} else {
success = SendFullColorRect(cl, w, h);
}
break;
case 1:
/* Solid rectangle */
success = SendSolidRect(cl);
break;
case 2:
/* Two-color rectangle */
success = SendMonoRect(cl, w, h);
break;
default:
/* Up to 256 different colors */
success = SendIndexedRect(cl, w, h);
}
return success;
}
static Bool
SendTightHeader(cl, x, y, w, h)
rfbClientPtr cl;
int x, y, w, h;
{
rfbFramebufferUpdateRectHeader rect;
if (ublen + sz_rfbFramebufferUpdateRectHeader > UPDATE_BUF_SIZE) {
if (!rfbSendUpdateBuf(cl))
return FALSE;
}
rect.r.x = Swap16IfLE(x);
rect.r.y = Swap16IfLE(y);
rect.r.w = Swap16IfLE(w);
rect.r.h = Swap16IfLE(h);
rect.encoding = Swap32IfLE(rfbEncodingTight);
memcpy(&updateBuf[ublen], (char *)&rect,
sz_rfbFramebufferUpdateRectHeader);
ublen += sz_rfbFramebufferUpdateRectHeader;
cl->rfbRectanglesSent[rfbEncodingTight]++;
cl->rfbBytesSent[rfbEncodingTight] += sz_rfbFramebufferUpdateRectHeader;
return TRUE;
}
/*
* Subencoding implementations.
*/
static Bool
SendSolidRect(cl)
rfbClientPtr cl;
{
int len;
if (usePixelFormat24) {
Pack24(tightBeforeBuf, &cl->format, 1);
len = 3;
} else
len = cl->format.bitsPerPixel / 8;
if (ublen + 1 + len > UPDATE_BUF_SIZE) {
if (!rfbSendUpdateBuf(cl))
return FALSE;
}
updateBuf[ublen++] = (char)(rfbTightFill << 4);
memcpy (&updateBuf[ublen], tightBeforeBuf, len);
ublen += len;
cl->rfbBytesSent[rfbEncodingTight] += len + 1;
return TRUE;
}
static Bool
SendMonoRect(cl, w, h)
rfbClientPtr cl;
int w, h;
{
int streamId = 1;
int paletteLen, dataLen;
if ( (ublen + TIGHT_MIN_TO_COMPRESS + 6 +
2 * cl->format.bitsPerPixel / 8) > UPDATE_BUF_SIZE ) {
if (!rfbSendUpdateBuf(cl))
return FALSE;
}
/* Prepare tight encoding header. */
dataLen = (w + 7) / 8;
dataLen *= h;
if (tightConf[compressLevel].monoZlibLevel == 0)
updateBuf[ublen++] = (char)((rfbTightNoZlib | rfbTightExplicitFilter) << 4);
else
updateBuf[ublen++] = (streamId | rfbTightExplicitFilter) << 4;
updateBuf[ublen++] = rfbTightFilterPalette;
updateBuf[ublen++] = 1;
/* Prepare palette, convert image. */
switch (cl->format.bitsPerPixel) {
case 32:
EncodeMonoRect32((CARD8 *)tightBeforeBuf, w, h);
((CARD32 *)tightAfterBuf)[0] = monoBackground;
((CARD32 *)tightAfterBuf)[1] = monoForeground;
if (usePixelFormat24) {
Pack24(tightAfterBuf, &cl->format, 2);
paletteLen = 6;
} else
paletteLen = 8;
memcpy(&updateBuf[ublen], tightAfterBuf, paletteLen);
ublen += paletteLen;
cl->rfbBytesSent[rfbEncodingTight] += 3 + paletteLen;
break;
case 16:
EncodeMonoRect16((CARD8 *)tightBeforeBuf, w, h);
((CARD16 *)tightAfterBuf)[0] = (CARD16)monoBackground;
((CARD16 *)tightAfterBuf)[1] = (CARD16)monoForeground;
memcpy(&updateBuf[ublen], tightAfterBuf, 4);
ublen += 4;
cl->rfbBytesSent[rfbEncodingTight] += 7;
break;
default:
EncodeMonoRect8((CARD8 *)tightBeforeBuf, w, h);
updateBuf[ublen++] = (char)monoBackground;
updateBuf[ublen++] = (char)monoForeground;
cl->rfbBytesSent[rfbEncodingTight] += 5;
}
return CompressData(cl, streamId, dataLen,
tightConf[compressLevel].monoZlibLevel,
Z_DEFAULT_STRATEGY);
}
static Bool
SendIndexedRect(cl, w, h)
rfbClientPtr cl;
int w, h;
{
int streamId = 2;
int i, entryLen;
if ( (ublen + TIGHT_MIN_TO_COMPRESS + 6 +
paletteNumColors * cl->format.bitsPerPixel / 8) > UPDATE_BUF_SIZE ) {
if (!rfbSendUpdateBuf(cl))
return FALSE;
}
/* Prepare tight encoding header. */
if (tightConf[compressLevel].idxZlibLevel == 0)
updateBuf[ublen++] = (char)((rfbTightNoZlib | rfbTightExplicitFilter) << 4);
else
updateBuf[ublen++] = (streamId | rfbTightExplicitFilter) << 4;
updateBuf[ublen++] = rfbTightFilterPalette;
updateBuf[ublen++] = (char)(paletteNumColors - 1);
/* Prepare palette, convert image. */
switch (cl->format.bitsPerPixel) {
case 32:
EncodeIndexedRect32((CARD8 *)tightBeforeBuf, w * h);
for (i = 0; i < paletteNumColors; i++) {
((CARD32 *)tightAfterBuf)[i] =
palette.entry[i].listNode->rgb;
}
if (usePixelFormat24) {
Pack24(tightAfterBuf, &cl->format, paletteNumColors);
entryLen = 3;
} else
entryLen = 4;
memcpy(&updateBuf[ublen], tightAfterBuf, paletteNumColors * entryLen);
ublen += paletteNumColors * entryLen;
cl->rfbBytesSent[rfbEncodingTight] += 3 + paletteNumColors * entryLen;
break;
case 16:
EncodeIndexedRect16((CARD8 *)tightBeforeBuf, w * h);
for (i = 0; i < paletteNumColors; i++) {
((CARD16 *)tightAfterBuf)[i] =
(CARD16)palette.entry[i].listNode->rgb;
}
memcpy(&updateBuf[ublen], tightAfterBuf, paletteNumColors * 2);
ublen += paletteNumColors * 2;
cl->rfbBytesSent[rfbEncodingTight] += 3 + paletteNumColors * 2;
break;
default:
return FALSE; /* Should never happen. */
}
return CompressData(cl, streamId, w * h,
tightConf[compressLevel].idxZlibLevel,
Z_DEFAULT_STRATEGY);
}
static Bool
SendFullColorRect(cl, w, h)
rfbClientPtr cl;
int w, h;
{
int streamId = 0;
int len;
if (ublen + TIGHT_MIN_TO_COMPRESS + 1 > UPDATE_BUF_SIZE) {
if (!rfbSendUpdateBuf(cl))
return FALSE;
}
if (tightConf[compressLevel].rawZlibLevel == 0)
updateBuf[ublen++] = (char)(rfbTightNoZlib << 4);
else
updateBuf[ublen++] = 0x00; /* stream id = 0, no flushing, no filter */
cl->rfbBytesSent[rfbEncodingTight]++;
if (usePixelFormat24) {
Pack24(tightBeforeBuf, &cl->format, w * h);
len = 3;
} else
len = cl->format.bitsPerPixel / 8;
return CompressData(cl, streamId, w * h * len,
tightConf[compressLevel].rawZlibLevel,
Z_DEFAULT_STRATEGY);
}
static Bool
CompressData(cl, streamId, dataLen, zlibLevel, zlibStrategy)
rfbClientPtr cl;
int streamId, dataLen, zlibLevel, zlibStrategy;
{
z_streamp pz;
int err, i;
if (dataLen < TIGHT_MIN_TO_COMPRESS) {
memcpy(&updateBuf[ublen], tightBeforeBuf, dataLen);
ublen += dataLen;
cl->rfbBytesSent[rfbEncodingTight] += dataLen;
return TRUE;
}
if (zlibLevel == 0)
return SendCompressedData (cl, tightBeforeBuf, dataLen);
pz = &cl->zsStruct[streamId];
/* Initialize compression stream if needed. */
if (!cl->zsActive[streamId]) {
pz->zalloc = Z_NULL;
pz->zfree = Z_NULL;
pz->opaque = Z_NULL;
err = deflateInit2 (pz, zlibLevel, Z_DEFLATED, MAX_WBITS,
MAX_MEM_LEVEL, zlibStrategy);
if (err != Z_OK)
return FALSE;
cl->zsActive[streamId] = TRUE;
cl->zsLevel[streamId] = zlibLevel;
}
/* Prepare buffer pointers. */
pz->next_in = (Bytef *)tightBeforeBuf;
pz->avail_in = dataLen;
pz->next_out = (Bytef *)tightAfterBuf;
pz->avail_out = tightAfterBufSize;
/* Change compression parameters if needed. */
if (zlibLevel != cl->zsLevel[streamId]) {
if (deflateParams (pz, zlibLevel, zlibStrategy) != Z_OK) {
return FALSE;
}
cl->zsLevel[streamId] = zlibLevel;
}
/* Actual compression. */
if ( deflate (pz, Z_SYNC_FLUSH) != Z_OK ||
pz->avail_in != 0 || pz->avail_out == 0 ) {
return FALSE;
}
return SendCompressedData(cl, tightAfterBuf,
tightAfterBufSize - pz->avail_out);
}
static Bool SendCompressedData(cl, buf, compressedLen)
rfbClientPtr cl;
char *buf;
int compressedLen;
{
int i, portionLen;
updateBuf[ublen++] = compressedLen & 0x7F;
cl->rfbBytesSent[rfbEncodingTight]++;
if (compressedLen > 0x7F) {
updateBuf[ublen-1] |= 0x80;
updateBuf[ublen++] = compressedLen >> 7 & 0x7F;
cl->rfbBytesSent[rfbEncodingTight]++;
if (compressedLen > 0x3FFF) {
updateBuf[ublen-1] |= 0x80;
updateBuf[ublen++] = compressedLen >> 14 & 0xFF;
cl->rfbBytesSent[rfbEncodingTight]++;
}
}
portionLen = UPDATE_BUF_SIZE;
for (i = 0; i < compressedLen; i += portionLen) {
if (i + portionLen > compressedLen) {
portionLen = compressedLen - i;
}
if (ublen + portionLen > UPDATE_BUF_SIZE) {
if (!rfbSendUpdateBuf(cl))
return FALSE;
}
memcpy(&updateBuf[ublen], &buf[i], portionLen);
ublen += portionLen;
}
cl->rfbBytesSent[rfbEncodingTight] += compressedLen;
return TRUE;
}
/*
* Code to determine how many different colors used in rectangle.
*/
static void
FillPalette8(count)
int count;
{
CARD8 *data = (CARD8 *)tightBeforeBuf;
CARD8 c0, c1;
int i, n0, n1;
paletteNumColors = 0;
c0 = data[0];
for (i = 1; i < count && data[i] == c0; i++);
if (i == count) {
paletteNumColors = 1;
return; /* Solid rectangle */
}
if (paletteMaxColors < 2)
return;
n0 = i;
c1 = data[i];
n1 = 0;
for (i++; i < count; i++) {
if (data[i] == c0) {
n0++;
} else if (data[i] == c1) {
n1++;
} else
break;
}
if (i == count) {
if (n0 > n1) {
monoBackground = (CARD32)c0;
monoForeground = (CARD32)c1;
} else {
monoBackground = (CARD32)c1;
monoForeground = (CARD32)c0;
}
paletteNumColors = 2; /* Two colors */
}
}
#define DEFINE_FILL_PALETTE_FUNCTION(bpp) \
\
static void \
FillPalette##bpp(count) \
int count; \
{ \
CARD##bpp *data = (CARD##bpp *)tightBeforeBuf; \
CARD##bpp c0, c1, ci; \
int i, n0, n1, ni; \
\
c0 = data[0]; \
for (i = 1; i < count && data[i] == c0; i++); \
if (i >= count) { \
paletteNumColors = 1; /* Solid rectangle */ \
return; \
} \
\
if (paletteMaxColors < 2) { \
paletteNumColors = 0; /* Full-color encoding preferred */ \
return; \
} \
\
n0 = i; \
c1 = data[i]; \
n1 = 0; \
for (i++; i < count; i++) { \
ci = data[i]; \
if (ci == c0) { \
n0++; \
} else if (ci == c1) { \
n1++; \
} else \
break; \
} \
if (i >= count) { \
if (n0 > n1) { \
monoBackground = (CARD32)c0; \
monoForeground = (CARD32)c1; \
} else { \
monoBackground = (CARD32)c1; \
monoForeground = (CARD32)c0; \
} \
paletteNumColors = 2; /* Two colors */ \
return; \
} \
\
PaletteReset(); \
PaletteInsert (c0, (CARD32)n0, bpp); \
PaletteInsert (c1, (CARD32)n1, bpp); \
\
ni = 1; \
for (i++; i < count; i++) { \
if (data[i] == ci) { \
ni++; \
} else { \
if (!PaletteInsert (ci, (CARD32)ni, bpp)) \
return; \
ci = data[i]; \
ni = 1; \
} \
} \
PaletteInsert (ci, (CARD32)ni, bpp); \
}
DEFINE_FILL_PALETTE_FUNCTION(16)
DEFINE_FILL_PALETTE_FUNCTION(32)
#define DEFINE_FAST_FILL_PALETTE_FUNCTION(bpp) \
\
static void \
FastFillPalette##bpp(cl, data, w, pitch, h) \
rfbClientPtr cl; \
CARD##bpp *data; \
int w, pitch, h; \
{ \
CARD##bpp c0, c1, ci, mask, c0t, c1t, cit; \
int i, j, i2, j2, n0, n1, ni; \
\
if (cl->translateFn != rfbTranslateNone) { \
mask = rfbServerFormat.redMax << rfbServerFormat.redShift; \
mask |= rfbServerFormat.greenMax << rfbServerFormat.greenShift; \
mask |= rfbServerFormat.blueMax << rfbServerFormat.blueShift; \
} else mask = ~0; \
\
c0 = data[0] & mask; \
for (j = 0; j < h; j++) { \
for (i = 0; i < w; i++) { \
if ((data[j * pitch + i] & mask) != c0) \
goto done; \
} \
} \
done: \
if (j >= h) { \
paletteNumColors = 1; /* Solid rectangle */ \
return; \
} \
if (paletteMaxColors < 2) { \
paletteNumColors = 0; /* Full-color encoding preferred */ \
return; \
} \
\
n0 = j * w + i; \
c1 = data[j * pitch + i] & mask; \
n1 = 0; \
i++; if (i >= w) {i = 0; j++;} \
for (j2 = j; j2 < h; j2++) { \
for (i2 = i; i2 < w; i2++) { \
ci = data[j2 * pitch + i2] & mask; \
if (ci == c0) { \
n0++; \
} else if (ci == c1) { \
n1++; \
} else \
goto done2; \
} \
i = 0; \
} \
done2: \
(*cl->translateFn)(cl->translateLookupTable, &rfbServerFormat, \
&cl->format, (char *)&c0, (char *)&c0t, bpp/8, \
1, 1); \
(*cl->translateFn)(cl->translateLookupTable, &rfbServerFormat, \
&cl->format, (char *)&c1, (char *)&c1t, bpp/8, \
1, 1); \
if (j2 >= h) { \
if (n0 > n1) { \
monoBackground = (CARD32)c0t; \
monoForeground = (CARD32)c1t; \
} else { \
monoBackground = (CARD32)c1t; \
monoForeground = (CARD32)c0t; \
} \
paletteNumColors = 2; /* Two colors */ \
return; \
} \
\
PaletteReset(); \
PaletteInsert (c0t, (CARD32)n0, bpp); \
PaletteInsert (c1t, (CARD32)n1, bpp); \
\
ni = 1; \
i2++; if (i2 >= w) {i2 = 0; j2++;} \
for (j = j2; j < h; j++) { \
for (i = i2; i < w; i++) { \
if ((data[j * pitch + i] & mask) == ci) { \
ni++; \
} else { \
(*cl->translateFn)(cl->translateLookupTable, \
&rfbServerFormat, &cl->format, \
(char *)&ci, (char *)&cit, bpp/8, \
1, 1); \
if (!PaletteInsert (cit, (CARD32)ni, bpp)) \
return; \
ci = data[j * pitch + i] & mask; \
ni = 1; \
} \
} \
i2 = 0; \
} \
\
(*cl->translateFn)(cl->translateLookupTable, &rfbServerFormat, \
&cl->format, (char *)&ci, (char *)&cit, bpp/8, \
1, 1); \
PaletteInsert (cit, (CARD32)ni, bpp); \
}
DEFINE_FAST_FILL_PALETTE_FUNCTION(16)
DEFINE_FAST_FILL_PALETTE_FUNCTION(32)
/*
* Functions to operate with palette structures.
*/
#define HASH_FUNC16(rgb) ((int)((((rgb) >> 8) + (rgb)) & 0xFF))
#define HASH_FUNC32(rgb) ((int)((((rgb) >> 16) + ((rgb) >> 8)) & 0xFF))
static void
PaletteReset(void)
{
paletteNumColors = 0;
memset(palette.hash, 0, 256 * sizeof(COLOR_LIST *));
}
static int
PaletteInsert(rgb, numPixels, bpp)
CARD32 rgb;
int numPixels;
int bpp;
{
COLOR_LIST *pnode;
COLOR_LIST *prev_pnode = NULL;
int hash_key, idx, new_idx, count;
hash_key = (bpp == 16) ? HASH_FUNC16(rgb) : HASH_FUNC32(rgb);
pnode = palette.hash[hash_key];
while (pnode != NULL) {
if (pnode->rgb == rgb) {
/* Such palette entry already exists. */
new_idx = idx = pnode->idx;
count = palette.entry[idx].numPixels + numPixels;
if (new_idx && palette.entry[new_idx-1].numPixels < count) {
do {
palette.entry[new_idx] = palette.entry[new_idx-1];
palette.entry[new_idx].listNode->idx = new_idx;
new_idx--;
}
while (new_idx && palette.entry[new_idx-1].numPixels < count);
palette.entry[new_idx].listNode = pnode;
pnode->idx = new_idx;
}
palette.entry[new_idx].numPixels = count;
return paletteNumColors;
}
prev_pnode = pnode;
pnode = pnode->next;
}
/* Check if palette is full. */
if (paletteNumColors == 256 || paletteNumColors == paletteMaxColors) {
paletteNumColors = 0;
return 0;
}
/* Move palette entries with lesser pixel counts. */
for ( idx = paletteNumColors;
idx > 0 && palette.entry[idx-1].numPixels < numPixels;
idx-- ) {
palette.entry[idx] = palette.entry[idx-1];
palette.entry[idx].listNode->idx = idx;
}
/* Add new palette entry into the freed slot. */
pnode = &palette.list[paletteNumColors];
if (prev_pnode != NULL) {
prev_pnode->next = pnode;
} else {
palette.hash[hash_key] = pnode;
}
pnode->next = NULL;
pnode->idx = idx;
pnode->rgb = rgb;
palette.entry[idx].listNode = pnode;
palette.entry[idx].numPixels = numPixels;
return (++paletteNumColors);
}
/*
* Converting 32-bit color samples into 24-bit colors.
* Should be called only when redMax, greenMax and blueMax are 255.
* Color components assumed to be byte-aligned.
*/
static void Pack24(buf, fmt, count)
char *buf;
rfbPixelFormat *fmt;
int count;
{
CARD32 *buf32;
CARD32 pix;
int r_shift, g_shift, b_shift;
buf32 = (CARD32 *)buf;
if (!rfbServerFormat.bigEndian == !fmt->bigEndian) {
r_shift = fmt->redShift;
g_shift = fmt->greenShift;
b_shift = fmt->blueShift;
} else {
r_shift = 24 - fmt->redShift;
g_shift = 24 - fmt->greenShift;
b_shift = 24 - fmt->blueShift;
}
while (count--) {
pix = *buf32++;
*buf++ = (char)(pix >> r_shift);
*buf++ = (char)(pix >> g_shift);
*buf++ = (char)(pix >> b_shift);
}
}
/*
* Converting truecolor samples into palette indices.
*/
#define DEFINE_IDX_ENCODE_FUNCTION(bpp) \
\
static void \
EncodeIndexedRect##bpp(buf, count) \
CARD8 *buf; \
int count; \
{ \
COLOR_LIST *pnode; \
CARD##bpp *src; \
CARD##bpp rgb; \
int rep = 0; \
\
src = (CARD##bpp *) buf; \
\
while (count--) { \
rgb = *src++; \
while (count && *src == rgb) { \
rep++, src++, count--; \
} \
pnode = palette.hash[HASH_FUNC##bpp(rgb)]; \
while (pnode != NULL) { \
if ((CARD##bpp)pnode->rgb == rgb) { \
*buf++ = (CARD8)pnode->idx; \
while (rep) { \
*buf++ = (CARD8)pnode->idx; \
rep--; \
} \
break; \
} \
pnode = pnode->next; \
} \
} \
}
DEFINE_IDX_ENCODE_FUNCTION(16)
DEFINE_IDX_ENCODE_FUNCTION(32)
#define DEFINE_MONO_ENCODE_FUNCTION(bpp) \
\
static void \
EncodeMonoRect##bpp(buf, w, h) \
CARD8 *buf; \
int w, h; \
{ \
CARD##bpp *ptr; \
CARD##bpp bg; \
unsigned int value, mask; \
int aligned_width; \
int x, y, bg_bits; \
\
ptr = (CARD##bpp *) buf; \
bg = (CARD##bpp) monoBackground; \
aligned_width = w - w % 8; \
\
for (y = 0; y < h; y++) { \
for (x = 0; x < aligned_width; x += 8) { \
for (bg_bits = 0; bg_bits < 8; bg_bits++) { \
if (*ptr++ != bg) \
break; \
} \
if (bg_bits == 8) { \
*buf++ = 0; \
continue; \
} \
mask = 0x80 >> bg_bits; \
value = mask; \
for (bg_bits++; bg_bits < 8; bg_bits++) { \
mask >>= 1; \
if (*ptr++ != bg) { \
value |= mask; \
} \
} \
*buf++ = (CARD8)value; \
} \
\
mask = 0x80; \
value = 0; \
if (x >= w) \
continue; \
\
for (; x < w; x++) { \
if (*ptr++ != bg) { \
value |= mask; \
} \
mask >>= 1; \
} \
*buf++ = (CARD8)value; \
} \
}
DEFINE_MONO_ENCODE_FUNCTION(8)
DEFINE_MONO_ENCODE_FUNCTION(16)
DEFINE_MONO_ENCODE_FUNCTION(32)
/*
* JPEG compression stuff.
*/
static unsigned long jpegDstDataLen;
static tjhandle j=NULL;
static Bool
SendJpegRect(cl, x, y, w, h, quality)
rfbClientPtr cl;
int x, y, w, h;
int quality;
{
int dy;
unsigned char *srcbuf;
int ps=rfbServerFormat.bitsPerPixel/8;
int subsamp=subsampLevel2tjsubsamp[subsampLevel];
unsigned long size=0;
int flags=0, pitch;
unsigned char *tmpbuf=NULL;
if (rfbServerFormat.bitsPerPixel == 8)
return SendFullColorRect(cl, w, h);
if(ps<2) {
rfbLog("Error: JPEG requires 16-bit, 24-bit, or 32-bit pixel format.\n");
return 0;
}
if(!j) {
if((j=tjInitCompress())==NULL) {
rfbLog("JPEG Error: %s\n", tjGetErrorStr()); return 0;
}
}
if (tightAfterBufSize < TJBUFSIZE(w,h)) {
if (tightAfterBuf == NULL)
tightAfterBuf = (char *)xalloc(TJBUFSIZE(w,h));
else
tightAfterBuf = (char *)xrealloc(tightAfterBuf,
TJBUFSIZE(w,h));
if(!tightAfterBuf) {
rfbLog("Memory allocation failure!\n");
return 0;
}
tightAfterBufSize = TJBUFSIZE(w,h);
}
if (ps == 2) {
CARD16 *srcptr, pix;
unsigned char *dst;
int inRed, inGreen, inBlue, i, j;
if((tmpbuf=(unsigned char *)malloc(w*h*3))==NULL)
rfbLog("Memory allocation failure!\n");
srcptr = (CARD16 *)
&rfbScreen.pfbMemory[y * rfbScreen.paddedWidthInBytes +
x * ps];
dst = tmpbuf;
for(j=0; j<h; j++) {
CARD16 *srcptr2=srcptr;
unsigned char *dst2=dst;
for(i=0; i<w; i++) {
pix = *srcptr2++;
inRed = (int)
(pix >> rfbServerFormat.redShift & rfbServerFormat.redMax);
inGreen = (int)
(pix >> rfbServerFormat.greenShift & rfbServerFormat.greenMax);
inBlue = (int)
(pix >> rfbServerFormat.blueShift & rfbServerFormat.blueMax);
*dst2++ = (CARD8)((inRed * 255 + rfbServerFormat.redMax / 2) /
rfbServerFormat.redMax);
*dst2++ = (CARD8)((inGreen * 255 + rfbServerFormat.greenMax / 2) /
rfbServerFormat.greenMax);
*dst2++ = (CARD8)((inBlue * 255 + rfbServerFormat.blueMax / 2) /
rfbServerFormat.blueMax);
}
srcptr+=rfbScreen.paddedWidthInBytes/ps;
dst+=w*3;
}
srcbuf = tmpbuf;
pitch = w*3;
ps = 3;
} else {
if(rfbServerFormat.bigEndian && ps==4) flags|=TJ_ALPHAFIRST;
if(rfbServerFormat.redShift==16 && rfbServerFormat.blueShift==0)
flags|=TJ_BGR;
if(rfbServerFormat.bigEndian) flags^=TJ_BGR;
srcbuf=(unsigned char *)&rfbScreen.pfbMemory[y *
rfbScreen.paddedWidthInBytes + x * ps];
pitch=rfbScreen.paddedWidthInBytes;
}
if(tjCompress(j, srcbuf, w, pitch, h, ps, (unsigned char *)tightAfterBuf,
&size, subsamp, quality, flags)==-1) {
rfbLog("JPEG Error: %s\n", tjGetErrorStr());
if(tmpbuf) {free(tmpbuf); tmpbuf=NULL;}
return 0;
}
jpegDstDataLen=(int)size;
if(tmpbuf) {free(tmpbuf); tmpbuf=NULL;}
if (ublen + TIGHT_MIN_TO_COMPRESS + 1 > UPDATE_BUF_SIZE) {
if (!rfbSendUpdateBuf(cl))
return FALSE;
}
updateBuf[ublen++] = (char)(rfbTightJpeg << 4);
cl->rfbBytesSent[rfbEncodingTight]++;
return SendCompressedData(cl, tightAfterBuf, jpegDstDataLen);
}
/* Copyright (C)2004 Landmark Graphics
* Copyright (C)2005, 2006 Sun Microsystems, Inc.
*
* This library is free software and may be redistributed and/or modified under
* the terms of the wxWindows Library License, Version 3 or (at your option)
* any later version. The full license is in the LICENSE.txt file included
* with this distribution.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* wxWindows Library License for more details.
*/
#if (defined(_MSC_VER) || defined(__CYGWIN__) || defined(__MINGW32__)) && defined(_WIN32) && defined(DLLDEFINE)
#define DLLEXPORT __declspec(dllexport)
#else
#define DLLEXPORT
#endif
#define DLLCALL
/* Subsampling */
#define NUMSUBOPT 4
enum {TJ_444=0, TJ_422, TJ_411, TJ_GRAYSCALE};
/* Flags */
#define TJ_BGR 1
#define TJ_BOTTOMUP 2
#define TJ_FORCEMMX 8 /* Force IPP to use MMX code even if SSE available */
#define TJ_FORCESSE 16 /* Force IPP to use SSE1 code even if SSE2 available */
#define TJ_FORCESSE2 32 /* Force IPP to use SSE2 code (useful if auto-detect is not working properly) */
#define TJ_ALPHAFIRST 64 /* BGR buffer is ABGR and RGB buffer is ARGB */
#define TJ_FORCESSE3 128 /* Force IPP to use SSE3 code (useful if auto-detect is not working properly) */
typedef void* tjhandle;
#define TJPAD(p) (((p)+3)&(~3))
#ifndef max
#define max(a,b) ((a)>(b)?(a):(b))
#endif
#ifdef __cplusplus
extern "C" {
#endif
/* API follows */
/*
tjhandle tjInitCompress(void)
Creates a new JPEG compressor instance, allocates memory for the structures,
and returns a handle to the instance. Most applications will only
need to call this once at the beginning of the program or once for each
concurrent thread. Don't try to create a new instance every time you
compress an image, because this will cause performance to suffer.
RETURNS: NULL on error
*/
DLLEXPORT tjhandle DLLCALL tjInitCompress(void);
/*
int tjCompress(tjhandle j,
unsigned char *srcbuf, int width, int pitch, int height, int pixelsize,
unsigned char *dstbuf, unsigned long *size,
int jpegsubsamp, int jpegqual, int flags)
[INPUT] j = instance handle previously returned from a call to
tjInitCompress()
[INPUT] srcbuf = pointer to user-allocated image buffer containing pixels in
RGB(A) or BGR(A) form
[INPUT] width = width (in pixels) of the source image
[INPUT] pitch = bytes per line of the source image (width*pixelsize if the
bitmap is unpadded, else TJPAD(width*pixelsize) if each line of the bitmap
is padded to the nearest 32-bit boundary, such as is the case for Windows
bitmaps. You can also be clever and use this parameter to skip lines, etc.,
as long as the pitch is greater than 0.)
[INPUT] height = height (in pixels) of the source image
[INPUT] pixelsize = size (in bytes) of each pixel in the source image
RGBA and BGRA: 4, RGB and BGR: 3
[INPUT] dstbuf = pointer to user-allocated image buffer which will receive
the JPEG image. Use the macro TJBUFSIZE(width, height) to determine
the appropriate size for this buffer based on the image width and height.
[OUTPUT] size = pointer to unsigned long which receives the size (in bytes)
of the compressed image
[INPUT] jpegsubsamp = Specifies either 4:1:1, 4:2:2, or 4:4:4 subsampling.
When the image is converted from the RGB to YCbCr colorspace as part of the
JPEG compression process, every other Cb and Cr (chrominance) pixel can be
discarded to produce a smaller image with little perceptible loss of
image clarity (the human eye is more sensitive to small changes in
brightness than small changes in color.)
TJ_411: 4:1:1 subsampling. Discards every other Cb, Cr pixel in both
horizontal and vertical directions.
TJ_422: 4:2:2 subsampling. Discards every other Cb, Cr pixel only in
the horizontal direction.
TJ_444: no subsampling.
TJ_GRAYSCALE: Generate grayscale JPEG image
[INPUT] jpegqual = JPEG quality (an integer between 0 and 100 inclusive.)
[INPUT] flags = the bitwise OR of one or more of the following
TJ_BGR: The components of each pixel in the source image are stored in
B,G,R order, not R,G,B
TJ_BOTTOMUP: The source image is stored in bottom-up (Windows) order,
not top-down
TJ_FORCEMMX: Valid only for the Intel Performance Primitives implementation
of this codec-- force IPP to use MMX code (bypass CPU auto-detection)
TJ_FORCESSE: Valid only for the Intel Performance Primitives implementation
of this codec-- force IPP to use SSE code (bypass CPU auto-detection)
TJ_FORCESSE2: Valid only for the Intel Performance Primitives implementation
of this codec-- force IPP to use SSE2 code (bypass CPU auto-detection)
TJ_FORCESSE3: Valid only for the Intel Performance Primitives implementation
of this codec-- force IPP to use SSE3 code (bypass CPU auto-detection)
RETURNS: 0 on success, -1 on error
*/
DLLEXPORT int DLLCALL tjCompress(tjhandle j,
unsigned char *srcbuf, int width, int pitch, int height, int pixelsize,
unsigned char *dstbuf, unsigned long *size,
int jpegsubsamp, int jpegqual, int flags);
DLLEXPORT unsigned long DLLCALL TJBUFSIZE(int width, int height);
/*
tjhandle tjInitDecompress(void)
Creates a new JPEG decompressor instance, allocates memory for the
structures, and returns a handle to the instance. Most applications will
only need to call this once at the beginning of the program or once for each
concurrent thread. Don't try to create a new instance every time you
decompress an image, because this will cause performance to suffer.
RETURNS: NULL on error
*/
DLLEXPORT tjhandle DLLCALL tjInitDecompress(void);
/*
int tjDecompressHeader(tjhandle j,
unsigned char *srcbuf, unsigned long size,
int *width, int *height)
[INPUT] j = instance handle previously returned from a call to
tjInitDecompress()
[INPUT] srcbuf = pointer to a user-allocated buffer containing the JPEG image
to decompress
[INPUT] size = size of the JPEG image buffer (in bytes)
[OUTPUT] width = width (in pixels) of the JPEG image
[OUTPUT] height = height (in pixels) of the JPEG image
RETURNS: 0 on success, -1 on error
*/
DLLEXPORT int DLLCALL tjDecompressHeader(tjhandle j,
unsigned char *srcbuf, unsigned long size,
int *width, int *height);
/*
int tjDecompress(tjhandle j,
unsigned char *srcbuf, unsigned long size,
unsigned char *dstbuf, int width, int pitch, int height, int pixelsize,
int flags)
[INPUT] j = instance handle previously returned from a call to
tjInitDecompress()
[INPUT] srcbuf = pointer to a user-allocated buffer containing the JPEG image
to decompress
[INPUT] size = size of the JPEG image buffer (in bytes)
[INPUT] dstbuf = pointer to user-allocated image buffer which will receive
the bitmap image. This buffer should normally be pitch*height
bytes in size, although this pointer may also be used to decompress into
a specific region of a larger buffer.
[INPUT] width = width (in pixels) of the destination image
[INPUT] pitch = bytes per line of the destination image (width*pixelsize if the
bitmap is unpadded, else TJPAD(width*pixelsize) if each line of the bitmap
is padded to the nearest 32-bit boundary, such as is the case for Windows
bitmaps. You can also be clever and use this parameter to skip lines, etc.,
as long as the pitch is greater than 0.)
[INPUT] height = height (in pixels) of the destination image
[INPUT] pixelsize = size (in bytes) of each pixel in the destination image
RGBA/RGBx and BGRA/BGRx: 4, RGB and BGR: 3
[INPUT] flags = the bitwise OR of one or more of the following
TJ_BGR: The components of each pixel in the destination image should be
written in B,G,R order, not R,G,B
TJ_BOTTOMUP: The destination image should be stored in bottom-up
(Windows) order, not top-down
TJ_FORCEMMX: Valid only for the Intel Performance Primitives implementation
of this codec-- force IPP to use MMX code (bypass CPU auto-detection)
TJ_FORCESSE: Valid only for the Intel Performance Primitives implementation
of this codec-- force IPP to use SSE code (bypass CPU auto-detection)
TJ_FORCESSE2: Valid only for the Intel Performance Primitives implementation
of this codec-- force IPP to use SSE2 code (bypass CPU auto-detection)
RETURNS: 0 on success, -1 on error
*/
DLLEXPORT int DLLCALL tjDecompress(tjhandle j,
unsigned char *srcbuf, unsigned long size,
unsigned char *dstbuf, int width, int pitch, int height, int pixelsize,
int flags);
/*
int tjDestroy(tjhandle h)
Frees structures associated with a compression or decompression instance
[INPUT] h = instance handle (returned from a previous call to
tjInitCompress() or tjInitDecompress()
RETURNS: 0 on success, -1 on error
*/
DLLEXPORT int DLLCALL tjDestroy(tjhandle h);
/*
char *tjGetErrorStr(void)
Returns a descriptive error message explaining why the last command failed
*/
DLLEXPORT char* DLLCALL tjGetErrorStr(void);
#ifdef __cplusplus
}
#endif
#!/bin/sh
ldir="../../../libvncserver"
if [ ! -f "$ldir/tight.c.ORIG" ]; then
ls -l "$ldir/tight.c.ORIG"
exit 1
fi
set -xv
rm -f "$ldir/tight.c" "$ldir/turbojpeg.h"
mv "$ldir/tight.c.ORIG" "$ldir/tight.c"
ls -l $ldir/tight.c*
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment