From 8745812a5f077f715b003ca4022d20a72eb783ba Mon Sep 17 00:00:00 2001 From: Ku6epXBOCTuK Date: Mon, 24 Aug 2026 17:41:08 +0500 Subject: [PATCH] feat: add tool registry, icons --- web/src/lib/registry.ts | 130 +++++++++++++++++++++++++++++++- web/src/lib/tools/tool-icons.ts | 13 +++- 2 files changed, 141 insertions(+), 2 deletions(-) diff --git a/web/src/lib/registry.ts b/web/src/lib/registry.ts index e9d16d8..5556a84 100644 --- a/web/src/lib/registry.ts +++ b/web/src/lib/registry.ts @@ -28,23 +28,29 @@ import { } from './core/morphology'; import { gaussianBlur, sharpen as sharpenImage } from './core/convolution'; import { gradientImage, noiseImage, solidImage } from './core/generate'; +import { rotateFreeImage, skewImage, transformImage, zoomImage } from './core/affine'; +import { vignette } from './core/effects'; import { + autoContrast, brightnessContrast, changeHue, extractChannel, + gammaCorrection, grayscale, invert, posterize, sepia, setOpacity, swapChannels, + temperature, + tint, thresholdBlackWhite, twoColors, type ChannelSwapPair, type RgbChannel } from './core/color'; import { centerByAlpha, crop, expandCanvas, flip, resize, rotate90, tile } from './core/geometry'; -import { decodeTextImage, toBase64, toDataUrl, type OutputMime } from './core/io'; +import { decodeSvgText, decodeTextImage, jpegRoundtrip, toBase64, toDataUrl, type OutputMime } from './core/io'; import { hexToPixels, pixelsToHex } from './core/text'; import { clonePixelImage, type PixelImage } from './core/types'; @@ -860,6 +866,128 @@ export const TOOLS: ToolEntry[] = [ ? 'Да — все пиксели являются оттенками серого.' : 'Нет — найдены цветные пиксели.' }, + { + id: 'skew-png', + title: 'Наклонить PNG', + description: 'Сдвигает содержимое по горизонтали и вертикали — эффект перспективы.', + category: 'geometry', + params: [ + { id: 'degX', label: 'Наклон по X, °', type: 'slider', min: -80, max: 80, step: 1, default: 0 }, + { id: 'degY', label: 'Наклон по Y, °', type: 'slider', min: -80, max: 80, step: 1, default: 0 } + ], + run: (img, p) => skewImage(img, num(p, 'degX'), num(p, 'degY')) + }, + { + id: 'rotate-free-png', + title: 'Повернуть на произвольный угол', + description: + 'Поворот на любой угол. Холст расширяется под новые габариты, углы остаются прозрачными.', + category: 'geometry', + params: [ + { id: 'angle', label: 'Угол, °', type: 'slider', min: -180, max: 180, step: 1, default: 15 } + ], + run: (img, p) => rotateFreeImage(img, num(p, 'angle')) + }, + { + id: 'zoom-png', + title: 'Приблизить PNG', + description: + 'Увеличивает содержимое к центру. Холст прежнего размера — края обрезаются.', + category: 'geometry', + params: [ + { id: 'scale', label: 'Масштаб, %', type: 'slider', min: 100, max: 500, step: 10, default: 200 } + ], + run: (img, p) => zoomImage(img, num(p, 'scale')) + }, + { + id: 'shift-png', + title: 'Сдвинуть PNG', + description: 'Перемещает содержимое на заданное смещение по X и Y.', + category: 'geometry', + params: [ + { id: 'offsetX', label: 'Смещение X, px', type: 'number', min: -5000, max: 5000, step: 1, default: 0 }, + { id: 'offsetY', label: 'Смещение Y, px', type: 'number', min: -5000, max: 5000, step: 1, default: 0 }, + { id: 'color', label: 'Цвет фона', type: 'color', default: '#ffffff' } + ], + run: (img, p) => + transformImage( + img, + [1, 0, 0, 1, -Math.trunc(num(p, 'offsetX')), -Math.trunc(num(p, 'offsetY'))], + img.width, + img.height + ) + }, + { + id: 'vignette-png', + title: 'Виньетка PNG', + description: 'Плавно затемняет края изображения, центр не затрагивает.', + category: 'filters', + params: [ + { id: 'strength', label: 'Сила затемнения, %', type: 'slider', min: 0, max: 100, step: 5, default: 50 } + ], + run: (img, p) => vignette(img, num(p, 'strength')) + }, + { + id: 'jpeg-artifacts-png', + title: 'Артефакты JPEG', + description: + 'Имитирует пережатие в JPEG с низким качеством — видимые квадраты и размытие цветов.', + category: 'filters', + params: [ + { id: 'quality', label: 'Качество JPEG', type: 'slider', min: 1, max: 50, step: 1, default: 10 } + ], + run: (img, p) => jpegRoundtrip(img, num(p, 'quality')) + }, + { + id: 'gamma-png', + title: 'Гамма-коррекция PNG', + description: 'Корректирует яркость средних тонов. <1 темнее, >1 светлее, 1 — без изменений.', + category: 'color', + params: [ + { id: 'value', label: 'Гамма', type: 'slider', min: 0.1, max: 3, step: 0.05, default: 1 } + ], + run: (img, p) => gammaCorrection(img, num(p, 'value')) + }, + { + id: 'auto-contrast-png', + title: 'Автоконтраст PNG', + description: 'Растягивает диапазон каждого канала на весь доступный диапазон яркости.', + category: 'color', + params: [], + run: (img) => autoContrast(img) + }, + { + id: 'temperature-png', + title: 'Температура PNG', + description: 'Отрицательные значения делают изображение теплее (оранжевее), положительные — холоднее (синеватее).', + category: 'color', + params: [ + { id: 'percent', label: 'Температура', type: 'slider', min: -100, max: 100, step: 1, default: 0 } + ], + run: (img, p) => temperature(img, num(p, 'percent')) + }, + { + id: 'tint-png', + title: 'Тонировать PNG', + description: 'Умножает цветовые каналы на выбранный оттенок с заданной силой.', + category: 'color', + params: [ + { id: 'color', label: 'Цвет тонирования', type: 'color', default: '#ffb060' }, + { id: 'strength', label: 'Сила, %', type: 'slider', min: 0, max: 100, step: 1, default: 30 } + ], + run: (img, p) => tint(img, str(p, 'color'), num(p, 'strength')) + }, + { + id: 'svg-to-png', + title: 'SVG в PNG', + description: + 'Dекодирует SVG-разметку в растровое изображение. Вставьте SVG-код слева.', + category: 'convert', + sourceMode: 'text', + params: [{ id: 'width', label: 'Ширина результата, px', type: 'number', min: 1, max: 10000, step: 1, default: 512 }], + runFromText: (text, p) => decodeSvgText(text, Math.trunc(num(p, 'width'))), + run: (img) => clonePixelImage(img) + }, { id: 'png-is-transparent', title: 'Проверить: PNG прозрачный?', diff --git a/web/src/lib/tools/tool-icons.ts b/web/src/lib/tools/tool-icons.ts index ca79c0e..54694e0 100644 --- a/web/src/lib/tools/tool-icons.ts +++ b/web/src/lib/tools/tool-icons.ts @@ -99,5 +99,16 @@ export const TOOL_ICONS: Record = { 'make-thinner-png': ZoomOut, 'harden-alpha-png': Sun, 'despeckle-alpha-png': SearchCheck, - 'close-holes-png': Hammer + 'close-holes-png': Hammer, + 'skew-png': Repeat2, + 'rotate-free-png': RotateCw, + 'zoom-png': ZoomIn, + 'shift-png': Crosshair, + 'vignette-png': Sun, + 'jpeg-artifacts-png': Layers, + 'gamma-png': Contrast, + 'auto-contrast-png': Contrast, + 'temperature-png': Sun, + 'tint-png': Droplet, + 'svg-to-png': FileImage };