var chip8;
(function (chip8) {
'use strict';
/**
* Pixel size (square pixel side size)
* @type {number}
* @private
*/
var PIXEL_SIZE = 10;
/**
* Constructs monochromatic screen instance. Handles chip8 output.
* This class should be treated as abstract class. It implements logic but does not perform any rendering.
* @param {number} cols - number of colums
* @param {number} rows - number of rows
* @param {object} [options] - additional options
* @class chip8.Screen
*/
function Screen(cols, rows, options) {
this.cols = cols;
this.rows = rows;
this.options = options || {};
this.pixelmap = null;
this.clear();
}
/**
* Toggles pixel at location (x, y).
* @param {number} x
* @param {number} y
* @returns {boolean} true if collision occurs, false otherwise.
* @function togglePixel
* @memberof chip8.Screen
* @instance
*/
Screen.prototype.togglePixel = function (x, y) {
var idx = x + y * this.cols,
collision;
collision = !!this.pixelmap[idx];
this.pixelmap[idx] ^= 1;
return collision;
};
/**
* Clears internal screen representation.
* @function clear
* @memberof chip8.Screen
* @instance
*/
Screen.prototype.clear = function () {
this.pixelmap = new Uint8Array(this.rows * this.cols);
};
/**
* Does repaint. Implement in subclass.
* @function repaint
* @memberof chip8.Screen
* @instance
*/
Screen.prototype.repaint = function () {
// override
};
/**
* Extends abstract screen. Extending object should implement *render* method and optionally *initialize* method
* which works as constructor.
* @param {object} obj - object which extends base functionality.
* @returns {function} new constructor.
* @function extend
* @memberof chip8.Screen
* @static
*/
Screen.extend = function (obj) {
var F = function () {
if (obj.initialize) {
obj.initialize.apply(this, arguments);
} else {
F.prototype.constructor.apply(this, arguments);
}
};
F.prototype = new Screen(null, null, {});
F.prototype.repaint = obj.repaint;
return F;
};
chip8.Screen = Screen;
chip8.CanvasScreen = Screen.extend(/** lends chip8.CanvasScreen.prototype */ {
/**
* Renders to canvas.
* @param {number} cols - number of colums
* @param {number} rows - number of rows
* @param {object} options - additional options
* @param {object} options.ctx - canvas context
* @param {boolean} [options.drawGrid] - true if pixel grid should be rendered (default: true)
* @param {string} [options.bgColor] - background color (default: #080808)
* @param {string} [options.fgColor] - foreground color (default: #FFF)
* @param {string} [options.gridColor] - grid color (default: #121212)
* @constructs chip8.CanvasScreen
* @extends chip8.Screen
*/
initialize: function (cols, rows, options) {
chip8.CanvasScreen.prototype.constructor.apply(this, arguments);
this.drawGrid = ((typeof options.drawGrid !== 'undefined') ? options.drawGrid : true);
this.bgColor = ((typeof options.bgColor !== 'undefined') ? options.bgColor : '#080808');
this.fgColor = ((typeof options.fgColor !== 'undefined') ? options.fgColor : '#FFF');
this.gridColor = ((typeof options.gridColor !== 'undefined') ? options.gridColor : '#121212');
this.ctx = options.ctx;
this.ctx.canvas.width = cols * PIXEL_SIZE;
this.ctx.canvas.height = rows * PIXEL_SIZE;
},
/**
* Repaints screen.
* @memberof chip8.CanvasScreen#
*/
repaint: function () {
var x, y, i, len;
// background
this.ctx.fillStyle = this.bgColor;
this.ctx.fillRect(0, 0, this.cols * PIXEL_SIZE, this.rows * PIXEL_SIZE);
// pixels
this.ctx.fillStyle = this.fgColor;
for (i = 0, len = this.rows * this.cols; i < len; ++i) {
if (this.pixelmap[i]) {
x = i % this.cols;
y = Math.floor(i / this.cols);
this.ctx.fillRect(x * PIXEL_SIZE, y * PIXEL_SIZE, PIXEL_SIZE, PIXEL_SIZE);
}
}
// grid
if (this.drawGrid) {
this.ctx.strokeStyle = this.gridColor;
for (i = 1; i < this.cols; ++i) {
this.ctx.beginPath();
this.ctx.moveTo(i * PIXEL_SIZE, 0);
this.ctx.lineTo(i * PIXEL_SIZE, this.rows * PIXEL_SIZE);
this.ctx.stroke();
}
for (i = 1; i < this.rows; ++i) {
this.ctx.beginPath();
this.ctx.moveTo(0, i * PIXEL_SIZE);
this.ctx.lineTo(this.cols * PIXEL_SIZE, i * PIXEL_SIZE);
this.ctx.stroke();
}
}
}
});
})(chip8 || (chip8 = {}));