oculante

repository·master·Indexed 23 days ago

https://github.com/woelper/oculante

A minimalistic, fast, hardware-accelerated, and privacy-respecting image viewer (version 0.9.2) featuring image analysis, non-destructive editing, and file management. It supports a wide array of formats including standard (JPEG, PNG, WebP, AVIF, JXL), advanced (HDR, EXR, DICOM, PSD, RAW), and specialized formats. The tool includes a CLI for network listening and stdin piping, and is available via Cargo, Flatpak, and various OS package managers.

Tokens
13.2K
Snippets
22
Records
77
Agent score
81%

What's inside oculante

  1. Overview of Oculante features

    master

    Oculante is a hardware-accelerated image viewer with the following core capabilities:

    • Image Analysis: Pixel position, color info, color histogram, and precise picking.
    • Editing: Non-destructive editing, painting, and an operator stack. Includes lossless JPEG editing (crop, rotate, mirror).
    • File Management: Built-in manager to bookmark directories and manage files. Includes a dedicated comparison mode that preserves zoom/position when flipping between images.
    • Color Handling: Supports individual RGBA channel inspection and unassociated/unpremultiplied alpha display.
    • UI Features: Dark/Light/System themes, Zen Mode, Always on Top, and Fit image to view.
    • Metadata: Support for EXIF data and saving metafile edit stacks.
  2. Oculante Licensing and Usage

    master

    Oculante is licensed under the MIT license. However, certain components, specifically the LUTs located in res/LUT, are under the GPL license.

    Important for developers: If you wish to use Oculante in a project without publishing your own source code, you must remove all GPL-licensed components and their references from the project.

  3. Configure extended language support

    master

    Oculante supports languages such as Arabic, Chinese, Japanese, Korean, and Taiwanese, provided your system has the appropriate font families installed. A recommended font family for broad coverage is Noto Sans.

    Supported Font Families by Language:

    Simplified Chinese

    • Heiti SC, Songti SC, Noto Sans CJK SC, Noto Sans SC, WenQuanYi Zen Hei, SimSun, PingFang SC, Source Han Sans CN

    Traditional Chinese

    • Source Han Sans HK

    Japanese

    • Noto Sans JP, Noto Sans CJK JP, Source Han Sans JP, MS Gothic

    Korean

    • Source Han Sans KR

    Taiwanese

    • Source Han Sans TW

    Arabic

    • Noto Sans Arabic, Amiri, Lateef, Al Tarikh, Segoe UI
  4. Use dialogs for one-time styled modals

    master

    For use cases where you want to both open and style a modal as a single, one-time action (such as displaying a function's result), use the dialog API. This is useful for transient notifications or status updates.

    There are two ways to approach this:

    1. Immediate show: Call modal.show_dialog() within your frame loop.
    2. Builder pattern: Use modal.dialog() to configure the dialog's properties (title, body, icon) and then call .open() to trigger it.
    // Option 1: Show the dialog immediately (calling every frame)
    let modal = Modal::new(ctx, "my_dialog");
    // ...
    modal.show_dialog();
    
    // Option 2: Builder pattern (happens once)
    if let Ok(data) = my_function() {
        modal.dialog()
            .with_title("my_function's result is...")
            .with_body("my_function was successful!")
            .with_icon(Icon::Success)
            .open()
    }
  5. Implement normal modal usage with egui-modal

    master

    To use egui-modal in a standard way, you must instantiate a Modal every frame within your egui update loop. You then use modal.show() to define the modal's content and modal.open() to trigger its visibility when a condition is met (e.g., a button click).

    Inside the show closure, you can use helper methods like title, frame, body, and buttons to apply consistent styling, though you can also provide custom UI directly.

    /* calling every frame */
    
    let modal = Modal::new(ctx, "my_modal");
    
    // What goes inside the modal
    modal.show(|ui| {
        // these helper functions help set the ui based on the modal's
        // set style, but they are not required and you can put whatever
        // ui you want inside [`.show()`]
        modal.title(ui, "Hello world!");
        modal.frame(ui, |ui| {
            modal.body(ui, "This is a modal.");
        });
        modal.buttons(ui, |ui| {
            // After clicking, the modal is automatically closed
            if modal.button(ui, "close").clicked() {
                println!("Hello world!")
            };
        }); 
    });
    
    if ui.button("Open the modal").clicked() {
        // Show the modal
        modal.open();
    }
  6. Uninstall Oculante

    master

    To uninstall Oculante, delete the executable file and the corresponding data folder for your OS:

    • Windows: ~/AppData/Local/.oculante
    • Mac: ~/Library/Application Support/oculante
    • Linux & BSD: ~/.local/share/oculante
  7. Configure build dependencies for Oculante

    master

    If you are building Oculante from source, you will need the following dependencies:

    Linux (Debian):

    sudo apt-get install libxcb-shape0-dev libxcb-xfixes0-dev libgtk-3-dev libasound2-dev nasm cmake

    Windows:

    Mac:

    • brew install nasm cmake
    • If building on macOS, you may need to set the following environment variable: export SHADERC_LIB_DIR=/opt/homebrew/lib
  8. Install Oculante via various package managers

    master

    Oculante can be installed using several methods depending on your operating system and preferred package manager.

    Cargo (Rust)

    cargo install oculante

    Linux

    • Arch Linux: pacman -S oculante or paru -S oculante-git
    • NixOS: Add pkgs.oculante to your environment.systemPackages.
    • openSUSE: zypper install oculante
    • Flatpak: flatpak install flathub io.github.woelper.Oculante

    BSD

    • FreeBSD: pkg install oculante
    • NetBSD: pkgin install oculante (Note: NetBSD builds use glsl-to-spirv instead of shaderc)

    Windows

    • Scoop: scoop install extras/oculante
  9. Configure HEIF decoder security limits

    master

    Oculante allows fine-grained control over libheif security limits via DecoderSettings and HeifLimits. This is useful for preventing resource exhaustion when decoding potentially malicious HEIF files.

    Limits can be set using the Limit enum:

    • Limit::Default: Uses the library's default.
    • Limit::NoLimit: Disables the specific limit.
    • Limit::U64(u64): Sets a specific 64-bit unsigned integer limit.
    • Limit::U32(u32): Sets a specific 32-bit unsigned integer limit.

    Available limit categories include image_size_pixels, number_of_tiles, bayer_pattern_pixels, items, color_profile_size, memory_block_size, components, iloc_extents_per_item, size_entity_group, and children_per_box.

  10. Handle image frame updates in Oculante

    master

    Oculante processes various Frame types to update the application state and display images. The application handles several frame variants to manage image buffers, metadata, and texture updates:

    • Image-bearing frames: Variants like AnimationStart, Still, EditResult, CompareResult, Animation, and ImageCollectionMember trigger an update to state.image_geometry.dimensions, load the image into state.current_image, and update the state.current_texture using set_image.
    • Texture updates: The UpdateTexture frame allows refreshing the current texture. It prefers using state.edit_state.result_pixel_op if a pixel operation result is available; otherwise, it falls back to the state.current_image.
    • Metadata management: If a frame is not an Animation, state.image_metadata is cleared.

    When an image is successfully loaded, state.new_image_loaded is set to true, which can be used to reset UI state like aspect ratios.

    match frame {
        Frame::AnimationStart(img)
        | Frame::Still(img)
        | Frame::EditResult(img)
        | Frame::CompareResult(img, _)
        | Frame::Animation(img, _)
        | Frame::ImageCollectionMember(img) => {
            state.image_geometry.dimensions = img.dimensions();
            state.current_texture.set_image(&img, gfx, &state.persistent_settings);
            state.current_image = Some(img);
            state.new_image_loaded = true;
        }
        Frame::UpdateTexture => {
            // ... updates texture from edit_state or current_image
        }
        _ => {}
    }
  11. Oculante image loading logic

    master

    When launching Oculante, the application processes input paths as follows:

    • Single Path (File): Loads the specific image.
    • Single Path (Directory): Finds the first image in the directory using find_first_image_in_directory and loads it.
    • Multiple Paths:
      • If all paths are files, Oculante treats them as a fixed set of images for the scrubber (no directory traversal).
      • If paths are directories, it loads the first image from the first directory and allows traversal.

    If --stdin is used, the application reads the entire stream into memory and attempts to decode it using image::load_from_memory.