Clojure imaging WITHOUT java.desktop — decoding, encoding, transforms,
vector drawing, text, SVG rasterisation and plotting, all in-process through
the JDK Foreign Function & Memory API over a first-party Rust cdylib
(native/imaging-c: the image crate + resvg/usvg/tiny-skia).
Why it exists: javax.imageio, java.awt.image.BufferedImage and
Graphics2D drag the whole AWT/Java2D/ImageIO subgraph — service loaders,
sun.java2d.loops JNI blit primitives, the font manager — into a GraalVM
native image, which is both huge and, on a headless server, pointless. This
library replaces that surface with a ~5 MB cdylib and zero JNI: pure
downcalls, no subprocess, no -Djava.awt.headless, no fontconfig.
Everything is bytes in, bytes out. An Img is an opaque handle on the Rust
side; it is AutoCloseable (use with-open for tight loops) AND registered
with a java.lang.ref.Cleaner, so forgetting to close leaks nothing
permanently.
Run the JVM with --enable-native-access=ALL-UNNAMED so the foreign linker
may load the library without a restricted-method warning.
The library is resolved ONCE, lazily, the first time it's needed:
com.blockether.imaging.native.path system
property — an explicit path to the cdylib (used verbatim).prebuilds/<platform>/<lib>, shipped by
com.blockether/imaging-native-<platform>. This is the native-image path.imaging-native-<platform> jar resolved through
clojure.tools.deps (honouring Maven repos/mirrors/settings.xml),
extracted + cached. Disable with IMAGING_DISABLE_DOWNLOAD=1.<platform> in { linux-x64 linux-arm64 darwin-arm64 darwin-x64 windows-x64 }.
Clojure imaging WITHOUT `java.desktop` — decoding, encoding, transforms,
vector drawing, text, SVG rasterisation and plotting, all in-process through
the JDK Foreign Function & Memory API over a first-party Rust cdylib
(`native/imaging-c`: the `image` crate + `resvg`/`usvg`/`tiny-skia`).
Why it exists: `javax.imageio`, `java.awt.image.BufferedImage` and
`Graphics2D` drag the whole AWT/Java2D/ImageIO subgraph — service loaders,
`sun.java2d.loops` JNI blit primitives, the font manager — into a GraalVM
native image, which is both huge and, on a headless server, pointless. This
library replaces that surface with a ~5 MB cdylib and zero JNI: pure
downcalls, no subprocess, no `-Djava.awt.headless`, no fontconfig.
Everything is bytes in, bytes out. An `Img` is an opaque handle on the Rust
side; it is `AutoCloseable` (use `with-open` for tight loops) AND registered
with a `java.lang.ref.Cleaner`, so forgetting to close leaks nothing
permanently.
Run the JVM with `--enable-native-access=ALL-UNNAMED` so the foreign linker
may load the library without a restricted-method warning.
The library is resolved ONCE, lazily, the first time it's needed:
1. IMAGING_NATIVE_PATH env / `com.blockether.imaging.native.path` system
property — an explicit path to the cdylib (used verbatim).
2. A bundled classpath resource `prebuilds/<platform>/<lib>`, shipped by
`com.blockether/imaging-native-<platform>`. This is the native-image path.
3. A runtime download: the `imaging-native-<platform>` jar resolved through
`clojure.tools.deps` (honouring Maven repos/mirrors/settings.xml),
extracted + cached. Disable with IMAGING_DISABLE_DOWNLOAD=1.
`<platform>` in { linux-x64 linux-arm64 darwin-arm64 darwin-x64 windows-x64 }.(adjust img opts)Photometric adjustment: :brightness :contrast :saturation :opacity
(1.0 = unchanged).
Photometric adjustment: `:brightness` `:contrast` `:saturation` `:opacity` (1.0 = unchanged).
(blank w h)(blank w h c)A new w x h image filled with c (default transparent).
A new `w` x `h` image filled with `c` (default transparent).
(close! img)Free an image handle now. Idempotent; Img is also AutoCloseable.
Free an image handle now. Idempotent; `Img` is also `AutoCloseable`.
(color c)A colour as the packed 0xRRGGBBAA long the cdylib takes. Accepts a packed
integer, "#rgb" / "#rrggbb" / "#rrggbbaa", a basic CSS colour name
(string or keyword), or [r g b] / [r g b a] with 0-255 channels (alpha may
also be a 0.0-1.0 double).
A colour as the packed `0xRRGGBBAA` long the cdylib takes. Accepts a packed integer, `"#rgb"` / `"#rrggbb"` / `"#rrggbbaa"`, a basic CSS colour name (string or keyword), or `[r g b]` / `[r g b a]` with 0-255 channels (alpha may also be a 0.0-1.0 double).
(convert data)(convert data opts)One-shot decode -> normalise -> re-encode: the "make this attachment safe to send" path, without ever materialising an image handle.
Options: :format :quality :max-width :max-height :width :height
:background (flatten alpha onto it) :filter :max-bytes (retry at lower
quality/scale until the output fits). Returns a byte[].
One-shot decode -> normalise -> re-encode: the "make this attachment safe to send" path, without ever materialising an image handle. Options: `:format` `:quality` `:max-width` `:max-height` `:width` `:height` `:background` (flatten alpha onto it) `:filter` `:max-bytes` (retry at lower quality/scale until the output fits). Returns a byte[].
(decode data)Decode PNG/JPEG/WebP/GIF/BMP/TIFF/ICO/QOI/PNM/TGA/HDR/EXR bytes — or an
SVG/SVGZ document — into an Img handle. Close it (with-open) when done.
Decode PNG/JPEG/WebP/GIF/BMP/TIFF/ICO/QOI/PNM/TGA/HDR/EXR bytes — or an SVG/SVGZ document — into an `Img` handle. Close it (`with-open`) when done.
(draw! img ops)Run a batch of vector drawing ops on img, IN PLACE — this is the
Graphics2D replacement. One call per batch keeps FFI chatter out of drawing
loops; ops are applied in order and inherit the batch's style defaults.
Style keys (batch level or per op): :fill :stroke :stroke-width :dash
:cap (:butt :round :square) :join (:miter :round :bevel)
:size :family :weight :italic :letter-spacing :line-height.
Ops (:op):
:clear — fill everything with :fill
:clip — clip to :x :y :w :h (:op :reset-clip to drop it)
:line — :x1 :y1 :x2 :y2 (or :points)
:polyline / :polygon — :points [[x y] ...], :close
:rect — :x :y :w :h, optional :radius (rounded)
:circle — :cx :cy :r :ellipse — :cx :cy :rx :ry
:arc / :wedge — :cx :cy :r :r-inner :start :end (degrees)
:path — :d (SVG path data), :fill-rule
:text — :text :x :y :anchor (:start|:middle|:end) :baseline :rotate
:image — :image (an Img), :x :y :w :h :opacity
Returns img.
Run a batch of vector drawing ops on `img`, IN PLACE — this is the `Graphics2D` replacement. One call per batch keeps FFI chatter out of drawing loops; ops are applied in order and inherit the batch's style defaults. Style keys (batch level or per op): `:fill` `:stroke` `:stroke-width` `:dash` `:cap` (`:butt` `:round` `:square`) `:join` (`:miter` `:round` `:bevel`) `:size` `:family` `:weight` `:italic` `:letter-spacing` `:line-height`. Ops (`:op`): `:clear` — fill everything with `:fill` `:clip` — clip to `:x :y :w :h` (`:op :reset-clip` to drop it) `:line` — `:x1 :y1 :x2 :y2` (or `:points`) `:polyline` / `:polygon` — `:points [[x y] ...]`, `:close` `:rect` — `:x :y :w :h`, optional `:radius` (rounded) `:circle` — `:cx :cy :r` `:ellipse` — `:cx :cy :rx :ry` `:arc` / `:wedge` — `:cx :cy :r :r-inner :start :end` (degrees) `:path` — `:d` (SVG path data), `:fill-rule` `:text` — `:text :x :y :anchor (:start|:middle|:end) :baseline :rotate` `:image` — `:image` (an `Img`), `:x :y :w :h :opacity` Returns `img`.
(encode img)(encode img fmt)(encode img fmt quality)Encode to fmt (:png :jpeg :webp :gif :bmp :tiff :ico :qoi
:pnm :tga); returns a byte[]. quality (1-100, default 85) applies to the
lossy formats.
Encode to `fmt` (`:png` `:jpeg` `:webp` `:gif` `:bmp` `:tiff` `:ico` `:qoi` `:pnm` `:tga`); returns a byte[]. `quality` (1-100, default 85) applies to the lossy formats.
(flatten img)(flatten img bg)Composite onto an opaque background (default white).
Composite onto an opaque background (default white).
(flip img mode):horizontal/:h or :vertical/:v.
`:horizontal`/`:h` or `:vertical`/`:v`.
(fonts)Every font family known to the shared font database, as a vector of strings. The four Noto faces (sans regular/bold/italic + mono) are embedded, so text and SVG rendering never depend on system fonts.
Every font family known to the shared font database, as a vector of strings. The four Noto faces (sans regular/bold/italic + mono) are embedded, so text and SVG rendering never depend on system fonts.
(from-pixels rgba w h)An image from straight (non-premultiplied) RGBA8 rows — w*h*4 bytes.
An image from straight (non-premultiplied) RGBA8 rows — `w*h*4` bytes.
(get-pixel img x y)Packed 0xRRGGBBAA of one pixel, or nil when out of bounds.
Packed `0xRRGGBBAA` of one pixel, or nil when out of bounds.
(paste! dst src x y)(paste! dst src x y {:keys [blend] :or {blend true}})Draw src into dst at (x,y), IN PLACE. :blend false replaces the
destination pixels instead of alpha-compositing.
Draw `src` into `dst` at (x,y), IN PLACE. `:blend` false replaces the destination pixels instead of alpha-compositing.
(pixels img)The image's straight RGBA8 rows as a byte[].
The image's straight RGBA8 rows as a byte[].
(plot spec)Render a plot specification to an image. See com.blockether.imaging.plot for
the spec builders (line, bar, scatter, hist, pie, heatmap, …) and
the full vocabulary.
Render a plot specification to an image. See `com.blockether.imaging.plot` for the spec builders (`line`, `bar`, `scatter`, `hist`, `pie`, `heatmap`, …) and the full vocabulary.
(probe data)Identify encoded bytes without a full decode:
{:format :width :height :is-animated :frames}. SVG documents report
:format "svg".
Identify encoded bytes without a full decode:
`{:format :width :height :is-animated :frames}`. SVG documents report
`:format "svg"`.(register-font! font)Add a TTF/OTF/TTC (bytes/File/path/stream) to the font database for this process; returns the vector of families it provided.
Add a TTF/OTF/TTC (bytes/File/path/stream) to the font database for this process; returns the vector of families it provided.
(render-svg svg)(render-svg svg opts)Rasterise an SVG/SVGZ document (bytes, markup String, File, path, stream) with
resvg. Options: :width :height :scale :max-width :max-height
:background :dpi :font-family :font-size :base-url.
Rasterise an SVG/SVGZ document (bytes, markup String, File, path, stream) with resvg. Options: `:width` `:height` `:scale` `:max-width` `:max-height` `:background` `:dpi` `:font-family` `:font-size` `:base-url`.
(resize img w h)(resize img w h filter)Resample to exactly w x h. filter: :nearest :triangle/:bilinear
:catmullrom/:bicubic :gaussian :lanczos3 (default), passed through.
Resample to exactly `w` x `h`. `filter`: `:nearest` `:triangle`/`:bilinear` `:catmullrom`/`:bicubic` `:gaussian` `:lanczos3` (default), passed through.
(rotate img degrees)(rotate img degrees {:keys [expand background]})Rotate counter-clockwise by degrees. :expand true grows the canvas to hold
the rotated image; :background fills behind it.
Rotate counter-clockwise by `degrees`. `:expand` true grows the canvas to hold the rotated image; `:background` fills behind it.
(save! img file)(save! img file {:keys [format quality] :as _opts})Encode img and write it to file; the format comes from the extension
unless :format says otherwise. Returns the file.
Encode `img` and write it to `file`; the format comes from the extension unless `:format` says otherwise. Returns the file.
(text-measure spec)Measure a text run without drawing it — same :text :size :family :weight :italic :letter-spacing :line-height keys as the :text draw op. Returns
{:width :height :x :y :lines}, the ink box relative to the first baseline.
Measure a text run without drawing it — same `:text :size :family :weight
:italic :letter-spacing :line-height` keys as the `:text` draw op. Returns
`{:width :height :x :y :lines}`, the ink box relative to the first baseline.(thumbnail img max-w max-h)Scale DOWN to fit inside max-w x max-h, preserving aspect ratio.
Scale DOWN to fit inside `max-w` x `max-h`, preserving aspect ratio.
(version)The cdylib's version string (crate version + backing engines).
The cdylib's version string (crate version + backing engines).
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |