Add colour map support (non-true-color).

In colourMap mode there are 256 colours in a colour palette sent from
the server via the SetColourMapEntries message. This reduces the
bandwidth by about 1/4. However, appearance can be somewhat less than
ideal (pinks instead of gray, etc).

It also increases client side rendering performance especially on
firefox. Rendering a full 800x600 update takes about 950ms in
firefox on my system compared to about 1400ms. Round-trip time for
a full frame buffer update is even better on firefox (due to
performance of the flash WebSocket emulator). Reduced from about
1800ms to 1100ms on firefox (for 800x600 full update).
This commit is contained in:
Joel Martin 2010-06-01 14:34:27 -05:00
parent 507b473a2e
commit d41c33e4b7
2 changed files with 96 additions and 27 deletions

View File

@ -14,6 +14,9 @@ var Canvas = {
prefer_js : false,
true_color : false,
colourMap : [],
c_x : 0,
c_y : 0,
c_wx : 0,
@ -74,7 +77,7 @@ ctxDisable: function (e) {
},
init: function (id, width, height, keyDown, keyUp,
init: function (id, width, height, true_color, keyDown, keyUp,
mouseDown, mouseUp, mouseMove, mouseWheel) {
console.log(">> Canvas.init");
@ -105,6 +108,8 @@ init: function (id, width, height, keyDown, keyUp,
Canvas.c_y = c.getPosition().y;
Canvas.c_wx = c.getSize().x;
Canvas.c_wy = c.getSize().y;
Canvas.true_color = true_color;
Canvas.colourMap = [];
if (! c.getContext) { return; }
Canvas.ctx = c.getContext('2d');
@ -147,21 +152,26 @@ stop: function () {
* gecko, Javascript array handling is much slower.
*/
getTile: function(x, y, width, height, color) {
var img, data, p, red, green, blue, j, i;
var img, data, p, rgb, red, green, blue, j, i;
img = {'x': x, 'y': y, 'width': width, 'height': height,
'data': []};
if (Canvas.prefer_js) {
data = img.data;
red = color[0];
green = color[1];
blue = color[2];
if (Canvas.true_color) {
rgb = color;
} else {
rgb = Canvas.colourMap[color[0]];
}
red = rgb[0];
green = rgb[1];
blue = rgb[2];
for (j = 0; j < height; j++) {
for (i = 0; i < width; i++) {
p = (i + (j * width) ) * 4;
img.data[p + 0] = red;
img.data[p + 1] = green;
img.data[p + 2] = blue;
//img.data[p + 3] = 255; // Set Alpha
data[p + 0] = red;
data[p + 1] = green;
data[p + 2] = blue;
//data[p + 3] = 255; // Set Alpha
}
}
} else {
@ -171,13 +181,18 @@ getTile: function(x, y, width, height, color) {
},
setTile: function(img, x, y, w, h, color) {
var data, p, red, green, blue, width, j, i;
var data, p, rgb, red, green, blue, width, j, i;
if (Canvas.prefer_js) {
data = img.data;
width = img.width;
red = color[0];
green = color[1];
blue = color[2];
if (Canvas.true_color) {
rgb = color;
} else {
rgb = Canvas.colourMap[color[0]];
}
red = rgb[0];
green = rgb[1];
blue = rgb[2];
for (j = 0; j < h; j++) {
for (i = 0; i < w; i++) {
p = (x + i + ((y + j) * width) ) * 4;
@ -208,20 +223,48 @@ rgbxImage: function(x, y, width, height, arr, offset) {
/* Old firefox and Opera don't support createImageData */
img = Canvas.ctx.getImageData(0, 0, width, height);
data = img.data;
for (i=0; i < (width * height * 4); i=i+4) {
j=i+offset;
for (i=0, j=offset; i < (width * height * 4); i=i+4, j=j+4) {
data[i + 0] = arr[j + 0];
data[i + 1] = arr[j + 1];
data[i + 2] = arr[j + 2];
data[i + 3] = 255; // Set Alpha
}
Canvas.ctx.putImageData(img, x, y);
},
cmapImage: function(x, y, width, height, arr, offset) {
var img, i, j, k, data, rgb, cmap;
img = Canvas.ctx.getImageData(0, 0, width, height);
data = img.data;
cmap = Canvas.colourMap;
//console.log("cmapImage x: " + x + ", y: " + y + "arr.slice(0,20): " + arr.slice(0,20));
for (i=0, j=offset; i < (width * height * 4); i=i+4, j++) {
rgb = cmap[arr[j]];
data[i + 0] = rgb[0];
data[i + 1] = rgb[1];
data[i + 2] = rgb[2];
data[i + 3] = 255; // Set Alpha
}
Canvas.ctx.putImageData(img, x, y);
},
blitImage: function(x, y, width, height, arr, offset) {
if (Canvas.true_color) {
Canvas.rgbxImage(x, y, width, height, arr, offset);
} else {
Canvas.cmapImage(x, y, width, height, arr, offset);
}
},
fillRect: function(x, y, width, height, color) {
var newStyle = "rgb(" + color[0] + "," + color[1] + "," + color[2] + ")";
var rgb, newStyle;
if (Canvas.true_color) {
rgb = color;
} else {
rgb = Canvas.colourMap[color[0]];
}
if (newStyle !== Canvas.prevStyle) {
newStyle = "rgb(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + ")";
Canvas.ctx.fillStyle = newStyle;
Canvas.prevStyle = newStyle;
}

48
vnc.js
View File

@ -77,6 +77,10 @@ FBU : {
background : null
},
true_color : false,
fb_Bpp : 4,
fb_depth : 3,
// DOM objects
statusLine : null,
connectBtn : null,
@ -102,7 +106,6 @@ password : '',
fb_width : 0,
fb_height : 0,
fb_name : "",
fb_Bpp : 4,
rre_chunk : 100,
timing : {
@ -293,10 +296,18 @@ init_msg: function () {
name_length = RQ.shift32();
RFB.fb_name = RQ.shiftStr(name_length);
Canvas.init('VNC_canvas', RFB.fb_width, RFB.fb_height,
Canvas.init('VNC_canvas', RFB.fb_width, RFB.fb_height, RFB.true_color,
RFB.keyDown, RFB.keyUp, RFB.mouseDown, RFB.mouseUp,
RFB.mouseMove, RFB.mouseWheel);
if (RFB.true_color) {
RFB.fb_Bpp = 4;
RFB.fb_depth = 3;
} else {
RFB.fb_Bpp = 1;
RFB.fb_depth = 1;
}
response = RFB.pixelFormat();
response = response.concat(RFB.encodings());
response = response.concat(RFB.fbUpdateRequest(0));
@ -318,7 +329,8 @@ normal_msg: function () {
//console.log(">> normal_msg");
var RQ = RFB.RQ, FBU = RFB.FBU, now, fbu_rt_diff,
ret = true, msg_type, num_colours, msg;
ret = true, msg_type, msg,
c, first_colour, num_colours, red, green, blue;
if (FBU.rects > 0) {
msg_type = 0;
@ -414,11 +426,21 @@ normal_msg: function () {
break;
case 1: // SetColourMapEntries
console.log("SetColourMapEntries (unsupported)");
console.log("SetColourMapEntries");
RQ.shift8(); // Padding
RQ.shift16(); // First colour
first_colour = RQ.shift16(); // First colour
num_colours = RQ.shift16();
RQ.shiftBytes(num_colours * 6);
for (c=0; c < num_colours; c++) {
red = RQ.shift16();
//console.log("red before: " + red);
red = parseInt(red / 256, 10);
//console.log("red after: " + red);
green = parseInt(RQ.shift16() / 256, 10);
blue = parseInt(RQ.shift16() / 256, 10);
Canvas.colourMap[first_colour + c] = [red, green, blue];
}
console.log("Registered " + num_colours + " colourMap entries");
//console.log("colourMap: " + Canvas.colourMap);
break;
case 2: // Bell
console.log("Bell (unsupported)");
@ -477,7 +499,7 @@ display_raw: function () {
cur_y = FBU.y + (FBU.height - FBU.lines);
cur_height = Math.min(FBU.lines,
Math.floor(RQ.length/(FBU.width * RFB.fb_Bpp)));
Canvas.rgbxImage(FBU.x, cur_y, FBU.width, cur_height, RQ, 0);
Canvas.blitImage(FBU.x, cur_y, FBU.width, cur_height, RQ, 0);
RQ.shiftBytes(FBU.width * cur_height * RFB.fb_Bpp);
FBU.lines -= cur_height;
@ -629,7 +651,7 @@ display_hextile: function() {
Canvas.fillRect(x, y, w, h, FBU.background);
}
} else if (FBU.subencoding & 0x01) { // Raw
Canvas.rgbxImage(x, y, w, h, RQ, idx);
Canvas.blitImage(x, y, w, h, RQ, idx);
} else {
if (FBU.subencoding & 0x02) { // Background
FBU.background = RQ.slice(idx, idx + RFB.fb_Bpp);
@ -694,9 +716,9 @@ pixelFormat: function () {
arr.push8(0); // padding
arr.push8(RFB.fb_Bpp * 8); // bits-per-pixel
arr.push8(24); // depth
arr.push8(RFB.fb_depth * 8); // depth
arr.push8(0); // little-endian
arr.push8(1); // true-color
arr.push8(RFB.true_color); // true-color
arr.push16(255); // red-max
arr.push16(255); // green-max
@ -1187,7 +1209,7 @@ init_vars: function () {
},
connect: function (host, port, password, encrypt) {
connect: function (host, port, password, encrypt, true_color) {
console.log(">> connect");
RFB.host = (host !== undefined) ? host :
@ -1198,6 +1220,8 @@ connect: function (host, port, password, encrypt) {
$('VNC_password').value;
RFB.encrypt = (encrypt !== undefined) ? encrypt :
$('VNC_encrypt').checked;
RFB.true_color = (true_color !== undefined) ? true_color:
$('VNC_true_color').checked;
if ((!RFB.host) || (!RFB.port)) {
alert("Must set host and port");
return;
@ -1253,6 +1277,8 @@ load: function (target) {
html += ' type="password"></li>';
html += ' <li>Encrypt: <input id="VNC_encrypt"';
html += ' type="checkbox"></li>';
html += ' <li>True Color: <input id="VNC_true_color"';
html += ' type="checkbox" checked></li>';
html += ' <li><input id="VNC_connect_button" type="button"';
html += ' value="Loading" disabled></li>';
html += ' </ul>';