|
| 1 | +/** |
| 2 | + * generate_reference.ts — one-time script to produce reference_data.bin |
| 3 | + * |
| 4 | + * Uses the Zig-built libpict.dylib to generate ground-truth resize output. |
| 5 | + * Commit the resulting reference_data.bin; future CI uses it without Zig. |
| 6 | + * |
| 7 | + * Run: bun run test/generate_reference.ts |
| 8 | + */ |
| 9 | + |
| 10 | +import { dlopen, FFIType, ptr, toArrayBuffer } from "bun:ffi"; |
| 11 | +import { writeFileSync } from "fs"; |
| 12 | +import { join } from "path"; |
| 13 | + |
| 14 | +const ZIG_LIB = "/Users/tuki/Develop/Projects/zenpix/npm/zenpix-darwin-arm64/libpict.dylib"; |
| 15 | + |
| 16 | +const lib = dlopen(ZIG_LIB, { |
| 17 | + pict_resize: { |
| 18 | + args: [FFIType.ptr, FFIType.u32, FFIType.u32, FFIType.u8, |
| 19 | + FFIType.u32, FFIType.u32, FFIType.u32, FFIType.ptr], |
| 20 | + returns: FFIType.ptr, |
| 21 | + }, |
| 22 | + pict_free_buffer: { |
| 23 | + args: [FFIType.ptr, FFIType.u64], |
| 24 | + returns: FFIType.void, |
| 25 | + }, |
| 26 | +}); |
| 27 | + |
| 28 | +// ── Synthetic input: 64×48 RGB gradient ────────────────────────────────────── |
| 29 | +const SRC_W = 64, SRC_H = 48, CH = 3; |
| 30 | +const src = new Uint8Array(SRC_W * SRC_H * CH); |
| 31 | +for (let y = 0; y < SRC_H; y++) { |
| 32 | + for (let x = 0; x < SRC_W; x++) { |
| 33 | + const i = (y * SRC_W + x) * CH; |
| 34 | + src[i + 0] = Math.round(x * 255 / (SRC_W - 1)); // R: left→right |
| 35 | + src[i + 1] = Math.round(y * 255 / (SRC_H - 1)); // G: top→bottom |
| 36 | + src[i + 2] = Math.round((x + y) * 255 / (SRC_W + SRC_H - 2));// B: diagonal |
| 37 | + } |
| 38 | +} |
| 39 | + |
| 40 | +// ── Resize cases ────────────────────────────────────────────────────────────── |
| 41 | +const cases = [ |
| 42 | + { dstW: 20, dstH: 15, label: "64×48 → 20×15 (downscale ×0.3)" }, |
| 43 | + { dstW: 128, dstH: 96, label: "64×48 → 128×96 (upscale ×2)" }, |
| 44 | + { dstW: 64, dstH: 48, label: "64×48 → 64×48 (identity)" }, |
| 45 | +]; |
| 46 | + |
| 47 | +// ── Generate ────────────────────────────────────────────────────────────────── |
| 48 | +const parts: Buffer[] = []; |
| 49 | + |
| 50 | +const countBuf = Buffer.alloc(4); |
| 51 | +countBuf.writeUInt32LE(cases.length, 0); |
| 52 | +parts.push(countBuf); |
| 53 | + |
| 54 | +for (const { dstW, dstH, label } of cases) { |
| 55 | + const outLen = new BigUint64Array(1); |
| 56 | + const result = lib.symbols.pict_resize( |
| 57 | + ptr(src), SRC_W, SRC_H, CH, |
| 58 | + dstW, dstH, |
| 59 | + 1, // n_threads = 1 (deterministic) |
| 60 | + ptr(outLen), |
| 61 | + ); |
| 62 | + if (!result) throw new Error(`Zig pict_resize failed: ${label}`); |
| 63 | + |
| 64 | + const pixels = new Uint8Array(toArrayBuffer(result, 0, Number(outLen[0])).slice(0)); |
| 65 | + lib.symbols.pict_free_buffer(result, outLen[0]); |
| 66 | + |
| 67 | + const header = Buffer.alloc(16); |
| 68 | + header.writeUInt32LE(dstW, 0); |
| 69 | + header.writeUInt32LE(dstH, 4); |
| 70 | + header.writeUInt32LE(CH, 8); |
| 71 | + header.writeUInt32LE(pixels.length, 12); |
| 72 | + |
| 73 | + parts.push(header, Buffer.from(pixels)); |
| 74 | + console.log(` ${label}: ${pixels.length} bytes`); |
| 75 | +} |
| 76 | + |
| 77 | +lib.close(); |
| 78 | + |
| 79 | +// ── Write ───────────────────────────────────────────────────────────────────── |
| 80 | +const outPath = join(import.meta.dir, "reference_data.bin"); |
| 81 | +writeFileSync(outPath, Buffer.concat(parts)); |
| 82 | +console.log(`\nSaved ${outPath}`); |
0 commit comments