## termimg demo — full visual showcase of every rendering path. ## ## Usage: ## nim c -r --path:src examples/demo.nim ## ## Shows: scanner pattern, rainbow gradient, target circles, ## gallery layout (3 across), column layout (3 densities stacked), ## fit-mode comparison grid, and individual renders for each protocol. import std/[strutils, math] import termimg # ── Pattern generators ────────────────────────────────────────── proc checkerboard(w, h, csize: int): seq[uint8] = result = newSeq[uint8](w * h * 4) for y in 0 ..< h: for x in 0 ..< w: let off = (y * w + x) * 4 let bright = if ((x div csize) + (y div csize)) mod 2 == 0: 220 else: 40 result[off + 0] = uint8(bright) result[off + 1] = uint8(bright) result[off + 2] = uint8(bright) result[off + 3] = 255 proc rainbowBars(w, h: int): seq[uint8] = result = newSeq[uint8](w * h * 4) for y in 0 ..< h: for x in 0 ..< w: let off = (y * w + x) * 4 let band = float(x) / float(w) * 6.0 let iband = int(band) let frac = band - float(iband) var r, g, b: float case iband of 0: (r, g, b) = (1.0, frac, 0.0) of 1: (r, g, b) = (1.0 - frac, 1.0, 0.0) of 2: (r, g, b) = (0.0, 1.0, frac) of 3: (r, g, b) = (0.0, 1.0 - frac, 1.0) of 4: (r, g, b) = (frac, 0.0, 1.0) else: (r, g, b) = (1.0, 0.0, 1.0 - frac) let ymul = 0.6 + 0.4 * float(y) / float(h) result[off + 0] = uint8(clamp(int(r * 255.0 * ymul), 0, 255)) result[off + 1] = uint8(clamp(int(g * 255.0 * ymul), 0, 255)) result[off + 2] = uint8(clamp(int(b * 255.0 * ymul), 0, 255)) result[off + 3] = 255 proc targetCircle(w, h: int): seq[uint8] = result = newSeq[uint8](w * h * 4) let cx = float(w) / 2.0 let cy = float(h) / 2.0 let maxR = min(cx, cy) for y in 0 ..< h: for x in 0 ..< w: let off = (y * w + x) * 4 let dx = float(x) - cx let dy = float(y) - cy let r = sqrt(dx * dx + dy * dy) let ring = int(r / (maxR / 5.0)) mod 2 if ring == 0: result[off + 0] = 200; result[off + 1] = 50; result[off + 2] = 50 else: result[off + 0] = 255; result[off + 1] = 255; result[off + 2] = 255 result[off + 3] = 255 proc warmGlow(w, h: int): seq[uint8] = result = newSeq[uint8](w * h * 4) let cx = float(w) / 2.0 let cy = float(h) / 2.0 # maxR intentionally omitted: warmGlow normalizes by cx,cy directly for y in 0 ..< h: for x in 0 ..< w: let off = (y * w + x) * 4 let dx = (float(x) - cx) / cx let dy = (float(y) - cy) / cy let dist = sqrt(dx * dx + dy * dy) let glow = clamp(int(255.0 * exp(-dist * 2.5)), 0, 255) result[off + 0] = uint8(glow) result[off + 1] = uint8(glow * 3 div 5) result[off + 2] = uint8(glow div 3) result[off + 3] = 255 # ── Side-by-side render helper ───────────────────────────────── proc renderSideBySide(images: openArray[seq[uint8]], widths: openArray[int], heights: openArray[int], caps: TerminalCapabilities, opts: RenderOptions, gap: int = 2): string = ## Renders N images at the same geometry and stitches them row-by-row. let geo = computeGeometry(widths[0], heights[0], caps, opts) var parts = newSeq[(seq[string], int)]() # (lines, frames_consumed) for i in 0 ..< images.len: let outStr = renderHalfBlock( ImageData(width: widths[i], height: heights[i], data: images[i]), opts.backgroundRgb, geo.columns, geo.sampleHeight) parts.add (outStr.split('\n'), 0) let maxRows = min(min(parts[0][0].len, parts[1][0].len), (if parts.len > 2: parts[2][0].len else: parts[1][0].len)) for row in 0 ..< maxRows: for i in 0 ..< parts.len: if row < parts[i][0].len: result.add parts[i][0][row] if i < parts.len - 1: result.add spaces(gap) result.add "\n" when isMainModule: echo "" echo "╭──────────────────────────────────────────────╮" echo "│ termimg — Full Demo │" echo "╰──────────────────────────────────────────────╯" echo "" let caps = detectCapabilities() echo "Terminal: ", caps.columns, "x", caps.rows, " cells" echo "Cell: ", caps.cell.width, "x", caps.cell.height, " px" echo "" echo "Detected protocols:" for proto in [ptKitty, ptIterm2, ptSixel, ptQuarterBlock, ptHalfBlock]: let mark = if caps.supports(proto): " ✓" else: " ✗" echo " ", ($proto).align(14), mark echo "" # ── Pattern generation ────────────────────────────────────────── const patternW = 64 const patternH = 48 let chk = checkerboard(patternW, patternH, 8) let rnb = rainbowBars(patternW, patternH) let tgt = targetCircle(patternW, patternH) let glow = warmGlow(patternW, patternH) # ── 1. Gallery: 3 patterns side-by-side ───────────────────────── echo "── Gallery: 3 patterns side-by-side ──" var optsGal = defaultOptions() optsGal.maxWidth = (caps.columns - 4) div 3 optsGal.fit = fmWidth stdout.write(renderSideBySide([chk, rnb, tgt], [patternW, patternW, patternW], [patternH, patternH, patternH], caps, optsGal)) echo "" # ── 2. All four patterns in a column (thumbnail pipeline) ────── echo "── All four patterns (thumbnail pipeline) ──" let thumbOpts = thumbnailOptions() for (name, pix) in [("Checkerboard", chk), ("Rainbow bars", rnb), ("Target circles", tgt), ("Warm glow", glow)]: echo " ", name, ":" let outStr = renderImageRgba(pix, patternW, patternH, caps, thumbOpts) stdout.write(outStr) stdout.write("\n") echo "" # ── 3. Column layout: rainbow at 3 densities for comparison ──── echo "── Column layout: rainbow at 3 block densities ──" let colW = caps.columns - 2 for density in [bdFull, bdHalf, bdQuarter]: var opts = defaultOptions() opts.density = density opts.maxWidth = colW opts.fit = fmWidth let geo = computeGeometry(patternW, patternH, caps, opts) let outStr = renderHalfBlock( ImageData(width: patternW, height: patternH, data: rnb), opts.backgroundRgb, geo.columns, geo.sampleHeight) stdout.write(outStr) stdout.write("\n") echo " ($1 — $2 cols x $3 samples)" % [$density, $geo.columns, $geo.sampleHeight] echo "" echo "" # ── 4. Fit-mode comparison grid (quarter-block, dithered) ────── echo "── Fit-mode grid: target circles at 4 fit modes ──" let halfW = caps.columns div 2 - 1 for fit in [fmContain, fmWidth, fmOriginal, fmStretch]: var opts = defaultOptions() opts.fit = fit opts.maxWidth = halfW opts.density = bdQuarter let geo = computeGeometry(patternW, patternH, caps, opts) let outStr = renderQuarterBlock( ImageData(width: patternW, height: patternH, data: tgt), opts.backgroundRgb, geo.columns, geo.sampleHeight, dither = true) stdout.write(outStr) stdout.write("\n") echo " ($1 — $2 px, $3 cols x $4 samples)" % [ $fit, $geo.pixelWidth & "x" & $geo.pixelHeight, $geo.columns, $geo.sampleHeight] echo "" echo "" # ── 5. Individual renders per protocol ────────────────────────── echo "── Default render (half-block, contain fit) ──" let out1 = renderImageRgba(chk, patternW, patternH, caps, defaultOptions()) stdout.write(out1) stdout.write("\n") echo "" echo "── Quarter-block thumbnail (40 cols, dithered) ──" let out2 = renderImageRgba(rnb, patternW, patternH, caps, thumbnailOptions()) stdout.write(out2) stdout.write("\n") echo "" # ── 6. Extreme aspect-ratio demo ──────────────────────────────── echo "── Wide vs tall: checkerboard at extreme ratios ──" var optsW = defaultOptions() optsW.maxWidth = caps.columns - 2 optsW.fit = fmWidth let geoW = computeGeometry(patternW, patternH, caps, optsW) let outW = renderHalfBlock( ImageData(width: patternW, height: patternH, data: chk), optsW.backgroundRgb, geoW.columns, geoW.sampleHeight) stdout.write(outW) echo " (wide: $1 cols x $2 samples)" % [$geoW.columns, $geoW.sampleHeight] echo "" let tallPixels = checkerboard(patternH, patternW, 6) # 48x64 var optsT = defaultOptions() optsT.maxHeight = caps.rows - 4 optsT.fit = fmContain let geoT = computeGeometry(patternH, patternW, caps, optsT) let outT = renderHalfBlock( ImageData(width: patternH, height: patternW, data: tallPixels), optsT.backgroundRgb, geoT.columns, geoT.sampleHeight) stdout.write(outT) echo " (tall: $1 cols x $2 samples)" % [$geoT.columns, $geoT.sampleHeight] echo "" echo "╭──────────────────────────────────────────────╮" echo "│ Demo complete. All renders done. │" echo "╰──────────────────────────────────────────────╯"