From 490d471c53b1791398d6c30f0efce54925939f33 Mon Sep 17 00:00:00 2001 From: Joel Martin Date: Mon, 26 Sep 2011 11:28:24 -0500 Subject: [PATCH] Remove support for non-HTML5 browsers. Display API change: - getTile -> startTile (no longer returns a tile) - setSubTile -> subTile (drop img/tile first parameter) - putTile -> finishTile (no longer takes img/tile paramter) The Display tile logic uses canvas image data directly and caches/reuses a 16x16 imageData tile (for other sizes, the tile is create for each call). This gives a 30% speedup on Chrome 13 (and no significant change for Firefox 3.6/4.0). Other: - Remove rgbxImageFill and cmapImageFill routines. - Simplify constructor tests and just error if createImageData is not supported by canvas instead of . - Remove webkit canvas bug workaround that effects Chrome 7. Chrome 7 usage share is now less than 0.5 percent and the workaround is ugly. Drop the function wrapping in the constructor and the canvas flush() routine. - Remove support for getImageData (Opera 11+ now required) Update browser support list: - Chrome 8+ (really any except 7) - Firefox 3.6+ - Safari 4+ - Opera 11+ - IE9+ - iOS 4.2+ --- include/display.js | 182 ++++++++++----------------------------------- include/rfb.js | 8 +- 2 files changed, 45 insertions(+), 145 deletions(-) diff --git a/include/display.js b/include/display.js index a03b6118d..0438d8d18 100644 --- a/include/display.js +++ b/include/display.js @@ -19,11 +19,9 @@ var that = {}, // Public API methods c_ctx = null, c_forceCanvas = false, - c_imageData, c_rgbxImage, c_cmapImage, - // Predefine function variables (jslint) - imageDataCreate, imageDataGet, rgbxImageData, cmapImageData, - rgbxImageFill, cmapImageFill, setFillColor, rescale, flush, + imageDataGet, rgbxImageData, cmapImageData, + setFillColor, rescale, // The full frame buffer (logical canvas) size fb_width = 0, @@ -33,9 +31,11 @@ var that = {}, // Public API methods cleanRect = {'x1': 0, 'y1': 0, 'x2': -1, 'y2': -1}, c_prevStyle = "", + tile = null, + tile16x16 = null, + tile_x = 0, + tile_y = 0; - c_webkit_bug = false, - c_flush_timer = null; // Configuration attributes Util.conf_defaults(conf, that, defaults, [ @@ -66,15 +66,6 @@ that.get_width = function() { return fb_width; }; that.set_height = function (val) { that.resize(fb_width, val); }; that.get_height = function() { return fb_height; }; -that.set_prefer_js = function(val) { - if (val && c_forceCanvas) { - Util.Warn("Preferring Javascript to Canvas ops is not supported"); - return false; - } - conf.prefer_js = val; - return true; -}; - // @@ -85,7 +76,7 @@ that.set_prefer_js = function(val) { function constructor() { Util.Debug(">> Display.constructor"); - var c, func, imgTest, tval, i, curDat, curSave, + var c, func, i, curDat, curSave, has_imageData = false, UE = Util.Engine; if (! conf.target) { throw("target must be set"); } @@ -108,71 +99,20 @@ function constructor() { that.clear(); - /* - * Determine browser Canvas feature support - * and select fastest rendering methods - */ - tval = 0; - try { - imgTest = c_ctx.getImageData(0, 0, 1,1); - imgTest.data[0] = 123; - imgTest.data[3] = 255; - c_ctx.putImageData(imgTest, 0, 0); - tval = c_ctx.getImageData(0, 0, 1, 1).data[0]; - if (tval === 123) { - has_imageData = true; - } - } catch (exc1) {} - - if (has_imageData) { - Util.Info("Canvas supports imageData"); - c_forceCanvas = false; - if (c_ctx.createImageData) { - // If it's there, it's faster - Util.Info("Using Canvas createImageData"); - conf.render_mode = "createImageData rendering"; - c_imageData = imageDataCreate; - } else if (c_ctx.getImageData) { - // I think this is mostly just Opera - Util.Info("Using Canvas getImageData"); - conf.render_mode = "getImageData rendering"; - c_imageData = imageDataGet; - } - Util.Info("Prefering javascript operations"); - if (conf.prefer_js === null) { - conf.prefer_js = true; - } - c_rgbxImage = rgbxImageData; - c_cmapImage = cmapImageData; + // Check canvas features + if ('createImageData' in c_ctx) { + conf.render_mode = "canvas rendering"; } else { - Util.Warn("Canvas lacks imageData, using fillRect (slow)"); - conf.render_mode = "fillRect rendering (slow)"; - c_forceCanvas = true; - conf.prefer_js = false; - c_rgbxImage = rgbxImageFill; - c_cmapImage = cmapImageFill; - } - - if (UE.webkit && UE.webkit >= 534.7 && UE.webkit <= 534.9) { - // Workaround WebKit canvas rendering bug #46319 - conf.render_mode += ", webkit bug workaround"; - Util.Debug("Working around WebKit bug #46319"); - c_webkit_bug = true; - for (func in {"fillRect":1, "copyImage":1, "rgbxImage":1, - "cmapImage":1, "blitStringImage":1}) { - that[func] = (function() { - var myfunc = that[func]; // Save original function - //Util.Debug("Wrapping " + func); - return function() { - myfunc.apply(this, arguments); - if (!c_flush_timer) { - c_flush_timer = setTimeout(flush, 100); - } - }; - }()); - } + throw("Canvas does not support createImageData"); + } + if (conf.prefer_js === null) { + Util.Info("Prefering javascript operations"); + conf.prefer_js = true; } + // Initialize cached tile imageData + tile16x16 = c_ctx.createImageData(16, 16); + /* * Determine browser support for setting the cursor via data URI * scheme @@ -425,18 +365,6 @@ that.absY = function(y) { } -// Force canvas redraw (for webkit bug #46319 workaround) -flush = function() { - var old_val; - //Util.Debug(">> flush"); - old_val = conf.target.style.marginRight; - conf.target.style.marginRight = "1px"; - c_flush_timer = null; - setTimeout(function () { - conf.target.style.marginRight = old_val; - }, 1); -}; - setFillColor = function(color) { var rgb, newStyle; if (conf.true_color) { @@ -498,10 +426,16 @@ that.copyImage = function(old_x, old_y, new_x, new_y, w, h) { * faster than direct Canvas fillStyle, fillRect rendering. In * gecko, Javascript array handling is much slower. */ -that.getTile = function(x, y, width, height, color) { - var img, data = [], rgb, red, green, blue, i; - img = {'x': x, 'y': y, 'width': width, 'height': height, - 'data': data}; +that.startTile = function(x, y, width, height, color) { + var data, rgb, red, green, blue, i; + tile_x = x; + tile_y = y; + if ((width === 16) && (height === 16)) { + tile = tile16x16; + } else { + tile = c_ctx.createImageData(width, height); + } + data = tile.data; if (conf.prefer_js) { if (conf.true_color) { rgb = color; @@ -515,18 +449,18 @@ that.getTile = function(x, y, width, height, color) { data[i ] = red; data[i + 1] = green; data[i + 2] = blue; + data[i + 3] = 255; } } else { that.fillRect(x, y, width, height, color); } - return img; }; -that.setSubTile = function(img, x, y, w, h, color) { +that.subTile = function(x, y, w, h, color) { var data, p, rgb, red, green, blue, width, j, i, xend, yend; if (conf.prefer_js) { - data = img.data; - width = img.width; + data = tile.data; + width = tile.width; if (conf.true_color) { rgb = color; } else { @@ -543,25 +477,19 @@ that.setSubTile = function(img, x, y, w, h, color) { data[p ] = red; data[p + 1] = green; data[p + 2] = blue; + data[p + 3] = 255; } } } else { - that.fillRect(img.x + x, img.y + y, w, h, color); + that.fillRect(tile_x + x, tile_y + y, w, h, color); } }; -that.putTile = function(img) { +that.finishTile = function() { if (conf.prefer_js) { - c_rgbxImage(img.x, img.y, img.width, img.height, img.data, 0); + c_ctx.putImageData(tile, tile_x - viewport.x, tile_y - viewport.y) } - // else: No-op, under gecko already done by setSubTile -}; - -imageDataGet = function(width, height) { - return c_ctx.getImageData(0, 0, width, height); -}; -imageDataCreate = function(width, height) { - return c_ctx.createImageData(width, height); + // else: No-op, if not prefer_js then already done by setSubTile }; rgbxImageData = function(x, y, width, height, arr, offset) { @@ -569,12 +497,11 @@ rgbxImageData = function(x, y, width, height, arr, offset) { /* if ((x - v.x >= v.w) || (y - v.y >= v.h) || (x - v.x + width < 0) || (y - v.y + height < 0)) { - //console.log("skipping, out of bounds: ", x, y); // Skipping because outside of viewport return; } */ - img = c_imageData(width, height); + img = c_ctx.createImageData(width, height); data = img.data; for (i=0, j=offset; i < (width * height * 4); i=i+4, j=j+4) { data[i ] = arr[j ]; @@ -585,22 +512,9 @@ rgbxImageData = function(x, y, width, height, arr, offset) { c_ctx.putImageData(img, x - v.x, y - v.y); }; -// really slow fallback if we don't have imageData -rgbxImageFill = function(x, y, width, height, arr, offset) { - var i, j, sx = 0, sy = 0; - for (i=0, j=offset; i < (width * height); i+=1, j+=4) { - that.fillRect(x+sx, y+sy, 1, 1, [arr[j], 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 = c_imageData(width, height); + img = c_ctx.createImageData(width, height); data = img.data; cmap = conf.colourMap; for (i=0, j=offset; i < (width * height * 4); i+=4, j+=1) { @@ -613,25 +527,11 @@ cmapImageData = function(x, y, width, height, arr, offset) { c_ctx.putImageData(img, x - viewport.x, y - viewport.y); }; -cmapImageFill = function(x, y, width, height, arr, offset) { - var i, j, sx = 0, sy = 0, cmap; - cmap = conf.colourMap; - for (i=0, j=offset; i < (width * height); i+=1, j+=1) { - that.fillRect(x+sx, y+sy, 1, 1, [arr[j]]); - sx += 1; - if ((sx % width) === 0) { - sx = 0; - sy += 1; - } - } -}; - - that.blitImage = function(x, y, width, height, arr, offset) { if (conf.true_color) { - c_rgbxImage(x, y, width, height, arr, offset); + rgbxImageData(x, y, width, height, arr, offset); } else { - c_cmapImage(x, y, width, height, arr, offset); + cmapImageData(x, y, width, height, arr, offset); } }; diff --git a/include/rfb.js b/include/rfb.js index ca8ec03d8..e161532d4 100644 --- a/include/rfb.js +++ b/include/rfb.js @@ -1096,7 +1096,7 @@ encHandlers.RRE = function display_rre() { encHandlers.HEXTILE = function display_hextile() { //Util.Debug(">> display_hextile"); - var subencoding, subrects, tile, color, cur_tile, + var subencoding, subrects, color, cur_tile, tile_x, x, w, tile_y, y, h, xy, s, sx, sy, wh, sw, sh, rQ = ws.get_rQ(), rQi = ws.get_rQi(); @@ -1185,7 +1185,7 @@ encHandlers.HEXTILE = function display_hextile() { rQi += fb_Bpp; } - tile = display.getTile(x, y, w, h, FBU.background); + display.startTile(x, y, w, h, FBU.background); if (FBU.subencoding & 0x08) { // AnySubrects subrects = rQ[rQi]; rQi += 1; @@ -1206,10 +1206,10 @@ encHandlers.HEXTILE = function display_hextile() { sw = (wh >> 4) + 1; sh = (wh & 0x0f) + 1; - display.setSubTile(tile, sx, sy, sw, sh, color); + display.subTile(sx, sy, sw, sh, color); } } - display.putTile(tile); + display.finishTile(); } ws.set_rQi(rQi); FBU.lastsubencoding = FBU.subencoding;