]> git.proxmox.com Git - mirror_novnc.git/commitdiff
Add colour map support (non-true-color).
authorJoel Martin <jmartin@sentryds.com>
Tue, 1 Jun 2010 19:34:27 +0000 (14:34 -0500)
committerJoel Martin <jmartin@sentryds.com>
Tue, 1 Jun 2010 19:34:27 +0000 (14:34 -0500)
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).

include/canvas.js
vnc.js

index e1f1863d3b3a283b78dc8ffabd925ab96cf33109..2213b604c15ef061566238094ac0a48070fb332f 100644 (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;
     }
diff --git a/vnc.js b/vnc.js
index 31044f478e8289026e59861884a687ab969359e0..074559bd31a71d2a8acfef1e7077d24b086255ab 100644 (file)
--- a/vnc.js
+++ b/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>';