Photo Merge — Kotlin source
Combine photos side by side, stacked, or in a grid — uniform cells, adjustable gap, one PNG out. Runs entirely in your browser.
This is the Kotlin implementation — the same logic the interactive tool runs, in a shareable, citable form.
// photo-merge — Kotlin port: side-by-side merge cell layout.
/** A decoded image's dimensions. In the browser tool the bitmap is
* drawImage'd into its letterboxed cell; the geometry here is the
* deterministic part of that pipeline. */
data class ImageSize(val width: Int, val height: Int)
/** Where one image lands on the merged canvas. */
data class Placement(val scale: Double, val x: Int, val y: Int, val w: Int, val h: Int)
data class MergedLayout(val canvasW: Int, val canvasH: Int, val placements: List<Placement>)
/** Mirrors mergeSideBySide in typescript.ts: cellW/cellH are the largest
* input dimensions, the canvas is n cells + (n + 1) gaps wide and one
* cell + 2 gaps tall, and each image is scaled by
* min(cellW / w, cellH / h) and centered inside its cell.
* Math.round rounds half up, matching JS exactly. */
fun mergeLayout(images: List<ImageSize>, gap: Int): MergedLayout {
val n = images.size
val cellW = images.maxOf { it.width }
val cellH = images.maxOf { it.height }
val placements = images.mapIndexed { i, img ->
val scale = minOf(
cellW.toDouble() / img.width,
cellH.toDouble() / img.height,
)
val w = Math.round(img.width * scale).toInt()
val h = Math.round(img.height * scale).toInt()
Placement(
scale,
gap + i * (cellW + gap) + Math.round((cellW - w) / 2.0).toInt(),
gap + Math.round((cellH - h) / 2.0).toInt(),
w,
h,
)
}
return MergedLayout(n * cellW + (n + 1) * gap, cellH + 2 * gap, placements)
}
fun main() {
// 800x600 + 600x800 + 1024x768 with a 12px gap -> 3120x824 canvas.
val layout = mergeLayout(
listOf(
ImageSize(800, 600),
ImageSize(600, 800),
ImageSize(1024, 768),
),
gap = 12,
)
println("canvas ${layout.canvasW}x${layout.canvasH}")
layout.placements.forEachIndexed { i, p ->
println("image $i: (${p.x}, ${p.y}) ${p.w}x${p.h} scale=${"%.4f".format(p.scale)}")
}
}
Also available in 8 other languages
Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →