A hero banner and a square tile may come from the same source image but retain different areas. Before exporting both, a layout pipeline can calculate which source pixels survive every planned crop and whether a supplied subject box stays inside that intersection.
1. Put every rectangle in source coordinates
Use the dimensions of the final, orientation-corrected source. Each crop is an axis-aligned rectangle described by x, y, width and height, all in source pixels. The origin is the top-left corner. A crop does not contain coordinates from a resized preview.
Treat rectangles as half-open regions: x is included and x + width is the boundary just beyond the right edge. Two crops that merely touch at an edge have no shared pixel area. The same convention applies to the bottom edge.
The optional subject box is supplied by the caller. It can come from a manual layout decision or an earlier detector, but this program neither finds the subject nor decides whether that box is accurate.
2. Intersect the planned crop windows
The common left and top boundaries are the largest left and top values among the crops. The common right and bottom boundaries are the smallest right and bottom values. A positive width and height define the intersection; otherwise the result is null.
Save this as shared-crop.mjs. It accepts integer source dimensions from 1 to 1,000,000 and requires every positive-size rectangle to stay inside the source. The explicit dimension cap also keeps the maximum area at 10^12, within exact safe-integer arithmetic.
export function sharedCropArea(width, height, crops, subject = null) {
const positive = n => Number.isSafeInteger(n) && n > 0 && n <= 1_000_000;
if (!positive(width) || !positive(height)) {
throw new TypeError('Source dimensions must be integers from 1 to 1000000');
}
const rect = value => {
if (!value || typeof value !== 'object') throw new TypeError('Expected a rectangle');
const { x, y, width: w, height: h } = value;
if (!Number.isSafeInteger(x) || !Number.isSafeInteger(y) ||
x < 0 || y < 0 || !positive(w) || !positive(h) ||
x + w > width || y + h > height) {
throw new TypeError('Rectangle must be inside the source with positive integer size');
}
return { x, y, width: w, height: h };
};
if (!Array.isArray(crops) || crops.length < 2) {
throw new TypeError('Provide at least two crop rectangles');
}
const boxes = crops.map(rect);
const target = subject === null ? null : rect(subject);
let left = 0, top = 0, right = width, bottom = height;
for (const box of boxes) {
left = Math.max(left, box.x);
top = Math.max(top, box.y);
right = Math.min(right, box.x + box.width);
bottom = Math.min(bottom, box.y + box.height);
}
const shared = right > left && bottom > top
? { x: left, y: top, width: right - left, height: bottom - top }
: null;
const subjectFits = target === null ? null : shared !== null &&
target.x >= left && target.y >= top &&
target.x + target.width <= right && target.y + target.height <= bottom;
return { shared, sharedArea: shared ? shared.width * shared.height : 0, subjectFits };
}
A subjectFits result of true means the entire supplied subject rectangle is contained in every crop. False means some part is outside the shared region, or the crops have no intersection. Null means no subject box was supplied. It does not turn an absent measurement into a pass.
3. Describe a banner and square tile
Save this synthetic example as job.json. The source is 1600 × 900. The first window is a centered 1600 × 800 banner, with a 2:1 ratio. The second is a centered 900 × 900 square. Neither rectangle represents an image file read by the script.
{
"width": 1600,
"height": 900,
"crops": [
{
"x": 0,
"y": 50,
"width": 1600,
"height": 800
},
{
"x": 350,
"y": 0,
"width": 900,
"height": 900
}
],
"subject": {
"x": 600,
"y": 180,
"width": 400,
"height": 500
}
}
The shared rectangle starts at x = 350 and y = 50. Its right boundary is 1250 and bottom boundary is 850, producing width 900 and height 800. The shared area is 720,000 source pixels. The subject spans x = 600 to 1000 and y = 180 to 680, so the whole box fits.
Moving only the subject’s x coordinate to 300 makes subjectFits false. That box still fits within the original source, but its leftmost 50 pixels lie outside the square crop. Moving the crop or revising the composition is a separate decision; the checker does not silently reposition either one.
4. Read one JSON job from the command line
Save this as crop-cli.mjs in the same directory. Exit 0 means a nonempty shared region exists and no supplied subject box fails containment. Exit 1 means there is no common area or the subject box fails. Exit 2 means invalid input or a command error.
import { readFile } from 'node:fs/promises';
import { sharedCropArea } from './shared-crop.mjs';
try {
if (process.argv.length !== 3) throw new Error('Usage: node crop-cli.mjs job.json');
const job = JSON.parse(await readFile(process.argv[2], 'utf8'));
const result = sharedCropArea(job.width, job.height, job.crops, job.subject ?? null);
console.log(JSON.stringify(result, null, 2));
process.exitCode = result.shared && result.subjectFits !== false ? 0 : 1;
} catch (error) {
console.error(error.message);
process.exitCode = 2;
}
When subjectFits is null, exit 0 only reports that the crop intersection exists. A consuming application should still display the null value so a missing subject box is not mistaken for a checked composition.
5. Check the example and boundary cases
node crop-cli.mjs job.json
The supplied fixture exits 0 and returns shared = {x:350, y:50, width:900, height:800}, sharedArea = 720000 and subjectFits = true. A separate fixture with subject.x = 300 exits 1. A zero source width and a missing filename both exit 2.
The code passed 23 synthetic checks with Node.js 24.20.0. The cases include identical crops, disjoint crops, edge contact, a one-pixel intersection, a third crop, a subject exactly on the intersection boundaries, invalid coordinates and preservation of the input data. The command-line exit paths were exercised independently.
All of these checks used local JSON and rectangle arithmetic. No source image was decoded, generated, cropped, exported or visually inspected.
6. Use the result as layout metadata
Store the source dimensions and crop rectangles beside the export job. If a later stage changes orientation, scales the source or shifts a focal point, recompute the rectangles in the updated coordinate system before using this result.
The shared region describes source geometry. It does not measure the subject’s size after each crop is resized, account for a circular mask or describe platform UI overlays. Those need separate geometry. Even a fully contained subject may be too small in the final layout.
Use a false result to identify a concrete layout task: adjust a crop window, move the intended subject area or prepare separate compositions. Keep the original box and the revised box in the job record so a later export can be traced to the layout that was actually checked.
Drafted with AI assistance. The working code was tested on synthetic rectangle data only.
PixMind source: Choosing a composition and aspect ratio. This developer adaptation addresses crop metadata without depending on a model’s current options, price or generation behavior.
JavaScript reference: MDN Number.isSafeInteger.
About the author
- The PixMind team shares practical AI image and video workflows. PixMind brings text-to-image, reference-based creation, editing, and video tools into one workspace. Website: https://www.pixmind.io/
Comments
Join the discussionNo comments yet. Be the first to comment.