1 /* jshint expr: true */
2 var expect
= chai
.expect
;
4 import Base64
from '../core/base64.js';
5 import Display
from '../core/display.js';
7 import sinon
from '../vendor/sinon.js';
9 describe('Display/Canvas Helper', function () {
11 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
12 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
13 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
14 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255
16 checked_data
= new Uint8Array(checked_data
);
18 var basic_data
= [0xff, 0x00, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0xff, 0xff, 0xff, 255];
19 basic_data
= new Uint8Array(basic_data
);
21 function make_image_canvas (input_data
) {
22 var canvas
= document
.createElement('canvas');
25 var ctx
= canvas
.getContext('2d');
26 var data
= ctx
.createImageData(4, 4);
27 for (var i
= 0; i
< checked_data
.length
; i
++) { data
.data
[i
] = input_data
[i
]; }
28 ctx
.putImageData(data
, 0, 0);
32 function make_image_png (input_data
) {
33 var canvas
= make_image_canvas(input_data
);
34 var url
= canvas
.toDataURL();
35 var data
= url
.split(",")[1];
36 return Base64
.decode(data
);
39 describe('viewport handling', function () {
41 beforeEach(function () {
42 display
= new Display(document
.createElement('canvas'));
43 display
.clipViewport
= true;
45 display
.viewportChangeSize(3, 3);
46 display
.viewportChangePos(1, 1);
49 it('should take viewport location into consideration when drawing images', function () {
51 display
.viewportChangeSize(2, 2);
52 display
.drawImage(make_image_canvas(basic_data
), 1, 1);
55 var expected
= new Uint8Array(16);
57 for (i
= 0; i
< 8; i
++) { expected
[i
] = basic_data
[i
]; }
58 for (i
= 8; i
< 16; i
++) { expected
[i
] = 0; }
59 expect(display
).to
.have
.displayed(expected
);
62 it('should resize the target canvas when resizing the viewport', function() {
63 display
.viewportChangeSize(2, 2);
64 expect(display
._target
.width
).to
.equal(2);
65 expect(display
._target
.height
).to
.equal(2);
68 it('should move the viewport if necessary', function() {
69 display
.viewportChangeSize(5, 5);
70 expect(display
.absX(0)).to
.equal(0);
71 expect(display
.absY(0)).to
.equal(0);
72 expect(display
._target
.width
).to
.equal(5);
73 expect(display
._target
.height
).to
.equal(5);
76 it('should limit the viewport to the framebuffer size', function() {
77 display
.viewportChangeSize(6, 6);
78 expect(display
._target
.width
).to
.equal(5);
79 expect(display
._target
.height
).to
.equal(5);
82 it('should redraw when moving the viewport', function () {
83 display
.flip
= sinon
.spy();
84 display
.viewportChangePos(-1, 1);
85 expect(display
.flip
).to
.have
.been
.calledOnce
;
88 it('should redraw when resizing the viewport', function () {
89 display
.flip
= sinon
.spy();
90 display
.viewportChangeSize(2, 2);
91 expect(display
.flip
).to
.have
.been
.calledOnce
;
94 it('should report clipping when framebuffer > viewport', function () {
95 expect(display
.isClipped
).to
.be
.true;
98 it('should report not clipping when framebuffer = viewport', function () {
99 display
.viewportChangeSize(5, 5);
100 expect(display
.isClipped
).to
.be
.false;
103 it('should show the entire framebuffer when disabling the viewport', function() {
104 display
.clipViewport
= false;
105 expect(display
.absX(0)).to
.equal(0);
106 expect(display
.absY(0)).to
.equal(0);
107 expect(display
._target
.width
).to
.equal(5);
108 expect(display
._target
.height
).to
.equal(5);
111 it('should ignore viewport changes when the viewport is disabled', function() {
112 display
.clipViewport
= false;
113 display
.viewportChangeSize(2, 2);
114 display
.viewportChangePos(1, 1);
115 expect(display
.absX(0)).to
.equal(0);
116 expect(display
.absY(0)).to
.equal(0);
117 expect(display
._target
.width
).to
.equal(5);
118 expect(display
._target
.height
).to
.equal(5);
121 it('should show the entire framebuffer just after enabling the viewport', function() {
122 display
.clipViewport
= false;
123 display
.clipViewport
= true;
124 expect(display
.absX(0)).to
.equal(0);
125 expect(display
.absY(0)).to
.equal(0);
126 expect(display
._target
.width
).to
.equal(5);
127 expect(display
._target
.height
).to
.equal(5);
131 describe('resizing', function () {
133 beforeEach(function () {
134 display
= new Display(document
.createElement('canvas'));
135 display
.clipViewport
= false;
136 display
.resize(4, 4);
139 it('should change the size of the logical canvas', function () {
140 display
.resize(5, 7);
141 expect(display
._fb_width
).to
.equal(5);
142 expect(display
._fb_height
).to
.equal(7);
145 it('should keep the framebuffer data', function () {
146 display
.fillRect(0, 0, 4, 4, [0, 0, 0xff]);
147 display
.resize(2, 2);
150 for (var i
= 0; i
< 4 * 2*2; i
+= 4) {
152 expected
[i
+1] = expected
[i
+2] = 0;
153 expected
[i
+3] = 0xff;
155 expect(display
).to
.have
.displayed(new Uint8Array(expected
));
158 describe('viewport', function () {
159 beforeEach(function () {
160 display
.clipViewport
= true;
161 display
.viewportChangeSize(3, 3);
162 display
.viewportChangePos(1, 1);
165 it('should keep the viewport position and size if possible', function () {
166 display
.resize(6, 6);
167 expect(display
.absX(0)).to
.equal(1);
168 expect(display
.absY(0)).to
.equal(1);
169 expect(display
._target
.width
).to
.equal(3);
170 expect(display
._target
.height
).to
.equal(3);
173 it('should move the viewport if necessary', function () {
174 display
.resize(3, 3);
175 expect(display
.absX(0)).to
.equal(0);
176 expect(display
.absY(0)).to
.equal(0);
177 expect(display
._target
.width
).to
.equal(3);
178 expect(display
._target
.height
).to
.equal(3);
181 it('should shrink the viewport if necessary', function () {
182 display
.resize(2, 2);
183 expect(display
.absX(0)).to
.equal(0);
184 expect(display
.absY(0)).to
.equal(0);
185 expect(display
._target
.width
).to
.equal(2);
186 expect(display
._target
.height
).to
.equal(2);
191 describe('rescaling', function () {
195 beforeEach(function () {
196 canvas
= document
.createElement('canvas');
197 display
= new Display(canvas
);
198 display
.clipViewport
= true;
199 display
.resize(4, 4);
200 display
.viewportChangeSize(3, 3);
201 display
.viewportChangePos(1, 1);
202 document
.body
.appendChild(canvas
);
205 afterEach(function () {
206 document
.body
.removeChild(canvas
);
209 it('should not change the bitmap size of the canvas', function () {
211 expect(canvas
.width
).to
.equal(3);
212 expect(canvas
.height
).to
.equal(3);
215 it('should change the effective rendered size of the canvas', function () {
217 expect(canvas
.clientWidth
).to
.equal(6);
218 expect(canvas
.clientHeight
).to
.equal(6);
221 it('should not change when resizing', function () {
223 display
.resize(5, 5);
224 expect(display
.scale
).to
.equal(2.0);
225 expect(canvas
.width
).to
.equal(3);
226 expect(canvas
.height
).to
.equal(3);
227 expect(canvas
.clientWidth
).to
.equal(6);
228 expect(canvas
.clientHeight
).to
.equal(6);
232 describe('autoscaling', function () {
236 beforeEach(function () {
237 canvas
= document
.createElement('canvas');
238 display
= new Display(canvas
);
239 display
.clipViewport
= true;
240 display
.resize(4, 3);
241 document
.body
.appendChild(canvas
);
244 afterEach(function () {
245 document
.body
.removeChild(canvas
);
248 it('should preserve aspect ratio while autoscaling', function () {
249 display
.autoscale(16, 9);
250 expect(canvas
.clientWidth
/ canvas
.clientHeight
).to
.equal(4 / 3);
253 it('should use width to determine scale when the current aspect ratio is wider than the target', function () {
254 display
.autoscale(9, 16);
255 expect(display
.absX(9)).to
.equal(4);
256 expect(display
.absY(18)).to
.equal(8);
257 expect(canvas
.clientWidth
).to
.equal(9);
258 expect(canvas
.clientHeight
).to
.equal(7); // round 9 / (4 / 3)
261 it('should use height to determine scale when the current aspect ratio is taller than the target', function () {
262 display
.autoscale(16, 9);
263 expect(display
.absX(9)).to
.equal(3);
264 expect(display
.absY(18)).to
.equal(6);
265 expect(canvas
.clientWidth
).to
.equal(12); // 16 * (4 / 3)
266 expect(canvas
.clientHeight
).to
.equal(9);
270 it('should not change the bitmap size of the canvas', function () {
271 display
.autoscale(16, 9);
272 expect(canvas
.width
).to
.equal(4);
273 expect(canvas
.height
).to
.equal(3);
277 describe('drawing', function () {
279 // TODO(directxman12): improve the tests for each of the drawing functions to cover more than just the
282 beforeEach(function () {
283 display
= new Display(document
.createElement('canvas'));
284 display
.resize(4, 4);
287 it('should clear the screen on #clear without a logo set', function () {
288 display
.fillRect(0, 0, 4, 4, [0x00, 0x00, 0xff]);
289 display
._logo
= null;
291 display
.resize(4, 4);
293 for (var i
= 0; i
< 4 * display
._fb_width
* display
._fb_height
; i
++) { empty
[i
] = 0; }
294 expect(display
).to
.have
.displayed(new Uint8Array(empty
));
297 it('should draw the logo on #clear with a logo set', function (done
) {
298 display
._logo
= { width
: 4, height
: 4, type
: "image/png", data
: make_image_png(checked_data
) };
300 display
.onflush = function () {
301 expect(display
).to
.have
.displayed(checked_data
);
302 expect(display
._fb_width
).to
.equal(4);
303 expect(display
._fb_height
).to
.equal(4);
309 it('should not draw directly on the target canvas', function () {
310 display
.fillRect(0, 0, 4, 4, [0, 0, 0xff]);
312 display
.fillRect(0, 0, 4, 4, [0, 0xff, 0]);
314 for (var i
= 0; i
< 4 * display
._fb_width
* display
._fb_height
; i
+= 4) {
316 expected
[i
+1] = expected
[i
+2] = 0;
317 expected
[i
+3] = 0xff;
319 expect(display
).to
.have
.displayed(new Uint8Array(expected
));
322 it('should support filling a rectangle with particular color via #fillRect', function () {
323 display
.fillRect(0, 0, 4, 4, [0, 0xff, 0]);
324 display
.fillRect(0, 0, 2, 2, [0xff, 0, 0]);
325 display
.fillRect(2, 2, 2, 2, [0xff, 0, 0]);
327 expect(display
).to
.have
.displayed(checked_data
);
330 it('should support copying an portion of the canvas via #copyImage', function () {
331 display
.fillRect(0, 0, 4, 4, [0, 0xff, 0]);
332 display
.fillRect(0, 0, 2, 2, [0xff, 0, 0x00]);
333 display
.copyImage(0, 0, 2, 2, 2, 2);
335 expect(display
).to
.have
.displayed(checked_data
);
338 it('should support drawing images via #imageRect', function (done
) {
339 display
.imageRect(0, 0, "image/png", make_image_png(checked_data
));
341 display
.onflush = function () {
342 expect(display
).to
.have
.displayed(checked_data
);
348 it('should support drawing tile data with a background color and sub tiles', function () {
349 display
.startTile(0, 0, 4, 4, [0, 0xff, 0]);
350 display
.subTile(0, 0, 2, 2, [0xff, 0, 0]);
351 display
.subTile(2, 2, 2, 2, [0xff, 0, 0]);
352 display
.finishTile();
354 expect(display
).to
.have
.displayed(checked_data
);
357 it('should support drawing BGRX blit images with true color via #blitImage', function () {
359 for (var i
= 0; i
< 16; i
++) {
360 data
[i
* 4] = checked_data
[i
* 4 + 2];
361 data
[i
* 4 + 1] = checked_data
[i
* 4 + 1];
362 data
[i
* 4 + 2] = checked_data
[i
* 4];
363 data
[i
* 4 + 3] = checked_data
[i
* 4 + 3];
365 display
.blitImage(0, 0, 4, 4, data
, 0);
367 expect(display
).to
.have
.displayed(checked_data
);
370 it('should support drawing RGB blit images with true color via #blitRgbImage', function () {
372 for (var i
= 0; i
< 16; i
++) {
373 data
[i
* 3] = checked_data
[i
* 4];
374 data
[i
* 3 + 1] = checked_data
[i
* 4 + 1];
375 data
[i
* 3 + 2] = checked_data
[i
* 4 + 2];
377 display
.blitRgbImage(0, 0, 4, 4, data
, 0);
379 expect(display
).to
.have
.displayed(checked_data
);
382 it('should support drawing an image object via #drawImage', function () {
383 var img
= make_image_canvas(checked_data
);
384 display
.drawImage(img
, 0, 0);
386 expect(display
).to
.have
.displayed(checked_data
);
390 describe('the render queue processor', function () {
392 beforeEach(function () {
393 display
= new Display(document
.createElement('canvas'));
394 display
.resize(4, 4);
395 sinon
.spy(display
, '_scan_renderQ');
398 afterEach(function () {
399 window
.requestAnimationFrame
= this.old_requestAnimationFrame
;
402 it('should try to process an item when it is pushed on, if nothing else is on the queue', function () {
403 display
._renderQ_push({ type
: 'noop' }); // does nothing
404 expect(display
._scan_renderQ
).to
.have
.been
.calledOnce
;
407 it('should not try to process an item when it is pushed on if we are waiting for other items', function () {
408 display
._renderQ
.length
= 2;
409 display
._renderQ_push({ type
: 'noop' });
410 expect(display
._scan_renderQ
).to
.not
.have
.been
.called
;
413 it('should wait until an image is loaded to attempt to draw it and the rest of the queue', function () {
414 var img
= { complete
: false, addEventListener
: sinon
.spy() }
415 display
._renderQ
= [{ type
: 'img', x
: 3, y
: 4, img
: img
},
416 { type
: 'fill', x
: 1, y
: 2, width
: 3, height
: 4, color
: 5 }];
417 display
.drawImage
= sinon
.spy();
418 display
.fillRect
= sinon
.spy();
420 display
._scan_renderQ();
421 expect(display
.drawImage
).to
.not
.have
.been
.called
;
422 expect(display
.fillRect
).to
.not
.have
.been
.called
;
423 expect(img
.addEventListener
).to
.have
.been
.calledOnce
;
425 display
._renderQ
[0].img
.complete
= true;
426 display
._scan_renderQ();
427 expect(display
.drawImage
).to
.have
.been
.calledOnce
;
428 expect(display
.fillRect
).to
.have
.been
.calledOnce
;
429 expect(img
.addEventListener
).to
.have
.been
.calledOnce
;
432 it('should call callback when queue is flushed', function () {
433 display
.onflush
= sinon
.spy();
434 display
.fillRect(0, 0, 4, 4, [0, 0xff, 0]);
435 expect(display
.onflush
).to
.not
.have
.been
.called
;
437 expect(display
.onflush
).to
.have
.been
.calledOnce
;
440 it('should draw a blit image on type "blit"', function () {
441 display
.blitImage
= sinon
.spy();
442 display
._renderQ_push({ type
: 'blit', x
: 3, y
: 4, width
: 5, height
: 6, data
: [7, 8, 9] });
443 expect(display
.blitImage
).to
.have
.been
.calledOnce
;
444 expect(display
.blitImage
).to
.have
.been
.calledWith(3, 4, 5, 6, [7, 8, 9], 0);
447 it('should draw a blit RGB image on type "blitRgb"', function () {
448 display
.blitRgbImage
= sinon
.spy();
449 display
._renderQ_push({ type
: 'blitRgb', x
: 3, y
: 4, width
: 5, height
: 6, data
: [7, 8, 9] });
450 expect(display
.blitRgbImage
).to
.have
.been
.calledOnce
;
451 expect(display
.blitRgbImage
).to
.have
.been
.calledWith(3, 4, 5, 6, [7, 8, 9], 0);
454 it('should copy a region on type "copy"', function () {
455 display
.copyImage
= sinon
.spy();
456 display
._renderQ_push({ type
: 'copy', x
: 3, y
: 4, width
: 5, height
: 6, old_x
: 7, old_y
: 8 });
457 expect(display
.copyImage
).to
.have
.been
.calledOnce
;
458 expect(display
.copyImage
).to
.have
.been
.calledWith(7, 8, 3, 4, 5, 6);
461 it('should fill a rect with a given color on type "fill"', function () {
462 display
.fillRect
= sinon
.spy();
463 display
._renderQ_push({ type
: 'fill', x
: 3, y
: 4, width
: 5, height
: 6, color
: [7, 8, 9]});
464 expect(display
.fillRect
).to
.have
.been
.calledOnce
;
465 expect(display
.fillRect
).to
.have
.been
.calledWith(3, 4, 5, 6, [7, 8, 9]);
468 it('should draw an image from an image object on type "img" (if complete)', function () {
469 display
.drawImage
= sinon
.spy();
470 display
._renderQ_push({ type
: 'img', x
: 3, y
: 4, img
: { complete
: true } });
471 expect(display
.drawImage
).to
.have
.been
.calledOnce
;
472 expect(display
.drawImage
).to
.have
.been
.calledWith({ complete
: true }, 3, 4);