You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, rivetSize = 5, rivetSpacing = 50, rivetColor = '#888888', contrastLevel = 50, brightnessOffset = 10) {
// --- Start Helper Functions ---
function _parseHex(hex) {
hex = hex.replace(/^#/, '');
if (hex.length === 3) { // Expand shorthand hex (e.g., "03F" -> "0033FF")
hex = hex.split('').map(char => char + char).join('');
}
const bigint = parseInt(hex, 16);
const r = (bigint >> 16) & 255;
const g = (bigint >> 8) & 255;
const b = bigint & 255;
return [r, g, b];
}
function _componentToHex(c) {
const hex = Math.round(c).toString(16); // Ensure c is an integer before converting
return hex.length == 1 ? "0" + hex : hex;
}
function _rgbToHex(r, g, b) {
return "#" + _componentToHex(r) + _componentToHex(g) + _componentToHex(b);
}
function _adjustHexColor(hex, amount) {
let [r, g, b] = _parseHex(hex);
r = Math.max(0, Math.min(255, r + amount));
g = Math.max(0, Math.min(255, g + amount));
b = Math.max(0, Math.min(255, b + amount));
return _rgbToHex(r, g, b);
}
// --- End Helper Functions ---
const canvas = document.createElement('canvas');
// Use naturalWidth/Height for images that might have been scaled via CSS width/height attributes
// but fall back to width/height if naturalWidth/Height are 0 (e.g. not an HTMLImageElement or not loaded)
canvas.width = originalImg.naturalWidth || originalImg.width;
canvas.height = originalImg.naturalHeight || originalImg.height;
// If image dimensions are invalid, return an empty (or error indicating) canvas
if (canvas.width === 0 || canvas.height === 0) {
console.error("Image has no dimensions or is not fully loaded. Cannot process.");
// Optionally draw an error message on the canvas
// const errorCtx = canvas.getContext('2d');
// if(errorCtx) {
// canvas.width = canvas.width || 100; canvas.height = canvas.height || 30; // Min size for error
// errorCtx.font = "12px Arial";
// errorCtx.fillText("Error: Image not loaded", 5, 15);
// }
return canvas;
}
const ctx = canvas.getContext('2d');
if (!ctx) {
console.error("Could not get 2D rendering context for canvas.");
return canvas; // Return empty canvas if context cannot be obtained
}
// 1. Draw original image onto the canvas
ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
// 2. Process image data for metallic effect (Grayscale, Contrast, Brightness)
// This block is wrapped to ensure imageData operations only happen on valid canvas area
if (canvas.width > 0 && canvas.height > 0) {
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
// Map contrastLevel (0-100) to a factor (1-3)
// 0 -> factor 1 (no change)
// 50 -> factor 2
// 100 -> factor 3
const contrastFactor = 1 + (contrastLevel / 100) * 2;
for (let i = 0; i < data.length; i += 4) {
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];
// Grayscale conversion using the luminosity method (standard perceptual weights)
let gray = 0.299 * r + 0.587 * g + 0.114 * b;
// Contrast adjustment
// Formula: NewValue = Factor * (OldValue - MidPoint) + MidPoint
// MidPoint for 8-bit color is 128
gray = contrastFactor * (gray - 128) + 128;
// Brightness adjustment (additive offset)
gray += brightnessOffset;
// Clamp the resulting gray value to the valid [0, 255] range
gray = Math.max(0, Math.min(255, gray));
// Apply the new gray value to R, G, and B channels
data[i] = gray;
data[i + 1] = gray;
data[i + 2] = gray;
// Alpha channel (data[i+3]) remains unchanged
}
ctx.putImageData(imageData, 0, 0);
}
// 3. Draw Rivets (if rivetSize and rivetSpacing are positive)
if (rivetSize > 0 && rivetSpacing > 0) {
const rivetLighterColor = _adjustHexColor(rivetColor, 40); // Lighter shade for highlight
const rivetDarkerColor = _adjustHexColor(rivetColor, -40); // Darker shade for shadow/edge
// Configure a subtle drop shadow for the rivets to give them depth
ctx.shadowColor = 'rgba(0, 0, 0, 0.4)'; // Shadow color
ctx.shadowBlur = Math.max(1, rivetSize * 0.3); // Blur radius, ensure at least 1px
ctx.shadowOffsetX = rivetSize * 0.15; // Horizontal offset
ctx.shadowOffsetY = rivetSize * 0.15; // Vertical offset
// Iterate over the canvas to place rivets based on spacing
// Start from half spacing to center the grid of rivets
for (let y = rivetSpacing / 2; y < canvas.height; y += rivetSpacing) {
for (let x = rivetSpacing / 2; x < canvas.width; x += rivetSpacing) {
// Create a radial gradient for the rivet body to simulate a metallic sheen
// The gradient is offset slightly to simulate a light source from top-left
const gradX = x - rivetSize * 0.25; // X-coordinate of gradient start circle
const gradY = y - rivetSize * 0.25; // Y-coordinate of gradient start circle
const gradR0 = Math.max(1, rivetSize * 0.1); // Radius of gradient start circle (highlight spot)
const gradient = ctx.createRadialGradient(
gradX, gradY, gradR0, // Inner circle (highlight)
x, y, rivetSize // Outer circle (rivet edge)
);
// Define color stops for the gradient
gradient.addColorStop(0, rivetLighterColor); // Center of highlight
gradient.addColorStop(0.7, rivetColor); // Main rivet color
gradient.addColorStop(1, rivetDarkerColor); // Edge of rivet
ctx.fillStyle = gradient; // Apply the gradient fill
// Draw the rivet
ctx.beginPath();
ctx.arc(x, y, rivetSize, 0, 2 * Math.PI); // Full circle
ctx.fill();
}
}
// Reset shadow properties to avoid affecting subsequent drawing operations (if any)
ctx.shadowColor = 'transparent';
ctx.shadowBlur = 0;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
}
return canvas;
}
Apply Changes