You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, amplitudeX = 20, frequencyY = 4, phaseY = 0, amplitudeY = 10, frequencyX = 3, phaseX = 0) {
// Get image dimensions
const width = originalImg.naturalWidth || originalImg.width;
const height = originalImg.naturalHeight || originalImg.height;
// Create the destination canvas
const destCanvas = document.createElement('canvas');
destCanvas.width = width;
destCanvas.height = height;
const destCtx = destCanvas.getContext('2d');
// Check if context was successfully created
if (!destCtx) {
console.error("Image Brain Wave Filter: Failed to get 2D context for destination canvas.");
// If context cannot be obtained, set canvas to 0x0 to signal critical error
destCanvas.width = 0;
destCanvas.height = 0;
return destCanvas;
}
// Handle cases where image dimensions are invalid
if (width === 0 || height === 0) {
console.warn("Image Brain Wave Filter: Image has zero width or height.");
// Canvas is already created with 0x0 or WxH dimensions, will be blank.
return destCanvas;
}
// Create a source canvas to draw the original image and get its pixel data
const srcCanvas = document.createElement('canvas');
srcCanvas.width = width;
srcCanvas.height = height;
const srcCtx = srcCanvas.getContext('2d');
if (!srcCtx) {
console.error("Image Brain Wave Filter: Failed to get 2D context for source canvas.");
// Draw an error message on the destination canvas
destCtx.fillStyle = 'rgb(100,0,0)'; // Dark red background
destCtx.fillRect(0,0,width,height);
destCtx.fillStyle = 'white';
destCtx.font = "bold 14px Arial";
destCtx.textAlign = "center";
destCtx.textBaseline = "middle";
destCtx.fillText("Error: Canvas Source Context", width / 2, height / 2);
return destCanvas;
}
// Draw the original image onto the source canvas
try {
srcCtx.drawImage(originalImg, 0, 0, width, height);
} catch (e) {
console.error("Image Brain Wave Filter: Error drawing original image to source canvas:", e);
destCtx.fillStyle = 'rgb(100,0,0)';
destCtx.fillRect(0,0,width,height);
destCtx.fillStyle = 'white';
destCtx.font = "bold 14px Arial";
destCtx.textAlign = "center";
destCtx.textBaseline = "middle";
destCtx.fillText("Error: Drawing Image to Buffer", width / 2, height / 2);
return destCanvas;
}
let sourceImageData;
try {
// Attempt to get pixel data from the source canvas
sourceImageData = srcCtx.getImageData(0, 0, width, height);
} catch (e) {
console.error("Image Brain Wave Filter: Error getting image data (likely CORS issue):", e);
// If getImageData fails (e.g., CORS), draw the original image on destCanvas
// and overlay an error message.
try {
destCtx.drawImage(originalImg, 0, 0, width, height);
} catch (drawErr) {
// If drawing originalImg also fails here, clear to a solid color before text
console.error("Image Brain Wave Filter: Error drawing original image to destination canvas (fallback):", drawErr);
destCtx.fillStyle = 'rgb(100,0,0)'; // Dark red background
destCtx.fillRect(0,0,width,height);
}
// Error message box styling
const boxHeight = Math.min(60, Math.max(30, height * 0.3)); // Responsive box height
const boxY = height / 2 - boxHeight / 2;
destCtx.fillStyle = 'rgba(0, 0, 0, 0.75)';
destCtx.fillRect(0, boxY, width, boxHeight);
destCtx.fillStyle = 'white';
const mainFontSize = Math.min(16, Math.max(10, height * 0.1)); // Responsive font size
destCtx.font = `bold ${mainFontSize}px Arial`;
destCtx.textAlign = "center";
destCtx.textBaseline = "middle";
destCtx.fillText("Error: Could not process pixels.", width / 2, height / 2 - boxHeight / 4);
const subFontSize = Math.min(12, Math.max(8, height * 0.08));
destCtx.font = `${subFontSize}px Arial`;
destCtx.fillText("(Likely Cross-Origin Image Restriction)", width / 2, height / 2 + boxHeight / 4);
return destCanvas;
}
const sourceData = sourceImageData.data;
// Prepare image data for the destination canvas
const destImageData = destCtx.createImageData(width, height);
const destData = destImageData.data;
const PI2 = Math.PI * 2;
// Iterate over each pixel in the destination image
for (let y = 0; y < height; y++) {
const normalizedY = y / height; // Y position normalized to [0, 1]
for (let x = 0; x < width; x++) {
const normalizedX = x / width; // X position normalized to [0, 1]
// Calculate wave angles based on normalized positions, frequency, and phase
const angleYWave = normalizedY * frequencyY * PI2 + phaseY;
const angleXWave = normalizedX * frequencyX * PI2 + phaseX;
// Calculate displacement amounts
const offsetX = amplitudeX * Math.sin(angleYWave);
const offsetY = amplitudeY * Math.sin(angleXWave);
// Determine source pixel coordinates after displacement
let srcX = x + offsetX;
let srcY = y + offsetY;
// Clamp source coordinates to be within the image bounds
srcX = Math.max(0, Math.min(width - 1, srcX));
srcY = Math.max(0, Math.min(height - 1, srcY));
// Round to nearest integer coordinates for sampling (Nearest Neighbor)
const srcX_int = Math.round(srcX);
const srcY_int = Math.round(srcY);
// Calculate 1D array indices for source and destination pixels
const destPixelIndex = (y * width + x) * 4;
const sourcePixelIndex = (srcY_int * width + srcX_int) * 4;
// Copy RGBA data from source pixel to destination pixel
destData[destPixelIndex] = sourceData[sourcePixelIndex]; // Red
destData[destPixelIndex + 1] = sourceData[sourcePixelIndex + 1]; // Green
destData[destPixelIndex + 2] = sourceData[sourcePixelIndex + 2]; // Blue
destData[destPixelIndex + 3] = sourceData[sourcePixelIndex + 3]; // Alpha
}
}
// Put the manipulated pixel data onto the destination canvas
destCtx.putImageData(destImageData, 0, 0);
return destCanvas;
}
Apply Changes