Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Page
Library
Module
Module type
Parameter
Class
Class type
Source
app.ml1 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 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289open Tsdl open Tsdl_image open Formats.Manager open Logging let min_win_w, min_win_h = (400, 300) let owin_w, owin_h = (ref 0, ref 0) let app_data_path = match Scope_sites.Sites.app_data with | [] -> fatal rc_Error "No app data path provided" | h :: _ -> h let init_app () : Sdl.window * Sdl.renderer = Sdl.init Sdl.Init.video |> ignore ; let window = Sdl.create_window "Scope Image Viewer" ~x:Sdl.Window.pos_centered ~y:Sdl.Window.pos_centered ~w:800 ~h:600 Sdl.Window.shown |> Result.get_ok in owin_w := 800 ; owin_h := 600 ; Sdl.set_window_resizable window true ; Sdl.set_window_minimum_size window ~w:min_win_w ~h:min_win_h |> ignore ; let icon_path = app_data_path ^ "/logo.png" in let surface = Image.load icon_path |> Result.get_ok in Sdl.set_window_icon window surface ; Sdl.free_surface surface ; let renderer = Sdl.create_renderer window ~index:(-1) ~flags:Sdl.Renderer.accelerated |> Result.get_ok in (window, renderer) type settings = {mutable scale: float; mutable offset: int * int} let min_zoom, max_zoom = (0.1, 100.) let checker_tex = ref None let get_checker_texture renderer tile_size = match !checker_tex with | Some t -> t | None -> let surf = Sdl.create_rgb_surface_with_format ~w:tile_size ~h:tile_size ~depth:32 Sdl.Pixel.format_argb8888 |> Result.get_ok in let pixels = Sdl.get_surface_pixels surf Bigarray.int32 in for y = 0 to tile_size - 1 do for x = 0 to tile_size - 1 do let is_white = ((x / (tile_size / 2)) + (y / (tile_size / 2))) mod 2 = 0 in let color = if is_white then Int32.of_string "0xFFFFFFFF" (* white *) else Int32.of_string "0xFFCCCCCC" (* light gray *) in Bigarray.Array1.set pixels ((y * tile_size) + x) color done done ; let tex = Sdl.create_texture_from_surface renderer surf |> Result.get_ok in Sdl.free_surface surf ; checker_tex := Some tex ; tex let draw_checker_background renderer checker_tex win_w win_h = let _, _, (tw, th) = Sdl.query_texture checker_tex |> Result.get_ok in let rec loop_y y = if y < win_h then ( let rec loop_x x = if x < win_w then ( let dst = Sdl.Rect.create ~x ~y ~w:tw ~h:th in Sdl.render_copy renderer ~dst checker_tex |> ignore ; loop_x (x + tw) ) in loop_x 0 ; loop_y (y + th) ) in loop_y 0 let clear window renderer = let win_w, win_h = Sdl.get_window_size window in Sdl.render_clear renderer |> ignore ; draw_checker_background renderer (get_checker_texture renderer 16) win_w win_h exception InvalidImage let draw_texture (window : Sdl.window) (renderer : Sdl.renderer) (img : Formats.Format.format) (texture : Sdl.texture option) (settings : settings) = (* compute scaled dimensions *) let scaled_w = int_of_float (float img#width *. settings.scale) in let scaled_h = int_of_float (float img#height *. settings.scale) in (* get current window size *) let win_w, win_h = (max img#width min_win_w, max img#height min_win_h) in if win_w <> !owin_w || win_h <> !owin_h then ( Sdl.set_window_size window ~w:win_w ~h:win_h ; owin_w := win_w ; owin_h := win_h ) ; (* calculate top-left corner to center the image *) let dst_x = ((win_w - scaled_w) / 2) + fst settings.offset in let dst_y = ((win_h - scaled_h) / 2) + snd settings.offset in let dst_rect = Sdl.Rect.create ~x:dst_x ~y:dst_y ~w:scaled_w ~h:scaled_h in _log Log_Debug "%s %d %d (scaled %.2f)\n" img#filename img#width img#height settings.scale ; let tex = match texture with | Some tex -> tex | None -> ( match img#to_surf with | Some surf -> let tex = Sdl.create_texture_from_surface renderer surf |> Result.get_ok in Sdl.free_surface surf ; tex | None -> raise InvalidImage ) in clear window renderer ; Sdl.render_copy renderer ~dst:dst_rect tex |> ignore ; Sdl.render_present renderer ; tex let is_plus event = let key = Sdl.Event.(get event keyboard_keycode) in let mods = Sdl.get_mod_state () in (key = Sdl.K.equals && mods land Sdl.Kmod.shift <> 0) || key = Sdl.K.kp_plus let default_settings settings = settings.scale <- 1. ; settings.offset <- (0, 0) let quit () = Sdl.quit () ; exit 0 let remove_from_array (arr : 'a array ref) (idx : int) : unit = let n = Array.length !arr in if idx < 0 || idx >= n then invalid_arg "remove_from_array: index out of bounds" ; arr := Array.init (n - 1) (fun i -> if i < idx then !arr.(i) else !arr.(i + 1) ) let main_loop window renderer (image_paths : string list) : unit = let settings = {scale= 1.; offset= (0, 0)} in if image_paths = [] then () else let image_paths = ref (Array.of_list image_paths) in let n = Array.length !image_paths in let loaded_imgs = ref (Array.make n None) in let loaded_textures = ref (Array.make n None) in let idx = ref 0 in let rec draw_at ?(present_after_clear = true) ?(update_name : bool = false) i = if Array.for_all (fun i -> match i with Some (Error _) -> true | _ -> false) !loaded_imgs then quit () else ( clear window renderer ; if present_after_clear then Sdl.render_present renderer ; match !loaded_imgs.(i) with | Some (Ok img) -> if update_name then Sdl.set_window_title window (!image_paths.(i) ^ " - Scope Image File Viewer") ; draw_texture window renderer img !loaded_textures.(i) settings |> ignore | Some (Error _) -> remove_from_array loaded_imgs i ; remove_from_array loaded_textures i ; remove_from_array image_paths i ; draw_at ~update_name ~present_after_clear i | None -> ( match format_image !image_paths.(i) with | Ok img -> ( if update_name then Sdl.set_window_title window (!image_paths.(i) ^ " - Scope Image File Viewer") ; try let tex = Some (draw_texture window renderer img None settings) in !loaded_imgs.(i) <- Some (Ok img) ; !loaded_textures.(i) <- tex with InvalidImage -> remove_from_array image_paths i ; remove_from_array loaded_imgs i ; remove_from_array loaded_textures i ; draw_at ~present_after_clear ~update_name i ) | Error s -> _log Log_Error "Skipping invalid image '%s': %s\n" !image_paths.(i) s ; remove_from_array image_paths i ; remove_from_array loaded_imgs i ; remove_from_array loaded_textures i ; draw_at ~present_after_clear ~update_name i ) ) in draw_at ~present_after_clear:true ~update_name:true !idx ; let break = ref false in let event = Sdl.Event.create () in let dragging = ref false in let last_mouse_pos = ref (0, 0) in while not !break do if Sdl.poll_event (Some event) then match Sdl.Event.(get event typ) with | t when t = Sdl.Event.quit -> break := true | t when t = Sdl.Event.key_down -> ( match Sdl.Event.(get event Sdl.Event.keyboard_keycode) with | k when k = Sdl.K.right || k = Sdl.K.down -> let new_idx = if !idx < n - 1 then !idx + 1 else 0 in if new_idx <> !idx then ( idx := new_idx ; default_settings settings ; draw_at ~update_name:true !idx ) | k when k = Sdl.K.left || k = Sdl.K.up -> let new_idx = if !idx > 0 then !idx - 1 else n - 1 in if new_idx <> !idx then ( idx := new_idx ; default_settings settings ; draw_at ~update_name:true !idx ) | k when k = Sdl.K.escape -> break := true | _ when is_plus event -> settings.scale <- min max_zoom (settings.scale *. 1.25) ; draw_at !idx | k when k = Sdl.K.minus || k = Sdl.K.kp_minus -> settings.scale <- max min_zoom (settings.scale /. 1.25) ; draw_at !idx | _ -> () ) | t when t = Sdl.Event.mouse_button_down -> let x = Sdl.Event.(get event mouse_button_x) and y = Sdl.Event.(get event mouse_button_y) in if Sdl.Event.(get event mouse_button_button) = Sdl.Button.left then ( dragging := true ; last_mouse_pos := (x, y) ) | t when t = Sdl.Event.mouse_button_up -> if Sdl.Event.(get event mouse_button_button) = Sdl.Button.left then dragging := false | t when t = Sdl.Event.mouse_motion -> if !dragging then ( let x = Sdl.Event.(get event mouse_motion_x) and y = Sdl.Event.(get event mouse_motion_y) in let lx, ly = !last_mouse_pos in let dx = x - lx and dy = y - ly in let ox, oy = settings.offset in settings.offset <- (ox + dx, oy + dy) ; last_mouse_pos := (x, y) ; draw_at ~present_after_clear:false !idx ) | _ -> () done ; quit ()