Photo Merge — Swift 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 Swift implementation — the same logic the interactive tool runs, in a shareable, citable form.
// photo-merge — Swift port: side-by-side merge cell layout.
import Foundation
/// 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.
struct ImageSize {
var width: Int
var height: Int
}
/// Where one image lands on the merged canvas.
struct Placement {
var scale: Double
var x: Int
var y: Int
var w: Int
var h: Int
}
struct MergedLayout {
var canvasW: Int
var canvasH: Int
var placements: [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.
/// `rounded(.awayFromZero)` matches JS Math.round for positive values.
func mergeLayout(_ images: [ImageSize], gap: Int = 12) -> MergedLayout {
let n = images.count
let cellW = images.map(\.width).max() ?? 0
let cellH = images.map(\.height).max() ?? 0
func round(_ v: Double) -> Int { Int(v.rounded(.awayFromZero)) }
var placements: [Placement] = []
placements.reserveCapacity(n)
for (i, img) in images.enumerated() {
let scale = min(Double(cellW) / Double(img.width),
Double(cellH) / Double(img.height))
let w = round(Double(img.width) * scale)
let h = round(Double(img.height) * scale)
placements.append(Placement(
scale: scale,
x: gap + i * (cellW + gap) + round(Double(cellW - w) / 2),
y: gap + round(Double(cellH - h) / 2),
w: w,
h: h))
}
return MergedLayout(canvasW: n * cellW + (n + 1) * gap,
canvasH: cellH + 2 * gap,
placements: placements)
}
// 800x600 + 600x800 + 1024x768 with a 12px gap -> 3120x824 canvas.
let layout = mergeLayout([
ImageSize(width: 800, height: 600),
ImageSize(width: 600, height: 800),
ImageSize(width: 1024, height: 768),
], gap: 12)
print("canvas \(layout.canvasW)x\(layout.canvasH)")
for (i, p) in layout.placements.enumerated() {
let scale = String(format: "%.4f", p.scale)
print("image \(i): (\(p.x), \(p.y)) \(p.w)x\(p.h) scale=\(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 →