git @ Cat's Eye Technologies yoob.js / master src / yoob / chargen.js
master

Tree @master (Download .tar.gz)

chargen.js @masterraw · history · blame

/*
 * This file is part of yoob.js version 0.10
 * Available from https://github.com/catseye/yoob.js/
 * This file is in the public domain.  See http://unlicense.org/ for details.
 */
if (window.yoob === undefined) yoob = {};

/*
 * yoob.Chargen is an object for producing bitmapped-character-like displays,
 * such as those found on 8-bit home computers of olde.  A monochromatic image,
 * with the character patterns in a regular grid, must be supplied.  Chromatic
 * versions of the character patterns, in each of the given colours, will be
 * automatically created.
 *
 * The character data may also be modified programatically.
 *
 * There may be better ways to accomplish this feat -- perhaps far, far better
 * ways -- but this aims to be reasonable and good-enough-for-now.
 */
yoob.Chargen = function() {
    this.init = function(cfg) {
        this.colorTriples = cfg.colorTriples || [[0, 0, 0], [255, 255, 255]];
        this.charWidth = cfg.charWidth || 8;
        this.charHeight = cfg.charHeight || 8;
        this.charsPerRow = cfg.charsPerRow || 16;
        this.rows = cfg.rows || 16;
        this.colorToAlpha = cfg.colorToAlpha;
        this.bitmaps = [];  // canvas elements

        for (var i = 0; i < this.colorTriples.length; i++) {
            var canvas = document.createElement("canvas");
            canvas.width = this.charWidth * this.charsPerRow;
            canvas.height = this.charHeight * this.rows;
            this.bitmaps.push(canvas);
        }

        this.img = new Image();
        var $this = this;
        this.img.onload = function() {
            $this.reset();
            if (cfg.onLoad) cfg.onLoad();
        }
        this.img.src = cfg.imageSrc;
        return this;
    };

    /*
     * Copy the image of a character from this Chargen into a given
     * drawing context.  x and y are canvas positions of the destination.
     */
    this.blitChar = function(charNum, charColor, ctx, x, y,
                             destWidth, destHeight) {
        destWidth = destWidth || this.charWidth;
        destHeight = destHeight || this.charHeight;
        var srcCharX = (charNum % this.charsPerRow) * this.charWidth;
        var srcCharY = Math.floor(charNum / this.charsPerRow) * this.charHeight;
        ctx.drawImage(this.bitmaps[charColor],
          /* source */ srcCharX, srcCharY, this.charWidth, this.charHeight,
          /* dest   */ x, y, destWidth, destHeight);
    };

    this.reset = function() {
        var ctx = this.bitmaps[0].getContext('2d');
        var w = this.img.width;
        var h = this.img.height;
        ctx.clearRect(0, 0, w, h);
        ctx.drawImage(this.img, 0, 0, w, h);
        this._createChromaticBitmaps();
    };

    /*
     * Probably you will never need to call this directly.
     */
    this._createChromaticBitmaps = function() {
        var ctx = this.bitmaps[0].getContext('2d');
        var w = this.img.width;
        var h = this.img.height;
        var imageData = ctx.getImageData(0, 0, w, h);
        for (var color = 0; color < this.colorTriples.length; color++) {
            var newData = ctx.getImageData(0, 0, w, h);
            for (var y = 0; y < h; y++) {
                for (var x = 0; x < w; x++) {
                    var index = (y * w + x) * 4;
                    var red = imageData.data[index];
                    var green = imageData.data[index + 1];
                    var blue = imageData.data[index + 2];
                    var alpha = imageData.data[index + 3];
                    if (this.colorToAlpha &&
                        red === this.colorToAlpha[0] &&
                        green === this.colorToAlpha[1] &&
                        blue === this.colorToAlpha[2]) {
                        alpha = 0;
                    }
                    newData.data[index] = this.colorTriples[color][0];
                    newData.data[index + 1] = this.colorTriples[color][1];
                    newData.data[index + 2] = this.colorTriples[color][2];
                    newData.data[index + 3] = alpha;
                }
            }
            this.bitmaps[color].getContext('2d').putImageData(newData, 0, 0);
        }
    };

    this.modifyChar = function(charNum, bytes) {
        var w = this.charWidth;
        var h = this.charHeight;
        var srcCharX = (charNum % this.charsPerRow) * w;
        var srcCharY = Math.floor(charNum / this.charsPerRow) * h;
        var ctx = this.bitmaps[0].getContext('2d');
        var newData = ctx.createImageData(w, h);
        for (var y = 0; y < h; y++) {
            var byt = bytes[y];
            for (var x = w - 1; x >= 0; x--) {
                var index = (y * w + x) * 4;
                if (byt & 1 === 1) {
                    newData.data[index] = 0;
                    newData.data[index + 1] = 0;
                    newData.data[index + 2] = 0;
                    newData.data[index + 3] = 255;
                }
                byt >>= 1;
            }
        }
        ctx.clearRect(srcCharX, srcCharY, w, h);
        ctx.putImageData(newData, srcCharX, srcCharY);
        // FIXME this is such overkill!!!
        this._createChromaticBitmaps();
    };
};