You can edit the below JavaScript code to customize the image tool.
// Helper function to parse a color string (e.g., 'red', '#FF0000', 'rgb(255,0,0)')
// into an {r, g, b} object.
function _getParsedColorRgb(colorStr) {
const canvas = document.createElement('canvas');
canvas.width = 1;
canvas.height = 1;
const ctx = canvas.getContext('2d');
if (!ctx) {
// Fallback to black if context cannot be obtained (highly unlikely in browser).
return { r: 0, g: 0, b: 0 };
}
// Set a default fillStyle in case colorStr is completely invalid.
// Canvas spec dictates that an invalid color string makes fillStyle ignore the new value.
// So whatever it was before will remain. For a new canvas, it's black.
// Explicitly setting black first ensures predictable fallback if colorStr becomes e.g. "transparent"
// (which would make getImageData return 0,0,0,0).
ctx.fillStyle = 'black'; // Default to black
ctx.fillStyle = colorStr; // Attempt to set the user-provided color
ctx.fillRect(0, 0, 1, 1); // Draw the color onto the 1x1 canvas
const data = ctx.getImageData(0, 0, 1, 1).data;
return { r: data[0], g: data[1], b: data[2] };
}
function processImage(originalImg, saturation = 1.2, contrast = 1.1, brightness = 1.05, tintRgbCsv = '0,20,50', tintOpacity = 0.05, vignetteStrength = 0.4, vignetteFalloff = 0.6, vignetteColor = 'black') {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
if (!ctx) {
console.error("Failed to get 2D context. Your browser might not support Canvas fully or encountered an error.");
// Return a minimal canvas as per requirements (must return a canvas or Image).
const fallbackCanvas = document.createElement('canvas');
fallbackCanvas.width = 1;
fallbackCanvas.height = 1;
// No context here to draw anything, but an empty 1x1 canvas is provided.
return fallbackCanvas;
}
const w = originalImg.naturalWidth;
const h = originalImg.naturalHeight;
if (w === 0 || h === 0) {
console.warn("Original image has zero width or height. Returning a 1x1 transparent canvas.");
canvas.width = 1;
canvas.height = 1;
// ctx is valid here, so we can clear it to be transparent.
ctx.clearRect(0, 0, 1, 1);
return canvas;
}
canvas.width = w;
canvas.height = h;
// 1. Apply global image adjustments (saturation, contrast, brightness)
// Clamp parameter values to sensible ranges.
const Fsaturation = Math.max(0, saturation);
const Fcontrast = Math.max(0, contrast);
const Fbrightness = Math.max(0, brightness);
let filterClauses = [];
if (Fsaturation !== 1) filterClauses.push(`saturate(${Fsaturation})`);
if (Fcontrast !== 1) filterClauses.push(`contrast(${Fcontrast})`);
if (Fbrightness !== 1) filterClauses.push(`brightness(${Fbrightness})`);
const filterString = filterClauses.join(' ');
if (filterString) {
ctx.filter = filterString;
}
ctx.drawImage(originalImg, 0, 0, w, h);
if (filterString) { // Reset filter only if it was applied
ctx.filter = 'none';
}
// 2. Apply Tint
const VtintOpacity = Math.max(0, Math.min(1, tintOpacity)); // Clamp opacity 0-1
if (VtintOpacity > 0 && tintRgbCsv && typeof tintRgbCsv === 'string') {
const trimmedTintRgbCsv = tintRgbCsv.trim();
if (trimmedTintRgbCsv !== '') {
const rgbParts = trimmedTintRgbCsv.split(',').map(s => parseInt(s.trim(), 10));
if (rgbParts.length === 3 && rgbParts.every(val => !isNaN(val) && val >= 0 && val <= 255)) {
ctx.fillStyle = `rgba(${rgbParts[0]}, ${rgbParts[1]}, ${rgbParts[2]}, ${VtintOpacity})`;
ctx.fillRect(0, 0, w, h);
} else {
console.warn(`Invalid tintRgbCsv format: "${tintRgbCsv}". Expected 'R,G,B' (e.g., '0,20,50'). Skipping tint.`);
}
}
}
// 3. Apply Vignette
const Vstrength = Math.max(0, Math.min(1, vignetteStrength)); // Clamp strength 0-1
if (Vstrength > 0) {
// Clamp falloff: 0 <= falloff < 1. Max 0.999 to prevent innerRadius == outerRadius.
let Vfalloff = Math.max(0, Math.min(0.999, vignetteFalloff));
const cx = w / 2;
const cy = h / 2;
// outerRadius is the distance from center to a corner.
const outerRadius = Math.sqrt(cx * cx + cy * cy);
if (outerRadius > 0) { // Proceed only if image has dimensions making outerRadius > 0
let innerRadius = outerRadius * Vfalloff;
// Ensure innerRadius < outerRadius for a valid gradient.
// This might happen if outerRadius is very small and Vfalloff is high.
if (innerRadius >= outerRadius) {
innerRadius = Math.max(0, outerRadius - 0.1); // Ensure a small gradient band and non-negative.
}
const gradient = ctx.createRadialGradient(cx, cy, innerRadius, cx, cy, outerRadius);
const baseVignetteColorRgb = _getParsedColorRgb(vignetteColor);
gradient.addColorStop(0, `rgba(${baseVignetteColorRgb.r}, ${baseVignetteColorRgb.g}, ${baseVignetteColorRgb.b}, 0)`);
gradient.addColorStop(1, `rgba(${baseVignetteColorRgb.r}, ${baseVignetteColorRgb.g}, ${baseVignetteColorRgb.b}, ${Vstrength})`);
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, w, h);
}
}
return canvas;
}
Free Image Tool Creator
Can't find the image tool you're looking for? Create one based on your own needs now!
The Image Drone Footage Filter Effect Tool is designed to enhance drone footage by allowing users to apply various visual adjustments. It offers functionalities such as adjusting saturation, contrast, and brightness to improve the overall image quality. Users can also add a custom color tint with adjustable opacity, and apply a vignette effect to help focus on the central subject of the image. This tool is useful for videographers and drone enthusiasts looking to enhance their footage for presentations, films, or personal projects.