diff --git a/index.html b/index.html index 9ed6b53..69ad34c 100644 --- a/index.html +++ b/index.html @@ -22,14 +22,18 @@

- Currently converts to grayscale and reuses existing similar blocks as it goes, - increasing a similarity threshold (from 0) until the set fits in 128 chars. - Next step: correctly handle inverse video similarities - Further step: don't end up stuck between 64 and 128 blocks :D + Currently just converts to grayscale and counts up unique blocks. + Next step: decimate if > 128 unique blocks per image, and combine + the most similar blocks in the output.

+

Source video

+
+ +

Work canvas

+
@@ -69,50 +73,8 @@ return Array.from(block).map((n) => n.toString(16)).join(''); } - function inverse(pixel) { - return ~pixel & 0xf; - } - - function matchBlocks(a, b, threshold) { - for (let i = 0; i < blockWidth * blockHeight; i++) { - if (Math.abs(a[i] - b[i]) > threshold) { - return false; - } - } - return true; - } - - function matchBlocksInverse(a, b, threshold) { - for (let i = 0; i < blockWidth * blockHeight; i++) { - if (Math.abs(a[i] - inverse(b[i])) > threshold) { - return false; - } - } - return true; - } - - function drawChar(imageData, cx, cy, char, charset) { - let invert = Boolean(char & 0x80); - char &= 0x7f; - if (char >= charset.length) { - return; - } - let block = charset[char]; - for (let y = 0; y < blockHeight; y++) { - for (let x = 0; x < blockWidth; x++) { - let i = y * blockWidth + x; - let ii = (y + cy * blockHeight) * imageData.width + (x + cx * blockWidth); - let gray16 = block[i]; - if (invert) { - gray16 = inverse(gray16); - } - let gray256 = Math.round(gray16 * 255 / 15); - imageData.data[ii * 4] = gray256; - imageData.data[ii * 4 + 1] = gray256; - imageData.data[ii * 4 + 2] = gray256; - imageData.data[ii * 4 + 3] = 255; - } - } + function inverse(block) { + return block.map((n) => ~n & 0xf); } function update() { @@ -159,12 +121,35 @@ // // First pass: uniques extraction // Convert the 4bpp pixel indices into hex strings + let blockMap = {}; let uniques = []; - + /* + for (let i = 0; i < chars.length; i++) { + let char = chars[i]; + let block = blocks[char]; + let key = hexify(blocks[i]); + let keyInverse = hexify(inverse(block)); + if (blockMap[key]) { + char = blockMap[key]; + } else if (blockMap[keyInverse]) { + char = blockMap[keyInverse]; + } else { + char = uniques.push(block) - 1; + blockMap[key] = char; + blockMap[keyInverse] = char; + } + chars[i] = char; + } + */ for (let threshold = 0; threshold < 16; threshold++) { charIter: - for (let i = 0; i < blocks.length; i++) { - let block = blocks[i]; + for (let i = 0; i < chars.length; i++) { + let char = chars[i]; + let block = blocks[char]; + if (!block) { + debugger + throw new Error('missing block'); + } fontMatch: for (let j = 0; j < uniques.length; j++) { @@ -173,16 +158,14 @@ debugger throw new Error('missing other'); } - if (matchBlocks(block, other, threshold)) { - // we're close enough to reuse a character - chars[i] = j; - continue charIter; - } else if (matchBlocksInverse(block, other, threshold)) { - chars[i] = j | 0x8000; - continue charIter; - } else { - continue fontMatch; + for (let k = 0; k < blockWidth * blockHeight; k++) { + if (Math.abs(block[k] - other[k]) > threshold) { + continue fontMatch; + } } + // we're close enough to reuse a character + chars[i] = j; + continue charIter; } // add a new char chars[i] = uniques.push(block) - 1; @@ -191,34 +174,55 @@ break; } // We need to decimate further + blocks = uniques; uniques = []; } let span = document.querySelector('#block-count'); span.textContent = `${uniques.length}`; - // Font + // Font (currently wrong! :D) let fontCtx = document.querySelector('#font').getContext('2d'); let font = fontCtx.createImageData(16 * blockWidth, 16 * blockHeight); for (let hi = 0; hi < 16; hi++) { for (let lo = 0; lo < 16; lo++) { let char = (hi << 4) | lo; - drawChar(font, lo, hi, char, uniques); + let invert = Boolean(char & 0x80); + char &= 0x7f; + if (char >= uniques.length) { + continue; + } + let block = uniques[char]; + for (let y = 0; y < blockHeight; y++) { + for (let x = 0; x < blockWidth; x++) { + let i = y * blockWidth + x; + let ii = (y + hi * blockHeight) * 16 * blockWidth + (x + lo * blockWidth); + if (block.length < i) { + debugger; + } + let gray16 = block[i]; + if (invert) { + gray16 = ~gray16 & 0x0f; + } + let gray256 = Math.round(gray16 * 255 / 15); + font.data[ii * 4] = gray256; + font.data[ii * 4 + 1] = gray256; + font.data[ii * 4 + 2] = gray256; + font.data[ii * 4 + 3] = 255; + } + } } } fontCtx.putImageData(font, 0, 0); // Redraw the blocks - - for (let cy = 0; cy < heightBlocks; cy++) { - for (let cx = 0; cx < widthBlocks; cx++) { - let i = cy * widthBlocks + cx; - let char = chars[i]; - if (char & 0x8000) { - // we use a bigger bit during earlier stages - char &= 0x7f; - char |= 0x80; - } - drawChar(bits, cx, cy, char, uniques); + for (let y = 0; y < height; y++) { + for (let x = 0; x < width; x++) { + let i = y * width + x; + let gray16 = pixels[i]; + let gray256 = Math.round(gray16 * 255 / 15); + data[i * 4] = gray256; + data[i * 4 + 1] = gray256; + data[i * 4 + 2] = gray256; } } ctx.putImageData(bits, 0, 0); @@ -227,10 +231,7 @@ let timer = null; source.addEventListener('playing', () => { if (!timer) { - // target 8 fps - // not sure we can get any faster - // downloads over sio - timer = setInterval(update, 1000 / 8); + timer = setInterval(update, 1000 / 10); } update(); });