dithers 0.1.0

A image dithering toolkit for applying various dithering algorithms to images.
Documentation
##################################################
# Variables
#

rust_env := "rustup show"
rust_edition := "2024"
open := if os() == "linux" { "xdg-open" } else if os() == "macos" { "open" } else { "start \"\" /max" }
app_name := "dithers"
crate_name := "dithers"
args := ""
project_directory := justfile_directory()
release := `git describe --tags --always`
version := "0.1.0"
url := "https://github.com/tschinz/dithers"

##################################################
# COMMANDS
#

# List all commands
@default:
    just --list

# Information about the environment
@info:
    echo "Environment Informations\n------------------------\n"
    echo "OS   : {{ os() }}({{ arch() }})"
    echo "Open : {{ open }}"
    echo "Rust :"
    echo "`{{ rust_env }}`"

# Install dependencies
install:
    curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
    cargo install --locked trunk
    cargo install cargo-sbom
    brew install trivy

# install the release version (default is the latest)
install-release release=release:
    cargo install --git {{ url }} --tag {{ release }}

# install the nightly release
install-nightly:
    cargo install --git {{ url }}

# Build and copy the release version of the program
build:
    cargo build --release
    mkdir -p bin && cp target/release/{{ app_name }} bin/

# Run the program in debug mode
run args=args:
    cargo run -- {{ args }}

# create a release version of the program
changelog version=version:
    git cliff --tag {{ version }}

# Test the program in debug mode in folder test

# Test the program with comprehensive Rust tests
test:
    cargo test

# Run integration tests only
test-integration:
    cargo test --test dither_integration_tests

# Run unit tests only
test-unit:
    cargo test --lib

# Generate sample images for all algorithms (optional visual testing)
generate-samples:
    @echo "Generating samples for all dithering algorithms..."

    # Error diffusion algorithms
    cargo run -- -i test/in/glace-1280_853.jpg -o test/out/sample-floyd-steinberg-mono.jpg -d floyd-steinberg -c monochrome
    cargo run -- -i test/in/glace-1280_853.jpg -o test/out/sample-floyd-steinberg-8c.jpg -d floyd-steinberg -c color8
    cargo run -- -i test/in/glace-1280_853.jpg -o test/out/sample-floyd-steinberg-16c.jpg -d floyd-steinberg -c color16

    cargo run -- -i test/in/glace-1280_853.jpg -o test/out/sample-jarvis-mono.jpg -d jarvis -c monochrome
    cargo run -- -i test/in/glace-1280_853.jpg -o test/out/sample-jarvis-8c.jpg -d jarvis -c color8

    cargo run -- -i test/in/glace-1280_853.jpg -o test/out/sample-atkinson-mono.jpg -d atkinson -c monochrome
    cargo run -- -i test/in/glace-1280_853.jpg -o test/out/sample-atkinson-8c.jpg -d atkinson -c color8

    cargo run -- -i test/in/glace-1280_853.jpg -o test/out/sample-stucki-mono.jpg -d stucki -c monochrome
    cargo run -- -i test/in/glace-1280_853.jpg -o test/out/sample-stucki-16c.jpg -d stucki -c color16

    cargo run -- -i test/in/glace-1280_853.jpg -o test/out/sample-burkes-8c.jpg -d burkes -c color8

    cargo run -- -i test/in/glace-1280_853.jpg -o test/out/sample-sierra-mono.jpg -d sierra -c monochrome
    cargo run -- -i test/in/glace-1280_853.jpg -o test/out/sample-sierra-16c.jpg -d sierra -c color16

    cargo run -- -i test/in/glace-1280_853.jpg -o test/out/sample-two-row-sierra-8c.jpg -d two-row-sierra -c color8

    cargo run -- -i test/in/glace-1280_853.jpg -o test/out/sample-sierra-lite-mono.jpg -d sierra-lite -c monochrome

    cargo run -- -i test/in/glace-1280_853.jpg -o test/out/sample-simple2d-8c.jpg -d simple2-d -c color8

    # Ordered dithering (Bayer matrices)
    cargo run -- -i test/in/glace-1280_853.jpg -o test/out/sample-bayer2x2-mono.jpg -d bayer2x2 -c monochrome
    cargo run -- -i test/in/glace-1280_853.jpg -o test/out/sample-bayer2x2-8c.jpg -d bayer2x2 -c color8

    cargo run -- -i test/in/glace-1280_853.jpg -o test/out/sample-bayer4x4-mono.jpg -d bayer4x4 -c monochrome
    cargo run -- -i test/in/glace-1280_853.jpg -o test/out/sample-bayer4x4-16c.jpg -d bayer4x4 -c color16

    cargo run -- -i test/in/glace-1280_853.jpg -o test/out/sample-bayer8x8-mono.jpg -d bayer8x8 -c monochrome
    cargo run -- -i test/in/glace-1280_853.jpg -o test/out/sample-bayer8x8-8c.jpg -d bayer8x8 -c color8

    # No dithering (palette quantization only)
    cargo run -- -i test/in/glace-1280_853.jpg -o test/out/sample-none-mono.jpg -d none -c monochrome
    cargo run -- -i test/in/glace-1280_853.jpg -o test/out/sample-none-8c.jpg -d none -c color8

    @echo "✓ Sample images generated in test/out/"
    @echo "Generated $(ls test/out/sample-*.jpg | wc -l | tr -d ' ') sample images covering all algorithms"

