You can edit the below JavaScript code to customize the image tool.
/**
* Crops an image to a custom polygon shape.
*
* @param {Image} originalImg The original HTMLImageElement (must be loaded, e.g., originalImg.complete should be true).
* @param {string} [pointsStr=""] A string representing the polygon points, e.g., "x1,y1 x2,y2 x3,y3 ...".
* Points are separated by spaces, coordinates (x,y) by a comma.
* If empty, invalid, or defines less than 3 points,
* it defaults to a rectangle covering the entire image.
* @returns {HTMLCanvasElement} A new canvas element containing the cropped image.
* The canvas dimensions will be the bounding box of the polygon.
* If the polygon (or the original image, in default mode) has zero area,
* an empty canvas of the corresponding dimensions (e.g., 0xH, Wx0, or 0x0) is returned.
*/
function processImage(originalImg, pointsStr = "") {
// Validate originalImg
// An Image object has width/height properties. If not loaded, these are often 0.
// The problem implies it's a usable JavaScript Image object.
if (!originalImg || typeof originalImg.width === 'undefined' || typeof originalImg.height === 'undefined') {
console.error("Invalid originalImg provided. It must be an Image object with width and height properties.");
const errCanvas = document.createElement('canvas');
errCanvas.width = 0;
errCanvas.height = 0;
return errCanvas;
}
// A further check could be `if (!originalImg.complete || originalImg.naturalWidth === 0)`
// but assuming `originalImg` is ready as per typical usage of such a function.
// 1. Parse pointsStr
let points = [];
if (typeof pointsStr === 'string' && pointsStr.trim() !== "") {
const stringPairs = pointsStr.trim().split(/\s+/); // Split by one or more spaces
for (const pair of stringPairs) {
const coords = pair.split(',');
if (coords.length === 2) {
const x = parseFloat(coords[0]);
const y = parseFloat(coords[1]);
if (!isNaN(x) && !isNaN(y)) {
points.push({ x, y });
} else {
points = []; // Invalidate all parsed points if one coordinate pair is bad
console.warn("Invalid coordinate value in pointsStr pair:", pair, "- NaN found after parsing.");
break;
}
} else {
points = []; // Invalidate all parsed points if pair format is bad
console.warn("Invalid point pair format in pointsStr (expected 'x,y'):", pair);
break;
}
}
}
// 2. Handle default or insufficient points for a polygon
let isDefaultApplied = false;
if (points.length < 3) {
// Check if user actually provided a non-empty pointsStr which turned out insufficient/invalid
if (typeof pointsStr === 'string' && pointsStr.trim() !== "") {
if (points.length > 0) { // Some points parsed, but not enough
console.warn(`Provided pointsStr ("${pointsStr}") resulted in ${points.length} valid points. A polygon requires at least 3. Defaulting to full image crop.`);
} else { // No points parsed from the provided string (all invalid or format issues)
console.warn(`Provided pointsStr ("${pointsStr}") could not be parsed into any valid points. A polygon requires at least 3. Defaulting to full image crop.`);
}
}
// Apply default: a rectangle covering the entire image
points = [
{ x: 0, y: 0 },
{ x: originalImg.width, y: 0 },
{ x: originalImg.width, y: originalImg.height },
{ x: 0, y: originalImg.height }
];
isDefaultApplied = true; // Mark that default points are now in use
}
// 3. Calculate the bounding box of the polygon
// This uses the finalized `points` array (either parsed or defaulted).
let minX = points[0].x;
let minY = points[0].y;
let maxX = points[0].x;
let maxY = points[0].y;
for (let i = 1; i < points.length; i++) {
minX = Math.min(minX, points[i].x);
minY = Math.min(minY, points[i].y);
maxX = Math.max(maxX, points[i].x);
maxY = Math.max(maxY, points[i].y);
}
// Ensure non-negative dimensions for bounding box.
const boundingBoxWidth = Math.max(0, maxX - minX);
const boundingBoxHeight = Math.max(0, maxY - minY);
// Create the output canvas with dimensions of the_polygon's bounding box
const outputCanvas = document.createElement('canvas');
outputCanvas.width = boundingBoxWidth;
outputCanvas.height = boundingBoxHeight;
// If the bounding box has zero area (e.g., width or height is 0),
// no drawing is needed. Return the (potentially 0-dimension) canvas.
if (boundingBoxWidth === 0 || boundingBoxHeight === 0) {
// Warn if user's specific points led to this, not if default logic for a 0-dim image did.
if (!isDefaultApplied) {
console.warn("Polygon results in zero area (e.g., all points collinear, or covers zero width/height). Returning an empty canvas of the calculated size.");
}
return outputCanvas;
}
const outputCtx = outputCanvas.getContext('2d');
// 4. Draw the polygon shape on the output canvas to use as a mask
outputCtx.beginPath();
// Path points must be translated to be relative to the bounding box's top-left corner (minX, minY),
// which becomes (0,0) on the outputCanvas.
outputCtx.moveTo(points[0].x - minX, points[0].y - minY);
for (let i = 1; i < points.length; i++) {
outputCtx.lineTo(points[i].x - minX, points[i].y - minY);
}
outputCtx.closePath();
// Fill the path. This filled shape will serve as the mask for the image.
// Any opaque color works; 'black' is conventional.
outputCtx.fillStyle = 'black';
outputCtx.fill(); // Uses non-zero winding rule by default for complex polygons
// 5. Use 'source-in' global composite operation.
// This operation means the "new" drawing (the originalImg) will only be visible
// where it overlaps with the "existing" content (the filled polygon mask).
// Effectively, the filled polygon becomes transparent, and the image is shown through it.
outputCtx.globalCompositeOperation = 'source-in';
// 6. Draw the original image onto the output canvas.
// The image is drawn offset by (-minX, -minY). This aligns the
// part of the original image corresponding to the polygon's bounding box
// with the output canvas. The 'source-in' operation then performs the "crop".
outputCtx.drawImage(originalImg, -minX, -minY, originalImg.width, originalImg.height);
// 7. Reset globalCompositeOperation to default (optional, but good practice if context were reused).
// outputCtx.globalCompositeOperation = 'source-over';
return outputCanvas;
}
Free Image Tool Creator
Can't find the image tool you're looking for? Create one based on your own needs now!
The Image Custom Polygon Cropper allows users to crop images into custom polygon shapes. By specifying the vertices of the polygon through a string of coordinates, users can create unique crop shapes beyond standard rectangles. This tool is particularly useful for graphic designers, artists, or anyone looking to create custom visuals for presentations, social media content, or other digital formats. If fewer than three points are provided, the tool defaults to cropping the image as a rectangle. The resulting cropped image is returned in a canvas format, ready for further use.