Explorar el Código

Various cross-browser fixes.

Now working under Arora 0.5.

But not Konqueror 4.2.2 (WebSockets never connects).

IE support with excanvas still pending.
Joel Martin hace 15 años
padre
commit
d93d3e09ab
Se han modificado 9 ficheros con 238 adiciones y 99 borrados
  1. 8 3
      README.md
  2. 4 4
      include/base64.js
  3. 156 40
      include/canvas.js
  4. 8 8
      include/util.js
  5. 8 5
      include/vnc.js
  6. 46 33
      tests/canvas.html
  7. 0 0
      tests/face.png.js
  8. 4 6
      tests/input.html
  9. 4 0
      vnc.html

+ 8 - 3
README.md

@@ -84,11 +84,16 @@ Browser Support
 
 I only currently test under Linux. Here are the current results:
 
-* Chrome 5.0.* beta: Works great. Native WebSockets support. Very fast.
+* Chrome 5.0.375.29 beta: Works great. Native WebSockets support. Very
+  fast.
 * firefox 3.5, 3.7: Works. Uses flash WebSockets emulator. Large
-  desktops with full-color image backgrounds are slow.
+  full-color images are slow.
+* Arora 0.50: Works. Broken putImageData so large full-color images
+  are slow.
+
 * Opera 10.10: Unusable: drops web-socket-js events.
-* Opera 10.60: Unusable: throws "WRONG_ARGUMENTS_ERR" on connect.
+* Opera 10.60: Broken: throws "WRONG_ARGUMENTS_ERR" on connect.
+* Konqueror 4.2.2: Broken: flash WebSockets emulator never connects.
 
 
 Integration

+ 4 - 4
include/base64.js

