1 /* jshint expr: true */
2 var expect
= chai
.expect
;
4 import Base64
from '../core/base64.js';
5 import Display
from '../core/display.js';
6 import { _forceCursorURIs
, browserSupportsCursorURIs
} from '../core/util/browsers.js';
8 import sinon
from '../vendor/sinon.js';
10 describe('Display/Canvas Helper', function () {
12 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
13 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
14 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
15 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255
17 checked_data
= new Uint8Array(checked_data
);
19 var basic_data
= [0xff, 0x00, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0xff, 0xff, 0xff, 255];
20 basic_data
= new Uint8Array(basic_data
);
22 function make_image_canvas (input_data
) {
23 var canvas
= document
.createElement('canvas');
26 var ctx
= canvas
.getContext('2d');
27 var data
= ctx
.createImageData(4, 4);
28 for (var i
= 0; i
< checked_data
.length
; i
++) { data
.data
[i
] = input_data
[i
]; }
29 ctx
.putImageData(data
, 0, 0);
33 function make_image_png (input_data
) {
34 var canvas
= make_image_canvas(input_data
);
35 var url
= canvas
.toDataURL();
36 var data
= url
.split(",")[1];
37 return Base64
.decode(data
);
40 describe('checking for cursor uri support', function () {
41 it('should disable cursor URIs if there is no support', function () {
42 _forceCursorURIs(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 _forceCursorURIs(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 _forceCursorURIs(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;
60 describe('viewport handling', function () {
62 beforeEach(function () {
63 display
= new Display({ target
: document
.createElement('canvas'), prefer_js
: false, viewport
: true });
65 display
.viewportChangeSize(3, 3);
66 display
.viewportChangePos(1, 1);
69 it('should take viewport location into consideration when drawing images', function () {
71 display
.viewportChangeSize(2, 2);
72 display
.drawImage(make_image_canvas(basic_data
), 1, 1);
75 var expected
= new Uint8Array(16);
77 for (i
= 0; i
< 8; i
++) { expected
[i
] = basic_data
[i
]; }
78 for (i
= 8; i
< 16; i
++) { expected
[i
] = 0; }
79 expect(display
).to
.have
.displayed(expected
);
82 it('should resize the target canvas when resizing the viewport', function() {
83 display
.viewportChangeSize(2, 2);
84 expect(display
._target
.width
).to
.equal(2);
85 expect(display
._target
.height
).to
.equal(2);
88 it('should move the viewport if necessary', function() {
89 display
.viewportChangeSize(5, 5);
90 expect(display
.absX(0)).to
.equal(0);
91 expect(display
.absY(0)).to
.equal(0);
92 expect(display
._target
.width
).to
.equal(5);
93 expect(display
._target
.height
).to
.equal(5);
96 it('should limit the viewport to the framebuffer size', function() {
97 display
.viewportChangeSize(6, 6);
98 expect(display
._target
.width
).to
.equal(5);
99 expect(display
._target
.height
).to
.equal(5);
102 it('should redraw when moving the viewport', function () {
103 display
.flip
= sinon
.spy();
104 display
.viewportChangePos(-1, 1);
105 expect(display
.flip
).to
.have
.been
.calledOnce
;
108 it('should redraw when resizing the viewport', function () {
109 display
.flip
= sinon
.spy();
110 display
.viewportChangeSize(2, 2);
111 expect(display
.flip
).to
.have
.been
.calledOnce
;
114 it('should report clipping when framebuffer > viewport', function () {
115 var clipping
= display
.clippingDisplay();
116 expect(clipping
).to
.be
.true;
119 it('should report not clipping when framebuffer = viewport', function () {
120 display
.viewportChangeSize(5, 5);
121 var clipping
= display
.clippingDisplay();
122 expect(clipping
).to
.be
.false;
125 it('should show the entire framebuffer when disabling the viewport', function() {
126 display
.set_viewport(false);
127 expect(display
.absX(0)).to
.equal(0);
128 expect(display
.absY(0)).to
.equal(0);
129 expect(display
._target
.width
).to
.equal(5);
130 expect(display
._target
.height
).to
.equal(5);
133 it('should ignore viewport changes when the viewport is disabled', function() {
134 display
.set_viewport(false);
135 display
.viewportChangeSize(2, 2);
136 display
.viewportChangePos(1, 1);
137 expect(display
.absX(0)).to
.equal(0);
138 expect(display
.absY(0)).to
.equal(0);
139 expect(display
._target
.width
).to
.equal(5);
140 expect(display
._target
.height
).to
.equal(5);
143 it('should show the entire framebuffer just after enabling the viewport', function() {
144 display
.set_viewport(false);
145 display
.set_viewport(true);
146 expect(display
.absX(0)).to
.equal(0);
147 expect(display
.absY(0)).to
.equal(0);
148 expect(display
._target
.width
).to
.equal(5);
149 expect(display
._target
.height
).to
.equal(5);
153 describe('resizing', function () {
155 beforeEach(function () {
156 display
= new Display({ target
: document
.createElement('canvas'), prefer_js
: false, viewport
: false });
157 display
.resize(4, 4);
160 it('should change the size of the logical canvas', function () {
161 display
.resize(5, 7);
162 expect(display
._fb_width
).to
.equal(5);
163 expect(display
._fb_height
).to
.equal(7);
166 it('should keep the framebuffer data', function () {
167 display
.fillRect(0, 0, 4, 4, [0, 0, 0xff]);
168 display
.resize(2, 2);
171 for (var i
= 0; i
< 4 * 2*2; i
+= 4) {
173 expected
[i
+1] = expected
[i
+2] = 0;
174 expected
[i
+3] = 0xff;
176 expect(display
).to
.have
.displayed(new Uint8Array(expected
));
179 describe('viewport', function () {
180 beforeEach(function () {
181 display
.set_viewport(true);
182 display
.viewportChangeSize(3, 3);
183 display
.viewportChangePos(1, 1);
186 it('should keep the viewport position and size if possible', function () {
187 display
.resize(6, 6);
188 expect(display
.absX(0)).to
.equal(1);
189 expect(display
.absY(0)).to
.equal(1);
190 expect(display
._target
.width
).to
.equal(3);
191 expect(display
._target
.height
).to
.equal(3);
194 it('should move the viewport if necessary', function () {
195 display
.resize(3, 3);
196 expect(display
.absX(0)).to
.equal(0);
197 expect(display
.absY(0)).to
.equal(0);
198 expect(display
._target
.width
).to
.equal(3);
199 expect(display
._target
.height
).to
.equal(3);
202 it('should shrink the viewport if necessary', function () {
203 display
.resize(2, 2);
204 expect(display
.absX(0)).to
.equal(0);
205 expect(display
.absY(0)).to
.equal(0);
206 expect(display
._target
.width
).to
.equal(2);
207 expect(display
._target
.height
).to
.equal(2);
212 describe('rescaling', function () {
216 beforeEach(function () {
217 display
= new Display({ target
: document
.createElement('canvas'), prefer_js
: false, viewport
: true });
218 display
.resize(4, 4);
219 display
.viewportChangeSize(3, 3);
220 display
.viewportChangePos(1, 1);
221 canvas
= display
.get_target();
222 document
.body
.appendChild(canvas
);
225 afterEach(function () {
226 document
.body
.removeChild(canvas
);
229 it('should not change the bitmap size of the canvas', function () {
230 display
.set_scale(2.0);
231 expect(canvas
.width
).to
.equal(3);
232 expect(canvas
.height
).to
.equal(3);
235 it('should change the effective rendered size of the canvas', function () {
236 display
.set_scale(2.0);
237 expect(canvas
.clientWidth
).to
.equal(6);
238 expect(canvas
.clientHeight
).to
.equal(6);
241 it('should not change when resizing', function () {
242 display
.set_scale(2.0);
243 display
.resize(5, 5);
244 expect(display
.get_scale()).to
.equal(2.0);
245 expect(canvas
.width
).to
.equal(3);
246 expect(canvas
.height
).to
.equal(3);
247 expect(canvas
.clientWidth
).to
.equal(6);
248 expect(canvas
.clientHeight
).to
.equal(6);
252 describe('autoscaling', function () {
256 beforeEach(function () {
257 display
= new Display({ target
: document
.createElement('canvas'), prefer_js
: false, viewport
: true });
258 display
.resize(4, 3);
259 canvas
= display
.get_target();
260 document
.body
.appendChild(canvas
);
263 afterEach(function () {
264 document
.body
.removeChild(canvas
);
267 it('should preserve aspect ratio while autoscaling', function () {
268 display
.autoscale(16, 9);
269 expect(canvas
.clientWidth
/ canvas
.clientHeight
).to
.equal(4 / 3);
272 it('should use width to determine scale when the current aspect ratio is wider than the target', function () {
273 display
.autoscale(9, 16);
274 expect(display
.absX(9)).to
.equal(4);
275 expect(display
.absY(18)).to
.equal(8);
276 expect(canvas
.clientWidth
).to
.equal(9);
277 expect(canvas
.clientHeight
).to
.equal(7); // round 9 / (4 / 3)
280 it('should use height to determine scale when the current aspect ratio is taller than the target', function () {
281 display
.autoscale(16, 9);
282 expect(display
.absX(9)).to
.equal(3);
283 expect(display
.absY(18)).to
.equal(6);
284 expect(canvas
.clientWidth
).to
.equal(12); // 16 * (4 / 3)
285 expect(canvas
.clientHeight
).to
.equal(9);
289 it('should not change the bitmap size of the canvas', function () {
290 display
.autoscale(16, 9);
291 expect(canvas
.width
).to
.equal(4);
292 expect(canvas
.height
).to
.equal(3);
295 it('should not upscale when downscaleOnly is true', function () {
296 display
.autoscale(2, 2, true);
297 expect(display
.absX(9)).to
.equal(18);
298 expect(display
.absY(18)).to
.equal(36);
299 expect(canvas
.clientWidth
).to
.equal(2);
300 expect(canvas
.clientHeight
).to
.equal(2);
302 display
.autoscale(16, 9, true);
303 expect(display
.absX(9)).to
.equal(9);
304 expect(display
.absY(18)).to
.equal(18);
305 expect(canvas
.clientWidth
).to
.equal(4);
306 expect(canvas
.clientHeight
).to
.equal(3);
310 describe('drawing', function () {
312 // TODO(directxman12): improve the tests for each of the drawing functions to cover more than just the
314 function drawing_tests (pref_js
) {
316 beforeEach(function () {
317 display
= new Display({ target
: document
.createElement('canvas'), prefer_js
: pref_js
});
318 display
.resize(4, 4);
321 it('should clear the screen on #clear without a logo set', function () {
322 display
.fillRect(0, 0, 4, 4, [0x00, 0x00, 0xff]);
323 display
._logo
= null;
325 display
.resize(4, 4);
327 for (var i
= 0; i
< 4 * display
._fb_width
* display
._fb_height
; i
++) { empty
[i
] = 0; }
328 expect(display
).to
.have
.displayed(new Uint8Array(empty
));
331 it('should draw the logo on #clear with a logo set', function (done
) {
332 display
._logo
= { width
: 4, height
: 4, type
: "image/png", data
: make_image_png(checked_data
) };
334 display
.set_onFlush(function () {
335 expect(display
).to
.have
.displayed(checked_data
);
336 expect(display
._fb_width
).to
.equal(4);
337 expect(display
._fb_height
).to
.equal(4);
343 it('should not draw directly on the target canvas', function () {
344 display
.fillRect(0, 0, 4, 4, [0, 0, 0xff]);
346 display
.fillRect(0, 0, 4, 4, [0, 0xff, 0]);
348 for (var i
= 0; i
< 4 * display
._fb_width
* display
._fb_height
; i
+= 4) {
350 expected
[i
+1] = expected
[i
+2] = 0;
351 expected
[i
+3] = 0xff;
353 expect(display
).to
.have
.displayed(new Uint8Array(expected
));
356 it('should support filling a rectangle with particular color via #fillRect', function () {
357 display
.fillRect(0, 0, 4, 4, [0, 0xff, 0]);
358 display
.fillRect(0, 0, 2, 2, [0xff, 0, 0]);
359 display
.fillRect(2, 2, 2, 2, [0xff, 0, 0]);
361 expect(display
).to
.have
.displayed(checked_data
);
364 it('should support copying an portion of the canvas via #copyImage', function () {
365 display
.fillRect(0, 0, 4, 4, [0, 0xff, 0]);
366 display
.fillRect(0, 0, 2, 2, [0xff, 0, 0x00]);
367 display
.copyImage(0, 0, 2, 2, 2, 2);
369 expect(display
).to
.have
.displayed(checked_data
);
372 it('should support drawing images via #imageRect', function (done
) {
373 display
.imageRect(0, 0, "image/png", make_image_png(checked_data
));
375 display
.set_onFlush(function () {
376 expect(display
).to
.have
.displayed(checked_data
);
382 it('should support drawing tile data with a background color and sub tiles', function () {
383 display
.startTile(0, 0, 4, 4, [0, 0xff, 0]);
384 display
.subTile(0, 0, 2, 2, [0xff, 0, 0]);
385 display
.subTile(2, 2, 2, 2, [0xff, 0, 0]);
386 display
.finishTile();
388 expect(display
).to
.have
.displayed(checked_data
);
391 it('should support drawing BGRX blit images with true color via #blitImage', function () {
393 for (var i
= 0; i
< 16; i
++) {
394 data
[i
* 4] = checked_data
[i
* 4 + 2];
395 data
[i
* 4 + 1] = checked_data
[i
* 4 + 1];
396 data
[i
* 4 + 2] = checked_data
[i
* 4];
397 data
[i
* 4 + 3] = checked_data
[i
* 4 + 3];
399 display
.blitImage(0, 0, 4, 4, data
, 0);
401 expect(display
).to
.have
.displayed(checked_data
);
404 it('should support drawing RGB blit images with true color via #blitRgbImage', function () {
406 for (var i
= 0; i
< 16; i
++) {
407 data
[i
* 3] = checked_data
[i
* 4];
408 data
[i
* 3 + 1] = checked_data
[i
* 4 + 1];
409 data
[i
* 3 + 2] = checked_data
[i
* 4 + 2];
411 display
.blitRgbImage(0, 0, 4, 4, data
, 0);
413 expect(display
).to
.have
.displayed(checked_data
);
416 it('should support drawing an image object via #drawImage', function () {
417 var img
= make_image_canvas(checked_data
);
418 display
.drawImage(img
, 0, 0);
420 expect(display
).to
.have
.displayed(checked_data
);
424 describe('(prefering native methods)', function () { drawing_tests
.call(this, false); });
425 describe('(prefering JavaScript)', function () { drawing_tests
.call(this, true); });
428 describe('the render queue processor', function () {
430 beforeEach(function () {
431 display
= new Display({ target
: document
.createElement('canvas'), prefer_js
: false });
432 display
.resize(4, 4);
433 sinon
.spy(display
, '_scan_renderQ');
436 afterEach(function () {
437 window
.requestAnimationFrame
= this.old_requestAnimationFrame
;
440 it('should try to process an item when it is pushed on, if nothing else is on the queue', function () {
441 display
._renderQ_push({ type
: 'noop' }); // does nothing
442 expect(display
._scan_renderQ
).to
.have
.been
.calledOnce
;
445 it('should not try to process an item when it is pushed on if we are waiting for other items', function () {
446 display
._renderQ
.length
= 2;
447 display
._renderQ_push({ type
: 'noop' });
448 expect(display
._scan_renderQ
).to
.not
.have
.been
.called
;
451 it('should wait until an image is loaded to attempt to draw it and the rest of the queue', function () {
452 var img
= { complete
: false, addEventListener
: sinon
.spy() }
453 display
._renderQ
= [{ type
: 'img', x
: 3, y
: 4, img
: img
},
454 { type
: 'fill', x
: 1, y
: 2, width
: 3, height
: 4, color
: 5 }];
455 display
.drawImage
= sinon
.spy();
456 display
.fillRect
= sinon
.spy();
458 display
._scan_renderQ();
459 expect(display
.drawImage
).to
.not
.have
.been
.called
;
460 expect(display
.fillRect
).to
.not
.have
.been
.called
;
461 expect(img
.addEventListener
).to
.have
.been
.calledOnce
;
463 display
._renderQ
[0].img
.complete
= true;
464 display
._scan_renderQ();
465 expect(display
.drawImage
).to
.have
.been
.calledOnce
;
466 expect(display
.fillRect
).to
.have
.been
.calledOnce
;
467 expect(img
.addEventListener
).to
.have
.been
.calledOnce
;
470 it('should call callback when queue is flushed', function () {
471 display
.set_onFlush(sinon
.spy());
472 display
.fillRect(0, 0, 4, 4, [0, 0xff, 0]);
473 expect(display
.get_onFlush()).to
.not
.have
.been
.called
;
475 expect(display
.get_onFlush()).to
.have
.been
.calledOnce
;
478 it('should draw a blit image on type "blit"', function () {
479 display
.blitImage
= sinon
.spy();
480 display
._renderQ_push({ type
: 'blit', x
: 3, y
: 4, width
: 5, height
: 6, data
: [7, 8, 9] });
481 expect(display
.blitImage
).to
.have
.been
.calledOnce
;
482 expect(display
.blitImage
).to
.have
.been
.calledWith(3, 4, 5, 6, [7, 8, 9], 0);
485 it('should draw a blit RGB image on type "blitRgb"', function () {
486 display
.blitRgbImage
= sinon
.spy();
487 display
._renderQ_push({ type
: 'blitRgb', x
: 3, y
: 4, width
: 5, height
: 6, data
: [7, 8, 9] });
488 expect(display
.blitRgbImage
).to
.have
.been
.calledOnce
;
489 expect(display
.blitRgbImage
).to
.have
.been
.calledWith(3, 4, 5, 6, [7, 8, 9], 0);
492 it('should copy a region on type "copy"', function () {
493 display
.copyImage
= sinon
.spy();
494 display
._renderQ_push({ type
: 'copy', x
: 3, y
: 4, width
: 5, height
: 6, old_x
: 7, old_y
: 8 });
495 expect(display
.copyImage
).to
.have
.been
.calledOnce
;
496 expect(display
.copyImage
).to
.have
.been
.calledWith(7, 8, 3, 4, 5, 6);
499 it('should fill a rect with a given color on type "fill"', function () {
500 display
.fillRect
= sinon
.spy();
501 display
._renderQ_push({ type
: 'fill', x
: 3, y
: 4, width
: 5, height
: 6, color
: [7, 8, 9]});
502 expect(display
.fillRect
).to
.have
.been
.calledOnce
;
503 expect(display
.fillRect
).to
.have
.been
.calledWith(3, 4, 5, 6, [7, 8, 9]);
506 it('should draw an image from an image object on type "img" (if complete)', function () {
507 display
.drawImage
= sinon
.spy();
508 display
._renderQ_push({ type
: 'img', x
: 3, y
: 4, img
: { complete
: true } });
509 expect(display
.drawImage
).to
.have
.been
.calledOnce
;
510 expect(display
.drawImage
).to
.have
.been
.calledWith({ complete
: true }, 3, 4);