feat: add shape masks

This commit is contained in:
2026-08-26 11:45:14 +05:00
parent 553601acc1
commit d65adcd16c
6 changed files with 290 additions and 4 deletions
+99 -2
View File
@@ -84,7 +84,14 @@ import {
rarityPredicate,
renderPredicateMask
} from './core/masks';
import { renderSpace, SPACES, type SpaceId } from './core/channels';
import { renderSpace, SPACES, type SpaceId } from './core/channels';
import {
boxTest,
circleTest,
renderShape,
starTest,
wavyTest
} from './core/shapes';
import { hexToPixels, pixelsToHex } from './core/text';
import { clonePixelImage, type PixelImage } from './core/types';
@@ -741,7 +748,97 @@ export const TOOLS: ToolEntry[] = [
],
run: (img, p) => tile(img, num(p, 'columns'), num(p, 'rows'))
},
{
{
id: 'circle-mask-png',
title: 'Circle Mask PNG',
description: 'Cuts the image into a circle. Diameter is set as a share of the smaller side.',
category: 'alpha',
params: [
{ id: 'size', label: 'Diameter, % of smaller side', type: 'slider', min: 20, max: 100, step: 1, default: 100 },
{ id: 'offsetX', label: 'Offset X, %', type: 'slider', min: -50, max: 50, step: 1, default: 0 },
{ id: 'offsetY', label: 'Offset Y, %', type: 'slider', min: -50, max: 50, step: 1, default: 0 }
],
run: (img, p) =>
renderShape(
img,
circleTest(num(p, 'size') / 200),
num(p, 'offsetX') / 100,
num(p, 'offsetY') / 100
)
},
{
id: 'square-mask-png',
title: 'Square Mask PNG',
description: 'Cuts the image into a rectangle with sides as a share of the smaller side.',
category: 'alpha',
params: [
{ id: 'widthPct', label: 'Width, % of smaller side', type: 'slider', min: 10, max: 100, step: 1, default: 100 },
{ id: 'heightPct', label: 'Height, % of smaller side', type: 'slider', min: 10, max: 100, step: 1, default: 100 },
{ id: 'offsetX', label: 'Offset X, %', type: 'slider', min: -50, max: 50, step: 1, default: 0 },
{ id: 'offsetY', label: 'Offset Y, %', type: 'slider', min: -50, max: 50, step: 1, default: 0 }
],
run: (img, p) =>
renderShape(
img,
boxTest(num(p, 'widthPct') / 200, num(p, 'heightPct') / 200),
num(p, 'offsetX') / 100,
num(p, 'offsetY') / 100
)
},
{
id: 'star-mask-png',
title: 'Star Mask PNG',
description: 'Cuts the image into an n-pointed star with adjustable inner radius and rotation.',
category: 'alpha',
params: [
{ id: 'points', label: 'Points', type: 'slider', min: 3, max: 12, step: 1, default: 5 },
{ id: 'innerRadius', label: 'Inner radius, %', type: 'slider', min: 10, max: 90, step: 1, default: 45 },
{ id: 'size', label: 'Outer radius, % of smaller side', type: 'slider', min: 20, max: 100, step: 1, default: 100 },
{ id: 'rotation', label: 'Rotation, °', type: 'slider', min: -180, max: 180, step: 1, default: 0 },
{ id: 'offsetX', label: 'Offset X, %', type: 'slider', min: -50, max: 50, step: 1, default: 0 },
{ id: 'offsetY', label: 'Offset Y, %', type: 'slider', min: -50, max: 50, step: 1, default: 0 }
],
run: (img, p) =>
renderShape(
img,
starTest(
num(p, 'points'),
num(p, 'innerRadius') / 100,
num(p, 'size') / 200,
num(p, 'rotation')
),
num(p, 'offsetX') / 100,
num(p, 'offsetY') / 100
)
},
{
id: 'wavy-mask-png',
title: 'Wavy Mask PNG',
description:
'Cuts the image into a wavy-edged circle: radius is modulated by a sine with chosen amplitude and frequency.',
category: 'alpha',
params: [
{ id: 'size', label: 'Base radius, % of smaller side', type: 'slider', min: 20, max: 100, step: 1, default: 90 },
{ id: 'amplitude', label: 'Wave amplitude, %', type: 'slider', min: 2, max: 30, step: 1, default: 8 },
{ id: 'waves', label: 'Waves count', type: 'slider', min: 3, max: 24, step: 1, default: 8 },
{ id: 'phase', label: 'Phase, °', type: 'slider', min: 0, max: 360, step: 1, default: 0 },
{ id: 'offsetX', label: 'Offset X, %', type: 'slider', min: -50, max: 50, step: 1, default: 0 },
{ id: 'offsetY', label: 'Offset Y, %', type: 'slider', min: -50, max: 50, step: 1, default: 0 }
],
run: (img, p) =>
renderShape(
img,
wavyTest(
num(p, 'size') / 200,
num(p, 'amplitude') / 200,
num(p, 'waves'),
num(p, 'phase')
),
num(p, 'offsetX') / 100,
num(p, 'offsetY') / 100
)
},
{
id: 'center-by-alpha-png',
title: 'Center PNG by content',
description: