Skip to content

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

// Image Resize & Watermark — C++ (17, OpenCV) port of the image-resize-and-watermark tool.

#include <opencv2/opencv.hpp>
#include <string>
#include <vector>

// Mirrors the TS resizeAndWatermark: aspect-preserving resize, then a
// bottom-right watermark — white text at 70% alpha over a dark outline (the
// strokeText/fillText pair), drawn on a transparent layer and alpha-composited
// so both passes keep their opacity.
cv::Mat resizeAndWatermark(const cv::Mat& src, int targetWidth, const std::string& text) {
    int h = cvRound(static_cast<double>(src.rows) / src.cols * targetWidth);
    cv::Mat img;
    // INTER_AREA is OpenCV's high-quality downscaler — imageSmoothingQuality 'high'.
    cv::resize(src, img, cv::Size(targetWidth, h), 0, 0, cv::INTER_AREA);

    int fs = cvRound(cv::min(targetWidth, h) * 6.0 / 100.0);
    double fscale = fs / 22.0; // HERSHEY glyphs are ~22 px tall at scale 1
    int baseline = 0;
    cv::Size box = cv::getTextSize(text, cv::FONT_HERSHEY_DUPLEX, fscale, 2, &baseline);
    cv::Point org(targetWidth - box.width - 8, h - 8);

    cv::Mat layer = cv::Mat::zeros(img.size(), CV_8UC4);
    cv::Scalar dark(0, 0, 0, 255 * 0.55), white(255, 255, 255, 255 * 0.7);
    // strokeText: outline via 8 offset draws, then the fillText on top.
    for (int dx = -2; dx <= 2; dx += 2)
        for (int dy = -2; dy <= 2; dy += 2)
            cv::putText(layer, text, org + cv::Point(dx, dy),
                        cv::FONT_HERSHEY_DUPLEX, fscale, dark, 2, cv::LINE_AA);
    cv::putText(layer, text, org, cv::FONT_HERSHEY_DUPLEX, fscale, white, 2, cv::LINE_AA);

    // Composite the BGRA layer over the image using its alpha channel.
    std::vector<cv::Mat> lch, ich;
    cv::split(layer, lch);
    cv::split(img, ich);
    cv::Mat a, inv;
    lch[3].convertTo(a, CV_32F, 1.0 / 255);
    cv::subtract(cv::Scalar(1.0), a, inv);
    for (int i = 0; i < 3; i++) {
        cv::Mat f, lf;
        ich[i].convertTo(f, CV_32F);
        lch[i].convertTo(lf, CV_32F);
        f = f.mul(inv) + lf.mul(a);
        f.convertTo(ich[i], CV_8U);
    }
    cv::merge(ich, img);
    return img;
}

int main() {
    cv::Mat out = resizeAndWatermark(cv::imread("input.jpg"), 1280, "CosmoDev");
    std::vector<int> webp{cv::IMWRITE_WEBP_QUALITY, 92}; // TS encodes WebP at 0.92
    cv::imwrite("output.webp", out, webp);
}

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 →