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 (C11 + stb single-header libs) port of the image-resize-and-watermark tool.
#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_WRITE_IMPLEMENTATION
#define STB_IMAGE_RESIZE_IMPLEMENTATION
#define STB_EASY_FONT_IMPLEMENTATION
#include <math.h>
#include <stdlib.h>
#include "stb_easy_font.h"
#include "stb_image.h"
#include "stb_image_resize.h"
#include "stb_image_write.h"
/* Watermark style, mirroring the TS canvas constants. */
#define MARGIN 8
#define FILL_ALPHA 0.7f
#define STROKE_ALPHA 0.55f
/* Blend one stb_easy_font quad run over the RGB image. Vertices come out at
* ~24 px glyph size; scale + (ox, oy) lift them to the watermark size/place. */
static void blend_quads(unsigned char *img, int w, int h, const char *v,
int nvert, float ox, float oy, float scale,
unsigned char r, unsigned char g, unsigned char b, float a)
{
for (int i = 0; i < nvert; i += 4) { /* easy font emits filled quads */
int x0 = (int)(ox + scale * fminf(fminf(v[2*i+0], v[2*i+2]), fminf(v[2*i+4], v[2*i+6])));
int x1 = (int)(ox + scale * fmaxf(fmaxf(v[2*i+0], v[2*i+2]), fmaxf(v[2*i+4], v[2*i+6])) + 0.5f);
int y0 = (int)(oy + scale * fminf(fminf(v[2*i+1], v[2*i+3]), fminf(v[2*i+5], v[2*i+7])));
int y1 = (int)(oy + scale * fmaxf(fmaxf(v[2*i+1], v[2*i+3]), fmaxf(v[2*i+5], v[2*i+7])) + 0.5f);
for (int y = y0; y < y1; y++) {
if (y < 0 || y >= h) continue;
for (int x = x0; x < x1; x++) {
if (x < 0 || x >= w) continue;
unsigned char *p = img + 3 * ((size_t)y * w + x);
p[0] = (unsigned char)(p[0] * (1 - a) + r * a);
p[1] = (unsigned char)(p[1] * (1 - a) + g * a);
p[2] = (unsigned char)(p[2] * (1 - a) + b * a);
}
}
}
}
/* Watermark bottom-right: dark outline pass, then white fill — the strokeText/fillText pair of the TS code. */
static void watermark(unsigned char *rgb, int w, int h, const char *text)
{
int fs = (int)lroundf(fminf((float)w, (float)h) * 6.0f / 100.0f);
float scale = (float)fs / 24.0f;
char quads[4096]; /* 2 floats per vertex, 4 vertices per quad */
int n = stb_easy_font_print(0, 0, (char *)text, NULL, quads, (int)sizeof quads);
float tw = 0.0f; /* scaled glyph-run width (measureText analogue) */
for (int i = 0; i < n; i++) tw = fmaxf(tw, quads[2 * i] * scale);
float x = (float)w - tw - MARGIN;
float y = (float)h - MARGIN - fs; /* TS baseline is h-8; easy font is top-left */
float outline = fmaxf(1.0f, fs / 14.0f); /* lineWidth = max(1, fontSize/14) */
static const float dirs[8][2] = {{1,0},{-1,0},{0,1},{0,-1},{1,1},{1,-1},{-1,1},{-1,-1}};
for (int d = 0; d < 8; d++)
blend_quads(rgb, w, h, quads, n, x + dirs[d][0] * outline, y + dirs[d][1] * outline,
scale, 0, 0, 0, STROKE_ALPHA);
blend_quads(rgb, w, h, quads, n, x, y, scale, 255, 255, 255, FILL_ALPHA);
}
int main(void)
{
int w, h, n;
unsigned char *src = stbi_load("input.jpg", &w, &h, &n, 3);
if (!src) return 1;
int tw = 1280; /* aspect-preserving target: h' = round(h / w * targetWidth) */
int th = (int)lroundf((float)h / (float)w * tw);
unsigned char *out = malloc(3 * (size_t)tw * th);
if (!out) return 1;
stbir_resize_uint8(src, w, h, 0, out, tw, th, 0, 3);
watermark(out, tw, th, "CosmoDev"); /* easy font covers ASCII only */
stbi_write_jpg("output.jpg", tw, th, 3, out, 92); /* 0-100 scale ↔ TS 0.92 */
free(out);
stbi_image_free(src);
return 0;
}
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 →