Skip to content

Image Resize & Watermark — Kotlin source

Batch-resize images by width, height or percentage and stamp a text watermark with position, opacity and size control — entirely in your browser, nothing uploaded.

This is the Kotlin implementation — the same logic the interactive tool runs, in a shareable, citable form.

// Image Resize & Watermark — Kotlin (JVM 17, java.awt) port of the image-resize-and-watermark tool.

import java.awt.AlphaComposite
import java.awt.BasicStroke
import java.awt.Color
import java.awt.Font
import java.awt.RenderingHints
import java.awt.image.BufferedImage
import java.io.File
import javax.imageio.ImageIO

// Mirrors the TS resizeAndWatermark: aspect-preserving resize, then a
// bottom-right watermark — white fill at 70% alpha over a dark outline
// (strokeText + fillText), both from one GlyphVector outline.
fun resizeAndWatermark(src: BufferedImage, targetWidth: Int, watermark: String): BufferedImage {
    val h = Math.round(src.height.toDouble() / src.width * targetWidth).toInt()
    val img = BufferedImage(targetWidth, h, BufferedImage.TYPE_INT_RGB)
    img.createGraphics().apply {
        setRenderingHint(
            RenderingHints.KEY_INTERPOLATION,
            RenderingHints.VALUE_INTERPOLATION_BICUBIC, // imageSmoothingQuality 'high'
        )
        setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON)
        drawImage(src, 0, 0, targetWidth, h, null)

        val fs = Math.round(minOf(targetWidth, h) * 6 / 100.0).toInt()
        font = Font(Font.SANS_SERIF, Font.BOLD, fs) // Bold ≈ the TS 600 weight
        val width = fontMetrics.stringWidth(watermark) // measureText analogue
        val outline = font.createGlyphVector(fontRenderContext, watermark)
            .getOutline((targetWidth - width - 8).toFloat(), (h - 8).toFloat()) // baseline at h-8

        composite = AlphaComposite.SrcOver.derive(0.7f) // the TS globalAlpha = 0.7
        stroke = BasicStroke(maxOf(1f, fs / 14f), BasicStroke.JOIN_ROUND, BasicStroke.CAP_ROUND)
        color = Color(0f, 0f, 0f, 0.55f) // 0.55-alpha stroke
        draw(outline) // strokeText
        color = Color(1f, 1f, 1f, 0.7f) // 0.7-alpha white fill
        fill(outline) // fillText
        dispose()
    }
    return img
}

fun main() {
    val out = resizeAndWatermark(ImageIO.read(File("input.jpg")), 1280, "CosmoDev")
    ImageIO.write(out, "jpg", File("output.jpg")) // WebP needs a plugin (e.g. TwelveMonkeys)
}

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 →