commit 5a32b7b0258226cd5459b7d59357ddbabab0d9bd
parent e88e049576327c5e591ca16f06587f2d09630df9
Author: Andy Herbert <andy.herbert@gmail.com>
Date: Thu, 31 Oct 2013 22:32:47 +0000
Substantial changes to the font rendering routines to limit putImageData() calls to an absolute minimum.
Diffstat:
M | ansilove.js | | | 130 | +++++++++++++++++++++++++++++++++++++++---------------------------------------- |
1 file changed, 64 insertions(+), 66 deletions(-)
diff --git a/ansilove.js b/ansilove.js
@@ -245,13 +245,13 @@ var AnsiLove = (function () {
};
function read(file, width, height, fontSize, amigaFont) {
- var bits, fontBitWidth, canvas, imageData, fontBuffer;
+ var bits, fontBitWidth, imageData, fontBuffer, fontBuffer24Bit;
bits = new Uint8Array(width * height * fontSize);
fontBitWidth = width * height;
- canvas = createCanvas(width, height);
- imageData = canvas.getContext("2d").getImageData(0, 0, width, height);
fontBuffer = [];
+ fontBuffer24Bit = new Uint8Array(width * height * 4);
+ imageData = createCanvas(width, height).getContext("2d").getImageData(0, 0, width, height);
(function () {
var i, j, k, v;
@@ -263,7 +263,7 @@ var AnsiLove = (function () {
}
}());
- function draw(ctx, x, y, charCode, palette, fg, bg) {
+ function getData(charCode, palette, fg, bg) {
var i, j, k, bufferIndex;
bufferIndex = charCode + (fg << 8) + (bg << 12);
@@ -281,46 +281,32 @@ var AnsiLove = (function () {
}
}
}
- imageData.data.set(fontBuffer[bufferIndex], 0);
- ctx.putImageData(imageData, x * width, y * height, 0, 0, width, height);
+ return fontBuffer[bufferIndex];
}
- function draw24Bit(ctx, x, y, charCode, fg, bg) {
+ function get24BitData(charCode, fg, bg) {
var i, j, k;
for (i = 0, j = charCode * fontBitWidth, k = 0; i < fontBitWidth; ++i, ++j, k += 4) {
if (bits[j]) {
- imageData.data.set(fg, k);
+ fontBuffer24Bit.set(fg, k);
} else {
if (amigaFont && (fg > 7) && (i > 0) && bits[j - 1]) {
- imageData.data.set(fg, k);
+ fontBuffer24Bit.set(fg, k);
} else {
- imageData.data.set(bg, k);
+ fontBuffer24Bit.set(bg, k);
}
}
}
- ctx.putImageData(imageData, x * width, y * height, 0, 0, width, height);
- }
-
- function getHeight() {
- return height;
- }
-
- function getWidth() {
- return width;
- }
-
- function setWidth(newWidth) {
- width = newWidth;
+ return fontBuffer24Bit;
}
return {
- "draw": draw,
- "draw24Bit": draw24Bit,
+ "getData": getData,
+ "get24BitData": get24BitData,
"fontSize": fontSize,
- "getHeight": getHeight,
- "getWidth" : getWidth,
- "setWidth": setWidth
+ "height": height,
+ "width" : width
};
}
@@ -569,39 +555,47 @@ var AnsiLove = (function () {
}
function display(raw, start, length, altFont, options) {
- var canvas, font, end, ctx, i, j, x, y, fg, bg, chunky;
+ var canvas, font, end, ctx, i, j, k, l, x, chunky, fontBitWidth, fontDisplayWidth, displayFontBitWidth, imageData, fontData, rowOffset, screenOffset, fontOffset;
font = raw.font || altFont || Font.preset("80x25");
- if (font.getWidth() === 9 && (options.bits !== "9" || options.thumbnail)) {
- font.setWidth(8);
- }
+ fontDisplayWidth = (font.width === 9 && (options.bits !== "9" || options.thumbnail)) ? 8 : font.width;
+
+ fontBitWidth = font.width * 4;
+ displayFontBitWidth = fontDisplayWidth * 4;
end = Math.min(start + length, raw.imageData.length);
- canvas = createCanvas(raw.width * font.getWidth(), (end - start) / raw.rowLength * font.getHeight());
+ canvas = createCanvas(raw.width * fontDisplayWidth, (end - start) / raw.rowLength * font.height);
ctx = canvas.getContext("2d");
+ imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
+ rowOffset = canvas.width * 4;
+
if (raw.palette) {
- for (i = start, j = x = y = 0; i < end; i += 2) {
- fg = raw.imageData[i + 1] & 15;
- bg = raw.imageData[i + 1] >> 4;
- font.draw(ctx, x++, y, raw.imageData[i], raw.palette, fg, bg);
- if (x % raw.width === 0) {
- x = 0;
- ++y;
+ for (i = start, screenOffset = 0, j = x = 0; i < end; i += 2, screenOffset += displayFontBitWidth) {
+ fontData = font.getData(raw.imageData[i], raw.palette, raw.imageData[i + 1] & 15, raw.imageData[i + 1] >> 4);
+ for (fontOffset = screenOffset, k = l = 0; k < font.height; ++k, fontOffset += rowOffset, l += fontBitWidth) {
+ imageData.data.set(fontData.subarray(l, l + displayFontBitWidth), fontOffset);
+ }
+ if (++x % raw.width === 0) {
+ screenOffset += (font.height - 1) * rowOffset;
}
}
} else {
- for (i = start, j = x = y = 0; i < end; i += 9) {
- font.draw24Bit(ctx, x++, y, raw.imageData[i], raw.imageData.subarray(i + 1, i + 5), raw.imageData.subarray(i + 5, i + 9));
- if (x % raw.width === 0) {
- x = 0;
- ++y;
+ for (i = start, screenOffset = 0, j = x = 0; i < end; i += 9, screenOffset += displayFontBitWidth) {
+ fontData = font.get24BitData(raw.imageData[i], raw.imageData.subarray(i + 1, i + 5), raw.imageData.subarray(i + 5, i + 9));
+ for (fontOffset = screenOffset, k = l = 0; k < font.height; ++k, fontOffset += rowOffset, l += fontBitWidth) {
+ imageData.data.set(fontData.subarray(l, l + displayFontBitWidth), fontOffset);
+ }
+ if (++x % raw.width === 0) {
+ screenOffset += (font.height - 1) * rowOffset;
}
}
}
+ ctx.putImageData(imageData, 0, 0);
+
if (options.thumbnail) {
chunky = Math.pow(2, 4 - options.thumbnail);
canvas = scaleCanvas(canvas, chunky, chunky);
@@ -1293,11 +1287,11 @@ var AnsiLove = (function () {
}
function Ansimation(bytes, options) {
- var timer, interval, file, font, icecolors, bits, palette, columns, rows, screenClear, canvas, ctx, blinkCanvas, buffer, bufferCtx, blinkCtx, escaped, escapeCode, j, code, values, x, y, savedX, savedY, foreground, background, drawForeground, drawBackground, bold, inverse, blink, characterWidth, characterHeight;
+ var timer, interval, file, font, fontDisplayWidth, icecolors, bits, palette, columns, rows, screenClear, canvas, ctx, blinkCanvas, buffer, bufferCtx, blinkCtx, escaped, escapeCode, j, code, values, x, y, savedX, savedY, foreground, background, drawForeground, drawBackground, bold, inverse, blink, fontImageData;
file = new File(bytes);
icecolors = (options.icecolors === undefined) ? false : (options.icecolors === 1);
- bits = options.bits || 8;
+ bits = options.bits || "8";
screenClear = (options["2J"] === undefined) ? true : (options["2J"] === 1);
switch (bits) {
@@ -1323,15 +1317,11 @@ var AnsiLove = (function () {
font = Font.has(options.font) ? Font.preset(options.font) : Font.preset("80x25");
- if (font.getWidth() === 9 && bits !== "9") {
- font.setWidth(8);
- }
-
- characterWidth = font.getWidth();
- characterHeight = font.getHeight();
+ fontDisplayWidth = (font.width === 9 && bits !== "9") ? 8 : font.width;
- canvas = createCanvas(columns * font.getWidth(), rows * font.getHeight());
+ canvas = createCanvas(columns * fontDisplayWidth, rows * font.height);
ctx = canvas.getContext("2d");
+ fontImageData = ctx.createImageData(font.width, font.height);
blinkCanvas = [createCanvas(canvas.width, canvas.height), createCanvas(canvas.width, canvas.height)];
buffer = createCanvas(canvas.width, canvas.height);
@@ -1357,25 +1347,25 @@ var AnsiLove = (function () {
function clearBlinkChar(charX, charY) {
var sx, sy;
- sx = charX * characterWidth;
- sy = charY * characterHeight;
- blinkCtx[0].clearRect(sx, sy, characterWidth, characterHeight);
- blinkCtx[1].clearRect(sx, sy, characterWidth, characterHeight);
+ sx = charX * fontDisplayWidth;
+ sy = charY * font.height;
+ blinkCtx[0].clearRect(sx, sy, fontDisplayWidth, font.height);
+ blinkCtx[1].clearRect(sx, sy, fontDisplayWidth, font.height);
}
function newLine() {
x = 1;
if (y === rows - 1) {
- ctx.drawImage(canvas, 0, characterHeight, canvas.width, canvas.height - characterHeight * 2, 0, 0, canvas.width, canvas.height - characterHeight * 2);
+ ctx.drawImage(canvas, 0, font.height, canvas.width, canvas.height - font.height * 2, 0, 0, canvas.width, canvas.height - font.height * 2);
bufferCtx.clearRect(0, 0, canvas.width, canvas.height);
- bufferCtx.drawImage(blinkCanvas[0], 0, characterHeight, canvas.width, canvas.height - characterHeight * 2, 0, 0, canvas.width, canvas.height - characterHeight * 2);
+ bufferCtx.drawImage(blinkCanvas[0], 0, font.height, canvas.width, canvas.height - font.height * 2, 0, 0, canvas.width, canvas.height - font.height * 2);
blinkCtx[0].clearRect(0, 0, canvas.width, canvas.height);
blinkCtx[0].drawImage(buffer, 0, 0);
bufferCtx.clearRect(0, 0, canvas.width, canvas.height);
- bufferCtx.drawImage(blinkCanvas[1], 0, characterHeight, canvas.width, canvas.height - characterHeight * 2, 0, 0, canvas.width, canvas.height - characterHeight * 2);
+ bufferCtx.drawImage(blinkCanvas[1], 0, font.height, canvas.width, canvas.height - font.height * 2, 0, 0, canvas.width, canvas.height - font.height * 2);
blinkCtx[1].clearRect(0, 0, canvas.width, canvas.height);
blinkCtx[1].drawImage(buffer, 0, 0);
- clearScreen(0, canvas.height - characterHeight * 2, canvas.width, characterHeight);
+ clearScreen(0, canvas.height - font.height * 2, canvas.width, font.height);
return true;
}
++y;
@@ -1452,7 +1442,7 @@ var AnsiLove = (function () {
}
break;
case "K":
- clearScreen((x - 1) * characterWidth, (y - 1) * characterHeight, canvas.width - (x - 1) * characterWidth, characterHeight);
+ clearScreen((x - 1) * fontDisplayWidth, (y - 1) * font.height, canvas.width - (x - 1) * fontDisplayWidth, font.height);
break;
case "m":
for (j = 0; j < values.length; ++j) {
@@ -1533,15 +1523,23 @@ var AnsiLove = (function () {
drawForeground = foreground;
drawBackground = background;
}
+ if (bold) {
+ drawForeground += 8;
+ }
+ if (blink && icecolors) {
+ drawBackground += 8;
+ }
+ fontImageData.data.set(font.getData(code, palette, drawForeground, drawBackground), 0);
+ ctx.putImageData(fontImageData, (x - 1) * fontDisplayWidth, (y - 1) * font.height, 0, 0, fontDisplayWidth, font.height);
if (!icecolors) {
if (blink) {
- font.draw(blinkCtx[0], x - 1, y - 1, code, palette, bold ? (drawForeground + 8) : drawForeground, drawBackground);
- font.draw(blinkCtx[1], x - 1, y - 1, code, palette, drawBackground, drawBackground);
+ blinkCtx[0].putImageData(fontImageData, (x - 1) * fontDisplayWidth, (y - 1) * font.height, 0, 0, fontDisplayWidth, font.height);
+ fontImageData.data.set(font.getData(code, palette, drawBackground, drawBackground), 0);
+ blinkCtx[1].putImageData(fontImageData, (x - 1) * fontDisplayWidth, (y - 1) * font.height, 0, 0, fontDisplayWidth, font.height);
} else {
clearBlinkChar(x - 1, y - 1);
}
}
- font.draw(ctx, x - 1, y - 1, code, palette, bold ? (drawForeground + 8) : drawForeground, (blink && icecolors) ? (drawBackground + 8) : drawBackground);
if (++x === columns + 1) {
if (newLine()) {
return i + 1;