@@ -49,7 +49,7 @@ base64Pad     : '=',
 
 encode: function (data) {
     var result = '';
-    var chrTable = Base64.toBase64Table;
+    var chrTable = Base64.toBase64Table.split('');
     var pad = Base64.base64Pad;
     var length = data.length;
     var i;
@@ -107,11 +107,11 @@ decode: function (data, offset) {
     // Convert one by one.
     var idx = 0;
     for (var i = offset; i < data.length; i++) {
-        var c = binTable[data[i].charCodeAt(0) & 0x7f];
-        var padding = (data[i] == pad);
+        var c = binTable[data.charCodeAt(i) & 0x7f];
+        var padding = (data.charAt(i) == pad);
         // Skip illegal characters and whitespace
         if (c == -1) {
-            console.log("Illegal character '" + data[i].charCodeAt(0) + "'");
+            console.log("Illegal character '" + data.charCodeAt(i) + "'");
             continue;
         }
         

+ 156 - 40
include/canvas.js

@@ -10,10 +10,22 @@
 /*jslint white: false, bitwise: false */
 /*global window, console, $, Util */
 
+var Canvas, Canvas_native;
+
+(function () {
+    var pre, extra = "", start, end;
+    if (document.createElement('canvas').getContext) {
+        Canvas_native = true;
+    } else {
+        Canvas_native = false;
+        document.write("<script src='excanvas'><\/script>");
+    }
+}());
+
 // Everything namespaced inside Canvas
-var Canvas = {
+Canvas = {
 
-prefer_js : false,
+prefer_js  : false,
 
 true_color : false,
 colourMap  : [],
@@ -93,7 +105,7 @@ onKeyDown: function (e) {
 },
 
 onKeyUp : function (e) {
-    //console.log("keyup: " + e.key + "(" + e.code + ")");
+    //console.log("keyup: " + Canvas.getKeysym(e));
     if (! Canvas.focused) {
         return true;
     }
@@ -122,17 +134,86 @@ onMouseDisable: function (e) {
 },
 
 
-init: function (id, width, height, true_color, keyPress,
-                mouseButton, mouseMove) {
+init: function (id) {
+    var c, imgTest, arora;
     console.log(">> Canvas.init");
 
     Canvas.id = id;
+    c = $(Canvas.id);
+
+    if (Canvas_native) {
+        console.log("Using native canvas");
+        // Use default Canvas functions
+    } else {
+        console.warn("Using excanvas canvas emulation");
+        G_vmlCanvasManager.initElement(c);
+    }
+
+    if (! c.getContext) { throw("No getContext method"); }
+    Canvas.ctx = c.getContext('2d'); 
+
+    Canvas.clear();
+
+
+    /*
+     * Determine browser feature support and most optimal rendering
+     * methods
+     */
+    tval = 0;
+    try {
+        imgTest = Canvas.ctx.getImageData(0, 0, 1,1);
+        imgTest.data[0] = 123;
+        imgTest.data[3] = 255;
+        Canvas.ctx.putImageData(imgTest, 0, 0);
+        tval = Canvas.ctx.getImageData(0, 0, 1, 1).data[0];
+    } catch (exc) {
+    }
+    if (tval === 123) {
+        Canvas._rgbxImage = Canvas._rgbxImageData;
+        Canvas._cmapImage = Canvas._cmapImageData;
+        if (Canvas.ctx.createImageData) {
+            // If it's there, it's faster
+            console.log("Using Canvas createImageData");
+            Canvas._imageData = Canvas._imageDataCreate;
+        } else if (Canvas.ctx.getImageData) {
+            console.log("Using Canvas getImageData");
+            Canvas._imageData = Canvas._imageDataGet;
+        } else {
+            console.log("No imageData support");
+            return false;
+        }
+        if (Util.Engine.webkit) {
+            console.log("Prefering javascript operations");
+            Canvas.prefer_js = true;
+        } else {
+            console.log("Prefering Canvas operations");
+            Canvas.prefer_js = false;
+        }
+    } else {
+        console.log("No imageData, using fillRect (slow)");
+        Canvas._rgbxImage = Canvas._rgbxImageFill;
+        Canvas._cmapImage = Canvas._cmapImageFill;
+        Canvas.prefer_js = false;
+    }
 
+    Canvas.colourMap = [];
+    Canvas.prevStyle = "";
+    Canvas.focused = true;
+
+    //console.log("<< Canvas.init");
+    return true;
+},
+    
+
+start: function (keyPress, mouseButton, mouseMove) {
+    var c;
+    console.log(">> Canvas.start");
+
+    c = $(Canvas.id);
     Canvas.keyPress = keyPress || null;
     Canvas.mouseButton = mouseButton || null;
     Canvas.mouseMove = mouseMove || null;
 
-    var c = $(Canvas.id);
     Util.addEvent(document, 'keydown', Canvas.onKeyDown);
     Util.addEvent(document, 'keyup', Canvas.onKeyUp);
     Util.addEvent(c, 'mousedown', Canvas.onMouseDown);
@@ -145,34 +226,26 @@ init: function (id, width, height, true_color, keyPress,
     Util.addEvent(document, 'click', Canvas.onMouseDisable);
     Util.addEvent(document.body, 'contextmenu', Canvas.onMouseDisable);
 
-    Canvas.resize(width, height);
-    Canvas.c_wx = c.offsetWidth;
-    Canvas.c_wy = c.offsetHeight;
-    Canvas.true_color = true_color;
-    Canvas.colourMap = [];
-
-    if (! c.getContext) { return; }
-    Canvas.ctx = c.getContext('2d'); 
-    
-    Canvas.prevStyle = "";
-    Canvas.focused = true;
-
-    if (Util.Engine.webkit) {
-        Canvas.prefer_js = true;
-    }
-
-    //console.log("<< Canvas.init");
+    //console.log("<< Canvas.start");
 },
 
 clear: function () {
-    Canvas.ctx.clearRect(0, 0, Canvas.c_wx, Canvas.c_wy);
     Canvas.resize(640, 20);
+    Canvas.ctx.clearRect(0, 0, Canvas.c_wx, Canvas.c_wy);
 },
 
-resize: function (width, height) {
+resize: function (width, height, true_color) {
     var c = $(Canvas.id);
+
+    if (typeof true_color !== "undefined") {
+        Canvas.true_color = true_color;
+    }
+
     c.width = width;
     c.height = height;
+
+    Canvas.c_wx = c.offsetWidth;
+    Canvas.c_wy = c.offsetHeight;
 },
 
 stop: function () {
@@ -226,7 +299,7 @@ getTile: function(x, y, width, height, color) {
     return img;
 },
 
-setTile: function(img, x, y, w, h, color) {
+setSubTile: function(img, x, y, w, h, color) {
     var data, p, rgb, red, green, blue, width, j, i;
     if (Canvas.prefer_js) {
         data = img.data;
@@ -255,19 +328,25 @@ setTile: function(img, x, y, w, h, color) {
 
 putTile: function(img) {
     if (Canvas.prefer_js) {
-        Canvas.rgbxImage(img.x, img.y, img.width, img.height, img.data, 0);
-        //Canvas.ctx.putImageData(img, img.x, img.y);
+        Canvas._rgbxImage(img.x, img.y, img.width, img.height, img.data, 0);
     } else {
-        // No-op, under gecko already done by setTile
+        // No-op, under gecko already done by setSubTile
     }
 },
 
+_imageDataGet: function(width, height) {
+    return Canvas.ctx.getImageData(0, 0, width, height);
+},
+_imageDataCreate: function(width, height) {
+    return Canvas.ctx.createImageData(width, height);
+},
+_imageDataRaw: function(width, height) {
+    return {'data': [], 'width': width, 'height': height};
+},
 
-rgbxImage: function(x, y, width, height, arr, offset) {
+_rgbxImageData: function(x, y, width, height, arr, offset) {
     var img, i, j, data;
-    //console.log("rfbxImage: img: " + img + " x: " + x + " y: " + y + " width: " + width + " height: " + height);
-    /* Old firefox and Opera don't support createImageData */
-    img = Canvas.ctx.getImageData(0, 0, width, height);
+    img = Canvas._imageData(width, height);
     data = img.data;
     for (i=0, j=offset; i < (width * height * 4); i=i+4, j=j+4) {
         data[i + 0] = arr[j + 0];
@@ -278,13 +357,25 @@ rgbxImage: function(x, y, width, height, arr, offset) {
     Canvas.ctx.putImageData(img, x, y);
 },
 
-cmapImage: function(x, y, width, height, arr, offset) {
+// really slow fallback if we don't have imageData
+_rgbxImageFill: function(x, y, width, height, arr, offset) {
+    var sx = 0, sy = 0;
+    for (i=0, j=offset; i < (width * height); i+=1, j+=4) {
+        Canvas.fillRect(x+sx, y+sy, 1, 1, [arr[j+0], arr[j+1], arr[j+2]]);
+        sx += 1;
+        if ((sx % width) === 0) {
+            sx = 0;
+            sy += 1;
+        }
+    }
+},
+
+_cmapImageData: function(x, y, width, height, arr, offset) {
     var img, i, j, data, rgb, cmap;
-    img = Canvas.ctx.getImageData(0, 0, width, height);
+    img = Canvas._imageData(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 += 1) {
+    for (i=0, j=offset; i < (width * height * 4); i+=4, j+=1) {
         rgb = cmap[arr[j]];
         data[i + 0] = rgb[0];
         data[i + 1] = rgb[1];
@@ -294,15 +385,36 @@ cmapImage: function(x, y, width, height, arr, offset) {
     Canvas.ctx.putImageData(img, x, y);
 },
 
+_cmapImageFill: function(x, y, width, height, arr, offset) {
+    var sx = 0, sy = 0;
+    cmap = Canvas.colourMap;
+    console.log("here1: arr[2]: " + arr[2] + ", cmap[arr[2]]: " + cmap[arr[2]]);
+    for (i=0, j=offset; i < (width * height); i+=1, j+=1) {
+        Canvas.fillRect(x+sx, y+sy, 1, 1, [arr[j]]);
+        sx += 1;
+        if ((sx % width) === 0) {
+            sx = 0;
+            sy += 1;
+        }
+    }
+},
+
+
 blitImage: function(x, y, width, height, arr, offset) {
     if (Canvas.true_color) {
-        Canvas.rgbxImage(x, y, width, height, arr, offset);
+        Canvas._rgbxImage(x, y, width, height, arr, offset);
     } else {
-        Canvas.cmapImage(x, y, width, height, arr, offset);
+        Canvas._cmapImage(x, y, width, height, arr, offset);
     }
 },
 
-fillRect: function(x, y, width, height, color) {
+blitStringImage: function(str, x, y) {
+    var img = new Image();
+    img.onload = function () { Canvas.ctx.drawImage(img, x, y); };
+    img.src = str;
+},
+
+setFillColor: function(color) {
     var rgb, newStyle;
     if (Canvas.true_color) {
         rgb = color;
@@ -314,6 +426,10 @@ fillRect: function(x, y, width, height, color) {
         Canvas.ctx.fillStyle = newStyle;
         Canvas.prevStyle = newStyle;
     }
+},
+
+fillRect: function(x, y, width, height, color) {
+    Canvas.setFillColor(color);
     Canvas.ctx.fillRect(x, y, width, height);
 },
 

+ 8 - 8
include/util.js

@@ -171,24 +171,24 @@ Util.getEventPosition = function (e, obj) {
 
 // Event registration. Based on: http://www.scottandrew.com/weblog/articles/cbs-events
 Util.addEvent = function (obj, evType, fn){
-    if (obj.addEventListener){
-        obj.addEventListener(evType, fn, false); 
-        return true;
-    } else if (obj.attachEvent){
+    if (obj.attachEvent){
         var r = obj.attachEvent("on"+evType, fn);
         return r;
+    } else if (obj.addEventListener){
+        obj.addEventListener(evType, fn, false); 
+        return true;
     } else {
         throw("Handler could not be attached");
     }
 };
 
 Util.removeEvent = function(obj, evType, fn){
-    if (obj.removeEventListener){
-        obj.removeEventListener(evType, fn, false);
-        return true;
-    } else if (obj.detachEvent){
+    if (obj.detachEvent){
         var r = obj.detachEvent("on"+evType, fn);
         return r;
+    } else if (obj.removeEventListener){
+        obj.removeEventListener(evType, fn, false);
+        return true;
     } else {
         throw("Handler could not be removed");
     }

+ 8 - 5
include/vnc.js

@@ -64,7 +64,7 @@ true_color     : false,
 
 b64encode      : true,  // false means UTF-8 on the wire
 //b64encode      : false,  // false means UTF-8 on the wire
-connectTimeout : 1000,  // time to wait for connection
+connectTimeout : 3000,  // time to wait for connection
 
 
 // In preference order
@@ -140,6 +140,9 @@ load: function () {
         }
     }
 
+    // Initialize canvas/fxcanvas
+    Canvas.init(RFB.canvasID);
+
     // Populate encoding lookup tables
     RFB.encHandlers = {};
     RFB.encNames = {};
@@ -466,8 +469,8 @@ init_msg: function () {
         name_length   = RQ.shift32();
         RFB.fb_name = RQ.shiftStr(name_length);
 
-        Canvas.init(RFB.canvasID, RFB.fb_width, RFB.fb_height, RFB.true_color,
-                RFB.keyPress, RFB.mouseButton, RFB.mouseMove);
+        Canvas.resize(RFB.fb_width, RFB.fb_height, RFB.true_color);
+        Canvas.start(RFB.keyPress, RFB.mouseButton, RFB.mouseMove);
 
         if (RFB.true_color) {
             RFB.fb_Bpp           = 4;
@@ -873,7 +876,7 @@ display_hextile: function() {
                     sw = (wh >> 4)   + 1;
                     sh = (wh & 0x0f) + 1;
 
-                    Canvas.setTile(tile, sx, sy, sw, sh, color);
+                    Canvas.setSubTile(tile, sx, sy, sw, sh, color);
                 }
             }
             Canvas.putTile(tile);
@@ -1191,7 +1194,7 @@ recv_message_reorder: function(e) {
     } else {
         console.warn("sequence number mismatch: expected " +
                      RFB.RQ_seq_num + ", got " + seq_num);
-        if (RFB.RQ_reorder.length > 20) {
+        if (RFB.RQ_reorder.length > 40) {
             RFB.updateState('failed', "Re-order queue too long");
         } else {
             RFB.RQ_reorder = RFB.RQ_reorder.concat(e.data.substr(0));

+ 46 - 33
tests/canvas.html

@@ -3,13 +3,16 @@
     <body>
         Iterations: <input id='iterations' style='width:50' value="100">&nbsp;
 
-        <input id='startButton' type='button' value='Start' style='width:100px'
-            onclick="start();">&nbsp;
+        Width: <input id='width' style='width:50' value="640">&nbsp;
+        Height: <input id='height' style='width:50' value="480">&nbsp;
+
+        <input id='startButton' type='button' value='Do Performance Test'
+            style='width:150px' onclick="begin();">&nbsp;
 
         <br><br>
 
-        Canvas:<br>
-        <canvas id="canvas" width="640" height="20"
+        <b>Canvas</b> (should see three squares and two happy faces):<br>
+        <canvas id="canvas" width="200" height="100"
                 style="border-style: dotted; border-width: 1px;">
             Canvas not supported.
         </canvas>
@@ -23,14 +26,12 @@
     <script type='text/javascript' 
         src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
     -->
-
-    <script src="include/mootools.js"></script>
     <script src="include/util.js"></script>
     <script src="include/canvas.js"></script>
-
+    <script src="face.png.js"></script>
     <script>
         var msg_cnt = 0;
-        var width = 800, height = 600;
+        var start_width = 300, start_height = 100;
         var iterations;
 
         function message(str) {
@@ -40,35 +41,39 @@
             cell.scrollTop = cell.scrollHeight;
         }
 
-        function draw () {
+        function test_functions () {
             var img, x, y;
-            /* Border */
-            Canvas.ctx.stroke();  
-            Canvas.ctx.rect(0, 0, Canvas.c_wx, Canvas.c_wy);
-            Canvas.ctx.stroke();  
+            Canvas.fillRect(0, 0, Canvas.c_wx, Canvas.c_wy, [240,240,240]);
+
+            Canvas.blitStringImage("data:image/png;base64," + face64, 150, 40);
 
-            /*
-            // Does not work in firefox
             var himg = new Image();
-            himg.src = "head_ani2.gif"
-            Canvas.ctx.drawImage(himg, 10, 10);
-            */
+            himg.onload = function () {
+                Canvas.ctx.drawImage(himg, 200, 10); };
+            himg.src = "face.png";
 
             /* Test array image data */
-            //img = Canvas.ctx.createImageData(50, 50);
-            img = Canvas.ctx.getImageData(0, 0, 50, 50);
+            data = [];
             for (y=0; y< 50; y++) {
                 for (x=0; x< 50; x++) {
-                    img.data[(y*50 + x)*4 + 0] = 255 - parseInt((255 / 50) * y, 10);
-                    img.data[(y*50 + x)*4 + 1] = parseInt((255 / 50) * y, 10);
-                    img.data[(y*50 + x)*4 + 2] = parseInt((255 / 50) * x, 10);
-                    img.data[(y*50 + x)*4 + 3] = 255;
+                    data[(y*50 + x)*4 + 0] = 255 - parseInt((255 / 50) * y, 10);
+                    data[(y*50 + x)*4 + 1] = parseInt((255 / 50) * y, 10);
+                    data[(y*50 + x)*4 + 2] = parseInt((255 / 50) * x, 10);
+                    data[(y*50 + x)*4 + 3] = 255;
                 }
             }
-            Canvas.ctx.putImageData(img, 100, 100);
+            Canvas.blitImage(30, 10, 50, 50, data, 0);
+
+            //Canvas.prefer_js = false;
+            img = Canvas.getTile(5,5,16,16,[0,128,128]);
+            Canvas.putTile(img);
+
+            img = Canvas.getTile(90,15,16,16,[0,0,0]);
+            Canvas.setSubTile(img, 0,0,16,16,[128,128,0]);
+            Canvas.putTile(img);
         }
 
-        function start () {
+        function begin () {
             $('startButton').value = "Running";
             $('startButton').disabled = true;
             setTimeout(start_delayed, 250);
@@ -89,18 +94,26 @@
             message("prefer Canvas ops: " + time2 + "ms total, " +
                     (time2 / iterations) + "ms per frame");
 
+            Canvas.resize(start_width, start_height, true);
+            test_functions();
             $('startButton').disabled = false;
             $('startButton').value = "Start";
         }
 
         function run_test () {
-            var color, start_time = (new Date()).getTime();
+            var width, height;
+            width = $('width').value;
+            height = $('height').value;
+            Canvas.resize(width, height);
+            var color, start_time = (new Date()).getTime(), w, h;
             for (var i=0; i < iterations; i++) {
                 color = [128, 128, (255 / iterations) * i, 0];
                 for (var x=0; x < width; x = x + 16) {
-                    for (var y=0; y < width; y = y + 16) {
-                        var tile = Canvas.getTile(x, y, 16, 16, color);
-                        Canvas.setTile(tile, 0, 0, 16, 16, color);
+                    for (var y=0; y < height; y = y + 16) {
+                        w = Math.min(16, width - x);
+                        h = Math.min(16, height - y);
+                        var tile = Canvas.getTile(x, y, w, h, color);
+                        Canvas.setSubTile(tile, 0, 0, w, h, color);
                         Canvas.putTile(tile);
                     }
                 }
@@ -110,11 +123,11 @@
         }
 
         window.onload = function() {
-            Canvas.init('canvas', width, height);
-            Canvas.stop();   // Shut-off event interception
             $('iterations').value = 10;
-            draw();
+            Canvas.init('canvas');
+            Canvas.resize(start_width, start_height, true);
             message("Canvas initialized");
+            test_functions();
         }
     </script>
 </html>

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
tests/face.png.js


+ 4 - 6
tests/input.html

@@ -18,14 +18,11 @@
     <script type='text/javascript' 
         src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
     -->
-
-    <script src="include/mootools.js"></script>
     <script src="include/util.js"></script>
     <script src="include/canvas.js"></script>
-
     <script>
         var msg_cnt = 0;
-        var width = 1280, height = 600;
+        var width = 400, height = 200;
         var iterations;
 
         function message(str) {
@@ -54,8 +51,9 @@
         }
 
         window.onload = function() {
-            Canvas.init('canvas', width, height, true, keyPress,
-                        mouseButton, mouseMove);
+            Canvas.init('canvas');
+            Canvas.resize(width, height);
+            Canvas.start(keyPress, mouseButton, mouseMove);
             message("Canvas initialized");
         }
     </script>

+ 4 - 0
vnc.html

@@ -11,6 +11,10 @@ noVNC example: simple example using default controls
         <div id='vnc'>Loading</div>
     </body>
 
+    <!--
+    <script type='text/javascript' 
+        src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
+    -->
     <script src="include/vnc.js"></script>
     <script src="include/default_controls.js"></script>
     <script>

Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio