Skip to content

Instantly share code, notes, and snippets.

@AaronKow
Created July 2, 2025 02:30
Show Gist options
  • Select an option

  • Save AaronKow/596543f2f3cba515e503faf7b32409cf to your computer and use it in GitHub Desktop.

Select an option

Save AaronKow/596543f2f3cba515e503faf7b32409cf to your computer and use it in GitHub Desktop.

Revisions

  1. AaronKow created this gist Jul 2, 2025.
    160 changes: 160 additions & 0 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,160 @@
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>ASCII → RP2040 Sprite Converter</title>
    <style>
    body { font-family: system-ui, sans-serif; margin: 1rem; background: #f4f4f5; }
    h1 { font-size: 1.5rem; margin-bottom: 1rem; }
    textarea, pre { width: 100%; box-sizing: border-box; border: 1px solid #ddd; border-radius: 0.5rem; padding: 0.75rem; font-family: monospace; background: #fff; }
    textarea { min-height: 160px; resize: vertical; }
    button {
    padding: 0.6rem 1rem; margin-top: 0.75rem; border: none;
    border-radius: 0.5rem; cursor: pointer; font-weight: 600;
    background: #2563eb; color: #fff;
    }
    button:disabled { opacity: 0.5; cursor: not-allowed; }
    .flex { display: flex; gap: 1rem; flex-wrap: wrap; }
    .half { flex: 1 1 300px; min-width: 300px; }
    label { font-weight: 600; display: block; margin-bottom: 0.25rem; }
    </style>
    </head>
    <body>
    <h1>ASCII → C Sprite Converter (SSD1306 128×64)</h1>

    <button id="load-demo">Load Demo</button>

    <div class="flex">
    <div class="half">
    <label for="ascii">1️⃣ Draw sprite (non-space & non-dot = pixel ON)</label>
    <textarea id="ascii" placeholder="Example (16×16 smiley using . for blank):&#10................&#10.##############.&#10.#....##....##..&#10.#....##....##..&#10.#............#.&#10.#....######..#.&#10.#............#.&#10.#..##....##..#.&#10.#...######...#.&#10.##############.&#10................&#10................&#10................&#10................&#10................&#10................"></textarea>
    </div>

    <div class="half">
    <label for="c-output">2️⃣ Generated C code</label>
    <textarea id="c-output" readonly placeholder="C array will appear here…"></textarea>
    <button id="copy" disabled>Copy&nbsp;to&nbsp;Clipboard</button>
    </div>
    </div>

    <button id="convert">Convert → C Bitmap</button>
    <pre id="info"></pre>

    <script>
    const asciiEl = document.getElementById('ascii');
    const outputEl = document.getElementById('c-output');
    const infoEl = document.getElementById('info');
    const convertBtn = document.getElementById('convert');
    const copyBtn = document.getElementById('copy');
    const loadDemoBtn = document.getElementById('load-demo');

    const demoSprite = [
    "................",
    ".##############.",
    ".#....##....##..",
    ".#....##....##..",
    ".#............#.",
    ".#....######..#.",
    ".#............#.",
    ".#..##....##..#.",
    ".#...######...#.",
    ".##############.",
    "................",
    "................",
    "................",
    "................",
    "................",
    "................"
    ].join("\n");

    function asciiToBytes(lines) {
    const height = lines.length;
    const width = Math.max(...lines.map(l => l.length));

    if (width > 128 || height > 64) throw new Error('Sprite exceeds 128×64 limit');

    // Pad lines to equal width
    lines = lines.map(l => l.padEnd(width, ' '));

    const bytes = [];
    for (let y = 0; y < height; y += 8) {
    for (let x = 0; x < width; x++) {
    let byte = 0;
    for (let bit = 0; bit < 8; bit++) {
    const yy = y + bit;
    let pxOn = false;
    if (yy < height) {
    const ch = lines[yy][x];
    pxOn = ch !== ' ' && ch !== '.'; // spaces & dots are OFF
    }
    byte |= (pxOn ? 1 : 0) << bit; // LSB = top pixel
    }
    bytes.push(byte);
    }
    }
    return { width, height, bytes };
    }

    function generateC({ width, height, bytes }) {
    const hex = bytes.map(b => '0x' + b.toString(16).padStart(2, '0'));
    return `// Width: ${width}, Height: ${height}\nconst uint8_t sprite_${width}x${height}[${bytes.length}] = {\n ${hex.join(', ')}\n};`;
    }

    convertBtn.addEventListener('click', () => {
    try {
    const lines = asciiEl.value.replace(/\r/g, '').split('\n').filter(l => l.length);
    if (!lines.length) throw new Error('Please enter ASCII art');
    const data = asciiToBytes(lines);
    const cCode = generateC(data);
    outputEl.value = cCode;
    infoEl.textContent = `Converted: ${data.width}×${data.height}${data.bytes.length} bytes`;
    copyBtn.disabled = false;
    } catch (err) {
    infoEl.textContent = err.message;
    outputEl.value = '';
    copyBtn.disabled = true;
    }
    });

    copyBtn.addEventListener('click', async () => {
    try {
    await navigator.clipboard.writeText(outputEl.value);
    copyBtn.textContent = 'Copied!';
    setTimeout(() => (copyBtn.textContent = 'Copy to Clipboard'), 1500);
    } catch {
    alert('Clipboard failed. Please copy manually.');
    }
    });

    loadDemoBtn.addEventListener('click', () => {
    asciiEl.value = demoSprite;
    outputEl.value = '';
    infoEl.textContent = '';
    copyBtn.disabled = true;
    });
    </script>

    <details style="margin-top:1rem;">
    <summary><strong>How to use in C (TinyUSB / Pico‑SDK)</strong></summary>
    <pre>
    // Include the array this tool generated (e.g. sprite_16x16)
    #include "sprite.h"

    void oled_draw_sprite(uint8_t x, uint8_t page, const uint8_t *sprite,
    uint8_t w, uint8_t h) {
    /* SSD1306 page‑addressed write */
    for (uint8_t col = 0; col < w; col++) {
    for (uint8_t p = 0; p < (h + 7) / 8; p++) {
    ssd1306_set_cursor(x + col, page + p);
    ssd1306_write_data(sprite[col + p * w]);
    }
    }
    }

    // Example usage
    oled_draw_sprite(0, 0, sprite_16x16, 16, 16);
    </pre>
    </details>
    </body>
    </html>