You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(
originalImg,
opacity = 0.15,
textureType = "noise", // "noise" or "lines"
grainAmount = 0.2, // For "noise" type: 0.0 to 1.0, fractional strength of noise
monochromeNoiseStr = "true", // For "noise" type: "true" or "false" for grayscale noise
lineColor = "rgba(0,0,0,0.05)", // For "lines" type: CSS color string for lines
lineSpacing = 4, // For "lines" type: Spacing between lines in pixels
lineWidth = 1, // For "lines" type: Width of lines in pixels
blendMode = "overlay" // Canvas globalCompositeOperation blend mode
) {
// Default values for robust parameter parsing (fallback if Number(value) is NaN)
const defaults = {
opacity: 0.15,
grainAmount: 0.2,
lineSpacing: 4,
lineWidth: 1,
// Note: string parameters like textureType, monochromeNoiseStr, lineColor, blendMode
// will use their provided default if argument is undefined.
// String() conversion handles nulls to "null", etc.
};
let num; // Temporary variable for numeric conversion
num = Number(opacity);
const finalOpacity = isNaN(num) ? defaults.opacity : Math.max(0, Math.min(1, num));
const finalTextureType = String(textureType).toLowerCase();
num = Number(grainAmount);
const finalGrainAmount = isNaN(num) ? defaults.grainAmount : Math.max(0, Math.min(1, num));
// Only "true" (case-insensitive) enables monochrome noise. Other strings (incl. "false") disable it.
const finalMonochromeNoise = String(monochromeNoiseStr).toLowerCase() === "true";
const finalLineColor = String(lineColor); // CSS color strings are generally robust
num = Number(lineSpacing);
const finalLineSpacing = isNaN(num) ? defaults.lineSpacing : Math.max(1, Math.floor(num));
num = Number(lineWidth);
const finalLineWidth = isNaN(num) ? defaults.lineWidth : Math.max(1, Math.floor(num));
const finalBlendMode = String(blendMode);
// Main canvas setup
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const w = originalImg.naturalWidth || originalImg.width;
const h = originalImg.naturalHeight || originalImg.height;
if (w === 0 || h === 0) {
canvas.width = w; // Will be 0
canvas.height = h; // Will be 0
return canvas; // Return empty canvas for 0-size image
}
canvas.width = w;
canvas.height = h;
// 1. Draw the original image onto the main canvas
ctx.drawImage(originalImg, 0, 0, w, h);
// 2. Create the texture layer on a separate (temporary) canvas
const textureCanvas = document.createElement('canvas');
textureCanvas.width = w;
textureCanvas.height = h;
const textureCtx = textureCanvas.getContext('2d');
if (finalTextureType === "noise") {
const noiseImageData = textureCtx.createImageData(w, h);
const noiseData = noiseImageData.data;
// Calculate noise deviation: finalGrainAmount (0-1) scales max deviation from 128.
// Max deviation from 128 is finalGrainAmount * 127.5.
// Random factor is in [-1, 1].
const deviation = finalGrainAmount * 127.5;
for (let i = 0; i < noiseData.length; i += 4) {
let rVal, gVal, bVal;
const randomFactor = Math.random() * 2 - 1; // Random number in [-1, 1]
if (finalMonochromeNoise) {
const noise = 128 + randomFactor * deviation;
rVal = gVal = bVal = Math.max(0, Math.min(255, noise));
} else {
rVal = Math.max(0, Math.min(255, 128 + (Math.random() * 2 - 1) * deviation));
gVal = Math.max(0, Math.min(255, 128 + (Math.random() * 2 - 1) * deviation));
bVal = Math.max(0, Math.min(255, 128 + (Math.random() * 2 - 1) * deviation));
}
noiseData[i] = rVal;
noiseData[i + 1] = gVal;
noiseData[i + 2] = bVal;
noiseData[i + 3] = 255; // Noise layer is fully opaque
}
textureCtx.putImageData(noiseImageData, 0, 0);
} else if (finalTextureType === "lines") {
// Create a small pattern canvas for lines
const patternCanvas = document.createElement('canvas');
patternCanvas.width = finalLineSpacing;
patternCanvas.height = finalLineSpacing;
const pCtx = patternCanvas.getContext('2d');
pCtx.fillStyle = finalLineColor; // Apply line color (can include alpha)
// Draw a horizontal line at the top of the pattern tile
pCtx.fillRect(0, 0, finalLineSpacing, finalLineWidth);
// Draw a vertical line at the left of the pattern tile
pCtx.fillRect(0, 0, finalLineWidth, finalLineSpacing);
const pattern = textureCtx.createPattern(patternCanvas, 'repeat');
if (pattern) { // createPattern can return null if dimensions are invalid (though guarded by Math.max(1,...))
textureCtx.fillStyle = pattern;
textureCtx.fillRect(0, 0, w, h); // Fill texture canvas with the repeating pattern
}
} else {
// Unknown textureType or "none" specified.
// In this case, textureCanvas remains transparent (default state).
// console.warn(`Unknown or unsupported textureType: '${finalTextureType}'. No texture will be applied.`);
}
// 3. Blend the texture layer onto the main canvas
if (finalOpacity > 0) { // No need to blend if texture is fully transparent
ctx.globalAlpha = finalOpacity;
// Browsers usually default to 'source-over' for invalid composite operations.
ctx.globalCompositeOperation = finalBlendMode;
ctx.drawImage(textureCanvas, 0, 0, w, h);
// 4. Reset blending properties on the main context
ctx.globalAlpha = 1.0;
ctx.globalCompositeOperation = 'source-over';
}
return canvas;
}
Apply Changes