diff --git a/README.md b/README.md index eb9c3b1..e61a037 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,16 @@ import { DitherGradient } from 'ditherdance/react' ``` +## Gallery + +Every tile is one string or preset, rendered by `scripts/gallery.mjs`. + +| ada | grace | linus | margaret | +|:-:|:-:|:-:|:-:| +| `name="ada@example.com"` | `name="grace"` | `name="linus"` | `name="margaret"` | +| ember | lagoon | prism | newsprint | +| `preset="ember"` | `preset="lagoon"` | `preset="prism"` | `preset="newsprint"` | + ## Images Try every method on your own picture at [dither.dance/image](https://dither.dance/image). diff --git a/docs/gallery/ada.png b/docs/gallery/ada.png new file mode 100644 index 0000000..2eaaa1d Binary files /dev/null and b/docs/gallery/ada.png differ diff --git a/docs/gallery/ember.png b/docs/gallery/ember.png new file mode 100644 index 0000000..b054d33 Binary files /dev/null and b/docs/gallery/ember.png differ diff --git a/docs/gallery/grace.png b/docs/gallery/grace.png new file mode 100644 index 0000000..008f901 Binary files /dev/null and b/docs/gallery/grace.png differ diff --git a/docs/gallery/lagoon.png b/docs/gallery/lagoon.png new file mode 100644 index 0000000..1f88f5d Binary files /dev/null and b/docs/gallery/lagoon.png differ diff --git a/docs/gallery/linus.png b/docs/gallery/linus.png new file mode 100644 index 0000000..a424934 Binary files /dev/null and b/docs/gallery/linus.png differ diff --git a/docs/gallery/margaret.png b/docs/gallery/margaret.png new file mode 100644 index 0000000..82bc4de Binary files /dev/null and b/docs/gallery/margaret.png differ diff --git a/docs/gallery/newsprint.png b/docs/gallery/newsprint.png new file mode 100644 index 0000000..aa010c4 Binary files /dev/null and b/docs/gallery/newsprint.png differ diff --git a/docs/gallery/prism.png b/docs/gallery/prism.png new file mode 100644 index 0000000..15079d5 Binary files /dev/null and b/docs/gallery/prism.png differ diff --git a/scripts/gallery.mjs b/scripts/gallery.mjs new file mode 100644 index 0000000..8ebf548 --- /dev/null +++ b/scripts/gallery.mjs @@ -0,0 +1,79 @@ +// Renders the README gallery to docs/gallery/*.png with the CPU renderer. +// Run after a build: npm run build && node scripts/gallery.mjs +import { mkdirSync, writeFileSync } from 'node:fs' +import { deflateSync } from 'node:zlib' +import { normalize, renderPixels } from '../dist/gradient/index.js' + +const TILES = [ + { file: 'ada', options: { name: 'ada@example.com' } }, + { file: 'grace', options: { name: 'grace' } }, + { file: 'linus', options: { name: 'linus' } }, + { file: 'margaret', options: { name: 'margaret' } }, + { file: 'ember', options: { preset: 'ember' } }, + { file: 'lagoon', options: { preset: 'lagoon' } }, + { file: 'prism', options: { preset: 'prism' } }, + { file: 'newsprint', options: { preset: 'newsprint' } }, +] + +// One dither cell becomes CELL x CELL pixels, like pixelSize on a 1x screen. +const COLS = 120 +const ROWS = 80 +const CELL = 3 + +const crcTable = Array.from({ length: 256 }, (_, n) => { + let c = n + for (let k = 0; k < 8; k++) c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1 + return c >>> 0 +}) +const crc32 = (buf) => { + let c = 0xffffffff + for (const b of buf) c = crcTable[(c ^ b) & 0xff] ^ (c >>> 8) + return (c ^ 0xffffffff) >>> 0 +} +const chunk = (type, data) => { + const out = Buffer.alloc(12 + data.length) + out.writeUInt32BE(data.length, 0) + out.write(type, 4, 'ascii') + data.copy(out, 8) + out.writeUInt32BE(crc32(out.subarray(4, 8 + data.length)), 8 + data.length) + return out +} + +function png(rgba, width, height) { + const ihdr = Buffer.alloc(13) + ihdr.writeUInt32BE(width, 0) + ihdr.writeUInt32BE(height, 4) + ihdr.set([8, 6, 0, 0, 0], 8) // 8-bit RGBA + const raw = Buffer.alloc((width * 4 + 1) * height) + for (let y = 0; y < height; y++) { + raw[y * (width * 4 + 1)] = 0 // no filter + rgba.copy(raw, y * (width * 4 + 1) + 1, y * width * 4, (y + 1) * width * 4) + } + return Buffer.concat([ + Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]), + chunk('IHDR', ihdr), + chunk('IDAT', deflateSync(raw, { level: 9 })), + chunk('IEND', Buffer.alloc(0)), + ]) +} + +function upscale(cells, cols, rows, scale) { + const w = cols * scale + const out = Buffer.alloc(w * rows * scale * 4) + for (let y = 0; y < rows * scale; y++) { + for (let x = 0; x < w; x++) { + const src = (((y / scale) | 0) * cols + ((x / scale) | 0)) * 4 + out.set(cells.subarray(src, src + 4), (y * w + x) * 4) + } + } + return out +} + +const dir = new URL('../docs/gallery/', import.meta.url) +mkdirSync(dir, { recursive: true }) +for (const { file, options } of TILES) { + const cells = renderPixels(normalize(options), COLS, ROWS) + const image = upscale(cells, COLS, ROWS, CELL) + writeFileSync(new URL(`${file}.png`, dir), png(image, COLS * CELL, ROWS * CELL)) + console.log(`docs/gallery/${file}.png`) +}