You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(
originalImg,
numMainCracks = 6,
mainCrackColor = "rgba(240, 240, 240, 0.9)",
maxCrackThickness = 5,
minCrackThickness = 1,
impactXPercent = -1, // -1 for random, 0-100 for specific percentage
impactYPercent = -1, // -1 for random, 0-100 for specific percentage
jaggedness = 10, // Max perpendicular deviation for crack segments (pixels)
crackSegmentLength = 25, // Average length of a segment in a crack line (pixels)
addSecondaryCracks = 1, // 1 to add secondary cracks, 0 to disable
secondaryCracksPerMain = 2, // Average number of secondary cracks per main crack
secondaryCrackLengthFactor = 0.35, // Secondary crack length relative to its main crack
secondaryCrackThicknessFactor = 0.6 // Secondary crack thickness relative to its main crack
) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const w = originalImg.naturalWidth;
const h = originalImg.naturalHeight;
if (w === 0 || h === 0) {
canvas.width = w; // Could be 0
canvas.height = h; // Could be 0
console.warn("Image has zero width or height. Output might be empty or an error might occur.");
// Depending on requirements, could throw an error or return an empty/minimal canvas
if (w === 0 || h === 0) return canvas; // Return 0x0 canvas if image is 0x0
}
canvas.width = w;
canvas.height = h;
// Draw the original image onto the canvas
ctx.drawImage(originalImg, 0, 0, w, h);
// Sanitize numeric parameters
numMainCracks = Math.max(0, parseInt(numMainCracks, 10) || 0);
let nMaxCrackThickness = Math.max(0, parseFloat(maxCrackThickness) || 0);
let nMinCrackThickness = Math.max(0, parseFloat(minCrackThickness) || 0);
if (nMinCrackThickness > nMaxCrackThickness) {
[nMinCrackThickness, nMaxCrackThickness] = [nMaxCrackThickness, nMinCrackThickness]; // Swap if min > max
}
// Ensure minCrackThickness is at least a very small positive number if max is positive, to avoid 0 thickness if max and min are same
if (nMaxCrackThickness > 0 && nMinCrackThickness === 0) nMinCrackThickness = Math.min(0.1, nMaxCrackThickness);
jaggedness = Math.max(0, parseFloat(jaggedness) || 0);
crackSegmentLength = Math.max(5, parseFloat(crackSegmentLength) || 5); // Min segment length of 5px
addSecondaryCracks = parseInt(addSecondaryCracks, 10) || 0;
secondaryCracksPerMain = Math.max(0, parseInt(secondaryCracksPerMain, 10) || 0);
secondaryCrackLengthFactor = Math.max(0, parseFloat(secondaryCrackLengthFactor) || 0);
secondaryCrackThicknessFactor = Math.max(0, parseFloat(secondaryCrackThicknessFactor) || 0);
// Helper function to draw a single jagged crack line
function drawJaggedLine(context, x1, y1, x2, y2, thickness, currentJaggedness, currentSegmentLength) {
context.beginPath();
context.moveTo(x1, y1);
const dx = x2 - x1;
const dy = y2 - y1;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance === 0) {
return;
}
currentSegmentLength = Math.max(5, currentSegmentLength); // Ensure segment length is positive
const numSegments = Math.max(1, Math.floor(distance / currentSegmentLength));
const normalX = -dy / distance;
const normalY = dx / distance;
for (let i = 1; i <= numSegments; i++) {
const t = i / numSegments;
const currentTargetX = x1 + dx * t;
const currentTargetY = y1 + dy * t;
let actualX = currentTargetX;
let actualY = currentTargetY;
if (i < numSegments && currentJaggedness > 0) {
const randomOffset = (Math.random() - 0.5) * 2 * currentJaggedness;
actualX += normalX * randomOffset;
actualY += normalY * randomOffset;
}
context.lineTo(actualX, actualY);
}
context.lineWidth = Math.max(0.1, thickness);
context.stroke();
}
// Determine impact point of the cracks
let impactX, impactY;
const cleanImpactXPercent = parseFloat(impactXPercent);
const cleanImpactYPercent = parseFloat(impactYPercent);
if (cleanImpactXPercent >= 0 && cleanImpactXPercent <= 100) {
impactX = w * (cleanImpactXPercent / 100);
} else {
impactX = Math.random() * w;
}
if (cleanImpactYPercent >= 0 && cleanImpactYPercent <= 100) {
impactY = h * (cleanImpactYPercent / 100);
} else {
impactY = Math.random() * h;
}
impactX = Math.max(0, Math.min(w, impactX));
impactY = Math.max(0, Math.min(h, impactY));
ctx.strokeStyle = mainCrackColor;
ctx.lineCap = "round";
ctx.lineJoin = "round";
const mainCracksData = [];
// Generate and draw main cracks
for (let i = 0; i < numMainCracks; i++) {
const angle = Math.random() * Math.PI * 2;
const length = (Math.random() * 0.6 + 0.4) * Math.max(w, h) * 0.85;
const endX = impactX + Math.cos(angle) * length;
const endY = impactY + Math.sin(angle) * length;
const thicknessRange = Math.max(0, nMaxCrackThickness - nMinCrackThickness);
const crackThickness = nMinCrackThickness + Math.random() * thicknessRange;
if (crackThickness <= 0) continue; // Skip if thickness is zero or less
drawJaggedLine(ctx, impactX, impactY, endX, endY, crackThickness, jaggedness, crackSegmentLength);
if (addSecondaryCracks !== 0) {
const actualLength = Math.sqrt(Math.pow(endX - impactX, 2) + Math.pow(endY - impactY, 2));
if (actualLength > 0) {
mainCracksData.push({
startX: impactX, startY: impactY,
endX: endX, endY: endY,
mainAngle: angle,
mainLength: actualLength,
mainThickness: crackThickness
});
}
}
}
// Generate and draw secondary cracks
if (addSecondaryCracks !== 0 && mainCracksData.length > 0) {
mainCracksData.forEach(mainCrack => {
for (let j = 0; j < secondaryCracksPerMain; j++) {
if (Math.random() < 0.8) {
const t = Math.random() * 0.8 + 0.1;
const branchStartX = mainCrack.startX + (mainCrack.endX - mainCrack.startX) * t;
const branchStartY = mainCrack.startY + (mainCrack.endY - mainCrack.startY) * t;
const deviation = (Math.random() - 0.5) * Math.PI / 1.8;
const secondaryAngle = mainCrack.mainAngle + deviation;
const lengthRandomFactor = Math.random() * 0.5 + 0.5;
const secondaryLength = mainCrack.mainLength * secondaryCrackLengthFactor * lengthRandomFactor;
if (secondaryLength < Math.min(10, crackSegmentLength)) continue;
const secondaryEndX = branchStartX + Math.cos(secondaryAngle) * secondaryLength;
const secondaryEndY = branchStartY + Math.sin(secondaryAngle) * secondaryLength;
const thicknessRandomFactor = Math.random() * 0.5 + 0.5;
let secThickness = mainCrack.mainThickness * secondaryCrackThicknessFactor * thicknessRandomFactor;
secThickness = Math.max(0.5, secThickness); // Absolute minimum thickness for secondary cracks
secThickness = Math.min(secThickness, mainCrack.mainThickness); // Don't exceed parent thickness
if (secThickness <= 0) continue;
const secJaggedness = jaggedness * (Math.random() * 0.3 + 0.6);
const secSegmentLength = crackSegmentLength * (Math.random() * 0.4 + 0.7);
drawJaggedLine(ctx, branchStartX, branchStartY, secondaryEndX, secondaryEndY, secThickness, secJaggedness, secSegmentLength);
}
}
});
}
return canvas;
}
Apply Changes