You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, redOffsetX = -5, redOffsetY = 0, greenOffsetX = 0, greenOffsetY = 5, blueOffsetX = 5, blueOffsetY = 0) {
// Ensure originalImg has dimensions. If not, it might not be loaded or is invalid.
if (!originalImg || typeof originalImg.width === 'undefined' || originalImg.width === 0 || typeof originalImg.height === 'undefined' || originalImg.height === 0) {
console.error("Original image is invalid, not loaded, or has zero dimensions.");
// Return a small, empty canvas as a fallback.
const emptyCanvas = document.createElement('canvas');
emptyCanvas.width = 1; // Avoid 0x0 canvas if it causes issues downstream
emptyCanvas.height = 1;
return emptyCanvas;
}
const width = originalImg.width;
const height = originalImg.height;
// 1. Create the output canvas
const outputCanvas = document.createElement('canvas');
outputCanvas.width = width;
outputCanvas.height = height;
const ctx = outputCanvas.getContext('2d');
// 2. Create a temporary canvas for pixel manipulation
const tempCanvas = document.createElement('canvas');
tempCanvas.width = width;
tempCanvas.height = height;
// The 'willReadFrequently' hint can optimize getImageData/putImageData operations.
const tempCtx = tempCanvas.getContext('2d', { willReadFrequently: true });
// Draw the original image onto the temporary canvas
tempCtx.drawImage(originalImg, 0, 0, width, height);
let originalImageData;
try {
// Get pixel data from the temporary canvas
originalImageData = tempCtx.getImageData(0, 0, width, height);
} catch (e) {
// This can happen if the image is cross-origin and taints the canvas.
console.error("Error getting image data, possibly due to cross-origin restrictions:", e);
// Fallback: return a canvas with the original image drawn (no effect applied).
ctx.drawImage(originalImg, 0, 0, width, height);
return outputCanvas;
}
const originalData = originalImageData.data;
// 3. Create ImageData objects for each color channel
const redChannelImageData = tempCtx.createImageData(width, height);
const greenChannelImageData = tempCtx.createImageData(width, height);
const blueChannelImageData = tempCtx.createImageData(width, height);
const rData = redChannelImageData.data;
const gData = greenChannelImageData.data;
const bData = blueChannelImageData.data;
// Populate the channel-specific ImageData
for (let i = 0; i < originalData.length; i += 4) {
const r = originalData[i];
const g = originalData[i + 1];
const b = originalData[i + 2];
const a = originalData[i + 3];
// Red channel data
rData[i] = r;
rData[i + 1] = 0; // Zero out green
rData[i + 2] = 0; // Zero out blue
rData[i + 3] = a; // Preserve alpha
// Green channel data
gData[i] = 0; // Zero out red
gData[i + 1] = g;
gData[i + 2] = 0; // Zero out blue
gData[i + 3] = a; // Preserve alpha
// Blue channel data
bData[i] = 0; // Zero out red
bData[i + 1] = 0; // Zero out green
bData[i + 2] = b;
bData[i + 3] = a; // Preserve alpha
}
// 4. Draw the channels onto the output canvas with specified offsets and blending
ctx.clearRect(0, 0, width, height); // Clear the output canvas
// 'lighter' blending mode adds color values, good for prism/chromatic aberration
ctx.globalCompositeOperation = 'lighter';
// Convert string parameters to numbers for safety, though drawImage often coerces.
const numRedOffsetX = Number(redOffsetX);
const numRedOffsetY = Number(redOffsetY);
const numGreenOffsetX = Number(greenOffsetX);
const numGreenOffsetY = Number(greenOffsetY);
const numBlueOffsetX = Number(blueOffsetX);
const numBlueOffsetY = Number(blueOffsetY);
// Draw Red Channel
// Put red channel ImageData onto tempCanvas, then draw tempCanvas to outputCanvas
tempCtx.putImageData(redChannelImageData, 0, 0);
ctx.drawImage(tempCanvas, numRedOffsetX, numRedOffsetY);
// Draw Green Channel
tempCtx.putImageData(greenChannelImageData, 0, 0);
ctx.drawImage(tempCanvas, numGreenOffsetX, numGreenOffsetY);
// Draw Blue Channel
tempCtx.putImageData(blueChannelImageData, 0, 0);
ctx.drawImage(tempCanvas, numBlueOffsetX, numBlueOffsetY);
// Reset globalCompositeOperation to default
ctx.globalCompositeOperation = 'source-over';
return outputCanvas;
}
Apply Changes