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_imaging/Brush.ml.html
Source file Brush.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(* 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 Brush.mli *) type t = { radius : float; hardness : float; opacity : float } let coverage (b : t) (d : float) : float = let inner = b.radius *. b.hardness in if d <= inner then 1. else if d >= b.radius then 0. else (* a smooth step from 1 at the inner radius to 0 at the edge *) let t = (d -. inner) /. (b.radius -. inner) in 1. -. (t *. t *. (3. -. (2. *. t))) let spacing (b : t) ((x0, y0) : float * float) ((x1, y1) : float * float) : (float * float) list = let step = Float.max 0.5 (b.radius /. 2.) in let dx = x1 -. x0 and dy = y1 -. y0 in let n = int_of_float (sqrt ((dx *. dx) +. (dy *. dy)) /. step) in List.init (n + 1) (fun i -> let t = if n = 0 then 0. else float_of_int i *. step /. sqrt ((dx *. dx) +. (dy *. dy)) in (x0 +. (t *. dx), y0 +. (t *. dy))) (* the pixels a dab at (cx, cy) touches, and its coverage of each *) let dab (b : t) (w : int) (h : int) ((cx, cy) : float * float) (f : int -> int -> float -> unit) : unit = let r = int_of_float (Float.ceil b.radius) in let ix = int_of_float cx and iy = int_of_float cy in for y = max 0 (iy - r - 1) to min (h - 1) (iy + r + 1) do for x = max 0 (ix - r - 1) to min (w - 1) (ix + r + 1) do let dx = float_of_int x +. 0.5 -. cx and dy = float_of_int y +. 0.5 -. cy in let c = coverage b (sqrt ((dx *. dx) +. (dy *. dy))) in if c > 0. then f x y c done done let stroke_mask (b : t) (w : int) (h : int) (points : (float * float) list) : Mask.t = let m = Mask.empty w h in List.iter (fun p -> dab b w h p (fun x y c -> let v = int_of_float (Float.round (c *. 255.)) in if v > Mask.get m x y then Bytes.set m.alpha ((y * w) + x) (Char.chr v))) points; m (* the stroke's mask at the brush's opacity, within the selection *) let limited (b : t) (selection : Mask.t option) (m : Mask.t) : Mask.t = let m = { m with alpha = Bytes.map (fun c -> Char.chr (int_of_float (Float.round (float_of_int (Char.code c) *. b.opacity)))) m.alpha } in match selection with Some s -> Mask.intersect m s | None -> m let paint (b : t) (colour : int * int * int) ?selection (img : Pixels.image) (points : (float * float) list) : Pixels.image = Composite.fill (limited b selection (stroke_mask b img.width img.height points)) colour img let selected (selection : Mask.t option) x y = match selection with Some s -> float_of_int (Mask.get s x y) /. 255. | None -> 1. let airbrush (b : t) ((r, g, bl) : int * int * int) ~(flow : float) ?selection (img : Pixels.image) (points : (float * float) list) : Pixels.image = let out = Pixels.copy img in List.iter (fun p -> dab b img.width img.height p (fun x y c -> let a = c *. flow *. b.opacity *. selected selection x y in (* the alpha too: sprayed on a layer's transparent pixels, the paint builds up there as well *) List.iteri (fun ch v -> let old = float_of_int (Pixels.get out x y ch) in Pixels.set out x y ch (Pixels.clamp (int_of_float (Float.round (old +. ((float_of_int v -. old) *. a)))))) [ r; g; bl; 255 ])) points; out let stamp (b : t) ~(source : Pixels.image) ~(offset : int * int) ?selection (img : Pixels.image) (points : (float * float) list) : Pixels.image = let dx, dy = offset in let shifted = Pixels.copy img in for y = 0 to img.height - 1 do for x = 0 to img.width - 1 do for c = 0 to 2 do Pixels.set shifted x y c (Pixels.get source (x + dx) (y + dy) c) done done done; Composite.through (limited b selection (stroke_mask b img.width img.height points)) ~before:img ~after:shifted let smudge (b : t) ~(strength : float) ?selection (img : Pixels.image) (points : (float * float) list) : Pixels.image = let out = Pixels.copy img in let rec go = function | (px, py) :: ((cx, cy) :: _ as rest) -> (* this dab takes the colours under the one before: all of them read first, then written, the dab's pixels only *) let ox = int_of_float (Float.round (px -. cx)) and oy = int_of_float (Float.round (py -. cy)) in let changes = ref [] in dab b img.width img.height (cx, cy) (fun x y c -> let a = c *. strength *. b.opacity *. selected selection x y in for ch = 0 to 2 do let old = float_of_int (Pixels.get out x y ch) and carried = float_of_int (Pixels.get out (x + ox) (y + oy) ch) in changes := (x, y, ch, Pixels.clamp (int_of_float (Float.round (old +. ((carried -. old) *. a))))) :: !changes done); List.iter (fun (x, y, ch, v) -> Pixels.set out x y ch v) !changes; go rest | _ -> () in go points; out
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>