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_xpm/Xpm.ml.html
Source file Xpm.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(* 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 Xpm.mli *) type color = (int * int * int) option type t = { name : string; colors : (char * color) list; rows : string list } let fail fmt = Printf.ksprintf (fun s -> failwith ("XPM: " ^ s)) fmt (*****************************************************************************) (* Reading *) (*****************************************************************************) (* the position of [sub] in [s] from [from], if any *) let rec find (s : string) (sub : string) (from : int) : int option = if from + String.length sub > String.length s then None else if String.sub s from (String.length sub) = sub then Some from else find s sub (from + 1) (* the C strings of the file, in order, outside the comments *) let strings (text : string) : string list = let n = String.length text in let rec go i acc = if i >= n then List.rev acc else if i + 1 < n && text.[i] = '/' && text.[i + 1] = '*' then match find text "*/" (i + 2) with Some j -> go (j + 2) acc | None -> List.rev acc else if text.[i] = '"' then ( let b = Buffer.create 16 in let rec str j = if j >= n then fail "a string is not closed" else if text.[j] = '"' then j + 1 else if text.[j] = '\\' && j + 1 < n then (Buffer.add_char b text.[j + 1]; str (j + 2)) else (Buffer.add_char b text.[j]; str (j + 1)) in let next = str (i + 1) in go next (Buffer.contents b :: acc)) else go (i + 1) acc in go 0 [] (* the array's name: the identifier before the first "[]" *) let name (text : string) : string = match find text "[]" 0 with | None -> "" | Some j -> let is_ident c = c = '_' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') in let stop = ref j in while !stop > 0 && text.[!stop - 1] = ' ' do decr stop done; let start = ref !stop in while !start > 0 && is_ident text.[!start - 1] do decr start done; String.sub text !start (!stop - !start) let words (s : string) : string list = String.split_on_char ' ' s |> List.concat_map (String.split_on_char '\t') |> List.filter (( <> ) "") let hex (s : string) : int = match int_of_string_opt ("0x" ^ s) with Some v -> v | None -> fail "bad color #%s" s let color_value (v : string) : color = let n = String.length v in match String.lowercase_ascii v with | "none" -> None | "black" -> Some (0, 0, 0) | "white" -> Some (255, 255, 255) | "red" -> Some (255, 0, 0) | "green" -> Some (0, 255, 0) | "blue" -> Some (0, 0, 255) | _ when n = 7 && v.[0] = '#' -> Some (hex (String.sub v 1 2), hex (String.sub v 3 2), hex (String.sub v 5 2)) (* 16 bits a channel: the high byte of each *) | _ when n = 13 && v.[0] = '#' -> Some (hex (String.sub v 1 2), hex (String.sub v 5 2), hex (String.sub v 9 2)) | _ -> fail "color %S not read (only #rrggbb, None and a few names)" v (* "R c #dc281e": the character, then keys and values; the 'c' one *) let color_line (line : string) : char * color = if line = "" then fail "an empty color line"; let rec value = function | "c" :: v :: _ -> color_value v | _ :: rest -> value rest | [] -> fail "no 'c' color for %C" line.[0] in (line.[0], value (words (String.sub line 1 (String.length line - 1)))) let parse (text : string) : t = match strings text with | [] -> fail "no strings: not an XPM file" | header :: rest -> ( match List.map int_of_string_opt (words header) with | Some w :: Some h :: Some ncolors :: Some cpp :: _ -> if cpp <> 1 then fail "%d characters per pixel, only 1 read" cpp; if List.length rest < ncolors + h then fail "%d strings, %d expected" (List.length rest) (ncolors + h); let colors = List.filteri (fun i _ -> i < ncolors) rest |> List.map color_line in let rows = List.filteri (fun i _ -> i >= ncolors && i < ncolors + h) rest in rows |> List.iteri (fun r row -> if String.length row <> w then fail "row %d is %d wide, not %d" r (String.length row) w; String.iter (fun c -> if not (List.mem_assoc c colors) then fail "%C in row %d is not in the palette" c r) row); { name = name text; colors; rows } | _ -> fail "bad header %S, expected: width height colors characters-per-pixel" header) (*****************************************************************************) (* Writing *) (*****************************************************************************) let print (t : t) : string = let w = List.fold_left (fun acc r -> max acc (String.length r)) 0 t.rows in let color (c, v) = match v with None -> Printf.sprintf "%c c None" c | Some (r, g, b) -> Printf.sprintf "%c c #%02x%02x%02x" c r g b in let lines = Printf.sprintf "%d %d %d 1" w (List.length t.rows) (List.length t.colors) :: List.map color t.colors @ t.rows in Printf.sprintf "/* XPM */\nstatic char *%s[] = {\n%s\n};\n" t.name (lines |> List.map (fun l -> "\"" ^ l ^ "\"") |> String.concat ",\n")
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>