UNPKG

pixelbutler

Version:

Low-res bitmap render engine for big screens

317 lines (316 loc) 11.6 kB
'use strict'; var RGBA = require('./RGBA'); var microFont = require('../font/Micro'); var color = require('./color'); var util = require('./util'); var clamp = util.clamp; var alpha = new RGBA(0, 0, 0, 0); var black = new RGBA(0, 0, 0); var magenta = new RGBA(255, 0, 255); var Bitmap = (function () { function Bitmap(width, height, useAlpha, buffer) { if (typeof useAlpha === "undefined") { useAlpha = false; } if (typeof buffer === "undefined") { buffer = null; } this.width = width; this.height = height; this.useAlpha = useAlpha; this.channels = (useAlpha ? 4 : 3); if (buffer) { var total = (this.width * this.height * this.channels); if (buffer.byteLength !== total) { throw new Error('bad raw data dimensions; expected ' + total + ', received ' + buffer.byteLength); } this.buffer = buffer; this.data = new Uint8ClampedArray(this.buffer); } else { this._resetData(); } } Bitmap.prototype._resetData = function () { this.buffer = new ArrayBuffer(this.width * this.height * this.channels); this.data = new Uint8ClampedArray(this.buffer); }; Bitmap.prototype.resizeTo = function (width, height) { if (width === this.width && height === this.height) { return; } this.width = width; this.height = height; this._resetData(); }; Bitmap.prototype.setPixel = function (x, y, col) { x = Math.floor(x); y = Math.floor(y); if (x < 0 || y < 0 || x >= this.width || y >= this.height) { return; } var p = (x + y * this.width) * this.channels; this.data[p] = col.r; this.data[p + 1] = col.g; this.data[p + 2] = col.b; }; Bitmap.prototype.getPixel = function (x, y, col) { x = Math.floor(x); y = Math.floor(y); if (x < 0 || y < 0 || x >= this.width || y >= this.height) { return null; } col = (col || new RGBA()); var p = (x + y * this.width) * this.channels; col.r = this.data[p]; col.g = this.data[p + 1]; col.b = this.data[p + 2]; return col; }; Bitmap.prototype.fillRect = function (x, y, width, height, col) { x = Math.floor(x); y = Math.floor(y); width = Math.floor(width); height = Math.floor(height); if (x >= this.width || y >= this.height || x + width < 0 || y + height < 0) { return; } var left = x; var right = x + width; var top = y; var bottom = y + height; if (left < 0) { left = 0; } if (top < 0) { top = 0; } if (right >= this.width) { right = this.width; } if (bottom >= this.height) { bottom = this.height; } for (var iy = top; iy < bottom; iy++) { for (var ix = left; ix < right; ix++) { var write = (ix + iy * this.width) * this.channels; this.data[write] = col.r; this.data[write + 1] = col.g; this.data[write + 2] = col.b; } } }; Bitmap.prototype.drawLineH = function (x, y, size, col) { var right = clamp(Math.floor(x + size), 0, this.width); x = clamp(Math.floor(x), 0, this.width); y = clamp(Math.floor(y), 0, this.height); for (; x < right; x++) { var write = (x + y * this.width) * this.channels; this.data[write] = col.r; this.data[write + 1] = col.g; this.data[write + 2] = col.b; } }; Bitmap.prototype.drawLineV = function (x, y, size, col) { var bottom = clamp(Math.floor(y + size), 0, this.height); x = clamp(Math.floor(x), 0, this.width); y = clamp(Math.floor(y), 0, this.height); for (; y < bottom; y++) { var write = (x + y * this.width) * this.channels; this.data[write] = col.r; this.data[write + 1] = col.g; this.data[write + 2] = col.b; } }; Bitmap.prototype.drawRect = function (x, y, width, height, col) { x = Math.floor(x); y = Math.floor(y); width = Math.floor(width); height = Math.floor(height); this.drawLineH(x, y, width, col); this.drawLineH(x, y + height - 1, width, col); this.drawLineV(x, y, height, col); this.drawLineV(x + width - 1, y, height, col); }; Bitmap.prototype.fillCircle = function (x, y, r, col) { x = Math.floor(x); y = Math.floor(y); r = Math.floor(r); for (var iy = -r; iy <= r; iy++) { for (var ix = -r; ix <= r; ix++) { if (x + ix < 0 || y + iy < 0 || x + ix >= this.width || y + iy >= this.height) { continue; } if (ix * ix + iy * iy <= r * r) { var write = (x + ix + (y + iy) * this.width) * this.channels; this.data[write] = col.r; this.data[write + 1] = col.g; this.data[write + 2] = col.b; } } } }; Bitmap.prototype.drawCircle = function (x, y, r, col) { x = Math.floor(x); y = Math.floor(y); r = Math.floor(r); for (var i = 0; i < 360; i++) { var cx = Math.round(Math.cos(i * (Math.PI / 180)) * r) + x; var cy = Math.round(Math.sin(i * (Math.PI / 180)) * r) + y; if (cx < 0 || cy < 0 || cx >= this.width || cy >= this.height) { continue; } var write = (cx + cy * this.width) * this.channels; this.data[write] = col.r; this.data[write + 1] = col.g; this.data[write + 2] = col.b; } }; Bitmap.prototype.shader = function (f) { var iy; var ix; var col; var rgb = new RGBA(); for (iy = 0; iy < this.height; iy++) { for (ix = 0; ix < this.width; ix++) { var index = (ix + iy * this.width) * this.channels; rgb.r = this.data[index]; rgb.g = this.data[index + 1]; rgb.b = this.data[index + 2]; col = f(ix, iy, rgb); this.data[index] = col.r; this.data[index + 1] = col.g; this.data[index + 2] = col.b; } } }; Bitmap.prototype.text = function (x, y, txt, col) { txt = String(txt); for (var i = 0; i < txt.length; i++) { x += this.drawChar(x, y, txt.charAt(i), col) + 1; } }; Bitmap.prototype.drawChar = function (x, y, chr, col) { var char = microFont.chars[chr.toUpperCase()]; if (!char) { return 0; } for (var iy = 0; iy < microFont.height; iy++) { for (var ix = 0; ix < char.width; ix++) { if (char.map[iy * char.width + ix]) { this.setPixel(x + ix, y + iy, col); } } } return char.width; }; Bitmap.prototype.blit = function (sprite, x, y) { x = (x ? Math.floor(x) : 0); y = (y ? Math.floor(y) : 0); var iy; var ix; var read; var write; if (x >= this.width || y >= this.height || x + sprite.width < 0 || y + sprite.height < 0) { return; } var left = x; var right = x + sprite.width; var top = y; var bottom = y + sprite.height; if (left < 0) { left = 0; } if (top < 0) { top = 0; } if (right >= this.width) { right = this.width; } if (bottom >= this.height) { bottom = this.height; } if (sprite.useAlpha) { for (iy = top; iy < bottom; iy++) { for (ix = left; ix < right; ix++) { read = (ix - x + (iy - y) * sprite.width) * sprite.channels; write = (ix + iy * this.width) * this.channels; var alpha = sprite.data[read + 3] / 255; var inv = 1 - alpha; this.data[write] = Math.round(this.data[write] * inv + sprite.data[read] * alpha); this.data[write + 1] = Math.round(this.data[write + 1] * inv + sprite.data[read + 1] * alpha); this.data[write + 2] = Math.round(this.data[write + 2] * inv + sprite.data[read + 2] * alpha); } } } else { for (iy = top; iy < bottom; iy++) { for (ix = left; ix < right; ix++) { read = (ix - x + (iy - y) * sprite.width) * sprite.channels; write = (ix + iy * this.width) * this.channels; this.data[write] = sprite.data[read]; this.data[write + 1] = sprite.data[read + 1]; this.data[write + 2] = sprite.data[read + 2]; } } } }; Bitmap.prototype.clear = function (col) { col = col || black; var lim; var i; if (this.useAlpha && color.useAlpha(col)) { lim = this.width * this.height * 4; for (i = 0; i < lim; i += 4) { this.data[i] = col.r; this.data[i + 1] = col.g; this.data[i + 2] = col.b; this.data[i + 3] = col.a; } } else { lim = this.width * this.height * this.channels; for (i = 0; i < lim; i += this.channels) { this.data[i] = col.r; this.data[i + 1] = col.g; this.data[i + 2] = col.b; } } }; Bitmap.prototype.clearAlpha = function (alpha) { if (typeof alpha === "undefined") { alpha = 0; } if (!this.useAlpha) { return; } var lim = this.width * this.height * 4; for (var i = 3; i < lim; i += 4) { this.data[i] = alpha; } }; Bitmap.clipFromData = function (inputData, inputWidth, inputHeight, inputChannels, x, y, width, height, useAlpha) { var channels = useAlpha ? 4 : 3; var data = new Uint8Array(height * width * channels); var iy; var ix; var read; var write; if (useAlpha) { for (iy = 0; iy < height; iy++) { for (ix = 0; ix < width; ix++) { read = (ix + x + (iy + y) * inputWidth) * inputChannels; write = (ix + iy * width) * channels; data[write] = inputData[read]; data[write + 1] = inputData[read + 1]; data[write + 2] = inputData[read + 2]; data[write + 3] = inputData[read + 3]; } } } else { for (iy = 0; iy < height; iy++) { for (ix = 0; ix < width; ix++) { read = (ix + x + (iy + y) * inputWidth) * inputChannels; write = (ix + iy * width) * channels; data[write] = inputData[read]; data[write + 1] = inputData[read + 1]; data[write + 2] = inputData[read + 2]; } } } return new Bitmap(width, height, useAlpha, data); }; return Bitmap; })(); module.exports = Bitmap;