You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(
originalImg,
textQuery = "Введите запрос",
mode = "typography",
fontSize = 16,
fontColor = "#FFFFFF",
backgroundColor = "#000000"
) {
const canvas = document.createElement("canvas");
const width = originalImg.width;
const height = originalImg.height;
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext("2d");
// Ensure a reasonable minimum font size to prevent extreme performance issues
const parsedFontSize = Math.max(4, parseInt(fontSize, 10)) || 16;
if (mode === "typography") {
const tempCanvas = document.createElement("canvas");
tempCanvas.width = width;
tempCanvas.height = height;
const tempCtx = tempCanvas.getContext("2d");
// Draw the image to read its pixels
tempCtx.drawImage(originalImg, 0, 0, width, height);
let imageData;
try {
imageData = tempCtx.getImageData(0, 0, width, height).data;
} catch (e) {
// Fallback to text over image if cross-origin security rules block pixel access
return drawOverlay(ctx, originalImg, textQuery, parsedFontSize, fontColor, width, height);
}
// Fill background
ctx.fillStyle = backgroundColor;
ctx.fillRect(0, 0, width, height);
ctx.font = `bold ${parsedFontSize}px monospace`;
ctx.textBaseline = "top";
const textLen = textQuery.length;
if (textLen === 0) {
ctx.drawImage(originalImg, 0, 0);
return canvas;
}
// Monospace measurement gives predictable uniform grid steps
const charWidth = ctx.measureText("M").width;
let charIndex = 0;
// Render the image typographically
for (let y = 0; y < height; y += parsedFontSize) {
for (let x = 0; x < width; x += charWidth) {
const px = Math.floor(x + charWidth / 2);
const py = Math.floor(y + parsedFontSize / 2);
if (px >= width || py >= height) continue;
const index = (py * width + px) * 4;
const r = imageData[index];
const g = imageData[index + 1];
const b = imageData[index + 2];
const a = imageData[index + 3];
// If pixel has visibility, draw the next character matching pixel color
if (a > 10) {
const char = textQuery[charIndex % textLen];
ctx.fillStyle = `rgba(${r}, ${g}, ${b}, ${a / 255})`;
ctx.fillText(char, x, y);
charIndex++;
}
}
// Add slight shifting effect on every line
charIndex++;
}
} else {
// Overlay standard approach
drawOverlay(ctx, originalImg, textQuery, parsedFontSize, fontColor, width, height);
}
return canvas;
// Helper to draw conventional overlaid and automatically wrapped text on the image
function drawOverlay(ctx, img, text, size, color, w, h) {
ctx.drawImage(img, 0, 0, w, h);
if (!text || text.length === 0) return canvas;
ctx.fillStyle = color;
ctx.font = `bold ${size}px sans-serif`;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
// Add a high-contrast shadow/border for immediate readability
ctx.shadowColor = "rgba(0, 0, 0, 0.8)";
ctx.shadowBlur = 4;
ctx.shadowOffsetX = 2;
ctx.shadowOffsetY = 2;
const maxWidth = w * 0.9;
const words = text.split(' ');
let line = '';
const lines = [];
// Word wrapper calculation
for (let n = 0; n < words.length; n++) {
const testLine = line + words[n] + ' ';
const metrics = ctx.measureText(testLine);
if (metrics.width > maxWidth && n > 0) {
lines.push(line);
line = words[n] + ' ';
} else {
line = testLine;
}
}
lines.push(line);
// Center on canvas
const lineHeight = size * 1.2;
const totalHeight = lines.length * lineHeight;
let startY = (h - totalHeight) / 2 + lineHeight / 2;
for (let i = 0; i < lines.length; i++) {
ctx.fillText(lines[i].trim(), w / 2, startY + i * lineHeight);
}
return canvas;
}
}
Apply Changes