@codingfactory/mediables-vue 2.5.0 → 2.6.1
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/{PixiFrameExporter-BrsHCMHb.cjs → PixiFrameExporter-CetwQ3WI.cjs} +2 -2
- package/dist/{PixiFrameExporter-BrsHCMHb.cjs.map → PixiFrameExporter-CetwQ3WI.cjs.map} +1 -1
- package/dist/{PixiFrameExporter-DFq2mc-y.js → PixiFrameExporter-DFchWLl5.js} +2 -2
- package/dist/{PixiFrameExporter-DFq2mc-y.js.map → PixiFrameExporter-DFchWLl5.js.map} +1 -1
- package/dist/components/ImageEditor/ImageEditor.vue.d.ts +1 -0
- package/dist/components/ImageEditorModal.vue.d.ts +2 -0
- package/dist/components/MediaManagementView.vue.d.ts +2 -0
- package/dist/composables/useFloatingPills.d.ts +4 -0
- package/dist/composables/useMediaDeletion.d.ts +66 -0
- package/dist/composables/useMediaTrash.d.ts +64 -0
- package/dist/editor-BTwIhrcA.cjs +2 -0
- package/dist/editor-BTwIhrcA.cjs.map +1 -0
- package/dist/{editor-2Q72CZjK.js → editor-CYj5y5bp.js} +1417 -1167
- package/dist/editor-CYj5y5bp.js.map +1 -0
- package/dist/filters/registry.d.ts +4 -0
- package/dist/{index-CaQc9GBh.js → index-DNgtg1bF.js} +12107 -10230
- package/dist/index-DNgtg1bF.js.map +1 -0
- package/dist/index-nQskCfGV.cjs +342 -0
- package/dist/index-nQskCfGV.cjs.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/mediables-vanilla.cjs +1 -1
- package/dist/mediables-vanilla.mjs +1 -1
- package/dist/mediables-vue.cjs +1 -1
- package/dist/mediables-vue.mjs +69 -66
- package/dist/render-page/assets/{index-ZZVWF3LA.js → index-y90zwXpc.js} +713 -531
- package/dist/render-page/index.html +1 -1
- package/dist/style.css +1 -1
- package/dist/types/media.d.ts +26 -0
- package/dist/types/mediaLibraryPicker.d.ts +1 -0
- package/package.json +2 -2
- package/dist/editor-2Q72CZjK.js.map +0 -1
- package/dist/editor-DjvxEsss.cjs +0 -42
- package/dist/editor-DjvxEsss.cjs.map +0 -1
- package/dist/index-BTGCVCn6.cjs +0 -342
- package/dist/index-BTGCVCn6.cjs.map +0 -1
- package/dist/index-CaQc9GBh.js.map +0 -1
|
@@ -63773,7 +63773,7 @@ __publicField$1(_TwistFilter, "DEFAULT_OPTIONS", {
|
|
|
63773
63773
|
angle: 4,
|
|
63774
63774
|
offset: { x: 0, y: 0 }
|
|
63775
63775
|
});
|
|
63776
|
-
let TwistFilter = _TwistFilter;
|
|
63776
|
+
let TwistFilter$1 = _TwistFilter;
|
|
63777
63777
|
var fragment = "precision highp float;\nin vec2 vTextureCoord;\nout vec4 finalColor;\n\nuniform sampler2D uTexture;\nuniform float uStrength;\nuniform vec2 uCenter;\nuniform vec2 uRadii;\n\nuniform vec4 uInputSize;\n\nconst float MAX_KERNEL_SIZE = ${MAX_KERNEL_SIZE};\n\n// author: http://byteblacksmith.com/improvements-to-the-canonical-one-liner-glsl-rand-for-opengl-es-2-0/\nhighp float rand(vec2 co, float seed) {\n const highp float a = 12.9898, b = 78.233, c = 43758.5453;\n highp float dt = dot(co + seed, vec2(a, b)), sn = mod(dt, 3.14159);\n return fract(sin(sn) * c + seed);\n}\n\nvoid main() {\n float minGradient = uRadii[0] * 0.3;\n float innerRadius = (uRadii[0] + minGradient * 0.5) / uInputSize.x;\n\n float gradient = uRadii[1] * 0.3;\n float radius = (uRadii[1] - gradient * 0.5) / uInputSize.x;\n\n float countLimit = MAX_KERNEL_SIZE;\n\n vec2 dir = vec2(uCenter.xy / uInputSize.xy - vTextureCoord);\n float dist = length(vec2(dir.x, dir.y * uInputSize.y / uInputSize.x));\n\n float strength = uStrength;\n\n float delta = 0.0;\n float gap;\n if (dist < innerRadius) {\n delta = innerRadius - dist;\n gap = minGradient;\n } else if (radius >= 0.0 && dist > radius) { // radius < 0 means it's infinity\n delta = dist - radius;\n gap = gradient;\n }\n\n if (delta > 0.0) {\n float normalCount = gap / uInputSize.x;\n delta = (normalCount - delta) / normalCount;\n countLimit *= delta;\n strength *= delta;\n if (countLimit < 1.0)\n {\n gl_FragColor = texture(uTexture, vTextureCoord);\n return;\n }\n }\n\n // randomize the lookup values to hide the fixed number of samples\n float offset = rand(vTextureCoord, 0.0);\n\n float total = 0.0;\n vec4 color = vec4(0.0);\n\n dir *= strength;\n\n for (float t = 0.0; t < MAX_KERNEL_SIZE; t++) {\n float percent = (t + offset) / MAX_KERNEL_SIZE;\n float weight = 4.0 * (percent - percent * percent);\n vec2 p = vTextureCoord + dir * percent;\n vec4 sample = texture(uTexture, p);\n\n // switch to pre-multiplied alpha to correctly blur transparent images\n // sample.rgb *= sample.a;\n\n color += sample * weight;\n total += weight;\n\n if (t > countLimit){\n break;\n }\n }\n\n color /= total;\n // switch back from pre-multiplied alpha\n // color.rgb /= color.a + 0.00001;\n\n gl_FragColor = color;\n}\n";
|
|
63778
63778
|
var source = "struct ZoomBlurUniforms {\n uStrength:f32,\n uCenter:vec2<f32>,\n uRadii:vec2<f32>,\n};\n\nstruct GlobalFilterUniforms {\n uInputSize:vec4<f32>,\n uInputPixel:vec4<f32>,\n uInputClamp:vec4<f32>,\n uOutputFrame:vec4<f32>,\n uGlobalFrame:vec4<f32>,\n uOutputTexture:vec4<f32>,\n};\n\n@group(0) @binding(0) var<uniform> gfu: GlobalFilterUniforms;\n\n@group(0) @binding(1) var uTexture: texture_2d<f32>; \n@group(0) @binding(2) var uSampler: sampler;\n@group(1) @binding(0) var<uniform> zoomBlurUniforms : ZoomBlurUniforms;\n\n@fragment\nfn mainFragment(\n @builtin(position) position: vec4<f32>,\n @location(0) uv : vec2<f32>\n) -> @location(0) vec4<f32> {\n let uStrength = zoomBlurUniforms.uStrength;\n let uCenter = zoomBlurUniforms.uCenter;\n let uRadii = zoomBlurUniforms.uRadii;\n\n let minGradient: f32 = uRadii[0] * 0.3;\n let innerRadius: f32 = (uRadii[0] + minGradient * 0.5) / gfu.uInputSize.x;\n\n let gradient: f32 = uRadii[1] * 0.3;\n let radius: f32 = (uRadii[1] - gradient * 0.5) / gfu.uInputSize.x;\n\n let MAX_KERNEL_SIZE: f32 = ${MAX_KERNEL_SIZE};\n\n var countLimit: f32 = MAX_KERNEL_SIZE;\n\n var dir: vec2<f32> = vec2<f32>(uCenter / gfu.uInputSize.xy - uv);\n let dist: f32 = length(vec2<f32>(dir.x, dir.y * gfu.uInputSize.y / gfu.uInputSize.x));\n\n var strength: f32 = uStrength;\n\n var delta: f32 = 0.0;\n var gap: f32;\n\n if (dist < innerRadius) {\n delta = innerRadius - dist;\n gap = minGradient;\n } else if (radius >= 0.0 && dist > radius) { // radius < 0 means it's infinity\n delta = dist - radius;\n gap = gradient;\n }\n\n var returnColorOnly: bool = false;\n\n if (delta > 0.0) {\n let normalCount: f32 = gap / gfu.uInputSize.x;\n delta = (normalCount - delta) / normalCount;\n countLimit *= delta;\n strength *= delta;\n \n if (countLimit < 1.0)\n {\n returnColorOnly = true;;\n }\n }\n\n // randomize the lookup values to hide the fixed number of samples\n let offset: f32 = rand(uv, 0.0);\n\n var total: f32 = 0.0;\n var color: vec4<f32> = vec4<f32>(0.);\n\n dir *= strength;\n\n for (var t = 0.0; t < MAX_KERNEL_SIZE; t += 1.0) {\n let percent: f32 = (t + offset) / MAX_KERNEL_SIZE;\n let weight: f32 = 4.0 * (percent - percent * percent);\n let p: vec2<f32> = uv + dir * percent;\n let sample: vec4<f32> = textureSample(uTexture, uSampler, p);\n \n if (t < countLimit)\n {\n color += sample * weight;\n total += weight;\n }\n }\n\n color /= total;\n\n return select(color, textureSample(uTexture, uSampler, uv), returnColorOnly);\n}\n\nfn modulo(x: f32, y: f32) -> f32\n{\n return x - y * floor(x/y);\n}\n\n// author: http://byteblacksmith.com/improvements-to-the-canonical-one-liner-glsl-rand-for-opengl-es-2-0/\nfn rand(co: vec2<f32>, seed: f32) -> f32\n{\n let a: f32 = 12.9898;\n let b: f32 = 78.233;\n let c: f32 = 43758.5453;\n let dt: f32 = dot(co + seed, vec2<f32>(a, b));\n let sn: f32 = modulo(dt, 3.14159);\n return fract(sin(sn) * c + seed);\n}";
|
|
63779
63779
|
var __defProp = Object.defineProperty;
|
|
@@ -63929,7 +63929,7 @@ const PixiFilters = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineP
|
|
|
63929
63929
|
SimplexNoiseFilter: SimplexNoiseFilter$1,
|
|
63930
63930
|
TiltShiftAxisFilter,
|
|
63931
63931
|
TiltShiftFilter: TiltShiftFilter$1,
|
|
63932
|
-
TwistFilter,
|
|
63932
|
+
TwistFilter: TwistFilter$1,
|
|
63933
63933
|
ZoomBlurFilter: ZoomBlurFilter$1,
|
|
63934
63934
|
angleFromCssOrientation,
|
|
63935
63935
|
angleFromDirectionalValue,
|
|
@@ -64472,60 +64472,30 @@ registerFilter({
|
|
|
64472
64472
|
}
|
|
64473
64473
|
]
|
|
64474
64474
|
});
|
|
64475
|
+
const clamp01 = (n2) => Math.max(0, Math.min(1, n2));
|
|
64475
64476
|
registerFilter({
|
|
64476
64477
|
id: "alpha",
|
|
64477
64478
|
name: "Alpha",
|
|
64478
64479
|
category: "adjust",
|
|
64479
|
-
description: "Adjust the
|
|
64480
|
-
// Create an instance of the ColorMatrixFilter with alpha adjustment
|
|
64480
|
+
description: "Adjust the opacity of the image",
|
|
64481
64481
|
createFilter: (params) => {
|
|
64482
|
-
|
|
64483
|
-
|
|
64484
|
-
|
|
64485
|
-
|
|
64486
|
-
|
|
64487
|
-
|
|
64488
|
-
|
|
64489
|
-
|
|
64490
|
-
filter.updateUIParam = function(key, value) {
|
|
64491
|
-
try {
|
|
64492
|
-
console.log(`Alpha filter updateUIParam called: ${key} = ${value}`);
|
|
64493
|
-
const customParams = this._customParams || {};
|
|
64494
|
-
this._customParams = customParams;
|
|
64495
|
-
if (key === "alpha") {
|
|
64496
|
-
const numValue = Number(value);
|
|
64497
|
-
this.alpha = numValue;
|
|
64498
|
-
customParams.alpha = numValue;
|
|
64499
|
-
console.log(`Updated alpha to ${numValue}`);
|
|
64500
|
-
} else {
|
|
64501
|
-
if (key in this) {
|
|
64502
|
-
this[key] = value;
|
|
64503
|
-
console.log(`Updated ${key} to ${value}`);
|
|
64504
|
-
} else {
|
|
64505
|
-
console.warn(`Unknown parameter for Alpha filter: ${key}`);
|
|
64506
|
-
}
|
|
64507
|
-
}
|
|
64508
|
-
} catch (error) {
|
|
64509
|
-
console.error(`Error updating Alpha filter parameter ${key}:`, error);
|
|
64510
|
-
}
|
|
64511
|
-
};
|
|
64512
|
-
console.log("Alpha filter created successfully");
|
|
64513
|
-
return filter;
|
|
64514
|
-
} catch (error) {
|
|
64515
|
-
console.error("Failed to create Alpha filter:", error);
|
|
64516
|
-
return null;
|
|
64517
|
-
}
|
|
64482
|
+
const alpha = clamp01(params.alpha !== void 0 ? Number(params.alpha) : 1);
|
|
64483
|
+
const filter = new AlphaFilter(alpha);
|
|
64484
|
+
filter.updateUIParam = function(key, value) {
|
|
64485
|
+
if (key === "alpha") {
|
|
64486
|
+
this.alpha = clamp01(Number(value));
|
|
64487
|
+
}
|
|
64488
|
+
};
|
|
64489
|
+
return filter;
|
|
64518
64490
|
},
|
|
64519
|
-
// Default parameter values
|
|
64520
64491
|
defaultParams: {
|
|
64521
64492
|
alpha: 1
|
|
64522
64493
|
},
|
|
64523
|
-
// UI controls for the filter
|
|
64524
64494
|
controls: [
|
|
64525
64495
|
{
|
|
64526
64496
|
id: "alpha",
|
|
64527
64497
|
type: "slider",
|
|
64528
|
-
label: "
|
|
64498
|
+
label: "Opacity",
|
|
64529
64499
|
property: "alpha",
|
|
64530
64500
|
min: 0,
|
|
64531
64501
|
max: 1,
|
|
@@ -64611,57 +64581,80 @@ registerFilter({
|
|
|
64611
64581
|
}
|
|
64612
64582
|
]
|
|
64613
64583
|
});
|
|
64584
|
+
function applyColorMatrixParams(filter, params) {
|
|
64585
|
+
if (typeof filter.reset === "function") {
|
|
64586
|
+
filter.reset();
|
|
64587
|
+
}
|
|
64588
|
+
if (params.brightness !== 1) {
|
|
64589
|
+
filter.brightness(params.brightness, false);
|
|
64590
|
+
}
|
|
64591
|
+
if (params.contrast !== 1) {
|
|
64592
|
+
filter.contrast(params.contrast, false);
|
|
64593
|
+
}
|
|
64594
|
+
if (params.saturation !== 1) {
|
|
64595
|
+
filter.saturate(params.saturation, false);
|
|
64596
|
+
}
|
|
64597
|
+
if (params.hue !== 0) {
|
|
64598
|
+
filter.hue(params.hue, false);
|
|
64599
|
+
}
|
|
64600
|
+
if (params.sepia) {
|
|
64601
|
+
filter.sepia(false);
|
|
64602
|
+
}
|
|
64603
|
+
if (params.negative) {
|
|
64604
|
+
filter.negative(false);
|
|
64605
|
+
}
|
|
64606
|
+
}
|
|
64607
|
+
function normalizeParams(raw) {
|
|
64608
|
+
return {
|
|
64609
|
+
brightness: Number(raw.brightness ?? 1),
|
|
64610
|
+
contrast: Number(raw.contrast ?? 1),
|
|
64611
|
+
saturation: Number(raw.saturation ?? 1),
|
|
64612
|
+
hue: Number(raw.hue ?? 0),
|
|
64613
|
+
sepia: Boolean(raw.sepia),
|
|
64614
|
+
negative: Boolean(raw.negative)
|
|
64615
|
+
};
|
|
64616
|
+
}
|
|
64614
64617
|
registerFilter({
|
|
64615
64618
|
id: "color-matrix",
|
|
64616
64619
|
name: "Color Matrix",
|
|
64617
64620
|
category: "advanced",
|
|
64618
|
-
description: "Advanced color adjustments including sepia, hue rotation, and
|
|
64619
|
-
// Create an instance of the ColorMatrixFilter with the provided parameters
|
|
64621
|
+
description: "Advanced color adjustments including sepia, hue rotation, and negative",
|
|
64620
64622
|
createFilter: (params) => {
|
|
64621
64623
|
const MatrixFilterClass = ColorMatrixFilter$3;
|
|
64622
|
-
if (!MatrixFilterClass)
|
|
64623
|
-
console.error("ColorMatrixFilter not available in PIXI");
|
|
64624
|
-
return null;
|
|
64625
|
-
}
|
|
64624
|
+
if (!MatrixFilterClass) return null;
|
|
64626
64625
|
const filter = new MatrixFilterClass();
|
|
64627
|
-
|
|
64628
|
-
|
|
64629
|
-
|
|
64630
|
-
|
|
64631
|
-
|
|
64632
|
-
|
|
64633
|
-
|
|
64634
|
-
|
|
64635
|
-
|
|
64636
|
-
|
|
64637
|
-
|
|
64638
|
-
|
|
64639
|
-
|
|
64640
|
-
|
|
64641
|
-
|
|
64642
|
-
|
|
64643
|
-
|
|
64644
|
-
|
|
64645
|
-
}
|
|
64646
|
-
if (params.negative && typeof filter.negative === "function") {
|
|
64647
|
-
filter.negative(false);
|
|
64626
|
+
const normalized = normalizeParams(params);
|
|
64627
|
+
filter._customParams = { ...normalized };
|
|
64628
|
+
applyColorMatrixParams(filter, normalized);
|
|
64629
|
+
filter.updateUIParam = function(key, value) {
|
|
64630
|
+
const stored = this._customParams ?? normalizeParams({});
|
|
64631
|
+
switch (key) {
|
|
64632
|
+
case "brightness":
|
|
64633
|
+
case "contrast":
|
|
64634
|
+
case "saturation":
|
|
64635
|
+
case "hue":
|
|
64636
|
+
stored[key] = Number(value);
|
|
64637
|
+
break;
|
|
64638
|
+
case "sepia":
|
|
64639
|
+
case "negative":
|
|
64640
|
+
stored[key] = Boolean(value);
|
|
64641
|
+
break;
|
|
64642
|
+
default:
|
|
64643
|
+
return;
|
|
64648
64644
|
}
|
|
64649
|
-
|
|
64650
|
-
|
|
64651
|
-
|
|
64652
|
-
|
|
64653
|
-
}
|
|
64645
|
+
this._customParams = stored;
|
|
64646
|
+
applyColorMatrixParams(this, stored);
|
|
64647
|
+
};
|
|
64648
|
+
return filter;
|
|
64654
64649
|
},
|
|
64655
|
-
// Default parameter values
|
|
64656
64650
|
defaultParams: {
|
|
64657
64651
|
brightness: 1,
|
|
64658
64652
|
contrast: 1,
|
|
64659
64653
|
saturation: 1,
|
|
64660
64654
|
hue: 0,
|
|
64661
|
-
sepia:
|
|
64655
|
+
sepia: false,
|
|
64662
64656
|
negative: false
|
|
64663
64657
|
},
|
|
64664
|
-
// UI controls for the filter
|
|
64665
64658
|
controls: [
|
|
64666
64659
|
{
|
|
64667
64660
|
id: "brightness",
|
|
@@ -64703,15 +64696,14 @@ registerFilter({
|
|
|
64703
64696
|
step: 1,
|
|
64704
64697
|
default: 0
|
|
64705
64698
|
},
|
|
64699
|
+
// Sepia is a toggle in phase 1 — PIXI v8's ColorMatrixFilter.sepia()
|
|
64700
|
+
// doesn't accept a smooth intensity, so a slider here would be misleading.
|
|
64706
64701
|
{
|
|
64707
64702
|
id: "sepia",
|
|
64708
|
-
type: "
|
|
64703
|
+
type: "toggle",
|
|
64709
64704
|
label: "Sepia",
|
|
64710
64705
|
property: "sepia",
|
|
64711
|
-
|
|
64712
|
-
max: 1,
|
|
64713
|
-
step: 0.01,
|
|
64714
|
-
default: 0
|
|
64706
|
+
default: false
|
|
64715
64707
|
},
|
|
64716
64708
|
{
|
|
64717
64709
|
id: "negative",
|
|
@@ -64829,10 +64821,11 @@ registerFilter({
|
|
|
64829
64821
|
*/
|
|
64830
64822
|
createFilter: (params) => {
|
|
64831
64823
|
try {
|
|
64832
|
-
console.log("Creating Drop Shadow filter with params:", params);
|
|
64833
64824
|
const color = params.color ? params.color.replace("#", "0x") : "0x000000";
|
|
64834
64825
|
const distance = params.distance !== void 0 ? params.distance : 5;
|
|
64835
64826
|
const angle = params.angle !== void 0 ? params.angle : 90;
|
|
64827
|
+
const blur = params.blur !== void 0 ? params.blur : 2;
|
|
64828
|
+
const quality = params.quality !== void 0 ? params.quality : 3;
|
|
64836
64829
|
const offset = {
|
|
64837
64830
|
x: distance * Math.cos(angle * Math.PI / 180),
|
|
64838
64831
|
y: distance * Math.sin(angle * Math.PI / 180)
|
|
@@ -64841,14 +64834,21 @@ registerFilter({
|
|
|
64841
64834
|
offset,
|
|
64842
64835
|
color: parseInt(color, 16),
|
|
64843
64836
|
alpha: params.alpha !== void 0 ? params.alpha : 0.5,
|
|
64844
|
-
blur
|
|
64845
|
-
quality
|
|
64837
|
+
blur,
|
|
64838
|
+
quality,
|
|
64846
64839
|
shadowOnly: params.shadowOnly !== void 0 ? params.shadowOnly : false,
|
|
64847
64840
|
pixelSize: {
|
|
64848
64841
|
x: params.pixelSizeX !== void 0 ? params.pixelSizeX : 1,
|
|
64849
64842
|
y: params.pixelSizeY !== void 0 ? params.pixelSizeY : 1
|
|
64850
64843
|
}
|
|
64851
64844
|
});
|
|
64845
|
+
const computePadding = (b2, q, offX, offY) => {
|
|
64846
|
+
const kernel = b2 * q * 2;
|
|
64847
|
+
const mag = Math.sqrt(offX * offX + offY * offY);
|
|
64848
|
+
return Math.ceil(kernel + mag + 2);
|
|
64849
|
+
};
|
|
64850
|
+
filter._exportPadding = computePadding(blur, quality, offset.x, offset.y);
|
|
64851
|
+
filter.padding = Math.max(filter.padding ?? 0, filter._exportPadding);
|
|
64852
64852
|
filter._customParams = {
|
|
64853
64853
|
...params,
|
|
64854
64854
|
// Also store the calculated offset for use in updateUIParam
|
|
@@ -64856,6 +64856,50 @@ registerFilter({
|
|
|
64856
64856
|
_distance: distance,
|
|
64857
64857
|
_angle: angle
|
|
64858
64858
|
};
|
|
64859
|
+
const createScaledForExport = (rawParams, scale) => {
|
|
64860
|
+
const dist = Number(rawParams._distance ?? rawParams.distance ?? 5) * scale;
|
|
64861
|
+
const ang = Number(rawParams._angle ?? rawParams.angle ?? 90);
|
|
64862
|
+
const blr = Number(rawParams.blur ?? 2) * scale;
|
|
64863
|
+
const qual = Number(rawParams.quality ?? 3);
|
|
64864
|
+
const off = {
|
|
64865
|
+
x: dist * Math.cos(ang * Math.PI / 180),
|
|
64866
|
+
y: dist * Math.sin(ang * Math.PI / 180)
|
|
64867
|
+
};
|
|
64868
|
+
const colorStr = rawParams.color ? String(rawParams.color).replace("#", "0x") : "0x000000";
|
|
64869
|
+
const next = new DropShadowFilter({
|
|
64870
|
+
offset: off,
|
|
64871
|
+
color: parseInt(colorStr, 16),
|
|
64872
|
+
alpha: rawParams.alpha !== void 0 ? Number(rawParams.alpha) : 0.5,
|
|
64873
|
+
blur: blr,
|
|
64874
|
+
quality: qual,
|
|
64875
|
+
shadowOnly: rawParams.shadowOnly !== void 0 ? Boolean(rawParams.shadowOnly) : false,
|
|
64876
|
+
pixelSize: {
|
|
64877
|
+
x: rawParams.pixelSizeX !== void 0 ? Number(rawParams.pixelSizeX) : 1,
|
|
64878
|
+
y: rawParams.pixelSizeY !== void 0 ? Number(rawParams.pixelSizeY) : 1
|
|
64879
|
+
}
|
|
64880
|
+
});
|
|
64881
|
+
const padding = computePadding(blr, qual, off.x, off.y);
|
|
64882
|
+
next._exportPadding = padding;
|
|
64883
|
+
next.padding = Math.max(next.padding ?? 0, padding);
|
|
64884
|
+
return next;
|
|
64885
|
+
};
|
|
64886
|
+
filter.createExportFilter = function(options = {}) {
|
|
64887
|
+
const scale = Number.isFinite(options.previewToNativeScale) ? Math.max(1, Number(options.previewToNativeScale)) : 1;
|
|
64888
|
+
const stored = this._customParams || params;
|
|
64889
|
+
return createScaledForExport(stored, scale);
|
|
64890
|
+
};
|
|
64891
|
+
filter.getExportPadding = function() {
|
|
64892
|
+
return Number(this._exportPadding || this.padding || 0);
|
|
64893
|
+
};
|
|
64894
|
+
const refreshPadding = (f2) => {
|
|
64895
|
+
var _a;
|
|
64896
|
+
const off = ((_a = f2._customParams) == null ? void 0 : _a._offset) ?? { x: 0, y: 0 };
|
|
64897
|
+
const b2 = Number(f2.blur ?? 0);
|
|
64898
|
+
const q = Number(f2.quality ?? 1);
|
|
64899
|
+
const p2 = computePadding(b2, q, off.x, off.y);
|
|
64900
|
+
f2._exportPadding = p2;
|
|
64901
|
+
f2.padding = Math.max(f2.padding ?? 0, p2);
|
|
64902
|
+
};
|
|
64859
64903
|
filter.updateUIParam = function(key, value) {
|
|
64860
64904
|
try {
|
|
64861
64905
|
const customParams = this._customParams || {};
|
|
@@ -64864,43 +64908,39 @@ registerFilter({
|
|
|
64864
64908
|
switch (key) {
|
|
64865
64909
|
case "color":
|
|
64866
64910
|
if (typeof value === "string") {
|
|
64867
|
-
|
|
64868
|
-
this.color = colorNum;
|
|
64869
|
-
console.log(`Updated color to ${value} (${colorNum})`);
|
|
64911
|
+
this.color = parseInt(value.replace("#", "0x"), 16);
|
|
64870
64912
|
}
|
|
64871
64913
|
break;
|
|
64872
64914
|
case "alpha":
|
|
64873
64915
|
this.alpha = Number(value);
|
|
64874
|
-
console.log(`Updated alpha to ${value}`);
|
|
64875
64916
|
break;
|
|
64876
64917
|
case "blur":
|
|
64877
64918
|
this.blur = Number(value);
|
|
64878
|
-
|
|
64919
|
+
refreshPadding(this);
|
|
64879
64920
|
break;
|
|
64880
64921
|
case "quality":
|
|
64881
64922
|
this.quality = Number(value);
|
|
64882
|
-
|
|
64923
|
+
refreshPadding(this);
|
|
64883
64924
|
break;
|
|
64884
64925
|
case "shadowOnly":
|
|
64885
64926
|
this.shadowOnly = Boolean(value);
|
|
64886
|
-
console.log(`Updated shadowOnly to ${value}`);
|
|
64887
64927
|
break;
|
|
64888
64928
|
case "pixelSizeX":
|
|
64889
64929
|
this.pixelSizeX = Number(value);
|
|
64890
64930
|
customParams.pixelSizeX = Number(value);
|
|
64891
|
-
console.log(`Updated pixelSizeX to ${value}`);
|
|
64892
64931
|
break;
|
|
64893
64932
|
case "pixelSizeY":
|
|
64894
64933
|
this.pixelSizeY = Number(value);
|
|
64895
64934
|
customParams.pixelSizeY = Number(value);
|
|
64896
|
-
console.log(`Updated pixelSizeY to ${value}`);
|
|
64897
64935
|
break;
|
|
64898
64936
|
case "distance":
|
|
64899
|
-
case "angle":
|
|
64937
|
+
case "angle": {
|
|
64900
64938
|
if (key === "distance") {
|
|
64901
64939
|
customParams._distance = Number(value);
|
|
64940
|
+
customParams.distance = Number(value);
|
|
64902
64941
|
} else {
|
|
64903
64942
|
customParams._angle = Number(value);
|
|
64943
|
+
customParams.angle = Number(value);
|
|
64904
64944
|
}
|
|
64905
64945
|
const newOffset = {
|
|
64906
64946
|
x: customParams._distance * Math.cos(customParams._angle * Math.PI / 180),
|
|
@@ -64908,27 +64948,23 @@ registerFilter({
|
|
|
64908
64948
|
};
|
|
64909
64949
|
customParams._offset = newOffset;
|
|
64910
64950
|
this.offset = newOffset;
|
|
64911
|
-
|
|
64951
|
+
refreshPadding(this);
|
|
64912
64952
|
break;
|
|
64953
|
+
}
|
|
64913
64954
|
default:
|
|
64914
64955
|
if (key in this) {
|
|
64915
64956
|
this[key] = value;
|
|
64916
|
-
console.log(`Updated ${key} to ${value}`);
|
|
64917
|
-
} else {
|
|
64918
|
-
console.warn(`Attempted to update unknown property ${key}`);
|
|
64919
64957
|
}
|
|
64920
64958
|
break;
|
|
64921
64959
|
}
|
|
64922
64960
|
return true;
|
|
64923
|
-
} catch
|
|
64924
|
-
console.error(`Failed to update parameter ${key} using updateUIParam:`, error);
|
|
64961
|
+
} catch {
|
|
64925
64962
|
if (this._customParams) {
|
|
64926
64963
|
this._customParams[key] = value;
|
|
64927
64964
|
}
|
|
64928
64965
|
return false;
|
|
64929
64966
|
}
|
|
64930
64967
|
};
|
|
64931
|
-
console.log("Drop Shadow filter created successfully");
|
|
64932
64968
|
return filter;
|
|
64933
64969
|
} catch (error) {
|
|
64934
64970
|
console.error("Failed to create Drop Shadow filter:", error);
|
|
@@ -65955,261 +65991,200 @@ registerFilter({
|
|
|
65955
65991
|
]
|
|
65956
65992
|
});
|
|
65957
65993
|
const { ColorGradientFilter } = PixiFilters;
|
|
65994
|
+
function hexToInt(hex) {
|
|
65995
|
+
return parseInt(hex.replace("#", "0x"), 16);
|
|
65996
|
+
}
|
|
65997
|
+
function intToHex(num) {
|
|
65998
|
+
return "#" + num.toString(16).padStart(6, "0");
|
|
65999
|
+
}
|
|
66000
|
+
function normalizeStopForFilter(stop) {
|
|
66001
|
+
return {
|
|
66002
|
+
offset: Number(stop.offset),
|
|
66003
|
+
color: typeof stop.color === "string" ? hexToInt(stop.color) : Number(stop.color),
|
|
66004
|
+
alpha: Number(stop.alpha)
|
|
66005
|
+
};
|
|
66006
|
+
}
|
|
66007
|
+
function hexStopFromFilter(stop) {
|
|
66008
|
+
return {
|
|
66009
|
+
offset: stop.offset,
|
|
66010
|
+
color: typeof stop.color === "number" ? intToHex(stop.color) : stop.color,
|
|
66011
|
+
alpha: stop.alpha
|
|
66012
|
+
};
|
|
66013
|
+
}
|
|
65958
66014
|
registerFilter({
|
|
65959
66015
|
id: "color-gradient",
|
|
65960
66016
|
name: "Color Gradient",
|
|
65961
66017
|
category: "color",
|
|
65962
66018
|
description: "Applies a linear, radial or conic color gradient over the image with multiple color stops",
|
|
65963
|
-
/**
|
|
65964
|
-
* Create an instance of the ColorGradientFilter with the provided parameters
|
|
65965
|
-
* This implementation supports unlimited color stops like the PixiJS example app
|
|
65966
|
-
*/
|
|
65967
66019
|
createFilter: (params) => {
|
|
65968
|
-
|
|
65969
|
-
|
|
65970
|
-
|
|
65971
|
-
|
|
65972
|
-
|
|
65973
|
-
|
|
65974
|
-
|
|
65975
|
-
offset:
|
|
65976
|
-
|
|
65977
|
-
|
|
65978
|
-
|
|
65979
|
-
|
|
65980
|
-
|
|
65981
|
-
|
|
65982
|
-
|
|
65983
|
-
|
|
65984
|
-
|
|
65985
|
-
|
|
65986
|
-
|
|
65987
|
-
|
|
65988
|
-
|
|
65989
|
-
|
|
65990
|
-
|
|
65991
|
-
|
|
65992
|
-
|
|
65993
|
-
|
|
65994
|
-
|
|
65995
|
-
|
|
65996
|
-
|
|
65997
|
-
|
|
65998
|
-
|
|
65999
|
-
|
|
66000
|
-
|
|
66001
|
-
|
|
66002
|
-
});
|
|
66003
|
-
if (!this.stops || !Array.isArray(this.stops)) {
|
|
66004
|
-
console.error("FILTER getColorStopsForUI - No stops array found on filter instance!");
|
|
66005
|
-
return [];
|
|
66006
|
-
}
|
|
66007
|
-
const result = this.stops.map((stop) => ({
|
|
66008
|
-
offset: stop.offset,
|
|
66009
|
-
color: typeof stop.color === "number" ? "#" + stop.color.toString(16).padStart(6, "0") : stop.color,
|
|
66010
|
-
alpha: stop.alpha
|
|
66011
|
-
}));
|
|
66012
|
-
console.log("FILTER getColorStopsForUI - Returning formatted stops:", result);
|
|
66013
|
-
return result;
|
|
66020
|
+
const incomingStops = params.colorStops || [
|
|
66021
|
+
{ offset: 0, color: "#ff0000", alpha: 1 },
|
|
66022
|
+
{ offset: 1, color: "#0000ff", alpha: 1 }
|
|
66023
|
+
];
|
|
66024
|
+
let stops = incomingStops.map(normalizeStopForFilter);
|
|
66025
|
+
if (stops.length < 2) {
|
|
66026
|
+
stops = [
|
|
66027
|
+
{ offset: 0, color: 16711680, alpha: 1 },
|
|
66028
|
+
{ offset: 1, color: 255, alpha: 1 }
|
|
66029
|
+
];
|
|
66030
|
+
}
|
|
66031
|
+
stops.sort((a2, b2) => a2.offset - b2.offset);
|
|
66032
|
+
const filter = new ColorGradientFilter({
|
|
66033
|
+
type: params.gradientType,
|
|
66034
|
+
stops,
|
|
66035
|
+
angle: params.angle,
|
|
66036
|
+
alpha: params.alpha,
|
|
66037
|
+
maxColors: params.maxColors || 0,
|
|
66038
|
+
replace: params.replace
|
|
66039
|
+
});
|
|
66040
|
+
filter._customParams = {
|
|
66041
|
+
cssGradient: params.cssGradient || ""
|
|
66042
|
+
};
|
|
66043
|
+
filter.getSerializableParams = function() {
|
|
66044
|
+
var _a;
|
|
66045
|
+
const stopsHex = Array.isArray(this.stops) ? this.stops.map(hexStopFromFilter) : [];
|
|
66046
|
+
return {
|
|
66047
|
+
gradientType: this.type,
|
|
66048
|
+
colorStops: stopsHex,
|
|
66049
|
+
angle: this.angle,
|
|
66050
|
+
alpha: this.alpha,
|
|
66051
|
+
maxColors: this.maxColors,
|
|
66052
|
+
replace: this.replace,
|
|
66053
|
+
cssGradient: ((_a = this._customParams) == null ? void 0 : _a.cssGradient) ?? ""
|
|
66014
66054
|
};
|
|
66015
|
-
|
|
66016
|
-
|
|
66017
|
-
|
|
66018
|
-
|
|
66019
|
-
|
|
66020
|
-
|
|
66021
|
-
|
|
66022
|
-
|
|
66023
|
-
|
|
66024
|
-
|
|
66025
|
-
|
|
66026
|
-
|
|
66027
|
-
|
|
66028
|
-
}
|
|
66029
|
-
|
|
66030
|
-
id: `colorStop-${index}-offset`,
|
|
66031
|
-
type: "slider",
|
|
66032
|
-
label: `Stop ${index + 1} Position`,
|
|
66033
|
-
property: `colorStops[${index}].offset`,
|
|
66034
|
-
min: 0,
|
|
66035
|
-
max: 1,
|
|
66036
|
-
step: 0.01,
|
|
66037
|
-
default: stop.offset
|
|
66038
|
-
});
|
|
66039
|
-
controls.push({
|
|
66040
|
-
id: `colorStop-${index}-alpha`,
|
|
66041
|
-
type: "slider",
|
|
66042
|
-
label: `Stop ${index + 1} Alpha`,
|
|
66043
|
-
property: `colorStops[${index}].alpha`,
|
|
66044
|
-
min: 0,
|
|
66045
|
-
max: 1,
|
|
66046
|
-
step: 0.01,
|
|
66047
|
-
default: stop.alpha
|
|
66048
|
-
});
|
|
66055
|
+
};
|
|
66056
|
+
filter.getColorStopsForUI = function() {
|
|
66057
|
+
if (!Array.isArray(this.stops)) return [];
|
|
66058
|
+
return this.stops.map(hexStopFromFilter);
|
|
66059
|
+
};
|
|
66060
|
+
filter.getDynamicControls = function() {
|
|
66061
|
+
const colorStops = this.getColorStopsForUI();
|
|
66062
|
+
const controls = [];
|
|
66063
|
+
colorStops.forEach((stop, index) => {
|
|
66064
|
+
controls.push({
|
|
66065
|
+
id: `colorStop-${index}-color`,
|
|
66066
|
+
type: "color",
|
|
66067
|
+
label: `Stop ${index + 1} Color`,
|
|
66068
|
+
property: `colorStops[${index}].color`,
|
|
66069
|
+
default: stop.color
|
|
66049
66070
|
});
|
|
66050
|
-
|
|
66051
|
-
|
|
66052
|
-
|
|
66053
|
-
|
|
66054
|
-
|
|
66055
|
-
|
|
66056
|
-
|
|
66057
|
-
|
|
66058
|
-
|
|
66059
|
-
|
|
66060
|
-
|
|
66061
|
-
|
|
66062
|
-
|
|
66063
|
-
|
|
66064
|
-
|
|
66065
|
-
|
|
66066
|
-
|
|
66067
|
-
|
|
66068
|
-
|
|
66069
|
-
|
|
66070
|
-
|
|
66071
|
-
|
|
66072
|
-
|
|
66073
|
-
|
|
66074
|
-
|
|
66075
|
-
|
|
66076
|
-
|
|
66077
|
-
|
|
66078
|
-
|
|
66079
|
-
|
|
66080
|
-
|
|
66081
|
-
|
|
66082
|
-
|
|
66083
|
-
|
|
66084
|
-
|
|
66085
|
-
|
|
66086
|
-
|
|
66087
|
-
|
|
66088
|
-
|
|
66089
|
-
|
|
66090
|
-
|
|
66091
|
-
|
|
66092
|
-
|
|
66093
|
-
|
|
66094
|
-
|
|
66095
|
-
|
|
66096
|
-
|
|
66097
|
-
|
|
66098
|
-
|
|
66099
|
-
|
|
66100
|
-
customParams.colorStops = this.stops.map((stop) => ({
|
|
66101
|
-
offset: stop.offset,
|
|
66102
|
-
color: typeof stop.color === "number" ? "#" + stop.color.toString(16).padStart(6, "0") : stop.color,
|
|
66103
|
-
alpha: stop.alpha
|
|
66104
|
-
}));
|
|
66105
|
-
console.log("Added new color stop, total stops:", this.stops.length, "Updated colorStops:", customParams.colorStops);
|
|
66106
|
-
break;
|
|
66107
|
-
case "removeColorStop":
|
|
66108
|
-
console.log("Remove color stop command received");
|
|
66109
|
-
const currentStopsForRemove = [...this.stops];
|
|
66110
|
-
if (currentStopsForRemove.length > 2) {
|
|
66111
|
-
currentStopsForRemove.pop();
|
|
66112
|
-
this.stops = currentStopsForRemove;
|
|
66113
|
-
customParams.colorStops = this.stops.map((stop) => ({
|
|
66114
|
-
offset: stop.offset,
|
|
66115
|
-
color: typeof stop.color === "number" ? "#" + stop.color.toString(16).padStart(6, "0") : stop.color,
|
|
66116
|
-
alpha: stop.alpha
|
|
66117
|
-
}));
|
|
66118
|
-
console.log("Removed last color stop, remaining stops:", this.stops.length, "Updated colorStops:", customParams.colorStops);
|
|
66119
|
-
} else {
|
|
66120
|
-
console.warn("Cannot remove stop - minimum of 2 stops required");
|
|
66121
|
-
}
|
|
66122
|
-
break;
|
|
66123
|
-
case "cssGradient":
|
|
66124
|
-
if (value && typeof value === "string" && value.trim() !== "") {
|
|
66125
|
-
try {
|
|
66126
|
-
const tempFilter = new ColorGradientFilter({ css: value });
|
|
66127
|
-
this.type = tempFilter.type;
|
|
66128
|
-
this.angle = tempFilter.angle;
|
|
66129
|
-
this.stops = [...tempFilter.stops];
|
|
66130
|
-
customParams.colorStops = this.stops.map((stop) => ({
|
|
66131
|
-
offset: stop.offset,
|
|
66132
|
-
color: typeof stop.color === "number" ? "#" + stop.color.toString(16).padStart(6, "0") : stop.color,
|
|
66133
|
-
alpha: stop.alpha
|
|
66134
|
-
}));
|
|
66135
|
-
console.log("Applied CSS gradient, new stops:", this.stops.length, "Updated colorStops:", customParams.colorStops);
|
|
66136
|
-
} catch (error) {
|
|
66137
|
-
console.error("Failed to parse CSS gradient:", error);
|
|
66138
|
-
}
|
|
66139
|
-
}
|
|
66140
|
-
break;
|
|
66141
|
-
case "colorStops":
|
|
66142
|
-
if (Array.isArray(value)) {
|
|
66143
|
-
const processedStops = value.map((stop) => ({
|
|
66144
|
-
offset: stop.offset,
|
|
66145
|
-
color: typeof stop.color === "string" ? parseInt(stop.color.replace("#", "0x")) : stop.color,
|
|
66146
|
-
alpha: stop.alpha
|
|
66147
|
-
}));
|
|
66148
|
-
processedStops.sort((a2, b2) => a2.offset - b2.offset);
|
|
66149
|
-
this.stops = processedStops;
|
|
66071
|
+
controls.push({
|
|
66072
|
+
id: `colorStop-${index}-offset`,
|
|
66073
|
+
type: "slider",
|
|
66074
|
+
label: `Stop ${index + 1} Position`,
|
|
66075
|
+
property: `colorStops[${index}].offset`,
|
|
66076
|
+
min: 0,
|
|
66077
|
+
max: 1,
|
|
66078
|
+
step: 0.01,
|
|
66079
|
+
default: stop.offset
|
|
66080
|
+
});
|
|
66081
|
+
controls.push({
|
|
66082
|
+
id: `colorStop-${index}-alpha`,
|
|
66083
|
+
type: "slider",
|
|
66084
|
+
label: `Stop ${index + 1} Alpha`,
|
|
66085
|
+
property: `colorStops[${index}].alpha`,
|
|
66086
|
+
min: 0,
|
|
66087
|
+
max: 1,
|
|
66088
|
+
step: 0.01,
|
|
66089
|
+
default: stop.alpha
|
|
66090
|
+
});
|
|
66091
|
+
});
|
|
66092
|
+
return controls;
|
|
66093
|
+
};
|
|
66094
|
+
filter.handleButtonAction = function(action) {
|
|
66095
|
+
if (action === "addColorStop" || action === "removeColorStop") {
|
|
66096
|
+
this.updateUIParam(action, true);
|
|
66097
|
+
}
|
|
66098
|
+
};
|
|
66099
|
+
filter.updateUIParam = function(key, value) {
|
|
66100
|
+
const customParams = this._customParams ?? {};
|
|
66101
|
+
this._customParams = customParams;
|
|
66102
|
+
switch (key) {
|
|
66103
|
+
case "gradientType":
|
|
66104
|
+
this.type = Number(value);
|
|
66105
|
+
return;
|
|
66106
|
+
case "angle":
|
|
66107
|
+
case "alpha":
|
|
66108
|
+
case "maxColors":
|
|
66109
|
+
this[key] = Number(value);
|
|
66110
|
+
return;
|
|
66111
|
+
case "replace":
|
|
66112
|
+
this.replace = Boolean(value);
|
|
66113
|
+
return;
|
|
66114
|
+
case "addColorStop": {
|
|
66115
|
+
const current = Array.isArray(this.stops) ? [...this.stops] : [];
|
|
66116
|
+
const randomColor = Math.floor(Math.random() * 255) << 16 | Math.floor(Math.random() * 255) << 8 | Math.floor(Math.random() * 255);
|
|
66117
|
+
if (current.length > 0) {
|
|
66118
|
+
const scale = 0.8;
|
|
66119
|
+
for (const stop of current) {
|
|
66120
|
+
stop.offset *= scale;
|
|
66150
66121
|
}
|
|
66151
|
-
|
|
66152
|
-
|
|
66153
|
-
|
|
66154
|
-
|
|
66155
|
-
|
|
66156
|
-
|
|
66157
|
-
|
|
66158
|
-
|
|
66159
|
-
|
|
66160
|
-
|
|
66161
|
-
|
|
66162
|
-
|
|
66163
|
-
|
|
66164
|
-
|
|
66165
|
-
|
|
66166
|
-
|
|
66167
|
-
|
|
66168
|
-
|
|
66169
|
-
|
|
66170
|
-
|
|
66171
|
-
|
|
66172
|
-
|
|
66173
|
-
|
|
66174
|
-
|
|
66175
|
-
|
|
66176
|
-
|
|
66177
|
-
|
|
66178
|
-
|
|
66122
|
+
}
|
|
66123
|
+
current.push({ offset: 1, color: randomColor, alpha: 1 });
|
|
66124
|
+
current.sort((a2, b2) => a2.offset - b2.offset);
|
|
66125
|
+
this.stops = current;
|
|
66126
|
+
return;
|
|
66127
|
+
}
|
|
66128
|
+
case "removeColorStop": {
|
|
66129
|
+
const current = Array.isArray(this.stops) ? [...this.stops] : [];
|
|
66130
|
+
if (current.length > 2) {
|
|
66131
|
+
current.pop();
|
|
66132
|
+
this.stops = current;
|
|
66133
|
+
}
|
|
66134
|
+
return;
|
|
66135
|
+
}
|
|
66136
|
+
case "cssGradient": {
|
|
66137
|
+
const str = typeof value === "string" ? value.trim() : "";
|
|
66138
|
+
customParams.cssGradient = str;
|
|
66139
|
+
if (!str) return;
|
|
66140
|
+
try {
|
|
66141
|
+
const parsed = new ColorGradientFilter({ css: str });
|
|
66142
|
+
this.type = parsed.type;
|
|
66143
|
+
this.angle = parsed.angle;
|
|
66144
|
+
this.stops = [...parsed.stops];
|
|
66145
|
+
} catch {
|
|
66146
|
+
}
|
|
66147
|
+
return;
|
|
66148
|
+
}
|
|
66149
|
+
case "colorStops": {
|
|
66150
|
+
if (!Array.isArray(value)) return;
|
|
66151
|
+
const normalized = value.map(normalizeStopForFilter);
|
|
66152
|
+
normalized.sort((a2, b2) => a2.offset - b2.offset);
|
|
66153
|
+
this.stops = normalized;
|
|
66154
|
+
return;
|
|
66155
|
+
}
|
|
66156
|
+
default: {
|
|
66157
|
+
const dynamicMatch = /^colorStops\[(\d+)\]\.(offset|color|alpha)$/.exec(key);
|
|
66158
|
+
if (dynamicMatch) {
|
|
66159
|
+
const [, indexStr, prop] = dynamicMatch;
|
|
66160
|
+
const index = Number(indexStr);
|
|
66161
|
+
const stops2 = Array.isArray(this.stops) ? [...this.stops] : [];
|
|
66162
|
+
if (index < 0 || index >= stops2.length) return;
|
|
66163
|
+
if (prop === "color") {
|
|
66164
|
+
stops2[index].color = typeof value === "string" ? hexToInt(value) : Number(value);
|
|
66165
|
+
} else if (prop === "offset" || prop === "alpha") {
|
|
66166
|
+
stops2[index][prop] = Number(value);
|
|
66179
66167
|
}
|
|
66180
|
-
|
|
66168
|
+
this.stops = stops2;
|
|
66169
|
+
}
|
|
66181
66170
|
}
|
|
66182
|
-
}
|
|
66183
|
-
|
|
66184
|
-
|
|
66185
|
-
} catch (error) {
|
|
66186
|
-
console.error("Failed to create ColorGradientFilter:", error);
|
|
66187
|
-
return null;
|
|
66188
|
-
}
|
|
66171
|
+
}
|
|
66172
|
+
};
|
|
66173
|
+
return filter;
|
|
66189
66174
|
},
|
|
66190
|
-
// Default parameter values - matching the example app
|
|
66191
66175
|
defaultParams: {
|
|
66192
66176
|
gradientType: 0,
|
|
66193
|
-
// 0: linear, 1: radial, 2: conic
|
|
66194
66177
|
colorStops: [
|
|
66195
66178
|
{ offset: 0, color: "#ff0000", alpha: 1 },
|
|
66196
66179
|
{ offset: 1, color: "#0000ff", alpha: 1 }
|
|
66197
66180
|
],
|
|
66198
|
-
// Controls for adding/removing stops
|
|
66199
|
-
addColorStop: false,
|
|
66200
|
-
// Button trigger for adding a color stop
|
|
66201
|
-
removeColorStop: false,
|
|
66202
|
-
// Button trigger for removing a color stop
|
|
66203
66181
|
cssGradient: "",
|
|
66204
|
-
// CSS gradient string input
|
|
66205
66182
|
angle: 90,
|
|
66206
66183
|
alpha: 0.75,
|
|
66207
66184
|
maxColors: 0,
|
|
66208
66185
|
replace: false
|
|
66209
66186
|
},
|
|
66210
|
-
// UI controls for the filter
|
|
66211
66187
|
controls: [
|
|
66212
|
-
// Basic gradient controls
|
|
66213
66188
|
{
|
|
66214
66189
|
id: "gradientType",
|
|
66215
66190
|
type: "select",
|
|
@@ -66259,7 +66234,6 @@ registerFilter({
|
|
|
66259
66234
|
property: "replace",
|
|
66260
66235
|
default: false
|
|
66261
66236
|
},
|
|
66262
|
-
// Advanced color stop management - simpler approach
|
|
66263
66237
|
{
|
|
66264
66238
|
id: "addColorStop",
|
|
66265
66239
|
type: "button",
|
|
@@ -66278,7 +66252,7 @@ registerFilter({
|
|
|
66278
66252
|
label: "CSS Gradient",
|
|
66279
66253
|
property: "cssGradient",
|
|
66280
66254
|
default: "",
|
|
66281
|
-
|
|
66255
|
+
placeholder: "e.g. linear-gradient(to right, red, blue)"
|
|
66282
66256
|
}
|
|
66283
66257
|
]
|
|
66284
66258
|
});
|
|
@@ -67549,29 +67523,37 @@ registerFilter({
|
|
|
67549
67523
|
]
|
|
67550
67524
|
});
|
|
67551
67525
|
const { BulgePinchFilter } = PixiFilters;
|
|
67526
|
+
const computeBulgePadding = (radius) => {
|
|
67527
|
+
const r2 = Math.max(0, Number(radius) || 0);
|
|
67528
|
+
return Math.ceil(r2 + 2);
|
|
67529
|
+
};
|
|
67552
67530
|
registerFilter({
|
|
67553
67531
|
id: "bulge-pinch",
|
|
67554
|
-
// ID must match what the application expects
|
|
67555
67532
|
name: "Bulge/Pinch",
|
|
67556
67533
|
category: "distortion",
|
|
67557
67534
|
description: "Creates a bulge or pinch effect in a circular area",
|
|
67558
|
-
// Create an instance of the BulgePinchFilter with the provided parameters
|
|
67559
67535
|
createFilter: (params) => {
|
|
67560
67536
|
try {
|
|
67561
|
-
console.log("Creating BulgePinchFilter with params:", params);
|
|
67562
67537
|
const centerX = params.centerX ?? 0.5;
|
|
67563
67538
|
const centerY = params.centerY ?? 0.5;
|
|
67564
67539
|
const radius = params.radius ?? 100;
|
|
67565
67540
|
const strength = params.strength ?? 1;
|
|
67566
67541
|
const filter = new BulgePinchFilter({
|
|
67567
|
-
center: {
|
|
67568
|
-
x: centerX,
|
|
67569
|
-
y: centerY
|
|
67570
|
-
},
|
|
67542
|
+
center: { x: centerX, y: centerY },
|
|
67571
67543
|
radius,
|
|
67572
67544
|
strength
|
|
67573
67545
|
});
|
|
67546
|
+
const padding = computeBulgePadding(radius);
|
|
67547
|
+
filter.padding = Math.max(filter.padding ?? 0, padding);
|
|
67548
|
+
filter._exportPadding = padding;
|
|
67574
67549
|
filter._customParams = { ...params };
|
|
67550
|
+
const refreshPadding = (f2) => {
|
|
67551
|
+
var _a;
|
|
67552
|
+
const r2 = Number(((_a = f2._customParams) == null ? void 0 : _a.radius) ?? f2.radius ?? 0);
|
|
67553
|
+
const p2 = computeBulgePadding(r2);
|
|
67554
|
+
f2._exportPadding = p2;
|
|
67555
|
+
f2.padding = Math.max(f2.padding ?? 0, p2);
|
|
67556
|
+
};
|
|
67575
67557
|
filter.updateUIParam = function(key, value) {
|
|
67576
67558
|
const customParams = this._customParams || {};
|
|
67577
67559
|
this._customParams = customParams;
|
|
@@ -67580,44 +67562,58 @@ registerFilter({
|
|
|
67580
67562
|
case "centerX":
|
|
67581
67563
|
if (!this.center) this.center = { x: 0.5, y: 0.5 };
|
|
67582
67564
|
this.center.x = value;
|
|
67583
|
-
console.log(`Updated center.x to ${value}`);
|
|
67584
67565
|
break;
|
|
67585
67566
|
case "centerY":
|
|
67586
67567
|
if (!this.center) this.center = { x: 0.5, y: 0.5 };
|
|
67587
67568
|
this.center.y = value;
|
|
67588
|
-
console.log(`Updated center.y to ${value}`);
|
|
67589
67569
|
break;
|
|
67590
67570
|
case "radius":
|
|
67571
|
+
this.radius = Number(value);
|
|
67572
|
+
refreshPadding(this);
|
|
67573
|
+
break;
|
|
67591
67574
|
case "strength":
|
|
67592
|
-
this
|
|
67593
|
-
console.log(`Updated ${key} to ${value}`);
|
|
67575
|
+
this.strength = Number(value);
|
|
67594
67576
|
break;
|
|
67595
67577
|
default:
|
|
67596
67578
|
if (key in this) {
|
|
67597
67579
|
this[key] = value;
|
|
67598
|
-
console.log(`Updated ${key} to ${value}`);
|
|
67599
|
-
} else {
|
|
67600
|
-
console.warn(`Attempted to update unknown property ${key}`);
|
|
67601
67580
|
}
|
|
67602
67581
|
break;
|
|
67603
67582
|
}
|
|
67604
67583
|
return true;
|
|
67605
67584
|
};
|
|
67606
|
-
|
|
67585
|
+
filter.createExportFilter = function(options = {}) {
|
|
67586
|
+
const scale = Number.isFinite(options.previewToNativeScale) ? Math.max(1, Number(options.previewToNativeScale)) : 1;
|
|
67587
|
+
const stored = this._customParams || params;
|
|
67588
|
+
const scaledRadius = Number(stored.radius ?? 100) * scale;
|
|
67589
|
+
const next = new BulgePinchFilter({
|
|
67590
|
+
center: {
|
|
67591
|
+
x: Number(stored.centerX ?? 0.5),
|
|
67592
|
+
y: Number(stored.centerY ?? 0.5)
|
|
67593
|
+
},
|
|
67594
|
+
radius: scaledRadius,
|
|
67595
|
+
strength: Number(stored.strength ?? 1)
|
|
67596
|
+
});
|
|
67597
|
+
const padding2 = computeBulgePadding(scaledRadius);
|
|
67598
|
+
next._exportPadding = padding2;
|
|
67599
|
+
next.padding = Math.max(next.padding ?? 0, padding2);
|
|
67600
|
+
return next;
|
|
67601
|
+
};
|
|
67602
|
+
filter.getExportPadding = function() {
|
|
67603
|
+
return Number(this._exportPadding || this.padding || 0);
|
|
67604
|
+
};
|
|
67607
67605
|
return filter;
|
|
67608
67606
|
} catch (error) {
|
|
67609
67607
|
console.error("Failed to create BulgePinchFilter:", error);
|
|
67610
67608
|
return null;
|
|
67611
67609
|
}
|
|
67612
67610
|
},
|
|
67613
|
-
// Default parameter values
|
|
67614
67611
|
defaultParams: {
|
|
67615
67612
|
centerX: 0.5,
|
|
67616
67613
|
centerY: 0.5,
|
|
67617
67614
|
radius: 100,
|
|
67618
67615
|
strength: 1
|
|
67619
67616
|
},
|
|
67620
|
-
// UI controls for the filter
|
|
67621
67617
|
controls: [
|
|
67622
67618
|
{
|
|
67623
67619
|
id: "centerX",
|
|
@@ -70454,131 +70450,31 @@ registerFilter({
|
|
|
70454
70450
|
}
|
|
70455
70451
|
]
|
|
70456
70452
|
});
|
|
70457
|
-
|
|
70458
|
-
|
|
70459
|
-
|
|
70460
|
-
|
|
70461
|
-
|
|
70462
|
-
|
|
70463
|
-
|
|
70464
|
-
|
|
70465
|
-
|
|
70466
|
-
|
|
70467
|
-
|
|
70468
|
-
|
|
70469
|
-
|
|
70470
|
-
|
|
70471
|
-
|
|
70472
|
-
|
|
70473
|
-
|
|
70474
|
-
|
|
70475
|
-
|
|
70476
|
-
|
|
70477
|
-
|
|
70478
|
-
|
|
70479
|
-
|
|
70480
|
-
|
|
70481
|
-
|
|
70482
|
-
vec2 dir = coord - uCenter;
|
|
70483
|
-
float dist = length(dir);
|
|
70484
|
-
|
|
70485
|
-
if (dist < uRadius) {
|
|
70486
|
-
float percent = (uRadius - dist) / uRadius;
|
|
70487
|
-
float theta = percent * percent * uAngle;
|
|
70488
|
-
float s = sin(theta);
|
|
70489
|
-
float c = cos(theta);
|
|
70490
|
-
|
|
70491
|
-
dir = vec2(
|
|
70492
|
-
dir.x * c - dir.y * s,
|
|
70493
|
-
dir.x * s + dir.y * c
|
|
70494
|
-
);
|
|
70495
|
-
|
|
70496
|
-
coord = dir + uCenter;
|
|
70497
|
-
}
|
|
70498
|
-
|
|
70499
|
-
gl_FragColor = texture2D(uSampler, coord);
|
|
70500
|
-
}
|
|
70501
|
-
`
|
|
70502
|
-
});
|
|
70503
|
-
super({
|
|
70504
|
-
glProgram: glProgram2,
|
|
70505
|
-
resources: {
|
|
70506
|
-
twistUniforms: {
|
|
70507
|
-
uCenter: { type: "vec2<f32>", value: [0.5, 0.5] },
|
|
70508
|
-
uRadius: { type: "f32", value: 0.25 },
|
|
70509
|
-
uAngle: { type: "f32", value: 4 }
|
|
70510
|
-
}
|
|
70511
|
-
}
|
|
70512
|
-
});
|
|
70513
|
-
this._centerX = 0.5;
|
|
70514
|
-
this._centerY = 0.5;
|
|
70515
|
-
this._radius = 0.25;
|
|
70516
|
-
this._angle = 4;
|
|
70517
|
-
if (params) {
|
|
70518
|
-
if (params.centerX !== void 0) this._centerX = params.centerX;
|
|
70519
|
-
if (params.centerY !== void 0) this._centerY = params.centerY;
|
|
70520
|
-
if (params.radius !== void 0) this._radius = params.radius;
|
|
70521
|
-
if (params.angle !== void 0) this._angle = params.angle;
|
|
70522
|
-
}
|
|
70523
|
-
this._updateUniforms();
|
|
70524
|
-
}
|
|
70525
|
-
_updateUniforms() {
|
|
70526
|
-
if (this.uniforms) {
|
|
70527
|
-
this.uniforms.uCenter = [this._centerX, this._centerY];
|
|
70528
|
-
this.uniforms.uRadius = this._radius;
|
|
70529
|
-
this.uniforms.uAngle = this._angle;
|
|
70530
|
-
}
|
|
70531
|
-
}
|
|
70532
|
-
get centerX() {
|
|
70533
|
-
return this._centerX;
|
|
70534
|
-
}
|
|
70535
|
-
set centerX(value) {
|
|
70536
|
-
this._centerX = value;
|
|
70537
|
-
this._updateUniforms();
|
|
70538
|
-
}
|
|
70539
|
-
get centerY() {
|
|
70540
|
-
return this._centerY;
|
|
70541
|
-
}
|
|
70542
|
-
set centerY(value) {
|
|
70543
|
-
this._centerY = value;
|
|
70544
|
-
this._updateUniforms();
|
|
70545
|
-
}
|
|
70546
|
-
get radius() {
|
|
70547
|
-
return this._radius;
|
|
70548
|
-
}
|
|
70549
|
-
set radius(value) {
|
|
70550
|
-
this._radius = value;
|
|
70551
|
-
this._updateUniforms();
|
|
70552
|
-
}
|
|
70553
|
-
get angle() {
|
|
70554
|
-
return this._angle;
|
|
70555
|
-
}
|
|
70556
|
-
set angle(value) {
|
|
70557
|
-
this._angle = value;
|
|
70558
|
-
this._updateUniforms();
|
|
70559
|
-
}
|
|
70560
|
-
/**
|
|
70561
|
-
* Handle UI parameter updates
|
|
70562
|
-
*/
|
|
70563
|
-
updateUIParam(key, value) {
|
|
70564
|
-
switch (key) {
|
|
70565
|
-
case "centerX":
|
|
70566
|
-
this.centerX = value;
|
|
70567
|
-
break;
|
|
70568
|
-
case "centerY":
|
|
70569
|
-
this.centerY = value;
|
|
70570
|
-
break;
|
|
70571
|
-
case "radius":
|
|
70572
|
-
this.radius = value;
|
|
70573
|
-
break;
|
|
70574
|
-
case "angle":
|
|
70575
|
-
this.angle = value;
|
|
70576
|
-
break;
|
|
70577
|
-
default:
|
|
70578
|
-
console.warn(`Unknown parameter '${key}' for TwistFilter`);
|
|
70579
|
-
}
|
|
70580
|
-
}
|
|
70581
|
-
}
|
|
70453
|
+
const { TwistFilter } = PixiFilters;
|
|
70454
|
+
const computeTwistPadding = (radiusPx) => {
|
|
70455
|
+
return Math.ceil(Math.max(0, Number(radiusPx) || 0) + 2);
|
|
70456
|
+
};
|
|
70457
|
+
const buildTwistFilter = (raw, previewToNativeScale = 1) => {
|
|
70458
|
+
const width = Math.max(1, Number(raw._sourceWidth ?? 0));
|
|
70459
|
+
const height = Math.max(1, Number(raw._sourceHeight ?? 0));
|
|
70460
|
+
const minEdge = Math.min(width, height);
|
|
70461
|
+
const centerX = Number(raw.centerX ?? 0.5);
|
|
70462
|
+
const centerY = Number(raw.centerY ?? 0.5);
|
|
70463
|
+
const normalizedRadius = Number(raw.radius ?? 0.25);
|
|
70464
|
+
const angle = Number(raw.angle ?? 4);
|
|
70465
|
+
const offsetX = centerX * width * previewToNativeScale;
|
|
70466
|
+
const offsetY = centerY * height * previewToNativeScale;
|
|
70467
|
+
const radiusPx = normalizedRadius * minEdge * previewToNativeScale;
|
|
70468
|
+
const filter = new TwistFilter({
|
|
70469
|
+
offset: { x: offsetX, y: offsetY },
|
|
70470
|
+
radius: radiusPx,
|
|
70471
|
+
angle
|
|
70472
|
+
});
|
|
70473
|
+
const padding = computeTwistPadding(radiusPx);
|
|
70474
|
+
filter.padding = Math.max(filter.padding ?? 0, padding);
|
|
70475
|
+
filter._exportPadding = padding;
|
|
70476
|
+
return filter;
|
|
70477
|
+
};
|
|
70582
70478
|
registerFilter({
|
|
70583
70479
|
id: "twist",
|
|
70584
70480
|
name: "Twist",
|
|
@@ -70586,33 +70482,59 @@ registerFilter({
|
|
|
70586
70482
|
description: "Creates a twisting distortion effect around a central point",
|
|
70587
70483
|
createFilter: (params) => {
|
|
70588
70484
|
try {
|
|
70589
|
-
|
|
70590
|
-
const filter = new NormalizedTwistFilter({
|
|
70485
|
+
const raw = {
|
|
70591
70486
|
centerX: params.centerX ?? 0.5,
|
|
70592
70487
|
centerY: params.centerY ?? 0.5,
|
|
70593
70488
|
radius: params.radius ?? 0.25,
|
|
70594
|
-
angle: params.angle ?? 4
|
|
70595
|
-
|
|
70596
|
-
|
|
70597
|
-
|
|
70489
|
+
angle: params.angle ?? 4,
|
|
70490
|
+
_sourceWidth: params._sourceWidth,
|
|
70491
|
+
_sourceHeight: params._sourceHeight
|
|
70492
|
+
};
|
|
70493
|
+
const filter = buildTwistFilter(raw, 1);
|
|
70494
|
+
filter._customParams = { ...raw };
|
|
70495
|
+
filter.updateUIParam = function(key, value) {
|
|
70496
|
+
const customParams = this._customParams || {};
|
|
70497
|
+
this._customParams = customParams;
|
|
70498
|
+
customParams[key] = value;
|
|
70499
|
+
const width = Math.max(1, Number(customParams._sourceWidth ?? 0));
|
|
70500
|
+
const height = Math.max(1, Number(customParams._sourceHeight ?? 0));
|
|
70501
|
+
const minEdge = Math.min(width, height);
|
|
70502
|
+
const centerX = Number(customParams.centerX ?? 0.5);
|
|
70503
|
+
const centerY = Number(customParams.centerY ?? 0.5);
|
|
70504
|
+
const normalizedRadius = Number(customParams.radius ?? 0.25);
|
|
70505
|
+
const angle = Number(customParams.angle ?? 4);
|
|
70506
|
+
if (this.offset) {
|
|
70507
|
+
this.offset.x = centerX * width;
|
|
70508
|
+
this.offset.y = centerY * height;
|
|
70509
|
+
}
|
|
70510
|
+
this.radius = normalizedRadius * minEdge;
|
|
70511
|
+
this.angle = angle;
|
|
70512
|
+
const padding = computeTwistPadding(this.radius);
|
|
70513
|
+
this._exportPadding = padding;
|
|
70514
|
+
this.padding = Math.max(this.padding ?? 0, padding);
|
|
70515
|
+
return true;
|
|
70516
|
+
};
|
|
70517
|
+
filter.createExportFilter = function(options = {}) {
|
|
70518
|
+
const scale = Number.isFinite(options.previewToNativeScale) ? Math.max(1, Number(options.previewToNativeScale)) : 1;
|
|
70519
|
+
const stored = this._customParams || raw;
|
|
70520
|
+
return buildTwistFilter(stored, scale);
|
|
70521
|
+
};
|
|
70522
|
+
filter.getExportPadding = function() {
|
|
70523
|
+
return Number(this._exportPadding || this.padding || 0);
|
|
70524
|
+
};
|
|
70598
70525
|
return filter;
|
|
70599
70526
|
} catch (error) {
|
|
70600
70527
|
console.error("Failed to create TwistFilter:", error);
|
|
70601
70528
|
return null;
|
|
70602
70529
|
}
|
|
70603
70530
|
},
|
|
70604
|
-
// Default parameter values
|
|
70531
|
+
// Default parameter values — normalized (0-1) for intuitive slider use.
|
|
70605
70532
|
defaultParams: {
|
|
70606
70533
|
centerX: 0.5,
|
|
70607
|
-
// Center of image
|
|
70608
70534
|
centerY: 0.5,
|
|
70609
|
-
// Center of image
|
|
70610
70535
|
radius: 0.25,
|
|
70611
|
-
// 25% of image size
|
|
70612
70536
|
angle: 4
|
|
70613
|
-
// Twist amount in radians
|
|
70614
70537
|
},
|
|
70615
|
-
// UI controls for the filter
|
|
70616
70538
|
controls: [
|
|
70617
70539
|
{
|
|
70618
70540
|
id: "angle",
|
|
@@ -70634,7 +70556,7 @@ registerFilter({
|
|
|
70634
70556
|
max: 0.75,
|
|
70635
70557
|
step: 0.01,
|
|
70636
70558
|
default: 0.25,
|
|
70637
|
-
tooltip: "Size of the twist effect (
|
|
70559
|
+
tooltip: "Size of the twist effect (relative to image)"
|
|
70638
70560
|
},
|
|
70639
70561
|
{
|
|
70640
70562
|
id: "centerX",
|
|
@@ -71008,6 +70930,124 @@ function createChunkWriteQueue() {
|
|
|
71008
70930
|
}
|
|
71009
70931
|
};
|
|
71010
70932
|
}
|
|
70933
|
+
const DEFAULT_SEEK_TIMEOUT_MS = 4e3;
|
|
70934
|
+
const DEFAULT_SEEK_RETRY_ATTEMPTS = 3;
|
|
70935
|
+
const MAX_SEEK_RETRY_ATTEMPTS = 6;
|
|
70936
|
+
const MIN_SEEK_TIMEOUT_MS = 250;
|
|
70937
|
+
const MAX_SEEK_NUDGE_SECONDS = 0.25;
|
|
70938
|
+
const MIN_SEEK_EPSILON_SECONDS = 1e-3;
|
|
70939
|
+
function normalizeSeekTimeoutMs(value) {
|
|
70940
|
+
if (!Number.isFinite(value) || value === void 0 || value < MIN_SEEK_TIMEOUT_MS) {
|
|
70941
|
+
return DEFAULT_SEEK_TIMEOUT_MS;
|
|
70942
|
+
}
|
|
70943
|
+
return Math.floor(value);
|
|
70944
|
+
}
|
|
70945
|
+
function normalizeSeekRetryAttempts(value) {
|
|
70946
|
+
if (!Number.isFinite(value) || value === void 0 || value < 1) {
|
|
70947
|
+
return DEFAULT_SEEK_RETRY_ATTEMPTS;
|
|
70948
|
+
}
|
|
70949
|
+
return Math.min(MAX_SEEK_RETRY_ATTEMPTS, Math.floor(value));
|
|
70950
|
+
}
|
|
70951
|
+
function clampSeekTime(targetTime, videoDuration) {
|
|
70952
|
+
const normalizedTarget = Number.isFinite(targetTime) ? Math.max(0, targetTime) : 0;
|
|
70953
|
+
if (videoDuration === null || !Number.isFinite(videoDuration) || videoDuration <= 0) {
|
|
70954
|
+
return normalizedTarget;
|
|
70955
|
+
}
|
|
70956
|
+
const maxSeekTime = Math.max(0, videoDuration - MIN_SEEK_EPSILON_SECONDS);
|
|
70957
|
+
return Math.min(normalizedTarget, maxSeekTime);
|
|
70958
|
+
}
|
|
70959
|
+
function resolveSeekAttemptPlan(targetTime, { attempt, fps, videoDuration }) {
|
|
70960
|
+
const normalizedAttempt = Math.max(1, Math.floor(attempt));
|
|
70961
|
+
const normalizedFps = Number.isFinite(fps) && fps > 0 ? fps : 30;
|
|
70962
|
+
const clampedTarget = clampSeekTime(targetTime, videoDuration);
|
|
70963
|
+
if (normalizedAttempt === 1) {
|
|
70964
|
+
return [clampedTarget];
|
|
70965
|
+
}
|
|
70966
|
+
const retryIndex = normalizedAttempt - 1;
|
|
70967
|
+
const preferredDirection = retryIndex % 2 === 1 ? 1 : -1;
|
|
70968
|
+
const nudgeMagnitude = Math.min(
|
|
70969
|
+
Math.max(1 / normalizedFps, 0.05) * Math.ceil(retryIndex / 2),
|
|
70970
|
+
MAX_SEEK_NUDGE_SECONDS
|
|
70971
|
+
);
|
|
70972
|
+
for (const direction of [preferredDirection, -preferredDirection]) {
|
|
70973
|
+
const nudgedTarget = clampSeekTime(
|
|
70974
|
+
clampedTarget + direction * nudgeMagnitude,
|
|
70975
|
+
videoDuration
|
|
70976
|
+
);
|
|
70977
|
+
if (Math.abs(nudgedTarget - clampedTarget) < MIN_SEEK_EPSILON_SECONDS) {
|
|
70978
|
+
continue;
|
|
70979
|
+
}
|
|
70980
|
+
return [nudgedTarget, clampedTarget];
|
|
70981
|
+
}
|
|
70982
|
+
return [clampedTarget];
|
|
70983
|
+
}
|
|
70984
|
+
const PROBE_TIMEOUT_MS = 5e3;
|
|
70985
|
+
const PROBE_SEEK_TARGET = 1e9;
|
|
70986
|
+
async function resolveVideoDuration(video, recipe) {
|
|
70987
|
+
var _a;
|
|
70988
|
+
const initial = video.duration;
|
|
70989
|
+
if (Number.isFinite(initial) && initial > 0) {
|
|
70990
|
+
return initial;
|
|
70991
|
+
}
|
|
70992
|
+
console.warn(
|
|
70993
|
+
`[Render] video.duration=${initial}; attempting seek-past-end workaround`
|
|
70994
|
+
);
|
|
70995
|
+
const probed = await probeDurationViaSeek(video);
|
|
70996
|
+
if (Number.isFinite(probed) && probed > 0) {
|
|
70997
|
+
resetTime(video);
|
|
70998
|
+
return probed;
|
|
70999
|
+
}
|
|
71000
|
+
const fallback = (_a = recipe.source) == null ? void 0 : _a.duration;
|
|
71001
|
+
if (typeof fallback === "number" && Number.isFinite(fallback) && fallback > 0) {
|
|
71002
|
+
console.warn(
|
|
71003
|
+
`[Render] seek probe did not yield a valid duration; falling back to recipe.source.duration=${fallback}`
|
|
71004
|
+
);
|
|
71005
|
+
resetTime(video);
|
|
71006
|
+
return fallback;
|
|
71007
|
+
}
|
|
71008
|
+
throw new Error(`Invalid video duration: ${video.duration}`);
|
|
71009
|
+
}
|
|
71010
|
+
function probeDurationViaSeek(video) {
|
|
71011
|
+
return new Promise((resolve) => {
|
|
71012
|
+
let done = false;
|
|
71013
|
+
const cleanup = () => {
|
|
71014
|
+
video.removeEventListener("seeked", onSeeked);
|
|
71015
|
+
video.removeEventListener("durationchange", onDurationChange);
|
|
71016
|
+
video.removeEventListener("error", onError);
|
|
71017
|
+
window.clearTimeout(timeoutId);
|
|
71018
|
+
};
|
|
71019
|
+
const finish = (value) => {
|
|
71020
|
+
if (done) return;
|
|
71021
|
+
done = true;
|
|
71022
|
+
cleanup();
|
|
71023
|
+
resolve(value);
|
|
71024
|
+
};
|
|
71025
|
+
const onSeeked = () => finish(video.duration);
|
|
71026
|
+
const onDurationChange = () => {
|
|
71027
|
+
if (Number.isFinite(video.duration) && video.duration > 0) {
|
|
71028
|
+
finish(video.duration);
|
|
71029
|
+
}
|
|
71030
|
+
};
|
|
71031
|
+
const onError = () => finish(Number.NaN);
|
|
71032
|
+
const timeoutId = window.setTimeout(() => finish(Number.NaN), PROBE_TIMEOUT_MS);
|
|
71033
|
+
video.addEventListener("seeked", onSeeked);
|
|
71034
|
+
video.addEventListener("durationchange", onDurationChange);
|
|
71035
|
+
video.addEventListener("error", onError);
|
|
71036
|
+
try {
|
|
71037
|
+
video.currentTime = PROBE_SEEK_TARGET;
|
|
71038
|
+
} catch {
|
|
71039
|
+
finish(Number.NaN);
|
|
71040
|
+
}
|
|
71041
|
+
});
|
|
71042
|
+
}
|
|
71043
|
+
function resetTime(video) {
|
|
71044
|
+
try {
|
|
71045
|
+
video.currentTime = 0;
|
|
71046
|
+
} catch {
|
|
71047
|
+
}
|
|
71048
|
+
}
|
|
71049
|
+
const HAVE_CURRENT_DATA = 2;
|
|
71050
|
+
const PENDING_SEEK_DRAIN_TIMEOUT_MS = 1e3;
|
|
71011
71051
|
function resolveFitMode(recipe) {
|
|
71012
71052
|
var _a;
|
|
71013
71053
|
const mode = (_a = recipe.metadata) == null ? void 0 : _a.fit_mode;
|
|
@@ -71062,47 +71102,181 @@ function toBase64(buffer) {
|
|
|
71062
71102
|
}
|
|
71063
71103
|
return btoa(binary);
|
|
71064
71104
|
}
|
|
71065
|
-
|
|
71105
|
+
function waitForMilliseconds(milliseconds) {
|
|
71066
71106
|
return new Promise((resolve) => {
|
|
71067
|
-
|
|
71068
|
-
|
|
71069
|
-
|
|
71070
|
-
|
|
71071
|
-
|
|
71107
|
+
window.setTimeout(resolve, milliseconds);
|
|
71108
|
+
});
|
|
71109
|
+
}
|
|
71110
|
+
function touchRenderHeartbeat(stage) {
|
|
71111
|
+
window.__RENDER_HEARTBEAT__ = Date.now();
|
|
71112
|
+
if (stage !== void 0) {
|
|
71113
|
+
window.__RENDER_STAGE__ = stage;
|
|
71114
|
+
}
|
|
71115
|
+
}
|
|
71116
|
+
async function waitForPendingSeekToSettle(video, timeoutMs) {
|
|
71117
|
+
if (!video.seeking) {
|
|
71118
|
+
return true;
|
|
71119
|
+
}
|
|
71120
|
+
return new Promise((resolve) => {
|
|
71121
|
+
let settled = false;
|
|
71122
|
+
const finish = (result) => {
|
|
71123
|
+
if (settled) {
|
|
71124
|
+
return;
|
|
71072
71125
|
}
|
|
71073
|
-
|
|
71126
|
+
settled = true;
|
|
71127
|
+
window.clearTimeout(timeoutId);
|
|
71128
|
+
video.removeEventListener("seeked", onSeeked);
|
|
71129
|
+
video.removeEventListener("error", onError);
|
|
71130
|
+
resolve(result);
|
|
71131
|
+
};
|
|
71132
|
+
const onSeeked = () => finish(true);
|
|
71133
|
+
const onError = () => finish(false);
|
|
71134
|
+
const timeoutId = window.setTimeout(() => finish(false), timeoutMs);
|
|
71135
|
+
video.addEventListener("seeked", onSeeked);
|
|
71136
|
+
video.addEventListener("error", onError);
|
|
71137
|
+
});
|
|
71138
|
+
}
|
|
71139
|
+
async function drawCurrentVideoFrameToCanvas(video, sourceCtx, sourceWidth, sourceHeight) {
|
|
71140
|
+
if (video.readyState < HAVE_CURRENT_DATA) {
|
|
71141
|
+
throw new Error(
|
|
71142
|
+
`Video frame data unavailable before source copy (readyState=${video.readyState}, seeking=${video.seeking}, currentTime=${video.currentTime.toFixed(4)}, duration=${video.duration}, networkState=${video.networkState})`
|
|
71143
|
+
);
|
|
71144
|
+
}
|
|
71145
|
+
if (typeof createImageBitmap === "function") {
|
|
71146
|
+
const bitmap = await createImageBitmap(video);
|
|
71147
|
+
try {
|
|
71148
|
+
sourceCtx.clearRect(0, 0, sourceWidth, sourceHeight);
|
|
71149
|
+
sourceCtx.drawImage(bitmap, 0, 0, sourceWidth, sourceHeight);
|
|
71150
|
+
} finally {
|
|
71151
|
+
bitmap.close();
|
|
71152
|
+
}
|
|
71153
|
+
return;
|
|
71154
|
+
}
|
|
71155
|
+
sourceCtx.clearRect(0, 0, sourceWidth, sourceHeight);
|
|
71156
|
+
sourceCtx.drawImage(video, 0, 0, sourceWidth, sourceHeight);
|
|
71157
|
+
}
|
|
71158
|
+
async function ensureFrameDecoded(video, timeoutMs) {
|
|
71159
|
+
return new Promise((resolve, reject) => {
|
|
71160
|
+
let settled = false;
|
|
71161
|
+
let rvfcId = null;
|
|
71162
|
+
const finish = (error) => {
|
|
71163
|
+
if (settled) {
|
|
71164
|
+
return;
|
|
71165
|
+
}
|
|
71166
|
+
settled = true;
|
|
71167
|
+
window.clearTimeout(timeoutId);
|
|
71168
|
+
if (rvfcId !== null && typeof video.cancelVideoFrameCallback === "function") {
|
|
71169
|
+
video.cancelVideoFrameCallback(rvfcId);
|
|
71170
|
+
}
|
|
71171
|
+
if (error !== void 0) {
|
|
71172
|
+
reject(error);
|
|
71173
|
+
return;
|
|
71174
|
+
}
|
|
71175
|
+
resolve();
|
|
71176
|
+
};
|
|
71177
|
+
const timeoutId = window.setTimeout(() => {
|
|
71178
|
+
finish(new Error(`Frame decode timed out after ${timeoutMs}ms`));
|
|
71179
|
+
}, timeoutMs);
|
|
71180
|
+
if (typeof video.requestVideoFrameCallback === "function") {
|
|
71181
|
+
rvfcId = video.requestVideoFrameCallback(() => {
|
|
71182
|
+
finish();
|
|
71183
|
+
});
|
|
71184
|
+
}
|
|
71074
71185
|
requestAnimationFrame(() => {
|
|
71075
71186
|
requestAnimationFrame(() => {
|
|
71076
|
-
|
|
71077
|
-
resolved = true;
|
|
71078
|
-
video.cancelVideoFrameCallback(rvfcId);
|
|
71079
|
-
resolve();
|
|
71080
|
-
}
|
|
71187
|
+
finish();
|
|
71081
71188
|
});
|
|
71082
71189
|
});
|
|
71083
71190
|
});
|
|
71084
71191
|
}
|
|
71085
|
-
function
|
|
71192
|
+
function seekVideoOnce(video, timeSec, timeoutMs) {
|
|
71086
71193
|
return new Promise((resolve, reject) => {
|
|
71087
|
-
|
|
71088
|
-
|
|
71089
|
-
|
|
71090
|
-
|
|
71091
|
-
|
|
71194
|
+
let settled = false;
|
|
71195
|
+
const finish = (error) => {
|
|
71196
|
+
if (settled) {
|
|
71197
|
+
return;
|
|
71198
|
+
}
|
|
71199
|
+
settled = true;
|
|
71200
|
+
window.clearTimeout(timeoutId);
|
|
71092
71201
|
video.removeEventListener("seeked", onSeeked);
|
|
71093
71202
|
video.removeEventListener("error", onError);
|
|
71094
|
-
|
|
71203
|
+
if (error !== void 0) {
|
|
71204
|
+
reject(error);
|
|
71205
|
+
return;
|
|
71206
|
+
}
|
|
71207
|
+
resolve();
|
|
71208
|
+
};
|
|
71209
|
+
const onSeeked = () => {
|
|
71210
|
+
ensureFrameDecoded(video, timeoutMs).then(() => finish()).catch((error) => {
|
|
71211
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
71212
|
+
finish(new Error(`Frame decode failed at ${timeSec}s: ${message}`));
|
|
71213
|
+
});
|
|
71095
71214
|
};
|
|
71096
71215
|
const onError = () => {
|
|
71097
|
-
|
|
71098
|
-
video.removeEventListener("error", onError);
|
|
71099
|
-
reject(new Error(`Video seek failed at ${timeSec}s`));
|
|
71216
|
+
finish(new Error(`Video seek failed at ${timeSec}s`));
|
|
71100
71217
|
};
|
|
71218
|
+
const timeoutId = window.setTimeout(() => {
|
|
71219
|
+
finish(new Error(
|
|
71220
|
+
`Video seek timed out at ${timeSec}s after ${timeoutMs}ms (readyState=${video.readyState}, seeking=${video.seeking}, currentTime=${video.currentTime.toFixed(4)}, duration=${video.duration}, networkState=${video.networkState}, paused=${video.paused}, ended=${video.ended})`
|
|
71221
|
+
));
|
|
71222
|
+
}, timeoutMs);
|
|
71223
|
+
if (!video.seeking && Math.abs(video.currentTime - timeSec) < 1e-3) {
|
|
71224
|
+
ensureFrameDecoded(video, timeoutMs).then(() => finish()).catch((error) => {
|
|
71225
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
71226
|
+
finish(new Error(`Frame decode failed at ${timeSec}s: ${message}`));
|
|
71227
|
+
});
|
|
71228
|
+
return;
|
|
71229
|
+
}
|
|
71101
71230
|
video.addEventListener("seeked", onSeeked);
|
|
71102
71231
|
video.addEventListener("error", onError);
|
|
71103
|
-
|
|
71232
|
+
try {
|
|
71233
|
+
video.currentTime = timeSec;
|
|
71234
|
+
} catch (error) {
|
|
71235
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
71236
|
+
finish(new Error(`Unable to start video seek at ${timeSec}s: ${message}`));
|
|
71237
|
+
}
|
|
71104
71238
|
});
|
|
71105
71239
|
}
|
|
71240
|
+
async function seekVideo(video, timeSec, timeoutMs, retryAttempts, fps) {
|
|
71241
|
+
const normalizedTimeoutMs = normalizeSeekTimeoutMs(timeoutMs);
|
|
71242
|
+
const normalizedRetryAttempts = normalizeSeekRetryAttempts(retryAttempts);
|
|
71243
|
+
const normalizedDuration = Number.isFinite(video.duration) && video.duration > 0 ? video.duration : null;
|
|
71244
|
+
let lastError = null;
|
|
71245
|
+
for (let attempt = 1; attempt <= normalizedRetryAttempts; attempt += 1) {
|
|
71246
|
+
const targets = resolveSeekAttemptPlan(timeSec, {
|
|
71247
|
+
attempt,
|
|
71248
|
+
fps,
|
|
71249
|
+
videoDuration: normalizedDuration
|
|
71250
|
+
});
|
|
71251
|
+
try {
|
|
71252
|
+
for (const target of targets) {
|
|
71253
|
+
touchRenderHeartbeat("seeking");
|
|
71254
|
+
await seekVideoOnce(video, target, normalizedTimeoutMs);
|
|
71255
|
+
}
|
|
71256
|
+
return;
|
|
71257
|
+
} catch (error) {
|
|
71258
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
71259
|
+
console.warn("[RenderVideo] seek attempt failed", {
|
|
71260
|
+
attempt,
|
|
71261
|
+
targets,
|
|
71262
|
+
error: lastError.message
|
|
71263
|
+
});
|
|
71264
|
+
if (attempt < normalizedRetryAttempts) {
|
|
71265
|
+
const pendingSeekSettled = await waitForPendingSeekToSettle(
|
|
71266
|
+
video,
|
|
71267
|
+
Math.min(PENDING_SEEK_DRAIN_TIMEOUT_MS, normalizedTimeoutMs)
|
|
71268
|
+
);
|
|
71269
|
+
if (!pendingSeekSettled) {
|
|
71270
|
+
break;
|
|
71271
|
+
}
|
|
71272
|
+
await waitForMilliseconds(Math.min(250 * attempt, 1e3));
|
|
71273
|
+
}
|
|
71274
|
+
}
|
|
71275
|
+
}
|
|
71276
|
+
throw new Error(
|
|
71277
|
+
`Video seek failed at ${timeSec}s after ${normalizedRetryAttempts} attempts: ${(lastError == null ? void 0 : lastError.message) ?? "unknown error"}`
|
|
71278
|
+
);
|
|
71279
|
+
}
|
|
71106
71280
|
function waitForVideoReady(video) {
|
|
71107
71281
|
return new Promise((resolve, reject) => {
|
|
71108
71282
|
if (video.readyState >= 1) {
|
|
@@ -71178,6 +71352,8 @@ async function main() {
|
|
|
71178
71352
|
window.__RENDER_PROGRESS__ = 0;
|
|
71179
71353
|
window.__RENDER_ERROR__ = null;
|
|
71180
71354
|
window.__RENDER_AUDIO_STATUS__ = null;
|
|
71355
|
+
window.__RENDER_HEARTBEAT__ = Date.now();
|
|
71356
|
+
window.__RENDER_STAGE__ = "initializing";
|
|
71181
71357
|
try {
|
|
71182
71358
|
const config = window.__RENDER_CONFIG__;
|
|
71183
71359
|
if (!config) throw new Error("No __RENDER_CONFIG__ found on window");
|
|
@@ -71192,13 +71368,12 @@ async function main() {
|
|
|
71192
71368
|
} = config;
|
|
71193
71369
|
const trimStart = resolveTrimStart(recipe);
|
|
71194
71370
|
const video = document.getElementById("source-video");
|
|
71371
|
+
touchRenderHeartbeat("loading-source");
|
|
71195
71372
|
video.src = sourceUrl;
|
|
71196
71373
|
video.load();
|
|
71197
71374
|
await waitForVideoReady(video);
|
|
71198
|
-
|
|
71199
|
-
|
|
71200
|
-
throw new Error(`Invalid video duration: ${videoDuration}`);
|
|
71201
|
-
}
|
|
71375
|
+
touchRenderHeartbeat("probing-duration");
|
|
71376
|
+
const videoDuration = await resolveVideoDuration(video, recipe);
|
|
71202
71377
|
const trimEnd = resolveTrimEnd(recipe, videoDuration);
|
|
71203
71378
|
const exportDuration = Math.max(0, trimEnd - trimStart);
|
|
71204
71379
|
const totalFrames = Math.max(1, Math.ceil(exportDuration * fps));
|
|
@@ -71208,6 +71383,7 @@ async function main() {
|
|
|
71208
71383
|
window.__RENDER_AUDIO_STATUS__ = "skipped:AudioEncoder-unavailable";
|
|
71209
71384
|
}
|
|
71210
71385
|
const audioPromise = hasAudioEncoder ? decodeAudioFromSource(sourceUrl) : Promise.resolve(null);
|
|
71386
|
+
touchRenderHeartbeat("initializing-pixi");
|
|
71211
71387
|
const app = new Application();
|
|
71212
71388
|
await app.init({
|
|
71213
71389
|
width,
|
|
@@ -71335,26 +71511,24 @@ async function main() {
|
|
|
71335
71511
|
}
|
|
71336
71512
|
});
|
|
71337
71513
|
encoder.configure(encoderConfig);
|
|
71338
|
-
const targetCanvas = document.createElement("canvas");
|
|
71339
|
-
targetCanvas.width = width;
|
|
71340
|
-
targetCanvas.height = height;
|
|
71341
|
-
const targetCtx = targetCanvas.getContext("2d");
|
|
71342
|
-
if (!targetCtx) throw new Error("Failed to create 2D context for export");
|
|
71343
71514
|
for (let frame = 0; frame < totalFrames; frame++) {
|
|
71344
71515
|
const timeSec = trimStart + frame / fps;
|
|
71345
|
-
await seekVideo(
|
|
71346
|
-
|
|
71516
|
+
await seekVideo(
|
|
71517
|
+
video,
|
|
71518
|
+
timeSec,
|
|
71519
|
+
config.seekTimeoutMs ?? 4e3,
|
|
71520
|
+
config.seekRetryAttempts ?? 3,
|
|
71521
|
+
fps
|
|
71522
|
+
);
|
|
71523
|
+
await drawCurrentVideoFrameToCanvas(video, sourceCtx, sourceWidth, sourceHeight);
|
|
71347
71524
|
videoTexture.source.update();
|
|
71348
71525
|
app.render();
|
|
71349
|
-
const gl = app.renderer.gl;
|
|
71350
|
-
if (gl) {
|
|
71351
|
-
gl.finish();
|
|
71352
|
-
}
|
|
71353
|
-
targetCtx.clearRect(0, 0, width, height);
|
|
71354
|
-
targetCtx.drawImage(app.canvas, 0, 0, width, height);
|
|
71355
71526
|
const timestamp = Math.floor(frame / fps * 1e6);
|
|
71356
71527
|
const frameDuration = Math.floor(1e6 / fps);
|
|
71357
|
-
const videoFrame = new VideoFrame(
|
|
71528
|
+
const videoFrame = new VideoFrame(
|
|
71529
|
+
app.canvas,
|
|
71530
|
+
{ timestamp, duration: frameDuration }
|
|
71531
|
+
);
|
|
71358
71532
|
const keyFrame = frame % (fps * 2) === 0;
|
|
71359
71533
|
encoder.encode(videoFrame, { keyFrame });
|
|
71360
71534
|
videoFrame.close();
|
|
@@ -71364,11 +71538,14 @@ async function main() {
|
|
|
71364
71538
|
});
|
|
71365
71539
|
}
|
|
71366
71540
|
window.__RENDER_PROGRESS__ = Math.round((frame + 1) / totalFrames * 90);
|
|
71541
|
+
touchRenderHeartbeat("encoding-video");
|
|
71367
71542
|
}
|
|
71543
|
+
touchRenderHeartbeat("flushing-video");
|
|
71368
71544
|
await encoder.flush();
|
|
71369
71545
|
encoder.close();
|
|
71370
71546
|
if (hasAudio) {
|
|
71371
71547
|
window.__RENDER_PROGRESS__ = 91;
|
|
71548
|
+
touchRenderHeartbeat("encoding-audio");
|
|
71372
71549
|
const trimmed = trimAudioBuffer(audioBuffer, trimStart, trimEnd);
|
|
71373
71550
|
const audioEncoderConfig = {
|
|
71374
71551
|
codec: "mp4a.40.2",
|
|
@@ -71414,7 +71591,9 @@ async function main() {
|
|
|
71414
71591
|
audioEncoder.addEventListener("dequeue", () => resolve(), { once: true });
|
|
71415
71592
|
});
|
|
71416
71593
|
}
|
|
71594
|
+
touchRenderHeartbeat("encoding-audio");
|
|
71417
71595
|
}
|
|
71596
|
+
touchRenderHeartbeat("flushing-audio");
|
|
71418
71597
|
await audioEncoder.flush();
|
|
71419
71598
|
audioEncoder.close();
|
|
71420
71599
|
console.log("[RenderAudio] Audio encoding complete");
|
|
@@ -71425,15 +71604,18 @@ async function main() {
|
|
|
71425
71604
|
}
|
|
71426
71605
|
window.__RENDER_PROGRESS__ = 99;
|
|
71427
71606
|
}
|
|
71607
|
+
touchRenderHeartbeat("finalizing-muxer");
|
|
71428
71608
|
muxer.finalize();
|
|
71429
71609
|
await chunkWriteQueue.flush();
|
|
71430
71610
|
window.__RENDER_PROGRESS__ = 100;
|
|
71431
71611
|
window.__RENDER_STATUS__ = "done";
|
|
71612
|
+
touchRenderHeartbeat("done");
|
|
71432
71613
|
app.destroy(true);
|
|
71433
71614
|
} catch (err) {
|
|
71434
71615
|
const message = err instanceof Error ? err.message : String(err);
|
|
71435
71616
|
window.__RENDER_ERROR__ = message;
|
|
71436
71617
|
window.__RENDER_STATUS__ = "error";
|
|
71618
|
+
touchRenderHeartbeat("error");
|
|
71437
71619
|
throw err;
|
|
71438
71620
|
}
|
|
71439
71621
|
}
|