Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 9 additions & 26 deletions src/utils_gif.ml
Original file line number Diff line number Diff line change
Expand Up @@ -15,39 +15,21 @@ let color_table_of_palette (p : Palette.t) : ColorTable.t =
let b = rgb land 0xFF in
(r, g, b))

let pad_palette_to_power_of_two (colors : ColorTable.t) : ColorTable.t =
let len = Array.length colors in
let next_pow2 n =
let rec loop x = if x >= n then x else loop (x * 2) in
loop 1
in
let target_len = min 256 (next_pow2 len) in
Array.init target_len (fun i -> if i < len then colors.(i) else (0, 0, 0))

let capture_frame (screen : Screen.t) (fb : Framebuffer.t) =
let width, height = Screen.dimensions screen in
let scale = Screen.scale screen in
let palette = Screen.palette screen in
let palette_size = Palette.size palette in

let scaled_width = width * scale in
let scaled_height = height * scale in

let size = scaled_width * scaled_height in

let colors =
palette |> color_table_of_palette |> pad_palette_to_power_of_two
in

let color_depth =
let len = Array.length colors in
let rec bits_needed n b =
if n <= 1 then b else bits_needed (n / 2) (b + 1)
in
min 8 (max 2 (bits_needed (len - 1) 1))
in
let colors = palette |> color_table_of_palette in

let pixels =
List.init size (fun idx ->
Array.init size (fun idx ->
let x = idx mod scaled_width in
let y = idx / scaled_width in
let src_x = x / scale in
Expand All @@ -59,14 +41,15 @@ let capture_frame (screen : Screen.t) (fb : Framebuffer.t) =
failwith
(Printf.sprintf "Invalid pixel coordinate (%d,%d)" src_x src_y)
in
if v < 0 || v > Palette.size palette then
if v < 0 || v > palette_size then
failwith
(Printf.sprintf "Framebuffer value %d out of byte range at (%d,%d)"
v src_x src_y);
(Z.of_int v, color_depth))
v)
in

let flattened = Lzw.flatten_codes 8 pixels in
let compressed = Lzw.encode flattened color_depth in
(* Claudius attempts to run at 60 fps, and GIF delay time is specified as multiples
of 1/100th of a second, so the closest we can do is 2 (50 FPS). *)
let delay_time = Some 2 in

Image.v (scaled_width, scaled_height) colors compressed color_depth true
Image.of_pixels ~delay_time (scaled_width, scaled_height) colors pixels
4 changes: 0 additions & 4 deletions src/utils_gif.mli
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,6 @@ val timestamp : string -> string
val color_table_of_palette : Palette.t -> ColorTable.t
(** [color_table_of_palette palette] converts a palette to a GIF color table. *)

val pad_palette_to_power_of_two : ColorTable.t -> ColorTable.t
(** [pad_palette_to_power_of_two table] extends a color table to the next power
of two, padding with black as needed, up to maximum 256 colors. *)

val capture_frame : Screen.t -> Framebuffer.t -> Image.t
(** [capture_frame screen framebuffer] captures the current framebuffer contents
as a compressed GIF image. Raises [Failure] if framebuffer contains invalid
Expand Down