this may take a while, please be patient
// Utility code for DK converter. It can also draw normal pixel arts, at an optimized speed. [
// stores cached pixel art graphics buffers
var _pixelArtCache = {};
// is it an array?
function isArray(a) {
return Array.isArray(a);
}
// is it a PImage or similar object?
function isPImage(obj) {
if (!obj) { return false; }
try {
return (typeof obj.width === "number") || (typeof obj.get === "function");
} catch (e) {
return false;
}
}
// is it an RLE frame (array of strings)?
function isRLEFrame(frame) {
return isArray(frame) && frame.length > 0 && typeof frame[0] === "string";
}
// build a cache key for pixel art rendering
function buildCacheKey(data, frameIndex, pixelSize, colorMap) {
var prefix = "";
try {
prefix = JSON.stringify(data);
} catch (e) {
prefix = String(data);
}
var cmap = "";
try {
cmap = JSON.stringify(colorMap);
} catch (e2) {
cmap = String(colorMap);
}
return prefix + "|" + String(frameIndex) + "|" + String(pixelSize) + "|" + cmap;
}
// Clear the entire pixel art cache
function clearPixelArtCache() {
_pixelArtCache = {};
}
// Invalidate a specific pixel art cache entry
function invalidatePixelArtCacheFor(data, frameIndex, pixelSize, colorMap) {
var key = buildCacheKey(data, frameIndex, pixelSize, colorMap);
if (_pixelArtCache[key]) {
delete _pixelArtCache[key];
}
}
// Decode a single RLE row string into an array of characters
function decodeRLERow(row) {
var out = [];
if (!row) { return out; }
var i = 0;
var n = row.length;
while (i < n) {
var ch = row.charAt(i);
// skip digits (should not happen at this point)
if (ch >= '0' && ch <= '9') {
i++;
continue;
}
// advance past the char
i++;
// collect digits (if any) to form the count
var numStr = "";
while (i < n) {
var d = row.charAt(i);
if (d < '0' || d > '9') { break; }
numStr += d;
i++;
}
var count = numStr.length ? parseInt(numStr, 10) : 1;
for (var k = 0; k < count; k++) {
out.push(ch);
}
}
return out;
}
// Decode an RLE image (array of RLE row strings) into a 2D array of characters
function decodeRLEImage(lines) {
var pixels = [];
for (var y = 0; y < lines.length; y++) {
pixels[y] = decodeRLERow(lines[y] || "");
}
return pixels;
}
// Check if a frame has any visible color based on the color map
function frameHasVisibleColor(frameData, colorMap) {
if (!frameData) { return false; }
for (var i = 0; i < frameData.length; i++) {
var row = frameData[i];
if (!row) { continue; }
// check each character in the row
for (var k = 0; k < row.length; k++) {
var ch = row[k];
if (ch !== "-" && colorMap && colorMap[ch] !== undefined) {
return true;
}
}
}
return false;
}
// Draw decoded pixel data to a graphics buffer
function drawDecodedToGraphics(g, px, pixelSize, palette) {
// Attempt to disable smoothing to keep crisp edges
try {
// idk if this actually works in KA
if (typeof g.noSmooth === "function") { g.noSmooth(); }
} catch (e) { }
// ensure corner alignment
try {
if (typeof g.rectMode === "function") { g.rectMode(CORNER); }
} catch (e2) { }
g.noStroke();
for (var y = 0; y < px.length; y++) {
var row = px[y] || [];
var x = 0;
while (x < row.length) {
var ch = row[x];
var start = x;
x++;
while (x < row.length && row[x] === ch) { x++; }
var run = x - start;
if (ch === "-" || ch === null || ch === undefined) {
// transparent pixel, skip
continue;
}
var col = palette && palette[ch];
if (!col) {
// unmapped symbol, skip
continue;
}
// compute rectangle coordinates
var x1 = round(start * pixelSize);
var x2 = round((start + run) * pixelSize);
var y1 = round(y * pixelSize);
var y2 = round((y + 1) * pixelSize);
var gw = x2 - x1;
var gh = y2 - y1;
if (gw <= 0 || gh <= 0) { continue; }
if (isArray(col)) {
if (col.length === 4) {
g.fill(col[0], col[1], col[2], col[3]);
} else {
g.fill(col[0], col[1], col[2]);
}
} else {
g.fill(col);
}
g.rect(x1, y1, gw, gh);
}
}
}
// Main renderer
function drawHugePixelArt(startX, startY, pixelSize, canvasData, colorMap, frame) {
// tiny safety margin for buffer sizing, but not used for drawing
var sizeMargin = 1;
noStroke();
// IMAGE PATHS first
if (isArray(canvasData) && canvasData.length > 0 && isPImage(canvasData[0])) {
if (frame === undefined) { frame = 1; }
frame = Math.floor(frame);
var idx = ((frame - 1) % canvasData.length + canvasData.length) % canvasData.length;
var img = canvasData[idx];
if (!img) { return; }
var w = (typeof img.width === "number") ? img.width : (img.getWidth ? img.getWidth() : 0);
var h = (typeof img.height === "number") ? img.height : (img.getHeight ? img.getHeight() : 0);
image(img, startX, startY, round(w * pixelSize), round(h * pixelSize));
return;
}
if (isPImage(canvasData)) {
var w2 = (typeof canvasData.width === "number") ? canvasData.width : (canvasData.getWidth ? canvasData.getWidth() : 0);
var h2 = (typeof canvasData.height === "number") ? canvasData.height : (canvasData.getHeight ? canvasData.getHeight() : 0);
image(canvasData, startX, startY, round(w2 * pixelSize), round(h2 * pixelSize));
return;
}
// RLE PATH
var isFrames = isArray(canvasData) && canvasData.length > 0 && isRLEFrame(canvasData[0]);
var data;
var frameIndex = 0;
if (isFrames) {
if (frame === undefined) { frame = 1; }
frame = Math.floor(frame);
var idx2 = ((frame - 1) % canvasData.length + canvasData.length) % canvasData.length;
var attempts = 0;
var chosenIndex = idx2;
while (attempts < canvasData.length) {
var candidate = canvasData[chosenIndex];
if (frameHasVisibleColor(candidate, colorMap)) {
break;
}
chosenIndex = (chosenIndex - 1 + canvasData.length) % canvasData.length;
attempts++;
}
data = canvasData[chosenIndex];
frameIndex = chosenIndex;
} else {
if (!isArray(canvasData) || !isRLEFrame(canvasData)) {
return;
}
data = canvasData;
frameIndex = 0;
}
if (!data || data.length === 0) { return; }
var cacheKey = buildCacheKey(data, frameIndex, pixelSize, colorMap);
if (!_pixelArtCache[cacheKey]) {
// decode fully (2D char array)
var decoded = decodeRLEImage(data);
// compute max cols and rows
var maxCols = 0;
for (var r = 0; r < decoded.length; r++) {
var ln = decoded[r] ? decoded[r].length : 0;
if (ln > maxCols) { maxCols = ln; }
}
var rows = decoded.length;
var gw = max(1, round(maxCols * pixelSize) + sizeMargin);
var gh = max(1, round(rows * pixelSize) + sizeMargin);
var g = createGraphics(gw, gh, P2D);
g.background(0, 0, 0, 0);
// ensure corner alignment and crisp edges
try {
if (typeof g.rectMode === "function") { g.rectMode(CORNER); }
if (typeof g.noSmooth === "function") { g.noSmooth(); }
} catch (e3) { }
g.noStroke();
// draw to graphics buffer
drawDecodedToGraphics(g, decoded, pixelSize, colorMap);
_pixelArtCache[cacheKey] = g;
}
// let's light this candle
image(_pixelArtCache[cacheKey], startX, startY);
}
// ]
Contact us at realdigitsofpi@gmail.com