Skip to content

Commit 3be5d32

Browse files
committed
Rewrite the Screen module to use ImageData instead of fillRect
1 parent b28c1d8 commit 3be5d32

File tree

2 files changed

+17
-42
lines changed

2 files changed

+17
-42
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,5 @@ Running Pong.hack at http://0.0.0.0:4225
2727

2828
This will launch a local web server on port 4225 ("HACK" on a phone
2929
keypad). Navigating to http://0.0.0.0:4225 in a reasonably recent
30-
browser will run the Hack program and display its output in a 256x512
30+
browser will run the Hack program and display its output in a 512x256
3131
HTML5 canvas element.

src/screen.js

+16-41
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
(function() {
2-
var X_MASK = (1 << 9) - 1,
3-
MSB_MASK = 1 << 15;
2+
var WIDTH = 512,
3+
HEIGHT = 256;
44

55
function Screen(elem) {
66
this.canvas = elem.getContext('2d');
7+
this.image = this.canvas.createImageData(WIDTH, HEIGHT);
78
this.store = {};
89
}
910

@@ -18,50 +19,24 @@
1819

1920
Screen.prototype.redrawPixels = function (addr, bits) {
2021
var offset = 16 * addr, // Each address corresponds to 16 horizontal pixels.
21-
x = offset & X_MASK, // Modulo: offset & (COLUMNS - 1) = offset % COLUMNS
22-
y = offset >> 9, // Integer division: offset >> log2(COLUMNS) == Math.floor(offset / COLUMNS)
23-
run = 1, // run will hold how many contiguous pixels with the same colour we've seen.
24-
prev, // prev will hold the colour of the previous pixel in the loop.
25-
colour; // colour will hold the current pixel's colour.
22+
idx = offset << 2, // Multiply offset by 4 because each pixel has RGBA components.
23+
colour;
2624

27-
// Initialise prev to be the colour corresponding to the MSB.
28-
// This way we can skip the first iteration of the loop below.
29-
prev = (bits & MSB_MASK) ? 'black' : 'white';
25+
// We need to loop over all 16 bits stored at addr.
26+
for (var i = 0; i < 16; i++) {
27+
// Examine the ith bit: true means black, false means white.
28+
colour = bits & (1 << i) ? 0 : 255;
3029

31-
// We need to loop over 16 bits, but as we've already peeked
32-
// at the first one, this loop can start from 15 instead.
33-
//
34-
// Note that this loop runs from the MSB (rightmost pixel)
35-
// to the LSB (leftmost pixel). Having a decrementing loop
36-
// with no bounds checking gives us a tiny speed boost, but
37-
// more importantly it makes our drawing code easier below.
38-
for (var i = 15; i--;) {
39-
colour = (bits & (1 << i)) ? 'black' : 'white';
30+
// Set the RGB components of the pixel.
31+
this.image.data[idx++] = colour;
32+
this.image.data[idx++] = colour;
33+
this.image.data[idx++] = colour;
4034

41-
// If this pixel's colour is the same as the previous pixel's
42-
// colour, increase the length of the current "run" and move
43-
// on to the next pixel before we do any drawing.
44-
if (prev == colour) {
45-
run++;
46-
continue;
47-
}
48-
49-
// If the run has ended, we need to draw the block of pixels
50-
// up to, but excluding, the current one. As we're iterating
51-
// from right to left, we can draw a rectangle starting at
52-
// the previous pixel (x + i + 1), with length run.
53-
this.canvas.fillStyle = prev;
54-
this.canvas.fillRect(x + i + 1, y, run, 1);
55-
56-
// Start a new run of length 1 for the current pixel's colour.
57-
prev = colour;
58-
run = 1;
35+
// Always set alpha to 255 (opaque).
36+
this.image.data[idx++] = 255;
5937
}
6038

61-
// Now that we've processed all the pixels, we can draw the last
62-
// (leftmost) run of pixels, which may have length from 1 to 16.
63-
this.canvas.fillStyle = colour;
64-
this.canvas.fillRect(x, y, run, 1);
39+
this.canvas.putImageData(this.image, 0, 0);
6540
};
6641

6742
window.Screen = Screen;

0 commit comments

Comments
 (0)