Skip to content
Image docs0.3

Immutable image processing

Latest stable Based on Image release 0.3.0

Library module

This module is the pure-JVM image-processing foundation. It wraps Scrimage and Java2D with Kotlin factories, immutable drawing, filter and transformation DSLs, suspend-friendly I/O, batch pipelines, thumbnails, tiles, analysis, and similarity metrics.

It is the default choice when native libvips setup is unnecessary or when a service needs rich Java2D/Scrimage operations rather than a narrow high-throughput resize pipeline.

Use it for loading common web images, composing filters and watermarks, generating thumbnails, bounded batch work, similarity analysis, SVG rasterization, and coroutine-facing encoders. Choose a vips backend instead when native throughput, native memory behavior, or HEIF-family codecs dominate the decision.

Maven coordinate: io.github.bluetape4k.image:bluetape4k-images

dependencies {
implementation(platform("io.github.bluetape4k:bluetape4k-dependencies:<version>"))
implementation("io.github.bluetape4k.image:bluetape4k-images")
}
  • ImmutableImage operations return new values. withGraphics draws on a copy and always disposes Graphics2D.
  • BufferedImage.useGraphics is intentionally mutable; it disposes the graphics object but changes the receiver.
  • Scrimage codecs are blocking. Suspend wrappers move them to Dispatchers.IO; they do not turn the codec into non-blocking I/O.
  • Resource ownership is overload-specific: BufferedSource and InputStream remain caller-owned, while Source and SuspendedSource overloads buffer and close the source.
  • Batch work is bounded by per-image pixels, in-flight pixels, and parallelism.
val image = immutableImageOf(Path.of("photo.jpg"))
val output = image.applyFilters {
brightness(1.08f)
gaussianBlur(radius = 2)
watermark("© bluetape4k")
}
output.suspendWrite(
SuspendJpegWriter.Default.withCompression(85),
Path.of("photo-ready.jpg"),
)
  • Load: immutableImageOf and suspendImmutableImageOf accept bytes, streams, Okio sources, files, and paths.
  • Filter: applyFilters composes blur, color, effect, style, border, caption, and watermark operations.
  • Transform: use autoCrop, smartCropTo, clahe, perspectiveTransform, rotation, and flips.
  • Compare: use perceptual hashes, histogram similarity, MSE, PSNR, SSIM/MSSIM, or block descriptors.
  • Batch: processImages returns ImageBatchResult; select a writer in ImageProcessingDsl and finish with writeImagesTo.
  • Generate variants: configure ThumbnailPipeline or split/process/merge through TileProcessor.

Prefer ImmutableImage at application boundaries. For batch jobs, probe dimensions before full decode, keep skipFailures=false for fail-fast jobs, and enable it only with an onFailure observer. Keep output names relative: writeImagesTo rejects paths that normalize outside its output directory.

Use suspend Okio overloads for lifecycle consistency when the caller already owns that boundary, not as a latency optimization. Release benchmarks found the Path boundary faster for the measured local workloads.

CAPTCHA, OCR, and Ktor modules build on this module. Spring storage is separate from the processing DSL. images-vips-api is a different binding-neutral native surface rather than a drop-in implementation of Scrimage APIs.

ImageProcessingOptions defaults to available-processor parallelism, 16,777,216 pixels per image, and 33,554,432 in-flight pixels. largeJobs() raises the bounds explicitly. The default maximum tile count is 65,536.

BatikSvgRasterizer blocks external entities and DTD loading, disables external resources by default, and enforces dimensions and timeout. Keep those defaults for untrusted SVG.

Batch failures identify VALIDATION, LOAD, TRANSFORM, or WRITE through ImageBatchException/ImageBatchResult.Failure. Decode and writer exceptions otherwise propagate from Scrimage or ImageIO. Cancellation is rethrown rather than converted into a skipped item.

Monitor failure counts by stage and actual image dimensions, not only compressed byte size. Set pixel budgets from production traffic. Golden-image updates are opt-in; do not enable bluetape4k.images.golden.update in ordinary CI.

The release suite covers factories and ownership, filters, properties, golden outputs, batch limits, thumbnail and tile pipelines, similarity, SVG security, and suspend writers. Run ./gradlew :bluetape4k-images:test for the pure-JVM lane.

Begin with examples/basic-processing, then study ImageProcessingDsl, ThumbnailPipeline, and TileProcessor. Read the benchmark reports before choosing libvips solely for speed; the published figures are workload- and host-specific.

Suspend wrappers still bridge to blocking codecs. AVIF/HEIC types in this module are incubating contracts without a pure-JVM implementation in 0.3.0. Compressed-byte limits do not replace pixel limits, and ImageIO codec availability can vary with the runtime classpath.

These diagrams are loaded directly from README assets published with the 0.3.0 release and pinned to its immutable commit. They describe this manual’s released structure and runtime flows, not later Snapshot changes. Select a preview to open the SVG at the same release commit.

Processing Pipeline diagram

Release README: images/README.md

Transform Architecture diagram

Release README: images/README.md

Image Analysis diagram

Release README: images/README.md

Images Core API Classes diagram

Release README: images/README.md

Images Filter Classes diagram

Release README: images/README.md

Images Writer Classes diagram

Release README: images/README.md