Skip to content

Photo Merge — C 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 C implementation — the same logic the interactive tool runs, in a shareable, citable form.

/* photo-merge — C (C11) port: side-by-side merge cell layout. */

#include <math.h>
#include <stdio.h>

/* 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. */
typedef struct {
    int width, height;
} ImageSize;

/* Where one image lands on the merged canvas. */
typedef struct {
    double scale;
    int x, y, w, h;
} Placement;

/* Merged canvas dimensions plus one placement per input image.
 * 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. JS Math.round
 * rounds half up; lround matches that for the positive values here.
 * Returns the number of placements written (== n). */
int merge_layout(const ImageSize *images, int n, int gap,
                 int *canvas_w, int *canvas_h, Placement *out) {
    int cell_w = 0, cell_h = 0;
    for (int i = 0; i < n; i++) {
        if (images[i].width > cell_w) cell_w = images[i].width;
        if (images[i].height > cell_h) cell_h = images[i].height;
    }
    *canvas_w = n * cell_w + (n + 1) * gap;
    *canvas_h = cell_h + 2 * gap;

    for (int i = 0; i < n; i++) {
        double scale = cell_w / (double)images[i].width;
        double hscale = cell_h / (double)images[i].height;
        if (hscale < scale) scale = hscale;
        int w = (int)lround(images[i].width * scale);
        int h = (int)lround(images[i].height * scale);
        out[i].scale = scale;
        out[i].w = w;
        out[i].h = h;
        out[i].x = gap + i * (cell_w + gap)
                   + (int)lround((cell_w - w) / 2.0);
        out[i].y = gap + (int)lround((cell_h - h) / 2.0);
    }
    return n;
}

int main(void) {
    /* 800x600 + 600x800 + 1024x768 with a 12px gap -> 3120x824 canvas. */
    ImageSize images[3] = {{800, 600}, {600, 800}, {1024, 768}};
    Placement placements[3];
    int canvas_w, canvas_h;
    int n = merge_layout(images, 3, 12, &canvas_w, &canvas_h, placements);
    printf("canvas %dx%d\n", canvas_w, canvas_h);
    for (int i = 0; i < n; i++) {
        printf("image %d: (%d, %d) %dx%d scale=%.4f\n", i,
               placements[i].x, placements[i].y,
               placements[i].w, placements[i].h, placements[i].scale);
    }
    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 →