4bbfdcceff
audio-surface.el / image-surface.el: own-core additive-synthesis WAV and
raster-PNG renderers (integer-only DSP, since EL has no floats), rendered
from learned engram signatures via a pluggable surface-profile
abstraction (surface-profile.el). audio-demo.el / image-demo.el are
drivers. NOTE: demo files hardcode absolute paths to this worktree's own
directory — will need a path fixup before landing.
elp/projector/ is a Python package the author's own README marks as
"STAGING/PROOF-OF-SHAPE — not the deliverable", superseded by the native
.el surface-profile work above; kept as a validated architecture proof.
Generated output (elp/faculty/{out,sig}, elp/projector/out,
__pycache__) intentionally excluded.
413 lines
13 KiB
EmacsLisp
413 lines
13 KiB
EmacsLisp
// image-surface.el - Native own-core raster PNG surface (the image efferent
|
|
// twin of audio). Renders a 64x64 RGB scene deterministically from a frame's
|
|
// meaning-geometry, then serialises a byte-valid PNG entirely own-core:
|
|
// 8-byte magic, IHDR, IDAT (zlib STORED/uncompressed DEFLATE + Adler32), IEND,
|
|
// with a per-chunk CRC32 computed via software xor32 (EL has no bitwise ops).
|
|
//
|
|
// The RGB palette basis is read from elp/faculty/sig/scene.basis (data, not
|
|
// literals) - the same read-from-learned discipline as the audio signatures.
|
|
// Integer-only throughout; pixels are composed functionally (painter's order)
|
|
// so no list mutation is needed.
|
|
|
|
// -- small int/parse helpers (self-contained) ----------------------------------
|
|
|
|
fn i_str_to_int(s: String) -> Int {
|
|
let n: Int = str_len(s)
|
|
let i: Int = 0
|
|
let v: Int = 0
|
|
while i < n {
|
|
let c: Int = str_char_code(s, i)
|
|
if c >= 48 {
|
|
if c < 58 {
|
|
let v: Int = v * 10 + (c - 48)
|
|
}
|
|
}
|
|
let i: Int = i + 1
|
|
}
|
|
return v
|
|
}
|
|
|
|
fn basis_load(path: String) -> [String] {
|
|
return str_split(fs_read(path), "\n")
|
|
}
|
|
|
|
fn basis_field(lines: [String], key: String) -> String {
|
|
let pref: String = key + ": "
|
|
let n: Int = native_list_len(lines)
|
|
let plen: Int = str_len(pref)
|
|
let i: Int = 0
|
|
while i < n {
|
|
let ln: String = native_list_get(lines, i)
|
|
if str_starts_with(ln, pref) {
|
|
return str_slice(ln, plen, str_len(ln))
|
|
}
|
|
let i: Int = i + 1
|
|
}
|
|
return ""
|
|
}
|
|
|
|
fn parse_rgb(csv: String) -> [Int] {
|
|
let parts: [String] = str_split(csv, ",")
|
|
let out: [Int] = native_list_empty()
|
|
let n: Int = native_list_len(parts)
|
|
let i: Int = 0
|
|
while i < n {
|
|
let v: Int = i_str_to_int(native_list_get(parts, i))
|
|
let out: [Int] = native_list_append(out, v)
|
|
let i: Int = i + 1
|
|
}
|
|
return out
|
|
}
|
|
|
|
// -- software 32-bit XOR (no bitwise ops in EL) --------------------------------
|
|
|
|
fn xor32(a: Int, b: Int) -> Int {
|
|
let r: Int = 0
|
|
let bit: Int = 1
|
|
let i: Int = 0
|
|
while i < 32 {
|
|
let abit: Int = (a / bit) % 2
|
|
let bbit: Int = (b / bit) % 2
|
|
if abit != bbit {
|
|
let add: Int = bit
|
|
let r: Int = r + add
|
|
}
|
|
let bit: Int = bit * 2
|
|
let i: Int = i + 1
|
|
}
|
|
return r
|
|
}
|
|
|
|
// -- CRC32 (table-driven, table built with xor32) ------------------------------
|
|
|
|
fn crc_table() -> [Int] {
|
|
let t: [Int] = native_list_empty()
|
|
let n: Int = 0
|
|
while n < 256 {
|
|
let c: Int = n
|
|
let k: Int = 0
|
|
while k < 8 {
|
|
if c % 2 == 1 {
|
|
let h: Int = c / 2
|
|
let c: Int = xor32(h, 3988292384)
|
|
} else {
|
|
let c: Int = c / 2
|
|
}
|
|
let k: Int = k + 1
|
|
}
|
|
let t: [Int] = native_list_append(t, c)
|
|
let n: Int = n + 1
|
|
}
|
|
return t
|
|
}
|
|
|
|
fn crc32_of(bytes: [Int], table: [Int]) -> Int {
|
|
let crc: Int = 4294967295
|
|
let n: Int = native_list_len(bytes)
|
|
let i: Int = 0
|
|
while i < n {
|
|
let b: Int = native_list_get(bytes, i)
|
|
let lo: Int = crc % 256
|
|
let idx: Int = xor32(lo, b) % 256
|
|
let tv: Int = native_list_get(table, idx)
|
|
let hi: Int = crc / 256
|
|
let crc: Int = xor32(hi, tv)
|
|
let i: Int = i + 1
|
|
}
|
|
return xor32(crc, 4294967295)
|
|
}
|
|
|
|
// -- Adler32 (for the zlib trailer) --------------------------------------------
|
|
|
|
fn adler32_of(bytes: [Int]) -> Int {
|
|
let a: Int = 1
|
|
let b: Int = 0
|
|
let n: Int = native_list_len(bytes)
|
|
let i: Int = 0
|
|
while i < n {
|
|
let byte: Int = native_list_get(bytes, i)
|
|
let a: Int = (a + byte) % 65521
|
|
let b: Int = (b + a) % 65521
|
|
let i: Int = i + 1
|
|
}
|
|
return b * 65536 + a
|
|
}
|
|
|
|
// -- byte-list append helpers --------------------------------------------------
|
|
|
|
fn app_u32be(dst: [Int], v: Int) -> [Int] {
|
|
let dst: [Int] = native_list_append(dst, (v / 16777216) % 256)
|
|
let dst: [Int] = native_list_append(dst, (v / 65536) % 256)
|
|
let dst: [Int] = native_list_append(dst, (v / 256) % 256)
|
|
let dst: [Int] = native_list_append(dst, v % 256)
|
|
return dst
|
|
}
|
|
|
|
fn app_tag(dst: [Int], s: String) -> [Int] {
|
|
let n: Int = str_len(s)
|
|
let i: Int = 0
|
|
while i < n {
|
|
let dst: [Int] = native_list_append(dst, str_char_code(s, i))
|
|
let i: Int = i + 1
|
|
}
|
|
return dst
|
|
}
|
|
|
|
fn app_all(dst: [Int], src: [Int]) -> [Int] {
|
|
let n: Int = native_list_len(src)
|
|
let i: Int = 0
|
|
while i < n {
|
|
let dst: [Int] = native_list_append(dst, native_list_get(src, i))
|
|
let i: Int = i + 1
|
|
}
|
|
return dst
|
|
}
|
|
|
|
// -- plan: frame meaning-geometry -> shape atoms -------------------------------
|
|
// shape = [type, x, y, size, r, g, b] (type 0=rect 1=disc 2=triangle)
|
|
|
|
fn charsum(s: String) -> Int {
|
|
let n: Int = str_len(s)
|
|
let i: Int = 0
|
|
let acc: Int = 0
|
|
while i < n {
|
|
let c: Int = str_char_code(s, i)
|
|
let acc: Int = acc + c
|
|
let i: Int = i + 1
|
|
}
|
|
return acc
|
|
}
|
|
|
|
fn micro_of(s: String) -> Int {
|
|
let dot: Int = str_index_of(s, ".")
|
|
if dot < 0 { return i_str_to_int(s) * 1000000 }
|
|
let n: Int = str_len(s)
|
|
let fp: String = str_slice(s, dot + 1, n)
|
|
let ip: String = str_slice(s, 0, dot)
|
|
let iv: Int = i_str_to_int(ip)
|
|
let fv: Int = 0
|
|
let scale: Int = 100000
|
|
let fl: Int = str_len(fp)
|
|
let i: Int = 0
|
|
while i < 6 {
|
|
let d: Int = 0
|
|
if i < fl { let d: Int = str_char_code(fp, i) - 48 }
|
|
let fv: Int = fv + d * scale
|
|
let scale: Int = scale / 10
|
|
let i: Int = i + 1
|
|
}
|
|
return iv * 1000000 + fv
|
|
}
|
|
|
|
fn plan_scene(frames: [[String]], warm: [Int], cool: [Int]) -> [[Int]] {
|
|
let shapes: [[Int]] = native_list_empty()
|
|
let nf: Int = native_list_len(frames)
|
|
let fi: Int = 0
|
|
while fi < nf {
|
|
let fr: [String] = native_list_get(frames, fi)
|
|
let relation: String = surface_get(fr, "relation")
|
|
let polarity: String = surface_get(fr, "polarity")
|
|
let confidence: String = surface_get(fr, "confidence")
|
|
let importance: String = surface_get(fr, "importance")
|
|
let salience: String = surface_get(fr, "salience")
|
|
// relation -> shape type
|
|
let stype: Int = charsum(relation) % 3
|
|
// confidence -> size (8..22)
|
|
let cmi: Int = micro_of(confidence)
|
|
let size: Int = 8 + cmi / 71428
|
|
// salience -> y
|
|
let sal: Int = i_str_to_int(salience)
|
|
let y: Int = 6 + sal * 26
|
|
// subj_id/index -> x
|
|
let x: Int = 4 + (fi * 10) % 48
|
|
// polarity -> warm/cool base color
|
|
let br: Int = native_list_get(warm, 0)
|
|
let bg2: Int = native_list_get(warm, 1)
|
|
let bb: Int = native_list_get(warm, 2)
|
|
if str_eq(polarity, "neg") {
|
|
let br: Int = native_list_get(cool, 0)
|
|
let bg2: Int = native_list_get(cool, 1)
|
|
let bb: Int = native_list_get(cool, 2)
|
|
}
|
|
// importance -> brightness (500..1000 permille)
|
|
let imi: Int = micro_of(importance)
|
|
let bpm: Int = 500 + imi / 2000
|
|
let r: Int = br * bpm / 1000
|
|
let g: Int = bg2 * bpm / 1000
|
|
let b: Int = bb * bpm / 1000
|
|
let sh: [Int] = native_list_empty()
|
|
let sh: [Int] = native_list_append(sh, stype)
|
|
let sh: [Int] = native_list_append(sh, x)
|
|
let sh: [Int] = native_list_append(sh, y)
|
|
let sh: [Int] = native_list_append(sh, size)
|
|
let sh: [Int] = native_list_append(sh, r)
|
|
let sh: [Int] = native_list_append(sh, g)
|
|
let sh: [Int] = native_list_append(sh, b)
|
|
let shapes: [[Int]] = native_list_append(shapes, sh)
|
|
let fi: Int = fi + 1
|
|
}
|
|
return shapes
|
|
}
|
|
|
|
// covers: is (px,py) inside this shape?
|
|
fn covers(sh: [Int], px: Int, py: Int) -> Bool {
|
|
let stype: Int = native_list_get(sh, 0)
|
|
let sx: Int = native_list_get(sh, 1)
|
|
let sy: Int = native_list_get(sh, 2)
|
|
let size: Int = native_list_get(sh, 3)
|
|
let cx: Int = sx + size / 2
|
|
if stype == 0 {
|
|
if px >= sx {
|
|
if px < sx + size {
|
|
if py >= sy {
|
|
if py < sy + size {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
if stype == 1 {
|
|
let rad: Int = size / 2
|
|
let dx: Int = px - cx
|
|
let dy: Int = py - (sy + rad)
|
|
if dx * dx + dy * dy <= rad * rad {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
// triangle: apex at top (sy), base at sy+size
|
|
if py >= sy {
|
|
if py < sy + size {
|
|
let dyv: Int = py - sy
|
|
let halfw: Int = dyv / 2
|
|
let dxv: Int = px - cx
|
|
let adx: Int = dxv
|
|
if adx < 0 { let adx: Int = 0 - dxv }
|
|
if adx <= halfw {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// pixel_color: painter's algorithm - last covering shape wins. Returns [r,g,b].
|
|
fn pixel_color(px: Int, py: Int, shapes: [[Int]], bg: [Int]) -> [Int] {
|
|
let r: Int = native_list_get(bg, 0)
|
|
let g: Int = native_list_get(bg, 1)
|
|
let b: Int = native_list_get(bg, 2)
|
|
let n: Int = native_list_len(shapes)
|
|
let i: Int = 0
|
|
while i < n {
|
|
let sh: [Int] = native_list_get(shapes, i)
|
|
if covers(sh, px, py) {
|
|
let r: Int = native_list_get(sh, 4)
|
|
let g: Int = native_list_get(sh, 5)
|
|
let b: Int = native_list_get(sh, 6)
|
|
}
|
|
let i: Int = i + 1
|
|
}
|
|
let out: [Int] = native_list_empty()
|
|
let out: [Int] = native_list_append(out, r)
|
|
let out: [Int] = native_list_append(out, g)
|
|
let out: [Int] = native_list_append(out, b)
|
|
return out
|
|
}
|
|
|
|
// rasterize: build the raw (filtered) scanline byte stream, filter byte 0 / row.
|
|
fn rasterize(w: Int, h: Int, shapes: [[Int]], bg: [Int]) -> [Int] {
|
|
let raw: [Int] = native_list_empty()
|
|
let y: Int = 0
|
|
while y < h {
|
|
let raw: [Int] = native_list_append(raw, 0)
|
|
let x: Int = 0
|
|
while x < w {
|
|
let col: [Int] = pixel_color(x, y, shapes, bg)
|
|
let raw: [Int] = native_list_append(raw, native_list_get(col, 0))
|
|
let raw: [Int] = native_list_append(raw, native_list_get(col, 1))
|
|
let raw: [Int] = native_list_append(raw, native_list_get(col, 2))
|
|
let x: Int = x + 1
|
|
}
|
|
let y: Int = y + 1
|
|
}
|
|
return raw
|
|
}
|
|
|
|
// zlib stream with a single STORED (uncompressed) DEFLATE block + Adler32.
|
|
fn zlib_store(raw: [Int]) -> [Int] {
|
|
let z: [Int] = native_list_empty()
|
|
let z: [Int] = native_list_append(z, 120)
|
|
let z: [Int] = native_list_append(z, 1)
|
|
let z: [Int] = native_list_append(z, 1)
|
|
let len: Int = native_list_len(raw)
|
|
let nlen: Int = 65535 - len
|
|
let z: [Int] = native_list_append(z, len % 256)
|
|
let z: [Int] = native_list_append(z, (len / 256) % 256)
|
|
let z: [Int] = native_list_append(z, nlen % 256)
|
|
let z: [Int] = native_list_append(z, (nlen / 256) % 256)
|
|
let z: [Int] = app_all(z, raw)
|
|
let ad: Int = adler32_of(raw)
|
|
let z: [Int] = app_u32be(z, ad)
|
|
return z
|
|
}
|
|
|
|
// append a full PNG chunk: length + (type+data) + crc32(type+data).
|
|
fn app_chunk(png: [Int], type_and_data: [Int], table: [Int]) -> [Int] {
|
|
let total: Int = native_list_len(type_and_data)
|
|
let dlen: Int = total - 4
|
|
let png: [Int] = app_u32be(png, dlen)
|
|
let png: [Int] = app_all(png, type_and_data)
|
|
let crc: Int = crc32_of(type_and_data, table)
|
|
let png: [Int] = app_u32be(png, crc)
|
|
return png
|
|
}
|
|
|
|
fn png_build(w: Int, h: Int, raw: [Int], table: [Int]) -> [Int] {
|
|
let png: [Int] = native_list_empty()
|
|
// 8-byte signature
|
|
let png: [Int] = native_list_append(png, 137)
|
|
let png: [Int] = native_list_append(png, 80)
|
|
let png: [Int] = native_list_append(png, 78)
|
|
let png: [Int] = native_list_append(png, 71)
|
|
let png: [Int] = native_list_append(png, 13)
|
|
let png: [Int] = native_list_append(png, 10)
|
|
let png: [Int] = native_list_append(png, 26)
|
|
let png: [Int] = native_list_append(png, 10)
|
|
// IHDR
|
|
let ihdr: [Int] = native_list_empty()
|
|
let ihdr: [Int] = app_tag(ihdr, "IHDR")
|
|
let ihdr: [Int] = app_u32be(ihdr, w)
|
|
let ihdr: [Int] = app_u32be(ihdr, h)
|
|
let ihdr: [Int] = native_list_append(ihdr, 8)
|
|
let ihdr: [Int] = native_list_append(ihdr, 2)
|
|
let ihdr: [Int] = native_list_append(ihdr, 0)
|
|
let ihdr: [Int] = native_list_append(ihdr, 0)
|
|
let ihdr: [Int] = native_list_append(ihdr, 0)
|
|
let png: [Int] = app_chunk(png, ihdr, table)
|
|
// IDAT
|
|
let z: [Int] = zlib_store(raw)
|
|
let idat: [Int] = native_list_empty()
|
|
let idat: [Int] = app_tag(idat, "IDAT")
|
|
let idat: [Int] = app_all(idat, z)
|
|
let png: [Int] = app_chunk(png, idat, table)
|
|
// IEND
|
|
let iend: [Int] = native_list_empty()
|
|
let iend: [Int] = app_tag(iend, "IEND")
|
|
let png: [Int] = app_chunk(png, iend, table)
|
|
return png
|
|
}
|
|
|
|
fn png_write(path: String, png: [Int]) -> Int {
|
|
let n: Int = native_list_len(png)
|
|
let buf: String = __str_alloc(n)
|
|
let i: Int = 0
|
|
while i < n {
|
|
let buf: String = __str_set_char(buf, i, native_list_get(png, i))
|
|
let i: Int = i + 1
|
|
}
|
|
let ok: Int = fs_write_bytes(path, buf, n)
|
|
return ok
|
|
}
|