1 // requires local modules: util, base64, display
2 // requires test modules: assertions
3 /* jshint expr: true */
4 var expect
= chai
.expect
;
6 describe('Display/Canvas Helper', function () {
8 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
9 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
10 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
11 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255
13 checked_data
= new Uint8Array(checked_data
);
15 var basic_data
= [0xff, 0x00, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0xff, 0xff, 0xff, 255];
16 basic_data
= new Uint8Array(basic_data
);
18 function make_image_canvas (input_data
) {
19 var canvas
= document
.createElement('canvas');
22 var ctx
= canvas
.getContext('2d');
23 var data
= ctx
.createImageData(4, 4);
24 for (var i
= 0; i
< checked_data
.length
; i
++) { data
.data
[i
] = input_data
[i
]; }
25 ctx
.putImageData(data
, 0, 0);
29 describe('checking for cursor uri support', function () {
30 beforeEach(function () {
31 this._old_browser_supports_cursor_uris
= Util
.browserSupportsCursorURIs
;
34 it('should disable cursor URIs if there is no support', function () {
35 Util
.browserSupportsCursorURIs = function () { return false; };
36 var display
= new Display({ target
: document
.createElement('canvas'), prefer_js
: true, viewport
: false });
37 expect(display
._cursor_uri
).to
.be
.false;
40 it('should enable cursor URIs if there is support', function () {
41 Util
.browserSupportsCursorURIs = function () { return true; };
42 var display
= new Display({ target
: document
.createElement('canvas'), prefer_js
: true, viewport
: false });
43 expect(display
._cursor_uri
).to
.be
.true;
46 it('respect the cursor_uri option if there is support', function () {
47 Util
.browserSupportsCursorURIs = function () { return false; };
48 var display
= new Display({ target
: document
.createElement('canvas'), prefer_js
: true, viewport
: false, cursor_uri
: false });
49 expect(display
._cursor_uri
).to
.be
.false;
52 afterEach(function () {
53 Util
.browserSupportsCursorURIs
= this._old_browser_supports_cursor_uris
;
57 describe('viewport handling', function () {
59 beforeEach(function () {
60 display
= new Display({ target
: document
.createElement('canvas'), prefer_js
: false, viewport
: true });
62 display
.viewportChangeSize(3, 3);
63 display
.viewportChangePos(1, 1);
64 display
.getCleanDirtyReset();
67 it('should take viewport location into consideration when drawing images', function () {
69 display
.set_height(4);
70 display
.viewportChangeSize(2, 2);
71 display
.drawImage(make_image_canvas(basic_data
), 1, 1);
73 var expected
= new Uint8Array(16);
75 for (i
= 0; i
< 8; i
++) { expected
[i
] = basic_data
[i
]; }
76 for (i
= 8; i
< 16; i
++) { expected
[i
] = 0; }
77 expect(display
).to
.have
.displayed(expected
);
80 it('should redraw the left side when shifted left', function () {
81 display
.viewportChangePos(-1, 0);
82 var cdr
= display
.getCleanDirtyReset();
83 expect(cdr
.cleanBox
).to
.deep
.equal({ x
: 1, y
: 1, w
: 2, h
: 3 });
84 expect(cdr
.dirtyBoxes
).to
.have
.length(1);
85 expect(cdr
.dirtyBoxes
[0]).to
.deep
.equal({ x
: 0, y
: 1, w
: 2, h
: 3 });
88 it('should redraw the right side when shifted right', function () {
89 display
.viewportChangePos(1, 0);
90 var cdr
= display
.getCleanDirtyReset();
91 expect(cdr
.cleanBox
).to
.deep
.equal({ x
: 2, y
: 1, w
: 2, h
: 3 });
92 expect(cdr
.dirtyBoxes
).to
.have
.length(1);
93 expect(cdr
.dirtyBoxes
[0]).to
.deep
.equal({ x
: 4, y
: 1, w
: 1, h
: 3 });
96 it('should redraw the top part when shifted up', function () {
97 display
.viewportChangePos(0, -1);
98 var cdr
= display
.getCleanDirtyReset();
99 expect(cdr
.cleanBox
).to
.deep
.equal({ x
: 1, y
: 1, w
: 3, h
: 2 });
100 expect(cdr
.dirtyBoxes
).to
.have
.length(1);
101 expect(cdr
.dirtyBoxes
[0]).to
.deep
.equal({ x
: 1, y
: 0, w
: 3, h
: 1 });
104 it('should redraw the bottom part when shifted down', function () {
105 display
.viewportChangePos(0, 1);
106 var cdr
= display
.getCleanDirtyReset();
107 expect(cdr
.cleanBox
).to
.deep
.equal({ x
: 1, y
: 2, w
: 3, h
: 2 });
108 expect(cdr
.dirtyBoxes
).to
.have
.length(1);
109 expect(cdr
.dirtyBoxes
[0]).to
.deep
.equal({ x
: 1, y
: 4, w
: 3, h
: 1 });
112 it('should reset the entire viewport to being clean after calculating the clean/dirty boxes', function () {
113 display
.viewportChangePos(0, 1);
114 var cdr1
= display
.getCleanDirtyReset();
115 var cdr2
= display
.getCleanDirtyReset();
116 expect(cdr1
).to
.not
.deep
.equal(cdr2
);
117 expect(cdr2
.cleanBox
).to
.deep
.equal({ x
: 1, y
: 2, w
: 3, h
: 3 });
118 expect(cdr2
.dirtyBoxes
).to
.be
.empty
;
121 it('should simply mark the whole display area as dirty if not using viewports', function () {
122 display
= new Display({ target
: document
.createElement('canvas'), prefer_js
: false, viewport
: false });
123 display
.resize(5, 5);
124 var cdr
= display
.getCleanDirtyReset();
125 expect(cdr
.cleanBox
).to
.deep
.equal({ x
: 0, y
: 0, w
: 0, h
: 0 });
126 expect(cdr
.dirtyBoxes
).to
.have
.length(1);
127 expect(cdr
.dirtyBoxes
[0]).to
.deep
.equal({ x
: 0, y
: 0, w
: 5, h
: 5 });
131 describe('clipping', function () {
133 beforeEach(function () {
134 display
= new Display({ target
: document
.createElement('canvas'), prefer_js
: false, viewport
: true });
135 display
.resize(4, 3);
138 it('should report true when no max-size and framebuffer > viewport', function () {
139 display
.viewportChangeSize(2,2);
140 var clipping
= display
.clippingDisplay();
141 expect(clipping
).to
.be
.true;
144 it('should report false when no max-size and framebuffer = viewport', function () {
145 var clipping
= display
.clippingDisplay();
146 expect(clipping
).to
.be
.false;
149 it('should report true when viewport > max-size and framebuffer > viewport', function () {
150 display
.viewportChangeSize(2,2);
151 display
.set_maxWidth(1);
152 display
.set_maxHeight(2);
153 var clipping
= display
.clippingDisplay();
154 expect(clipping
).to
.be
.true;
157 it('should report true when viewport > max-size and framebuffer = viewport', function () {
158 display
.set_maxWidth(1);
159 display
.set_maxHeight(2);
160 var clipping
= display
.clippingDisplay();
161 expect(clipping
).to
.be
.true;
165 describe('resizing', function () {
167 beforeEach(function () {
168 display
= new Display({ target
: document
.createElement('canvas'), prefer_js
: false, viewport
: true });
169 display
.resize(4, 3);
172 it('should change the size of the logical canvas', function () {
173 display
.resize(5, 7);
174 expect(display
._fb_width
).to
.equal(5);
175 expect(display
._fb_height
).to
.equal(7);
178 it('should update the viewport dimensions', function () {
179 sinon
.spy(display
, 'viewportChangeSize');
180 display
.resize(2, 2);
181 expect(display
.viewportChangeSize
).to
.have
.been
.calledOnce
;
185 describe('rescaling', function () {
189 beforeEach(function () {
190 display
= new Display({ target
: document
.createElement('canvas'), prefer_js
: false, viewport
: true });
191 display
.resize(4, 3);
192 canvas
= display
.get_target();
193 document
.body
.appendChild(canvas
);
196 afterEach(function () {
197 document
.body
.removeChild(canvas
);
200 it('should not change the bitmap size of the canvas', function () {
201 display
.set_scale(0.5);
202 expect(canvas
.width
).to
.equal(4);
203 expect(canvas
.height
).to
.equal(3);
206 it('should change the effective rendered size of the canvas', function () {
207 display
.set_scale(0.5);
208 expect(canvas
.clientWidth
).to
.equal(2);
209 expect(canvas
.clientHeight
).to
.equal(2);
213 describe('autoscaling', function () {
217 beforeEach(function () {
218 display
= new Display({ target
: document
.createElement('canvas'), prefer_js
: false, viewport
: true });
219 display
.resize(4, 3);
220 canvas
= display
.get_target();
221 document
.body
.appendChild(canvas
);
224 afterEach(function () {
225 document
.body
.removeChild(canvas
);
228 it('should preserve aspect ratio while autoscaling', function () {
229 display
.autoscale(16, 9);
230 expect(canvas
.clientWidth
/ canvas
.clientHeight
).to
.equal(4 / 3);
233 it('should use width to determine scale when the current aspect ratio is wider than the target', function () {
234 expect(display
.autoscale(9, 16)).to
.equal(9 / 4);
235 expect(canvas
.clientWidth
).to
.equal(9);
236 expect(canvas
.clientHeight
).to
.equal(7); // round 9 / (4 / 3)
239 it('should use height to determine scale when the current aspect ratio is taller than the target', function () {
240 expect(display
.autoscale(16, 9)).to
.equal(3); // 9 / 3
241 expect(canvas
.clientWidth
).to
.equal(12); // 16 * (4 / 3)
242 expect(canvas
.clientHeight
).to
.equal(9);
246 it('should not change the bitmap size of the canvas', function () {
247 display
.autoscale(16, 9);
248 expect(canvas
.width
).to
.equal(4);
249 expect(canvas
.height
).to
.equal(3);
252 it('should not upscale when downscaleOnly is true', function () {
253 expect(display
.autoscale(2, 2, true)).to
.equal(0.5);
254 expect(canvas
.clientWidth
).to
.equal(2);
255 expect(canvas
.clientHeight
).to
.equal(2);
257 expect(display
.autoscale(16, 9, true)).to
.equal(1.0);
258 expect(canvas
.clientWidth
).to
.equal(4);
259 expect(canvas
.clientHeight
).to
.equal(3);
263 describe('drawing', function () {
265 // TODO(directxman12): improve the tests for each of the drawing functions to cover more than just the
267 function drawing_tests (pref_js
) {
269 beforeEach(function () {
270 display
= new Display({ target
: document
.createElement('canvas'), prefer_js
: pref_js
});
271 display
.resize(4, 4);
274 it('should clear the screen on #clear without a logo set', function () {
275 display
.fillRect(0, 0, 4, 4, [0x00, 0x00, 0xff]);
276 display
._logo
= null;
278 display
.resize(4, 4);
280 for (var i
= 0; i
< 4 * display
._fb_width
* display
._fb_height
; i
++) { empty
[i
] = 0; }
281 expect(display
).to
.have
.displayed(new Uint8Array(empty
));
284 it('should draw the logo on #clear with a logo set', function (done
) {
285 display
._logo
= { width
: 4, height
: 4, data
: make_image_canvas(checked_data
).toDataURL() };
286 display
._drawCtx
._act_drawImg
= display
._drawCtx
.drawImage
;
287 display
._drawCtx
.drawImage = function (img
, x
, y
) {
288 this._act_drawImg(img
, x
, y
);
289 expect(display
).to
.have
.displayed(checked_data
);
293 expect(display
._fb_width
).to
.equal(4);
294 expect(display
._fb_height
).to
.equal(4);
297 it('should support filling a rectangle with particular color via #fillRect', function () {
298 display
.fillRect(0, 0, 4, 4, [0, 0xff, 0]);
299 display
.fillRect(0, 0, 2, 2, [0xff, 0, 0]);
300 display
.fillRect(2, 2, 2, 2, [0xff, 0, 0]);
301 expect(display
).to
.have
.displayed(checked_data
);
304 it('should support copying an portion of the canvas via #copyImage', function () {
305 display
.fillRect(0, 0, 4, 4, [0, 0xff, 0]);
306 display
.fillRect(0, 0, 2, 2, [0xff, 0, 0x00]);
307 display
.copyImage(0, 0, 2, 2, 2, 2);
308 expect(display
).to
.have
.displayed(checked_data
);
311 it('should support drawing tile data with a background color and sub tiles', function () {
312 display
.startTile(0, 0, 4, 4, [0, 0xff, 0]);
313 display
.subTile(0, 0, 2, 2, [0xff, 0, 0]);
314 display
.subTile(2, 2, 2, 2, [0xff, 0, 0]);
315 display
.finishTile();
316 expect(display
).to
.have
.displayed(checked_data
);
319 it('should support drawing BGRX blit images with true color via #blitImage', function () {
321 for (var i
= 0; i
< 16; i
++) {
322 data
[i
* 4] = checked_data
[i
* 4 + 2];
323 data
[i
* 4 + 1] = checked_data
[i
* 4 + 1];
324 data
[i
* 4 + 2] = checked_data
[i
* 4];
325 data
[i
* 4 + 3] = checked_data
[i
* 4 + 3];
327 display
.blitImage(0, 0, 4, 4, data
, 0);
328 expect(display
).to
.have
.displayed(checked_data
);
331 it('should support drawing RGB blit images with true color via #blitRgbImage', function () {
333 for (var i
= 0; i
< 16; i
++) {
334 data
[i
* 3] = checked_data
[i
* 4];
335 data
[i
* 3 + 1] = checked_data
[i
* 4 + 1];
336 data
[i
* 3 + 2] = checked_data
[i
* 4 + 2];
338 display
.blitRgbImage(0, 0, 4, 4, data
, 0);
339 expect(display
).to
.have
.displayed(checked_data
);
342 it('should support drawing blit images from a data URL via #blitStringImage', function (done
) {
343 var img_url
= make_image_canvas(checked_data
).toDataURL();
344 display
._drawCtx
._act_drawImg
= display
._drawCtx
.drawImage
;
345 display
._drawCtx
.drawImage = function (img
, x
, y
) {
346 this._act_drawImg(img
, x
, y
);
347 expect(display
).to
.have
.displayed(checked_data
);
350 display
.blitStringImage(img_url
, 0, 0);
353 it('should support drawing solid colors with color maps', function () {
354 display
._true_color
= false;
355 display
.set_colourMap({ 0: [0xff, 0, 0], 1: [0, 0xff, 0] });
356 display
.fillRect(0, 0, 4, 4, 1);
357 display
.fillRect(0, 0, 2, 2, 0);
358 display
.fillRect(2, 2, 2, 2, 0);
359 expect(display
).to
.have
.displayed(checked_data
);
362 it('should support drawing blit images with color maps', function () {
363 display
._true_color
= false;
364 display
.set_colourMap({ 1: [0xff, 0, 0], 0: [0, 0xff, 0] });
365 var data
= [1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1].map(function (elem
) { return [elem
]; });
366 display
.blitImage(0, 0, 4, 4, data
, 0);
367 expect(display
).to
.have
.displayed(checked_data
);
370 it('should support drawing an image object via #drawImage', function () {
371 var img
= make_image_canvas(checked_data
);
372 display
.drawImage(img
, 0, 0);
373 expect(display
).to
.have
.displayed(checked_data
);
377 describe('(prefering native methods)', function () { drawing_tests
.call(this, false); });
378 describe('(prefering JavaScript)', function () { drawing_tests
.call(this, true); });
381 describe('the render queue processor', function () {
383 beforeEach(function () {
384 display
= new Display({ target
: document
.createElement('canvas'), prefer_js
: false });
385 display
.resize(4, 4);
386 sinon
.spy(display
, '_scan_renderQ');
389 afterEach(function () {
390 window
.requestAnimationFrame
= this.old_requestAnimationFrame
;
393 it('should try to process an item when it is pushed on, if nothing else is on the queue', function () {
394 display
._renderQ_push({ type
: 'noop' }); // does nothing
395 expect(display
._scan_renderQ
).to
.have
.been
.calledOnce
;
398 it('should not try to process an item when it is pushed on if we are waiting for other items', function () {
399 display
._renderQ
.length
= 2;
400 display
._renderQ_push({ type
: 'noop' });
401 expect(display
._scan_renderQ
).to
.not
.have
.been
.called
;
404 it('should wait until an image is loaded to attempt to draw it and the rest of the queue', function () {
405 var img
= { complete
: false, addEventListener
: sinon
.spy() }
406 display
._renderQ
= [{ type
: 'img', x
: 3, y
: 4, img
: img
},
407 { type
: 'fill', x
: 1, y
: 2, width
: 3, height
: 4, color
: 5 }];
408 display
.drawImage
= sinon
.spy();
409 display
.fillRect
= sinon
.spy();
411 display
._scan_renderQ();
412 expect(display
.drawImage
).to
.not
.have
.been
.called
;
413 expect(display
.fillRect
).to
.not
.have
.been
.called
;
414 expect(img
.addEventListener
).to
.have
.been
.calledOnce
;
416 display
._renderQ
[0].img
.complete
= true;
417 display
._scan_renderQ();
418 expect(display
.drawImage
).to
.have
.been
.calledOnce
;
419 expect(display
.fillRect
).to
.have
.been
.calledOnce
;
420 expect(img
.addEventListener
).to
.have
.been
.calledOnce
;
423 it('should draw a blit image on type "blit"', function () {
424 display
.blitImage
= sinon
.spy();
425 display
._renderQ_push({ type
: 'blit', x
: 3, y
: 4, width
: 5, height
: 6, data
: [7, 8, 9] });
426 expect(display
.blitImage
).to
.have
.been
.calledOnce
;
427 expect(display
.blitImage
).to
.have
.been
.calledWith(3, 4, 5, 6, [7, 8, 9], 0);
430 it('should draw a blit RGB image on type "blitRgb"', function () {
431 display
.blitRgbImage
= sinon
.spy();
432 display
._renderQ_push({ type
: 'blitRgb', x
: 3, y
: 4, width
: 5, height
: 6, data
: [7, 8, 9] });
433 expect(display
.blitRgbImage
).to
.have
.been
.calledOnce
;
434 expect(display
.blitRgbImage
).to
.have
.been
.calledWith(3, 4, 5, 6, [7, 8, 9], 0);
437 it('should copy a region on type "copy"', function () {
438 display
.copyImage
= sinon
.spy();
439 display
._renderQ_push({ type
: 'copy', x
: 3, y
: 4, width
: 5, height
: 6, old_x
: 7, old_y
: 8 });
440 expect(display
.copyImage
).to
.have
.been
.calledOnce
;
441 expect(display
.copyImage
).to
.have
.been
.calledWith(7, 8, 3, 4, 5, 6);
444 it('should fill a rect with a given color on type "fill"', function () {
445 display
.fillRect
= sinon
.spy();
446 display
._renderQ_push({ type
: 'fill', x
: 3, y
: 4, width
: 5, height
: 6, color
: [7, 8, 9]});
447 expect(display
.fillRect
).to
.have
.been
.calledOnce
;
448 expect(display
.fillRect
).to
.have
.been
.calledWith(3, 4, 5, 6, [7, 8, 9]);
451 it('should draw an image from an image object on type "img" (if complete)', function () {
452 display
.drawImage
= sinon
.spy();
453 display
._renderQ_push({ type
: 'img', x
: 3, y
: 4, img
: { complete
: true } });
454 expect(display
.drawImage
).to
.have
.been
.calledOnce
;
455 expect(display
.drawImage
).to
.have
.been
.calledWith({ complete
: true }, 3, 4);