# Clean up generated sample files
clean-samples:
    rm -f test/out/sample-*.jpg

# Check code with clippy
clippy:
    cargo clippy -- -D warnings

# Run rustfmt with custom configuration
rustfmt:
    find {{ invocation_directory() }} -name \*.rs -exec rustfmt {} \;

# Generate and open rustdoc documentation
doc:
    @echo "Generating rustdoc documentation..."
    cargo doc --no-deps --document-private-items
    @echo "✓ Documentation generated"
    @echo "Opening documentation in browser..."
    {{ open }} target/doc/{{ crate_name }}/index.html

# Generate rustdoc documentation without opening
doc-build:
    @echo "Generating rustdoc documentation..."
    cargo doc --no-deps --document-private-items
    @echo "✓ Documentation generated at target/doc/{{ crate_name }}/index.html"

# Generate SBOM for Dependency Track
sbom:
    cargo sbom --output-format cyclone_dx_json_1_6 > target/sbom-cyclone_dx_1_6.json

# Upload SBOM to Dependency Track (requires DT_API_KEY, DT_PROJECT_UUID, DT_BASE_URL env vars)
sbom-upload:
    #!/usr/bin/env bash
    set -euo pipefail
    echo "Uploading SBOM to Dependency Track..."
    # Load .env file if it exists
    if [[ -f .env ]]; then
        echo "Loading configuration from .env file..."
        export $(grep -v '^#' .env | grep -v '^$' | xargs)
    fi
    if [[ -z "${DT_API_KEY:-}" ]] || [[ -z "${DT_PROJECT_UUID:-}" ]] || [[ -z "${DT_BASE_URL:-}" ]]; then
        echo "Error: Required environment variables not set:"
        echo "  DT_API_KEY - Your Dependency Track API key"
        echo "  DT_PROJECT_UUID - Your project UUID"
        echo "  DT_BASE_URL - Your Dependency Track base URL"
        echo ""
        echo "Example:"
        echo "  export DT_BASE_URL=https://dt-api.zahno.dev"
        echo "  export DT_API_KEY=your_api_key_here"
        echo "  export DT_PROJECT_UUID=your_project_uuid_here"
        echo "  just sbom-upload"
        exit 1
    fi
    just sbom
    curl -X POST "${DT_BASE_URL}/api/v1/bom" \
        -H "X-Api-Key: ${DT_API_KEY}" \
        -H "Content-Type: multipart/form-data" \
        -F "project=${DT_PROJECT_UUID}" \
        -F "bom=@target/sbom-cyclone_dx_1_6.json"
    echo "✓ SBOM uploaded successfully to Dependency Track"

# Trivy comprehensive security scan (alias for backwards compatibility)
trivy:
    trivy fs --scanners vuln,secret,misconfig --format table .

# output the help information
help:
    cargo run -- -h