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 function make_image_png (input_data
) {
30 var canvas
= make_image_canvas(input_data
);
31 var url
= canvas
.toDataURL();
32 var data
= url
.split(",")[1];
33 return Base64
.decode(data
);
36 describe('checking for cursor uri support', function () {
37 beforeEach(function () {
38 this._old_browser_supports_cursor_uris
= Util
.browserSupportsCursorURIs
;
41 it('should disable cursor URIs if there is no support', function () {
42 Util
.browserSupportsCursorURIs = function () { return false; };
43 var display
= new Display({ target
: document
.createElement('canvas'), prefer_js
: true, viewport
: false });
44 expect(display
._cursor_uri
).to
.be
.false;
47 it('should enable cursor URIs if there is support', function () {
48 Util
.browserSupportsCursorURIs = function () { return true; };
49 var display
= new Display({ target
: document
.createElement('canvas'), prefer_js
: true, viewport
: false });
50 expect(display
._cursor_uri
).to
.be
.true;
53 it('respect the cursor_uri option if there is support', function () {
54 Util
.browserSupportsCursorURIs = function () { return false; };
55 var display
= new Display({ target
: document
.createElement('canvas'), prefer_js
: true, viewport
: false, cursor_uri
: false });
56 expect(display
._cursor_uri
).to
.be
.false;
59 afterEach(function () {
60 Util
.browserSupportsCursorURIs
= this._old_browser_supports_cursor_uris
;
64 describe('viewport handling', function () {
66 beforeEach(function () {
67 display
= new Display({ target
: document
.createElement('canvas'), prefer_js
: false, viewport
: true });
69 display
.viewportChangeSize(3, 3);
70 display
.viewportChangePos(1, 1);
73 it('should take viewport location into consideration when drawing images', function () {
75 display
.viewportChangeSize(2, 2);
76 display
.drawImage(make_image_canvas(basic_data
), 1, 1);
79 var expected
= new Uint8Array(16);
81 for (i
= 0; i
< 8; i
++) { expected
[i
] = basic_data
[i
]; }
82 for (i
= 8; i
< 16; i
++) { expected
[i
] = 0; }
83 expect(display
).to
.have
.displayed(expected
);
86 it('should resize the target canvas when resizing the viewport', function() {
87 display
.viewportChangeSize(2, 2);
88 expect(display
._target
.width
).to
.equal(2);
89 expect(display
._target
.height
).to
.equal(2);
92 it('should move the viewport if necessary', function() {
93 display
.viewportChangeSize(5, 5);
94 expect(display
.absX(0)).to
.equal(0);
95 expect(display
.absY(0)).to
.equal(0);
96 expect(display
._target
.width
).to
.equal(5);
97 expect(display
._target
.height
).to
.equal(5);
100 it('should limit the viewport to the framebuffer size', function() {
101 display
.viewportChangeSize(6, 6);
102 expect(display
._target
.width
).to
.equal(5);
103 expect(display
._target
.height
).to
.equal(5);
106 it('should redraw when moving the viewport', function () {
107 display
.flip
= sinon
.spy();
108 display
.viewportChangePos(-1, 1);
109 expect(display
.flip
).to
.have
.been
.calledOnce
;
112 it('should redraw when resizing the viewport', function () {
113 display
.flip
= sinon
.spy();
114 display
.viewportChangeSize(2, 2);
115 expect(display
.flip
).to
.have
.been
.calledOnce
;
118 it('should report clipping when framebuffer > viewport', function () {
119 var clipping
= display
.clippingDisplay();
120 expect(clipping
).to
.be
.true;
123 it('should report not clipping when framebuffer = viewport', function () {
124 display
.viewportChangeSize(5, 5);
125 var clipping
= display
.clippingDisplay();
126 expect(clipping
).to
.be
.false;
129 it('should show the entire framebuffer when disabling the viewport', function() {
130 display
.set_viewport(false);
131 expect(display
.absX(0)).to
.equal(0);
132 expect(display
.absY(0)).to
.equal(0);
133 expect(display
._target
.width
).to
.equal(5);
134 expect(display
._target
.height
).to
.equal(5);
137 it('should ignore viewport changes when the viewport is disabled', function() {
138 display
.set_viewport(false);
139 display
.viewportChangeSize(2, 2);
140 display
.viewportChangePos(1, 1);
141 expect(display
.absX(0)).to
.equal(0);
142 expect(display
.absY(0)).to
.equal(0);
143 expect(display
._target
.width
).to
.equal(5);
144 expect(display
._target
.height
).to
.equal(5);
147 it('should show the entire framebuffer just after enabling the viewport', function() {
148 display
.set_viewport(false);
149 display
.set_viewport(true);
150 expect(display
.absX(0)).to
.equal(0);
151 expect(display
.absY(0)).to
.equal(0);
152 expect(display
._target
.width
).to
.equal(5);
153 expect(display
._target
.height
).to
.equal(5);
157 describe('resizing', function () {
159 beforeEach(function () {
160 display
= new Display({ target
: document
.createElement('canvas'), prefer_js
: false, viewport
: false });
161 display
.resize(4, 4);
164 it('should change the size of the logical canvas', function () {
165 display
.resize(5, 7);
166 expect(display
._fb_width
).to
.equal(5);
167 expect(display
._fb_height
).to
.equal(7);
170 it('should keep the framebuffer data', function () {
171 display
.fillRect(0, 0, 4, 4, [0, 0, 0xff]);
172 display
.resize(2, 2);
175 for (var i
= 0; i
< 4 * 2*2; i
+= 4) {
177 expected
[i
+1] = expected
[i
+2] = 0;
178 expected
[i
+3] = 0xff;
180 expect(display
).to
.have
.displayed(new Uint8Array(expected
));
183 describe('viewport', function () {
184 beforeEach(function () {
185 display
.set_viewport(true);
186 display
.viewportChangeSize(3, 3);
187 display
.viewportChangePos(1, 1);
190 it('should keep the viewport position and size if possible', function () {
191 display
.resize(6, 6);
192 expect(display
.absX(0)).to
.equal(1);
193 expect(display
.absY(0)).to
.equal(1);
194 expect(display
._target
.width
).to
.equal(3);
195 expect(display
._target
.height
).to
.equal(3);
198 it('should move the viewport if necessary', function () {
199 display
.resize(3, 3);
200 expect(display
.absX(0)).to
.equal(0);
201 expect(display
.absY(0)).to
.equal(0);
202 expect(display
._target
.width
).to
.equal(3);
203 expect(display
._target
.height
).to
.equal(3);
206 it('should shrink the viewport if necessary', function () {
207 display
.resize(2, 2);
208 expect(display
.absX(0)).to
.equal(0);
209 expect(display
.absY(0)).to
.equal(0);
210 expect(display
._target
.width
).to
.equal(2);
211 expect(display
._target
.height
).to
.equal(2);
216 describe('rescaling', function () {
220 beforeEach(function () {
221 display
= new Display({ target
: document
.createElement('canvas'), prefer_js
: false, viewport
: true });
222 display
.resize(4, 4);
223 display
.viewportChangeSize(3, 3);
224 display
.viewportChangePos(1, 1);
225 canvas
= display
.get_target();
226 document
.body
.appendChild(canvas
);
229 afterEach(function () {
230 document
.body
.removeChild(canvas
);
233 it('should not change the bitmap size of the canvas', function () {
234 display
.set_scale(2.0);
235 expect(canvas
.width
).to
.equal(3);
236 expect(canvas
.height
).to
.equal(3);
239 it('should change the effective rendered size of the canvas', function () {
240 display
.set_scale(2.0);
241 expect(canvas
.clientWidth
).to
.equal(6);
242 expect(canvas
.clientHeight
).to
.equal(6);
245 it('should not change when resizing', function () {
246 display
.set_scale(2.0);
247 display
.resize(5, 5);
248 expect(display
.get_scale()).to
.equal(2.0);
249 expect(canvas
.width
).to
.equal(3);
250 expect(canvas
.height
).to
.equal(3);
251 expect(canvas
.clientWidth
).to
.equal(6);
252 expect(canvas
.clientHeight
).to
.equal(6);
256 describe('autoscaling', function () {
260 beforeEach(function () {
261 display
= new Display({ target
: document
.createElement('canvas'), prefer_js
: false, viewport
: true });
262 display
.resize(4, 3);
263 canvas
= display
.get_target();
264 document
.body
.appendChild(canvas
);
267 afterEach(function () {
268 document
.body
.removeChild(canvas
);
271 it('should preserve aspect ratio while autoscaling', function () {
272 display
.autoscale(16, 9);
273 expect(canvas
.clientWidth
/ canvas
.clientHeight
).to
.equal(4 / 3);
276 it('should use width to determine scale when the current aspect ratio is wider than the target', function () {
277 display
.autoscale(9, 16);
278 expect(display
.absX(9)).to
.equal(4);
279 expect(display
.absY(18)).to
.equal(8);
280 expect(canvas
.clientWidth
).to
.equal(9);
281 expect(canvas
.clientHeight
).to
.equal(7); // round 9 / (4 / 3)
284 it('should use height to determine scale when the current aspect ratio is taller than the target', function () {
285 display
.autoscale(16, 9);
286 expect(display
.absX(9)).to
.equal(3);
287 expect(display
.absY(18)).to
.equal(6);
288 expect(canvas
.clientWidth
).to
.equal(12); // 16 * (4 / 3)
289 expect(canvas
.clientHeight
).to
.equal(9);
293 it('should not change the bitmap size of the canvas', function () {
294 display
.autoscale(16, 9);
295 expect(canvas
.width
).to
.equal(4);
296 expect(canvas
.height
).to
.equal(3);
299 it('should not upscale when downscaleOnly is true', function () {
300 display
.autoscale(2, 2, true);
301 expect(display
.absX(9)).to
.equal(18);
302 expect(display
.absY(18)).to
.equal(36);
303 expect(canvas
.clientWidth
).to
.equal(2);
304 expect(canvas
.clientHeight
).to
.equal(2);
306 display
.autoscale(16, 9, true);
307 expect(display
.absX(9)).to
.equal(9);
308 expect(display
.absY(18)).to
.equal(18);
309 expect(canvas
.clientWidth
).to
.equal(4);
310 expect(canvas
.clientHeight
).to
.equal(3);
314 describe('drawing', function () {
316 // TODO(directxman12): improve the tests for each of the drawing functions to cover more than just the
318 function drawing_tests (pref_js
) {
320 beforeEach(function () {
321 display
= new Display({ target
: document
.createElement('canvas'), prefer_js
: pref_js
});
322 display
.resize(4, 4);
325 it('should clear the screen on #clear without a logo set', function () {
326 display
.fillRect(0, 0, 4, 4, [0x00, 0x00, 0xff]);
327 display
._logo
= null;
329 display
.resize(4, 4);
331 for (var i
= 0; i
< 4 * display
._fb_width
* display
._fb_height
; i
++) { empty
[i
] = 0; }
332 expect(display
).to
.have
.displayed(new Uint8Array(empty
));
335 it('should draw the logo on #clear with a logo set', function (done
) {
336 display
._logo
= { width
: 4, height
: 4, type
: "image/png", data
: make_image_png(checked_data
) };
338 display
.set_onFlush(function () {
339 expect(display
).to
.have
.displayed(checked_data
);
340 expect(display
._fb_width
).to
.equal(4);
341 expect(display
._fb_height
).to
.equal(4);
347 it('should not draw directly on the target canvas', function () {
348 display
.fillRect(0, 0, 4, 4, [0, 0, 0xff]);
350 display
.fillRect(0, 0, 4, 4, [0, 0xff, 0]);
352 for (var i
= 0; i
< 4 * display
._fb_width
* display
._fb_height
; i
+= 4) {
354 expected
[i
+1] = expected
[i
+2] = 0;
355 expected
[i
+3] = 0xff;
357 expect(display
).to
.have
.displayed(new Uint8Array(expected
));
360 it('should support filling a rectangle with particular color via #fillRect', function () {
361 display
.fillRect(0, 0, 4, 4, [0, 0xff, 0]);
362 display
.fillRect(0, 0, 2, 2, [0xff, 0, 0]);
363 display
.fillRect(2, 2, 2, 2, [0xff, 0, 0]);
365 expect(display
).to
.have
.displayed(checked_data
);
368 it('should support copying an portion of the canvas via #copyImage', function () {
369 display
.fillRect(0, 0, 4, 4, [0, 0xff, 0]);
370 display
.fillRect(0, 0, 2, 2, [0xff, 0, 0x00]);
371 display
.copyImage(0, 0, 2, 2, 2, 2);
373 expect(display
).to
.have
.displayed(checked_data
);
376 it('should support drawing images via #imageRect', function (done
) {
377 display
.imageRect(0, 0, "image/png", make_image_png(checked_data
));
379 display
.set_onFlush(function () {
380 expect(display
).to
.have
.displayed(checked_data
);
386 it('should support drawing tile data with a background color and sub tiles', function () {
387 display
.startTile(0, 0, 4, 4, [0, 0xff, 0]);
388 display
.subTile(0, 0, 2, 2, [0xff, 0, 0]);
389 display
.subTile(2, 2, 2, 2, [0xff, 0, 0]);
390 display
.finishTile();
392 expect(display
).to
.have
.displayed(checked_data
);
395 it('should support drawing BGRX blit images with true color via #blitImage', function () {
397 for (var i
= 0; i
< 16; i
++) {
398 data
[i
* 4] = checked_data
[i
* 4 + 2];
399 data
[i
* 4 + 1] = checked_data
[i
* 4 + 1];
400 data
[i
* 4 + 2] = checked_data
[i
* 4];
401 data
[i
* 4 + 3] = checked_data
[i
* 4 + 3];
403 display
.blitImage(0, 0, 4, 4, data
, 0);
405 expect(display
).to
.have
.displayed(checked_data
);
408 it('should support drawing RGB blit images with true color via #blitRgbImage', function () {
410 for (var i
= 0; i
< 16; i
++) {
411 data
[i
* 3] = checked_data
[i
* 4];
412 data
[i
* 3 + 1] = checked_data
[i
* 4 + 1];
413 data
[i
* 3 + 2] = checked_data
[i
* 4 + 2];
415 display
.blitRgbImage(0, 0, 4, 4, data
, 0);
417 expect(display
).to
.have
.displayed(checked_data
);
420 it('should support drawing solid colors with color maps', function () {
421 display
._true_color
= false;
422 display
.set_colourMap({ 0: [0xff, 0, 0], 1: [0, 0xff, 0] });
423 display
.fillRect(0, 0, 4, 4, 1);
424 display
.fillRect(0, 0, 2, 2, 0);
425 display
.fillRect(2, 2, 2, 2, 0);
427 expect(display
).to
.have
.displayed(checked_data
);
430 it('should support drawing blit images with color maps', function () {
431 display
._true_color
= false;
432 display
.set_colourMap({ 1: [0xff, 0, 0], 0: [0, 0xff, 0] });
433 var data
= [1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1].map(function (elem
) { return [elem
]; });
434 display
.blitImage(0, 0, 4, 4, data
, 0);
436 expect(display
).to
.have
.displayed(checked_data
);
439 it('should support drawing an image object via #drawImage', function () {
440 var img
= make_image_canvas(checked_data
);
441 display
.drawImage(img
, 0, 0);
443 expect(display
).to
.have
.displayed(checked_data
);
447 describe('(prefering native methods)', function () { drawing_tests
.call(this, false); });
448 describe('(prefering JavaScript)', function () { drawing_tests
.call(this, true); });
451 describe('the render queue processor', function () {
453 beforeEach(function () {
454 display
= new Display({ target
: document
.createElement('canvas'), prefer_js
: false });
455 display
.resize(4, 4);
456 sinon
.spy(display
, '_scan_renderQ');
459 afterEach(function () {
460 window
.requestAnimationFrame
= this.old_requestAnimationFrame
;
463 it('should try to process an item when it is pushed on, if nothing else is on the queue', function () {
464 display
._renderQ_push({ type
: 'noop' }); // does nothing
465 expect(display
._scan_renderQ
).to
.have
.been
.calledOnce
;
468 it('should not try to process an item when it is pushed on if we are waiting for other items', function () {
469 display
._renderQ
.length
= 2;
470 display
._renderQ_push({ type
: 'noop' });
471 expect(display
._scan_renderQ
).to
.not
.have
.been
.called
;
474 it('should wait until an image is loaded to attempt to draw it and the rest of the queue', function () {
475 var img
= { complete
: false, addEventListener
: sinon
.spy() }
476 display
._renderQ
= [{ type
: 'img', x
: 3, y
: 4, img
: img
},
477 { type
: 'fill', x
: 1, y
: 2, width
: 3, height
: 4, color
: 5 }];
478 display
.drawImage
= sinon
.spy();
479 display
.fillRect
= sinon
.spy();
481 display
._scan_renderQ();
482 expect(display
.drawImage
).to
.not
.have
.been
.called
;
483 expect(display
.fillRect
).to
.not
.have
.been
.called
;
484 expect(img
.addEventListener
).to
.have
.been
.calledOnce
;
486 display
._renderQ
[0].img
.complete
= true;
487 display
._scan_renderQ();
488 expect(display
.drawImage
).to
.have
.been
.calledOnce
;
489 expect(display
.fillRect
).to
.have
.been
.calledOnce
;
490 expect(img
.addEventListener
).to
.have
.been
.calledOnce
;
493 it('should call callback when queue is flushed', function () {
494 display
.set_onFlush(sinon
.spy());
495 display
.fillRect(0, 0, 4, 4, [0, 0xff, 0]);
496 expect(display
.get_onFlush()).to
.not
.have
.been
.called
;
498 expect(display
.get_onFlush()).to
.have
.been
.calledOnce
;
501 it('should draw a blit image on type "blit"', function () {
502 display
.blitImage
= sinon
.spy();
503 display
._renderQ_push({ type
: 'blit', x
: 3, y
: 4, width
: 5, height
: 6, data
: [7, 8, 9] });
504 expect(display
.blitImage
).to
.have
.been
.calledOnce
;
505 expect(display
.blitImage
).to
.have
.been
.calledWith(3, 4, 5, 6, [7, 8, 9], 0);
508 it('should draw a blit RGB image on type "blitRgb"', function () {
509 display
.blitRgbImage
= sinon
.spy();
510 display
._renderQ_push({ type
: 'blitRgb', x
: 3, y
: 4, width
: 5, height
: 6, data
: [7, 8, 9] });
511 expect(display
.blitRgbImage
).to
.have
.been
.calledOnce
;
512 expect(display
.blitRgbImage
).to
.have
.been
.calledWith(3, 4, 5, 6, [7, 8, 9], 0);
515 it('should copy a region on type "copy"', function () {
516 display
.copyImage
= sinon
.spy();
517 display
._renderQ_push({ type
: 'copy', x
: 3, y
: 4, width
: 5, height
: 6, old_x
: 7, old_y
: 8 });
518 expect(display
.copyImage
).to
.have
.been
.calledOnce
;
519 expect(display
.copyImage
).to
.have
.been
.calledWith(7, 8, 3, 4, 5, 6);
522 it('should fill a rect with a given color on type "fill"', function () {
523 display
.fillRect
= sinon
.spy();
524 display
._renderQ_push({ type
: 'fill', x
: 3, y
: 4, width
: 5, height
: 6, color
: [7, 8, 9]});
525 expect(display
.fillRect
).to
.have
.been
.calledOnce
;
526 expect(display
.fillRect
).to
.have
.been
.calledWith(3, 4, 5, 6, [7, 8, 9]);
529 it('should draw an image from an image object on type "img" (if complete)', function () {
530 display
.drawImage
= sinon
.spy();
531 display
._renderQ_push({ type
: 'img', x
: 3, y
: 4, img
: { complete
: true } });
532 expect(display
.drawImage
).to
.have
.been
.calledOnce
;
533 expect(display
.drawImage
).to
.have
.been
.calledWith({ complete
: true }, 3, 4);