]>
Commit | Line | Data |
---|---|---|
1e13775b | 1 | // requires local modules: util, base64, display |
2c9623b5 | 2 | // requires test modules: assertions |
1e13775b SR |
3 | /* jshint expr: true */ |
4 | var expect = chai.expect; | |
5 | ||
1e13775b SR |
6 | describe('Display/Canvas Helper', function () { |
7 | var checked_data = [ | |
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 | |
12 | ]; | |
13 | checked_data = new Uint8Array(checked_data); | |
14 | ||
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); | |
17 | ||
18 | function make_image_canvas (input_data) { | |
19 | var canvas = document.createElement('canvas'); | |
20 | canvas.width = 4; | |
21 | canvas.height = 4; | |
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); | |
26 | return canvas; | |
27 | } | |
28 | ||
29 | describe('viewport handling', function () { | |
30 | var display; | |
31 | beforeEach(function () { | |
32 | display = new Display({ target: document.createElement('canvas'), prefer_js: false, viewport: true }); | |
33 | display.resize(5, 5); | |
34 | display.viewportChange(1, 1, 3, 3); | |
35 | display.getCleanDirtyReset(); | |
36 | }); | |
37 | ||
38 | it('should take viewport location into consideration when drawing images', function () { | |
39 | display.resize(4, 4); | |
40 | display.viewportChange(0, 0, 2, 2); | |
41 | display.drawImage(make_image_canvas(basic_data), 1, 1); | |
42 | ||
43 | var expected = new Uint8Array(16); | |
44 | var i; | |
45 | for (i = 0; i < 8; i++) { expected[i] = basic_data[i]; } | |
46 | for (i = 8; i < 16; i++) { expected[i] = 0; } | |
47 | expect(display).to.have.displayed(expected); | |
48 | }); | |
49 | ||
50 | it('should redraw the left side when shifted left', function () { | |
51 | display.viewportChange(-1, 0, 3, 3); | |
52 | var cdr = display.getCleanDirtyReset(); | |
53 | expect(cdr.cleanBox).to.deep.equal({ x: 1, y: 1, w: 2, h: 3 }); | |
54 | expect(cdr.dirtyBoxes).to.have.length(1); | |
55 | expect(cdr.dirtyBoxes[0]).to.deep.equal({ x: 0, y: 1, w: 2, h: 3 }); | |
56 | }); | |
57 | ||
58 | it('should redraw the right side when shifted right', function () { | |
59 | display.viewportChange(1, 0, 3, 3); | |
60 | var cdr = display.getCleanDirtyReset(); | |
61 | expect(cdr.cleanBox).to.deep.equal({ x: 2, y: 1, w: 2, h: 3 }); | |
62 | expect(cdr.dirtyBoxes).to.have.length(1); | |
63 | expect(cdr.dirtyBoxes[0]).to.deep.equal({ x: 4, y: 1, w: 1, h: 3 }); | |
64 | }); | |
65 | ||
66 | it('should redraw the top part when shifted up', function () { | |
67 | display.viewportChange(0, -1, 3, 3); | |
68 | var cdr = display.getCleanDirtyReset(); | |
69 | expect(cdr.cleanBox).to.deep.equal({ x: 1, y: 1, w: 3, h: 2 }); | |
70 | expect(cdr.dirtyBoxes).to.have.length(1); | |
71 | expect(cdr.dirtyBoxes[0]).to.deep.equal({ x: 1, y: 0, w: 3, h: 1 }); | |
72 | }); | |
73 | ||
74 | it('should redraw the bottom part when shifted down', function () { | |
75 | display.viewportChange(0, 1, 3, 3); | |
76 | var cdr = display.getCleanDirtyReset(); | |
77 | expect(cdr.cleanBox).to.deep.equal({ x: 1, y: 2, w: 3, h: 2 }); | |
78 | expect(cdr.dirtyBoxes).to.have.length(1); | |
79 | expect(cdr.dirtyBoxes[0]).to.deep.equal({ x: 1, y: 4, w: 3, h: 1 }); | |
80 | }); | |
81 | ||
82 | it('should reset the entire viewport to being clean after calculating the clean/dirty boxes', function () { | |
83 | display.viewportChange(0, 1, 3, 3); | |
84 | var cdr1 = display.getCleanDirtyReset(); | |
85 | var cdr2 = display.getCleanDirtyReset(); | |
86 | expect(cdr1).to.not.deep.equal(cdr2); | |
87 | expect(cdr2.cleanBox).to.deep.equal({ x: 1, y: 2, w: 3, h: 3 }); | |
88 | expect(cdr2.dirtyBoxes).to.be.empty; | |
89 | }); | |
90 | ||
91 | it('should simply mark the whole display area as dirty if not using viewports', function () { | |
92 | display = new Display({ target: document.createElement('canvas'), prefer_js: false, viewport: false }); | |
93 | display.resize(5, 5); | |
94 | var cdr = display.getCleanDirtyReset(); | |
95 | expect(cdr.cleanBox).to.deep.equal({ x: 0, y: 0, w: 0, h: 0 }); | |
96 | expect(cdr.dirtyBoxes).to.have.length(1); | |
97 | expect(cdr.dirtyBoxes[0]).to.deep.equal({ x: 0, y: 0, w: 5, h: 5 }); | |
98 | }); | |
99 | }); | |
100 | ||
101 | describe('resizing', function () { | |
102 | var display; | |
103 | beforeEach(function () { | |
104 | display = new Display({ target: document.createElement('canvas'), prefer_js: false, viewport: true }); | |
105 | display.resize(4, 3); | |
106 | }); | |
107 | ||
108 | it('should change the size of the logical canvas', function () { | |
109 | display.resize(5, 7); | |
110 | expect(display._fb_width).to.equal(5); | |
111 | expect(display._fb_height).to.equal(7); | |
112 | }); | |
113 | ||
114 | it('should update the viewport dimensions', function () { | |
115 | sinon.spy(display, 'viewportChange'); | |
116 | display.resize(2, 2); | |
117 | expect(display.viewportChange).to.have.been.calledOnce; | |
118 | }); | |
119 | }); | |
120 | ||
121 | describe('drawing', function () { | |
122 | ||
123 | // TODO(directxman12): improve the tests for each of the drawing functions to cover more than just the | |
124 | // basic cases | |
125 | function drawing_tests (pref_js) { | |
126 | var display; | |
127 | beforeEach(function () { | |
128 | display = new Display({ target: document.createElement('canvas'), prefer_js: pref_js }); | |
129 | display.resize(4, 4); | |
130 | }); | |
131 | ||
132 | it('should clear the screen on #clear without a logo set', function () { | |
133 | display.fillRect(0, 0, 4, 4, [0x00, 0x00, 0xff]); | |
134 | display._logo = null; | |
135 | display.clear(); | |
136 | display.resize(4, 4); | |
137 | var empty = []; | |
138 | for (var i = 0; i < 4 * display._fb_width * display._fb_height; i++) { empty[i] = 0; } | |
139 | expect(display).to.have.displayed(new Uint8Array(empty)); | |
140 | }); | |
141 | ||
142 | it('should draw the logo on #clear with a logo set', function (done) { | |
143 | display._logo = { width: 4, height: 4, data: make_image_canvas(checked_data).toDataURL() }; | |
144 | display._drawCtx._act_drawImg = display._drawCtx.drawImage; | |
145 | display._drawCtx.drawImage = function (img, x, y) { | |
146 | this._act_drawImg(img, x, y); | |
147 | expect(display).to.have.displayed(checked_data); | |
148 | done(); | |
149 | }; | |
150 | display.clear(); | |
151 | expect(display._fb_width).to.equal(4); | |
152 | expect(display._fb_height).to.equal(4); | |
153 | }); | |
154 | ||
155 | it('should support filling a rectangle with particular color via #fillRect', function () { | |
156 | display.fillRect(0, 0, 4, 4, [0, 0xff, 0]); | |
157 | display.fillRect(0, 0, 2, 2, [0xff, 0, 0]); | |
158 | display.fillRect(2, 2, 2, 2, [0xff, 0, 0]); | |
159 | expect(display).to.have.displayed(checked_data); | |
160 | }); | |
161 | ||
162 | it('should support copying an portion of the canvas via #copyImage', function () { | |
163 | display.fillRect(0, 0, 4, 4, [0, 0xff, 0]); | |
164 | display.fillRect(0, 0, 2, 2, [0xff, 0, 0x00]); | |
165 | display.copyImage(0, 0, 2, 2, 2, 2); | |
166 | expect(display).to.have.displayed(checked_data); | |
167 | }); | |
168 | ||
169 | it('should support drawing tile data with a background color and sub tiles', function () { | |
170 | display.startTile(0, 0, 4, 4, [0, 0xff, 0]); | |
171 | display.subTile(0, 0, 2, 2, [0xff, 0, 0]); | |
172 | display.subTile(2, 2, 2, 2, [0xff, 0, 0]); | |
173 | display.finishTile(); | |
174 | expect(display).to.have.displayed(checked_data); | |
175 | }); | |
176 | ||
177 | it('should support drawing BGRX blit images with true color via #blitImage', function () { | |
178 | var data = []; | |
179 | for (var i = 0; i < 16; i++) { | |
180 | data[i * 4] = checked_data[i * 4 + 2]; | |
181 | data[i * 4 + 1] = checked_data[i * 4 + 1]; | |
182 | data[i * 4 + 2] = checked_data[i * 4]; | |
183 | data[i * 4 + 3] = checked_data[i * 4 + 3]; | |
184 | } | |
185 | display.blitImage(0, 0, 4, 4, data, 0); | |
186 | expect(display).to.have.displayed(checked_data); | |
187 | }); | |
188 | ||
189 | it('should support drawing RGB blit images with true color via #blitRgbImage', function () { | |
190 | var data = []; | |
191 | for (var i = 0; i < 16; i++) { | |
192 | data[i * 3] = checked_data[i * 4]; | |
193 | data[i * 3 + 1] = checked_data[i * 4 + 1]; | |
194 | data[i * 3 + 2] = checked_data[i * 4 + 2]; | |
195 | } | |
196 | display.blitRgbImage(0, 0, 4, 4, data, 0); | |
197 | expect(display).to.have.displayed(checked_data); | |
198 | }); | |
199 | ||
200 | it('should support drawing blit images from a data URL via #blitStringImage', function (done) { | |
201 | var img_url = make_image_canvas(checked_data).toDataURL(); | |
202 | display._drawCtx._act_drawImg = display._drawCtx.drawImage; | |
203 | display._drawCtx.drawImage = function (img, x, y) { | |
204 | this._act_drawImg(img, x, y); | |
205 | expect(display).to.have.displayed(checked_data); | |
206 | done(); | |
207 | }; | |
208 | display.blitStringImage(img_url, 0, 0); | |
209 | }); | |
210 | ||
211 | it('should support drawing solid colors with color maps', function () { | |
212 | display._true_color = false; | |
213 | display.set_colourMap({ 0: [0xff, 0, 0], 1: [0, 0xff, 0] }); | |
214 | display.fillRect(0, 0, 4, 4, [1]); | |
215 | display.fillRect(0, 0, 2, 2, [0]); | |
216 | display.fillRect(2, 2, 2, 2, [0]); | |
217 | expect(display).to.have.displayed(checked_data); | |
218 | }); | |
219 | ||
220 | it('should support drawing blit images with color maps', function () { | |
221 | display._true_color = false; | |
222 | display.set_colourMap({ 1: [0xff, 0, 0], 0: [0, 0xff, 0] }); | |
223 | var data = [1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1].map(function (elem) { return [elem]; }); | |
224 | display.blitImage(0, 0, 4, 4, data, 0); | |
225 | expect(display).to.have.displayed(checked_data); | |
226 | }); | |
227 | ||
228 | it('should support drawing an image object via #drawImage', function () { | |
229 | var img = make_image_canvas(checked_data); | |
230 | display.drawImage(img, 0, 0); | |
231 | expect(display).to.have.displayed(checked_data); | |
232 | }); | |
233 | } | |
234 | ||
235 | describe('(prefering native methods)', function () { drawing_tests.call(this, false); }); | |
236 | describe('(prefering JavaScript)', function () { drawing_tests.call(this, true); }); | |
237 | }); | |
238 | ||
239 | describe('the render queue processor', function () { | |
240 | var display; | |
241 | beforeEach(function () { | |
242 | display = new Display({ target: document.createElement('canvas'), prefer_js: false }); | |
243 | display.resize(4, 4); | |
244 | sinon.spy(display, '_scan_renderQ'); | |
245 | this.old_requestAnimFrame = window.requestAnimFrame; | |
246 | window.requestAnimFrame = function (cb) { | |
247 | this.next_frame_cb = cb; | |
248 | }.bind(this); | |
249 | this.next_frame = function () { this.next_frame_cb(); }; | |
250 | }); | |
251 | ||
252 | afterEach(function () { | |
253 | window.requestAnimFrame = this.old_requestAnimFrame; | |
254 | }); | |
255 | ||
256 | it('should try to process an item when it is pushed on, if nothing else is on the queue', function () { | |
257 | display.renderQ_push({ type: 'noop' }); // does nothing | |
258 | expect(display._scan_renderQ).to.have.been.calledOnce; | |
259 | }); | |
260 | ||
261 | it('should not try to process an item when it is pushed on if we are waiting for other items', function () { | |
262 | display._renderQ.length = 2; | |
263 | display.renderQ_push({ type: 'noop' }); | |
264 | expect(display._scan_renderQ).to.not.have.been.called; | |
265 | }); | |
266 | ||
267 | it('should wait until an image is loaded to attempt to draw it and the rest of the queue', function () { | |
268 | var img = { complete: false }; | |
269 | display._renderQ = [{ type: 'img', x: 3, y: 4, img: img }, | |
270 | { type: 'fill', x: 1, y: 2, width: 3, height: 4, color: 5 }]; | |
271 | display.drawImage = sinon.spy(); | |
272 | display.fillRect = sinon.spy(); | |
273 | ||
274 | display._scan_renderQ(); | |
275 | expect(display.drawImage).to.not.have.been.called; | |
276 | expect(display.fillRect).to.not.have.been.called; | |
277 | ||
278 | display._renderQ[0].img.complete = true; | |
279 | this.next_frame(); | |
280 | expect(display.drawImage).to.have.been.calledOnce; | |
281 | expect(display.fillRect).to.have.been.calledOnce; | |
282 | }); | |
283 | ||
284 | it('should draw a blit image on type "blit"', function () { | |
285 | display.blitImage = sinon.spy(); | |
286 | display.renderQ_push({ type: 'blit', x: 3, y: 4, width: 5, height: 6, data: [7, 8, 9] }); | |
287 | expect(display.blitImage).to.have.been.calledOnce; | |
288 | expect(display.blitImage).to.have.been.calledWith(3, 4, 5, 6, [7, 8, 9], 0); | |
289 | }); | |
290 | ||
291 | it('should draw a blit RGB image on type "blitRgb"', function () { | |
292 | display.blitRgbImage = sinon.spy(); | |
293 | display.renderQ_push({ type: 'blitRgb', x: 3, y: 4, width: 5, height: 6, data: [7, 8, 9] }); | |
294 | expect(display.blitRgbImage).to.have.been.calledOnce; | |
295 | expect(display.blitRgbImage).to.have.been.calledWith(3, 4, 5, 6, [7, 8, 9], 0); | |
296 | }); | |
297 | ||
298 | it('should copy a region on type "copy"', function () { | |
299 | display.copyImage = sinon.spy(); | |
300 | display.renderQ_push({ type: 'copy', x: 3, y: 4, width: 5, height: 6, old_x: 7, old_y: 8 }); | |
301 | expect(display.copyImage).to.have.been.calledOnce; | |
302 | expect(display.copyImage).to.have.been.calledWith(7, 8, 3, 4, 5, 6); | |
303 | }); | |
304 | ||
305 | it('should fill a rect with a given color on type "fill"', function () { | |
306 | display.fillRect = sinon.spy(); | |
307 | display.renderQ_push({ type: 'fill', x: 3, y: 4, width: 5, height: 6, color: [7, 8, 9]}); | |
308 | expect(display.fillRect).to.have.been.calledOnce; | |
309 | expect(display.fillRect).to.have.been.calledWith(3, 4, 5, 6, [7, 8, 9]); | |
310 | }); | |
311 | ||
312 | it('should draw an image from an image object on type "img" (if complete)', function () { | |
313 | display.drawImage = sinon.spy(); | |
314 | display.renderQ_push({ type: 'img', x: 3, y: 4, img: { complete: true } }); | |
315 | expect(display.drawImage).to.have.been.calledOnce; | |
316 | expect(display.drawImage).to.have.been.calledWith({ complete: true }, 3, 4); | |
317 | }); | |
318 | }); | |
319 | }); |