You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, projectName = "Studio Database Model", language = "ru", theme = "dark") {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// Set standard presentation dimensions
canvas.width = 900;
canvas.height = 600;
const isDark = theme.toLowerCase() !== 'light';
const isRu = language.toLowerCase() === 'ru';
// Theme color palette
const colors = {
bg: isDark ? '#1e1e2e' : '#eff1f5',
panel: isDark ? '#313244' : '#ffffff',
text: isDark ? '#cdd6f4' : '#4c4f69',
accent: isDark ? '#89b4fa' : '#1e66f5',
line: isDark ? '#6c7086' : '#9ca0b0',
grid: isDark ? 'rgba(255, 255, 255, 0.04)' : 'rgba(0, 0, 0, 0.05)',
headerText: isDark ? '#11111b' : '#ffffff'
};
// Dictionary for localization
const labels = {
projects: isRu ? "Проекты (Projects)" : "Projects",
media: isRu ? "Медиа Ассеты (Media)" : "Media Assets",
users: isRu ? "Сотрудники (Users)" : "Users",
queue: isRu ? "Очередь Рендера (Queue)" : "Render Queue",
name: isRu ? "название" : "name",
status: isRu ? "статус" : "status",
pid: isRu ? "ид_проекта" : "project_id",
file: isRu ? "путь_файла" : "file_path",
size: isRu ? "размер" : "size",
username: isRu ? "логин" : "username",
role: isRu ? "права" : "role",
progress: isRu ? "прогресс" : "progress"
};
// Fill background
ctx.fillStyle = colors.bg;
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Draw Grid Pattern
ctx.strokeStyle = colors.grid;
ctx.lineWidth = 1;
for(let i = 0; i <= canvas.width; i += 30) {
ctx.beginPath(); ctx.moveTo(i, 0); ctx.lineTo(i, canvas.height); ctx.stroke();
}
for(let i = 0; i <= canvas.height; i += 30) {
ctx.beginPath(); ctx.moveTo(0, i); ctx.lineTo(canvas.width, i); ctx.stroke();
}
// Top Header Bar
ctx.fillStyle = colors.panel;
ctx.shadowColor = 'rgba(0, 0, 0, 0.2)';
ctx.shadowBlur = 10;
ctx.fillRect(0, 0, canvas.width, 70);
ctx.shadowBlur = 0; // Reset shadow
// Draw Original Image as Studio Logo
const logoSize = 46;
const logoX = 20, logoY = 12;
ctx.save();
ctx.beginPath();
ctx.arc(logoX + logoSize / 2, logoY + logoSize / 2, logoSize / 2, 0, Math.PI * 2);
ctx.closePath();
ctx.clip();
ctx.drawImage(originalImg, logoX, logoY, logoSize, logoSize);
ctx.restore();
// Draw Title
ctx.fillStyle = colors.text;
ctx.font = 'bold 22px "Segoe UI", Arial, sans-serif';
ctx.fillText(`${projectName} - ${isRu ? 'Проект Базы Данных' : 'Database Project'}`, logoX + logoSize + 20, logoY + 30);
// Define mock database tables
const tables = [
{
id: 't1', x: 60, y: 180, w: 220,
title: labels.projects,
fields: [
{n: "id", t: "PK INT"},
{n: labels.name, t: "VARCHAR"},
{n: labels.status, t: "ENUM"}
]
},
{
id: 't2', x: 380, y: 350, w: 220,
title: labels.media,
fields: [
{n: "id", t: "PK INT"},
{n: labels.pid, t: "FK INT"},
{n: labels.file, t: "VARCHAR"},
{n: labels.size, t: "FLOAT"}
]
},
{
id: 't3', x: 380, y: 120, w: 220,
title: labels.users,
fields: [
{n: "id", t: "PK INT"},
{n: labels.username, t: "VARCHAR"},
{n: labels.role, t: "VARCHAR"}
]
},
{
id: 't4', x: 660, y: 230, w: 200,
title: labels.queue,
fields: [
{n: "id", t: "PK INT"},
{n: labels.pid, t: "FK INT"},
{n: labels.progress, t: "INT"}
]
},
];
// Helper to calculate height of each table
tables.forEach(t => {
t.h = 45 + t.fields.length * 30 + 10;
});
// Draw connecting lines (schema relations)
function drawConnection(x1, y1, x2, y2) {
ctx.strokeStyle = colors.line;
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.bezierCurveTo(x1 + 60, y1, x2 - 60, y2, x2, y2);
ctx.stroke();
// Draw relation dots
ctx.fillStyle = colors.accent;
ctx.beginPath(); ctx.arc(x1, y1, 4, 0, Math.PI * 2); ctx.fill();
ctx.beginPath(); ctx.arc(x2, y2, 4, 0, Math.PI * 2); ctx.fill();
}
// projects(id) -> media(pid)
drawConnection(tables[0].x + tables[0].w, tables[0].y + 70, tables[1].x, tables[1].y + 100);
// projects(id) -> queue(pid)
drawConnection(tables[0].x + tables[0].w, tables[0].y + 70, tables[3].x, tables[3].y + 100);
// users -> queue (hypothetical creator relation)
drawConnection(tables[2].x + tables[2].w, tables[2].y + 70, tables[3].x, tables[3].y + 70);
// Polyfill for roundRect if absolutely necessary for extremely old browsers
const drawRoundRect = (x, y, w, h, r, isTopOnly) => {
ctx.beginPath();
ctx.moveTo(x + r, y);
ctx.lineTo(x + w - r, y);
ctx.quadraticCurveTo(x + w, y, x + w, y + r);
ctx.lineTo(x + w, y + h - (isTopOnly ? 0 : r));
if(!isTopOnly) ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
ctx.lineTo(x + (isTopOnly ? 0 : r), y + h);
if(!isTopOnly) ctx.quadraticCurveTo(x, y + h, x, y + h - r);
ctx.lineTo(x, y + r);
ctx.quadraticCurveTo(x, y, x + r, y);
ctx.closePath();
ctx.fill();
};
// Draw Tables
tables.forEach(t => {
// Drop Shadow
ctx.shadowColor = 'rgba(0, 0, 0, 0.15)';
ctx.shadowBlur = 12;
ctx.shadowOffsetY = 4;
ctx.fillStyle = colors.panel;
// Background card
drawRoundRect(t.x, t.y, t.w, t.h, 8, false);
ctx.shadowBlur = 0;
ctx.shadowOffsetY = 0;
// Header Background
ctx.fillStyle = colors.accent;
drawRoundRect(t.x, t.y, t.w, 35, 8, true);
// Header Text
ctx.fillStyle = colors.headerText;
ctx.font = 'bold 15px "Segoe UI", Arial, sans-serif';
ctx.fillText(t.title, t.x + 12, t.y + 23);
// Table Rows (Fields)
ctx.font = '14px "Courier New", monospace';
t.fields.forEach((field, i) => {
const rowY = t.y + 65 + (i * 30);
// Field Name
ctx.fillStyle = colors.text;
ctx.fillText(field.n, t.x + 15, rowY);
// Field Type
ctx.fillStyle = field.t.includes('PK') ? '#f9e2af' : (field.t.includes('FK') ? '#fab387' : colors.line);
const typeW = ctx.measureText(field.t).width;
ctx.fillText(field.t, t.x + t.w - typeW - 15, rowY);
// Separator line
if (i < t.fields.length - 1) {
ctx.strokeStyle = isDark ? 'rgba(255,255,255,0.05)' : 'rgba(0,0,0,0.05)';
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(t.x + 15, rowY + 10);
ctx.lineTo(t.x + t.w - 15, rowY + 10);
ctx.stroke();
}
});
});
return canvas;
}
Apply Changes