package tiny_libs
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>
From-scratch libraries for teaching: graphics, audio, compression, crypto, networking and more
Install
dune-project
Dependency
Authors
Maintainers
Sources
0.3.6.tar.gz
md5=7c636383d146d30ac6f2fa234a6253c8
sha512=c79f3823c5f8f57e5038eb640d487c61168b84aa07c61999d6622ef9fd0c890e2b03b4c6a7cdbbe9352a49e25dda00ac7bb14693cee8e3d7beeed251351a2af0
doc/src/tiny_libs.graphics_ilbm/Ilbm.ml.html
Source file Ilbm.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165(* Claude Code * * Copyright (C) 2026 Yoann Padioleau * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public License * (LGPL) as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. *) (* See Ilbm.mli *) type range = { low : int; high : int; rate : int; active : bool; reverse : bool } type t = { width : int; height : int; planes : int; pixels : Bytes.t; palette : (int * int * int) array; ranges : range list } let steps_per_second (r : range) : float = float_of_int r.rate *. 60. /. 16384. (* a row of a plane: 16 bits a word, padded *) let row_bytes (width : int) : int = (width + 15) / 16 * 2 let plane_rows (t : t) (y : int) : Bytes.t list = List.init t.planes (fun p -> let row = Bytes.make (row_bytes t.width) '\000' in for x = 0 to t.width - 1 do let colour = Char.code (Bytes.get t.pixels ((y * t.width) + x)) in if (colour lsr p) land 1 = 1 then let i = x / 8 in Bytes.set row i (Char.chr (Char.code (Bytes.get row i) lor (0x80 lsr (x mod 8)))) done; row) (*****************************************************************************) (* Writing *) (*****************************************************************************) let u16 b v = Buffer.add_char b (Char.chr ((v lsr 8) land 0xff)); Buffer.add_char b (Char.chr (v land 0xff)) let u32 b v = u16 b ((v lsr 16) land 0xffff); u16 b (v land 0xffff) (* a chunk: its name, its length, its data, and a pad byte to an even length *) let chunk (b : Buffer.t) (id : string) (data : string) : unit = Buffer.add_string b id; u32 b (String.length data); Buffer.add_string b data; if String.length data mod 2 = 1 then Buffer.add_char b '\000' let encode (t : t) : string = let bmhd = Buffer.create 20 in u16 bmhd t.width; u16 bmhd t.height; u16 bmhd 0; u16 bmhd 0; Buffer.add_char bmhd (Char.chr t.planes); Buffer.add_char bmhd '\000' (* no mask *); Buffer.add_char bmhd '\001' (* ByteRun1 *); Buffer.add_char bmhd '\000'; u16 bmhd 0 (* the transparent colour *); Buffer.add_char bmhd '\010'; Buffer.add_char bmhd '\011' (* the pixels' aspect, 10:11, low resolution's *); u16 bmhd t.width; u16 bmhd t.height; let cmap = Buffer.create 96 in Array.iter (fun (r, g, b) -> List.iter (fun v -> Buffer.add_char cmap (Char.chr v)) [ r; g; b ]) t.palette; let body = Buffer.create (t.width * t.height / 2) in for y = 0 to t.height - 1 do List.iter (fun row -> Buffer.add_bytes body (Packbits.encode row)) (plane_rows t y) done; let form = Buffer.create 4096 in Buffer.add_string form "ILBM"; chunk form "BMHD" (Buffer.contents bmhd); chunk form "CMAP" (Buffer.contents cmap); List.iter (fun r -> let c = Buffer.create 8 in u16 c 0; u16 c r.rate; u16 c ((if r.active then 1 else 0) lor if r.reverse then 2 else 0); Buffer.add_char c (Char.chr r.low); Buffer.add_char c (Char.chr r.high); chunk form "CRNG" (Buffer.contents c)) t.ranges; chunk form "BODY" (Buffer.contents body); let file = Buffer.create (Buffer.length form + 8) in chunk file "FORM" (Buffer.contents form); Buffer.contents file (*****************************************************************************) (* Reading *) (*****************************************************************************) let decode (s : string) : t = let n = String.length s in let byte i = if i < n then Char.code s.[i] else failwith "ILBM: cut short" in let get16 i = (byte i lsl 8) lor byte (i + 1) in let get32 i = (get16 i lsl 16) lor get16 (i + 2) in if n < 12 || String.sub s 0 4 <> "FORM" || String.sub s 8 4 <> "ILBM" then failwith "ILBM: not an IFF ILBM file"; let width = ref 0 and height = ref 0 and planes = ref 0 and masking = ref 0 and compression = ref 0 in let palette = ref [||] and ranges = ref [] and body = ref None in (* the chunks, one after the other; the ones we don't know skipped *) let rec chunks i = if i + 8 <= n then begin let id = String.sub s i 4 and len = get32 (i + 4) in let data = i + 8 in (match id with | "BMHD" -> width := get16 data; height := get16 (data + 2); planes := byte (data + 8); masking := byte (data + 9); compression := byte (data + 10) | "CMAP" -> palette := Array.init (len / 3) (fun k -> (byte (data + (3 * k)), byte (data + (3 * k) + 1), byte (data + (3 * k) + 2))) | "CRNG" -> let flags = get16 (data + 4) in ranges := { rate = get16 (data + 2); active = flags land 1 = 1; reverse = flags land 2 = 2; low = byte (data + 6); high = byte (data + 7) } :: !ranges | "BODY" -> body := Some (data, len) | _ -> ()); chunks (data + len + (len mod 2)) end in chunks 12; let w = !width and h = !height and np = !planes in if w = 0 || np = 0 then failwith "ILBM: no BMHD"; let pixels = Bytes.make (w * h) '\000' in (match !body with | None -> failwith "ILBM: no BODY" | Some (start, _) -> let rb = row_bytes w in let src = Bytes.unsafe_of_string s in let pos = ref start in let read_row () = if !compression = 1 then begin let row, next = Packbits.decode src ~pos:!pos ~len:rb in pos := next; row end else begin let row = Bytes.sub src !pos rb in pos := !pos + rb; row end in for y = 0 to h - 1 do for p = 0 to np - 1 do let row = read_row () in for x = 0 to w - 1 do if Char.code (Bytes.get row (x / 8)) land (0x80 lsr (x mod 8)) <> 0 then let i = (y * w) + x in Bytes.set pixels i (Char.chr (Char.code (Bytes.get pixels i) lor (1 lsl p))) done done; (* a mask plane, when there is one: read, not kept *) if !masking = 1 then ignore (read_row ()) done); { width = w; height = h; planes = np; pixels; palette = !palette; ranges = List.rev !ranges } let to_rgba (t : t) : Rgba_image.t = let img = Rgba_image.create ~width:t.width ~height:t.height in for i = 0 to (t.width * t.height) - 1 do let c = Char.code (Bytes.get t.pixels i) in let r, g, b = if c < Array.length t.palette then t.palette.(c) else (0, 0, 0) in Bigarray.Array1.set img.rgba (4 * i) r; Bigarray.Array1.set img.rgba ((4 * i) + 1) g; Bigarray.Array1.set img.rgba ((4 * i) + 2) b; Bigarray.Array1.set img.rgba ((4 * i) + 3) 255 done; img
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>