From 34d8b844ae225a4b1a20b79506be8f543bda9d10 Mon Sep 17 00:00:00 2001 From: Joel Martin Date: Thu, 17 May 2012 11:13:08 -0500 Subject: [PATCH] Move render queue processing to Display and use requestAnimationFrame The imgQ code in RFB should be a generic rendering queue system in Display. The reason for the render queue in the first place is that images loaded from raw data URI strings aren't immediately ready to display so we have to wait for them to complete 'loading'. However, when data URI images are mixed with other types of rendering actions then things can get out of order. This is the reason for the rendering queue. Currently this only keeps display actions for tight and tightPNG related actions in order (because they use a mix of fills, raw pixel data and data URI images). --- include/display.js | 51 +++++++++++++++++++++++++++++++++++++++++++++- include/rfb.js | 47 ++++++++++-------------------------------- include/util.js | 15 ++++++++++++++ 3 files changed, 76 insertions(+), 37 deletions(-) diff --git a/include/display.js b/include/display.js index 1fe883cc..8252ef83 100644 --- a/include/display.js +++ b/include/display.js @@ -19,9 +19,12 @@ var that = {}, // Public API methods c_ctx = null, c_forceCanvas = false, + // Queued drawing actions for in-order rendering + renderQ = [], + // Predefine function variables (jslint) imageDataGet, rgbImageData, bgrxImageData, cmapImageData, - setFillColor, rescale, + setFillColor, rescale, scan_renderQ, // The full frame buffer (logical canvas) size fb_width = 0, @@ -412,6 +415,8 @@ that.clear = function() { c_ctx.clearRect(0, 0, viewport.w, viewport.h); } + renderQ = []; + // No benefit over default ("source-over") in Chrome and firefox //c_ctx.globalCompositeOperation = "copy"; }; @@ -582,6 +587,50 @@ that.drawImage = function(img, x, y) { c_ctx.drawImage(img, x - viewport.x, y - viewport.y); }; +that.renderQ_push = function(action) { + renderQ.push(action); + if (renderQ.length === 1) { + // Check if it can be rendered immediately + scan_renderQ(); + } +}; + +scan_renderQ = function() { + var a, ready = true; + while (ready && renderQ.length > 0) { + a = renderQ[0]; + switch (a.type) { + case 'copy': + that.copyImage(a.old_x, a.old_y, a.x, a.y, a.width, a.height); + break; + case 'fill': + that.fillRect(a.x, a.y, a.width, a.height, a.color); + break; + case 'blit': + that.blitImage(a.x, a.y, a.width, a.height, a.data, 0); + break; + case 'blitRgb': + that.blitRgbImage(a.x, a.y, a.width, a.height, a.data, 0); + break; + case 'img': + if (a.img.complete) { + that.drawImage(a.img, a.x, a.y); + } else { + // We need to wait for this image to 'load' + // to keep things in-order + ready = false; + } + break; + } + if (ready) { + a = renderQ.shift(); + } + } + if (renderQ.length > 0) { + requestAnimFrame(scan_renderQ); + } +}; + that.changeCursor = function(pixels, mask, hotx, hoty, w, h) { if (conf.cursor_uri === false) { diff --git a/include/rfb.js b/include/rfb.js index fea2fcf0..f80d190e 100644 --- a/include/rfb.js +++ b/include/rfb.js @@ -26,7 +26,7 @@ var that = {}, // Public API methods pixelFormat, clientEncodings, fbUpdateRequest, fbUpdateRequests, keyEvent, pointerEvent, clientCutText, - getTightCLength, extract_data_uri, scan_tight_imgQ, + getTightCLength, extract_data_uri, keyPress, mouseButton, mouseMove, checkEvents, // Overridable for testing @@ -93,7 +93,6 @@ var that = {}, // Public API methods encoding : 0, subencoding : -1, background : null, - imgQ : [], // TIGHT_PNG image queue zlibs : [] // TIGHT zlib streams }, @@ -103,7 +102,6 @@ var that = {}, // Public API methods fb_height = 0, fb_name = "", - scan_imgQ_rate = 40, // 25 times per second or so last_req_time = 0, rre_chunk_sz = 100, @@ -314,7 +312,6 @@ init_vars = function() { FBU.subrects = 0; // RRE and HEXTILE FBU.lines = 0; // RAW FBU.tiles = 0; // HEXTILE - FBU.imgQ = []; // TIGHT_PNG image queue FBU.zlibs = []; // TIGHT zlib encoders mouse_buttonMask = 0; mouse_arr = []; @@ -888,7 +885,6 @@ init_msg = function() { /* Start pushing/polling */ setTimeout(checkEvents, conf.check_rate); - setTimeout(scan_tight_imgQ, scan_imgQ_rate); if (conf.encrypt) { updateState('normal', "Connected (encrypted) to: " + fb_name); @@ -1409,9 +1405,9 @@ function display_tight(isTightPNG) { } } - FBU.imgQ.push({ - 'type': 'rgb', - 'img': {'complete': true, 'data': dest}, + display.renderQ_push({ + 'type': 'blitRgb', + 'data': dest, 'x': FBU.x, 'y': FBU.y, 'width': FBU.width, @@ -1440,9 +1436,9 @@ function display_tight(isTightPNG) { data = decompress(ws.rQshiftBytes(clength[1])); } - FBU.imgQ.push({ - 'type': 'rgb', - 'img': {'complete': true, 'data': data}, + display.renderQ_push({ + 'type': 'blitRgb', + 'data': data, 'x': FBU.x, 'y': FBU.y, 'width': FBU.width, @@ -1489,9 +1485,8 @@ function display_tight(isTightPNG) { case "fill": ws.rQshift8(); // shift off ctl color = ws.rQshiftBytes(fb_depth); - FBU.imgQ.push({ + display.renderQ_push({ 'type': 'fill', - 'img': {'complete': true}, 'x': FBU.x, 'y': FBU.y, 'width': FBU.width, @@ -1509,14 +1504,13 @@ function display_tight(isTightPNG) { // clength[0] + ", clength[1]: " + clength[1]); ws.rQshiftBytes(1 + clength[0]); // shift off ctl + compact length img = new Image(); - //img.onload = scan_tight_imgQ; - FBU.imgQ.push({ + img.src = "data:image/" + cmode + + extract_data_uri(ws.rQshiftBytes(clength[1])); + display.renderQ_push({ 'type': 'img', 'img': img, 'x': FBU.x, 'y': FBU.y}); - img.src = "data:image/" + cmode + - extract_data_uri(ws.rQshiftBytes(clength[1])); img = null; break; case "filter": @@ -1550,25 +1544,6 @@ extract_data_uri = function(arr) { return ";base64," + Base64.encode(arr); }; -scan_tight_imgQ = function() { - var data, imgQ, ctx; - ctx = display.get_context(); - if (rfb_state === 'normal') { - imgQ = FBU.imgQ; - while ((imgQ.length > 0) && (imgQ[0].img.complete)) { - data = imgQ.shift(); - if (data.type === 'fill') { - display.fillRect(data.x, data.y, data.width, data.height, data.color); - } else if (data.type === 'rgb') { - display.blitRgbImage(data.x, data.y, data.width, data.height, data.img.data, 0); - } else { - display.drawImage(data.img, data.x, data.y); - } - } - setTimeout(scan_tight_imgQ, scan_imgQ_rate); - } -}; - encHandlers.TIGHT = function () { return display_tight(false); }; encHandlers.TIGHT_PNG = function () { return display_tight(true); }; diff --git a/include/util.js b/include/util.js index ddc1914c..2c0e4ed5 100644 --- a/include/util.js +++ b/include/util.js @@ -57,6 +57,21 @@ if (!Array.prototype.map) }; } +// +// requestAnimationFrame shim with setTimeout fallback +// + +window.requestAnimFrame = (function(){ + return window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function(callback){ + window.setTimeout(callback, 1000 / 60); + }; +})(); + /* * ------------------------------------------------------ * Namespaced in Util