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_gif/Gif.ml.html
Source file Gif.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(* 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 Gif.mli *) type frame = { x : int; y : int; patch : Rgba_image.t; delay : float; disposal : int; palette : string; min_code_size : int; lzw : string; } (*****************************************************************************) (* Frames *) (*****************************************************************************) (* the rows of an interlaced frame of [h] rows, in the order they are * stored: every 8th from 0, every 8th from 4, every 4th from 2, every * 2nd from 1 *) let interlaced_rows (h : int) : int array = let passes = [ (0, 8); (4, 8); (2, 4); (1, 2) ] in passes |> List.map (fun (start, step) -> List.init (max 0 ((h - start + step - 1) / step)) (fun k -> start + (k * step))) |> List.concat |> Array.of_list let frames (s : string) : (int * int) * frame list = let len = String.length s in let byte i = if i < len then Char.code s.[i] else failwith "GIF: the file ends early" in let u16 i = byte i lor (byte (i + 1) lsl 8) in if len < 13 || (String.sub s 0 6 <> "GIF87a" && String.sub s 0 6 <> "GIF89a") then failwith "GIF: not a GIF"; let width = u16 6 and height = u16 8 in let palette_size flags = if flags land 0x80 <> 0 then 3 * (1 lsl (1 + (flags land 7))) else 0 in let global_end = 13 + palette_size (byte 10) in let global = if global_end > len then failwith "GIF: the file ends early" else String.sub s 13 (global_end - 13) in (* the sub-blocks starting at [i], joined, and the position after them *) let sub_blocks i = let b = Buffer.create 256 in let rec loop i = let size = byte i in if size = 0 then i + 1 else begin if i + 1 + size > len then failwith "GIF: the file ends early"; Buffer.add_substring b s (i + 1) size; loop (i + 1 + size) end in let after = loop i in (Buffer.contents b, after) in (* the graphic control extension seen last, for the next frame: * (packed, delay, transparent index) *) let rec loop i gce acc = if i >= len then List.rev acc (* no 0x3B: accepted, as browsers do *) else match byte i with | 0x3B -> List.rev acc | 0x21 when byte (i + 1) = 0xF9 -> let gce = (byte (i + 3), u16 (i + 4), byte (i + 6)) in loop (snd (sub_blocks (i + 2))) gce acc | 0x21 -> loop (snd (sub_blocks (i + 2))) gce acc | 0x2C -> let x = u16 (i + 1) and y = u16 (i + 3) and w = u16 (i + 5) and h = u16 (i + 7) in let flags = byte (i + 9) in let local_end = i + 10 + palette_size flags in let palette = if flags land 0x80 <> 0 then String.sub s (i + 10) (local_end - i - 10) else global in if palette = "" then failwith "GIF: a frame without a palette"; let data, after = sub_blocks (local_end + 1) in let indices = Lzw.decode ~min_code_size:(byte local_end) data ~npixels:(w * h) in let packed, delay, transparent = gce in let transparent = if packed land 1 = 1 then transparent else -1 in let rows = if flags land 0x40 <> 0 then interlaced_rows h else Array.init h (fun j -> j) in let patch = Rgba_image.create ~width:w ~height:h in for k = 0 to h - 1 do (* the [k]th row stored is row [rows.(k)] of the patch *) for col = 0 to w - 1 do let index = Char.code (Bytes.get indices ((k * w) + col)) in let o = ((rows.(k) * w) + col) * 4 in if index <> transparent then begin let color c = if (3 * index) + 2 < String.length palette then Char.code palette.[(3 * index) + c] else 0 in patch.rgba.{o} <- color 0; patch.rgba.{o + 1} <- color 1; patch.rgba.{o + 2} <- color 2; patch.rgba.{o + 3} <- 255 end done done; let frame = { x; y; patch; delay = (if delay <= 1 then 0.1 else float delay /. 100.); disposal = (packed lsr 2) land 7; palette; min_code_size = byte local_end; lzw = data } in loop after (0, 0, 0) (frame :: acc) | c -> failwith (Printf.sprintf "GIF: unexpected block 0x%02X at %d" c i) in ((width, height), loop global_end (0, 0, 0) []) (*****************************************************************************) (* Composition *) (*****************************************************************************) let animation (s : string) : (Rgba_image.t * float) list = let (w, h), frames = frames s in let canvas = Rgba_image.create ~width:w ~height:h in let copy (img : Rgba_image.t) : Rgba_image.t = let c = Rgba_image.create ~width:img.width ~height:img.height in Bigarray.Array1.blit img.rgba c.rgba; c in (* the pixels of the frame's patch that are inside the canvas; [f] * gets the offset in the canvas, and in the patch *) let iter_patch (fr : frame) f = for j = 0 to fr.patch.height - 1 do for i = 0 to fr.patch.width - 1 do let cx = fr.x + i and cy = fr.y + j in if cx < w && cy < h then f (((cy * w) + cx) * 4) (((j * fr.patch.width) + i) * 4) done done in frames |> List.map (fun (fr : frame) -> let before = if fr.disposal = 3 then Some (copy canvas) else None in (* the patch's transparent pixels (alpha 0) let the picture * below show through *) let p = fr.patch.rgba in iter_patch fr (fun co po -> if p.{po + 3} <> 0 then for k = 0 to 3 do canvas.rgba.{co + k} <- p.{po + k} done); let snapshot = copy canvas in (match (fr.disposal, before) with | 2, _ -> iter_patch fr (fun co _ -> for k = 0 to 3 do canvas.rgba.{co + k} <- 0 done) | 3, Some c -> Bigarray.Array1.blit c.rgba canvas.rgba | _ -> ()); (snapshot, fr.delay)) let decode (s : string) : Rgba_image.t = match animation s with | (first, _) :: _ -> first | [] -> failwith "GIF: no frame"
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>