0 | |
/*
|
1 | |
|
2 | |
60 fps is 16.666 msec per frame.
|
3 | |
50 fps is 20 msec per frame.
|
4 | |
30 fps is 33.333 msec per frame.
|
5 | |
|
6 | |
Trying to draw all 320x200 pixels per frame takes about 360ms per frame
|
7 | |
(on Firefox 19 on Ubuntu 12.04, 2.1GHz x 2 laptop), at least by drawing
|
8 | |
them each as a rectangle, so that's out. But dividing the screen up into
|
9 | |
"programmable" characters ought to be much faster.
|
10 | |
|
11 | |
Indeed, if they are just 8x8 pixels of a solid colour, the entire 40x25
|
12 | |
screen can be updated in typ. less than 16 msec, meaning it can support
|
13 | |
a frame rate of 60 fps.
|
14 | |
|
15 | |
If each also has a character blitted onto it, the screen can be updated
|
16 | |
in typ. 26 msec, which is not quite inside 50 fps, but comfortably inside
|
17 | |
30 fps.
|
18 | |
|
19 | |
Not scaling while blitting the character seem to make it slightly faster
|
20 | |
(21 msec) but this is with the same 8x8 pixel source. With a 16x16 pixel
|
21 | |
source, it's comparable (23 msec?), so it's not all in the scaling. (Also,
|
22 | |
scaling anti-aliases the edges, which looks slightly un-retro.)
|
23 | |
|
24 | |
(Also these measurements are being taken at 5 fps, which seems to make
|
25 | |
the update routine take more time, Omaha knows why.)
|
26 | |
|
27 | |
I'm sure we could add some (16?) larger (64x64?) sprites onto here with
|
28 | |
few problems, especially if we're OK with 30fps.
|
29 | |
|
30 | |
To support 8 foreground colours, we copy the original charset bitmap
|
31 | |
(black) into one of 8 hidden canvases, and use canvas pixel operations
|
32 | |
to copy it into each of the other 7, using a different destination colour
|
33 | |
in each.
|
34 | |
|
35 | |
*/
|
36 | |
|
37 | |
RetroBitMap = function() {
|
38 | |
var canvas;
|
39 | |
var ctx;
|
40 | |
var intervalId;
|
41 | |
var scaleX = 2;
|
42 | |
var scaleY = 2;
|
43 | |
var charWidth = 8;
|
44 | |
var charHeight = 8;
|
45 | |
var srcCharsPerRow = 8;
|
46 | |
|
47 | |
var img = new Image();
|
48 | |
|
49 | |
var colors = [
|
50 | |
"#000000",
|
51 | |
"#0000ff",
|
52 | |
"#00ff00",
|
53 | |
"#00ffff",
|
54 | |
"#ff0000",
|
55 | |
"#ff00ff",
|
56 | |
"#ffff00",
|
57 | |
"#ffffff"
|
58 | |
];
|
59 | |
|
60 | |
var colorTriples = [
|
61 | |
[0, 0, 0],
|
62 | |
[0, 0, 255],
|
63 | |
[0, 255, 0],
|
64 | |
[0, 255, 255],
|
65 | |
[255, 0, 0],
|
66 | |
[255, 0, 255],
|
67 | |
[255, 255, 0],
|
68 | |
[255, 255, 255]
|
69 | |
];
|
70 | |
|
71 | |
var bgColorMemory = new Array();
|
72 | |
var fgColorMemory = new Array();
|
73 | |
var characterMemory = new Array();
|
74 | |
var chargen = new Array();
|
75 | |
var width = 40;
|
76 | |
var height = 25;
|
77 | |
this.bgColor = 0;
|
78 | |
this.fgColor = 0;
|
79 | |
|
80 | |
this.setBgColor = function(c) {
|
81 | |
this.bgColor = c;
|
82 | |
};
|
83 | |
|
84 | |
this.setFgColor = function(c) {
|
85 | |
this.fgColor = c;
|
86 | |
};
|
87 | |
|
88 | |
this.plot = function(x, y, charnum) {
|
89 | |
fgColorMemory[x + y * width] = this.fgColor;
|
90 | |
bgColorMemory[x + y * width] = this.bgColor;
|
91 | |
characterMemory[x + y * width] = charnum;
|
92 | |
};
|
93 | |
|
94 | |
this.drawFrame = function() {
|
95 | |
var status = document.getElementById('status');
|
96 | |
var start = new Date().getTime();
|
97 | |
var c;
|
98 | |
for (var x = 0; x < width; x++) {
|
99 | |
for (var y = 0; y < height; y++) {
|
100 | |
ctx.fillStyle = colors[bgColorMemory[x + y * width] || 0];
|
101 | |
ctx.fillRect(x * charWidth * scaleX, y * charHeight * scaleY,
|
102 | |
charWidth * scaleX, charHeight * scaleY);
|
103 | |
/* blit character image */
|
104 | |
var charNum = characterMemory[x + y * width] || 0;
|
105 | |
var srcCharX = (charNum % srcCharsPerRow) * charWidth;
|
106 | |
var srcCharY = Math.floor(charNum / srcCharsPerRow) * charHeight;
|
107 | |
ctx.drawImage(chargen[fgColorMemory[x + y * width] || 0],
|
108 | |
/* source */ srcCharX, srcCharY, charWidth, charHeight,
|
109 | |
/* dest */ x * charWidth * scaleX, y * charHeight * scaleY,
|
110 | |
charWidth * scaleX, charHeight * scaleY);
|
111 | |
}
|
112 | |
}
|
113 | |
var middle = new Date().getTime();
|
114 | |
for (var x = 0; x < width; x++) {
|
115 | |
for (var y = 0; y < height; y++) {
|
116 | |
var bgColorNum = Math.floor(Math.random() * colors.length);
|
117 | |
var fgColorNum = Math.floor(Math.random() * colors.length);
|
118 | |
var charNum = Math.floor(Math.random() * srcCharsPerRow);
|
119 | |
this.setBgColor(bgColorNum);
|
120 | |
this.setFgColor(fgColorNum);
|
121 | |
this.plot(x, y, charNum);
|
122 | |
}
|
123 | |
}
|
124 | |
var lastly = new Date().getTime();
|
125 | |
status.innerHTML = 'draw: ' + (middle-start) + 'ms, update: ' + (lastly-middle) + 'ms';
|
126 | |
};
|
127 | |
|
128 | |
this.start = function(c) {
|
129 | |
canvas = c;
|
130 | |
ctx = canvas.getContext('2d');
|
131 | |
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
132 | |
var self = this;
|
133 | |
var fps = 50;
|
134 | |
canvas = c;
|
135 | |
var self = this;
|
136 | |
img.onload = function() {
|
137 | |
self.createColoredCharsets();
|
138 | |
intervalId = setInterval(function() { self.drawFrame(); }, 1000/fps);
|
139 | |
}
|
140 | |
img.src = 'charset8.png';
|
141 | |
};
|
142 | |
|
143 | |
this.createColoredCharsets = function() {
|
144 | |
var charset_0 = document.getElementById('charset_0');
|
145 | |
var charset_0_ctx = charset_0.getContext('2d');
|
146 | |
chargen[0] = charset_0;
|
147 | |
charset_0_ctx.drawImage(img, 0, 0, img.width, img.height);
|
148 | |
var imageData = charset_0_ctx.getImageData(0, 0, img.width, img.height);
|
149 | |
var w = imageData.width;
|
150 | |
var h = imageData.height;
|
151 | |
for (var color = 1; color < 8; color++) {
|
152 | |
var charset = document.getElementById('charset_' + color);
|
153 | |
chargen[color] = charset;
|
154 | |
var charset_ctx = charset.getContext('2d');
|
155 | |
var newData = charset_0_ctx.getImageData(0, 0, img.width, img.height);
|
156 | |
for (var y = 0; y < h; y++) {
|
157 | |
for (var x = 0; x < w; x++) {
|
158 | |
var index = (y * w + x) * 4;
|
159 | |
var red = imageData.data[index];
|
160 | |
var green = imageData.data[index + 1];
|
161 | |
var blue = imageData.data[index + 2];
|
162 | |
var alpha = imageData.data[index + 3];
|
163 | |
newData.data[index] = colorTriples[color][0];
|
164 | |
newData.data[index + 1] = colorTriples[color][1];
|
165 | |
newData.data[index + 2] = colorTriples[color][2];
|
166 | |
newData.data[index + 3] = alpha;
|
167 | |
}
|
168 | |
}
|
169 | |
charset_ctx.putImageData(newData, 0, 0);
|
170 | |
}
|
171 | |
};
|
172 | |
};
|