@pixerate/editor 0.259.0 → 0.261.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/index.cjs +2 -2
- package/dist/agent/index.js +1 -1
- package/dist/animation-editor/index.cjs +61 -59
- package/dist/animation-editor/index.js +5 -3
- package/dist/brushes/index.cjs +52 -52
- package/dist/brushes/index.js +6 -6
- package/dist/{brushstrokeRender-JI4W6F2G.js → brushstrokeRender-25Q5BEY6.js} +17 -7
- package/dist/{brushstrokeRender-MQ6PSDM5.cjs → brushstrokeRender-N6UPZSQS.cjs} +31 -21
- package/dist/canvas/brushTypes.d.cts +5 -0
- package/dist/canvas/brushTypes.d.ts +5 -0
- package/dist/canvas/drawController.d.cts +15 -1
- package/dist/canvas/drawController.d.ts +15 -1
- package/dist/canvas/index.cjs +136 -102
- package/dist/canvas/index.js +35 -1
- package/dist/{chunk-3PPJRMIG.js → chunk-3O47TPGX.js} +197 -18
- package/dist/{chunk-5Q5MLXAD.cjs → chunk-3TQW5MPW.cjs} +23 -23
- package/dist/{chunk-JBBJE76O.cjs → chunk-3ZRNK3RO.cjs} +1138 -85
- package/dist/{chunk-YSGW7VV4.cjs → chunk-43NT242D.cjs} +1401 -228
- package/dist/{chunk-SMTUQWGY.js → chunk-5IW65VQ6.js} +1 -1
- package/dist/{chunk-YQ4PQTBB.js → chunk-6NZL24W4.js} +9 -1
- package/dist/{chunk-O3ZVZSNJ.cjs → chunk-7AUQ2UJ3.cjs} +137 -129
- package/dist/{chunk-4M4E3N6X.js → chunk-CQVTGBQI.js} +1 -1
- package/dist/{chunk-SSFMWJDK.js → chunk-D4TE5JNW.js} +1192 -19
- package/dist/{chunk-AORZ5M5C.js → chunk-EHALWBBD.js} +1 -1
- package/dist/{chunk-6WCQ35GI.cjs → chunk-NHA26O5X.cjs} +2 -2
- package/dist/{chunk-DPYAO5U6.cjs → chunk-PVJMTLPJ.cjs} +197 -18
- package/dist/{chunk-MQGQTKVW.cjs → chunk-PYTJBVW5.cjs} +29 -29
- package/dist/{chunk-IXQZHCOF.js → chunk-QAR2N7HZ.js} +1 -1
- package/dist/{chunk-5P4JEOIV.js → chunk-TMDDMJCR.js} +2 -2
- package/dist/{chunk-RSIKDWOP.cjs → chunk-TZOX7KWV.cjs} +15 -15
- package/dist/{chunk-IPUTIUGQ.cjs → chunk-UVZ7RQOE.cjs} +1 -1
- package/dist/{chunk-A7GJ6BXI.js → chunk-WJ4WQJCC.js} +1062 -9
- package/dist/{chunk-ICTPLVSR.js → chunk-X2EXYY4J.js} +1 -1
- package/dist/{chunk-WQCNC4DK.js → chunk-XC6DPRYI.js} +695 -4
- package/dist/{chunk-KBYB7OWB.cjs → chunk-YHDHFJUC.cjs} +776 -85
- package/dist/{chunk-P3PZ63M3.cjs → chunk-YLUIFXSL.cjs} +35 -35
- package/dist/commands/index.cjs +2 -2
- package/dist/commands/index.js +1 -1
- package/dist/image-editor/index.cjs +1385 -2484
- package/dist/image-editor/index.js +139 -1238
- package/dist/image-editor/selection-worker.cjs +3 -4
- package/dist/image-editor/selection-worker.js +1 -2
- package/dist/paint/index.cjs +3 -5
- package/dist/paint/index.js +40 -42
- package/dist/paint-editor/contract.d.cts +29 -0
- package/dist/paint-editor/contract.d.ts +29 -0
- package/dist/paint-editor/controller.d.cts +17 -0
- package/dist/paint-editor/controller.d.ts +17 -0
- package/dist/paint-editor/dab-stroke.d.cts +5 -0
- package/dist/paint-editor/dab-stroke.d.ts +5 -0
- package/dist/paint-editor/index.cjs +610 -327
- package/dist/paint-editor/index.d.cts +2 -0
- package/dist/paint-editor/index.d.ts +2 -0
- package/dist/paint-editor/index.js +337 -54
- package/dist/paint-editor/path-samples.d.cts +19 -0
- package/dist/paint-editor/path-samples.d.ts +19 -0
- package/dist/paint-editor/stamp-tip.d.cts +17 -0
- package/dist/paint-editor/stamp-tip.d.ts +17 -0
- package/dist/paint-editor/ui.d.cts +27 -1
- package/dist/paint-editor/ui.d.ts +27 -1
- package/dist/raster/index.cjs +2 -4
- package/dist/raster/index.js +6 -8
- package/dist/vector/brushstroke.d.cts +6 -0
- package/dist/vector/brushstroke.d.ts +6 -0
- package/dist/vector/controller.d.cts +11 -2
- package/dist/vector/controller.d.ts +11 -2
- package/dist/vector/index.cjs +990 -973
- package/dist/vector/index.js +28 -11
- package/package.json +1 -1
- package/skills/pixerate-brushes/SKILL.md +1 -1
- package/skills/pixerate-canvas/SKILL.md +3 -1
- package/skills/pixerate-paint-editor/SKILL.md +2 -0
- package/skills/pixerate-paint-sketch-to-finish/SKILL.md +5 -1
- package/skills/pixerate-vector/SKILL.md +2 -2
- package/dist/chunk-2VJAVXLI.js +0 -192
- package/dist/chunk-62Z5WLRA.js +0 -700
- package/dist/chunk-BOHDJJ2O.cjs +0 -192
- package/dist/chunk-NKGB64CW.cjs +0 -700
- package/dist/chunk-SS53NNZO.cjs +0 -1066
- package/dist/chunk-ZNA3IQZF.js +0 -1066
|
@@ -8,46 +8,7 @@ import {
|
|
|
8
8
|
parseOpenTypeFont,
|
|
9
9
|
removeAnchorsKeepingShape,
|
|
10
10
|
traceField
|
|
11
|
-
} from "../chunk-
|
|
12
|
-
import {
|
|
13
|
-
ColorGmm,
|
|
14
|
-
GAMMA,
|
|
15
|
-
GridGraph,
|
|
16
|
-
REFINE_EDIT_KINDS,
|
|
17
|
-
SelectionWorkerClient,
|
|
18
|
-
UNITS,
|
|
19
|
-
WORK_SIZE,
|
|
20
|
-
applyRefineEdits,
|
|
21
|
-
boxBlur,
|
|
22
|
-
brushStrokeMask,
|
|
23
|
-
deserializeMask,
|
|
24
|
-
ellipsePoints,
|
|
25
|
-
ellipseSelection,
|
|
26
|
-
findSky,
|
|
27
|
-
findSubject,
|
|
28
|
-
focusArea,
|
|
29
|
-
grabCutLabels,
|
|
30
|
-
keepMainPieces,
|
|
31
|
-
normalizeRect,
|
|
32
|
-
otsuThreshold,
|
|
33
|
-
polygonCoverage,
|
|
34
|
-
polygonCoverageBox,
|
|
35
|
-
polygonSelection,
|
|
36
|
-
quickSelectStroke,
|
|
37
|
-
rectSelection,
|
|
38
|
-
runSelectionJob,
|
|
39
|
-
selectObjectIn,
|
|
40
|
-
serializeMask,
|
|
41
|
-
slicSuperpixels,
|
|
42
|
-
smoothnessWeights,
|
|
43
|
-
softMaskToSelection,
|
|
44
|
-
startSelectionWorker,
|
|
45
|
-
superpixelSaliency,
|
|
46
|
-
upscaleRegion,
|
|
47
|
-
workImage,
|
|
48
|
-
workLuma,
|
|
49
|
-
workPlanes
|
|
50
|
-
} from "../chunk-A7GJ6BXI.js";
|
|
11
|
+
} from "../chunk-CQVTGBQI.js";
|
|
51
12
|
import {
|
|
52
13
|
ADJUSTMENT_KEYS,
|
|
53
14
|
ADJUSTMENT_KINDS,
|
|
@@ -58,14 +19,19 @@ import {
|
|
|
58
19
|
BACKGROUND_LAYER_NAME,
|
|
59
20
|
BLUR_FILTER_ID,
|
|
60
21
|
BROWSER_IMAGE_TYPES,
|
|
22
|
+
BRUSH_OPTION_CONTROLS,
|
|
23
|
+
CLONE_TRANSFORM_CONTROLS,
|
|
24
|
+
COLOR_RANGE_PRESET_LABELS,
|
|
61
25
|
D50,
|
|
62
26
|
D65,
|
|
63
27
|
DEFAULT_ADJUSTMENTS_VALUES,
|
|
64
28
|
DEFAULT_DEPTH_MASK_VALUES,
|
|
65
29
|
DEFAULT_MITER_LIMIT,
|
|
30
|
+
DEFAULT_SELECTION_TOOL_OPTIONS,
|
|
66
31
|
DEFAULT_TRANSFORM_VALUES,
|
|
67
32
|
DEVELOP_FILTER_ID,
|
|
68
33
|
EDGE_COLOR_RADIUS,
|
|
34
|
+
EdgeMap,
|
|
69
35
|
FREE_TRANSFORM_MODES,
|
|
70
36
|
HUE_RANGE_LABELS,
|
|
71
37
|
IDENTITY_MATRIX3,
|
|
@@ -81,14 +47,26 @@ import {
|
|
|
81
47
|
MAX_KELVIN,
|
|
82
48
|
MIN_KELVIN,
|
|
83
49
|
MIRED_PER_TEMPERATURE_UNIT,
|
|
50
|
+
MIXER_MIXING_OPTIONS,
|
|
51
|
+
MIXER_OPTION_CONTROLS,
|
|
52
|
+
PAINT_TOOLS,
|
|
84
53
|
PARAGRAPH_STYLE_FIELDS,
|
|
85
54
|
PATTERN_BLOCK_KEYS,
|
|
86
55
|
PSD_BLEND_KEYS,
|
|
56
|
+
PaintGesture,
|
|
87
57
|
PsdError,
|
|
58
|
+
REFINE_EDGE_CONTROLS,
|
|
59
|
+
REFINE_OUTPUTS,
|
|
60
|
+
RETOUCH_CHOICES,
|
|
61
|
+
RETOUCH_OPTION_CONTROLS,
|
|
62
|
+
RETOUCH_TOGGLES,
|
|
63
|
+
SELECTION_MODE_LABELS,
|
|
64
|
+
SELECTION_TOOLS,
|
|
88
65
|
SHAPE_OPERATIONS,
|
|
89
66
|
SHAPE_RENDER_MARGIN,
|
|
90
67
|
SMART_RENDER_MARGIN,
|
|
91
68
|
SUPPORTED_ADJUSTMENT_KEYS,
|
|
69
|
+
SelectionGesture,
|
|
92
70
|
TEXT_ALIGNS,
|
|
93
71
|
TEXT_ORIENTATIONS,
|
|
94
72
|
TEXT_RENDER_MARGIN,
|
|
@@ -142,6 +120,8 @@ import {
|
|
|
142
120
|
calculateCropDrag,
|
|
143
121
|
canvasToImagePoint,
|
|
144
122
|
clampCropToImage,
|
|
123
|
+
cloneSourceLabel,
|
|
124
|
+
cloneTransformView,
|
|
145
125
|
colorBalanceChannel,
|
|
146
126
|
compileAdjustmentLayer,
|
|
147
127
|
componentPath,
|
|
@@ -170,6 +150,7 @@ import {
|
|
|
170
150
|
depthMaskAlpha,
|
|
171
151
|
depthRasterMask,
|
|
172
152
|
describeLayerEffects,
|
|
153
|
+
describeSelection,
|
|
173
154
|
describeTransform,
|
|
174
155
|
deserializeEffects,
|
|
175
156
|
deserializeLayer,
|
|
@@ -198,6 +179,10 @@ import {
|
|
|
198
179
|
getViewportMetrics,
|
|
199
180
|
getViewportProjection,
|
|
200
181
|
groupLayers,
|
|
182
|
+
handleMaskViewKey,
|
|
183
|
+
handlePaintShortcut,
|
|
184
|
+
handleQuickMaskKey,
|
|
185
|
+
handleSelectionShortcut,
|
|
201
186
|
hasEdgeUnmix,
|
|
202
187
|
hasResampledLegacyMask,
|
|
203
188
|
hasSoftBand,
|
|
@@ -225,9 +210,11 @@ import {
|
|
|
225
210
|
isDefaultDepthMask,
|
|
226
211
|
isIdentityTransform,
|
|
227
212
|
isLegacyShape,
|
|
213
|
+
isPaintTool,
|
|
228
214
|
isPaintableLayer,
|
|
229
215
|
isQuarterTurnSwapped,
|
|
230
216
|
isRenderedLayer,
|
|
217
|
+
isRetouchPaintTool,
|
|
231
218
|
isSmartSurfaceIncomplete,
|
|
232
219
|
isSpatialFilter,
|
|
233
220
|
isTransformAnchor,
|
|
@@ -251,6 +238,8 @@ import {
|
|
|
251
238
|
linearToSrgb as linearToSrgb2,
|
|
252
239
|
liveComponent,
|
|
253
240
|
liveShapeSubpaths,
|
|
241
|
+
liveWire,
|
|
242
|
+
magneticAnchorSpacing,
|
|
254
243
|
mapCropHandle,
|
|
255
244
|
mapSubpaths,
|
|
256
245
|
maskCoverageOverCanvas,
|
|
@@ -336,20 +325,24 @@ import {
|
|
|
336
325
|
resolveVectorMasks,
|
|
337
326
|
rgbToGray,
|
|
338
327
|
rgbToHsl,
|
|
328
|
+
rgbUnitToHex,
|
|
339
329
|
runLayerFilters,
|
|
340
330
|
runLayerFiltersFloat,
|
|
341
331
|
sampleLut,
|
|
342
332
|
sampleToneLut,
|
|
333
|
+
selectionModeFromModifiers,
|
|
343
334
|
serializeContents,
|
|
344
335
|
serializeEffects,
|
|
345
336
|
serializeLayer,
|
|
346
337
|
serializeLayers,
|
|
347
338
|
serializeLayersExternal,
|
|
348
339
|
serializePatterns,
|
|
340
|
+
setCloneTransformValue,
|
|
349
341
|
setLayerEffects,
|
|
350
342
|
setLayerEffectsEnabled,
|
|
351
343
|
setLayerMask,
|
|
352
344
|
setLayerProperties,
|
|
345
|
+
setRetouchChoice,
|
|
353
346
|
setSmartFilterMask,
|
|
354
347
|
setSmartFiltersEnabled,
|
|
355
348
|
setSmartPlacement,
|
|
@@ -359,8 +352,12 @@ import {
|
|
|
359
352
|
shapeLayers,
|
|
360
353
|
shapeOutline,
|
|
361
354
|
shapeRenderClip,
|
|
355
|
+
simplifyPixelPath,
|
|
362
356
|
smartContentsFile,
|
|
363
357
|
smartObjectLayers,
|
|
358
|
+
snapPolygonToEdges,
|
|
359
|
+
snapTo45,
|
|
360
|
+
snapToEdge,
|
|
364
361
|
srgbToLinear,
|
|
365
362
|
tempTintToXy,
|
|
366
363
|
temperatureToKelvin,
|
|
@@ -413,15 +410,25 @@ import {
|
|
|
413
410
|
writePsd,
|
|
414
411
|
xyToTempTint,
|
|
415
412
|
xyToUv
|
|
416
|
-
} from "../chunk-
|
|
413
|
+
} from "../chunk-D4TE5JNW.js";
|
|
417
414
|
import {
|
|
415
|
+
ColorGmm,
|
|
418
416
|
DEFAULT_REFINE_EDGE_PARAMS,
|
|
419
417
|
EMPTY_DISTANCE,
|
|
418
|
+
GAMMA,
|
|
420
419
|
GUIDED_FILTER_EPS,
|
|
420
|
+
GridGraph,
|
|
421
|
+
REFINE_EDIT_KINDS,
|
|
421
422
|
SELECTION_MODES,
|
|
422
423
|
SELECTION_MODIFY_OPS,
|
|
424
|
+
SelectionWorkerClient,
|
|
425
|
+
UNITS,
|
|
426
|
+
WORK_SIZE,
|
|
427
|
+
applyRefineEdits,
|
|
423
428
|
borderCoverage,
|
|
429
|
+
boxBlur,
|
|
424
430
|
boxMean,
|
|
431
|
+
brushStrokeMask,
|
|
425
432
|
combineSelections,
|
|
426
433
|
computeSelectionBounds,
|
|
427
434
|
contractCoverage,
|
|
@@ -429,13 +436,20 @@ import {
|
|
|
429
436
|
createSelectionMask,
|
|
430
437
|
decodeSelectionRle,
|
|
431
438
|
decontaminateColors,
|
|
439
|
+
deserializeMask,
|
|
432
440
|
deserializeSelection,
|
|
433
441
|
distanceTransform,
|
|
434
442
|
edgeWidth,
|
|
443
|
+
ellipsePoints,
|
|
444
|
+
ellipseSelection,
|
|
435
445
|
encodeSelectionRle,
|
|
436
446
|
expandCoverage,
|
|
437
447
|
featherCoverage,
|
|
448
|
+
findSky,
|
|
449
|
+
findSubject,
|
|
450
|
+
focusArea,
|
|
438
451
|
fullSelection,
|
|
452
|
+
grabCutLabels,
|
|
439
453
|
greyMorphology,
|
|
440
454
|
guidePlanes,
|
|
441
455
|
guidedFilterColor,
|
|
@@ -443,13 +457,23 @@ import {
|
|
|
443
457
|
invertSelection,
|
|
444
458
|
isSelectionEmpty,
|
|
445
459
|
isSelectionMode,
|
|
460
|
+
keepMainPieces,
|
|
446
461
|
modifySelection,
|
|
462
|
+
normalizeRect,
|
|
447
463
|
normalizeRefineEdgeParams,
|
|
464
|
+
otsuThreshold,
|
|
448
465
|
parseSelectionMode,
|
|
466
|
+
polygonCoverage,
|
|
467
|
+
polygonCoverageBox,
|
|
468
|
+
polygonSelection,
|
|
469
|
+
quickSelectStroke,
|
|
470
|
+
rectSelection,
|
|
449
471
|
refineCoverage,
|
|
450
472
|
refineEdgeHalo,
|
|
451
473
|
refineSelection,
|
|
452
474
|
resizeSelection,
|
|
475
|
+
runSelectionJob,
|
|
476
|
+
selectObjectIn,
|
|
453
477
|
selectionArea,
|
|
454
478
|
selectionFromCoverage,
|
|
455
479
|
selectionFromPixels,
|
|
@@ -458,30 +482,28 @@ import {
|
|
|
458
482
|
selectionToCoverage,
|
|
459
483
|
selectionToPixels,
|
|
460
484
|
selectionValueAt,
|
|
485
|
+
serializeMask,
|
|
461
486
|
serializeSelection,
|
|
462
487
|
shiftCoverage,
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
488
|
+
slicSuperpixels,
|
|
489
|
+
smoothCoverage,
|
|
490
|
+
smoothnessWeights,
|
|
491
|
+
softMaskToSelection,
|
|
492
|
+
startSelectionWorker,
|
|
493
|
+
superpixelSaliency,
|
|
494
|
+
upscaleRegion,
|
|
495
|
+
workImage,
|
|
496
|
+
workLuma,
|
|
497
|
+
workPlanes
|
|
498
|
+
} from "../chunk-WJ4WQJCC.js";
|
|
471
499
|
import {
|
|
472
500
|
createCaptureTool
|
|
473
501
|
} from "../chunk-5ED73EC6.js";
|
|
474
|
-
import {
|
|
475
|
-
LAYER_EFFECT_MENU,
|
|
476
|
-
effectControlChange,
|
|
477
|
-
layerEffectControls,
|
|
478
|
-
layerEffectsBadge,
|
|
479
|
-
layerEffectsPanel
|
|
480
|
-
} from "../chunk-2VJAVXLI.js";
|
|
481
502
|
import {
|
|
482
503
|
DEFAULT_GLOBAL_LIGHT,
|
|
483
504
|
INTERPOLATIONS,
|
|
484
505
|
LAYER_EFFECT_KINDS,
|
|
506
|
+
LAYER_EFFECT_MENU,
|
|
485
507
|
LayerEffectError,
|
|
486
508
|
MAX_EFFECT_REACH,
|
|
487
509
|
MAX_TRANSFORM_PIXELS,
|
|
@@ -497,6 +519,7 @@ import {
|
|
|
497
519
|
defaultEffectGradient,
|
|
498
520
|
defaultLayerEffect,
|
|
499
521
|
effectColorHex,
|
|
522
|
+
effectControlChange,
|
|
500
523
|
effectReach,
|
|
501
524
|
effectsFingerprint,
|
|
502
525
|
effectsMargin,
|
|
@@ -510,8 +533,11 @@ import {
|
|
|
510
533
|
isLayerEffectKind,
|
|
511
534
|
isLayerLocked,
|
|
512
535
|
keysWeight,
|
|
536
|
+
layerEffectControls,
|
|
513
537
|
layerEffectFields,
|
|
514
538
|
layerEffectLabel,
|
|
539
|
+
layerEffectsBadge,
|
|
540
|
+
layerEffectsPanel,
|
|
515
541
|
mapLayer,
|
|
516
542
|
mergeLayerEffect,
|
|
517
543
|
newEffectId,
|
|
@@ -527,7 +553,7 @@ import {
|
|
|
527
553
|
validateLayerEffects,
|
|
528
554
|
warpSurface,
|
|
529
555
|
workingToSamples
|
|
530
|
-
} from "../chunk-
|
|
556
|
+
} from "../chunk-3O47TPGX.js";
|
|
531
557
|
import {
|
|
532
558
|
applyHomography,
|
|
533
559
|
boxQuad,
|
|
@@ -541,50 +567,10 @@ import {
|
|
|
541
567
|
DIRECT_GAUSSIAN_MAX_SIGMA,
|
|
542
568
|
gaussianBlur
|
|
543
569
|
} from "../chunk-ZIF4UJJ6.js";
|
|
544
|
-
import {
|
|
545
|
-
DEFAULT_RETOUCH,
|
|
546
|
-
IDENTITY_CLONE_TRANSFORM,
|
|
547
|
-
RETOUCH_SAMPLES,
|
|
548
|
-
RegionPixelSource,
|
|
549
|
-
RetouchDabStroke,
|
|
550
|
-
SPOT_HEAL_TYPES,
|
|
551
|
-
TONE_RANGES,
|
|
552
|
-
TilePixelSource,
|
|
553
|
-
TransformedPixelSource,
|
|
554
|
-
applyRetouchToTile,
|
|
555
|
-
applyToneToDeepTile,
|
|
556
|
-
boxBlurPixels,
|
|
557
|
-
cloneSamplePoint,
|
|
558
|
-
dodgeBurn,
|
|
559
|
-
dodgeBurnCurve,
|
|
560
|
-
healPixels,
|
|
561
|
-
isIdentityCloneTransform,
|
|
562
|
-
luma,
|
|
563
|
-
normalizeRetouch,
|
|
564
|
-
perDabStrength,
|
|
565
|
-
readSourceRegion,
|
|
566
|
-
retouchFilter,
|
|
567
|
-
rgbaViewOfGrey,
|
|
568
|
-
saturationOf,
|
|
569
|
-
smudgeEffect,
|
|
570
|
-
sponge,
|
|
571
|
-
spongeEffect,
|
|
572
|
-
spotHealPixels,
|
|
573
|
-
spotHealWindow,
|
|
574
|
-
toneOf
|
|
575
|
-
} from "../chunk-62Z5WLRA.js";
|
|
576
|
-
import {
|
|
577
|
-
bestOffset,
|
|
578
|
-
contentAwareFill,
|
|
579
|
-
dilateMask,
|
|
580
|
-
maskBounds,
|
|
581
|
-
membraneFill,
|
|
582
|
-
seamlessClone,
|
|
583
|
-
solveMembrane
|
|
584
|
-
} from "../chunk-VWEWY4FY.js";
|
|
585
570
|
import {
|
|
586
571
|
BODY_SCATTER,
|
|
587
572
|
DEFAULT_FLOW,
|
|
573
|
+
DEFAULT_RETOUCH,
|
|
588
574
|
DEFAULT_WET_PAPER,
|
|
589
575
|
DEFAULT_WET_PARAMS,
|
|
590
576
|
DRY_EPS,
|
|
@@ -599,6 +585,7 @@ import {
|
|
|
599
585
|
FLAT,
|
|
600
586
|
FREE_INTERVAL,
|
|
601
587
|
HIDE,
|
|
588
|
+
IDENTITY_CLONE_TRANSFORM,
|
|
602
589
|
MASS_CHANNELS,
|
|
603
590
|
MAX_BATCH,
|
|
604
591
|
MAX_TILT_ACCEL,
|
|
@@ -624,12 +611,19 @@ import {
|
|
|
624
611
|
P_SW,
|
|
625
612
|
P_UX,
|
|
626
613
|
P_UY,
|
|
614
|
+
RETOUCH_SAMPLES,
|
|
615
|
+
RegionPixelSource,
|
|
616
|
+
RetouchDabStroke,
|
|
627
617
|
SET_PLANES,
|
|
628
618
|
SLOT_BYTES,
|
|
629
619
|
SLOT_PLANES,
|
|
630
620
|
SLOT_SIDE,
|
|
621
|
+
SPOT_HEAL_TYPES,
|
|
631
622
|
SRGB_TO_LINEAR,
|
|
632
623
|
STRIP,
|
|
624
|
+
TONE_RANGES,
|
|
625
|
+
TilePixelSource,
|
|
626
|
+
TransformedPixelSource,
|
|
633
627
|
U_MAX,
|
|
634
628
|
W9,
|
|
635
629
|
WET_FLOW_KINDS,
|
|
@@ -643,11 +637,17 @@ import {
|
|
|
643
637
|
WetWorkerPool,
|
|
644
638
|
WetWorld,
|
|
645
639
|
WetWorldError,
|
|
640
|
+
applyRetouchToTile,
|
|
641
|
+
applyToneToDeepTile,
|
|
642
|
+
boxBlurPixels,
|
|
646
643
|
canShareMemory,
|
|
644
|
+
cloneSamplePoint,
|
|
647
645
|
createPigmentTools,
|
|
648
646
|
createWetGpuEngine,
|
|
649
647
|
dexp,
|
|
650
648
|
dlog,
|
|
649
|
+
dodgeBurn,
|
|
650
|
+
dodgeBurnCurve,
|
|
651
651
|
dpow,
|
|
652
652
|
dsincos,
|
|
653
653
|
edgeBits,
|
|
@@ -656,28 +656,52 @@ import {
|
|
|
656
656
|
filmReflectance,
|
|
657
657
|
findWebGpu,
|
|
658
658
|
flowField,
|
|
659
|
+
healPixels,
|
|
660
|
+
isIdentityCloneTransform,
|
|
659
661
|
latticeViscosity,
|
|
660
662
|
linearToSrgb8,
|
|
661
663
|
loadWetKernel,
|
|
664
|
+
luma,
|
|
662
665
|
moveWetSnapshot,
|
|
663
666
|
normalizeFlow,
|
|
667
|
+
normalizeRetouch,
|
|
664
668
|
normalizeTilt,
|
|
665
669
|
normalizeWetParams,
|
|
666
670
|
packWetParams,
|
|
671
|
+
perDabStrength,
|
|
667
672
|
pigmentColor,
|
|
668
673
|
pigmentOf,
|
|
669
674
|
pixelPigment,
|
|
670
675
|
pointerPressure,
|
|
671
676
|
pointerStrokeSample,
|
|
672
677
|
pointerStrokeSamples,
|
|
678
|
+
readSourceRegion,
|
|
679
|
+
retouchFilter,
|
|
680
|
+
rgbaViewOfGrey,
|
|
681
|
+
saturationOf,
|
|
673
682
|
shadeLightTile,
|
|
674
683
|
shadeTile,
|
|
684
|
+
smudgeEffect,
|
|
685
|
+
sponge,
|
|
686
|
+
spongeEffect,
|
|
687
|
+
spotHealPixels,
|
|
688
|
+
spotHealWindow,
|
|
675
689
|
srgbByteToLinear,
|
|
676
690
|
stepSlotTs,
|
|
691
|
+
toneOf,
|
|
677
692
|
wetGpuSupport,
|
|
678
693
|
wetSimdSupported,
|
|
679
694
|
wetSupport
|
|
680
|
-
} from "../chunk-
|
|
695
|
+
} from "../chunk-XC6DPRYI.js";
|
|
696
|
+
import {
|
|
697
|
+
bestOffset,
|
|
698
|
+
contentAwareFill,
|
|
699
|
+
dilateMask,
|
|
700
|
+
maskBounds,
|
|
701
|
+
membraneFill,
|
|
702
|
+
seamlessClone,
|
|
703
|
+
solveMembrane
|
|
704
|
+
} from "../chunk-VWEWY4FY.js";
|
|
681
705
|
import {
|
|
682
706
|
BUILTIN_PROFILES,
|
|
683
707
|
CmsError,
|
|
@@ -746,14 +770,14 @@ import {
|
|
|
746
770
|
warpMeshParamAt,
|
|
747
771
|
warpMeshRows,
|
|
748
772
|
warpPresetMesh
|
|
749
|
-
} from "../chunk-
|
|
773
|
+
} from "../chunk-6NZL24W4.js";
|
|
750
774
|
import {
|
|
751
775
|
IMAGE_PROFILE,
|
|
752
776
|
checkBrush,
|
|
753
777
|
closest,
|
|
754
778
|
createBrushLibrary,
|
|
755
779
|
resolveBrush
|
|
756
|
-
} from "../chunk-
|
|
780
|
+
} from "../chunk-5IW65VQ6.js";
|
|
757
781
|
import "../chunk-APX63ILU.js";
|
|
758
782
|
import "../chunk-M6DNZNVY.js";
|
|
759
783
|
import {
|
|
@@ -762,6 +786,12 @@ import {
|
|
|
762
786
|
segmentNearest
|
|
763
787
|
} from "../chunk-WEIK3UYB.js";
|
|
764
788
|
import "../chunk-PWBZH3FK.js";
|
|
789
|
+
import {
|
|
790
|
+
brushColourHex
|
|
791
|
+
} from "../chunk-FJBQTZS4.js";
|
|
792
|
+
import {
|
|
793
|
+
parseCssColor
|
|
794
|
+
} from "../chunk-3CI7FRMK.js";
|
|
765
795
|
import "../chunk-SZTI6EER.js";
|
|
766
796
|
import {
|
|
767
797
|
EffectRendererPool
|
|
@@ -3935,312 +3965,6 @@ function pathToSelection(components, width, height, options = {}) {
|
|
|
3935
3965
|
return feather > 0 && mask.bounds ? modifySelection(mask, "feather", feather) : mask;
|
|
3936
3966
|
}
|
|
3937
3967
|
|
|
3938
|
-
// src/image-editor/selection/magnetic.ts
|
|
3939
|
-
var BLOCK = 64;
|
|
3940
|
-
var W_ZERO = 0.3;
|
|
3941
|
-
var W_GRADIENT = 0.4;
|
|
3942
|
-
var W_DIRECTION = 0.3;
|
|
3943
|
-
var EdgeMap = class {
|
|
3944
|
-
width;
|
|
3945
|
-
height;
|
|
3946
|
-
contrast;
|
|
3947
|
-
luma;
|
|
3948
|
-
blocks = /* @__PURE__ */ new Map();
|
|
3949
|
-
constructor(pixels, options = {}) {
|
|
3950
|
-
const { width, height, data } = pixels;
|
|
3951
|
-
if (!(width >= 1 && height >= 1) || data.length < width * height * 4) throw new Error("Invalid pixel buffer for edge detection.");
|
|
3952
|
-
this.width = width;
|
|
3953
|
-
this.height = height;
|
|
3954
|
-
this.contrast = Math.min(1, Math.max(0, options.contrast ?? 0.1));
|
|
3955
|
-
const luma2 = new Uint8Array(width * height);
|
|
3956
|
-
for (let i = 0, p = 0; i < luma2.length; i++, p += 4) {
|
|
3957
|
-
const a = data[p + 3] / 255;
|
|
3958
|
-
const y = 0.299 * data[p] + 0.587 * data[p + 1] + 0.114 * data[p + 2];
|
|
3959
|
-
luma2[i] = Math.round(y * a + 255 * (1 - a));
|
|
3960
|
-
}
|
|
3961
|
-
this.luma = luma2;
|
|
3962
|
-
}
|
|
3963
|
-
/** Edge strength at (x, y) in whole pixels, 0..1 (0 outside the image or below the contrast). */
|
|
3964
|
-
strength(x, y) {
|
|
3965
|
-
if (x < 0 || y < 0 || x >= this.width || y >= this.height) return 0;
|
|
3966
|
-
const b = this.block(x, y);
|
|
3967
|
-
return b.strength[y % BLOCK * BLOCK + x % BLOCK];
|
|
3968
|
-
}
|
|
3969
|
-
block(x, y) {
|
|
3970
|
-
const bx = Math.floor(x / BLOCK);
|
|
3971
|
-
const by = Math.floor(y / BLOCK);
|
|
3972
|
-
const key2 = by * 65536 + bx;
|
|
3973
|
-
let b = this.blocks.get(key2);
|
|
3974
|
-
if (!b) {
|
|
3975
|
-
b = this.computeBlock(bx * BLOCK, by * BLOCK);
|
|
3976
|
-
this.blocks.set(key2, b);
|
|
3977
|
-
}
|
|
3978
|
-
return b;
|
|
3979
|
-
}
|
|
3980
|
-
computeBlock(x0, y0) {
|
|
3981
|
-
const { width: W, height: H, luma: luma2 } = this;
|
|
3982
|
-
const M = 2;
|
|
3983
|
-
const S = BLOCK + 2 * M;
|
|
3984
|
-
const smooth = new Float32Array(S * S);
|
|
3985
|
-
const at = (x, y) => luma2[Math.min(H - 1, Math.max(0, y)) * W + Math.min(W - 1, Math.max(0, x))];
|
|
3986
|
-
for (let j = 0; j < S; j++) {
|
|
3987
|
-
const y = y0 - M + j;
|
|
3988
|
-
for (let i = 0; i < S; i++) {
|
|
3989
|
-
const x = x0 - M + i;
|
|
3990
|
-
const v = at(x - 1, y - 1) + 2 * at(x, y - 1) + at(x + 1, y - 1) + 2 * at(x - 1, y) + 4 * at(x, y) + 2 * at(x + 1, y) + at(x - 1, y + 1) + 2 * at(x, y + 1) + at(x + 1, y + 1);
|
|
3991
|
-
smooth[j * S + i] = v / (16 * 255);
|
|
3992
|
-
}
|
|
3993
|
-
}
|
|
3994
|
-
const lap = new Float32Array(S * S);
|
|
3995
|
-
for (let j = 1; j < S - 1; j++) {
|
|
3996
|
-
for (let i = 1; i < S - 1; i++) {
|
|
3997
|
-
const k = j * S + i;
|
|
3998
|
-
lap[k] = smooth[k - 1] + smooth[k + 1] + smooth[k - S] + smooth[k + S] - 4 * smooth[k];
|
|
3999
|
-
}
|
|
4000
|
-
}
|
|
4001
|
-
const n = BLOCK * BLOCK;
|
|
4002
|
-
const strength = new Float32Array(n);
|
|
4003
|
-
const dirX = new Float32Array(n);
|
|
4004
|
-
const dirY = new Float32Array(n);
|
|
4005
|
-
const zero = new Uint8Array(n);
|
|
4006
|
-
const c = this.contrast;
|
|
4007
|
-
for (let j = 0; j < BLOCK; j++) {
|
|
4008
|
-
if (y0 + j >= H) break;
|
|
4009
|
-
for (let i = 0; i < BLOCK; i++) {
|
|
4010
|
-
if (x0 + i >= W) break;
|
|
4011
|
-
const k = (j + M) * S + (i + M);
|
|
4012
|
-
const gx = (smooth[k + 1] - smooth[k - 1]) / 2;
|
|
4013
|
-
const gy = (smooth[k + S] - smooth[k - S]) / 2;
|
|
4014
|
-
const g = Math.hypot(gx, gy);
|
|
4015
|
-
const e = Math.min(1, 2 * g);
|
|
4016
|
-
if (e < c || g === 0) continue;
|
|
4017
|
-
const o = j * BLOCK + i;
|
|
4018
|
-
strength[o] = e;
|
|
4019
|
-
dirX[o] = gy / g;
|
|
4020
|
-
dirY[o] = -gx / g;
|
|
4021
|
-
const l = lap[k];
|
|
4022
|
-
for (const d of [1, -1, S, -S]) {
|
|
4023
|
-
const m = lap[k + d];
|
|
4024
|
-
if (l > 0 && m < 0 || l < 0 && m > 0 || l === 0) {
|
|
4025
|
-
if (Math.abs(l) <= Math.abs(m)) {
|
|
4026
|
-
zero[o] = 1;
|
|
4027
|
-
break;
|
|
4028
|
-
}
|
|
4029
|
-
}
|
|
4030
|
-
}
|
|
4031
|
-
}
|
|
4032
|
-
}
|
|
4033
|
-
return { strength, dirX, dirY, zero };
|
|
4034
|
-
}
|
|
4035
|
-
/** Features of the pixel at (x, y), which must be inside the image. */
|
|
4036
|
-
features(x, y) {
|
|
4037
|
-
const b = this.block(x, y);
|
|
4038
|
-
const o = y % BLOCK * BLOCK + x % BLOCK;
|
|
4039
|
-
return { strength: b.strength[o], dirX: b.dirX[o], dirY: b.dirY[o], zero: b.zero[o] };
|
|
4040
|
-
}
|
|
4041
|
-
};
|
|
4042
|
-
var clampPixel = (v, max) => Math.min(max - 1, Math.max(0, Math.floor(v)));
|
|
4043
|
-
function snapToEdge(map, point2, width = 10) {
|
|
4044
|
-
const cx = clampPixel(point2.x, map.width);
|
|
4045
|
-
const cy = clampPixel(point2.y, map.height);
|
|
4046
|
-
const r = Math.max(0, Math.round(width));
|
|
4047
|
-
let best = 0;
|
|
4048
|
-
let bestD = Infinity;
|
|
4049
|
-
let bx = cx;
|
|
4050
|
-
let by = cy;
|
|
4051
|
-
for (let y = Math.max(0, cy - r); y <= Math.min(map.height - 1, cy + r); y++) {
|
|
4052
|
-
for (let x = Math.max(0, cx - r); x <= Math.min(map.width - 1, cx + r); x++) {
|
|
4053
|
-
const d = (x - cx) ** 2 + (y - cy) ** 2;
|
|
4054
|
-
if (d > r * r) continue;
|
|
4055
|
-
const s = map.strength(x, y);
|
|
4056
|
-
if (s > best + 0.02 || s > 0 && Math.abs(s - best) <= 0.02 && d < bestD) {
|
|
4057
|
-
if (s > best) best = s;
|
|
4058
|
-
bestD = d;
|
|
4059
|
-
bx = x;
|
|
4060
|
-
by = y;
|
|
4061
|
-
}
|
|
4062
|
-
}
|
|
4063
|
-
}
|
|
4064
|
-
return best > 0 ? { x: bx + 0.5, y: by + 0.5 } : point2;
|
|
4065
|
-
}
|
|
4066
|
-
var Heap = class {
|
|
4067
|
-
ids = new Int32Array(256);
|
|
4068
|
-
keys = new Float64Array(256);
|
|
4069
|
-
size = 0;
|
|
4070
|
-
push(id, key2) {
|
|
4071
|
-
if (this.size === this.ids.length) {
|
|
4072
|
-
const ids = new Int32Array(this.size * 2);
|
|
4073
|
-
ids.set(this.ids);
|
|
4074
|
-
const keys = new Float64Array(this.size * 2);
|
|
4075
|
-
keys.set(this.keys);
|
|
4076
|
-
this.ids = ids;
|
|
4077
|
-
this.keys = keys;
|
|
4078
|
-
}
|
|
4079
|
-
let i = this.size++;
|
|
4080
|
-
while (i > 0) {
|
|
4081
|
-
const p = i - 1 >> 1;
|
|
4082
|
-
if (this.keys[p] <= key2) break;
|
|
4083
|
-
this.ids[i] = this.ids[p];
|
|
4084
|
-
this.keys[i] = this.keys[p];
|
|
4085
|
-
i = p;
|
|
4086
|
-
}
|
|
4087
|
-
this.ids[i] = id;
|
|
4088
|
-
this.keys[i] = key2;
|
|
4089
|
-
}
|
|
4090
|
-
/** Removes the cheapest entry; read it from `topId` / `topKey` first. */
|
|
4091
|
-
pop() {
|
|
4092
|
-
const n = --this.size;
|
|
4093
|
-
const id = this.ids[n];
|
|
4094
|
-
const key2 = this.keys[n];
|
|
4095
|
-
let i = 0;
|
|
4096
|
-
for (; ; ) {
|
|
4097
|
-
let c = 2 * i + 1;
|
|
4098
|
-
if (c >= n) break;
|
|
4099
|
-
if (c + 1 < n && this.keys[c + 1] < this.keys[c]) c++;
|
|
4100
|
-
if (this.keys[c] >= key2) break;
|
|
4101
|
-
this.ids[i] = this.ids[c];
|
|
4102
|
-
this.keys[i] = this.keys[c];
|
|
4103
|
-
i = c;
|
|
4104
|
-
}
|
|
4105
|
-
this.ids[i] = id;
|
|
4106
|
-
this.keys[i] = key2;
|
|
4107
|
-
}
|
|
4108
|
-
get topId() {
|
|
4109
|
-
return this.ids[0];
|
|
4110
|
-
}
|
|
4111
|
-
get topKey() {
|
|
4112
|
-
return this.keys[0];
|
|
4113
|
-
}
|
|
4114
|
-
};
|
|
4115
|
-
var NEIGHBOURS = [
|
|
4116
|
-
[1, 0],
|
|
4117
|
-
[-1, 0],
|
|
4118
|
-
[0, 1],
|
|
4119
|
-
[0, -1],
|
|
4120
|
-
[1, 1],
|
|
4121
|
-
[1, -1],
|
|
4122
|
-
[-1, 1],
|
|
4123
|
-
[-1, -1]
|
|
4124
|
-
];
|
|
4125
|
-
function liveWire(map, from, to, band) {
|
|
4126
|
-
const ax = clampPixel(from.x, map.width);
|
|
4127
|
-
const ay = clampPixel(from.y, map.height);
|
|
4128
|
-
const bx = clampPixel(to.x, map.width);
|
|
4129
|
-
const by = clampPixel(to.y, map.height);
|
|
4130
|
-
if (ax === bx && ay === by) return [{ x: ax + 0.5, y: ay + 0.5 }];
|
|
4131
|
-
const len = Math.hypot(bx - ax, by - ay);
|
|
4132
|
-
const half = Math.max(2, band ?? Math.max(16, len / 4));
|
|
4133
|
-
const rx0 = Math.max(0, Math.floor(Math.min(ax, bx) - half));
|
|
4134
|
-
const ry0 = Math.max(0, Math.floor(Math.min(ay, by) - half));
|
|
4135
|
-
const rx1 = Math.min(map.width - 1, Math.ceil(Math.max(ax, bx) + half));
|
|
4136
|
-
const ry1 = Math.min(map.height - 1, Math.ceil(Math.max(ay, by) + half));
|
|
4137
|
-
const w = rx1 - rx0 + 1;
|
|
4138
|
-
const h = ry1 - ry0 + 1;
|
|
4139
|
-
const n = w * h;
|
|
4140
|
-
const strength = new Float32Array(n);
|
|
4141
|
-
const dirX = new Float32Array(n);
|
|
4142
|
-
const dirY = new Float32Array(n);
|
|
4143
|
-
const zero = new Uint8Array(n);
|
|
4144
|
-
const inside = new Uint8Array(n);
|
|
4145
|
-
let maxStrength = 0;
|
|
4146
|
-
const ux = (bx - ax) / len;
|
|
4147
|
-
const uy = (by - ay) / len;
|
|
4148
|
-
for (let y = 0; y < h; y++) {
|
|
4149
|
-
for (let x = 0; x < w; x++) {
|
|
4150
|
-
const px = rx0 + x - ax;
|
|
4151
|
-
const py = ry0 + y - ay;
|
|
4152
|
-
const t = Math.min(len, Math.max(0, px * ux + py * uy));
|
|
4153
|
-
if (Math.hypot(px - t * ux, py - t * uy) > half) continue;
|
|
4154
|
-
const i = y * w + x;
|
|
4155
|
-
inside[i] = 1;
|
|
4156
|
-
const f = map.features(rx0 + x, ry0 + y);
|
|
4157
|
-
strength[i] = f.strength;
|
|
4158
|
-
dirX[i] = f.dirX;
|
|
4159
|
-
dirY[i] = f.dirY;
|
|
4160
|
-
zero[i] = f.zero;
|
|
4161
|
-
if (f.strength > maxStrength) maxStrength = f.strength;
|
|
4162
|
-
}
|
|
4163
|
-
}
|
|
4164
|
-
const scale = maxStrength > 0 ? 1 / maxStrength : 0;
|
|
4165
|
-
const dist2 = new Float64Array(n).fill(Infinity);
|
|
4166
|
-
const prev = new Int32Array(n).fill(-1);
|
|
4167
|
-
const done = new Uint8Array(n);
|
|
4168
|
-
const start = (ay - ry0) * w + (ax - rx0);
|
|
4169
|
-
const goal = (by - ry0) * w + (bx - rx0);
|
|
4170
|
-
dist2[start] = 0;
|
|
4171
|
-
const heap = new Heap();
|
|
4172
|
-
heap.push(start, 0);
|
|
4173
|
-
while (heap.size) {
|
|
4174
|
-
const p = heap.topId;
|
|
4175
|
-
const d = heap.topKey;
|
|
4176
|
-
heap.pop();
|
|
4177
|
-
if (done[p] || d > dist2[p]) continue;
|
|
4178
|
-
done[p] = 1;
|
|
4179
|
-
if (p === goal) break;
|
|
4180
|
-
const x = p % w;
|
|
4181
|
-
const y = (p - x) / w;
|
|
4182
|
-
for (const [dx, dy] of NEIGHBOURS) {
|
|
4183
|
-
const qx = x + dx;
|
|
4184
|
-
const qy = y + dy;
|
|
4185
|
-
if (qx < 0 || qy < 0 || qx >= w || qy >= h) continue;
|
|
4186
|
-
const q = qy * w + qx;
|
|
4187
|
-
if (!inside[q] || done[q]) continue;
|
|
4188
|
-
const step = dx && dy ? Math.SQRT2 : 1;
|
|
4189
|
-
const lx = dx / step;
|
|
4190
|
-
const ly = dy / step;
|
|
4191
|
-
const dp = Math.min(1, Math.abs(dirX[p] * lx + dirY[p] * ly));
|
|
4192
|
-
const dq = Math.min(1, Math.abs(dirX[q] * lx + dirY[q] * ly));
|
|
4193
|
-
const fD = (Math.acos(dp) + Math.acos(dq)) / Math.PI;
|
|
4194
|
-
const fG = 1 - strength[q] * scale;
|
|
4195
|
-
const fZ = 1 - zero[q];
|
|
4196
|
-
const cost = (W_ZERO * fZ + W_GRADIENT * fG + W_DIRECTION * fD + 0.01) * step;
|
|
4197
|
-
const nd = d + cost;
|
|
4198
|
-
if (nd < dist2[q]) {
|
|
4199
|
-
dist2[q] = nd;
|
|
4200
|
-
prev[q] = p;
|
|
4201
|
-
heap.push(q, nd);
|
|
4202
|
-
}
|
|
4203
|
-
}
|
|
4204
|
-
}
|
|
4205
|
-
if (prev[goal] < 0) return [{ x: ax + 0.5, y: ay + 0.5 }, { x: bx + 0.5, y: by + 0.5 }];
|
|
4206
|
-
const path = [];
|
|
4207
|
-
for (let p = goal; p >= 0; p = prev[p]) {
|
|
4208
|
-
const x = p % w;
|
|
4209
|
-
path.push({ x: rx0 + x + 0.5, y: ry0 + (p - x) / w + 0.5 });
|
|
4210
|
-
}
|
|
4211
|
-
return path.reverse();
|
|
4212
|
-
}
|
|
4213
|
-
function simplifyPixelPath(points) {
|
|
4214
|
-
if (points.length < 3) return points.slice();
|
|
4215
|
-
const out = [points[0]];
|
|
4216
|
-
for (let i = 1; i < points.length - 1; i++) {
|
|
4217
|
-
const a = out[out.length - 1];
|
|
4218
|
-
const b = points[i];
|
|
4219
|
-
const c = points[i + 1];
|
|
4220
|
-
const cross = (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
|
|
4221
|
-
if (Math.abs(cross) > 1e-9) out.push(b);
|
|
4222
|
-
}
|
|
4223
|
-
out.push(points[points.length - 1]);
|
|
4224
|
-
return out;
|
|
4225
|
-
}
|
|
4226
|
-
function snapPolygonToEdges(pixels, points, options = {}) {
|
|
4227
|
-
const map = pixels instanceof EdgeMap ? pixels : new EdgeMap(pixels, options);
|
|
4228
|
-
const width = options.width ?? 10;
|
|
4229
|
-
const snapped = points.map((p) => snapToEdge(map, p, width));
|
|
4230
|
-
const out = [];
|
|
4231
|
-
for (let i = 0; i < snapped.length; i++) {
|
|
4232
|
-
const a = snapped[i];
|
|
4233
|
-
const b = snapped[(i + 1) % snapped.length];
|
|
4234
|
-
const seg = liveWire(map, a, b, Math.max(2 * width, 16, Math.hypot(b.x - a.x, b.y - a.y) / 4));
|
|
4235
|
-
out.push(...seg.slice(0, -1));
|
|
4236
|
-
}
|
|
4237
|
-
return simplifyPixelPath(out);
|
|
4238
|
-
}
|
|
4239
|
-
function magneticAnchorSpacing(frequency = 57) {
|
|
4240
|
-
const f = Math.min(100, Math.max(0, frequency));
|
|
4241
|
-
return 8 + (100 - f);
|
|
4242
|
-
}
|
|
4243
|
-
|
|
4244
3968
|
// src/image-editor/paths.ts
|
|
4245
3969
|
var WORK_PATH_NAME = "Work Path";
|
|
4246
3970
|
var counter = 0;
|
|
@@ -8066,8 +7790,8 @@ var ImageEditorController = class {
|
|
|
8066
7790
|
const { layer } = this.requireVectorMask(id);
|
|
8067
7791
|
const { width, height } = this.canvasSize;
|
|
8068
7792
|
const raster = layer.mask;
|
|
8069
|
-
const
|
|
8070
|
-
const baked = effectiveLayerMask(
|
|
7793
|
+
const plain2 = { ...layer, vectorMask: { ...layer.vectorMask, enabled: true }, mask: raster ? { surface: raster.surface, defaultValue: raster.defaultValue } : void 0 };
|
|
7794
|
+
const baked = effectiveLayerMask(plain2, width, height, this.guideSurfaceOf);
|
|
8071
7795
|
const mask = raster ? { ...raster, surface: baked.surface, defaultValue: baked.defaultValue } : baked;
|
|
8072
7796
|
this.commitLayers(
|
|
8073
7797
|
mapLayer(this.state.layers, id, (l) => {
|
|
@@ -8762,8 +8486,8 @@ var ImageEditorController = class {
|
|
|
8762
8486
|
const old = layer?.contents;
|
|
8763
8487
|
const { width, height } = edited.imageDimensions;
|
|
8764
8488
|
const only = edited.layers.length === 1 ? edited.layers[0] : null;
|
|
8765
|
-
const
|
|
8766
|
-
if (old?.kind === "raster" &&
|
|
8489
|
+
const plain2 = only?.type === "pixel" && only.visible && only.opacity === 1 && only.blend === "normal" && !only.mask && !only.effects && !only.filters?.length && !only.clipped && (only.fillOpacity ?? 1) === 1 && only.source !== "document";
|
|
8490
|
+
if (old?.kind === "raster" && plain2) {
|
|
8767
8491
|
const px = readRegion(only.surface, { x: -only.offset.x, y: -only.offset.y, width, height });
|
|
8768
8492
|
next = rasterContents({ width, height, data: px }, old.name, old.id);
|
|
8769
8493
|
} else {
|
|
@@ -10698,8 +10422,8 @@ var ImageEditorController = class {
|
|
|
10698
10422
|
return { output, layerId, refineMode, ...documentId ? { documentId } : {} };
|
|
10699
10423
|
}
|
|
10700
10424
|
return this.asOneStep(() => {
|
|
10701
|
-
const
|
|
10702
|
-
if (
|
|
10425
|
+
const plain2 = output === "selection" && !edits.length && !regions2 && refineMode === "color";
|
|
10426
|
+
if (plain2) this.withRecipeStep({ op: "refine", params: { ...params }, ...samplingFields(options) }, () => this.setSelection(refined));
|
|
10703
10427
|
else this.setSelection(refined);
|
|
10704
10428
|
if (output === "selection") return { output, refineMode };
|
|
10705
10429
|
if (output === "layer-mask") {
|
|
@@ -12459,10 +12183,10 @@ var ImageEditorController = class {
|
|
|
12459
12183
|
const mask = layer.mask;
|
|
12460
12184
|
if (!mask?.source) return mask;
|
|
12461
12185
|
add2("Parametric (depth) masks were baked into pixel masks", layer.name);
|
|
12462
|
-
const
|
|
12186
|
+
const plain2 = { ...layer, mask: { ...mask, density: 1, feather: 0, enabled: true } };
|
|
12463
12187
|
const ox = layer.type === "pixel" ? layer.offset.x : 0;
|
|
12464
12188
|
const oy = layer.type === "pixel" ? layer.offset.y : 0;
|
|
12465
|
-
const coverage = compositor.maskCoverage(
|
|
12189
|
+
const coverage = compositor.maskCoverage(plain2, canvas, sources);
|
|
12466
12190
|
const bytes = new Uint8ClampedArray(width * height);
|
|
12467
12191
|
if (coverage) for (let i = 0; i < bytes.length; i++) bytes[i] = Math.round(coverage[i] * 255);
|
|
12468
12192
|
else bytes.fill(255);
|
|
@@ -13809,95 +13533,6 @@ function agentDragDraft(mode, box, image) {
|
|
|
13809
13533
|
};
|
|
13810
13534
|
}
|
|
13811
13535
|
|
|
13812
|
-
// src/image-editor/selection/tool-options.ts
|
|
13813
|
-
var SELECTION_TOOLS = [
|
|
13814
|
-
{ id: "wand", label: "Magic Wand" },
|
|
13815
|
-
{ id: "rect", label: "Rectangle" },
|
|
13816
|
-
{ id: "ellipse", label: "Ellipse" },
|
|
13817
|
-
{ id: "row", label: "Single Row" },
|
|
13818
|
-
{ id: "column", label: "Single Column" },
|
|
13819
|
-
{ id: "lasso", label: "Lasso" },
|
|
13820
|
-
{ id: "polygon", label: "Polygonal Lasso" },
|
|
13821
|
-
{ id: "magnetic", label: "Magnetic Lasso" },
|
|
13822
|
-
{ id: "quick", label: "Quick Selection" },
|
|
13823
|
-
{ id: "object", label: "Object Selection" },
|
|
13824
|
-
{ id: "range", label: "Color Range" },
|
|
13825
|
-
{ id: "type", label: "Type Mask" },
|
|
13826
|
-
{ id: "type-vertical", label: "Vertical Type Mask" }
|
|
13827
|
-
];
|
|
13828
|
-
var COLOR_RANGE_PRESET_LABELS = {
|
|
13829
|
-
reds: "Reds",
|
|
13830
|
-
yellows: "Yellows",
|
|
13831
|
-
greens: "Greens",
|
|
13832
|
-
cyans: "Cyans",
|
|
13833
|
-
blues: "Blues",
|
|
13834
|
-
magentas: "Magentas",
|
|
13835
|
-
highlights: "Highlights",
|
|
13836
|
-
midtones: "Midtones",
|
|
13837
|
-
shadows: "Shadows",
|
|
13838
|
-
skinTones: "Skin Tones"
|
|
13839
|
-
};
|
|
13840
|
-
var SELECTION_MODE_LABELS = {
|
|
13841
|
-
replace: "New",
|
|
13842
|
-
add: "Add",
|
|
13843
|
-
subtract: "Subtract",
|
|
13844
|
-
intersect: "Intersect"
|
|
13845
|
-
};
|
|
13846
|
-
var DEFAULT_SELECTION_TOOL_OPTIONS = {
|
|
13847
|
-
tool: "wand",
|
|
13848
|
-
mode: "replace",
|
|
13849
|
-
tolerance: 32,
|
|
13850
|
-
contiguous: true,
|
|
13851
|
-
antiAlias: true,
|
|
13852
|
-
sampleMerged: false,
|
|
13853
|
-
distance: "rgb",
|
|
13854
|
-
feather: 0,
|
|
13855
|
-
fuzziness: 40,
|
|
13856
|
-
localized: 0,
|
|
13857
|
-
rangeInvert: false,
|
|
13858
|
-
magneticWidth: 10,
|
|
13859
|
-
magneticContrast: 10,
|
|
13860
|
-
magneticFrequency: 57,
|
|
13861
|
-
quickSize: 30,
|
|
13862
|
-
quickSampleAll: true,
|
|
13863
|
-
quickEnhance: false,
|
|
13864
|
-
objectSampleAll: true,
|
|
13865
|
-
objectEnhance: false,
|
|
13866
|
-
focusRange: 50,
|
|
13867
|
-
focusNoise: 0,
|
|
13868
|
-
focusSoftness: 4,
|
|
13869
|
-
typeText: "Type",
|
|
13870
|
-
typeSize: 72,
|
|
13871
|
-
typeFamily: "sans-serif"
|
|
13872
|
-
};
|
|
13873
|
-
function selectionModeFromModifiers(modifiers, fallback) {
|
|
13874
|
-
if (modifiers.shiftKey && modifiers.altKey) return "intersect";
|
|
13875
|
-
if (modifiers.shiftKey) return "add";
|
|
13876
|
-
if (modifiers.altKey) return "subtract";
|
|
13877
|
-
return fallback;
|
|
13878
|
-
}
|
|
13879
|
-
var REFINE_EDGE_CONTROLS = [
|
|
13880
|
-
{ id: "radius", label: "Radius", min: 0, max: 64, step: 1, unit: "px" },
|
|
13881
|
-
{ id: "smooth", label: "Smooth", min: 0, max: 100, step: 1, unit: "" },
|
|
13882
|
-
{ id: "feather", label: "Feather", min: 0, max: 50, step: 0.5, unit: "px" },
|
|
13883
|
-
{ id: "contrast", label: "Contrast", min: 0, max: 100, step: 1, unit: "%" },
|
|
13884
|
-
{ id: "shiftEdge", label: "Shift Edge", min: -100, max: 100, step: 1, unit: "%" }
|
|
13885
|
-
];
|
|
13886
|
-
var REFINE_OUTPUTS = [
|
|
13887
|
-
{ id: "selection", label: "Selection" },
|
|
13888
|
-
{ id: "layer-mask", label: "Layer Mask" },
|
|
13889
|
-
{ id: "new-layer", label: "New Layer" },
|
|
13890
|
-
{ id: "new-layer-with-mask", label: "New Layer with Layer Mask" },
|
|
13891
|
-
{ id: "new-document", label: "New Document", needsDocuments: true },
|
|
13892
|
-
{ id: "new-document-with-mask", label: "New Document with Layer Mask", needsDocuments: true }
|
|
13893
|
-
];
|
|
13894
|
-
function describeSelection(mask) {
|
|
13895
|
-
if (!mask || !mask.bounds) return "No selection";
|
|
13896
|
-
const pct = selectionArea(mask) / (mask.width * mask.height) * 100;
|
|
13897
|
-
const shown = pct >= 10 ? Math.round(pct) : Math.round(pct * 10) / 10;
|
|
13898
|
-
return `${mask.bounds.width} \xD7 ${mask.bounds.height} px selected (${shown}% of the image)`;
|
|
13899
|
-
}
|
|
13900
|
-
|
|
13901
13536
|
// src/image-editor/selection-tools.ts
|
|
13902
13537
|
var modeSchema = {
|
|
13903
13538
|
type: "string",
|
|
@@ -16339,740 +15974,6 @@ function createImageLayerTools(controller) {
|
|
|
16339
15974
|
];
|
|
16340
15975
|
}
|
|
16341
15976
|
|
|
16342
|
-
// src/image-editor/selection/gesture.ts
|
|
16343
|
-
function snapTo45(from, point2) {
|
|
16344
|
-
const dx = point2.x - from.x;
|
|
16345
|
-
const dy = point2.y - from.y;
|
|
16346
|
-
const len = Math.hypot(dx, dy);
|
|
16347
|
-
if (len === 0) return point2;
|
|
16348
|
-
const a = Math.round(Math.atan2(dy, dx) / (Math.PI / 4)) * (Math.PI / 4);
|
|
16349
|
-
return { x: from.x + Math.cos(a) * len, y: from.y + Math.sin(a) * len };
|
|
16350
|
-
}
|
|
16351
|
-
var SelectionGesture = class {
|
|
16352
|
-
constructor(controller, getOptions) {
|
|
16353
|
-
this.controller = controller;
|
|
16354
|
-
this.getOptions = getOptions;
|
|
16355
|
-
}
|
|
16356
|
-
controller;
|
|
16357
|
-
getOptions;
|
|
16358
|
-
start = null;
|
|
16359
|
-
points = [];
|
|
16360
|
-
current = null;
|
|
16361
|
-
modifiers = {};
|
|
16362
|
-
/** Polygonal lasso corners while one is being drawn. */
|
|
16363
|
-
polygon = null;
|
|
16364
|
-
pressedAt = null;
|
|
16365
|
-
freehand = false;
|
|
16366
|
-
magnetic = null;
|
|
16367
|
-
/** A click this close to the first corner (natural pixels) closes a polygonal lasso. */
|
|
16368
|
-
closeRadius = 8;
|
|
16369
|
-
/** The last error (e.g. pixels unavailable for the wand), for the UI to show. */
|
|
16370
|
-
error = null;
|
|
16371
|
-
pending = Promise.resolve();
|
|
16372
|
-
/** Quick Selection while dragging: the selection before the stroke, how the stroke combines, the pixels. */
|
|
16373
|
-
quick = null;
|
|
16374
|
-
livePreviews = Promise.resolve();
|
|
16375
|
-
get active() {
|
|
16376
|
-
return this.start !== null || this.polygon !== null || this.magnetic !== null;
|
|
16377
|
-
}
|
|
16378
|
-
/** Resolves once a Quick Selection stroke or an Object Selection has been applied. */
|
|
16379
|
-
get settled() {
|
|
16380
|
-
return this.pending;
|
|
16381
|
-
}
|
|
16382
|
-
/** Resolves once no live Quick Selection preview is being computed. */
|
|
16383
|
-
get previewed() {
|
|
16384
|
-
return this.livePreviews;
|
|
16385
|
-
}
|
|
16386
|
-
/** Grows the Quick Selection preview to the stroke so far: one at a time, the newest points following. */
|
|
16387
|
-
growPreview() {
|
|
16388
|
-
const q = this.quick;
|
|
16389
|
-
if (!q) return;
|
|
16390
|
-
if (q.running) {
|
|
16391
|
-
q.dirty = true;
|
|
16392
|
-
return;
|
|
16393
|
-
}
|
|
16394
|
-
q.running = true;
|
|
16395
|
-
this.livePreviews = (async () => {
|
|
16396
|
-
do {
|
|
16397
|
-
q.dirty = false;
|
|
16398
|
-
const stroke = this.points.slice();
|
|
16399
|
-
const options = this.getOptions();
|
|
16400
|
-
try {
|
|
16401
|
-
const pixels = await q.pixels;
|
|
16402
|
-
if (!pixels || this.quick !== q) break;
|
|
16403
|
-
const { mask } = await this.controller.runSelectionJob(
|
|
16404
|
-
{
|
|
16405
|
-
op: "quick",
|
|
16406
|
-
stroke,
|
|
16407
|
-
size: options.quickSize ?? 30,
|
|
16408
|
-
grow: q.mode === "add",
|
|
16409
|
-
enhanceEdge: options.quickEnhance ?? false,
|
|
16410
|
-
selection: q.mode === "add" && q.base?.bounds ? serializeMask(q.base) : null
|
|
16411
|
-
},
|
|
16412
|
-
pixels
|
|
16413
|
-
);
|
|
16414
|
-
if (this.quick === q) this.controller.setSelectionPreview(combineSelections(q.base, mask, q.mode));
|
|
16415
|
-
} catch {
|
|
16416
|
-
break;
|
|
16417
|
-
}
|
|
16418
|
-
} while (q.dirty && this.quick === q);
|
|
16419
|
-
q.running = false;
|
|
16420
|
-
})();
|
|
16421
|
-
}
|
|
16422
|
-
/** The anchors of a Magnetic Lasso being drawn (empty otherwise). */
|
|
16423
|
-
get magneticAnchors() {
|
|
16424
|
-
const m = this.magnetic;
|
|
16425
|
-
return m ? m.anchors.map((i) => m.fixed[i]) : [];
|
|
16426
|
-
}
|
|
16427
|
-
/** The outline of the shape being drawn, or `null`. */
|
|
16428
|
-
get draftPath() {
|
|
16429
|
-
if (this.magnetic) return [...this.magnetic.fixed, ...this.magnetic.live.slice(1)];
|
|
16430
|
-
if (this.polygon) return this.current ? [...this.polygon, this.current] : this.polygon.slice();
|
|
16431
|
-
if (!this.start || !this.current) return null;
|
|
16432
|
-
const { tool } = this.getOptions();
|
|
16433
|
-
if (tool === "quick") return null;
|
|
16434
|
-
if (tool === "lasso") return this.points.length > 1 ? this.points.slice() : null;
|
|
16435
|
-
const rect3 = normalizeRect({
|
|
16436
|
-
x: this.start.x,
|
|
16437
|
-
y: this.start.y,
|
|
16438
|
-
width: this.current.x - this.start.x,
|
|
16439
|
-
height: this.current.y - this.start.y
|
|
16440
|
-
});
|
|
16441
|
-
if (rect3.width < 1 && rect3.height < 1) return null;
|
|
16442
|
-
if (tool === "ellipse") return ellipsePoints(rect3);
|
|
16443
|
-
return [
|
|
16444
|
-
{ x: rect3.x, y: rect3.y },
|
|
16445
|
-
{ x: rect3.x + rect3.width, y: rect3.y },
|
|
16446
|
-
{ x: rect3.x + rect3.width, y: rect3.y + rect3.height },
|
|
16447
|
-
{ x: rect3.x, y: rect3.y + rect3.height }
|
|
16448
|
-
];
|
|
16449
|
-
}
|
|
16450
|
-
/** Pointer down. Resolves once a wand click has been applied. */
|
|
16451
|
-
async begin(point2, modifiers = {}) {
|
|
16452
|
-
const options = this.getOptions();
|
|
16453
|
-
this.error = null;
|
|
16454
|
-
if (this.polygon && options.tool !== "polygon" || this.magnetic && options.tool !== "magnetic") this.cancel();
|
|
16455
|
-
if (options.tool === "magnetic") return this.magneticClick(point2, modifiers, options);
|
|
16456
|
-
if (options.tool === "polygon") {
|
|
16457
|
-
this.pressedAt = point2;
|
|
16458
|
-
this.freehand = false;
|
|
16459
|
-
if (!this.polygon) {
|
|
16460
|
-
this.modifiers = modifiers;
|
|
16461
|
-
this.polygon = [point2];
|
|
16462
|
-
this.current = point2;
|
|
16463
|
-
return;
|
|
16464
|
-
}
|
|
16465
|
-
const last = this.polygon[this.polygon.length - 1];
|
|
16466
|
-
const p = modifiers.shiftKey ? snapTo45(last, point2) : point2;
|
|
16467
|
-
const first = this.polygon[0];
|
|
16468
|
-
if (this.polygon.length >= 3 && Math.hypot(p.x - first.x, p.y - first.y) <= this.closeRadius) {
|
|
16469
|
-
this.close();
|
|
16470
|
-
return;
|
|
16471
|
-
}
|
|
16472
|
-
if (Math.hypot(p.x - last.x, p.y - last.y) >= 0.5) this.polygon.push(p);
|
|
16473
|
-
this.current = p;
|
|
16474
|
-
if ((modifiers.detail ?? 1) >= 2) this.close();
|
|
16475
|
-
return;
|
|
16476
|
-
}
|
|
16477
|
-
this.modifiers = modifiers;
|
|
16478
|
-
if (options.tool === "row" || options.tool === "column") {
|
|
16479
|
-
this.start = null;
|
|
16480
|
-
try {
|
|
16481
|
-
this.controller.selectLine(options.tool, options.tool === "row" ? point2.y : point2.x, {
|
|
16482
|
-
mode: selectionModeFromModifiers(modifiers, options.mode),
|
|
16483
|
-
feather: options.feather ?? 0
|
|
16484
|
-
});
|
|
16485
|
-
} catch (err) {
|
|
16486
|
-
this.error = err instanceof Error ? err.message : String(err);
|
|
16487
|
-
}
|
|
16488
|
-
return;
|
|
16489
|
-
}
|
|
16490
|
-
if (options.tool === "type" || options.tool === "type-vertical") {
|
|
16491
|
-
this.start = null;
|
|
16492
|
-
try {
|
|
16493
|
-
this.controller.selectTextShape({
|
|
16494
|
-
text: options.typeText || "Type",
|
|
16495
|
-
x: point2.x,
|
|
16496
|
-
y: point2.y,
|
|
16497
|
-
orientation: options.tool === "type-vertical" ? "vertical" : "horizontal",
|
|
16498
|
-
style: { fontSize: options.typeSize ?? 72, fontFamily: options.typeFamily || "sans-serif" },
|
|
16499
|
-
mode: selectionModeFromModifiers(modifiers, options.mode),
|
|
16500
|
-
feather: options.feather ?? 0,
|
|
16501
|
-
antiAlias: options.antiAlias
|
|
16502
|
-
});
|
|
16503
|
-
} catch (err) {
|
|
16504
|
-
this.error = err instanceof Error ? err.message : String(err);
|
|
16505
|
-
}
|
|
16506
|
-
return;
|
|
16507
|
-
}
|
|
16508
|
-
if (options.tool === "wand" || options.tool === "range") {
|
|
16509
|
-
this.start = null;
|
|
16510
|
-
const common = {
|
|
16511
|
-
sampleMerged: options.sampleMerged,
|
|
16512
|
-
sample: options.sample,
|
|
16513
|
-
feather: options.feather ?? 0,
|
|
16514
|
-
mode: selectionModeFromModifiers(modifiers, options.mode)
|
|
16515
|
-
};
|
|
16516
|
-
try {
|
|
16517
|
-
if (options.tool === "range") {
|
|
16518
|
-
const pixels = await this.controller.getSamplePixels(common);
|
|
16519
|
-
if (!pixels) throw new Error("The image pixels are not available to sample.");
|
|
16520
|
-
const x = Math.min(pixels.width - 1, Math.max(0, Math.floor(point2.x)));
|
|
16521
|
-
const y = Math.min(pixels.height - 1, Math.max(0, Math.floor(point2.y)));
|
|
16522
|
-
const p = (y * pixels.width + x) * 4;
|
|
16523
|
-
const color = [pixels.data[p], pixels.data[p + 1], pixels.data[p + 2]];
|
|
16524
|
-
await this.controller.selectColorRange([{ color, at: point2 }], {
|
|
16525
|
-
...common,
|
|
16526
|
-
fuzziness: options.fuzziness ?? 40,
|
|
16527
|
-
localized: options.localized ? options.localized : void 0,
|
|
16528
|
-
invert: options.rangeInvert ?? false
|
|
16529
|
-
});
|
|
16530
|
-
return;
|
|
16531
|
-
}
|
|
16532
|
-
await this.controller.magicWand(point2, {
|
|
16533
|
-
...common,
|
|
16534
|
-
tolerance: options.tolerance,
|
|
16535
|
-
contiguous: options.contiguous,
|
|
16536
|
-
antiAlias: options.antiAlias,
|
|
16537
|
-
distance: options.distance
|
|
16538
|
-
});
|
|
16539
|
-
} catch (err) {
|
|
16540
|
-
this.error = err instanceof Error ? err.message : String(err);
|
|
16541
|
-
}
|
|
16542
|
-
return;
|
|
16543
|
-
}
|
|
16544
|
-
this.start = point2;
|
|
16545
|
-
this.current = point2;
|
|
16546
|
-
this.points = [point2];
|
|
16547
|
-
if (options.tool === "quick") {
|
|
16548
|
-
const picked = selectionModeFromModifiers(modifiers, options.mode);
|
|
16549
|
-
const base = this.controller.getSelection();
|
|
16550
|
-
this.quick = {
|
|
16551
|
-
base,
|
|
16552
|
-
mode: picked === "replace" && base ? "add" : picked,
|
|
16553
|
-
pixels: this.controller.getSamplePixels({ sample: options.quickSampleAll === false ? "active-layer" : "composite" }).catch(() => null),
|
|
16554
|
-
running: false,
|
|
16555
|
-
dirty: false
|
|
16556
|
-
};
|
|
16557
|
-
this.growPreview();
|
|
16558
|
-
}
|
|
16559
|
-
}
|
|
16560
|
-
/** Pointer move; returns true when the draft changed (re-render). */
|
|
16561
|
-
move(point2, modifiers = {}) {
|
|
16562
|
-
if (this.magnetic) {
|
|
16563
|
-
this.current = point2;
|
|
16564
|
-
this.followEdges(point2);
|
|
16565
|
-
return true;
|
|
16566
|
-
}
|
|
16567
|
-
if (this.polygon) {
|
|
16568
|
-
const last = this.polygon[this.polygon.length - 1];
|
|
16569
|
-
if (this.pressedAt && (this.freehand || Math.hypot(point2.x - this.pressedAt.x, point2.y - this.pressedAt.y) >= 3)) {
|
|
16570
|
-
this.freehand = true;
|
|
16571
|
-
if (Math.hypot(point2.x - last.x, point2.y - last.y) >= 0.5) this.polygon.push(point2);
|
|
16572
|
-
this.current = null;
|
|
16573
|
-
return true;
|
|
16574
|
-
}
|
|
16575
|
-
this.current = modifiers.shiftKey ? snapTo45(last, point2) : point2;
|
|
16576
|
-
return true;
|
|
16577
|
-
}
|
|
16578
|
-
if (!this.start) return false;
|
|
16579
|
-
this.current = point2;
|
|
16580
|
-
const drawn = this.getOptions().tool;
|
|
16581
|
-
if (drawn === "lasso" || drawn === "quick") {
|
|
16582
|
-
const last = this.points[this.points.length - 1];
|
|
16583
|
-
if (Math.hypot(point2.x - last.x, point2.y - last.y) < 0.5) return false;
|
|
16584
|
-
this.points.push(point2);
|
|
16585
|
-
if (drawn === "quick") this.growPreview();
|
|
16586
|
-
}
|
|
16587
|
-
return true;
|
|
16588
|
-
}
|
|
16589
|
-
/** Pointer up: commits the shape. Returns true when the selection changed. */
|
|
16590
|
-
end(point2, modifiers) {
|
|
16591
|
-
if (this.magnetic) return false;
|
|
16592
|
-
if (this.polygon) {
|
|
16593
|
-
if (point2 && this.freehand) this.move(point2);
|
|
16594
|
-
this.pressedAt = null;
|
|
16595
|
-
this.freehand = false;
|
|
16596
|
-
return false;
|
|
16597
|
-
}
|
|
16598
|
-
if (!this.start) return false;
|
|
16599
|
-
if (point2) this.move(point2);
|
|
16600
|
-
const options = this.getOptions();
|
|
16601
|
-
const mode = selectionModeFromModifiers(modifiers ?? this.modifiers, options.mode);
|
|
16602
|
-
if (options.tool === "object") {
|
|
16603
|
-
const rect4 = normalizeRect({ x: this.start.x, y: this.start.y, width: this.current.x - this.start.x, height: this.current.y - this.start.y });
|
|
16604
|
-
this.start = null;
|
|
16605
|
-
this.current = null;
|
|
16606
|
-
this.points = [];
|
|
16607
|
-
const target = rect4.width >= 4 && rect4.height >= 4 ? { rect: rect4 } : { point: { x: rect4.x + rect4.width / 2, y: rect4.y + rect4.height / 2 } };
|
|
16608
|
-
this.pending = this.controller.selectObject(target, { mode, sampleAllLayers: options.objectSampleAll ?? true, enhanceEdge: options.objectEnhance ?? false }).then(
|
|
16609
|
-
() => void 0,
|
|
16610
|
-
(err) => {
|
|
16611
|
-
this.error = err instanceof Error ? err.message : String(err);
|
|
16612
|
-
}
|
|
16613
|
-
);
|
|
16614
|
-
return false;
|
|
16615
|
-
}
|
|
16616
|
-
if (options.tool === "quick") {
|
|
16617
|
-
const stroke = this.points;
|
|
16618
|
-
this.start = null;
|
|
16619
|
-
this.current = null;
|
|
16620
|
-
this.points = [];
|
|
16621
|
-
const strokeMode = this.quick?.mode ?? (mode === "replace" && this.controller.getSelection() ? "add" : mode);
|
|
16622
|
-
this.quick = null;
|
|
16623
|
-
this.pending = this.controller.quickSelect([stroke], {
|
|
16624
|
-
size: options.quickSize ?? 30,
|
|
16625
|
-
mode: strokeMode,
|
|
16626
|
-
sampleAllLayers: options.quickSampleAll ?? true,
|
|
16627
|
-
enhanceEdge: options.quickEnhance ?? false
|
|
16628
|
-
}).then(
|
|
16629
|
-
() => void 0,
|
|
16630
|
-
(err) => {
|
|
16631
|
-
this.error = err instanceof Error ? err.message : String(err);
|
|
16632
|
-
}
|
|
16633
|
-
).finally(() => this.controller.setSelectionPreview(void 0));
|
|
16634
|
-
return false;
|
|
16635
|
-
}
|
|
16636
|
-
const path = this.draftPath;
|
|
16637
|
-
const rect3 = normalizeRect({
|
|
16638
|
-
x: this.start.x,
|
|
16639
|
-
y: this.start.y,
|
|
16640
|
-
width: this.current.x - this.start.x,
|
|
16641
|
-
height: this.current.y - this.start.y
|
|
16642
|
-
});
|
|
16643
|
-
this.start = null;
|
|
16644
|
-
this.current = null;
|
|
16645
|
-
this.points = [];
|
|
16646
|
-
if (!path) return mode === "replace" ? this.controller.deselect() : false;
|
|
16647
|
-
const opts = { mode, antiAlias: options.antiAlias, feather: options.feather ?? 0 };
|
|
16648
|
-
if (options.tool === "lasso") return path.length >= 3 ? this.controller.selectPolygon(path, opts) : false;
|
|
16649
|
-
return options.tool === "ellipse" ? this.controller.selectEllipse(rect3, opts) : this.controller.selectRect(rect3, opts);
|
|
16650
|
-
}
|
|
16651
|
-
/** Closes a polygonal lasso and selects it. Returns true when the selection changed. */
|
|
16652
|
-
close() {
|
|
16653
|
-
let polygon = this.polygon;
|
|
16654
|
-
const m = this.magnetic;
|
|
16655
|
-
if (m) {
|
|
16656
|
-
const tail = m.edges && m.fixed.length > 1 ? liveWire(m.edges, m.fixed[m.fixed.length - 1], m.fixed[0], this.corridor(m.fixed[m.fixed.length - 1], m.fixed[0])) : [];
|
|
16657
|
-
polygon = simplifyPixelPath([...m.fixed, ...tail.slice(1, -1)]);
|
|
16658
|
-
}
|
|
16659
|
-
if (!polygon) return false;
|
|
16660
|
-
const options = this.getOptions();
|
|
16661
|
-
this.cancel();
|
|
16662
|
-
if (polygon.length < 3) return false;
|
|
16663
|
-
return this.controller.selectPolygon(polygon, {
|
|
16664
|
-
mode: selectionModeFromModifiers(this.modifiers, options.mode),
|
|
16665
|
-
antiAlias: options.antiAlias,
|
|
16666
|
-
feather: options.feather ?? 0
|
|
16667
|
-
});
|
|
16668
|
-
}
|
|
16669
|
-
/**
|
|
16670
|
-
* Keys for a polygonal lasso being drawn: Enter closes it, Escape cancels, Backspace / Delete
|
|
16671
|
-
* removes the last corner. Returns true when the key was used (the host prevents its default).
|
|
16672
|
-
*/
|
|
16673
|
-
key(key2) {
|
|
16674
|
-
if (!this.polygon && !this.magnetic) return false;
|
|
16675
|
-
if (key2 === "Enter") this.close();
|
|
16676
|
-
else if (key2 === "Escape") this.cancel();
|
|
16677
|
-
else if (key2 === "Backspace" || key2 === "Delete") {
|
|
16678
|
-
const m = this.magnetic;
|
|
16679
|
-
if (m) {
|
|
16680
|
-
m.anchors.pop();
|
|
16681
|
-
if (!m.anchors.length) this.cancel();
|
|
16682
|
-
else {
|
|
16683
|
-
m.fixed.length = m.anchors[m.anchors.length - 1] + 1;
|
|
16684
|
-
m.live = [];
|
|
16685
|
-
if (this.current) this.followEdges(this.current, false);
|
|
16686
|
-
}
|
|
16687
|
-
} else {
|
|
16688
|
-
this.polygon.pop();
|
|
16689
|
-
if (!this.polygon.length) this.cancel();
|
|
16690
|
-
}
|
|
16691
|
-
} else return false;
|
|
16692
|
-
return true;
|
|
16693
|
-
}
|
|
16694
|
-
/** Corridor half-width for a live-wire between two points. */
|
|
16695
|
-
corridor(a, b) {
|
|
16696
|
-
return Math.max(2 * (this.getOptions().magneticWidth ?? 10), 16, Math.hypot(b.x - a.x, b.y - a.y) / 4);
|
|
16697
|
-
}
|
|
16698
|
-
async magneticClick(point2, modifiers, options) {
|
|
16699
|
-
const m = this.magnetic;
|
|
16700
|
-
if (!m) {
|
|
16701
|
-
this.modifiers = modifiers;
|
|
16702
|
-
const draft = { fixed: [point2], anchors: [0], live: [], edges: null };
|
|
16703
|
-
this.magnetic = draft;
|
|
16704
|
-
this.current = point2;
|
|
16705
|
-
try {
|
|
16706
|
-
const pixels = await this.controller.getSamplePixels({ sampleMerged: options.sampleMerged, sample: options.sample });
|
|
16707
|
-
if (!pixels) throw new Error("The image pixels are not available to find edges in.");
|
|
16708
|
-
if (this.magnetic !== draft) return;
|
|
16709
|
-
draft.edges = new EdgeMap(pixels, { contrast: (options.magneticContrast ?? 10) / 100 });
|
|
16710
|
-
draft.fixed = [snapToEdge(draft.edges, point2, options.magneticWidth ?? 10)];
|
|
16711
|
-
if (this.current) this.followEdges(this.current);
|
|
16712
|
-
} catch (err) {
|
|
16713
|
-
this.error = err instanceof Error ? err.message : String(err);
|
|
16714
|
-
if (this.magnetic === draft) this.cancel();
|
|
16715
|
-
}
|
|
16716
|
-
return;
|
|
16717
|
-
}
|
|
16718
|
-
const first = m.fixed[0];
|
|
16719
|
-
if (m.anchors.length >= 2 && Math.hypot(point2.x - first.x, point2.y - first.y) <= this.closeRadius) {
|
|
16720
|
-
this.close();
|
|
16721
|
-
return;
|
|
16722
|
-
}
|
|
16723
|
-
this.current = point2;
|
|
16724
|
-
this.followEdges(point2);
|
|
16725
|
-
if (m.live.length > 1) {
|
|
16726
|
-
m.fixed.push(...m.live.slice(1));
|
|
16727
|
-
m.anchors.push(m.fixed.length - 1);
|
|
16728
|
-
m.live = [];
|
|
16729
|
-
}
|
|
16730
|
-
if ((modifiers.detail ?? 1) >= 2) this.close();
|
|
16731
|
-
}
|
|
16732
|
-
/** Updates the live path to `point`, fixing anchors along it every `frequency` px (with `place`). */
|
|
16733
|
-
followEdges(point2, place = true) {
|
|
16734
|
-
const m = this.magnetic;
|
|
16735
|
-
const last = () => m.fixed[m.fixed.length - 1];
|
|
16736
|
-
if (!m.edges) {
|
|
16737
|
-
m.live = [last(), point2];
|
|
16738
|
-
return;
|
|
16739
|
-
}
|
|
16740
|
-
const options = this.getOptions();
|
|
16741
|
-
const target = snapToEdge(m.edges, point2, options.magneticWidth ?? 10);
|
|
16742
|
-
const spacing = magneticAnchorSpacing(options.magneticFrequency ?? 57);
|
|
16743
|
-
for (let guard = 0; guard < 64; guard++) {
|
|
16744
|
-
const path = liveWire(m.edges, last(), target, this.corridor(last(), target));
|
|
16745
|
-
let length = 0;
|
|
16746
|
-
let k = 0;
|
|
16747
|
-
for (k = 1; k < path.length; k++) {
|
|
16748
|
-
length += Math.hypot(path[k].x - path[k - 1].x, path[k].y - path[k - 1].y);
|
|
16749
|
-
if (length >= spacing) break;
|
|
16750
|
-
}
|
|
16751
|
-
if (!place || k >= path.length - 1) {
|
|
16752
|
-
m.live = path;
|
|
16753
|
-
return;
|
|
16754
|
-
}
|
|
16755
|
-
m.fixed.push(...path.slice(1, k + 1));
|
|
16756
|
-
m.anchors.push(m.fixed.length - 1);
|
|
16757
|
-
}
|
|
16758
|
-
m.live = [last(), target];
|
|
16759
|
-
}
|
|
16760
|
-
/** Abandons a drag (or a polygonal lasso) without changing the selection. */
|
|
16761
|
-
cancel() {
|
|
16762
|
-
if (this.quick) {
|
|
16763
|
-
this.quick = null;
|
|
16764
|
-
this.controller.setSelectionPreview(void 0);
|
|
16765
|
-
}
|
|
16766
|
-
this.start = null;
|
|
16767
|
-
this.current = null;
|
|
16768
|
-
this.points = [];
|
|
16769
|
-
this.polygon = null;
|
|
16770
|
-
this.magnetic = null;
|
|
16771
|
-
this.pressedAt = null;
|
|
16772
|
-
this.freehand = false;
|
|
16773
|
-
}
|
|
16774
|
-
};
|
|
16775
|
-
function handleSelectionShortcut(controller, e, onError) {
|
|
16776
|
-
if (!(e.ctrlKey || e.metaKey)) return false;
|
|
16777
|
-
const key2 = e.key.toLowerCase();
|
|
16778
|
-
const run = (fn) => {
|
|
16779
|
-
try {
|
|
16780
|
-
fn();
|
|
16781
|
-
} catch (err) {
|
|
16782
|
-
onError?.(err instanceof Error ? err.message : String(err));
|
|
16783
|
-
}
|
|
16784
|
-
return true;
|
|
16785
|
-
};
|
|
16786
|
-
if (key2 === "c") return run(() => controller.copyPixels({ merged: Boolean(e.shiftKey) }));
|
|
16787
|
-
if (key2 === "x" && !e.shiftKey) return run(() => controller.cutPixels());
|
|
16788
|
-
if (key2 === "v") return run(() => controller.pastePixels(e.shiftKey && e.altKey ? "into" : e.shiftKey ? "in-place" : "new-layer"));
|
|
16789
|
-
if (key2 === "d") return run(() => e.shiftKey ? controller.reselect() : controller.deselect());
|
|
16790
|
-
if (key2 === "i" && e.shiftKey) return run(() => controller.invertSelection());
|
|
16791
|
-
return false;
|
|
16792
|
-
}
|
|
16793
|
-
function handleMaskViewKey(controller, e) {
|
|
16794
|
-
if (e.ctrlKey || e.metaKey || e.altKey || e.key !== "\\") return false;
|
|
16795
|
-
const id = controller.getState().activeLayerId;
|
|
16796
|
-
const layer = id ? controller.getLayer(id) : null;
|
|
16797
|
-
if (!layer?.mask || layer.mask.source) return false;
|
|
16798
|
-
const view = controller.getMaskView();
|
|
16799
|
-
controller.setMaskView(view?.layerId === layer.id && view.mode === "overlay" ? "off" : "overlay", layer.id);
|
|
16800
|
-
return true;
|
|
16801
|
-
}
|
|
16802
|
-
function handleQuickMaskKey(controller, e) {
|
|
16803
|
-
if (e.ctrlKey || e.metaKey || e.altKey || e.key.toLowerCase() !== "q") return false;
|
|
16804
|
-
controller.setQuickMaskMode(!controller.quickMaskEditing);
|
|
16805
|
-
return true;
|
|
16806
|
-
}
|
|
16807
|
-
|
|
16808
|
-
// src/image-editor/layers/paint-ui.ts
|
|
16809
|
-
var plain2 = { kind: "plain" };
|
|
16810
|
-
var BRUSH_OPTION_CONTROLS = [
|
|
16811
|
-
{ id: "size", label: "Size", section: "layer", min: 1, max: 500, step: 1, default: 20, decimals: 0, track: plain2, unit: "px" },
|
|
16812
|
-
{ id: "hardness", label: "Hardness", section: "layer", min: 0, max: 1, step: 0.01, default: 1, decimals: 0, track: plain2, displayScale: 100, unit: "%" },
|
|
16813
|
-
{ id: "opacity", label: "Opacity", section: "layer", min: 0, max: 1, step: 0.01, default: 1, decimals: 0, track: plain2, displayScale: 100, unit: "%" },
|
|
16814
|
-
{ id: "flow", label: "Flow", section: "layer", min: 0, max: 1, step: 0.01, default: 1, decimals: 0, track: plain2, displayScale: 100, unit: "%" },
|
|
16815
|
-
{ id: "smoothing", label: "Smoothing", section: "layer", min: 0, max: 0.95, step: 0.01, default: 0, decimals: 0, track: plain2, displayScale: 100, unit: "%" }
|
|
16816
|
-
];
|
|
16817
|
-
var MIXER_OPTION_CONTROLS = [
|
|
16818
|
-
{ id: "wet", label: "Wet", section: "layer", min: 0, max: 1, step: 0.01, default: 0.5, decimals: 0, track: plain2, displayScale: 100, unit: "%" },
|
|
16819
|
-
{ id: "load", label: "Load", section: "layer", min: 0, max: 1, step: 0.01, default: 0.5, decimals: 0, track: plain2, displayScale: 100, unit: "%" },
|
|
16820
|
-
{ id: "mix", label: "Mix", section: "layer", min: 0, max: 1, step: 0.01, default: 0.5, decimals: 0, track: plain2, displayScale: 100, unit: "%" }
|
|
16821
|
-
];
|
|
16822
|
-
var MIXER_MIXING_OPTIONS = [
|
|
16823
|
-
{ id: "rgb", label: "RGB", title: "Mix colours as RGB averages" },
|
|
16824
|
-
{ id: "pigment", label: "Pigment", title: "Mix colours like paint: blue and yellow make green" }
|
|
16825
|
-
];
|
|
16826
|
-
var PAINT_TOOLS = [
|
|
16827
|
-
{ id: "move", label: "Move", shortcut: "V" },
|
|
16828
|
-
{ id: "brush", label: "Brush", shortcut: "B" },
|
|
16829
|
-
{ id: "mixer", label: "Mixer", shortcut: "Shift+B" },
|
|
16830
|
-
{ id: "eraser", label: "Eraser", shortcut: "E" },
|
|
16831
|
-
{ id: "clone", label: "Clone", shortcut: "S" },
|
|
16832
|
-
{ id: "heal", label: "Heal", shortcut: "Shift+J" },
|
|
16833
|
-
{ id: "spot-heal", label: "Spot Heal", shortcut: "J" },
|
|
16834
|
-
{ id: "smudge", label: "Smudge", shortcut: "" },
|
|
16835
|
-
{ id: "dodge", label: "Dodge", shortcut: "O" },
|
|
16836
|
-
{ id: "burn", label: "Burn", shortcut: "Shift+O" },
|
|
16837
|
-
{ id: "sponge", label: "Sponge", shortcut: "Shift+O" },
|
|
16838
|
-
{ id: "blur", label: "Blur", shortcut: "" },
|
|
16839
|
-
{ id: "sharpen", label: "Sharpen", shortcut: "" },
|
|
16840
|
-
{ id: "history", label: "History", shortcut: "Y" }
|
|
16841
|
-
];
|
|
16842
|
-
var isPaintTool = (tool) => PAINT_TOOLS.some((t) => t.id === tool);
|
|
16843
|
-
var isRetouchPaintTool = (tool) => tool === "clone" || tool === "heal" || tool === "spot-heal" || tool === "smudge" || tool === "dodge" || tool === "burn" || tool === "sponge" || tool === "blur" || tool === "sharpen" || tool === "history";
|
|
16844
|
-
var RETOUCH_OPTION_CONTROLS = [
|
|
16845
|
-
{ id: "exposure", label: "Exposure", section: "layer", min: 0, max: 1, step: 0.01, default: 0.5, decimals: 0, track: plain2, displayScale: 100, unit: "%", tools: ["dodge", "burn"] },
|
|
16846
|
-
{ id: "strength", label: "Strength", section: "layer", min: 0, max: 1, step: 0.01, default: 0.5, decimals: 0, track: plain2, displayScale: 100, unit: "%", tools: ["smudge", "blur", "sharpen"] },
|
|
16847
|
-
{ id: "historySteps", label: "Steps back", section: "layer", min: 0, max: 50, step: 1, default: 0, decimals: 0, track: plain2, unit: "", tools: ["history"] }
|
|
16848
|
-
];
|
|
16849
|
-
var CLONE_TRANSFORM_CONTROLS = [
|
|
16850
|
-
{ id: "width", label: "Source W", section: "layer", min: 1, max: 400, step: 1, default: 100, decimals: 0, track: plain2, unit: "%" },
|
|
16851
|
-
{ id: "height", label: "Source H", section: "layer", min: 1, max: 400, step: 1, default: 100, decimals: 0, track: plain2, unit: "%" },
|
|
16852
|
-
{ id: "angle", label: "Source angle", section: "layer", min: -180, max: 180, step: 1, default: 0, decimals: 0, track: plain2, unit: "\xB0" }
|
|
16853
|
-
];
|
|
16854
|
-
function cloneTransformView(controller) {
|
|
16855
|
-
const t = controller.getState().paint.cloneTransform;
|
|
16856
|
-
return {
|
|
16857
|
-
width: Math.round(Math.abs(t.scaleX) * 100),
|
|
16858
|
-
height: Math.round(Math.abs(t.scaleY) * 100),
|
|
16859
|
-
angle: t.rotate,
|
|
16860
|
-
flipH: t.scaleX < 0,
|
|
16861
|
-
flipV: t.scaleY < 0,
|
|
16862
|
-
identity: t.scaleX === 1 && t.scaleY === 1 && t.rotate === 0
|
|
16863
|
-
};
|
|
16864
|
-
}
|
|
16865
|
-
function setCloneTransformValue(controller, id, value) {
|
|
16866
|
-
const t = controller.getState().paint.cloneTransform;
|
|
16867
|
-
if (id === "width") controller.setCloneTransform({ scaleX: Math.sign(t.scaleX) * (value ?? 100) / 100 });
|
|
16868
|
-
else if (id === "height") controller.setCloneTransform({ scaleY: Math.sign(t.scaleY) * (value ?? 100) / 100 });
|
|
16869
|
-
else if (id === "angle") controller.setCloneTransform({ rotate: value ?? 0 });
|
|
16870
|
-
else if (id === "flipH") controller.setCloneTransform({ scaleX: -t.scaleX });
|
|
16871
|
-
else controller.setCloneTransform({ scaleY: -t.scaleY });
|
|
16872
|
-
}
|
|
16873
|
-
var RETOUCH_CHOICES = [
|
|
16874
|
-
{
|
|
16875
|
-
id: "sample",
|
|
16876
|
-
label: "Sample",
|
|
16877
|
-
tools: ["clone", "heal", "spot-heal"],
|
|
16878
|
-
options: [
|
|
16879
|
-
{ id: "current", label: "Current", title: "Sample the layer being retouched" },
|
|
16880
|
-
{ id: "current-below", label: "Current & Below", title: "Sample this layer and the layers under it" },
|
|
16881
|
-
{ id: "all", label: "All", title: "Sample every layer (retouch onto an empty layer)" }
|
|
16882
|
-
]
|
|
16883
|
-
},
|
|
16884
|
-
{
|
|
16885
|
-
id: "spotType",
|
|
16886
|
-
label: "Type",
|
|
16887
|
-
tools: ["spot-heal"],
|
|
16888
|
-
options: [
|
|
16889
|
-
{ id: "proximity-match", label: "Proximity Match", title: "Copy the best matching patch nearby" },
|
|
16890
|
-
{ id: "content-aware", label: "Content-Aware", title: "Synthesise texture and edges from patches around the spot" },
|
|
16891
|
-
{ id: "smooth", label: "Smooth", title: "Fill smoothly from the edges" }
|
|
16892
|
-
]
|
|
16893
|
-
},
|
|
16894
|
-
{
|
|
16895
|
-
id: "range",
|
|
16896
|
-
label: "Range",
|
|
16897
|
-
tools: ["dodge", "burn"],
|
|
16898
|
-
options: [
|
|
16899
|
-
{ id: "shadows", label: "Shadows", title: "Change the dark tones most" },
|
|
16900
|
-
{ id: "midtones", label: "Midtones", title: "Change the middle tones most" },
|
|
16901
|
-
{ id: "highlights", label: "Highlights", title: "Change the light tones most" }
|
|
16902
|
-
]
|
|
16903
|
-
},
|
|
16904
|
-
{
|
|
16905
|
-
id: "spongeMode",
|
|
16906
|
-
label: "Mode",
|
|
16907
|
-
tools: ["sponge"],
|
|
16908
|
-
options: [
|
|
16909
|
-
{ id: "desaturate", label: "Desaturate", title: "Take colour out" },
|
|
16910
|
-
{ id: "saturate", label: "Saturate", title: "Add colour" }
|
|
16911
|
-
]
|
|
16912
|
-
}
|
|
16913
|
-
];
|
|
16914
|
-
var RETOUCH_TOGGLES = [
|
|
16915
|
-
{ id: "aligned", label: "Aligned", tools: ["clone", "heal"] },
|
|
16916
|
-
{ id: "protectTones", label: "Protect tones", tools: ["dodge", "burn"] },
|
|
16917
|
-
{ id: "vibrance", label: "Vibrance", tools: ["sponge"] }
|
|
16918
|
-
];
|
|
16919
|
-
function setRetouchChoice(controller, id, value) {
|
|
16920
|
-
controller.setPaintOptions({ retouch: { [id]: value } });
|
|
16921
|
-
}
|
|
16922
|
-
function cloneSourceLabel(controller) {
|
|
16923
|
-
const cs = controller.getState().paint.cloneSource;
|
|
16924
|
-
return cs ? `Source ${cs.x}, ${cs.y}${cs.offset ? ` (offset ${cs.offset.x}, ${cs.offset.y})` : ""}` : "Alt-click to set the source";
|
|
16925
|
-
}
|
|
16926
|
-
function rgbUnitToHex(c) {
|
|
16927
|
-
return `#${c.map((v) => Math.round(Math.min(1, Math.max(0, v)) * 255).toString(16).padStart(2, "0")).join("")}`;
|
|
16928
|
-
}
|
|
16929
|
-
function sizeStep(size) {
|
|
16930
|
-
return size < 10 ? 1 : size < 50 ? 5 : size < 100 ? 10 : size < 200 ? 25 : size < 500 ? 50 : 100;
|
|
16931
|
-
}
|
|
16932
|
-
var isTyping = (t) => {
|
|
16933
|
-
const el = t;
|
|
16934
|
-
return Boolean(el && (el.isContentEditable || el.tagName === "INPUT" || el.tagName === "TEXTAREA" || el.tagName === "SELECT"));
|
|
16935
|
-
};
|
|
16936
|
-
function handlePaintShortcut(controller, e) {
|
|
16937
|
-
if (e.ctrlKey || e.metaKey || e.altKey || isTyping(e.target)) return false;
|
|
16938
|
-
const brush = controller.getState().paint.brush;
|
|
16939
|
-
const set = (b) => controller.setPaintOptions({ brush: b });
|
|
16940
|
-
const key2 = e.key;
|
|
16941
|
-
if (handleMaskViewKey(controller, e)) return true;
|
|
16942
|
-
switch (key2.toLowerCase()) {
|
|
16943
|
-
case "q":
|
|
16944
|
-
controller.setQuickMaskMode(!controller.quickMaskEditing);
|
|
16945
|
-
return true;
|
|
16946
|
-
case "b":
|
|
16947
|
-
controller.setTool(e.shiftKey || key2 === "B" ? "mixer" : "brush");
|
|
16948
|
-
return true;
|
|
16949
|
-
case "e":
|
|
16950
|
-
controller.setTool("eraser");
|
|
16951
|
-
return true;
|
|
16952
|
-
case "v":
|
|
16953
|
-
controller.setTool("move");
|
|
16954
|
-
return true;
|
|
16955
|
-
case "s":
|
|
16956
|
-
controller.setTool("clone");
|
|
16957
|
-
return true;
|
|
16958
|
-
case "j":
|
|
16959
|
-
controller.setTool(e.shiftKey || key2 === "J" ? controller.getState().activeTool === "heal" ? "spot-heal" : "heal" : "spot-heal");
|
|
16960
|
-
return true;
|
|
16961
|
-
case "o": {
|
|
16962
|
-
const cur = controller.getState().activeTool;
|
|
16963
|
-
const next = e.shiftKey || key2 === "O" ? cur === "dodge" ? "burn" : cur === "burn" ? "sponge" : "dodge" : "dodge";
|
|
16964
|
-
controller.setTool(next);
|
|
16965
|
-
return true;
|
|
16966
|
-
}
|
|
16967
|
-
case "y":
|
|
16968
|
-
controller.setTool("history");
|
|
16969
|
-
return true;
|
|
16970
|
-
case "d":
|
|
16971
|
-
controller.resetPaintColors();
|
|
16972
|
-
return true;
|
|
16973
|
-
case "x":
|
|
16974
|
-
controller.swapPaintColors();
|
|
16975
|
-
return true;
|
|
16976
|
-
}
|
|
16977
|
-
if (key2 === "[" || key2 === "{") {
|
|
16978
|
-
if (e.shiftKey || key2 === "{") set({ hardness: Math.max(0, Math.round((brush.hardness - 0.25) * 4) / 4) });
|
|
16979
|
-
else set({ size: Math.max(1, brush.size - sizeStep(brush.size - 1)) });
|
|
16980
|
-
return true;
|
|
16981
|
-
}
|
|
16982
|
-
if (key2 === "]" || key2 === "}") {
|
|
16983
|
-
if (e.shiftKey || key2 === "}") set({ hardness: Math.min(1, Math.round((brush.hardness + 0.25) * 4) / 4) });
|
|
16984
|
-
else set({ size: Math.min(MAX_BRUSH_SIZE, brush.size + sizeStep(brush.size)) });
|
|
16985
|
-
return true;
|
|
16986
|
-
}
|
|
16987
|
-
if (/^[0-9]$/.test(key2) && isPaintTool(controller.getState().activeTool)) {
|
|
16988
|
-
set({ opacity: key2 === "0" ? 1 : Number(key2) / 10 });
|
|
16989
|
-
return true;
|
|
16990
|
-
}
|
|
16991
|
-
return false;
|
|
16992
|
-
}
|
|
16993
|
-
var PaintGesture = class {
|
|
16994
|
-
constructor(controller) {
|
|
16995
|
-
this.controller = controller;
|
|
16996
|
-
}
|
|
16997
|
-
controller;
|
|
16998
|
-
stroke = null;
|
|
16999
|
-
move = null;
|
|
17000
|
-
/** The last refusal (e.g. "layer is locked"), for the editor to show. */
|
|
17001
|
-
error = null;
|
|
17002
|
-
get active() {
|
|
17003
|
-
return Boolean(this.stroke || this.move);
|
|
17004
|
-
}
|
|
17005
|
-
down(sample) {
|
|
17006
|
-
this.error = null;
|
|
17007
|
-
const tool = this.controller.getState().activeTool;
|
|
17008
|
-
try {
|
|
17009
|
-
if (tool === "move") {
|
|
17010
|
-
const layer = this.controller.getActiveLayer();
|
|
17011
|
-
if (!layer) throw new Error("Select a layer to move in the Layers panel.");
|
|
17012
|
-
if (layer.type !== "pixel" && layer.type !== "shape" && layer.type !== "smart") throw new Error(`Only pixel, shape and smart object layers move; "${layer.name}" is a ${layer.type} layer.`);
|
|
17013
|
-
if (layer.locks?.all || layer.locks?.position) throw new Error(`Layer "${layer.name}" has its position locked.`);
|
|
17014
|
-
const origin = layer.type === "pixel" ? { ...layer.offset } : layer.type === "smart" ? { ...layer.placement.corners[0] } : (() => {
|
|
17015
|
-
const b = shapeGeometryBounds(layer);
|
|
17016
|
-
return b ? { x: b.x, y: b.y } : { x: 0, y: 0 };
|
|
17017
|
-
})();
|
|
17018
|
-
this.move = { id: layer.id, start: { x: sample.x, y: sample.y }, origin, moved: false };
|
|
17019
|
-
return null;
|
|
17020
|
-
}
|
|
17021
|
-
if ((tool === "clone" || tool === "heal") && sample.altKey) {
|
|
17022
|
-
this.controller.setCloneSource({ x: sample.x, y: sample.y });
|
|
17023
|
-
return null;
|
|
17024
|
-
}
|
|
17025
|
-
if (isPaintTool(tool)) {
|
|
17026
|
-
this.stroke = this.controller.beginStroke({ tool });
|
|
17027
|
-
this.stroke.push([{ x: sample.x, y: sample.y, pressure: sample.pressure }]);
|
|
17028
|
-
return null;
|
|
17029
|
-
}
|
|
17030
|
-
} catch (err) {
|
|
17031
|
-
this.error = userErrorMessage(err);
|
|
17032
|
-
this.stroke = null;
|
|
17033
|
-
this.move = null;
|
|
17034
|
-
}
|
|
17035
|
-
return this.error;
|
|
17036
|
-
}
|
|
17037
|
-
/** Feeds pointer samples (all coalesced events of one move, in order). */
|
|
17038
|
-
moveTo(samples) {
|
|
17039
|
-
if (!samples.length) return;
|
|
17040
|
-
if (this.stroke) {
|
|
17041
|
-
this.stroke.push(samples);
|
|
17042
|
-
return;
|
|
17043
|
-
}
|
|
17044
|
-
const m = this.move;
|
|
17045
|
-
if (m) {
|
|
17046
|
-
const last = samples[samples.length - 1];
|
|
17047
|
-
const x = m.origin.x + Math.round(last.x - m.start.x);
|
|
17048
|
-
const y = m.origin.y + Math.round(last.y - m.start.y);
|
|
17049
|
-
try {
|
|
17050
|
-
this.controller.offsetLayer(m.id, { x, y }, !m.moved);
|
|
17051
|
-
m.moved = true;
|
|
17052
|
-
} catch (err) {
|
|
17053
|
-
this.error = userErrorMessage(err);
|
|
17054
|
-
this.move = null;
|
|
17055
|
-
}
|
|
17056
|
-
}
|
|
17057
|
-
}
|
|
17058
|
-
up() {
|
|
17059
|
-
if (this.stroke) {
|
|
17060
|
-
try {
|
|
17061
|
-
this.stroke.end();
|
|
17062
|
-
} catch (err) {
|
|
17063
|
-
this.error = userErrorMessage(err);
|
|
17064
|
-
}
|
|
17065
|
-
}
|
|
17066
|
-
this.stroke = null;
|
|
17067
|
-
this.move = null;
|
|
17068
|
-
}
|
|
17069
|
-
cancel() {
|
|
17070
|
-
this.stroke?.cancel();
|
|
17071
|
-
this.stroke = null;
|
|
17072
|
-
this.move = null;
|
|
17073
|
-
}
|
|
17074
|
-
};
|
|
17075
|
-
|
|
17076
15977
|
// src/image-editor/paint-tools.ts
|
|
17077
15978
|
var READ3 = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false };
|
|
17078
15979
|
var WRITE3 = { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false };
|