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:
parent
507b473a2e
commit
d41c33e4b7
|
@ -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
48
vnc.js
|
@ -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>';
|
||||
|
|
Loading…
Reference in New Issue