You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, brightnessThreshold = "50", assetScaleMultiplier = "1.0") {
const thresh = parseInt(brightnessThreshold, 10);
const scaleFactor = parseFloat(assetScaleMultiplier) || 1.0;
const canvas = document.createElement('canvas');
const width = originalImg.width;
const height = originalImg.height;
// Ensure actual size exists to avoid infinite loops or empty outputs
if (width === 0 || height === 0) return canvas;
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
// Extract original image pixel data to identify assets
ctx.drawImage(originalImg, 0, 0);
const imgData = ctx.getImageData(0, 0, width, height);
const data = imgData.data;
// Reset canvas to draw the new modded environment
ctx.clearRect(0, 0, width, height);
// 1. Draw Forest Setting Background
// Sky Gradient
const skyGradient = ctx.createLinearGradient(0, 0, 0, height);
skyGradient.addColorStop(0, '#4fa4f5'); // Deep sky blue
skyGradient.addColorStop(1, '#bce3ff'); // Light horizon
ctx.fillStyle = skyGradient;
ctx.fillRect(0, 0, width, height);
// Sun / Cloud accent
ctx.fillStyle = 'rgba(255, 255, 255, 0.9)';
ctx.beginPath();
ctx.arc(width * 0.75, height * 0.25, Math.min(width, height) * 0.1, 0, Math.PI * 2);
ctx.fill();
// Distant background mountains
ctx.fillStyle = '#418239';
ctx.beginPath();
ctx.ellipse(width * 0.3, height + 20, width * 0.6, height * 0.35, 0, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = '#55a64b';
ctx.beginPath();
ctx.ellipse(width * 0.7, height + 10, width * 0.55, height * 0.25, 0, 0, Math.PI * 2);
ctx.fill();
// Foreground Forest Trees
const drawPineTree = (tx, ty, scale) => {
// Trunk
ctx.fillStyle = '#5c3d22';
ctx.fillRect(tx - 3 * scale, ty - 8 * scale, 6 * scale, 16 * scale);
// Leaves (overlapping triangles)
ctx.fillStyle = '#2b6623';
for (let i = 0; i < 3; i++) {
const h = ty - (15 + i * 12) * scale;
ctx.beginPath();
ctx.moveTo(tx, h - 25 * scale);
ctx.lineTo(tx + (20 - i*3) * scale, h + 10 * scale);
ctx.lineTo(tx - (20 - i*3) * scale, h + 10 * scale);
ctx.fill();
}
};
// Populate bottom ridge with trees
const treeSpacing = Math.max(30, width / 20);
for (let i = 0; i < width + treeSpacing; i += treeSpacing + (i % 20)) {
const yLine = height - (Math.abs(Math.sin(i)) * 20 + 20);
drawPineTree(i, yLine, (1.5 + (i % 3) * 0.3) * scaleFactor);
}
// 2. Identify Original Game Assets using Connected Component Labeling (CCL)
const visited = new Uint8Array(width * height);
const queue = new Int32Array(width * height * 2); // Max capacity queue to avoid GC thrashing
const blobs = [];
const addNeighbor = (nx, ny) => {
if (nx >= 0 && nx < width && ny >= 0 && ny < height) {
const index = ny * width + nx;
if (!visited[index]) {
const i4 = index * 4;
if (data[i4 + 3] >= 50) { // Ensure unvisited pixel is not highly transparent
const brightness = (data[i4] + data[i4 + 1] + data[i4 + 2]) / 3;
if (brightness > thresh) {
visited[index] = 1;
queue[qTail++] = nx;
queue[qTail++] = ny;
}
}
}
}
};
let qHead = 0;
let qTail = 0;
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
const idx = y * width + x;
if (visited[idx]) continue;
const idx4 = idx * 4;
// Check if pixel belongs to an entity (foreground)
let isEntity = false;
if (data[idx4 + 3] >= 50) {
const brightness = (data[idx4] + data[idx4 + 1] + data[idx4 + 2]) / 3;
if (brightness > thresh) {
isEntity = true;
}
}
if (isEntity) {
visited[idx] = 1;
qHead = 0;
qTail = 0;
queue[qTail++] = x;
queue[qTail++] = y;
const blob = { minX: x, maxX: x, minY: y, maxY: y, count: 0 };
// BFS loop for contiguous entity shape extraction
while (qHead < qTail) {
const px = queue[qHead++];
const py = queue[qHead++];
blob.count++;
if (px < blob.minX) blob.minX = px;
if (px > blob.maxX) blob.maxX = px;
if (py < blob.minY) blob.minY = py;
if (py > blob.maxY) blob.maxY = py;
// 8-way directional connectivity checks
addNeighbor(px + 1, py);
addNeighbor(px - 1, py);
addNeighbor(px, py + 1);
addNeighbor(px, py - 1);
addNeighbor(px + 1, py + 1);
addNeighbor(px - 1, py - 1);
addNeighbor(px + 1, py - 1);
addNeighbor(px - 1, py + 1);
}
blobs.push(blob);
} else {
visited[idx] = 1;
}
}
}
// 3. Draw Thematically Modified Redrawn Assets
blobs.forEach(blob => {
const w = blob.maxX - blob.minX;
const h = blob.maxY - blob.minY;
const cx = blob.minX + w / 2;
const cy = blob.minY + h / 2;
const size = Math.max(w, h);
if (blob.count < 6 || size < 4) {
// Stars in space are converted into distant V-birds
ctx.strokeStyle = 'rgba(70, 70, 70, 0.6)';
ctx.lineWidth = 1.5;
ctx.beginPath();
ctx.moveTo(cx - 3, cy - 2);
ctx.lineTo(cx, cy);
ctx.lineTo(cx + 3, cy - 2);
ctx.stroke();
} else {
// Treat larger blobs as Ships / Enemies
ctx.save();
ctx.translate(cx, cy);
// Generate a consistent pseudo-random behavior/styling depending on location
const entityHash = (Math.floor(cx) * 17 + Math.floor(cy) * 31) % 100;
if (size > width * 0.05 || size > 30) {
// Large Entity -> Player ship style reimagined as a Forest Defender Jet
ctx.scale((size / 30) * scaleFactor, (size / 30) * scaleFactor);
// Hull
ctx.fillStyle = '#f0f5ff';
ctx.beginPath();
ctx.moveTo(0, -15);
ctx.lineTo(12, 10);
ctx.lineTo(-12, 10);
ctx.closePath();
ctx.fill();
// Cockpit window
ctx.fillStyle = '#00ccff';
ctx.beginPath();
ctx.ellipse(0, -2, 3.5, 6, 0, 0, Math.PI * 2);
ctx.fill();
// Detailed Wings
ctx.fillStyle = '#4268a8';
ctx.beginPath();
ctx.moveTo(8, 5);
ctx.lineTo(22, 12);
ctx.lineTo(10, 10);
ctx.fill();
ctx.beginPath();
ctx.moveTo(-8, 5);
ctx.lineTo(-22, 12);
ctx.lineTo(-10, 10);
ctx.fill();
// Exhaust Flame
ctx.fillStyle = '#ff8800';
ctx.beginPath();
ctx.arc(0, 12, 5, 0, Math.PI, false);
ctx.fill();
ctx.fillStyle = '#ffff00';
ctx.beginPath();
ctx.arc(0, 11, 2.5, 0, Math.PI, false);
ctx.fill();
} else {
// Small Entity -> Aliens/Enemies reimagined
ctx.scale((size / 15) * scaleFactor, (size / 15) * scaleFactor);
if (entityHash > 50) {
// Reimagined saucer - Floating Metallic Disk
ctx.fillStyle = '#78828f';
ctx.beginPath();
ctx.ellipse(0, 0, 14, 5, 0, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = '#ff4d4d'; // Red hostile glow
ctx.beginPath();
ctx.arc(0, -3, 6, 0, Math.PI, true);
ctx.fill();
// Disk spots
ctx.fillStyle = '#c0cad6';
for(let k of [-8, 0, 8]) {
ctx.beginPath();
ctx.arc(k, 1, 1.5, 0, Math.PI * 2);
ctx.fill();
}
} else {
// Reimagined tiny fighters
ctx.fillStyle = '#c75620';
ctx.beginPath();
ctx.moveTo(0, 8);
ctx.lineTo(10, -5);
ctx.lineTo(-10, -5);
ctx.closePath();
ctx.fill();
ctx.fillStyle = '#ffb99c';
ctx.beginPath();
ctx.moveTo(0, 3);
ctx.lineTo(5, -2);
ctx.lineTo(-5, -2);
ctx.fill();
}
}
ctx.restore();
}
});
return canvas;
}
Apply Changes