{
  "markdown": "# imgcli — command-line image conversion & processing in C\n\n[![Install on Smithery](https://smithery.ai/badge/swperb/imgcli)](https://smithery.ai/servers/swperb/imgcli)\n[![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/swperb/imgcli/badge)](https://scorecard.dev/viewer/?uri=github.com/swperb/imgcli)\n[![Flawfinder C security scan](https://github.com/swperb/imgcli/actions/workflows/flawfinder.yml/badge.svg)](https://github.com/swperb/imgcli/actions/workflows/flawfinder.yml)\n\n**ImageMagick's power, ffmpeg's syntax, zero dependencies.** A tiny C binary that\nconverts, resizes, crops, filters, and composites images — PNG, JPEG, BMP, TGA,\nGIF, PPM, QOI — driven by an ffmpeg-style filtergraph. No system libraries;\ncompiles anywhere a C11 compiler exists.\n\n> ⚡ **~8× faster than ImageMagick** on common resize/filter ops, in a **232 KB**\n> single binary (~470× smaller) — [see the benchmarks](#benchmarks). A lightweight\n> alternative to ImageMagick `convert` or `ffmpeg` for still images.\n\n![imgcli demo](docs/img/demo.gif)\n\n```sh\n# Convert + resize + filter in one pass:\nimgcli -i photo.jpg -vf \"scale=1024:-1,grayscale,contrast=1.2,gblur=1.5\" out.png\n```\n\n**What it does:** format conversion · resize / scale · crop · pad · rotate · flip ·\nbrightness / contrast / saturation / gamma / hue · grayscale · sepia · invert ·\nthreshold · box & Gaussian blur · sharpen · Sobel edge detect · emboss · custom\nconvolution kernels · alpha-composite overlay · draw boxes · solid fills.\n\n**Design:** the same paradigm as ffmpeg — *decode → normalize to one common frame\nformat (RGBA) → run a comma-separated filtergraph → encode by output extension.*\n\n## Benchmarks\n\nimgcli vs ImageMagick on an Apple Silicon Mac (single-threaded, warm cache).\nReproduce with `make && bench/bench.sh` — see [bench/RESULTS.md](bench/RESULTS.md)\nfor the full method and caveats.\n\n| Operation | imgcli | ImageMagick | speedup |\n| --- | ---: | ---: | ---: |\n| PNG→JPEG + resize (one-shot) | **1.6 ms** | 4.7 ms | **2.9×** |\n| Resize 1200×1200 → 300w | **6.0 ms** | 51.6 ms | **8.6×** |\n| Grayscale 1200×1200 | **48.4 ms** | 389 ms | **8.0×** |\n\n| Footprint | imgcli | ImageMagick |\n| --- | ---: | ---: |\n| Install size | **232 KB** | ~109 MB (≈470× larger) |\n| Dependencies | **0** (libc only) | 17 packages |\n\nImageMagick is a far broader 16-bit/HDRI toolkit with 200+ formats; imgcli is a\nlean 8-bit pipeline that wins on speed, size, and startup — the shape that\nmatters when an agent or script spawns one process per task.\n\n## Dependencies & formats\n\nThe **default build is dependency-free** — a single binary that links only the\nsystem C library (`libc`). Decoding/encoding is handled by the bundled,\npublic-domain [stb](https://github.com/nothings/stb) and\n[qoi](https://github.com/phoboslab/qoi) single-header libraries, compiled\ndirectly in. Nothing to `apt install`, no shared libraries, no version hell.\n\n- **Built-in formats — no dependencies:** PNG, JPEG, BMP, TGA, GIF, PPM, QOI.\n  Other formats that can be implemented in plain C (e.g. TIFF) will also be built\n  in and keep the binary dependency-free.\n- **Formats that need external libraries — opt-in only:** **WebP, AVIF, HEIC, and JPEG XL**\n  cannot be decoded without large external libraries (libwebp, libavif, libheif, libjxl);\n  there is no public-domain single-header decoder for them and they are not\n  practical to hand-roll. If imgcli adds these, it will be **strictly via opt-in\n  build flags** (e.g. `make WEBP=1`) that link those libraries.\n\n> **The \"zero dependencies\" claim applies to the default build, and always will —\n> opt-in format libraries are never compiled into it.** A build that enables such\n> a flag is, by definition, no longer dependency-free, and that trade-off is\n> stated at the point you opt in.\n\n## Why it's structured this way\n\n| ffmpeg concept        | imgcli equivalent                                            |\n| --------------------- | -------------------------------------------------------------- |\n| `AVFrame` / pixfmt    | every image is normalized to packed 8-bit **RGBA** (`Image`)   |\n| demuxer/decoder       | `img_load` via vendored **stb_image** (PNG/JPEG/BMP/TGA/GIF/…)  |\n| `-vf` filtergraph     | `name=a:b:c, name, …` chain parsed in `filters.c`              |\n| `AVFilter`            | each filter mutates or replaces the current frame              |\n| muxer (by extension)  | `img_save` (png/jpg/bmp/tga + a hand-written PPM writer)        |\n| `lavfi` test sources  | `testsrc=`, `color=`, `gradient=`, `checker=` generators       |\n\nCodecs come from the public-domain [stb](https://github.com/nothings/stb)\nsingle-header libraries (`third_party/`). They're bundled, not linked, so the\ntool stays portable and self-contained while still reading/writing the formats\nthe world actually uses — the same trade-off ffmpeg makes by leaning on codec\nlibraries instead of reinventing them.\n\n## Install\n\n```sh\n# Homebrew (macOS / Linux)\nbrew install swperb/tap/imgcli\n\n# Nix (no install — run straight from GitHub)\nnix run github:swperb/imgcli -- -y -i in.png out.jpg\nnix profile install github:swperb/imgcli\n\n# Debian / Ubuntu / WSL (.deb from the latest release)\ncurl -fsSLO https://github.com/swperb/imgcli/releases/latest/download/imgcli_0.5.0_amd64.deb\nsudo apt install ./imgcli_0.5.0_amd64.deb\n\n# Arch (AUR)\nyay -S imgcli            # or: paru -S imgcli\n\n# Docker\ndocker run --rm -v \"$PWD:/work\" -w /work ghcr.io/swperb/imgcli -y -i in.png out.jpg\n\n# Windows — winget\nwinget install swperb.imgcli\n# Windows — Scoop (installs straight from the manifest)\nscoop install https://raw.githubusercontent.com/swperb/imgcli/main/packaging/scoop/imgcli.json\n\n# Prebuilt binaries: https://github.com/swperb/imgcli/releases\n\n# From source (only a C compiler and -lm required)\nmake            # produces ./imgcli\nmake demo       # generates a few sample images\nsudo make install   # installs the binary + man page to /usr/local\n```\n\n### Use it as a native agent tool (MCP)\n\nAn [MCP](https://modelcontextprotocol.io) server wraps imgcli so AI agents can\ncall `convert_image`, `probe_image`, and `list_filters` directly — see\n[mcp/](mcp/README.md).\n\n## Usage\n\n```\nimgcli [-i INPUT]... [-vf GRAPH] [-q N] [-f FMT] [-y|-n] [--json] OUTPUT\n\n  -i INPUT     a file, '-' for stdin, or a generator (testsrc=WxH,\n               color=NAME:WxH, gradient=WxH, checker=WxH). Repeat -i for\n               compositing inputs; the first is the primary frame, the rest\n               feed `overlay`.\n  -vf GRAPH    filtergraph, e.g. \"scale=800:-1,grayscale,gblur=2\"\n  -q N         JPEG quality 1..100 (default 90)\n  -f FMT       output format (png/jpg/bmp/tga/ppm/qoi); required when OUTPUT\n               is '-' (stdout), an optional override for files\n  -y / -n      overwrite / never overwrite the output\n  --out-dir D  batch mode: write one output per input into D (basename kept,\n               extension from -f or the input). Globs in -i are expanded.\n  --fail-fast  in batch mode, stop at the first failing file\n  --json       emit one machine-readable JSON result line (an array in batch)\n  --quiet      suppress the human-readable success line\n  --dry-run    validate the filtergraph + report output dims; write nothing\n  -filters [NAME]  list every filter, or just NAME's syntax (add --json for a\n               machine-readable list)\n  -info        print input dimensions and exit\n  -V           print version\n  -h           help\n\nOUTPUT is a file path or '-' for stdout. When piping, the result line is\nwritten to stderr so stdout carries only the encoded image bytes.\n\nBatch mode (`--out-dir`) processes each input independently and keeps going on\nper-file errors (use `--fail-fast` to stop). The exit code is non-zero if any\nfile failed.\n```\n\nColours accept `#rgb`, `#rrggbb`, `#rrggbbaa`, `0x…`, `r-g-b[-a]`, or names\n(`red`, `white`, `transparent`, …). No commas, so they're safe inside a graph.\n\n## Filters\n\n**Geometry** — `scale=W:H[:nearest|bilinear|bicubic|lanczos]` (`-1` keeps aspect;\n`bicubic`/`lanczos` are high-quality separable resamplers that anti-alias on\ndownscale and stay sharp on upscale — `bilinear` is the default),\n`crop=W:H[:X:Y]`, `pad=W:H[:X:Y[:color]]`, `hflip`, `vflip`,\n`transpose=90|180|270`, `rotate=DEG[:color]` (arbitrary angle, canvas expands).\n\n**Colour** — `grayscale`, `invert`, `sepia`, `solarize`, `brightness=V`, `temperature=V`,\n`contrast=V`, `saturation=V`, `gamma=V`, `hue=DEG`, `threshold=V`, `opacity=V`, `tint=color`.\n\n**Convolution** — `blur=R` (box), `gblur=SIGMA` (separable Gaussian),\n`sharpen[=AMOUNT]`, `edge` (Sobel), `emboss`, `convolution=K[:DIV:BIAS]`\n(custom N×N kernel, e.g. `convolution=0 -1 0 -1 5 -1 0 -1 0`).\n\n**Composite / draw** — `overlay=X:Y[:INDEX]` (alpha \"over\" compositing of\nanother `-i` input), `fill=color`, `drawbox=X:Y:W:H:color[:fill|thickness]`.\n\n## Examples\n\n```sh\n# Thumbnail, preserving aspect ratio (lanczos = best downscale quality)\nimgcli -i photo.jpg -vf \"scale=400:-1:lanczos\" thumb.png\n\n# Stylise: desaturate a touch, boost contrast, soften\nimgcli -i photo.jpg -vf \"saturation=0.6,contrast=1.15,gblur=1\" look.jpg\n\n# Watermark a logo in the top-left, 60% opacity, then convert to JPEG\nimgcli -i page.png -i logo.png -vf \"opacity=0.6,overlay=24:24\" out.jpg\n\n# Rotate 30° onto a transparent canvas\nimgcli -i sticker.png -vf \"rotate=30:transparent\" rotated.png\n\n# No input file? Generate a test card.\nimgcli -i testsrc=640x480 card.png\n\n# Pipe-friendly: read stdin, write stdout (use -f to name the output format)\ncurl -s https://example.com/in.png | imgcli -i - -vf \"scale=800:-1\" -f jpg - > out.jpg\n\n# Batch a whole folder into thumbnails (one call; per-file results)\nimgcli -i \"photos/*.jpg\" --out-dir thumbs -vf \"scale=400:-1:lanczos\" -f png\n```\n\n## For AI agents & scripting\n\nimgcli is built to be a reliable tool in an automated pipeline: **one\nself-contained binary, no dependencies, deterministic, non-interactive, and\nmachine-readable**. See [AGENTS.md](AGENTS.md) for a token-economical recipe sheet.\n\n```sh\n# Always pass -y (don't prompt) and --json (parseable result) in automation:\nimgcli --json -y -i in.jpg -vf \"scale=512:-1\" out.png\n# -> {\"ok\":true,\"output\":\"out.png\",\"width\":512,\"height\":341,\"format\":\"png\",\"bytes\":34122}\n```\n\n- **Deterministic & non-interactive** — never prompts; refuses to overwrite\n  without `-y`; one process per conversion.\n- **Structured output** — `--json` for results, `--quiet` to silence chatter;\n  stable exit codes (`0` ok, `1` runtime error, `2` usage error).\n- **No network, no subprocesses** — safe to run on untrusted inputs in a sandbox.\n- **Probe without converting** — `imgcli --json -info -i file.jpg`.\n\n## Security\n\nimgcli decodes untrusted image files in C, so memory safety is taken seriously.\nThe codebase has been audited against the OWASP Top 10, common C/CWE classes, and\nffmpeg's historical vulnerability classes. Highlights:\n\n- **Decompression-bomb safe** — dimensions are validated from the header *before*\n  pixels are decoded; hard caps on size (16384 px/axis, 64 Mpx).\n- **Integer-overflow-safe allocation** through a single capped choke point.\n- **No protocols/URLs/subprocesses** — ffmpeg's worst class (SSRF / file-read via\n  HLS playlists) is structurally impossible here.\n- **Hardened build** (`_FORTIFY_SOURCE`, stack protector, PIE/RELRO, format\n  warnings), **ASan/UBSan** (`make asan`), and a **fuzz harness** (`make fuzz`).\n\nFull threat model, OWASP/CWE mapping, ffmpeg-CVE-class analysis, and dependency\npolicy: **[SECURITY.md](SECURITY.md)**.\n\n## Layout\n\n```\nsrc/image.{h,c}    Image (RGBA frame) + load/save (stb glue, PPM writer)\nsrc/filters.{h,c}  filtergraph parser + every filter + registry\nsrc/source.{h,c}   synthetic input generators\nsrc/util.{h,c}     colour / size parsing\nsrc/main.c         CLI argument handling (incl. --json output)\nthird_party/       vendored stb_image.h, stb_image_write.h (public domain)\nfuzz/              libFuzzer harness for the decode -> filtergraph path\nAGENTS.md          token-economical usage guide for agents/scripts\nSECURITY.md        threat model, OWASP/CWE mapping, hardening, dependency policy\n```\n\n## Contributing\n\nContributor setup, validation targets, filter-registry guidance, and PR expectations live in [CONTRIBUTING.md](CONTRIBUTING.md).\n\nHave a question or idea? Use [Discussions](https://github.com/swperb/imgcli/discussions). For bugs and feature/format requests, open an issue — the templates will guide you.\n\n## Support\n\nIf imgcli is useful to you, consider [sponsoring its development](https://github.com/sponsors/swperb) — it funds new formats, security hardening, and maintenance. See [SPONSORS.md](SPONSORS.md).\n\n## License\n\nThe imgcli source is yours to use freely ([MIT](LICENSE)). The vendored stb\nheaders are public domain (or MIT, at your option) and the vendored `qoi.h` is\nMIT-licensed; full third-party notices are in\n[`THIRD_PARTY_NOTICES.md`](THIRD_PARTY_NOTICES.md).\n",
  "bytes": 12896,
  "sha": "2d879166a5243b85e640e2b2458501ae5cb4e06f027cef96164d3ab84b591a81",
  "repo_slug": "swperb/imgcli",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_swperb_imgcli_6dc6d62b/readme"
}