Browse Source

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 15 năm trước cách đây
mục cha
commit
d93d3e09ab
9 tập tin đã thay đổi với 238 bổ sung99 xóa
  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>

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 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>

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác