You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, asteroidCount = 50, minAsteroidSize = 5, maxAsteroidSize = 30, asteroidBaseColor = "#808080", asteroidColorVariation = 0.2, highlightFactor = 1.3, shadowFactor = 0.7) {
// Helper function: parseHexColor
// Parses a hex color string (e.g., #FF0000 or #F00) into an {r, g, b} object.
// Defaults to black if parsing fails.
function _parseHexColor(hex) {
hex = String(hex).replace(/^#/, '');
if (hex.length === 3) {
hex = hex.split('').map(char => char + char).join('');
}
if (hex.length !== 6) {
// console.warn("Invalid hex color for parsing:", hex, "- defaulting to black.");
return { r: 0, g: 0, b: 0 };
}
const bigint = parseInt(hex, 16);
if (isNaN(bigint)) {
// console.warn("Invalid hex color value:", hex, "- defaulting to black.");
return { r: 0, g: 0, b: 0 };
}
const r = (bigint >> 16) & 255;
const g = (bigint >> 8) & 255;
const b = bigint & 255;
return { r, g, b };
}
// Helper function: componentToHex
// Converts a single color component (0-255) to its 2-digit hex representation.
function _componentToHex(c) {
const hex = Math.round(c).toString(16);
return hex.length == 1 ? "0" + hex : hex;
}
// Helper function: rgbToHex
// Converts an {r, g, b} object back to a hex color string.
function _rgbToHex(r, g, b) {
return "#" + _componentToHex(r) + _componentToHex(g) + _componentToHex(b);
}
// Helper function: varyColor
// Takes a base hex color and a variation factor (0-1).
// Returns a new hex color with R, G, B components randomly shifted.
function _varyColor(baseHexColor, variation) {
const baseColor = _parseHexColor(baseHexColor);
const v = Math.max(0, Math.min(1, Number(variation) || 0));
const r = Math.max(0, Math.min(255, baseColor.r + (Math.random() - 0.5) * 2 * 255 * v));
const g = Math.max(0, Math.min(255, baseColor.g + (Math.random() - 0.5) * 2 * 255 * v));
const b = Math.max(0, Math.min(255, baseColor.b + (Math.random() - 0.5) * 2 * 255 * v));
return _rgbToHex(r, g, b);
}
// Helper function: adjustBrightness
// Takes a hex color and a brightness factor.
// factor > 1 lightens, factor < 1 darkens.
// Returns a new hex color with adjusted brightness.
function _adjustBrightness(hexColor, factor) {
const color = _parseHexColor(hexColor);
const f = Number(factor) || 1.0;
const r = Math.max(0, Math.min(255, color.r * f));
const g = Math.max(0, Math.min(255, color.g * f));
const b = Math.max(0, Math.min(255, color.b * f));
return _rgbToHex(r, g, b);
}
const canvas = document.createElement('canvas');
canvas.width = originalImg.naturalWidth || originalImg.width;
canvas.height = originalImg.naturalHeight || originalImg.height;
if (canvas.width === 0 || canvas.height === 0) {
console.error("Image has zero width or height. Cannot process.");
// Return an empty (but valid) canvas to avoid breaking downstream code
const errorCtx = canvas.getContext('2d');
if (errorCtx) { // Check if context can be obtained
errorCtx.fillStyle = 'red';
errorCtx.fillRect(0,0,1,1); // Make it 1x1 if it was 0x0
if (canvas.width === 0) canvas.width = 1;
if (canvas.height === 0) canvas.height = 1;
}
return canvas;
}
const ctx = canvas.getContext('2d');
// Draw original image onto the canvas
ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
// Sanitize numerical parameters
const numAsteroids = Math.max(0, parseInt(String(asteroidCount), 10) || 0);
let minSize = Math.max(1, parseFloat(String(minAsteroidSize)) || 1);
let maxSize = Math.max(minSize, parseFloat(String(maxAsteroidSize)) || minSize);
const hFactor = Math.max(0, parseFloat(String(highlightFactor)) || 1.0);
const sFactor = Math.max(0, parseFloat(String(shadowFactor)) || 1.0);
const colorVar = Math.max(0, Math.min(1, parseFloat(String(asteroidColorVariation)) || 0));
for (let i = 0; i < numAsteroids; i++) {
const x = Math.random() * canvas.width;
const y = Math.random() * canvas.height;
const sizeRange = Math.max(0, maxSize - minSize);
const currentSize = minSize + Math.random() * sizeRange;
if (currentSize <= 0) continue;
const rotation = Math.random() * 2 * Math.PI;
// Determine the asteroid's main color and its shaded variants
const currentAsteroidMainColor = _varyColor(asteroidBaseColor, colorVar);
const lighterColor = _adjustBrightness(currentAsteroidMainColor, hFactor);
const darkerColor = _adjustBrightness(currentAsteroidMainColor, sFactor);
// Generate vertices for an irregular polygon representing the asteroid
const vertices = [];
const numVertices = Math.floor(5 + Math.random() * 7); // 5 to 11 vertices
let maxVertexRadius = 0;
for (let j = 0; j < numVertices; j++) {
const angle = (j / numVertices) * 2 * Math.PI;
// Radius for each_vertex varies for irregular shape: 0.6 * currentSize to 1.4 * currentSize
const radius = currentSize * (0.6 + Math.random() * 0.8);
if (radius > maxVertexRadius) maxVertexRadius = radius;
vertices.push({
x: radius * Math.cos(angle),
y: radius * Math.sin(angle)
});
}
if (vertices.length < 3) continue;
ctx.save();
ctx.translate(x, y); // Move to asteroid's position
ctx.rotate(rotation); // Apply random rotation
// Create a linear gradient for a 3D effect
// The gradient spans across the asteroid's maximum possible extent
const gradRadius = maxVertexRadius; // Ensure gradient covers the entire shape
const gradient = ctx.createLinearGradient(-gradRadius, -gradRadius, gradRadius, gradRadius);
gradient.addColorStop(0, lighterColor); // Highlight
gradient.addColorStop(0.5, currentAsteroidMainColor); // Mid-tone
gradient.addColorStop(1, darkerColor); // Shadow
ctx.fillStyle = gradient;
// Draw the asteroid polygon
ctx.beginPath();
ctx.moveTo(vertices[0].x, vertices[0].y);
for (let k = 1; k < numVertices; k++) {
ctx.lineTo(vertices[k].x, vertices[k].y);
}
ctx.closePath();
ctx.fill();
ctx.restore(); // Restore context state (transformations)
}
return canvas;
}
Apply Changes