diff options
author | Pierre Ossman <ossman@cendio.se> | 2016-10-25 16:58:21 +0200 |
---|---|---|
committer | Pierre Ossman <ossman@cendio.se> | 2016-12-09 09:20:50 +0100 |
commit | 2ba767a7fe13191c6a7e5b6d8bc43c552e2f1cba (patch) | |
tree | 65819f61bd4b0d6658150b1d3383479783acceb3 | |
parent | 18d21e36217e809a50f6f9ceec7c2ad7777cabf6 (diff) | |
download | novnc-2ba767a7fe13191c6a7e5b6d8bc43c552e2f1cba.tar.gz |
Use double buffering for the display
Do all rendering to a hidden canvas and then copy over the finished
frame to the visible canvas once everything is done. This simplifies
things and solves some bugs as we can retain a copy of the entire
frame buffer.
-rw-r--r-- | core/display.js | 263 | ||||
-rw-r--r-- | core/rfb.js | 35 | ||||
-rw-r--r-- | tests/test.display.js | 92 | ||||
-rw-r--r-- | tests/test.rfb.js | 40 |
4 files changed, 143 insertions, 287 deletions
diff --git a/core/display.js b/core/display.js index ee7c0b6..fcc83b2 100644 --- a/core/display.js +++ b/core/display.js @@ -32,7 +32,6 @@ // the visible "physical canvas" viewport this._viewportLoc = { 'x': 0, 'y': 0, 'w': 0, 'h': 0 }; - this._cleanRect = { 'x1': 0, 'y1': 0, 'x2': -1, 'y2': -1 }; this._prevDrawStyle = ""; this._tile = null; @@ -51,6 +50,7 @@ Util.Debug(">> Display.constructor"); + // The visible canvas if (!this._target) { throw new Error("Target must be set"); } @@ -63,9 +63,11 @@ throw new Error("no getContext method"); } - if (!this._drawCtx) { - this._drawCtx = this._target.getContext('2d'); - } + this._targetCtx = this._target.getContext('2d'); + + // The hidden canvas, where we do the actual rendering + this._backbuffer = document.createElement('canvas'); + this._drawCtx = this._backbuffer.getContext('2d'); Util.Debug("User Agent: " + navigator.userAgent); if (Util.Engine.gecko) { Util.Debug("Browser: gecko " + Util.Engine.gecko); } @@ -145,78 +147,9 @@ Util.Debug("viewportChange deltaX: " + deltaX + ", deltaY: " + deltaY); vp.x += deltaX; - vx2 += deltaX; vp.y += deltaY; - vy2 += deltaY; - - // Update the clean rectangle - var cr = this._cleanRect; - if (vp.x > cr.x1) { - cr.x1 = vp.x; - } - if (vx2 < cr.x2) { - cr.x2 = vx2; - } - if (vp.y > cr.y1) { - cr.y1 = vp.y; - } - if (vy2 < cr.y2) { - cr.y2 = vy2; - } - - var x1, w; - if (deltaX < 0) { - // Shift viewport left, redraw left section - x1 = 0; - w = -deltaX; - } else { - // Shift viewport right, redraw right section - x1 = vp.w - deltaX; - w = deltaX; - } - - var y1, h; - if (deltaY < 0) { - // Shift viewport up, redraw top section - y1 = 0; - h = -deltaY; - } else { - // Shift viewport down, redraw bottom section - y1 = vp.h - deltaY; - h = deltaY; - } - - var saveStyle = this._drawCtx.fillStyle; - var canvas = this._target; - this._drawCtx.fillStyle = "rgb(255,255,255)"; - - // Due to this bug among others [1] we need to disable the image-smoothing to - // avoid getting a blur effect when panning. - // - // 1. https://bugzilla.mozilla.org/show_bug.cgi?id=1194719 - // - // We need to set these every time since all properties are reset - // when the the size is changed - if (this._drawCtx.mozImageSmoothingEnabled) { - this._drawCtx.mozImageSmoothingEnabled = false; - } else if (this._drawCtx.webkitImageSmoothingEnabled) { - this._drawCtx.webkitImageSmoothingEnabled = false; - } else if (this._drawCtx.msImageSmoothingEnabled) { - this._drawCtx.msImageSmoothingEnabled = false; - } else if (this._drawCtx.imageSmoothingEnabled) { - this._drawCtx.imageSmoothingEnabled = false; - } - // Copy the valid part of the viewport to the shifted location - this._drawCtx.drawImage(canvas, 0, 0, vp.w, vp.h, -deltaX, -deltaY, vp.w, vp.h); - - if (deltaX !== 0) { - this._drawCtx.fillRect(x1, 0, w, vp.h); - } - if (deltaY !== 0) { - this._drawCtx.fillRect(0, y1, vp.w, h); - } - this._drawCtx.fillStyle = saveStyle; + this.flip(); }, viewportChangeSize: function(width, height) { @@ -240,29 +173,11 @@ } } - var cr = this._cleanRect; - - if (width < vp.w && cr.x2 > vp.x + width - 1) { - cr.x2 = vp.x + width - 1; - } - if (height < vp.h && cr.y2 > vp.y + height - 1) { - cr.y2 = vp.y + height - 1; - } - vp.w = width; vp.h = height; var canvas = this._target; if (canvas.width !== width || canvas.height !== height) { - - // We have to save the canvas data since changing the size will clear it - var saveImg = null; - if (vp.w > 0 && vp.h > 0 && canvas.width > 0 && canvas.height > 0) { - var img_width = canvas.width < vp.w ? canvas.width : vp.w; - var img_height = canvas.height < vp.h ? canvas.height : vp.h; - saveImg = this._drawCtx.getImageData(0, 0, img_width, img_height); - } - if (canvas.width !== width) { canvas.width = width; canvas.style.width = width + 'px'; @@ -271,63 +186,11 @@ canvas.height = height; canvas.style.height = height + 'px'; } - - if (saveImg) { - this._drawCtx.putImageData(saveImg, 0, 0); - } + this.flip(); } } }, - // Return a map of clean and dirty areas of the viewport and reset the - // tracking of clean and dirty areas - // - // Returns: { 'cleanBox': { 'x': x, 'y': y, 'w': w, 'h': h}, - // 'dirtyBoxes': [{ 'x': x, 'y': y, 'w': w, 'h': h }, ...] } - getCleanDirtyReset: function () { - var vp = this._viewportLoc; - var cr = this._cleanRect; - - var cleanBox = { 'x': cr.x1, 'y': cr.y1, - 'w': cr.x2 - cr.x1 + 1, 'h': cr.y2 - cr.y1 + 1 }; - - var dirtyBoxes = []; - if (cr.x1 >= cr.x2 || cr.y1 >= cr.y2) { - // Whole viewport is dirty - dirtyBoxes.push({ 'x': vp.x, 'y': vp.y, 'w': vp.w, 'h': vp.h }); - } else { - // Redraw dirty regions - var vx2 = vp.x + vp.w - 1; - var vy2 = vp.y + vp.h - 1; - - if (vp.x < cr.x1) { - // left side dirty region - dirtyBoxes.push({'x': vp.x, 'y': vp.y, - 'w': cr.x1 - vp.x + 1, 'h': vp.h}); - } - if (vx2 > cr.x2) { - // right side dirty region - dirtyBoxes.push({'x': cr.x2 + 1, 'y': vp.y, - 'w': vx2 - cr.x2, 'h': vp.h}); - } - if(vp.y < cr.y1) { - // top/middle dirty region - dirtyBoxes.push({'x': cr.x1, 'y': vp.y, - 'w': cr.x2 - cr.x1 + 1, 'h': cr.y1 - vp.y}); - } - if (vy2 > cr.y2) { - // bottom/middle dirty region - dirtyBoxes.push({'x': cr.x1, 'y': cr.y2 + 1, - 'w': cr.x2 - cr.x1 + 1, 'h': vy2 - cr.y2}); - } - } - - this._cleanRect = {'x1': vp.x, 'y1': vp.y, - 'x2': vp.x + vp.w - 1, 'y2': vp.y + vp.h - 1}; - - return {'cleanBox': cleanBox, 'dirtyBoxes': dirtyBoxes}; - }, - absX: function (x) { return x + this._viewportLoc.x; }, @@ -342,27 +205,63 @@ this._fb_width = width; this._fb_height = height; + var canvas = this._backbuffer; + if (canvas.width !== width || canvas.height !== height) { + + // We have to save the canvas data since changing the size will clear it + var saveImg = null; + if (canvas.width > 0 && canvas.height > 0) { + saveImg = this._drawCtx.getImageData(0, 0, canvas.width, canvas.height); + } + + if (canvas.width !== width) { + canvas.width = width; + } + if (canvas.height !== height) { + canvas.height = height; + } + + if (saveImg) { + this._drawCtx.putImageData(saveImg, 0, 0); + } + } + this._rescale(this._scale); this.viewportChangeSize(); }, + // Update the visible canvas with the contents of the + // rendering canvas + flip: function(from_queue) { + if (this._renderQ.length !== 0 && !from_queue) { + this._renderQ_push({ + 'type': 'flip' + }); + } else { + // FIXME: We may need to disable image smoothing here + // as well (see copyImage()), but we haven't + // noticed any problem yet. + this._targetCtx.drawImage(this._backbuffer, + this._viewportLoc.x, + this._viewportLoc.y, + this._viewportLoc.w, + this._viewportLoc.h, + 0, 0, + this._viewportLoc.w, + this._viewportLoc.h); + } + }, + clear: function () { if (this._logo) { this.resize(this._logo.width, this._logo.height); this.imageRect(0, 0, this._logo.type, this._logo.data); } else { - if (Util.Engine.trident === 6) { - // NB(directxman12): there's a bug in IE10 where we can fail to actually - // clear the canvas here because of the resize. - // Clearing the current viewport first fixes the issue - this._drawCtx.clearRect(0, 0, this._viewportLoc.w, this._viewportLoc.h); - } this.resize(240, 20); - this._drawCtx.clearRect(0, 0, this._viewportLoc.w, this._viewportLoc.h); + this._drawCtx.clearRect(0, 0, this._fb_width, this._fb_height); } - - this._renderQ = []; + this.flip(); }, pending: function() { @@ -389,7 +288,7 @@ }); } else { this._setFillColor(color); - this._drawCtx.fillRect(x - this._viewportLoc.x, y - this._viewportLoc.y, width, height); + this._drawCtx.fillRect(x, y, width, height); } }, @@ -405,12 +304,21 @@ 'height': h, }); } else { - var x1 = old_x - this._viewportLoc.x; - var y1 = old_y - this._viewportLoc.y; - var x2 = new_x - this._viewportLoc.x; - var y2 = new_y - this._viewportLoc.y; + // Due to this bug among others [1] we need to disable the image-smoothing to + // avoid getting a blur effect when copying data. + // + // 1. https://bugzilla.mozilla.org/show_bug.cgi?id=1194719 + // + // We need to set these every time since all properties are reset + // when the the size is changed + this._drawCtx.mozImageSmoothingEnabled = false; + this._drawCtx.webkitImageSmoothingEnabled = false; + this._drawCtx.msImageSmoothingEnabled = false; + this._drawCtx.imageSmoothingEnabled = false; - this._drawCtx.drawImage(this._target, x1, y1, w, h, x2, y2, w, h); + this._drawCtx.drawImage(this._backbuffer, + old_x, old_y, w, h, + new_x, new_y, w, h); } }, @@ -492,8 +400,7 @@ // draw the current tile to the screen finishTile: function () { if (this._prefer_js) { - this._drawCtx.putImageData(this._tile, this._tile_x - this._viewportLoc.x, - this._tile_y - this._viewportLoc.y); + this._drawCtx.putImageData(this._tile, this._tile_x, this._tile_y); } // else: No-op -- already done by setSubTile }, @@ -514,9 +421,9 @@ 'height': height, }); } else if (this._true_color) { - this._bgrxImageData(x, y, this._viewportLoc.x, this._viewportLoc.y, width, height, arr, offset); + this._bgrxImageData(x, y, width, height, arr, offset); } else { - this._cmapImageData(x, y, this._viewportLoc.x, this._viewportLoc.y, width, height, arr, offset); + this._cmapImageData(x, y, width, height, arr, offset); } }, @@ -536,10 +443,10 @@ 'height': height, }); } else if (this._true_color) { - this._rgbImageData(x, y, this._viewportLoc.x, this._viewportLoc.y, width, height, arr, offset); + this._rgbImageData(x, y, width, height, arr, offset); } else { // probably wrong? - this._cmapImageData(x, y, this._viewportLoc.x, this._viewportLoc.y, width, height, arr, offset); + this._cmapImageData(x, y, width, height, arr, offset); } }, @@ -559,13 +466,12 @@ 'height': height, }); } else { - this._rgbxImageData(x, y, this._viewportLoc.x, this._viewportLoc.y, width, height, arr, offset); + this._rgbxImageData(x, y, width, height, arr, offset); } }, - // wrap ctx.drawImage but relative to viewport drawImage: function (img, x, y) { - this._drawCtx.drawImage(img, x - this._viewportLoc.x, y - this._viewportLoc.y); + this._drawCtx.drawImage(img, x, y); }, changeCursor: function (pixels, mask, hotx, hoty, w, h) { @@ -697,7 +603,7 @@ } }, - _rgbImageData: function (x, y, vx, vy, width, height, arr, offset) { + _rgbImageData: function (x, y, width, height, arr, offset) { var img = this._drawCtx.createImageData(width, height); var data = img.data; for (var i = 0, j = offset; i < width * height * 4; i += 4, j += 3) { @@ -706,10 +612,10 @@ data[i + 2] = arr[j + 2]; data[i + 3] = 255; // Alpha } - this._drawCtx.putImageData(img, x - vx, y - vy); + this._drawCtx.putImageData(img, x, y); }, - _bgrxImageData: function (x, y, vx, vy, width, height, arr, offset) { + _bgrxImageData: function (x, y, width, height, arr, offset) { var img = this._drawCtx.createImageData(width, height); var data = img.data; for (var i = 0, j = offset; i < width * height * 4; i += 4, j += 4) { @@ -718,10 +624,10 @@ data[i + 2] = arr[j]; data[i + 3] = 255; // Alpha } - this._drawCtx.putImageData(img, x - vx, y - vy); + this._drawCtx.putImageData(img, x, y); }, - _rgbxImageData: function (x, y, vx, vy, width, height, arr, offset) { + _rgbxImageData: function (x, y, width, height, arr, offset) { // NB(directxman12): arr must be an Type Array view var img; if (SUPPORTS_IMAGEDATA_CONSTRUCTOR) { @@ -730,10 +636,10 @@ img = this._drawCtx.createImageData(width, height); img.data.set(new Uint8ClampedArray(arr.buffer, arr.byteOffset, width * height * 4)); } - this._drawCtx.putImageData(img, x - vx, y - vy); + this._drawCtx.putImageData(img, x, y); }, - _cmapImageData: function (x, y, vx, vy, width, height, arr, offset) { + _cmapImageData: function (x, y, width, height, arr, offset) { var img = this._drawCtx.createImageData(width, height); var data = img.data; var cmap = this._colourMap; @@ -744,7 +650,7 @@ data[i + 2] = bgr[0]; data[i + 3] = 255; // Alpha } - this._drawCtx.putImageData(img, x - vx, y - vy); + this._drawCtx.putImageData(img, x, y); }, _renderQ_push: function (action) { @@ -768,6 +674,9 @@ while (ready && this._renderQ.length > 0) { var a = this._renderQ[0]; switch (a.type) { + case 'flip': + this.flip(true); + break; case 'copy': this.copyImage(a.old_x, a.old_y, a.x, a.y, a.width, a.height, true); break; diff --git a/core/rfb.js b/core/rfb.js index 224e822..8a5944f 100644 --- a/core/rfb.js +++ b/core/rfb.js @@ -1042,7 +1042,7 @@ RFB.messages.pixelFormat(this._sock, this._fb_Bpp, this._fb_depth, this._true_color); RFB.messages.clientEncodings(this._sock, this._encodings, this._local_cursor, this._true_color); - RFB.messages.fbUpdateRequests(this._sock, false, this._display.getCleanDirtyReset(), this._fb_width, this._fb_height); + RFB.messages.fbUpdateRequest(this._sock, false, 0, 0, this._fb_width, this._fb_height); this._timing.fbu_rt_start = (new Date()).getTime(); this._timing.pixels = 0; @@ -1198,11 +1198,9 @@ switch (msg_type) { case 0: // FramebufferUpdate var ret = this._framebufferUpdate(); - if (ret) { - RFB.messages.fbUpdateRequests(this._sock, - this._enabledContinuousUpdates, - this._display.getCleanDirtyReset(), - this._fb_width, this._fb_height); + if (ret && !this._enabledContinuousUpdates) { + RFB.messages.fbUpdateRequest(this._sock, true, 0, 0, + this._fb_width, this._fb_height); } return ret; @@ -1346,6 +1344,8 @@ if (!ret) { return ret; } // need more data } + this._display.flip(); + this._onFBUComplete(this, {'x': this._FBU.x, 'y': this._FBU.y, 'width': this._FBU.width, 'height': this._FBU.height, @@ -1664,27 +1664,6 @@ sock.flush(); }, - fbUpdateRequests: function (sock, onlyNonInc, cleanDirty, fb_width, fb_height) { - var offsetIncrement = 0; - - var cb = cleanDirty.cleanBox; - var w, h; - if (!onlyNonInc && (cb.w > 0 && cb.h > 0)) { - w = typeof cb.w === "undefined" ? fb_width : cb.w; - h = typeof cb.h === "undefined" ? fb_height : cb.h; - // Request incremental for clean box - RFB.messages.fbUpdateRequest(sock, 1, cb.x, cb.y, w, h); - } - - for (var i = 0; i < cleanDirty.dirtyBoxes.length; i++) { - var db = cleanDirty.dirtyBoxes[i]; - // Force all (non-incremental) for dirty box - w = typeof db.w === "undefined" ? fb_width : db.w; - h = typeof db.h === "undefined" ? fb_height : db.h; - RFB.messages.fbUpdateRequest(sock, 0, db.x, db.y, w, h); - } - }, - fbUpdateRequest: function (sock, incremental, x, y, w, h) { var buff = sock._sQ; var offset = sock._sQlen; @@ -1693,7 +1672,7 @@ if (typeof(y) === "undefined") { y = 0; } buff[offset] = 3; // msg-type - buff[offset + 1] = incremental; + buff[offset + 1] = incremental ? 1 : 0; buff[offset + 2] = (x >> 8) & 0xFF; buff[offset + 3] = x & 0xFF; diff --git a/tests/test.display.js b/tests/test.display.js index 449052c..95bf2fd 100644 --- a/tests/test.display.js +++ b/tests/test.display.js @@ -68,7 +68,6 @@ describe('Display/Canvas Helper', function () { display.resize(5, 5); display.viewportChangeSize(3, 3); display.viewportChangePos(1, 1); - display.getCleanDirtyReset(); }); it('should take viewport location into consideration when drawing images', function () { @@ -76,6 +75,7 @@ describe('Display/Canvas Helper', function () { display.set_height(4); display.viewportChangeSize(2, 2); display.drawImage(make_image_canvas(basic_data), 1, 1); + display.flip(); var expected = new Uint8Array(16); var i; @@ -84,54 +84,22 @@ describe('Display/Canvas Helper', function () { expect(display).to.have.displayed(expected); }); - it('should redraw the left side when shifted left', function () { - display.viewportChangePos(-1, 0); - var cdr = display.getCleanDirtyReset(); - expect(cdr.cleanBox).to.deep.equal({ x: 1, y: 1, w: 2, h: 3 }); - expect(cdr.dirtyBoxes).to.have.length(1); - expect(cdr.dirtyBoxes[0]).to.deep.equal({ x: 0, y: 1, w: 2, h: 3 }); - }); - - it('should redraw the right side when shifted right', function () { - display.viewportChangePos(1, 0); - var cdr = display.getCleanDirtyReset(); - expect(cdr.cleanBox).to.deep.equal({ x: 2, y: 1, w: 2, h: 3 }); - expect(cdr.dirtyBoxes).to.have.length(1); - expect(cdr.dirtyBoxes[0]).to.deep.equal({ x: 4, y: 1, w: 1, h: 3 }); - }); - - it('should redraw the top part when shifted up', function () { - display.viewportChangePos(0, -1); - var cdr = display.getCleanDirtyReset(); - expect(cdr.cleanBox).to.deep.equal({ x: 1, y: 1, w: 3, h: 2 }); - expect(cdr.dirtyBoxes).to.have.length(1); - expect(cdr.dirtyBoxes[0]).to.deep.equal({ x: 1, y: 0, w: 3, h: 1 }); - }); - - it('should redraw the bottom part when shifted down', function () { - display.viewportChangePos(0, 1); - var cdr = display.getCleanDirtyReset(); - expect(cdr.cleanBox).to.deep.equal({ x: 1, y: 2, w: 3, h: 2 }); - expect(cdr.dirtyBoxes).to.have.length(1); - expect(cdr.dirtyBoxes[0]).to.deep.equal({ x: 1, y: 4, w: 3, h: 1 }); + if('should resize the target canvas when resizing the viewport', function() { + display.viewportChangeSize(2, 2); + expect(canvas.width).to.equal(2); + expect(canvas.height).to.equal(2); }); - it('should reset the entire viewport to being clean after calculating the clean/dirty boxes', function () { - display.viewportChangePos(0, 1); - var cdr1 = display.getCleanDirtyReset(); - var cdr2 = display.getCleanDirtyReset(); - expect(cdr1).to.not.deep.equal(cdr2); - expect(cdr2.cleanBox).to.deep.equal({ x: 1, y: 2, w: 3, h: 3 }); - expect(cdr2.dirtyBoxes).to.be.empty; + it('should redraw when moving the viewport', function () { + display.flip = sinon.spy(); + display.viewportChangePos(-1, 1); + expect(display.flip).to.have.been.calledOnce; }); - it('should simply mark the whole display area as dirty if not using viewports', function () { - display = new Display({ target: document.createElement('canvas'), prefer_js: false, viewport: false }); - display.resize(5, 5); - var cdr = display.getCleanDirtyReset(); - expect(cdr.cleanBox).to.deep.equal({ x: 0, y: 0, w: 0, h: 0 }); - expect(cdr.dirtyBoxes).to.have.length(1); - expect(cdr.dirtyBoxes[0]).to.deep.equal({ x: 0, y: 0, w: 5, h: 5 }); + it('should redraw when resizing the viewport', function () { + display.flip = sinon.spy(); + display.viewportChangeSize(2, 2); + expect(display.flip).to.have.been.calledOnce; }); }); @@ -187,6 +155,19 @@ describe('Display/Canvas Helper', function () { display.resize(2, 2); expect(display.viewportChangeSize).to.have.been.calledOnce; }); + + it('should keep the framebuffer data', function () { + display.fillRect(0, 0, 4, 3, [0, 0, 0xff]); + display.resize(2, 2); + display.flip(); + var expected = []; + for (var i = 0; i < 4 * 2*2; i += 4) { + expected[i] = 0xff; + expected[i+1] = expected[i+2] = 0; + expected[i+3] = 0xff; + } + expect(display).to.have.displayed(new Uint8Array(expected)); + }); }); describe('rescaling', function () { @@ -300,10 +281,24 @@ describe('Display/Canvas Helper', function () { display.flush(); }); + it('should not draw directly on the target canvas', function () { + display.fillRect(0, 0, 4, 4, [0, 0, 0xff]); + display.flip(); + display.fillRect(0, 0, 4, 4, [0, 0xff, 0]); + var expected = []; + for (var i = 0; i < 4 * display._fb_width * display._fb_height; i += 4) { + expected[i] = 0xff; + expected[i+1] = expected[i+2] = 0; + expected[i+3] = 0xff; + } + expect(display).to.have.displayed(new Uint8Array(expected)); + }); + it('should support filling a rectangle with particular color via #fillRect', function () { display.fillRect(0, 0, 4, 4, [0, 0xff, 0]); display.fillRect(0, 0, 2, 2, [0xff, 0, 0]); display.fillRect(2, 2, 2, 2, [0xff, 0, 0]); + display.flip(); expect(display).to.have.displayed(checked_data); }); @@ -311,6 +306,7 @@ describe('Display/Canvas Helper', function () { display.fillRect(0, 0, 4, 4, [0, 0xff, 0]); display.fillRect(0, 0, 2, 2, [0xff, 0, 0x00]); display.copyImage(0, 0, 2, 2, 2, 2); + display.flip(); expect(display).to.have.displayed(checked_data); }); @@ -329,6 +325,7 @@ describe('Display/Canvas Helper', function () { display.subTile(0, 0, 2, 2, [0xff, 0, 0]); display.subTile(2, 2, 2, 2, [0xff, 0, 0]); display.finishTile(); + display.flip(); expect(display).to.have.displayed(checked_data); }); @@ -341,6 +338,7 @@ describe('Display/Canvas Helper', function () { data[i * 4 + 3] = checked_data[i * 4 + 3]; } display.blitImage(0, 0, 4, 4, data, 0); + display.flip(); expect(display).to.have.displayed(checked_data); }); @@ -352,6 +350,7 @@ describe('Display/Canvas Helper', function () { data[i * 3 + 2] = checked_data[i * 4 + 2]; } display.blitRgbImage(0, 0, 4, 4, data, 0); + display.flip(); expect(display).to.have.displayed(checked_data); }); @@ -361,6 +360,7 @@ describe('Display/Canvas Helper', function () { display.fillRect(0, 0, 4, 4, 1); display.fillRect(0, 0, 2, 2, 0); display.fillRect(2, 2, 2, 2, 0); + display.flip(); expect(display).to.have.displayed(checked_data); }); @@ -369,12 +369,14 @@ describe('Display/Canvas Helper', function () { display.set_colourMap({ 1: [0xff, 0, 0], 0: [0, 0xff, 0] }); var data = [1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1].map(function (elem) { return [elem]; }); display.blitImage(0, 0, 4, 4, data, 0); + display.flip(); expect(display).to.have.displayed(checked_data); }); it('should support drawing an image object via #drawImage', function () { var img = make_image_canvas(checked_data); display.drawImage(img, 0, 0); + display.flip(); expect(display).to.have.displayed(checked_data); }); } diff --git a/tests/test.rfb.js b/tests/test.rfb.js index ad512f0..c252c09 100644 --- a/tests/test.rfb.js +++ b/tests/test.rfb.js @@ -1253,7 +1253,7 @@ describe('Remote Frame Buffer Protocol Client', function() { it('should send an update request if there is sufficient data', function () { var expected_msg = {_sQ: new Uint8Array(10), _sQlen: 0, flush: function() {}}; - RFB.messages.fbUpdateRequest(expected_msg, false, 0, 0, 240, 20); + RFB.messages.fbUpdateRequest(expected_msg, true, 0, 0, 640, 20); client._framebufferUpdate = function () { return true; }; client._sock._websocket._receive_data(new Uint8Array([0])); @@ -1268,7 +1268,7 @@ describe('Remote Frame Buffer Protocol Client', function() { it('should resume receiving an update if we previously did not have enough data', function () { var expected_msg = {_sQ: new Uint8Array(10), _sQlen: 0, flush: function() {}}; - RFB.messages.fbUpdateRequest(expected_msg, false, 0, 0, 240, 20); + RFB.messages.fbUpdateRequest(expected_msg, true, 0, 0, 640, 20); // just enough to set FBU.rects client._sock._websocket._receive_data(new Uint8Array([0, 0, 0, 3])); @@ -1280,43 +1280,9 @@ describe('Remote Frame Buffer Protocol Client', function() { expect(client._sock).to.have.sent(expected_msg._sQ); }); - it('should send a request for both clean and dirty areas', function () { - var expected_msg = {_sQ: new Uint8Array(20), _sQlen: 0, flush: function() {}}; - var expected_cdr = { cleanBox: { x: 0, y: 0, w: 120, h: 20 }, - dirtyBoxes: [ { x: 120, y: 0, w: 120, h: 20 } ] }; - - RFB.messages.fbUpdateRequest(expected_msg, true, 0, 0, 120, 20); - RFB.messages.fbUpdateRequest(expected_msg, false, 120, 0, 120, 20); - - client._framebufferUpdate = function () { return true; }; - client._display.getCleanDirtyReset = function () { return expected_cdr; }; - client._sock._websocket._receive_data(new Uint8Array([0])); - - expect(client._sock).to.have.sent(expected_msg._sQ); - }); - - it('should only request non-incremental rects in continuous updates mode', function () { - var expected_msg = {_sQ: new Uint8Array(10), _sQlen: 0, flush: function() {}}; - var expected_cdr = { cleanBox: { x: 0, y: 0, w: 120, h: 20 }, - dirtyBoxes: [ { x: 120, y: 0, w: 120, h: 20 } ] }; - - RFB.messages.fbUpdateRequest(expected_msg, false, 120, 0, 120, 20); - - client._enabledContinuousUpdates = true; - client._framebufferUpdate = function () { return true; }; - client._display.getCleanDirtyReset = function () { return expected_cdr; }; - client._sock._websocket._receive_data(new Uint8Array([0])); - - expect(client._sock).to.have.sent(expected_msg._sQ); - }); - - it('should not send a request in continuous updates mode when clean', function () { - var expected_cdr = { cleanBox: { x: 0, y: 0, w: 240, h: 20 }, - dirtyBoxes: [] }; - + it('should not send a request in continuous updates mode', function () { client._enabledContinuousUpdates = true; client._framebufferUpdate = function () { return true; }; - client._display.getCleanDirtyReset = function () { return expected_cdr; }; client._sock._websocket._receive_data(new Uint8Array([0])); expect(client._sock._websocket._get_sent_data()).to.have.length(0); |