Skip to content

Image Resize & Watermark — Java 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 Java implementation — the same logic the interactive tool runs, in a shareable, citable form.

// Image Resize & Watermark — Java (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.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;

public final class ImageResizeAndWatermark {

    // 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.
    public static BufferedImage run(BufferedImage src, int targetWidth, String watermark) {
        int h = (int) Math.round((double) src.getHeight() / src.getWidth() * targetWidth);
        BufferedImage img = new BufferedImage(targetWidth, h, BufferedImage.TYPE_INT_RGB);
        Graphics2D g = img.createGraphics();
        try {
            g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                    RenderingHints.VALUE_INTERPOLATION_BICUBIC); // imageSmoothingQuality 'high'
            g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
            g.drawImage(src, 0, 0, targetWidth, h, null);

            int fs = (int) Math.round(Math.min(targetWidth, h) * 6 / 100.0);
            g.setFont(new Font(Font.SANS_SERIF, Font.BOLD, fs)); // Bold ≈ the TS 600 weight
            FontRenderContext frc = g.getFontRenderContext();
            GlyphVector glyphs = g.getFont().createGlyphVector(frc, watermark);
            Rectangle b = glyphs.getPixelBounds(frc, 0, 0); // measureText analogue

            float x = targetWidth - b.width - 8;
            float y = h - 8; // baseline anchored 8 px above the bottom, as in the TS code
            Shape outline = glyphs.getOutline(x, y);

            g.setComposite(AlphaComposite.SrcOver.derive(0.7f)); // the TS globalAlpha = 0.7
            g.setStroke(new BasicStroke(Math.max(1f, fs / 14f),
                    BasicStroke.JOIN_ROUND, BasicStroke.CAP_ROUND));
            g.setColor(new Color(0f, 0f, 0f, 0.55f)); // 0.55-alpha stroke
            g.draw(outline); // strokeText
            g.setColor(new Color(1f, 1f, 1f, 0.7f)); // 0.7-alpha white fill
            g.fill(outline); // fillText
        } finally {
            g.dispose();
        }
        return img;
    }

    public static void main(String[] args) throws Exception {
        BufferedImage out = run(ImageIO.read(new File("input.jpg")), 1280, "CosmoDev");
        ImageIO.write(out, "jpg", new 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 →